feat: dashboard web FastAPI Sprint 4

Ajout d'un dashboard lecture seule par-dessus la DB SQLite existante.

Fichiers créés :
  - tickettracker/web/queries.py   : 7 fonctions SQL (stats, compare, historique...)
  - tickettracker/web/api.py       : router /api/* JSON (FastAPI)
  - tickettracker/web/app.py       : routes HTML + Jinja2 + point d'entrée uvicorn
  - tickettracker/web/templates/   : base.html, index.html, compare.html, product.html, receipt.html
  - tickettracker/web/static/style.css : personnalisations Pico CSS
  - tests/test_web.py              : 19 tests (96 passent, 1 xfail OCR)

Fichiers modifiés :
  - requirements.txt : +fastapi, uvicorn[standard], jinja2, python-multipart, httpx
  - config.py        : +DB_PATH (lu depuis TICKETTRACKER_DB_PATH)

Lancement : python -m tickettracker.web.app

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 20:04:55 +01:00
parent 1e5fc97bb7
commit 30e4b3e144
13 changed files with 1334 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
{% extends "base.html" %}
{% block title %}Comparer les prix — TicketTracker{% endblock %}
{% block content %}
<h1>Comparaison Picnic vs Leclerc</h1>
{% if empty %}
<article>
<p>
Aucun produit commun trouvé entre Picnic et Leclerc.
</p>
<p>
Pour voir une comparaison, vous devez :
</p>
<ol>
<li>Importer des tickets des deux enseignes</li>
<li>Normaliser les noms d'articles avec la CLI</li>
</ol>
<pre><code>python -m tickettracker.cli normalize</code></pre>
</article>
{% else %}
<p>Produits présents chez les deux enseignes, triés par écart de prix décroissant.</p>
<div class="overflow-auto">
<table>
<thead>
<tr>
<th>Produit</th>
<th>Picnic moy.</th>
<th>Leclerc moy.</th>
<th>Écart €</th>
<th>Écart %</th>
<th></th>
</tr>
</thead>
<tbody>
{% for p in products %}
<tr>
<td>{{ p.name }}</td>
<td>{{ "%.2f"|format(p.price_picnic) }} €</td>
<td>{{ "%.2f"|format(p.price_leclerc) }} €</td>
<td class="{% if p.diff > 0 %}diff-positive{% elif p.diff < 0 %}diff-negative{% endif %}">
{{ "%+.2f"|format(p.diff) }} €
</td>
<td class="{% if p.diff > 0 %}diff-positive{% elif p.diff < 0 %}diff-negative{% endif %}">
{{ "%+.1f"|format(p.diff_pct) }} %
</td>
<td>
<a href="/product/{{ p.name | urlquote }}">Historique</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<p><small>Positif = Leclerc plus cher, négatif = Picnic plus cher.</small></p>
{% endif %}
{% endblock %}