Esmaeilkianii commited on
Commit
1663e7a
·
verified ·
1 Parent(s): 6334bc6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +172 -337
app.py CHANGED
@@ -28,11 +28,12 @@ from streamlit_extras.add_vertical_space import add_vertical_space
28
  from streamlit_card import card
29
  import pydeck as pdk
30
  import math
 
31
 
32
  # Page configuration with custom theme
33
  st.set_page_config(
34
- page_title="سامانه هوشمند پایش مزارع نیشکر دهخدا",
35
- page_icon="🌿",
36
  layout="wide",
37
  initial_sidebar_state="expanded"
38
  )
@@ -195,34 +196,77 @@ def initialize_earth_engine():
195
 
196
  # Load data
197
  @st.cache_data
198
- def load_farm_data():
199
  try:
200
- df = pd.read_csv("پایگاه داده (1).csv")
201
- return df
 
 
 
 
 
 
202
  except Exception as e:
203
- st.error(f"خطا در بارگذاری داده‌های مزارع: {e}")
204
  return pd.DataFrame()
205
 
206
- @st.cache_data
207
- def load_coordinates_data():
208
  try:
209
- df = pd.read_csv("farm_coordinates.csv")
210
- return df
211
- except Exception as e:
212
- st.error(f"خطا در بارگذاری داده‌های مختصات: {e}")
213
- return pd.DataFrame()
 
 
 
 
214
 
215
- # Load animation JSON
216
- @st.cache_data
217
- def load_lottie_url(url: str):
218
- r = requests.get(url)
219
- if r.status_code != 200:
220
- return None
221
- return r.json()
222
 
223
- # Create Earth Engine map
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  def create_ee_map(farm_id, date_str, layer_type="NDVI"):
225
  try:
 
226
  farm_row = coordinates_df[coordinates_df['نام'] == farm_id].iloc[0]
227
  lat, lon = farm_row['عرض جغرافیایی'], farm_row['طول جغرافیایی']
228
 
@@ -318,9 +362,10 @@ def create_ee_map(farm_id, date_str, layer_type="NDVI"):
318
  st.error(f"خطا در ایجاد نقشه: {e}")
319
  return None
320
 
321
- # Calculate statistics for a farm using GEE
322
  def calculate_farm_stats(farm_id, date_str, layer_type="NDVI"):
323
  try:
 
324
  farm_row = coordinates_df[coordinates_df['نام'] == farm_id].iloc[0]
325
  lat, lon = farm_row['عرض جغرافیایی'], farm_row['طول جغرافیایی']
326
  region = ee.Geometry.Point([lon, lat]).buffer(1500)
@@ -372,96 +417,27 @@ def calculate_farm_stats(farm_id, date_str, layer_type="NDVI"):
372
  st.error(f"خطا در محاسبه آمار: {e}")
373
  return None
374
 
375
- # تابع برای محاسبات خودکار
376
- def calculate_metrics(df):
377
- # محاسبه میانگین ارتف��ع مزرعه
378
- station_cols = ['ایستگاه 1', 'ایستگاه 2', 'ایستگاه 3', 'ایستگاه 4', 'ایستگاه 5']
379
- df['ارتفاع هفته جاری مزرعه'] = df[station_cols].apply(
380
- lambda row: np.mean([x for x in row if x != 0 and pd.notnull(x)]) if any(x != 0 and pd.notnull(x) for x in row) else 0, axis=1
381
- )
382
-
383
- # محاسبه رشد هفتگی (فرض می‌کنیم ستون "هفته" وجود دارد)
384
- df['رشد هفته جاری'] = df['ارتفاع هفته جاری مزرعه'] - df['ارتفاع هفته گذشته مزرعه'].fillna(0)
385
-
386
- # محاسبه انحراف نیتروژن
387
- df['نیتروژن انحراف'] = df['نیتروژن فعلی'] - df['نیتروژن استاندارد فعلی'].fillna(0)
388
-
389
- # محاسبه انحراف رطوبت
390
- df['رطوبت انحراف'] = df['رطوبت غلاف فعلی'] - df['رطوبت استاندارد فعلی'].fillna(0)
391
-
392
- # محاسبه نسبت مساحت کراپ لاگ (فرض بر این است که مساحت کراپ لاگ برابر با مساحت زیرمجموعه است)
393
- df['نسبت مساحت کراپ لاگ'] = df['مساحت زیرمجموعه'] / df['مساحت داشت'].replace(0, np.nan)
394
-
395
- return df
396
-
397
- # تابع برای تولید گزارش PDF
398
- def generate_pdf_report(df, report_type, week=None, day=None):
399
- pdf = FPDF()
400
- pdf.add_page()
401
- pdf.add_font('Vazir', '', 'Vazirmatn-Regular.ttf', uni=True)
402
- pdf.set_font('Vazir', '', 12)
403
-
404
- if report_type == "روزانه":
405
- title = f"گزارش روزانه - تاریخ: {day}"
406
- elif report_type == "هفتگی":
407
- title = f"گزارش هفتگی - هفته: {week}"
408
- else:
409
- title = "گزارش تجمیعی"
410
-
411
- pdf.cell(200, 10, title, ln=True, align='C')
412
- pdf.ln(10)
413
-
414
- # سرستون‌ها
415
- cols = df.columns.tolist()
416
- pdf.set_font('Vazir', '', 10)
417
- for col in cols:
418
- pdf.cell(40, 10, col, border=1)
419
- pdf.ln()
420
-
421
- # داده‌ها
422
- for index, row in df.iterrows():
423
- for col in cols:
424
- pdf.cell(40, 10, str(row[col]) if pd.notnull(row[col]) else "0", border=1)
425
- pdf.ln()
426
-
427
- # ذخیره PDF در حافظه
428
- pdf_output = pdf.output(dest='S').encode('latin1')
429
- return pdf_output
430
-
431
- # Initialize Earth Engine
432
- ee_initialized = initialize_earth_engine()
433
-
434
  # Load data
