From d53a9a9d33278a14e9469ebc2ef0c145ecb715db Mon Sep 17 00:00:00 2001 From: Colin Powell Date: Mon, 20 Jul 2026 23:48:17 -0400 Subject: [PATCH] Fix a bug in lifeevent urls --- PROJECT.org | 119 ++++++++++++++++++++++++++++- vrobbler/apps/lifeevents/models.py | 2 +- vrobbler/apps/lifeevents/urls.py | 2 +- 3 files changed, 120 insertions(+), 3 deletions(-) diff --git a/PROJECT.org b/PROJECT.org index 18b9599..18d0cba 100644 --- a/PROJECT.org +++ b/PROJECT.org @@ -88,7 +88,104 @@ fetching and simple saving. *** Metadata sources **** Scraper -* Backlog [0/24] :vrobbler:project:personal: +* Android App [0/7] :android:mobile:api: +** TODO [#A] Generate Kotlin API client from OpenAPI spec :android:codegen: + +*** Description + +Use openapi-generator to produce a Kotlin Retrofit client from the schema. +This gives us typed data classes, request builders, and a service interface +for every endpoint. The generated code lives in a separate Gradle module +(api-client) so it can be tested independently. + +*** Tasks +- Add openapi-generator to project tooling +- Configure codegen for Kotlin + Retrofit + Moshi/Gson +- Generate client into android/api-client module +- Verify generated code compiles against the API +- Add a thin wrapper for token auth interceptor + +** TODO [#A] Scaffold Android project with Jetpack Compose :android:setup: + +*** Description + +Create the Android app module with a standard Compose architecture: single +activity, Navigation Compose, Hilt for DI, and a data layer backed by the +generated API client. Use Material 3 theming. + +*** Tasks +- Create Android project (Kotlin, minSdk 26) +- Add dependencies: Compose, Navigation, Hilt, OkHttp, Coil +- Set up package structure: ui/, data/, di/, navigation/ +- Add token storage (EncryptedSharedPreferences) +- Add auth interceptor for OkHttp +- Create MainActivity with NavHost + +** TODO [#B] Build scrobble list and detail screens :android:scrobbles: + +*** Description + +The core screen: a scrollable list of recent scrobbles grouped by date, with +a detail view showing the media item, timestamp, and any log data. Pull-to- +refresh. Infinite scroll pagination. + +*** Tasks +- ScrobbleListScreen: LazyColumn with date headers +- ScrobbleDetailScreen: media info, timestamps, log JSON +- ViewModel with Paging 3 integration +- Filter by media_type (bottom sheet or chips) +- Pull-to-refresh support + +** TODO [#B] Build media catalog screens :android:media: + +*** Description + +Browse screens for the major media types: tracks, videos, books, foods, beers, +board games, etc. Each is a list + detail pattern using the catalog endpoints +(not scrobbles). Start with the 5 most-used types and add more incrementally. + +*** Tasks +- TrackListScreen / TrackDetailScreen +- VideoListScreen / VideoDetailScreen +- BookListScreen / BookDetailScreen +- FoodListScreen / FoodDetailScreen +- BoardGameListScreen / BoardGameDetailScreen +- Shared list component (ReusableMediaList) to reduce duplication +- Search/filter per screen + +** TODO [#B] Add trend viewing screens :android:trends: + +*** Description + +Display the pre-computed trend data from the trends API. Start with the +overview (trend list with enable/disable), then individual trend detail +screens with the period selector (7d / 30d / 90d / 1yr). The trend +partials are HTML/JS charts — for mobile, either render them in a WebView +or extract the JSON data and render natively with a Compose charting +library. + +*** Tasks +- TrendListScreen: list of available trends with status +- TrendDetailScreen: period selector + chart rendering +- Decide chart approach (WebView vs native Compose charts) +- Period selector (horizontal chips for 7/30/90/365) + +** TODO [#C] Add scrobble creation from the app :android:create: + +*** Description + +Allow creating scrobbles directly from the app. This is lower priority +since most scrobbling happens via webhooks (Jellyfin, Mopidy, KoReader), +but useful for manual entries (food, drinks, board games, tasks, life +events). + +*** Tasks +- Scrobble creation form per media type (start simple with food + drink) +- Media item picker (search existing or create new) +- Timestamp picker (default to now) +- Optional: barcode scanning for food/drinks via CameraX + +* Backlog [1/26] :vrobbler:project:personal: ** TODO [#C] After transition to linux add curl_cffi as webpage scrapper again :webpages:metadata: ** TODO [#C] Create small utility to clean up tracks scrobbled with wonky playback times :bug:music:scrobbles: :PROPERTIES: @@ -601,6 +698,26 @@ The Edit log form should have from top to bottom: *** Description ** TODO [#A] Add trends tests for concurrent trends :trends:tests:concurrent: +** TODO [#A] Add drf-spectacular for OpenAPI schema generation :api:documentation: + +*** Description + +Install drf-spectacular and configure it to generate an OpenAPI 3.0 schema for +all 48+ REST endpoints. This is a prerequisite for auto-generating the Kotlin +networking layer. Add the schema endpoint at /api/schema/ and the Swagger UI +at /api/docs/. + +**** Tasks +- Add drf-spectacular to dependencies +- Configure in REST_FRAMEWORK settings +- Add schema and docs URL patterns +- Verify schema generates cleanly for all ViewSets +- Fix any serializer issues that block schema generation + +** DONE [#A] Fix bugs in lifeevent urls :bug:urls: +:PROPERTIES: +:ID: ac6c0102-4de7-b6e4-65de-8231752d9a31 +:END: * Version 61.4 [1/1] ** DONE [#B] Add 7-day period for trends :trends: :PROPERTIES: diff --git a/vrobbler/apps/lifeevents/models.py b/vrobbler/apps/lifeevents/models.py index b960a5e..7667329 100644 --- a/vrobbler/apps/lifeevents/models.py +++ b/vrobbler/apps/lifeevents/models.py @@ -20,7 +20,7 @@ class LifeEvent(ScrobblableMixin): return self.title def get_absolute_url(self): - return reverse("life-events:life-event_detail", kwargs={"slug": self.uuid}) + return reverse("life-events:lifeevent_detail", kwargs={"slug": self.uuid}) @property def logdata_cls(self): diff --git a/vrobbler/apps/lifeevents/urls.py b/vrobbler/apps/lifeevents/urls.py index 8adf737..a800e36 100644 --- a/vrobbler/apps/lifeevents/urls.py +++ b/vrobbler/apps/lifeevents/urls.py @@ -9,6 +9,6 @@ urlpatterns = [ path( "lifeevent//", views.LifeEventDetailView.as_view(), - name="life-event_detail", + name="lifeevent_detail", ), ]