justKevv commited on
Commit
c7cc1b5
·
1 Parent(s): 39d6711

feat(database): added logger and create to a new database method

Browse files
Files changed (3) hide show
  1. app.py +81 -2
  2. controller.py +182 -158
  3. database.py +65 -13
app.py CHANGED
@@ -1,8 +1,12 @@
1
  from fastapi import FastAPI
2
  from controller import Controller
 
 
3
 
4
  app = FastAPI()
5
- controller = Controller()
 
 
6
 
7
  @app.get("/")
8
  def greet_json():
@@ -10,4 +14,79 @@ def greet_json():
10
 
11
  @app.get("/predict-machine")
12
  def predict_machine():
13
- return controller.predict_binary()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from fastapi import FastAPI
2
  from controller import Controller
3
+ from database import Database
4
+ import logging
5
 
6
  app = FastAPI()
7
+ database = Database()
8
+
9
+ logger = logging.getLogger(__name__)
10
 
11
  @app.get("/")
12
  def greet_json():
 
14
 
15
  @app.get("/predict-machine")
16
  def predict_machine():
17
+ logger.info("Prediction request received")
18
+
19
+ sensor = database.get_sensor_readings()
20
+
21
+ if sensor is None:
22
+ logging.warning("No sensor data available")
23
+ return {
24
+ "success": False,
25
+ "error": "No sensor data available"
26
+ }
27
+
28
+ sensor_udi = sensor.get("udi")
29
+
30
+ logger.debug(f"Processing sensor data for machine_id: {sensor.get('machine_id')}")
31
+
32
+ controller = Controller(database_sensor=sensor)
33
+
34
+ binary_result = controller.predict_binary()
35
+ if not binary_result.get("success"):
36
+ logger.error(f"Binary prediction failed: {binary_result.get('error')}")
37
+ database.reset_last_processed_id()
38
+ return binary_result
39
+
40
+ if binary_result.get("failure_predicted"):
41
+ logger.info("Failure predicted - running classification and time series analysis")
42
+ classification_result = controller.predict_classification()
43
+ time_series_result = controller.predict_time_series()
44
+
45
+ if classification_result.get("success") and time_series_result.get("success"):
46
+ logger.info(f"Prediction successful - failure_type: {classification_result.get('failure_type')}")
47
+
48
+ save_result = database.create_new_predictions(
49
+ machine_id=sensor.get("machine_id"),
50
+ timestamp=sensor.get("timestamp"),
51
+ risk_score=classification_result.get("risk_score"),
52
+ failure_predicted=True,
53
+ failure_type=classification_result.get("failure_type"),
54
+ predicted_failure_time=time_series_result.get("predictions", {}).get("predicted_timestamp"),
55
+ confidence=classification_result.get("confidence")
56
+ )
57
+
58
+ if save_result is None or save_result.get("error"):
59
+ logger.error(f"Failed to save prediction for UDI {sensor_udi} - resetting for retry")
60
+ database.reset_last_processed_id()
61
+ return {
62
+ "success": False,
63
+ "error": "Failed to save prediction to database"
64
+ }
65
+ logger.info(f"Prediction successfully saved for UDI {sensor_udi}")
66
+ return {
67
+ "success": True,
68
+ "failure_predicted": True,
69
+ "failure_type": classification_result.get("failure_type"),
70
+ "confidence": classification_result.get("confidence"),
71
+ "risk_score": classification_result.get("risk_score"),
72
+ "risk_level": classification_result.get("risk_level"),
73
+ "all_probabilities": classification_result.get("all_probabilities"),
74
+ "prediction_time_stamp": time_series_result.get("predictions"),
75
+ "timestamp": sensor.get("timestamp")
76
+ }
77
+ else:
78
+ logger.error(f"Classification or time series prediction failed for UDI {sensor_udi}")
79
+ database.reset_last_processed_id()
80
+
81
+ return binary_result
82
+ else:
83
+ database.create_new_predictions(
84
+ machine_id=sensor.get("machine_id"),
85
+ timestamp=sensor.get("timestamp"),
86
+ risk_score=binary_result.get("risk_score"),
87
+ failure_predicted=binary_result.get("failure_predicted"),
88
+ failure_type=NULL,
89
+ predicted_failure_time=NULL,
90
+ confidence=binary_result.get("confidence")
91
+ )
92
+ return binary_result
controller.py CHANGED
@@ -1,217 +1,241 @@
 
 
1
  from database import Database