435
- farm_df = load_farm_data()
436
- coordinates_df = load_coordinates_data()
437
 
438
- # Load animations
439
- lottie_farm = load_lottie_url('https://assets5.lottiefiles.com/packages/lf20_ystsffqy.json')
440
- lottie_analysis = load_lottie_url('https://assets3.lottiefiles.com/packages/lf20_qp1q7mct.json')
441
- lottie_report = load_lottie_url('https://assets9.lottiefiles.com/packages/lf20_vwcugezu.json')
442
-
443
- # Create session state for storing data with Persian column names
444
- if 'heights_df' not in st.session_state:
445
- st.session_state.heights_df = pd.DataFrame(columns=[
446
- 'ردیف', 'نماینده', 'کانال', 'اداره', 'تولید', 'مساحت داشت', 'مساحت زیرمجموعه', 'واریته', 'سن',
447
- 'ایستگاه 1', 'ایستگاه 2', 'ایستگاه 3', 'ایستگاه 4', 'ایستگاه 5', 'ارتفاع هفته جاری مزرعه',
448
- 'ارتفاع هفته گذشته مزرعه', 'رشد هفته جاری', 'رشد هفته گذشته', 'نیتروژن فعلی', 'نیتروژن استاندارد فعلی',
449
- 'نیتروژن قبلی', 'نیتروژن استاندارد قبلی', 'رطوبت غلاف فعلی', 'رطوبت استاندارد فعلی', 'رطوبت غلاف قبلی',
450
- 'رطوبت استاندارد قبلی', 'چاهک 1', 'تاریخ قرائت', 'چاهک 2', 'تاریخ قرائت2', 'نیتروژن انحراف', 'رطوبت انحراف', 'نسبت مساحت کراپ لاگ'
451
  ])
452
 
453
  # Main header
454
  st.markdown('<div class="main-header">', unsafe_allow_html=True)
455
- st.markdown('<h1>سامانه هوشمند پایش مزارع نیشکر دهخدا</h1>', unsafe_allow_html=True)
456
- st.markdown('<p>پلتفرم جامع مدیریت، پایش و تحلیل داده‌های مزارع نیشکر با استفاده از تصاویر ماهواره‌ای و هوش مصنوعی</p>', unsafe_allow_html=True)
457
  st.markdown('</div>', unsafe_allow_html=True)
458
 
459
  # Navigation menu
