MTeguri commited on
Commit
fdcd652
·
1 Parent(s): c9c99bb

Refactor check_thresholds and run_troubleshooting functions: Enhance type annotations, improve alert generation logic, and update return messages for better clarity and consistency.

Browse files
Files changed (1) hide show
  1. app.py +44 -33
app.py CHANGED
@@ -89,23 +89,34 @@ import pandas as pd
89
  import supabase
90
  import datetime # Import datetime here as it's used in run_troubleshooting
91
  import pytz # Import pytz for timezone conversion
92
-
93
-
94
- # Assuming the data loading and check_thresholds function from the previous cell are available
95
-
96
- # トラブルシューティング実行関数の定義
97
- # ① check_thresholds の戻り DataFrameは列を先固定して
98
- def check_thresholds(sensor_df_filtered, threshold_df):
99
- alerts = []
 
 
 
 
 
 
 
 
 
 
 
100
 
101
  threshold_df['下限'] = pd.to_numeric(threshold_df['下限'], errors='coerce')
102
  threshold_df['上限'] = pd.to_numeric(threshold_df['上限'], errors='coerce')
103
 
104
  for _, row in threshold_df.iterrows():
105
- metric = row["指標名"]
106
- min_val = row["下限"]
107
- max_val = row["上限"]
108
- data_no = row["No."]
109
 
110
  if metric not in sensor_df_filtered.columns:
111
  continue
@@ -116,8 +127,7 @@ def check_thresholds(sensor_df_filtered, threshold_df):
116
  if index not in sensor_df_filtered.index:
117
  continue
118
 
119
- # 常に 'datetime' 列を優先し、無ければ index を使う
120
- timestamp = (
121
  sensor_df_filtered.loc[index, "datetime"]
122
  if "datetime" in sensor_df_filtered.columns else index
123
  )
@@ -126,7 +136,7 @@ def check_thresholds(sensor_df_filtered, threshold_df):
126
  alerts.append({
127
  "timestamp": timestamp,
128
  "metric": metric,
129
- "value": value,
130
  "status": f"下限値 {min_val} 未満",
131
  "data no.": data_no
132
  })
@@ -135,33 +145,40 @@ def check_thresholds(sensor_df_filtered, threshold_df):
135
  alerts.append({
136
  "timestamp": timestamp,
137
  "metric": metric,
138
- "value": value,
139
  "status": f"上限値 {max_val} 超過",
140
  "data no.": data_no
141
  })
142
 
143
- # ← ここで列を固定。空でも 'timestamp' などの列が存在するようにする
144
  return pd.DataFrame(alerts, columns=["timestamp", "metric", "value", "status", "data no."])
145
 
146
 
147
- # ② run_troubleshooting 内の空チェックとタイムゾーン担保
148
  # トラブルシューティング実行関数
149
- def run_troubleshooting(hours: int = 24): # デフォルト24時間
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  try:
151
  current_time_utc = datetime.datetime.now(datetime.timezone.utc)
152
-
153
- # ユーザー指定の時間分さかのぼる
154
  time_start_utc = current_time_utc - datetime.timedelta(hours=hours)
155
 
156
  global sensor_df, threshold_df, troubleshooting_df
157
 
158
- # 指定範囲のセンサーデータを抽出
159
  recent_sensor_df = sensor_df[
160
  (sensor_df['datetime'] >= time_start_utc) &
161
  (sensor_df['datetime'] <= current_time_utc)
162
  ].copy()
163
 
164
- # 閾値チェック実行
165
  alerts_df = check_thresholds(recent_sensor_df, threshold_df)
166
 
167
  if alerts_df.empty:
@@ -171,28 +188,24 @@ def run_troubleshooting(hours: int = 24): # ← デフォルト24時間
171
  multiple_data_nos_timestamps = grouped_alerts[grouped_alerts > 1].index.tolist()
172
 
173
  filtered_alerts_df = alerts_df[alerts_df['timestamp'].isin(multiple_data_nos_timestamps)]
174
-
175
- # ここで空になるケースにも対応
176
  if filtered_alerts_df.empty:
177
- return "過去24時間 異常ありません(複数指標の同時異常なし)"
178
 
179
  data_nos_by_timestamp = filtered_alerts_df.groupby('timestamp')['data no.'].unique().apply(list)
