Commit Graph

252 Commits

Author SHA1 Message Date
Colin Maudry e59e2e50b3 test: adapter les tests DataTable historiques à la migration AG Grid de /tableau (#41) 2026-07-10 14:20:27 +02:00
Colin Maudry c81533aa8e test(tableau): intégration AG Grid end-to-end (#41)
Vérifie via Selenium/DashComposite que /tableau charge et affiche la
grille AG Grid avec au moins une ligne rendue, sans erreur console
SEVERE. Dernière tâche de la migration DataTable -> AG Grid.
2026-07-10 14:13:43 +02:00
Colin Maudry 8efd8db07e feat(tableau): vues sauvegardées en JSON AST, retrait URL riche (#41)
Les vues stockent désormais {filterModel, columnState} en JSON dans
saved_views.query au lieu d'une query string DSL. Le rappel d'une vue
applique filterModel + columnState à la grille AG Grid via un nouveau
callback apply_saved_view, déclenché par pattern-matching sur les items
du menu "Mes vues" (désormais cliquables au lieu de liens href).

Retire restore_view_from_url, sync_url_and_reset_button,
show_confirmation, le bouton "Partager la vue" et le clientside
clean_filters de tableau.py - la migration AG Grid retire le mécanisme
d'URL riche sans rétrocompatibilité.

Le lien "Ouvrir" de compte/vues.py pointe maintenant vers /tableau nu
(limitation connue documentée, le rappel cross-page reste à faire).
2026-07-10 08:35:31 +02:00
Colin Maudry fbea2ebd53 feat(tableau): export Excel via DuckDB (AST->SQL) (#41)
Rewrite download_data to read filterModel/columnState from the AG Grid
component and add export_dataframe (compiles filterModel to SQL via
filtermodel_to_ast/ast_to_sql, builds ORDER BY, excludes hidden columns,
queries DuckDB via query_marches). Replaces the old Polars filter_query
pipeline tied to the removed DataTable.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 08:26:10 +02:00
Colin Maudry 97e1d0b5de feat(grid): columnDefs + fabrique ag_grid (#41) 2026-07-10 08:05:19 +02:00
Colin Maudry 2ce52d9eab feat(grid): fetch_grid_page datasource server-side AG Grid (#41) 2026-07-10 08:00:59 +02:00
Colin Maudry a7662a8561 feat(query): sort_model_to_sql + sérialisation AST (#41)
Ajoute sort_model_to_sql (adapte le sortModel AG Grid vers sort_by_to_sql
existant) et ast_to_dict/ast_from_dict pour le round-trip JSON de l'AST,
nécessaire aux vues sauvegardées.
2026-07-10 07:57:41 +02:00
Colin Maudry 33ea1205cf feat(query): filtermodel_to_ast (filterModel AG Grid -> AST) (#41) 2026-07-10 07:53:46 +02:00
Colin Maudry 979874760d fix(query): range sur colonnes non-numériques + blank/notBlank sur numérique/date (#41)
- `range` était ignoré silencieusement (TRUE, pas de filtre) sur les
  colonnes texte/date car seul `_numeric_to_sql` le gérait. Ajout du
  cas `range` dans la branche texte/date (BETWEEN, CAST VARCHAR pour
  les dates, comme les autres opérateurs de comparaison).
- `blank`/`notBlank` comparaient toujours à `''`, ce qui fait planter
  DuckDB (Conversion Error) sur les colonnes numériques/date. Le
  check est déplacé après le calcul de is_numeric/col_is_date : ces
  types utilisent IS [NOT] NULL sans comparaison à chaîne vide.
2026-07-10 07:49:43 +02:00
Colin Maudry 68f615370f feat(query): AST de filtre + compilateur ast_to_sql (#41)
Ajoute une représentation canonique du filtre sous forme d'AST booléen
(Condition/And/Or/Not) et son compilateur vers SQL DuckDB paramétré
(ast_to_sql). Réutilise tokenize_text_filter pour les feuilles texte.

Fondation pour la migration /tableau vers dash-ag-grid : cet AST sera
alimenté par le filterModel d'AG Grid (tâche suivante) et, plus tard,
par un champ de requête booléenne libre (#97).
2026-07-10 07:44:30 +02:00
Colin Maudry d69ae9b8de Merge branch '111-mcp-tools' into dev 2026-07-09 21:53:57 +02:00
Colin Maudry 24bc1c08c1 test(mcp): combler les lacunes de couverture relevées par la revue finale
Ajoute des tests de bout en bout pour les filtres montant/date/cpv, le
chemin titulaire de search_organisations, et la pagination (page>1,
clamping page=0). Normalise montant_total en float dans les deux
branches de compute_org_stats.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 21:43:26 +02:00
Colin Maudry 5a6bd59297 feat(mcp): expose les 4 fonctions métier via @mcp_enabled
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 20:35:24 +02:00
Colin Maudry ee98aa8186 feat(mcp): tools stats_acheteur / stats_titulaire (agrégations) 2026-07-09 20:30:16 +02:00
Colin Maudry 11da1e9d8d feat(mcp): tool rechercher_marches (filtres hybrides + pagination) 2026-07-09 20:23:05 +02:00
Colin Maudry 47694f2e5d test(mcp): vérifier l'extraction HTML->texte plain du nom (revue Task 4) 2026-07-09 20:18:28 +02:00
Colin Maudry fb4ffaa3e2 Revert "feat(mcp): search_org accepte track=False pour ne pas polluer Matomo"
This reverts commit ce9a79f420.
2026-07-09 20:16:08 +02:00
Colin Maudry ef1e4503c9 Cleanup commentaires .env 2026-07-09 20:14:37 +02:00
Colin Maudry e2d1082e92 feat(mcp): tool search_organisations (résolution nom -> id)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 20:13:17 +02:00
Colin Maudry 9303c5e206 feat(mcp): search_org accepte track=False pour ne pas polluer Matomo
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 20:04:55 +02:00
Colin Maudry ce9a79f420 feat(mcp): search_org accepte track=False pour ne pas polluer Matomo
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 20:01:57 +02:00
Colin Maudry 571c56ef71 feat(mcp): helper track_mcp_tool pour tracer les appels MCP dans Matomo 2026-07-09 19:57:09 +02:00
Colin Maudry e407f87529 feat(mcp): sérialisation Polars -> JSON pour les tools MCP
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 19:53:08 +02:00
Colin Maudry 2376987982 fix(admin): revérifier is_admin() dans le callback _update_table (#110)
Le callback Dash _update_table est un endpoint serveur global
(/_dash-update-component) invocable indépendamment du layout : la garde
is_admin() de layout() ne protégeait que l'affichage. Sans contrôle dans
le callback, un non-admin — voire un anonyme sur le chemin lecture —
pouvait lire/écrire toute la base SQLite utilisateurs.

Ajoute la garde en tête du callback + test de non-régression.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 18:47:11 +02:00
Colin Maudry 2150ade42b test(abonnement): adapter les tests de _reabo_button à sa nouvelle signature
_reabo_button() ne prend plus has_used_trial (bouton de réabonnement
simplifié) ; les deux anciens tests testant M'abonner/Me réabonner
sont remplacés par un test du comportement actuel.
2026-07-06 11:01:32 +02:00
Colin Maudry 92e30979d9 fix(abonnement): erreur Dash au chargement de mes-infos en mode configure
Le callback _toggle_submit référence inf-cb-retractation/inf-cb-cgu en
Input sans condition, mais ces cases n'étaient rendues qu'en mode
"subscribe". Elles sont maintenant toujours montées (masquées et
pré-cochées en mode configure) pour que Dash trouve toujours ces ids.
2026-07-06 11:01:26 +02:00
Colin Maudry ad028b9e6b Rédactionnel 2026-07-06 10:49:08 +02:00
Colin Maudry fb3292d41e feat(admin): afficher l'email dans subscriptions et subscriber_state
user_id seul n'est pas assez parlant pour débugger ; jointure LEFT JOIN
sur users pour afficher l'email en colonne (lecture seule).
2026-07-06 10:42:53 +02:00
Colin Maudry e8f74aa529 feat(abonnement): message 'changement à la prochaine échéance' sous les cards (#109)
Ajoute la fonction _change_hint et étend le callback _select_plan pour afficher
un hint "changement à la prochaine échéance" quand l'utilisateur sélectionne
un plan différent du plan courant.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-05 20:46:54 +02:00
Colin Maudry 7ae5b75db9 feat(abonnement): mode configure sur mes-infos (formule préactivée, sans cases) (#109)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 20:42:09 +02:00
Colin Maudry a2962c2163 feat(abonnement): prix + bouton Configurer sur /compte/abonnement (#109)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-05 20:35:50 +02:00
Colin Maudry b04f97cb8c feat(abonnement): route /subscriptions/update (maj formule + facturation) (#109)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 20:31:42 +02:00
Colin Maudry 3645c098c2 feat(abonnement): client.change_subscription (PUT /v1/subscription) (#109)
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-05 20:28:11 +02:00
Colin Maudry ee214677bd feat(abonnement): bouton changer de méthode de paiement sur /compte/abonnement
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-05 17:03:28 +02:00
Colin Maudry 081811bea7 feat(abonnement): route /subscriptions/change-payment-method 2026-07-05 16:52:16 +02:00
Colin Maudry 1cfb3e91e2 feat(abonnement): client.get_payment_info_url pour le changement de carte Frisbii 2026-07-05 16:46:38 +02:00
Colin Maudry 349fb17c90 feat(abonnement): sélection de formule par cartes cliquables dans mes-infos
Remplace la garde ?plan= (redirection si absent) par une sélection
interactive : deux cartes de formule (réutilisées de la page publique via
_plan_card) cliquables, un callback recopie le choix dans un champ caché
natif soumis avec le POST du formulaire. Le bouton de soumission reste
désactivé tant qu'aucune formule n'est choisie (en plus des cases à
cocher existantes).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 23:43:39 +02:00
Colin Maudry 315c500005 feat(abonnement): CTA connexion vers /a-propos/abonnement
Remplace le CTA du bas de /connexion ("Créer un compte avec mon adresse
email" → "/inscription") par un lien vers l'offre d'abonnement ("Pas encore
de compte ? Voir les abonnements" → "/a-propos/abonnement").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 23:39:06 +02:00
Colin Maudry 3b744d87f0 feat(abonnement): /compte/abonnement non-abonné affiche M'abonner/Me réabonner 2026-07-04 23:34:35 +02:00
Colin Maudry c5c63cb68e fix(abonnement): calcul du prix TTC robuste (évite les artefacts flottants)
Remplace le hack `str(int(prix_ht) * 1.2).replace('.0', '')` par
`round(prix_ht * 1.2, 2)` formaté avec `:g`, qui gère correctement les
prix qui ne multiplient pas proprement par 1.2 (ex: 24 -> 28.8 au lieu
de 28.799999999999997).

Le fichier src/pages/compte/abonnement.py contient le même bug mais
n'est pas touché ici : sa copie de _plan_card sera supprimée à la
tâche 4 du plan de refonte du tunnel d'abonnement.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 23:28:14 +02:00
Colin Maudry e904ec4a7d feat(abonnement): page publique avec cards et bouton Je m'abonne conditionnel
La page publique /a-propos/abonnement affiche désormais les cards de
formule (informatives, sans bouton par card), un explainer des
fonctionnalités, et un unique CTA "Je m'abonne" dont la cible dépend
de l'état d'authentification/abonnement. Retrait de la sous-section
"Fonctionnalités incluses" des CGU (doublon avec l'explainer).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 23:21:19 +02:00
Colin Maudry 6464baebd2 feat(auth): auto-login à la validation d'email, redirection vers mes-infos
Quand l'utilisateur valide son email, il est maintenant connecté automatiquement
via login_user() et redirigé directement vers /compte/abonnement/mes-infos
au lieu de /connexion?verified=1.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 23:16:29 +02:00
Colin Maudry cc2b34e84a feat(abonnement): linkedin_button paramétrable par next, inscription vers mes-infos
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 23:13:11 +02:00
Colin Maudry 1551faaf0c Normaliser les échecs d'écriture set_cell en ValueError
- IntegrityError (ex: email déjà utilisé) est désormais capturée et
  reconvertie en ValueError, pour rester dans le funnel d'alerte
  existant du callback admin au lieu de faire planter le callback Dash.
- Une UPDATE qui touche 0 ligne (ligne supprimée entre le chargement du
  tableau et la soumission de l'édition) lève désormais une ValueError
  au lieu d'être silencieusement traitée comme un succès (ce qui aurait
  créé un log d'audit trompeur).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 23:10:32 +02:00
Colin Maudry eb8f54dbf2 fix(figures): calculer center/zoom côté serveur au lieu d'utiliser bounds
La prop `bounds` de dash-leaflet lève une exception JS (TypeError sur
.equals() avec une valeur précédente indéfinie) au tout premier montage
du composant Map — confirmé via la stack trace navigateur, reproductible
même en dernière version (1.1.3). Cette exception laissait le clustering
dans un état incohérent (declustering impossible avant un zoom complet
suivi d'un dézoom).

bounds_to_center_zoom calcule un center/zoom approximatifs (projection
Web Mercator, taille de conteneur supposée) pour cadrer l'organisme et
ses contreparties, en évitant complètement la prop `bounds` bugguée.
Cadrage moins précis qu'un vrai fitBounds() navigateur, mais robuste.
2026-07-03 22:54:21 +02:00
Colin Maudry 6739e4926b test(admin): add end-to-end Selenium coverage for the generic table editor
Covers anonymous/non-admin access (404), and the full admin flow: switch
table, edit a subscriptions.prix_ht cell through the real DataTable UI,
verify the write lands in the DB, and verify the edit is logged and
visible in the admin_actions table.

tests/users.test.sqlite is committed and shared by the whole Selenium
session, so _cleanup_user deletes every row the tests create. Beyond
resetting sqlite_sequence (already needed for the AUTOINCREMENT
counter), a plain DELETE also leaves stale, never-zeroed bytes behind
in the admin_actions/subscriptions b-tree pages once they go back to
zero rows, which byte-diffs the file even though its logical content
is unchanged. VACUUM rebuilds the file from live data only, producing
a deterministic page layout (verified empirically across independent
runs with different random test data). Re-baselined the fixture to
that canonical vacuumed state so `git status` stays clean after
running the suite.
2026-07-03 22:43:53 +02:00
Colin Maudry e763a0b878 Taille du point de l'org 2026-07-03 22:29:04 +02:00
Colin Maudry f1c28acc96 feat(figures): mettre en avant le point de l'organisme consulté sur la carte
Marque is_home=True sur les marqueurs du home_type (acheteur ou titulaire
selon la page) dans get_org_location_map. Côté client, pointToLayer donne
à ce point un rayon légèrement plus grand (8 vs 5) et le ramène toujours
au premier plan (bringToFront), indépendamment de l'ordre des couches.
2026-07-03 22:08:43 +02:00
Colin Maudry 8b69fee31c feat(figures): ajouter get_org_location_map (carte cluster organisme + contrepartie) 2026-07-03 15:59:47 +02:00
Colin Maudry 21d3a30d8b refactor(figures): extraite build_org_markers pour réutilisation 2026-07-03 15:55:29 +02:00