pypostcards 3.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. pypostcards-3.3.0/PKG-INFO +63 -0
  2. pypostcards-3.3.0/README.md +217 -0
  3. pypostcards-3.3.0/pyproject.toml +142 -0
  4. pypostcards-3.3.0/setup.cfg +4 -0
  5. pypostcards-3.3.0/src/flpostcards/__init__.py +276 -0
  6. pypostcards-3.3.0/src/flpostcards/auth.py +142 -0
  7. pypostcards-3.3.0/src/flpostcards/blueprints/api/__init__.py +1147 -0
  8. pypostcards-3.3.0/src/flpostcards/blueprints/contact/__init__.py +131 -0
  9. pypostcards-3.3.0/src/flpostcards/blueprints/gallery/__init__.py +135 -0
  10. pypostcards-3.3.0/src/flpostcards/blueprints/home/__init__.py +342 -0
  11. pypostcards-3.3.0/src/flpostcards/blueprints/map/__init__.py +151 -0
  12. pypostcards-3.3.0/src/flpostcards/blueprints/slideshow/__init__.py +94 -0
  13. pypostcards-3.3.0/src/flpostcards/blueprints/travel/__init__.py +109 -0
  14. pypostcards-3.3.0/src/flpostcards/extensions.py +24 -0
  15. pypostcards-3.3.0/src/flpostcards/icon_generator.py +304 -0
  16. pypostcards-3.3.0/src/flpostcards/images.py +43 -0
  17. pypostcards-3.3.0/src/flpostcards/osm_static_map.py +159 -0
  18. pypostcards-3.3.0/src/flpostcards/run.py +7 -0
  19. pypostcards-3.3.0/src/flpostcards/static/css/style.css +1236 -0
  20. pypostcards-3.3.0/src/flpostcards/static/icon.png +0 -0
  21. pypostcards-3.3.0/src/flpostcards/static/js/fullscreen.js +120 -0
  22. pypostcards-3.3.0/src/flpostcards/static/js/gallery-filters.js +62 -0
  23. pypostcards-3.3.0/src/flpostcards/static/js/map.js +181 -0
  24. pypostcards-3.3.0/src/flpostcards/static/js/nav-controls.js +92 -0
  25. pypostcards-3.3.0/src/flpostcards/static/js/slideshow.js +239 -0
  26. pypostcards-3.3.0/src/flpostcards/static/js/travel-slideshow.js +332 -0
  27. pypostcards-3.3.0/src/flpostcards/static/js/wake-lock.js +59 -0
  28. pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/images/layers-2x.png +0 -0
  29. pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/images/layers.png +0 -0
  30. pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/images/marker-icon-2x.png +0 -0
  31. pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/images/marker-icon.png +0 -0
  32. pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/images/marker-shadow.png +0 -0
  33. pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/leaflet.css +661 -0
  34. pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/leaflet.js +6 -0
  35. pypostcards-3.3.0/src/flpostcards/static/vendor/nosleep/NoSleep.min.js +2 -0
  36. pypostcards-3.3.0/src/flpostcards/templates/base.html +97 -0
  37. pypostcards-3.3.0/src/flpostcards/templates/card/detail.html +141 -0
  38. pypostcards-3.3.0/src/flpostcards/templates/contact/index.html +54 -0
  39. pypostcards-3.3.0/src/flpostcards/templates/gallery/index.html +160 -0
  40. pypostcards-3.3.0/src/flpostcards/templates/home/index.html +59 -0
  41. pypostcards-3.3.0/src/flpostcards/templates/map/index.html +48 -0
  42. pypostcards-3.3.0/src/flpostcards/templates/slideshow/index.html +52 -0
  43. pypostcards-3.3.0/src/flpostcards/templates/travel/detail.html +60 -0
  44. pypostcards-3.3.0/src/flpostcards/templates/travel/index.html +42 -0
  45. pypostcards-3.3.0/src/flpostcards/translations/en/LC_MESSAGES/flpostcards.mo +0 -0
  46. pypostcards-3.3.0/src/flpostcards/translations/en/LC_MESSAGES/flpostcards.po +390 -0
  47. pypostcards-3.3.0/src/flpostcards/translations/fr/LC_MESSAGES/flpostcards.mo +0 -0
  48. pypostcards-3.3.0/src/flpostcards/translations/fr/LC_MESSAGES/flpostcards.po +390 -0
  49. pypostcards-3.3.0/src/libpostcards/__init__.py +1 -0
  50. pypostcards-3.3.0/src/libpostcards/model.py +1623 -0
  51. pypostcards-3.3.0/src/libpostcards/scan_corrector.py +462 -0
  52. pypostcards-3.3.0/src/libpostcards/similar.py +828 -0
  53. pypostcards-3.3.0/src/libpostcards/translations/en/LC_MESSAGES/libpostcards.mo +0 -0
  54. pypostcards-3.3.0/src/libpostcards/translations/en/LC_MESSAGES/libpostcards.po +21 -0
  55. pypostcards-3.3.0/src/libpostcards/translations/fr/LC_MESSAGES/libpostcards.mo +0 -0
  56. pypostcards-3.3.0/src/libpostcards/translations/fr/LC_MESSAGES/libpostcards.po +21 -0
  57. pypostcards-3.3.0/src/pypostcards.egg-info/PKG-INFO +63 -0
  58. pypostcards-3.3.0/src/pypostcards.egg-info/SOURCES.txt +97 -0
  59. pypostcards-3.3.0/src/pypostcards.egg-info/dependency_links.txt +1 -0
  60. pypostcards-3.3.0/src/pypostcards.egg-info/entry_points.txt +6 -0
  61. pypostcards-3.3.0/src/pypostcards.egg-info/requires.txt +57 -0
  62. pypostcards-3.3.0/src/pypostcards.egg-info/top_level.txt +4 -0
  63. pypostcards-3.3.0/src/simpostcards/__init__.py +80 -0
  64. pypostcards-3.3.0/src/simpostcards/blueprints/api/__init__.py +117 -0
  65. pypostcards-3.3.0/src/simpostcards/libs/__init__.py +1 -0
  66. pypostcards-3.3.0/src/simpostcards/libs/corrector.py +57 -0
  67. pypostcards-3.3.0/src/simpostcards/libs/hasher.py +47 -0
  68. pypostcards-3.3.0/src/simpostcards/run.py +6 -0
  69. pypostcards-3.3.0/src/tkpostcards/__init__.py +46 -0
  70. pypostcards-3.3.0/src/tkpostcards/images/kartotek_tkinter_256.png +0 -0
  71. pypostcards-3.3.0/src/tkpostcards/images/ktimport_256.png +0 -0
  72. pypostcards-3.3.0/src/tkpostcards/images/ktmanager_256.png +0 -0
  73. pypostcards-3.3.0/src/tkpostcards/images/ktscan_256.png +0 -0
  74. pypostcards-3.3.0/src/tkpostcards/images/kttools_256.png +0 -0
  75. pypostcards-3.3.0/src/tkpostcards/libs/__init__.py +3 -0
  76. pypostcards-3.3.0/src/tkpostcards/libs/backup.py +170 -0
  77. pypostcards-3.3.0/src/tkpostcards/libs/detect_postcards.py +233 -0
  78. pypostcards-3.3.0/src/tkpostcards/libs/importdir.py +107 -0
  79. pypostcards-3.3.0/src/tkpostcards/libs/objdetect.py +510 -0
  80. pypostcards-3.3.0/src/tkpostcards/libs/ocr.py +37 -0
  81. pypostcards-3.3.0/src/tkpostcards/libs/publish.py +63 -0
  82. pypostcards-3.3.0/src/tkpostcards/libs/remotesync.py +1283 -0
  83. pypostcards-3.3.0/src/tkpostcards/libs/scan_add.py +189 -0
  84. pypostcards-3.3.0/src/tkpostcards/libs/scan_editor.py +127 -0
  85. pypostcards-3.3.0/src/tkpostcards/libs/scan_prepare.py +116 -0
  86. pypostcards-3.3.0/src/tkpostcards/libs/size.py +98 -0
  87. pypostcards-3.3.0/src/tkpostcards/libs/transparency.py +164 -0
  88. pypostcards-3.3.0/src/tkpostcards/libs/travel.py +251 -0
  89. pypostcards-3.3.0/src/tkpostcards/scripts/__init__.py +12 -0
  90. pypostcards-3.3.0/src/tkpostcards/scripts/tktools.py +547 -0
  91. pypostcards-3.3.0/src/tkpostcards/tkimport.py +926 -0
  92. pypostcards-3.3.0/src/tkpostcards/tkmanager.py +4629 -0
  93. pypostcards-3.3.0/src/tkpostcards/tkscan.py +1331 -0
  94. pypostcards-3.3.0/src/tkpostcards/translations/en/LC_MESSAGES/tkpostcards.mo +0 -0
  95. pypostcards-3.3.0/src/tkpostcards/translations/en/LC_MESSAGES/tkpostcards.po +1352 -0
  96. pypostcards-3.3.0/src/tkpostcards/translations/fr/LC_MESSAGES/tkpostcards.mo +0 -0
  97. pypostcards-3.3.0/src/tkpostcards/translations/fr/LC_MESSAGES/tkpostcards.po +1350 -0
  98. pypostcards-3.3.0/tests/test_remotesync.py +672 -0
  99. pypostcards-3.3.0/tests/test_similar.py +127 -0
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.4
2
+ Name: pypostcards
3
+ Version: 3.3.0
4
+ Summary: Scan, manage and publish your postcards collection
5
+ Author-email: bibi21000 <bibi21000@gmail.com>
6
+ Maintainer-email: bibi21000 <bibi21000@gmail.com>
7
+ Project-URL: HomePage, https://github.com/bibi21000/pypostcards
8
+ Project-URL: Issues, https://github.com/bibi21000/pypostcards/issues
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Framework :: Flask
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Requires-Python: >=3.9
17
+ Requires-Dist: pillow
18
+ Provides-Extra: flask
19
+ Requires-Dist: Flask; extra == "flask"
20
+ Requires-Dist: Flask-Babel; extra == "flask"
21
+ Requires-Dist: Werkzeug; extra == "flask"
22
+ Requires-Dist: Jinja2; extra == "flask"
23
+ Requires-Dist: MarkupSafe; extra == "flask"
24
+ Requires-Dist: Flask-Cachecontrol; extra == "flask"
25
+ Requires-Dist: itsdangerous; extra == "flask"
26
+ Requires-Dist: requests; extra == "flask"
27
+ Requires-Dist: PyJWT; extra == "flask"
28
+ Requires-Dist: redis; extra == "flask"
29
+ Requires-Dist: flask-limiter; extra == "flask"
30
+ Provides-Extra: prod
31
+ Requires-Dist: gunicorn; extra == "prod"
32
+ Provides-Extra: ktmanager
33
+ Requires-Dist: click; extra == "ktmanager"
34
+ Requires-Dist: numpy; extra == "ktmanager"
35
+ Requires-Dist: tqdm; extra == "ktmanager"
36
+ Requires-Dist: pyzstd; extra == "ktmanager"
37
+ Requires-Dist: paramiko; extra == "ktmanager"
38
+ Provides-Extra: ktimport
39
+ Requires-Dist: deskew; extra == "ktimport"
40
+ Requires-Dist: opencv-python-headless; extra == "ktimport"
41
+ Requires-Dist: numpy; extra == "ktimport"
42
+ Provides-Extra: ocr
43
+ Requires-Dist: pytesseract; extra == "ocr"
44
+ Provides-Extra: similar
45
+ Requires-Dist: requests; extra == "similar"
46
+ Requires-Dist: imagehash; extra == "similar"
47
+ Requires-Dist: torch; extra == "similar"
48
+ Requires-Dist: torchvision; extra == "similar"
49
+ Requires-Dist: open-clip-torch; extra == "similar"
50
+ Provides-Extra: travel
51
+ Requires-Dist: ortools; extra == "travel"
52
+ Provides-Extra: objdetect
53
+ Requires-Dist: ultralytics; extra == "objdetect"
54
+ Requires-Dist: transformers; extra == "objdetect"
55
+ Requires-Dist: torch; extra == "objdetect"
56
+ Provides-Extra: dev
57
+ Requires-Dist: build; extra == "dev"
58
+ Requires-Dist: pytest; extra == "dev"
59
+ Requires-Dist: coverage; extra == "dev"
60
+ Requires-Dist: ruff; extra == "dev"
61
+ Requires-Dist: Babel; extra == "dev"
62
+ Provides-Extra: appimage
63
+ Requires-Dist: python-appimage; extra == "appimage"
@@ -0,0 +1,217 @@
1
+ # Patch : simpostcards + `/api/v1/similar` + index à chemins relatifs
2
+
3
+ Cette archive cumule l'ensemble des changements liés à la recherche de
4
+ cartes similaires :
5
+
6
+ 1. Nouveau package `simpostcards` (API `POST /api/compute_hashes`).
7
+ 2. `scan_corrector.py` et `similar.py` déplacés de `src/tkpostcards/libs/`
8
+ vers `src/libpostcards/` (code partagé, plus de dépendance
9
+ `simpostcards → tkpostcards`).
10
+ 3. `POST /api/v1/similar` côté `flpostcards`, qui reçoit une photo
11
+ depuis l'appli mobile, la fait analyser par `simpostcards`, compare
12
+ les hashs à l'index local et renvoie les cartes qui correspondent.
13
+ 4. **Nouveau** : l'index (`postcards.pkl`) stocke désormais des chemins
14
+ **relatifs à `datadir`** au lieu de chemins absolus.
15
+
16
+ ## Fichiers de cette archive
17
+
18
+ ```
19
+ src/simpostcards/ # package (inchangé depuis les patchs précédents)
20
+ src/libpostcards/scan_corrector.py # inchangé depuis les patchs précédents
21
+ src/libpostcards/similar.py # MODIFIÉ : chargement paresseux du modèle CLIP
22
+ # + search_hashes() / hashes_from_hex()
23
+ # + chemins d'index relatifs à datadir
24
+ src/tkpostcards/scripts/tktools.py # MODIFIÉ : PostcardSearcher(datadir=...) partout
25
+ # + suppression d'entrée d'index (clé relative)
26
+ src/tkpostcards/tkmanager.py # MODIFIÉ : idem (1 site)
27
+ src/flpostcards/__init__.py # nouvelles clés de config (inchangé depuis le patch précédent)
28
+ src/flpostcards/images.py # inchangé, réutilisé par /api/v1/similar
29
+ src/flpostcards/blueprints/api/__init__.py # POST /api/v1/similar + PostcardSearcher(datadir=...)
30
+ pyproject.toml # "requests" ajouté au groupe [flask]
31
+ ```
32
+
33
+ À supprimer dans votre arborescence (comme pour les patchs précédents) :
34
+ ```
35
+ src/tkpostcards/libs/scan_corrector.py
36
+ src/tkpostcards/libs/similar.py
37
+ ```
38
+
39
+ ## `libpostcards/similar.py` : chargement paresseux du modèle CLIP
40
+
41
+ `PostcardSearcher.__init__` chargeait le modèle CLIP (`open_clip`)
42
+ immédiatement, ce qui est coûteux et inutile côté `flpostcards` : celui-ci
43
+ ne calcule jamais d'embedding lui-même (c'est `simpostcards` qui renvoie
44
+ les hashs), il ne fait que **comparer des hashs déjà calculés** à
45
+ l'index.
46
+
47
+ - Le modèle n'est désormais chargé qu'à la première utilisation réelle
48
+ (`compute_hashes` / `compute_embedding`, via `_ensure_model()`).
49
+ Aucun changement de comportement pour `tktools similar index`.
50
+ - Nouvelle méthode `search_hashes(hashes, threshold=70, max_results=None)` :
51
+ compare des hashs déjà calculés (hex ou `imagehash.ImageHash`) à
52
+ l'index via `multi_hash_similarity` uniquement (**sans** charger le
53
+ modèle CLIP). C'est cette méthode qu'utilise `/api/v1/similar`.
54
+ - Nouvelle méthode statique `hashes_from_hex(...)` : convertit le dict
55
+ JSON `{"ahash": "...", "dhash": "...", "phash": "...", "whash": "..."}`
56
+ renvoyé par `simpostcards` en dict d'`imagehash.ImageHash`.
57
+
58
+ ⚠️ `postcards.pkl` contient malgré tout des tenseurs `torch` (les
59
+ embeddings CLIP indexés par `tktools similar index`) : `pickle.load()`
60
+ a donc toujours besoin que `torch` soit installé pour désérialiser le
61
+ fichier, même si `flpostcards` ne s'en sert pas pour le score. Installer
62
+ le groupe `[similar]` (`pip install -e .[similar]`) reste nécessaire
63
+ côté serveur `flpostcards` pour que `/api/v1/similar` fonctionne.
64
+
65
+ ## `libpostcards/similar.py` : index avec chemins relatifs à `datadir`
66
+
67
+ L'index stockait jusqu'ici des chemins **absolus** (clés du dict +
68
+ champ `"path"` de chaque entrée), typiquement générés depuis
69
+ `str(Path(location).rglob(...))`. Problème : un index construit sur
70
+ une machine (ex. `/home/alice/postcards/datadir/cards/...`) ne
71
+ correspond plus une fois copié/publié sur un serveur avec une autre
72
+ racine (ex. `/srv/postcards/datadir/...`) — les chemins ne pointent
73
+ plus vers rien de correct sur la machine cible.
74
+
75
+ **`PostcardSearcher` accepte maintenant un paramètre `datadir`** :
76
+ ```python
77
+ searcher = PostcardSearcher(datadir=common.datadir) # ou current_app.config["DATADIR"]
78
+ ```
79
+ Quand `datadir` est fourni, `compute_hashes()` / `build_index()`
80
+ stockent des chemins **relatifs à `datadir`** (ex. `"cards/423_R.tiff"`
81
+ au lieu de `"/home/alice/postcards/datadir/cards/423_R.tiff"`). Un
82
+ index republié sur un autre serveur reste donc valide, tant que
83
+ `datadir` a la même structure interne (peu importe son chemin absolu).
84
+
85
+ Deux nouvelles méthodes publiques :
86
+ - `searcher.relative_path(path)` — convertit un chemin en relatif à
87
+ `datadir` (retombe sur le chemin tel quel s'il est hors `datadir`,
88
+ ou si `datadir` n'est pas configuré — comportement historique
89
+ préservé par défaut, ex. `search_url`/`search_clipboard` qui
90
+ travaillent sur des fichiers temporaires hors `datadir`).
91
+ - `searcher.absolute_path(path)` — l'inverse : reconstruit un chemin
92
+ absolu à partir d'une entrée d'index (utile pour rouvrir le fichier
93
+ correspondant, ex. régénération de vignette).
94
+
95
+ **Tous les points d'entrée qui construisent l'index passent maintenant
96
+ `datadir`** (`tktools.py` : 7 sites, `tkmanager.py` : 1 site,
97
+ `flpostcards/blueprints/api/__init__.py` : 1 site pour
98
+ `/api/v1/similar`, même si ce dernier ne fait que charger l'index).
99
+ La suppression d'entrée d'index dans `tktools.py` (commande `delete`) a
100
+ été mise à jour pour utiliser le même format de clé relative, avec
101
+ `dict.pop(..., None)` (au lieu de `del`) pour rester robuste si vous
102
+ exécutez cette commande sur un `postcards.pkl` encore au format absolu
103
+ (avant migration).
104
+
105
+ `extract_card_id()` (parsing du nom de fichier) et les affichages CLI
106
+ (`tktools similar ...`) n'ont pas eu besoin de changement : ils ne
107
+ dépendaient déjà que du nom de fichier, pas du chemin absolu.
108
+
109
+ ⚠️ **Migration** : après avoir mis à jour votre code, régénérez l'index
110
+ une fois (`tktools similar index`) pour que `postcards.pkl` passe au
111
+ nouveau format de chemins relatifs. Les anciens index (chemins
112
+ absolus) restent lisibles sans erreur ; les recherches par hash
113
+ continuent de fonctionner quel que soit le format — seule la commande
114
+ `delete` a besoin du nouveau format pour retrouver l'entrée
115
+ correspondante.
116
+
117
+ ## `flpostcards` : route `/api/v1/similar`
118
+
119
+ **POST /api/v1/similar**
120
+
121
+ Requête (`multipart/form-data`) :
122
+ - `image` (obligatoire) — la photo prise par l'appli mobile
123
+ - `threshold` (optionnel, 0-100) — seuil de similarité, défaut
124
+ `SIMILAR_DEFAULT_THRESHOLD` (70)
125
+
126
+ Déroulé :
127
+ 1. L'image est transmise telle quelle à `simpostcards`
128
+ (`POST {SIMPOSTCARDS_URL}/api/compute_hashes`), qui la redresse/détoure
129
+ et renvoie ses hashs perceptuels.
130
+ 2. Ces hashs sont comparés à l'index `datadir/postcards.pkl`
131
+ (`PostcardSearcher.search_hashes`, sans embedding CLIP).
132
+ 3. Pour chaque carte au-dessus du seuil, le chemin indexé
133
+ (`cards/<cardid>_R.tiff`) donne l'id de carte
134
+ (`PostcardSearcher.extract_card_id`), mis en correspondance avec les
135
+ PNG `size_div3`/`size_div10` via `flpostcards.images.card_images`.
136
+
137
+ Réponse (200) — liste triée par score décroissant :
138
+ ```json
139
+ [
140
+ {
141
+ "id": "423",
142
+ "score": "91%",
143
+ "uri_div3": "https://mondomaine/images/size_div3/423_R.png",
144
+ "uri_div10": "https://mondomaine/images/size_div10/423_R.png"
145
+ }
146
+ ]
147
+ ```
148
+
149
+ Erreurs :
150
+ - `400 {"error": "..."}` — pas d'image envoyée, `threshold` invalide,
151
+ ou image rejetée par `simpostcards` (image illisible)
152
+ - `502 {"error": "..."}` — service `simpostcards` injoignable, en
153
+ timeout, ou réponse invalide
154
+
155
+ L'index `postcards.pkl` est mis en cache sur `current_app` et rechargé
156
+ automatiquement si le fichier est remplacé (même mécanisme que
157
+ `Model._get_conn` pour `postcards.sqlite`) — pas besoin de redémarrer
158
+ gunicorn après une republication.
159
+
160
+ ## Configuration (`postcards.conf`, section `[flask]`)
161
+
162
+ Nouvelles clés, toutes optionnelles (valeurs par défaut indiquées) :
163
+ ```ini
164
+ [flask]
165
+ # ... clés existantes ...
166
+ simpostcards_url = http://simpostcards:8004
167
+ similar_default_threshold = 70
168
+ similar_max_results = 20
169
+ similar_timeout_s = 30
170
+ ```
171
+
172
+ ## Installation
173
+
174
+ - Ajouter `"requests"` au groupe `flask` de `[project.optional-dependencies]`
175
+ dans votre `pyproject.toml` (déjà fait dans le fichier fourni ici).
176
+ - `flpostcards` a besoin, en plus de `.[flask]`, du groupe `.[similar]`
177
+ (torch, open-clip-torch, imagehash, requests, tqdm) pour que
178
+ `/api/v1/similar` fonctionne — c'est le prix du format `postcards.pkl`
179
+ actuel (voir avertissement plus haut). Dans le `Makefile`, cible `venv` :
180
+ ```
181
+ ./venv/bin/pip install -e .[flask]
182
+ ./venv/bin/pip install -e .[similar]
183
+ ./venv/bin/pip install -e .[simpostcards]
184
+ ```
185
+ - Après mise à jour, régénérer l'index une fois :
186
+ ```
187
+ ./venv/bin/tktools similar index
188
+ ```
189
+
190
+ ## Validation effectuée
191
+
192
+ - `py_compile` sur tous les fichiers modifiés.
193
+ - Test unitaire de `search_hashes()`/`hashes_from_hex()` avec `torch`
194
+ et `open_clip` *stubbés*, pour vérifier que `PostcardSearcher()` ne
195
+ charge jamais le modèle CLIP tant qu'on ne demande que des
196
+ recherches par hash.
197
+ - Test unitaire dédié des chemins relatifs : `build_index()` sur un
198
+ `datadir` temporaire → clés et champs `"path"` bien relatifs (jamais
199
+ absolus), `absolute_path()` qui résout vers un fichier réellement
200
+ existant, ré-indexation incrémentale (mtime) qui continue à
201
+ fonctionner avec les nouvelles clés, `extract_card_id()` toujours
202
+ correct, et repli correct sur un chemin absolu pour un fichier hors
203
+ `datadir` (cas `search_url`/`search_clipboard`).
204
+ - Test d'intégration bout en bout : serveur `simpostcards` réel démarré
205
+ en tâche de fond, `flpostcards` configuré pour lui parler, un faux
206
+ `postcards.pkl` avec 2 cartes indexées, une "photo" envoyée à
207
+ `/api/v1/similar` → réponse `200`, la bonne carte ressort en premier
208
+ avec `uri_div3`/`uri_div10` correctement générées.
209
+
210
+ ## Rappel : patchs précédents
211
+
212
+ Voir les messages précédents pour le détail de `simpostcards`
213
+ lui-même (endpoint `POST /api/compute_hashes`, correctif du bug
214
+ `HoughLinesP` dans `scan_corrector.py`) et du déplacement initial de
215
+ `scan_corrector.py`/`similar.py` vers `libpostcards` — non reproduits
216
+ ici pour éviter la redondance, mais tous les fichiers correspondants
217
+ sont bien inclus dans cette archive.
@@ -0,0 +1,142 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [tool.setuptools]
6
+ include-package-data = true
7
+
8
+ [tool.setuptools.packages.find]
9
+ where = ["src"]
10
+
11
+ [tool.setuptools.package-dir]
12
+ "" = "src"
13
+
14
+ [project]
15
+ name = "pypostcards"
16
+ authors = [
17
+ {name = "bibi21000", email = "bibi21000@gmail.com"},
18
+ ]
19
+ maintainers = [
20
+ {name = "bibi21000", email = "bibi21000@gmail.com"}
21
+ ]
22
+ description = "Scan, manage and publish your postcards collection"
23
+ version = "3.3.0"
24
+ requires-python = ">=3.9"
25
+ dependencies = [
26
+ "pillow",
27
+ ]
28
+ classifiers = [
29
+ "Development Status :: 4 - Beta",
30
+ "Framework :: Flask",
31
+ "Programming Language :: Python :: 3",
32
+ "Programming Language :: Python :: 3.11",
33
+ "Programming Language :: Python :: 3.12",
34
+ "Programming Language :: Python :: 3.13",
35
+ "Programming Language :: Python :: 3.14",
36
+ ]
37
+
38
+ [project.optional-dependencies]
39
+ flask = [
40
+ "Flask",
41
+ "Flask-Babel",
42
+ "Werkzeug",
43
+ "Jinja2",
44
+ "MarkupSafe",
45
+ "Flask-Cachecontrol",
46
+ "itsdangerous",
47
+ "requests",
48
+ "PyJWT",
49
+ "redis",
50
+ "flask-limiter",
51
+ ]
52
+ prod = [
53
+ "gunicorn"
54
+ ]
55
+
56
+ ktmanager = [
57
+ "click",
58
+ "numpy",
59
+ "tqdm",
60
+ "pyzstd",
61
+ "paramiko",
62
+ ]
63
+ ktimport = [
64
+ "deskew",
65
+ "opencv-python-headless",
66
+ "numpy"
67
+ ]
68
+ ocr = [
69
+ "pytesseract",
70
+ ]
71
+ similar = [
72
+ "requests",
73
+ "imagehash",
74
+ "torch",
75
+ "torchvision",
76
+ "open-clip-torch",
77
+ ]
78
+ travel = [
79
+ "ortools"
80
+ ]
81
+ objdetect = [
82
+ "ultralytics",
83
+ "transformers",
84
+ "torch",
85
+ ]
86
+
87
+ dev = [
88
+ "build",
89
+ "pytest",
90
+ "coverage",
91
+ "ruff",
92
+ "Babel",
93
+ ]
94
+ appimage = [
95
+ "python-appimage",
96
+ ]
97
+
98
+ [project.urls]
99
+ HomePage = "https://github.com/bibi21000/pypostcards"
100
+ Issues = "https://github.com/bibi21000/pypostcards/issues"
101
+
102
+ [project.scripts]
103
+ kttools = "tkpostcards.scripts.tktools:cli"
104
+ ktmanager = "tkpostcards.tkmanager:run"
105
+ ktimport = "tkpostcards.tkimport:run"
106
+ ktscan = "tkpostcards.tkscan:run"
107
+ simrun = "simpostcards.run:main"
108
+
109
+ [tool.black]
110
+ line-length = 88
111
+ target-version = ["py312"]
112
+
113
+ [tool.ruff]
114
+ line-length = 88
115
+ target-version = "py312"
116
+
117
+ [tool.pytest.ini_options]
118
+ testpaths = [
119
+ "tests"
120
+ ]
121
+
122
+ [tool.babel]
123
+ domain = "flpostcards"
124
+ mapping_file = "babel.cfg"
125
+
126
+ [tool.setuptools.package-data]
127
+ tkpostcards = [
128
+ "translations/**/*.po",
129
+ "translations/**/*.mo",
130
+ "images/*.png",
131
+ ]
132
+ flpostcards = [
133
+ "translations/**/*.po",
134
+ "translations/**/*.mo",
135
+ "templates/**/*",
136
+ "static/**/*",
137
+ ]
138
+ libpostcards = [
139
+ "translations/**/*.po",
140
+ "translations/**/*.mo",
141
+ ]
142
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+