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.
- pypostcards-3.3.0/PKG-INFO +63 -0
- pypostcards-3.3.0/README.md +217 -0
- pypostcards-3.3.0/pyproject.toml +142 -0
- pypostcards-3.3.0/setup.cfg +4 -0
- pypostcards-3.3.0/src/flpostcards/__init__.py +276 -0
- pypostcards-3.3.0/src/flpostcards/auth.py +142 -0
- pypostcards-3.3.0/src/flpostcards/blueprints/api/__init__.py +1147 -0
- pypostcards-3.3.0/src/flpostcards/blueprints/contact/__init__.py +131 -0
- pypostcards-3.3.0/src/flpostcards/blueprints/gallery/__init__.py +135 -0
- pypostcards-3.3.0/src/flpostcards/blueprints/home/__init__.py +342 -0
- pypostcards-3.3.0/src/flpostcards/blueprints/map/__init__.py +151 -0
- pypostcards-3.3.0/src/flpostcards/blueprints/slideshow/__init__.py +94 -0
- pypostcards-3.3.0/src/flpostcards/blueprints/travel/__init__.py +109 -0
- pypostcards-3.3.0/src/flpostcards/extensions.py +24 -0
- pypostcards-3.3.0/src/flpostcards/icon_generator.py +304 -0
- pypostcards-3.3.0/src/flpostcards/images.py +43 -0
- pypostcards-3.3.0/src/flpostcards/osm_static_map.py +159 -0
- pypostcards-3.3.0/src/flpostcards/run.py +7 -0
- pypostcards-3.3.0/src/flpostcards/static/css/style.css +1236 -0
- pypostcards-3.3.0/src/flpostcards/static/icon.png +0 -0
- pypostcards-3.3.0/src/flpostcards/static/js/fullscreen.js +120 -0
- pypostcards-3.3.0/src/flpostcards/static/js/gallery-filters.js +62 -0
- pypostcards-3.3.0/src/flpostcards/static/js/map.js +181 -0
- pypostcards-3.3.0/src/flpostcards/static/js/nav-controls.js +92 -0
- pypostcards-3.3.0/src/flpostcards/static/js/slideshow.js +239 -0
- pypostcards-3.3.0/src/flpostcards/static/js/travel-slideshow.js +332 -0
- pypostcards-3.3.0/src/flpostcards/static/js/wake-lock.js +59 -0
- pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/images/layers-2x.png +0 -0
- pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/images/layers.png +0 -0
- pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/images/marker-icon-2x.png +0 -0
- pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/images/marker-icon.png +0 -0
- pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/images/marker-shadow.png +0 -0
- pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/leaflet.css +661 -0
- pypostcards-3.3.0/src/flpostcards/static/vendor/leaflet/leaflet.js +6 -0
- pypostcards-3.3.0/src/flpostcards/static/vendor/nosleep/NoSleep.min.js +2 -0
- pypostcards-3.3.0/src/flpostcards/templates/base.html +97 -0
- pypostcards-3.3.0/src/flpostcards/templates/card/detail.html +141 -0
- pypostcards-3.3.0/src/flpostcards/templates/contact/index.html +54 -0
- pypostcards-3.3.0/src/flpostcards/templates/gallery/index.html +160 -0
- pypostcards-3.3.0/src/flpostcards/templates/home/index.html +59 -0
- pypostcards-3.3.0/src/flpostcards/templates/map/index.html +48 -0
- pypostcards-3.3.0/src/flpostcards/templates/slideshow/index.html +52 -0
- pypostcards-3.3.0/src/flpostcards/templates/travel/detail.html +60 -0
- pypostcards-3.3.0/src/flpostcards/templates/travel/index.html +42 -0
- pypostcards-3.3.0/src/flpostcards/translations/en/LC_MESSAGES/flpostcards.mo +0 -0
- pypostcards-3.3.0/src/flpostcards/translations/en/LC_MESSAGES/flpostcards.po +390 -0
- pypostcards-3.3.0/src/flpostcards/translations/fr/LC_MESSAGES/flpostcards.mo +0 -0
- pypostcards-3.3.0/src/flpostcards/translations/fr/LC_MESSAGES/flpostcards.po +390 -0
- pypostcards-3.3.0/src/libpostcards/__init__.py +1 -0
- pypostcards-3.3.0/src/libpostcards/model.py +1623 -0
- pypostcards-3.3.0/src/libpostcards/scan_corrector.py +462 -0
- pypostcards-3.3.0/src/libpostcards/similar.py +828 -0
- pypostcards-3.3.0/src/libpostcards/translations/en/LC_MESSAGES/libpostcards.mo +0 -0
- pypostcards-3.3.0/src/libpostcards/translations/en/LC_MESSAGES/libpostcards.po +21 -0
- pypostcards-3.3.0/src/libpostcards/translations/fr/LC_MESSAGES/libpostcards.mo +0 -0
- pypostcards-3.3.0/src/libpostcards/translations/fr/LC_MESSAGES/libpostcards.po +21 -0
- pypostcards-3.3.0/src/pypostcards.egg-info/PKG-INFO +63 -0
- pypostcards-3.3.0/src/pypostcards.egg-info/SOURCES.txt +97 -0
- pypostcards-3.3.0/src/pypostcards.egg-info/dependency_links.txt +1 -0
- pypostcards-3.3.0/src/pypostcards.egg-info/entry_points.txt +6 -0
- pypostcards-3.3.0/src/pypostcards.egg-info/requires.txt +57 -0
- pypostcards-3.3.0/src/pypostcards.egg-info/top_level.txt +4 -0
- pypostcards-3.3.0/src/simpostcards/__init__.py +80 -0
- pypostcards-3.3.0/src/simpostcards/blueprints/api/__init__.py +117 -0
- pypostcards-3.3.0/src/simpostcards/libs/__init__.py +1 -0
- pypostcards-3.3.0/src/simpostcards/libs/corrector.py +57 -0
- pypostcards-3.3.0/src/simpostcards/libs/hasher.py +47 -0
- pypostcards-3.3.0/src/simpostcards/run.py +6 -0
- pypostcards-3.3.0/src/tkpostcards/__init__.py +46 -0
- pypostcards-3.3.0/src/tkpostcards/images/kartotek_tkinter_256.png +0 -0
- pypostcards-3.3.0/src/tkpostcards/images/ktimport_256.png +0 -0
- pypostcards-3.3.0/src/tkpostcards/images/ktmanager_256.png +0 -0
- pypostcards-3.3.0/src/tkpostcards/images/ktscan_256.png +0 -0
- pypostcards-3.3.0/src/tkpostcards/images/kttools_256.png +0 -0
- pypostcards-3.3.0/src/tkpostcards/libs/__init__.py +3 -0
- pypostcards-3.3.0/src/tkpostcards/libs/backup.py +170 -0
- pypostcards-3.3.0/src/tkpostcards/libs/detect_postcards.py +233 -0
- pypostcards-3.3.0/src/tkpostcards/libs/importdir.py +107 -0
- pypostcards-3.3.0/src/tkpostcards/libs/objdetect.py +510 -0
- pypostcards-3.3.0/src/tkpostcards/libs/ocr.py +37 -0
- pypostcards-3.3.0/src/tkpostcards/libs/publish.py +63 -0
- pypostcards-3.3.0/src/tkpostcards/libs/remotesync.py +1283 -0
- pypostcards-3.3.0/src/tkpostcards/libs/scan_add.py +189 -0
- pypostcards-3.3.0/src/tkpostcards/libs/scan_editor.py +127 -0
- pypostcards-3.3.0/src/tkpostcards/libs/scan_prepare.py +116 -0
- pypostcards-3.3.0/src/tkpostcards/libs/size.py +98 -0
- pypostcards-3.3.0/src/tkpostcards/libs/transparency.py +164 -0
- pypostcards-3.3.0/src/tkpostcards/libs/travel.py +251 -0
- pypostcards-3.3.0/src/tkpostcards/scripts/__init__.py +12 -0
- pypostcards-3.3.0/src/tkpostcards/scripts/tktools.py +547 -0
- pypostcards-3.3.0/src/tkpostcards/tkimport.py +926 -0
- pypostcards-3.3.0/src/tkpostcards/tkmanager.py +4629 -0
- pypostcards-3.3.0/src/tkpostcards/tkscan.py +1331 -0
- pypostcards-3.3.0/src/tkpostcards/translations/en/LC_MESSAGES/tkpostcards.mo +0 -0
- pypostcards-3.3.0/src/tkpostcards/translations/en/LC_MESSAGES/tkpostcards.po +1352 -0
- pypostcards-3.3.0/src/tkpostcards/translations/fr/LC_MESSAGES/tkpostcards.mo +0 -0
- pypostcards-3.3.0/src/tkpostcards/translations/fr/LC_MESSAGES/tkpostcards.po +1350 -0
- pypostcards-3.3.0/tests/test_remotesync.py +672 -0
- 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
|
+
|