fonzarely/regine-photos-archiverpublic Fork
⑂ main
Commits
⬇ Clone ▾
git clone https://git.rickub.com/fonzarely/regine-photos-archiver.git
git clone ssh://git@rickub.com/fonzarely/regine-photos-archiver.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
implemente e849fcb · on main · Fabien Champigny · Sep 19, 2026
tasks.md · 167 lines · 11.0 KBmarkdown
Blame HistoryOpen raw

description: "Task list for feature implementation"

Tasks: Profil de boîtiers optionnel, déclaré à la demande

Input: Design documents from /specs/002-profil-boitiers-optionnel/
Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md

Tests: Incluses — plan.md § Project Structure nomme explicitement les fichiers de test attendus, ce qui vaut demande explicite au sens des règles de génération.

Organisation: Tâches groupées par user story (spec.md) pour une implémentation et une validation indépendantes de chacune.

Dépendances inter-specs

Dépendance Fournit État
specs/004-categorisation-dossiers/tasks.md Squelette du monorepo (packages/regine-core), regine_core.config.db.open_context_db (base de contexte centralisée) tasks.md existe — à exécuter avant la Phase 1 si ce n'est pas déjà fait

Aucune autre dépendance externe : exiftool est un binaire système à installer sur le poste (pas une tâche de ce fichier).

Phase 1: Setup

  • T001 Vérifier que packages/regine-core/src/regine_core/config/db.py existe déjà (créé par specs/004-categorisation-dossiers/tasks.md, T009) ; si absent, exécuter d'abord ce fichier de tâches
  • T002 [P] Créer packages/regine-core/src/regine_core/metadata/__init__.py et packages/regine-core/src/regine_core/metadata/exif.py (squelette)
  • T003 [P] Créer packages/regine-core/src/regine_core/camera_profile/__init__.py
  • T004 [P] Vérifier que exiftool est disponible sur le poste de développement (exiftool -ver) — exiftool 13.55 présent, aucune documentation supplémentaire nécessaire

Phase 2: Foundational (Blocking Prerequisites)

Purpose: Infrastructure partagée requise par les trois user stories.

⚠️ CRITICAL: Aucune user story ne peut être implémentée avant la fin de cette phase.

  • T005 Implémenter le wrapper de processus exiftool -stay_open (ouverture, envoi de commandes, fermeture propre) dans packages/regine-core/src/regine_core/metadata/exif.py (cf. research.md § 1)
  • T006 Implémenter read_camera_tags(chemin: Path) -> CameraTags dans metadata/exif.py (lit Model/BodySerialNumber, retourne None si absent/inexploitable)
  • T007 [P] Test unitaire de read_camera_tags dans packages/regine-core/tests/unit/test_exif_reader.py (modèle présent, numéro de série absent, métadonnées effacées) — validé avec de vrais appels exiftool sur fixture JPEG
  • T008 Ajouter la table boitiers (id, modele, numero_serie nullable, nom_lisible nullable, premiere_rencontre, source) au schéma de packages/regine-core/src/regine_core/config/db.py (dépend de T001 ; unicité sur (modele, numero_serie) cf. data-model.md) — implémentée dans camera_profile/db.py (ensure_schema), cohérent avec le placement décidé en research.md § 2 (le fichier reste ouvert via config.db.open_context_db)
  • T009 [P] Créer packages/regine-core/src/regine_core/camera_profile/db.py : get_or_create_boitier(modele, numero_serie=None) -> int, list_boitiers() -> list[Boitier], rename_boitier(id, nom) -> None (dépend de T008)

Checkpoint: Fondations prêtes — les phases User Story peuvent commencer.


Phase 3: User Story 1 - Premier import sans configuration préalable (Priority: P1) 🎯 MVP

Goal: Garantir qu'aucun profil de boîtiers déclaré n'est requis avant un import, et que la désambiguïsation par défaut (sans collision) ne pose aucune question (FR-001/002/008).

