La fiche du lot 15 annonçait « dernier loyer connu, arrêté après févr. 26 » alors qu'un locataire y est entré le 8 juillet — et la courbe juste dessous, où la barre de juillet est bien là, la contredisait. Le loyer en vigueur se lit sur les paliers, et un mois facturé au seul prorata n'en ouvre pas : celui d'avant s'arrête donc à la dernière mensualité pleine, quatre mois avant le nouveau bail. Ne rien lire après lui confond deux états que le compte rendu sépare : un lot qui n'est plus loué, et un lot entre deux locataires. La distinction tient à la frontière qui sert déjà à séparer loyer plein et prorata, la période portée par le compte rendu : - un prorata qui finit le dernier jour du mois sans partir du premier facture la fin du mois : quelqu'un est entré, le bail court encore après ; - un prorata qui part du premier sans l'atteindre facture le début : le bail s'arrête là ; - un montant négatif ne fait ni l'un ni l'autre. Un avoir porte parfois la période exacte de la ligne qu'il annule, et le lire comme une entrée inventerait une relocation là où rien n'a été loué. L'entrée l'emporte sur la sortie parce qu'elle vient après : ce lot voit son locataire partir le 10 mars et le suivant arriver le 8 juillet, et c'est le second qui décrit son état. Le montant affiché ne bouge pas — le nouveau bail n'a été facturé qu'au prorata, et en tirer un loyer mensuel l'inventerait. Seul ce qu'on en dit change : « dernier loyer plein · nouveau bail depuis juil. 26 ». Sans relocation, la sortie date enfin la fin de la location au mois où elle a lieu : « arrêté en cours de mars 26 » plutôt qu'« arrêté après févr. 26 », qui l'avançait d'un mois. Sur le parc : 31 lots loués, 1 relouté, 1 réellement arrêté. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Plesna Gerance
Extracteur de comptes rendus de gerance Oralia/ICS.
Prerequis
- Python >= 3.10
- uv
- Node.js >= 22
L'extraction de PDF utilise
pdfplumber(Python pur) : aucun binaire systeme requis (plus besoin depoppler-utils/pdftotext).
Installation
uv sync
cd frontend && npm install
Developpement
Lancer backend et frontend dans deux terminaux separés :
make dev_back # Backend sur http://localhost:8000 (auto-reload)
make dev_front # Frontend sur http://localhost:5173 (hot-reload)
Ou les deux en parallele :
make dev
Ouvrir http://localhost:5173 — le proxy Vite redirige /api vers le backend.
Tests
make test # ruff check + pytest + vitest, soit exactement ce que la CI verifie
Les tests de non-regression des parseurs comparent l'extraction de PDF reels a
des references figees. PDF (data/) et references (tests/golden/) contiennent
des donnees personnelles et ne sont pas versionnes : ces tests se sautent
d'eux-memes la ou le corpus est absent, CI comprise. Les lancer pour de vrai
suppose donc un corpus local (cf. tests/test_parsers_golden.py).
Versions et cycle de vie
La production heberge des donnees reelles : elle suit des versions, pas la
pointe de main.
Ce qu'est une version
Un tag git vMAJEUR.MINEUR.CORRECTIF (semver), pose sur main. Le meme numero
est ecrit dans cinq fichiers, que l'outillage tient d'accord entre eux :
pyproject.toml, src/plesna_gerance/__init__.py, frontend/package.json,
frontend/package-lock.json (qui porte lui aussi la version du paquet racine,
sans quoi npm ci peut refuser de tourner en CI) et packaging/installer.iss
(version affichee par l'installeur Windows).
Quand incrementer quoi :
| Segment | Quand | Exemple |
|---|---|---|
CORRECTIF (0.1.1) |
correction sans changement d'usage | un montant mal lu |
MINEUR (0.2.0) |
nouvelle fonctionnalite, donnees existantes intactes | une nouvelle page |
MAJEUR (1.0.0) |
rupture : migration de base ou changement d'usage a annoncer | refonte du referentiel |
Publier une version
make test # ce que la CI verifiera de toute facon
make release VERSION=0.2.0 # aligne les 4 fichiers, commite, pose le tag
git push origin main v0.2.0 # <- c'est ce push qui publie
make release ne pousse pas : le tag reste local tant qu'on ne l'a pas
envoye, ce qui laisse le temps de relire le commit de version. Le script
(scripts/release.py) refuse d'avancer hors de main, sur un arbre sale, ou
si le tag existe deja — un tag publie ne se reecrit pas.
Ce que declenche le push d'un tag
.gitea/workflows/docker-publish.yml: lance d'abord les tests (ruff,pytest,vitest), et seulement s'ils passent construit et publie l'image sous trois tags —0.2.0,0.2et0. Epingler0.2.0fige le deploiement a l'octet pres ;0.2laisse entrer les correctifs de la serie..github/workflows/build-windows.yml: construit l'executable et l'installeur Windows (necessite un runnerwindows-latest).
Un push sur main sans tag publie latest et main : utile pour essayer
la pointe, jamais pour la production.
Deployer une version
La version deployee est epinglee dans .env, lu par docker-compose.yml :
cp .env.example .env # une seule fois
# editer PLESNA_VERSION=0.2.0
make docker # pull + up -d
Revenir en arriere, c'est la meme manoeuvre : remettre le numero precedent dans
.env et relancer make docker. Les donnees vivent dans le volume nomme
plesna-data, independamment de l'image — un retour arriere d'image ne les
touche pas. En revanche une version qui a migre le schema de la base ne se
defait pas en changeant le numero : sauvegarder le volume avant une montee de
version majeure.
Production (Docker)
Un conteneur unique : l'image embarque le frontend Vue builde, servi par le backend FastAPI (API + interface web sur le meme port). Pas de nginx.
L'image est publiee par la CI Gitea (.gitea/workflows/docker-publish.yml)
sur git.opytex.org/lafrite/plesna-gerance. Le docker-compose.yml la tire
directement (pas de build local), a la version epinglee dans .env :
make docker # podman-compose pull && podman-compose up -d
# ou : docker compose pull && docker compose up -d
Accessible sur http://localhost:8080.
Les donnees (base SQLite + documents) sont declarees via PLESNA_DATA_DIR
(voir docker-compose.yml) et persistees dans un volume nomme dedie
(plesna-data), donc conservees entre les redemarrages et recreations du
conteneur.
Build local de l'image (test avant publication) :
make docker-build(podman build -f backend.Dockerfile -t plesna-gerance:local .).
Application de bureau (Windows, sans ligne de commande)
L'application peut etre empaquetee en executable Windows autonome : un seul fichier que l'utilisateur final installe et lance via une icone, sans Python, sans Node, sans terminal. L'extraction etant en Python pur, aucun binaire externe n'est requis.
Tester le mode bureau (depuis les sources)
Sous Windows (pywebview utilise WebView2, deja present) :
uv sync --group desktop
cd frontend && npm run build && cd ..
uv run plesna-gerance desktop # ouvre une fenetre native
Sous Linux, pywebview a besoin d'un moteur de rendu. Le groupe
desktop-linux fournit un backend Qt entierement pip-installable :
uv sync --group desktop-linux
cd frontend && npm run build && cd ..
uv run plesna-gerance desktop # necessite un environnement graphique ($DISPLAY)
Ce backend Qt ne sert qu'a tester le mode fenetre sous Linux : il n'est pas embarque dans le build Windows.
Produire l'executable + l'installeur
Deux options (le build doit se faire sur Windows, PyInstaller ne croise pas les plateformes) :
-
Sur une machine Windows — installer uv, Node.js et (optionnel) Inno Setup 6, puis :
powershell -ExecutionPolicy Bypass -File packaging\build_windows.ps1Produit
dist\PlesnaGerance.exeet, si Inno Setup est present,dist\PlesnaGerance-Setup.exe. -
Sans machine Windows — via GitHub Actions : le workflow
.github/workflows/build-windows.ymlcompile sur un runner Windows. Le declencher (onglet Actions ou en poussant un tagv*) puis telecharger l'artefactPlesnaGerance-windows.
Cote utilisateur final
Lancer PlesnaGerance-Setup.exe, suivre l'assistant (installation par
utilisateur, sans droits administrateur), puis cliquer sur l'icone Plesna
Gerance. Les donnees (base et documents) sont stockees dans
%APPDATA%\PlesnaGerance et conservees entre les mises a jour.
Assistant IA (optionnel) : la page IA necessite Ollama installe separement. Sans Ollama, l'application fonctionne normalement et la page IA indique simplement que le service est indisponible.