Skip to content

Conversation

@davidp57
Copy link
Contributor

@davidp57 davidp57 commented Feb 10, 2026

📋 Overview

This PR adds editable tags for the games.
Tags are grouped by categories:

  • priority (how hard want I to play this game)
  • personal rating (my own rating of this game)
  • Playtime (how much did I play this game) - this category's tags are automatically assigned for Steam games but can be overriden

The tags are visible in both the library and the details pages.

In addition, the user is able to select multiple games in the library view (either by individually clicking on them after started the selection mode, or by shift and/or control clicking to select an interval).

I've also added "hide", "mark NSFW" and "delete" actions to the edit bar.

✅ Test Coverage: 177/177 passing

New Test Files (65 tests)

tests/test_api_metadata_endpoints.py - 35 tests

Complete API integration tests covering all metadata endpoints:

  • Priority endpoints (6 tests): set/clear priority, validation, error handling
  • Personal rating endpoints (5 tests): 1-10 ratings, remove (rating=0), validation
  • Manual playtime tags (5 tests): set, replace, remove tags, validation
  • Bulk priority (3 tests): set multiple games, empty list validation
  • Bulk rating (4 tests): set/remove ratings, validation
  • Bulk hide/NSFW (4 tests): mark multiple games
  • Bulk delete (3 tests): delete multiple, cascade label associations
  • Bulk add to collection (4 tests): add games, duplicate handling
  • System tags (1 test): manual trigger for auto-tagging

tests/test_database_migrations.py - 12 tests

Database schema migrations and constraints:

  • ✅ Collections→Labels migration (data preservation, idempotency)
  • ✅ Metadata columns addition (priority, personal_rating)
  • ✅ CHECK constraints enforcement (valid priorities, rating 0-10)
  • ✅ CASCADE delete behavior (labels, games)

tests/test_edge_cases_labels.py - 13 tests

Edge cases and performance tests:

  • ✅ Games with all metadata (priority + rating + tags + collections)
  • ✅ System label deletion impact (no crashes, graceful handling)
  • ✅ NULL playtime handling (correct behavior for GamePass games)
  • ✅ Performance: 1000 games auto-tagged in <5s
  • ✅ Batch vs individual tagging performance comparison
  • ✅ Complex filter queries with multiple criteria
  • ✅ Transaction atomicity

tests/test_system_labels_auto_tagging.py - +5 tests

Manual tag persistence tests added to existing suite:

  • ✅ Manual tags survive auto-tagging cycles (auto=0 persists)
  • ✅ Manual tags work on non-Steam games (GOG, Epic, etc.)
  • ✅ Manual tag removal allows auto-tagging to resume
  • ✅ Manual tags take precedence over auto tags
  • ✅ Auto tags replaced correctly when playtime changes

📚 Documentation

New Files

docs/api-metadata-endpoints.md (~700 lines)

Complete API reference with:

  • Request/response schemas for all 13 metadata endpoints
  • Curl examples for each endpoint
  • Comprehensive error codes reference (400, 404, 500)
  • Developer notes on validation, transactions, performance

docs/contributing-labels-system.md (~600 lines)

Developer contribution guide:

  • System architecture with database schema diagrams
  • Tutorial: Adding a new system label (Marathon example: 200+ hours)
  • Tutorial: Adding a new metadata field (completion_status example)
  • Performance optimization techniques (batch processing, caching, indexing)
  • Migration best practices (idempotency, testing, rollback procedures)
  • Testing guidelines with examples

Enhanced Files

docs/system-labels-auto-tagging.md (+~400 lines)

User documentation enriched with:

  • Quick Start guide: 6 step-by-step workflows (sync, priorities, ratings, bulk actions)
  • FAQ section: 12 common questions with detailed answers:
    • Why aren't my Epic/GOG games auto-tagged?
    • Do manual tags get overwritten by auto-tagging?
    • How do I bulk-unrate games?
    • Can I change playtime boundaries?
    • What happens if I delete a system label?
    • And 7 more...
  • Keyboard shortcuts: Shift-click range selection in multi-select mode
  • Link to API reference for developers

🔧 Technical Fixes

  • Fix type issues in bulk operations (tuple unpacking instead of list concatenation)
  • Fix get_db import in test fixtures (use web.dependencies instead of web.database)
  • Add check_same_thread=False to SQLite test connections for FastAPI compatibility
  • Correct sqlite3.Row vs tuple comparisons in migration tests

📊 CHANGELOG

