Esmaeilkianii commited on
Commit
bfd16b4
·
verified ·
1 Parent(s): ad60938

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +285 -77
app.py CHANGED
@@ -18,7 +18,7 @@ st.set_page_config(
18
  initial_sidebar_state="expanded"
19
  )
20
 
21
- # CSS برای فارسی‌سازی و بهبود ظاهر
22
  st.markdown("""
23
  <style>
24
  @font-face {
@@ -27,73 +27,144 @@ st.markdown("""
27
  }
28
 
29
  * {
30
- font-family: 'IRANSans', 'Sahel', sans-serif !important;
 
 
 
 
 
31
  }
32
 
33
  .main {
34
  direction: rtl;
35
  text-align: right;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  }
37
 
38
  .stButton button {
39
- background-color: #1e6b45;
40
  color: white;
41
- border-radius: 8px;
42
- padding: 10px 24px;
43
- transition: all 0.3s;
 
 
 
 
44
  }
45
 
46
  .stButton button:hover {
47
- background-color: #2a8c5c;
48
- transform: translateY(-2px);
49
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
 
 
 
 
 
50
  }
51
 
52
  h1, h2, h3 {
53
  color: #1e6b45;
 
54
  }
55
 
56
  .card {
57
- background-color: #f8f9fa;
58
- border-radius: 10px;
59
- padding: 20px;
60
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
61
- margin-bottom: 20px;
 
 
 
 
 
 
62
  }
63
 
64
  .metric-card {
 
 
65
  padding: 20px;
66
- border-radius: 10px;
67
  text-align: center;
68
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
 
69
  }
70
 
71
- input[type="number"] {
72
- direction: ltr !important;
73
- text-align: left !important;
74
  }
75
 
76
- .dataframe {
77
- direction: ltr !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  }
79
 
80
- .dataframe th, .dataframe td {
81
- text-align: left !important;
 
 
82
  }
83
 
84
  .map-container {
85
  height: 500px;
86
  width: 100%;
87
- border-radius: 10px;
88
- box-shadow: 0 4px 10px rgba(0,0,0,0.1);
89
  overflow: hidden;
90
  }
 
 
 
 
 
 
 
 
 
91
  </style>