2
  from model import Model
3
  from utils import (
 
4
  convert_cyclical_to_original,
5
  create_timestamp_from_predictions,
6
- prepare_prediction_data,
7
- prepare_sensor_data_for_anomaly,
8
- calculate_risk_score,
9
- get_risk_level,
10
  get_failure_severity,
11
  get_failure_type_name,
 
 
 
12
  )
13
 
14
- from datetime import datetime
 
15
 
16
  class Controller:
17
- def __init__(self):
18
- self.__database = Database()
19
- self.__sensor = self.__database.get_sensor_readings()
20
  self.__model = Model()
21
-
22
- def _get_hardcoded_error_sensor(self):
23
- """Hardcoded sensor data yang pasti error untuk testing"""
24
- return {
25
- "id": 9999,
26
- "air_temp": 298.9,
27
- "process_temp": 309.1,
28
- "rotational_speed": 2861,
29
- "torque": 4.6,
30
- "tool_wear": 143,
31
- "timestamp": datetime.now(),
32
- "created_at": datetime.now()
33
- }
34
  # Binary method
35
  def predict_binary(self):
36
  if self.__sensor is None:
 
37
  return {
38
  "success": False,
39
  "error": "No sensor data available from database.",
40
  }
41
-
 
 
 
 
 
 
 
42
  if (
43
  self.__model.model_binary is None
44
  or self.__model.preprocessor_anomaly is None
45
  ):
 
46
  return {
47
  "success": False,
48
  "error": "Binary model or preprocessor not loaded.",
49
  }
50
-
51
- X_scaled = prepare_sensor_data_for_anomaly(
52
- self.__sensor, self.__model.preprocessor_anomaly
53
- )
54
-
55
- if X_scaled is None:
56
- return {"success": False, "error": "Failed to prepare sensor data."}
57
-
58
- if hasattr(self.__model.model_binary, "predict_proba"):
59
- probabilities = self.__model.model_binary.predict_proba(X_scaled)[0]
60
- confidence_normal = float(probabilities[0])
61
- confidence_error = float(probabilities[1])
62
 
63
- is_error = int(confidence_error > 0.5)
64
- confidence = confidence_error if is_error else confidence_normal
65
- else:
66
- prediction = self.__model.model_binary.predict(X_scaled)
67
- is_error = int(prediction[0])
68
- confidence = 1.0 if is_error else 0.0
69
-
70
- risk_score = confidence * 100 if is_error else (1 - confidence) * 100
71
-
72
- result = {
73
- "success": True,
74
- "failure_predicted": bool(is_error),
75
- "confidence": float(f"{confidence}"),
76
- "risk_score": float(f"{risk_score}"),
77
- "sensor_data": {
78
- "air_temp": self.__sensor.get("air_temp"),
79
- "process_temp": self.__sensor.get("process_temp"),
80
- "rotational_speed": self.__sensor.get("rotational_speed"),
81
- "torque": self.__sensor.get("torque"),
82
- "tool_wear": self.__sensor.get("tool_wear"),
83
- },
84
- }
85
-
86
- if is_error:
87
- multiclass_result = self.predict_classification()
88
- if multiclass_result.get("success"):
89
- result["failure_analysis"] = {
90
- "failure_type": multiclass_result.get("failure_type"),
91
- "confidence": multiclass_result.get("confidence"),
92
- "risk_level": multiclass_result.get("risk_level"),
93
- "risk_score": multiclass_result.get("risk_score"),
94
- "all_probabilities": multiclass_result.get("all_probabilities"),
95
- }
96
- else:
97
- result["failure_analysis"] = {
98
- "failure_type": "No Failure",
99
- "risk_level": "Very Low",
100
  }
101
-
102
- return result
103
 
104
  # Classification Method
105
  def predict_classification(self):
106
  if self.__sensor is None:
 
107
  return {
108
  "success": False,
109
  "error": "No sensor data available from database.",
110
  }
 
 
 
 
 
 
 
111
 
112
  if (
113
  self.__model.model_multiclass is None
114
  or self.__model.preprocessor_anomaly is None
115
  ):
 
116
  return {"success": False, "error": "Model or scalers not loaded."}
