Commit Graph

102 Commits

Author SHA1 Message Date
Claudio Ortolina 445033861d Reuse fixture 2026-02-10 19:32:28 +00:00
Claudio Ortolina fe748782c7 Reorder record sets via drag and drop 2026-02-10 11:16:41 +00:00
Claudio Ortolina 84159a1c0d Improve test coverage
Files Created (8 files, 54 new tests)

  Fixtures

  1. test/support/fixtures/music_library/record_sets.ex — record_set/1
and record_set_with_records/2 helpers
  2. test/support/fixtures/music_library/online_store_templates.ex —
online_store_template/1 helper

  Context Tests

  3. test/music_library/record_sets_test.exs — 20 tests covering search,
count, CRUD, add/remove/move records
  4. test/music_library/online_store_templates_test.exs — 8 tests
covering list, CRUD, URL generation, validation
  5. test/music_library/search_test.exs — 5 tests covering universal
search and search counts
  6. test/music_library/notes_test.exs — 4 tests covering get, create,
update

  LiveView Tests

  7. test/music_library_web/live/record_set_live/index_test.exs — 11
tests covering listing, empty state, search, ordering,
create/edit/delete sets, remove records, reorder records
  8.
test/music_library_web/live/online_store_template_live/index_test.exs —
6 tests covering listing, create/edit/delete, toggle enabled

  Results

  - 297 tests, 0 failures (up from 243 — 54 new tests added)
  - All existing tests continue to pass

There's a remaining gettext warning that needs to be investigated
2026-02-07 10:39:22 +00:00
Claudio Ortolina faf8500256 Ensure uniqueness of scrobble rules 2026-02-02 10:32:32 +00:00
Claudio Ortolina 5692be4e63 Update dependencies
credo 1.7.13 => 1.7.14 - requires updates to pass updated checks
2025-12-01 16:09:51 +00:00
Copilot e11894c095 Batch scrobble rule application by type to reduce N queries to 2 (#61)
* Implement optimized batch rule application

- Add apply_all_album_rules/1 to batch apply all album rules in single query
- Add apply_all_artist_rules/1 to batch apply all artist rules in single query
- Update apply_all_rules/0 to use new batch functions
- Add comprehensive tests for batch application
- Use CASE statement in SQL to apply multiple rules efficiently

Co-authored-by: cloud8421 <537608+cloud8421@users.noreply.github.com>

* Add batch application support for track-filtered rules

- Extend apply_all_album_rules/2 to support filtering by tracks
- Extend apply_all_artist_rules/2 to support filtering by tracks
- Update apply_all_rules/1 to use batch functions for track-filtered application
- Remove duplicate function definitions
- Ensures both main use cases (all tracks and specific tracks) are optimized

Co-authored-by: cloud8421 <537608+cloud8421@users.noreply.github.com>

* Add comprehensive documentation for optimization

- Document the problem and solution approach
- Explain SQL generation and performance impact
- Detail trade-offs and backward compatibility
- Include future considerations and scalability notes

Co-authored-by: cloud8421 <537608+cloud8421@users.noreply.github.com>

* Apply credo suggestions and format

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: cloud8421 <537608+cloud8421@users.noreply.github.com>
Co-authored-by: Claudio Ortolina <cloud8421@gmail.com>
2025-11-10 20:04:34 +00:00
Claudio Ortolina d38089d82d Show similarity in % points 2025-10-12 20:46:01 +02:00
Claudio Ortolina ee199272b1 Use sqlite vector 2025-10-12 13:36:42 +02:00
Claudio Ortolina 14934b25d9 Load the sqlite-vec extension 2025-10-11 21:47:02 +02:00
Claudio Ortolina 9cae972bc5 Add similar records
Initial stab by Claude
2025-10-11 21:39:59 +02:00
Claudio Ortolina 621fe385e3 Show record status markers in scrobbled tracks index 2025-09-29 11:04:35 +03:00
Claudio Ortolina 90b3fa1fdc Refactor Records.Cover to Assets.Image 2025-09-20 07:42:38 +03:00
Claudio Ortolina aa72692eaf Cache transformations 2025-09-17 16:54:47 +03:00
Claudio Ortolina dc9c8fca8e Add Asset.Transform module
Allows building URLs with embedded transformation options
2025-09-17 09:33:13 +03:00
Claudio Ortolina eec097dd43 Address warnings 2025-09-16 13:15:16 +03:00
Claudio Ortolina dae334b1b7 Scrobbled tracks CRUD
- Failing tests
- Warnings
2025-09-16 12:45:36 +03:00
Claudio Ortolina 1b89f244f8 Change test to avoid timing-related failures
When inserting twice, the second insertion is a no-op, due to the fact
that we use `on_conflict: :nothing` in the `Repo.insert/2` call.

This means that for fields generated client side like timestamps we
don't get the stored values, but the client side values (which haven't
been persisted).

