ecopipeline 0.7.5__py3-none-any.whl → 0.7.7__py3-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.
@@ -1,3 +1,3 @@
1
- from .extract import get_noaa_data, json_to_df, extract_files, get_last_full_day_from_db, get_db_row_from_time, extract_new, csv_to_df, get_sub_dirs, msa_to_df, fm_api_to_df, small_planet_control_to_df, dent_csv_to_df, flow_csv_to_df, pull_egauge_data, egauge_csv_to_df, remove_char_sequence_from_csv_header
1
+ from .extract import get_noaa_data, json_to_df, extract_files, get_last_full_day_from_db, get_db_row_from_time, extract_new, csv_to_df, get_sub_dirs, msa_to_df, fm_api_to_df, small_planet_control_to_df, dent_csv_to_df, flow_csv_to_df, pull_egauge_data, egauge_csv_to_df, remove_char_sequence_from_csv_header, tb_api_to_df
2
2
  __all__ = ["get_noaa_data", "json_to_df", "extract_files", "get_last_full_day_from_db", "get_db_row_from_time", 'extract_new', "csv_to_df", "get_sub_dirs", "msa_to_df", "fm_api_to_df",
3
- "small_planet_control_to_df","dent_csv_to_df","flow_csv_to_df","pull_egauge_data", "egauge_csv_to_df","remove_char_sequence_from_csv_header"]
3
+ "small_planet_control_to_df","dent_csv_to_df","flow_csv_to_df","pull_egauge_data", "egauge_csv_to_df","remove_char_sequence_from_csv_header", "tb_api_to_df"]
@@ -14,6 +14,7 @@ from pytz import timezone, utc
14
14
  import mysql.connector.errors as mysqlerrors
15
15
  import requests
16
16
  import subprocess
17
+ import traceback
17
18
 
18
19
 
19
20
  def get_last_full_day_from_db(config : ConfigManager, table_identifier : str = "minute") -> datetime:
