electricore-client 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- electricore_client-0.1.0/.gitignore +202 -0
- electricore_client-0.1.0/CHANGELOG.md +31 -0
- electricore_client-0.1.0/PKG-INFO +71 -0
- electricore_client-0.1.0/README.md +44 -0
- electricore_client-0.1.0/pyproject.toml +60 -0
- electricore_client-0.1.0/src/electricore_client/__init__.py +53 -0
- electricore_client-0.1.0/src/electricore_client/arrow.py +128 -0
- electricore_client-0.1.0/src/electricore_client/client.py +180 -0
- electricore_client-0.1.0/src/electricore_client/exceptions.py +31 -0
- electricore_client-0.1.0/src/electricore_client/headers.py +44 -0
- electricore_client-0.1.0/src/electricore_client/models/__init__.py +40 -0
- electricore_client-0.1.0/src/electricore_client/models/chronologie.py +110 -0
- electricore_client-0.1.0/src/electricore_client/models/meta_periodes.py +88 -0
- electricore_client-0.1.0/src/electricore_client/models/turpe_variable.py +57 -0
- electricore_client-0.1.0/src/electricore_client/py.typed +0 -0
- electricore_client-0.1.0/src/electricore_client/streaming.py +113 -0
- electricore_client-0.1.0/src/electricore_client/transport.py +90 -0
- electricore_client-0.1.0/tests/test_arrow.py +80 -0
- electricore_client-0.1.0/tests/test_chronologie.py +154 -0
- electricore_client-0.1.0/tests/test_meta_periodes.py +197 -0
- electricore_client-0.1.0/tests/test_purity.py +39 -0
- electricore_client-0.1.0/tests/test_transport.py +83 -0
- electricore_client-0.1.0/tests/test_turpe_variable.py +131 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
!deploy/lib/
|
|
19
|
+
lib64/
|
|
20
|
+
parts/
|
|
21
|
+
sdist/
|
|
22
|
+
var/
|
|
23
|
+
wheels/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
# Usually these files are written by a python script from a template
|
|
32
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
33
|
+
*.manifest
|
|
34
|
+
*.spec
|
|
35
|
+
|
|
36
|
+
# Installer logs
|
|
37
|
+
pip-log.txt
|
|
38
|
+
pip-delete-this-directory.txt
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
*.py,cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
cover/
|
|
54
|
+
|
|
55
|
+
# Translations
|
|
56
|
+
*.mo
|
|
57
|
+
*.pot
|
|
58
|
+
|
|
59
|
+
# Django stuff:
|
|
60
|
+
*.log
|
|
61
|
+
local_settings.py
|
|
62
|
+
db.sqlite3
|
|
63
|
+
db.sqlite3-journal
|
|
64
|
+
|
|
65
|
+
# Flask stuff:
|
|
66
|
+
instance/
|
|
67
|
+
.webassets-cache
|
|
68
|
+
|
|
69
|
+
# Scrapy stuff:
|
|
70
|
+
.scrapy
|
|
71
|
+
|
|
72
|
+
# Sphinx documentation
|
|
73
|
+
docs/_build/
|
|
74
|
+
|
|
75
|
+
# PyBuilder
|
|
76
|
+
.pybuilder/
|
|
77
|
+
target/
|
|
78
|
+
|
|
79
|
+
# Jupyter Notebook
|
|
80
|
+
.ipynb_checkpoints
|
|
81
|
+
|
|
82
|
+
# IPython
|
|
83
|
+
profile_default/
|
|
84
|
+
ipython_config.py
|
|
85
|
+
|
|
86
|
+
# pyenv
|
|
87
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
88
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
89
|
+
# .python-version
|
|
90
|
+
|
|
91
|
+
# pipenv
|
|
92
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
93
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
94
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
95
|
+
# install all needed dependencies.
|
|
96
|
+
#Pipfile.lock
|
|
97
|
+
|
|
98
|
+
# UV
|
|
99
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
100
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
101
|
+
# commonly ignored for libraries.
|
|
102
|
+
#uv.lock
|
|
103
|
+
|
|
104
|
+
# poetry
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
106
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
107
|
+
# commonly ignored for libraries.
|
|
108
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
109
|
+
#poetry.lock
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
#pdm.lock
|
|
114
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
115
|
+
# in version control.
|
|
116
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
117
|
+
.pdm.toml
|
|
118
|
+
.pdm-python
|
|
119
|
+
.pdm-build/
|
|
120
|
+
|
|
121
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
122
|
+
__pypackages__/
|
|
123
|
+
|
|
124
|
+
# Celery stuff
|
|
125
|
+
celerybeat-schedule
|
|
126
|
+
celerybeat.pid
|
|
127
|
+
|
|
128
|
+
# SageMath parsed files
|
|
129
|
+
*.sage.py
|
|
130
|
+
|
|
131
|
+
# Environments
|
|
132
|
+
.env
|
|
133
|
+
.venv
|
|
134
|
+
env/
|
|
135
|
+
venv/
|
|
136
|
+
ENV/
|
|
137
|
+
env.bak/
|
|
138
|
+
venv.bak/
|
|
139
|
+
|
|
140
|
+
# Spyder project settings
|
|
141
|
+
.spyderproject
|
|
142
|
+
.spyproject
|
|
143
|
+
|
|
144
|
+
# Rope project settings
|
|
145
|
+
.ropeproject
|
|
146
|
+
|
|
147
|
+
# mkdocs documentation
|
|
148
|
+
/site
|
|
149
|
+
|
|
150
|
+
# mypy
|
|
151
|
+
.mypy_cache/
|
|
152
|
+
.dmypy.json
|
|
153
|
+
dmypy.json
|
|
154
|
+
|
|
155
|
+
# Pyre type checker
|
|
156
|
+
.pyre/
|
|
157
|
+
|
|
158
|
+
# pytype static type analyzer
|
|
159
|
+
.pytype/
|
|
160
|
+
|
|
161
|
+
# Cython debug symbols
|
|
162
|
+
cython_debug/
|
|
163
|
+
|
|
164
|
+
# PyCharm
|
|
165
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
166
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
167
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
168
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
169
|
+
#.idea/
|
|
170
|
+
|
|
171
|
+
# PyPI configuration file
|
|
172
|
+
.pypirc
|
|
173
|
+
|
|
174
|
+
# Codanna code intelligence cache
|
|
175
|
+
.codanna/
|
|
176
|
+
.codannaignore
|
|
177
|
+
.fastembed_cache/
|
|
178
|
+
|
|
179
|
+
# DLT secrets (credentials, API keys)
|
|
180
|
+
electricore/ingestion/.dlt/secrets.toml
|
|
181
|
+
|
|
182
|
+
# Données locales générées
|
|
183
|
+
*.duckdb
|
|
184
|
+
perimetre_manquants/
|
|
185
|
+
surveillance_perimetre_manquants.csv
|
|
186
|
+
.serena/
|
|
187
|
+
|
|
188
|
+
# Artefacts de déploiement Docker générés à partir des fichiers .example
|
|
189
|
+
# (les utilisateurs copient *.example vers la version sans suffixe pour les éditer)
|
|
190
|
+
deploy/docker/Caddyfile
|
|
191
|
+
deploy/docker/crontab
|
|
192
|
+
deploy/docker/docker-compose.*.yml
|
|
193
|
+
!deploy/docker/docker-compose.yml
|
|
194
|
+
|
|
195
|
+
# Sortie locale du skill /sota-scan
|
|
196
|
+
.sota/
|
|
197
|
+
|
|
198
|
+
# Artefacts de build dbt (target par base, cf. pipeline_dbt)
|
|
199
|
+
electricore/ingestion/.dbt_target_*/
|
|
200
|
+
|
|
201
|
+
# repo-scoped GitHub PAT for in-sandbox automation — never commit
|
|
202
|
+
.gh-token
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog — electricore-client
|
|
2
|
+
|
|
3
|
+
Toutes les évolutions notables de ce paquet sont consignées ici.
|
|
4
|
+
Le format suit [Keep a Changelog](https://keepachangelog.com/fr/1.0.0/) ;
|
|
5
|
+
le versionnage suit [SemVer](https://semver.org/lang/fr/).
|
|
6
|
+
|
|
7
|
+
## [0.1.0] — non publié
|
|
8
|
+
|
|
9
|
+
### Ajouté
|
|
10
|
+
- Squelette du paquet `electricore-client` (top-level `electricore_client`),
|
|
11
|
+
distribué séparément du moteur. Dépendances de base **httpx + pydantic**.
|
|
12
|
+
- Substrat de transport partagé `_BaseClient` : URL de base, en-tête
|
|
13
|
+
`X-API-Key`, timeout, conversion d'erreur HTTP **503 → `IngestionEnCours`**,
|
|
14
|
+
garde de version de contrat asymétrique (warn si serveur en avance, raise si
|
|
15
|
+
en retard).
|
|
16
|
+
- Modèle d'en-têtes de métadonnées `EnTetesMeta` (`contract_version`, `mois`,
|
|
17
|
+
`grain`).
|
|
18
|
+
- Méthode `meta_periodes(...)` : flux JSONL typé de `PeriodeMeta` (contrat v3,
|
|
19
|
+
relevés imbriqués ADR-0038), context-manager, métadonnées en en-têtes,
|
|
20
|
+
sans pagination, `.collect()`.
|
|
21
|
+
- Méthode `chronologie(...)` : flux JSONL d'une union discriminée
|
|
22
|
+
`LigneChronologie` (`LigneEvenement | LigneReleve | LignePeriodeEnergie`,
|
|
23
|
+
contrat v1), validation `pdl` XOR `rsc` côté client.
|
|
24
|
+
- Méthode `turpe_variable(...)` : POST RPC, résultats typés indexés par l'`id`
|
|
25
|
+
opaque ré-émis. Modèles `LigneTurpeVariable` / résultat single-sourcés.
|
|
26
|
+
- Extra `[arrow]` : client Arrow historique (`flux/releves/facturation/accise/
|
|
27
|
+
cta` → `pl.DataFrame`) dans `electricore_client.arrow`, polars importé
|
|
28
|
+
paresseusement (la base reste polars-free).
|
|
29
|
+
- CI/CD : `release-client.yml` (tags `client-v*`, publication PyPI Trusted
|
|
30
|
+
Publishing / OIDC) + job `test-client` (install isolé, garantie polars-free).
|
|
31
|
+
Conception : ADR-0043.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: electricore-client
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Client Python léger (httpx + pydantic) vers l'API facturiste electricore — sans polars/duckdb/fastapi
|
|
5
|
+
Project-URL: Homepage, https://github.com/Energie-De-Nantes/electricore
|
|
6
|
+
Project-URL: Repository, https://github.com/Energie-De-Nantes/electricore
|
|
7
|
+
Project-URL: Issues, https://github.com/Energie-De-Nantes/electricore/issues
|
|
8
|
+
Author: Virgile
|
|
9
|
+
License: AGPL-3.0
|
|
10
|
+
Keywords: api-client,electricore,enedis,energy,facturation
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: httpx>=0.27.0
|
|
23
|
+
Requires-Dist: pydantic>=2.0.0
|
|
24
|
+
Provides-Extra: arrow
|
|
25
|
+
Requires-Dist: polars<2.0.0,>=1.0.0; extra == 'arrow'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# electricore-client
|
|
29
|
+
|
|
30
|
+
Client Python **léger** vers l'API facturiste [electricore](https://github.com/Energie-De-Nantes/electricore).
|
|
31
|
+
|
|
32
|
+
Dépendances de base : **httpx + pydantic uniquement** — pas de polars, duckdb ni
|
|
33
|
+
fastapi. Pensé pour être consommé par `souscriptions_odoo` (Odoo 19) et tout
|
|
34
|
+
intégrateur qui n'a pas besoin de tirer le moteur entier.
|
|
35
|
+
|
|
36
|
+
Lecture seule sur electricore (« Odoo tire d'electricore », ADR-0027/0012).
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install electricore-client # base : httpx + pydantic
|
|
42
|
+
pip install "electricore-client[arrow]" # + client Arrow (DataFrames polars)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from electricore_client import ElectricoreClient
|
|
49
|
+
|
|
50
|
+
client = ElectricoreClient(url="https://electricore.example", api_key="…")
|
|
51
|
+
|
|
52
|
+
# Méta-périodes mensuelles (flux JSONL typé, sans pagination)
|
|
53
|
+
with client.meta_periodes(mois="2026-05-01") as stream:
|
|
54
|
+
print(stream.contract_version) # version de contrat (en-tête)
|
|
55
|
+
for periode in stream: # itère des PeriodeMeta typés
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
# Chronologie d'un point ou d'un contrat (union discriminée)
|
|
59
|
+
with client.chronologie(pdl="12345678901234") as stream:
|
|
60
|
+
lignes = stream.collect()
|
|
61
|
+
|
|
62
|
+
# Calculateur TURPE variable (POST RPC, pas un stream)
|
|
63
|
+
resultats = client.turpe_variable([...]) # résultats indexés par id opaque
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Le client Arrow historique (`flux/releves/facturation/accise/cta` → `pl.DataFrame`)
|
|
67
|
+
vit dans `electricore_client.arrow`, derrière l'extra `[arrow]`.
|
|
68
|
+
|
|
69
|
+
## Conception
|
|
70
|
+
|
|
71
|
+
Voir [ADR-0043](https://github.com/Energie-De-Nantes/electricore/blob/main/docs/adr/0043-electricore-client-paquet-separe.md).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# electricore-client
|
|
2
|
+
|
|
3
|
+
Client Python **léger** vers l'API facturiste [electricore](https://github.com/Energie-De-Nantes/electricore).
|
|
4
|
+
|
|
5
|
+
Dépendances de base : **httpx + pydantic uniquement** — pas de polars, duckdb ni
|
|
6
|
+
fastapi. Pensé pour être consommé par `souscriptions_odoo` (Odoo 19) et tout
|
|
7
|
+
intégrateur qui n'a pas besoin de tirer le moteur entier.
|
|
8
|
+
|
|
9
|
+
Lecture seule sur electricore (« Odoo tire d'electricore », ADR-0027/0012).
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install electricore-client # base : httpx + pydantic
|
|
15
|
+
pip install "electricore-client[arrow]" # + client Arrow (DataFrames polars)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from electricore_client import ElectricoreClient
|
|
22
|
+
|
|
23
|
+
client = ElectricoreClient(url="https://electricore.example", api_key="…")
|
|
24
|
+
|
|
25
|
+
# Méta-périodes mensuelles (flux JSONL typé, sans pagination)
|
|
26
|
+
with client.meta_periodes(mois="2026-05-01") as stream:
|
|
27
|
+
print(stream.contract_version) # version de contrat (en-tête)
|
|
28
|
+
for periode in stream: # itère des PeriodeMeta typés
|
|
29
|
+
...
|
|
30
|
+
|
|
31
|
+
# Chronologie d'un point ou d'un contrat (union discriminée)
|
|
32
|
+
with client.chronologie(pdl="12345678901234") as stream:
|
|
33
|
+
lignes = stream.collect()
|
|
34
|
+
|
|
35
|
+
# Calculateur TURPE variable (POST RPC, pas un stream)
|
|
36
|
+
resultats = client.turpe_variable([...]) # résultats indexés par id opaque
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Le client Arrow historique (`flux/releves/facturation/accise/cta` → `pl.DataFrame`)
|
|
40
|
+
vit dans `electricore_client.arrow`, derrière l'extra `[arrow]`.
|
|
41
|
+
|
|
42
|
+
## Conception
|
|
43
|
+
|
|
44
|
+
Voir [ADR-0043](https://github.com/Energie-De-Nantes/electricore/blob/main/docs/adr/0043-electricore-client-paquet-separe.md).
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "electricore-client"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Client Python léger (httpx + pydantic) vers l'API facturiste electricore — sans polars/duckdb/fastapi"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Virgile"}
|
|
7
|
+
]
|
|
8
|
+
license = {text = "AGPL-3.0"}
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
keywords = [
|
|
12
|
+
"electricore",
|
|
13
|
+
"energy",
|
|
14
|
+
"enedis",
|
|
15
|
+
"api-client",
|
|
16
|
+
"facturation",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"License :: OSI Approved :: GNU Affero General Public License v3",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Typing :: Typed",
|
|
29
|
+
]
|
|
30
|
+
# Deps de base STRICTEMENT légères : httpx + pydantic (aucune trace de
|
|
31
|
+
# polars/duckdb/fastapi). C'est l'invariant prouvé par le test de pureté.
|
|
32
|
+
dependencies = [
|
|
33
|
+
"httpx>=0.27.0",
|
|
34
|
+
"pydantic>=2.0.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
# Extra `arrow` : recolle le client Arrow historique (DataFrames polars).
|
|
39
|
+
# polars n'est importé que paresseusement, dans le sous-module Arrow — jamais
|
|
40
|
+
# au top-level (l'invariant de pureté tient sans cet extra).
|
|
41
|
+
arrow = [
|
|
42
|
+
"polars>=1.0.0,<2.0.0",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.urls]
|
|
46
|
+
Homepage = "https://github.com/Energie-De-Nantes/electricore"
|
|
47
|
+
Repository = "https://github.com/Energie-De-Nantes/electricore"
|
|
48
|
+
Issues = "https://github.com/Energie-De-Nantes/electricore/issues"
|
|
49
|
+
|
|
50
|
+
[build-system]
|
|
51
|
+
requires = ["hatchling"]
|
|
52
|
+
build-backend = "hatchling.build"
|
|
53
|
+
|
|
54
|
+
[tool.hatch.build.targets.wheel]
|
|
55
|
+
packages = ["src/electricore_client"]
|
|
56
|
+
|
|
57
|
+
[dependency-groups]
|
|
58
|
+
test = [
|
|
59
|
+
"pytest>=8.2.0",
|
|
60
|
+
]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""electricore-client — client léger vers l'API facturiste electricore.
|
|
2
|
+
|
|
3
|
+
Distribué séparément du moteur, dépendances de base **httpx + pydantic
|
|
4
|
+
uniquement** : ce paquet n'importe jamais polars/duckdb/fastapi au top-level
|
|
5
|
+
(invariant prouvé par le test de pureté). Le client Arrow historique
|
|
6
|
+
(DataFrames polars) vit dans le sous-module `electricore_client.arrow`, derrière
|
|
7
|
+
l'extra `[arrow]`, et n'est *pas* importé ici.
|
|
8
|
+
|
|
9
|
+
Lecture seule sur electricore (ADR-0012). Voir ADR-0043 pour la conception.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from .client import ElectricoreClient
|
|
15
|
+
from .exceptions import (
|
|
16
|
+
ContractVersionError,
|
|
17
|
+
ElectricoreClientError,
|
|
18
|
+
IngestionEnCours,
|
|
19
|
+
)
|
|
20
|
+
from .headers import EnTetesMeta
|
|
21
|
+
from .models import (
|
|
22
|
+
LigneChronologie,
|
|
23
|
+
LigneEvenement,
|
|
24
|
+
LignePeriodeEnergie,
|
|
25
|
+
LigneReleve,
|
|
26
|
+
LigneTurpeVariable,
|
|
27
|
+
ObjetReleve,
|
|
28
|
+
PeriodeMeta,
|
|
29
|
+
ResultatTurpeVariable,
|
|
30
|
+
TurpeVariableRequest,
|
|
31
|
+
)
|
|
32
|
+
from .streaming import JsonlStream
|
|
33
|
+
|
|
34
|
+
__version__ = "0.1.0"
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"ElectricoreClient",
|
|
38
|
+
"ElectricoreClientError",
|
|
39
|
+
"IngestionEnCours",
|
|
40
|
+
"ContractVersionError",
|
|
41
|
+
"EnTetesMeta",
|
|
42
|
+
"JsonlStream",
|
|
43
|
+
"PeriodeMeta",
|
|
44
|
+
"ObjetReleve",
|
|
45
|
+
"LigneChronologie",
|
|
46
|
+
"LigneEvenement",
|
|
47
|
+
"LigneReleve",
|
|
48
|
+
"LignePeriodeEnergie",
|
|
49
|
+
"LigneTurpeVariable",
|
|
50
|
+
"TurpeVariableRequest",
|
|
51
|
+
"ResultatTurpeVariable",
|
|
52
|
+
"__version__",
|
|
53
|
+
]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Client Arrow historique : endpoints structurés en `pl.DataFrame` (extra `[arrow]`).
|
|
2
|
+
|
|
3
|
+
Ce sous-module **n'est jamais importé au top-level** de `electricore_client` :
|
|
4
|
+
il porte le seul code qui dépend de polars, et l'importe **paresseusement** (à
|
|
5
|
+
l'appel d'une méthode). La base du paquet reste donc polars-free — l'invariant
|
|
6
|
+
prouvé par le test de pureté tient même quand `[arrow]` n'est pas installé.
|
|
7
|
+
|
|
8
|
+
Endpoints Arrow IPC (`/flux/{table}.arrow`, `/releves.arrow`, `/facturation/
|
|
9
|
+
detail.arrow`, `/taxes/{accise,cta}/detail.arrow`) — pour les notebooks distants
|
|
10
|
+
qui n'ont pas la base DuckDB en local (ADR-0009). Lecture seule (ADR-0012).
|
|
11
|
+
|
|
12
|
+
Installer avec l'extra : `pip install "electricore-client[arrow]"`.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import io
|
|
18
|
+
from typing import TYPE_CHECKING, Any
|
|
19
|
+
|
|
20
|
+
from .transport import _BaseClient
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING: # pragma: no cover - annotations only, jamais exécuté
|
|
23
|
+
import polars as pl
|
|
24
|
+
|
|
25
|
+
_HINT_INSTALL = (
|
|
26
|
+
'Le client Arrow nécessite polars : installez l\'extra `arrow` (`pip install "electricore-client[arrow]"`).'
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _polars():
|
|
31
|
+
"""Importe polars paresseusement, avec un message d'install clair s'il manque."""
|
|
32
|
+
try:
|
|
33
|
+
import polars as pl
|
|
34
|
+
except ModuleNotFoundError as exc: # pragma: no cover - testé via monkeypatch
|
|
35
|
+
raise ModuleNotFoundError(_HINT_INSTALL) from exc
|
|
36
|
+
return pl
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ElectricoreArrowClient(_BaseClient):
|
|
40
|
+
"""Client Arrow synchrone : endpoints structurés rendus en `pl.DataFrame`.
|
|
41
|
+
|
|
42
|
+
Hérite du substrat de transport partagé (URL, `X-API-Key`, timeout, 503 →
|
|
43
|
+
`IngestionEnCours`). polars n'est tiré qu'au premier appel de méthode.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def _get_arrow(self, path: str, params: dict) -> pl.DataFrame:
|
|
47
|
+
pl = _polars()
|
|
48
|
+
response = self._http.get(f"{self.url}{path}", params=params, headers=self._headers)
|
|
49
|
+
self._raise_for_status(response)
|
|
50
|
+
return pl.read_ipc_stream(io.BytesIO(response.content))
|
|
51
|
+
|
|
52
|
+
def flux(
|
|
53
|
+
self,
|
|
54
|
+
table_name: str,
|
|
55
|
+
*,
|
|
56
|
+
prm: str | None = None,
|
|
57
|
+
limit: int = 1_000_000,
|
|
58
|
+
) -> pl.DataFrame:
|
|
59
|
+
"""Contenu brut d'un flux Enedis (c15, r151, f15, etc.) en Polars.
|
|
60
|
+
|
|
61
|
+
Équivalent HTTP des `c15()`/`r151()`/`f15()` du `DuckDBQuery` — pour les
|
|
62
|
+
notebooks distants sans accès local à la base (ADR-0009).
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
table_name: nom de la table flux (`c15`, `r151`, `r15`, `f15_detail`, …).
|
|
66
|
+
prm: filtre optionnel sur la colonne `pdl`.
|
|
67
|
+
limit: nombre maximum de lignes (défaut 1 000 000, max serveur 10 000 000).
|
|
68
|
+
"""
|
|
69
|
+
params: dict[str, Any] = {"limit": limit}
|
|
70
|
+
if prm:
|
|
71
|
+
params["prm"] = prm
|
|
72
|
+
return self._get_arrow(f"/flux/{table_name}.arrow", params)
|
|
73
|
+
|
|
74
|
+
def releves(
|
|
75
|
+
self,
|
|
76
|
+
*,
|
|
77
|
+
prm: str | None = None,
|
|
78
|
+
source: str | None = None,
|
|
79
|
+
debut: str | None = None,
|
|
80
|
+
fin: str | None = None,
|
|
81
|
+
limit: int = 1_000_000,
|
|
82
|
+
) -> pl.DataFrame:
|
|
83
|
+
"""Mart de relevés canonique `releves` (ADR-0029) en Polars.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
prm: filtre optionnel sur la colonne `pdl`.
|
|
87
|
+
source: filtre optionnel (`flux_R151` / `flux_R64` / `flux_C15`).
|
|
88
|
+
debut: borne basse incluse sur `date_releve` (ex. `"2025-01-01"`).
|
|
89
|
+
fin: borne haute incluse sur `date_releve` (ex. `"2025-12-31"`).
|
|
90
|
+
limit: nombre maximum de lignes (défaut 1 000 000, max serveur 10 000 000).
|
|
91
|
+
"""
|
|
92
|
+
params: dict[str, Any] = {"limit": limit}
|
|
93
|
+
if prm:
|
|
94
|
+
params["prm"] = prm
|
|
95
|
+
if source:
|
|
96
|
+
params["source"] = source
|
|
97
|
+
if debut:
|
|
98
|
+
params["debut"] = debut
|
|
99
|
+
if fin:
|
|
100
|
+
params["fin"] = fin
|
|
101
|
+
return self._get_arrow("/releves.arrow", params)
|
|
102
|
+
|
|
103
|
+
def facturation(self, mois: str | None = None) -> pl.DataFrame:
|
|
104
|
+
"""`lignes_facture_rapprochees` pour le mois donné.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
mois: "YYYY-MM-DD" — défaut : dernier mois disponible côté serveur.
|
|
108
|
+
"""
|
|
109
|
+
return self._get_arrow("/facturation/detail.arrow", {"mois": mois} if mois else {})
|
|
110
|
+
|
|
111
|
+
def accise(self, trimestre: str | None = None) -> pl.DataFrame:
|
|
112
|
+
"""Détail Accise TICFE pour le trimestre donné.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
trimestre: "YYYY-TX" (ex. "2025-T1") — défaut : tous les trimestres.
|
|
116
|
+
"""
|
|
117
|
+
return self._get_arrow("/taxes/accise/detail.arrow", {"trimestre": trimestre} if trimestre else {})
|
|
118
|
+
|
|
119
|
+
def cta(self, trimestre: str | None = None) -> pl.DataFrame:
|
|
120
|
+
"""Détail CTA mensuel pour le trimestre donné.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
trimestre: "YYYY-TX" (ex. "2025-T1") — défaut : tous les trimestres.
|
|
124
|
+
"""
|
|
125
|
+
return self._get_arrow("/taxes/cta/detail.arrow", {"trimestre": trimestre} if trimestre else {})
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
__all__ = ["ElectricoreArrowClient"]
|