From 0ccf35f24164b80927656bca7aa4f32268049739 Mon Sep 17 00:00:00 2001 From: KoalaTrapLord <265133236+KoalaTrapLord@users.noreply.github.com> Date: Thu, 19 Mar 2026 11:22:09 -0400 Subject: [PATCH] feat(phase-5): implement collection filter with CSV loader --- .../deck_builder/core/collection_filter.py | 87 ++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/src/commander_ai_lab/deck_builder/core/collection_filter.py b/src/commander_ai_lab/deck_builder/core/collection_filter.py index 512739a..43ae541 100644 --- a/src/commander_ai_lab/deck_builder/core/collection_filter.py +++ b/src/commander_ai_lab/deck_builder/core/collection_filter.py @@ -1 +1,86 @@ -# Collection filter (collection-only mode) — Phase 5 +""" +Collection filter for the Commander AI Deck Builder. + +Loads a user's card collection from CSV and filters candidate +cards to only those the user owns (collection-only mode). +""" + +from __future__ import annotations + +import csv +import logging +from pathlib import Path +from typing import Dict, List, Optional, Set + +from .models import CardEntry + +logger = logging.getLogger(__name__) + + +def load_collection(path: str) -> Dict[str, int]: + """ + Load a card collection from CSV. + + Expected CSV columns: name, quantity, [set_code] + Returns a dict of card_name -> quantity. + """ + collection: Dict[str, int] = {} + csv_path = Path(path) + + if not csv_path.exists(): + logger.error(f"Collection file not found: {path}") + return collection + + with open(csv_path, "r", encoding="utf-8") as f: + reader = csv.DictReader(f) + for row in reader: + name = row.get("name", "").strip() + if not name: + continue + qty = int(row.get("quantity", 1)) + # Accumulate quantities (user may have multiple entries) + collection[name] = collection.get(name, 0) + qty + + logger.info(f"Loaded collection: {len(collection)} unique cards from {path}") + return collection + + +def get_collection_names(path: str) -> Set[str]: + """Load collection and return just the set of card names.""" + return set(load_collection(path).keys()) + + +def filter_by_collection( + cards: List[CardEntry], + collection_path: str, +) -> List[CardEntry]: + """ + Filter a list of CardEntry objects to only those in the collection. + + Cards not found in the collection are removed. + """ + collection = load_collection(collection_path) + if not collection: + logger.warning("Empty collection — returning all cards unfiltered") + return cards + + filtered = [] + for card in cards: + if card.name in collection: + filtered.append(card) + else: + logger.debug(f"Filtered out (not in collection): {card.name}") + + logger.info( + f"Collection filter: {len(filtered)}/{len(cards)} cards matched" + ) + return filtered + + +def filter_names_by_collection( + names: List[str], + collection_path: str, +) -> List[str]: + """Filter a list of card name strings to only those in the collection.""" + owned = get_collection_names(collection_path) + return [n for n in names if n in owned]