In case of the test crossing the second boundary, the second asset would
be different because of the timestamps, and the test would fail.

This test is better. It tests that no matter how many times we insert
the same thing we always have one stored.
2025-09-15 07:42:03 +03:00
Claudio Ortolina 408899747e Add module to format numbers 2025-09-02 08:26:59 +03:00
Claudio Ortolina dc77d337c0 Migrate from embedded covers to assets 2025-09-01 16:20:41 +03:00
Claudio Ortolina cbf567a502 Support assets upserts 2025-09-01 15:06:00 +03:00
Claudio Ortolina db843bf061 Add convenience to store images 2025-09-01 11:59:16 +03:00
Claudio Ortolina a605610c9d Add base infra to store assets 2025-09-01 11:59:16 +03:00
Claudio Ortolina df4ca83aeb Add ability to rotate dominant colors 2025-08-27 21:11:13 +03:00
Claudio Ortolina f37719f272 Rename ScrobbleRules.Worker to Worker.ApplyScrobbleRules 2025-07-07 20:14:32 +01:00
Claudio Ortolina aee08cc2bb Use Enum for scrobble rule type 2025-07-04 14:23:19 +01:00
Claudio Ortolina 93e10bc5d9 Remove excessively defensive coding 2025-07-03 21:18:21 +01:00
Claudio Ortolina b2bc5f07f7 Remove unnecessary code 2025-07-03 21:10:59 +01:00
Claudio Ortolina 024ba38b63 Use Ecto.UUID instead of hand-rolled type 2025-07-03 21:07:42 +01:00
Claudio Ortolina d91685b646 First rough implementation of scrobble rules (Claude)
This plan outlines the implementation of functionality to manage
arbitrary rules for fixing data in the `scrobbled_tracks` table. The
system will support two types of transformations:

1. **Album Rule**: Update the album MusicBrainz ID of all tracks
belonging to a specific album (identified by album title)
2. **Artist Rule**: Update the artist MusicBrainz ID of all tracks
belonging to a specific artist (identified by artist name)

- `scrobbled_tracks` table exists with the following structure:
  - `scrobbled_at_uts` (integer, primary key)
  - `musicbrainz_id` (string) - track MusicBrainz ID
  - `title` (string) - track title
  - `cover_url` (string)
  - `scrobbled_at_label` (string)
  - `artist` (map) - embedded artist data with `musicbrainz_id` and
`name`
  - `album` (map) - embedded album data with `musicbrainz_id` and
`title`
  - `last_fm_data` (map) - raw Last.fm API response

- `LastFm.Track` - Schema for scrobbled tracks
- `LastFm.Artist` - Embedded artist schema
- `LastFm.Album` - Embedded album schema

- Main navigation has Stats, Collection, Wishlist sections
- "More" dropdown contains development tools and utilities
- Uses Phoenix LiveView for interactive components

**File**:
`priv/repo/migrations/YYYYMMDDHHMMSS_create_scrobble_rules.exs`

Create `scrobble_rules` table with:

- `id` (primary key)
- `type` (string) - "album" or "artist"
- `match_value` (string) - album title or artist name to match
- `target_musicbrainz_id` (string) - MusicBrainz ID to set
- `enabled` (boolean) - whether rule is active
- `description` (text) - optional description for the rule
- `inserted_at` (timestamp)
- `updated_at` (timestamp)

Indexes:

- `type, match_value` (compound index for efficient matching)
- `enabled` (for filtering active rules)