@@ -151,11 +152,17 @@ def extract_new(startTime: datetime, filenames: List[str], decihex = False, time
151
152
 
152
153
 
153
154
  else:
155
+ endTime_int = endTime
154
156
  if epochFormat:
155
157
  startTime_int = int(startTime.timestamp())
158
+ if not endTime is None:
159
+ endTime_int = int(endTime.timestamp())
156
160
  else:
157
161
  startTime_int = int(startTime.strftime(dateFormat))
158
- return_list = list(filter(lambda filename: int(filename[dateStringStartIdx:dateStringEndIdx]) >= startTime_int and (endTime is None or int(filename[dateStringStartIdx:dateStringStartIdx+14]) < int(endTime.strftime("%Y%m%d%H%M%S"))), filenames))
162
+ if not endTime is None:
163
+ endTime_int = int(endTime.strftime(dateFormat)
164
+ )
165
+ return_list = list(filter(lambda filename: int(filename[dateStringStartIdx:dateStringEndIdx]) >= startTime_int and (endTime_int is None or int(filename[dateStringStartIdx:dateStringEndIdx]) < endTime_int), filenames))
159
166
  return return_list
160
167
 
161
168
  def extract_files(extension: str, config: ConfigManager, data_sub_dir : str = "", file_prefix : str = "") -> List[str]:
@@ -745,6 +752,90 @@ def pull_egauge_data(config: ConfigManager, eGauge_ids: list, eGauge_usr : str,
745
752
  print(f"Could not download new data from eGauge device: {e}")
746
753
 
747
754
  os.chdir(original_directory)
755
+
756
+ def tb_api_to_df(config: ConfigManager, startTime: datetime = None, endTime: datetime = None):
757
+ if endTime is None:
758
+ endTime = datetime.now()
759
+ if startTime is None:
760
+ # 28 hours to ensure encapsulation of last day
761
+ startTime = endTime - timedelta(hours=28)
762
+
763
+ if endTime - timedelta(hours=4) > startTime:
764
+ time_diff = endTime - startTime
765
+ midpointTime = startTime + time_diff / 2
766
+ # recursively construct the df
767
+ df_1 = tb_api_to_df(config, startTime, midpointTime)
768
+ df_2 = tb_api_to_df(config, midpointTime, endTime)
769
+ df = pd.concat([df_1, df_2])
770
+ df = df.sort_index()
771
+ df = df.groupby(df.index).mean()
772
+ return df
773
+
774
+ url = f'https://thingsboard.cloud/api/plugins/telemetry/DEVICE/{config.api_device_id}/values/timeseries'
775
+ token = config.get_thingsboard_token()
776
+ keys = _get_tb_keys(config, token)
777
+ if len(keys) <= 0:
778
+ raise Exception(f"No sensors available at ThingsBoard site with id {config.api_device_id}")
779
+ key_string = ','.join(keys)
780
+ params = {
781
+ 'keys': key_string,
782
+ 'startTs': f'{int(startTime.timestamp())*1000}',
783
+ 'endTs': f'{int(endTime.timestamp())*1000}',
784
+ 'orderBy': 'ASC',
785
+ 'useStrictDataTypes': 'false'
786
+ }
787
+
788
+ # Headers
789
+ headers = {
790
+ 'accept': 'application/json',
791
+ 'X-Authorization': f'Bearer {token}'
792
+ }
793
+
794
+ try:
795
+ response = requests.get(url, headers=headers, params=params)
796
+ if response.status_code == 200:
797
+ response_json = response.json()
798
+ data = {}
799
+ for key, records in response_json.items():
800
+ try:
801
+ series = pd.Series(
802
+ data={record['ts']: float(record['value']) for record in records}
803
+ )
804
+ data[key] = series
805
+ except:
806
+ print_statement = f"Could not convert {key} values to floats."
807
+ # print(print_statement)
808
+ df = pd.DataFrame(data)
809
+ df.index = pd.to_datetime(df.index, unit='ms')
810
+ df = df.sort_index()
811
+ return df
812
+
813
+ print(f"Failed to make GET request. Status code: {response.status_code} {response.json()}")
814
+ return pd.DataFrame()
815
+ except Exception as e:
816
+ traceback.print_exc()
817
+ print(f"An error occurred: {e}")
818
+ return pd.DataFrame()
819
+
820
+ def _get_tb_keys(config: ConfigManager, token : str) -> List[str]:
821
+ url = f'https://thingsboard.cloud/api/plugins/telemetry/DEVICE/{config.api_device_id}/keys/timeseries'
822
+
823
+ # Headers
824
+ headers = {
825
+ 'accept': 'application/json',
826
+ 'X-Authorization': f'Bearer {token}'
827
+ }
828
+
829
+ try:
830
+ response = requests.get(url, headers=headers)
831
+ if response.status_code == 200:
832
+ return response.json()
833
+
834
+ print(f"Failed to make GET request. Status code: {response.status_code} {response.json()}")
835
+ return []
836
+ except Exception as e:
837
+ print(f"An error occurred: {e}")
838
+ return []
748
839
 
749
840
  def get_sub_dirs(dir: str) -> List[str]:
750
841
  """
@@ -64,10 +64,16 @@ class ConfigManager:
64
64
  self.data_directory = configure.get('data', 'directory')
65
65
  configured_data_method = True
66
66
  if 'fieldManager_api_usr' in configure['data'] and 'fieldManager_api_pw' in configure['data'] and 'fieldManager_device_id' in configure['data']:
67
+ # LEGACY, Remove when you can
67
68
  self.api_usr = configure.get('data', 'fieldManager_api_usr')
68
69
  self.api_pw = configure.get('data', 'fieldManager_api_pw')
69
70
  self.api_device_id = configure.get('data','fieldManager_device_id')
70
71
  configured_data_method = True
72
+ elif 'api_usr' in configure['data'] and 'api_pw' in configure['data'] and 'device_id' in configure['data']:
73
+ self.api_usr = configure.get('data', 'api_usr')
74
+ self.api_pw = configure.get('data', 'api_pw')
75
+ self.api_device_id = configure.get('data','device_id')
76
+ configured_data_method = True
71
77
  if not configured_data_method:
72
78
  raise Exception('data configuration section missing or incomplete in configuration file.')
73
79
 
@@ -205,6 +211,7 @@ class ConfigManager:
205
211
  return connection, connection.cursor()
206
212
 
207
213
  def get_fm_token(self) -> str:
214
+ # for getting feild manager api token
208
215
  if self.api_usr is None or self.api_pw is None:
209
216
  raise Exception("Cannot retrieve Field Manager API token. Credentials were not provided in configuration file.")
210
217
  url = f"https://www.fieldpop.io/rest/login?username={self.api_usr}&password={self.api_pw}"
@@ -221,6 +228,36 @@ class ConfigManager:
221
228
  print(f"An error occurred: {e}")
222
229
  return None
223
230
 
231
+ def get_thingsboard_token(self) -> str:
232
+ # for getting ThingsBoard api token
233
+ if self.api_usr is None or self.api_pw is None:
234
+ raise Exception("Cannot retrieve ThingsBoard API token. Credentials were not provided in configuration file.")
235
+ url = 'https://thingsboard.cloud/api/auth/login'
236
+
237
+ # Request payload (data to send in the POST)
238
+ payload = {
239
+ 'username': self.api_usr,
240
+ 'password': self.api_pw
241
+ }
242
+
243
+ # Headers
244
+ headers = {
245
+ 'Content-Type': 'application/json',
246
+ 'Accept': 'application/json'
247
+ }
248
+ try:
249
+ response = requests.post(url, json=payload, headers=headers)
250
+ # Check if the request was successful (status code 200)
251
+ if response.status_code == 200:
252
+ response = response.json() # Return the response data as JSON
253
+ return response['token']
254
+ else:
255
+ print(f"Failed to make GET request. Status code: {response.status_code}")
256
+ return None
257
+ except Exception as e:
258
+ print(f"An error occurred: {e}")
259
+ return None
260
+
224
261
  def get_fm_device_id(self) -> str:
225
262
  if self.api_device_id is None:
226
263
  raise Exception("Field Manager device ID has not been configured.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ecopipeline
3
- Version: 0.7.5
3
+ Version: 0.7.7
4
4
  Summary: Contains functions for use in Ecotope Datapipelines
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Classifier: License :: OSI Approved :: GNU General Public License (GPL)
@@ -1,17 +1,17 @@
1
1
  ecopipeline/__init__.py,sha256=vCRzwd781ciCSXMP1ycM_BXAqxj3KVaNKIjsLOPcbwc,171
2
- ecopipeline/extract/__init__.py,sha256=3u_CUMdCguVewU3kN8x6xhVNyo1-p-gwTrhjOh7Psqg,645
3
- ecopipeline/extract/extract.py,sha256=heWcWTeRVTRITh_1sHVnkaKOOi5PwUOEVIi4k5tw2Z8,43384
2
+ ecopipeline/extract/__init__.py,sha256=gQ3sak6NJ63Gpo-hZXrtZfeKOTHLRyAVXfTgxxRpqPo,675
3
+ ecopipeline/extract/extract.py,sha256=ITXr8XuadQ81kYA006hygvIYgmkpQXTL6KN4QTpNOvE,46707
4
4
  ecopipeline/load/__init__.py,sha256=NLa_efQJZ8aP-J0Y5xx9DP7mtfRH9jY6Jz1ZMZN_BAA,292
5
5
  ecopipeline/load/load.py,sha256=D3KdQZ08hYRso42p59YE-eVQaTHuANCJ0YeJvRnOgG4,23078
6
6
  ecopipeline/transform/__init__.py,sha256=hYb4F64fXdXtjBSYCqv6gLFBwKZjjnl0z7s291pFE98,2505
7
7
  ecopipeline/transform/bayview.py,sha256=TP24dnTsUD95X-f6732egPZKjepFLJgDm9ImGr-fppY,17899
8
8
  ecopipeline/transform/lbnl.py,sha256=EQ54G4rJXaZ7pwVusKcdK2KBehSdCsNo2ybphtMGs7o,33400
9
9
  ecopipeline/transform/transform.py,sha256=DBQD4WqKmdXnGQMAj6tg75HtXiSemIc7c6nZxzA2aXc,48958
10
- ecopipeline/utils/ConfigManager.py,sha256=avhUEpx7WhhmnT-MB8OQ_siVgS1k5zp7KCYOyzzOJkE,10506
10
+ ecopipeline/utils/ConfigManager.py,sha256=-g1wtExdvhYO5Y6Q3cRbywa__DxRMFruLrB4YanwaPY,12168
11
11
  ecopipeline/utils/__init__.py,sha256=ccWUR0m7gD9DfcgsxBCLOfi4lho6RdYuB2Ugy_g6ZdQ,28
12
12
  ecopipeline/utils/unit_convert.py,sha256=VFh1we2Y8KV3u21BeWb-U3TlZJXo83q5vdxxkpgcuME,3064
13
- ecopipeline-0.7.5.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- ecopipeline-0.7.5.dist-info/METADATA,sha256=rj-QU_0DXM8mf-P6vLFzhxRB5ErzXVkqTMWFhA3ZywU,2329
15
- ecopipeline-0.7.5.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
16
- ecopipeline-0.7.5.dist-info/top_level.txt,sha256=WOPFJH2LIgKqm4lk2OnFF5cgVkYibkaBxIxgvLgO7y0,12
17
- ecopipeline-0.7.5.dist-info/RECORD,,
13
+ ecopipeline-0.7.7.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ ecopipeline-0.7.7.dist-info/METADATA,sha256=ShFLOFd95TQUDVZ0IZU4QIS8iLX_kjyd2wS95fMQCEU,2329
15
+ ecopipeline-0.7.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ ecopipeline-0.7.7.dist-info/top_level.txt,sha256=WOPFJH2LIgKqm4lk2OnFF5cgVkYibkaBxIxgvLgO7y0,12
17
+ ecopipeline-0.7.7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.1.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5