92
  """, unsafe_allow_html=True)
93
 
94
  # خواندن فایل‌های پایگاه داده
95
- farms_df = pd.read_csv("پایگاه داده (1).csv")
96
- coordinates_df = pd.read_csv("farm_coordinates.csv")
 
 
 
 
97
 
98
  # تنظیم Earth Engine
99
  @st.cache_resource
@@ -128,6 +199,93 @@ def initialize_earth_engine():
128
 
129
  earth_engine_initialized = initialize_earth_engine()
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  # مدیریت وضعیت برنامه
132
  if 'selected_tab' not in st.session_state:
133
  st.session_state.selected_tab = "ورود اطلاعات"
@@ -144,7 +302,7 @@ if 'ndvi_map' not in st.session_state:
144
  def calculate_average_height(row):
145
  stations = [row[f'ایستگاه {i}'] for i in range(1, 6) if f'ایستگاه {i}' in row and row[f'ایستگاه {i}'] is not None]
146
  valid_stations = [s for s in stations if s > 0]
147
- return sum(valid_stations) / len(valid_stations) if valid_stations else 0
148
 
149
  # تابع برای ایجاد نقشه NDVI با مختصات دقیق
150
  def create_ndvi_map(farm_id, date=None):
@@ -152,14 +310,10 @@ def create_ndvi_map(farm_id, date=None):
152
  st.warning("اتصال به Earth Engine برقرار نشد. نقشه NDVI نمایش داده نمی‌شود.")
153
  return None
154
  try:
155
- # دریافت مختصات مزرعه از فایل coordinates_df
156
  farm_coords = coordinates_df[coordinates_df['نام'] == farm_id].iloc[0]
157
  lat = farm_coords['عرض جغرافیایی']
158
  lon = farm_coords['طول جغرافیایی']
159
-
160
- # ایجاد نقشه با مختصات دقیق
161
  m = folium.Map(location=[lat, lon], zoom_start=12)
162
-
163
  if date:
164
  date_obj = datetime.strptime(date, '%Y-%m-%d')
165
  start_date = date_obj - timedelta(days=5)
@@ -169,7 +323,6 @@ def create_ndvi_map(farm_id, date=None):
169
  else:
170
  start_date_str = '2023-01-01'
171
  end_date_str = '2023-12-31'
172
-
173
  region = ee.Geometry.Point([lon, lat]).buffer(1000)
174
  s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
175
  .filterDate(start_date_str, end_date_str) \
@@ -208,30 +361,80 @@ with st.sidebar:
208
  st.subheader("شرکت کشت و صنعت نیشکر دهخدا")
209
  menu = st.selectbox(
210
  "انتخاب صفحه",
211
- ["ورود اطلاعات", "نقشه NDVI"] # فقط بخش‌های موردنظر برای ساده‌سازی
212
  )
213
  st.session_state.selected_tab = menu
214
  st.markdown("---")
215
- st.info("نسخه آزمایشی")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
- # بخش ورود اطلاعات
218
- if st.session_state.selected_tab == "ورود اطلاعات":
219
- st.title("ورود اطلاعات روزانه")
 
220
 
221
- # انتخاب هفته
222
  weeks = [str(i) for i in range(1, 23)]
223
  selected_week = st.selectbox("انتخاب هفته", weeks, index=weeks.index(st.session_state.selected_week))
224
  st.session_state.selected_week = selected_week
225
 
226
- # انتخاب روز هفته
227
  days_of_week = ["شنبه", "یکشنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنجشنبه"]
228
  selected_day = st.selectbox("انتخاب روز", days_of_week, index=days_of_week.index(st.session_state.selected_day))
229
  st.session_state.selected_day = selected_day
230
 
231
- # فیلتر مزارع بر اساس روز انتخاب‌شده
232
  filtered_farms = farms_df[farms_df['روز'] == selected_day].copy()
233
-
234
- # تعریف ستون‌های جدول
235
  columns = {
236
  'مزرعه': filtered_farms['مزرعه'],
237
  'ایستگاه 1': [0] * len(filtered_farms),
@@ -247,7 +450,6 @@ if st.session_state.selected_tab == "ورود اطلاعات":
247
  }
248
  data_df = pd.DataFrame(columns)
249
 
250
- # بارگذاری داده‌های قبلی (اگر وجود داشته باشد)
251
  week_day_key = f"week_{selected_week}_day_{selected_day}"
252
  if week_day_key in st.session_state.farm_data:
253
  for idx, farm in filtered_farms.iterrows():
@@ -259,7 +461,6 @@ if st.session_state.selected_tab == "ورود اطلاعات":
259
  data_df.at[idx, col] = saved_data[col]
260
  data_df.at[idx, 'میانگین ارتفاع'] = calculate_average_height(data_df.iloc[idx])
261
 
262
- # نمایش جدول ورود داده‌ها
263
  st.subheader(f"ورود داده‌های هفته {selected_week} - روز {selected_day}")
264
  edited_df = st.data_editor(
265
  data_df,
@@ -282,29 +483,25 @@ if st.session_state.selected_tab == "ورود اطلاعات":
282
  use_container_width=True
283
  )
284
 
285
- # محاسبه داینامیک میانگین ارتفاع
286
  for idx, row in edited_df.iterrows():
287
  edited_df.at[idx, 'میانگین ارتفاع'] = calculate_average_height(row)
288
 
289
- # نمایش خلاصه داینامیک
290
- st.markdown("---")
291
- st.subheader("خلاصه وضعیت داده‌های واردشده")
292
  col1, col2, col3, col4 = st.columns(4)
293
  with col1:
294
  total_avg = edited_df['میانگین ارتفاع'].mean()
295
- st.metric("میانگین کل ارتفاع", f"{total_avg:.1f} سانتی‌متر" if total_avg > 0 else "0")
296
  with col2:
297
  farms_entered = len(edited_df[edited_df['میانگین ارتفاع'] > 0])
298
- st.metric("تعداد مزارع واردشده", f"{farms_entered} از {len(filtered_farms)}")
299
  with col3:
300
  avg_moisture = edited_df['رطوبت غلاف'].mean()
301
- st.metric("میانگین رطوبت غلاف", f"{avg_moisture:.1f}%" if avg_moisture > 0 else "0")
302
  with col4:
303
  avg_nitrogen = edited_df['نیتروژن'].mean()
304
- st.metric("میانگین نیتروژن", f"{avg_nitrogen:.1f}%" if avg_nitrogen > 0 else "0")
305
 
306
- # دکمه ذخیره
307
- if st.button("ذخیره اطلاعات"):
308
  if week_day_key not in st.session_state.farm_data:
309
  st.session_state.farm_data[week_day_key] = {}
310
  for idx, row in edited_df.iterrows():
@@ -321,26 +518,37 @@ if st.session_state.selected_tab == "ورود اطلاعات":
321
  'نیتروژن': row['نیتروژن'],
322
  'میانگین ارتفاع': row['میانگین ارتفاع']
323
  }
324
- st.success("داده‌ها با موفقیت ذخیره شدند.")
 
325
 
326
- # بخش نقشه NDVI
327
- elif st.session_state.selected_tab == "نقشه NDVI":
328
- st.title("نمایش نقشه NDVI مزارع")
329
- selected_farm = st.selectbox(
330
- "انتخاب مزرعه",
331
- farms_df['مزرعه'].tolist()
332
- )
333
- selected_date = st.date_input(
334
- "انتخاب تاریخ",
335
- value=datetime.now(),
336
- max_value=datetime.now()
337
- )
338
- if st.button("نمایش نقشه"):
339
- with st.spinner("در حال تولید نقشه..."):
340
- ndvi_map = create_ndvi_map(selected_farm, selected_date.strftime('%Y-%m-%d'))
341
- if ndvi_map:
342
- st.markdown('<div class="map-container">', unsafe_allow_html=True)
343
- folium_static(ndvi_map, width=1200, height=500)
344
- st.markdown('</div>', unsafe_allow_html=True)
345
- else:
346
- st.error("نقشه NDVI تولید نشد.")
 
 
 
 
 
 
 
 
 
 
 
18
  initial_sidebar_state="expanded"
19
  )
20
 
21
+ # CSS پیشرفته برای تحول در ظاهر
22
  st.markdown("""
