feat(saints-dictons): table saint_du_jour + API + import standalone 366j

- Nouveau modèle SaintDuJour (mois+jour+saints_json, indépendant de l'année)
- Router /api/saints et /api/saints/jour (mois+jour → liste de prénoms)
- Script standalone import_webapp_db.py : saints_du_jour.json → saint_du_jour,
  dictons_du_jour.json → dicton ; modes replace/append, --dry-run, --region
- Données JSON 366 jours : saints_du_jour.json + dictons_du_jour.json
- Scripts scraping/export calendrier_lunaire/saints_dictons/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 19:54:47 +01:00
parent a9f0556d73
commit 0d3bf205b1
9 changed files with 8886 additions and 0 deletions

View File

@@ -49,6 +49,7 @@ from app.routers import ( # noqa
media,
tools,
dictons,
saints,
astuces,
recoltes,
lunar,
@@ -64,6 +65,7 @@ app.include_router(settings.router, prefix="/api")
app.include_router(media.router, prefix="/api")
app.include_router(tools.router, prefix="/api")
app.include_router(dictons.router, prefix="/api")
app.include_router(saints.router, prefix="/api")
app.include_router(astuces.router, prefix="/api")
app.include_router(recoltes.router, prefix="/api")
app.include_router(lunar.router, prefix="/api")

View File

@@ -9,3 +9,4 @@ from app.models.dicton import Dicton # noqa
from app.models.astuce import Astuce # noqa
from app.models.recolte import Recolte, Observation # noqa
from app.models.meteo import MeteoStation, MeteoOpenMeteo # noqa
from app.models.saint import SaintDuJour # noqa

View File

@@ -0,0 +1,14 @@
from typing import Optional
from sqlmodel import Field, SQLModel
class SaintDuJour(SQLModel, table=True):
"""Saints fêtés pour un jour donné (indépendant de l'année)."""
__tablename__ = "saint_du_jour"
id: Optional[int] = Field(default=None, primary_key=True)
mois: int = Field(index=True) # 1-12
jour: int = Field(index=True) # 1-31
saints_json: str = Field(default="[]") # JSON array : ["St-Basile", "St-Grégoire", ...]
source_url: Optional[str] = None # URL source de scraping

View File

@@ -0,0 +1,57 @@
"""Router saints — consultation des saints du jour (indépendant de l'année)."""
import json
from typing import Any, List, Optional
from fastapi import APIRouter, Depends, Query
from sqlmodel import Session, select
from app.database import get_session
from app.models.saint import SaintDuJour
router = APIRouter(tags=["saints"])
@router.get("/saints", response_model=List[SaintDuJour])
def list_saints(
mois: Optional[int] = Query(None, ge=1, le=12),
jour: Optional[int] = Query(None, ge=1, le=31),
session: Session = Depends(get_session),
):
"""Liste les saints. Filtrer par mois et/ou jour."""
q = select(SaintDuJour)
if mois is not None:
q = q.where(SaintDuJour.mois == mois)
if jour is not None:
q = q.where(SaintDuJour.jour == jour)
q = q.order_by(SaintDuJour.mois, SaintDuJour.jour)
return session.exec(q).all()
@router.get("/saints/jour", response_model=dict)
def get_saints_du_jour(
mois: int = Query(..., ge=1, le=12),
jour: int = Query(..., ge=1, le=31),
session: Session = Depends(get_session),
) -> dict[str, Any]:
"""Retourne les saints et leur liste parsée pour un jour précis."""
row = session.exec(
select(SaintDuJour).where(
SaintDuJour.mois == mois,
SaintDuJour.jour == jour,
)
).first()
if not row:
return {"mois": mois, "jour": jour, "saints": []}
try:
saints_list = json.loads(row.saints_json)
except (json.JSONDecodeError, TypeError):
saints_list = []
return {
"mois": row.mois,
"jour": row.jour,
"saints": saints_list,
"source_url": row.source_url,
}