ecopipeline 0.9.3__py3-none-any.whl → 0.10.0__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.
- ecopipeline/event_tracking/__init__.py +1 -1
- ecopipeline/event_tracking/event_tracking.py +129 -2
- ecopipeline/extract/extract.py +3 -2
- {ecopipeline-0.9.3.dist-info → ecopipeline-0.10.0.dist-info}/METADATA +1 -1
- {ecopipeline-0.9.3.dist-info → ecopipeline-0.10.0.dist-info}/RECORD +8 -8
- {ecopipeline-0.9.3.dist-info → ecopipeline-0.10.0.dist-info}/WHEEL +0 -0
- {ecopipeline-0.9.3.dist-info → ecopipeline-0.10.0.dist-info}/licenses/LICENSE +0 -0
- {ecopipeline-0.9.3.dist-info → ecopipeline-0.10.0.dist-info}/top_level.txt +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
from .event_tracking import *
|
|
2
|
-
__all__ = ['flag_boundary_alarms','power_ratio_alarm']
|
|
2
|
+
__all__ = ['central_alarm_df_creator','flag_boundary_alarms','power_ratio_alarm','flag_abnormal_COP']
|
|
@@ -2,6 +2,107 @@ import pandas as pd
|
|
|
2
2
|
import numpy as np
|
|
3
3
|
import datetime as dt
|
|
4
4
|
from ecopipeline import ConfigManager
|
|
5
|
+
import re
|
|
6
|
+
import mysql.connector.errors as mysqlerrors
|
|
7
|
+
|
|
8
|
+
def central_alarm_df_creator(df: pd.DataFrame, daily_data : pd.DataFrame, config : ConfigManager, system: str = "",
|
|
9
|
+
default_cop_high_bound : float = 4.5, default_cop_low_bound : float = 0,
|
|
10
|
+
default_boundary_fault_time : int = 15, site_name : str = None) -> pd.DataFrame:
|
|
11
|
+
day_list = daily_data.index.to_list()
|
|
12
|
+
print('Checking for alarms...')
|
|
13
|
+
alarm_df = _convert_silent_alarm_dict_to_df({})
|
|
14
|
+
boundary_alarm_df = flag_boundary_alarms(df, config, full_days=day_list, system=system, default_fault_time= default_boundary_fault_time)
|
|
15
|
+
pwr_alarm_df = power_ratio_alarm(daily_data, config, system=system)
|
|
16
|
+
abnormal_COP_df = flag_abnormal_COP(daily_data, config, system = system, default_high_bound=default_cop_high_bound, default_low_bound=default_cop_low_bound)
|
|
17
|
+
|
|
18
|
+
if len(boundary_alarm_df) > 0:
|
|
19
|
+
print("Boundary alarms detected. Adding them to event df...")
|
|
20
|
+
alarm_df = boundary_alarm_df
|
|
21
|
+
else:
|
|
22
|
+
print("No boundary alarms detected.")
|
|
23
|
+
|
|
24
|
+
if len(pwr_alarm_df) > 0:
|
|
25
|
+
print("Power alarms detected. Adding them to event df...")
|
|
26
|
+
alarm_df = pd.concat([alarm_df, pwr_alarm_df])
|
|
27
|
+
else:
|
|
28
|
+
print("No power alarms detected.")
|
|
29
|
+
|
|
30
|
+
if _check_if_during_ongoing_cop_alarm(daily_data, config, site_name):
|
|
31
|
+
print("Ongoing DATA_LOSS_COP detected. No further DATA_LOSS_COP events will be uploaded")
|
|
32
|
+
elif len(abnormal_COP_df) > 0:
|
|
33
|
+
print("Abnormal COPs detected. Adding them to event df...")
|
|
34
|
+
alarm_df = pd.concat([alarm_df, abnormal_COP_df])
|
|
35
|
+
else:
|
|
36
|
+
print("No abnormal COPs.")
|
|
37
|
+
|
|
38
|
+
return alarm_df
|
|
39
|
+
|
|
40
|
+
def flag_abnormal_COP(daily_data: pd.DataFrame, config : ConfigManager, system: str = "", default_high_bound : float = 4.5, default_low_bound : float = 0) -> pd.DataFrame:
|
|
41
|
+
variable_names_path = config.get_var_names_path()
|
|
42
|
+
try:
|
|
43
|
+
bounds_df = pd.read_csv(variable_names_path)
|
|
44
|
+
except FileNotFoundError:
|
|
45
|
+
print("File Not Found: ", variable_names_path)
|
|
46
|
+
return pd.DataFrame()
|
|
47
|
+
|
|
48
|
+
if (system != ""):
|
|
49
|
+
if not 'system' in bounds_df.columns:
|
|
50
|
+
raise Exception("system parameter is non null, however, system is not present in Variable_Names.csv")
|
|
51
|
+
bounds_df = bounds_df.loc[bounds_df['system'] == system]
|
|
52
|
+
if not "variable_name" in bounds_df.columns:
|
|
53
|
+
raise Exception(f"variable_name is not present in Variable_Names.csv")
|
|
54
|
+
if not 'pretty_name' in bounds_df.columns:
|
|
55
|
+
bounds_df['pretty_name'] = bounds_df['variable_name']
|
|
56
|
+
else:
|
|
57
|
+
bounds_df['pretty_name'] = bounds_df['pretty_name'].fillna(bounds_df['variable_name'])
|
|
58
|
+
if not 'high_alarm' in bounds_df.columns:
|
|
59
|
+
bounds_df['high_alarm'] = default_high_bound
|
|
60
|
+
else:
|
|
61
|
+
bounds_df['high_alarm'] = bounds_df['high_alarm'].fillna(default_high_bound)
|
|
62
|
+
if not 'low_alarm' in bounds_df.columns:
|
|
63
|
+
bounds_df['low_alarm'] = default_low_bound
|
|
64
|
+
else:
|
|
65
|
+
bounds_df['low_alarm'] = bounds_df['low_alarm'].fillna(default_low_bound)
|
|
66
|
+
|
|
67
|
+
bounds_df = bounds_df.loc[:, ["variable_name", "high_alarm", "low_alarm", "pretty_name"]]
|
|
68
|
+
bounds_df.dropna(axis=0, thresh=2, inplace=True)
|
|
69
|
+
bounds_df.set_index(['variable_name'], inplace=True)
|
|
70
|
+
|
|
71
|
+
cop_pattern = re.compile(r'^(COP\w*|SystemCOP\w*)$')
|
|
72
|
+
cop_columns = [col for col in daily_data.columns if re.match(cop_pattern, col)]
|
|
73
|
+
|
|
74
|
+
alarms_dict = {}
|
|
75
|
+
if not daily_data.empty and len(cop_columns) > 0:
|
|
76
|
+
for bound_var, bounds in bounds_df.iterrows():
|
|
77
|
+
if bound_var in cop_columns:
|
|
78
|
+
for day, day_values in daily_data.iterrows():
|
|
79
|
+
if day_values[bound_var] > bounds['high_alarm'] or day_values[bound_var] < bounds['low_alarm']:
|
|
80
|
+
alarm_str = f"{bounds['pretty_name']} = {round(day_values[bound_var],2)}"
|
|
81
|
+
if day in alarms_dict:
|
|
82
|
+
alarms_dict[day] = alarms_dict[day] + f", {alarm_str}"
|
|
83
|
+
else:
|
|
84
|
+
alarms_dict[day] = f"Unexpected COP Value(s) detected: {alarm_str}"
|
|
85
|
+
return _convert_event_type_dict_to_df(alarms_dict)
|
|
86
|
+
|
|
87
|
+
def _check_if_during_ongoing_cop_alarm(daily_df : pd.DataFrame, config : ConfigManager, site_name : str = None) -> bool:
|
|
88
|
+
if site_name is None:
|
|
89
|
+
site_name = config.get_site_name()
|
|
90
|
+
connection, cursor = config.connect_db()
|
|
91
|
+
on_going_cop = False
|
|
92
|
+
try:
|
|
93
|
+
# find existing times in database for upsert statement
|
|
94
|
+
cursor.execute(
|
|
95
|
+
f"SELECT id FROM site_events WHERE start_time_pt <= '{daily_df.index.min()}' AND (end_time_pt IS NULL OR end_time_pt >= '{daily_df.index.max()}') AND site_name = '{site_name}' AND event_type = 'DATA_LOSS_COP'")
|
|
96
|
+
# Fetch the results into a DataFrame
|
|
97
|
+
existing_rows = pd.DataFrame(cursor.fetchall(), columns=['id'])
|
|
98
|
+
if not existing_rows.empty:
|
|
99
|
+
on_going_cop = True
|
|
100
|
+
|
|
101
|
+
except mysqlerrors.Error as e:
|
|
102
|
+
print(f"Retrieving data from site_events caused exception: {e}")
|
|
103
|
+
connection.close()
|
|
104
|
+
cursor.close()
|
|
105
|
+
return on_going_cop
|
|
5
106
|
|
|
6
107
|
def flag_boundary_alarms(df: pd.DataFrame, config : ConfigManager, default_fault_time : int = 15, system: str = "", full_days : list = None) -> pd.DataFrame:
|
|
7
108
|
"""
|
|
@@ -50,6 +151,8 @@ def flag_boundary_alarms(df: pd.DataFrame, config : ConfigManager, default_fault
|
|
|
50
151
|
raise Exception(f"{required_column} is not present in Variable_Names.csv")
|
|
51
152
|
if not 'pretty_name' in bounds_df.columns:
|
|
52
153
|
bounds_df['pretty_name'] = bounds_df['variable_name']
|
|
154
|
+
else:
|
|
155
|
+
bounds_df['pretty_name'] = bounds_df['pretty_name'].fillna(bounds_df['variable_name'])
|
|
53
156
|
if not 'fault_time' in bounds_df.columns:
|
|
54
157
|
bounds_df['fault_time'] = default_fault_time
|
|
55
158
|
|
|
@@ -100,6 +203,25 @@ def _convert_silent_alarm_dict_to_df(alarm_dict : dict) -> pd.DataFrame:
|
|
|
100
203
|
event_df.set_index('start_time_pt', inplace=True)
|
|
101
204
|
return event_df
|
|
102
205
|
|
|
206
|
+
def _convert_event_type_dict_to_df(alarm_dict : dict, event_type = 'DATA_LOSS_COP') -> pd.DataFrame:
|
|
207
|
+
events = {
|
|
208
|
+
'start_time_pt' : [],
|
|
209
|
+
'end_time_pt' : [],
|
|
210
|
+
'event_type' : [],
|
|
211
|
+
'event_detail' : [],
|
|
212
|
+
'variable_name' : []
|
|
213
|
+
}
|
|
214
|
+
for key, value in alarm_dict.items():
|
|
215
|
+
events['start_time_pt'].append(key)
|
|
216
|
+
events['end_time_pt'].append(key)
|
|
217
|
+
events['event_type'].append(event_type)
|
|
218
|
+
events['event_detail'].append(value)
|
|
219
|
+
events['variable_name'].append(None)
|
|
220
|
+
|
|
221
|
+
event_df = pd.DataFrame(events)
|
|
222
|
+
event_df.set_index('start_time_pt', inplace=True)
|
|
223
|
+
return event_df
|
|
224
|
+
|
|
103
225
|
def _check_and_add_alarm(df : pd.DataFrame, mask : pd.Series, alarms_dict, day, fault_time : int, var_name : str, pretty_name : str, alarm_type : str = 'Lower'):
|
|
104
226
|
# KNOWN BUG : Avg value during fault time excludes the first (fault_time-1) minutes of each fault window
|
|
105
227
|
next_day = day + pd.Timedelta(days=1)
|
|
@@ -128,7 +250,7 @@ def _check_and_add_alarm(df : pd.DataFrame, mask : pd.Series, alarms_dict, day,
|
|
|
128
250
|
else:
|
|
129
251
|
alarms_dict[day] = [[var_name, alarm_string]]
|
|
130
252
|
|
|
131
|
-
def power_ratio_alarm(daily_df: pd.DataFrame, config : ConfigManager, system: str = "") -> pd.DataFrame:
|
|
253
|
+
def power_ratio_alarm(daily_df: pd.DataFrame, config : ConfigManager, system: str = "", verbose : bool = False) -> pd.DataFrame:
|
|
132
254
|
daily_df_copy = daily_df.copy()
|
|
133
255
|
variable_names_path = config.get_var_names_path()
|
|
134
256
|
try:
|
|
@@ -146,6 +268,8 @@ def power_ratio_alarm(daily_df: pd.DataFrame, config : ConfigManager, system: st
|
|
|
146
268
|
raise Exception(f"{required_column} is not present in Variable_Names.csv")
|
|
147
269
|
if not 'pretty_name' in ratios_df.columns:
|
|
148
270
|
ratios_df['pretty_name'] = ratios_df['variable_name']
|
|
271
|
+
else:
|
|
272
|
+
ratios_df['pretty_name'] = ratios_df['pretty_name'].fillna(ratios_df['variable_name'])
|
|
149
273
|
ratios_df = ratios_df.loc[:, ["variable_name", "alarm_codes", "pretty_name"]]
|
|
150
274
|
ratios_df = ratios_df[ratios_df['alarm_codes'].str.contains('PR', na=False)]
|
|
151
275
|
ratios_df.dropna(axis=0, thresh=2, inplace=True)
|
|
@@ -170,13 +294,16 @@ def power_ratio_alarm(daily_df: pd.DataFrame, config : ConfigManager, system: st
|
|
|
170
294
|
ratio_dict[pr_id][3].append(ratios['pretty_name'])
|
|
171
295
|
else:
|
|
172
296
|
ratio_dict[pr_id] = [[ratios_var],[float(low_high[0])],[float(low_high[1])],[ratios['pretty_name']]]
|
|
173
|
-
|
|
297
|
+
if verbose:
|
|
298
|
+
print("ratio_dict keys:", ratio_dict.keys())
|
|
174
299
|
alarms = {}
|
|
175
300
|
for key, value_list in ratio_dict.items():
|
|
176
301
|
daily_df_copy[key] = daily_df_copy[value_list[0]].sum(axis=1)
|
|
177
302
|
for i in range(len(value_list[0])):
|
|
178
303
|
column_name = value_list[0][i]
|
|
179
304
|
daily_df_copy[f'{column_name}_{key}'] = (daily_df_copy[column_name]/daily_df_copy[key]) * 100
|
|
305
|
+
if verbose:
|
|
306
|
+
print(f"Ratios for {column_name}_{key}",daily_df_copy[f'{column_name}_{key}'])
|
|
180
307
|
_check_and_add_ratio_alarm(daily_df_copy, key, column_name, value_list[3][i], alarms, value_list[2][i], value_list[1][i])
|
|
181
308
|
return _convert_silent_alarm_dict_to_df(alarms)
|
|
182
309
|
|
ecopipeline/extract/extract.py
CHANGED
|
@@ -686,15 +686,16 @@ def fm_api_to_df(config: ConfigManager, startTime: datetime = None, endTime: dat
|
|
|
686
686
|
device_id = config.get_fm_device_id()
|
|
687
687
|
url = f"https://www.fieldpop.io/rest/method/fieldpop-api/deviceDataLog?happn_token={api_token}&deviceID={device_id}"
|
|
688
688
|
if not startTime is None:
|
|
689
|
-
url = f"{url}&startUTCsec={startTime.timestamp()}"
|
|
689
|
+
url = f"{url}&startUTCsec={int(startTime.timestamp())}"
|
|
690
690
|
else:
|
|
691
691
|
startTime = datetime(2000, 1, 1, 0, 0, 0) # Jan 1, 2000
|
|
692
692
|
if not endTime is None:
|
|
693
|
-
url = f"{url}&endUTCsec={endTime.timestamp()}"
|
|
693
|
+
url = f"{url}&endUTCsec={int(endTime.timestamp())}"
|
|
694
694
|
else:
|
|
695
695
|
endTime = datetime.now()
|
|
696
696
|
|
|
697
697
|
try:
|
|
698
|
+
print(url)
|
|
698
699
|
response = requests.get(url)
|
|
699
700
|
if response.status_code == 200:
|
|
700
701
|
df = pd.DataFrame()
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
ecopipeline/__init__.py,sha256=d48mO5La6OrQDkRe_qqoY6lUx7x-e8krOH388jmWjwU,218
|
|
2
|
-
ecopipeline/event_tracking/__init__.py,sha256=
|
|
3
|
-
ecopipeline/event_tracking/event_tracking.py,sha256=
|
|
2
|
+
ecopipeline/event_tracking/__init__.py,sha256=SV2kkvJgptjeyLQlqHWcDRpQO6-JC433_dRZ3H9-ZNU,131
|
|
3
|
+
ecopipeline/event_tracking/event_tracking.py,sha256=ly6y6IM8_jlwa9dZ-FlAYEC-rbNKPc1NByYD12lyl2w,23168
|
|
4
4
|
ecopipeline/extract/__init__.py,sha256=gQ3sak6NJ63Gpo-hZXrtZfeKOTHLRyAVXfTgxxRpqPo,675
|
|
5
|
-
ecopipeline/extract/extract.py,sha256=
|
|
5
|
+
ecopipeline/extract/extract.py,sha256=y32feIIzgABwrwfduNQM1hICmkVOU4PYu6-M07zCLpU,51422
|
|
6
6
|
ecopipeline/load/__init__.py,sha256=NLa_efQJZ8aP-J0Y5xx9DP7mtfRH9jY6Jz1ZMZN_BAA,292
|
|
7
7
|
ecopipeline/load/load.py,sha256=Ptxr0MOjns_HeVSmZsLLApHJGB-z6XOB2m8LNiVaD7E,23860
|
|
8
8
|
ecopipeline/transform/__init__.py,sha256=YveBLBsNhfI4qZP04doa0NrTbEKvjDAUDEKtEPdFPfU,2545
|
|
@@ -12,8 +12,8 @@ ecopipeline/transform/transform.py,sha256=wL4B00XBwLWVlf7goOLSHKgLFmIsXprQNepGLL
|
|
|
12
12
|
ecopipeline/utils/ConfigManager.py,sha256=-g1wtExdvhYO5Y6Q3cRbywa__DxRMFruLrB4YanwaPY,12168
|
|
13
13
|
ecopipeline/utils/__init__.py,sha256=ccWUR0m7gD9DfcgsxBCLOfi4lho6RdYuB2Ugy_g6ZdQ,28
|
|
14
14
|
ecopipeline/utils/unit_convert.py,sha256=VFh1we2Y8KV3u21BeWb-U3TlZJXo83q5vdxxkpgcuME,3064
|
|
15
|
-
ecopipeline-0.
|
|
16
|
-
ecopipeline-0.
|
|
17
|
-
ecopipeline-0.
|
|
18
|
-
ecopipeline-0.
|
|
19
|
-
ecopipeline-0.
|
|
15
|
+
ecopipeline-0.10.0.dist-info/licenses/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
ecopipeline-0.10.0.dist-info/METADATA,sha256=ZN-EhGciOr1dM0SmgaolzbTsq7RV4kwsUPZvYqvmc7s,2330
|
|
17
|
+
ecopipeline-0.10.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
18
|
+
ecopipeline-0.10.0.dist-info/top_level.txt,sha256=WOPFJH2LIgKqm4lk2OnFF5cgVkYibkaBxIxgvLgO7y0,12
|
|
19
|
+
ecopipeline-0.10.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|