117
-
118
- X_scaled = prepare_sensor_data_for_anomaly(
119
- self.__sensor, self.__model.preprocessor_anomaly
120
- )
121
-
122
- if X_scaled is None:
123
- return {"success": False, "error": "Failed to prepare sensor data."}
124
-
125
- prediction = self.__model.model_multiclass.predict(X_scaled)
126
-
127
- if hasattr(self.__model.model_multiclass, "predict_proba"):
128
- probabilities = self.__model.model_multiclass.predict_proba(X_scaled)[0]
129
- failure_index = int(probabilities.argmax())
130
- confidence = float(probabilities[failure_index])
131
-
132
- all_probs = {
133
- get_failure_type_name(i): float(prob)
134
- for i, prob in enumerate(probabilities)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  }
136
- else:
137
- failure_index = int(prediction[0])
138
- confidence = 1.0
139
- all_probs = None
140
-
141
- failure_type = get_failure_type_name(failure_index)
142
-
143
- severity = get_failure_severity(failure_index)
144
- risk_score = calculate_risk_score(confidence, severity)
145
- risk_level = get_risk_level(risk_score)
146
-
147
- return {
148
- "success": True,
149
- "failure_type": failure_type,
150
- "confidence": float(f"{confidence}"),
151
- "risk_score": float(f"{risk_score}"),
152
- "risk_level": risk_level,
153
- "all_probabilities": all_probs,
154
- }
155
 
156
  # Time series method
157
  def predict_time_series(self):
158
  if self.__sensor is None:
 
159
  return {
160
  "success": False,
161
  "error": "No sensor data available from database.",
162
  }
 
 
 
 
 
 
 
163
 
164
  if (
165
  self.__model.model_lstm is None
166
  or self.__model.scaler_x is None
167
  or self.__model.scaler_y is None
168
  ):
 
169
  return {"success": False, "error": "Model or scalers not loaded."}
170
-
171
- timestamp = self.__sensor.get("timestamp", None)
172
-
173
- X_sequence = prepare_prediction_data(
174
- self.__sensor, timestamp, self.__model.scaler_x, 32
175
- )
176
-
177
- if X_sequence is None:
178
- return {"success": False, "error": "Failed to prepare prediction data."}
179
-
180
- scaled_prediction = self.__model.model_lstm.predict(X_sequence, verbose=0)
181
-
182
- prediction = self.__model.scaler_y.inverse_transform(scaled_prediction)
183
-
184
- pred_values = prediction[0]
185
- original_values = convert_cyclical_to_original(
186
- pred_values[0], # hour_sin
187
- pred_values[1], # hour_cos
188
- pred_values[2], # dayofweek_sin
189
- pred_values[3], # dayofweek_cos
190
- pred_values[4], # dayofyear_sin
191
- pred_values[5], # dayofyear_cos
192
- pred_values[6], # month_sin
193
- pred_values[7], # month_cos
194
- )
195
-
196
- predicted_timestamp = create_timestamp_from_predictions(
197
- original_values, self.__sensor.get("timestamp", None)
198
- )
199
-
200
- return {
201
- "success": True,
202
- "predictions": {
203
- "hour": original_values["hour"],
204
- "dayofweek": original_values["dayofweek"],
205
- "dayofyear": original_values["dayofyear"],
206
- "month": original_values["month"],
207
- "predicted_timestamp": predicted_timestamp,
208
- },
209
- "raw_sensor_data": {
210
- "air_temp": self.__sensor.get("air_temp"),
211
- "process_temp": self.__sensor.get("process_temp"),
212
- "rotational_speed": self.__sensor.get("rotational_speed"),
213
- "torque": self.__sensor.get("torque"),
214
- "tool_wear": self.__sensor.get("tool_wear"),
215
- },
216
- "input_timestamp": str(timestamp),
217
- }
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+
3
  from database import Database
4
  from model import Model
5
  from utils import (
6
+ calculate_risk_score,
7
  convert_cyclical_to_original,
8
  create_timestamp_from_predictions,
 
 
 
 
9
  get_failure_severity,
10
  get_failure_type_name,
11
+ get_risk_level,
12
+ prepare_prediction_data,
13
+ prepare_sensor_data_for_anomaly,
14
  )
15
 
16
+ logger = logging.getLogger(__name__)
17
+
18
 
19
  class Controller:
20
+ def __init__(self, database_sensor):
21
+ Database()
22
+ self.__sensor = database_sensor
23
  self.__model = Model()
