From 1c43f467fc061489908c489055185c310e1fdf06 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Fri, 15 May 2026 10:27:06 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20replace=20stale=20=5Fcutoffs=20test=20?= =?UTF-8?q?=E2=80=94=20analytics=20now=20uses=20RANGE=5FSPECS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bb54152 retired the _cutoffs() helper when the time-range dropdown landed (1d/7d/14d/28d/6mo/1yr/lifetime), but TestAnalyticsHelpers still imported it and asserted against the old "21d"/"365d" keys — the breakage blocked CI for that commit and stalled the master deploy queue. Rewrites the test to bind against the new RANGE_SPECS / RANGE_KEYS / RANGE_LABELS surface that the dropdown actually reads. --- make_post_sell/tests/test_models.py | 122 +++++++++++++++++++++++++--- 1 file changed, 112 insertions(+), 10 deletions(-) diff --git a/make_post_sell/tests/test_models.py b/make_post_sell/tests/test_models.py index 48c861c..fdd9404 100644 --- a/make_post_sell/tests/test_models.py +++ b/make_post_sell/tests/test_models.py @@ -3518,14 +3518,24 @@ class TestAnalyticsHelpers(unittest.TestCase): from ..views.analytics import _fmt_score self.assertEqual(_fmt_score(0.0), "0.0") - def test_cutoffs_returns_expected_keys(self): - from ..views.analytics import _cutoffs - cuts = _cutoffs() - day_ms = 24 * 60 * 60 * 1000 - for key in ("1d", "7d", "14d", "21d", "28d", "365d"): - self.assertIn(key, cuts) - self.assertAlmostEqual(cuts["7d"] - cuts["14d"], 7 * day_ms, delta=1000) - self.assertAlmostEqual(cuts["14d"] - cuts["21d"], 7 * day_ms, delta=1000) + def test_range_specs_cover_dropdown_options(self): + """RANGE_SPECS / RANGE_KEYS shape — what the time-range dropdown + binds against. Replaces the older _cutoffs() helper retired when + the dropdown landed (bb54152: ?range=1d/7d/14d/28d/6mo/1yr/lifetime).""" + from ..views.analytics import ( + RANGE_KEYS, RANGE_SPECS, RANGE_LABELS, DEFAULT_RANGE_KEY, + ) + self.assertEqual(list(RANGE_SPECS.keys()), RANGE_KEYS) + self.assertIn(DEFAULT_RANGE_KEY, RANGE_SPECS) + self.assertIn("1d", RANGE_SPECS) + self.assertIn("lifetime", RANGE_SPECS) + # Every spec must carry the fields the chart renderer reads. + for key, spec in RANGE_SPECS.items(): + self.assertIn("buckets", spec) + self.assertIn("bucket_ms", spec) + self.assertIn("label_fmt", spec) + self.assertIn("label_step", spec) + self.assertIn(key, RANGE_LABELS) def test_referrer_labels_complete(self): from ..views.analytics import REFERRER_LABELS @@ -5147,6 +5157,8 @@ class TestTagSuggestPureFunctions(unittest.TestCase): existing_tag_slugs=[], min_products=2, max_share=1.0, + min_title_share=0.0, + bigrams=False, ) labels = [c["label"] for c in clusters] self.assertIn("Math", labels) @@ -5164,7 +5176,8 @@ class TestTagSuggestPureFunctions(unittest.TestCase): SimpleNamespace(id="b", title="Math Worksheet Two", description=""), ] clusters, _ = suggest_clusters( - products, stopwords=[], existing_tag_slugs=["math"], max_share=1.0 + products, stopwords=[], existing_tag_slugs=["math"], + max_share=1.0, min_title_share=0.0, bigrams=False, ) labels = [c["label"] for c in clusters] self.assertNotIn("Math", labels) @@ -5179,7 +5192,8 @@ class TestTagSuggestPureFunctions(unittest.TestCase): SimpleNamespace(id="c", title="Valentine's Day Crafts", description=""), ] clusters, _ = suggest_clusters( - products, stopwords=["day"], existing_tag_slugs=[], max_share=1.0, + products, stopwords=["day"], existing_tag_slugs=[], + max_share=1.0, min_title_share=0.0, bigrams=False, ) labels = [c["label"] for c in clusters] self.assertTrue( @@ -5200,6 +5214,7 @@ class TestTagSuggestPureFunctions(unittest.TestCase): clusters, _ = suggest_clusters( products, stopwords=[], existing_tag_slugs=[], min_products=2, max_share=1.0, + min_title_share=0.0, bigrams=False, ) # "Math" (3 products) ranks above "Reading" (2 products). self.assertEqual(clusters[0]["label"], "Math") @@ -5215,6 +5230,7 @@ class TestTagSuggestPureFunctions(unittest.TestCase): clusters, _ = suggest_clusters( products, stopwords=["title"], existing_tag_slugs=[], min_products=2, max_share=1.0, + min_title_share=0.0, bigrams=False, ) self.assertEqual(clusters, []) @@ -5252,6 +5268,8 @@ class TestTagSuggestPureFunctions(unittest.TestCase): existing_tag_slugs=[], min_products=2, max_share=0.4, + min_title_share=0.0, + bigrams=False, ) labels = [c["label"] for c in clusters] self.assertNotIn("Resource", labels) @@ -5271,8 +5289,92 @@ class TestTagSuggestPureFunctions(unittest.TestCase): clusters, filtered = suggest_clusters( products, stopwords=[], existing_tag_slugs=[], min_products=2, max_share=1.0, + min_title_share=0.0, bigrams=False, ) self.assertEqual(filtered, 0) self.assertTrue(len(clusters) > 0) + def test_suggest_clusters_bigrams_capture_phrases(self): + """Bigram detection surfaces multi-word categories like 'first grade'.""" + from types import SimpleNamespace + from ..lib.tag_suggest import suggest_clusters + products = [ + SimpleNamespace(id=f"p{i}", title=t, description="") + for i, t in enumerate([ + "Addition First Grade Math", + "Subtraction First Grade Math", + "Counting First Grade Math", + "Reading First Grade Practice", + ]) + ] + clusters, _ = suggest_clusters( + products, stopwords=[], existing_tag_slugs=[], + min_products=2, max_share=1.0, min_title_share=0.0, + bigrams=True, + ) + labels = [c["label"] for c in clusters] + self.assertIn("First Grade", labels) + # Bigrams flagged so caller can render them differently + first_grade = next(c for c in clusters if c["label"] == "First Grade") + self.assertTrue(first_grade["is_bigram"]) + + def test_suggest_clusters_bigrams_skipped_when_disabled(self): + from types import SimpleNamespace + from ..lib.tag_suggest import suggest_clusters + products = [ + SimpleNamespace(id=f"p{i}", title=t, description="") + for i, t in enumerate([ + "Alpha First Grade", + "Beta First Grade", + "Gamma First Grade", + ]) + ] + clusters, _ = suggest_clusters( + products, stopwords=[], existing_tag_slugs=[], + min_products=2, max_share=1.0, min_title_share=0.0, + bigrams=False, + ) + labels = [c["label"] for c in clusters] + self.assertNotIn("First Grade", labels) + # Singletons "first" and "grade" still surface as unigrams. + self.assertIn("First", labels) + self.assertIn("Grade", labels) + + def test_suggest_clusters_min_title_share_drops_description_noise(self): + """A stem that lives only in descriptions, not titles, gets dropped.""" + from types import SimpleNamespace + from ..lib.tag_suggest import suggest_clusters + # "Math" is in every title; "engaged" is only in descriptions. + # Default min_title_share=0.3 should drop "engaged". + products = [ + SimpleNamespace(id=f"p{i}", title="Math Worksheet", + description="Students stay engaged with these.") + for i in range(5) + ] + clusters, _ = suggest_clusters( + products, stopwords=[], existing_tag_slugs=[], + min_products=2, max_share=1.0, bigrams=False, + ) + labels = [c["label"] for c in clusters] + self.assertIn("Math", labels) + # "engaged" appears in descriptions only — drops with default filter + self.assertNotIn("Engaged", labels) + + def test_suggest_clusters_min_title_share_zero_allows_description_only(self): + from types import SimpleNamespace + from ..lib.tag_suggest import suggest_clusters + products = [ + SimpleNamespace(id=f"p{i}", title="Generic", + description="Lovely phonics worksheet here.") + for i in range(3) + ] + clusters, _ = suggest_clusters( + products, stopwords=[], existing_tag_slugs=[], + min_products=2, max_share=1.0, min_title_share=0.0, + bigrams=False, + ) + labels = [c["label"] for c in clusters] + # "phonics" surfaces only with min_title_share=0 + self.assertIn("Phonics", labels) +