23
  <style>
24
  @font-face {
 
27
  }
28
 
29
  * {
30
+ font-family: 'IRANSans', sans-serif !important;
31
+ }
32
+
33
+ body {
34
+ background: linear-gradient(135deg, #e8f5e9 0%, #f1f8e9 100%);
35
+ color: #2e2e2e;
36
  }
37
 
38
  .main {
39
  direction: rtl;
40
  text-align: right;
41
+ padding: 20px;
42
+ }
43
+
44
+ .sidebar .sidebar-content {
45
+ background: linear-gradient(180deg, #1e6b45 0%, #2a8c5c 100%);
46
+ color: white;
47
+ border-radius: 15px;
48
+ box-shadow: 0 5px 15px rgba(0,0,0,0.2);
49
+ padding: 20px;
50
+ transition: all 0.3s ease;
51
+ }
52
+
53
+ .sidebar .sidebar-content:hover {
54
+ transform: translateY(-5px);
55
  }
56
 
57
  .stButton button {
58
+ background: linear-gradient(90deg, #1e6b45, #2a8c5c);
59
  color: white;
60
+ border: none;
61
+ border-radius: 25px;
62
+ padding: 12px 30px;
63
+ font-size: 16px;
64
+ font-weight: bold;
65
+ box-shadow: 0 4px 12px rgba(0,0,0,0.2);
66
+ transition: all 0.3s ease;
67
  }
68
 
69
  .stButton button:hover {
70
+ background: linear-gradient(90deg, #2a8c5c, #3aa674);
71
+ transform: translateY(-3px);
72
+ box-shadow: 0 6px 18px rgba(0,0,0,0.3);
73
+ }
74
+
75
+ .stButton button:active {
76
+ transform: translateY(1px);
77
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
78
  }
79
 
80
  h1, h2, h3 {
81
  color: #1e6b45;
82
+ text-shadow: 1px 1px 3px rgba(0,0,0,0.1);
83
  }
84
 
85
  .card {
86
+ background: white;
87
+ border-radius: 15px;
88
+ padding: 25px;
89
+ box-shadow: 0 8px 20px rgba(0,0,0,0.1);
90
+ margin-bottom: 30px;
91
+ transition: all 0.3s ease;
92
+ }
93
+
94
+ .card:hover {
95
+ transform: translateY(-5px);
96
+ box-shadow: 0 12px 25px rgba(0,0,0,0.15);
97
  }
98
 
99
  .metric-card {
100
+ background: white;
101
+ border-radius: 15px;
102
  padding: 20px;
 
103
  text-align: center;
104
+ box-shadow: 0 5px 15px rgba(0,0,0,0.1);
105
+ transition: all 0.3s ease;
106
  }
107
 
108
+ .metric-card:hover {
109
+ transform: scale(1.05);
110
+ box-shadow: 0 8px 20px rgba(0,0,0,0.15);
111
  }
112
 
113
+ .metric-green {
114
+ border-left: 6px solid #00c853;
115
+ }
116
+
117
+ .metric-yellow {
118
+ border-left: 6px solid #ffd600;
119
+ }
120
+
121
+ .metric-red {
122
+ border-left: 6px solid #ff3d00;
123
+ }
124
+
125
+ .metric-blue {
126
+ border-left: 6px solid #2196f3;
127
+ }
128
+
129
+ .stSelectbox select {
130
+ background: white;
131
+ border-radius: 10px;
132
+ padding: 10px;
133
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
134
  }
135
 
136
+ .dataframe {
137
+ border-radius: 10px;
138
+ overflow: hidden;
139
+ box-shadow: 0 5px 15px rgba(0,0,0,0.1);
140
  }
141
 
142
  .map-container {
143
  height: 500px;
144
  width: 100%;
145
+ border-radius: 15px;
146
+ box-shadow: 0 8px 20px rgba(0,0,0,0.1);
147
  overflow: hidden;
148
  }
149
+
150
+ .highlight {
151
+ animation: glow 1.5s infinite alternate;
152
+ }
153
+
154
+ @keyframes glow {
155
+ 0% { box-shadow: 0 0 5px #1e6b45; }
156
+ 100% { box-shadow: 0 0 15px #2a8c5c; }
157
+ }
158
  </style>
159
  """, unsafe_allow_html=True)
160
 
161
  # خواندن فایل‌های پایگاه داده
162
+ try:
163
+ farms_df = pd.read_csv("پایگاه داده (1).csv")
164
+ coordinates_df = pd.read_csv("farm_coordinates.csv")
165
+ except FileNotFoundError as e:
166
+ st.error(f"فایل موردنظر یافت نشد: {e}")
167
+ st.stop()
168
 
169
  # تنظیم Earth Engine
170
  @st.cache_resource
 
199
 
200
  earth_engine_initialized = initialize_earth_engine()
201
 
202
+ # نمونه داده (برای داشبورد و گزارش‌ها)
203
+ @st.cache_data
204
+ def generate_sample_data():
205
+ farm_data = {
206
+ 'Farm_ID': farms_df['مزرعه'].tolist(),
207
+ 'Farm_Name': farms_df['مزرعه'].tolist(),
208
+ 'Channel_ID': farms_df['کانال'].tolist(),
209
+ 'Administration_ID': farms_df['اداره'].tolist(),
210
+ 'Administration_Name': [f'اداره {i}' for i in farms_df['اداره']],
211
+ 'Production_Section': [1 if i <= 2 else 2 for i in farms_df['اداره']],
212
+ 'Area': [np.random.randint(50, 200) for _ in range(len(farms_df))],
213
+ 'Variety': farms_df['واریته'].tolist(),
214
+ 'Crop_Age': [int(age.split('/')[0]) for age in farms_df['سن']]
215
+ }
216
+ farms_sample_df = pd.DataFrame(farm_data)
217
+
218
+ height_data = []
219
+ today = datetime.now()
220
+ for week in range(1, 23):
221
+ date = today - timedelta(days=(22-week)*7)
222
+ for farm_id in farms_sample_df['Farm_ID']:
223
+ stations = [np.random.randint(150, 220) for _ in range(5)]
224
+ avg_height = int(np.mean(stations))
225
+ groundwater = [np.random.randint(50, 100) for _ in range(2)]
226
+ sheath_moisture = np.random.randint(60, 90)
227
+ nitrogen = np.random.randint(20, 40)
228
+ height_data.append({
229
+ 'Measurement_ID': f"{farm_id}-W{week}",
230
+ 'Farm_ID': farm_id,
231
+ 'Week': week,
232
+ 'Measurement_Date': date.strftime('%Y-%m-%d'),
233
+ 'Height': avg_height,
234
+ 'Station1': stations[0],
235
+ 'Station2': stations[1],
236
+ 'Station3': stations[2],
237
+ 'Station4': stations[3],
238
+ 'Station5': stations[4],
239
+ 'Groundwater1': groundwater[0],
240
+ 'Groundwater2': groundwater[1],
241
+ 'Sheath_Moisture': sheath_moisture,
242
+ 'Nitrogen': nitrogen
243
+ })
244
+ heights_df = pd.DataFrame(height_data)
245
+
246
+ weekly_report_data = []
247
+ for farm_id in farms_sample_df['Farm_ID']:
248
+ farm_heights = heights_df[heights_df['Farm_ID'] == farm_id].sort_values('Week')
249
+ for week in range(1, 23):
250
+ current_week_data = farm_heights[farm_heights['Week'] == week]
251
+ if current_week_data.empty:
252
+ continue
253
+ current_height = current_week_data['Height'].values[0]
254
+ if week > 1:
255
+ prev_week_data = farm_heights[farm_heights['Week'] == week-1]
256
+ if not prev_week_data.empty:
257
+ prev_height = prev_week_data['Height'].values[0]
258
+ growth_change = current_height - prev_height
259
+ else:
260
+ growth_change = 0
261
+ else:
262
+ growth_change = 0
263
+ all_farms_this_week = heights_df[heights_df['Week'] == week]
264
+ avg_height_all_farms = all_farms_this_week['Height'].mean()
265
+ if current_height > avg_height_all_farms + 5:
266
+ growth_status = 'خوب'
267
+ elif current_height < avg_height_all_farms - 5:
268
+ growth_status = 'ضعیف'
269
+ else:
270
+ growth_status = 'متوسط'
271
+ weekly_report_data.append({
272
+ 'Report_ID': f"{farm_id}-R{week}",
273
+ 'Farm_ID': farm_id,
274
+ 'Week': week,
275
+ 'Average_Height': current_height,
276
+ 'Growth_Change': growth_change,
277
+ 'Growth_Status': growth_status,
278
+ 'Regional_Average': int(avg_height_all_farms)
279
+ })
280
+ weekly_report_df = pd.DataFrame(weekly_report_data)
281
+
282
+ return farms_sample_df, heights_df, weekly_report_df
283
+
284
+ farms_sample_df, heights_df, weekly_report_df = generate_sample_data()
285
+ report_with_details = pd.merge(weekly_report_df, farms_sample_df, on='Farm_ID')
286
+ latest_week = heights_df['Week'].max()
287
+ latest_reports = report_with_details[report_with_details['Week'] == latest_week]
288
+
289
  # مدیریت وضعیت برنامه
290
  if 'selected_tab' not in st.session_state:
291
  st.session_state.selected_tab = "ورود اطلاعات"
 
302
  def calculate_average_height(row):
303
  stations = [row[f'ایستگاه {i}'] for i in range(1, 6) if f'ایستگاه {i}' in row and row[f'ایستگاه {i}'] is not None]
304
  valid_stations = [s for s in stations if s > 0]
305
+ return(sum(valid_stations) / len(valid_stations)) if valid_stations else 0
306
 
307
  # تابع برای ایجاد نقشه NDVI با مختصات دقیق
308
  def create_ndvi_map(farm_id, date=None):
 
310
  st.warning("اتصال به Earth Engine برقرار نشد. نقشه NDVI نمایش داده نمی‌شود.")
311
  return None
312
  try:
 
313
  farm_coords = coordinates_df[coordinates_df['نام'] == farm_id].iloc[0]
314
  lat = farm_coords['عرض جغرافیایی']
315
  lon = farm_coords['طول جغرافیایی']
 
 
316
  m = folium.Map(location=[lat, lon], zoom_start=12)
 
317
  if date:
318
  date_obj = datetime.strptime(date, '%Y-%m-%d')
319
  start_date = date_obj - timedelta(days=5)
 
323
  else:
324
  start_date_str = '2023-01-01'
325
  end_date_str = '2023-12-31'
 
326
  region = ee.Geometry.Point([lon, lat]).buffer(1000)
327
  s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
328
  .filterDate(start_date_str, end_date_str) \
 
361
  st.subheader("شرکت کشت و صنعت نیشکر دهخدا")
362
  menu = st.selectbox(
363
  "انتخاب صفحه",
364
+ ["داشبورد", "ورود اطلاعات", "گزارش‌گیری", "تحلیل داده‌ها", "تنظیمات"]
365
  )
366
  st.session_state.selected_tab = menu
367
  st.markdown("---")
368
+ st.info("نسخه حرفه‌ای 2.0")
369
+
370
+ # داشبورد
371
+ if st.session_state.selected_tab == "داشبورد":
372
+ st.title("📊 داشبورد پایش مزارع نیشکر")
373
+ st.markdown('<div class="card">', unsafe_allow_html=True)
374
+ st.subheader("وضعیت کلی مزارع")
375
+ status_counts = latest_reports['Growth_Status'].value_counts()
376
+ good_count = status_counts.get('خوب', 0)
377
+ medium_count = status_counts.get('متوسط', 0)
378
+ bad_count = status_counts.get('ضعیف', 0)
379
+ col1, col2, col3, col4 = st.columns(4)
380
+ with col1:
381
+ st.markdown('<div class="metric-card metric-green">'f'<h2>{good_count}</h2>''<p>مزارع با وضعیت خوب</p>''</div>', unsafe_allow_html=True)
382
+ with col2:
383
+ st.markdown('<div class="metric-card metric-yellow">'f'<h2>{medium_count}</h2>''<p>مزارع با وضعیت متوسط</p>''</div>', unsafe_allow_html=True)
384
+ with col3:
385
+ st.markdown('<div class="metric-card metric-red">'f'<h2>{bad_count}</h2>''<p>مزارع با وضعیت ضعیف</p>''</div>', unsafe_allow_html=True)
386
+ with col4:
387
+ avg_height = int(latest_reports['Average_Height'].mean())
388
+ st.markdown('<div class="metric-card metric-blue">'f'<h2>{avg_height} سانتی‌متر</h2>''<p>میانگین ارتفاع هفته جاری</p>''</div>', unsafe_allow_html=True)
389
+ st.markdown('</div>', unsafe_allow_html=True)
390
+
391
+ st.markdown('<div class="card">', unsafe_allow_html=True)
392
+ col1, col2 = st.columns(2)
393
+ with col1:
394
+ st.subheader("📈 میانگین ارتفاع به تفکیک اداره")
395
+ admin_heights = latest_reports.groupby('Administration_Name')['Average_Height'].mean().reset_index()
396
+ admin_heights['Average_Height'] = admin_heights['Average_Height'].astype(int)
397
+ fig = px.bar(
398
+ admin_heights,
399
+ x='Administration_Name',
400
+ y='Average_Height',
401
+ color='Average_Height',
402
+ color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
403
+ labels={'Administration_Name': 'اداره', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
404
+ )
405
+ fig.update_layout(height=400, xaxis_title="اداره", yaxis_title="میانگین ارتفاع (سانتی‌متر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
406
+ st.plotly_chart(fig, use_container_width=True)
407
+ with col2:
408
+ st.subheader("📊 مقایسه واریته‌ها")
409
+ variety_heights = latest_reports.groupby('Variety')['Average_Height'].mean().reset_index()
410
+ variety_heights['Average_Height'] = variety_heights['Average_Height'].astype(int)
411
+ fig = px.bar(
412
+ variety_heights,
413
+ x='Variety',
414
+ y='Average_Height',
415
+ color='Variety',
416
+ color_discrete_sequence=["#00c853", "#2196f3"],
417
+ labels={'Variety': 'واریته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
418
+ )
419
+ fig.update_layout(height=400, xaxis_title="واریته", yaxis_title="میانگین ارتفاع (سانتی‌متر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
420
+ st.plotly_chart(fig, use_container_width=True)
421
+ st.markdown('</div>', unsafe_allow_html=True)
422
 
423
+ # ورود اطلاعات
424
+ elif st.session_state.selected_tab == "ورود اطلاعات":
425
+ st.title("📋 ورود اطلاعات روزانه")
426
+ st.markdown('<div class="card">', unsafe_allow_html=True)
427
 
 
428
  weeks = [str(i) for i in range(1, 23)]
429
  selected_week = st.selectbox("انتخاب هفته", weeks, index=weeks.index(st.session_state.selected_week))
430
  st.session_state.selected_week = selected_week
431
 
 
432
  days_of_week = ["شنبه", "یکشنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنجشنبه"]
433
  selected_day = st.selectbox("انتخاب روز", days_of_week, index=days_of_week.index(st.session_state.selected_day))
434
  st.session_state.selected_day = selected_day
435
 
 
436
  filtered_farms = farms_df[farms_df['روز'] == selected_day].copy()
437
+
 
438
  columns = {
439
  'مزرعه': filtered_farms['مزرعه'],
440
  'ایستگاه 1': [0] * len(filtered_farms),
 
450
  }
451
  data_df = pd.DataFrame(columns)
452
 
 
453
  week_day_key = f"week_{selected_week}_day_{selected_day}"
454
  if week_day_key in st.session_state.farm_data:
455
  for idx, farm in filtered_farms.iterrows():
 
461
  data_df.at[idx, col] = saved_data[col]
462
  data_df.at[idx, 'میانگین ارتفاع'] = calculate_average_height(data_df.iloc[idx])
463
 
 
464
  st.subheader(f"ورود داده‌های هفته {selected_week} - روز {selected_day}")
465
  edited_df = st.data_editor(
466
  data_df,
 
483
  use_container_width=True
484
  )
485
 
 
486
  for idx, row in edited_df.iterrows():
487
  edited_df.at[idx, 'میانگین ارتفاع'] = calculate_average_height(row)
488
 
489
+ st.subheader("خلاصه وضعیت داده‌ها")
 
 
490
  col1, col2, col3, col4 = st.columns(4)
491
  with col1:
492
  total_avg = edited_df['میانگین ارتفاع'].mean()
493
+ st.markdown(f'<div class="metric-card metric-green"><h3>{total_avg:.1f} سانتی‌متر</h3><p>میانگین کل ارتفاع</p></div>', unsafe_allow_html=True)
494
  with col2:
495
  farms_entered = len(edited_df[edited_df['میانگین ارتفاع'] > 0])
496
+ st.markdown(f'<div class="metric-card metric-blue"><h3>{farms_entered}/{len(filtered_farms)}</h3><p>مزارع واردشده</p></div>', unsafe_allow_html=True)
497
  with col3:
498
  avg_moisture = edited_df['رطوبت غلاف'].mean()
499
+ st.markdown(f'<div class="metric-card metric-yellow"><h3>{avg_moisture:.1f}%</h3><p>میانگین رطوبت</p></div>', unsafe_allow_html=True)
500
  with col4:
501
  avg_nitrogen = edited_df['نیتروژن'].mean()
502
+ st.markdown(f'<div class="metric-card metric-red"><h3>{avg_nitrogen:.1f}%</h3><p>میانگین نیتروژن</p></div>', unsafe_allow_html=True)
503
 
504
+ if st.button("💾 ذخیره اطلاعات"):
 
505
  if week_day_key not in st.session_state.farm_data:
506
  st.session_state.farm_data[week_day_key] = {}
507
  for idx, row in edited_df.iterrows():
 
518
  'نیتروژن': row['نیتروژن'],
519
  'میانگین ارتفاع': row['میانگین ارتفاع']
520
  }
521
+ st.success("داده‌ها ذخیره شدند!")
522
+ st.markdown('</div>', unsafe_allow_html=True)
523
 
524
+ # گزارش‌گیری
525
+ elif st.session_state.selected_tab == "گزارش‌گیری":
526
+ st.title("📑 گزارش‌گیری")
527
+ st.markdown('<div class="card">', unsafe_allow_html=True)
528
+ report_type = st.radio("نوع گزارش", ["گزارش مزرعه", "گزارش اداره"])
529
+ if report_type == "گزارش مزرعه":
530
+ st.subheader("گزارش به تفکیک مزرعه")
531
+ selected_farm = st.selectbox("انتخاب مزرعه", farms_sample_df['Farm_ID'].tolist())
532
+ farm_reports = weekly_report_df[weekly_report_df['Farm_ID'] == selected_farm].sort_values('Week')
533
+ if not farm_reports.empty:
534
+ fig = px.line(farm_reports, x='Week', y='Average_Height', title="روند رشد مزرعه", markers=True)
535
+ fig.update_layout(font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
536
+ st.plotly_chart(fig, use_container_width=True)
537
+ st.markdown('</div>', unsafe_allow_html=True)
538
+
539
+ # تحلیل داده‌ها
540
+ elif st.session_state.selected_tab == "تحلیل داده‌ها":
541
+ st.title("🔍 تحلیل داده‌ها")
542
+ st.markdown('<div class="card">', unsafe_allow_html=True)
543
+ variety_weekly = report_with_details.groupby(['Variety', 'Week'])['Average_Height'].mean().reset_index()
544
+ fig = px.line(variety_weekly, x='Week', y='Average_Height', color='Variety', title="تحلیل واریته‌ها", markers=True)
545
+ fig.update_layout(font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
546
+ st.plotly_chart(fig, use_container_width=True)
547
+ st.markdown('</div>', unsafe_allow_html=True)
548
+
549
+ # تنظیمات
550
+ elif st.session_state.selected_tab == "تنظیمات":
551
+ st.title("⚙️ تنظیمات")
552
+ st.markdown('<div class="card">', unsafe_allow_html=True)
553
+ st.write("در حال توسعه...")
554
+ st.markdown('</div>', unsafe_allow_html=True)