24
+
 
 
 
 
 
 
 
 
 
 
 
 
25
  # Binary method
26
  def predict_binary(self):
27
  if self.__sensor is None:
28
+ logger.warning("Binary prediction attempted with no sensor data")
29
  return {
30
  "success": False,
31
  "error": "No sensor data available from database.",
32
  }
33
+
34
+ if self.__sensor.get("message") == "Data already predicted":
35
+ logger.info(f"Skipping binary prediction - data already predicted for UDI: {self.__sensor.get('udi')}")
36
+ return {
37
+ "success": False,
38
+ "error": "Data already predicted",
39
+ }
40
+
41
  if (
42
  self.__model.model_binary is None
43
  or self.__model.preprocessor_anomaly is None
44
  ):
45
+ logger.error("Binary model or preprocessor not loaded - cannot perform prediction")
46
  return {
47
  "success": False,
48
  "error": "Binary model or preprocessor not loaded.",
49
  }
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
+ try:
52
+ X_scaled = prepare_sensor_data_for_anomaly(
53
+ self.__sensor, self.__model.preprocessor_anomaly
54
+ )
55
+
56
+ if X_scaled is None:
57
+ logger.error("Failed to prepare sensor data for binary prediction")
58
+ return {"success": False, "error": "Failed to prepare sensor data."}
59
+
60
+ if hasattr(self.__model.model_binary, "predict_proba"):
61
+ probabilities = self.__model.model_binary.predict_proba(X_scaled)[0]
62
+ confidence_normal = float(probabilities[0])
63
+ confidence_error = float(probabilities[1])
64
+
65
+ is_error = int(confidence_error > 0.5)
66
+ confidence = confidence_error if is_error else confidence_normal
67
+ else:
68
+ prediction = self.__model.model_binary.predict(X_scaled)
69
+ is_error = int(prediction[0])
70
+ confidence = 1.0 if is_error else 0.0
71
+
72
+ risk_score = confidence * 100 if is_error else (1 - confidence) * 100
73
+
74
+ logger.info(f"Binary prediction complete - Failure: {bool(is_error)}, Confidence: {confidence:.2f}, Risk: {risk_score:.2f}")
75
+
76
+ result = {
77
+ "success": True,
78
+ "failure_predicted": bool(is_error),
79
+ "confidence": float(f"{confidence}"),
80
+ "risk_score": float(f"{risk_score}"),
81
+ }
82
+ return result
83
+ except Exception as e:
84
+ logger.error(f"Exception in binary prediction: {str(e)}")
85
+ return {
86
+ "success": False,
87
+ "message": f"Failed to predict binary data: {str(e)}"
88
  }
 
 
89
 
90
  # Classification Method
91
  def predict_classification(self):
92
  if self.__sensor is None:
93
+ logger.warning("Classification prediction attempted with no sensor data")
94
  return {
95
  "success": False,
96
  "error": "No sensor data available from database.",
97
  }
98
+
99
+ if self.__sensor.get("message") == "Data already predicted":
100
+ logger.info(f"Skipping classification prediction - data already predicted for UDI: {self.__sensor.get('udi')}")
101
+ return {
102
+ "success": False,
103
+ "error": "Data already predicted",
104
+ }
105
 
106
  if (
107
  self.__model.model_multiclass is None
108
  or self.__model.preprocessor_anomaly is None
109
  ):
110
+ logger.error("Multiclass model or preprocessor not loaded - cannot perform prediction")
111
  return {"success": False, "error": "Model or scalers not loaded."}
