Document WCX project split analysis
This commit is contained in:
462
docs/database-schema-gap-analysis.md
Normal file
462
docs/database-schema-gap-analysis.md
Normal file
@ -0,0 +1,462 @@
|
|||||||
|
# Gap-analys för reproducerbart facitdatabasschema
|
||||||
|
|
||||||
|
## 1. Omfattning och metod
|
||||||
|
|
||||||
|
Analysen är avgränsad till om facitdatabasens struktur kan återskapas från
|
||||||
|
versionshanterade filer. Följande har inspekterats:
|
||||||
|
|
||||||
|
- `docs/project-split-analysis.md` och
|
||||||
|
`docs/project-split-analysis-summary.md`,
|
||||||
|
- `scripts/schema.sql`,
|
||||||
|
- databasberoendena i `import_site.py`, `migrate_csv.py`, `check_ocr.py`,
|
||||||
|
`process_pending_ocr.py`, `match_filenames.py` och
|
||||||
|
`test_duration_matching.py`,
|
||||||
|
- `sqlite_schema`, `PRAGMA table_xinfo`, `PRAGMA foreign_key_list`,
|
||||||
|
`PRAGMA index_list` och `PRAGMA index_xinfo` i
|
||||||
|
`/storage/disk1/WCX/database/wcx.db`, öppnad med `sqlite3 -readonly`, och
|
||||||
|
- resultatet av att läsa `scripts/schema.sql` i en separat SQLite-databas i
|
||||||
|
minnet.
|
||||||
|
|
||||||
|
**Verifierat faktum:** Produktionsdatabasen öppnades endast med
|
||||||
|
`sqlite3 -readonly`. Ingen kontroll eller verifiering skrev till den.
|
||||||
|
|
||||||
|
**Verifierat faktum:** `PRAGMA integrity_check` gav `ok` och
|
||||||
|
`PRAGMA foreign_key_check` gav inga rader. Både `PRAGMA user_version` och
|
||||||
|
`PRAGMA application_id` är `0`.
|
||||||
|
|
||||||
|
**Antagande:** Schemat som finns i den inspekterade `wcx.db` är den avsedda
|
||||||
|
produktionsmodellen. Analysen kan verifiera faktisk struktur och kodberoenden,
|
||||||
|
men inte om varje historiskt schemaingrepp var avsiktligt.
|
||||||
|
|
||||||
|
## 2. Fullständigt faktiskt produktionsschema
|
||||||
|
|
||||||
|
### 2.1 Tabeller
|
||||||
|
|
||||||
|
**Verifierat faktum:** Följande användardefinierade tabeller finns i
|
||||||
|
produktionen. SQL-definitionerna nedan är hämtade från `sqlite_schema`. Den
|
||||||
|
kompakta placeringen av OCR-kolumnerna på samma rad som `modified_at` är bara
|
||||||
|
hur SQLite lagrade den stegvis ändrade tabellens SQL-text; den ändrar inte
|
||||||
|
kolumnernas semantik.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE movie (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
aka TEXT,
|
||||||
|
nationality TEXT,
|
||||||
|
age INTEGER,
|
||||||
|
shoot_location TEXT,
|
||||||
|
shoot_date TEXT,
|
||||||
|
duration_seconds INTEGER,
|
||||||
|
description TEXT,
|
||||||
|
rating REAL,
|
||||||
|
web_url TEXT,
|
||||||
|
thumbnail TEXT,
|
||||||
|
published TEXT,
|
||||||
|
updated TEXT,
|
||||||
|
|
||||||
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
modified_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ocr_status TEXT NOT NULL DEFAULT 'pending',
|
||||||
|
ocr_raw_text TEXT,
|
||||||
|
ocr_error TEXT,
|
||||||
|
ocr_processed_at TEXT,
|
||||||
|
|
||||||
|
CHECK (age IS NULL OR age >= 0),
|
||||||
|
CHECK (rating IS NULL OR rating BETWEEN 0 AND 10),
|
||||||
|
CHECK (duration_seconds IS NULL OR duration_seconds >= 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE movie_history (
|
||||||
|
history_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
||||||
|
movie_id TEXT NOT NULL,
|
||||||
|
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
aka TEXT,
|
||||||
|
nationality TEXT,
|
||||||
|
age INTEGER,
|
||||||
|
shoot_location TEXT,
|
||||||
|
shoot_date TEXT,
|
||||||
|
duration_seconds INTEGER,
|
||||||
|
description TEXT,
|
||||||
|
rating REAL,
|
||||||
|
web_url TEXT,
|
||||||
|
thumbnail TEXT,
|
||||||
|
published TEXT,
|
||||||
|
updated TEXT,
|
||||||
|
|
||||||
|
created_at TEXT,
|
||||||
|
modified_at TEXT,
|
||||||
|
|
||||||
|
ocr_status TEXT,
|
||||||
|
ocr_raw_text TEXT,
|
||||||
|
ocr_error TEXT,
|
||||||
|
ocr_processed_at TEXT,
|
||||||
|
|
||||||
|
archived_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
change_source TEXT NOT NULL DEFAULT 'site_import',
|
||||||
|
change_summary TEXT NOT NULL,
|
||||||
|
|
||||||
|
FOREIGN KEY (movie_id) REFERENCES movie(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE movie_name_alias (
|
||||||
|
alias_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
||||||
|
movie_id TEXT NOT NULL,
|
||||||
|
alias TEXT NOT NULL,
|
||||||
|
normalized_alias TEXT NOT NULL,
|
||||||
|
source TEXT NOT NULL DEFAULT 'manual',
|
||||||
|
|
||||||
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
FOREIGN KEY (movie_id) REFERENCES movie(id),
|
||||||
|
|
||||||
|
UNIQUE (movie_id, normalized_alias)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verifierat faktum:** SQLite har dessutom skapat den interna tabellen
|
||||||
|
`sqlite_sequence(name, seq)` eftersom `movie_history.history_id` och
|
||||||
|
`movie_name_alias.alias_id` använder `AUTOINCREMENT`. Den ska inte deklareras
|
||||||
|
manuellt i projektets schema.
|
||||||
|
|
||||||
|
### 2.2 Index
|
||||||
|
|
||||||
|
**Verifierat faktum:** Produktionen innehåller följande explicita index:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE INDEX idx_movie_history_movie_id
|
||||||
|
ON movie_history(movie_id);
|
||||||
|
|
||||||
|
CREATE INDEX idx_movie_history_archived_at
|
||||||
|
ON movie_history(archived_at);
|
||||||
|
|
||||||
|
CREATE INDEX idx_movie_name_alias_movie_id
|
||||||
|
ON movie_name_alias(movie_id);
|
||||||
|
|
||||||
|
CREATE INDEX idx_movie_name_alias_normalized
|
||||||
|
ON movie_name_alias(normalized_alias);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verifierat faktum:** SQLite har också skapat två unika automatiska index:
|
||||||
|
|
||||||
|
| Index | Ursprung | Nyckelkolumner | Partiellt |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `sqlite_autoindex_movie_1` | `PRIMARY KEY` | `movie.id` | nej |
|
||||||
|
| `sqlite_autoindex_movie_name_alias_1` | `UNIQUE` | `movie_id`, `normalized_alias` | nej |
|
||||||
|
|
||||||
|
Alla sex index använder stigande ordning och `BINARY`-kollation. De fyra
|
||||||
|
explicita indexen är icke-unika och icke-partiella.
|
||||||
|
|
||||||
|
### 2.3 Constraints
|
||||||
|
|
||||||
|
**Verifierat faktum:** Produktionsschemat deklarerar:
|
||||||
|
|
||||||
|
- primärnyckel på `movie.id`,
|
||||||
|
- primärnyckel med `AUTOINCREMENT` på `movie_history.history_id`,
|
||||||
|
- primärnyckel med `AUTOINCREMENT` på `movie_name_alias.alias_id`,
|
||||||
|
- `NOT NULL` enligt SQL-definitionerna ovan,
|
||||||
|
- standardvärdena `CURRENT_TIMESTAMP`, `'pending'`, `'site_import'` och
|
||||||
|
`'manual'` enligt SQL-definitionerna ovan,
|
||||||
|
- `CHECK` för icke-negativ `age` och `duration_seconds` samt `rating` i
|
||||||
|
intervallet 0–10,
|
||||||
|
- främmande nyckel från `movie_history.movie_id` till `movie.id`, med
|
||||||
|
`NO ACTION` för både update och delete,
|
||||||
|
- främmande nyckel från `movie_name_alias.movie_id` till `movie.id`, med
|
||||||
|
`NO ACTION` för både update och delete, och
|
||||||
|
- unikhet för kombinationen
|
||||||
|
`movie_name_alias(movie_id, normalized_alias)`.
|
||||||
|
|
||||||
|
**Verifierat faktum:** Det finns ingen `CHECK`-constraint som begränsar
|
||||||
|
`ocr_status` till de statusvärden som koden använder (`pending`, `completed`,
|
||||||
|
`manual_review` och `failed`).
|
||||||
|
|
||||||
|
**Verifierat faktum:** Främmande nycklar är del av schemat, men SQLite kräver
|
||||||
|
att `PRAGMA foreign_keys = ON` aktiveras per anslutning för enforcement.
|
||||||
|
`import_site.py` aktiverar detta. De övriga inspekterade Python-skripten gör
|
||||||
|
det inte. Detta är ett runtime-beteende, inte en strukturell skillnad mellan
|
||||||
|
produktionsschemat och Git-schemat.
|
||||||
|
|
||||||
|
### 2.4 Triggers och views
|
||||||
|
|
||||||
|
**Verifierat faktum:** Produktionsdatabasen innehåller inga triggers och inga
|
||||||
|
views. Historisering görs uttryckligen av `import_site.py`, inte av en trigger.
|
||||||
|
|
||||||
|
## 3. Schema som kan återskapas från Git i dag
|
||||||
|
|
||||||
|
**Verifierat faktum:** När `scripts/schema.sql` läses i en tom SQLite-databas
|
||||||
|
skapas `movie`, `movie_history`, `movie_name_alias`, den interna
|
||||||
|
`sqlite_sequence`, samma fyra explicita index och samma två automatiska index
|
||||||
|
som i produktionen. Inga triggers eller views skapas.
|
||||||
|
|
||||||
|
Det återskapade Git-schemats `movie` är:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE IF NOT EXISTS movie (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
aka TEXT,
|
||||||
|
nationality TEXT,
|
||||||
|
age INTEGER,
|
||||||
|
shoot_location TEXT,
|
||||||
|
shoot_date TEXT,
|
||||||
|
duration_seconds INTEGER,
|
||||||
|
description TEXT,
|
||||||
|
rating REAL,
|
||||||
|
web_url TEXT,
|
||||||
|
thumbnail TEXT,
|
||||||
|
published TEXT,
|
||||||
|
updated TEXT,
|
||||||
|
|
||||||
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
modified_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CHECK (age IS NULL OR age >= 0),
|
||||||
|
CHECK (rating IS NULL OR rating BETWEEN 0 AND 10),
|
||||||
|
CHECK (duration_seconds IS NULL OR duration_seconds >= 0)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Git-schemats `movie_history` och `movie_name_alias` är semantiskt identiska
|
||||||
|
med definitionerna i avsnitt 2.1. De omfattar redan samtliga fyra
|
||||||
|
OCR-kolumner i `movie_history`. Git skapar även exakt de fyra explicita index
|
||||||
|
som visas i avsnitt 2.2.
|
||||||
|
|
||||||
|
**Verifierat faktum:** `IF NOT EXISTS` i Git-filen men inte i den lagrade
|
||||||
|
produktionsdefinitionen är ingen strukturell skillnad i den skapade databasen.
|
||||||
|
|
||||||
|
**Verifierat faktum:** Git innehåller ingen versionsmarkör eller
|
||||||
|
migrationshistorik. En nyskapad databas får `user_version = 0`, precis som den
|
||||||
|
inspekterade produktionsdatabasen.
|
||||||
|
|
||||||
|
## 4. Exakt jämförelse
|
||||||
|
|
||||||
|
### 4.1 Skillnader
|
||||||
|
|
||||||
|
**Verifierat faktum:** Den fullständiga strukturella skillnaden är fyra
|
||||||
|
kolumner som finns i produktionens `movie` men saknas i Git-schemats `movie`:
|
||||||
|
|
||||||
|
| Position i produktion | Kolumn | Typ | `NOT NULL` | Standardvärde | Saknas från Git |
|
||||||
|
| ---: | --- | --- | --- | --- | --- |
|
||||||
|
| 16 | `ocr_status` | `TEXT` | ja | `'pending'` | ja |
|
||||||
|
| 17 | `ocr_raw_text` | `TEXT` | nej | inget | ja |
|
||||||
|
| 18 | `ocr_error` | `TEXT` | nej | inget | ja |
|
||||||
|
| 19 | `ocr_processed_at` | `TEXT` | nej | inget | ja |
|
||||||
|
|
||||||
|
Konsekvenserna är konkreta:
|
||||||
|
|
||||||
|
- nya `movie`-rader från `import_site.py` förlitar sig på standardvärdet
|
||||||
|
`'pending'`,
|
||||||
|
- OCR-kön kan inte läsa `ocr_status` eller `ocr_error`,
|
||||||
|
- OCR-resultat kan inte lagras, och
|
||||||
|
- `import_site.py` kan inte läsa en komplett rad för arkivering till
|
||||||
|
`movie_history`.
|
||||||
|
|
||||||
|
### 4.2 Element som inte skiljer sig
|
||||||
|
|
||||||
|
**Verifierat faktum:** Följande matchar exakt semantiskt mellan produktion och
|
||||||
|
en tom databas skapad från Git:
|
||||||
|
|
||||||
|
- tabellerna `movie_history` och `movie_name_alias`, inklusive samtliga
|
||||||
|
kolumner, ordning, typer, nullbarhet och standardvärden,
|
||||||
|
- alla `movie`-kolumner som föregår OCR-kolumnerna,
|
||||||
|
- samtliga tre `CHECK`-constraints på `movie`,
|
||||||
|
- båda främmande nycklarna och deras `NO ACTION`-beteende,
|
||||||
|
- unikhetskravet på `(movie_id, normalized_alias)`,
|
||||||
|
- samtliga fyra explicita index,
|
||||||
|
- båda automatiska unika indexen,
|
||||||
|
- avsaknaden av triggers, och
|
||||||
|
- avsaknaden av views.
|
||||||
|
|
||||||
|
**Verifierat faktum:** Inga tabeller, index, constraints, triggers eller views
|
||||||
|
finns endast i Git-schemat. Den interna tabellen `sqlite_sequence` uppstår i
|
||||||
|
båda fallen och är inte ett gap.
|
||||||
|
|
||||||
|
## 5. Skriptberoenden per schemaelement
|
||||||
|
|
||||||
|
### 5.1 `movie`
|
||||||
|
|
||||||
|
| Skript | Lästa eller skrivna element | Förutsättning |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `import_site.py` | Alla 20 produktionskolumner läses för befintliga poster; webbägda fält och `modified_at` uppdateras; nya poster infogas. | Kräver de fyra OCR-kolumnerna för `SELECT` och historisering. Nya poster förlitar sig på `ocr_status DEFAULT 'pending'`. Förlitar sig även på `movie_history`, dess OCR-kolumner och obligatoriska `change_summary`. Aktiverar främmande nycklar. |
|
||||||
|
| `migrate_csv.py` | Läser `id`; infogar eller uppdaterar alla metadatafält samt `ocr_status`; uppdaterar `modified_at`. | Kräver `ocr_status`. Anger status explicit och förlitar sig därför inte på dess default i sina egna inserts. Skriptet aktiverar inte främmande nycklar. |
|
||||||
|
| `check_ocr.py` | Läser `name`, `thumbnail`; skriver `nationality`, `shoot_location`, `shoot_date`, samtliga fyra OCR-kolumner och `modified_at`. | Kräver alla fyra OCR-kolumnerna. Använder statusvärdena `completed`, `manual_review` och `failed`, men schemat validerar inte statusdomänen. |
|
||||||
|
| `process_pending_ocr.py` | Läser `id`, `name`, `published`, `ocr_status`, `ocr_error`. | Kräver `ocr_status` och `ocr_error`; förutsätter att nya obehandlade poster får status `pending`. |
|
||||||
|
| `match_filenames.py` | Läser `id`, `name`, `duration_seconds`. | Fungerar mot både produktions- och Git-schemat. |
|
||||||
|
| `test_duration_matching.py` | Läser `id`, `name`, `duration_seconds`. | Fungerar mot både produktions- och Git-schemat. |
|
||||||
|
|
||||||
|
### 5.2 `movie_history`
|
||||||
|
|
||||||
|
| Skript | Beroende |
|
||||||
|
| --- | --- |
|
||||||
|
| `import_site.py` | Infogar den föregående kompletta `movie`-raden, inklusive samtliga OCR-fält, samt `change_source` och `change_summary`. `archived_at` lämnas till standardvärdet. |
|
||||||
|
| `match_filenames.py` | Läser `movie_id`, `duration_seconds` och `archived_at`. |
|
||||||
|
| `test_duration_matching.py` | Läser `movie_id` och `duration_seconds`. |
|
||||||
|
|
||||||
|
**Verifierat faktum:** Inget skript förutsätter en historiseringstrigger;
|
||||||
|
`import_site.py` gör historik-insert och film-update i samma anslutningskontext.
|
||||||
|
|
||||||
|
### 5.3 `movie_name_alias`
|
||||||
|
|
||||||
|
| Skript | Beroende |
|
||||||
|
| --- | --- |
|
||||||
|
| `match_filenames.py` | Läser `movie_id`, `alias`, `normalized_alias` och `source`. |
|
||||||
|
|
||||||
|
**Verifierat faktum:** Inget inspekterat skript skriver alias. Constraints och
|
||||||
|
index på aliastabellen finns ändå likadant i produktion och Git.
|
||||||
|
|
||||||
|
## 6. Minsta säkra åtgärd
|
||||||
|
|
||||||
|
**Rekommendation:** Gör en enda avgränsad schemaändring: lägg till de fyra
|
||||||
|
verifierade produktionskolumnerna i `movie` i `scripts/schema.sql`, direkt
|
||||||
|
efter `modified_at`, med exakt följande definitioner:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
ocr_status TEXT NOT NULL DEFAULT 'pending',
|
||||||
|
ocr_raw_text TEXT,
|
||||||
|
ocr_error TEXT,
|
||||||
|
ocr_processed_at TEXT,
|
||||||
|
```
|
||||||
|
|
||||||
|
Detta är den minsta åtgärden eftersom alla andra tabeller, index och
|
||||||
|
constraints redan reproduceras. Lägg inte samtidigt till triggers, views,
|
||||||
|
nya index, status-`CHECK`, ändrad främmande-nyckelpolicy eller andra
|
||||||
|
modellförbättringar; sådana ändringar skulle gå utöver att återskapa dagens
|
||||||
|
produktion.
|
||||||
|
|
||||||
|
**Rekommendation:** Behandla den korrigerade `schema.sql` som ett komplett
|
||||||
|
grundschema för helt nya databaser. Verifiera det först mot en ny temporär
|
||||||
|
databas. Kör inte den kompletta filen som en påstådd reparationsmigration mot
|
||||||
|
produktion: `CREATE TABLE IF NOT EXISTS` kompletterar inte kolumner i en redan
|
||||||
|
existerande tabell.
|
||||||
|
|
||||||
|
**Antagande:** Produktionsdatabasen behöver ingen strukturändring för detta
|
||||||
|
gap, eftersom den redan har exakt de fyra föreslagna kolumnerna. Den behöver
|
||||||
|
endast adopteras av en framtida migrationsmodell efter separat kontroll.
|
||||||
|
|
||||||
|
## 7. Enkel migrationsmodell för SQLite
|
||||||
|
|
||||||
|
**Rekommendation:** Inför en liten, versionshanterad modell med två delar:
|
||||||
|
|
||||||
|
1. `schema.sql` är aktuell baseline för nya, tomma databaser.
|
||||||
|
2. En katalog med ordnade migrationer, exempelvis
|
||||||
|
`migrations/0001_add_movie_ocr_columns.sql`, beskriver övergångar för äldre
|
||||||
|
databaser.
|
||||||
|
|
||||||
|
Använd en liten Python-runner baserad på standardbibliotekets `sqlite3` och en
|
||||||
|
tabell som exempelvis:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE schema_migration (
|
||||||
|
version INTEGER PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL UNIQUE,
|
||||||
|
applied_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Runnern bör:
|
||||||
|
|
||||||
|
- öppna en explicit vald databas och aldrig ha produktionssökvägen som dold
|
||||||
|
teststandard,
|
||||||
|
- aktivera `PRAGMA foreign_keys = ON`,
|
||||||
|
- kontrollera befintliga kolumner med `PRAGMA table_xinfo` före migration,
|
||||||
|
- köra varje ej registrerad migration atomärt i en transaktion,
|
||||||
|
- registrera versionen först när hela migrationen lyckats,
|
||||||
|
- avbryta om databasen har en okänd eller partiell struktur, och
|
||||||
|
- köra `PRAGMA foreign_key_check` efteråt.
|
||||||
|
|
||||||
|
**Rekommendation:** Eftersom SQLite saknar portabelt
|
||||||
|
`ALTER TABLE ... ADD COLUMN IF NOT EXISTS` ska migration `0001` inte bara
|
||||||
|
exekveras blint. Runnern ska skilja mellan tre tillstånd:
|
||||||
|
|
||||||
|
- inga OCR-kolumner finns: lägg till alla fyra och registrera migrationen,
|
||||||
|
- alla fyra finns med exakt rätt typ, nullbarhet och default: registrera
|
||||||
|
migrationen som adopterad utan att ändra tabellen, och
|
||||||
|
- endast några finns eller definitionerna avviker: avbryt och kräv manuell
|
||||||
|
analys.
|
||||||
|
|
||||||
|
Exempel på de faktiska övergångssatserna för ett äldre Git-skapat schema är:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
ALTER TABLE movie
|
||||||
|
ADD COLUMN ocr_status TEXT NOT NULL DEFAULT 'pending';
|
||||||
|
ALTER TABLE movie
|
||||||
|
ADD COLUMN ocr_raw_text TEXT;
|
||||||
|
ALTER TABLE movie
|
||||||
|
ADD COLUMN ocr_error TEXT;
|
||||||
|
ALTER TABLE movie
|
||||||
|
ADD COLUMN ocr_processed_at TEXT;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Rekommendation:** Använd antingen `schema_migration` eller konsekvent
|
||||||
|
`PRAGMA user_version` som versionskälla, inte två oberoende sanningar. Tabellen
|
||||||
|
är enklare att granska eftersom den även lagrar migrationsnamn och tidpunkt.
|
||||||
|
Nuvarande `user_version = 0` visar att ingen versionsmodell används i dag.
|
||||||
|
|
||||||
|
## 8. Verifieringsplan utan produktionsskrivningar
|
||||||
|
|
||||||
|
Alla steg nedan ska använda en ny katalog skapad med `mktemp -d` och en ny
|
||||||
|
databasfil där. Ingen testparameter får peka på
|
||||||
|
`/storage/disk1/WCX/database/wcx.db`.
|
||||||
|
|
||||||
|
1. **Skydda referensen.** Registrera produktionsdatabasens filstorlek, mtime
|
||||||
|
och kryptografiska hash före testet. Använd därefter endast
|
||||||
|
`sqlite3 -readonly` om referensmetadata behöver läsas igen.
|
||||||
|
2. **Skapa baseline.** Skapa `${TEMP_DIR}/wcx.db` från den korrigerade
|
||||||
|
`schema.sql`.
|
||||||
|
3. **Jämför struktur maskinellt.** Jämför tabeller, kolumner, ordning, typer,
|
||||||
|
nullbarhet, defaults, primärnyckelflaggor, främmande nycklar, explicita och
|
||||||
|
automatiska index, indexkolumner, triggers och views mot den dokumenterade
|
||||||
|
produktionsmodellen. Ignorera endast harmlösa skillnader i SQL-format och
|
||||||
|
`IF NOT EXISTS`.
|
||||||
|
4. **Kontrollera constraints.** Verifiera att ogiltig negativ `age` och
|
||||||
|
`duration_seconds`, rating utanför 0–10, dubblett av
|
||||||
|
`(movie_id, normalized_alias)` samt främmande nyckelbrott med
|
||||||
|
`PRAGMA foreign_keys = ON` nekas i temporärdatabasen.
|
||||||
|
5. **Kontrollera defaults.** Infoga en minimal `movie(id, name)` och verifiera
|
||||||
|
att `created_at` och `modified_at` sätts samt att `ocr_status = 'pending'`
|
||||||
|
och övriga OCR-fält är `NULL`.
|
||||||
|
6. **Testa import.** Kör `import_site.py` med explicit `--input` till en liten
|
||||||
|
temporär JSON-fil och explicit `--database` till temporärdatabasen. Kör en
|
||||||
|
andra import med nyare uppdateringsdatum och verifiera att
|
||||||
|
`movie_history` får hela den föregående raden inklusive OCR-fälten.
|
||||||
|
7. **Testa CSV-operationer.** Kör först `migrate_csv.py --dry-run`. Om apply
|
||||||
|
behöver provas, använd endast explicit `--database` till temporärdatabasen
|
||||||
|
och en isolerad test-CSV.
|
||||||
|
8. **Testa OCR:s databasberoenden utan API-anrop.** Anropa de rena
|
||||||
|
databasfunktionerna i `check_ocr.py` mot temporärdatabasen för statusfallen
|
||||||
|
failed, manual review och completed. Verifiera alla skrivna metadata- och
|
||||||
|
OCR-kolumner. Testa `process_pending_ocr.py`-frågorna separat så att
|
||||||
|
`pending` kan väljas och `ocr_error` läsas. Gör inget Vision API-anrop.
|
||||||
|
9. **Testa konsumentfrågorna.** Kör matchningskodens databasladdning och
|
||||||
|
längddiagnostik mot temporärdatabasen efter att film, alias och historik har
|
||||||
|
lagts in.
|
||||||
|
10. **Testa migrationsvägen separat.** Skapa ytterligare en ny temporär
|
||||||
|
databas från dagens ofullständiga Git-schema, kör migration `0001`, och
|
||||||
|
upprepa strukturjämförelsen. Testa även adoption mot en ny databas som
|
||||||
|
redan har alla fyra korrekta kolumner samt avbrott mot ett medvetet
|
||||||
|
partiellt schema.
|
||||||
|
11. **Kör integritetskontroller.** Kräv `PRAGMA integrity_check = 'ok'` och
|
||||||
|
tomt resultat från `PRAGMA foreign_key_check` i varje temporärdatabas.
|
||||||
|
12. **Bekräfta produktionsskyddet.** Kontrollera att produktionens storlek,
|
||||||
|
mtime och hash är identiska med värdena från steg 1. Radera därefter endast
|
||||||
|
den explicit skapade temporärkatalogen.
|
||||||
|
|
||||||
|
**Rekommendation:** Godkänn reproducerbarheten först när både baseline-vägen
|
||||||
|
och migrationsvägen ger samma semantiska schema som avsnitt 2 och samtliga
|
||||||
|
databasberoende skript klarar sina isolerade operationer mot temporärdata.
|
||||||
|
|
||||||
|
## 9. Slutsats
|
||||||
|
|
||||||
|
**Verifierat faktum:** Facitdatabasen är nästan, men inte helt,
|
||||||
|
reproducerbar från Git. Gapet består exakt av fyra OCR-kolumner i `movie`.
|
||||||
|
Inga tabeller, index, constraints, triggers eller views saknas utöver dessa
|
||||||
|
kolumndefinitioner, och inga sådana objekt skiljer sig i övrigt.
|
||||||
|
|
||||||
|
**Rekommendation:** Komplettera baselineschemat med de fyra exakta
|
||||||
|
produktionsdefinitionerna och inför en liten, transaktionell migrationsrunner
|
||||||
|
som kan migrera äldre Git-skapade databaser och adoptera en redan korrekt
|
||||||
|
produktion utan att ändra den. All utveckling och verifiering ska ske mot nya
|
||||||
|
temporära databaser.
|
||||||
101
docs/project-split-analysis-summary.md
Normal file
101
docs/project-split-analysis-summary.md
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
# Sammanfattning: uppdelning av WCX-projektet
|
||||||
|
|
||||||
|
## Databasschemarisken
|
||||||
|
|
||||||
|
Den viktigaste risken före en projektuppdelning är att det versionshanterade
|
||||||
|
databasschemat inte återskapar den databas som produktionen faktiskt använder.
|
||||||
|
`database/wcx.db` innehåller OCR-kolumnerna `ocr_status`, `ocr_raw_text`,
|
||||||
|
`ocr_error` och `ocr_processed_at` i tabellen `movie`, men kolumnerna saknas i
|
||||||
|
`scripts/schema.sql`. Samtidigt förutsätter OCR-skripten och dokumentationen att
|
||||||
|
de finns.
|
||||||
|
|
||||||
|
En ny databas skapad enbart från Git blir därför inte kompatibel med hela
|
||||||
|
facitflödet. Efter en uppdelning kan detta se ut som ett fel i den flyttade
|
||||||
|
koden, trots att grundorsaken är ett ofullständigt schema. Facitschemat måste
|
||||||
|
därför göras komplett och reproducerbart innan filer eller drift flyttas.
|
||||||
|
Ändringen ska utvecklas och verifieras mot en ny, disposable databas och inte
|
||||||
|
tillämpas blint på `database/wcx.db`.
|
||||||
|
|
||||||
|
Filhanteringen har dessutom ett direkt kontrakt mot tabellerna `movie`,
|
||||||
|
`movie_name_alias` och `movie_history`. Inkompatibla ändringar i dessa tabeller
|
||||||
|
kan bryta det andra projektet. Detta läskontrakt behöver dokumenteras och
|
||||||
|
versionshanteras eller åtminstone omfattas av en tydlig ändringsprocess.
|
||||||
|
|
||||||
|
## Föreslagen målarkitektur
|
||||||
|
|
||||||
|
Repositoryt delas i två självständiga Git-projekt:
|
||||||
|
|
||||||
|
1. **Facitprojektet** äger schema, migrationsdata, webbsynkronisering, import,
|
||||||
|
OCR och schemalagd uppdatering. Det är ensam ägare av och enda skrivare till
|
||||||
|
tabellerna `movie`, `movie_history` och `movie_name_alias`.
|
||||||
|
2. **Filhanteringsprojektet** äger filinventering, `ffprobe`-integration,
|
||||||
|
matchningskod, tester och matchningsdokumentation. Det läser facitdatabasen
|
||||||
|
genom en konfigurerbar SQLite-sökväg och ansluter strikt skrivskyddat med
|
||||||
|
`mode=ro`. Filsystemsrättigheter bör också neka skrivning där det är
|
||||||
|
praktiskt möjligt.
|
||||||
|
|
||||||
|
Gränsen mellan projekten bör inledningsvis vara ett litet, dokumenterat
|
||||||
|
SQLite-läskontrakt. Ett API eller gemensamt Python-bibliotek behövs inte för den
|
||||||
|
nuvarande lokala användningen. Om filhanteringen senare behöver lagra lokala
|
||||||
|
filsökvägar, manuella beslut eller bearbetningsstatus ska den få en egen
|
||||||
|
databas. Den får referera till ett stabilt `movie.id`, men ska aldrig skriva
|
||||||
|
lokalt tillstånd i facitdatabasen.
|
||||||
|
|
||||||
|
Runtime-data ska hållas tydligt åtskild från Git-spårad kod. Den genererade
|
||||||
|
`import/wcx_site_index.json` bör behandlas enligt ett uttryckligt beslut om
|
||||||
|
runtime-data och normalt inte följa med som källfil till ett nytt repository.
|
||||||
|
|
||||||
|
## Beslut före implementation
|
||||||
|
|
||||||
|
Följande behöver avgöras innan den fysiska uppdelningen:
|
||||||
|
|
||||||
|
- Ska ett korrigerat komplett grundschema vara tillräckligt, eller behövs även
|
||||||
|
en versionshanterad migrationsmekanism för framtida schemaändringar?
|
||||||
|
- Var ska facitdatabasen ligga: i facitprojektets runtime-katalog eller i en
|
||||||
|
separat, stabil datakatalog?
|
||||||
|
- Ska projekten köras med samma operativsystemkonto, eller ska
|
||||||
|
filhanteringsprocessen få ett separat konto utan skrivrättighet till facit?
|
||||||
|
- Hur versionssätts läskontraktet, och hur samordnas inkompatibla
|
||||||
|
schemaändringar mellan projekten?
|
||||||
|
- Är `movie.id` tillräckligt stabilt för framtida externa referenser från en
|
||||||
|
separat filhanteringsdatabas?
|
||||||
|
- Ska `import/wcx_site_index.json` tas bort ur Git-indexet och endast genereras
|
||||||
|
vid körning?
|
||||||
|
- Var finns de externa systemd-enheterna, och vilka sökvägar, miljövariabler,
|
||||||
|
rättigheter och låsfiler måste ändras?
|
||||||
|
- Vilka namn och installationsplatser ska de två nya Git-projekten ha?
|
||||||
|
- Om produktionsdatabasen ska flyttas: vilken slutlig runtime-plats,
|
||||||
|
backupmetod och rättighetsmodell ska användas?
|
||||||
|
|
||||||
|
Direkt SQLite-läsning antas ge tillräcklig samtidighet och tillgänglighet. Om
|
||||||
|
det antagandet inte gäller behöver arkitekturen omprövas innan implementation.
|
||||||
|
|
||||||
|
## Rekommenderad migreringsordning
|
||||||
|
|
||||||
|
1. **Fastställ ansvar och läskontrakt.** Dokumentera att facitprojektet är ensam
|
||||||
|
skrivare samt exakt vilka tabeller och kolumner filhanteringen får läsa.
|
||||||
|
2. **Gör schemat reproducerbart.** Komplettera schemahanteringen och verifiera
|
||||||
|
en helt ny disposable databas, inklusive import- och OCR-operationer. Lämna
|
||||||
|
produktionsdatabasen orörd.
|
||||||
|
3. **Bestäm hanteringen av genererade filer.** Rätta Git-index och
|
||||||
|
dokumentation om import-JSON ska vara ren runtime-data.
|
||||||
|
4. **Gör sökvägar flyttbara i nuvarande repository.** Behåll explicita
|
||||||
|
kommandoradsargument, gör databassökvägar konfigurerbara och använd relativa
|
||||||
|
skriptsökvägar där det passar. Kontrollera även verklig systemd-konfiguration.
|
||||||
|
5. **Tvinga fram read-only.** Byt filhanteringens anslutningar till SQLite URI
|
||||||
|
med `mode=ro` och verifiera både normal läsning och att skrivning nekas.
|
||||||
|
Använd inte `immutable=1` för en databas som uppdateras samtidigt.
|
||||||
|
6. **Separera dokumentation och filstruktur logiskt.** Gör respektive projekt
|
||||||
|
självständigt begripligt och verifiera att gamla absoluta sökvägar är borta.
|
||||||
|
7. **Skapa de två Git-projekten.** Flytta först när schema, konfiguration och
|
||||||
|
read-only-gräns är verifierade. Testa facitimport och filmatchning separat
|
||||||
|
och behåll det gamla repositoryt tills båda fungerar.
|
||||||
|
8. **Flytta drift och systemd sist.** Kör facit manuellt från den nya platsen
|
||||||
|
före schemalagd produktionsdrift. Använd en säker SQLite-backupmetod om
|
||||||
|
databasen måste flyttas medan systemet är i bruk.
|
||||||
|
9. **Inför en separat filhanteringsdatabas endast vid konkret behov.** Detta är
|
||||||
|
en senare funktionsändring, inte en förutsättning för projektuppdelningen.
|
||||||
|
|
||||||
|
Schemaändring, read-only-förstärkning, filflytt och produktionsdrift bör göras
|
||||||
|
som separata, granskningsbara förändringar så att eventuella fel går att
|
||||||
|
isolera.
|
||||||
565
docs/project-split-analysis.md
Normal file
565
docs/project-split-analysis.md
Normal file
@ -0,0 +1,565 @@
|
|||||||
|
# Analys och migreringsplan för uppdelning av WCX-projektet
|
||||||
|
|
||||||
|
## 1. Syfte och avgränsning
|
||||||
|
|
||||||
|
Detta dokument beskriver hur det nuvarande WCX-repositoryt kan delas upp i
|
||||||
|
två separata Git-projekt:
|
||||||
|
|
||||||
|
1. ett facitprojekt som underhåller och äger filmmetadata i SQLite, och
|
||||||
|
2. ett filhanteringsprojekt som inventerar och matchar lokala videofiler.
|
||||||
|
|
||||||
|
Den grundläggande ägargränsen är att endast facitprojektet får skriva till
|
||||||
|
facitdatabasen. Filhanteringsprojektet får läsa den direkt, men anslutningen
|
||||||
|
ska vara strikt skrivskyddad. Filhanteringen kan senare få en egen databas för
|
||||||
|
lokala filer och matchningsbeslut.
|
||||||
|
|
||||||
|
Ett API eller ett gemensamt Python-bibliotek ingår inte i den föreslagna
|
||||||
|
lösningen. Ett sådant lager bör införas först om konkreta framtida behov visar
|
||||||
|
att direkt, skrivskyddad SQLite-läsning inte räcker.
|
||||||
|
|
||||||
|
Märkningarna i dokumentet betyder:
|
||||||
|
|
||||||
|
- **Verifierat faktum:** observerat i nuvarande repository eller databas.
|
||||||
|
- **Antagande:** rimlig utgångspunkt som behöver bekräftas.
|
||||||
|
- **Rekommendation:** föreslagen riktning, inte ett redan fattat beslut.
|
||||||
|
- **Ej fattat beslut:** en fråga som måste avgöras före eller under migreringen.
|
||||||
|
|
||||||
|
## 2. Nulägesanalys
|
||||||
|
|
||||||
|
### 2.1 Repositorystruktur
|
||||||
|
|
||||||
|
**Verifierat faktum:** Repositoryt innehåller följande huvudsakliga delar:
|
||||||
|
|
||||||
|
```text
|
||||||
|
WCX/
|
||||||
|
├── README.md
|
||||||
|
├── database/
|
||||||
|
│ ├── wcx.db
|
||||||
|
│ ├── wcx-test.db
|
||||||
|
│ ├── wcx-before-csv-migration.db
|
||||||
|
│ ├── update_wcx.lock
|
||||||
|
│ └── last_scheduled_update
|
||||||
|
├── import/
|
||||||
|
│ └── wcx_site_index.json
|
||||||
|
├── migration/
|
||||||
|
│ └── persons.utf8bom.csv
|
||||||
|
├── docs/
|
||||||
|
│ ├── filename-matching-requirements.md
|
||||||
|
│ └── filename-matching-requirements-prompt.md
|
||||||
|
└── scripts/
|
||||||
|
├── schema.sql
|
||||||
|
├── wcx_sync.py
|
||||||
|
├── import_site.py
|
||||||
|
├── migrate_csv.py
|
||||||
|
├── update_wcx.sh
|
||||||
|
├── scheduled_update_wcx.sh
|
||||||
|
├── process_pending_ocr.py
|
||||||
|
├── check_ocr.py
|
||||||
|
├── ocr.sh
|
||||||
|
├── parse_ocr.py
|
||||||
|
├── match_filenames.py
|
||||||
|
└── test_duration_matching.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verifierat faktum:** Produktionsdatabasen och databasens test- och
|
||||||
|
säkerhetskopior finns lokalt under `database/` och matchar repositoryts
|
||||||
|
ignore-regler för `database/*.db`.
|
||||||
|
|
||||||
|
**Verifierat faktum:** `import/wcx_site_index.json` är spårad av Git trots att
|
||||||
|
README anger att `import/*.json` är genererade filer som inte ska
|
||||||
|
versionshanteras. En ignore-regel slutar inte spåra en fil som redan finns i
|
||||||
|
Git-indexet.
|
||||||
|
|
||||||
|
**Verifierat faktum:** README beskriver huvudsakligen uppbyggnad och underhåll
|
||||||
|
av facitdatabasen. Kraven för filnamnsmatchning ligger separat under `docs/`.
|
||||||
|
|
||||||
|
### 2.2 Identifierade ansvarsområden
|
||||||
|
|
||||||
|
Facitunderhåll omfattar:
|
||||||
|
|
||||||
|
- hämtning av webbplatsens metadata,
|
||||||
|
- import och historisering av webbplatsdata,
|
||||||
|
- initialt schema och framtida schemaförändringar,
|
||||||
|
- återställning eller komplettering från historisk CSV,
|
||||||
|
- OCR och validering av OCR-resultat,
|
||||||
|
- manuellt eller automatiskt underhållen filmmetadata,
|
||||||
|
- schemalagd körning och exklusiv rätt att skriva facitdatabasen.
|
||||||
|
|
||||||
|
Filhantering omfattar:
|
||||||
|
|
||||||
|
- inventering av lokala videofiler,
|
||||||
|
- läsning av filmetadata med `ffprobe`,
|
||||||
|
- normalisering och matchning av filnamn,
|
||||||
|
- jämförelse med aktuella namn, alias och historiska längder,
|
||||||
|
- rapportering av säkra, tvetydiga och uteblivna matchningar,
|
||||||
|
- senare eventuell lagring av lokal filstatus i en separat databas.
|
||||||
|
|
||||||
|
**Verifierat faktum:** Nuvarande implementation har redan en naturlig gräns:
|
||||||
|
facitskripten skriver SQLite-data, medan matchningsskripten endast utför
|
||||||
|
`SELECT` och inte importerar kod från facitskripten.
|
||||||
|
|
||||||
|
## 3. Klassificering av skript och data
|
||||||
|
|
||||||
|
### 3.1 Facitunderhåll
|
||||||
|
|
||||||
|
**Verifierat faktum:** Följande filer hör till facitunderhåll:
|
||||||
|
|
||||||
|
| Fil | Ansvar |
|
||||||
|
| --- | --- |
|
||||||
|
| `scripts/schema.sql` | Definierar tabeller och index. |
|
||||||
|
| `scripts/wcx_sync.py` | Hämtar WCX-metadata och skriver ett JSON-index. |
|
||||||
|
| `scripts/import_site.py` | Infogar och uppdaterar `movie` samt arkiverar tidigare värden i `movie_history`. |
|
||||||
|
| `scripts/migrate_csv.py` | Validerar historisk CSV och kan infoga eller uppdatera `movie`. |
|
||||||
|
| `scripts/process_pending_ocr.py` | Läser väntande OCR-poster och startar behandling. |
|
||||||
|
| `scripts/check_ocr.py` | Läser en filmpost och skriver OCR-resultat och status till `movie`. |
|
||||||
|
| `scripts/ocr.sh` | Anropar Google Vision för en bild. |
|
||||||
|
| `scripts/parse_ocr.py` | Tolkar OCR-text; använder inte databasen direkt. |
|
||||||
|
| `scripts/update_wcx.sh` | Orkestrerar synkronisering, import och OCR. |
|
||||||
|
| `scripts/scheduled_update_wcx.sh` | Begränsar och startar schemalagda facituppdateringar. |
|
||||||
|
| `migration/persons.utf8bom.csv` | Historisk, versionshanterad metadatakälla. |
|
||||||
|
|
||||||
|
**Rekommendation:** Dessa filer bör tillsammans med en renodlad facit-README
|
||||||
|
flyttas till facitprojektet.
|
||||||
|
|
||||||
|
### 3.2 Filhantering
|
||||||
|
|
||||||
|
**Verifierat faktum:** Följande filer hör till filhantering:
|
||||||
|
|
||||||
|
| Fil | Ansvar |
|
||||||
|
| --- | --- |
|
||||||
|
| `scripts/match_filenames.py` | Läser videofiler och facitdata, kör `ffprobe` och klassificerar matchningar. |
|
||||||
|
| `scripts/test_duration_matching.py` | Diagnostiserar en fils längd mot aktuell och historisk facitlängd. |
|
||||||
|
| `docs/filename-matching-requirements.md` | Kravspecifikation för matchning. |
|
||||||
|
| `docs/filename-matching-requirements-prompt.md` | Historiskt arbets- och kravunderlag för matchningen. |
|
||||||
|
|
||||||
|
**Rekommendation:** `match_filenames.py`, dess tester och den relevanta
|
||||||
|
dokumentationen bör flyttas tillsammans till filhanteringsprojektet.
|
||||||
|
|
||||||
|
**Ej fattat beslut:** Det behöver avgöras om
|
||||||
|
`filename-matching-requirements-prompt.md` ska följa med som historiskt
|
||||||
|
arbetsmaterial, arkiveras eller utelämnas ur det nya projektet.
|
||||||
|
|
||||||
|
## 4. Nuvarande beroenden
|
||||||
|
|
||||||
|
### 4.1 Facitflöde
|
||||||
|
|
||||||
|
**Verifierat faktum:** Den schemalagda körkedjan är:
|
||||||
|
|
||||||
|
```text
|
||||||
|
scheduled_update_wcx.sh
|
||||||
|
→ update_wcx.sh
|
||||||
|
→ wcx_sync.py
|
||||||
|
→ import_site.py
|
||||||
|
→ process_pending_ocr.py
|
||||||
|
→ check_ocr.py
|
||||||
|
→ ocr.sh
|
||||||
|
→ parse_ocr.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Webbplatsens data flödar enligt följande:
|
||||||
|
|
||||||
|
```text
|
||||||
|
WCX-webbplats
|
||||||
|
→ wcx_sync.py
|
||||||
|
→ import/wcx_site_index.json
|
||||||
|
→ import_site.py
|
||||||
|
→ database/wcx.db
|
||||||
|
```
|
||||||
|
|
||||||
|
`import_site.py`, `migrate_csv.py` och `check_ocr.py` har avsiktliga
|
||||||
|
databasskrivningar. `process_pending_ocr.py` läser databasen direkt och låter
|
||||||
|
`check_ocr.py` utföra skrivningarna.
|
||||||
|
|
||||||
|
### 4.2 Filhanteringens facitberoende
|
||||||
|
|
||||||
|
**Verifierat faktum:** `match_filenames.py` läser följande minimala
|
||||||
|
databaskontrakt:
|
||||||
|
|
||||||
|
```text
|
||||||
|
movie:
|
||||||
|
id
|
||||||
|
name
|
||||||
|
duration_seconds
|
||||||
|
|
||||||
|
movie_name_alias:
|
||||||
|
movie_id
|
||||||
|
alias
|
||||||
|
normalized_alias
|
||||||
|
source
|
||||||
|
|
||||||
|
movie_history:
|
||||||
|
movie_id
|
||||||
|
duration_seconds
|
||||||
|
archived_at
|
||||||
|
```
|
||||||
|
|
||||||
|
`test_duration_matching.py` läser `movie.id`, `movie.duration_seconds` och
|
||||||
|
historiska längder ur `movie_history`.
|
||||||
|
|
||||||
|
**Verifierat faktum:** Matchningen har inga gemensamma Python-importer med
|
||||||
|
facitunderhållet. Kopplingen består av SQLite-databasens placering och schema.
|
||||||
|
|
||||||
|
**Verifierat faktum:** `match_filenames.py` och
|
||||||
|
`test_duration_matching.py` använder vanlig `sqlite3.connect(path)`. Deras SQL
|
||||||
|
är för närvarande endast läsande, men SQLite-anslutningen tvingar inte fram
|
||||||
|
read-only-läge.
|
||||||
|
|
||||||
|
**Rekommendation:** Filhanteringsprojektet ska öppna databasen med SQLite URI
|
||||||
|
och `mode=ro`. Filsystemsrättigheter bör om möjligt också neka den process som
|
||||||
|
kör filhanteringen skrivrättighet till facitdatabasen.
|
||||||
|
|
||||||
|
### 4.3 Hårdkodade sökvägar
|
||||||
|
|
||||||
|
**Verifierat faktum:** Följande produktionssökvägar är hårdkodade:
|
||||||
|
|
||||||
|
- `check_ocr.py`: databas, `ocr.sh` och `parse_ocr.py` under
|
||||||
|
`/storage/disk1/WCX`.
|
||||||
|
- `process_pending_ocr.py`: databas och `check_ocr.py` under
|
||||||
|
`/storage/disk1/WCX`.
|
||||||
|
- `import_site.py`: import-JSON och databas under `/storage/disk1/WCX`.
|
||||||
|
- `migrate_csv.py`: migrations-CSV och databas under `/storage/disk1/WCX`.
|
||||||
|
- `update_wcx.sh` och `scheduled_update_wcx.sh`:
|
||||||
|
`ROOT_DIR=/storage/disk1/WCX`.
|
||||||
|
- `match_filenames.py` och `test_duration_matching.py`:
|
||||||
|
`/storage/disk1/WCX/database/wcx.db`.
|
||||||
|
|
||||||
|
**Verifierat faktum:** `wcx_sync.py` har
|
||||||
|
`/storage/disk1/X/wcx_index.json` som eget standardutdata, medan
|
||||||
|
`update_wcx.sh` skickar den avsedda sökvägen
|
||||||
|
`/storage/disk1/WCX/import/wcx_site_index.json` explicit.
|
||||||
|
|
||||||
|
**Verifierat faktum:** README och matchningsdokumenten innehåller också flera
|
||||||
|
absoluta sökvägar till nuvarande repository.
|
||||||
|
|
||||||
|
## 5. Identifierat schemaproblem
|
||||||
|
|
||||||
|
**Verifierat faktum:** Den faktiska `database/wcx.db` innehåller tabellerna
|
||||||
|
`movie`, `movie_history` och `movie_name_alias`.
|
||||||
|
|
||||||
|
**Verifierat faktum:** Den faktiska tabellen `movie` innehåller dessutom
|
||||||
|
följande OCR-kolumner:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ocr_status
|
||||||
|
ocr_raw_text
|
||||||
|
ocr_error
|
||||||
|
ocr_processed_at
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verifierat faktum:** Dessa fyra kolumner saknas i nuvarande
|
||||||
|
`scripts/schema.sql`, trots att facitskripten och README förutsätter att de
|
||||||
|
finns.
|
||||||
|
|
||||||
|
Konsekvensen är att en ny databas som skapas enbart från det
|
||||||
|
versionshanterade schemat inte är kompatibel med hela facitflödet. Vid en
|
||||||
|
projektuppdelning kan detta orsaka svårdiagnostiserade fel: den flyttade koden
|
||||||
|
kan verka felaktig trots att den verkliga orsaken är att Git inte innehåller
|
||||||
|
ett komplett, reproducerbart schema.
|
||||||
|
|
||||||
|
**Rekommendation:** Gör facitdatabasens schema reproducerbart innan den fysiska
|
||||||
|
projektuppdelningen. Ändringen ska utvecklas och verifieras mot en ny,
|
||||||
|
disposable databas och får inte tillämpas blint på `database/wcx.db`.
|
||||||
|
|
||||||
|
**Ej fattat beslut:** Det behöver avgöras om nuvarande databas kan beskrivas av
|
||||||
|
ett korrigerat komplett grundschema, eller om projektet även behöver en
|
||||||
|
versionshanterad migrationsmekanism för framtida schemaändringar.
|
||||||
|
|
||||||
|
## 6. Risker och öppna frågor
|
||||||
|
|
||||||
|
### 6.1 Risker
|
||||||
|
|
||||||
|
1. **Oavsiktliga databasskrivningar från filhanteringen.** Nuvarande kod är
|
||||||
|
läsande men anslutningen är inte tekniskt read-only.
|
||||||
|
2. **Schemaändringar bryter konsumenten.** Filhanteringen beror direkt på tre
|
||||||
|
facittabeller och ett begränsat antal kolumner.
|
||||||
|
3. **Ofullständigt schema ger icke reproducerbara installationer.** OCR-flödet
|
||||||
|
kan inte byggas upp korrekt från nuvarande `schema.sql`.
|
||||||
|
4. **Hårdkodade sökvägar bryts vid flytt.** Både Python-, shell- och externa
|
||||||
|
systemd-konfigurationer kan peka på den gamla platsen.
|
||||||
|
5. **Felaktig kopiering av en aktiv SQLite-databas.** Om WAL används kan en
|
||||||
|
kopia av endast `.db` bli inkonsekvent. `immutable=1` är inte lämpligt för
|
||||||
|
en databas som facitprojektet fortsätter uppdatera.
|
||||||
|
6. **Otydligt dataägarskap.** Alias och historiska längder används av
|
||||||
|
filhanteringen men måste fortsatt ägas av facitprojektet.
|
||||||
|
7. **Genererad JSON är spårad.** Den kan oavsiktligt följa med som källfil till
|
||||||
|
ett nytt repository.
|
||||||
|
8. **Tester beror på lokal produktionslik data.** Det saknas ett isolerat,
|
||||||
|
reproducerbart testunderlag för hela gränsen mellan projekten.
|
||||||
|
9. **Driftkonfiguration ligger utanför Git.** README hänvisar till systemd-
|
||||||
|
enheter som inte finns i repositoryt och kan innehålla fler absoluta
|
||||||
|
sökvägar.
|
||||||
|
|
||||||
|
### 6.2 Öppna frågor
|
||||||
|
|
||||||
|
- **Ej fattat beslut:** Var ska facitdatabasen ligga efter uppdelningen: under
|
||||||
|
facitprojektets runtime-katalog eller i en separat stabil datakatalog?
|
||||||
|
- **Ej fattat beslut:** Ska båda projekten köras av samma operativsystemkonto,
|
||||||
|
eller ska filhanteringen få ett konto som saknar skrivrättighet till facit?
|
||||||
|
- **Ej fattat beslut:** Hur ska databasens läskontrakt versionssättas och hur
|
||||||
|
ska inkompatibla schemaändringar kommuniceras mellan projekten?
|
||||||
|
- **Ej fattat beslut:** Ska `import/wcx_site_index.json` tas bort ur Git-indexet
|
||||||
|
och endast genereras vid körning?
|
||||||
|
- **Ej fattat beslut:** Var finns systemd-enheterna, och vilka sökvägar måste
|
||||||
|
uppdateras när facitprojektet flyttas?
|
||||||
|
- **Antagande:** `movie.id` är en stabil identitet som en framtida separat
|
||||||
|
filhanteringsdatabas kan referera till.
|
||||||
|
- **Antagande:** Direkt SQLite-läsning ger tillräcklig samtidighet och
|
||||||
|
tillgänglighet för den aktuella lokala användningen.
|
||||||
|
|
||||||
|
## 7. Rekommenderad målarkitektur
|
||||||
|
|
||||||
|
### 7.1 Facitprojekt
|
||||||
|
|
||||||
|
**Rekommendation:** Facitprojektet ska innehålla schema, migrationsdata,
|
||||||
|
webbsynkronisering, import, OCR och schemalagd uppdatering. Det ska vara ensam
|
||||||
|
ägare av följande data:
|
||||||
|
|
||||||
|
```text
|
||||||
|
movie
|
||||||
|
movie_history
|
||||||
|
movie_name_alias
|
||||||
|
```
|
||||||
|
|
||||||
|
Det ska vara den enda komponent som har rätt att skriva facitdatabasen.
|
||||||
|
Runtime-data bör hållas tydligt åtskild från Git-spårad källkod, även om den
|
||||||
|
fysiskt ligger nära projektet.
|
||||||
|
|
||||||
|
### 7.2 Filhanteringsprojekt
|
||||||
|
|
||||||
|
**Rekommendation:** Filhanteringsprojektet ska innehålla matchningskod,
|
||||||
|
`ffprobe`-integration, tester och matchningsdokumentation. Databasens sökväg
|
||||||
|
ska vara konfigurerbar och anslutningen strikt read-only.
|
||||||
|
|
||||||
|
Filhanteringen ska inte skapa alias, historik eller matchningsposter i
|
||||||
|
facitdatabasen. Om beständigt tillstånd senare behövs ska en separat databas
|
||||||
|
ägas av filhanteringsprojektet. Den kan referera till facitets stabila
|
||||||
|
`movie.id`, men får inte använda främmande nycklar som kräver skrivning i eller
|
||||||
|
tät koppling till facitfilen.
|
||||||
|
|
||||||
|
### 7.3 Gräns mellan projekten
|
||||||
|
|
||||||
|
**Rekommendation:** Den initiala gränsen ska vara ett litet dokumenterat
|
||||||
|
SQLite-läskontrakt, inte ett API och inte ett gemensamt Python-bibliotek.
|
||||||
|
|
||||||
|
```text
|
||||||
|
facitprojekt ── skriver ──> facit.sqlite
|
||||||
|
│
|
||||||
|
└── strikt read-only ──> filhanteringsprojekt
|
||||||
|
|
||||||
|
filhanteringsprojekt ── kan senare skriva ──> egen filhanteringsdatabas
|
||||||
|
```
|
||||||
|
|
||||||
|
Facitprojektet ska kunna ändra intern implementation fritt så länge det
|
||||||
|
dokumenterade läskontraktet förblir kompatibelt, eller ändringen samordnas med
|
||||||
|
filhanteringsprojektet.
|
||||||
|
|
||||||
|
## 8. Stegvis migreringsplan
|
||||||
|
|
||||||
|
Varje steg nedan anger om det enbart gäller dokumentation eller struktur, om
|
||||||
|
det kan påverka körbart beteende samt hur det bör verifieras.
|
||||||
|
|
||||||
|
### Steg 1: Dokumentera ansvar och läskontrakt
|
||||||
|
|
||||||
|
**Rekommendation:** Fastställ skriftligt att facitprojektet äger samtliga
|
||||||
|
facittabeller och är ensam skrivare. Dokumentera de tabeller och kolumner som
|
||||||
|
filhanteringen får läsa.
|
||||||
|
|
||||||
|
- Typ: ren dokumentationsändring.
|
||||||
|
- Körbart beteende: ingen påverkan.
|
||||||
|
- Verifiering:
|
||||||
|
- jämför kontraktet med alla `SELECT` i matchningsskripten,
|
||||||
|
- kontrollera att inga skrivande SQL-satser finns i filhanteringsdelen,
|
||||||
|
- granska och godkänn den avsedda ägargränsen.
|
||||||
|
|
||||||
|
### Steg 2: Gör facitschemat reproducerbart
|
||||||
|
|
||||||
|
**Rekommendation:** Uppdatera facitets schemahantering så att en ny databas får
|
||||||
|
alla kolumner som facitflödet behöver, inklusive OCR-kolumnerna.
|
||||||
|
|
||||||
|
- Typ: kod-/schemaändring.
|
||||||
|
- Körbart beteende: kan påverkas; nya databaser och återställningsflöden
|
||||||
|
förändras.
|
||||||
|
- Verifiering:
|
||||||
|
- skapa en ny disposable databas från versionshanterade schemafiler,
|
||||||
|
- jämför tabeller, kolumner, index, begränsningar och standardvärden med den
|
||||||
|
avsedda modellen,
|
||||||
|
- kör syntaxkontroll av Python-skripten,
|
||||||
|
- kör import mot en liten test-JSON,
|
||||||
|
- kör OCR-flödets databasoperationer utan att använda produktionsdatabasen,
|
||||||
|
- kontrollera att `database/wcx.db` är oförändrad.
|
||||||
|
|
||||||
|
### Steg 3: Rätta dokumenterad hantering av genererade filer
|
||||||
|
|
||||||
|
**Rekommendation:** Bestäm om `import/wcx_site_index.json` är runtime-data. Om
|
||||||
|
så är fallet, sluta spåra filen utan att radera den lokala runtime-kopian och
|
||||||
|
anpassa dokumentationen.
|
||||||
|
|
||||||
|
- Typ: Git-struktur och dokumentation.
|
||||||
|
- Körbart beteende: normalt ingen påverkan, men en ny checkout kommer inte
|
||||||
|
längre innehålla en färdig importfil.
|
||||||
|
- Verifiering:
|
||||||
|
- kontrollera `.gitignore` och Git-index,
|
||||||
|
- kör synkskriptet mot en tillfällig utdatafil,
|
||||||
|
- verifiera att uppdateringsflödet skapar importfilen innan import,
|
||||||
|
- verifiera att inga genererade data lagts till i Git.
|
||||||
|
|
||||||
|
### Steg 4: Gör sökvägar flyttbara i nuvarande repository
|
||||||
|
|
||||||
|
**Rekommendation:** Behåll explicita kommandoradsargument för databas och
|
||||||
|
indata. Beräkna interna skriptsökvägar relativt skriptens plats där det är
|
||||||
|
lämpligt. Lägg till konfigurerbar databas till diagnostik som saknar det.
|
||||||
|
Behåll om möjligt nuvarande standardvärden under övergången.
|
||||||
|
|
||||||
|
- Typ: kod- och konfigurationsstruktur.
|
||||||
|
- Körbart beteende: kan påverkas, särskilt standardkörningar och schemalagd
|
||||||
|
drift.
|
||||||
|
- Verifiering:
|
||||||
|
- kör `python3 -m py_compile scripts/*.py`,
|
||||||
|
- kör varje relevant kommando med explicita sökvägar mot disposable data,
|
||||||
|
- kör `scripts/update_wcx.sh --skip-ocr` mot en säker testuppsättning eller
|
||||||
|
testa dess delkommandon separat,
|
||||||
|
- verifiera både nuvarande standardplats och en alternativ projektplats,
|
||||||
|
- inspektera systemd-enheternas verkliga kommandon och sökvägar.
|
||||||
|
|
||||||
|
### Steg 5: Tvinga fram read-only i filhanteringen
|
||||||
|
|
||||||
|
**Rekommendation:** Ändra filhanteringens SQLite-anslutningar till URI-baserat
|
||||||
|
`mode=ro`. Använd inte `immutable=1` för en databas som kan uppdateras medan
|
||||||
|
den läses.
|
||||||
|
|
||||||
|
- Typ: kodändring och säkerhetsförstärkning.
|
||||||
|
- Körbart beteende: kan påverkas; fel sökväg och otillräckliga läsrättigheter
|
||||||
|
ska nu ge tydliga fel i stället för att en ny databas skapas eller öppnas
|
||||||
|
skrivbart.
|
||||||
|
- Verifiering:
|
||||||
|
- kör matchning och längddiagnostik mot en disposable facitdatabas,
|
||||||
|
- kontrollera databasfilens hash och mtime före och efter,
|
||||||
|
- verifiera att läsning fungerar när filen saknar skrivrättighet för
|
||||||
|
processen,
|
||||||
|
- lägg in ett isolerat negativt test som visar att en `INSERT` via samma
|
||||||
|
anslutningssätt nekas,
|
||||||
|
- kontrollera att samtidig normal facituppdatering och läsning hanteras av
|
||||||
|
SQLite utan att använda osäkra filkopior.
|
||||||
|
|
||||||
|
### Steg 6: Separera dokumentation och filstruktur logiskt
|
||||||
|
|
||||||
|
**Rekommendation:** Renodla facitets README och skapa en självständig README
|
||||||
|
för filhanteringen. Ordna filerna enligt den planerade projektgränsen, men
|
||||||
|
behåll dem tillfälligt i samma Git-repository om det förenklar verifieringen.
|
||||||
|
|
||||||
|
- Typ: i första hand dokumentation och struktur.
|
||||||
|
- Körbart beteende: ingen påverkan om endast dokument flyttas; möjliga
|
||||||
|
sökvägseffekter om skript flyttas.
|
||||||
|
- Verifiering:
|
||||||
|
- följ installations- och körinstruktionerna från en ren testkatalog,
|
||||||
|
- sök efter gamla absoluta projektsökvägar,
|
||||||
|
- kontrollera att shellskript hittar sina delskript efter en eventuell flytt,
|
||||||
|
- kontrollera att facit- och filhanteringsdokumentation är självständigt
|
||||||
|
begripliga.
|
||||||
|
|
||||||
|
### Steg 7: Skapa två Git-projekt
|
||||||
|
|
||||||
|
**Rekommendation:** Flytta facit- respektive filhanteringsfiler till separata
|
||||||
|
repositoryn först när schema, konfiguration och read-only-gräns är verifierade.
|
||||||
|
Behåll det gamla repositoryt tills båda nya projekten har körts framgångsrikt.
|
||||||
|
|
||||||
|
Föreslagen facitomfattning:
|
||||||
|
|
||||||
|
```text
|
||||||
|
README.md
|
||||||
|
scripts/schema.sql
|
||||||
|
scripts/wcx_sync.py
|
||||||
|
scripts/import_site.py
|
||||||
|
scripts/migrate_csv.py
|
||||||
|
scripts/process_pending_ocr.py
|
||||||
|
scripts/check_ocr.py
|
||||||
|
scripts/ocr.sh
|
||||||
|
scripts/parse_ocr.py
|
||||||
|
scripts/update_wcx.sh
|
||||||
|
scripts/scheduled_update_wcx.sh
|
||||||
|
migration/
|
||||||
|
```
|
||||||
|
|
||||||
|
Föreslagen filhanteringsomfattning:
|
||||||
|
|
||||||
|
```text
|
||||||
|
README.md
|
||||||
|
scripts/match_filenames.py
|
||||||
|
scripts/test_duration_matching.py
|
||||||
|
docs/filename-matching-requirements.md
|
||||||
|
```
|
||||||
|
|
||||||
|
- Typ: Git- och projektstruktur.
|
||||||
|
- Körbart beteende: kan påverkas genom nya sökvägar, installation och drift.
|
||||||
|
- Verifiering:
|
||||||
|
- kontrollera `git status` och ignore-regler i båda projekten,
|
||||||
|
- kör syntaxkontroll och respektive projekts diagnostik,
|
||||||
|
- kör facitimport mot en disposable databas,
|
||||||
|
- kör filmatchning mot samma databas i read-only-läge,
|
||||||
|
- verifiera att filhanteringen fungerar utan facitprojektets källkod i
|
||||||
|
`PYTHONPATH` eller samma katalog,
|
||||||
|
- kontrollera att inga databaser, API-nycklar eller genererade JSON-filer har
|
||||||
|
följt med i Git.
|
||||||
|
|
||||||
|
### Steg 8: Flytta drift och systemd till facitprojektet
|
||||||
|
|
||||||
|
**Rekommendation:** Uppdatera externa systemd-enheter och runtime-sökvägar
|
||||||
|
först efter en lyckad manuell facitkörning från den nya platsen. Databasen ska
|
||||||
|
inte kopieras medan den skrivs; använd en säker SQLite-backupmetod om en fysisk
|
||||||
|
flytt behövs.
|
||||||
|
|
||||||
|
- Typ: extern konfiguration och driftsättning.
|
||||||
|
- Körbart beteende: direkt påverkan på schemalagd produktion.
|
||||||
|
- Verifiering:
|
||||||
|
- inspektera enhetsfiler och miljövariabler,
|
||||||
|
- kör facituppdateringen manuellt med `--skip-ocr` mot avsedd databas,
|
||||||
|
- kör därefter en kontrollerad OCR-körning om API-nyckel finns,
|
||||||
|
- starta tjänsten manuellt och kontrollera logg, låsfil och tidsstämpel,
|
||||||
|
- verifiera att endast en uppdatering kan köras samtidigt,
|
||||||
|
- kör filhantering som separat läsare efter uppdateringen.
|
||||||
|
|
||||||
|
### Steg 9: Inför separat filhanteringsdatabas först vid konkret behov
|
||||||
|
|
||||||
|
**Rekommendation:** Skapa inte en andra databas som del av den första
|
||||||
|
uppdelningen. Inför den först när lokala filsökvägar, manuella beslut eller
|
||||||
|
bearbetningsstatus behöver beständig lagring.
|
||||||
|
|
||||||
|
- Typ: framtida funktions- och schemaändring i filhanteringsprojektet.
|
||||||
|
- Körbart beteende: påverkar filhantering men inte facitdatabasen.
|
||||||
|
- Verifiering:
|
||||||
|
- testa den nya databasen isolerat,
|
||||||
|
- verifiera att facitanslutningen fortfarande är `mode=ro`,
|
||||||
|
- kontrollera att endast stabilt `movie.id` lagras som extern referens,
|
||||||
|
- verifiera att inga skrivningar riktas till den anslutna facitdatabasen.
|
||||||
|
|
||||||
|
## 9. Rekommenderad ordning och stoppunkter
|
||||||
|
|
||||||
|
**Rekommendation:** Utför steg 1–3 innan sökvägar eller körkod flyttas. Dessa
|
||||||
|
steg etablerar ägarskap och gör facitdatabasen reproducerbar.
|
||||||
|
|
||||||
|
Utför steg 4–5 i det befintliga repositoryt och verifiera oförändrat beteende
|
||||||
|
mot disposable data. Dessa är de viktigaste tekniska förutsättningarna för en
|
||||||
|
säker uppdelning.
|
||||||
|
|
||||||
|
Utför steg 6–8 som separata, granskningsbara förändringar. Lägg inte
|
||||||
|
schemaändring, read-only-förstärkning, filflytt och produktionsdrift i samma
|
||||||
|
ändring, eftersom fel då blir svårare att isolera.
|
||||||
|
|
||||||
|
**Ej fattat beslut:** Exakta namn och installationsplatser för de två nya
|
||||||
|
Git-projekten är inte bestämda.
|
||||||
|
|
||||||
|
**Ej fattat beslut:** Ingen fysisk flytt av produktionsdatabasen bör planeras
|
||||||
|
förrän dess slutliga runtime-plats, backupmetod och filrättigheter är beslutade.
|
||||||
|
|
||||||
|
## 10. Sammanfattande rekommendation
|
||||||
|
|
||||||
|
Den befintliga koden har redan en användbar ansvarslinje: facitverktygen
|
||||||
|
producerar och underhåller SQLite-data, medan filhanteringen konsumerar ett
|
||||||
|
litet urval av denna data. Uppdelningen behöver därför inte börja med ett API
|
||||||
|
eller koddelning.
|
||||||
|
|
||||||
|
Den säkra vägen är att först göra facitschemat komplett och reproducerbart,
|
||||||
|
dokumentera det lilla läskontraktet, göra sökvägar konfigurerbara och tvinga
|
||||||
|
filhanteringens anslutning till read-only. Därefter kan filerna delas mellan
|
||||||
|
två Git-projekt med betydligt lägre risk. En separat filhanteringsdatabas bör
|
||||||
|
vänta tills beständigt lokalt tillstånd faktiskt behövs.
|
||||||
Reference in New Issue
Block a user