Independent Test: Lire les tags de fichiers de modèles différents sans jamais appeler resolve_collision (puisqu'aucune collision n'existe) ; vérifier qu'aucune entrée boitiers n'est créée tant qu'aucune collision réelle ne survient.

Tests for User Story 1

  • T010 [P] [US1] Test unitaire : la détection d'un modèle jamais rencontré, sans collision, ne crée aucune entrée dans boitiers et ne lève aucune exception, dans packages/regine-core/tests/unit/test_resolve_collision.py

Implementation for User Story 1

  • T011 [US1] Documenter (docstring) dans packages/regine-core/src/regine_core/camera_profile/resolve.py le contrat d'appel de resolve_collision : ne DOIT être invoquée par l'appelant (futur specs/001-import-photos) que pour des fichiers déjà identifiés en collision réelle par somme de contrôle (FR-003/FR-008) — aucune détection de collision n'est de la responsabilité de ce module

Checkpoint: User Story 1 fonctionnelle et testable indépendamment (MVP — garantit l'absence de friction par défaut).


Phase 4: User Story 2 - Résolution ponctuelle d'une collision réelle entre boîtiers (Priority: P2)

Goal: Distinguer automatiquement deux fichiers en collision par leur tag de modèle, et réutiliser une résolution déjà établie (FR-002/003/006).

Independent Test: Deux fichiers de même nom d'origine, modèles EXIF différents → résolus automatiquement sans question ; un fichier ultérieur du même modèle/numéro de série réutilise le même boitiers.id.

Tests for User Story 2

  • T012 [P] [US2] Test unitaire : resolve_collision avec deux fichiers de modèles différents → a_etiqueter vide, id distincts, dans test_resolve_collision.py
  • T013 [P] [US2] Test unitaire : réutilisation d'un boitiers.id déjà créé pour un modèle/numéro de série déjà rencontré, dans test_resolve_collision.py
  • T014 [US2] Test d'intégration : persistance d'une résolution entre deux appels successifs (simulant deux imports), dans packages/regine-core/tests/integration/test_boitier_persistence.py

Implementation for User Story 2

  • T015 [US2] Implémenter resolve_collision(fichiers: list[Path]) -> CollisionResolution dans packages/regine-core/src/regine_core/camera_profile/resolve.py — regroupement par modele, résolution automatique quand les modèles diffèrent (dépend de T006, T009)
  • T016 [US2] Implémenter la réutilisation automatique d'un boitiers.id déjà connu (même modele/numero_serie) sans nouvelle question, dans resolve.py (FR-006)

Checkpoint: User Stories 1 ET 2 fonctionnelles indépendamment.


Phase 5: User Story 3 - Repli sur numéro de série pour deux boîtiers identiques (Priority: P3)

Goal: Distinguer deux fichiers du même modèle par leur numéro de série ; à défaut, demander un étiquetage manuel (FR-004/005/007).

Independent Test: Collision entre deux fichiers de même modèle, numéros de série différents et exploitables → résolution automatique ; numéros de série absents/identiques → groupe renvoyé dans a_etiqueter, résolu ensuite par assign_manual_source.

Tests for User Story 3

  • T017 [P] [US3] Test unitaire : resolve_collision avec même modèle, numéros de série différents et exploitables → résolution automatique, dans test_resolve_collision.py
  • T018 [P] [US3] Test unitaire : même modèle, numéro de série absent/identique/non exploitable → groupe dans a_etiqueter, jamais résolu arbitrairement, dans test_resolve_collision.py — bug trouvé et corrigé en cours d'implémentation : les fichiers indistincts partageant le même modèle doivent former un seul groupe, pas un groupe par fichier
  • T019 [US3] Test unitaire de assign_manual_source (crée une nouvelle entrée si boitier_id=None, réutilise sinon) dans test_resolve_collision.py
  • T020 [US3] Test unitaire de list_boitiers/rename_boitier (consultation et modification à tout moment, FR-007) dans packages/regine-core/tests/unit/test_camera_profile_db.py

Implementation for User Story 3

  • T021 [US3] Étendre resolve_collision : repli sur numero_serie au sein d'un même modele (FR-004), dans resolve.py (dépend de T015)
  • T022 [US3] Implémenter la construction du groupe a_etiqueter quand ni le modèle ni le numéro de série ne suffisent (FR-005), dans resolve.py
  • T023 [US3] Implémenter assign_manual_source(fichiers, boitier_id=None) -> int dans packages/regine-core/src/regine_core/camera_profile/resolve.py (dépend de T009)

Checkpoint: Les trois user stories fonctionnelles indépendamment.


Phase 6: Polish & Cross-Cutting Concerns

  • T024 [P] Exécuter manuellement les 5 scénarios de specs/002-profil-boitiers-optionnel/quickstart.md et consigner le résultat — 2026-09-19 : les 5 scénarios passent ; le scénario 5 (réutilisation) a révélé un bug réel (numéro de série ignoré dans la branche "modèle unique"), corrigé et couvert par un test de non-régression
  • T025 [P] ruff check --fix sur packages/regine-core — 0 erreur restante, code formaté
  • T026 Mettre à jour contracts/regine-core-api.md si l'implémentation révèle un écart avec les signatures documentées

Dependencies & Execution Order

Phase Dependencies

  • Setup (Phase 1) : dépend de specs/004-categorisation-dossiers/tasks.md (packages, config.db) déjà exécutées.
  • Foundational (Phase 2) : dépend de Setup — BLOQUE toutes les user stories.
  • User Stories (Phase 3–5) : dépendent toutes de Foundational.
    • US1 (P1) : aucune dépendance sur une autre user story.
    • US2 (P2) : étend resolve.py initié en US1 (documentation du contrat) mais reste testable indépendamment.
    • US3 (P3) : étend resolve_collision de US2 (T015 → T021/T022) ; livrable séparément.
  • Polish (Phase 6) : dépend des user stories livrées (au minimum US1+US2).

Parallel Opportunities

  • T002–T004 (Phase 1) : fichiers/vérifications distincts.
  • T007, T009 (Phase 2) : parallélisables une fois T006/T008 terminés respectivement.
  • Tests marqués [P] au sein de chaque story.

Parallel Example: User Story 3

Task: "Test unitaire repli numéro de série dans packages/regine-core/tests/unit/test_resolve_collision.py"
Task: "Test unitaire groupe a_etiqueter dans packages/regine-core/tests/unit/test_resolve_collision.py"
Task: "Test unitaire list_boitiers/rename_boitier dans packages/regine-core/tests/unit/test_camera_profile_db.py"

Implementation Strategy

MVP First (User Story 1 uniquement)

  1. Setup + Foundational.
  2. US1 : garantir l'absence de friction par défaut (documentation de contrat + test de non-régression).
  3. Arrêt et validation : Scénario 1 de quickstart.md.

Livraison incrémentale

  1. Setup + Foundational → socle prêt (lecture EXIF, table boitiers).
  2. US1 → aucune question par défaut → valider Scénario 1.
  3. US2 → résolution par modèle → valider Scénarios 2 et 5.
  4. US3 → repli numéro de série + étiquetage manuel → valider Scénarios 3 et 4.

Notes

  • [P] = fichiers différents, sans dépendance non résolue.
  • Ce module n'a pas de surface CLI propre (cf. plan.md) : son intégration réelle relève de specs/001-import-photos (T034 de son propre tasks.md) et de specs/003-config-contexte-travail (écran de nommage des boîtiers).
  • Ne pas recréer le squelette monorepo ni regine_core.config.db : à étendre (T008), jamais dupliquer.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
---

description: "Task list for feature implementation"
---

# Tasks: Profil de boîtiers optionnel, déclaré à la demande

**Input**: Design documents from `/specs/002-profil-boitiers-optionnel/`
**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/, quickstart.md

**Tests**: Incluses — `plan.md` § Project Structure nomme explicitement les fichiers de test attendus, ce qui vaut demande explicite au sens des règles de génération.

**Organisation**: Tâches groupées par user story (spec.md) pour une implémentation et une validation indépendantes de chacune.

## Dépendances inter-specs

| Dépendance | Fournit | État |
|---|---|---|
| `specs/004-categorisation-dossiers/tasks.md` | Squelette du monorepo (`packages/regine-core`), `regine_core.config.db.open_context_db` (base de contexte centralisée) | tasks.md existe — **à exécuter avant la Phase 1** si ce n'est pas déjà fait |

Aucune autre dépendance externe : `exiftool` est un binaire système à installer sur le poste (pas une tâche de ce fichier).

## Phase 1: Setup

- [X] T001 Vérifier que `packages/regine-core/src/regine_core/config/db.py` existe déjà (créé par `specs/004-categorisation-dossiers/tasks.md`, T009) ; si absent, exécuter d'abord ce fichier de tâches
- [X] T002 [P] Créer `packages/regine-core/src/regine_core/metadata/__init__.py` et `packages/regine-core/src/regine_core/metadata/exif.py` (squelette)
- [X] T003 [P] Créer `packages/regine-core/src/regine_core/camera_profile/__init__.py`
- [X] T004 [P] Vérifier que `exiftool` est disponible sur le poste de développement (`exiftool -ver`) — `exiftool 13.55` présent, aucune documentation supplémentaire nécessaire

---

## Phase 2: Foundational (Blocking Prerequisites)

**Purpose**: Infrastructure partagée requise par les trois user stories.

**⚠️ CRITICAL**: Aucune user story ne peut être implémentée avant la fin de cette phase.

- [X] T005 Implémenter le wrapper de processus `exiftool -stay_open` (ouverture, envoi de commandes, fermeture propre) dans `packages/regine-core/src/regine_core/metadata/exif.py` (cf. `research.md` § 1)
- [X] T006 Implémenter `read_camera_tags(chemin: Path) -> CameraTags` dans `metadata/exif.py` (lit `Model`/`BodySerialNumber`, retourne `None` si absent/inexploitable)
- [X] T007 [P] Test unitaire de `read_camera_tags` dans `packages/regine-core/tests/unit/test_exif_reader.py` (modèle présent, numéro de série absent, métadonnées effacées) — validé avec de vrais appels `exiftool` sur fixture JPEG
- [X] T008 Ajouter la table `boitiers` (`id`, `modele`, `numero_serie` nullable, `nom_lisible` nullable, `premiere_rencontre`, `source`) au schéma de `packages/regine-core/src/regine_core/config/db.py` (dépend de T001 ; unicité sur `(modele, numero_serie)` cf. `data-model.md`) — implémentée dans `camera_profile/db.py` (`ensure_schema`), cohérent avec le placement décidé en research.md § 2 (le fichier reste ouvert via `config.db.open_context_db`)
- [X] T009 [P] Créer `packages/regine-core/src/regine_core/camera_profile/db.py` : `get_or_create_boitier(modele, numero_serie=None) -> int`, `list_boitiers() -> list[Boitier]`, `rename_boitier(id, nom) -> None` (dépend de T008)

**Checkpoint**: Fondations prêtes — les phases User Story peuvent commencer.

---

## Phase 3: User Story 1 - Premier import sans configuration préalable (Priority: P1) 🎯 MVP

**Goal**: Garantir qu'aucun profil de boîtiers déclaré n'est requis avant un import, et que la désambiguïsation par défaut (sans collision) ne pose aucune question (FR-001/002/008).

**Independent Test**: Lire les tags de fichiers de modèles différents sans jamais appeler `resolve_collision` (puisqu'aucune collision n'existe) ; vérifier qu'aucune entrée `boitiers` n'est créée tant qu'aucune collision réelle ne survient.

### Tests for User Story 1

- [X] T010 [P] [US1] Test unitaire : la détection d'un modèle jamais rencontré, sans collision, ne crée aucune entrée dans `boitiers` et ne lève aucune exception, dans `packages/regine-core/tests/unit/test_resolve_collision.py`

### Implementation for User Story 1

- [X] T011 [US1] Documenter (docstring) dans `packages/regine-core/src/regine_core/camera_profile/resolve.py` le contrat d'appel de `resolve_collision` : ne DOIT être invoquée par l'appelant (futur `specs/001-import-photos`) que pour des fichiers déjà identifiés en collision réelle par somme de contrôle (FR-003/FR-008) — aucune détection de collision n'est de la responsabilité de ce module

**Checkpoint**: User Story 1 fonctionnelle et testable indépendamment (MVP — garantit l'absence de friction par défaut).

---

## Phase 4: User Story 2 - Résolution ponctuelle d'une collision réelle entre boîtiers (Priority: P2)

**Goal**: Distinguer automatiquement deux fichiers en collision par leur tag de modèle, et réutiliser une résolution déjà établie (FR-002/003/006).

**Independent Test**: Deux fichiers de même nom d'origine, modèles EXIF différents → résolus automatiquement sans question ; un fichier ultérieur du même modèle/numéro de série réutilise le même `boitiers.id`.

### Tests for User Story 2

- [X] T012 [P] [US2] Test unitaire : `resolve_collision` avec deux fichiers de modèles différents → `a_etiqueter` vide, `id` distincts, dans `test_resolve_collision.py`
- [X] T013 [P] [US2] Test unitaire : réutilisation d'un `boitiers.id` déjà créé pour un modèle/numéro de série déjà rencontré, dans `test_resolve_collision.py`
- [X] T014 [US2] Test d'intégration : persistance d'une résolution entre deux appels successifs (simulant deux imports), dans `packages/regine-core/tests/integration/test_boitier_persistence.py`

### Implementation for User Story 2

- [X] T015 [US2] Implémenter `resolve_collision(fichiers: list[Path]) -> CollisionResolution` dans `packages/regine-core/src/regine_core/camera_profile/resolve.py` — regroupement par `modele`, résolution automatique quand les modèles diffèrent (dépend de T006, T009)
- [X] T016 [US2] Implémenter la réutilisation automatique d'un `boitiers.id` déjà connu (même `modele`/`numero_serie`) sans nouvelle question, dans `resolve.py` (FR-006)

**Checkpoint**: User Stories 1 ET 2 fonctionnelles indépendamment.

---

## Phase 5: User Story 3 - Repli sur numéro de série pour deux boîtiers identiques (Priority: P3)

**Goal**: Distinguer deux fichiers du même modèle par leur numéro de série ; à défaut, demander un étiquetage manuel (FR-004/005/007).

**Independent Test**: Collision entre deux fichiers de même modèle, numéros de série différents et exploitables → résolution automatique ; numéros de série absents/identiques → groupe renvoyé dans `a_etiqueter`, résolu ensuite par `assign_manual_source`.

### Tests for User Story 3

- [X] T017 [P] [US3] Test unitaire : `resolve_collision` avec même modèle, numéros de série différents et exploitables → résolution automatique, dans `test_resolve_collision.py`
- [X] T018 [P] [US3] Test unitaire : même modèle, numéro de série absent/identique/non exploitable → groupe dans `a_etiqueter`, jamais résolu arbitrairement, dans `test_resolve_collision.py` — **bug trouvé et corrigé en cours d'implémentation** : les fichiers indistincts partageant le même modèle doivent former un seul groupe, pas un groupe par fichier
- [X] T019 [US3] Test unitaire de `assign_manual_source` (crée une nouvelle entrée si `boitier_id=None`, réutilise sinon) dans `test_resolve_collision.py`
- [X] T020 [US3] Test unitaire de `list_boitiers`/`rename_boitier` (consultation et modification à tout moment, FR-007) dans `packages/regine-core/tests/unit/test_camera_profile_db.py`

### Implementation for User Story 3

- [X] T021 [US3] Étendre `resolve_collision` : repli sur `numero_serie` au sein d'un même `modele` (FR-004), dans `resolve.py` (dépend de T015)
- [X] T022 [US3] Implémenter la construction du groupe `a_etiqueter` quand ni le modèle ni le numéro de série ne suffisent (FR-005), dans `resolve.py`
- [X] T023 [US3] Implémenter `assign_manual_source(fichiers, boitier_id=None) -> int` dans `packages/regine-core/src/regine_core/camera_profile/resolve.py` (dépend de T009)

**Checkpoint**: Les trois user stories fonctionnelles indépendamment.

---

## Phase 6: Polish & Cross-Cutting Concerns

- [X] T024 [P] Exécuter manuellement les 5 scénarios de `specs/002-profil-boitiers-optionnel/quickstart.md` et consigner le résultat — **2026-09-19 : les 5 scénarios passent** ; le scénario 5 (réutilisation) a révélé un bug réel (numéro de série ignoré dans la branche "modèle unique"), corrigé et couvert par un test de non-régression
- [X] T025 [P] `ruff check --fix` sur `packages/regine-core` — 0 erreur restante, code formaté
- [X] T026 Mettre à jour `contracts/regine-core-api.md` si l'implémentation révèle un écart avec les signatures documentées

---

## Dependencies & Execution Order

### Phase Dependencies

- **Setup (Phase 1)** : dépend de `specs/004-categorisation-dossiers/tasks.md` (packages, `config.db`) déjà exécutées.
- **Foundational (Phase 2)** : dépend de Setup — BLOQUE toutes les user stories.
- **User Stories (Phase 3–5)** : dépendent toutes de Foundational.
  - US1 (P1) : aucune dépendance sur une autre user story.
  - US2 (P2) : étend `resolve.py` initié en US1 (documentation du contrat) mais reste testable indépendamment.
  - US3 (P3) : étend `resolve_collision` de US2 (T015 → T021/T022) ; livrable séparément.
- **Polish (Phase 6)** : dépend des user stories livrées (au minimum US1+US2).

### Parallel Opportunities

- T002–T004 (Phase 1) : fichiers/vérifications distincts.
- T007, T009 (Phase 2) : parallélisables une fois T006/T008 terminés respectivement.
- Tests marqués [P] au sein de chaque story.

---

## Parallel Example: User Story 3

```bash
Task: "Test unitaire repli numéro de série dans packages/regine-core/tests/unit/test_resolve_collision.py"
Task: "Test unitaire groupe a_etiqueter dans packages/regine-core/tests/unit/test_resolve_collision.py"
Task: "Test unitaire list_boitiers/rename_boitier dans packages/regine-core/tests/unit/test_camera_profile_db.py"
```

---

## Implementation Strategy

### MVP First (User Story 1 uniquement)

1. Setup + Foundational.
2. US1 : garantir l'absence de friction par défaut (documentation de contrat + test de non-régression).
3. **Arrêt et validation** : Scénario 1 de `quickstart.md`.

### Livraison incrémentale

1. Setup + Foundational → socle prêt (lecture EXIF, table `boitiers`).
2. US1 → aucune question par défaut → valider Scénario 1.
3. US2 → résolution par modèle → valider Scénarios 2 et 5.
4. US3 → repli numéro de série + étiquetage manuel → valider Scénarios 3 et 4.

## Notes

- [P] = fichiers différents, sans dépendance non résolue.
- Ce module n'a pas de surface CLI propre (cf. `plan.md`) : son intégration réelle relève de `specs/001-import-photos` (T034 de son propre tasks.md) et de `specs/003-config-contexte-travail` (écran de nommage des boîtiers).
- Ne pas recréer le squelette monorepo ni `regine_core.config.db` : à étendre (T008), jamais dupliquer.