112
+
113
+ try:
114
+ X_scaled = prepare_sensor_data_for_anomaly(
115
+ self.__sensor, self.__model.preprocessor_anomaly
116
+ )
117
+
118
+ if X_scaled is None:
119
+ logger.error("Failed to prepare sensor data for classification prediction")
120
+ return {"success": False, "error": "Failed to prepare sensor data."}
121
+
122
+ prediction = self.__model.model_multiclass.predict(X_scaled)
123
+
124
+ if hasattr(self.__model.model_multiclass, "predict_proba"):
125
+ probabilities = self.__model.model_multiclass.predict_proba(X_scaled)[0]
126
+ failure_index = int(probabilities.argmax())
127
+ confidence = float(probabilities[failure_index])
128
+
129
+ all_probs = {
130
+ get_failure_type_name(i): float(prob)
131
+ for i, prob in enumerate(probabilities)
132
+ }
133
+ else:
134
+ failure_index = int(prediction[0])
135
+ confidence = 1.0
136
+ all_probs = None
137
+
138
+ failure_type = get_failure_type_name(failure_index)
139
+
140
+ severity = get_failure_severity(failure_index)
141
+ risk_score = calculate_risk_score(confidence, severity)
142
+ risk_level = get_risk_level(risk_score)
143
+
144
+ logger.info(f"Classification prediction complete - Failure Type: {failure_type}, Confidence: {confidence:.2f}, Risk Level: {risk_level}, Risk Score: {risk_score:.2f}")
145
+
146
+ return {
147
+ "success": True,
148
+ "failure_type": failure_type,
149
+ "confidence": float(f"{confidence}"),
150
+ "risk_score": float(f"{risk_score}"),
151
+ "risk_level": risk_level,
152
+ "all_probabilities": all_probs,
153
+ }
154
+ except Exception as e:
155
+ logger.error(f"Exception in classification prediction: {str(e)}")
156
+ return {
157
+ "success": False,
158
+ "message": f"Failed to predict classification data: {str(e)}"
159
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
  # Time series method
162
  def predict_time_series(self):
163
  if self.__sensor is None:
164
+ logger.warning("Time series prediction attempted with no sensor data")
165
  return {
166
  "success": False,
167
  "error": "No sensor data available from database.",
168
  }
169
+
170
+ if self.__sensor.get("message") == "Data already predicted":
171
+ logger.info(f"Skipping time series prediction - data already predicted for UDI: {self.__sensor.get('udi')}")
172
+ return {
173
+ "success": False,
174
+ "error": "Data already predicted",
175
+ }
176
 
177
  if (
178
  self.__model.model_lstm is None
179
  or self.__model.scaler_x is None
180
  or self.__model.scaler_y is None
181
  ):
182
+ logger.error("LSTM model or scalers not loaded - cannot perform prediction")
183
  return {"success": False, "error": "Model or scalers not loaded."}
184
+
185
+ try:
186
+ timestamp = self.__sensor.get("timestamp", None)
187
+
188
+ X_sequence = prepare_prediction_data(
189
+ self.__sensor, timestamp, self.__model.scaler_x, 32
190
+ )
191
+
192
+ if X_sequence is None:
193
+ logger.error("Failed to prepare prediction data for time series")
194
+ return {"success": False, "error": "Failed to prepare prediction data."}
195
+
196
+ scaled_prediction = self.__model.model_lstm.predict(X_sequence, verbose=0)
197
+
198
+ prediction = self.__model.scaler_y.inverse_transform(scaled_prediction)
199
+
200
+ pred_values = prediction[0]
201
+ original_values = convert_cyclical_to_original(
202
+ pred_values[0], # hour_sin
203
+ pred_values[1], # hour_cos
204
+ pred_values[2], # dayofweek_sin
205
+ pred_values[3], # dayofweek_cos
206
+ pred_values[4], # dayofyear_sin
207
+ pred_values[5], # dayofyear_cos
208
+ pred_values[6], # month_sin
209
+ pred_values[7], # month_cos
210
+ )
211
+
212
+ predicted_timestamp = create_timestamp_from_predictions(
213
+ original_values, self.__sensor.get("timestamp", None)
214
+ )
215
+
216
+ logger.info(f"Time series prediction complete - Predicted Timestamp: {predicted_timestamp}, Hour: {original_values['hour']}, Day of Week: {original_values['dayofweek']}")
217
+
218
+ return {
219
+ "success": True,
220
+ "predictions": {
221
+ "hour": original_values["hour"],
222
+ "dayofweek": original_values["dayofweek"],
223
+ "dayofyear": original_values["dayofyear"],
224
+ "month": original_values["month"],
225
+ "predicted_timestamp": predicted_timestamp,
226
+ },
227
+ "raw_sensor_data": {
228
+ "air_temp": self.__sensor.get("air_temp"),
229
+ "process_temp": self.__sensor.get("process_temp"),
230
+ "rotational_speed": self.__sensor.get("rotational_speed"),
231
+ "torque": self.__sensor.get("torque"),
232
+ "tool_wear": self.__sensor.get("tool_wear"),
233
+ },
234
+ "input_timestamp": str(timestamp),
235
+ }
236
+ except Exception as e:
237
+ logger.error(f"Exception in time series prediction: {str(e)}")
238
+ return {
239
+ "success": False,
240
+ "message": f"Failed to predict time-series data: {str(e)}"
241
+ }
database.py CHANGED
@@ -1,8 +1,10 @@
1
  import os
 
2
  from dotenv import load_dotenv
3
  from supabase import create_client, Client
4
 
5
  load_dotenv()
 
6
 
7
  class Database():
8
  def __init__(self):
@@ -10,19 +12,69 @@ class Database():
10
  self.__key: str = os.environ.get("SUPABASE_KEY") or ""
11
 
12
  if not self.__url or not self.__key:
 
13
  raise ValueError("SUPABASE_URL or SUPABASE_KEY is missing in environment")
14
-
15
- self.__supabase: Client = create_client(self.__url, self.__key)
 
 
 
 
 
 
 
 
 
 
16
 
 
17
  def get_sensor_readings(self):
18
- response = (
19
- self.__supabase.table("sensor_readings")
20
- .select("*")
21
- .order("created_at", desc=True)
22
- .limit(1)
23
- .execute()
24
- )
25
-
26
- if response.data:
27
- return response.data[0]
28
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
+ import logging
3
  from dotenv import load_dotenv
4
  from supabase import create_client, Client
5
 
6
  load_dotenv()
7
+ logger = logging.getLogger(__name__)
8
 
9
  class Database():
10
  def __init__(self):
 
12
  self.__key: str = os.environ.get("SUPABASE_KEY") or ""
13
 
14
  if not self.__url or not self.__key:
15
+ logger.critical("SUPABASE_URL or SUPABASE_KEY is missing in environment")
16
  raise ValueError("SUPABASE_URL or SUPABASE_KEY is missing in environment")
17
+
18
+ try:
19
+ self.__supabase: Client = create_client(self.__url, self.__key)
20
+ self.__last_processed_id = None
21
+ logger.info("Database connection established successfully")
22
+ except Exception as e:
23
+ logger.critical(f"Failed to create Supabase client: {str(e)}")
24
+ raise
25
+
26
+ def reset_last_processed_id(self):
27
+ self.__last_processed_id = None
28
+ logger.info("Reset last_processed_id - data can be reprocessed")
29
 
30
+
31
  def get_sensor_readings(self):
32
+ try:
33
+ response = (
34
+ self.__supabase.table("sensor_readings")
35
+ .select("*")
36
+ .order("created_at", desc=True)
37
+ .limit(1)
38
+ .execute()
39
+ )
40
+
41
+ if response.data:
42
+ current_reading = response.data[0]
43
+
44
+ if self.__last_processed_id == current_reading.get("udi"):
45
+ logger.warning(f"Duplicate data detected - UDI {current_reading.get('udi')} already processed")
46
+ return {
47
+ "success": False,
48
+ "message": "Data already predicted",
49
+ }
50
+
51
+ self.__last_processed_id = current_reading.get("udi")
52
+ logger.debug(f"Retrieved sensor reading - UDI: {current_reading.get('udi')}, Machine: {current_reading.get('machine_id')}")
53
+ return current_reading
54
+
55
+ logger.warning("No sensor readings found in database")
56
+ return None
57
+ except Exception as e:
58
+ logger.error(f"Failed to retrieve sensor readings: {str(e)}")
59
+ return None
60
+
61
+ def create_new_predictions(self, machine_id, timestamp, risk_score, failure_predicted, failure_type, predicted_failure_time, confidence):
62
+ try:
63
+ response = (
64
+ self.__supabase.table("prediction_results")
65
+ .insert({
66
+ "machine_id": machine_id,
67
+ "timestamp": timestamp,
68
+ "risk_score": risk_score,
69
+ "failure_predicted": failure_predicted,
70
+ "failure_type": failure_type,
71
+ "predicted_failure_time": predicted_failure_time,
72
+ "confidence": confidence
73
+ })
74
+ .execute()
75
+ )
76
+ logger.info(f"Prediction saved - Machine: {machine_id}, Failure: {failure_predicted}, Risk: {risk_score}")
77
+ return {"success": True, "data": response}
78
+ except Exception as e:
79
+ logger.error(f"Failed to save prediction to database: {str(e)}")
80
+ return {"success": False, "error": str(e)}