Commit Graph

18 Commits

Author SHA1 Message Date
bvandeusen da9c19b2dc fc5: migration 0015 + MigrationRun model (kind/status as String(32), API-validated)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 08:11:42 -04:00
bvandeusen 6d67e6e987 fc3d: models — Source.consecutive_failures + ImportSettings scheduling knobs
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 15:58:42 -04:00
bvandeusen 159d4cb046 feat(fc3c): migration 0013 — download_event.metadata + import_settings downloader fields
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 20:36:19 -04:00
bvandeusen 2a18016b69 feat(fc3b): migration 0012 + AppSetting model
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 18:33:14 -04:00
bvandeusen e623e97be2 feat(fc3b): migration 0011 — credential schema aligned with GS wire fields
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 18:32:52 -04:00
bvandeusen 8f69478227 feat(attachments): PostAttachment model + 0009 (table + batch counter)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 11:10:53 -04:00
bvandeusen 63e7185811 feat(provenance): image_record.artist_id + 0008 backfill/drop-artist-tags
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 21:33:11 -04:00
bvandeusen 15dac50367 feat(post): add nullable description + attachment_count (migration 0007)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 09:38:43 -04:00
bvandeusen 9ef5e5047d feat(phash): import_settings.phash_threshold (migration 0006) exposed + validated in API
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 22:04:13 -04:00
bvandeusen b83baa7d7f feat(series): SeriesPage model + 0005 migration (unique image, ordered pages)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 23:33:12 -04:00
bvandeusen 0a4eb0bdc0 fix(fc2b): bare CHECK constraint names (naming-convention double-prefix)
Base.metadata's convention applies ck_%(table_name)s_%(constraint_name)s.
ml_settings and tag_allowlist passed already-prefixed names
(ck_ml_settings_singleton / ck_tag_allowlist_confidence_range), so the
ORM-side names came out doubled (ck_ml_settings_ck_ml_settings_singleton
etc.) and the migration-0003 smoke tests failed.