**File**: `lib/music_library/scrobble_rules/scrobble_rule.ex`

Create Ecto schema with:

- Field definitions matching migration
- Validations:
  - `type` must be "album" or "artist"
  - `match_value` required and non-empty
  - `target_musicbrainz_id` required and non-empty
  - `enabled` defaults to true
- Changeset functions for create/update operations

**File**: `lib/music_library/scrobble_rules.ex`

Implement context functions:

- `list_scrobble_rules(opts \\ [])` - list all rules with optional
filtering
- `get_scrobble_rule!(id)` - get specific rule by ID
- `create_scrobble_rule(attrs)` - create new rule
- `update_scrobble_rule(rule, attrs)` - update existing rule
- `delete_scrobble_rule(rule)` - delete rule
- `change_scrobble_rule(rule, attrs \\ %{})` - create changeset
- `list_enabled_rules()` - get only enabled rules for worker
- `apply_album_rule(rule)` - apply album transformation
- `apply_artist_rule(rule)` - apply artist transformation
- `apply_all_rules()` - apply all enabled rules

**File**: `lib/music_library_web/components/layouts/app.html.heex`

Add new navigation link in the "More" dropdown:

- Icon: `hero-adjustments-horizontal`
- Label: "Scrobble Rules"
- Route: `/scrobble-rules`
- Position: After "Oban" link, before separator

**File**: `lib/music_library_web/router.ex`

Add new LiveView routes in the authenticated scope:

- `/scrobble-rules` - index page
- `/scrobble-rules/new` - create new rule
- `/scrobble-rules/:id/edit` - edit existing rule

**File**: `lib/music_library_web/live/scrobble_rules_live/index.ex`

Implement LiveView with:

- List view showing all rules with status indicators
- Create/Edit forms with validation
- Delete confirmation
- Filter controls (by type, enabled/disabled)
- Manual rule application triggers
- Real-time updates via PubSub

**Template**:
`lib/music_library_web/live/scrobble_rules_live/index.html.heex`

- Table layout with rule details
- Action buttons (Edit, Delete, Apply)
- Form components for create/edit
- Status indicators for enabled/disabled rules

**File**:
`lib/music_library_web/live/scrobble_rules_live/form_component.ex`

Create reusable form component:

- Type selection (album/artist)
- Match value input with validation
- Target MusicBrainz ID input
- Description textarea
- Enable/disable toggle
- Save/Cancel actions

Where possible, use the components available at
<https://docs.fluxonui.com/1.1.4/overview.html>.

**File**: `lib/music_library/scrobble_rules/worker.ex`

Implement Oban worker:

- Scheduled to run periodically (e.g., every 30 minutes)
- Fetches all enabled rules
- Applies each rule in sequence
- Logs application results
- Handles errors gracefully
- Tracks last application time

**File**: `test/music_library/scrobble_rules_test.exs`

- Test all context functions
- Test rule application logic
- Test edge cases and error handling

**File**: `test/music_library/scrobble_rules/scrobble_rule_test.exs`

- Test schema validations
- Test changeset functions
- Test constraint validations

**File**: `test/music_library_web/live/scrobble_rules_live_test.exs`

- Test LiveView interactions
- Test form submissions
- Test real-time updates
- Test navigation and routing

**File**: `test/music_library/scrobble_rules/worker_test.exs`

- Test job execution
- Test rule application
- Test error handling
- Test scheduling

- Ensure atomic operations when applying rules
- Validate MusicBrainz IDs before applying
- Log all transformations for audit trail

- Index optimization for rule matching
- Batch processing for large datasets
- Rate limiting for external API calls

- Clear feedback on rule application
- Preview mode to show what would change
- Bulk operations for managing multiple rules
- Export/import functionality for rule sets

- Input validation and sanitization
- Rate limiting on rule creation
- Audit logging for all changes

```sql
-- scrobble_rules table
CREATE TABLE scrobble_rules (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  type TEXT NOT NULL CHECK (type IN ('album', 'artist')),
  match_value TEXT NOT NULL,
  target_musicbrainz_id TEXT NOT NULL,
  enabled BOOLEAN NOT NULL DEFAULT TRUE,
  description TEXT,
  inserted_at TIMESTAMP NOT NULL,
  updated_at TIMESTAMP NOT NULL
);

CREATE INDEX idx_scrobble_rules_type_match ON scrobble_rules(type,
match_value);
CREATE INDEX idx_scrobble_rules_enabled ON scrobble_rules(enabled);
```

