From 4b3ddf585356b217311c8e1a674dbe9a7505fc80 Mon Sep 17 00:00:00 2001 From: matt Date: Wed, 1 Oct 2025 11:11:44 -0700 Subject: [PATCH] fix(setup): restore security stamp filtering --- CHANGELOG.md | 2 +- RELEASE_NOTES_TEMPLATE.md | 28 ++++------------------ code/file_setup/setup_utils.py | 27 +++++++++++++++++++-- code/tests/test_setup_filters.py | 40 ++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 27 deletions(-) create mode 100644 code/tests/test_setup_filters.py diff --git a/CHANGELOG.md b/CHANGELOG.md index a239dda..feaf534 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,7 +36,7 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning - Preview performance CI check now waits for `/healthz` and retries theme catalog pagination fetches to dodge transient 500s during cold starts. ### Fixed -- _No changes yet._ +- Setup filtering now applies security-stamp exclusions case-insensitively so Acorn and Heart promo cards stay out of Commander-legal pools, with a regression test covering the behavior. ### Removed - Preview performance GitHub Actions workflow (`.github/workflows/preview-perf-ci.yml`) retired after persistent cold-start failures; run the regression helper script manually as needed. diff --git a/RELEASE_NOTES_TEMPLATE.md b/RELEASE_NOTES_TEMPLATE.md index be1f99e..bfdd081 100644 --- a/RELEASE_NOTES_TEMPLATE.md +++ b/RELEASE_NOTES_TEMPLATE.md @@ -1,31 +1,11 @@ # MTG Python Deckbuilder ${VERSION} ## Summary -- Introduced the Commander Browser with HTMX-powered pagination, theme surfacing, and direct Create Deck integration. -- Shared color-identity macro and accessible theme chips power the new commander rows. -- Manual QA walkthrough (desktop + mobile) recorded on 2025‑09‑30 with edge-case checks. -- Home dashboard aligns its quick actions with feature flags, exposing Commanders, Diagnostics, Random, Logs, and Setup where enabled. +- Restored setup filtering to exclude Acorn and Heart promotional security stamps so Commander card pools stay format-legal. +- Added a regression test that locks the security stamp filtering behavior in place. ## Added -- Commander browser skeleton page at `/commanders` with catalog-backed rows and accessible theme chips. -- Documented QA checklist and results for the commander browser launch in `docs/qa/commander_browser_walkthrough.md`. -- Shared color-identity macro for reusable mana dots across commander rows and other templates. -- Home dashboard Commander/Diagnostics shortcuts gated by feature flags so all primary destinations have quick actions. -- Manual QA pass entered into project docs (2025-09-30) outlining desktop, mobile, and edge-case validations. - -## Changed -- Commander list paginates in 20-item pages, with navigation controls mirrored above and below the results and automatic scroll-to-top. -- Commander hover preview shows card-only panel in browser context and removes the “+ more” overflow badge from theme chips. -- Content Security Policy upgrade directive ensures HTMX pagination requests remain HTTPS-safe behind proxies. -- Commander thumbnails adopt a fixed-width 160px frame (responsive on small screens) for consistent layout. -- Commander browser now separates name vs theme search, adds fuzzy theme suggestions, and tightens commander name matching to near-exact results. -- Commander search results stay put while filtering; typing no longer auto-scrolls the page away from the filter controls. -- Commander theme chips are larger, wrap cleanly, and display an accessible summary dialog when tapped on mobile. -- Theme dialogs now surface the full editorial description when available, improving longer summaries on small screens. -- Commander theme names unescape leading punctuation (e.g., +2/+2 Counters) so labels render without stray backslashes. -- Theme summary dialog also opens on desktop clicks, giving parity with mobile behavior. -- Mobile commander rows now feature larger thumbnails and a centered preview modal with expanded card art for improved readability. -- Preview performance CI check now waits for service health and retries catalog pagination fetches to smooth out transient 500s on cold boots. +- Regression test covering security-stamp filtering during setup to guard against future case-sensitivity regressions. ## Fixed -- Documented friendly handling for missing `commander_cards.csv` data during manual QA drills to prevent white-screen failures. \ No newline at end of file +- Setup filtering now applies security-stamp exclusions case-insensitively, preventing Acorn/Heart promo cards from entering Commander pools. \ No newline at end of file diff --git a/code/file_setup/setup_utils.py b/code/file_setup/setup_utils.py index afa88ad..5f6f9ff 100644 --- a/code/file_setup/setup_utils.py +++ b/code/file_setup/setup_utils.py @@ -194,13 +194,36 @@ def filter_dataframe(df: pd.DataFrame, banned_cards: List[str]) -> pd.DataFrame: filtered_df = df.copy() filter_config: FilterConfig = FILTER_CONFIG # Type hint for configuration for field, rules in filter_config.items(): + if field not in filtered_df.columns: + logger.warning('Skipping filter for missing field %s', field) + continue + for rule_type, values in rules.items(): + if not values: + continue + if rule_type == 'exclude': for value in values: - filtered_df = filtered_df[~filtered_df[field].str.contains(value, na=False)] + mask = filtered_df[field].astype(str).str.contains( + value, + case=False, + na=False, + regex=False + ) + filtered_df = filtered_df[~mask] elif rule_type == 'require': for value in values: - filtered_df = filtered_df[filtered_df[field].str.contains(value, na=False)] + mask = filtered_df[field].astype(str).str.contains( + value, + case=False, + na=False, + regex=False + ) + filtered_df = filtered_df[mask] + else: + logger.warning('Unknown filter rule type %s for field %s', rule_type, field) + continue + logger.debug(f'Applied {rule_type} filter for {field}: {values}') # Remove illegal sets diff --git a/code/tests/test_setup_filters.py b/code/tests/test_setup_filters.py new file mode 100644 index 0000000..06add42 --- /dev/null +++ b/code/tests/test_setup_filters.py @@ -0,0 +1,40 @@ +import pandas as pd + +from file_setup.setup_utils import filter_dataframe + + +def _record(name: str, security_stamp: str) -> dict[str, object]: + return { + "name": name, + "faceName": name, + "edhrecRank": 100, + "colorIdentity": "G", + "colors": "G", + "manaCost": "{G}", + "manaValue": 1, + "type": "Creature", + "layout": "normal", + "text": "", + "power": "1", + "toughness": "1", + "keywords": "", + "side": "a", + "availability": "paper,arena", + "promoTypes": "", + "securityStamp": security_stamp, + "printings": "RNA", + } + + +def test_filter_dataframe_removes_acorn_and_heart_security_stamps() -> None: + df = pd.DataFrame( + [ + _record("Acorn Card", "Acorn"), + _record("Heart Card", "heart"), + _record("Legal Card", ""), + ] + ) + + filtered = filter_dataframe(df, banned_cards=[]) + + assert list(filtered["name"]) == ["Legal Card"]