Export caldav with logs
This commit is contained in:
1 parent
af94d397f5
commit
c05fc432eb
5 files changed
+836
-49
No files matched your search
@@ -0,0 +1,235 @@
|
||||
# Export CalDAV vers CSV
|
||||
|
||||
Le script [`crealead-caldav.py`](../crealead-caldav.py) récupère des événements
|
||||
d'agendas accessibles en CalDAV, notamment Nextcloud, et produit un fichier CSV
|
||||
par calendrier configuré. Ces fichiers peuvent ensuite être utilisés par
|
||||
`csv-to-timemanager.py`.
|
||||
|
||||
## Pré-requis
|
||||
|
||||
- Python 3, avec `pip` et le module `venv`. L'environnement Docker du dépôt
|
||||
utilise Python 3.9.2.
|
||||
- Les dépendances de [`requirements.txt`](../requirements.txt), notamment
|
||||
`caldav`, `icalendar` et `pytz`.
|
||||
- Un serveur CalDAV accessible depuis la machine d'exécution et un compte
|
||||
autorisé à lire les calendriers à exporter.
|
||||
- Des droits de lecture sur `config.json` et d'écriture sur les fichiers CSV
|
||||
ainsi que sur le dossier `logs/`, situé à côté du script.
|
||||
- Bash pour utiliser le lanceur `exec-export-caldav.sh`.
|
||||
|
||||
Depuis la racine du dépôt, préparer l'environnement :
|
||||
|
||||
```bash
|
||||
python3 -m venv env
|
||||
source env/bin/activate
|
||||
python -m pip install -r requirements.txt
|
||||
```
|
||||
|
||||
L'environnement Docker fourni constitue une autre façon d'exécuter le script
|
||||
et les tests. Sa construction est décrite dans
|
||||
[`support/docker-dev/README.md`](../support/docker-dev/README.md).
|
||||
|
||||
## Usage
|
||||
|
||||
### Configuration minimale
|
||||
|
||||
Créer un fichier `config.json` à la racine du dépôt. Exemple pour exporter les
|
||||
événements d'octobre 2026 :
|
||||
|
||||
```json
|
||||
{
|
||||
"caldav_url": "https://cloud.example.com/remote.php/dav/",
|
||||
"username": "utilisateur",
|
||||
"password": "mot-de-passe-application",
|
||||
"year_start": 2026,
|
||||
"month_start": 10,
|
||||
"day_start": 1,
|
||||
"year_end": 2026,
|
||||
"month_end": 11,
|
||||
"day_end": 1,
|
||||
"calendars": [
|
||||
{
|
||||
"calendar_name": "Temps de travail",
|
||||
"csv_name": "csv/utilisateur.csv"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| Champ | Rôle |
|
||||
| --- | --- |
|
||||
| `caldav_url` | URL du serveur CalDAV. |
|
||||
| `username`, `password` | Identifiants du compte qui peut lire les calendriers. |
|
||||
| `year_start`, `month_start`, `day_start` | Date de début de la recherche, à minuit. Les valeurs sont des nombres entiers. |
|
||||
| `year_end`, `month_end`, `day_end` | Date de fin de la recherche, à minuit. Pour couvrir un mois, utiliser le premier jour du mois suivant. |
|
||||
| `calendars` | Liste des calendriers à exporter. |
|
||||
| `calendars[].calendar_name` | Texte contenu dans le nom du calendrier distant, avec respect de la casse. Il doit correspondre à un seul calendrier. |
|
||||
| `calendars[].csv_name` | Chemin du fichier CSV à écrire. Un chemin relatif est résolu depuis le répertoire de lancement. |
|
||||
|
||||
Ajouter une entrée dans `calendars` pour chaque calendrier à exporter.
|
||||
Les champs `db_*`, `user_name` et `api_password`, utilisés par d'autres scripts
|
||||
du dépôt, ne sont pas nécessaires à cet export.
|
||||
|
||||
`config.json` est ignoré par Git. Remplacer les valeurs d'exemple par les
|
||||
informations du serveur et du compte utilisés.
|
||||
|
||||
### Lancement
|
||||
|
||||
Lancer les commandes depuis la racine du dépôt : le script lit
|
||||
`./config.json`, et le lanceur recherche `env/bin/activate` dans le répertoire
|
||||
courant. Créer au préalable les dossiers de destination des CSV ; le script
|
||||
ne les crée pas.
|
||||
|
||||
```bash
|
||||
mkdir -p csv
|
||||
bash ./exec-export-caldav.sh
|
||||
```
|
||||
|
||||
Le lanceur active l'environnement `env/`, puis exécute le script. Il est aussi
|
||||
possible de lancer directement Python :
|
||||
|
||||
```bash
|
||||
env/bin/python crealead-caldav.py
|
||||
```
|
||||
|
||||
Le script ne propose pas d'arguments en ligne de commande. La période et les
|
||||
calendriers sont définis dans `config.json`.
|
||||
|
||||
### Fichiers produits et traitement des événements
|
||||
|
||||
Chaque CSV existant est remplacé lors de l'export. Les lignes sont triées par
|
||||
date et heure de début. Le séparateur est `;` et les colonnes sont :
|
||||
|
||||
```text
|
||||
Titre;Date debut;Heure debut;Date fin;Heure fin;Categories;Emplacement;Description
|
||||
```
|
||||
|
||||
Les événements récurrents sont demandés au serveur avec `expand=True`.
|
||||
Le lecteur iCalendar prend en charge les paramètres des propriétés, comme
|
||||
`TZID` et `VALUE=DATE`, ainsi que les textes répartis sur plusieurs lignes.
|
||||
Les heures sont converties vers `Europe/Paris`. Une heure sans fuseau est
|
||||
interprétée dans ce fuseau ; une date seule est exportée à minuit.
|
||||
Si `DTEND` est absent, `DURATION` peut fournir la date de fin.
|
||||
|
||||
Les événements sans propriété `SUMMARY` sont ignorés. Ceux dont les dates
|
||||
de début ou de fin sont absentes ou invalides sont également ignorés. Chaque
|
||||
exclusion laisse un avertissement détaillé dans le journal, y compris en
|
||||
l'absence de `SUMMARY`. Un document iCalendar illisible ou une ressource sans
|
||||
composant `VEVENT` est signalé puis ignoré. Les autres événements restent
|
||||
exportés. Si un calendrier configuré est introuvable ou correspond à plusieurs
|
||||
calendriers distants, le script affiche un message et s'arrête avant l'écriture
|
||||
des CSV.
|
||||
|
||||
En cas d'erreur d'écriture d'un CSV, le script s'arrête avec une exception.
|
||||
Il laisse un avertissement pour chaque événement du calendrier en cours,
|
||||
dont le fichier peut être incomplet, et des calendriers restant à écrire.
|
||||
Les fichiers déjà écrits avec succès sont conservés.
|
||||
|
||||
### Journalisation
|
||||
|
||||
Les sorties standard et d'erreur sont affichées dans le terminal et copiées
|
||||
dans `logs/crealead-caldav.log`, à côté du script. Le dossier est créé
|
||||
automatiquement. Le journal utilise UTF-8, conserve les exécutions précédentes
|
||||
et enregistre les traces d'exception en cas de plantage pendant le traitement.
|
||||
Il n'y a pas de rotation automatique du journal. `logs/` est ignoré par Git.
|
||||
|
||||
Chaque nouvelle ligne du journal est préfixée par l'heure d'écriture en UTC,
|
||||
au format ISO 8601 `YYYY-MM-DDTHH:MM:SSZ`, puis par `[info]` :
|
||||
|
||||
```text
|
||||
[2026-10-15T12:34:56Z]: [info] - csv/utilisateur.csv ...
|
||||
```
|
||||
|
||||
Cela s'applique aussi aux messages écrits en plusieurs fragments et à chaque
|
||||
ligne d'une trace d'exception. L'horodatage est ajouté dans le fichier de
|
||||
journal, sans modifier les messages ordinaires du terminal.
|
||||
|
||||
Tout événement non exporté est signalé avec `[warning][event]`, à la place de
|
||||
`[info]`. Le message indique le calendrier distant (ou son nom configuré),
|
||||
le fichier CSV, le titre, le début, la fin, l'UID et la raison de l'exclusion :
|
||||
|
||||
```text
|
||||
[2026-10-15T12:34:56Z]: [warning][event] - Calendrier='Temps de travail' ; CSV='csv/utilisateur.csv' ; titre='Réunion' ; début='2026-10-15T08:00:00Z' ; fin='indisponible' ; UID='reunion-1' ; événement non exporté : dates de début/fin absentes ou invalides (date absente ou invalide)
|
||||
```
|
||||
|
||||
L'heure entre crochets est celle du message ; les champs `début` et `fin`
|
||||
décrivent le moment de l'événement. Les dates et heures décodées sont affichées
|
||||
en UTC, les dates de journée entière restent des dates seules. En cas de
|
||||
document illisible ou de date invalide, le script récupère les champs bruts
|
||||
disponibles. Une métadonnée absente ou irrécupérable vaut `indisponible`.
|
||||
Les retours à la ligne dans les métadonnées et le motif sont échappés pour
|
||||
garder un avertissement sur une seule ligne. Ces avertissements sont aussi
|
||||
visibles dans le terminal.
|
||||
|
||||
## Tests
|
||||
|
||||
Les tests se trouvent dans
|
||||
[`tests/test_crealead_caldav.py`](../tests/test_crealead_caldav.py) et utilisent
|
||||
`unittest`, fourni avec Python. Aucune dépendance de test supplémentaire
|
||||
n'est nécessaire.
|
||||
|
||||
### Cas couverts
|
||||
|
||||
Les 13 tests de `ExportEventsTests` vérifient :
|
||||
|
||||
- La lecture de `DTSTART` et `DTEND` avec `TZID`, leur conversion vers Paris
|
||||
et le tri chronologique des lignes.
|
||||
- La conversion des dates UTC en été et en hiver, ainsi que celle d'un autre
|
||||
fuseau horaire.
|
||||
- Les événements sur une journée entière (`VALUE=DATE`), les heures sans
|
||||
fuseau et le calcul de la fin à partir de `DURATION`.
|
||||
- L'exclusion des événements sans propriété `SUMMARY`, des dates manquantes
|
||||
ou invalides et des documents iCalendar illisibles, sans bloquer les
|
||||
événements valides, ainsi que la présence d'un avertissement contextualisé
|
||||
dans le journal pour chaque événement écarté.
|
||||
- La récupération des métadonnées de chaque événement dans un document
|
||||
illisible, la conservation d'une date brute invalide et le signalement
|
||||
d'une ressource sans composant `VEVENT`.
|
||||
- La conservation des deux-points dans les textes, des paramètres de texte,
|
||||
des catégories et des lignes repliées, ainsi que le nettoyage de la
|
||||
description HTML encodée et le contenu du CSV écrit.
|
||||
|
||||
Les 9 tests de `LogOutputTests` vérifient :
|
||||
|
||||
- La création de `logs/` à côté du script, la copie des deux sorties et
|
||||
l'écriture immédiate des messages, y compris les accents.
|
||||
- L'ajout au journal existant et le choix du nom du journal d'après celui du
|
||||
script, y compris lorsqu'une exécution se termine sans retour à la ligne.
|
||||
- L'horodatage en UTC, le préfixe `[info]` de chaque ligne, les écritures en
|
||||
plusieurs fragments, les messages sur plusieurs lignes et les lignes vides.
|
||||
- Le préfixe `[warning][event]` sans `[info]`, les métadonnées de l'événement,
|
||||
l'échappement des retours à la ligne et la séparation d'un avertissement
|
||||
après un message ordinaire inachevé.
|
||||
- La sauvegarde des traces d'exception et la restauration des sorties après
|
||||
une erreur.
|
||||
- La conservation du code de sortie d'un `SystemExit`, sans ajouter de trace
|
||||
d'exception, et la sauvegarde de son éventuel message.
|
||||
|
||||
Le test de `ExportFailureTests` provoque une erreur d'écriture CSV. Il vérifie
|
||||
qu'un avertissement est enregistré pour les événements du calendrier en
|
||||
échec et des calendriers restant à écrire, et que le premier CSV déjà écrit
|
||||
est conservé.
|
||||
|
||||
Ces tests utilisent le vrai lecteur `icalendar`, des événements synthétiques
|
||||
et des dossiers temporaires pour les CSV et les journaux. Le module CalDAV
|
||||
et les réponses du serveur sont simulés : aucun accès réseau, compte,
|
||||
`config.json` ou calendrier réel n'est nécessaire. Ils vérifient que la
|
||||
recherche demande `expand=True`, mais ne valident pas l'expansion des
|
||||
récurrences par un serveur réel ni l'import dans TimeManager.
|
||||
|
||||
### Exécution
|
||||
|
||||
Depuis la racine du dépôt, avec les dépendances installées dans `env/` :
|
||||
|
||||
```bash
|
||||
env/bin/python -B -m unittest discover -s tests -v
|
||||
```
|
||||
|
||||
L'option `-B` évite de créer des fichiers de cache Python. La suite contient
|
||||
actuellement 23 tests ; une exécution réussie se termine par `OK`.
|
||||
|
||||
Avec l'image Docker du dépôt déjà construite :
|
||||
|
||||
```bash
|
||||
docker run --rm -v "$(pwd)":/app -w /app cal-to-time-manager:dev -B -m unittest discover -s tests -v
|
||||
```
|
||||
Reference in new issue
Block a user