krushimitravit commited on
Commit
e2728ec
·
verified ·
1 Parent(s): 580fa93

Upload 5 files

Browse files
Files changed (5) hide show
  1. Dockerfile +18 -0
  2. app.py +67 -0
  3. random_forest_model.pkl +3 -0
  4. requirements.txt +6 -0
  5. templates/index.html +484 -0
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Python runtime as a parent image
2
+ FROM python:3.9-slim
3
+
4
+ # Set the working directory
5
+ WORKDIR /app
6
+
7
+ # Copy the application files to the container
8
+ COPY . /app
9
+
10
+ # Install Python dependencies
11
+ RUN pip install --no-cache-dir -r requirements.txt
12
+
13
+
14
+ # Expose the Flask port (Hugging Face Spaces uses port 7860 by default)
15
+ EXPOSE 7860
16
+
17
+ # Command to run the Flask app
18
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from flask import Flask, request, jsonify, render_template
3
+ import joblib
4
+
5
+ # Initialize Flask app
6
+ app = Flask(__name__)
7
+
8
+ # Load the trained Random Forest model
9
+ rf_model = joblib.load('random_forest_model.pkl')
10
+
11
+ # Encoder classes
12
+ soil_type_classes = ['Alluvial Soil', 'Black Soil', 'Clay Soil', 'Red Soil']
13
+ crop_classes = ['All vegetables Tea Coffee Rubber Coconut Cashew Avocado',
14
+ 'Cotton Blackgram Oilseeds Pigeonpea',
15
+ 'Cotton Jowar Pigeonpea Blackgram',
16
+ 'Cotton Rice Pigeonpea Blackgram Sunflower',
17
+ 'Cotton Sorghum CerealCrops Blackgram',
18
+ 'Cotton Sugarcane Pigeonpea Sorghum',
19
+ 'Pearlmillet Basil Blackgram Sorghum',
20
+ 'Pearlmillet Maize Pigeonpea Greengram Garlic',
21
+ 'Pearlmillet Ragi Groundnut Potato All vegetables',
22
+ 'Soybean Pigeonpea Millets Greengram',
23
+ 'Soybean Pigeonpea Maize Sorghum']
24
+
25
+ # Route for the home page
26
+ @app.route('/')
27
+ def index():
28
+ return render_template('index.html', soil_types=soil_type_classes)
29
+
30
+ # Route to handle the prediction
31
+ @app.route('/predict', methods=['POST'])
32
+ def predict():
33
+ try:
34
+ # Get form data
35
+ soil_type = request.form.get('soil_type')
36
+ soil_depth = float(request.form.get('soil_depth'))
37
+ ph = float(request.form.get('ph'))
38
+ bulk_density = float(request.form.get('bulk_density'))
39
+ ec = float(request.form.get('ec'))
40
+ organic_carbon = float(request.form.get('organic_carbon'))
41
+ soil_moisture_retention = float(request.form.get('soil_moisture_retention'))
42
+ available_water_capacity = float(request.form.get('available_water_capacity'))
43
+ infiltration_rate = float(request.form.get('infiltration_rate'))
44
+ clay_percentage = float(request.form.get('clay_percentage'))
45
+
46
+ # Encode soil type
47
+ soil_type_encoded = soil_type_classes.index(soil_type)
48
+
49
+ # Create feature array
50
+ features = np.array([[soil_type_encoded, soil_depth, ph, bulk_density, ec, organic_carbon,
51
+ soil_moisture_retention, available_water_capacity, infiltration_rate, clay_percentage]])
52
+
53
+ # Make prediction
54
+ predicted_crop_index = rf_model.predict(features)[0]
55
+ predicted_crop = crop_classes[predicted_crop_index]
56
+
57
+ # Split the crops into separate columns (based on spaces)
58
+ predicted_crop_list = predicted_crop.split()
59
+
60
+ # Return the predicted crops as a list to the front-end
61
+ return jsonify({'predicted_crop_list': predicted_crop_list})
62
+
63
+ except Exception as e:
64
+ return jsonify({'error': str(e)})
65
+
66
+ if __name__ == '__main__':
67
+ app.run(port=7861,host='0.0.0.0')
random_forest_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:86b26bcc20dd3256e1cf6f5fd6becc749b1470999aba9ecb448737e60bd6d37e
3
+ size 397313
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gunicorn
2
+ flask
3
+ pandas
4
+ scikit-learn==1.5.2
5
+ joblib===1.4.2
6
+ numpy
templates/index.html ADDED
@@ -0,0 +1,484 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Crop Recommendation System</title>
7
+
8
+ <!-- Bootstrap Icons -->
9
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
10
+
11
+ <!-- Google Fonts - Outfit -->
12
+ <link rel="preconnect" href="https://fonts.googleapis.com">
13
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
14
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
15
+
16
+ <style>
17
+ /* ===== CSS VARIABLES - STYLE GUIDE ===== */
18
+ :root {
19
+ /* Primary Colors */
20
+ --deep-green: #1a5d3a;
21
+ --accent-green: #198754;
22
+ --darker-accent: #143d2e;
23
+
24
+ /* Neutral Colors */
25
+ --bg-color: #f8f9fa;
26
+ --surface: #ffffff;
27
+ --text-dark: #212529;
28
+ --text-muted: #6c757d;
29
+ --border-color: #dee2e6;
30
+
31
+ /* Component Specific */
32
+ --input-bg: #f8f9fa;
33
+ --upload-bg: #fcfcfc;
34
+ --upload-active-bg: #f0fff4;
35
+ }
36
+
37
+ /* ===== GLOBAL STYLES ===== */
38
+ * {
39
+ margin: 0;
40
+ padding: 0;
41
+ box-sizing: border-box;
42
+ }
43
+
44
+ body {
45
+ font-family: 'Outfit', sans-serif;
46
+ font-weight: 400;
47
+ background-color: #ffffff;
48
+ color: var(--text-dark);
49
+ line-height: 1.6;
50
+ }
51
+
52
+ /* ===== CURVED BOTTOM HERO HEADER ===== */
53
+ .hero-header {
54
+ background: var(--deep-green);
55
+ padding: 3rem 0 5rem 0;
56
+ margin-bottom: 3rem;
57
+ position: relative;
58
+ }
59
+
60
+ .hero-header h1 {
61
+ color: var(--surface);
62
+ font-weight: 700;
63
+ font-size: 2.5rem;
64
+ text-align: center;
65
+ margin: 0;
66
+ }
67
+
68
+ .hero-header p {
69
+ color: rgba(255, 255, 255, 0.9);
70
+ font-weight: 300;
71
+ font-size: 1.1rem;
72
+ text-align: center;
73
+ margin-top: 0.5rem;
74
+ }
75
+
76
+ /* ===== PROCESS VISUAL (3-STEP) ===== */
77
+ .process-steps {
78
+ display: flex;
79
+ justify-content: center;
80
+ align-items: center;
81
+ gap: 2rem;
82
+ margin: 2rem 0 3rem 0;
83
+ padding: 0 1rem;
84
+ }
85
+
86
+ .process-step {
87
+ display: flex;
88
+ flex-direction: column;
89
+ align-items: center;
90
+ gap: 0.5rem;
91
+ }
92
+
93
+ .process-icon {
94
+ width: 60px;
95
+ height: 60px;
96
+ background: var(--surface);
97
+ border-radius: 50%;
98
+ display: flex;
99
+ align-items: center;
100
+ justify-content: center;
101
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
102
+ }
103
+
104
+ .process-icon i {
105
+ font-size: 1.8rem;
106
+ color: var(--accent-green);
107
+ }
108
+
109
+ .process-label {
110
+ font-size: 0.9rem;
111
+ font-weight: 500;
112
+ color: var(--text-dark);
113
+ }
114
+
115
+ .process-arrow {
116
+ font-size: 1.5rem;
117
+ color: var(--text-muted);
118
+ margin-top: -1.5rem;
119
+ }
120
+
121
+ /* ===== FLOATING CARD CONTAINER ===== */
122
+ .main-container {
123
+ max-width: 1200px;
124
+ margin: 0 auto;
125
+ border-radius: 20px;
126
+ border: 3px dashed var(--accent-green);
127
+ }
128
+
129
+ .floating-card {
130
+ background: var(--surface);
131
+ border-radius: 20px;
132
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
133
+ padding: 3rem;
134
+ }
135
+
136
+ /* ===== FORM LAYOUT ===== */
137
+ .form-layout {
138
+ display: grid;
139
+ grid-template-columns: 1fr;
140
+ gap: 1.5rem;
141
+ }
142
+
143
+ .form-group {
144
+ display: flex;
145
+ flex-direction: column;
146
+ }
147
+
148
+ .form-group.full-width {
149
+ grid-column: 1 / -1;
150
+ }
151
+
152
+ .form-label {
153
+ font-weight: 500;
154
+ font-size: 0.95rem;
155
+ color: var(--text-dark);
156
+ margin-bottom: 0.5rem;
157
+ }
158
+
159
+ /* ===== INPUT FIELDS - STYLE GUIDE ===== */
160
+ .form-control,
161
+ .form-select {
162
+ background-color: var(--input-bg);
163
+ border: 1px solid var(--border-color);
164
+ border-radius: 8px;
165
+ padding: 0.75rem 1rem;
166
+ font-family: 'Outfit', sans-serif;
167
+ font-weight: 400;
168
+ font-size: 1rem;
169
+ color: var(--text-dark);
170
+ transition: all 0.3s ease;
171
+ }
172
+
173
+ .form-control:focus,
174
+ .form-select:focus {
175
+ background-color: var(--surface);
176
+ border-color: var(--accent-green);
177
+ box-shadow: 0 0 0 4px rgba(25, 135, 84, 0.1);
178
+ outline: none;
179
+ }
180
+
181
+ .form-control::placeholder {
182
+ color: var(--text-muted);
183
+ font-weight: 300;
184
+ }
185
+
186
+ /* ===== BUTTONS - STYLE GUIDE ===== */
187
+ .btn-primary-action {
188
+ background: var(--deep-green);
189
+ color: var(--surface);
190
+ border: none;
191
+ border-radius: 8px;
192
+ padding: 1rem 2rem;
193
+ font-family: 'Outfit', sans-serif;
194
+ font-weight: 500;
195
+ font-size: 1.1rem;
196
+ cursor: pointer;
197
+ transition: all 0.3s ease;
198
+ display: inline-flex;
199
+ align-items: center;
200
+ justify-content: center;
201
+ gap: 0.5rem;
202
+ width: 100%;
203
+ margin-top: 1rem;
204
+ }
205
+
206
+ .btn-primary-action:hover {
207
+ background: var(--darker-accent);
208
+ transform: translateY(-2px);
209
+ box-shadow: 0 6px 20px rgba(26, 93, 58, 0.3);
210
+ }
211
+
212
+ .btn-primary-action:active {
213
+ transform: translateY(0);
214
+ }
215
+
216
+ .btn-primary-action i {
217
+ font-size: 1.2rem;
218
+ }
219
+
220
+ /* ===== IMAGE SECTION ===== */
221
+ .image-showcase {
222
+ grid-column: 1 / -1;
223
+ margin-top: 2rem;
224
+ display: flex;
225
+ justify-content: center;
226
+ }
227
+
228
+ .showcase-img {
229
+ max-width: 100%;
230
+ height: auto;
231
+ border-radius: 20px;
232
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
233
+ border: 3px solid var(--accent-green);
234
+ }
235
+
236
+ /* ===== RESULT CONTAINER ===== */
237
+ .result-container {
238
+ background: var(--surface);
239
+ border-radius: 20px;
240
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
241
+ padding: 2.5rem;
242
+ margin-top: 2rem;
243
+ }
244
+
245
+ .result-container h3 {
246
+ color: var(--deep-green);
247
+ font-weight: 600;
248
+ font-size: 1.8rem;
249
+ text-align: center;
250
+ margin-bottom: 1.5rem;
251
+ }
252
+
253
+ .crop-results {
254
+ display: flex;
255
+ flex-wrap: wrap;
256
+ gap: 1rem;
257
+ justify-content: center;
258
+ }
259
+
260
+ .crop-badge {
261
+ background: linear-gradient(135deg, var(--accent-green), var(--deep-green));
262
+ color: var(--surface);
263
+ padding: 0.75rem 1.5rem;
264
+ border-radius: 50px;
265
+ font-weight: 500;
266
+ font-size: 1.1rem;
267
+ box-shadow: 0 4px 12px rgba(25, 135, 84, 0.2);
268
+ transition: all 0.3s ease;
269
+ }
270
+
271
+ .crop-badge:hover {
272
+ transform: translateY(-3px);
273
+ box-shadow: 0 6px 20px rgba(25, 135, 84, 0.3);
274
+ }
275
+
276
+ /* ===== RESPONSIVE DESIGN ===== */
277
+ @media (max-width: 768px) {
278
+ .hero-header h1 {
279
+ font-size: 2rem;
280
+ }
281
+
282
+ .floating-card {
283
+ padding: 2rem 1.5rem;
284
+ }
285
+
286
+ .form-layout {
287
+ grid-template-columns: 1fr;
288
+ }
289
+
290
+ .process-steps {
291
+ gap: 1rem;
292
+ }
293
+
294
+ .process-icon {
295
+ width: 50px;
296
+ height: 50px;
297
+ }
298
+
299
+ .process-icon i {
300
+ font-size: 1.5rem;
301
+ }
302
+
303
+ .process-arrow {
304
+ display: none;
305
+ }
306
+ }
307
+ </style>
308
+ </head>
309
+ <body>
310
+ <!-- CURVED BOTTOM HERO HEADER -->
311
+ <div class="hero-header">
312
+ <h1>🌾 Crop Recommendation System</h1>
313
+ <p>Smart soil analysis for optimal crop selection</p>
314
+ </div>
315
+
316
+ <!-- MAIN CONTAINER -->
317
+ <div class="main-container">
318
+ <!-- PROCESS VISUAL (3-STEP) -->
319
+
320
+
321
+ <!-- FLOATING CARD -->
322
+ <div class="floating-card">
323
+ <form id="cropForm" method="POST">
324
+ <div class="form-layout">
325
+ <!-- Soil Type -->
326
+ <div class="form-group">
327
+ <label class="form-label" for="soil_type">
328
+ <i class="bi bi-layers"></i> Soil Type
329
+ </label>
330
+ <select class="form-select form-control" id="soil_type" name="soil_type" required>
331
+ <option value="" disabled selected>Select Soil Type</option>
332
+ {% for soil in soil_types %}
333
+ <option value="{{ soil }}">{{ soil }}</option>
334
+ {% endfor %}
335
+ </select>
336
+ </div>
337
+
338
+ <!-- Soil Depth -->
339
+ <div class="form-group">
340
+ <label class="form-label" for="soil_depth">
341
+ <i class="bi bi-rulers"></i> Soil Depth (cm)
342
+ </label>
343
+ <input type="text" class="form-control" id="soil_depth" name="soil_depth" placeholder="e.g., 30" required>
344
+ </div>
345
+
346
+ <!-- pH Level -->
347
+ <div class="form-group">
348
+ <label class="form-label" for="ph">
349
+ <i class="bi bi-droplet"></i> pH Level
350
+ </label>
351
+ <input type="text" class="form-control" id="ph" name="ph" placeholder="e.g., 6.5" required>
352
+ </div>
353
+
354
+ <!-- Bulk Density -->
355
+ <div class="form-group">
356
+ <label class="form-label" for="bulk_density">
357
+ <i class="bi bi-box"></i> Bulk Density (Gm/cc)
358
+ </label>
359
+ <input type="text" class="form-control" id="bulk_density" name="bulk_density" placeholder="e.g., 1.4" required>
360
+ </div>
361
+
362
+ <!-- EC -->
363
+ <div class="form-group">
364
+ <label class="form-label" for="ec">
365
+ <i class="bi bi-lightning"></i> EC (dsm-1)
366
+ </label>
367
+ <input type="text" class="form-control" id="ec" name="ec" placeholder="e.g., 0.5" required>
368
+ </div>
369
+
370
+ <!-- Organic Carbon -->
371
+ <div class="form-group">
372
+ <label class="form-label" for="organic_carbon">
373
+ <i class="bi bi-graph-up"></i> Organic Carbon (%)
374
+ </label>
375
+ <input type="text" class="form-control" id="organic_carbon" name="organic_carbon" placeholder="e.g., 0.8" required>
376
+ </div>
377
+
378
+ <!-- Soil Moisture Retention -->
379
+ <div class="form-group">
380
+ <label class="form-label" for="soil_moisture_retention">
381
+ <i class="bi bi-moisture"></i> Soil Moisture Retention (%)
382
+ </label>
383
+ <input type="text" class="form-control" id="soil_moisture_retention" name="soil_moisture_retention" placeholder="e.g., 25" required>
384
+ </div>
385
+
386
+ <!-- Available Water Capacity -->
387
+ <div class="form-group">
388
+ <label class="form-label" for="available_water_capacity">
389
+ <i class="bi bi-water"></i> Available Water Capacity (m/m)
390
+ </label>
391
+ <input type="text" class="form-control" id="available_water_capacity" name="available_water_capacity" placeholder="e.g., 0.15" required>
392
+ </div>
393
+
394
+ <!-- Infiltration Rate -->
395
+ <div class="form-group">
396
+ <label class="form-label" for="infiltration_rate">
397
+ <i class="bi bi-arrow-down-circle"></i> Infiltration Rate (cm/hr)
398
+ </label>
399
+ <input type="text" class="form-control" id="infiltration_rate" name="infiltration_rate" placeholder="e.g., 2.5" required>
400
+ </div>
401
+
402
+ <!-- Clay Percentage -->
403
+ <div class="form-group">
404
+ <label class="form-label" for="clay_percentage">
405
+ <i class="bi bi-percent"></i> Clay Percentage (%)
406
+ </label>
407
+ <input type="text" class="form-control" id="clay_percentage" name="clay_percentage" placeholder="e.g., 35" required>
408
+ </div>
409
+
410
+ <!-- Image Showcase -->
411
+ <div class="image-showcase">
412
+ <img src="https://www.shutterstock.com/image-photo/young-corn-plants-growing-on-600nw-2299683499.jpg"
413
+ alt="Crop Field"
414
+ class="showcase-img">
415
+ </div>
416
+
417
+ <!-- Submit Button -->
418
+ <div class="form-group full-width">
419
+ <button type="submit" class="btn-primary-action">
420
+ <i class="bi bi-search"></i>
421
+ Get Crop Recommendations
422
+ </button>
423
+ </div>
424
+ </div>
425
+ </form>
426
+ </div>
427
+
428
+ <!-- RESULT CONTAINER -->
429
+ <div class="result-container" id="result-container" style="display: none;">
430
+ <h3>🌱 Recommended Crops</h3>
431
+ <div class="crop-results" id="crop-results"></div>
432
+ </div>
433
+ </div>
434
+
435
+ <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
436
+ <script>
437
+ $(document).ready(function () {
438
+ $('#cropForm').on('submit', function (e) {
439
+ e.preventDefault();
440
+
441
+ // Show loading state
442
+ const submitBtn = $(this).find('button[type="submit"]');
443
+ const originalText = submitBtn.html();
444
+ submitBtn.html('<i class="bi bi-hourglass-split"></i> Analyzing...');
445
+ submitBtn.prop('disabled', true);
446
+
447
+ $.ajax({
448
+ url: '/predict',
449
+ type: 'POST',
450
+ data: $(this).serialize(),
451
+ success: function (response) {
452
+ if (response.predicted_crop_list) {
453
+ $('#result-container').show();
454
+ var crops = response.predicted_crop_list;
455
+ var cropResults = $('#crop-results');
456
+ cropResults.empty();
457
+
458
+ crops.forEach(function (crop) {
459
+ cropResults.append('<div class="crop-badge">' + crop + '</div>');
460
+ });
461
+
462
+ // Scroll to results
463
+ $('html, body').animate({
464
+ scrollTop: $('#result-container').offset().top - 100
465
+ }, 500);
466
+ } else if (response.error) {
467
+ alert('Error: ' + response.error);
468
+ }
469
+
470
+ // Restore button
471
+ submitBtn.html(originalText);
472
+ submitBtn.prop('disabled', false);
473
+ },
474
+ error: function () {
475
+ alert('An error occurred while predicting.');
476
+ submitBtn.html(originalText);
477
+ submitBtn.prop('disabled', false);
478
+ }
479
+ });
480
+ });
481
+ });
482
+ </script>
483
+ </body>
484
+ </html>