Updated CHANGELOG.md with complete documentation of:

  • All new test files and coverage
  • All new documentation files
  • List of modified files
  • Technical improvements

🎯 Testing

All tests pass successfully:

pytest tests/ -v
# 177 passed in ~15s

Test coverage includes:

  • ✅ All API endpoints with success and error cases
  • ✅ Database migrations with rollback scenarios
  • ✅ Edge cases (NULL values, performance, system integrity)
  • ✅ Manual vs auto tag lifecycle
  • ✅ Bulk operations with validation

🔍 Code Quality

  • ✅ No linting errors (ruff)
  • ✅ No type errors (mypy/Pylance)
  • ✅ All tests passing
  • ✅ No VS Code errors/warnings
  • ✅ Documentation complete and accurate

📝 Checklist

  • Tests added for all new functionality
  • Tests updated for modified functionality
  • All tests passing (177/177)
  • Documentation complete (API + Developer + User guides)
  • CHANGELOG.md updated
  • No linting errors
  • No type errors
  • Code reviewed for best practices

🚀 Ready for Review

This PR completes the test and documentation requirements for the labels/metadata system. All functionality is thoroughly tested and documented for both users and developers.

davidp57 and others added 30 commits February 3, 2026 15:25
- Add PREDEFINED_QUERIES with 18 filters across 4 categories
- Implement global filter persistence via localStorage
- Convert /random to full page with configurable game grid
- Add comprehensive test suite (69 tests, 100% passing)
- Optimize performance with indexes and query caching
- Document complete filter system architecture
- Remove 'apply globally' checkbox for simpler UX
- Add .github/ and openspec/ to .gitignore
- Remove tracked files from these directories
- These folders contain local workflow configurations
…est case)

Based on this review:
> SQL errors on collection detail page
The .replace() chain in collections.py for prefixing column names with g. only covers 5 columns (playtime_hours, total_rating, added_at, release_date, nsfw). Filters like "community-favorites" (igdb_rating, igdb_rating_count), "critic-favorites" (aggregated_rating), and "recently-updated" (last_modified) will throw SQL errors when used on collection pages.
Based on this review:
Genre LIKE pattern missing closing quote
In discover.py, library.py (random route), and collections.py:

params.append(f'%"{genre.lower()}%')

Should be:

params.append(f'%"{genre.lower()}"%')

Without the closing ", the pattern %"action% could match unintended substrings.
Based on this review:
> Duplicate import in discover.py
get_query_filter_counts is imported twice on the same line.
- corrected json import in loop
- made bare exception handling less bare
Based on these reviews:

> Bare except: clauses
In collections.py and discover.py, the genre parsing uses bare except: which swallows all exceptions including KeyboardInterrupt and SystemExit. Please use except Exception: at minimum, or better yet except (json.JSONDecodeError, TypeError):.

> import json inside a loop
In collections.py, import json is inside the genre-parsing loop — should be at the top of the file.
Based on this review:
> TemplateResponse signature change
All TemplateResponse calls were changed from TemplateResponse("template.html", {"request": request, ...}) to TemplateResponse(request, "template.html", {...}). This requires a newer Starlette version. Since we don't pin versions in requirements.txt, this could break depending on the installed version. Can you either pin the minimum required version or keep the old signature?
… button)

Based on this review:
> Auto-apply conflicts with Apply button
The store/genre dropdowns have explicit "Apply" buttons, but filters.js also auto-applies after a 300ms debounce on checkbox changes. Users could get redirected before they finish selecting multiple options. I'd suggest picking one approach — either auto-apply (and remove the button) or manual apply (and remove the debounce).
… button) ; also, changed the filters logic (see filter-system.md) to combine filters with ORs in the same category and with ANDs between categories.

Based on this review:
> Auto-apply conflicts with Apply button
The store/genre dropdowns have explicit "Apply" buttons, but filters.js also auto-applies after a 300ms debounce on checkbox changes. Users could get redirected before they finish selecting multiple options. I'd suggest picking one approach — either auto-apply (and remove the button) or manual apply (and remove the debounce).
Based on this review:
> Mobile regression
The old index.html had mobile-specific styles transforming dropdowns into bottom sheets. The new filters.css responsive section only does flex-direction: column — the bottom-sheet behavior appears to be lost. Could you verify the filter bar works well on mobile?
Based on these reviews:

> query_filter_counts is always {} for discover, collection detail, and random pages — the Quick Filters dropdown won't show counts on those pages (intentional?)

