justKevv commited on
Commit
c341bcb
·
1 Parent(s): 8c90b73

fix(machine_id): change the last processed id to dict to better get the

Browse files
Files changed (3) hide show
  1. SETUP.md +152 -0
  2. app.py +5 -5
  3. database.py +14 -12
SETUP.md ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Setup and Recreation Guide
2
+
3
+ This document provides instructions on how to set up and recreate the Predictive Machine project locally without overwriting the `README.md` file (which is essential for Hugging Face Spaces).
4
+
5
+ ## Prerequisites
6
+
7
+ - Python 3.8 or higher
8
+ - pip (Python package manager)
9
+ - Git
10
+ - Docker (optional, for containerized deployment)
11
+
12
+ ## Local Setup
13
+
14
+ ### 1. Clone the Repository
15
+
16
+ ```bash
17
+ git clone https://huggingface.co/spaces/Asah-ML-Copilot-A25-CS047/Predictive-Machine
18
+ cd Predictive-Machine
19
+ ```
20
+
21
+ ### 2. Create a Virtual Environment
22
+
23
+ ```bash
24
+ python -m venv venv
25
+ source venv/bin/activate # On Windows: venv\Scripts\activate
26
+ ```
27
+
28
+ ### 3. Install Dependencies
29
+
30
+ ```bash
31
+ pip install -r requirements.txt
32
+ ```
33
+
34
+ ### 4. Download the Dataset
35
+
36
+ The project uses the Kaggle dataset: [Machine Predictive Maintenance Classification](https://www.kaggle.com/datasets/shivamb/machine-predictive-maintenance-classification/data)
37
+
38
+ - Download the dataset from Kaggle
39
+ - Extract it to a `data/` directory in the project root
40
+ - Or set up Kaggle API credentials:
41
+
42
+ ```bash
43
+ pip install kaggle
44
+ kaggle datasets download -d shivamb/machine-predictive-maintenance-classification
45
+ unzip machine-predictive-maintenance-classification.zip -d data/
46
+ ```
47
+
48
+ ### 5. Run the Application
49
+
50
+ ```bash
51
+ uvicorn app:app --reload
52
+ ```
53
+
54
+
55
+ ## Preserving README.md for Hugging Face Spaces
56
+
57
+ **Important:** The `README.md` file contains critical Hugging Face Spaces configuration metadata (YAML front matter). When updating the project:
58
+
59
+ 1. **Never overwrite or delete `README.md`**
60
+ 2. Keep the YAML header intact:
61
+ ```yaml
62
+ ---
63
+ title: Predictive Machine
64
+ emoji: 🚨
65
+ colorFrom: pink
66
+ colorTo: indigo
67
+ sdk: docker
68
+ pinned: true
69
+ license: apache-2.0
70
+ short_description: Predictive Machine Copilot for Asah by Dicoding x Accenture
71
+ datasets: [https://www.kaggle.com/datasets/shivamb/machine-predictive-maintenance-classification/data]
72
+ ---
73
+ ```
74
+ 3. Use this `SETUP.md` file for development documentation instead
75
+ 4. If you need to update README.md content, only modify the area AFTER the closing `---`
76
+
77
+ ## Docker Deployment
78
+
79
+ ### Build the Docker Image
80
+
81
+ ```bash
82
+ docker build -t predictive-machine .
83
+ ```
84
+
85
+ ### Run the Container
86
+
87
+ ```bash
88
+ docker run -p 7860:7860 predictive-machine
89
+ ```
90
+
91
+ The application will be available at `http://localhost:7860`
92
+
93
+ ## Project Structure
94
+
95
+ ```
96
+ Predictive-Machine/
97
+ ├── README.md # Hugging Face Spaces config (DO NOT OVERWRITE)
98
+ ├── SETUP.md # This file - local setup instructions
99
+ ├── Dockerfile # Docker configuration
100
+ ├── requirements.txt # Python dependencies
101
+ ├── app.py # Main application
102
+ ├── data/ # Dataset directory
103
+ ├── models/ # Trained models
104
+ └── src/ # Source code modules
105
+ ```
106
+
107
+ ## Development Workflow
108
+
109
+ 1. Create a new branch for features:
110
+ ```bash
111
+ git checkout -b feature/your-feature-name
112
+ ```
113
+
114
+ 2. Make changes and test locally
115
+
116
+ 3. Commit and push changes:
117
+ ```bash
118
+ git add .
119
+ git commit -m "Description of changes"
120
+ git push origin feature/your-feature-name
121
+ ```
122
+
123
+ 4. Create a pull request
124
+
125
+ 5. Once merged, the changes will be reflected in the Hugging Face Spaces deployment
126
+
127
+ ## Troubleshooting
128
+
129
+ ### Dataset Download Issues
130
+ - Ensure you have Kaggle API credentials configured: `~/.kaggle/kaggle.json`
131
+ - Or download manually from Kaggle and place files in `data/` directory
132
+
133
+ ### Dependency Conflicts
134
+ ```bash
135
+ pip install --upgrade pip
136
+ pip install -r requirements.txt --force-reinstall
137
+ ```
138
+
139
+ ### Docker Build Issues
140
+ ```bash
141
+ docker build --no-cache -t predictive-machine .
142
+ ```
143
+
144
+ ## License
145
+
146
+ This project is licensed under the Apache 2.0 License - see LICENSE file for details.
147
+
148
+ ## Resources
149
+
150
+ - [Hugging Face Spaces Documentation](https://huggingface.co/docs/hub/spaces)
151
+ - [Hugging Face Spaces Config Reference](https://huggingface.co/docs/hub/spaces-config-reference)
152
+ - [Dataset: Machine Predictive Maintenance Classification](https://www.kaggle.com/datasets/shivamb/machine-predictive-maintenance-classification)
app.py CHANGED
@@ -88,7 +88,7 @@ def predict_machine():
88
  if not binary_result.get("success"):
89
  logger.error(f"Binary prediction failed for {machine_name}: {binary_result.get('error')}")
90
  if binary_result.get("error") != "Data already predicted":
91
- database.reset_last_processed_id()
92
  all_results.append(binary_result)
93
  continue
94
 
@@ -124,7 +124,7 @@ def predict_machine():
124
 
125
  if save_result is None or save_result.get("error"):
126
  logger.error(f"Failed to save prediction for UDI {sensor_udi} - resetting for retry")
127
- database.reset_last_processed_id()
128
  all_results.append({
129
  "machine_name": machine_name,
130
  "success": False,
@@ -132,7 +132,7 @@ def predict_machine():
132
  })
133
  continue
134
  logger.info(f"Prediction successfully saved for UDI {sensor_udi}")
135
- database.mark_as_processed(sensor_udi)
136
 
137
  all_results.append({
138
  "machine_name": machine_name,
@@ -150,7 +150,7 @@ def predict_machine():
150
  else:
151
  logger.error(f"Classification or time series prediction failed for UDI {sensor_udi}")
152
  if not (isinstance(classification_result, dict) and classification_result.get("error") == "Data already predicted"):
153
- database.reset_last_processed_id()
154
  all_results.append(binary_result)
155
  else:
156
  logger.info(f"No failure predicted for UDI {sensor_udi}")
@@ -165,7 +165,7 @@ def predict_machine():
165
  )
166
 
167
  if save_result and save_result.get("success"):
168
- database.mark_as_processed(sensor_udi)
169
 
170
  all_results.append({
171
  "machine_name": machine_name,
 
88
  if not binary_result.get("success"):
89
  logger.error(f"Binary prediction failed for {machine_name}: {binary_result.get('error')}")
90
  if binary_result.get("error") != "Data already predicted":
91
+ database.reset_last_processed_id(machine_id)
92
  all_results.append(binary_result)
93
  continue
94
 
 
124
 
125
  if save_result is None or save_result.get("error"):
126
  logger.error(f"Failed to save prediction for UDI {sensor_udi} - resetting for retry")
127
+ database.reset_last_processed_id(machine_id)
128
  all_results.append({
129
  "machine_name": machine_name,
130
  "success": False,
 
132
  })
133
  continue
134
  logger.info(f"Prediction successfully saved for UDI {sensor_udi}")
135
+ database.mark_as_processed(machine_id, sensor_udi)
136
 
137
  all_results.append({
138
  "machine_name": machine_name,
 
150
  else:
151
  logger.error(f"Classification or time series prediction failed for UDI {sensor_udi}")
152
  if not (isinstance(classification_result, dict) and classification_result.get("error") == "Data already predicted"):
153
+ database.reset_last_processed_id(machine_id)
154
  all_results.append(binary_result)
155
  else:
156
  logger.info(f"No failure predicted for UDI {sensor_udi}")
 
165
  )
166
 
167
  if save_result and save_result.get("success"):
168
+ database.mark_as_processed(machine_id, sensor_udi)
169
 
170
  all_results.append({
171
  "machine_name": machine_name,
database.py CHANGED
@@ -17,18 +17,21 @@ class Database():
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 mark_as_processed(self, udi):
27
- self.__last_processed_id = udi
 
 
28
  logger.info(f"Marked UDI {udi} as processed")
29
 
30
- def reset_last_processed_id(self):
31
- self.__last_processed_id = None
 
32
  logger.info("Reset last_processed_id - data can be reprocessed")
33
 
34
  def get_all_machine_id(self):
@@ -42,7 +45,7 @@ class Database():
42
  except Exception as e:
43
  return e
44
 
45
- def get_sensor_readings(self, limit, machine_id):
46
  try:
47
  query = self.__supabase.table("sensor_readings").select("*")
48
 
@@ -61,19 +64,18 @@ class Database():
61
  return None
62
 
63
  if limit == 1:
64
- # Single reading mode - check for duplicates
65
  current_reading = response.data[0]
 
66
 
67
- # Prevent reprocessing the same UDI
68
- if self.__last_processed_id == current_reading.get("udi"):
69
- logger.warning(f"Duplicate data detected - UDI {current_reading.get('udi')} already processed")
70
  return {
71
  "success": False,
72
  "message": "Data already predicted",
73
  }
74
 
75
- self.__last_processed_id = current_reading.get("udi")
76
- logger.debug(f"Retrieved single sensor reading - UDI: {current_reading.get('udi')}, Machine: {current_reading.get('machine_id')}")
77
  return current_reading
78
 
79
  else:
 
17
 
18
  try:
19
  self.__supabase: Client = create_client(self.__url, self.__key)
20
+ self.__last_processed_ids = {}
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 mark_as_processed(self, machine_id, udi):
27
+ if machine_id not in self.__last_processed_ids:
28
+ self.__last_processed_ids[machine_id] = []
29
+ self.__last_processed_ids[machine_id].append(udi)
30
  logger.info(f"Marked UDI {udi} as processed")
31
 
32
+ def reset_last_processed_id(self, machine_id):
33
+ if machine_id in self.__last_processed_ids:
34
+ self.__last_processed_ids[machine_id] = []
35
  logger.info("Reset last_processed_id - data can be reprocessed")
36
 
37
  def get_all_machine_id(self):
 
45
  except Exception as e:
46
  return e
47
 
48
+ def get_sensor_readings(self, limit, machine_id):
49
  try:
50
  query = self.__supabase.table("sensor_readings").select("*")
51
 
 
64
  return None
65
 
66
  if limit == 1:
 
67
  current_reading = response.data[0]
68
+ current_udi = current_reading.get("udi")
69
 
70
+ machine_processed_udis = self.__last_processed_ids.get(machine_id, [])
71
+ if current_udi in machine_processed_udis:
72
+ logger.warning(f"Duplicate data detected for Machine {machine_id} - UDI {current_udi} already processed")
73
  return {
74
  "success": False,
75
  "message": "Data already predicted",
76
  }
77
 
78
+ logger.debug(f"Retrieved single sensor reading - UDI: {current_udi}, Machine: {machine_id}")
 
79
  return current_reading
80
 
81
  else: