Sompote commited on
Commit
80f87f8
·
verified ·
1 Parent(s): 6cc0f7d

Upload 15 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ logo2.e8c5ff97.png filter=lfs diff=lfs merge=lfs -text
Dockerfile CHANGED
@@ -1,21 +1,28 @@
1
- FROM python:3.9-slim
 
2
 
 
3
  WORKDIR /app
4
 
5
- RUN apt-get update && apt-get install -y \
6
- build-essential \
7
- curl \
8
- software-properties-common \
9
- git \
10
- && rm -rf /var/lib/apt/lists/*
11
 
12
- COPY requirements.txt ./
13
- COPY src/ ./src/
14
 
15
- RUN pip3 install -r requirements.txt
 
16
 
 
 
 
 
 
17
  EXPOSE 8501
18
 
19
- HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
 
 
20
 
21
- ENTRYPOINT ["streamlit", "run", "src/streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"]
 
 
1
+ # HDD Solution Predictor - Docker Deployment
2
+ FROM python:3.11-slim
3
 
4
+ # Set working directory
5
  WORKDIR /app
6
 
7
+ # Copy requirements first (for better caching)
8
+ COPY requirements.txt .
 
 
 
 
9
 
10
+ # Install Python dependencies
11
+ RUN pip install --no-cache-dir -r requirements.txt
12
 
13
+ # Copy application files
14
+ COPY . .
15
 
16
+ # Create non-root user for security
17
+ RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
18
+ USER appuser
19
+
20
+ # Expose port
21
  EXPOSE 8501
22
 
23
+ # Health check
24
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
25
+ CMD curl -f http://localhost:8501/_stcore/health || exit 1
26
 
27
+ # Run application
28
+ CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0", "--server.headless=true"]
HDD_result.xlsx ADDED
Binary file (9.14 kB). View file
 
Procfile ADDED
@@ -0,0 +1 @@
 
 
1
+ web: streamlit run app.py --server.port=$PORT --server.address=0.0.0.0
README1.md ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚀 HDD Solution Predictor - Deployment Package
2
+
3
+ Ready-to-deploy package for the HDD Solution Predictor application.
4
+
5
+ ## 📦 Package Contents
6
+
7
+ ### **Application Files**
8
+ - `app.py` - Main Streamlit application (recommended)
9
+ - `mobile_app.py` - Mobile-optimized version
10
+ - `requirements.txt` - Python dependencies
11
+
12
+ ### **Model Files**
13
+ - `decision_tree_model.pkl` - Trained Decision Tree model (100% accuracy)
14
+ - `dt_soil_encoder.pkl` - Soil type label encoder
15
+ - `dt_water_encoder.pkl` - Water table label encoder
16
+ - `dt_solution_encoder.pkl` - Solution label encoder
17
+
18
+ ### **Assets**
19
+ - `logo2.e8c5ff97.png` - MEA (Metropolitan Electricity Authority) logo
20
+ - `HDD_result.xlsx` - Training dataset
21
+
22
+ ### **Documentation**
23
+ - `README.md` - This deployment guide
24
+
25
+ ## 🔧 Quick Deployment
26
+
27
+ ### **1. Install Dependencies**
28
+ ```bash
29
+ pip install -r requirements.txt
30
+ ```
31
+
32
+ ### **2. Run Application**
33
+ ```bash
34
+ # Main application (recommended)
35
+ streamlit run app.py
36
+
37
+ # Or mobile version
38
+ streamlit run mobile_app.py
39
+ ```
40
+
41
+ ### **3. Access Application**
42
+ - Local: `http://localhost:8501`
43
+ - Network: Will be shown in terminal
44
+
45
+ ## 🌐 Deployment Options
46
+
47
+ ### **Option A: Local Development**
48
+ ```bash
49
+ git clone <repository>
50
+ cd deploy
51
+ pip install -r requirements.txt
52
+ streamlit run app.py
53
+ ```
54
+
55
+ ### **Option B: Streamlit Cloud**
56
+ 1. Upload all files to GitHub repository
57
+ 2. Connect to Streamlit Cloud
58
+ 3. Deploy from `deploy/app.py`
59
+
60
+ ### **Option C: Docker Deployment**
61
+ Create `Dockerfile`:
62
+ ```dockerfile
63
+ FROM python:3.11-slim
64
+
65
+ WORKDIR /app
66
+ COPY . .
67
+
68
+ RUN pip install -r requirements.txt
69
+
70
+ EXPOSE 8501
71
+
72
+ CMD ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]
73
+ ```
74
+
75
+ ### **Option D: Heroku Deployment**
76
+ Create `Procfile`:
77
+ ```
78
+ web: streamlit run app.py --server.port=$PORT --server.address=0.0.0.0
79
+ ```
80
+
81
+ ## 📊 Application Features
82
+
83
+ ### **Core Functionality**
84
+ - **Input Parameters**: Pipe diameter, soil type, water table
85
+ - **ML Prediction**: Decision Tree with 100% training accuracy
86
+ - **Solution Types**: A, B, C, D, E (different protection levels)
87
+
88
+ ### **Technical Specifications**
89
+ - **Framework**: Streamlit
90
+ - **ML Model**: Scikit-learn Decision Tree
91
+ - **Design**: Canva-style responsive UI
92
+ - **Branding**: MEA institutional logo
93
+
94
+ ### **Supported Solutions**
95
+ - **Solution A**: Enhanced Protection (Sheetpile + Trench + Grouting)
96
+ - **Solution B**: Maximum Protection (+ Casing)
97
+ - **Solution C**: Moderate Protection (Sheetpile + Trench)
98
+ - **Solution D**: Basic Protection (Grouting Only)
99
+ - **Solution E**: Minimal Intervention (No Additional Measures)
100
+
101
+ ## 🔍 System Requirements
102
+
103
+ ### **Python Version**
104
+ - Python 3.8 or higher (recommended: 3.11)
105
+
106
+ ### **Dependencies**
107
+ - streamlit
108
+ - pandas
109
+ - numpy
110
+ - scikit-learn
111
+ - joblib
112
+ - plotly
113
+ - openpyxl
114
+ - Pillow
115
+
116
+ ### **Resources**
117
+ - **RAM**: Minimum 512MB, Recommended 1GB
118
+ - **Storage**: ~50MB for all files
119
+ - **Network**: Internet connection for initial setup
120
+
121
+ ## 🔧 Configuration
122
+
123
+ ### **Environment Variables** (Optional)
124
+ ```bash
125
+ export STREAMLIT_SERVER_PORT=8501
126
+ export STREAMLIT_SERVER_ADDRESS=0.0.0.0
127
+ ```
128
+
129
+ ### **Streamlit Config** (Create `.streamlit/config.toml`)
130
+ ```toml
131
+ [server]
132
+ port = 8501
133
+ address = "0.0.0.0"
134
+
135
+ [theme]
136
+ primaryColor = "#6c5ce7"
137
+ backgroundColor = "#ffffff"
138
+ secondaryBackgroundColor = "#f0f2f6"
139
+ ```
140
+
141
+ ## 🚨 Troubleshooting
142
+
143
+ ### **Model Loading Issues**
144
+ - Ensure all `.pkl` files are in the same directory
145
+ - Check Python/scikit-learn version compatibility
146
+
147
+ ### **Logo Not Displaying**
148
+ - Verify `logo2.e8c5ff97.png` exists in directory
149
+ - App will show MEA text fallback if logo missing
150
+
151
+ ### **Port Already in Use**
152
+ ```bash
153
+ streamlit run app.py --server.port=8502
154
+ ```
155
+
156
+ ### **Permission Issues**
157
+ ```bash
158
+ chmod +x app.py
159
+ pip install --user -r requirements.txt
160
+ ```
161
+
162
+ ## 📱 Mobile Access
163
+
164
+ The application is fully responsive and works on:
165
+ - ✅ Desktop browsers
166
+ - ✅ Mobile phones
167
+ - ✅ Tablets
168
+ - ✅ Touch devices
169
+
170
+ ## 🔒 Security Notes
171
+
172
+ - Application runs locally by default
173
+ - No external API calls
174
+ - Model predictions processed locally
175
+ - Training data included for reference only
176
+
177
+ ## 📞 Support
178
+
179
+ ### **Common Issues**
180
+ 1. **Dependencies**: Run `pip install -r requirements.txt`
181
+ 2. **Port conflicts**: Use different port with `--server.port=XXXX`
182
+ 3. **File paths**: Ensure all files are in same directory
183
+
184
+ ### **Performance**
185
+ - **Model loading**: ~1-2 seconds on first run
186
+ - **Predictions**: Instant (<100ms)
187
+ - **UI rendering**: <1 second
188
+
189
+ ---
190
+
191
+ ## 🎯 Ready to Deploy!
192
+
193
+ 1. **Install requirements**: `pip install -r requirements.txt`
194
+ 2. **Run application**: `streamlit run app.py`
195
+ 3. **Open browser**: Navigate to displayed URL
196
+ 4. **Start predicting**: Enter parameters and get solutions!
197
+
198
+ **🏢 Powered by MEA (Metropolitan Electricity Authority)**
app.py ADDED
@@ -0,0 +1,387 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import numpy as np
3
+ import joblib
4
+ from PIL import Image
5
+ import base64
6
+ from io import BytesIO
7
+
8
+ # Page configuration
9
+ st.set_page_config(
10
+ page_title="HDD Solution Predictor",
11
+ page_icon="🔧",
12
+ layout="centered",
13
+ initial_sidebar_state="collapsed"
14
+ )
15
+
16
+ # Function to convert image to base64
17
+ def image_to_base64(image_path):
18
+ try:
19
+ with open(image_path, "rb") as img_file:
20
+ return base64.b64encode(img_file.read()).decode()
21
+ except:
22
+ return None
23
+
24
+ # Enhanced CSS with better styling
25
+ st.markdown("""
26
+ <style>
27
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
28
+
29
+ /* Hide Streamlit branding */
30
+ #MainMenu {visibility: hidden;}
31
+ footer {visibility: hidden;}
32
+ header {visibility: hidden;}
33
+ .stDeployButton {visibility: hidden;}
34
+
35
+ /* Main container */
36
+ .main {
37
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
38
+ min-height: 100vh;
39
+ padding: 2rem 1rem;
40
+ }
41
+
42
+ /* Logo container */
43
+ .logo-section {
44
+ text-align: center;
45
+ margin-bottom: 2rem;
46
+ padding: 1.5rem;
47
+ background: rgba(255,255,255,0.1);
48
+ border-radius: 20px;
49
+ backdrop-filter: blur(10px);
50
+ border: 1px solid rgba(255,255,255,0.2);
51
+ }
52
+
53
+ .logo-image {
54
+ max-width: 200px;
55
+ height: auto;
56
+ filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1));
57
+ }
58
+
59
+ /* Title styling */
60
+ .main-title {
61
+ font-family: 'Inter', sans-serif;
62
+ font-size: 2.5rem;
63
+ font-weight: 700;
64
+ color: white;
65
+ text-align: center;
66
+ margin: 1rem 0 0.5rem 0;
67
+ text-shadow: 0 2px 4px rgba(0,0,0,0.1);
68
+ }
69
+
70
+ .subtitle {
71
+ font-family: 'Inter', sans-serif;
72
+ font-size: 1.1rem;
73
+ color: rgba(255,255,255,0.9);
74
+ text-align: center;
75
+ margin-bottom: 2rem;
76
+ font-weight: 400;
77
+ }
78
+
79
+ /* Input container */
80
+ .input-container {
81
+ background: white;
82
+ border-radius: 25px;
83
+ padding: 2.5rem;
84
+ box-shadow: 0 20px 40px rgba(0,0,0,0.1);
85
+ margin: 2rem auto;
86
+ max-width: 500px;
87
+ border: 1px solid rgba(255,255,255,0.2);
88
+ }
89
+
90
+ /* Input styling */
91
+ .stSelectbox label, .stSlider label {
92
+ font-family: 'Inter', sans-serif;
93
+ font-weight: 600;
94
+ color: #2d3748;
95
+ font-size: 1rem;
96
+ margin-bottom: 0.5rem;
97
+ }
98
+
99
+ .stSelectbox > div > div {
100
+ background-color: #f8f9ff;
101
+ border-radius: 15px;
102
+ border: 2px solid #e1e8ff;
103
+ font-family: 'Inter', sans-serif;
104
+ font-size: 1rem;
105
+ padding: 0.5rem 1rem;
106
+ }
107
+
108
+ .stSlider > div > div {
109
+ background-color: #f8f9ff;
110
+ border-radius: 15px;
111
+ padding: 1.2rem;
112
+ border: 2px solid #e1e8ff;
113
+ }
114
+
115
+ /* Button styling */
116
+ .stButton > button {
117
+ background: linear-gradient(135deg, #6c5ce7, #fd79a8);
118
+ color: white;
119
+ border: none;
120
+ border-radius: 20px;
121
+ padding: 1rem 2rem;
122
+ font-family: 'Inter', sans-serif;
123
+ font-weight: 600;
124
+ font-size: 1.1rem;
125
+ box-shadow: 0 10px 25px rgba(108, 92, 231, 0.3);
126
+ transition: all 0.3s ease;
127
+ width: 100%;
128
+ margin-top: 2rem;
129
+ height: 60px;
130
+ }
131
+
132
+ .stButton > button:hover {
133
+ transform: translateY(-3px);
134
+ box-shadow: 0 15px 35px rgba(108, 92, 231, 0.4);
135
+ }
136
+
137
+ /* Result styling */
138
+ .result-container {
139
+ margin: 2rem auto;
140
+ max-width: 500px;
141
+ border-radius: 25px;
142
+ padding: 2.5rem;
143
+ text-align: center;
144
+ box-shadow: 0 20px 40px rgba(0,0,0,0.15);
145
+ border: 3px solid rgba(255,255,255,0.3);
146
+ }
147
+
148
+ .solution-badge {
149
+ display: inline-block;
150
+ font-size: 4rem;
151
+ font-weight: 700;
152
+ color: white;
153
+ background: rgba(255,255,255,0.2);
154
+ border-radius: 50%;
155
+ width: 100px;
156
+ height: 100px;
157
+ line-height: 100px;
158
+ margin-bottom: 1rem;
159
+ border: 4px solid rgba(255,255,255,0.3);
160
+ box-shadow: 0 10px 20px rgba(0,0,0,0.1);
161
+ }
162
+
163
+ .solution-title {
164
+ color: white;
165
+ font-family: 'Inter', sans-serif;
166
+ font-size: 1.5rem;
167
+ font-weight: 700;
168
+ margin-bottom: 0.5rem;
169
+ text-shadow: 0 2px 4px rgba(0,0,0,0.1);
170
+ }
171
+
172
+ .solution-description {
173
+ color: rgba(255,255,255,0.95);
174
+ font-family: 'Inter', sans-serif;
175
+ font-size: 1.1rem;
176
+ font-weight: 500;
177
+ line-height: 1.4;
178
+ }
179
+
180
+ /* Solution colors */
181
+ .solution-a { background: linear-gradient(135deg, #4CAF50, #45a049); }
182
+ .solution-b { background: linear-gradient(135deg, #FF9800, #f57c00); }
183
+ .solution-c { background: linear-gradient(135deg, #E91E63, #c2185b); }
184
+ .solution-d { background: linear-gradient(135deg, #9C27B0, #7b1fa2); }
185
+ .solution-e { background: linear-gradient(135deg, #8BC34A, #689f38); }
186
+
187
+ /* Footer */
188
+ .footer {
189
+ text-align: center;
190
+ margin-top: 2rem;
191
+ color: rgba(255,255,255,0.7);
192
+ font-family: 'Inter', sans-serif;
193
+ font-size: 0.9rem;
194
+ }
195
+
196
+ /* Responsive design */
197
+ @media (max-width: 768px) {
198
+ .input-container {
199
+ margin: 1rem;
200
+ padding: 2rem 1.5rem;
201
+ }
202
+ .main-title {
203
+ font-size: 2rem;
204
+ }
205
+ .solution-badge {
206
+ width: 80px;
207
+ height: 80px;
208
+ line-height: 80px;
209
+ font-size: 3rem;
210
+ }
211
+ .logo-section {
212
+ margin-bottom: 1rem;
213
+ padding: 1rem;
214
+ }
215
+ .logo-image {
216
+ max-width: 150px;
217
+ }
218
+ }
219
+ </style>
220
+ """, unsafe_allow_html=True)
221
+
222
+ # Load model function
223
+ @st.cache_resource
224
+ def load_model():
225
+ try:
226
+ model = joblib.load('decision_tree_model.pkl')
227
+ le_soil = joblib.load('dt_soil_encoder.pkl')
228
+ le_water = joblib.load('dt_water_encoder.pkl')
229
+ le_solution = joblib.load('dt_solution_encoder.pkl')
230
+ return model, le_soil, le_water, le_solution
231
+ except FileNotFoundError:
232
+ st.error("⚠️ Model files not found! Please run the training script first.")
233
+ return None, None, None, None
234
+
235
+ # Prediction function
236
+ def predict_solution(diameter, soil_type, high_water, model, le_soil, le_water, le_solution):
237
+ try:
238
+ # Encode inputs
239
+ soil_encoded = le_soil.transform([soil_type])[0]
240
+ water_encoded = le_water.transform([high_water])[0]
241
+
242
+ # Create feature vector
243
+ features = np.array([[diameter, soil_encoded, water_encoded]])
244
+
245
+ # Make prediction
246
+ prediction_encoded = model.predict(features)[0]
247
+ prediction = le_solution.inverse_transform([prediction_encoded])[0]
248
+
249
+ return prediction
250
+ except Exception as e:
251
+ return f"Error: {str(e)}"
252
+
253
+ def main():
254
+ # Logo section
255
+ st.markdown('<div class="logo-section">', unsafe_allow_html=True)
256
+
257
+ # Try to display logo with base64 encoding
258
+ logo_base64 = image_to_base64('logo2.e8c5ff97.png')
259
+ if logo_base64:
260
+ st.markdown(f'''
261
+ <img src="data:image/png;base64,{logo_base64}" class="logo-image" alt="MEA Logo">
262
+ ''', unsafe_allow_html=True)
263
+ else:
264
+ # Fallback: Try direct image display
265
+ try:
266
+ st.image('logo2.e8c5ff97.png', width=200)
267
+ except:
268
+ st.markdown('''
269
+ <div style="text-align: center; color: rgba(255,255,255,0.8); padding: 1rem;">
270
+ <h3 style="margin: 0; font-family: 'Inter', sans-serif;">🏢 MEA</h3>
271
+ <p style="margin: 0.5rem 0 0 0; font-family: 'Inter', sans-serif; font-size: 0.9rem;">
272
+ Metropolitan Electricity Authority
273
+ </p>
274
+ </div>
275
+ ''', unsafe_allow_html=True)
276
+
277
+ st.markdown('</div>', unsafe_allow_html=True)
278
+
279
+ # Title and subtitle
280
+ st.markdown('<h1 class="main-title">🔧 HDD Solution Predictor</h1>', unsafe_allow_html=True)
281
+ st.markdown('<p class="subtitle">Get instant recommendations for your drilling project</p>', unsafe_allow_html=True)
282
+
283
+ # Load model
284
+ model_data = load_model()
285
+ if model_data[0] is None:
286
+ st.stop()
287
+
288
+ model, le_soil, le_water, le_solution = model_data
289
+
290
+ # Input container
291
+ st.markdown('<div class="input-container">', unsafe_allow_html=True)
292
+
293
+ # Input controls with better spacing
294
+ st.markdown("### 📊 Project Parameters")
295
+
296
+ # Create two columns for better layout
297
+ col1, col2 = st.columns(2)
298
+
299
+ with col1:
300
+ diameter = st.slider(
301
+ "🔩 Pipe Diameter (m)",
302
+ min_value=0.5,
303
+ max_value=2.0,
304
+ value=1.2,
305
+ step=0.1,
306
+ help="Select the diameter of the pipe to be installed"
307
+ )
308
+
309
+ with col2:
310
+ soil_type = st.selectbox(
311
+ "🏔️ Soil Type",
312
+ options=['clay', 'sand'],
313
+ index=0,
314
+ help="Select the predominant soil type at the drilling site"
315
+ )
316
+
317
+ # Full width for water table
318
+ high_water = st.selectbox(
319
+ "💧 High Water Table",
320
+ options=['no', 'yes'],
321
+ index=0,
322
+ help="Is there a high water table present at the site?"
323
+ )
324
+
325
+ # Predict button
326
+ if st.button("🔮 Get Solution Recommendation"):
327
+ prediction = predict_solution(diameter, soil_type, high_water, model, le_soil, le_water, le_solution)
328
+
329
+ # Solution details
330
+ solution_details = {
331
+ 'A': {
332
+ 'name': 'Enhanced Protection',
333
+ 'description': 'Sheetpile + Trench + Grouting',
334
+ 'class': 'solution-a',
335
+ 'icon': '🛡️'
336
+ },
337
+ 'B': {
338
+ 'name': 'Maximum Protection',
339
+ 'description': 'Sheetpile + Trench + Grouting + Casing',
340
+ 'class': 'solution-b',
341
+ 'icon': '🏰'
342
+ },
343
+ 'C': {
344
+ 'name': 'Moderate Protection',
345
+ 'description': 'Sheetpile + Trench',
346
+ 'class': 'solution-c',
347
+ 'icon': '🔨'
348
+ },
349
+ 'D': {
350
+ 'name': 'Basic Protection',
351
+ 'description': 'Grouting Only',
352
+ 'class': 'solution-d',
353
+ 'icon': '💧'
354
+ },
355
+ 'E': {
356
+ 'name': 'Minimal Intervention',
357
+ 'description': 'No Additional Measures',
358
+ 'class': 'solution-e',
359
+ 'icon': '✅'
360
+ }
361
+ }
362
+
363
+ if prediction in solution_details:
364
+ details = solution_details[prediction]
365
+
366
+ st.markdown(f'''
367
+ <div class="result-container {details['class']}">
368
+ <div class="solution-badge">{prediction}</div>
369
+ <div class="solution-title">{details['name']}</div>
370
+ <div class="solution-description">{details['description']}</div>
371
+ </div>
372
+ ''', unsafe_allow_html=True)
373
+ else:
374
+ st.error(f"❌ Prediction error: {prediction}")
375
+
376
+ st.markdown('</div>', unsafe_allow_html=True)
377
+
378
+ # Footer
379
+ st.markdown('''
380
+ <div class="footer">
381
+ <p>💡 Powered by Decision Tree AI with 100% accuracy</p>
382
+ <p>🏢 Metropolitan Electricity Authority (MEA)</p>
383
+ </div>
384
+ ''', unsafe_allow_html=True)
385
+
386
+ if __name__ == "__main__":
387
+ main()
decision_tree_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a5e1c26e1d71052b5de7dc26b385a5e567ecd25863d2c34f51baf98f8e081f28
3
+ size 3457
docker-compose.yml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ hdd-predictor:
5
+ build: .
6
+ ports:
7
+ - "8501:8501"
8
+ environment:
9
+ - STREAMLIT_SERVER_PORT=8501
10
+ - STREAMLIT_SERVER_ADDRESS=0.0.0.0
11
+ volumes:
12
+ - ./logs:/app/logs
13
+ restart: unless-stopped
14
+ healthcheck:
15
+ test: ["CMD", "curl", "-f", "http://localhost:8501/_stcore/health"]
16
+ interval: 30s
17
+ timeout: 10s
18
+ retries: 3
19
+ start_period: 5s
20
+
21
+ networks:
22
+ default:
23
+ name: hdd-network
dt_soil_encoder.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f035faa1c97fc1b4fb934605a703aefc20d788233fd88aa3be70c0a42feced32
3
+ size 534
dt_solution_encoder.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:07cdb67d115d09a60b202140b73e9bfbd0fbfd58f09c9a23376797f249af227c
3
+ size 552
dt_water_encoder.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4d863f270f06401aa731ca10b4aa6182f4872b312a279d50c6933b28c27323a5
3
+ size 531
logo2.e8c5ff97.png ADDED

Git LFS Details

  • SHA256: 1a0f155b7d5dcbe04130573216645e0833422492be206ba8b903e48ccb959bad
  • Pointer size: 131 Bytes
  • Size of remote file: 124 kB
mobile_app.py ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import numpy as np
3
+ import joblib
4
+
5
+ # Page configuration
6
+ st.set_page_config(
7
+ page_title="HDD Predictor",
8
+ page_icon="🔧",
9
+ layout="centered",
10
+ initial_sidebar_state="collapsed"
11
+ )
12
+
13
+ # Ultra-minimal Canva-style CSS
14
+ st.markdown("""
15
+ <style>
16
+ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap');
17
+
18
+ /* Hide Streamlit elements */
19
+ #MainMenu {visibility: hidden;}
20
+ footer {visibility: hidden;}
21
+ header {visibility: hidden;}
22
+ .stDeployButton {visibility: hidden;}
23
+
24
+ /* Main background */
25
+ .main {
26
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
27
+ min-height: 100vh;
28
+ padding: 1rem 0;
29
+ }
30
+
31
+ /* Container */
32
+ .container {
33
+ background: white;
34
+ border-radius: 25px;
35
+ padding: 2rem 1.5rem;
36
+ margin: 1rem auto;
37
+ max-width: 400px;
38
+ box-shadow: 0 25px 50px rgba(0,0,0,0.15);
39
+ text-align: center;
40
+ }
41
+
42
+ /* Typography */
43
+ .title {
44
+ font-family: 'Poppins', sans-serif;
45
+ font-size: 1.8rem;
46
+ font-weight: 700;
47
+ color: #2d3748;
48
+ margin-bottom: 0.5rem;
49
+ }
50
+
51
+ .subtitle {
52
+ font-family: 'Poppins', sans-serif;
53
+ font-size: 0.9rem;
54
+ color: #718096;
55
+ margin-bottom: 2rem;
56
+ font-weight: 400;
57
+ }
58
+
59
+ /* Inputs */
60
+ .stSelectbox label, .stSlider label {
61
+ font-family: 'Poppins', sans-serif;
62
+ font-weight: 600;
63
+ color: #4a5568;
64
+ font-size: 0.95rem;
65
+ }
66
+
67
+ .stSelectbox > div > div {
68
+ border-radius: 15px;
69
+ border: 2px solid #e2e8f0;
70
+ font-family: 'Poppins', sans-serif;
71
+ }
72
+
73
+ .stSlider > div > div {
74
+ border-radius: 15px;
75
+ padding: 1rem;
76
+ background: #f7fafc;
77
+ }
78
+
79
+ /* Button */
80
+ .stButton > button {
81
+ background: linear-gradient(135deg, #667eea, #764ba2);
82
+ color: white;
83
+ border: none;
84
+ border-radius: 20px;
85
+ padding: 1rem 2rem;
86
+ font-family: 'Poppins', sans-serif;
87
+ font-weight: 600;
88
+ font-size: 1rem;
89
+ width: 100%;
90
+ margin: 1.5rem 0;
91
+ box-shadow: 0 10px 25px rgba(102, 126, 234, 0.3);
92
+ transition: all 0.3s ease;
93
+ }
94
+
95
+ .stButton > button:hover {
96
+ transform: translateY(-3px);
97
+ box-shadow: 0 15px 35px rgba(102, 126, 234, 0.4);
98
+ }
99
+
100
+ /* Result */
101
+ .result {
102
+ margin: 2rem 0;
103
+ padding: 2rem;
104
+ border-radius: 20px;
105
+ text-align: center;
106
+ }
107
+
108
+ .solution-icon {
109
+ font-size: 4rem;
110
+ margin-bottom: 1rem;
111
+ display: block;
112
+ }
113
+
114
+ .solution-title {
115
+ font-family: 'Poppins', sans-serif;
116
+ font-size: 1.5rem;
117
+ font-weight: 700;
118
+ color: white;
119
+ margin-bottom: 0.5rem;
120
+ }
121
+
122
+ .solution-desc {
123
+ font-family: 'Poppins', sans-serif;
124
+ font-size: 1rem;
125
+ color: rgba(255,255,255,0.9);
126
+ font-weight: 400;
127
+ }
128
+
129
+ /* Solution colors */
130
+ .sol-a { background: linear-gradient(135deg, #48bb78, #38a169); }
131
+ .sol-b { background: linear-gradient(135deg, #ed8936, #dd6b20); }
132
+ .sol-c { background: linear-gradient(135deg, #ed64a6, #d53f8c); }
133
+ .sol-d { background: linear-gradient(135deg, #9f7aea, #805ad5); }
134
+ .sol-e { background: linear-gradient(135deg, #68d391, #48bb78); }
135
+
136
+ /* Mobile logo styling */
137
+ .mobile-logo {
138
+ text-align: center;
139
+ margin-bottom: 1rem;
140
+ padding: 0.5rem;
141
+ background: rgba(255,255,255,0.1);
142
+ border-radius: 15px;
143
+ backdrop-filter: blur(10px);
144
+ }
145
+
146
+ .mobile-logo img {
147
+ filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));
148
+ margin: 0 0.5rem;
149
+ }
150
+ </style>
151
+ """, unsafe_allow_html=True)
152
+
153
+ # Load model
154
+ @st.cache_resource
155
+ def load_model():
156
+ try:
157
+ model = joblib.load('decision_tree_model.pkl')
158
+ le_soil = joblib.load('dt_soil_encoder.pkl')
159
+ le_water = joblib.load('dt_water_encoder.pkl')
160
+ le_solution = joblib.load('dt_solution_encoder.pkl')
161
+ return model, le_soil, le_water, le_solution
162
+ except FileNotFoundError:
163
+ st.error("Model files not found!")
164
+ return None, None, None, None
165
+
166
+ def predict_solution(diameter, soil_type, high_water, model, le_soil, le_water, le_solution):
167
+ try:
168
+ soil_encoded = le_soil.transform([soil_type])[0]
169
+ water_encoded = le_water.transform([high_water])[0]
170
+ features = np.array([[diameter, soil_encoded, water_encoded]])
171
+ prediction_encoded = model.predict(features)[0]
172
+ prediction = le_solution.inverse_transform([prediction_encoded])[0]
173
+ return prediction
174
+ except Exception as e:
175
+ return f"Error: {str(e)}"
176
+
177
+ def main():
178
+ # Logo section
179
+ st.markdown('<div class="mobile-logo">', unsafe_allow_html=True)
180
+
181
+ try:
182
+ # Center the single MEA logo
183
+ st.image('logo2.e8c5ff97.png', width=100)
184
+ except FileNotFoundError:
185
+ st.markdown("""
186
+ <div style="text-align: center; color: rgba(255,255,255,0.7); font-size: 0.8rem;">
187
+ 📁 MEA Logo not found
188
+ </div>
189
+ """, unsafe_allow_html=True)
190
+
191
+ st.markdown('</div>', unsafe_allow_html=True)
192
+
193
+ # Container start
194
+ st.markdown('<div class="container">', unsafe_allow_html=True)
195
+
196
+ # Header
197
+ st.markdown('<div class="title">🔧 HDD Predictor</div>', unsafe_allow_html=True)
198
+ st.markdown('<div class="subtitle">Quick drilling solution recommendation</div>', unsafe_allow_html=True)
199
+
200
+ # Load model
201
+ model_data = load_model()
202
+ if model_data[0] is None:
203
+ st.stop()
204
+
205
+ model, le_soil, le_water, le_solution = model_data
206
+
207
+ # Inputs
208
+ diameter = st.slider("Diameter (m)", 0.5, 2.0, 1.2, 0.1)
209
+ soil_type = st.selectbox("Soil", ['clay', 'sand'])
210
+ high_water = st.selectbox("High Water", ['no', 'yes'])
211
+
212
+ # Predict
213
+ if st.button("Get Solution"):
214
+ prediction = predict_solution(diameter, soil_type, high_water, model, le_soil, le_water, le_solution)
215
+
216
+ solutions = {
217
+ 'A': {'icon': '🛡️', 'title': 'Enhanced Protection', 'desc': 'Sheetpile + Trench + Grouting', 'class': 'sol-a'},
218
+ 'B': {'icon': '🏰', 'title': 'Maximum Protection', 'desc': 'Full System + Casing', 'class': 'sol-b'},
219
+ 'C': {'icon': '🔨', 'title': 'Moderate Protection', 'desc': 'Sheetpile + Trench', 'class': 'sol-c'},
220
+ 'D': {'icon': '💧', 'title': 'Basic Protection', 'desc': 'Grouting Only', 'class': 'sol-d'},
221
+ 'E': {'icon': '✅', 'title': 'Minimal Action', 'desc': 'No Additional Measures', 'class': 'sol-e'}
222
+ }
223
+
224
+ if prediction in solutions:
225
+ sol = solutions[prediction]
226
+ st.markdown(f'''
227
+ <div class="result {sol['class']}">
228
+ <div class="solution-icon">{sol['icon']}</div>
229
+ <div class="solution-title">Solution {prediction}</div>
230
+ <div class="solution-title">{sol['title']}</div>
231
+ <div class="solution-desc">{sol['desc']}</div>
232
+ </div>
233
+ ''', unsafe_allow_html=True)
234
+
235
+ # Container end
236
+ st.markdown('</div>', unsafe_allow_html=True)
237
+
238
+ if __name__ == "__main__":
239
+ main()
requirements.txt CHANGED
@@ -1,3 +1,8 @@
1
- altair
2
  pandas
3
- streamlit
 
 
 
 
 
 
1
+ streamlit
2
  pandas
3
+ numpy
4
+ scikit-learn
5
+ joblib
6
+ plotly
7
+ openpyxl
8
+ Pillow
run.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simple deployment runner for HDD Solution Predictor
4
+ """
5
+
6
+ import subprocess
7
+ import sys
8
+ import os
9
+ import webbrowser
10
+ import time
11
+
12
+ def check_files():
13
+ """Check if all required files exist"""
14
+ required_files = [
15
+ 'app.py',
16
+ 'requirements.txt',
17
+ 'decision_tree_model.pkl',
18
+ 'dt_soil_encoder.pkl',
19
+ 'dt_water_encoder.pkl',
20
+ 'dt_solution_encoder.pkl'
21
+ ]
22
+
23
+ missing = []
24
+ for file in required_files:
25
+ if not os.path.exists(file):
26
+ missing.append(file)
27
+
28
+ return missing
29
+
30
+ def install_requirements():
31
+ """Install Python requirements"""
32
+ try:
33
+ print("📦 Installing requirements...")
34
+ subprocess.run([sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt'],
35
+ check=True, capture_output=True)
36
+ print("✅ Requirements installed successfully")
37
+ return True
38
+ except subprocess.CalledProcessError:
39
+ print("❌ Failed to install requirements")
40
+ return False
41
+
42
+ def run_app(port=8501):
43
+ """Run the Streamlit app"""
44
+ try:
45
+ print(f"🚀 Starting HDD Solution Predictor on port {port}...")
46
+
47
+ # Start streamlit
48
+ process = subprocess.Popen([
49
+ 'streamlit', 'run', 'app.py',
50
+ f'--server.port={port}',
51
+ '--server.address=0.0.0.0'
52
+ ])
53
+
54
+ # Give it time to start
55
+ time.sleep(3)
56
+
57
+ # Open browser
58
+ print(f"🌐 Opening browser at http://localhost:{port}")
59
+ webbrowser.open(f'http://localhost:{port}')
60
+
61
+ print(f"\n✅ App running at: http://localhost:{port}")
62
+ print("⏹️ Press Ctrl+C to stop")
63
+
64
+ # Wait for interrupt
65
+ try:
66
+ process.wait()
67
+ except KeyboardInterrupt:
68
+ print("\n🛑 Stopping application...")
69
+ process.terminate()
70
+
71
+ except FileNotFoundError:
72
+ print("❌ Streamlit not found. Installing...")
73
+ subprocess.run([sys.executable, '-m', 'pip', 'install', 'streamlit'])
74
+ print("✅ Please run again")
75
+
76
+ except Exception as e:
77
+ print(f"❌ Error: {e}")
78
+
79
+ def main():
80
+ print("🔧 HDD Solution Predictor - Deployment Runner")
81
+ print("=" * 50)
82
+
83
+ # Check files
84
+ missing = check_files()
85
+ if missing:
86
+ print("❌ Missing files:")
87
+ for file in missing:
88
+ print(f" - {file}")
89
+ print("\n📋 Please ensure all files are in the deploy directory")
90
+ return
91
+
92
+ print("✅ All required files found")
93
+
94
+ # Check logo
95
+ if os.path.exists('logo2.e8c5ff97.png'):
96
+ print("✅ MEA logo found")
97
+ else:
98
+ print("⚠️ MEA logo not found (will show text fallback)")
99
+
100
+ # Install requirements
101
+ print("\n📦 Checking requirements...")
102
+ try:
103
+ import streamlit
104
+ print("✅ Streamlit already installed")
105
+ except ImportError:
106
+ if not install_requirements():
107
+ return
108
+
109
+ # Run app
110
+ print("\n🚀 Launching application...")
111
+ run_app()
112
+
113
+ if __name__ == "__main__":
114
+ main()
runtime.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ python-3.11.0