pygazpar 1.3.0a2__tar.gz → 1.3.0a6__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {pygazpar-1.3.0a2/pygazpar.egg-info → pygazpar-1.3.0a6}/PKG-INFO +5 -2
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar/datasource.py +115 -46
- pygazpar-1.3.0a6/pygazpar/version.py +1 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6/pygazpar.egg-info}/PKG-INFO +5 -2
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/tests/test_client.py +1 -1
- pygazpar-1.3.0a2/pygazpar/version.py +0 -1
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/LICENSE.md +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/MANIFEST.in +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/README.md +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar/__init__.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar/__main__.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar/client.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar/enum.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar/excelparser.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar/jsonparser.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar/resources/daily_data_sample.json +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar/resources/hourly_data_sample.json +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar/resources/monthly_data_sample.json +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar/resources/weekly_data_sample.json +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar/resources/yearly_data_sample.json +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar.egg-info/SOURCES.txt +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar.egg-info/dependency_links.txt +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar.egg-info/entry_points.txt +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar.egg-info/not-zip-safe +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar.egg-info/requires.txt +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/pygazpar.egg-info/top_level.txt +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/samples/__init__.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/samples/excelSample.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/samples/jsonSample.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/samples/testSample.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/setup.cfg +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/setup.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/tests/__init__.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/tests/test_datafileparser.py +0 -0
- {pygazpar-1.3.0a2 → pygazpar-1.3.0a6}/tests/test_datasource.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pygazpar
|
3
|
-
Version: 1.3.
|
3
|
+
Version: 1.3.0a6
|
4
4
|
Summary: Retrieve gas consumption from GrDF web site (French Gas Company)
|
5
5
|
Home-page: https://github.com/ssenart/pygazpar
|
6
6
|
Author: Stephane Senart
|
@@ -197,9 +197,12 @@ Description: # PyGazpar
|
|
197
197
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
198
198
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
199
199
|
|
200
|
-
|
200
|
+
|
201
|
+
## [1.2.1](https://github.com/ssenart/PyGazpar/compare/1.2.0...1.2.1) - 2024-05-04
|
201
202
|
|
202
203
|
### Fixed
|
204
|
+
- [#64](https://github.com/ssenart/PyGazpar/issues/64): [Issue] Captcha failed issue.
|
205
|
+
|
203
206
|
- [#63](https://github.com/ssenart/PyGazpar/issues/63): [Bug] If the latest received consumption is Sunday, then the last weekly period is duplicated.
|
204
207
|
|
205
208
|
## [1.2.0](https://github.com/ssenart/PyGazpar/compare/1.1.6...1.2.0) - 2022-12-16
|
@@ -2,7 +2,9 @@ import logging
|
|
2
2
|
import glob
|
3
3
|
import os
|
4
4
|
import json
|
5
|
+
import time
|
5
6
|
import pandas as pd
|
7
|
+
import http.cookiejar
|
6
8
|
from abc import ABC, abstractmethod
|
7
9
|
from typing import Any, List, Dict, cast, Optional
|
8
10
|
from requests import Session
|
@@ -11,15 +13,22 @@ from pygazpar.enum import Frequency, PropertyName
|
|
11
13
|
from pygazpar.excelparser import ExcelParser
|
12
14
|
from pygazpar.jsonparser import JsonParser
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
LOGIN_PAYLOAD = """{{
|
18
|
-
"email": "{0}",
|
16
|
+
SESSION_TOKEN_URL = "https://connexion.grdf.fr/api/v1/authn"
|
17
|
+
SESSION_TOKEN_PAYLOAD = """{{
|
18
|
+
"username": "{0}",
|
19
19
|
"password": "{1}",
|
20
|
-
"
|
21
|
-
|
22
|
-
|
20
|
+
"options": {{
|
21
|
+
"multiOptionalFactorEnroll": "false",
|
22
|
+
"warnBeforePasswordExpired": "false"
|
23
|
+
}}
|
24
|
+
}}"""
|
25
|
+
|
26
|
+
AUTH_TOKEN_URL = "https://connexion.grdf.fr/login/sessionCookieRedirect"
|
27
|
+
AUTH_TOKEN_PARAMS = """{{
|
28
|
+
"checkAccountSetupComplete": "true",
|
29
|
+
"token": "{0}",
|
30
|
+
"redirectUrl": "https://monespace.grdf.fr"
|
31
|
+
}}"""
|
23
32
|
|
24
33
|
Logger = logging.getLogger(__name__)
|
25
34
|
|
@@ -50,56 +59,59 @@ class WebDataSource(IDataSource):
|
|
50
59
|
# ------------------------------------------------------
|
51
60
|
def load(self, pceIdentifier: str, startDate: date, endDate: date, frequencies: Optional[List[Frequency]] = None) -> MeterReadingsByFrequency:
|
52
61
|
|
53
|
-
|
54
|
-
|
55
|
-
session.headers.update(LOGIN_HEADER)
|
56
|
-
|
57
|
-
self._login(session, self.__username, self.__password)
|
62
|
+
auth_token = self._login(self.__username, self.__password)
|
58
63
|
|
59
|
-
res = self._loadFromSession(
|
64
|
+
res = self._loadFromSession(auth_token, pceIdentifier, startDate, endDate, frequencies)
|
60
65
|
|
61
66
|
Logger.debug("The data update terminates normally")
|
62
67
|
|
63
68
|
return res
|
64
69
|
|
65
70
|
# ------------------------------------------------------
|
66
|
-
def _login(self,
|
71
|
+
def _login(self, username: str, password: str) -> str:
|
67
72
|
|
68
|
-
|
69
|
-
session.
|
70
|
-
|
71
|
-
|
72
|
-
auth_nonce = session.cookies.get("auth_nonce")
|
73
|
+
session = Session()
|
74
|
+
session.headers.update({"domain": "grdf.fr"})
|
75
|
+
session.headers.update({"Content-Type": "application/json"})
|
76
|
+
session.headers.update({"X-Requested-With": "XMLHttpRequest"})
|
73
77
|
|
74
|
-
|
75
|
-
payload = LOGIN_PAYLOAD.format(username, password, auth_nonce)
|
78
|
+
payload = SESSION_TOKEN_PAYLOAD.format(username, password)
|
76
79
|
|
77
|
-
|
78
|
-
data = json.loads(payload)
|
80
|
+
response = session.post(SESSION_TOKEN_URL, data=payload)
|
79
81
|
|
80
|
-
|
81
|
-
|
82
|
+
if response.status_code != 200:
|
83
|
+
raise Exception(f"An error occurred while logging in. Status code: {response.status_code} - {response.text}")
|
82
84
|
|
83
|
-
|
84
|
-
loginData = response.json()
|
85
|
+
session_token = response.json().get("sessionToken")
|
85
86
|
|
86
|
-
|
87
|
+
Logger.debug("Session token: %s", session_token)
|
88
|
+
|
89
|
+
jar = http.cookiejar.CookieJar()
|
87
90
|
|
88
|
-
|
89
|
-
|
91
|
+
session = Session()
|
92
|
+
session.headers.update({"Content-Type": "application/json"})
|
93
|
+
session.headers.update({"X-Requested-With": "XMLHttpRequest"})
|
94
|
+
|
95
|
+
params = json.loads(AUTH_TOKEN_PARAMS.format(session_token))
|
96
|
+
|
97
|
+
response = session.get(AUTH_TOKEN_URL, params=params, allow_redirects=True, cookies=jar)
|
98
|
+
|
99
|
+
if response.status_code != 200:
|
100
|
+
raise Exception(f"An error occurred while getting the auth token. Status code: {response.status_code} - {response.text}")
|
90
101
|
|
91
|
-
|
92
|
-
|
102
|
+
auth_token = session.cookies.get("auth_token", domain="monespace.grdf.fr")
|
103
|
+
|
104
|
+
return auth_token
|
93
105
|
|
94
106
|
@abstractmethod
|
95
|
-
def _loadFromSession(self,
|
107
|
+
def _loadFromSession(self, auth_token: str, pceIdentifier: str, startDate: date, endDate: date, frequencies: Optional[List[Frequency]] = None) -> MeterReadingsByFrequency:
|
96
108
|
pass
|
97
109
|
|
98
110
|
|
99
111
|
# ------------------------------------------------------------------------------------------------------------
|
100
112
|
class ExcelWebDataSource(WebDataSource):
|
101
113
|
|
102
|
-
DATA_URL = "https://monespace.grdf.fr/api/e-conso/pce/consommation/informatives/telecharger?dateDebut={0}&dateFin={1}&frequence={3}&pceList
|
114
|
+
DATA_URL = "https://monespace.grdf.fr/api/e-conso/pce/consommation/informatives/telecharger?dateDebut={0}&dateFin={1}&frequence={3}&pceList[]={2}"
|
103
115
|
|
104
116
|
DATE_FORMAT = "%Y-%m-%d"
|
105
117
|
|
@@ -121,7 +133,7 @@ class ExcelWebDataSource(WebDataSource):
|
|
121
133
|
self.__tmpDirectory = tmpDirectory
|
122
134
|
|
123
135
|
# ------------------------------------------------------
|
124
|
-
def _loadFromSession(self,
|
136
|
+
def _loadFromSession(self, auth_token: str, pceIdentifier: str, startDate: date, endDate: date, frequencies: Optional[List[Frequency]] = None) -> MeterReadingsByFrequency:
|
125
137
|
|
126
138
|
res = {}
|
127
139
|
|
@@ -145,11 +157,31 @@ class ExcelWebDataSource(WebDataSource):
|
|
145
157
|
# Inject parameters.
|
146
158
|
downloadUrl = ExcelWebDataSource.DATA_URL.format(startDate.strftime(ExcelWebDataSource.DATE_FORMAT), endDate.strftime(ExcelWebDataSource.DATE_FORMAT), pceIdentifier, ExcelWebDataSource.FREQUENCY_VALUES[frequency])
|
147
159
|
|
148
|
-
session.get(downloadUrl) # First request does not return anything : strange...
|
149
|
-
|
150
160
|
Logger.debug(f"Loading data of frequency {ExcelWebDataSource.FREQUENCY_VALUES[frequency]} from {startDate.strftime(ExcelWebDataSource.DATE_FORMAT)} to {endDate.strftime(ExcelWebDataSource.DATE_FORMAT)}")
|
151
161
|
|
152
|
-
|
162
|
+
# Retry mechanism.
|
163
|
+
retry = 10
|
164
|
+
while retry > 0:
|
165
|
+
|
166
|
+
# Create a session.
|
167
|
+
session = Session()
|
168
|
+
session.headers.update({"Host": "monespace.grdf.fr"})
|
169
|
+
session.headers.update({"Domain": "grdf.fr"})
|
170
|
+
session.headers.update({"X-Requested-With": "XMLHttpRequest"})
|
171
|
+
session.headers.update({"Accept": "application/json"})
|
172
|
+
session.cookies.set("auth_token", auth_token, domain="monespace.grdf.fr")
|
173
|
+
|
174
|
+
try:
|
175
|
+
self.__downloadFile(session, downloadUrl, self.__tmpDirectory)
|
176
|
+
break
|
177
|
+
except Exception as e:
|
178
|
+
|
179
|
+
if retry == 1:
|
180
|
+
raise e
|
181
|
+
|
182
|
+
Logger.error("An error occurred while loading data. Retry in 3 seconds.")
|
183
|
+
time.sleep(3)
|
184
|
+
retry -= 1
|
153
185
|
|
154
186
|
# Load the XLSX file into the data structure
|
155
187
|
file_list = glob.glob(data_file_path_pattern)
|
@@ -159,7 +191,11 @@ class ExcelWebDataSource(WebDataSource):
|
|
159
191
|
|
160
192
|
for filename in file_list:
|
161
193
|
res[frequency.value] = ExcelParser.parse(filename, frequency if frequency != Frequency.YEARLY else Frequency.DAILY)
|
162
|
-
|
194
|
+
try:
|
195
|
+
# openpyxl does not close the file properly.
|
196
|
+
os.remove(filename)
|
197
|
+
except Exception:
|
198
|
+
pass
|
163
199
|
|
164
200
|
# We compute yearly from daily data.
|
165
201
|
if frequency == Frequency.YEARLY:
|
@@ -172,6 +208,12 @@ class ExcelWebDataSource(WebDataSource):
|
|
172
208
|
|
173
209
|
response = session.get(url)
|
174
210
|
|
211
|
+
if "text/html" in response.headers.get("Content-Type"):
|
212
|
+
raise Exception("An error occurred while loading data. Please check your credentials.")
|
213
|
+
|
214
|
+
if response.status_code != 200:
|
215
|
+
raise Exception(f"An error occurred while loading data. Status code: {response.status_code} - {response.text}")
|
216
|
+
|
175
217
|
response.raise_for_status()
|
176
218
|
|
177
219
|
filename = response.headers["Content-Disposition"].split("filename=")[1]
|
@@ -210,7 +252,7 @@ class ExcelFileDataSource(IDataSource):
|
|
210
252
|
# ------------------------------------------------------------------------------------------------------------
|
211
253
|
class JsonWebDataSource(WebDataSource):
|
212
254
|
|
213
|
-
DATA_URL = "https://monespace.grdf.fr/api/e-conso/pce/consommation/informatives?dateDebut={0}&dateFin={1}&pceList
|
255
|
+
DATA_URL = "https://monespace.grdf.fr/api/e-conso/pce/consommation/informatives?dateDebut={0}&dateFin={1}&pceList[]={2}"
|
214
256
|
|
215
257
|
TEMPERATURES_URL = "https://monespace.grdf.fr/api/e-conso/pce/{0}/meteo?dateFinPeriode={1}&nbJours={2}"
|
216
258
|
|
@@ -222,7 +264,7 @@ class JsonWebDataSource(WebDataSource):
|
|
222
264
|
|
223
265
|
super().__init__(username, password)
|
224
266
|
|
225
|
-
def _loadFromSession(self,
|
267
|
+
def _loadFromSession(self, auth_token: str, pceIdentifier: str, startDate: date, endDate: date, frequencies: Optional[List[Frequency]] = None) -> MeterReadingsByFrequency:
|
226
268
|
|
227
269
|
res = {}
|
228
270
|
|
@@ -237,11 +279,38 @@ class JsonWebDataSource(WebDataSource):
|
|
237
279
|
# Data URL: Inject parameters.
|
238
280
|
downloadUrl = JsonWebDataSource.DATA_URL.format(startDate.strftime(JsonWebDataSource.INPUT_DATE_FORMAT), endDate.strftime(JsonWebDataSource.INPUT_DATE_FORMAT), pceIdentifier)
|
239
281
|
|
240
|
-
#
|
241
|
-
|
282
|
+
# Retry mechanism.
|
283
|
+
retry = 10
|
284
|
+
while retry > 0:
|
285
|
+
|
286
|
+
# Create a session.
|
287
|
+
session = Session()
|
288
|
+
session.headers.update({"Host": "monespace.grdf.fr"})
|
289
|
+
session.headers.update({"Domain": "grdf.fr"})
|
290
|
+
session.headers.update({"X-Requested-With": "XMLHttpRequest"})
|
291
|
+
session.headers.update({"Accept": "application/json"})
|
292
|
+
session.cookies.set("auth_token", auth_token, domain="monespace.grdf.fr")
|
293
|
+
|
294
|
+
try:
|
295
|
+
response = session.get(downloadUrl)
|
296
|
+
|
297
|
+
if "text/html" in response.headers.get("Content-Type"):
|
298
|
+
raise Exception("An error occurred while loading data. Please check your credentials.")
|
299
|
+
|
300
|
+
if response.status_code != 200:
|
301
|
+
raise Exception(f"An error occurred while loading data. Status code: {response.status_code} - {response.text}")
|
302
|
+
|
303
|
+
break
|
304
|
+
except Exception as e:
|
305
|
+
|
306
|
+
if retry == 1:
|
307
|
+
raise e
|
308
|
+
|
309
|
+
Logger.error("An error occurred while loading data. Retry in 3 seconds.")
|
310
|
+
time.sleep(3)
|
311
|
+
retry -= 1
|
242
312
|
|
243
|
-
|
244
|
-
data = session.get(downloadUrl).text
|
313
|
+
data = response.text
|
245
314
|
|
246
315
|
# Temperatures URL: Inject parameters.
|
247
316
|
endDate = date.today() - timedelta(days=1) if endDate >= date.today() else endDate
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "1.3.0a6"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: pygazpar
|
3
|
-
Version: 1.3.
|
3
|
+
Version: 1.3.0a6
|
4
4
|
Summary: Retrieve gas consumption from GrDF web site (French Gas Company)
|
5
5
|
Home-page: https://github.com/ssenart/pygazpar
|
6
6
|
Author: Stephane Senart
|
@@ -197,9 +197,12 @@ Description: # PyGazpar
|
|
197
197
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
198
198
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
199
199
|
|
200
|
-
|
200
|
+
|
201
|
+
## [1.2.1](https://github.com/ssenart/PyGazpar/compare/1.2.0...1.2.1) - 2024-05-04
|
201
202
|
|
202
203
|
### Fixed
|
204
|
+
- [#64](https://github.com/ssenart/PyGazpar/issues/64): [Issue] Captcha failed issue.
|
205
|
+
|
203
206
|
- [#63](https://github.com/ssenart/PyGazpar/issues/63): [Bug] If the latest received consumption is Sunday, then the last weekly period is duplicated.
|
204
207
|
|
205
208
|
## [1.2.0](https://github.com/ssenart/PyGazpar/compare/1.1.6...1.2.0) - 2022-12-16
|
@@ -75,7 +75,7 @@ class TestClient:
|
|
75
75
|
assert (len(data[Frequency.MONTHLY.value]) >= 12 and len(data[Frequency.MONTHLY.value]) <= 13)
|
76
76
|
|
77
77
|
def test_yearly_jsonweb(self):
|
78
|
-
client = Client(
|
78
|
+
client = Client(JsonWebDataSource(self.__username, self.__password))
|
79
79
|
|
80
80
|
data = client.loadSince(self.__pceIdentifier, 365, [Frequency.YEARLY])
|
81
81
|
|
@@ -1 +0,0 @@
|
|
1
|
-
__version__ = "1.3.0a2"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|