Skip to main content

Moteur de traitement données énergétiques françaises - Architecture Polars/DuckDB pour flux Enedis

Project description

⚡ ElectriCore - Moteur de traitement données énergétiques

ElectriCore est un outil libre pour reprendre le contrôle des données du réseau électrique français. Architecture moderne Polars + DuckDB pour transformer les flux bruts Enedis en données exploitables par LibreWatt, Odoo et autres outils de suivi énergétique.

🎯 Objectifs

Un outil de calcul énergétique performant et maintenable pour :

  • Transformer les flux XML/CSV Enedis en données structurées
  • Calculer les indicateurs essentiels (périmètre, abonnements, consommations, TURPE)
  • Exposer les données via API REST sécurisée
  • Intégrer avec Odoo et autres systèmes tiers

🏗️ Architecture - 3 Modules Principaux

electricore/
├── etl/              # 📥 ETL - Extraction & Transformation (DLT)
│   ├── sources/      # Sources de données (SFTP Enedis)
│   ├── transformers/ # Transformations modulaires (crypto, archive, parsers)
│   └── connectors/   # Connecteurs externes (Odoo)
│
├── core/             # 🧮 CORE - Calculs énergétiques (Polars)
│   ├── models/       # Modèles Pandera (validation des données)
│   ├── pipelines/    # Pipelines de calcul (périmètre, abonnements, énergie, turpe)
│   └── loaders/      # Query builders (DuckDB, Polars)
│
└── api/              # 🌐 API - Accès aux données (FastAPI)
    ├── services/     # Services de requêtage (DuckDB)
    └── main.py       # Application FastAPI avec authentification

Diagramme de flux

