# Databázové schéma — fotky_buzalkovi

PostgreSQL 192.168.1.76:5432, databáze `fotky_buzalkovi`.

---

## Skupina 1: Zpracované fotky

Tyto tabulky obsahují naparsované informace o fotkách — EXIF, hashe, metadata, tagy.
Jsou základem pro veškerou další práci (vyhledávání, deduplikace, organizace).

### photos (vyprázdněno 2026-05-26, naplňuje se z `zaloha_obrazku`)

Hlavní tabulka. Každý řádek = jedna unikátní fotka identifikovaná hashem `sha256_file`.

| Sloupec | Typ | Nullable | Default | Popis |
|---------|-----|----------|---------|-------|
| **id** | BIGSERIAL | NO | autoincrement | PK |
| zaloha_id | INTEGER | YES | — | FK → zaloha_obrazku(id) ON DELETE SET NULL (propojení s nasbíranou zálohou) |
| **sha256_file** | CHAR(64) | NO | — | SHA-256 celého souboru (UNIQUE) |
| sha256_pixels | CHAR(64) | YES | — | SHA-256 pixelových dat (odhalí změnu jen v metadatech) |
| phash | BIGINT | YES | — | Perceptuální hash (vizuální podobnost) |
| dhash | BIGINT | YES | — | Difference hash (vizuální podobnost) |
| **file_path** | VARCHAR(2000) | NO | — | Absolutní cesta k souboru |
| file_path_relative | VARCHAR(2000) | YES | — | Relativní cesta |
| **file_name** | VARCHAR(500) | NO | — | Název souboru |
| file_stem | VARCHAR(500) | YES | — | Název bez přípony |
| file_ext | VARCHAR(20) | YES | — | Přípona (.jpg, .png, …) |
| file_size | BIGINT | YES | — | Velikost v bajtech |
| mime_type | VARCHAR(50) | YES | — | MIME typ (image/jpeg, …) |
| format | VARCHAR(20) | YES | — | Formát obrázku (JPEG, PNG, …) |
| mode | VARCHAR(20) | YES | — | Barevný mód (RGB, L, RGBA, …) |
| width | INT | YES | — | Šířka v pixelech |
| height | INT | YES | — | Výška v pixelech |
| megapixels | NUMERIC | YES | — | Rozlišení v megapixelech |
| has_transparency | BOOLEAN | YES | false | Má alfa kanál |
| icc_profile | BOOLEAN | YES | false | Obsahuje ICC profil |
| embedded_thumbnail | BOOLEAN | YES | false | Obsahuje vložený náhled |
| taken_at | TIMESTAMPTZ | YES | — | Datum pořízení fotky |
| taken_at_source | VARCHAR(20) | YES | — | Zdroj datumu (exif / mtime / …) |
| mtime | TIMESTAMPTZ | YES | — | Datum poslední modifikace souboru |
| collected_at | TIMESTAMPTZ | YES | — | Datum sběru/importu do pipeline |
| camera_make | VARCHAR(100) | YES | — | Výrobce fotoaparátu |
| camera_model | VARCHAR(255) | YES | — | Model fotoaparátu |
| lens_model | VARCHAR(255) | YES | — | Model objektivu |
| iso | INT | YES | — | ISO citlivost |
| aperture | NUMERIC | YES | — | Clona (f/2.8, …) |
| exposure_time | VARCHAR(30) | YES | — | Expoziční čas (1/250, …) |
| focal_length_mm | NUMERIC | YES | — | Ohnisková vzdálenost v mm |
| gps_lat | NUMERIC | YES | — | GPS šířka |
| gps_lon | NUMERIC | YES | — | GPS délka |
| gps_altitude | NUMERIC | YES | — | GPS nadmořská výška |
| is_screenshot | BOOLEAN | YES | false | Detekováno jako screenshot |
| face_count | INT | YES | — | Počet detekovaných obličejů |
| exif_raw | JSONB | YES | — | Kompletní surová EXIF data |
| iptc_raw | JSONB | YES | — | Kompletní surová IPTC data |
| xmp_raw | JSONB | YES | — | Kompletní surová XMP data |
| imported_at | TIMESTAMPTZ | YES | now() | Kdy byl záznam vložen do DB |
| processing_status | VARCHAR(50) | YES | 'pending' | Stav zpracování |

**Indexy:**
- `photos_pkey` — PK (id)
- `photos_sha256_file_key` — UNIQUE (sha256_file)
- `idx_photos_sha256_pixels` — (sha256_pixels)
- `idx_photos_phash` — (phash)
- `idx_photos_taken_at` — (taken_at)
- `idx_photos_camera_model` — (camera_model)
- `idx_photos_file_name` — (file_name)
- `idx_photos_file_ext` — (file_ext)
- `idx_photos_exif_gin` — GIN (exif_raw)
- `idx_photos_zaloha_id` — (zaloha_id) — pro rychlý resume při importu

---

### tags

Hierarchická tabulka tagů. Podporuje stromovou strukturu přes `parent_tag_id`.