> Dead .games-grid CSS rule with flex properties in index.html (overridden by the grid definition later — looks like a copy-paste artifact)
…ters

Changes the /random endpoint to redirect to a single random game (instead of
showing a grid of 12 games), while applying global filters before selection.

Changes:
- Modified /random route to apply filters then redirect to one game
- Added filters.js to all pages with Random links to ensure filter persistence
- Removed random.html template and shared-game-cards.css (no longer needed)
- Updated documentation to reflect new behavior

This addresses PR review feedback requesting the original "surprise me with
one game" behavior while maintaining global filter support.

> /random endpoint behavior change
This currently redirects to a single random game detail page. The PR changes it to render a full grid of 12 games. I'd like to discuss this — I'm not sure I want to lose the "surprise me with one game" behavior. Could we keep the old redirect as the default and add the grid as a separate route (e.g. /random?grid=true) or a different path?
Phase 1 & 2: Backend implementation for unified labels system

- Migrate collections to labels with new fields (type, color, icon, system)
- Add priority (high/medium/low) and personal_rating (0-10) columns to games
- Create system_labels service with 5 auto playtime tags
- Add API endpoints for priority and personal rating (single + bulk)
- Add manual playtime tag endpoint for non-Steam games
- Update delete endpoints to use game_labels instead of collection_games

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Phase 3 (partial): Frontend UI for priority and personal rating

- Add Priority and Personal Rating buttons to floating action bar
- Add dropdown menus for selecting priority (high/medium/low) and rating (0-10)
- Add JavaScript functions for bulk operations (bulkSetPriority, bulkSetPersonalRating)
- Add CSS styling for new buttons and dropdown menus
- Auto-close dropdowns when clicking outside

Next: Add badges display on game cards, update filters, add sync hooks

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added visual badges on game cards to display:
- Priority indicators (high/medium/low) with colored circles
- Personal rating badges with star icons and numeric value

Badges are positioned in top-left corner of card covers with:
- Backdrop blur for readability
- Responsive sizing for different grid sizes
- Proper z-index layering above cover images

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Migrated all collections endpoints to use the new unified labels system:
- Changed table references from collections/collection_games to labels/game_labels
- Added type='collection' filters to all queries
- Updated column references (c.* to l.*, cg.* to gl.*)
- Maintained all existing API endpoints and functionality

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added "Playtime Tag" button to action bar with dropdown menu:
- 5 manual playtime tags: never-launched, just-tried, played, well-played, heavily-played
- Remove tag option
- Styled with blue color scheme to differentiate from priority/rating
- JavaScript function calls individual game endpoint for each selected game
- Auto-reloads page after applying tags

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed JavaScript parameter from tag_name to label_name to match
the Pydantic model definition in api_metadata.py

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
- Changed all system label names from French to English
- Added migration logic in ensure_system_labels() to update existing labels
- Fixed manual playtime tag endpoint to handle null (remove tag)
- Updated template dropdown to use English label names
- All 5 playtime tags now visible in UI

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Show dropdown first with visibility:hidden to get actual height
- Position dropdown above button using calculated height
- Close other dropdowns when opening playtime menu
- Prevents menu from being cut off at bottom of screen

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Added toast notification system with slide-in animations
- Toasts appear in top-right corner without interrupting workflow
- Success (green), error (red), and info (blue) variants
- Auto-dismiss after 3 seconds or click to close manually
- Replaced all alert() calls with showToast()
- More elegant and less disruptive UX

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Hold Shift and click to select all games between two selections
- More efficient for selecting multiple consecutive games
- Tracks last selected card for range calculation
- Works seamlessly with normal click selection

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added inline action buttons on game detail page for:
- Set Priority (high/medium/low/none)
- Personal Rating (0-10)
- Playtime Tag (5 system tags + remove)

Features:
- Compact button design integrated in hero section
- Dropdown menus for each action
- Toast notifications for feedback
- Auto-reload after changes
- No need to go to library for tagging single games

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@davidp57
Copy link
Contributor Author

Hi @sam1am!
I've added a few features related to the global filter system; this PR's branch is based on the feat-global-filters branch.
Basically, I've added user-editable tags in multiple categories, with a few managed automatically by the system ("Gameplay" tags which specify the time played are automated for Steam games).
Tell me if it's cool for you to merge.
Cheers!

@davidp57 davidp57 changed the title Add comprehensive test coverage and documentation for labels system New user-editable tags Feb 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant