pygazpar 1.3.0a2__py39-none-any.whl → 1.3.0a9__py39-none-any.whl
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.
- pygazpar/datasource.py +119 -47
 - pygazpar/version.py +1 -1
 - {pygazpar-1.3.0a2.dist-info → pygazpar-1.3.0a9.dist-info}/METADATA +16 -4
 - {pygazpar-1.3.0a2.dist-info → pygazpar-1.3.0a9.dist-info}/RECORD +10 -10
 - {pygazpar-1.3.0a2.dist-info → pygazpar-1.3.0a9.dist-info}/WHEEL +1 -1
 - tests/test_client.py +2 -2
 - tests/test_datasource.py +1 -1
 - {pygazpar-1.3.0a2.dist-info → pygazpar-1.3.0a9.dist-info}/LICENSE.md +0 -0
 - {pygazpar-1.3.0a2.dist-info → pygazpar-1.3.0a9.dist-info}/entry_points.txt +0 -0
 - {pygazpar-1.3.0a2.dist-info → pygazpar-1.3.0a9.dist-info}/top_level.txt +0 -0
 
    
        pygazpar/datasource.py
    CHANGED
    
    | 
         @@ -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)
         
     | 
| 
       87 
88 
     | 
    
         | 
| 
       88 
     | 
    
         
            -
                     
     | 
| 
       89 
     | 
    
         
            -
                        raise Exception(f"{loginData['error']} ({loginData['status']})")
         
     | 
| 
      
 89 
     | 
    
         
            +
                    jar = http.cookiejar.CookieJar()
         
     | 
| 
       90 
90 
     | 
    
         | 
| 
       91 
     | 
    
         
            -
                     
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
      
 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}")
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 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 
     | 
    
         | 
| 
         @@ -132,7 +144,10 @@ class ExcelWebDataSource(WebDataSource): 
     | 
|
| 
       132 
144 
     | 
    
         
             
                    file_list = glob.glob(data_file_path_pattern)
         
     | 
| 
       133 
145 
     | 
    
         
             
                    for filename in file_list:
         
     | 
| 
       134 
146 
     | 
    
         
             
                        if os.path.isfile(filename):
         
     | 
| 
       135 
     | 
    
         
            -
                             
     | 
| 
      
 147 
     | 
    
         
            +
                            try:
         
     | 
| 
      
 148 
     | 
    
         
            +
                                os.remove(filename)
         
     | 
| 
      
 149 
     | 
    
         
            +
                            except PermissionError:
         
     | 
| 
      
 150 
     | 
    
         
            +
                                pass
         
     | 
| 
       136 
151 
     | 
    
         | 
| 
       137 
152 
     | 
    
         
             
                    if frequencies is None:
         
     | 
| 
       138 
153 
     | 
    
         
             
                        # Transform Enum in List.
         
     | 
| 
         @@ -145,11 +160,31 @@ class ExcelWebDataSource(WebDataSource): 
     | 
|
| 
       145 
160 
     | 
    
         
             
                        # Inject parameters.
         
     | 
| 
       146 
161 
     | 
    
         
             
                        downloadUrl = ExcelWebDataSource.DATA_URL.format(startDate.strftime(ExcelWebDataSource.DATE_FORMAT), endDate.strftime(ExcelWebDataSource.DATE_FORMAT), pceIdentifier, ExcelWebDataSource.FREQUENCY_VALUES[frequency])
         
     | 
| 
       147 
162 
     | 
    
         | 
| 
       148 
     | 
    
         
            -
                        session.get(downloadUrl)  # First request does not return anything : strange...
         
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
163 
     | 
    
         
             
                        Logger.debug(f"Loading data of frequency {ExcelWebDataSource.FREQUENCY_VALUES[frequency]} from {startDate.strftime(ExcelWebDataSource.DATE_FORMAT)} to {endDate.strftime(ExcelWebDataSource.DATE_FORMAT)}")
         
     | 
| 
       151 
164 
     | 
    
         | 
| 
       152 
     | 
    
         
            -
                         
     | 
| 
      
 165 
     | 
    
         
            +
                        # Retry mechanism.
         
     | 
| 
      
 166 
     | 
    
         
            +
                        retry = 10
         
     | 
| 
      
 167 
     | 
    
         
            +
                        while retry > 0:
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                            # Create a session.
         
     | 
| 
      
 170 
     | 
    
         
            +
                            session = Session()
         
     | 
| 
      
 171 
     | 
    
         
            +
                            session.headers.update({"Host": "monespace.grdf.fr"})
         
     | 
| 
      
 172 
     | 
    
         
            +
                            session.headers.update({"Domain": "grdf.fr"})
         
     | 
| 
      
 173 
     | 
    
         
            +
                            session.headers.update({"X-Requested-With": "XMLHttpRequest"})
         
     | 
| 
      
 174 
     | 
    
         
            +
                            session.headers.update({"Accept": "application/json"})
         
     | 
| 
      
 175 
     | 
    
         
            +
                            session.cookies.set("auth_token", auth_token, domain="monespace.grdf.fr")
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                            try:
         
     | 
| 
      
 178 
     | 
    
         
            +
                                self.__downloadFile(session, downloadUrl, self.__tmpDirectory)
         
     | 
| 
      
 179 
     | 
    
         
            +
                                break
         
     | 
| 
      
 180 
     | 
    
         
            +
                            except Exception as e:
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                                if retry == 1:
         
     | 
| 
      
 183 
     | 
    
         
            +
                                    raise e
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
                                Logger.error("An error occurred while loading data. Retry in 3 seconds.")
         
     | 
| 
      
 186 
     | 
    
         
            +
                                time.sleep(3)
         
     | 
| 
      
 187 
     | 
    
         
            +
                                retry -= 1
         
     | 
| 
       153 
188 
     | 
    
         | 
| 
       154 
189 
     | 
    
         
             
                        # Load the XLSX file into the data structure
         
     | 
| 
       155 
190 
     | 
    
         
             
                        file_list = glob.glob(data_file_path_pattern)
         
     | 
| 
         @@ -159,7 +194,11 @@ class ExcelWebDataSource(WebDataSource): 
     | 
|
| 
       159 
194 
     | 
    
         | 
| 
       160 
195 
     | 
    
         
             
                        for filename in file_list:
         
     | 
| 
       161 
196 
     | 
    
         
             
                            res[frequency.value] = ExcelParser.parse(filename, frequency if frequency != Frequency.YEARLY else Frequency.DAILY)
         
     | 
| 
       162 
     | 
    
         
            -
                             
     | 
| 
      
 197 
     | 
    
         
            +
                            try:
         
     | 
| 
      
 198 
     | 
    
         
            +
                                # openpyxl does not close the file properly.
         
     | 
| 
      
 199 
     | 
    
         
            +
                                os.remove(filename)
         
     | 
| 
      
 200 
     | 
    
         
            +
                            except PermissionError:
         
     | 
| 
      
 201 
     | 
    
         
            +
                                pass
         
     | 
| 
       163 
202 
     | 
    
         | 
| 
       164 
203 
     | 
    
         
             
                        # We compute yearly from daily data.
         
     | 
| 
       165 
204 
     | 
    
         
             
                        if frequency == Frequency.YEARLY:
         
     | 
| 
         @@ -172,6 +211,12 @@ class ExcelWebDataSource(WebDataSource): 
     | 
|
| 
       172 
211 
     | 
    
         | 
| 
       173 
212 
     | 
    
         
             
                    response = session.get(url)
         
     | 
| 
       174 
213 
     | 
    
         | 
| 
      
 214 
     | 
    
         
            +
                    if "text/html" in response.headers.get("Content-Type"):
         
     | 
| 
      
 215 
     | 
    
         
            +
                        raise Exception("An error occurred while loading data. Please check your credentials.")
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
      
 217 
     | 
    
         
            +
                    if response.status_code != 200:
         
     | 
| 
      
 218 
     | 
    
         
            +
                        raise Exception(f"An error occurred while loading data. Status code: {response.status_code} - {response.text}")
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
       175 
220 
     | 
    
         
             
                    response.raise_for_status()
         
     | 
| 
       176 
221 
     | 
    
         | 
| 
       177 
222 
     | 
    
         
             
                    filename = response.headers["Content-Disposition"].split("filename=")[1]
         
     | 
| 
         @@ -210,7 +255,7 @@ class ExcelFileDataSource(IDataSource): 
     | 
|
| 
       210 
255 
     | 
    
         
             
            # ------------------------------------------------------------------------------------------------------------
         
     | 
| 
       211 
256 
     | 
    
         
             
            class JsonWebDataSource(WebDataSource):
         
     | 
| 
       212 
257 
     | 
    
         | 
| 
       213 
     | 
    
         
            -
                DATA_URL = "https://monespace.grdf.fr/api/e-conso/pce/consommation/informatives?dateDebut={0}&dateFin={1}&pceList 
     | 
| 
      
 258 
     | 
    
         
            +
                DATA_URL = "https://monespace.grdf.fr/api/e-conso/pce/consommation/informatives?dateDebut={0}&dateFin={1}&pceList[]={2}"
         
     | 
| 
       214 
259 
     | 
    
         | 
| 
       215 
260 
     | 
    
         
             
                TEMPERATURES_URL = "https://monespace.grdf.fr/api/e-conso/pce/{0}/meteo?dateFinPeriode={1}&nbJours={2}"
         
     | 
| 
       216 
261 
     | 
    
         | 
| 
         @@ -222,7 +267,7 @@ class JsonWebDataSource(WebDataSource): 
     | 
|
| 
       222 
267 
     | 
    
         | 
| 
       223 
268 
     | 
    
         
             
                    super().__init__(username, password)
         
     | 
| 
       224 
269 
     | 
    
         | 
| 
       225 
     | 
    
         
            -
                def _loadFromSession(self,  
     | 
| 
      
 270 
     | 
    
         
            +
                def _loadFromSession(self, auth_token: str, pceIdentifier: str, startDate: date, endDate: date, frequencies: Optional[List[Frequency]] = None) -> MeterReadingsByFrequency:
         
     | 
| 
       226 
271 
     | 
    
         | 
| 
       227 
272 
     | 
    
         
             
                    res = {}
         
     | 
| 
       228 
273 
     | 
    
         | 
| 
         @@ -237,11 +282,38 @@ class JsonWebDataSource(WebDataSource): 
     | 
|
| 
       237 
282 
     | 
    
         
             
                    # Data URL: Inject parameters.
         
     | 
| 
       238 
283 
     | 
    
         
             
                    downloadUrl = JsonWebDataSource.DATA_URL.format(startDate.strftime(JsonWebDataSource.INPUT_DATE_FORMAT), endDate.strftime(JsonWebDataSource.INPUT_DATE_FORMAT), pceIdentifier)
         
     | 
| 
       239 
284 
     | 
    
         | 
| 
       240 
     | 
    
         
            -
                    #  
     | 
| 
       241 
     | 
    
         
            -
                     
     | 
| 
      
 285 
     | 
    
         
            +
                    # Retry mechanism.
         
     | 
| 
      
 286 
     | 
    
         
            +
                    retry = 10
         
     | 
| 
      
 287 
     | 
    
         
            +
                    while retry > 0:
         
     | 
| 
      
 288 
     | 
    
         
            +
             
     | 
| 
      
 289 
     | 
    
         
            +
                        # Create a session.
         
     | 
| 
      
 290 
     | 
    
         
            +
                        session = Session()
         
     | 
| 
      
 291 
     | 
    
         
            +
                        session.headers.update({"Host": "monespace.grdf.fr"})
         
     | 
| 
      
 292 
     | 
    
         
            +
                        session.headers.update({"Domain": "grdf.fr"})
         
     | 
| 
      
 293 
     | 
    
         
            +
                        session.headers.update({"X-Requested-With": "XMLHttpRequest"})
         
     | 
| 
      
 294 
     | 
    
         
            +
                        session.headers.update({"Accept": "application/json"})
         
     | 
| 
      
 295 
     | 
    
         
            +
                        session.cookies.set("auth_token", auth_token, domain="monespace.grdf.fr")
         
     | 
| 
      
 296 
     | 
    
         
            +
             
     | 
| 
      
 297 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 298 
     | 
    
         
            +
                            response = session.get(downloadUrl)
         
     | 
| 
      
 299 
     | 
    
         
            +
             
     | 
| 
      
 300 
     | 
    
         
            +
                            if "text/html" in response.headers.get("Content-Type"):
         
     | 
| 
      
 301 
     | 
    
         
            +
                                raise Exception("An error occurred while loading data. Please check your credentials.")
         
     | 
| 
      
 302 
     | 
    
         
            +
             
     | 
| 
      
 303 
     | 
    
         
            +
                            if response.status_code != 200:
         
     | 
| 
      
 304 
     | 
    
         
            +
                                raise Exception(f"An error occurred while loading data. Status code: {response.status_code} - {response.text}")
         
     | 
| 
      
 305 
     | 
    
         
            +
             
     | 
| 
      
 306 
     | 
    
         
            +
                            break
         
     | 
| 
      
 307 
     | 
    
         
            +
                        except Exception as e:
         
     | 
| 
      
 308 
     | 
    
         
            +
             
     | 
| 
      
 309 
     | 
    
         
            +
                            if retry == 1:
         
     | 
| 
      
 310 
     | 
    
         
            +
                                raise e
         
     | 
| 
      
 311 
     | 
    
         
            +
             
     | 
| 
      
 312 
     | 
    
         
            +
                            Logger.error("An error occurred while loading data. Retry in 3 seconds.")
         
     | 
| 
      
 313 
     | 
    
         
            +
                            time.sleep(3)
         
     | 
| 
      
 314 
     | 
    
         
            +
                            retry -= 1
         
     | 
| 
       242 
315 
     | 
    
         | 
| 
       243 
     | 
    
         
            -
                     
     | 
| 
       244 
     | 
    
         
            -
                    data = session.get(downloadUrl).text
         
     | 
| 
      
 316 
     | 
    
         
            +
                    data = response.text
         
     | 
| 
       245 
317 
     | 
    
         | 
| 
       246 
318 
     | 
    
         
             
                    # Temperatures URL: Inject parameters.
         
     | 
| 
       247 
319 
     | 
    
         
             
                    endDate = date.today() - timedelta(days=1) if endDate >= date.today() else endDate
         
     | 
    
        pygazpar/version.py
    CHANGED
    
    | 
         @@ -1 +1 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            __version__ = "1.3. 
     | 
| 
      
 1 
     | 
    
         
            +
            __version__ = "1.3.0a9"
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            Metadata-Version: 2.1
         
     | 
| 
       2 
2 
     | 
    
         
             
            Name: pygazpar
         
     | 
| 
       3 
     | 
    
         
            -
            Version: 1.3. 
     | 
| 
      
 3 
     | 
    
         
            +
            Version: 1.3.0a9
         
     | 
| 
       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
         
     | 
| 
         @@ -25,11 +25,16 @@ Classifier: Programming Language :: Python :: 3.11 
     | 
|
| 
       25 
25 
     | 
    
         
             
            Requires-Python: >=3.7
         
     | 
| 
       26 
26 
     | 
    
         
             
            Description-Content-Type: text/markdown
         
     | 
| 
       27 
27 
     | 
    
         
             
            License-File: LICENSE.md
         
     | 
| 
       28 
     | 
    
         
            -
            Requires-Dist: openpyxl  
     | 
| 
       29 
     | 
    
         
            -
            Requires-Dist: requests  
     | 
| 
      
 28 
     | 
    
         
            +
            Requires-Dist: openpyxl >=2.6.3
         
     | 
| 
      
 29 
     | 
    
         
            +
            Requires-Dist: requests >=2.26.0
         
     | 
| 
       30 
30 
     | 
    
         
             
            Requires-Dist: pandas
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
32 
     | 
    
         
             
            # PyGazpar
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            ## <span style="color:green">!!! This library is working again. CAPTCHA has been removed !!!</span>
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            ## <span style="color:red">~~!!! This library is broken since CAPTCHA is mandatory on GrDF site !!!~~</span>
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
       33 
38 
     | 
    
         
             
            PyGazpar is a Python library for getting natural gas consumption from GrDF French provider.
         
     | 
| 
       34 
39 
     | 
    
         | 
| 
       35 
40 
     | 
    
         
             
            Their natural gas meter is called Gazpar. It is wireless and transmit the gas consumption once per day.
         
     | 
| 
         @@ -214,9 +219,16 @@ All notable changes to this project will be documented in this file. 
     | 
|
| 
       214 
219 
     | 
    
         
             
            The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
         
     | 
| 
       215 
220 
     | 
    
         
             
            and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
         
     | 
| 
       216 
221 
     | 
    
         | 
| 
       217 
     | 
    
         
            -
            ## [1.2. 
     | 
| 
      
 222 
     | 
    
         
            +
            ## [1.2.2](https://github.com/ssenart/PyGazpar/compare/1.2.1...1.2.2) - 2024-05-08
         
     | 
| 
       218 
223 
     | 
    
         | 
| 
       219 
224 
     | 
    
         
             
            ### Fixed
         
     | 
| 
      
 225 
     | 
    
         
            +
            - [#65](https://github.com/ssenart/PyGazpar/issues/65): [Bug] PermissionError happens when loading data from Excel file.
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
            ## [1.2.1](https://github.com/ssenart/PyGazpar/compare/1.2.0...1.2.1) - 2024-05-04
         
     | 
| 
      
 228 
     | 
    
         
            +
             
     | 
| 
      
 229 
     | 
    
         
            +
            ### Fixed
         
     | 
| 
      
 230 
     | 
    
         
            +
            - [#64](https://github.com/ssenart/PyGazpar/issues/64): [Issue] Captcha failed issue.
         
     | 
| 
      
 231 
     | 
    
         
            +
             
     | 
| 
       220 
232 
     | 
    
         
             
            - [#63](https://github.com/ssenart/PyGazpar/issues/63): [Bug] If the latest received consumption is Sunday, then the last weekly period is duplicated.
         
     | 
| 
       221 
233 
     | 
    
         | 
| 
       222 
234 
     | 
    
         
             
            ## [1.2.0](https://github.com/ssenart/PyGazpar/compare/1.1.6...1.2.0) - 2022-12-16
         
     | 
| 
         @@ -1,11 +1,11 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            pygazpar/__init__.py,sha256=qshO_XZbDA2Wrt80ABDs0MoScqJytClAuIJjAnILglk,309
         
     | 
| 
       2 
2 
     | 
    
         
             
            pygazpar/__main__.py,sha256=Pt3PInX7QiWcs0aBKZN90NTaU8KFnrQiZ5Hsow1eR5U,3177
         
     | 
| 
       3 
3 
     | 
    
         
             
            pygazpar/client.py,sha256=JdVm0jZbeibwtTumcRbUSFadfXnCUClPMjL95_J6p5Y,2595
         
     | 
| 
       4 
     | 
    
         
            -
            pygazpar/datasource.py,sha256= 
     | 
| 
      
 4 
     | 
    
         
            +
            pygazpar/datasource.py,sha256=nlIWxZ6SNSHf09BVBFIChSHf4dN05lC3LCxUUDRTylg,21470
         
     | 
| 
       5 
5 
     | 
    
         
             
            pygazpar/enum.py,sha256=3ZCk4SziXF6pxgP3MuQ1qxYfqB3X5DOV8Rtd0GHsK9w,898
         
     | 
| 
       6 
6 
     | 
    
         
             
            pygazpar/excelparser.py,sha256=glWlbj22pxYjHGKurOFmhzcVAoWCvfOHn7_Y6GgHUPo,5915
         
     | 
| 
       7 
7 
     | 
    
         
             
            pygazpar/jsonparser.py,sha256=AWdU3h7UohsOov8HpeP8GNuqcnDmM4r3I7-CI_crDvA,1804
         
     | 
| 
       8 
     | 
    
         
            -
            pygazpar/version.py,sha256= 
     | 
| 
      
 8 
     | 
    
         
            +
            pygazpar/version.py,sha256=M68dz_mPRENIQzrCD_i1UgDy4ON37VxutlMfPrsxFpY,24
         
     | 
| 
       9 
9 
     | 
    
         
             
            pygazpar/resources/daily_data_sample.json,sha256=YJovtrNUMs257magTfyxiewLmecySFypcelbGFUUeT8,199583
         
     | 
| 
       10 
10 
     | 
    
         
             
            pygazpar/resources/hourly_data_sample.json,sha256=N1F-Xz3GaBn2H1p7uKzhkhKCQV8QVR0t76XD6wmFtXA,3
         
     | 
| 
       11 
11 
     | 
    
         
             
            pygazpar/resources/monthly_data_sample.json,sha256=yrr4SqrB2MubeVU2HX_FRDZKHIhC0LXCqkO1iqnFWcg,3351
         
     | 
| 
         @@ -16,12 +16,12 @@ samples/excelSample.py,sha256=ltAl-bBz9-U9YI802JpcIswra-vDS7tR_KL5VNdxJ5c,765 
     | 
|
| 
       16 
16 
     | 
    
         
             
            samples/jsonSample.py,sha256=sYAIusdEJhZdwDAMgHqoWcwDR0FA2eWhSt_2gL_mJRk,736
         
     | 
| 
       17 
17 
     | 
    
         
             
            samples/testSample.py,sha256=UeirdEtezHwfZDv_75oxul17YzGWn5yZuHfJYTF3Ez0,387
         
     | 
| 
       18 
18 
     | 
    
         
             
            tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
       19 
     | 
    
         
            -
            tests/test_client.py,sha256= 
     | 
| 
      
 19 
     | 
    
         
            +
            tests/test_client.py,sha256=DVGGubMPMM56LvQW-4_pCouvG9qTCbVZVPoRTBGuCU4,5111
         
     | 
| 
       20 
20 
     | 
    
         
             
            tests/test_datafileparser.py,sha256=nAeUpOHtelblMpmbrrnf-2GuMjK5ai65veDoymceprE,818
         
     | 
| 
       21 
     | 
    
         
            -
            tests/test_datasource.py,sha256= 
     | 
| 
       22 
     | 
    
         
            -
            pygazpar-1.3. 
     | 
| 
       23 
     | 
    
         
            -
            pygazpar-1.3. 
     | 
| 
       24 
     | 
    
         
            -
            pygazpar-1.3. 
     | 
| 
       25 
     | 
    
         
            -
            pygazpar-1.3. 
     | 
| 
       26 
     | 
    
         
            -
            pygazpar-1.3. 
     | 
| 
       27 
     | 
    
         
            -
            pygazpar-1.3. 
     | 
| 
      
 21 
     | 
    
         
            +
            tests/test_datasource.py,sha256=Fkn9BOGVKITAgrx9XipR1_ykT7rdvPyt_PAg3ftjfSU,5983
         
     | 
| 
      
 22 
     | 
    
         
            +
            pygazpar-1.3.0a9.dist-info/LICENSE.md,sha256=XsCJx_7_BC9tvmE0ZxS1cTNR7ekurog_ea9ybdZ-8tc,1073
         
     | 
| 
      
 23 
     | 
    
         
            +
            pygazpar-1.3.0a9.dist-info/METADATA,sha256=BAd0ZNGUkpdCRe0CxbqHc-9X-1wa5MwrBFLVy6AEjvg,18193
         
     | 
| 
      
 24 
     | 
    
         
            +
            pygazpar-1.3.0a9.dist-info/WHEEL,sha256=kG0f_S63jJ569yg8d_OdUrqQSrnKsZBVKN5Kb1RSnpA,93
         
     | 
| 
      
 25 
     | 
    
         
            +
            pygazpar-1.3.0a9.dist-info/entry_points.txt,sha256=c_FMZPYlRv1w9EqfgWhlkdJOoje7FcglI0UMm2oRLoI,53
         
     | 
| 
      
 26 
     | 
    
         
            +
            pygazpar-1.3.0a9.dist-info/top_level.txt,sha256=P7qn-XtanDPBLQsTvjvLV71wH8RK0DYbx8tzN_rDS70,23
         
     | 
| 
      
 27 
     | 
    
         
            +
            pygazpar-1.3.0a9.dist-info/RECORD,,
         
     | 
    
        tests/test_client.py
    CHANGED
    
    | 
         @@ -72,10 +72,10 @@ class TestClient: 
     | 
|
| 
       72 
72 
     | 
    
         | 
| 
       73 
73 
     | 
    
         
             
                    data = client.loadSince(self.__pceIdentifier, 365, [Frequency.MONTHLY])
         
     | 
| 
       74 
74 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
                    assert (len(data[Frequency.MONTHLY.value]) >=  
     | 
| 
      
 75 
     | 
    
         
            +
                    assert (len(data[Frequency.MONTHLY.value]) >= 11 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 
     | 
    
         | 
    
        tests/test_datasource.py
    CHANGED
    
    | 
         @@ -143,7 +143,7 @@ class TestAllDataSource: 
     | 
|
| 
       143 
143 
     | 
    
         | 
| 
       144 
144 
     | 
    
         
             
                    assert (len(data[Frequency.WEEKLY.value]) >= 51 and len(data[Frequency.WEEKLY.value]) <= 54)
         
     | 
| 
       145 
145 
     | 
    
         | 
| 
       146 
     | 
    
         
            -
                    assert (len(data[Frequency.MONTHLY.value]) >=  
     | 
| 
      
 146 
     | 
    
         
            +
                    assert (len(data[Frequency.MONTHLY.value]) >= 11 and len(data[Frequency.MONTHLY.value]) <= 13)
         
     | 
| 
       147 
147 
     | 
    
         | 
| 
       148 
148 
     | 
    
         
             
                    assert (len(data[Frequency.YEARLY.value]) == 1)
         
     | 
| 
       149 
149 
     | 
    
         | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     |