# Research: Product Selection Landing Page **Branch**: `002-product-list` | **Date**: 2025-10-17 This document addresses technical decisions for the product selection landing page feature. Most infrastructure decisions were resolved in feature 001-build-an-application and are reused here. ## Existing Infrastructure (from 001-build-an-application) The following technical decisions from feature 001 are reused without modification: - **Flask 3.0+ with Jinja2**: Server-side rendering, no JavaScript - **File-based storage**: Product configs in `data/products/*/config.yaml` - **Product model**: Existing `app/models/product.py` with load methods - **Template patterns**: Minimal HTML/CSS, Jinja2 auto-escaping for XSS prevention - **Routing**: Flask route decorators, blueprint organization - **Testing**: pytest + pytest-flask for contract and integration tests **Reference**: See `/home/markus/workspace/reklamator/specs/001-build-an-application/research.md` for full details. --- ## New Technical Decision: Product List Retrieval & Sorting ### Decision: Extend Product model with `load_active()` class method **Rationale**: - Centralizes "active products only" filtering logic - Enables reuse if other features need active product lists - Encapsulates sorting algorithm in one place - Follows existing Product model pattern (e.g., `load_all()`, `load_by_id()`) **Implementation**: ```python # app/models/product.py - add class method @classmethod def load_active(cls): """Load all active products, sorted alphabetically by name then product_id. Returns: list[Product]: Active products with valid submission_url_slug, sorted by: 1. name (case-insensitive alphabetical) 2. product_id (alphabetical) as tiebreaker Products with missing/invalid submission_url_slug are excluded. """ all_products = cls.load_all() active = [p for p in all_products if p.status == 'active' and p.submission_url_slug] return sorted(active, key=lambda p: (p.name.lower(), p.product_id)) ``` **Sorting Algorithm**: - Primary sort: Product name (case-insensitive) - ensures alphabetical display - Secondary sort: Product ID - provides stable ordering when names are identical - Uses Python's built-in `sorted()` with tuple key for multi-level sorting **Performance Analysis**: - File I/O for 100 products: ~10-50ms (depends on disk speed) - In-memory sorting: <1ms for 100 items - Total expected latency: <100ms (well under 1-second SC-002 target) - No caching needed for MVP (file reads are sufficiently fast) **Filtering Logic**: - `status == 'active'`: Per FR-003, only show active products - `submission_url_slug`: Per FR-015, skip products with missing/invalid slugs - Combined with `and` operator: both conditions must be true **Alternatives Considered**: 1. **Sort in route handler**: Simpler but violates DRY if multiple routes need sorted product lists 2. **Database query with ORDER BY**: Contradicts file-based architecture decision from 001 3. **Pre-sorted cache**: Premature optimization - file reads are fast enough for 100 products 4. **Client-side sorting with JavaScript**: Violates no-JavaScript constraint from spec **Edge Cases Handled**: - No active products → Returns empty list (handled in template) - Missing submission_url_slug → Product excluded from list (per FR-015) - Identical product names → Sorted by product_id as tiebreaker - Case-insensitive sorting → "Apple" and "apple" sort together --- ## Validation All technical decisions align with: - **FR-002**: Retrieves from file-based storage ✅ - **FR-003**: Filters for active status ✅ - **FR-009**: Sorts alphabetically with tiebreaker ✅ - **FR-015**: Skips invalid submission_url_slug ✅ - **SC-002**: <1 second load time for 100 products ✅ **Next Phase**: Proceed to Phase 1 (data-model.md, contracts, quickstart.md)