Files
Tagger/PROJECT_NOTES.md

20 KiB

📝 Tagger - Centrální Poznámky Projektu

DŮLEŽITÉ: Tento soubor obsahuje VŠE co potřebuji vědět o projektu. Pokud pracuji na Tagger, VŽDY nejdříve přečtu tento soubor!

Poslední aktualizace: 2025-12-23 Verze: 1.0.2 Status: Stable, v aktivním vývoji


🎯 O projektu

Tagger je desktopová aplikace pro správu a organizaci souborů pomocí hierarchických tagů (štítků).

Hlavní funkce:

  • Rekurzivní procházení složek
  • Hierarchické tagy (kategorie/název)
  • Filtrování podle tagů
  • Metadata uložená v JSON souborech
  • Automatická detekce rozlišení videí (ffprobe)
  • Dvě verze GUI: klasické a moderní (qBittorrent-style)

📁 Struktura projektu

Tagger/
├── Tagger.py                    # Entry point - klasické GUI
├── Tagger_modern.py             # Entry point - moderní GUI
├── PROJECT_NOTES.md             # ← TENTO SOUBOR - HLAVNÍ ZDROJ PRAVDY
├── pyproject.toml               # Poetry konfigurace
├── poetry.lock                  # Zamčené verze závislostí
├── pytest.ini                   # Pytest konfigurace
├── .editorconfig                # Editor konfigurace
├── .gitignore                   # Git ignore pravidla
│
├── src/
│   ├── core/                    # Jádro aplikace (ŽÁDNÉ UI!)
│   │   ├── tag.py               # Tag value object (immutable)
│   │   ├── tag_manager.py       # Správa tagů a kategorií
│   │   ├── file.py              # File s metadaty
│   │   ├── file_manager.py      # Správa souborů, filtrování
│   │   ├── config.py            # Konfigurace (JSON)
│   │   ├── utils.py             # list_files() - rekurzivní procházení
│   │   ├── media_utils.py       # load_icon(), ffprobe
│   │   ├── constants.py         # APP_NAME, VERSION, APP_VIEWPORT
│   │   └── list_manager.py      # Třídění (málo používaný)
│   │
│   └── ui/
│       ├── gui.py               # Původní Tkinter GUI
│       ├── gui_modern.py        # Moderní qBittorrent-style GUI ✨ NOVÉ
│       └── gui_old.py           # Backup původního GUI
│
├── tests/                       # 116 testů, 100% core coverage
│   ├── __init__.py
│   ├── conftest.py              # Pytest fixtures
│   ├── test_tag.py              # 13 testů
│   ├── test_tag_manager.py      # 19 testů
│   ├── test_file.py             # 22 testů
│   ├── test_file_manager.py     # 22 testů
│   ├── test_utils.py            # 17 testů
│   ├── test_config.py           # 18 testů
│   ├── test_media_utils.py      # 3 testy
│   └── README.md                # Dokumentace testů
│
├── src/resources/
│   └── images/32/               # Ikony (16x16 PNG)
│       ├── 32_unchecked.png
│       ├── 32_checked.png
│       └── 32_tag.png
│
└── docs/                        # Dokumentace (ZASTARALÁ - použij tento soubor!)
    ├── ARCHITECTURE.md          # ⚠️ DEPRECATED - info je zde
    ├── CONTRIBUTING.md          # ⚠️ DEPRECATED - info je zde
    └── GUI_MODERN_README.md     # ⚠️ DEPRECATED - info je zde

🎨 Architektura

Vrstvová struktura

┌─────────────────────────────────┐
│  Presentation (UI)              │  ← gui.py, gui_modern.py
│  - Tkinter GUI                  │  - NESMÍ obsahovat business logiku
│  - Jen zobrazení + interakce   │  - NESMÍ importovat přímo z core
├─────────────────────────────────┤
│  Business Logic                 │  ← FileManager, TagManager
│  - Správa souborů/tagů          │  - Callable z UI
│  - Filtrování, validace         │  - Callback pattern pro notifikace
├─────────────────────────────────┤
│  Data Layer                     │  ← File, Tag (models)
│  - File, Tag třídy              │  - Immutable kde je možné
│  - Validation logic             │  - __eq__ a __hash__ správně
├─────────────────────────────────┤
│  Persistence                    │  ← config.py, .!tag soubory
│  - JSON soubory                 │  - UTF-8 encoding VŽDY
│  - Config management            │  - ensure_ascii=False
└─────────────────────────────────┘