460
  selected = option_menu(
461
  menu_title=None,
462
- options=["داشبورد", "نقشه مزارع", "ورود اطلاعات", "تحلیل داده‌ها", "گزارش‌گیری", "تنظیمات"],
463
- icons=["speedometer2", "map", "pencil-square", "graph-up", "file-earmark-text", "gear"],
464
- menu_icon="cast",
465
  default_index=0,
466
  orientation="horizontal",
467
  styles={
@@ -472,97 +448,24 @@ selected = option_menu(
472
  }
473
  )
474
 
475
- # Dashboard
476
- if selected == "داشبورد":
477
- col1, col2, col3, col4 = st.columns(4)
478
-
479
- with col1:
480
- st.markdown(f'<div class="metric-card"><div class="metric-value">{len(farm_df)}</div><div class="metric-label">تعداد مزارع</div></div>', unsafe_allow_html=True)
481
-
482
- with col2:
483
- active_farms = int(len(farm_df) * 0.85)
484
- st.markdown(f'<div class="metric-card"><div class="metric-value">{active_farms}</div><div class="metric-label">مزارع فعال</div></div>', unsafe_allow_html=True)
485
-
486
- with col3:
487
- avg_height = st.session_state.heights_df['ارتفاع هفته جاری مزرعه'].mean() if not st.session_state.heights_df.empty else 0
488
- st.markdown(f'<div class="metric-card"><div class="metric-value">{avg_height:.1f} cm</div><div class="metric-label">میانگین ارتفاع</div></div>', unsafe_allow_html=True)
489
-
490
- with col4:
491
- avg_moisture = st.session_state.heights_df['رطوبت غلاف فعلی'].mean() if not st.session_state.heights_df.empty else 0
492
- st.markdown(f'<div class="metric-card"><div class="metric-value">{avg_moisture:.1f}%</div><div class="metric-label">میانگین رطوبت</div></div>', unsafe_allow_html=True)
493
-
494
- tab1, tab2 = st.tabs(["نمای کلی", "نمودارها"])
495
-
496
- with tab1:
497
- st.markdown("### اطلاعات کلی مزارع")
498
- total_area = farm_df['مساحت داشت'].astype(float).sum() if 'مساحت داشت' in farm_df.columns else 0
499
- col1, col2, col3 = st.columns(3)
500
- col1.metric("تعداد کل مزارع", f"{len(farm_df)}")
501
- col2.metric("مساحت کل (هکتار)", f"{total_area:.2f}")
502
- col3.metric("تعداد کانال‌ها", f"{farm_df['کانال'].nunique() if 'کانال' in farm_df.columns else 0}")
503
-
504
- with tab2:
505
- if not st.session_state.heights_df.empty:
506
- fig = px.line(st.session_state.heights_df, x='هفته', y='ارتفاع هفته جاری مزرعه', title='روند ارتفاع هفتگی', labels={'هفته': 'هفته', 'ارتفاع هفته جاری مزرعه': 'ارتفاع (سانتی‌متر)'})
507
- st.plotly_chart(fig, use_container_width=True)
508
- else:
509
- st.warning("داده‌ای برای نمایش وجود ندارد.")
510
-
511
- # Map Page
512
- elif selected == "نقشه مزارع":
513
- st.markdown("## نقشه مزارع با شاخص‌های ماهواره‌ای")
514
- col1, col2 = st.columns([1, 3])
515
-
516
- with col1:
517
- selected_farm = st.selectbox("انتخاب مزرعه", options=coordinates_df['نام'].tolist(), index=0)
518
- selected_date = st.date_input("انتخاب تاریخ", value=datetime.now())
519
- selected_layer = st.selectbox("انتخاب شاخص", ["NDVI", "NDMI", "EVI", "NDWI"])
520
- if st.button("تولید نقشه"):
521
- with st.spinner('در حال تولید نقشه...'):
522
- m = create_ee_map(selected_farm, selected_date.strftime('%Y-%m-%d'), selected_layer)
523
- if m:
524
- folium_static(m, width=800, height=600)
525
- st.success("نقشه با موفقیت تولید شد!")
526
- else:
527
- st.error("خطا در تولید نقشه!")
528
-
529
- with col2:
530
- if 'last_map' in st.session_state:
531
- folium_static(st.session_state.last_map, width=800, height=600)
532
- stats = calculate_farm_stats(selected_farm, selected_date.strftime('%Y-%m-%d'), selected_layer)
533
- if stats:
534
- col1, col2, col3, col4 = st.columns(4)
535
- with col1:
536
- st.markdown(f'<div class="metric-card"><div class="metric-value">{stats["mean"]:.2f}</div><div class="metric-label">میانگین {selected_layer}</div></div>', unsafe_allow_html=True)
537
- with col2:
538
- st.markdown(f'<div class="metric-card"><div class="metric-value">{stats["max"]:.2f}</div><div class="metric-label">حداکثر {selected_layer}</div></div>', unsafe_allow_html=True)
539
- with col3:
540
- st.markdown(f'<div class="metric-card"><div class="metric-value">{stats["min"]:.2f}</div><div class="metric-label">حداقل {selected_layer}</div></div>', unsafe_allow_html=True)
541
- with col4:
542
- st.markdown(f'<div class="metric-card"><div class="metric-value">{stats["std_dev"]:.2f}</div><div class="metric-label">انحراف معیار</div></div>', unsafe_allow_html=True)
543
-
544
  # Data Entry Page
545
- elif selected == "ورود اطلاعات":
546
- st.markdown("## ورود اطلاعات روزانه مزارع")
547
  tabs = st.tabs(["ورود دستی", "آپلود فایل"])
548
 
549
  with tabs[0]:
550
- st.markdown("### ورود داده‌های مزارع")
551
- week = st.selectbox("انتخاب هفته", list(range(1, 23)), key="week_manual")
552
- days = st.session_state.heights_df['تاریخ قرائت'].unique()
553
- day = st.selectbox("انتخاب روز", days, key="day_manual")
554
 
555
  if st.button("نمایش داده‌ها"):
556
- filtered_df = st.session_state.heights_df[
557
- (st.session_state.heights_df['تاریخ قرائت'] == day) &
558
- (st.session_state.heights_df['هفته'] == week)
559
  ].copy()
560
  if filtered_df.empty:
561
- # اگر داده‌ای وجود ندارد، یک ردیف خالی با ستون‌های پیش‌فرض اضافه کن
562
- filtered_df = pd.DataFrame(columns=st.session_state.heights_df.columns)
563
  filtered_df.loc[0] = [None] * len(filtered_df.columns)
564
- filtered_df['هفته'] = week
565
- filtered_df['تاریخ قرائت'] = day
566
  st.session_state.filtered_df = filtered_df
567
 
568
  if 'filtered_df' in st.session_state:
@@ -570,39 +473,21 @@ elif selected == "ورود اطلاعات":
570
  st.session_state.filtered_df,
571
  num_rows="dynamic",
572
  column_config={
573
- "ردیف": st.column_config.TextColumn("ردیف", required=True),
574
- "نماینده": st.column_config.TextColumn("نماینده", required=True),
575
- "کانال": st.column_config.NumberColumn("کانال", min_value=1, step=1, required=True),
576
- "اداره": st.column_config.NumberColumn("اداره", min_value=1, step=1, required=True),
577
- "تولید": st.column_config.NumberColumn("تولید", min_value=1, step=1, required=True),
578
- "مساحت داشت": st.column_config.NumberColumn("مساحت داشت", min_value=0.0, step=0.1, required=True),
579
- "مساحت زیرمجموعه": st.column_config.NumberColumn("مساحت زیرمجموعه", min_value=0.0, step=0.1, required=True),
580
- "واریته": st.column_config.TextColumn("واریته", required=True),
581
- "سن": st.column_config.TextColumn("سن", required=True),
582
- "ایستگاه 1": st.column_config.NumberColumn("ایستگاه 1", min_value=0.0, step=0.1, required=True),
583
- "ایستگاه 2": st.column_config.NumberColumn("ایستگاه 2", min_value=0.0, step=0.1, required=True),
584
- "ایستگاه 3": st.column_config.NumberColumn("ایستگاه 3", min_value=0.0, step=0.1, required=True),
585
- "ایستگاه 4": st.column_config.NumberColumn("ایستگاه 4", min_value=0.0, step=0.1, required=True),
586
- "ایستگاه 5": st.column_config.NumberColumn("ایستگاه 5", min_value=0.0, step=0.1, required=True),
587
- "ارتفاع هفته جاری مزرعه": st.column_config.NumberColumn("ارتفاع هفته جاری مزرعه", min_value=0.0, step=0.1, disabled=True),
588
- "ارتفاع هفته گذشته مزرعه": st.column_config.NumberColumn("ارتفاع هفته گذشته مزرعه", min_value=0.0, step=0.1, required=True),
589
- "رشد هفته جاری": st.column_config.NumberColumn("رشد هفته جاری", min_value=0.0, step=0.1, disabled=True),
590
- "رشد هفته گذشته": st.column_config.NumberColumn("رشد هفته گذشته", min_value=0.0, step=0.1, required=True),
591
- "نیتروژن فعلی": st.column_config.NumberColumn("نیتروژن فعلی", min_value=0.0, step=0.1, required=True),
592
- "نیتروژن استاندارد فعلی": st.column_config.NumberColumn("نیتروژن استاندارد فعلی", min_value=0.0, step=0.1, required=True),
593
- "نیتروژن قبلی": st.column_config.NumberColumn("نیتروژن قبلی", min_value=0.0, step=0.1, required=True),
594
- "نیتروژن استاندارد قبلی": st.column_config.NumberColumn("نیتروژن استاندارد قبلی", min_value=0.0, step=0.1, required=True),
595
- "رطوبت غلاف فعلی": st.column_config.NumberColumn("رطوبت غلاف فعلی", min_value=0.0, step=0.1, required=True),
596
- "رطوبت استاندارد فعلی": st.column_config.NumberColumn("رطوبت استاندارد فعلی", min_value=0.0, step=0.1, required=True),
597
- "رطوبت غلاف قبلی": st.column_config.NumberColumn("رطوبت غلاف قبلی", min_value=0.0, step=0.1, required=True),
598
- "رطوبت استاندارد قبلی": st.column_config.NumberColumn("رطوبت استاندارد قبلی", min_value=0.0, step=0.1, required=True),
599
- "چاهک 1": st.column_config.NumberColumn("چاهک 1", min_value=0.0, step=0.1, required=True),
600
- "چاهک 2": st.column_config.NumberColumn("چاهک 2", min_value=0.0, step=0.1, required=True),
601
- "تاریخ قرائت": st.column_config.DateColumn("تاریخ قرائت", format="YYYY-MM-DD", required=True),
602
- "تاریخ قرائت2": st.column_config.DateColumn("تاریخ قرائت2", format="YYYY-MM-DD", required=True),
603
- "نیتروژن انحراف": st.column_config.NumberColumn("نیتروژن انحراف", min_value=-100.0, max_value=100.0, disabled=True),
604
- "رطوبت انحراف": st.column_config.NumberColumn("رطوبت انحراف", min_value=-100.0, max_value=100.0, disabled=True),
605
- "نسبت مساحت کراپ لاگ": st.column_config.NumberColumn("نسبت مساحت کراپ لاگ", min_value=0.0, max_value=10.0, disabled=True),
606
  },
607
  use_container_width=True,
608
  hide_index=True
@@ -610,167 +495,117 @@ elif selected == "ورود اطلاعات":
610
 
611
  if st.button("محاسبه و ذخیره"):
612
  calculated_df = calculate_metrics(edited_df)
613
- st.session_state.heights_df = pd.concat(
614
- [st.session_state.heights_df, calculated_df[~calculated_df.index.isin(st.session_state.heights_df.index)]],
615
  ignore_index=True
616
  )
617
- st.success("داده‌ها با موفقیت محاسبه و ذخیره شدند!")
618
  st.session_state.filtered_df = calculated_df
619
 
620
  with tabs[1]:
621
- st.markdown("### آپلود فایل اکسل")
622
- uploaded_file = st.file_uploader("فایل اکسل خود را آپلود کنید", type=["xlsx", "xls"])
623
-
624
  if uploaded_file is not None:
625
  try:
626
- df = pd.read_excel(uploaded_file)
627
-
628
- # Dynamic column mapping based on your file structure from the document
629
- column_mapping = {
630
- 'ردیف': 'ردیف',
631
- 'نماینده': 'نماینده',
632
- 'کانال': 'کانال',
633
- 'اداره': 'اداره',
634
- 'تولید': 'تولید',
635
- 'مساحت داشت': 'مساحت داشت',
636
- 'مساحت زیرمجموعه': 'مساحت زیرمجموعه',
637
- 'واریته': 'واریته',
638
- 'سن': 'سن',
639
- 'ایستگاه 1': 'ایستگاه 1',
640
- 'ایستگاه 2': 'ایستگاه 2',
641
- 'ایستگاه 3': 'ایستگاه 3',
642
- 'ایستگاه 4': 'ایستگاه 4',
643
- 'ایستگاه 5': 'ایستگاه 5',
644
- 'ارتفاع هفته جاری مزرعه': 'ارتفاع هفته جاری مزرعه',
645
- 'ارتفاع هفته گذشته مزرعه': 'ارتفاع هفته گذشته مزرعه',
646
- 'رشد هفته جاری': 'رشد هفته جاری',
647
- 'رشد هفته گذشته': 'رشد هفته گذشته',
648
- 'نیتروژن فعلی': 'نیتروژن فعلی',
649
- 'نیتروژن استاندارد فعلی': 'نیتروژن استاندارد فعلی',
650
- 'نیتروژن قبلی': 'نیتروژن قبلی',
651
- 'نیتروژن استاندارد قبلی': 'نیتروژن استاندارد قبلی',
652
- 'رطوبت غلاف فعلی': 'رطوبت غلاف فعلی',
653
- 'رطوبت استاندارد فعلی': 'رطوبت استاندارد فعلی',
654
- 'رطوبت غلاف قبلی': 'رطوبت غلاف قبلی',
655
- 'رطوبت استاندارد قبلی': 'رطوبت استاندارد قبلی',
656
- 'چاهک 1': 'چاهک 1',
657
- 'تاریخ قرائت': 'تاریخ قرائت',
658
- 'چاهک 2': 'چاهک 2',
659
- 'تاریخ قرائت2': 'تاریخ قرائت2'
660
- }
661
 
662
- # Map columns dynamically
663
- available_columns = df.columns.tolist()
664
- mapped_df = pd.DataFrame()
665
- for english_col, persian_col in column_mapping.items():
666
- if english_col in available_columns:
667
- mapped_df[persian_col] = df[english_col]
668
-
669
- # Required columns for the upload file
670
- required_upload_columns = ['ردیف', 'نماینده', 'کانال', 'اداره', 'تولید', 'مساحت داشت', 'مساحت زیرمجموعه', 'واریته', 'سن',
671
- 'ایستگاه 1', 'ایستگاه 2', 'ایستگاه 3', 'ایستگاه 4', 'ایستگاه 5', 'ارتفاع هفته جاری مزرعه',
672
- 'ارتفاع هفته گذشته مزرعه', 'رشد هفته جاری', 'رشد هفته گذشته', 'نیتروژن فعلی', 'نیتروژن استاندارد فعلی',
673
- 'نیتروژن قبلی', 'نیتروژن استاندارد قبلی', 'رطوبت غلاف فعلی', 'رطوبت استاندارد فعلی', 'رطوبت غلاف قبلی',
674
- 'رطوبت استاندارد قبلی', 'چاهک 1', 'تاریخ قرائت', 'چاهک 2', 'تاریخ قرائت2']
675
-
676
- # Check for missing required columns
677
- missing_columns = [col for col in required_upload_columns if col not in mapped_df.columns]
678
- if missing_columns:
679
- st.error(f"ستون‌های زیر در فایل یافت نشدند: {', '.join(missing_columns)}")
680
- else:
681
- # Convert date format if necessary (assuming Persian date format like 1403/02/22)
682
- def convert_persian_date(date_str):
683
- try:
684
- parts = str(date_str).split('/')
685
- if len(parts) == 3:
686
- year, month, day = map(int, parts)
687
- return f"{year}-{month:02d}-{day:02d}"
688
- return date_str
689
- except:
690
- return date_str
691
-
692
- mapped_df['تاریخ قرائت'] = mapped_df['تاریخ قرائت'].apply(convert_persian_date)
693
- mapped_df['تاریخ قرائت2'] = mapped_df['تاریخ قرائت2'].apply(convert_persian_date)
694
- mapped_df['تاریخ قرائت'] = pd.to_datetime(mapped_df['تاریخ قرائت'], errors='coerce')
695
- mapped_df['تاریخ قرائت2'] = pd.to_datetime(mapped_df['تاریخ قرائت2'], errors='coerce')
696
-
697
- # Ensure numeric columns
698
- numeric_cols = ['کانال', 'اداره', 'تولید', 'مساحت داشت', 'مساحت زیرمجموعه', 'ایستگاه 1', 'ایستگاه 2',
699
- 'ایستگاه 3', 'ایستگاه 4', 'ایستگاه 5', 'ارتفاع هفته جاری مزرعه', 'ارتفاع هفته گذشته مزرعه',
700
- 'رشد هفته جاری', 'رشد هفته گذشته', 'نیتروژن فعلی', 'نیتروژن استاندارد فعلی', 'نیتروژن قبلی',
701
- 'نیتروژن استاندارد قبلی', 'رطوبت غلاف فعلی', 'رطوبت استاندارد فعلی', 'رطوبت غلاف قبلی',
702
- 'رطوبت استاندارد قبلی', 'چاهک 1', 'چاهک 2']
703
- for col in numeric_cols:
704
- mapped_df[col] = pd.to_numeric(mapped_df[col], errors='coerce')
705
-
706
- # Calculate metrics
707
- calculated_df = calculate_metrics(mapped_df)
708
-
709
- # Append to session state
710
- st.session_state.heights_df = pd.concat(
711
- [st.session_state.heights_df, calculated_df[~calculated_df.index.isin(st.session_state.heights_df.index)]],
712
- ignore_index=True
713
- )
714
- st.success("داده‌ها از فایل با موفقیت بارگذاری و پردازش شدند!")
715
- st.dataframe(calculated_df)
716
  except Exception as e:
717
  st.error(f"خطا در پردازش فایل: {e}")
718
 
719
  st.markdown("### داده‌های فعلی")
720
- if not st.session_state.heights_df.empty:
721
- st.dataframe(st.session_state.heights_df)
722
  else:
723
  st.info("داده‌ای برای نمایش وجود ندارد.")
724
 
725
  # Data Analysis Page
726
- elif selected == "تحلیل داده‌ها":
727
  st.markdown("## تحلیل داده‌ها")
728
- if not st.session_state.heights_df.empty:
729
- st.markdown("### تحلیل رشد")
730
- fig = px.line(st.session_state.heights_df, x='هفته', y='ارتفاع هفته جاری مزرعه', color='ردیف', title='رشد مزارع بر اساس هفته')
731
  st.plotly_chart(fig, use_container_width=True)
732
 
733
- st.markdown("### تحلیل رطوبت")
734
- fig = px.scatter(st.session_state.heights_df, x='رطوبت غلاف فعلی', y='ارتفاع هفته جاری مزرعه', color='ردیف', title='رابطه رطوبت و ارتفاع')
735
  st.plotly_chart(fig, use_container_width=True)
736
  else:
737
  st.warning("داده‌ای برای تحلیل وجود ندارد.")
738
 
739
  # Reporting Page
740
- elif selected == "گزارش‌گیری":
741
  st.markdown("## گزارش‌گیری")
742
  report_type = st.selectbox("نوع گزارش", ["روزانه", "هفتگی", "تجمیعی"])
743
 
744
  if report_type == "روزانه":
745
- day = st.selectbox("انتخاب روز", st.session_state.heights_df['تاریخ قرائت'].unique())
746
- report_df = st.session_state.heights_df[st.session_state.heights_df['تاریخ قرائت'] == day]
 
747
  elif report_type == "هفتگی":
748
- week = st.selectbox("انتخاب هفته", list(range(1, 23)))
749
- report_df = st.session_state.heights_df[st.session_state.heights_df['هفته'] == week]
 
 
 
750
  else:
751
- report_df = st.session_state.heights_df
752
 
753
  st.dataframe(report_df)
754
 
755
  if st.button("تولید گزارش PDF"):
756
- pdf_data = generate_pdf_report(report_df, report_type, week if report_type == "هفتگی" else None, day if report_type == "روزانه" else None)
757
  b64 = base64.b64encode(pdf_data).decode('utf-8')
758
  href = f'<a href="data:application/pdf;base64,{b64}" download="report.pdf">دانلود گزارش PDF</a>'
759
  st.markdown(href, unsafe_allow_html=True)
760
 
761
- # Settings Page
762
- elif selected == "تنظیمات":
763
- st.markdown("## تنظیمات")
764
- st.write("در حال حاضر تنظیمات خاصی در دسترس نیست.")
765
-
766
- # ذخیره دائمی پایگاه داده
767
- if st.button("ذخیره پایگاه داده"):
768
- st.session_state.heights_df.to_csv("پایگاه داده.csv", index=False)
769
- st.success("پایگاه داده با موفقیت ذخیره شد!")
770
 
771
  # Footer
772
  st.markdown("""
773
  <footer style="position: relative; bottom: 0; width: 100%; background-color: #1a8754; color: white; text-align: center; padding: 1rem; margin-top: 2rem; border-top: 2px solid rgba(255, 255, 255, 0.1);">
774
- <p>© 2025 سامانه هوشمند پایش مزارع نیشکر دهخدا</p>
775
  </footer>
776
- """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  from streamlit_card import card
29
  import pydeck as pdk
30
  import math
31
+ from jdatetime import date as jdate
32
 
33
  # Page configuration with custom theme
34
  st.set_page_config(
35
+ page_title="سامانه مدیریت داده‌ها",
36
+ page_icon="📊",
37
  layout="wide",
38
  initial_sidebar_state="expanded"
39
  )
 
196
 
197
  # Load data
198
  @st.cache_data
199
+ def load_data():
200
  try:
201
+ uploaded_file = st.file_uploader("آپلود فایل CSV", type=["csv"], key="data_uploader")
202
+ if uploaded_file is not None:
203
+ df = pd.read_csv(uploaded_file)
204
+ # تبدیل تاریخ شمسی به میلادی (فرض می‌کنیم تاریخ‌ها به فرمت 1403/02/22 هستند)
205
+ df['تاریخ'] = pd.to_datetime(df['تاریخ'].apply(convert_persian_date), errors='coerce')
206
+ return df
207
+ else:
208
+ return pd.DataFrame()
209
  except Exception as e:
210
+ st.error(f"خطا در بارگذاری داده‌ها: {e}")
211
  return pd.DataFrame()
212
 
213
+ # تبدیل تاریخ شمسی به میلادی
214
+ def convert_persian_date(date_str):
215
  try:
216
+ parts = str(date_str).split('/')
217
+ if len(parts) == 3:
218
+ year, month, day = map(int, parts)
219
+ persian_date = jdate(year, month, day)
220
+ gregorian_date = persian_date.togregorian()
221
+ return gregorian_date.strftime('%Y-%m-%d')
222
+ return date_str
223
+ except:
224
+ return date_str
225
 
226
+ # محاسبات
227
+ def calculate_metrics(df):
228
+ # اضافه کردن ستون هفته از تاریخ (اختیاری، برای گزارش‌گیری)
229
+ df['هفته'] = df['تاریخ'].dt.isocalendar().week
230
+ return df
 
 
231
 
232
+ # تولید گزارش PDF
233
+ def generate_pdf_report(df, report_type, date=None, week=None):
234
+ pdf = FPDF()
235
+ pdf.add_page()
236
+ pdf.add_font('Vazir', '', 'Vazirmatn-Regular.ttf', uni=True)
237
+ pdf.set_font('Vazir', '', 12)
238
+
239
+ if report_type == "روزانه":
240
+ title = f"گزارش روزانه - تاریخ: {date.strftime('%Y-%m-%d') if date else 'بدون تاریخ'}"
241
+ elif report_type == "هفتگی":
242
+ title = f"گزارش هفتگی - هفته: {week}"
243
+ else:
244
+ title = "گزارش تجمیعی"
245
+
246
+ pdf.cell(200, 10, title, ln=True, align='C')
247
+ pdf.ln(10)
248
+
249
+ # سرستون‌ها
250
+ cols = df.columns.tolist()
251
+ pdf.set_font('Vazir', '', 10)
252
+ for col in cols:
253
+ pdf.cell(40, 10, col, border=1)
254
+ pdf.ln()
255
+
256
+ # داده‌ها
257
+ for index, row in df.iterrows():
258
+ for col in cols:
259
+ pdf.cell(40, 10, str(row[col]) if pd.notnull(row[col]) else "0", border=1)
260
+ pdf.ln()
261
+
262
+ # ذخیره PDF در حافظه
263
+ pdf_output = pdf.output(dest='S').encode('latin1')
264
+ return pdf_output
265
+
266
+ # Create Earth Engine map (optional, can be removed if not needed)
267
  def create_ee_map(farm_id, date_str, layer_type="NDVI"):
268
  try:
269
+ # Assuming coordinates_df is loaded; if not needed, this function can be removed
270
  farm_row = coordinates_df[coordinates_df['نام'] == farm_id].iloc[0]
271
  lat, lon = farm_row['عرض جغرافیایی'], farm_row['طول جغرافیایی']
272
 
 
362
  st.error(f"خطا در ایجاد نقشه: {e}")
363
  return None
364
 
365
+ # Calculate statistics for a farm using GEE (optional, can be removed if not needed)
366
  def calculate_farm_stats(farm_id, date_str, layer_type="NDVI"):
367
  try:
368
+ # Assuming coordinates_df is loaded; if not needed, this function can be removed
369
  farm_row = coordinates_df[coordinates_df['نام'] == farm_id].iloc[0]
370
  lat, lon = farm_row['عرض جغرافیایی'], farm_row['طول جغرافیایی']
371
  region = ee.Geometry.Point([lon, lat]).buffer(1500)
 
417
  st.error(f"خطا در محاسبه آمار: {e}")
418
  return None
419
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  # Load data
421
+ data_df = load_data()
 
422
 
423
+ # Create session state for storing data
424
+ if 'data_df' not in st.session_state:
425
+ st.session_state.data_df = pd.DataFrame(columns=[
426
+ 'شماره', 'تاریخ', 'تعداد', 'مبلغ', 'وضعیت', 'توضیحات', 'نوع پرداخت', 'شماره فاکتور',
427
+ 'شماره سفارش', 'کد مشتری', 'نام مشتری', 'شماره پیگیری', 'تاریخ تحویل', 'مقدار تخفیف', 'تاریخ ثبت'
 
 
 
 
 
 
 
 
428
  ])
429
 
430
  # Main header
431
  st.markdown('<div class="main-header">', unsafe_allow_html=True)
432
+ st.markdown('<h1>سامانه مدیریت داده‌ها</h1>', unsafe_allow_html=True)
433
+ st.markdown('<p>پلتفرم جامع ورود، مدیریت و گزارش‌گیری داده‌ها</p>', unsafe_allow_html=True)
434
  st.markdown('</div>', unsafe_allow_html=True)
435
 
436
  # Navigation menu
437
  selected = option_menu(
438
  menu_title=None,
439
+ options=["ورود اطلاعات", "تحلیل داده‌ها", "گزارش‌گیری"],
440
+ icons=["pencil-square", "graph-up", "file-earmark-text"],
 
441
  default_index=0,
442
  orientation="horizontal",
443
  styles={
 
448
  }
449
  )
450
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
  # Data Entry Page
452
+ if selected == "ورود اطلاعات":
453
+ st.markdown("## ورود اطلاعات")
454
  tabs = st.tabs(["ورود دستی", "آپلود فایل"])
455
 
456
  with tabs[0]:
457
+ st.markdown("### ورود داده‌ها به صورت دستی")
458
+ dates = st.session_state.data_df['تاریخ'].unique()
459
+ date = st.selectbox("انتخاب تاریخ", pd.to_datetime(dates).strftime('%Y/%m/%d').tolist() if len(dates) > 0 else ["1403/02/22"], key="date_manual")
 
460
 
461
  if st.button("نمایش داده‌ها"):
462
+ filtered_df = st.session_state.data_df[
463
+ st.session_state.data_df['تاریخ'] == pd.to_datetime(date, format='%Y/%m/%d')
 
464
  ].copy()
465
  if filtered_df.empty:
466
+ filtered_df = pd.DataFrame(columns=st.session_state.data_df.columns)
 
467
  filtered_df.loc[0] = [None] * len(filtered_df.columns)
468
+ filtered_df['تاریخ'] = pd.to_datetime(date, format='%Y/%m/%d')
 
469
  st.session_state.filtered_df = filtered_df
470
 
471
  if 'filtered_df' in st.session_state:
 
473
  st.session_state.filtered_df,
474
  num_rows="dynamic",
475
  column_config={
476
+ "شماره": st.column_config.NumberColumn("شماره", required=True),
477
+ "تاریخ": st.column_config.DateColumn("تاریخ", format="YYYY/MM/DD", required=True),
478
+ "تعداد": st.column_config.NumberColumn("تعداد", min_value=0, step=1, required=True),
479
+ "مبلغ": st.column_config.NumberColumn("مبلغ", min_value=0, step=1, required=True),
480
+ "وضعیت": st.column_config.TextColumn("وضعیت", required=True),
481
+ "توضیحات": st.column_config.TextColumn("توضیحات"),
482
+ "نوع پرداخت": st.column_config.TextColumn("نوع پرداخت", required=True),
483
+ "شماره فاکتور": st.column_config.TextColumn("شماره فاکتور", required=True),
484
+ "شماره سفارش": st.column_config.TextColumn("شماره سفارش", required=True),
485
+ "کد مشتری": st.column_config.TextColumn("کد مشتری", required=True),
486
+ "نام مشتری": st.column_config.TextColumn("نام مشتری", required=True),
487
+ "شماره پیگیری": st.column_config.TextColumn("شماره پیگیری"),
488
+ "تاریخ تحویل": st.column_config.DateColumn("تاریخ تحویل", format="YYYY/MM/DD"),
489
+ "مقدار تخفیف": st.column_config.NumberColumn("مقدار تخفیف", min_value=0, step=1),
490
+ "تاریخ ثبت": st.column_config.DateColumn("تاریخ ثبت", format="YYYY/MM/DD"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
  },
492
  use_container_width=True,
493
  hide_index=True
 
495
 
496
  if st.button("محاسبه و ذخیره"):
497
  calculated_df = calculate_metrics(edited_df)
498
+ st.session_state.data_df = pd.concat(
499
+ [st.session_state.data_df, calculated_df[~calculated_df.index.isin(st.session_state.data_df.index)]],
500
  ignore_index=True
501
  )
502
+ st.success("داده‌ها با موفقیت ذخیره شدند!")
503
  st.session_state.filtered_df = calculated_df
504
 
505
  with tabs[1]:
506
+ st.markdown("### آپلود فایل")
507
+ uploaded_file = st.file_uploader("آپلود فایل CSV", type=["csv"])
 
508
  if uploaded_file is not None:
509
  try:
510
+ df = pd.read_csv(uploaded_file)
511
+ # تبدیل تاریخ شمسی به میلادی
512
+ df['تاریخ'] = pd.to_datetime(df['تاریخ'].apply(convert_persian_date), errors='coerce')
513
+ if 'تاریخ تحویل' in df.columns:
514
+ df['تاریخ تحویل'] = pd.to_datetime(df['تاریخ تحویل'].apply(convert_persian_date), errors='coerce')
515
+ if 'تاریخ ثبت' in df.columns:
516
+ df['تاریخ ثبت'] = pd.to_datetime(df['تاریخ ثبت'].apply(convert_persian_date), errors='coerce')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517
 
518
+ calculated_df = calculate_metrics(df)
519
+ st.session_state.data_df = pd.concat(
520
+ [st.session_state.data_df, calculated_df[~calculated_df.index.isin(st.session_state.data_df.index)]],
521
+ ignore_index=True
522
+ )
523
+ st.success("داده‌ها از فایل با موفقیت بارگذاری شدند!")
524
+ st.dataframe(st.session_state.data_df)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525
  except Exception as e:
526
  st.error(f"خطا در پردازش فایل: {e}")
527
 
528
  st.markdown("### داده‌های فعلی")
529
+ if not st.session_state.data_df.empty:
530
+ st.dataframe(st.session_state.data_df)
531
  else:
532
  st.info("داده‌ای برای نمایش وجود ندارد.")
533
 
534
  # Data Analysis Page
535
+ if selected == "تحلیل داده‌ها":
536
  st.markdown("## تحلیل داده‌ها")
537
+ if not st.session_state.data_df.empty:
538
+ st.markdown("### روند مبلغ بر اساس تاریخ")
539
+ fig = px.line(st.session_state.data_df, x='تاریخ', y='مبلغ', title='روند مبلغ')
540
  st.plotly_chart(fig, use_container_width=True)
541
 
542
+ st.markdown("### رابطه تعداد و مبلغ")
543
+ fig = px.scatter(st.session_state.data_df, x='تعداد', y='مبلغ', color='وضعیت', title='رابطه تعداد و مبلغ')
544
  st.plotly_chart(fig, use_container_width=True)
545
  else:
546
  st.warning("داده‌ای برای تحلیل وجود ندارد.")
547
 
548
  # Reporting Page
549
+ if selected == "گزارش‌گیری":
550
  st.markdown("## گزارش‌گیری")
551
  report_type = st.selectbox("نوع گزارش", ["روزانه", "هفتگی", "تجمیعی"])
552
 
553
  if report_type == "روزانه":
554
+ dates = st.session_state.data_df['تاریخ'].unique()
555
+ date = st.selectbox("��نتخاب تاریخ", pd.to_datetime(dates).strftime('%Y/%m/%d').tolist() if len(dates) > 0 else ["1403/02/22"])
556
+ report_df = st.session_state.data_df[st.session_state.data_df['تاریخ'] == pd.to_datetime(date, format='%Y/%m/%d')]
557
  elif report_type == "هفتگی":
558
+ # محاسبه هفته از تاریخ
559
+ st.session_state.data_df['هفته'] = st.session_state.data_df['تاریخ'].dt.isocalendar().week
560
+ weeks = st.session_state.data_df['هفته'].unique()
561
+ week = st.selectbox("انتخاب هفته", sorted(weeks) if len(weeks) > 0 else [1])
562
+ report_df = st.session_state.data_df[st.session_state.data_df['هفته'] == week]
563
  else:
564
+ report_df = st.session_state.data_df
565
 
566
  st.dataframe(report_df)
567
 
568
  if st.button("تولید گزارش PDF"):
569
+ pdf_data = generate_pdf_report(report_df, report_type, date if report_type == "روزانه" else None, week if report_type == "هفتگی" else None)
570
  b64 = base64.b64encode(pdf_data).decode('utf-8')
571
  href = f'<a href="data:application/pdf;base64,{b64}" download="report.pdf">دانلود گزارش PDF</a>'
572
  st.markdown(href, unsafe_allow_html=True)
573
 
574
+ # ذخیره دائمی داده‌ها
575
+ if st.button("ذخیره داده‌ها"):
576
+ st.session_state.data_df.to_csv("داده‌ها.csv", index=False, date_format='%Y/%m/%d')
577
+ st.success("داده‌ها با موفقیت ذخیره شدند!")
 
 
 
 
 
578
 
579
  # Footer
580
  st.markdown("""
581
  <footer style="position: relative; bottom: 0; width: 100%; background-color: #1a8754; color: white; text-align: center; padding: 1rem; margin-top: 2rem; border-top: 2px solid rgba(255, 255, 255, 0.1);">
582
+ <p>© 2025 سامانه مدیریت داده‌ها</p>
583
  </footer>
584
+ """, unsafe_allow_html=True)
585
+
586
+ # Initialize Earth Engine (optional, can be removed if not needed)
587
+ ee_initialized = initialize_earth_engine()
588
+
589
+ # Load coordinates data (optional, can be removed if not needed)
590
+ @st.cache_data
591
+ def load_coordinates_data():
592
+ try:
593
+ df = pd.read_csv("farm_coordinates.csv")
594
+ return df
595
+ except Exception as e:
596
+ st.error(f"خطا در بارگذاری داده‌های مختصات: {e}")
597
+ return pd.DataFrame()
598
+
599
+ coordinates_df = load_coordinates_data()
600
+
601
+ # Load animations (optional, can be removed if not needed)
602
+ @st.cache_data
603
+ def load_lottie_url(url: str):
604
+ r = requests.get(url)
605
+ if r.status_code != 200:
606
+ return None
607
+ return r.json()
608
+
609
+ lottie_farm = load_lottie_url('https://assets5.lottiefiles.com/packages/lf20_ystsffqy.json')
610
+ lottie_analysis = load_lottie_url('https://assets3.lottiefiles.com/packages/lf20_qp1q7mct.json')
611
+ lottie_report = load_lottie_url('https://assets9.lottiefiles.com/packages/lf20_vwcugezu.json')