[release] Bump to version 65.0
All checks were successful
ci / test (push) Successful in 2m22s
ci / build-and-deploy (push) Successful in 1m26s

- Investigate how historice lastfm imports work
- Fix LookupError on scrobble start/long-plays views
- Add Flexify workout importer
- Add workouts app with exercise catalog and workout routines
- When sharing a scrobble, share the log data too
This commit is contained in:
2026-08-08 00:04:53 -04:00
parent 4e46bcd5c1
commit 3cec979d0a
3 changed files with 110 additions and 108 deletions

View File

@ -18,7 +18,7 @@ tasks, Todoist tasks, web pages I've read and trails I've hiked has turned out
to be sometimes cathartic and sometimes functional as I try to remember when I
did a thing.
* Backlog [5/29] :vrobbler:project:personal:
* Backlog [0/24] :vrobbler:project:personal:
** TODO [#C] After transition to linux add curl_cffi as webpage scrapper again :webpages:metadata:
:PROPERTIES:
:ID: d3cce1a7-d540-4d66-bf66-e75378e4eac7
@ -557,109 +557,3 @@ Added a `workouts` Django app so we can scrobble gym sessions.
and `tests/workouts_tests/` (unit conversion, form round-trips, importer,
imperial POST) included.
** DONE [#B] Investigate how historice lastfm imports work :importers:music:
:PROPERTIES:
:ID: f91fdd53-9da7-4859-9322-f08b2c587061
:END:
*** Description
Some time ago we added an ability for looking at the earliest LastFM import for
a given user and to kick off a series of monthly imports of past lastfm
scrobbles until there are no more. I tried running this a while ago and it
didn't work, and I wonder where we left it off.
*** Investigation findings
The historical import mechanism (`dispatch_historical_imports` in
`vrobbler/apps/scrobbles/importers/lastfm.py`) was added in commit `4e1c3ffb`
and ran successfully on 2026-05-24, importing 1,450 scrobbles covering
Feb-Apr 2023 (imports 1, 4, 5) — the user's full Last.fm history back to
registration.
Two real bugs were found and fixed:
- Resume cursor: the dispatch loop found the import with the earliest
`processed_finished` instead of the import covering the oldest scrobbles,
so re-running would re-dispatch already-imported months. It now walks back
from the earliest scrobble across all completed imports.
- Dedup: `import_from_lastfm` filtered existing scrobbles by `created`
(row-insert time) against the Last.fm play timestamp ± 20s, which never
matches for historical data and caused duplicate scrobbles on re-import.
It now compares `timestamp`. Also fixed a `tzinfo.name` crash for
`ZoneInfo` timezones.
Tests added in `tests/scrobbles_tests/test_lastfm.py` (5 tests) covering
dedup-by-timestamp, creation, and dispatch resume/no-resume behavior.
** DONE [#B] Fix LookupError on scrobble start/long-plays views :bug:scrobbles:videos:
:PROPERTIES:
:ID: 868e2a91-c974-4656-a8ae-b90a1f94ae74
:END:
- `PLAY_AGAIN_MEDIA["channels"]` and `LONG_PLAY_MEDIA["papers"]` keyed models
by app labels that don't exist (`Channel` lives in `videos`, `Paper` in
`books`), so `scrobble_start`, `scrobble_longplay_finish`, and the long-plays
views raised `LookupError: No installed app with label 'channels'` for every
request.
- `scrobble_start` now special-cases `channels` to the `videos` app and skips
unknown app labels; a shared `get_long_play_media_model()` helper in
`scrobbles/utils.py` maps the `papers` label to `books` for the long-play
lookups.
- Also fixed `Channel.scrobble_for_user` which still used the pre-refactor
`Scrobble.create_or_update(media_object=...)` signature (would raise
`TypeError` once the lookup reached it).
- Fixed in commit `fbb1725d`; audited all other `apps.get_model` callers of
the media maps (`AUTO_FINISH_MEDIA` uses class-name values only, harmless).
Regression tests: channel/video start, long-plays view.
** DONE [#B] Add Flexify workout importer :workouts:importer:
:PROPERTIES:
:ID: 96c9fbf5-a0d5-4f09-a72c-bb491b7ece18
:END:
*** Description
Import gym sessions and routines from a Flexify backup.
- `import_flexify()` in `workouts/importer.py` reads a Flexify sqlite backup
(`data/flexify-example.sqlite`) plus an optional plans CSV
(`data/flexify-plans-example.csv`). Plans become `WorkoutRoutine`s with
`WorkoutRoutineExercise` planned-exercise rows; logged `gym_sets` are
clustered into sessions (gap > 60 min or plan change) and converted into
workout scrobbles.
- A curated `FLEXIFY_EXERCISE_ALIASES` map resolves Flexify exercise names to
the wrkout catalog, falling back to `find_or_create` for unmatched names.
- `python manage.py import_flexify --sqlite ... --csv ... --user ...` command.
- `scan_webdav_for_flexify()` watches `var/workouts/` on WebDAV (via the
existing import_from_webdav sweep), pairing a plans CSV with the sqlite
backup, and queues `process_workout_import` celery tasks.
- `WorkoutImport` model (`BaseFileImportMixin`, new `file_hash` field for
change detection), admin, DRF `workout-imports` viewset, planned-exercises
block on the routine detail template, and MCP routine dict update.
** DONE [#B] Add workouts app with exercise catalog and workout routines :workouts:feature:
** DONE [#A] When sharing a scrobble, share the log data too :bug:scrobbles:sharing:
:PROPERTIES:
:ID: 46de9f0e-2727-63f4-0845-e714dbbe4a45
:END:
- Note taken on [2026-08-07 Fri 15:02]
An example is here: https://life.lab.unbl.ink/shared/I3fjpLS/
The agent session has no data when shared, becuase it's all stored in logdata.
There may be some logdata that's sensitive, look out for that and maybe put it
in a SENSITIVE_LOGDATA_KEYS dictionary in settings and exclude that from being
shown.
- Note taken on [2026-08-07 Fri]
Fixed. The share template (`scrobble_share.html`) now renders logdata for
AgentSession (the conversation) and Workout scrobbles, matching the detail
template. The shared view renders a non-interactive conversation (no follow-up
form, retry buttons, or htmx polling for anonymous viewers).
Added `SENSITIVE_LOGDATA_KEYS` in `vrobbler/settings.py` keyed by media type.
`Scrobble.public_log` and `Scrobble.public_logdata` strip those keys for
shared/public rendering; owner views keep the full `log`/`logdata`. Defaults
cover body-composition metrics on Task scrobbles and `bodyweight_kg` on
Workout scrobbles.