Esmaeilkianii commited on
Commit
d3068d2
·
verified ·
1 Parent(s): 8711916

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1697 -121
app.py CHANGED
@@ -1,125 +1,1701 @@
1
  import streamlit as st
2
- import folium
3
- from streamlit_folium import folium_static
4
- import ee
5
  import pandas as pd
6
- import altair as alt
7
- from concurrent.futures import ThreadPoolExecutor
8
  import numpy as np
9
-
10
- # احراز هویت Google Earth Engine
11
- service_account = 'dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com'
12
- credentials = ee.ServiceAccountCredentials(service_account, 'ee-esmaeilkiani13877-cfdea6eaf411.json')
13
- ee.Initialize(credentials)
14
-
15
- # بارگذاری داده‌ها
16
- farms_data = pd.read_csv('farm_coordinates.csv')
17
- local_data = pd.read_csv('پایگاه داده (1).csv')
18
-
19
- # تابع دریافت NDVI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  @st.cache_data
21
- def get_ndvi_image(farm_id, date):
22
- farm = farms_data[farms_data['Farm_ID'] == farm_id]
23
- geometry = ee.Geometry.Point([farm['Longitude'].values[0], farm['Latitude'].values[0]]).buffer(100)
24
- date_range = ee.Date(date).advance(-3, 'day'), ee.Date(date).advance(3, 'day')
25
- collection = ee.ImageCollection('COPERNICUS/S2') \
26
- .filterDate(date_range[0], date_range[1]) \
27
- .filterBounds(geometry) \
28
- .select(['B4', 'B8']) \
29
- .reduce(ee.Reducer.mean())
30
- ndvi = collection.normalizedDifference(['B8_mean', 'B4_mean']).rename('NDVI')
31
- return ndvi.clip(geometry)
32
-
33
- # تابع نمایش نقشه
34
- def show_ndvi_map(farm_id, date):
35
- ndvi_image = get_ndvi_image(farm_id, date)
36
- map_center = [farms_data[farms_data['Farm_ID'] == farm_id]['Latitude'].values[0],
37
- farms_data[farms_data['Farm_ID'] == farm_id]['Longitude'].values[0]]
38
- map = folium.Map(location=map_center, zoom_start=15)
39
- ndvi_vis = {'min': 0, 'max': 1, 'palette': ['red', 'yellow', 'green']}
40
- map.add_child(folium.GeoJson(ndvi_image.geometry(), name='Farm Boundary'))
41
- map.add_ee_layer(ndvi_image, ndvi_vis, 'NDVI')
42
- folium.LayerControl().add_to(map)
43
- return map
44
-
45
- # تابع تحلیل هفتگی
46
- def weekly_analysis(farm_id):
47
- dates = pd.date_range(end='2023-12-24', periods=5, freq='7D')
48
- ndvi_values = []
49
- height_values = []
50
- for date in dates:
51
- ndvi_image = get_ndvi_image(farm_id, date)
52
- ndvi_value = ndvi_image.reduceRegion(reducer=ee.Reducer.mean(), geometry=ndvi_image.geometry(), scale=10).get('NDVI').getInfo()
53
- ndvi_values.append(ndvi_value if ndvi_value else 0)
54
- local = local_data[(local_data['Farm_ID'] == farm_id) & (local_data['Date'] == str(date.date()))]
55
- height = local['Average_Height'].mean() if not local.empty else 0
56
- height_values.append(height)
57
-
58
- df = pd.DataFrame({'Date': dates, 'NDVI': ndvi_values, 'Height': height_values})
59
- df['NDVI_Change'] = df['NDVI'].diff()
60
- warnings = df[df['NDVI'] < 0.3].copy()
61
- suggestions = ["بررسی سیستم آبیاری" if h < df['Height'].mean() else "استفاده از کود" for h in warnings['Height']]
62
- warnings['Suggestions'] = suggestions
63
- return df, warnings
64
-
65
- # تابع نمودار رشد
66
- def growth_chart(farm_id):
67
- df, _ = weekly_analysis(farm_id)
68
- ndvi_chart = alt.Chart(df).mark_line().encode(
69
- x='Date:T', y='NDVI:Q', tooltip=['Date', 'NDVI']
70
- ).properties(title='رشد NDVI هفتگی').interactive()
71
- height_chart = alt.Chart(df).mark_line(color='green').encode(
72
- x='Date:T', y='Height:Q', tooltip=['Date', 'Height']
73
- ).properties(title='رشد ارتفاع هفتگی').interactive()
74
- return ndvi_chart, height_chart
75
-
76
- # رابط کاربری Streamlit
77
- st.title("پایش مزارع نیشکر")
78
-
79
- # سایدبار
80
- st.sidebar.title("تنظیمات")
81
- farm_id = st.sidebar.selectbox("انتخاب مزرعه", farms_data['Farm_ID'].astype(str) + " - " + farms_data['Farm_Name'])
82
- farm_id = int(farm_id.split(" - ")[0]) # استخراج Farm_ID
83
- date = st.sidebar.date_input("انتخاب تاریخ")
84
-
85
- # تب‌ها
86
- tabs = st.tabs(["نقشه NDVI", "تحلیل هفتگی", "نمودار رشد", "ورود داده‌های محلی"])
87
-
88
- # تب نقشه NDVI
89
- with tabs[0]:
90
- if st.button("نمایش نقشه"):
91
- with st.spinner("در حال بارگذاری نقشه..."):
92
- map = show_ndvi_map(farm_id, date)
93
- folium_static(map)
94
- st.write(f"وضعیت هواشناسی: 18°C") # مثال ساده
95
-
96
- # تب تحلیل هفتگی
97
- with tabs[1]:
98
- if st.button("نمایش تحلیل هفتگی"):
99
- df, warnings = weekly_analysis(farm_id)
100
- st.write("تغییرات NDVI و ارتفاع:")
101
- st.dataframe(df)
102
- if not warnings.empty:
103
- st.error("هشدارها:")
104
- for _, row in warnings.iterrows():
105
- st.write(f"تاریخ: {row['Date'].date()} - NDVI: {row['NDVI']:.2f} - پیشنهاد: {row['Suggestions']}")
106
-
107
- # تب نمودار رشد
108
- with tabs[2]:
109
- if st.button("نمایش نمودار"):
110
- ndvi_chart, height_chart = growth_chart(farm_id)
111
- st.altair_chart(ndvi_chart, use_container_width=True)
112
- st.altair_chart(height_chart, use_container_width=True)
113
-
114
- # تب ورود داده‌های محلی
115
- with tabs[3]:
116
- day = st.selectbox("انتخاب روز", ["شنبه", "یکشنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنج‌شنبه"])
117
- cols = st.columns(5)
118
- stations = [cols[i].number_input(f"ایستگاه {i+1}", min_value=0.0) for i in range(5)]
119
- groundwater = [st.number_input(f"چاهک {i+1}", min_value=0.0) for i in range(2)]
120
- moisture = st.number_input("رطوبت غلاف", min_value=0.0)
121
- nitrogen = st.number_input("نیتروژن", min_value=0.0)
122
- avg_height = np.mean(stations) if stations else 0
123
- st.write(f"میانگین ارتفاع: {avg_height:.2f}")
124
- if st.button("ذخیره داده‌ها"):
125
- st.success("داده‌ها ذخیره شدند!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
 
 
 
2
  import pandas as pd
 
 
3
  import numpy as np
4
+ import plotly.express as px
5
+ import plotly.graph_objects as go
6
+ from datetime import datetime, timedelta
7
+ import calendar
8
+ from PIL import Image
9
+ import base64
10
+ import io
11
+ # تنظیمات صفحه
12
+ st.set_page_config(
13
+ page_title="سامانه پایش مزارع نیشکر دهخدا",
14
+ page_icon="🌱",
15
+ layout="wide",
16
+ initial_sidebar_state="expanded"
17
+ )
18
+ # CSS برای فارسی‌سازی و بهبود ظاهر
19
+ st.markdown("""
20
+ <style>
21
+ @font-face {
22
+ font-family: 'IRANSans';
23
+ src: url('https://cdn.jsdelivr.net/gh/rastikerdar/[email protected]/dist/Sahel-FD.ttf') format('truetype');
24
+ }
25
+
26
+ * {
27
+ font-family: 'IRANSans', 'Sahel', sans-serif !important;
28
+ }
29
+
30
+ .main {
31
+ direction: rtl;
32
+ text-align: right;
33
+ }
34
+
35
+ .stButton button {
36
+ background-color: #1e6b45;
37
+ color: white;
38
+ border-radius: 8px;
39
+ padding: 10px 24px;
40
+ transition: all 0.3s;
41
+ }
42
+
43
+ .stButton button:hover {
44
+ background-color: #2a8c5c;
45
+ transform: translateY(-2px);
46
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
47
+ }
48
+
49
+ h1, h2, h3 {
50
+ color: #1e6b45;
51
+ }
52
+
53
+ .card {
54
+ background-color: #f8f9fa;
55
+ border-radius: 10px;
56
+ padding: 20px;
57
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
58
+ margin-bottom: 20px;
59
+ }
60
+
61
+ .status-good {
62
+ background-color: rgba(0, 200, 83, 0.2);
63
+ color: #00694b;
64
+ padding: 5px 10px;
65
+ border-radius: 5px;
66
+ font-weight: bold;
67
+ }
68
+
69
+ .status-medium {
70
+ background-color: rgba(255, 214, 0, 0.2);
71
+ color: #b7940a;
72
+ padding: 5px 10px;
73
+ border-radius: 5px;
74
+ font-weight: bold;
75
+ }
76
+
77
+ .status-bad {
78
+ background-color: rgba(255, 61, 0, 0.2);
79
+ color: #c62828;
80
+ padding: 5px 10px;
81
+ border-radius: 5px;
82
+ font-weight: bold;
83
+ }
84
+
85
+ .metric-card {
86
+ padding: 20px;
87
+ border-radius: 10px;
88
+ text-align: center;
89
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
90
+ transition: transform 0.3s;
91
+ }
92
+
93
+ .metric-card:hover {
94
+ transform: translateY(-5px);
95
+ }
96
+
97
+ .metric-green {
98
+ background-color: rgba(0, 200, 83, 0.2);
99
+ border-left: 5px solid #00c853;
100
+ }
101
+
102
+ .metric-yellow {
103
+ background-color: rgba(255, 214, 0, 0.2);
104
+ border-left: 5px solid #ffd600;
105
+ }
106
+
107
+ .metric-red {
108
+ background-color: rgba(255, 61, 0, 0.2);
109
+ border-left: 5px solid #ff3d00;
110
+ }
111
+
112
+ .metric-blue {
113
+ background-color: rgba(33, 150, 243, 0.2);
114
+ border-left: 5px solid #2196f3;
115
+ }
116
+
117
+ .stTabs [data-baseweb="tab-list"] {
118
+ gap: 10px;
119
+ }
120
+
121
+ .stTabs [data-baseweb="tab"] {
122
+ background-color: #f0f2f6;
123
+ border-radius: 8px 8px 0px 0px;
124
+ padding: 10px 20px;
125
+ margin-top: 5px;
126
+ }
127
+
128
+ .stTabs [aria-selected="true"] {
129
+ background-color: #1e6b45 !important;
130
+ color: white !important;
131
+ }
132
+ </style>
133
+ """, unsafe_allow_html=True)
134
+ # نمونه داده
135
  @st.cache_data
136
+ def generate_sample_data():
137
+ # ایجاد داده‌های مزارع
138
+ farm_data = {
139
+ 'Farm_ID': [f'{i:02d}-{j:02d}' for i in range(1, 5) for j in range(20, 30)],
140
+ 'Farm_Name': [f'مزرعه {i:02d}-{j:02d}' for i in range(1, 5) for j in range(20, 30)],
141
+ 'Channel_ID': [f'کانال {(i-1)*10 + j%10 + 1}' for i in range(1, 5) for j in range(20, 30)],
142
+ 'Administration_ID': [i for i in range(1, 5) for _ in range(10)],
143
+ 'Administration_Name': [f'اداره {i}' for i in range(1, 5) for _ in range(10)],
144
+ 'Production_Section': [1 if i <= 2 else 2 for i in range(1, 5) for _ in range(10)],
145
+ 'Area': [np.random.randint(50, 200) for _ in range(40)],
146
+ 'Variety': [np.random.choice(['CP69', 'CP73']) for _ in range(40)],
147
+ 'Crop_Age': [np.random.randint(1, 5) for _ in range(40)]
148
+ }
149
+
150
+ farms_df = pd.DataFrame(farm_data)
151
+
152
+ # ایجاد داده‌های ارتفاع
153
+ height_data = []
154
+ today = datetime.now()
155
+
156
+ # 5 هفته اخیر
157
+ for week in range(1, 6):
158
+ date = today - timedelta(days=(5-week)*7)
159
+ for farm_id in farms_df['Farm_ID']:
160
+ # مقادیر ایستگاه
161
+ stations = [np.random.randint(150, 220) for _ in range(5)]
162
+ # میانگین ارتفاع
163
+ avg_height = int(np.mean(stations))
164
+
165
+ # آب زیرزمینی
166
+ groundwater = [np.random.randint(50, 100) for _ in range(2)]
167
+
168
+ # رطوبت غلاف و نیتروژن
169
+ sheath_moisture = np.random.randint(60, 90)
170
+ nitrogen = np.random.randint(20, 40)
171
+
172
+ height_data.append({
173
+ 'Measurement_ID': f"{farm_id}-W{week}",
174
+ 'Farm_ID': farm_id,
175
+ 'Week': week,
176
+ 'Measurement_Date': date.strftime('%Y-%m-%d'),
177
+ 'Height': avg_height,
178
+ 'Station1': stations[0],
179
+ 'Station2': stations[1],
180
+ 'Station3': stations[2],
181
+ 'Station4': stations[3],
182
+ 'Station5': stations[4],
183
+ 'Groundwater1': groundwater[0],
184
+ 'Groundwater2': groundwater[1],
185
+ 'Sheath_Moisture': sheath_moisture,
186
+ 'Nitrogen': nitrogen
187
+ })
188
+
189
+ heights_df = pd.DataFrame(height_data)
190
+
191
+ # ایجاد داده‌های گزارش هفتگی
192
+ weekly_report_data = []
193
+
194
+ for farm_id in farms_df['Farm_ID']:
195
+ farm_heights = heights_df[heights_df['Farm_ID'] == farm_id].sort_values('Week')
196
+
197
+ for week in range(1, 6):
198
+ current_week_data = farm_heights[farm_heights['Week'] == week]
199
+ if current_week_data.empty:
200
+ continue
201
+
202
+ current_height = current_week_data['Height'].values[0]
203
+
204
+ # محاسبه تغییر رشد نسبت به هفته قبل
205
+ if week > 1:
206
+ prev_week_data = farm_heights[farm_heights['Week'] == week-1]
207
+ if not prev_week_data.empty:
208
+ prev_height = prev_week_data['Height'].values[0]
209
+ growth_change = current_height - prev_height
210
+ else:
211
+ growth_change = 0
212
+ else:
213
+ growth_change = 0
214
+
215
+ # محاسبه متوسط ارتفاع تمام مزارع در این هفته
216
+ all_farms_this_week = heights_df[heights_df['Week'] == week]
217
+ avg_height_all_farms = all_farms_this_week['Height'].mean()
218
+
219
+ # تعیین وضعیت بر اساس مقایسه با میانگین
220
+ if current_height > avg_height_all_farms + 5:
221
+ growth_status = 'خوب'
222
+ elif current_height < avg_height_all_farms - 5:
223
+ growth_status = 'ضعیف'
224
+ else:
225
+ growth_status = 'متوسط'
226
+
227
+ weekly_report_data.append({
228
+ 'Report_ID': f"{farm_id}-R{week}",
229
+ 'Farm_ID': farm_id,
230
+ 'Week': week,
231
+ 'Average_Height': current_height,
232
+ 'Growth_Change': growth_change,
233
+ 'Growth_Status': growth_status,
234
+ 'Regional_Average': int(avg_height_all_farms)
235
+ })
236
+
237
+ weekly_report_df = pd.DataFrame(weekly_report_data)
238
+
239
+ return farms_df, heights_df, weekly_report_df
240
+ # دریافت داده‌ها
241
+ farms_df, heights_df, weekly_report_df = generate_sample_data()
242
+ # ترکیب داده‌ها برای استفاده راحت‌تر
243
+ report_with_details = pd.merge(weekly_report_df, farms_df, on='Farm_ID')
244
+ latest_week = heights_df['Week'].max()
245
+ latest_reports = report_with_details[report_with_details['Week'] == latest_week]
246
+ # مدیریت وضعیت برنامه
247
+ if 'selected_tab' not in st.session_state:
248
+ st.session_state.selected_tab = "داشبورد"
249
+
250
+ if 'selected_day' not in st.session_state:
251
+ st.session_state.selected_day = "شنبه"
252
+ # منوی کناری
253
+ with st.sidebar:
254
+ st.image("https://via.placeholder.com/150x150.png?text=لوگو", width=150)
255
+ st.title("سامانه پایش مزارع نیشکر")
256
+ st.subheader("شرکت کشت و صنعت نیشکر دهخدا")
257
+
258
+ menu = st.selectbox(
259
+ "انتخاب صفحه",
260
+ ["داشبورد", "ورود اطلاعات", "گزارش‌گیری", "تحلیل داده‌ها", "تنظیمات"]
261
+ )
262
+
263
+ st.session_state.selected_tab = menu
264
+
265
+ st.markdown("---")
266
+ st.markdown("### راهنمای وضعیت رشد")
267
+
268
+ col1, col2 = st.columns(2)
269
+ with col1:
270
+ st.markdown('<div class="status-good">خوب</div>', unsafe_allow_html=True)
271
+ st.markdown('<div class="status-medium">متوسط</div>', unsafe_allow_html=True)
272
+ st.markdown('<div class="status-bad">ضعیف</div>', unsafe_allow_html=True)
273
+
274
+ with col2:
275
+ st.write("بالاتر از میانگین")
276
+ st.write("نزدیک به میانگین")
277
+ st.write("پایین‌تر از میانگین")
278
+
279
+ st.markdown("---")
280
+ st.info("نسخه آزمایشی 1.0")
281
+ # بخش داشبورد
282
+ if st.session_state.selected_tab == "داشبورد":
283
+ st.title("داشبورد پایش مزارع نیشکر")
284
+
285
+ # خلاصه وضعیت
286
+ st.subheader("وضعیت کلی مزارع")
287
+
288
+ # شمارش وضعیت‌های مختلف
289
+ status_counts = latest_reports['Growth_Status'].value_counts()
290
+ good_count = status_counts.get('خوب', 0)
291
+ medium_count = status_counts.get('متوسط', 0)
292
+ bad_count = status_counts.get('ضعیف', 0)
293
+
294
+ col1, col2, col3, col4 = st.columns(4)
295
+
296
+ with col1:
297
+ st.markdown('<div class="metric-card metric-green">'
298
+ f'<h2>{good_count}</h2>'
299
+ '<p>مزارع با وضعیت خوب</p>'
300
+ '</div>', unsafe_allow_html=True)
301
+
302
+ with col2:
303
+ st.markdown('<div class="metric-card metric-yellow">'
304
+ f'<h2>{medium_count}</h2>'
305
+ '<p>مزارع با وضعیت متوسط</p>'
306
+ '</div>', unsafe_allow_html=True)
307
+
308
+ with col3:
309
+ st.markdown('<div class="metric-card metric-red">'
310
+ f'<h2>{bad_count}</h2>'
311
+ '<p>مزارع با وضعیت ضعیف</p>'
312
+ '</div>', unsafe_allow_html=True)
313
+
314
+ with col4:
315
+ avg_height = int(latest_reports['Average_Height'].mean())
316
+ st.markdown('<div class="metric-card metric-blue">'
317
+ f'<h2>{avg_height} سانتی‌متر</h2>'
318
+ '<p>میانگین ارتفاع هفته جاری</p>'
319
+ '</div>', unsafe_allow_html=True)
320
+
321
+ st.markdown("---")
322
+
323
+ # نمودارها و گراف‌ها
324
+ col1, col2 = st.columns(2)
325
+
326
+ with col1:
327
+ st.subheader("میانگین ارتفاع به تفکیک اداره")
328
+
329
+ admin_heights = latest_reports.groupby('Administration_Name')['Average_Height'].mean().reset_index()
330
+ admin_heights['Average_Height'] = admin_heights['Average_Height'].astype(int)
331
+
332
+ fig = px.bar(
333
+ admin_heights,
334
+ x='Administration_Name',
335
+ y='Average_Height',
336
+ color='Average_Height',
337
+ color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
338
+ labels={'Administration_Name': 'اداره', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
339
+ )
340
+
341
+ fig.update_layout(
342
+ height=400,
343
+ xaxis_title="اداره",
344
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
345
+ font=dict(family="IRANSans"),
346
+ plot_bgcolor='rgba(0,0,0,0)'
347
+ )
348
+
349
+ st.plotly_chart(fig, use_container_width=True)
350
+
351
+ with col2:
352
+ st.subheader("مقایسه واریته‌ها")
353
+
354
+ variety_heights = latest_reports.groupby('Variety')['Average_Height'].mean().reset_index()
355
+ variety_heights['Average_Height'] = variety_heights['Average_Height'].astype(int)
356
+
357
+ fig = px.bar(
358
+ variety_heights,
359
+ x='Variety',
360
+ y='Average_Height',
361
+ color='Variety',
362
+ color_discrete_sequence=["#00c853", "#2196f3"],
363
+ labels={'Variety': 'واریته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
364
+ )
365
+
366
+ fig.update_layout(
367
+ height=400,
368
+ xaxis_title="واریته",
369
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
370
+ font=dict(family="IRANSans"),
371
+ plot_bgcolor='rgba(0,0,0,0)'
372
+ )
373
+
374
+ st.plotly_chart(fig, use_container_width=True)
375
+
376
+ # روند ارتفاع هفتگی
377
+ st.subheader("روند میانگین ارتفاع هفتگی")
378
+
379
+ weekly_avg = report_with_details.groupby('Week')['Average_Height'].mean().reset_index()
380
+ weekly_avg['Average_Height'] = weekly_avg['Average_Height'].astype(int)
381
+
382
+ fig = px.line(
383
+ weekly_avg,
384
+ x='Week',
385
+ y='Average_Height',
386
+ markers=True,
387
+ line_shape='spline',
388
+ labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
389
+ )
390
+
391
+ fig.update_traces(line=dict(width=3, color="#1e6b45"), marker=dict(size=10))
392
+
393
+ fig.update_layout(
394
+ height=400,
395
+ xaxis_title="هفته",
396
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
397
+ font=dict(family="IRANSans"),
398
+ plot_bgcolor='rgba(0,0,0,0)'
399
+ )
400
+
401
+ st.plotly_chart(fig, use_container_width=True)
402
+
403
+ # مزارع با بیشترین و کمترین رشد
404
+ st.markdown("---")
405
+ st.subheader("مزارع با بیشترین و کمترین رشد")
406
+
407
+ col1, col2 = st.columns(2)
408
+
409
+ # مزارع با بیشترین رشد
410
+ with col1:
411
+ st.markdown("#### بیشترین رشد هفتگی")
412
+ top_growth = report_with_details[report_with_details['Week'] == latest_week].sort_values('Growth_Change', ascending=False).head(5)
413
+
414
+ for _, row in top_growth.iterrows():
415
+ growth_color = "green" if row['Growth_Change'] > 0 else "red"
416
+ st.markdown(f"""
417
+ <div style="border: 1px solid #eee; padding: 10px; border-radius: 5px; margin-bottom: 10px;">
418
+ <h4 style="margin: 0;">{row['Farm_Name']} (اداره {row['Administration_ID']})</h4>
419
+ <p style="margin: 5px 0;">ارتفاع: {row['Average_Height']} سانتی‌متر</p>
420
+ <p style="color: {growth_color}; font-weight: bold; margin: 0;">
421
+ تغییر: {'+' if row['Growth_Change'] > 0 else ''}{row['Growth_Change']} سانتی‌متر
422
+ </p>
423
+ </div>
424
+ """, unsafe_allow_html=True)
425
+
426
+ # مزارع با کمترین رشد
427
+ with col2:
428
+ st.markdown("#### کمترین رشد هفتگی")
429
+ bottom_growth = report_with_details[report_with_details['Week'] == latest_week].sort_values('Growth_Change').head(5)
430
+
431
+ for _, row in bottom_growth.iterrows():
432
+ growth_color = "green" if row['Growth_Change'] > 0 else "red"
433
+ st.markdown(f"""
434
+ <div style="border: 1px solid #eee; padding: 10px; border-radius: 5px; margin-bottom: 10px;">
435
+ <h4 style="margin: 0;">{row['Farm_Name']} (اداره {row['Administration_ID']})</h4>
436
+ <p style="margin: 5px 0;">ارتفاع: {row['Average_Height']} سانتی‌متر</p>
437
+ <p style="color: {growth_color}; font-weight: bold; margin: 0;">
438
+ تغییر: {'+' if row['Growth_Change'] > 0 else ''}{row['Growth_Change']} سانتی‌متر
439
+ </p>
440
+ </div>
441
+ """, unsafe_allow_html=True)
442
+
443
+ # نقشه مزارع
444
+ st.markdown("---")
445
+ st.subheader("نقشه وضعیت مزارع")
446
+ st.markdown("(این نقشه به صورت نمادین نشان داده شده است. در نسخه عملیاتی با اطلاعات جغرافیایی دقیق پیاده‌سازی می‌شود.)")
447
+
448
+ # ایجاد نقشه نمادین با plotly
449
+ farm_status = latest_reports[['Farm_ID', 'Farm_Name', 'Administration_ID', 'Growth_Status']]
450
+
451
+ # ایجاد مختصات فرضی برای مزارع
452
+ np.random.seed(42) # برای تکرارپذیری
453
+ x_coords = np.random.uniform(0, 10, len(farm_status))
454
+ y_coords = np.random.uniform(0, 10, len(farm_status))
455
+
456
+ farm_status['x'] = x_coords
457
+ farm_status['y'] = y_coords
458
+
459
+ # تعیین رنگ بر اساس وضعیت
460
+ color_map = {
461
+ 'خوب': '#00c853',
462
+ 'متوسط': '#ffd600',
463
+ 'ضعیف': '#ff3d00'
464
+ }
465
+
466
+ farm_status['color'] = farm_status['Growth_Status'].map(color_map)
467
+
468
+ fig = go.Figure()
469
+
470
+ for status in ['خوب', 'متوسط', 'ضعیف']:
471
+ subset = farm_status[farm_status['Growth_Status'] == status]
472
+
473
+ fig.add_trace(go.Scatter(
474
+ x=subset['x'],
475
+ y=subset['y'],
476
+ mode='markers',
477
+ marker=dict(
478
+ size=15,
479
+ color=color_map[status],
480
+ line=dict(width=1, color='white')
481
+ ),
482
+ text=subset['Farm_Name'] + '<br>اداره ' + subset['Administration_ID'].astype(str),
483
+ hoverinfo='text',
484
+ name=status
485
+ ))
486
+
487
+ fig.update_layout(
488
+ height=500,
489
+ plot_bgcolor='rgba(240, 240, 240, 0.8)',
490
+ font=dict(family="IRANSans"),
491
+ showlegend=True,
492
+ xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
493
+ yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)
494
+ )
495
+
496
+ st.plotly_chart(fig, use_container_width=True)
497
+ # بخش ورود اطلاعات
498
+ elif st.session_state.selected_tab == "ورود اطلاعات":
499
+ st.title("ورود اطلاعات روزانه")
500
+
501
+ # انتخاب روز هفته
502
+ days_of_week = ["شنبه", "یکشنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنجشنبه"]
503
+ selected_day = st.selectbox("انتخاب روز", days_of_week, index=days_of_week.index(st.session_state.selected_day))
504
+ st.session_state.selected_day = selected_day
505
+
506
+ # انتخاب اداره
507
+ selected_admin = st.selectbox("انتخاب اداره", sorted(farms_df['Administration_ID'].unique()))
508
+
509
+ # فیلتر مزارع براساس اداره انتخاب شده
510
+ filtered_farms = farms_df[farms_df['Administration_ID'] == selected_admin]
511
+
512
+ st.subheader(f"ورود داده‌های روز {selected_day} - اداره {selected_admin}")
513
+
514
+ # ایجاد ستون‌های جدول
515
+ cols = st.columns((3, 1, 1, 1, 1, 1, 1, 1, 1, 1))
516
+ fields = ['مزرعه', 'ایستگاه 1', 'ایستگاه 2', 'ایستگاه 3', 'ایستگاه 4', 'ایستگاه 5', 'چاهک 1', 'چاهک 2', 'رطوبت غلاف', 'نیتروژن']
517
+
518
+ # عنوان ستون‌ها
519
+ for col, field_name in zip(cols, fields):
520
+ col.markdown(f"**{field_name}**")
521
+
522
+ # ایجاد حقول ورودی برای هر مزرعه
523
+ farm_data = {}
524
+
525
+ for _, farm in filtered_farms.iterrows():
526
+ farm_id = farm['Farm_ID']
527
+ farm_name = farm['Farm_Name']
528
+
529
+ cols = st.columns((3, 1, 1, 1, 1, 1, 1, 1, 1, 1))
530
+
531
+ cols[0].write(farm_name)
532
+
533
+ station_values = []
534
+
535
+ # ایستگاه‌های ارتفاع
536
+ for i in range(1, 6):
537
+ key = f"{farm_id}_station_{i}_{selected_day}"
538
+ value = cols[i].number_input("", min_value=0, max_value=300, step=1, key=key, label_visibility="collapsed")
539
+ station_values.append(value)
540
+
541
+ # چاهک‌های آب
542
+ for i in range(1, 3):
543
+ key = f"{farm_id}_groundwater_{i}_{selected_day}"
544
+ value = cols[5+i].number_input("", min_value=0, max_value=300, step=1, key=key, label_visibility="collapsed")
545
+
546
+ # رطوبت غلاف
547
+ key = f"{farm_id}_moisture_{selected_day}"
548
+ moisture = cols[7].number_input("", min_value=0, max_value=100, step=1, key=key, label_visibility="collapsed")
549
+
550
+ # نیتروژن
551
+ key = f"{farm_id}_nitrogen_{selected_day}"
552
+ nitrogen = cols[8].number_input("", min_value=0, max_value=100, step=1, key=key, label_visibility="collapsed")
553
+
554
+ # محاسبه میانگین ارتفاع
555
+ avg_height = 0
556
+ valid_stations = [s for s in station_values if s > 0]
557
+ if valid_stations:
558
+ avg_height = int(sum(valid_stations) / len(valid_stations))
559
+
560
+ # نمایش میانگین
561
+ cols[9].markdown(f"<div style='text-align: center; font-weight: bold;'>{avg_height if avg_height > 0 else '-'}</div>", unsafe_allow_html=True)
562
+
563
+ # ذخیره داده‌ها برای ثبت نهایی
564
+ farm_data[farm_id] = {
565
+ 'stations': station_values,
566
+ 'avg_height': avg_height,
567
+ 'moisture': moisture,
568
+ 'nitrogen': nitrogen
569
+ }
570
+
571
+ # دکمه ذخیره‌سازی
572
+ if st.button("ذخیره اطلاعات", key="save_daily_data"):
573
+ st.success("اطلاعات با موفقیت ذخیره شد. در نسخه عملیاتی، این داده‌ها در پایگاه داده ذخیره می‌شوند.")
574
+
575
+ # نمایش خلاصه داده‌های ثبت شده
576
+ st.subheader("خلاصه داده‌های ثبت شده:")
577
+
578
+ summary_data = []
579
+ for farm_id, data in farm_data.items():
580
+ if data['avg_height'] > 0: # فقط مزارعی که داده داشته‌اند
581
+ farm_info = filtered_farms[filtered_farms['Farm_ID'] == farm_id].iloc[0]
582
+ summary_data.append({
583
+ 'مزرعه': farm_info['Farm_Name'],
584
+ 'میانگین ارتفاع': data['avg_height'],
585
+ 'رطوبت غلاف': data['moisture'],
586
+ 'نیتروژن': data['nitrogen']
587
+ })
588
+
589
+ if summary_data:
590
+ summary_df = pd.DataFrame(summary_data)
591
+ st.dataframe(summary_df)
592
+ else:
593
+ st.info("هیچ داده‌ای برای نمایش وجود ندارد.")
594
+ # بخش گزارش‌گیری
595
+ elif st.session_state.selected_tab == "گزارش‌گیری":
596
+ st.title("گزارش‌های هفتگی")
597
+
598
+ # انتخاب نوع گزارش
599
+ report_type = st.radio(
600
+ "نوع گزارش",
601
+ ["گزارش به تفکیک مزرعه", "گزارش به تفکیک اداره", "گزارش به تفکیک کانال", "مزارع با وضعیت ضعیف"]
602
+ )
603
+
604
+ st.markdown("---")
605
+
606
+ if report_type == "گزارش به تفکیک مزرعه":
607
+ st.subheader("گزارش هفتگی به تفکیک مزرعه")
608
+
609
+ # انتخاب اداره
610
+ selected_admin = st.selectbox("انتخاب اداره", sorted(farms_df['Administration_ID'].unique()), key="admin_select_farm_report")
611
+
612
+ # فیلتر مزارع براساس اداره انتخاب شده
613
+ filtered_farms = farms_df[farms_df['Administration_ID'] == selected_admin]
614
+
615
+ # انتخاب مزرعه
616
+ selected_farm_id = st.selectbox(
617
+ "انتخاب مزرعه",
618
+ filtered_farms['Farm_ID'].tolist(),
619
+ format_func=lambda x: filtered_farms[filtered_farms['Farm_ID'] == x]['Farm_Name'].iloc[0]
620
+ )
621
+
622
+ # فیلتر داده‌های گزارش براساس مزرعه انتخاب شده
623
+ farm_reports = weekly_report_df[weekly_report_df['Farm_ID'] == selected_farm_id].sort_values('Week')
624
+
625
+ if not farm_reports.empty:
626
+ # اطلاعات مزرعه
627
+ farm_info = filtered_farms[filtered_farms['Farm_ID'] == selected_farm_id].iloc[0]
628
+
629
+ col1, col2, col3 = st.columns(3)
630
+ col1.metric("واریته", farm_info['Variety'])
631
+ col2.metric("سن کشت", f"{farm_info['Crop_Age']} سال")
632
+ col3.metric("مساحت", f"{farm_info['Area']} هکتار")
633
+
634
+ st.markdown("---")
635
+
636
+ # نمایش گراف روند رشد
637
+ st.subheader("روند رشد هفتگی")
638
+
639
+ fig = go.Figure()
640
+
641
+ # افزودن خط میانگین منطقه
642
+ fig.add_trace(go.Scatter(
643
+ x=farm_reports['Week'],
644
+ y=farm_reports['Regional_Average'],
645
+ mode='lines+markers',
646
+ name='میانگین منطقه',
647
+ line=dict(color='gray', width=2, dash='dot'),
648
+ marker=dict(size=8)
649
+ ))
650
+
651
+ # افزودن خط ارتفاع مزرعه
652
+ fig.add_trace(go.Scatter(
653
+ x=farm_reports['Week'],
654
+ y=farm_reports['Average_Height'],
655
+ mode='lines+markers',
656
+ name='ارتفاع مزرعه',
657
+ line=dict(color='#1e6b45', width=3),
658
+ marker=dict(size=10)
659
+ ))
660
+
661
+ fig.update_layout(
662
+ height=400,
663
+ xaxis_title="هفته",
664
+ yaxis_title="ارتفاع (سانتی‌متر)",
665
+ font=dict(family="IRANSans"),
666
+ plot_bgcolor='rgba(0,0,0,0)'
667
+ )
668
+
669
+ st.plotly_chart(fig, use_container_width=True)
670
+
671
+ # نمایش جدول داده‌ها
672
+ st.subheader("داده‌های هفتگی")
673
+
674
+ report_table = pd.DataFrame({
675
+ 'هفته': farm_reports['Week'],
676
+ 'تاریخ': farm_reports['Regional_Average'].apply(lambda x: f"هفته {x}"),
677
+ 'ارتفاع': farm_reports['Average_Height'],
678
+ 'میانگین منطقه': farm_reports['Regional_Average'],
679
+ 'تغییرات': farm_reports['Growth_Change'],
680
+ 'وضعیت': farm_reports['Growth_Status']
681
+ })
682
+
683
+ # تبدیل ستون وضعیت به صورت HTML با رنگ متناسب
684
+ def status_with_color(status):
685
+ colors = {'خوب': '#00c853', 'متوسط': '#ffd600', 'ضعیف': '#ff3d00'}
686
+ return f'<span style="color: {colors.get(status, "black")};">{status}</span>'
687
+
688
+ report_table['وضعیت'] = report_table['وضعیت'].apply(status_with_color)
689
+
690
+ st.markdown(report_table.to_html(escape=False, index=False), unsafe_allow_html=True)
691
+
692
+ # توصیه‌های کارشناسی
693
+ st.markdown("---")
694
+ st.subheader("توصیه‌های کارشناسی")
695
+
696
+ latest_report = farm_reports.iloc[-1]
697
+ latest_status = latest_report['Growth_Status']
698
+
699
+ if latest_status == 'خوب':
700
+ st.success("وضعیت رشد مزرعه مطلوب است. ادامه برنامه فعلی توصیه می‌شود.")
701
+ elif latest_status == 'متوسط':
702
+ st.warning("وضعیت رشد مزرعه متوسط است. بررسی وضعیت آبیاری و تغذیه توصیه می‌شود.")
703
+ else:
704
+ st.error("وضعیت رشد مزرعه ضعیف است. مشکلات احتمالی:")
705
+ st.markdown("""
706
+ - بررسی سیستم آبیاری و عدم گرفتگی
707
+ - بررسی وضعیت تغذیه و نیاز به کود
708
+ - بررسی آفات و بیماری‌های احتمالی
709
+ - بررسی وضعیت زهکشی مزرعه
710
+ """)
711
+ else:
712
+ st.warning("اطلاعاتی برای این مزرعه ثبت نشده است.")
713
+
714
+ elif report_type == "گزارش به تفکیک اداره":
715
+ st.subheader("گزارش هفتگی به تفکیک اداره")
716
+
717
+ # محاسبه میانگین ارتفاع به تفکیک اداره و هفته
718
+ admin_weekly_avg = report_with_details.groupby(['Administration_ID', 'Administration_Name', 'Week'])['Average_Height'].mean().reset_index()
719
+ admin_weekly_avg['Average_Height'] = admin_weekly_avg['Average_Height'].astype(int)
720
+
721
+ # نمودار مقایسه‌ای ادارات
722
+ fig = px.line(
723
+ admin_weekly_avg,
724
+ x='Week',
725
+ y='Average_Height',
726
+ color='Administration_Name',
727
+ markers=True,
728
+ labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Administration_Name': 'اداره'}
729
+ )
730
+
731
+ fig.update_traces(line=dict(width=3), marker=dict(size=8))
732
+
733
+ fig.update_layout(
734
+ height=500,
735
+ xaxis_title="هفته",
736
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
737
+ font=dict(family="IRANSans"),
738
+ plot_bgcolor='rgba(0,0,0,0)'
739
+ )
740
+
741
+ st.plotly_chart(fig, use_container_width=True)
742
+
743
+ # آخرین هفته
744
+ latest_week_data = admin_weekly_avg[admin_weekly_avg['Week'] == latest_week]
745
+
746
+ # جدول مقایسه آخرین هفته
747
+ st.subheader(f"مقایسه ادارات در هفته {latest_week}")
748
+
749
+ comparison_table = latest_week_data[['Administration_Name', 'Average_Height']].sort_values('Average_Height', ascending=False)
750
+ comparison_table.columns = ['اداره', 'میانگین ارتفاع (سانتی‌متر)']
751
+
752
+ st.table(comparison_table)
753
+
754
+ # رتبه‌بندی ادارات
755
+ st.subheader("رتبه‌بندی ادارات")
756
+
757
+ # حساب کردن میانگین رشد هر اداره
758
+ admin_growth = report_with_details.groupby(['Administration_ID', 'Administration_Name', 'Week'])['Growth_Change'].mean().reset_index()
759
+ admin_growth_latest = admin_growth[admin_growth['Week'] == latest_week].sort_values('Growth_Change', ascending=False)
760
+
761
+ # نمایش به صورت نمودار میله‌ای
762
+ fig = px.bar(
763
+ admin_growth_latest,
764
+ x='Administration_Name',
765
+ y='Growth_Change',
766
+ color='Growth_Change',
767
+ color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
768
+ labels={'Administration_Name': 'اداره', 'Growth_Change': 'میانگین تغییرات رشد (سانتی‌متر)'}
769
+ )
770
+
771
+ fig.update_layout(
772
+ height=400,
773
+ xaxis_title="اداره",
774
+ yaxis_title="میانگین تغییرات رشد (سانتی‌متر)",
775
+ font=dict(family="IRANSans"),
776
+ plot_bgcolor='rgba(0,0,0,0)'
777
+ )
778
+
779
+ st.plotly_chart(fig, use_container_width=True)
780
+
781
+ elif report_type == "گزارش به تفکیک کانال":
782
+ st.subheader("گزارش هفتگی به تفکیک کانال")
783
+
784
+ # انتخاب اداره
785
+ selected_admin = st.selectbox("انتخاب اداره", sorted(farms_df['Administration_ID'].unique()), key="admin_select_channel")
786
+
787
+ # فیلتر کانال‌های اداره انتخاب شده
788
+ filtered_channels = farms_df[farms_df['Administration_ID'] == selected_admin]['Channel_ID'].unique()
789
+
790
+ # محاسبه میانگین ارتفاع به تفکیک کانال و هفته
791
+ channel_data = pd.merge(report_with_details, farms_df[['Farm_ID', 'Channel_ID']], on='Farm_ID')
792
+ channel_weekly_avg = channel_data.groupby(['Channel_ID', 'Week'])['Average_Height'].mean().reset_index()
793
+ channel_weekly_avg['Average_Height'] = channel_weekly_avg['Average_Height'].astype(int)
794
+
795
+ # فیلتر کانال‌های اداره انتخاب شده
796
+ filtered_channel_data = channel_weekly_avg[channel_weekly_avg['Channel_ID'].isin(filtered_channels)]
797
+
798
+ # نمودار مقایسه‌ای کانال‌ها
799
+ fig = px.line(
800
+ filtered_channel_data,
801
+ x='Week',
802
+ y='Average_Height',
803
+ color='Channel_ID',
804
+ markers=True,
805
+ labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Channel_ID': 'کانال'}
806
+ )
807
+
808
+ fig.update_traces(line=dict(width=2), marker=dict(size=6))
809
+
810
+ fig.update_layout(
811
+ height=500,
812
+ xaxis_title="هفته",
813
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
814
+ font=dict(family="IRANSans"),
815
+ plot_bgcolor='rgba(0,0,0,0)'
816
+ )
817
+
818
+ st.plotly_chart(fig, use_container_width=True)
819
+
820
+ # آخرین هفته
821
+ latest_channel_data = filtered_channel_data[filtered_channel_data['Week'] == latest_week]
822
+
823
+ # جدول مقایسه آخرین هفته
824
+ st.subheader(f"مقایسه کانال‌ها در هفته {latest_week}")
825
+
826
+ comparison_table = latest_channel_data[['Channel_ID', 'Average_Height']].sort_values('Average_Height', ascending=False)
827
+ comparison_table.columns = ['کانال', 'میانگین ارتفاع (سانتی‌متر)']
828
+
829
+ st.table(comparison_table)
830
+
831
+ # نمودار میله‌ای مقایسه کانال‌ها
832
+ fig = px.bar(
833
+ comparison_table,
834
+ x='کانال',
835
+ y='میانگین ارتفاع (سانتی‌متر)',
836
+ color='میانگین ارتفاع (سانتی‌متر)',
837
+ color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
838
+ )
839
+
840
+ fig.update_layout(
841
+ height=400,
842
+ xaxis_title="کانال",
843
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
844
+ font=dict(family="IRANSans"),
845
+ plot_bgcolor='rgba(0,0,0,0)'
846
+ )
847
+
848
+ st.plotly_chart(fig, use_container_width=True)
849
+
850
+ elif report_type == "مزارع با وضعیت ضعیف":
851
+ st.subheader("گزارش مزارع با وضعیت ضعیف")
852
+
853
+ # فیلتر مزارع با وضعیت ضعیف در آخرین هفته
854
+ weak_farms = report_with_details[(report_with_details['Week'] == latest_week) &
855
+ (report_with_details['Growth_Status'] == 'ضعیف')]
856
+
857
+ if weak_farms.empty:
858
+ st.success("خوشبختانه هیچ مزرعه‌ای در وضعیت ضعیف وجود ندارد.")
859
+ else:
860
+ # تعداد مزارع ضعیف به تفکیک اداره
861
+ weak_by_admin = weak_farms.groupby('Administration_Name').size().reset_index(name='تعداد')
862
+
863
+ col1, col2 = st.columns(2)
864
+
865
+ with col1:
866
+ # نمودار دایره‌ای توزیع مزارع ضعیف
867
+ fig = px.pie(
868
+ weak_by_admin,
869
+ values='تعداد',
870
+ names='Administration_Name',
871
+ title='توزیع مزارع ضعیف بین ادارات'
872
+ )
873
+
874
+ fig.update_traces(textinfo='percent+label')
875
+
876
+ fig.update_layout(
877
+ height=350,
878
+ font=dict(family="IRANSans"),
879
+ )
880
+
881
+ st.plotly_chart(fig, use_container_width=True)
882
+
883
+ with col2:
884
+ # نمودار توزیع واریته‌ها
885
+ weak_by_variety = weak_farms.groupby('Variety').size().reset_index(name='تعداد')
886
+
887
+ fig = px.pie(
888
+ weak_by_variety,
889
+ values='تعداد',
890
+ names='Variety',
891
+ title='توزیع واریته‌ها در مزارع ضعیف'
892
+ )
893
+
894
+ fig.update_traces(textinfo='percent+label')
895
+
896
+ fig.update_layout(
897
+ height=350,
898
+ font=dict(family="IRANSans"),
899
+ )
900
+
901
+ st.plotly_chart(fig, use_container_width=True)
902
+
903
+ # جدول مزارع ضعیف
904
+ st.subheader("لیست مزارع با وضعیت ضعیف")
905
+
906
+ weak_farms_table = weak_farms[['Farm_Name', 'Administration_Name', 'Channel_ID', 'Average_Height', 'Regional_Average', 'Growth_Change', 'Variety', 'Crop_Age']]
907
+ weak_farms_table.columns = ['نام مزرعه', 'اداره', 'کانال', 'ارتفاع فعلی', 'میانگین منطقه', 'تغییر رشد', 'واریته', 'سن کشت']
908
+
909
+ st.dataframe(weak_farms_table.sort_values('ارتفاع فعلی'), use_container_width=True)
910
+
911
+ # تحلیل و پیشنهادات
912
+ st.markdown("---")
913
+ st.subheader("تحلیل عوامل مؤثر")
914
+
915
+ # مقایسه سن کشت
916
+ avg_age_weak = weak_farms['Crop_Age'].mean()
917
+ avg_age_all = farms_df['Crop_Age'].mean()
918
+
919
+ is_age_factor = avg_age_weak > avg_age_all + 0.5
920
+
921
+ # مقایسه واریته‌ها
922
+ variety_impact = weak_farms['Variety'].value_counts(normalize=True).idxmax()
923
+
924
+ # مقایسه کانال‌ها
925
+ channel_counts = weak_farms['Channel_ID'].value_counts()
926
+ problematic_channels = channel_counts[channel_counts > 2].index.tolist()
927
+
928
+ col1, col2 = st.columns(2)
929
+
930
+ with col1:
931
+ st.markdown("#### عوامل احتمالی")
932
+
933
+ factors = []
934
+
935
+ if is_age_factor:
936
+ factors.append("سن کشت بالا")
937
+
938
+ if len(weak_by_variety) == 1:
939
+ factors.append(f"واریته ({variety_impact})")
940
+
941
+ if problematic_channels:
942
+ channels_str = "، ".join(problematic_channels[:3])
943
+ factors.append(f"کانال‌های مشکل‌دار ({channels_str})")
944
+
945
+ if not factors:
946
+ factors.append("عوامل متفرقه")
947
+
948
+ for factor in factors:
949
+ st.markdown(f"- {factor}")
950
+
951
+ with col2:
952
+ st.markdown("#### اقدامات پیشنهادی")
953
+
954
+ st.markdown("""
955
+ - بازدید و بررسی میدانی مزارع ضعیف
956
+ - بررسی وضعیت آبیاری و زهکشی
957
+ - بررسی نیاز کودی و تغذیه
958
+ - بررسی آفات و بیماری‌های احتمالی
959
+ - برنامه‌ریزی برای جایگزینی مزارع با سن بالا
960
+ """)
961
+ # بخش تحلیل داده‌ها
962
+ elif st.session_state.selected_tab == "تحلیل داده‌ها":
963
+ st.title("تحلیل پیشرفته داده‌ها")
964
+
965
+ # انتخاب نوع تحلیل
966
+ analysis_type = st.selectbox(
967
+ "انتخاب نوع تحلیل",
968
+ ["تحلیل واریته‌ها", "تحلیل عوامل مؤثر بر رشد", "پیش‌بینی روند آینده", "مقایسه بخش‌های تولیدی"]
969
+ )
970
+
971
+ st.markdown("---")
972
+
973
+ if analysis_type == "تحلیل واریته‌ها":
974
+ st.subheader("مقایسه واریته‌های مختلف")
975
+
976
+ # تحلیل واریته‌ها در هفته‌های مختلف
977
+ variety_weekly = report_with_details.groupby(['Variety', 'Week'])['Average_Height'].mean().reset_index()
978
+ variety_weekly['Average_Height'] = variety_weekly['Average_Height'].round().astype(int)
979
+
980
+ # نمودار مقایسه‌ای واریته‌ها
981
+ fig = px.line(
982
+ variety_weekly,
983
+ x='Week',
984
+ y='Average_Height',
985
+ color='Variety',
986
+ markers=True,
987
+ labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Variety': 'واریته'}
988
+ )
989
+
990
+ fig.update_traces(line=dict(width=3), marker=dict(size=10))
991
+
992
+ fig.update_layout(
993
+ height=500,
994
+ xaxis_title="هفته",
995
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
996
+ font=dict(family="IRANSans"),
997
+ plot_bgcolor='rgba(0,0,0,0)'
998
+ )
999
+
1000
+ st.plotly_chart(fig, use_container_width=True)
1001
+
1002
+ # تحلیل رشد تجمعی واریته‌ها
1003
+ variety_growth = report_with_details.groupby(['Variety', 'Week'])['Growth_Change'].mean().reset_index()
1004
+
1005
+ # محاسبه رشد تجمعی
1006
+ varieties = variety_growth['Variety'].unique()
1007
+
1008
+ growth_data = []
1009
+
1010
+ for variety in varieties:
1011
+ variety_data = variety_growth[variety_growth['Variety'] == variety].sort_values('Week')
1012
+ cumulative_growth = 0
1013
+
1014
+ for _, row in variety_data.iterrows():
1015
+ week = row['Week']
1016
+ growth = row['Growth_Change']
1017
+ cumulative_growth += growth
1018
+
1019
+ growth_data.append({
1020
+ 'Variety': variety,
1021
+ 'Week': week,
1022
+ 'Cumulative_Growth': cumulative_growth
1023
+ })
1024
+
1025
+ cumulative_growth_df = pd.DataFrame(growth_data)
1026
+
1027
+ # نمودار رشد تجمعی
1028
+ st.subheader("رشد تجمعی واریته‌ها")
1029
+
1030
+ fig = px.line(
1031
+ cumulative_growth_df,
1032
+ x='Week',
1033
+ y='Cumulative_Growth',
1034
+ color='Variety',
1035
+ markers=True,
1036
+ labels={'Week': 'هفته', 'Cumulative_Growth': 'رشد تجمعی (سانتی‌متر)', 'Variety': 'واریته'}
1037
+ )
1038
+
1039
+ fig.update_traces(line=dict(width=3), marker=dict(size=10))
1040
+
1041
+ fig.update_layout(
1042
+ height=500,
1043
+ xaxis_title="هفته",
1044
+ yaxis_title="رشد تجمعی (سانتی‌متر)",
1045
+ font=dict(family="IRANSans"),
1046
+ plot_bgcolor='rgba(0,0,0,0)'
1047
+ )
1048
+
1049
+ st.plotly_chart(fig, use_container_width=True)
1050
+
1051
+ # تحلیل واریته‌ها بر اساس شاخص‌های دیگر
1052
+ st.subheader("شاخص‌های رشد به تفکیک واریته")
1053
+
1054
+ # میانگین تغییر رشد هفتگی
1055
+ avg_growth_by_variety = report_with_details.groupby('Variety')['Growth_Change'].mean().reset_index()
1056
+ avg_growth_by_variety['Growth_Change'] = avg_growth_by_variety['Growth_Change'].round(2)
1057
+
1058
+ # نسبت وضعیت‌های مختلف
1059
+ status_by_variety = report_with_details.groupby(['Variety', 'Growth_Status']).size().unstack(fill_value=0)
1060
+
1061
+ # محاسبه درصد‌ها
1062
+ status_by_variety['Total'] = status_by_variety.sum(axis=1)
1063
+
1064
+ for status in ['خوب', 'متوسط', 'ضعیف']:
1065
+ if status in status_by_variety.columns:
1066
+ status_by_variety[f'Percent_{status}'] = (status_by_variety[status] / status_by_variety['Total'] * 100).round(1)
1067
+
1068
+ # ترکیب داده‌ها
1069
+ variety_metrics = pd.merge(avg_growth_by_variety, status_by_variety.reset_index(), on='Variety')
1070
+
1071
+ col1, col2 = st.columns(2)
1072
+
1073
+ with col1:
1074
+ st.markdown("#### میانگین تغییر رشد هفتگی")
1075
+
1076
+ fig = px.bar(
1077
+ avg_growth_by_variety,
1078
+ x='Variety',
1079
+ y='Growth_Change',
1080
+ color='Growth_Change',
1081
+ color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
1082
+ labels={'Variety': 'واریته', 'Growth_Change': 'میانگین تغییر هفتگی (سانتی‌متر)'}
1083
+ )
1084
+
1085
+ fig.update_layout(
1086
+ height=350,
1087
+ xaxis_title="واریته",
1088
+ yaxis_title="میانگین تغییر هفتگی (سانتی‌متر)",
1089
+ font=dict(family="IRANSans"),
1090
+ plot_bgcolor='rgba(0,0,0,0)'
1091
+ )
1092
+
1093
+ st.plotly_chart(fig, use_container_width=True)
1094
+
1095
+ with col2:
1096
+ st.markdown("#### وضعیت رشد واریته‌ها")
1097
+
1098
+ status_data = []
1099
+
1100
+ for variety in varieties:
1101
+ variety_data = status_by_variety.loc[variety]
1102
+
1103
+ for status in ['خوب', 'متوسط', 'ضعیف']:
1104
+ if status in status_by_variety.columns:
1105
+ status_data.append({
1106
+ 'Variety': variety,
1107
+ 'Status': status,
1108
+ 'Percent': variety_data[f'Percent_{status}'] if f'Percent_{status}' in variety_data.index else 0
1109
+ })
1110
+
1111
+ status_df = pd.DataFrame(status_data)
1112
+
1113
+ fig = px.bar(
1114
+ status_df,
1115
+ x='Variety',
1116
+ y='Percent',
1117
+ color='Status',
1118
+ color_discrete_map={'خوب': '#00c853', 'متوسط': '#ffd600', 'ضعیف': '#ff3d00'},
1119
+ labels={'Variety': 'واریته', 'Percent': 'درصد', 'Status': 'وضعیت'}
1120
+ )
1121
+
1122
+ fig.update_layout(
1123
+ height=350,
1124
+ xaxis_title="واریته",
1125
+ yaxis_title="درصد",
1126
+ font=dict(family="IRANSans"),
1127
+ plot_bgcolor='rgba(0,0,0,0)',
1128
+ barmode='stack'
1129
+ )
1130
+
1131
+ st.plotly_chart(fig, use_container_width=True)
1132
+
1133
+ # تحلیل تأثیر سن کشت بر واریته‌ها
1134
+ st.subheader("تأثیر سن کشت بر واریته‌ها")
1135
+
1136
+ age_variety_height = report_with_details[report_with_details['Week'] == latest_week].groupby(['Variety', 'Crop_Age'])['Average_Height'].mean().reset_index()
1137
+
1138
+ fig = px.scatter(
1139
+ age_variety_height,
1140
+ x='Crop_Age',
1141
+ y='Average_Height',
1142
+ color='Variety',
1143
+ size='Average_Height',
1144
+ trendline='ols',
1145
+ labels={'Crop_Age': 'سن کشت (سال)', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Variety': 'واریته'}
1146
+ )
1147
+
1148
+ fig.update_layout(
1149
+ height=500,
1150
+ xaxis_title="سن کشت (سال)",
1151
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
1152
+ font=dict(family="IRANSans"),
1153
+ plot_bgcolor='rgba(0,0,0,0)'
1154
+ )
1155
+
1156
+ st.plotly_chart(fig, use_container_width=True)
1157
+
1158
+ elif analysis_type == "تحلیل عوامل مؤثر بر رشد":
1159
+ st.subheader("بررسی عوامل مؤثر بر رشد مزارع")
1160
+
1161
+ # عامل سن کشت
1162
+ st.markdown("#### تأثیر سن کشت بر ارتفاع")
1163
+
1164
+ age_height = report_with_details[report_with_details['Week'] == latest_week].groupby('Crop_Age')['Average_Height'].mean().reset_index()
1165
+
1166
+ fig = px.bar(
1167
+ age_height,
1168
+ x='Crop_Age',
1169
+ y='Average_Height',
1170
+ color='Average_Height',
1171
+ color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
1172
+ labels={'Crop_Age': 'سن کشت (سال)', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
1173
+ )
1174
+
1175
+ fig.update_layout(
1176
+ height=400,
1177
+ xaxis_title="سن کشت (سال)",
1178
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
1179
+ font=dict(family="IRANSans"),
1180
+ plot_bgcolor='rgba(0,0,0,0)'
1181
+ )
1182
+
1183
+ st.plotly_chart(fig, use_container_width=True)
1184
+
1185
+ # تحلیل توزیع وضعیت‌ها بر اساس سن کشت
1186
+ age_status = report_with_details[report_with_details['Week'] == latest_week].groupby(['Crop_Age', 'Growth_Status']).size().reset_index(name='Count')
1187
+
1188
+ fig = px.bar(
1189
+ age_status,
1190
+ x='Crop_Age',
1191
+ y='Count',
1192
+ color='Growth_Status',
1193
+ color_discrete_map={'خوب': '#00c853', 'متوسط': '#ffd600', 'ضعیف': '#ff3d00'},
1194
+ labels={'Crop_Age': 'سن کشت (سال)', 'Count': 'تعداد مزارع', 'Growth_Status': 'وضعیت'}
1195
+ )
1196
+
1197
+ fig.update_layout(
1198
+ height=400,
1199
+ xaxis_title="سن کشت (سال)",
1200
+ yaxis_title="تعداد مزارع",
1201
+ font=dict(family="IRANSans"),
1202
+ plot_bgcolor='rgba(0,0,0,0)',
1203
+ barmode='stack'
1204
+ )
1205
+
1206
+ st.plotly_chart(fig, use_container_width=True)
1207
+
1208
+ # تحلیل رابطه بین مساحت و رشد
1209
+ st.markdown("#### تأثیر مساحت بر رشد")
1210
+
1211
+ area_height = report_with_details[report_with_details['Week'] == latest_week]
1212
+
1213
+ fig = px.scatter(
1214
+ area_height,
1215
+ x='Area',
1216
+ y='Average_Height',
1217
+ color='Growth_Status',
1218
+ size='Growth_Change',
1219
+ color_discrete_map={'خوب': '#00c853', 'متوسط': '#ffd600', 'ضعیف': '#ff3d00'},
1220
+ labels={'Area': 'مساحت (هکتار)', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Growth_Status': 'وضعیت', 'Growth_Change': 'تغییر رشد'}
1221
+ )
1222
+
1223
+ fig.update_layout(
1224
+ height=500,
1225
+ xaxis_title="مساحت (هکتار)",
1226
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
1227
+ font=dict(family="IRANSans"),
1228
+ plot_bgcolor='rgba(0,0,0,0)'
1229
+ )
1230
+
1231
+ st.plotly_chart(fig, use_container_width=True)
1232
+
1233
+ # تحلیل اهمیت عوامل مختلف
1234
+ st.markdown("#### مقایسه اهمیت عوامل مختلف")
1235
+
1236
+ st.info("این بخش به صورت نمادین ارائه شده است و در نسخه عملیاتی با تحلیل واقعی و الگوریتم‌های یادگیری ماشین تکمیل می‌شود.")
1237
+
1238
+ factors = ['سن کشت', 'واریته', 'مساحت', 'اداره/کانال']
1239
+ importance = [75, 60, 30, 45]
1240
+
1241
+ fig = px.bar(
1242
+ x=factors,
1243
+ y=importance,
1244
+ labels={'x': 'عامل', 'y': 'میزان اهمیت (درصد)'},
1245
+ color=importance,
1246
+ color_continuous_scale=[(0, "#2196f3"), (1, "#1e6b45")]
1247
+ )
1248
+
1249
+ fig.update_layout(
1250
+ height=400,
1251
+ xaxis_title="عامل مؤثر",
1252
+ yaxis_title="میزان اهمیت (درصد)",
1253
+ font=dict(family="IRANSans"),
1254
+ plot_bgcolor='rgba(0,0,0,0)'
1255
+ )
1256
+
1257
+ st.plotly_chart(fig, use_container_width=True)
1258
+
1259
+ elif analysis_type == "پیش‌بینی روند آینده":
1260
+ st.subheader("پیش‌بینی روند رشد آینده")
1261
+
1262
+ st.info("این بخش به صورت نمادین ارائه شده است و در نسخه عملیاتی با الگوریتم‌های پیش‌بینی واقعی مانند ARIMA، رگرسیون یا شبکه‌های عصبی پیاده‌سازی می‌شود.")
1263
+
1264
+ # روند کلی ارتفاع
1265
+ weekly_avg = report_with_details.groupby('Week')['Average_Height'].mean().reset_index()
1266
+ weekly_avg['Average_Height'] = weekly_avg['Average_Height'].astype(int)
1267
+
1268
+ # پیش‌بینی ساده خطی
1269
+ weeks = weekly_avg['Week'].values
1270
+ heights = weekly_avg['Average_Height'].values
1271
+
1272
+ # پیش‌بینی ساده 3 هفته آینده
1273
+ future_weeks = np.array([latest_week + 1, latest_week + 2, latest_week + 3])
1274
+
1275
+ # تابع ساده برای ایجاد پیش‌بینی نمادین
1276
+ def simple_predict(weeks, heights, future_weeks):
1277
+ if len(weeks) >= 2:
1278
+ slope = (heights[-1] - heights[-2]) / (weeks[-1] - weeks[-2])
1279
+ last_height = heights[-1]
1280
+ last_week = weeks[-1]
1281
+
1282
+ predictions = [last_height + slope * (w - last_week) for w in future_weeks]
1283
+
1284
+ # اضافه کردن کمی نویز
1285
+ predictions = [p + np.random.randint(-3, 4) for p in predictions]
1286
+
1287
+ return predictions
1288
+ return [heights[-1]] * len(future_weeks)
1289
+
1290
+ future_heights = simple_predict(weeks, heights, future_weeks)
1291
+
1292
+ # ترکیب داده‌های واقعی و پیش‌بینی
1293
+ all_weeks = np.concatenate((weeks, future_weeks))
1294
+ all_heights = np.concatenate((heights, future_heights))
1295
+
1296
+ prediction_df = pd.DataFrame({
1297
+ 'Week': all_weeks,
1298
+ 'Average_Height': all_heights,
1299
+ 'Type': ['واقعی' if w <= latest_week else 'پیش‌بینی' for w in all_weeks]
1300
+ })
1301
+
1302
+ # نمودار پیش‌بینی
1303
+ fig = px.line(
1304
+ prediction_df,
1305
+ x='Week',
1306
+ y='Average_Height',
1307
+ color='Type',
1308
+ markers=True,
1309
+ color_discrete_map={'واقعی': '#1e6b45', 'پیش‌بینی': '#2196f3'},
1310
+ labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Type': 'نوع داده'}
1311
+ )
1312
+
1313
+ fig.update_traces(line=dict(width=3), marker=dict(size=10))
1314
+
1315
+ fig.add_vline(x=latest_week, line_dash="dash", line_color="gray")
1316
+
1317
+ fig.update_layout(
1318
+ height=500,
1319
+ xaxis_title="هفته",
1320
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
1321
+ font=dict(family="IRANSans"),
1322
+ plot_bgcolor='rgba(0,0,0,0)'
1323
+ )
1324
+
1325
+ st.plotly_chart(fig, use_container_width=True)
1326
+
1327
+ # پیش‌بینی به تفکیک اداره
1328
+ st.subheader("پیش‌بینی به تفکیک اداره")
1329
+
1330
+ admin_weekly_avg = report_with_details.groupby(['Administration_ID', 'Administration_Name', 'Week'])['Average_Height'].mean().reset_index()
1331
+ admin_weekly_avg['Average_Height'] = admin_weekly_avg['Average_Height'].astype(int)
1332
+
1333
+ admins = admin_weekly_avg['Administration_Name'].unique()
1334
+
1335
+ admin_predictions = []
1336
+
1337
+ for admin in admins:
1338
+ admin_data = admin_weekly_avg[admin_weekly_avg['Administration_Name'] == admin].sort_values('Week')
1339
+
1340
+ weeks = admin_data['Week'].values
1341
+ heights = admin_data['Average_Height'].values
1342
+
1343
+ future_heights = simple_predict(weeks, heights, future_weeks)
1344
+
1345
+ for w, h in zip(future_weeks, future_heights):
1346
+ admin_predictions.append({
1347
+ 'Week': w,
1348
+ 'Administration_Name': admin,
1349
+ 'Average_Height': h,
1350
+ 'Type': 'پیش‌بینی'
1351
+ })
1352
+
1353
+ admin_prediction_df = pd.DataFrame(admin_predictions)
1354
+
1355
+ # ترکیب با داده‌های واقعی
1356
+ admin_weekly_avg['Type'] = 'واقعی'
1357
+ combined_admin_df = pd.concat([admin_weekly_avg, admin_prediction_df])
1358
+
1359
+ # نمودار پیش‌بینی به تفکیک اداره
1360
+ fig = px.line(
1361
+ combined_admin_df,
1362
+ x='Week',
1363
+ y='Average_Height',
1364
+ color='Administration_Name',
1365
+ line_dash='Type',
1366
+ markers=True,
1367
+ labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Administration_Name': 'اداره', 'Type': 'نوع داده'}
1368
+ )
1369
+
1370
+ fig.update_traces(line=dict(width=2), marker=dict(size=8))
1371
+
1372
+ fig.add_vline(x=latest_week, line_dash="dash", line_color="gray")
1373
+
1374
+ fig.update_layout(
1375
+ height=500,
1376
+ xaxis_title="هفته",
1377
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
1378
+ font=dict(family="IRANSans"),
1379
+ plot_bgcolor='rgba(0,0,0,0)'
1380
+ )
1381
+
1382
+ st.plotly_chart(fig, use_container_width=True)
1383
+
1384
+ # خلاصه پیش‌بینی‌ها
1385
+ st.subheader("خلاصه پیش‌بینی‌ها برای هفته آینده")
1386
+
1387
+ next_week_prediction = admin_prediction_df[admin_prediction_df['Week'] == latest_week + 1][['Administration_Name', 'Average_Height']]
1388
+ next_week_prediction.columns = ['اداره', 'پیش‌بینی ارتفاع (سانتی‌متر)']
1389
+ next_week_prediction = next_week_prediction.sort_values('پیش‌بینی ارتفاع (سانتی‌متر)', ascending=False)
1390
+
1391
+ st.table(next_week_prediction)
1392
+
1393
+ elif analysis_type == "مقایسه بخش‌های تولیدی":
1394
+ st.subheader("مقایسه بخش‌های تولیدی")
1395
+
1396
+ # داده‌های بخش‌های تولیدی
1397
+ section_data = report_with_details.copy()
1398
+ section_data['Production_Section'] = section_data['Administration_ID'].apply(lambda x: 1 if x <= 2 else 2)
1399
+ section_data['Production_Section_Name'] = section_data['Production_Section'].apply(lambda x: f'بخش {x}')
1400
+
1401
+ # میانگین ارتفاع به تفکیک بخش و هفته
1402
+ section_weekly_avg = section_data.groupby(['Production_Section_Name', 'Week'])['Average_Height'].mean().reset_index()
1403
+ section_weekly_avg['Average_Height'] = section_weekly_avg['Average_Height'].astype(int)
1404
+
1405
+ # نمودار مقایسه‌ای
1406
+ fig = px.line(
1407
+ section_weekly_avg,
1408
+ x='Week',
1409
+ y='Average_Height',
1410
+ color='Production_Section_Name',
1411
+ markers=True,
1412
+ line_shape='spline',
1413
+ labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Production_Section_Name': 'بخش ت��لیدی'}
1414
+ )
1415
+
1416
+ fig.update_traces(line=dict(width=3), marker=dict(size=10))
1417
+
1418
+ fig.update_layout(
1419
+ height=500,
1420
+ xaxis_title="هفته",
1421
+ yaxis_title="میانگین ارتفاع (سانتی‌متر)",
1422
+ font=dict(family="IRANSans"),
1423
+ plot_bgcolor='rgba(0,0,0,0)'
1424
+ )
1425
+
1426
+ st.plotly_chart(fig, use_container_width=True)
1427
+
1428
+ # مقایسه تغییرات رشد
1429
+ section_growth = section_data.groupby(['Production_Section_Name', 'Week'])['Growth_Change'].mean().reset_index()
1430
+ section_growth['Growth_Change'] = section_growth['Growth_Change'].round(2)
1431
+
1432
+ # نمودار تغییرات
1433
+ fig = px.bar(
1434
+ section_growth,
1435
+ x='Week',
1436
+ y='Growth_Change',
1437
+ color='Production_Section_Name',
1438
+ barmode='group',
1439
+ labels={'Week': 'هفته', 'Growth_Change': 'میانگین تغییر رشد (سانتی‌متر)', 'Production_Section_Name': 'بخش تولیدی'}
1440
+ )
1441
+
1442
+ fig.update_layout(
1443
+ height=400,
1444
+ xaxis_title="هفته",
1445
+ yaxis_title="میانگین تغییر رشد (سانتی‌متر)",
1446
+ font=dict(family="IRANSans"),
1447
+ plot_bgcolor='rgba(0,0,0,0)'
1448
+ )
1449
+
1450
+ st.plotly_chart(fig, use_container_width=True)
1451
+
1452
+ # مقایسه توزیع وضعیت‌ها
1453
+ section_status = section_data[section_data['Week'] == latest_week].groupby(['Production_Section_Name', 'Growth_Status']).size().reset_index(name='Count')
1454
+
1455
+ # محاسبه درصدها
1456
+ section_total = section_status.groupby('Production_Section_Name')['Count'].sum().reset_index()
1457
+ section_status = pd.merge(section_status, section_total, on='Production_Section_Name', suffixes=('', '_Total'))
1458
+ section_status['Percent'] = (section_status['Count'] / section_status['Count_Total'] * 100).round(1)
1459
+
1460
+ # نمودار توزیع
1461
+ fig = px.bar(
1462
+ section_status,
1463
+ x='Production_Section_Name',
1464
+ y='Percent',
1465
+ color='Growth_Status',
1466
+ color_discrete_map={'خوب': '#00c853', 'متوسط': '#ffd600', 'ضعیف': '#ff3d00'},
1467
+ labels={'Production_Section_Name': 'بخش تولیدی', 'Percent': 'درصد', 'Growth_Status': 'وضعیت'},
1468
+ barmode='stack'
1469
+ )
1470
+
1471
+ fig.update_layout(
1472
+ height=400,
1473
+ xaxis_title="بخش تولیدی",
1474
+ yaxis_title="درصد",
1475
+ font=dict(family="IRANSans"),
1476
+ plot_bgcolor='rgba(0,0,0,0)'
1477
+ )
1478
+
1479
+ st.plotly_chart(fig, use_container_width=True)
1480
+
1481
+ # مقایسه مزارع برتر هر بخش
1482
+ st.subheader("مزارع برتر هر بخش")
1483
+
1484
+ col1, col2 = st.columns(2)
1485
+
1486
+ for section_id, section_col in [(1, col1), (2, col2)]:
1487
+ with section_col:
1488
+ st.markdown(f"#### بخش {section_id}")
1489
+
1490
+ top_farms = section_data[(section_data['Week'] == latest_week) & (section_data['Production_Section'] == section_id)].nlargest(5, 'Average_Height')
1491
+
1492
+ for _, farm in top_farms.iterrows():
1493
+ st.markdown(f"""
1494
+ <div style="border: 1px solid #eee; padding: 10px; border-radius: 5px; margin-bottom: 10px;">
1495
+ <h4 style="margin: 0;">{farm['Farm_Name']}</h4>
1496
+ <p style="margin: 5px 0;">ارتفاع: {farm['Average_Height']} سانتی‌متر</p>
1497
+ <p style="margin: 0;">اداره {farm['Administration_ID']} - {farm['Channel_ID']}</p>
1498
+ </div>
1499
+ """, unsafe_allow_html=True)
1500
+ # بخش تنظیمات
1501
+ elif st.session_state.selected_tab == "تنظیمات":
1502
+ st.title("تنظیمات سیستم")
1503
+
1504
+ # زبانه‌های تنظیمات
1505
+ settings_tab = st.selectbox(
1506
+ "بخش تنظیمات",
1507
+ ["تنظیمات عمومی", "مدیریت کاربران", "معیارهای ارزیابی", "پشتیبان‌گیری و بازیابی"]
1508
+ )
1509
+
1510
+ st.markdown("---")
1511
+
1512
+ if settings_tab == "تنظیمات عمومی":
1513
+ st.subheader("تنظیمات عمومی برنامه")
1514
+
1515
+ col1, col2 = st.columns(2)
1516
+
1517
+ with col1:
1518
+ st.markdown("#### تنظیمات نمایش")
1519
+
1520
+ theme = st.selectbox("تم برنامه", ["روشن", "تیره", "خودکار"])
1521
+ date_format = st.selectbox("قالب نمایش تاریخ", ["شمسی", "میلادی"])
1522
+ language = st.selectbox("زبان", ["فارسی", "انگلیسی"])
1523
+
1524
+ st.checkbox("نمایش راهنمای کاربری در شروع برنامه", value=True)
1525
+ st.checkbox("فعال‌سازی نم��یش هشدارها", value=True)
1526
+
1527
+ with col2:
1528
+ st.markdown("#### تنظیمات گزارش‌گیری")
1529
+
1530
+ default_admin = st.selectbox("اداره پیش‌فرض", [f"اداره {i}" for i in range(1, 5)])
1531
+ rows_per_page = st.slider("تعداد ردیف در هر صفحه", 10, 100, 25, 5)
1532
+
1533
+ st.checkbox("فعال‌سازی گزارش‌های خودکار هفتگی", value=True)
1534
+ st.checkbox("ارسال گزارش از طریق ایمیل", value=False)
1535
+
1536
+ report_email = st.text_input("ایمیل دریافت گزارش‌ها")
1537
+
1538
+ st.markdown("---")
1539
+
1540
+ col1, col2 = st.columns(2)
1541
+
1542
+ with col1:
1543
+ st.markdown("#### تنظیمات پیشرفته")
1544
+
1545
+ st.number_input("حداکثر روزهای نمایش تاریخچه", value=60, min_value=7, max_value=365)
1546
+ st.number_input("فاصله به‌روزرسانی خودکار (دقیقه)", value=30, min_value=5, max_value=120)
1547
+
1548
+ if st.button("ذخیره تنظیمات"):
1549
+ st.success("تنظیمات با موفقیت ذخیره شد.")
1550
+
1551
+ elif settings_tab == "مدیریت کاربران":
1552
+ st.subheader("مدیریت کاربران سیستم")
1553
+
1554
+ # نمونه کاربران
1555
+ users = [
1556
+ {"id": 1, "username": "admin", "name": "مدیر سیستم", "role": "مدیر", "active": True},
1557
+ {"id": 2, "username": "supervisor1", "name": "ناظر اداره 1", "role": "ناظر", "active": True},
1558
+ {"id": 3, "username": "supervisor2", "name": "ناظر اداره 2", "role": "ناظر", "active": True},
1559
+ {"id": 4, "username": "operator1", "name": "اپراتور 1", "role": "اپراتور", "active": True},
1560
+ {"id": 5, "username": "operator2", "name": "اپراتور 2", "role": "اپراتور", "active": False}
1561
+ ]
1562
+
1563
+ users_df = pd.DataFrame(users)
1564
+
1565
+ # فیلتر کاربران
1566
+ role_filter = st.selectbox("نمایش بر اساس نقش", ["همه", "مدیر", "ناظر", "اپراتور"])
1567
+
1568
+ if role_filter != "همه":
1569
+ filtered_users = users_df[users_df["role"] == role_filter]
1570
+ else:
1571
+ filtered_users = users_df
1572
+
1573
+ # نمایش کاربران
1574
+ for _, user in filtered_users.iterrows():
1575
+ col1, col2, col3 = st.columns([3, 2, 1])
1576
+
1577
+ with col1:
1578
+ st.write(f"**{user['name']}** ({user['username']})")
1579
+
1580
+ with col2:
1581
+ st.write(f"نقش: {user['role']}")
1582
+
1583
+ with col3:
1584
+ status = "فعال" if user["active"] else "غیرفعال"
1585
+ status_color = "green" if user["active"] else "red"
1586
+ st.markdown(f"<span style='color: {status_color};'>{status}</span>", unsafe_allow_html=True)
1587
+
1588
+ st.markdown("---")
1589
+
1590
+ # افزودن کاربر جدید
1591
+ st.subheader("افزودن کاربر جدید")
1592
+
1593
+ col1, col2 = st.columns(2)
1594
+
1595
+ with col1:
1596
+ new_username = st.text_input("نام کاربری")
1597
+ new_name = st.text_input("نام و نام خانوادگی")
1598
+
1599
+ with col2:
1600
+ new_role = st.selectbox("نقش", ["مدیر", "ناظر", "اپراتور"])
1601
+ new_password = st.text_input("رمز عبور", type="password")
1602
+
1603
+ if st.button("افزودن کاربر"):
1604
+ if new_username and new_name and new_password:
1605
+ st.success(f"کاربر {new_name} با موفقیت اضافه شد.")
1606
+ else:
1607
+ st.error("لطفاً تمام فیلدها را پر کنید.")
1608
+
1609
+ elif settings_tab == "معیارهای ارزیابی":
1610
+ st.subheader("تنظیم معیارهای ارزیابی")
1611
+
1612
+ # معیارهای ارتفاع
1613
+ st.markdown("#### معیارهای ارتفاع")
1614
+
1615
+ col1, col2 = st.columns(2)
1616
+
1617
+ with col1:
1618
+ st.number_input("حداقل ارتفاع مطلوب (سانتی‌متر)", value=180, step=5)
1619
+ st.number_input("حداقل تغییر رشد هفتگی مطلوب (سانتی‌متر)", value=5, step=1)
1620
+
1621
+ with col2:
1622
+ st.number_input("ارتفاع بحرانی (سانتی‌متر)", value=150, step=5)
1623
+ st.number_input("تغییر رشد هفتگی بحرانی (سانتی‌متر)", value=0, step=1)
1624
+
1625
+ # معیارهای شرایط محیطی
1626
+ st.markdown("#### معیارهای شرایط محیطی")
1627
+
1628
+ col1, col2 = st.columns(2)
1629
+
1630
+ with col1:
1631
+ st.number_input("حداقل رطوبت غلاف مطلوب (%)", value=70, step=5)
1632
+ st.number_input("حداکثر عمق آب زی��زمینی مطلوب (سانتی‌متر)", value=80, step=5)
1633
+
1634
+ with col2:
1635
+ st.number_input("حداقل نیتروژن مطلوب (%)", value=30, step=5)
1636
+
1637
+ # معیارهای مقایسه‌ای
1638
+ st.markdown("#### معیارهای مقایسه‌ای")
1639
+
1640
+ comparison_method = st.radio(
1641
+ "روش مقایسه برای تعیین وضعیت",
1642
+ ["مقایسه با میانگین کل", "مقایسه با میانگین اداره", "مقایسه با مقادیر مطلق"]
1643
+ )
1644
+
1645
+ if comparison_method == "مقایسه با میانگین کل":
1646
+ st.slider("آستانه انحراف از میانگین (سانتی‌متر)", 0, 20, 5)
1647
+ elif comparison_method == "مقایسه با میانگین اداره":
1648
+ st.slider("آستانه انحراف از میانگین اداره (سانتی‌متر)", 0, 20, 5)
1649
+
1650
+ if st.button("ذخیره معیارها"):
1651
+ st.success("معیارهای ارزیابی با موفقیت ذخیره شد.")
1652
+
1653
+ elif settings_tab == "پشتیبان‌گیری و بازیابی":
1654
+ st.subheader("پشتیبان‌گیری و بازیابی داده‌ها")
1655
+
1656
+ # پشتیبان‌گیری
1657
+ st.markdown("#### پشتیبان‌گیری")
1658
+
1659
+ backup_type = st.radio("نوع پشتیبان‌گیری", ["پشتیبان کامل", "فقط داده‌های ارتفاع", "فقط تنظیمات"])
1660
+
1661
+ col1, col2 = st.columns(2)
1662
+
1663
+ with col1:
1664
+ st.checkbox("فشرده‌سازی فایل پشتیبان", value=True)
1665
+
1666
+ with col2:
1667
+ st.checkbox("پشتیبان‌گیری خودکار هفتگی", value=True)
1668
+
1669
+ if st.button("ایجاد فایل پشتیبان"):
1670
+ # ایجاد فایل CSV پشتیبان
1671
+ if backup_type == "پشتیبان کامل" or backup_type == "فقط داده‌های ارتفاع":
1672
+ buffer = io.StringIO()
1673
+ heights_df.to_csv(buffer, index=False)
1674
+ buffer.seek(0)
1675
+
1676
+ # تبدیل به دانلود
1677
+ st.download_button(
1678
+ label="دانلود فایل پشتیبان",
1679
+ data=buffer,
1680
+ file_name="height_data_backup.csv",
1681
+ mime="text/csv"
1682
+ )
1683
+ else:
1684
+ st.success("فایل پشتیبان تنظیمات با موفقیت ایجاد شد.")
1685
+
1686
+ # بازیابی
1687
+ st.markdown("---")
1688
+ st.markdown("#### بازیابی داده‌ها")
1689
+
1690
+ upload_file = st.file_uploader("انتخاب فایل پشتیبان", type=["csv", "zip"])
1691
+
1692
+ if upload_file is not None:
1693
+ if st.button("بازیابی داده‌ها"):
1694
+ st.success("داده‌ها با موفقیت بازیابی شد.")
1695
+ st.warning("توجه: این عملیات در نسخه عملیاتی، داده‌های فعلی را با داده‌های پشتیبان جایگزین می‌کند.")
1696
+ # نمایش مکان‌نما
1697
+ st.markdown("""
1698
+ <div style="position: fixed; bottom: 20px; right: 20px; background-color: #1e6b45; color: white; padding: 10px 15px; border-radius: 50px; font-size: 14px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); z-index: 1000;">
1699
+ آخرین به‌روزرسانی: همین حالا
1700
+ </div>
1701
+ """, unsafe_allow_html=True)