180
 
181
- result_list = []
182
  for timestamp, data_nos in data_nos_by_timestamp.items():
183
  data_nos_str = ', '.join(map(str, data_nos))
184
  result_list.append({"timestamp": timestamp, "data_nos": data_nos_str})
185
 
186
  result_df = pd.DataFrame(result_list, columns=["timestamp", "data_nos"])
187
 
188
- # JST に変換(常に tz-aware 前提)
189
  JST = pytz.timezone('Asia/Tokyo')
190
  result_df['timestamp'] = result_df['timestamp'].dt.tz_convert(JST)
191
 
192
  if result_df.empty:
193
- return "過去24時間 異常ありません"
194
 
195
- # 以下、トラブルシューティング照合
196
  if '指標No.' not in troubleshooting_df.columns:
197
  return "設定テーブルに『指標No.』列が見つかりません。"
198
 
@@ -203,12 +216,11 @@ def run_troubleshooting(hours: int = 24): # ← デフォルト24時間
203
  lambda x: [int(i) for i in x if i.strip().isdigit()]
204
  )
205
 
206
- output_text = ""
207
  for i, result_nos in enumerate(result_data_nos_lists):
208
  result_timestamp = result_df.loc[i, 'timestamp']
209
  for j, troubleshooting_nos in enumerate(troubleshooting_indicator_lists):
210
  if set(troubleshooting_nos).issubset(set(result_nos)):
211
- # 列の存在チェックも加える
212
  if ('シチュエーション\n(対応が必要な状況)' in troubleshooting_df.columns and
213
  'sub goal到達のために必要な行動\n(解決策)' in troubleshooting_df.columns):
214
  troubleshooting_situation = troubleshooting_df.loc[j, 'シチュエーション\n(対応が必要な状況)']
@@ -227,7 +239,6 @@ def run_troubleshooting(hours: int = 24): # ← デフォルト24時間
227
  except Exception as e:
228
  return f"エラーが発生しました: {type(e).__name__} - {e}"
229
 
230
-
231
  # Gradioインターフェースの設定
