Skip to content

Commit 5dd8708

Browse files
roed314claude
andcommitted
Only highlight a select once it constrains the results
A select beside a text box usually says how to read that box, and is handed to a @search_parser function as its mode. Those return immediately when their field is empty, so such a select cannot affect the query on its own; TextBoxWithSelect now treats its select as constraining only once the text box it qualifies has been filled in. Six selects are read directly while parsing instead, and so do restrict the results by themselves: the level type in classical modular forms and modular curves, the conductor type in elliptic curves and mod-l Galois representations, and the two CMF parities, which usually arrive with their text boxes empty because simult_change() submits both at once. Those pass is_constraint=True. Modular curves matters in particular because modcurve_browse.html links straight to ?level_type=... searches. The hypergeometric prime and the abelian variety geometric-decomposition checkbox are the same shape: both only pick which column a partner box is matched against, so they are gated on those boxes rather than highlighted whenever set. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent a6cfd79 commit 5dd8708

12 files changed

Lines changed: 154 additions & 7 deletions

File tree

lmfdb/abvar/fq/main.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,23 @@ def AV_data(label):
162162
return datapage(labels, tables, title=f"Abelian variety isogeny class data - {label}", bread=bread, label_cols=label_cols, sorts=sorts)
163163

164164

165+
# The inputs whose database column the "Use geometric decomposition"
166+
# checkbox switches; see common_parse
167+
GEOM_DECOMP_INPUTS = (["dim%s_factors" % n for n in range(1, 6)]
168+
+ ["dim%s_distinct" % n for n in range(1, 4)]
169+
+ ["number_field", "galois_group"])
170+
171+
def use_geom_decomp_is_constraint(info):
172+
"""
173+
Whether the "Use geometric decomposition" checkbox is restricting which
174+
results are shown.
175+
176+
It only chooses which columns the dimension, number field and Galois
177+
group inputs are matched against, so with all of those empty it changes
178+
neither the results nor how they are displayed.
179+
"""
180+
return any((info.get(name) or '').strip() for name in GEOM_DECOMP_INPUTS)
181+
165182
class AbvarSearchArray(SearchArray):
166183
sorts = [("", "dimension", ['g', 'q', 'poly']),
167184
("q", "field", ['q', 'g', 'poly']),
@@ -426,7 +443,8 @@ def nbsp(knowl, label):
426443
use_geom_decomp = CheckBox(
427444
"use_geom_decomp",
428445
label=uglabel,
429-
short_label=uglabel
446+
short_label=uglabel,
447+
is_constraint=use_geom_decomp_is_constraint
430448
)
431449
use_geom_index = CheckboxSpacer(use_geom_decomp, colspan=4, advanced=True)
432450
use_geom_refine = CheckboxSpacer(use_geom_decomp, colspan=5, advanced=True)

lmfdb/abvar/fq/test_av.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@
33

44
class AVTest(LmfdbTest):
55
# All tests should pass
6+
def test_geom_decomp_active_classes(self):
7+
r"""
8+
Check that the geometric decomposition checkbox is only marked as
9+
constraining the results once one of the inputs it re-targets is used
10+
"""
11+
url = "/Variety/Abelian/Fq/?q=2&g=2&use_geom_decomp=yes&search_type=List"
12+
self.assertEqual(self.search_classes(url, "use_geom_decomp"), set())
13+
url += "&dim1_factors=1"
14+
self.assertEqual(self.search_classes(url, "use_geom_decomp"),
15+
{"search_constraint", "search_active"})
16+
617
def test_polynomial(self):
718
r"""
819
Check that the formatted polynomial displays correctly

lmfdb/classical_modular_forms/main.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1582,6 +1582,9 @@ class CMFSearchArray(SearchArray):
15821582
}
15831583

15841584
def __init__(self):
1585+
# Unlike most quantifier selects, this one restricts the results on
1586+
# its own: common_parse turns prime/square/squarefree/... into a
1587+
# level_is_* condition whether or not a level was entered
15851588
level_quantifier = SelectBox(
15861589
name='level_type',
15871590
options=[('', ''),
@@ -1594,7 +1597,8 @@ def __init__(self):
15941597
('divides','divides'),
15951598
('multiple','multiple of'),
15961599
],
1597-
min_width=110)
1600+
min_width=110,
1601+
is_constraint=True)
15981602
level = TextBoxWithSelect(
15991603
name='level',
16001604
label='Level',
@@ -1603,10 +1607,14 @@ def __init__(self):
16031607
example_span='4, 1-20',
16041608
select_box=level_quantifier)
16051609

1610+
# The parities also restrict the results on their own, and in fact
1611+
# usually arrive that way: simult_change() sets every simult_select
1612+
# at once, so both are submitted even with the text boxes empty
16061613
weight_quantifier = ParityMod(
16071614
name='weight_parity',
16081615
classes=["simult_select"],
1609-
extra=['onchange="simult_change(event);"'])
1616+
extra=['onchange="simult_change(event);"'],
1617+
is_constraint=True)
16101618

16111619
weight = TextBoxWithSelect(
16121620
name='weight',
@@ -1619,7 +1627,8 @@ def __init__(self):
16191627
character_quantifier = ParityMod(
16201628
name='char_parity',
16211629
classes=["simult_select"],
1622-
extra=['onchange="simult_change(event);"'])
1630+
extra=['onchange="simult_change(event);"'],
1631+
is_constraint=True)
16231632

16241633
character = TextBoxWithSelect(
16251634
name='char_label',

lmfdb/classical_modular_forms/test_cmf2.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,24 @@ def test_simult_select_classes(self):
214214
self.assertEqual(self.search_classes(url, name),
215215
{'simult_select', 'search_constraint'})
216216

217+
def test_quantifier_active_classes(self):
218+
# A quantifier select is handed to a parser that does nothing when
219+
# its text box is empty, so it is only active once that box is filled
220+
url = '/ModularForm/GL2/Q/holomorphic/?level=11'
221+
self.assertEqual(self.search_classes(url, 'prime_quantifier'), set())
222+
url = ('/ModularForm/GL2/Q/holomorphic/?level=11&level_primes=11'
223+
'&prime_quantifier=exactly')
224+
self.assertEqual(self.search_classes(url, 'prime_quantifier'),
225+
{'search_constraint', 'search_active'})
226+
227+
# The parities and the level type are the exceptions: common_parse
228+
# reads them whether or not their text box was filled in, so they do
229+
# constrain the results on their own and must stay highlighted
230+
url = '/ModularForm/GL2/Q/holomorphic/?level=11-20&weight_parity=odd'
231+
self.assertEqual(self.search_classes(url, 'weight_parity'),
232+
{'simult_select', 'search_constraint', 'search_active'})
233+
self.assertEqual(self.search_classes(url, 'weight'), {'search_constraint'})
234+
217235
def test_trivial_searches(self):
218236
from sage.all import Subsets
219237
for begin in [

lmfdb/elliptic_curves/elliptic_curve.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1291,6 +1291,9 @@ class ECSearchArray(SearchArray):
12911291
}
12921292

12931293
def __init__(self):
1294+
# Unlike most quantifier selects, this one restricts the results on
1295+
# its own: prime/p-power/sq-free set num_bad_primes and semistable
1296+
# whether or not a conductor was entered
12941297
conductor_quantifier = SelectBox(
12951298
name='conductor_type',
12961299
options=[('', ''),
@@ -1300,7 +1303,8 @@ def __init__(self):
13001303
('divides','divides'),
13011304
('multiple','multiple of'),
13021305
],
1303-
min_width=85)
1306+
min_width=85,
1307+
is_constraint=True)
13041308
cond = TextBoxWithSelect(
13051309
name="conductor",
13061310
label="Conductor",

lmfdb/hypergm/main.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,17 @@ def labels_page():
712712
learnmore=learnmore_list_remove('labels'))
713713

714714

715+
def p_is_constraint(info):
716+
"""
717+
Whether the prime $p$ input is restricting which results are shown.
718+
719+
The prime only picks which columns the $A_p$, $B_p$, $A^\\perp_p$ and
720+
$B^\\perp_p$ boxes are compared against, so with all four of those empty
721+
it changes neither the results nor how they are displayed.
722+
"""
723+
return any((info.get(name) or '').strip()
724+
for name in ['Ap', 'Bp', 'Apperp', 'Bpperp'])
725+
715726
class HGMSearchArray(SearchArray):
716727
_sort = [('', 'degree', ['degree', 'weight', 'A', 'B', 'label']),
717728
('weight', 'weight', ['weight', 'degree', 'A', 'B', 'label']),
@@ -764,7 +775,8 @@ def __init__(self):
764775
options=[("", 2),
765776
("3", 3),
766777
("5", 5),
767-
("7", 7)])
778+
("7", 7)],
779+
is_constraint=p_is_constraint)
768780
Ap = TextBox(
769781
name="Ap",
770782
label="$A_p$",

lmfdb/hypergm/test_hgm.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ def test_search_active_classes(self):
7373
self.assertEqual(self.search_classes(url, "weight"),
7474
{"family", "search_constraint"})
7575

76+
def test_prime_active_classes(self):
77+
# The prime only picks the columns the p-part boxes are compared
78+
# against, so on its own it does not constrain the results
79+
url = "/Motive/Hypergeometric/Q/?degree=4&p=3&search_type=Family"
80+
self.assertEqual(self.search_classes(url, "p"), set())
81+
url += "&Ap=%5B2%2C1%5D"
82+
self.assertEqual(self.search_classes(url, "p"),
83+
{"search_constraint", "search_active"})
84+
7685
def test_search_weight(self):
7786
self.check_args("/Motive/Hypergeometric/Q/?weight=3&search_type=Family", "A5_B6.6")
7887
self.not_check_args("/Motive/Hypergeometric/Q/?weight=3&search_type=Family", "A3_B4")

lmfdb/modl_galois_representations/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,9 @@ class ModLGalRepSearchArray(SearchArray):
227227
jump_knowl = "modlgal.search_input"
228228

229229
def __init__(self):
230+
# Unlike most quantifier selects, this one restricts the results on
231+
# its own: prime/p-power/sq-free set conductor_num_primes and
232+
# conductor_is_squarefree whether or not a conductor was entered
230233
conductor_quantifier = SelectBox(
231234
name='conductor_type',
232235
options=[('', ''),
@@ -236,6 +239,7 @@ def __init__(self):
236239
('divides','divides'),
237240
('multiple','multiple of'),
238241
],
242+
is_constraint=True,
239243
)
240244
conductor = TextBoxWithSelect(
241245
name="conductor",

lmfdb/modular_curves/main.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,10 @@ class ModCurveSearchArray(SearchArray):
767767
jump_knowl = "modcurve.search_input"
768768

769769
def __init__(self):
770+
# Unlike most quantifier selects, this one restricts the results on
771+
# its own: prime/p-power/sq-free set num_bad_primes and
772+
# level_is_squarefree whether or not a level was entered, and
773+
# modcurve_browse.html links straight to ?level_type=... searches
770774
level_quantifier = SelectBox(
771775
name="level_type",
772776
options=[('', ''),
@@ -776,7 +780,8 @@ def __init__(self):
776780
('divides', 'divides'),
777781
('multiple', 'multiple of'),
778782
],
779-
min_width=85)
783+
min_width=85,
784+
is_constraint=True)
780785
level = TextBoxWithSelect(
781786
name="level",
782787
knowl="modcurve.level",

lmfdb/modular_curves/test_modular_curves.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ def test_level_search(self):
3434
L = self.tc.get("/ModularCurve/Q/?start=0&level_type=divides&level=15")
3535
assert "15.12.0.b.2" in L.get_data(as_text=True)
3636

37+
def test_level_type_active_classes(self):
38+
# modcurve_browse.html links straight to ?level_type=... searches, so
39+
# the level type has to stay marked as constraining the results even
40+
# with no level entered, unlike the quantifier selects that only say
41+
# how to read the box beside them
42+
self.assertEqual(self.search_classes("/ModularCurve/Q/?level_type=prime",
43+
"level_type"),
44+
{"search_constraint", "search_active"})
45+
self.assertEqual(self.search_classes("/ModularCurve/Q/?level=13",
46+
"level_type"),
47+
{"search_constraint"})
48+
3749
def test_index_range(self):
3850
L = self.tc.get("/ModularCurve/Q/?index=100-1000")
3951
assert "6.144.1-6.b.1.1" in L.get_data(as_text=True)

0 commit comments

Comments
 (0)