| Sloupec | Typ | Nullable | Default | Popis |
|---------|-----|----------|---------|-------|
| **id** | SERIAL | NO | autoincrement | PK |
| **name** | VARCHAR(100) | NO | — | Název tagu |
| parent_tag_id | INT | YES | — | Rodičovský tag (FK → tags.id) |

**Constrainty:**
- PK (id)
- UNIQUE (name, parent_tag_id) — stejný název může existovat pod různými rodiči
- FK parent_tag_id → tags(id)

---

### photo_tags

Vazební tabulka M:N mezi `photos` a `tags`.

| Sloupec | Typ | Nullable | Default | Popis |
|---------|-----|----------|---------|-------|
| **photo_id** | BIGINT | NO | — | FK → photos(id) ON DELETE CASCADE |
| **tag_id** | INT | NO | — | FK → tags(id) ON DELETE CASCADE |
| source | VARCHAR(20) | YES | — | Zdroj tagu (manual / auto / …) |
| created_at | TIMESTAMPTZ | YES | now() | Kdy byl tag přiřazen |

**Constrainty:**
- PK (photo_id, tag_id)
- FK photo_id → photos(id) ON DELETE CASCADE
- FK tag_id → tags(id) ON DELETE CASCADE

---

## Skupina 2: Sběr a záloha fotek

Tyto tabulky slouží **výhradně** pro proces sběru fotek ze všech počítačů na jedno centrální
úložiště (Tower1). Neobsahují žádné informace o obsahu fotek — jen evidenci, odkud byly
soubory sebrány a kam byly zálohovány. S dalším zpracováním (EXIF, tagy, organizace) nemají
nic společného.

Skript: `00 PictureCollector/collect_pictures.py` (Linux) / `collect_pictures_windows.py` (Windows)

### zaloha_obrazku (39 961 záznamů)

Každý řádek = jeden unikátní soubor fyzicky uložený v záloze (identifikovaný BLAKE3 hashem).

| Sloupec | Typ | Nullable | Default | Popis |
|---------|-----|----------|---------|-------|
| **id** | SERIAL | NO | autoincrement | PK |
| **blake3_hash** | VARCHAR(64) | NO | — | BLAKE3 hash souboru (UNIQUE) |
| **cesta_zalohy** | TEXT | NO | — | Cesta k záloze na Tower1 |
| **nazev_souboru** | VARCHAR(512) | NO | — | Název souboru |
| velikost | BIGINT | YES | — | Velikost v bajtech |
| datum_kopirovani | TIMESTAMP | YES | now() | Kdy byl soubor zkopírován |

**Indexy:**
- `zaloha_obrazku_pkey` — PK (id)
- `zaloha_obrazku_blake3_hash_key` — UNIQUE (blake3_hash)
- `idx_zaloha_hash` — (blake3_hash)

---

### zdrojove_soubory (45 090 záznamů)

Každý řádek = jeden nalezený zdrojový soubor na nějakém počítači. Stejný soubor (stejný
BLAKE3 hash) může mít více záznamů, pokud existuje na různých místech/počítačích.

| Sloupec | Typ | Nullable | Default | Popis |
|---------|-----|----------|---------|-------|
| **id** | SERIAL | NO | autoincrement | PK |
| **hostname** | VARCHAR(255) | NO | — | Název počítače, kde byl soubor nalezen |
| **cesta_zdroje** | TEXT | NO | — | Původní cesta k souboru |
| **nazev_souboru** | VARCHAR(512) | NO | — | Název souboru |
| velikost | BIGINT | YES | — | Velikost v bajtech |
| datum_nalezeni | TIMESTAMP | YES | now() | Kdy byl soubor nalezen |
| **blake3_hash** | VARCHAR(64) | NO | — | BLAKE3 hash souboru |
| zaloha_id | INT | YES | — | FK → zaloha_obrazku(id) |

**Constrainty:**
- PK (id)
- UNIQUE (hostname, cesta_zdroje) — každý soubor z každého PC jen jednou
- FK zaloha_id → zaloha_obrazku(id)

**Indexy:**
- `idx_zdroj_hash` — (blake3_hash)
- `idx_zdroj_zaloha` — (zaloha_id)
- `idx_zdroj_host` — (hostname)

---

## Poznámky

- Počty záznamů jsou k datu 2026-05-24.
- Tabulka `cameras` z původního `create_schema.py` v DB neexistuje — informace o kameře
  jsou přímo ve sloupcích `camera_make` / `camera_model` v tabulce `photos`.
- EXIF parser: ExifRead (Pillow má bug v GPS).
- Tabulky skupiny 1 a skupiny 2 jsou propojené přes `photos.zaloha_id` → `zaloha_obrazku.id` (přidáno 2026-05-26).
  Skript `30 SběrDat/collect_and_import.py` čte záznamy z `zaloha_obrazku`, zpracuje metadata
  a uloží do `photos` včetně FK na zdrojovou zálohu.