```
lib/music_library/
├── scrobble_rules/
│   ├── scrobble_rule.ex          # Schema
│   └── worker.ex                 # Oban worker
└── scrobble_rules.ex             # Context

lib/music_library_web/
└── live/
    └── scrobble_rules_live/
        ├── index.ex              # LiveView
        ├── index.html.heex       # Template
        └── form_component.ex     # Form component

priv/repo/migrations/
└── YYYYMMDDHHMMSS_create_scrobble_rules.exs

test/
├── music_library/
│   ├── scrobble_rules_test.exs
│   └── scrobble_rules/
│       ├── scrobble_rule_test.exs
│       └── worker_test.exs
└── music_library_web/
    └── live/
        └── scrobble_rules_live_test.exs
```

1. Database migration and schema
2. Context module with basic CRUD operations
3. LiveView interface for rule management
4. Navigation and routing updates
5. Periodic worker implementation
6. Comprehensive testing
7. Documentation and deployment

This plan ensures a robust, maintainable solution that integrates
seamlessly with the existing Music Library application architecture.
2025-07-03 16:47:08 +01:00
Claudio Ortolina 07d9db6011 Speed up tests by caching cover data
Suggested and executed by Claude
2025-06-14 21:15:06 +03:00
Claudio Ortolina c5e47cbc8c Display dominant colors from the db 2025-06-07 20:07:23 +01:00
Claudio Ortolina fd997e853f Update artists when refreshing MusicBrainz data 2025-06-04 15:57:25 +01:00
Claudio Ortolina 676c2e4290 Enable quokka with :module_directives rule 2025-05-28 19:41:57 +01:00
Claudio Ortolina e083950138 Rename record.release to record.release_date
Includes changes to dependent tables (i.e. the record search index) and
related functions.
2025-05-01 14:04:57 +01:00
Claudio Ortolina ad6e8503ed Add tests around data consistency bg jobs for artist info 2025-04-29 21:05:02 +01:00
Claudio Ortolina 34069636a0 Add tests for Artists.fetch_artist_info/1 2025-04-28 11:04:45 +01:00
Claudio Ortolina 995773f673 Default covers to 2000px 2025-04-14 10:58:21 +01:00
Claudio Ortolina 961530cb51 Add tests for BarcodeScan.Result 2025-04-06 19:54:00 +01:00
Claudio Ortolina 93f6b260b2 Extract test fixture for release_group_releases 2025-03-01 07:40:00 +00:00
Claudio Ortolina a218942110 Remove mox in favour of built-in Req stubs 2025-02-28 16:41:16 +00:00
Claudio Ortolina 889ba61e4c Extract LastFm module 2025-02-21 16:02:18 +00:00
Claudio Ortolina 7df136c890 Sort aliases 2025-02-20 15:27:01 +00:00
Claudio Ortolina b2daa561d7 Use aliases 2025-02-20 15:27:01 +00:00
Claudio Ortolina 94a2784020 Move function in MusicBrainz main module 2025-02-17 22:07:49 +00:00
Claudio Ortolina e218b06350 Rename APIBehaviourMock -> APIMock 2025-02-17 21:41:50 +00:00
Claudio Ortolina 37f72261c3 Extract MusicBrainz main module 2025-02-17 17:30:13 +00:00
Claudio Ortolina 513ce6363f Rename RecordsFixtures -> Fixtures.Records 2025-02-17 16:08:19 +00:00
Claudio Ortolina ea7b5b646a Namespace MusicBrainz fixtures 2025-02-17 16:03:23 +00:00
Claudio Ortolina d27f3ccf72 Namespace Last.Fm fixtures 2025-02-17 15:52:54 +00:00
Claudio Ortolina d077f08afa Start making it more robust
- Use structured data for search/render
- Start testing component
- Start testing domain logic
2025-02-15 18:08:30 +00:00