232
  iface = gr.Interface(
233
  fn=run_troubleshooting,
 
89
  import supabase
90
  import datetime # Import datetime here as it's used in run_troubleshooting
91
  import pytz # Import pytz for timezone conversion
92
+ from typing import List, Dict, Union
93
+
94
+ # 閾値チェック関数
95
+ def check_thresholds(sensor_df_filtered: pd.DataFrame, threshold_df: pd.DataFrame) -> pd.DataFrame:
96
+ """
97
+ センサーデータして閾値チェックを行い、下限値未満や上限値超過を検出す
98
+
99
+ Args:
100
+ sensor_df_filtered (pd.DataFrame): 対象期間で抽出したセンサーデータ。
101
+ - 必須列: "datetime"(時刻情報), センサー値列(指標名と一致する列)
102
+ threshold_df (pd.DataFrame): 閾値情報のデータフレーム。
103
+ - 必須列: "指標名", "下限", "上限", "No."
104
+
105
+ Returns:
106
+ pd.DataFrame: 異常が検出された場合の結果データフレーム。
107
+ - 列: ["timestamp", "metric", "value", "status", "data no."]
108
+ - 検出されなければ空の DataFrame(ただし列は固定)。
109
+ """
110
+ alerts: List[Dict[str, Union[str, float, datetime.datetime]]] = []
111
 
112
  threshold_df['下限'] = pd.to_numeric(threshold_df['下限'], errors='coerce')
113
  threshold_df['上限'] = pd.to_numeric(threshold_df['上限'], errors='coerce')
114
 
115
  for _, row in threshold_df.iterrows():
116
+ metric: str = row["指標名"]
117
+ min_val: float = row["下限"]
118
+ max_val: float = row["上限"]
119
+ data_no: int = row["No."]
120
 
121
  if metric not in sensor_df_filtered.columns:
122
  continue
 
127
  if index not in sensor_df_filtered.index:
128
  continue
129
 
130
+ timestamp: Union[pd.Timestamp, int] = (
 
131
  sensor_df_filtered.loc[index, "datetime"]
132
  if "datetime" in sensor_df_filtered.columns else index
133
  )
 
136
  alerts.append({
137
  "timestamp": timestamp,
138
  "metric": metric,
139
+ "value": float(value),
140
  "status": f"下限値 {min_val} 未満",
141
  "data no.": data_no
142
  })
 
145
  alerts.append({
146
  "timestamp": timestamp,
147
  "metric": metric,
148
+ "value": float(value),
149
  "status": f"上限値 {max_val} 超過",
150
  "data no.": data_no
151
  })
152
 
 
153
  return pd.DataFrame(alerts, columns=["timestamp", "metric", "value", "status", "data no."])
154
 
155
 
 
156
  # トラブルシューティング実行関数
157
+ def run_troubleshooting(hours: int = 24) -> str:
158
+ """
159
+ 指定時間内のセンサーデータを対象に閾値チェックを行い、
160
+ 異常が同時に複数指標で発生した場合に対応策を返す。
161
+
162
+ Args:
163
+ hours (int, optional): 過去何時間分のデータをチェックするか。デフォルトは24。
164
+
165
+ Returns:
166
+ str: トラブルシューティング情報のテキスト。
167
+ - 異常がない場合: 「過去◯時間 異常ありません」
168
+ - 閾値超過がある場合: タイムスタンプと状況・解決策の一覧
169
+ - エラー時: エラーメッセージ
170
+ """
171
  try:
172
  current_time_utc = datetime.datetime.now(datetime.timezone.utc)
 
 
173
  time_start_utc = current_time_utc - datetime.timedelta(hours=hours)
174
 
175
  global sensor_df, threshold_df, troubleshooting_df
176
 
 
177
  recent_sensor_df = sensor_df[
178
  (sensor_df['datetime'] >= time_start_utc) &
179
  (sensor_df['datetime'] <= current_time_utc)
180
  ].copy()
181
 
 
182
  alerts_df = check_thresholds(recent_sensor_df, threshold_df)
183
 
184
  if alerts_df.empty:
 
188
  multiple_data_nos_timestamps = grouped_alerts[grouped_alerts > 1].index.tolist()
189
 
190
  filtered_alerts_df = alerts_df[alerts_df['timestamp'].isin(multiple_data_nos_timestamps)]
 
 
191
  if filtered_alerts_df.empty:
192
+ return f"過去{hours}時間 異常ありません(複数指標の同時異常なし)"
193
 
194
  data_nos_by_timestamp = filtered_alerts_df.groupby('timestamp')['data no.'].unique().apply(list)
195
 
196
+ result_list: List[Dict[str, Union[str, datetime.datetime]]] = []
197
  for timestamp, data_nos in data_nos_by_timestamp.items():
198
  data_nos_str = ', '.join(map(str, data_nos))
199
  result_list.append({"timestamp": timestamp, "data_nos": data_nos_str})
200
 
201
  result_df = pd.DataFrame(result_list, columns=["timestamp", "data_nos"])
202
 
 
203
  JST = pytz.timezone('Asia/Tokyo')
204
  result_df['timestamp'] = result_df['timestamp'].dt.tz_convert(JST)
205
 
206
  if result_df.empty:
207
+ return f"過去{hours}時間 異常ありません"
208
 
 
209
  if '指標No.' not in troubleshooting_df.columns:
210
  return "設定テーブルに『指標No.』列が見つかりません。"
211
 
 
216
  lambda x: [int(i) for i in x if i.strip().isdigit()]
217
  )
218
 
219
+ output_text: str = ""
220
  for i, result_nos in enumerate(result_data_nos_lists):
221
  result_timestamp = result_df.loc[i, 'timestamp']
222
  for j, troubleshooting_nos in enumerate(troubleshooting_indicator_lists):
223
  if set(troubleshooting_nos).issubset(set(result_nos)):
 
224
  if ('シチュエーション\n(対応が必要な状況)' in troubleshooting_df.columns and
225
  'sub goal到達のために必要な行動\n(解決策)' in troubleshooting_df.columns):
226
  troubleshooting_situation = troubleshooting_df.loc[j, 'シチュエーション\n(対応が必要な状況)']
 
239
  except Exception as e:
240
  return f"エラーが発生しました: {type(e).__name__} - {e}"
241
 
 
242
  # Gradioインターフェースの設定
243
  iface = gr.Interface(
244
  fn=run_troubleshooting,