Same class of bug fixed in FC-2a for ImportSettings — should have applied
that lesson here. Bare names ('singleton', 'confidence_range') let the
convention produce the final names that match migration 0003's literal
DDL. Migration unchanged; only the model __table_args__.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 12:05:41 -04:00
bvandeusen 906804140c feat(fc2b): schema migration 0003 — ML pipeline tables
Renames image_record.wd14_* -> tagger_* (we're on Camie now, not WD14).
Adds tag_allowlist (auto-apply opt-in, per-tag confidence),
tag_suggestion_rejection (per-image dismissals), tag_alias (composite
(string, category) -> canonical tag, resolved at read time),
tag_reference_embedding (per-tag SigLIP centroids), and the ml_settings
singleton (per-category + centroid thresholds, model version pins).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 07:35:22 -04:00
bvandeusen 23d2fb24ac fix(fc2a): three unit-test failures — async fixture decorator, name convention, lazy celery includes
test_health.py:
  @pytest.fixture on an `async def` function is rejected by pytest-asyncio
  1.x strict mode. Switched to @pytest_asyncio.fixture.

backend/app/models/import_settings.py:
  My constraint name was 'ck_import_settings_singleton' and Base.metadata's
  naming convention applies 'ck_<table>_<name>' on top, so the final ORM
  name was 'ck_import_settings_ck_import_settings_singleton' (double prefix).
  The migration creates the DB constraint as 'ck_import_settings_singleton'
  via raw alembic, so they didn't match. Fix: bare name 'singleton' in the
  model → convention produces 'ck_import_settings_singleton', matching the
  migration's literal name.

tests/test_tasks_register.py:
  Celery's include=[...] parameter on the constructor is lazy — task
  modules aren't imported until a worker boots. The test only imported
  the Celery instance, so the @celery.task decorators in scan.py /
  import_file.py / thumbnail.py never ran. Fix: explicit `import` of
  those modules for side-effect at the top of the test file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 15:40:47 -04:00
bvandeusen 80a5690740 fix(fc2a): apply ruff autofix + skip vue-tsc check (no tsconfig)
Ruff:
The remaining I001 errors came from ruff treating `alembic` as a first-
party module (because the alembic/ directory exists in the repo root)
rather than third-party. Ran `ruff check --fix` locally — auto-sorted
import groupings to put alembic/sqlalchemy alongside backend.* as first-
party, and trimmed redundant blank lines after a few import blocks.

Frontend:
`npm run check` (vue-tsc --noEmit) was failing because vue-tsc has no
tsconfig.json to read against, and the frontend is pure JS without
JSDoc annotations — vue-tsc had nothing to do. Skipping the step until
we add a tsconfig + convert to TS or add JSDoc annotations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 14:24:25 -04:00
bvandeusen b4e0d680f1 fix(fc2a): satisfy ruff 0.15.13 lint — UP017, UP042, I001
Ruff lint surfaced 23 violations across three rules; all addressed:

UP017 (Use datetime.UTC alias):
  Replaced 13 sites of datetime.now(timezone.utc) with datetime.now(UTC),
  also adjusted from-imports accordingly. UTC is a Python 3.11+ alias for
  timezone.utc that ruff's pyupgrade rules prefer.

UP042 (StrEnum):
  Replaced `class TagKind(str, Enum)` and `class SkipReason(str, Enum)`
  with `class Foo(StrEnum)`. StrEnum was added in Python 3.11 stdlib and
  is the modern idiom. Behavior is equivalent for our usage (the .value
  attribute, str(member) semantics).

I001 (Import sorting):
  Added `known-first-party = ["backend"]` to ruff.toml's [lint.isort] so
  ruff groups `backend.*` imports correctly. Without it, ruff treated
  them as third-party and demanded a different grouping. The existing
  import order is stdlib → third-party → first-party → local relative,
  which ruff now accepts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 14:11:35 -04:00
bvandeusen 9eb1614fbf feat(fc2a): schema migration 0002 — tag kinds + fandom hierarchy + import lifecycle
Adds Tag.kind enum (artist/character/fandom/general/series/archive/post/meta/rating),
Tag.fandom_id FK with CHECK constraint (only valid for kind='character'), and a
kind-aware uniqueness index so the same name can exist across kinds and the same
character name can exist in different fandoms.

Adds ImportBatch + ImportTask state-machine tables for scan tracking, plus a
single-row ImportSettings table (CHECK id=1) holding the importer's filter knobs.

Adds image_record.integrity_status column (defaults to 'unknown'); FC-2e
populates this via the integrity verifier.

Drops the unused tag.namespace column from FC-1 — superseded by kind.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 12:03:41 -04:00
bvandeusen d6a156dcd2 feat: define unified schema (Artist, Source, Post, ImageRecord, etc.) and initial migration
Implements the data model from spec §3 in one go so FC-2/FC-3 don't need
schema-adding migrations of their own. Artist is the unified entity for
both gallery 'artist:' tags and GallerySubscriber Subscriptions
(is_subscription flag). ImageProvenance is many-to-one, enabling the
enrich-on-duplicate rule for downloaded content that pHash-matches an
existing record.

The SigLIP embedding column uses pgvector(1152) for SigLIP-so400m;
swapping models in FC-2 will require a column-width migration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 07:34:29 -04:00
bvandeusen a03655039c feat: add SQLAlchemy declarative base, Alembic environment, and gitignore fix
Configures stable constraint naming so autogeneration produces clean diffs.
Alembic uses the sync psycopg driver while the runtime app uses asyncpg.

Also fixes a .gitignore bug caught during this task: the bare 'models/'
rule for the ML weights volume was matching backend/app/models/ (Python
package). Anchored all volume rules to repo root (/images/, /import/,
/downloads/, /models/, /postgres_data/, /redis_data/).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 07:32:58 -04:00