Klíčová pravidla

CO DĚLAT:

  1. UI NESMÍ obsahovat business logiku

    # ❌ ŠPATNĚ
    class GUI:
        def save_file(self):
            with open(file, 'w') as f:
                json.dump(data, f)
    
    # ✅ SPRÁVNĚ
    class GUI:
        def save_file(self):
            self.filemanager.save_file(file)
    
  2. Core moduly NESMÍ importovat UI

    # V src/core/*.py NIKDY:
    import tkinter
    from src.ui import anything
    
  3. Dependency Injection - předávat dependencies přes konstruktor

    class FileManager:
        def __init__(self, tagmanager: TagManager):
            self.tagmanager = tagmanager
    
  4. UTF-8 encoding VŠUDE

    with open(file, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
    
  5. Type hints VŽDY

    def filter_files(files: List[File], tags: List[Tag]) -> List[File]:
        pass
    

CO NEDĚLAT:

  1. Globální stav

    # ❌ NIKDY
    current_file = None  # global
    
  2. Magic numbers

    # ❌ ŠPATNĚ
    if len(files) > 100:
    
    # ✅ SPRÁVNĚ
    MAX_FILES = 100
    if len(files) > MAX_FILES:
    
  3. Ignorovat exceptions

    # ❌ NIKDY
    try:
        operation()
    except:
        pass
    
  4. Hardcoded paths

    # ❌ ŠPATNĚ
    icon = "/home/user/icon.png"
    
    # ✅ SPRÁVNĚ
    ICON_DIR = Path(__file__).parent / "resources"
    icon = ICON_DIR / "icon.png"
    

🔑 Klíčové komponenty

1. Tag (immutable value object)

class Tag:
    def __init__(self, category: str, name: str):
        self.category = category  # Nemění se po vytvoření!
        self.name = name

    @property
    def full_path(self) -> str:
        return f"{self.category}/{self.name}"

    def __eq__(self, other):
        return (self.category, self.name) == (other.category, other.name)

    def __hash__(self):
        return hash((self.category, self.name))

Proč immutable?

  • Lze použít jako klíč v dict/set
  • Thread-safe
  • Jasná sémantika rovnosti

2. File (reprezentace souboru s metadaty)

class File:
    def __init__(self, file_path: Path, tagmanager=None):
        self.file_path = file_path
        self.filename = file_path.name
        self.metadata_filename = parent / f".{filename}.!tag"
        self.tags: list[Tag] = []
        self.date: str | None = None
        self.get_metadata()  # Auto-load při vytvoření

Metadata format (.filename.!tag):

{
  "new": false,
  "ignored": false,
  "tags": ["Stav/Nové", "Video/HD"],
  "date": "2025-12-23"
}

DŮLEŽITÉ:

  • Každá změna (add_tag, set_date) automaticky volá save_metadata()
  • UTF-8 encoding!
  • ensure_ascii=False pro češtinu

3. TagManager (správa tagů)

class TagManager:
    def __init__(self):
        self.tags_by_category = {}  # {category: set(Tag)}

    def add_tag(self, category: str, name: str) -> Tag:
        # Vytvoří kategorii pokud neexistuje
        # Používá set - duplicity automaticky ignorovány
        # Vrací Tag objekt

Speciální chování:

  • Když odstraníš poslední tag z kategorie → kategorie se smaže
  • Set zajišťuje uniqueness
  • Vždy vrací Tag objekt (ne string)

4. FileManager (správa souborů)

class FileManager:
    def __init__(self, tagmanager: TagManager):
        self.filelist: list[File] = []
        self.tagmanager = tagmanager
        self.on_files_changed = None  # CALLBACK pro UI!
        self.config = load_config()

    def append(self, folder: Path):
        # Rekurzivně načte soubory
        # Ignoruje podle patterns
        # Vytvoří File objekty
        # Zavolá on_files_changed callback

Callback pattern:

# V GUI:
filemanager.on_files_changed = self.update_ui

# V FileManager:
if self.on_files_changed:
    self.on_files_changed(self.filelist)

Proč callback?

  • Core nezávisí na UI
  • Jednoduché na testování
  • Flexibilní (můžeš změnit UI bez změny core)

🎨 GUI Verze

Klasické GUI (gui.py)

┌─────────────────────────────────────────┐
│ Soubor │ Pohled │ Funkce              Menu
├──────────┬──────────────────────────────┤
│          │ [Filter____] [Name][Name][ASC]
│ Tree     │ ┌──────────────────────────┐ │
│ (tagy)   │ │ Listbox (soubory)        │ │
│  📂 Štítky│ │ - file1.txt — 2025-01-01│ │
│   ☑ Nové │ │ - file2.mp4              │ │
│   ☐ HD   │ │ - file3.jpg              │ │
│          │ └──────────────────────────┘ │
├──────────┴──────────────────────────────┤
│ Status: Připraven                        │
└─────────────────────────────────────────┘

Použít: poetry run python Tagger.py

Moderní GUI (gui_modern.py) NOVÉ

┌─────────────────────────────────────────────────────┐
│ 📁 Otevřít │ 🔄 │ 🏷️ │ 📅     🔍 [____]  Toolbar
├────────────┬────────────────────────────────────────┤
│ 📂 Štítky  │ ☐ Plná │ Třídění: [Název] [▲]         │
│  ├─📁 Stav │ ┌──────────────────────────────────┐  │
│  │ ☑ Nové  │ │ Název│Datum│Štítky│Velikost    │  │
│  │ ☐ OK    │ │file1 │2025 │HD    │1.2 MB      │  │
│  ├─📁 Video│ │file2 │     │4K    │15 MB       │  │
│  │ ☐ HD    │ └──────────────────────────────────┘  │
│  │ ☐ 4K    │                                       │
├────────────┴───────────────────────────────────────┤
│ Připraven              3 vybráno │ 125 souborů     │
└─────────────────────────────────────────────────────┘

Použít: poetry run python Tagger_modern.py

Nové funkce:

  • 📋 Tabulka s 4 sloupci (Název, Datum, Štítky, Velikost)
  • 🔧 Toolbar s tlačítky
  • ⌨️ Keyboard shortcuts (Ctrl+O, Ctrl+T, F5, Del...)
  • 📊 Status bar se 3 sekcemi
  • 🎨 qBittorrent-inspired design

Keyboard shortcuts:

  • Ctrl+O - Otevřít složku
  • Ctrl+Q - Ukončit
  • Ctrl+T - Přiřadit tagy
  • Ctrl+D - Nastavit datum
  • Ctrl+F - Focus search
  • F5 - Refresh
  • Del - Smazat z indexu

🔧 Vývoj

Setup prostředí

# Poetry environment (VŽDY použij poetry!)
poetry install
poetry shell

# Nebo přímo:
poetry run python Tagger_modern.py

Poetry environment path:

/home/honza/.cache/pypoetry/virtualenvs/tagger-qKyHMOtL-py3.12

Spuštění aplikace

# Moderní GUI (doporučeno)
poetry run python Tagger_modern.py

# Klasické GUI
poetry run python Tagger.py

Testy

# Všechny testy (116 testů)
poetry run pytest tests/ -v

# S coverage
poetry run pytest tests/ --cov=src/core --cov-report=html

# Konkrétní modul
poetry run pytest tests/test_file.py -v

# Quick check
poetry run pytest tests/ -q

Test coverage: 100% core modulů

Linting & Formatting

# TODO: Přidat black, flake8 do pyproject.toml
# Zatím manuální kontrola podle PEP 8

📝 Coding Standards

Python Style

  • PEP 8 s výjimkami:
    • Max line length: 120 (ne 79)
    • Indentation: 4 mezery (ne taby)
  • UTF-8 encoding všude
  • Type hints povinné
  • Docstrings pro public API

Naming Conventions

# Classes
class FileManager:
    pass

# Functions/methods
def load_config():
    pass

# Constants
APP_NAME = "Tagger"
MAX_FILES = 1000

# Private
def _internal_method():
    pass

Imports Order

# 1. Standard library
import os
import sys
from pathlib import Path

# 2. Third-party
import tkinter as tk
from PIL import Image

# 3. Local
from src.core.file import File
from src.core.tag import Tag

String Formatting

# ✅ F-strings
name = "John"
msg = f"Hello, {name}!"

# ❌ NE
msg = "Hello, " + name
msg = "Hello, {}".format(name)

🔀 Git Workflow

Branches

main/master    ← Production (NE commity přímo!)
  ↑
release        ← Release candidate
  ↑
devel          ← Development integration
  ↑
feature/*      ← Feature branches ← VYVÍJÍME TADY

Commit Messages

<type>: <subject>

[optional body]

🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Types:

  • feat: - Nová funkce
  • fix: - Bug fix
  • refactor: - Refactoring
  • test: - Testy
  • docs: - Dokumentace
  • style: - Formátování
  • chore: - Build, dependencies

Příklad:

git commit -m "feat: Add modern qBittorrent-style GUI

Implemented new GUI with toolbar, table view,
and keyboard shortcuts.

🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"

🎯 Design Decisions (ADR)

ADR-001: JSON soubory místo databáze

Rozhodnutí: Metadata v .filename.!tag JSON souborech

Proč:

  • Jednoduchý backup (copy složky)
  • Git-friendly
  • Portable
  • Metadata zůstanou při přesunu souboru

Kdy přehodnotit:

  • Pokud >10k souborů (zvážit SQLite)

ADR-002: Callback pattern pro UI updates

Rozhodnutí: on_files_changed callback

Proč:

  • Core nezávisí na UI
  • Jednoduché testování
  • Flexibilní

Alternativy zamítnuté:

  • Observer pattern (overkill)
  • Event system (složitější)

ADR-003: Tkinter pro GUI

Rozhodnutí: Tkinter (standard library)

Proč:

  • Žádné extra dependencies
  • Cross-platform
  • Dobře dokumentované

Alternativy:

  • Qt - lepší UI, ale větší závislost
  • Web - overkill pro desktop app

ADR-004: Poetry pro dependencies

Rozhodnutí: Poetry místo pip

Proč:

  • Deterministické buildy (poetry.lock)
  • Dev dependencies oddělené
  • Moderní tool

🐛 Známé problémy & TODO

Aktuální problémy

  1. Git merge konflikty

    • poetry.lock a pyproject.toml - konflikty při merge devel→feature
    • tests/test_image.py - deleted in devel, modified in feature
    • Stav: Nezresolváno ⚠️
  2. ListManager málo použitý

    • Třídící logika duplicitní v GUI
    • TODO: Refactor nebo odstranit
  3. Dlouhé operace blokují UI

    • ffprobe detection běží v main threadu
    • TODO: Threading pro dlouhé operace

Plánované features

  • Progress bar pro dlouhé operace
  • Undo/Redo mechanismus
  • Export do CSV/Excel
  • Dark mode theme
  • Drag & drop souborů
  • Image preview v sidebar
  • SQLite fallback pro >10k souborů
  • Full-text search

Nice to have

  • Plugin systém
  • Web interface (Flask)
  • Cloud sync (Dropbox, GDrive)
  • Batch rename podle tagů
  • Smart folder suggestions

📊 Metriky projektu

Řádky kódu: ~1060 Python LOC Testy: 116 (všechny ) Test coverage: 100% core modulů Python verze: 3.12 Dependencies: Pillow (PIL) Vývojové prostředí: Poetry

Performance:

  • Dobré: <1000 souborů
  • ⚠️ Přijatelné: 1000-5000 souborů
  • Pomalé: >5000 souborů

🔍 Debugování

Časté problémy

1. "Cannot import ImageTk"

# Řešení: Použij poetry environment
poetry run python Tagger_modern.py

2. "Config file not found"

# Normální při prvním spuštění
# Vytvoří se automaticky config.json

3. "Metadata corrupted"

# V config.py je graceful degradation
# Vrátí default config při chybě

Logování

# Zatím jen print() statements
# TODO: Přidat logging module

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

📚 Dokumentace

AKTUÁLNÍ:

  • PROJECT_NOTES.md - TENTO SOUBOR (single source of truth)
  • Docstrings v kódu

📝 Poznámka:

  • Všechny ostatní .md soubory byly smazány a skonsolidovány SEM
  • .gitignore ignoruje všechny .md kromě PROJECT_NOTES.md
  • Pokud vytvoříš nový .md, MUSÍŠ ho přidat do .gitignore whitelist

💡 Pro AI asistenty (jako Claude)

Když začínám práci na projektu:

  1. PŘEČTI TENTO SOUBOR CELÝ!
  2. Zkontroluj git status
  3. Aktivuj poetry environment
  4. Spusť testy (poetry run pytest tests/)
  5. Dodržuj pravidla výše

Při commitování:

  1. Testy prošly (pytest tests/)
  2. Type hints přidány
  3. UTF-8 encoding
  4. Žádné TODO/FIXME
  5. Commit message formát správný

Při přidání nové funkce:

  1. Testy napsány PŘED implementací (TDD)
  2. Dokumentace aktualizována (TENTO SOUBOR!)
  3. Architecture decision zdokumentováno (pokud významné)
  4. Type hints všude
  5. Error handling přidán

Při refactoringu:

  1. Testy před (měly by projít)
  2. Refactor
  3. Testy po (měly by stále projít)
  4. Update dokumentace

📞 Kontakt & Help

Autor: honza Repository: /home/honza/Dokumenty/Tagger Python: 3.12 OS: Linux 6.14.0-37-generic

Pro pomoc:

  • Přečti TENTO soubor
  • Podívej se do testů (tests/)
  • Zkontroluj docstrings v kódu
  • V nouzi spusť: poetry run python -i a explorej objekty

📅 Changelog

[Unreleased]

  • Merge konflikty s devel branch (poetry.lock, test_image.py)

[1.0.2] - 2025-12-23

  • Přidáno moderní GUI (gui_modern.py)
  • Keyboard shortcuts
  • Tabulkové zobrazení s 4 sloupci
  • Toolbar s tlačítky
  • 116 testů (100% core coverage)
  • 📝 Vytvoření PROJECT_NOTES.md (tento soubor)
  • 🔧 Poetry setup

[1.0.1] - 2025-10-05

  • 🐛 Bug fixy
  • Video resolution detection

[1.0.0] - 2025-10-05

  • 🎉 Initial release
  • Základní funkcionalita
  • Tkinter GUI
  • JSON metadata

🎉 Poznámky na závěr

Tento soubor je SINGLE SOURCE OF TRUTH pro projekt Tagger.

Když přidávám funkci, fixuju bug, nebo dělám změnu:

  1. Nejdřív PŘEČTU tento soubor
  2. Pak UPRAVÍM kód
  3. Pak AKTUALIZUJU tento soubor

Living document - průběžně aktualizován!


Last updated: 2025-12-23 18:30 Next review: Při každé větší změně Maintainer: Claude Sonnet 4.5 + honza


📋 Changelog dokumentace

2025-12-23 11:24 - Konsolidace dokumentace

  • Smazány: CONTRIBUTING.md, GUI_MODERN_README.md, docs/ARCHITECTURE.md
  • Vše skonsolidováno do PROJECT_NOTES.md
  • Vytvořen README.md pro GitHub (základní intro)
  • Aktualizován .gitignore (ignoruje všechny .md kromě PROJECT_NOTES.md a README.md)
  • PROJECT_NOTES.md je nyní jediný zdroj pravdy pro dokumentaci!