Add django-filter support to the ScrobbleViewSet with filters for:
- in_progress: boolean filter for active scrobbles
- visibility: enum filter (public/shared/private)
- media_type: enum filter for all 25 media types
- source: text search filter (icontains)
- played_to_completion: boolean filter
- timestamp_after/before: datetime range filters
Changes:
- Create scrobbles/api/filters.py with ScrobbleFilter class
- Add filterset_class to ScrobbleViewSet
- Filters appear in OpenAPI schema at /api/v1/scrobbles/
VideoGame was missing from the API router, causing
HyperlinkedModelSerializer to fail when generating URLs for scrobble
relationships.
Changes:
- Create videogames/api/ with serializers and views
- Register VideoGameViewSet at /api/v1/videogames/
- Register VideoGameCollectionViewSet at /api/v1/videogame-collections/
- Register VideoGamePlatformViewSet at /api/v1/videogame-platforms/
TrailViewSet and FoodCategoryViewSet were imported but never registered
in the DRF router, causing HyperlinkedModelSerializer to fail when
trying to generate URLs for scrobble relationships.
Changes:
- Add router.register(r'trails', TrailViewSet)
- Add router.register(r'food-categories', FoodCategoryViewSet)
The ScrobbleViewSet was using lookup_field="uuid" which caused
API detail views to fail for scrobbles with NULL UUIDs. Rather
than backfilling all NULL UUIDs, switch to pk lookup which is
always populated.
Changes:
- Remove lookup_field="uuid" from ScrobbleViewSet (defaults to pk)
- Remove extra_kwargs from ScrobbleSerializer (URL now uses pk)
- Update regenerate_share_token action signature to use **kwargs
The ScrobbleSerializer uses HyperlinkedModelSerializer with
lookup_field='uuid', but 3,273 of 6,155 Scrobbles had NULL UUIDs,
causing URL resolution to fail.
Fix:
- Add migration 0104 to backfill NULL UUIDs with uuid4()
- All Scrobbles now have valid UUIDs for URL resolution
Same issue as drink-detail: Paper model had a template URL but no API
ViewSet, causing HyperlinkedModelSerializer to fail.
Fix:
- Add PaperSerializer and PaperViewSet to books/api/
- Register PaperViewSet as 'papers' in the DRF router
HyperlinkedModelSerializer could not resolve drink-detail because no
API ViewSet existed for the Drink model, even though the template URL
existed in drinks/urls.py.
Fix:
- Add DrinkSerializer and DrinkViewSet to drinks/api/
- Register DrinkViewSet as 'drinks' in the DRF router (vrobbler/urls.py)
- Mark task 8f2a3b4c as DONE in PROJECT.org
GET /api/v1/scrobbles/ now resolves without error.
GET /api/v1/scrobbles/ fails because HyperlinkedModelSerializer
can't resolve drink-detail — no API ViewSet exists for it, only a
template view in drinks/urls.py.
Closes #2fddd5d0-1138-4a6e-96b7-5879035f033f
drf-spectacular provides OpenAPI 3.0 schema generation for all 52
registered ViewSet endpoints under api/v1/. This is a prerequisite for
auto-generating the Kotlin networking layer.
Changes:
- Add drf-spectacular dependency (0.27.x)
- Register in INSTALLED_APPS and configure DEFAULT_SCHEMA_CLASS
- Add /api/schema/ (OpenAPI YAML) and /api/docs/ (Swagger UI) routes
- Add SPECTACULAR_SETTINGS with enum name overrides to suppress warnings
Schema generates cleanly with 4 non-blocking warnings (1 unique:
TagListSerializerField defaulting to string) and 14 unique skipped
errors (all non-ViewSet APIViews like webhooks that lack serializers).
- Add openfoodfacts Python SDK dependency
- Create foods/sources/openfoodfacts.py API wrapper with text_search
- Add off_code field + find_or_create_from_search() to Food model
- Add nutrition fields (protein, fat, carbs, fiber, sugar, sodium),
ingredients, image, off_code to Drink model
- Create FoodSearchView and FoodScrobbleFromSearchView at /foods/search/
- Route beverages to Drink model based on OFF category keywords
- Update ManualScrobbleView to redirect '-f Food Name' to food search
- Add Drinks section to home page and scrobble detail templates
- Update drink_detail.html with nutrition grid and image display
Add referrerPolicy: 'origin' to the Leaflet tile layer in
geolocation_list.html and geolocation_detail.html to match the
other four templates that already had it.
Without this, those templates send the full page URL as Referer,
which can trigger OSM rate limiting (osm.wiki/blocked error).
Also marks the task as DONE in PROJECT.org.