Esmaeilkianii commited on
Commit
356fe1b
·
verified ·
1 Parent(s): 5e425ea

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -1023
app.py CHANGED
@@ -1,4 +1,4 @@
1
- import streamlit as st
2
  import pandas as pd
3
  import numpy as np
4
  import plotly.express as px
@@ -138,7 +138,6 @@ st.markdown("""
138
  color: white !important;
139
  }
140
 
141
- /* تنظیم جهت نمایش اعداد */
142
  input[type="number"] {
143
  direction: ltr !important;
144
  text-align: left !important;
@@ -152,7 +151,6 @@ st.markdown("""
152
  text-align: left !important;
153
  }
154
 
155
- /* استایل مخصوص برای نقشه */
156
  .map-container {
157
  height: 500px;
158
  width: 100%;
@@ -160,76 +158,15 @@ st.markdown("""
160
  box-shadow: 0 4px 10px rgba(0,0,0,0.1);
161
  overflow: hidden;
162
  }
163
-
164
- /* استایل برای لیبل‌های داینامیک */
165
- .dynamic-label {
166
- font-weight: bold;
167
- color: #1e6b45;
168
- transition: all 0.3s ease;
169
- }
170
-
171
- .dynamic-value {
172
- font-size: 1.2em;
173
- font-weight: bold;
174
- transition: all 0.3s ease;
175
- }
176
-
177
- .highlight-update {
178
- animation: pulse 1.5s ease;
179
- }
180
-
181
- @keyframes pulse {
182
- 0% { background-color: transparent; }
183
- 50% { background-color: rgba(46, 204, 113, 0.3); }
184
- 100% { background-color: transparent; }
185
- }
186
  </style>
187
  """, unsafe_allow_html=True)
188
 
189
- # افزودن جاوااسکریپت برای آپدیت داینامیک
190
- st.markdown("""
191
- <script>
192
- // تابع برای بروزرسانی داینامیک مقادیر
193
- function updateValues(inputId, avgId, value) {
194
- const avgElement = document.getElementById(avgId);
195
- if (avgElement) {
196
- avgElement.textContent = value;
197
- avgElement.classList.add('highlight-update');
198
-
199
- // حذف کلاس هایلایت بعد از 1.5 ثانیه
200
- setTimeout(() => {
201
- avgElement.classList.remove('highlight-update');
202
- }, 1500);
203
- }
204
- }
205
-
206
- // اضافه کردن event listener به همه ورودی‌های عددی
207
- document.addEventListener('DOMContentLoaded', function() {
208
- const numInputs = document.querySelectorAll('input[type="number"]');
209
- numInputs.forEach(input => {
210
- input.addEventListener('input', function() {
211
- // اینجا می‌توانیم یک پیام به Streamlit بفرستیم
212
- window.parent.postMessage({
213
- type: 'inputChange',
214
- inputId: input.id,
215
- value: input.value
216
- }, '*');
217
- });
218
- });
219
- });
220
- </script>
221
- """, unsafe_allow_html=True)
222
-
223
- # تنظیم Earth Engine
224
  @st.cache_resource
225
  def initialize_earth_engine():
226
  try:
227
- # مسیر فایل اعتبارنامه
228
  credentials_path = "ee-esmaeilkiani13877-cfdea6eaf411.json"
229
-
230
- # بررسی وجود فایل
231
  if not os.path.exists(credentials_path):
232
- # اگر فایل وجود ندارد، آن را ایجاد می‌کنیم
233
  credentials_dict = {
234
  "type": "service_account",
235
  "project_id": "ee-esmaeilkiani13877",
@@ -245,25 +182,21 @@ def initialize_earth_engine():
245
  with open(credentials_path, 'w') as f:
246
  json.dump(credentials_dict, f)
247
 
248
- # خواندن اعتبارنامه
249
  credentials = ee.ServiceAccountCredentials(
250
  "dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com",
251
  credentials_path
252
  )
253
-
254
  ee.Initialize(credentials)
255
  return True
256
  except Exception as e:
257
  st.error(f"خطا در اتصال به Earth Engine: {e}")
258
  return False
259
 
260
- # تلاش برای اتصال به Earth Engine
261
  earth_engine_initialized = initialize_earth_engine()
262
 
263
  # نمونه داده
264
  @st.cache_data
265
  def generate_sample_data():
266
- # ایجاد داده‌های مزارع
267
  farm_data = {
268
  'Farm_ID': [f'{i:02d}-{j:02d}' for i in range(1, 5) for j in range(20, 30)],
269
  'Farm_Name': [f'مزرعه {i:02d}-{j:02d}' for i in range(1, 5) for j in range(20, 30)],
@@ -275,29 +208,18 @@ def generate_sample_data():
275
  'Variety': [np.random.choice(['CP69', 'CP73']) for _ in range(40)],
276
  'Crop_Age': [np.random.randint(1, 5) for _ in range(40)]
277
  }
278
-
279
  farms_df = pd.DataFrame(farm_data)
280
 
281
- # ایجاد داده‌های ارتفاع
282
  height_data = []
283
  today = datetime.now()
284
-
285
- # 5 هفته اخیر
286
  for week in range(1, 6):
287
  date = today - timedelta(days=(5-week)*7)
288
  for farm_id in farms_df['Farm_ID']:
289
- # مقادیر ایستگاه
290
  stations = [np.random.randint(150, 220) for _ in range(5)]
291
- # میانگین ارتفاع
292
  avg_height = int(np.mean(stations))
293
-
294
- # آب زیرزمینی
295
  groundwater = [np.random.randint(50, 100) for _ in range(2)]
296
-
297
- # رطوبت غلاف و نیتروژن
298
  sheath_moisture = np.random.randint(60, 90)
299
  nitrogen = np.random.randint(20, 40)
300
-
301
  height_data.append({
302
  'Measurement_ID': f"{farm_id}-W{week}",
303
  'Farm_ID': farm_id,
@@ -314,23 +236,16 @@ def generate_sample_data():
314
  'Sheath_Moisture': sheath_moisture,
315
  'Nitrogen': nitrogen
316
  })
317
-
318
  heights_df = pd.DataFrame(height_data)
319
 
320
- # ایجاد داده‌های گزارش هفتگی
321
  weekly_report_data = []
322
-
323
  for farm_id in farms_df['Farm_ID']:
324
  farm_heights = heights_df[heights_df['Farm_ID'] == farm_id].sort_values('Week')
325
-
326
  for week in range(1, 6):
327
  current_week_data = farm_heights[farm_heights['Week'] == week]
328
  if current_week_data.empty:
329
  continue
330
-
331
  current_height = current_week_data['Height'].values[0]
332
-
333
- # محاسبه تغییر رشد نسبت به هفته قبل
334
  if week > 1:
335
  prev_week_data = farm_heights[farm_heights['Week'] == week-1]
336
  if not prev_week_data.empty:
@@ -340,19 +255,14 @@ def generate_sample_data():
340
  growth_change = 0
341
  else:
342
  growth_change = 0
343
-
344
- # محاسبه متوسط ارتفاع تمام مزارع در این هفته
345
  all_farms_this_week = heights_df[heights_df['Week'] == week]
346
  avg_height_all_farms = all_farms_this_week['Height'].mean()
347
-
348
- # تعیین وضعیت بر اساس مقایسه با میانگین
349
  if current_height > avg_height_all_farms + 5:
350
  growth_status = 'خوب'
351
  elif current_height < avg_height_all_farms - 5:
352
  growth_status = 'ضعیف'
353
  else:
354
  growth_status = 'متوسط'
355
-
356
  weekly_report_data.append({
357
  'Report_ID': f"{farm_id}-R{week}",
358
  'Farm_ID': farm_id,
@@ -362,105 +272,64 @@ def generate_sample_data():
362
  'Growth_Status': growth_status,
363
  'Regional_Average': int(avg_height_all_farms)
364
  })
365
-
366
  weekly_report_df = pd.DataFrame(weekly_report_data)
367
 
368
  return farms_df, heights_df, weekly_report_df
369
 
370
- # دریافت داده‌ها
371
  farms_df, heights_df, weekly_report_df = generate_sample_data()
372
-
373
- # ترکیب داده‌ها برای استفاده راحت‌تر
374
  report_with_details = pd.merge(weekly_report_df, farms_df, on='Farm_ID')
375
  latest_week = heights_df['Week'].max()
376
  latest_reports = report_with_details[report_with_details['Week'] == latest_week]
377
 
378
  # مدیریت وضعیت برنامه
379
  if 'selected_tab' not in st.session_state:
380
- st.session_state.selected_tab = "داشبورد"
381
-
382
  if 'selected_day' not in st.session_state:
383
  st.session_state.selected_day = "شنبه"
384
-
385
- # برای ذخیره داده‌های ورودی
386
  if 'farm_data' not in st.session_state:
387
  st.session_state.farm_data = {}
388
-
389
- # برای ذخیره نقشه تولید شده
390
  if 'ndvi_map' not in st.session_state:
391
  st.session_state.ndvi_map = None
392
 
393
  # تابع برای محاسبه میانگین ارتفاع
394
- def calculate_average_height(farm_id, stations):
 
395
  valid_stations = [s for s in stations if s > 0]
396
- if valid_stations:
397
- return int(sum(valid_stations) / len(valid_stations))
398
- return 0
399
 
400
- # تابع برای ایجاد نقشه NDVI
401
  def create_ndvi_map(farm_id=None, date=None):
402
  if not earth_engine_initialized:
403
  st.warning("اتصال به Earth Engine برقرار نشد. نقشه NDVI نمایش داده نمی‌شود.")
404
  return None
405
-
406
  try:
407
- # مختصات مرکزی تقریبی خوزستان (اهواز)
408
  center_point = [31.3183, 48.6706]
409
-
410
- # ایجاد نقشه
411
  m = folium.Map(location=center_point, zoom_start=10)
412
-
413
- # اگر مزرعه انتخاب شده است، آن را نمایش می‌دهیم
414
  if farm_id:
415
- # در اینجا باید مختصات دقیق مزرعه را داشته باشیم
416
- # برای مثال، از داده‌های نمونه استفاده می‌کنیم
417
  farm = farms_df[farms_df['Farm_ID'] == farm_id].iloc[0]
418
-
419
- # مختصات فرضی برای مزرعه
420
  lat = 31.3183 + (int(farm_id.split('-')[0]) - 1) * 0.05
421
  lon = 48.6706 + (int(farm_id.split('-')[1]) - 20) * 0.05
422
-
423
- # تهیه تصویر Sentinel-2
424
  if date:
425
- # تبدیل تاریخ به فرمت مناسب
426
- # فرض می‌کنیم تاریخ به فرمت YYYY-MM-DD است
427
- try:
428
- date_obj = datetime.strptime(date, '%Y-%m-%d')
429
- start_date = date_obj - timedelta(days=5)
430
- end_date = date_obj + timedelta(days=5)
431
-
432
- start_date_str = start_date.strftime('%Y-%m-%d')
433
- end_date_str = end_date.strftime('%Y-%m-%d')
434
- except:
435
- # اگر تاریخ معتبر نیست، از بازه پیش‌فرض استفاده می‌کنیم
436
- start_date_str = '2023-01-01'
437
- end_date_str = '2023-12-31'
438
  else:
439
- # بازه پیش‌فرض
440
  start_date_str = '2023-01-01'
441
  end_date_str = '2023-12-31'
442
-
443
- # ناحیه مورد نظر
444
- region = ee.Geometry.Point([lon, lat]).buffer(1000) # بافر 1 کیلومتری
445
-
446
- # دریافت تصویر Sentinel-2
447
  s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
448
  .filterDate(start_date_str, end_date_str) \
449
  .filterBounds(region) \
450
  .sort('CLOUD_COVERAGE_ASSESSMENT') \
451
  .first()
452
-
453
- # محاسبه NDVI
454
  ndvi = s2.normalizedDifference(['B8', 'B4']).rename('NDVI')
455
-
456
- # رنگ‌بندی NDVI
457
  ndvi_viz = {
458
  'min': -0.2,
459
  'max': 0.8,
460
  'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850']
461
  }
462
-
463
- # افزودن لایه NDVI به نقشه
464
  map_id_dict = ee.Image(ndvi).getMapId(ndvi_viz)
465
  folium.TileLayer(
466
  tiles=map_id_dict['tile_fetcher'].url_format,
@@ -469,107 +338,66 @@ def create_ndvi_map(farm_id=None, date=None):
469
  overlay=True,
470
  control=True
471
  ).add_to(m)
472
-
473
- # افزودن مارکر برای مزرعه
474
  folium.Marker(
475
  location=[lat, lon],
476
  popup=f'مزرعه {farm_id}',
477
  icon=folium.Icon(color='green', icon='leaf')
478
  ).add_to(m)
479
-
480
- # تنظیم نمای نقشه روی مزرعه
481
  m.fit_bounds([[lat-0.05, lon-0.05], [lat+0.05, lon+0.05]])
482
-
483
- # افزودن کنترل‌های لایه
484
  folium.LayerControl().add_to(m)
485
-
486
  return m
487
-
488
  except Exception as e:
489
  st.error(f"خطا در ایجاد نقشه NDVI: {e}")
490
  return None
491
 
492
- # منوی کناری
493
  with st.sidebar:
494
  st.image("https://via.placeholder.com/150x150.png?text=لوگو", width=150)
495
  st.title("سامانه پایش مزارع نیشکر")
496
  st.subheader("شرکت کشت و صنعت نیشکر دهخدا")
497
-
498
  menu = st.selectbox(
499
  "انتخاب صفحه",
500
  ["داشبورد", "ورود اطلاعات", "گزارش‌گیری", "تحلیل داده‌ها", "تنظیمات"]
501
  )
502
-
503
  st.session_state.selected_tab = menu
504
-
505
  st.markdown("---")
506
  st.markdown("### راهنمای وضعیت رشد")
507
-
508
  col1, col2 = st.columns(2)
509
  with col1:
510
  st.markdown('<div class="status-good">خوب</div>', unsafe_allow_html=True)
511
  st.markdown('<div class="status-medium">متوسط</div>', unsafe_allow_html=True)
512
  st.markdown('<div class="status-bad">ضعیف</div>', unsafe_allow_html=True)
513
-
514
  with col2:
515
  st.write("بالاتر از میانگین")
516
  st.write("نزدیک به میانگین")
517
  st.write("پایین‌تر از میانگین")
518
-
519
  st.markdown("---")
520
  st.info("نسخه آزمایشی 1.0")
521
 
522
- # بخش داشبورد
523
  if st.session_state.selected_tab == "داشبورد":
524
  st.title("داشبورد پایش مزارع نیشکر")
525
-
526
- # خلاصه وضعیت
527
  st.subheader("وضعیت کلی مزارع")
528
-
529
- # شمارش وضعیت‌های مختلف
530
  status_counts = latest_reports['Growth_Status'].value_counts()
531
  good_count = status_counts.get('خوب', 0)
532
  medium_count = status_counts.get('متوسط', 0)
533
  bad_count = status_counts.get('ضعیف', 0)
534
-
535
  col1, col2, col3, col4 = st.columns(4)
536
-
537
  with col1:
538
- st.markdown('<div class="metric-card metric-green">'
539
- f'<h2>{good_count}</h2>'
540
- '<p>مزارع با وضعیت خوب</p>'
541
- '</div>', unsafe_allow_html=True)
542
-
543
  with col2:
544
- st.markdown('<div class="metric-card metric-yellow">'
545
- f'<h2>{medium_count}</h2>'
546
- '<p>مزارع با وضعیت متوسط</p>'
547
- '</div>', unsafe_allow_html=True)
548
-
549
  with col3:
550
- st.markdown('<div class="metric-card metric-red">'
551
- f'<h2>{bad_count}</h2>'
552
- '<p>مزارع با وضعیت ضعیف</p>'
553
- '</div>', unsafe_allow_html=True)
554
-
555
  with col4:
556
  avg_height = int(latest_reports['Average_Height'].mean())
557
- st.markdown('<div class="metric-card metric-blue">'
558
- f'<h2>{avg_height} سانتی‌متر</h2>'
559
- '<p>میانگین ارتفاع هفته جاری</p>'
560
- '</div>', unsafe_allow_html=True)
561
-
562
  st.markdown("---")
563
-
564
- # نمودارها و گراف‌ها
565
  col1, col2 = st.columns(2)
566
-
567
  with col1:
568
  st.subheader("میانگین ارتفاع به تفکیک اداره")
569
-
570
  admin_heights = latest_reports.groupby('Administration_Name')['Average_Height'].mean().reset_index()
571
  admin_heights['Average_Height'] = admin_heights['Average_Height'].astype(int)
572
-
573
  fig = px.bar(
574
  admin_heights,
575
  x='Administration_Name',
@@ -578,23 +406,12 @@ if st.session_state.selected_tab == "داشبورد":
578
  color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
579
  labels={'Administration_Name': 'اداره', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
580
  )
581
-
582
- fig.update_layout(
583
- height=400,
584
- xaxis_title="اداره",
585
- yaxis_title="میانگین ارتفاع (سانتی‌متر)",
586
- font=dict(family="IRANSans"),
587
- plot_bgcolor='rgba(0,0,0,0)'
588
- )
589
-
590
  st.plotly_chart(fig, use_container_width=True)
591
-
592
  with col2:
593
  st.subheader("مقایسه واریته‌ها")
594
-
595
  variety_heights = latest_reports.groupby('Variety')['Average_Height'].mean().reset_index()
596
  variety_heights['Average_Height'] = variety_heights['Average_Height'].astype(int)
597
-
598
  fig = px.bar(
599
  variety_heights,
600
  x='Variety',
@@ -603,23 +420,11 @@ if st.session_state.selected_tab == "داشبورد":
603
  color_discrete_sequence=["#00c853", "#2196f3"],
604
  labels={'Variety': 'واریته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
605
  )
606
-
607
- fig.update_layout(
608
- height=400,
609
- xaxis_title="واریته",
610
- yaxis_title="میانگین ارتفاع (سانتی‌متر)",
611
- font=dict(family="IRANSans"),
612
- plot_bgcolor='rgba(0,0,0,0)'
613
- )
614
-
615
  st.plotly_chart(fig, use_container_width=True)
616
-
617
- # روند ارتفاع هفتگی
618
  st.subheader("روند میانگین ارتفاع هفتگی")
619
-
620
  weekly_avg = report_with_details.groupby('Week')['Average_Height'].mean().reset_index()
621
  weekly_avg['Average_Height'] = weekly_avg['Average_Height'].astype(int)
622
-
623
  fig = px.line(
624
  weekly_avg,
625
  x='Week',
@@ -628,82 +433,52 @@ if st.session_state.selected_tab == "داشبورد":
628
  line_shape='spline',
629
  labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
630
  )
631
-
632
  fig.update_traces(line=dict(width=3, color="#1e6b45"), marker=dict(size=10))
633
-
634
- fig.update_layout(
635
- height=400,
636
- xaxis_title="هفته",
637
- yaxis_title="میانگین ارتفاع (سانتی‌متر)",
638
- font=dict(family="IRANSans"),
639
- plot_bgcolor='rgba(0,0,0,0)'
640
- )
641
-
642
  st.plotly_chart(fig, use_container_width=True)
643
-
644
- # انتخاب مزرعه برای نمایش نقشه NDVI
645
  st.markdown("---")
646
  st.subheader("نقشه NDVI")
647
-
648
  col1, col2 = st.columns(2)
649
-
650
  with col1:
651
  selected_farm_for_map = st.selectbox(
652
  "انتخاب مزرعه",
653
  farms_df['Farm_ID'].tolist(),
654
  format_func=lambda x: farms_df[farms_df['Farm_ID'] == x]['Farm_Name'].iloc[0]
655
  )
656
-
657
  with col2:
658
- # تاریخ آخرین اندازه‌گیری
659
  latest_date = heights_df[heights_df['Farm_ID'] == selected_farm_for_map].sort_values('Week', ascending=False)['Measurement_Date'].iloc[0]
660
  selected_date_for_map = st.date_input(
661
  "انتخاب تاریخ",
662
  value=datetime.strptime(latest_date, '%Y-%m-%d'),
663
  max_value=datetime.now()
664
  )
665
-
666
- # تولید نقشه
667
  if st.button("نمایش نقشه NDVI"):
668
  with st.spinner("در حال تولید نقشه..."):
669
  ndvi_map = create_ndvi_map(selected_farm_for_map, selected_date_for_map.strftime('%Y-%m-%d'))
670
  st.session_state.ndvi_map = ndvi_map
671
-
672
- # نمایش نقشه
673
  if st.session_state.ndvi_map:
674
  st.markdown('<div class="map-container">', unsafe_allow_html=True)
675
  folium_static(st.session_state.ndvi_map, width=1200, height=500)
676
  st.markdown('</div>', unsafe_allow_html=True)
677
-
678
- # اطلاعات مزرعه انتخاب شده
679
  farm_info = farms_df[farms_df['Farm_ID'] == selected_farm_for_map].iloc[0]
680
  farm_measurements = heights_df[(heights_df['Farm_ID'] == selected_farm_for_map) &
681
  (heights_df['Measurement_Date'] == latest_date)].iloc[0]
682
-
683
  st.markdown(f"""
684
  ### اطلاعات مزرعه {farm_info['Farm_Name']}
685
-
686
  **اداره:** {farm_info['Administration_Name']} | **کانال:** {farm_info['Channel_ID']} | **واریته:** {farm_info['Variety']} | **سن کشت:** {farm_info['Crop_Age']} سال
687
-
688
  **آخرین اندازه‌گیری:** {latest_date}
689
  - **میانگین ارتفاع:** {farm_measurements['Height']} سانتی‌متر
690
  - **رطوبت غلاف:** {farm_measurements['Sheath_Moisture']}%
691
  - **نیتروژن:** {farm_measurements['Nitrogen']}%
692
  """)
693
  else:
694
- st.info("برای نمایش نقشه NDVI، یک مزرعه و تاریخ انتخاب کرده و دکمه «نمایش نقشه NDVI» را کلیک کنید.")
695
-
696
- # مزارع با بیشترین و کمترین رشد
697
  st.markdown("---")
698
  st.subheader("مزارع با بیشترین و کمترین رشد")
699
-
700
  col1, col2 = st.columns(2)
701
-
702
- # مزارع با بیشترین رشد
703
  with col1:
704
  st.markdown("#### بیشترین رشد هفتگی")
705
  top_growth = report_with_details[report_with_details['Week'] == latest_week].sort_values('Growth_Change', ascending=False).head(5)
706
-
707
  for _, row in top_growth.iterrows():
708
  growth_color = "green" if row['Growth_Change'] > 0 else "red"
709
  st.markdown(f"""
@@ -715,12 +490,9 @@ if st.session_state.selected_tab == "داشبورد":
715
  </p>
716
  </div>
717
  """, unsafe_allow_html=True)
718
-
719
- # مزارع با کمترین رشد
720
  with col2:
721
  st.markdown("#### کمترین رشد هفتگی")
722
  bottom_growth = report_with_details[report_with_details['Week'] == latest_week].sort_values('Growth_Change').head(5)
723
-
724
  for _, row in bottom_growth.iterrows():
725
  growth_color = "green" if row['Growth_Change'] > 0 else "red"
726
  st.markdown(f"""
@@ -733,309 +505,139 @@ if st.session_state.selected_tab == "داشبورد":
733
  </div>
734
  """, unsafe_allow_html=True)
735
 
736
- # بخش ورود اطلاعات
737
  elif st.session_state.selected_tab == "ورود اطلاعات":
738
  st.title("ورود اطلاعات روزانه")
739
-
740
  # انتخاب روز هفته
741
  days_of_week = ["شنبه", "یکشنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنجشنبه"]
742
  selected_day = st.selectbox("انتخاب روز", days_of_week, index=days_of_week.index(st.session_state.selected_day))
743
  st.session_state.selected_day = selected_day
744
-
745
  # انتخاب اداره
746
  selected_admin = st.selectbox("انتخاب اداره", sorted(farms_df['Administration_ID'].unique()))
747
-
748
- # فیلتر مزارع براساس اداره انتخاب شد��
749
- filtered_farms = farms_df[farms_df['Administration_ID'] == selected_admin]
750
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
751
  st.subheader(f"ورود داده‌های روز {selected_day} - اداره {selected_admin}")
752
-
753
- # خلاصه آماری زنده
754
- st.markdown("### خلاصه وضعیت داده‌های وارد شده", unsafe_allow_html=True)
755
-
756
- summary_cols = st.columns(4)
757
-
758
- # میانگین ارتفاع کل
759
- with summary_cols[0]:
760
- st.markdown(
761
- '<div class="metric-card metric-green">'
762
- '<div class="dynamic-label">میانگین ارتفاع کل</div>'
763
- f'<div class="dynamic-value" id="total_avg_height">0</div>'
764
- '<div>سانتی‌متر</div>'
765
- '</div>',
766
- unsafe_allow_html=True
767
- )
768
-
769
- # تعداد مزارع وارد شده
770
- with summary_cols[1]:
771
- st.markdown(
772
- '<div class="metric-card metric-blue">'
773
- '<div class="dynamic-label">تعداد مزارع وارد شده</div>'
774
- f'<div class="dynamic-value" id="farms_entered">0</div>'
775
- '<div>از {len(filtered_farms)} مزرعه</div>'
776
- '</div>',
777
- unsafe_allow_html=True
778
- )
779
-
780
- # میانگین رطوبت غلاف
781
- with summary_cols[2]:
782
- st.markdown(
783
- '<div class="metric-card metric-yellow">'
784
- '<div class="dynamic-label">میانگین رطوبت غلاف</div>'
785
- f'<div class="dynamic-value" id="avg_moisture">0</div>'
786
- '<div>درصد</div>'
787
- '</div>',
788
- unsafe_allow_html=True
789
- )
790
-
791
- # میانگین نیتروژن
792
- with summary_cols[3]:
793
- st.markdown(
794
- '<div class="metric-card metric-blue">'
795
- '<div class="dynamic-label">میانگین نیتروژن</div>'
796
- f'<div class="dynamic-value" id="avg_nitrogen">0</div>'
797
- '<div>درصد</div>'
798
- '</div>',
799
- unsafe_allow_html=True
800
- )
801
-
802
  st.markdown("---")
803
-
804
- # ایجاد ستون‌های جدول
805
- cols = st.columns((3, 1, 1, 1, 1, 1, 1, 1, 1, 1))
806
- fields = ['مزرعه', 'ایستگاه 1', 'ایستگاه 2', 'ایستگاه 3', 'ایستگاه 4', 'ایستگاه 5', 'چاهک 1', 'چاهک 2', 'رطوبت غلاف', 'نیتروژن']
807
-
808
- # عنوان ستون‌ها
809
- for col, field_name in zip(cols, fields):
810
- col.markdown(f"**{field_name}**")
811
-
812
- # ایجاد حقول ورودی برای هر مزرعه
813
- farm_data = {}
814
-
815
- # آیا اطلاعات قبلی وجود دارد؟
816
- for _, farm in filtered_farms.iterrows():
817
- farm_id = farm['Farm_ID']
818
- farm_name = farm['Farm_Name']
819
-
820
- cols = st.columns((3, 1, 1, 1, 1, 1, 1, 1, 1, 1))
821
-
822
- cols[0].write(farm_name)
823
-
824
- station_values = []
825
-
826
- # ایستگاه‌های ارتفاع
827
- for i in range(1, 6):
828
- input_key = f"{farm_id}_station_{i}_{selected_day}"
829
-
830
- # مقدار پیش‌فرض از داده‌های قبلی
831
- default_value = 0
832
- if farm_id in st.session_state.farm_data and f"station_{i}" in st.session_state.farm_data[farm_id]:
833
- default_value = st.session_state.farm_data[farm_id][f"station_{i}"]
834
-
835
- value = cols[i].number_input(
836
- "",
837
- min_value=0,
838
- max_value=300,
839
- value=default_value,
840
- step=1,
841
- key=input_key,
842
- label_visibility="collapsed",
843
- # به صورت داینامیک محاسبه را انجام می‌دهیم
844
- on_change=None
845
- )
846
- station_values.append(value)
847
-
848
- # ذخیره مقدار در session_state
849
- if farm_id not in st.session_state.farm_data:
850
- st.session_state.farm_data[farm_id] = {}
851
- st.session_state.farm_data[farm_id][f"station_{i}"] = value
852
-
853
- # چاهک‌های آب
854
- groundwater_values = []
855
- for i in range(1, 3):
856
- input_key = f"{farm_id}_groundwater_{i}_{selected_day}"
857
-
858
- # مقدار پیش‌فرض از داده‌های قبلی
859
- default_value = 0
860
- if farm_id in st.session_state.farm_data and f"groundwater_{i}" in st.session_state.farm_data[farm_id]:
861
- default_value = st.session_state.farm_data[farm_id][f"groundwater_{i}"]
862
-
863
- value = cols[5+i].number_input(
864
- "",
865
- min_value=0,
866
- max_value=300,
867
- value=default_value,
868
- step=1,
869
- key=input_key,
870
- label_visibility="collapsed"
871
- )
872
- groundwater_values.append(value)
873
-
874
- # ذخیره مقدار در session_state
875
- st.session_state.farm_data[farm_id][f"groundwater_{i}"] = value
876
-
877
- # رطوبت غلاف
878
- input_key = f"{farm_id}_moisture_{selected_day}"
879
-
880
- # مقدار پیش‌فرض از داده‌های قبلی
881
- default_value = 0
882
- if farm_id in st.session_state.farm_data and "moisture" in st.session_state.farm_data[farm_id]:
883
- default_value = st.session_state.farm_data[farm_id]["moisture"]
884
-
885
- moisture = cols[7].number_input(
886
- "",
887
- min_value=0,
888
- max_value=100,
889
- value=default_value,
890
- step=1,
891
- key=input_key,
892
- label_visibility="collapsed"
893
- )
894
-
895
- # ذخیره مقدار در session_state
896
- st.session_state.farm_data[farm_id]["moisture"] = moisture
897
-
898
- # نیتروژن
899
- input_key = f"{farm_id}_nitrogen_{selected_day}"
900
-
901
- # مقدار پیش‌فرض از داده‌های قبلی
902
- default_value = 0
903
- if farm_id in st.session_state.farm_data and "nitrogen" in st.session_state.farm_data[farm_id]:
904
- default_value = st.session_state.farm_data[farm_id]["nitrogen"]
905
-
906
- nitrogen = cols[8].number_input(
907
- "",
908
- min_value=0,
909
- max_value=100,
910
- value=default_value,
911
- step=1,
912
- key=input_key,
913
- label_visibility="collapsed"
914
- )
915
-
916
- # ذخیره مقدار در session_state
917
- st.session_state.farm_data[farm_id]["nitrogen"] = nitrogen
918
-
919
- # محاسبه میانگین ارتفاع
920
- avg_height = calculate_average_height(farm_id, station_values)
921
-
922
- # ذخیره میانگین در session_state
923
- st.session_state.farm_data[farm_id]["avg_height"] = avg_height
924
-
925
- # نمایش میانگین با ID منحصر به فرد برای آپدیت JavaScript
926
- avg_id = f"avg_{farm_id}"
927
- cols[9].markdown(f'<div id="{avg_id}" class="dynamic-value" style="text-align: center;">{avg_height if avg_height > 0 else "-"}</div>', unsafe_allow_html=True)
928
-
929
- # ذخیره داده‌ها برای ثبت نهایی
930
- farm_data[farm_id] = {
931
- 'stations': station_values,
932
- 'groundwater': groundwater_values,
933
- 'avg_height': avg_height,
934
- 'moisture': moisture,
935
- 'nitrogen': nitrogen
936
- }
937
-
938
- # محاسبه و به‌روزرسانی خلاصه آماری
939
- valid_heights = [data['avg_height'] for data in farm_data.values() if data['avg_height'] > 0]
940
- total_avg_height = int(sum(valid_heights) / len(valid_heights)) if valid_heights else 0
941
-
942
- farms_entered = len([data for data in farm_data.values() if data['avg_height'] > 0])
943
-
944
- valid_moisture = [data['moisture'] for data in farm_data.values() if data['moisture'] > 0]
945
- avg_moisture = int(sum(valid_moisture) / len(valid_moisture)) if valid_moisture else 0
946
-
947
- valid_nitrogen = [data['nitrogen'] for data in farm_data.values() if data['nitrogen'] > 0]
948
- avg_nitrogen = int(sum(valid_nitrogen) / len(valid_nitrogen)) if valid_nitrogen else 0
949
-
950
- # به‌روزرسانی میانگین‌ها با JavaScript
951
- st.markdown(f"""
952
- <script>
953
- document.getElementById('total_avg_height').textContent = '{total_avg_height}';
954
- document.getElementById('farms_entered').textContent = '{farms_entered}';
955
- document.getElementById('avg_moisture').textContent = '{avg_moisture}';
956
- document.getElementById('avg_nitrogen').textContent = '{avg_nitrogen}';
957
- </script>
958
- """, unsafe_allow_html=True)
959
-
960
- # دکمه ذخیره‌سازی
961
- if st.button("ذخیره اطلاعات", key="save_daily_data"):
962
- # بررسی وجود داده معتبر
963
- valid_data_exists = any(data['avg_height'] > 0 for data in farm_data.values())
964
-
965
- if valid_data_exists:
966
- with st.spinner('در حال ذخیره‌سازی اطلاعات...'):
967
- # شبیه‌سازی تأخیر ذخیره‌سازی
968
- time.sleep(1)
969
- st.success("اطلاعات با موفقیت ذخیره شد. در نسخه عملیاتی، این داده‌ها در پایگاه داده ذخیره می‌شوند.")
970
-
971
- # نمایش خلاصه داده‌های ثبت شده
972
- st.subheader("خلاصه داده‌های ثبت شده:")
973
-
974
- summary_data = []
975
- for farm_id, data in farm_data.items():
976
- if data['avg_height'] > 0: # فقط مزارعی که داده داشته‌اند
977
- farm_info = filtered_farms[filtered_farms['Farm_ID'] == farm_id].iloc[0]
978
- summary_data.append({
979
- 'مزرعه': farm_info['Farm_Name'],
980
- 'میانگین ارتفاع': data['avg_height'],
981
- 'رطوبت غلاف': data['moisture'],
982
- 'نیتروژن': data['nitrogen']
983
- })
984
-
985
- if summary_data:
986
- summary_df = pd.DataFrame(summary_data)
987
- st.dataframe(summary_df, use_container_width=True)
988
- else:
989
- st.warning("هیچ داده‌ای برای ذخیره‌سازی وارد نشده است. لطفاً حداقل برای یک مزرعه ارتفاع را وارد کنید.")
990
 
991
- # بخش گزارش‌گیری
992
  elif st.session_state.selected_tab == "گزارش‌گیری":
993
  st.title("گزارش‌های هفتگی")
994
-
995
- # انتخاب نوع گزارش
996
  report_type = st.radio(
997
  "نوع گزارش",
998
  ["گزارش به تفکیک مزرعه", "گزارش به تفکیک اداره", "گزارش به تفکیک کانال", "مزارع با وضعیت ضعیف"]
999
  )
1000
-
1001
  st.markdown("---")
1002
-
1003
  if report_type == "گزارش به تفکیک مزرعه":
1004
  st.subheader("گزارش هفتگی به تفکیک مزرعه")
1005
-
1006
- # انتخاب اداره
1007
  selected_admin = st.selectbox("انتخاب اداره", sorted(farms_df['Administration_ID'].unique()), key="admin_select_farm_report")
1008
-
1009
- # فیلتر مزارع براساس اداره انتخاب شده
1010
  filtered_farms = farms_df[farms_df['Administration_ID'] == selected_admin]
1011
-
1012
- # انتخاب مزرعه
1013
  selected_farm_id = st.selectbox(
1014
  "انتخاب مزرعه",
1015
  filtered_farms['Farm_ID'].tolist(),
1016
  format_func=lambda x: filtered_farms[filtered_farms['Farm_ID'] == x]['Farm_Name'].iloc[0]
1017
  )
1018
-
1019
- # فیلتر داده‌های گزارش براساس مزرعه انتخاب شده
1020
  farm_reports = weekly_report_df[weekly_report_df['Farm_ID'] == selected_farm_id].sort_values('Week')
1021
-
1022
  if not farm_reports.empty:
1023
- # اطلاعات مزرعه
1024
  farm_info = filtered_farms[filtered_farms['Farm_ID'] == selected_farm_id].iloc[0]
1025
-
1026
  col1, col2, col3 = st.columns(3)
1027
  col1.metric("واریته", farm_info['Variety'])
1028
  col2.metric("سن کشت", f"{farm_info['Crop_Age']} سال")
1029
  col3.metric("مساحت", f"{farm_info['Area']} هکتار")
1030
-
1031
  st.markdown("---")
1032
-
1033
- # نمایش گراف روند رشد
1034
  st.subheader("روند رشد هفتگی")
1035
-
1036
  fig = go.Figure()
1037
-
1038
- # افزودن خط میانگین منطقه
1039
  fig.add_trace(go.Scatter(
1040
  x=farm_reports['Week'],
1041
  y=farm_reports['Regional_Average'],
@@ -1044,8 +646,6 @@ elif st.session_state.selected_tab == "گزارش‌گیری":
1044
  line=dict(color='gray', width=2, dash='dot'),
1045
  marker=dict(size=8)
1046
  ))
1047
-
1048
- # افزودن خط ارتفاع مزرعه
1049
  fig.add_trace(go.Scatter(
1050
  x=farm_reports['Week'],
1051
  y=farm_reports['Average_Height'],
@@ -1054,59 +654,36 @@ elif st.session_state.selected_tab == "گزارش‌گیری":
1054
  line=dict(color='#1e6b45', width=3),
1055
  marker=dict(size=10)
1056
  ))
1057
-
1058
- fig.update_layout(
1059
- height=400,
1060
- xaxis_title="هفته",
1061
- yaxis_title="ارتفاع (سانتی‌متر)",
1062
- font=dict(family="IRANSans"),
1063
- plot_bgcolor='rgba(0,0,0,0)'
1064
- )
1065
-
1066
  st.plotly_chart(fig, use_container_width=True)
1067
-
1068
- # نمایش جدول داده‌ها
1069
  st.subheader("داده‌های هفتگی")
1070
-
1071
  report_table = pd.DataFrame({
1072
  'هفته': farm_reports['Week'],
1073
- 'تاریخ': farm_reports['Measurement_Date'],
1074
  'ارتفاع': farm_reports['Average_Height'],
1075
  'میانگین منطقه': farm_reports['Regional_Average'],
1076
  'تغییرات': farm_reports['Growth_Change'],
1077
  'وضعیت': farm_reports['Growth_Status']
1078
  })
1079
-
1080
- # تبدیل ستون وضعیت به صورت HTML با رنگ متناسب
1081
  def status_with_color(status):
1082
  colors = {'خوب': '#00c853', 'متوسط': '#ffd600', 'ضعیف': '#ff3d00'}
1083
  return f'<span style="color: {colors.get(status, "black")};">{status}</span>'
1084
-
1085
  report_table['وضعیت'] = report_table['وضعیت'].apply(status_with_color)
1086
-
1087
  st.markdown(report_table.to_html(escape=False, index=False), unsafe_allow_html=True)
1088
-
1089
- # نمایش نقشه NDVI برای مزرعه
1090
  st.subheader("نقشه NDVI مزرعه")
1091
-
1092
  if st.button("نمایش نقشه NDVI این مزرعه"):
1093
  with st.spinner("در حال تولید نقشه..."):
1094
- # استفاده از تاریخ آخرین اندازه‌گیری
1095
- latest_date = farm_reports.iloc[-1]['Measurement_Date']
1096
- ndvi_map = create_ndvi_map(selected_farm_id, latest_date)
1097
-
1098
  if ndvi_map:
1099
  st.markdown('<div class="map-container">', unsafe_allow_html=True)
1100
  folium_static(ndvi_map, width=1200, height=500)
1101
  st.markdown('</div>', unsafe_allow_html=True)
1102
-
1103
- # توصیه‌های کارشناسی
1104
  st.markdown("---")
1105
  st.subheader("توصیه‌های کارشناسی")
1106
-
1107
  latest_report = farm_reports.iloc[-1]
1108
  latest_status = latest_report['Growth_Status']
1109
-
1110
  if latest_status == 'خوب':
1111
  st.success("وضعیت رشد مزرعه مطلوب است. ادامه برنامه فعلی توصیه می‌شود.")
1112
  elif latest_status == 'متوسط':
@@ -1119,298 +696,19 @@ elif st.session_state.selected_tab == "گزارش‌گیری":
1119
  - بررسی آفات و بیماری‌های احتمالی
1120
  - بررسی وضعیت زهکشی مزرعه
1121
  """)
1122
- else:
1123
- st.warning("اطلاعاتی برای این مزرعه ثبت نشده است.")
1124
-
1125
- elif report_type == "گزارش به تفکیک اداره":
1126
- st.subheader("گزارش هفتگی به تفکیک اداره")
1127
-
1128
- # محاسبه میانگین ارتفاع به تفکیک اداره و هفته
1129
- admin_weekly_avg = report_with_details.groupby(['Administration_ID', 'Administration_Name', 'Week'])['Average_Height'].mean().reset_index()
1130
- admin_weekly_avg['Average_Height'] = admin_weekly_avg['Average_Height'].astype(int)
1131
-
1132
- # نمودار مقایسه‌ای ادارات
1133
- fig = px.line(
1134
- admin_weekly_avg,
1135
- x='Week',
1136
- y='Average_Height',
1137
- color='Administration_Name',
1138
- markers=True,
1139
- labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Administration_Name': 'اداره'}
1140
- )
1141
-
1142
- fig.update_traces(line=dict(width=3), marker=dict(size=8))
1143
-
1144
- fig.update_layout(
1145
- height=500,
1146
- xaxis_title="هفته",
1147
- yaxis_title="میانگین ارتفاع (سانتی‌متر)",
1148
- font=dict(family="IRANSans"),
1149
- plot_bgcolor='rgba(0,0,0,0)'
1150
- )
1151
-
1152
- st.plotly_chart(fig, use_container_width=True)
1153
-
1154
- # آخرین هفته
1155
- latest_week_data = admin_weekly_avg[admin_weekly_avg['Week'] == latest_week]
1156
-
1157
- # جدول مقایسه آخرین هفته
1158
- st.subheader(f"مقایسه ادارات در هفته {latest_week}")
1159
-
1160
- comparison_table = latest_week_data[['Administration_Name', 'Average_Height']].sort_values('Average_Height', ascending=False)
1161
- comparison_table.columns = ['اداره', 'میانگین ارتفاع (سانتی‌متر)']
1162
-
1163
- st.table(comparison_table)
1164
-
1165
- # رتبه‌بندی ادارات
1166
- st.subheader("رتبه‌بندی ادارات")
1167
-
1168
- # حساب کردن میانگین رشد هر اداره
1169
- admin_growth = report_with_details.groupby(['Administration_ID', 'Administration_Name', 'Week'])['Growth_Change'].mean().reset_index()
1170
- admin_growth_latest = admin_growth[admin_growth['Week'] == latest_week].sort_values('Growth_Change', ascending=False)
1171
-
1172
- # نمایش به صورت نمودار میله‌ای
1173
- fig = px.bar(
1174
- admin_growth_latest,
1175
- x='Administration_Name',
1176
- y='Growth_Change',
1177
- color='Growth_Change',
1178
- color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
1179
- labels={'Administration_Name': 'اداره', 'Growth_Change': 'میانگین تغییرات رشد (سانتی‌متر)'}
1180
- )
1181
-
1182
- fig.update_layout(
1183
- height=400,
1184
- xaxis_title="اداره",
1185
- yaxis_title="میانگین تغییرات رشد (سانتی‌متر)",
1186
- font=dict(family="IRANSans"),
1187
- plot_bgcolor='rgba(0,0,0,0)'
1188
- )
1189
-
1190
- st.plotly_chart(fig, use_container_width=True)
1191
-
1192
- elif report_type == "گزارش به تفکیک کانال":
1193
- st.subheader("گزارش هفتگی به تفکیک کانال")
1194
-
1195
- # انتخاب اداره
1196
- selected_admin = st.selectbox("انتخاب اداره", sorted(farms_df['Administration_ID'].unique()), key="admin_select_channel")
1197
-
1198
- # فیلتر کانال‌های اداره انتخاب شده
1199
- filtered_channels = farms_df[farms_df['Administration_ID'] == selected_admin]['Channel_ID'].unique()
1200
-
1201
- # محاسبه میانگین ارتفاع به تفکیک کانال و هفته
1202
- channel_data = pd.merge(report_with_details, farms_df[['Farm_ID', 'Channel_ID']], on='Farm_ID')
1203
- channel_weekly_avg = channel_data.groupby(['Channel_ID', 'Week'])['Average_Height'].mean().reset_index()
1204
- channel_weekly_avg['Average_Height'] = channel_weekly_avg['Average_Height'].astype(int)
1205
-
1206
- # فیلتر کانال‌های اداره انتخاب شده
1207
- filtered_channel_data = channel_weekly_avg[channel_weekly_avg['Channel_ID'].isin(filtered_channels)]
1208
-
1209
- # نمودار مقایسه‌ای کانال‌ها
1210
- fig = px.line(
1211
- filtered_channel_data,
1212
- x='Week',
1213
- y='Average_Height',
1214
- color='Channel_ID',
1215
- markers=True,
1216
- labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Channel_ID': 'کانال'}
1217
- )
1218
-
1219
- fig.update_traces(line=dict(width=2), marker=dict(size=6))
1220
-
1221
- fig.update_layout(
1222
- height=500,
1223
- xaxis_title="هفته",
1224
- yaxis_title="میانگین ارتفاع (سانتی‌متر)",
1225
- font=dict(family="IRANSans"),
1226
- plot_bgcolor='rgba(0,0,0,0)'
1227
- )
1228
-
1229
- st.plotly_chart(fig, use_container_width=True)
1230
-
1231
- # آخرین هفته
1232
- latest_channel_data = filtered_channel_data[filtered_channel_data['Week'] == latest_week]
1233
-
1234
- # جدول مقایسه آخرین هفته
1235
- st.subheader(f"مقایسه کانال‌ها در هفته {latest_week}")
1236
-
1237
- comparison_table = latest_channel_data[['Channel_ID', 'Average_Height']].sort_values('Average_Height', ascending=False)
1238
- comparison_table.columns = ['کانال', 'میانگین ارتفاع (سانتی‌متر)']
1239
-
1240
- st.table(comparison_table)
1241
-
1242
- # نمودار میله‌ای مقایسه کانال‌ها
1243
- fig = px.bar(
1244
- comparison_table,
1245
- x='کانال',
1246
- y='میانگین ارتفاع (سانتی‌متر)',
1247
- color='میانگین ارتفاع (سانتی‌متر)',
1248
- color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
1249
- )
1250
-
1251
- fig.update_layout(
1252
- height=400,
1253
- xaxis_title="کانال",
1254
- yaxis_title="میانگین ارتفاع (سانتی‌متر)",
1255
- font=dict(family="IRANSans"),
1256
- plot_bgcolor='rgba(0,0,0,0)'
1257
- )
1258
-
1259
- st.plotly_chart(fig, use_container_width=True)
1260
-
1261
- elif report_type == "مزارع با وضعیت ضعیف":
1262
- st.subheader("گزارش مزارع با وضعیت ضعیف")
1263
-
1264
- # فیلتر مزارع با وضعیت ضعیف در آخرین هفته
1265
- weak_farms = report_with_details[(report_with_details['Week'] == latest_week) &
1266
- (report_with_details['Growth_Status'] == 'ضعیف')]
1267
-
1268
- if weak_farms.empty:
1269
- st.success("خوشبختانه هیچ مزرعه‌ای در وضعیت ضعیف وجود ندارد.")
1270
- else:
1271
- # تعداد مزارع ضعیف به تفکیک اداره
1272
- weak_by_admin = weak_farms.groupby('Administration_Name').size().reset_index(name='تعداد')
1273
-
1274
- col1, col2 = st.columns(2)
1275
-
1276
- with col1:
1277
- # نمودار دایره‌ای توزیع مزارع ضعیف
1278
- fig = px.pie(
1279
- weak_by_admin,
1280
- values='تعداد',
1281
- names='Administration_Name',
1282
- title='توزیع مزارع ضعیف بین ادارات'
1283
- )
1284
-
1285
- fig.update_traces(textinfo='percent+label')
1286
-
1287
- fig.update_layout(
1288
- height=350,
1289
- font=dict(family="IRANSans"),
1290
- )
1291
-
1292
- st.plotly_chart(fig, use_container_width=True)
1293
-
1294
- with col2:
1295
- # نمودار توزیع واریته‌ها
1296
- weak_by_variety = weak_farms.groupby('Variety').size().reset_index(name='تعداد')
1297
-
1298
- fig = px.pie(
1299
- weak_by_variety,
1300
- values='تعداد',
1301
- names='Variety',
1302
- title='توزیع واریته‌ها در مزارع ضعیف'
1303
- )
1304
-
1305
- fig.update_traces(textinfo='percent+label')
1306
-
1307
- fig.update_layout(
1308
- height=350,
1309
- font=dict(family="IRANSans"),
1310
- )
1311
-
1312
- st.plotly_chart(fig, use_container_width=True)
1313
-
1314
- # جدول مزارع ضعیف
1315
- st.subheader("لیست مزارع با وضعیت ضعیف")
1316
-
1317
- weak_farms_table = weak_farms[['Farm_Name', 'Administration_Name', 'Channel_ID', 'Average_Height', 'Regional_Average', 'Growth_Change', 'Variety', 'Crop_Age']]
1318
- weak_farms_table.columns = ['نام مزرعه', 'اداره', 'کانال', 'ارتفاع فعلی', 'میانگین منطقه', 'تغییر رشد', 'واریته', 'سن کشت']
1319
-
1320
- st.dataframe(weak_farms_table.sort_values('ارتفاع فعلی'), use_container_width=True)
1321
-
1322
- # نمایش نقشه NDVI برای مزارع ضعیف
1323
- st.subheader("نقشه NDVI مزارع ضعیف")
1324
-
1325
- # انتخاب مزرعه برای نمایش
1326
- selected_weak_farm = st.selectbox(
1327
- "انتخاب مزرعه برای نمایش نقشه NDVI",
1328
- weak_farms['Farm_ID'].tolist(),
1329
- format_func=lambda x: weak_farms[weak_farms['Farm_ID'] == x]['Farm_Name'].iloc[0]
1330
- )
1331
-
1332
- if st.button("نمایش نقشه NDVI"):
1333
- with st.spinner("در حال تولید نقشه..."):
1334
- # استفاده از تاریخ آخرین اندازه‌گیری
1335
- farm_date = heights_df[heights_df['Farm_ID'] == selected_weak_farm].sort_values('Week', ascending=False)['Measurement_Date'].iloc[0]
1336
- ndvi_map = create_ndvi_map(selected_weak_farm, farm_date)
1337
-
1338
- if ndvi_map:
1339
- st.markdown('<div class="map-container">', unsafe_allow_html=True)
1340
- folium_static(ndvi_map, width=1200, height=500)
1341
- st.markdown('</div>', unsafe_allow_html=True)
1342
-
1343
- # تحلیل و پیشنهادات
1344
- st.markdown("---")
1345
- st.subheader("تحلیل عوامل مؤثر")
1346
-
1347
- # مقایسه سن کشت
1348
- avg_age_weak = weak_farms['Crop_Age'].mean()
1349
- avg_age_all = farms_df['Crop_Age'].mean()
1350
-
1351
- is_age_factor = avg_age_weak > avg_age_all + 0.5
1352
-
1353
- # مقایسه واریته‌ها
1354
- variety_impact = weak_farms['Variety'].value_counts(normalize=True).idxmax()
1355
-
1356
- # مقایسه کانال‌ها
1357
- channel_counts = weak_farms['Channel_ID'].value_counts()
1358
- problematic_channels = channel_counts[channel_counts > 2].index.tolist()
1359
-
1360
- col1, col2 = st.columns(2)
1361
-
1362
- with col1:
1363
- st.markdown("#### عوامل احتمالی")
1364
-
1365
- factors = []
1366
-
1367
- if is_age_factor:
1368
- factors.append("سن کشت بالا")
1369
-
1370
- if len(weak_by_variety) == 1:
1371
- factors.append(f"واریته ({variety_impact})")
1372
-
1373
- if problematic_channels:
1374
- channels_str = "، ".join(problematic_channels[:3])
1375
- factors.append(f"کانال‌های مشکل‌دار ({channels_str})")
1376
-
1377
- if not factors:
1378
- factors.append("عوامل متفرقه")
1379
-
1380
- for factor in factors:
1381
- st.markdown(f"- {factor}")
1382
-
1383
- with col2:
1384
- st.markdown("#### اقدامات پیشنهادی")
1385
-
1386
- st.markdown("""
1387
- - بازدید و بررسی میدانی مزارع ضعیف
1388
- - بررسی وضعیت آبیاری و زهکشی
1389
- - بررسی نیاز کودی و تغذیه
1390
- - بررسی آفات و بیماری‌های احتمالی
1391
- - برنامه‌ریزی برای جایگزینی مزارع با سن بالا
1392
- """)
1393
 
1394
- # بخش تحلیل داده‌ها
1395
  elif st.session_state.selected_tab == "تحلیل داده‌ها":
1396
  st.title("تحلیل پیشرفته داده‌ها")
1397
-
1398
- # انتخاب نوع تحلیل
1399
  analysis_type = st.selectbox(
1400
  "انتخاب نوع تحلیل",
1401
  ["تحلیل واریته‌ها", "تحلیل عوامل مؤثر بر رشد", "پیش‌بینی روند آینده", "مقایسه بخش‌های تولیدی"]
1402
  )
1403
-
1404
  st.markdown("---")
1405
-
1406
  if analysis_type == "تحلیل واریته‌ها":
1407
  st.subheader("مقایسه واریته‌های مختلف")
1408
-
1409
- # تحلیل واریته‌ها در هفته‌های مختلف
1410
  variety_weekly = report_with_details.groupby(['Variety', 'Week'])['Average_Height'].mean().reset_index()
1411
  variety_weekly['Average_Height'] = variety_weekly['Average_Height'].round().astype(int)
1412
-
1413
- # نمودار مقایسه‌ای واریته‌ها
1414
  fig = px.line(
1415
  variety_weekly,
1416
  x='Week',
@@ -1419,200 +717,6 @@ elif st.session_state.selected_tab == "تحلیل داده‌ها":
1419
  markers=True,
1420
  labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Variety': 'واریته'}
1421
  )
1422
-
1423
- fig.update_traces(line=dict(width=3), marker=dict(size=10))
1424
-
1425
- fig.update_layout(
1426
- height=500,
1427
- xaxis_title="هفته",
1428
- yaxis_title="میانگین ارتفاع (سانتی‌متر)",
1429
- font=dict(family="IRANSans"),
1430
- plot_bgcolor='rgba(0,0,0,0)'
1431
- )
1432
-
1433
- st.plotly_chart(fig, use_container_width=True)
1434
-
1435
- # تحلیل رشد تجمعی واریته‌ها
1436
- variety_growth = report_with_details.groupby(['Variety', 'Week'])['Growth_Change'].mean().reset_index()
1437
-
1438
- # محاسبه رشد تجمعی
1439
- varieties = variety_growth['Variety'].unique()
1440
-
1441
- growth_data = []
1442
-
1443
- for variety in varieties:
1444
- variety_data = variety_growth[variety_growth['Variety'] == variety].sort_values('Week')
1445
- cumulative_growth = 0
1446
-
1447
- for _, row in variety_data.iterrows():
1448
- week = row['Week']
1449
- growth = row['Growth_Change']
1450
- cumulative_growth += growth
1451
-
1452
- growth_data.append({
1453
- 'Variety': variety,
1454
- 'Week': week,
1455
- 'Cumulative_Growth': cumulative_growth
1456
- })
1457
-
1458
- cumulative_growth_df = pd.DataFrame(growth_data)
1459
-
1460
- # نمودار رشد تجمعی
1461
- st.subheader("رشد تجمعی واریته‌ها")
1462
-
1463
- fig = px.line(
1464
- cumulative_growth_df,
1465
- x='Week',
1466
- y='Cumulative_Growth',
1467
- color='Variety',
1468
- markers=True,
1469
- labels={'Week': 'هفته', 'Cumulative_Growth': 'رشد تجمعی (سانتی‌متر)', 'Variety': 'واریته'}
1470
- )
1471
-
1472
  fig.update_traces(line=dict(width=3), marker=dict(size=10))
1473
-
1474
- fig.update_layout(
1475
- height=500,
1476
- xaxis_title="هفته",
1477
- yaxis_title="رشد تجمعی (سانتی‌متر)",
1478
- font=dict(family="IRANSans"),
1479
- plot_bgcolor='rgba(0,0,0,0)'
1480
- )
1481
-
1482
- st.plotly_chart(fig, use_container_width=True)
1483
-
1484
- # تحلیل واریته‌ها بر اساس شاخص‌های دیگر
1485
- st.subheader("شاخص‌های رشد به تفکیک واریته")
1486
-
1487
- # میانگین تغییر رشد هفتگی
1488
- avg_growth_by_variety = report_with_details.groupby('Variety')['Growth_Change'].mean().reset_index()
1489
- avg_growth_by_variety['Growth_Change'] = avg_growth_by_variety['Growth_Change'].round(2)
1490
-
1491
- # نسبت وضعیت‌های مختلف
1492
- status_by_variety = report_with_details.groupby(['Variety', 'Growth_Status']).size().unstack(fill_value=0)
1493
-
1494
- # محاسبه درصد‌ها
1495
- status_by_variety['Total'] = status_by_variety.sum(axis=1)
1496
-
1497
- for status in ['خوب', 'متوسط', 'ضعیف']:
1498
- if status in status_by_variety.columns:
1499
- status_by_variety[f'Percent_{status}'] = (status_by_variety[status] / status_by_variety['Total'] * 100).round(1)
1500
-
1501
- # ترکیب داده‌ها
1502
- variety_metrics = pd.merge(avg_growth_by_variety, status_by_variety.reset_index(), on='Variety')
1503
-
1504
- col1, col2 = st.columns(2)
1505
-
1506
- with col1:
1507
- st.markdown("#### میانگین تغییر رشد هفتگی")
1508
-
1509
- fig = px.bar(
1510
- avg_growth_by_variety,
1511
- x='Variety',
1512
- y='Growth_Change',
1513
- color='Growth_Change',
1514
- color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
1515
- labels={'Variety': 'واریته', 'Growth_Change': 'میانگین تغییر هفتگی (سانتی‌متر)'}
1516
- )
1517
-
1518
- fig.update_layout(
1519
- height=350,
1520
- xaxis_title="واریته",
1521
- yaxis_title="میانگین تغییر هفتگی (سانتی‌متر)",
1522
- font=dict(family="IRANSans"),
1523
- plot_bgcolor='rgba(0,0,0,0)'
1524
- )
1525
-
1526
- st.plotly_chart(fig, use_container_width=True)
1527
-
1528
- with col2:
1529
- st.markdown("#### وضعیت رشد واریته‌ها")
1530
-
1531
- status_data = []
1532
-
1533
- for variety in varieties:
1534
- variety_data = status_by_variety.loc[variety]
1535
-
1536
- for status in ['خوب', 'متوسط', 'ضعیف']:
1537
- if status in status_by_variety.columns:
1538
- status_data.append({
1539
- 'Variety': variety,
1540
- 'Status': status,
1541
- 'Percent': variety_data[f'Percent_{status}'] if f'Percent_{status}' in variety_data.index else 0
1542
- })
1543
-
1544
- status_df = pd.DataFrame(status_data)
1545
-
1546
- fig = px.bar(
1547
- status_df,
1548
- x='Variety',
1549
- y='Percent',
1550
- color='Status',
1551
- color_discrete_map={'خوب': '#00c853', 'متوسط': '#ffd600', 'ضعیف': '#ff3d00'},
1552
- labels={'Variety': 'واریته', 'Percent': 'درصد', 'Status': 'وضعیت'}
1553
- )
1554
-
1555
- fig.update_layout(
1556
- height=350,
1557
- xaxis_title="واریته",
1558
- yaxis_title="درصد",
1559
- font=dict(family="IRANSans"),
1560
- plot_bgcolor='rgba(0,0,0,0)',
1561
- barmode='stack'
1562
- )
1563
-
1564
- st.plotly_chart(fig, use_container_width=True)
1565
-
1566
- # تحلیل تأثیر سن کشت بر واریته‌ها
1567
- st.subheader("تأثیر سن کشت بر واریته‌ها")
1568
-
1569
- age_variety_height = report_with_details[report_with_details['Week'] == latest_week].groupby(['Variety', 'Crop_Age'])['Average_Height'].mean().reset_index()
1570
-
1571
- fig = px.scatter(
1572
- age_variety_height,
1573
- x='Crop_Age',
1574
- y='Average_Height',
1575
- color='Variety',
1576
- size='Average_Height',
1577
- trendline='ols',
1578
- labels={'Crop_Age': 'سن کشت (سال)', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Variety': 'واریته'}
1579
- )
1580
-
1581
- fig.update_layout(
1582
- height=500,
1583
- xaxis_title="سن کشت (سال)",
1584
- yaxis_title="میانگین ارتفاع (سانتی‌متر)",
1585
- font=dict(family="IRANSans"),
1586
- plot_bgcolor='rgba(0,0,0,0)'
1587
- )
1588
-
1589
- st.plotly_chart(fig, use_container_width=True)
1590
-
1591
- elif analysis_type == "تحلیل عوامل مؤثر بر رشد":
1592
- st.subheader("بررسی عوامل مؤثر بر رشد مزارع")
1593
-
1594
- # عامل سن کشت
1595
- st.markdown("#### تأثیر سن کشت بر ارتفاع")
1596
-
1597
- age_height = report_with_details[report_with_details['Week'] == latest_week].groupby('Crop_Age')['Average_Height'].mean().reset_index()
1598
-
1599
- fig = px.bar(
1600
- age_height,
1601
- x='Crop_Age',
1602
- y='Average_Height',
1603
- color='Average_Height',
1604
- color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
1605
- labels={'Crop_Age': 'سن کشت (سال)', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
1606
- )
1607
-
1608
- fig.update_layout(
1609
- height=400,
1610
- xaxis_title="سن کشت (سال)",
1611
- yaxis_title="میانگین ارتفاع (سانتی‌متر)",
1612
- font=dict(family="IRANSans"),
1613
- plot_bgcolor='rgba(0,0,0,0)'
1614
- )
1615
-
1616
- st.plotly_chart(fig, use_container_width=True)
1617
-
1618
- # تحلیل توزیع وضعیت‌
 
1
+ import streamlit as st
2
  import pandas as pd
3
  import numpy as np
4
  import plotly.express as px
 
138
  color: white !important;
139
  }
140
 
 
141
  input[type="number"] {
142
  direction: ltr !important;
143
  text-align: left !important;
 
151
  text-align: left !important;
152
  }
153
 
 
154
  .map-container {
155
  height: 500px;
156
  width: 100%;
 
158
  box-shadow: 0 4px 10px rgba(0,0,0,0.1);
159
  overflow: hidden;
160
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  </style>
162
  """, unsafe_allow_html=True)
163
 
164
+ # تنظیم Earth Engine (بدون تغییر)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  @st.cache_resource
166
  def initialize_earth_engine():
167
  try:
 
168
  credentials_path = "ee-esmaeilkiani13877-cfdea6eaf411.json"
 
 
169
  if not os.path.exists(credentials_path):
 
170
  credentials_dict = {
171
  "type": "service_account",
172
  "project_id": "ee-esmaeilkiani13877",
 
182
  with open(credentials_path, 'w') as f:
183
  json.dump(credentials_dict, f)
184
 
 
185
  credentials = ee.ServiceAccountCredentials(
186
  "dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com",
187
  credentials_path
188
  )
 
189
  ee.Initialize(credentials)
190
  return True
191
  except Exception as e:
192
  st.error(f"خطا در اتصال به Earth Engine: {e}")
193
  return False
194
 
 
195
  earth_engine_initialized = initialize_earth_engine()
196
 
197
  # نمونه داده
198
  @st.cache_data
199
  def generate_sample_data():
 
200
  farm_data = {
201
  'Farm_ID': [f'{i:02d}-{j:02d}' for i in range(1, 5) for j in range(20, 30)],
202
  'Farm_Name': [f'مزرعه {i:02d}-{j:02d}' for i in range(1, 5) for j in range(20, 30)],
 
208
  'Variety': [np.random.choice(['CP69', 'CP73']) for _ in range(40)],
209
  'Crop_Age': [np.random.randint(1, 5) for _ in range(40)]
210
  }
 
211
  farms_df = pd.DataFrame(farm_data)
212
 
 
213
  height_data = []
214
  today = datetime.now()
 
 
215
  for week in range(1, 6):
216
  date = today - timedelta(days=(5-week)*7)
217
  for farm_id in farms_df['Farm_ID']:
 
218
  stations = [np.random.randint(150, 220) for _ in range(5)]
 
219
  avg_height = int(np.mean(stations))
 
 
220
  groundwater = [np.random.randint(50, 100) for _ in range(2)]
 
 
221
  sheath_moisture = np.random.randint(60, 90)
222
  nitrogen = np.random.randint(20, 40)
 
223
  height_data.append({
224
  'Measurement_ID': f"{farm_id}-W{week}",
225
  'Farm_ID': farm_id,
 
236
  'Sheath_Moisture': sheath_moisture,
237
  'Nitrogen': nitrogen
238
  })
 
239
  heights_df = pd.DataFrame(height_data)
240
 
 
241
  weekly_report_data = []
 
242
  for farm_id in farms_df['Farm_ID']:
243
  farm_heights = heights_df[heights_df['Farm_ID'] == farm_id].sort_values('Week')
 
244
  for week in range(1, 6):
245
  current_week_data = farm_heights[farm_heights['Week'] == week]
246
  if current_week_data.empty:
247
  continue
 
248
  current_height = current_week_data['Height'].values[0]
 
 
249
  if week > 1:
250
  prev_week_data = farm_heights[farm_heights['Week'] == week-1]
251
  if not prev_week_data.empty:
 
255
  growth_change = 0
256
  else:
257
  growth_change = 0
 
 
258
  all_farms_this_week = heights_df[heights_df['Week'] == week]
259
  avg_height_all_farms = all_farms_this_week['Height'].mean()
 
 
260
  if current_height > avg_height_all_farms + 5:
261
  growth_status = 'خوب'
262
  elif current_height < avg_height_all_farms - 5:
263
  growth_status = 'ضعیف'
264
  else:
265
  growth_status = 'متوسط'
 
266
  weekly_report_data.append({
267
  'Report_ID': f"{farm_id}-R{week}",
268
  'Farm_ID': farm_id,
 
272
  'Growth_Status': growth_status,
273
  'Regional_Average': int(avg_height_all_farms)
274
  })
 
275
  weekly_report_df = pd.DataFrame(weekly_report_data)
276
 
277
  return farms_df, heights_df, weekly_report_df
278
 
 
279
  farms_df, heights_df, weekly_report_df = generate_sample_data()
 
 
280
  report_with_details = pd.merge(weekly_report_df, farms_df, on='Farm_ID')
281
  latest_week = heights_df['Week'].max()
282
  latest_reports = report_with_details[report_with_details['Week'] == latest_week]
283
 
284
  # مدیریت وضعیت برنامه
285
  if 'selected_tab' not in st.session_state:
286
+ st.session_state.selected_tab = "ورود اطلاعات"
 
287
  if 'selected_day' not in st.session_state:
288
  st.session_state.selected_day = "شنبه"
 
 
289
  if 'farm_data' not in st.session_state:
290
  st.session_state.farm_data = {}
 
 
291
  if 'ndvi_map' not in st.session_state:
292
  st.session_state.ndvi_map = None
293
 
294
  # تابع برای محاسبه میانگین ارتفاع
295
+ def calculate_average_height(row):
296
+ stations = [row[f'ایستگاه {i}'] for i in range(1, 6) if f'ایستگاه {i}' in row and row[f'ایستگاه {i}'] is not None]
297
  valid_stations = [s for s in stations if s > 0]
298
+ return sum(valid_stations) / len(valid_stations) if valid_stations else 0
 
 
299
 
300
+ # تابع برای ایجاد نقشه NDVI (بدون تغییر)
301
  def create_ndvi_map(farm_id=None, date=None):
302
  if not earth_engine_initialized:
303
  st.warning("اتصال به Earth Engine برقرار نشد. نقشه NDVI نمایش داده نمی‌شود.")
304
  return None
 
305
  try:
 
306
  center_point = [31.3183, 48.6706]
 
 
307
  m = folium.Map(location=center_point, zoom_start=10)
 
 
308
  if farm_id:
 
 
309
  farm = farms_df[farms_df['Farm_ID'] == farm_id].iloc[0]
 
 
310
  lat = 31.3183 + (int(farm_id.split('-')[0]) - 1) * 0.05
311
  lon = 48.6706 + (int(farm_id.split('-')[1]) - 20) * 0.05
 
 
312
  if date:
313
+ date_obj = datetime.strptime(date, '%Y-%m-%d')
314
+ start_date = date_obj - timedelta(days=5)
315
+ end_date = date_obj + timedelta(days=5)
316
+ start_date_str = start_date.strftime('%Y-%m-%d')
317
+ end_date_str = end_date.strftime('%Y-%m-%d')
 
 
 
 
 
 
 
 
318
  else:
 
319
  start_date_str = '2023-01-01'
320
  end_date_str = '2023-12-31'
321
+ region = ee.Geometry.Point([lon, lat]).buffer(1000)
 
 
 
 
322
  s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
323
  .filterDate(start_date_str, end_date_str) \
324
  .filterBounds(region) \
325
  .sort('CLOUD_COVERAGE_ASSESSMENT') \
326
  .first()
 
 
327
  ndvi = s2.normalizedDifference(['B8', 'B4']).rename('NDVI')
 
 
328
  ndvi_viz = {
329
  'min': -0.2,
330
  'max': 0.8,
331
  'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850']
332
  }
 
 
333
  map_id_dict = ee.Image(ndvi).getMapId(ndvi_viz)
334
  folium.TileLayer(
335
  tiles=map_id_dict['tile_fetcher'].url_format,
 
338
  overlay=True,
339
  control=True
340
  ).add_to(m)
 
 
341
  folium.Marker(
342
  location=[lat, lon],
343
  popup=f'مزرعه {farm_id}',
344
  icon=folium.Icon(color='green', icon='leaf')
345
  ).add_to(m)
 
 
346
  m.fit_bounds([[lat-0.05, lon-0.05], [lat+0.05, lon+0.05]])
 
 
347
  folium.LayerControl().add_to(m)
 
348
  return m
 
349
  except Exception as e:
350
  st.error(f"خطا در ایجاد نقشه NDVI: {e}")
351
  return None
352
 
353
+ # منوی کناری (بدون تغییر)
354
  with st.sidebar:
355
  st.image("https://via.placeholder.com/150x150.png?text=لوگو", width=150)
356
  st.title("سامانه پایش مزارع نیشکر")
357
  st.subheader("شرکت کشت و صنعت نیشکر دهخدا")
 
358
  menu = st.selectbox(
359
  "انتخاب صفحه",
360
  ["داشبورد", "ورود اطلاعات", "گزارش‌گیری", "تحلیل داده‌ها", "تنظیمات"]
361
  )
 
362
  st.session_state.selected_tab = menu
 
363
  st.markdown("---")
364
  st.markdown("### راهنمای وضعیت رشد")
 
365
  col1, col2 = st.columns(2)
366
  with col1:
367
  st.markdown('<div class="status-good">خوب</div>', unsafe_allow_html=True)
368
  st.markdown('<div class="status-medium">متوسط</div>', unsafe_allow_html=True)
369
  st.markdown('<div class="status-bad">ضعیف</div>', unsafe_allow_html=True)
 
370
  with col2:
371
  st.write("بالاتر از میانگین")
372
  st.write("نزدیک به میانگین")
373
  st.write("پایین‌تر از میانگین")
 
374
  st.markdown("---")
375
  st.info("نسخه آزمایشی 1.0")
376
 
377
+ # داشبورد (بدون تغییر)
378
  if st.session_state.selected_tab == "داشبورد":
379
  st.title("داشبورد پایش مزارع نیشکر")
 
 
380
  st.subheader("وضعیت کلی مزارع")
 
 
381
  status_counts = latest_reports['Growth_Status'].value_counts()
382
  good_count = status_counts.get('خوب', 0)
383
  medium_count = status_counts.get('متوسط', 0)
384
  bad_count = status_counts.get('ضعیف', 0)
 
385
  col1, col2, col3, col4 = st.columns(4)
 
386
  with col1:
387
+ st.markdown('<div class="metric-card metric-green">'f'<h2>{good_count}</h2>''<p>مزارع با وضعیت خوب</p>''</div>', unsafe_allow_html=True)
 
 
 
 
388
  with col2:
389
+ st.markdown('<div class="metric-card metric-yellow">'f'<h2>{medium_count}</h2>''<p>مزارع با وضعیت متوسط</p>''</div>', unsafe_allow_html=True)
 
 
 
 
390
  with col3:
391
+ st.markdown('<div class="metric-card metric-red">'f'<h2>{bad_count}</h2>''<p>مزارع با وضعیت ضعیف</p>''</div>', unsafe_allow_html=True)
 
 
 
 
392
  with col4:
393
  avg_height = int(latest_reports['Average_Height'].mean())
394
+ st.markdown('<div class="metric-card metric-blue">'f'<h2>{avg_height} سانتی‌متر</h2>''<p>میانگین ارتفاع هفته جاری</p>''</div>', unsafe_allow_html=True)
 
 
 
 
395
  st.markdown("---")
 
 
396
  col1, col2 = st.columns(2)
 
397
  with col1:
398
  st.subheader("میانگین ارتفاع به تفکیک اداره")
 
399
  admin_heights = latest_reports.groupby('Administration_Name')['Average_Height'].mean().reset_index()
400
  admin_heights['Average_Height'] = admin_heights['Average_Height'].astype(int)
 
401
  fig = px.bar(
402
  admin_heights,
403
  x='Administration_Name',
 
406
  color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
407
  labels={'Administration_Name': 'اداره', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
408
  )
409
+ fig.update_layout(height=400, xaxis_title="اداره", yaxis_title="میانگین ارتفاع (سانتی‌متر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
 
 
 
 
 
 
 
 
410
  st.plotly_chart(fig, use_container_width=True)
 
411
  with col2:
412
  st.subheader("مقایسه واریته‌ها")
 
413
  variety_heights = latest_reports.groupby('Variety')['Average_Height'].mean().reset_index()
414
  variety_heights['Average_Height'] = variety_heights['Average_Height'].astype(int)
 
415
  fig = px.bar(
416
  variety_heights,
417
  x='Variety',
 
420
  color_discrete_sequence=["#00c853", "#2196f3"],
421
  labels={'Variety': 'واریته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
422
  )
423
+ fig.update_layout(height=400, xaxis_title="واریته", yaxis_title="میانگین ارتفاع (سانتی‌متر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
 
 
 
 
 
 
 
 
424
  st.plotly_chart(fig, use_container_width=True)
 
 
425
  st.subheader("روند میانگین ارتفاع هفتگی")
 
426
  weekly_avg = report_with_details.groupby('Week')['Average_Height'].mean().reset_index()
427
  weekly_avg['Average_Height'] = weekly_avg['Average_Height'].astype(int)
 
428
  fig = px.line(
429
  weekly_avg,
430
  x='Week',
 
433
  line_shape='spline',
434
  labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
435
  )
 
436
  fig.update_traces(line=dict(width=3, color="#1e6b45"), marker=dict(size=10))
437
+ fig.update_layout(height=400, xaxis_title="هفته", yaxis_title="میانگین ارتفاع (سانتی‌متر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
 
 
 
 
 
 
 
 
438
  st.plotly_chart(fig, use_container_width=True)
 
 
439
  st.markdown("---")
440
  st.subheader("نقشه NDVI")
 
441
  col1, col2 = st.columns(2)
 
442
  with col1:
443
  selected_farm_for_map = st.selectbox(
444
  "انتخاب مزرعه",
445
  farms_df['Farm_ID'].tolist(),
446
  format_func=lambda x: farms_df[farms_df['Farm_ID'] == x]['Farm_Name'].iloc[0]
447
  )
 
448
  with col2:
 
449
  latest_date = heights_df[heights_df['Farm_ID'] == selected_farm_for_map].sort_values('Week', ascending=False)['Measurement_Date'].iloc[0]
450
  selected_date_for_map = st.date_input(
451
  "انتخاب تاریخ",
452
  value=datetime.strptime(latest_date, '%Y-%m-%d'),
453
  max_value=datetime.now()
454
  )
 
 
455
  if st.button("نمایش نقشه NDVI"):
456
  with st.spinner("در حال تولید نقشه..."):
457
  ndvi_map = create_ndvi_map(selected_farm_for_map, selected_date_for_map.strftime('%Y-%m-%d'))
458
  st.session_state.ndvi_map = ndvi_map
 
 
459
  if st.session_state.ndvi_map:
460
  st.markdown('<div class="map-container">', unsafe_allow_html=True)
461
  folium_static(st.session_state.ndvi_map, width=1200, height=500)
462
  st.markdown('</div>', unsafe_allow_html=True)
 
 
463
  farm_info = farms_df[farms_df['Farm_ID'] == selected_farm_for_map].iloc[0]
464
  farm_measurements = heights_df[(heights_df['Farm_ID'] == selected_farm_for_map) &
465
  (heights_df['Measurement_Date'] == latest_date)].iloc[0]
 
466
  st.markdown(f"""
467
  ### اطلاعات مزرعه {farm_info['Farm_Name']}
 
468
  **اداره:** {farm_info['Administration_Name']} | **کانال:** {farm_info['Channel_ID']} | **واریته:** {farm_info['Variety']} | **سن کشت:** {farm_info['Crop_Age']} سال
 
469
  **آخرین اندازه‌گیری:** {latest_date}
470
  - **میانگین ارتفاع:** {farm_measurements['Height']} سانتی‌متر
471
  - **رطوبت غلاف:** {farm_measurements['Sheath_Moisture']}%
472
  - **نیتروژن:** {farm_measurements['Nitrogen']}%
473
  """)
474
  else:
475
+ st.info("برای نمایش نقشه NDVI، یک مزرعه و تاریخ انتخاب کنید.")
 
 
476
  st.markdown("---")
477
  st.subheader("مزارع با بیشترین و کمترین رشد")
 
478
  col1, col2 = st.columns(2)
 
 
479
  with col1:
480
  st.markdown("#### بیشترین رشد هفتگی")
481
  top_growth = report_with_details[report_with_details['Week'] == latest_week].sort_values('Growth_Change', ascending=False).head(5)
 
482
  for _, row in top_growth.iterrows():
483
  growth_color = "green" if row['Growth_Change'] > 0 else "red"
484
  st.markdown(f"""
 
490
  </p>
491
  </div>
492
  """, unsafe_allow_html=True)
 
 
493
  with col2:
494
  st.markdown("#### کمترین رشد هفتگی")
495
  bottom_growth = report_with_details[report_with_details['Week'] == latest_week].sort_values('Growth_Change').head(5)
 
496
  for _, row in bottom_growth.iterrows():
497
  growth_color = "green" if row['Growth_Change'] > 0 else "red"
498
  st.markdown(f"""
 
505
  </div>
506
  """, unsafe_allow_html=True)
507
 
508
+ # بخش ورود اطلاعات (بهبود یافته)
509
  elif st.session_state.selected_tab == "ورود اطلاعات":
510
  st.title("ورود اطلاعات روزانه")
511
+
512
  # انتخاب روز هفته
513
  days_of_week = ["شنبه", "یکشنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنجشنبه"]
514
  selected_day = st.selectbox("انتخاب روز", days_of_week, index=days_of_week.index(st.session_state.selected_day))
515
  st.session_state.selected_day = selected_day
516
+
517
  # انتخاب اداره
518
  selected_admin = st.selectbox("انتخاب اداره", sorted(farms_df['Administration_ID'].unique()))
519
+
520
+ # فیلتر مزارع بر اساس اداره انتخاب‌شده
521
+ filtered_farms = farms_df[farms_df['Administration_ID'] == selected_admin].copy()
522
+
523
+ # تعریف ستون‌های جدول
524
+ columns = {
525
+ 'مزرعه': filtered_farms['Farm_Name'],
526
+ 'ایستگاه 1': [0] * len(filtered_farms),
527
+ 'ایستگاه 2': [0] * len(filtered_farms),
528
+ 'ایستگاه 3': [0] * len(filtered_farms),
529
+ 'ایستگاه 4': [0] * len(filtered_farms),
530
+ 'ایستگاه 5': [0] * len(filtered_farms),
531
+ 'چاهک 1': [0] * len(filtered_farms),
532
+ 'چاهک 2': [0] * len(filtered_farms),
533
+ 'رطوبت غلاف': [0] * len(filtered_farms),
534
+ 'نیتروژن': [0] * len(filtered_farms),
535
+ 'میانگین ارتفاع': [0] * len(filtered_farms)
536
+ }
537
+ data_df = pd.DataFrame(columns)
538
+
539
+ # بارگذاری داده‌های قبلی (اگر وجود داشته باشد)
540
+ if selected_day in st.session_state.farm_data:
541
+ for idx, farm in filtered_farms.iterrows():
542
+ farm_id = farm['Farm_ID']
543
+ if farm_id in st.session_state.farm_data[selected_day]:
544
+ saved_data = st.session_state.farm_data[selected_day][farm_id]
545
+ for col in ['ایستگاه 1', 'ایستگاه 2', 'ایستگاه 3', 'ایستگاه 4', 'ایستگاه 5', 'چاهک 1', 'چاهک 2', 'رطوبت غلاف', 'نیتروژن']:
546
+ if col in saved_data:
547
+ data_df.at[idx, col] = saved_data[col]
548
+ data_df.at[idx, 'میانگین ارتفاع'] = calculate_average_height(data_df.iloc[idx])
549
+
550
+ # نمایش جدول ورود داده‌ها
551
  st.subheader(f"ورود داده‌های روز {selected_day} - اداره {selected_admin}")
552
+ edited_df = st.data_editor(
553
+ data_df,
554
+ num_rows="fixed",
555
+ key=f"data_editor_{selected_day}_{selected_admin}",
556
+ hide_index=True,
557
+ column_config={
558
+ "مزرعه": st.column_config.TextColumn("مزرعه", disabled=True),
559
+ "ایستگاه 1": st.column_config.NumberColumn("ایستگاه 1", min_value=0, max_value=300, step=1),
560
+ "ایستگاه 2": st.column_config.NumberColumn("ایستگاه 2", min_value=0, max_value=300, step=1),
561
+ "ایستگاه 3": st.column_config.NumberColumn("ایستگاه 3", min_value=0, max_value=300, step=1),
562
+ "ایستگاه 4": st.column_config.NumberColumn("ایستگاه 4", min_value=0, max_value=300, step=1),
563
+ "ایستگاه 5": st.column_config.NumberColumn("ایستگاه 5", min_value=0, max_value=300, step=1),
564
+ "چاهک 1": st.column_config.NumberColumn("چاهک 1", min_value=0, max_value=300, step=1),
565
+ "چاهک 2": st.column_config.NumberColumn("چاهک 2", min_value=0, max_value=300, step=1),
566
+ "رطوبت غلاف": st.column_config.NumberColumn("رطوبت غلاف", min_value=0, max_value=100, step=1),
567
+ "نیتروژن": st.column_config.NumberColumn("نیتروژن", min_value=0, max_value=100, step=1),
568
+ "میانگین ارتفاع": st.column_config.NumberColumn("میانگین ارتفاع", disabled=True)
569
+ },
570
+ use_container_width=True
571
+ )
572
+
573
+ # محاسبه داینامیک میانگین ارتفاع
574
+ for idx, row in edited_df.iterrows():
575
+ edited_df.at[idx, 'میانگین ارتفاع'] = calculate_average_height(row)
576
+
577
+ # نمایش خلاصه داینامیک
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578
  st.markdown("---")
579
+ st.subheader("خلاصه وضعیت داده‌های واردشده")
580
+ col1, col2, col3, col4 = st.columns(4)
581
+ with col1:
582
+ total_avg = edited_df['میانگین ارتفاع'].mean()
583
+ st.metric("میانگین کل ارتفاع", f"{total_avg:.1f} سانتی‌متر" if total_avg > 0 else "0")
584
+ with col2:
585
+ farms_entered = len(edited_df[edited_df['میانگین ارتفاع'] > 0])
586
+ st.metric("تعداد مزارع واردشده", f"{farms_entered} از {len(filtered_farms)}")
587
+ with col3:
588
+ avg_moisture = edited_df['رطوبت غلاف'].mean()
589
+ st.metric("میانگین رطوبت غلاف", f"{avg_moisture:.1f}%" if avg_moisture > 0 else "0")
590
+ with col4:
591
+ avg_nitrogen = edited_df['نیتروژن'].mean()
592
+ st.metric("میانگین نیتروژن", f"{avg_nitrogen:.1f}%" if avg_nitrogen > 0 else "0")
593
+
594
+ # دکمه ذخیره
595
+ if st.button("ذخیره اطلاعات"):
596
+ if selected_day not in st.session_state.farm_data:
597
+ st.session_state.farm_data[selected_day] = {}
598
+ for idx, row in edited_df.iterrows():
599
+ farm_id = filtered_farms.iloc[idx]['Farm_ID']
600
+ st.session_state.farm_data[selected_day][farm_id] = {
601
+ 'ایستگاه 1': row['ایستگاه 1'],
602
+ 'ایستگاه 2': row['ایستگاه 2'],
603
+ 'ایستگاه 3': row['ایستگاه 3'],
604
+ 'ایستگاه 4': row['ایستگاه 4'],
605
+ 'ایستگاه 5': row['ایستگاه 5'],
606
+ 'چاهک 1': row['چاهک 1'],
607
+ 'چاهک 2': row['چاهک 2'],
608
+ 'رطوبت غلاف': row['رطوبت غلاف'],
609
+ 'نیتروژن': row['نیتروژن'],
610
+ 'میانگین ارتفاع': row['میانگین ارتفاع']
611
+ }
612
+ st.success("داده‌ها با موفقیت ذخیره شدند.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
613
 
614
+ # گزارش‌گیری (بدون تغییر)
615
  elif st.session_state.selected_tab == "گزارش‌گیری":
616
  st.title("گزارش‌های هفتگی")
 
 
617
  report_type = st.radio(
618
  "نوع گزارش",
619
  ["گزارش به تفکیک مزرعه", "گزارش به تفکیک اداره", "گزارش به تفکیک کانال", "مزارع با وضعیت ضعیف"]
620
  )
 
621
  st.markdown("---")
 
622
  if report_type == "گزارش به تفکیک مزرعه":
623
  st.subheader("گزارش هفتگی به تفکیک مزرعه")
 
 
624
  selected_admin = st.selectbox("انتخاب اداره", sorted(farms_df['Administration_ID'].unique()), key="admin_select_farm_report")
 
 
625
  filtered_farms = farms_df[farms_df['Administration_ID'] == selected_admin]
 
 
626
  selected_farm_id = st.selectbox(
627
  "انتخاب مزرعه",
628
  filtered_farms['Farm_ID'].tolist(),
629
  format_func=lambda x: filtered_farms[filtered_farms['Farm_ID'] == x]['Farm_Name'].iloc[0]
630
  )
 
 
631
  farm_reports = weekly_report_df[weekly_report_df['Farm_ID'] == selected_farm_id].sort_values('Week')
 
632
  if not farm_reports.empty:
 
633
  farm_info = filtered_farms[filtered_farms['Farm_ID'] == selected_farm_id].iloc[0]
 
634
  col1, col2, col3 = st.columns(3)
635
  col1.metric("واریته", farm_info['Variety'])
636
  col2.metric("سن کشت", f"{farm_info['Crop_Age']} سال")
637
  col3.metric("مساحت", f"{farm_info['Area']} هکتار")
 
638
  st.markdown("---")
 
 
639
  st.subheader("روند رشد هفتگی")
 
640
  fig = go.Figure()
 
 
641
  fig.add_trace(go.Scatter(
642
  x=farm_reports['Week'],
643
  y=farm_reports['Regional_Average'],
 
646
  line=dict(color='gray', width=2, dash='dot'),
647
  marker=dict(size=8)
648
  ))
 
 
649
  fig.add_trace(go.Scatter(
650
  x=farm_reports['Week'],
651
  y=farm_reports['Average_Height'],
 
654
  line=dict(color='#1e6b45', width=3),
655
  marker=dict(size=10)
656
  ))
657
+ fig.update_layout(height=400, xaxis_title="هفته", yaxis_title="ارتفاع (سانتی‌متر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
 
 
 
 
 
 
 
 
658
  st.plotly_chart(fig, use_container_width=True)
 
 
659
  st.subheader("داده‌های هفتگی")
 
660
  report_table = pd.DataFrame({
661
  'هفته': farm_reports['Week'],
662
+ 'تاریخ': farm_reports['Week'].apply(lambda x: heights_df[heights_df['Week'] == x]['Measurement_Date'].iloc[0]),
663
  'ارتفاع': farm_reports['Average_Height'],
664
  'میانگین منطقه': farm_reports['Regional_Average'],
665
  'تغییرات': farm_reports['Growth_Change'],
666
  'وضعیت': farm_reports['Growth_Status']
667
  })
 
 
668
  def status_with_color(status):
669
  colors = {'خوب': '#00c853', 'متوسط': '#ffd600', 'ضعیف': '#ff3d00'}
670
  return f'<span style="color: {colors.get(status, "black")};">{status}</span>'
 
671
  report_table['وضعیت'] = report_table['وضعیت'].apply(status_with_color)
 
672
  st.markdown(report_table.to_html(escape=False, index=False), unsafe_allow_html=True)
 
 
673
  st.subheader("نقشه NDVI مزرعه")
 
674
  if st.button("نمایش نقشه NDVI این مزرعه"):
675
  with st.spinner("در حال تولید نقشه..."):
676
+ latest_date = farm_reports.iloc[-1]['Week']
677
+ farm_date = heights_df[heights_df['Week'] == latest_date]['Measurement_Date'].iloc[0]
678
+ ndvi_map = create_ndvi_map(selected_farm_id, farm_date)
 
679
  if ndvi_map:
680
  st.markdown('<div class="map-container">', unsafe_allow_html=True)
681
  folium_static(ndvi_map, width=1200, height=500)
682
  st.markdown('</div>', unsafe_allow_html=True)
 
 
683
  st.markdown("---")
684
  st.subheader("توصیه‌های کارشناسی")
 
685
  latest_report = farm_reports.iloc[-1]
686
  latest_status = latest_report['Growth_Status']
 
687
  if latest_status == 'خوب':
688
  st.success("وضعیت رشد مزرعه مطلوب است. ادامه برنامه فعلی توصیه می‌شود.")
689
  elif latest_status == 'متوسط':
 
696
  - بررسی آفات و بیماری‌های احتمالی
697
  - بررسی وضعیت زهکشی مزرعه
698
  """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
699
 
700
+ # تحلیل داده‌ها (بدون تغییر)
701
  elif st.session_state.selected_tab == "تحلیل داده‌ها":
702
  st.title("تحلیل پیشرفته داده‌ها")
 
 
703
  analysis_type = st.selectbox(
704
  "انتخاب نوع تحلیل",
705
  ["تحلیل واریته‌ها", "تحلیل عوامل مؤثر بر رشد", "پیش‌بینی روند آینده", "مقایسه بخش‌های تولیدی"]
706
  )
 
707
  st.markdown("---")
 
708
  if analysis_type == "تحلیل واریته‌ها":
709
  st.subheader("مقایسه واریته‌های مختلف")
 
 
710
  variety_weekly = report_with_details.groupby(['Variety', 'Week'])['Average_Height'].mean().reset_index()
711
  variety_weekly['Average_Height'] = variety_weekly['Average_Height'].round().astype(int)
 
 
712
  fig = px.line(
713
  variety_weekly,
714
  x='Week',
 
717
  markers=True,
718
  labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Variety': 'واریته'}
719
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
720
  fig.update_traces(line=dict(width=3), marker=dict(size=10))
721
+ fig.update_layout(height=500, xaxis_title="هفته", yaxis_title="میانگین ارتفاع (سانتی‌متر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
722
+ st.plotly_chart(fig, use_container_width=True)