graph TB
    SFTP_Enedis[/SFTP Enedis\] --> ETL_Enedis[ETL<br/>#40;data load tool#41;]
    SFTP_Axpo[/SFTP Axpo<br/>Courbes\] -.-> ETL_Axpo[ETL<br/>#40;data load tool#41;]
    ETL_Enedis --> DuckDB[(DuckDB)]
    ETL_Axpo -.-> DuckDB
    Odoo[(Odoo ERP)] --> OdooReader[OdooReader]
    OdooWriter[OdooWriter] --> Odoo

    DuckDB --> API[API REST<br/>#40;FastAPI#41;]
    DuckDB -->|Query Builder| Core[Core Pipelines<br/>#40;Polars#41;]
    OdooReader -->|Query Builder| Core
    OdooReader --> API
    Core --> API
    Core --> OdooWriter

    API -->|JSON| Client[\Clients API/]

    style API fill:#4CAF50,stroke:#2E7D32,stroke-width:3px,color:#fff
    style DuckDB fill:#1976D2,stroke:#0D47A1,color:#fff
    style Odoo fill:#FF9800,stroke:#E65100,color:#fff
    style Core fill:#9C27B0,stroke:#4A148C,color:#fff
    style ETL_Axpo stroke-dasharray: 5 5

📥 Module ETL - Extraction & Transformation

Pipeline ETL modulaire basé sur DLT (Data Load Tool) pour extraire et transformer les flux Enedis.

Flux supportés

Flux Description Tables générées
C15 Changements contractuels flux_c15
F12 Facturation distributeur flux_f12
F15 Facturation détaillée flux_f15_detail
R15 Relevés avec événements flux_r15, flux_r15_acc
R151 Relevés périodiques flux_r151
R64 Relevés JSON timeseries flux_r64

Architecture modulaire

# Pipeline ETL avec transformers chaînables
encrypted_files | decrypt_transformer | unzip_transformer | parse_transformer

Transformers disponibles :

  • crypto.py - Déchiffrement AES
  • archive.py - Extraction ZIP
  • parsers.py - Parsing XML/CSV

Utilisation

# Test rapide (2 fichiers)
poetry run python electricore/etl/pipeline_production.py test

# R151 complet (~6 secondes)
poetry run python electricore/etl/pipeline_production.py r151

# Tous les flux (production)
poetry run python electricore/etl/pipeline_production.py all

Résultat : Base DuckDB electricore/etl/flux_enedis_pipeline.duckdb avec toutes les tables flux.

📖 Documentation complète : electricore/etl/README.md


🧮 Module Core - Calculs Énergétiques Polars

Pipelines de calculs énergétiques basés sur Polars pur (LazyFrames + expressions fonctionnelles).

Pipelines disponibles

1. Périmètre - Détection changements contractuels

from electricore.core.pipelines.perimetre import pipeline_perimetre
from electricore.core.loaders import c15

# Depuis DuckDB avec Query Builder
historique_lf = (
    c15()
    .filter({"Date_Evenement": ">= '2024-01-01'"})
    .limit(1000)
    .lazy()
)

perimetre_df = pipeline_perimetre(historique_lf).collect()
# Colonnes: pdl, Date_Evenement, impacte_abonnement, impacte_energie, resume_modification

2. Abonnements - Périodes d'abonnement

from electricore.core.pipelines.abonnements import pipeline_abonnements

# Calcul périodes d'abonnement avec bornes temporelles
abonnements_df = pipeline_abonnements(
    perimetre_lf,
    date_debut="2024-01-01",
    date_fin="2024-12-31"
).collect()
# Colonnes: pdl, debut, fin, nb_jours, Puissance_Souscrite, Formule_Tarifaire_Acheminement

3. Énergies - Consommations par cadran

from electricore.core.pipelines.energie import pipeline_energie
from electricore.core.loaders import releves

relevés_lf = releves().filter({"date_releve": ">= '2024-01-01'"}).lazy()

energies_df = pipeline_energie(
    perimetre_lf,
    relevés_lf,
    date_debut="2024-01-01",
    date_fin="2024-12-31"
).collect()
# Colonnes: pdl, debut, fin, energie_hp, energie_hc, energie_base, ...

4. TURPE - Calcul taxes réglementaires

from electricore.core.pipelines.turpe import ajouter_turpe_fixe, ajouter_turpe_variable

# TURPE fixe (abonnement)
abonnements_turpe_df = ajouter_turpe_fixe(abonnements_df).collect()
# Colonnes: ..., turpe_fixe_annuel, turpe_fixe_journalier, turpe_fixe_periode

# TURPE variable (énergies)
energies_turpe_df = ajouter_turpe_variable(energies_df).collect()
# Colonnes: ..., turpe_hpb, turpe_hcb, turpe_hph, turpe_hch, turpe_variable_total

5. Facturation - Pipeline complet

from electricore.core.pipelines.orchestration import facturation

# Pipeline complet : périmètre → abonnements → énergies
resultat = facturation(
    historique_lf,
    relevés_lf,
    date_debut="2024-01-01",
    date_fin="2024-12-31"
)

# Résultats disponibles
print(resultat.abonnements.collect())  # Périodes d'abonnement
print(resultat.energies.collect())      # Consommations
print(resultat.factures.collect())      # Synthèses mensuelles

🔧 Interfaces de Requêtage

DuckDB Query Builder - Architecture Fonctionnelle Modulaire

Architecture en 6 modules pour performance et maintenabilité :

  • config.py - Configuration et connexions DuckDB
  • expressions.py - Expressions Polars pures réutilisables
  • transforms.py - Transformations composables avec compose()
  • sql.py - Génération SQL fonctionnelle (dataclasses frozen)
  • query.py - Query builder immutable (DuckDBQuery)
  • __init__.py - API publique + helper _CTEQuery pour requêtes CTE
from electricore.core.loaders import c15, r151, releves, releves_harmonises

# Historique périmètre (flux C15)
historique = (
    c15()
    .filter({"Date_Evenement": ">= '2024-01-01'"})
    .limit(100)
    .collect()
)

# Relevés périodiques (flux R151)
relevés = (
    r151()
    .filter({"pdl": ["PDL123", "PDL456"]})
    .limit(1000)
    .lazy()  # Retourne LazyFrame pour optimisations
)

# Relevés unifiés (R151 + R15) avec CTE
tous_releves = releves().collect()

# Relevés harmonisés (R151 + R64) avec CTE
releves_cross_flux = (
    releves_harmonises()
    .filter({"flux_origine": "R64"})
    .collect()
)

Fonctions disponibles : c15(), r151(), r15(), f15(), r64(), releves(), releves_harmonises()

Caractéristiques :

  • ✅ Immutabilité garantie (frozen dataclasses)
  • ✅ Composition fonctionnelle pure
  • ✅ Lazy evaluation optimisée
  • ✅ Support CTE (Common Table Expressions)
  • ✅ Validation Pandera intégrée

📖 Documentation complète : electricore/core/loaders/DUCKDB_INTEGRATION_GUIDE.md

Odoo Query Builder - Intégration ERP

from electricore.core.loaders import OdooReader
import polars as pl

config = {
    'url': 'https://odoo.example.com',
    'db': 'production',
    'username': 'api_user',
    'password': 'secret'
}

with OdooReader(config) as odoo:
    # Query builder avec navigation relationnelle
    factures_df = (
        odoo.query('sale.order', domain=[('x_pdl', '!=', False)])
        .follow('invoice_ids', fields=['name', 'invoice_date', 'amount_total'])
        .filter(pl.col('amount_total') > 100)
        .collect()
    )

    # Enrichissement avec données liées
    commandes_enrichies = (
        odoo.query('sale.order', fields=['name', 'date_order'])
        .enrich('partner_id', fields=['name', 'email'])
        .collect()
    )

Méthodes disponibles : .query(), .follow(), .enrich(), .filter(), .select(), .rename(), .collect()

📖 Documentation complète : docs/odoo-query-builder.md


🌐 Module API - Accès aux Données

API REST sécurisée basée sur FastAPI pour accéder aux données flux depuis DuckDB.

Endpoints

Publics (sans authentification)

  • GET / - Informations API et tables disponibles
  • GET /health - Statut API et base de données
  • GET /docs - Documentation Swagger interactive

Sécurisés (authentification requise)

  • GET /flux/{table_name} - Données d'une table flux
  • GET /flux/{table_name}/info - Métadonnées d'une table
  • GET /admin/api-keys - Configuration clés API

Utilisation

# Démarrer l'API
poetry run uvicorn electricore.api.main:app --reload

# Requête avec authentification
curl -H "X-API-Key: votre_cle" "http://localhost:8000/flux/r151?limit=10"

# Filtrer par PDL
curl -H "X-API-Key: votre_cle" "http://localhost:8000/flux/c15?prm=12345678901234"

# Métadonnées d'une table
curl -H "X-API-Key: votre_cle" "http://localhost:8000/flux/r151/info"

Configuration

Créer un fichier .env :

# Clé API (générer avec: python -c "import secrets; print(secrets.token_urlsafe(32))")
API_KEY=votre_cle_secrete_generee

# Ou plusieurs clés
API_KEYS=cle1,cle2,cle3

# Options d'authentification
ENABLE_API_KEY_HEADER=true
ENABLE_API_KEY_QUERY=false

📖 Documentation complète : electricore/api/README.md


🚀 Installation & Usage

Prérequis

  • Python 3.12+
  • Poetry

Installation

# Cloner le projet
git clone https://github.com/votre-org/electricore.git
cd electricore

# Installer les dépendances
poetry install

Commandes essentielles

# Tests
poetry run pytest -q

# Pipeline ETL complet
poetry run python electricore/etl/pipeline_production.py all

# API FastAPI
poetry run uvicorn electricore.api.main:app --reload

# Notebooks interactifs (Marimo)
poetry run marimo edit notebooks/demo_pipeline_abonnements_polars.py

🧪 Tests & Validation

Suite de tests moderne avec 186 tests (tous passants ✅) :

Infrastructure de test

  • Configuration pytest : 8 markers (unit, integration, slow, smoke, duckdb, odoo, hypothesis, skip_ci)
  • Fixtures partagées : Connexions DuckDB temporaires, données minimales, helpers d'assertion
  • Tests paramétrés : 39 tests avec @pytest.mark.parametrize pour réduire duplication
  • Tests snapshot : 10 tests Syrupy pour détection automatique de régression
  • Script anonymisation : Extraction sécurisée de cas métier réels

Types de tests

  • Tests unitaires (26 paramétrés) - Expressions Polars pures (périmètre, TURPE)
  • Tests d'intégration (10 snapshot) - Pipelines complets avec validation Pandera
  • Tests DuckDB - Query builders et transformations
  • Fixtures métier - Cas réels (MCT, MES/RES, changements)

Commandes

# Tous les tests
pytest

# Tests rapides uniquement
pytest -m unit

# Tests critiques (CI)
pytest -m smoke

# Exécution parallèle
pytest -n auto

# Avec coverage
pytest --cov=electricore --cov-report=html

Couverture : 49% (focus sur qualité plutôt que quantité)

📖 Documentation complète : tests/README.md


📊 Migration Polars - Complète ✅

ElectriCore utilise une architecture 100% Polars pour des performances optimales.

Avantages de l'architecture Polars

  • Performance : Zero-copy, vectorisation SIMD, multi-threading
  • 🔧 Lazy evaluation : Optimisations automatiques des requêtes
  • 🧩 Expressions pures : Code fonctionnel composable et testable
  • 🌐 Écosystème moderne : Compatible Arrow, DuckDB, Cloud
  • 🚀 Pérenne : Abandon dépendances pandas historiques

Pipelines migrés

  • Pipeline périmètre : 8 expressions composables + validation
  • Pipeline abonnements : Calcul périodes avec bornes temporelles
  • Pipeline énergies : Calcul consommations tous cadrans
  • Pipeline TURPE : Taxes fixes + variables avec validation réglementaire
  • Pipeline facturation : Orchestration complète avec agrégations

🗺️ Roadmap

Complété ✅

  • Migration Polars complète (périmètre, abonnements, énergies, turpe)
  • Query Builder DuckDB avec architecture fonctionnelle modulaire (6 modules)
  • Connecteur Odoo avec Query Builder
  • API FastAPI sécurisée avec authentification
  • Pipeline ETL modulaire avec DLT
  • Tests unitaires et validation (140 tests passent)

En cours 🔄

  • CI/CD GitHub Actions
  • Documentation API détaillée (OpenAPI)
  • Suivi et métriques de performance

À venir 📅

  • API SOAP Enedis (alternative SFTP)
  • Gestion prestations et affaires
  • Nouveaux connecteurs (Axpo, autres sources)
  • Calculs avancés (MCT, cas complexes)
  • Suivi des souscriptions aux services de données

📚 Documentation Complémentaire


🤝 Contribution

Les contributions sont les bienvenues ! Avant toute modification :

  1. Lancer les tests : poetry run pytest -q
  2. Vérifier la cohérence avec les patterns Polars existants
  3. Documenter les nouvelles fonctionnalités
  4. Suivre les conventions de code du projet

📄 Licence

GPLv3 - Voir LICENSE


🙏 Remerciements

  • Polars - Framework data processing moderne
  • DuckDB - Base analytique embarquée
  • DLT - Pipeline ETL déclaratif
  • FastAPI - Framework API performant
  • Pandera - Validation schémas données

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

electricore-1.3.0.tar.gz (102.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

electricore-1.3.0-py3-none-any.whl (131.9 kB view details)

Uploaded Python 3

File details

Details for the file electricore-1.3.0.tar.gz.

File metadata

  • Download URL: electricore-1.3.0.tar.gz
  • Upload date:
  • Size: 102.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.4 CPython/3.13.7 Linux/6.16.4-3-cachyos

File hashes

Hashes for electricore-1.3.0.tar.gz
Algorithm Hash digest
SHA256 06a7641687f1838661e572e9f5f1afd8b0a44c904faac4f8a07eb1939573a404
MD5 1441d64b725aa455927c163d1b8b09ad
BLAKE2b-256 a5dc2ecc0fba896315bbbcf8985a3f4aa0e4b1ac6d8e7fec02366cf53ac1b948

See more details on using hashes here.

File details

Details for the file electricore-1.3.0-py3-none-any.whl.

File metadata

  • Download URL: electricore-1.3.0-py3-none-any.whl
  • Upload date:
  • Size: 131.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.4 CPython/3.13.7 Linux/6.16.4-3-cachyos

File hashes

Hashes for electricore-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1e60bcc53266b0ef8d99ff5f991c2152cc54a306146f59c5858f8eea485a8600
MD5 480e1b61cf94a9a5aab71c26a0449676
BLAKE2b-256 e1b3eb735f65ad82d77351bff581c3d813684ea104ffb2a6bffdfda3afecaba5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page