ecopipeline 0.9.4__tar.gz → 0.10.1__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.
- {ecopipeline-0.9.4/src/ecopipeline.egg-info → ecopipeline-0.10.1}/PKG-INFO +1 -1
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/setup.cfg +1 -1
- ecopipeline-0.10.1/src/ecopipeline/event_tracking/__init__.py +2 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/event_tracking/event_tracking.py +127 -2
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/load/load.py +14 -5
- {ecopipeline-0.9.4 → ecopipeline-0.10.1/src/ecopipeline.egg-info}/PKG-INFO +1 -1
- ecopipeline-0.9.4/src/ecopipeline/event_tracking/__init__.py +0 -2
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/LICENSE +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/README.md +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/pyproject.toml +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/setup.py +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/__init__.py +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/extract/__init__.py +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/extract/extract.py +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/load/__init__.py +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/transform/__init__.py +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/transform/bayview.py +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/transform/lbnl.py +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/transform/transform.py +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/utils/ConfigManager.py +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/utils/__init__.py +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline/utils/unit_convert.py +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline.egg-info/SOURCES.txt +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline.egg-info/dependency_links.txt +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline.egg-info/requires.txt +0 -0
- {ecopipeline-0.9.4 → ecopipeline-0.10.1}/src/ecopipeline.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[metadata]
|
|
2
2
|
name = ecopipeline
|
|
3
|
-
version = 0.
|
|
3
|
+
version = 0.10.1
|
|
4
4
|
authors = ["Carlos Bello, <bellocarlos@seattleu.edu>, Emil Fahrig <fahrigemil@seattleu.edu>, Casey Mang <cmang@seattleu.edu>, Julian Harris <harrisjulian@seattleu.edu>, Roger Tram <rtram@seattleu.edu>, Nolan Price <nolan@ecotope.com>"]
|
|
5
5
|
description = Contains functions for use in Ecotope Datapipelines
|
|
6
6
|
long_description = file: README.md
|
|
@@ -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
|
"""
|
|
@@ -102,6 +203,25 @@ def _convert_silent_alarm_dict_to_df(alarm_dict : dict) -> pd.DataFrame:
|
|
|
102
203
|
event_df.set_index('start_time_pt', inplace=True)
|
|
103
204
|
return event_df
|
|
104
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
|
+
|
|
105
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'):
|
|
106
226
|
# KNOWN BUG : Avg value during fault time excludes the first (fault_time-1) minutes of each fault window
|
|
107
227
|
next_day = day + pd.Timedelta(days=1)
|
|
@@ -130,7 +250,7 @@ def _check_and_add_alarm(df : pd.DataFrame, mask : pd.Series, alarms_dict, day,
|
|
|
130
250
|
else:
|
|
131
251
|
alarms_dict[day] = [[var_name, alarm_string]]
|
|
132
252
|
|
|
133
|
-
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:
|
|
134
254
|
daily_df_copy = daily_df.copy()
|
|
135
255
|
variable_names_path = config.get_var_names_path()
|
|
136
256
|
try:
|
|
@@ -148,6 +268,8 @@ def power_ratio_alarm(daily_df: pd.DataFrame, config : ConfigManager, system: st
|
|
|
148
268
|
raise Exception(f"{required_column} is not present in Variable_Names.csv")
|
|
149
269
|
if not 'pretty_name' in ratios_df.columns:
|
|
150
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'])
|
|
151
273
|
ratios_df = ratios_df.loc[:, ["variable_name", "alarm_codes", "pretty_name"]]
|
|
152
274
|
ratios_df = ratios_df[ratios_df['alarm_codes'].str.contains('PR', na=False)]
|
|
153
275
|
ratios_df.dropna(axis=0, thresh=2, inplace=True)
|
|
@@ -172,13 +294,16 @@ def power_ratio_alarm(daily_df: pd.DataFrame, config : ConfigManager, system: st
|
|
|
172
294
|
ratio_dict[pr_id][3].append(ratios['pretty_name'])
|
|
173
295
|
else:
|
|
174
296
|
ratio_dict[pr_id] = [[ratios_var],[float(low_high[0])],[float(low_high[1])],[ratios['pretty_name']]]
|
|
175
|
-
|
|
297
|
+
if verbose:
|
|
298
|
+
print("ratio_dict keys:", ratio_dict.keys())
|
|
176
299
|
alarms = {}
|
|
177
300
|
for key, value_list in ratio_dict.items():
|
|
178
301
|
daily_df_copy[key] = daily_df_copy[value_list[0]].sum(axis=1)
|
|
179
302
|
for i in range(len(value_list[0])):
|
|
180
303
|
column_name = value_list[0][i]
|
|
181
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}'])
|
|
182
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])
|
|
183
308
|
return _convert_silent_alarm_dict_to_df(alarms)
|
|
184
309
|
|
|
@@ -302,6 +302,11 @@ def load_event_table(config : ConfigManager, event_df: pd.DataFrame, site_name :
|
|
|
302
302
|
bool:
|
|
303
303
|
A boolean value indicating if the data was successfully written to the database.
|
|
304
304
|
"""
|
|
305
|
+
# define constants
|
|
306
|
+
proj_cop_filters = ['MV_COMMISSIONED','PLANT_COMMISSIONED','DATA_LOSS_COP','SYSTEM_MAINTENANCE','SYSTEM_TESTING']
|
|
307
|
+
optim_cop_filters = ['MV_COMMISSIONED','PLANT_COMMISSIONED','DATA_LOSS_COP','INSTALLATION_ERROR_COP',
|
|
308
|
+
'PARTIAL_OCCUPANCY','SOO_PERIOD_COP','SYSTEM_TESTING','EQUIPMENT_MALFUNCTION',
|
|
309
|
+
'SYSTEM_MAINTENANCE']
|
|
305
310
|
# Drop empty columns
|
|
306
311
|
event_df = event_df.dropna(axis=1, how='all')
|
|
307
312
|
|
|
@@ -329,7 +334,7 @@ def load_event_table(config : ConfigManager, event_df: pd.DataFrame, site_name :
|
|
|
329
334
|
column_names += "," + column
|
|
330
335
|
|
|
331
336
|
# create SQL statement
|
|
332
|
-
insert_str = "INSERT INTO " + table_name + " (" + column_names + ", variable_name, last_modified_date, last_modified_by) VALUES (%s,%s,%s,%s,%s,%s,'"+datetime.now().strftime('%Y-%m-%d %H:%M:%S')+"','automatic_upload')"
|
|
337
|
+
insert_str = "INSERT INTO " + table_name + " (" + column_names + ", variable_name, summary_filtered, optim_filtered, last_modified_date, last_modified_by) VALUES (%s, %s, %s,%s,%s,%s,%s,%s,'"+datetime.now().strftime('%Y-%m-%d %H:%M:%S')+"','automatic_upload')"
|
|
333
338
|
|
|
334
339
|
if not 'variable_name' in event_df.columns:
|
|
335
340
|
event_df['variable_name'] = None
|
|
@@ -338,11 +343,15 @@ def load_event_table(config : ConfigManager, event_df: pd.DataFrame, site_name :
|
|
|
338
343
|
full_column_names.append('last_modified_date')
|
|
339
344
|
full_column_names.append('last_modified_by')
|
|
340
345
|
full_column_names.append('variable_name')
|
|
346
|
+
full_column_names.append('summary_filtered')
|
|
347
|
+
full_column_names.append('optim_filtered')
|
|
348
|
+
|
|
341
349
|
full_column_types = column_types[1:]
|
|
342
350
|
full_column_types.append('datetime')
|
|
343
351
|
full_column_types.append('varchar(60)')
|
|
344
352
|
full_column_types.append('varchar(70)')
|
|
345
|
-
|
|
353
|
+
full_column_types.append('tinyint(1)')
|
|
354
|
+
full_column_types.append('tinyint(1)')
|
|
346
355
|
|
|
347
356
|
existing_rows = pd.DataFrame({
|
|
348
357
|
'start_time_pt' : [],
|
|
@@ -376,7 +385,7 @@ def load_event_table(config : ConfigManager, event_df: pd.DataFrame, site_name :
|
|
|
376
385
|
ignoredRows = 0
|
|
377
386
|
try:
|
|
378
387
|
for index, row in event_df.iterrows():
|
|
379
|
-
time_data = [index,site_name,row['end_time_pt'],row['event_type'],row['event_detail'],row['variable_name']]
|
|
388
|
+
time_data = [index,site_name,row['end_time_pt'],row['event_type'],row['event_detail'],row['variable_name'], row['event_type'] in proj_cop_filters, row['event_type'] in optim_cop_filters]
|
|
380
389
|
#remove nans and infinites
|
|
381
390
|
time_data = [None if (x is None or pd.isna(x)) else x for x in time_data]
|
|
382
391
|
time_data = [None if (x == float('inf') or x == float('-inf')) else x for x in time_data]
|
|
@@ -436,8 +445,8 @@ def report_data_loss(config : ConfigManager, site_name : str = None):
|
|
|
436
445
|
print(f"logging DATA_LOSS_COP into {table_name}")
|
|
437
446
|
|
|
438
447
|
# create SQL statement
|
|
439
|
-
insert_str = "INSERT INTO " + table_name + " (start_time_pt, site_name, event_detail, event_type, last_modified_date, last_modified_by) VALUES "
|
|
440
|
-
insert_str += f"('{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}','{site_name}','{error_string}','DATA_LOSS_COP','{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}','automatic_upload')"
|
|
448
|
+
insert_str = "INSERT INTO " + table_name + " (start_time_pt, site_name, event_detail, event_type, summary_filtered, optim_filtered, last_modified_date, last_modified_by) VALUES "
|
|
449
|
+
insert_str += f"('{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}','{site_name}','{error_string}','DATA_LOSS_COP', true, true, '{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}','automatic_upload')"
|
|
441
450
|
|
|
442
451
|
existing_rows = pd.DataFrame({
|
|
443
452
|
'id' : []
|
|
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
|