Sugarcane / app.py
Esmaeilkianii's picture
Update app.py
339d280 verified
raw
history blame
36.1 kB
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime, timedelta
import calendar
from PIL import Image
import base64
import io
import os
import ee
import json
import folium
from streamlit_folium import folium_static
import time
# تنظیمات صفحه
st.set_page_config(
page_title="سامانه پایش مزارع نیشکر دهخدا",
page_icon="🌱",
layout="wide",
initial_sidebar_state="expanded"
)
# CSS برای فارسی‌سازی و بهبود ظاهر
st.markdown("""
<style>
@font-face {
font-family: 'IRANSans';
src: url('https://cdn.jsdelivr.net/gh/rastikerdar/[email protected]/dist/Sahel-FD.ttf') format('truetype');
}
* {
font-family: 'IRANSans', 'Sahel', sans-serif !important;
}
.main {
direction: rtl;
text-align: right;
}
.stButton button {
background-color: #1e6b45;
color: white;
border-radius: 8px;
padding: 10px 24px;
transition: all 0.3s;
}
.stButton button:hover {
background-color: #2a8c5c;
transform: translateY(-2px);
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
h1, h2, h3 {
color: #1e6b45;
}
.card {
background-color: #f8f9fa;
border-radius: 10px;
padding: 20px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.status-good {
background-color: rgba(0, 200, 83, 0.2);
color: #00694b;
padding: 5px 10px;
border-radius: 5px;
font-weight: bold;
}
.status-medium {
background-color: rgba(255, 214, 0, 0.2);
color: #b7940a;
padding: 5px 10px;
border-radius: 5px;
font-weight: bold;
}
.status-bad {
background-color: rgba(255, 61, 0, 0.2);
color: #c62828;
padding: 5px 10px;
border-radius: 5px;
font-weight: bold;
}
.metric-card {
padding: 20px;
border-radius: 10px;
text-align: center;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: transform 0.3s;
}
.metric-card:hover {
transform: translateY(-5px);
}
.metric-green {
background-color: rgba(0, 200, 83, 0.2);
border-left: 5px solid #00c853;
}
.metric-yellow {
background-color: rgba(255, 214, 0, 0.2);
border-left: 5px solid #ffd600;
}
.metric-red {
background-color: rgba(255, 61, 0, 0.2);
border-left: 5px solid #ff3d00;
}
.metric-blue {
background-color: rgba(33, 150, 243, 0.2);
border-left: 5px solid #2196f3;
}
.stTabs [data-baseweb="tab-list"] {
gap: 10px;
}
.stTabs [data-baseweb="tab"] {
background-color: #f0f2f6;
border-radius: 8px 8px 0px 0px;
padding: 10px 20px;
margin-top: 5px;
}
.stTabs [aria-selected="true"] {
background-color: #1e6b45 !important;
color: white !important;
}
input[type="number"] {
direction: ltr !important;
text-align: left !important;
}
.dataframe {
direction: ltr !important;
}
.dataframe th, .dataframe td {
text-align: left !important;
}
.map-container {
height: 500px;
width: 100%;
border-radius: 10px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
overflow: hidden;
}
</style>
""", unsafe_allow_html=True)
# تنظیم Earth Engine (بدون تغییر)
@st.cache_resource
def initialize_earth_engine():
try:
credentials_path = "ee-esmaeilkiani13877-cfdea6eaf411.json"
if not os.path.exists(credentials_path):
credentials_dict = {
"type": "service_account",
"project_id": "ee-esmaeilkiani13877",
"private_key_id": "cfdea6eaf411",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDJ/yKp8y/jQBPM\n4YVhfMJKTFMgpKqnHjFm/HYxs9JZ3TbYGcIm1B+p61mFzc0YeZ80K7it6C9I+PKe\n8n9HnVjzTKHkLIl38OKIv8sxB2Z8Imi/aZl1Xq+ilcJ7gXU2NwvfCPOLjqYZrYKX\ntV4SJAJ8BvnPi9NMlMIwYeI4q+S8e/2XtYs/91uFQVQcDzIBZHKj8QIzfRTUPkWS\nR8C+QyGXcWJ+Z0qbkH5re+h1+qR55XVkmAj3R7DHx+Z0D6KmPGCKlW+qp07KjDcK\nB0JfG1IZvbXlJD7HJ3A9mM8LAZotGI/1sFMIFSvUopV2QC6nffn8GYxeIGIiStO0\nUwCvxEtnAgMBAAECggEAGAHplcHENLgWfxNb0goGCFrUplS+Ub4AOtCyWIwIzz+4\nEYnwPSNXUQdWuG6JS9EqIzrD0UY/yVQNtPCK6QrkbNpXgyUPdCMvQToLBi+XrS0M\nq2/yIu1mzDL8J5khG1zRjdtHOfLm3xgEcJ6h4c5bjGiKlK5F9tfgYRBEBnTG3m9X\nvSu/j3wlR01XEE8rYOUbzlq0FLHLh+XEPPcsHqmUKY6dGP/8LrHPL+QBwzZ9RcFU\nOHhc0NBaxkRb4vF8UEjA9PpYEMBg90UfaF9Jnkf+A3CyTNkeKvmIcHgwIR3cyM/s\nBCu8p9MpaBcKKT5gQJvk2A4WOrJz5Ag1tYgp7OeOQQKBgQD5mwxh3k1PcZ9Ugubn\ndIrJ3aLRpdhbQYoCyzQqffNp9OX6NqNBu4dDMe4+QFBM6XTYNyj4vqLQk7NQ8kh5\nP/xnW4L3PBhPzkPIxP0wnqwTbgRREyCfVpWy6JHcUQP508VBaXQr5P6Tk/UDpCp7\nMx2FsQCeQhNPGVQmPzwYdTrdFwKBgQDPQb3+1DCRwY5NjKf6/LgIcO9LHvZ1mM0u\nK05iGDXd7XA3gQBQw+e5OXUy8Z75Ql0DD0evXws9liVYDkrxsyLJ1tBg+hmxbYnK\nO8anx4FyRCMO3evbDD/aJhZxiUbFNnwX3tvPOcDsWFQaAzWM2NAF9JUiK6ixSKSi\nzp/qbfJxIQKBgFx0QyFfEhPqxieyPezLJEXhY9ernxVMzxIEX7CnDQvj6nR2j4v0\nJw/l+rfji1ZvOV0N5vhlQylV1jdAv6XEVYGYFTZqW/d1g+1wLTdT4jxDWkVKFJXK\nodoeJvoyAgYTc8Khh3Pjp7BOFRmqmgzQlRwxIOjVDYwkQsIODhvbhnF3AoGAO3i3\n3QZCgzsVmNUZXUXgjpZ/PSfHAQ/zVwTAlTKCqzAmkCd93OBGjVk0AEBI5YK1D/Vx\nwK1Qj8USlD+UWFH+XmJlKzM0HKsrU1XT9+ssK/qTJImTiUzEzHSqqOgBSCJMHmYZ\nHnOBLfM1+jDxhjMRQKy5JFZyWXUNKNLZ9M9G6KECgYAsbrZKYCC6PaeN4ETP6zw7\nCB42mPH5pJDlzUy1b5CuYXJS9QbpyHsqTUhiWnMm7jJ7TOWxXiGK8UE3+aJFCygB\nxXHxLmqDc5yLqVyatk7xbpOLweUNbiPeSR8HBQjPxjqHupA3WRg1j7gNdxOcq/YH\n5r1SuNjhWf8sJJrZdK/V5A==\n-----END PRIVATE KEY-----\n",
"client_email": "dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com",
"client_id": "112129892578518408444",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/dehkhodamap-e9f0da4ce9f6514021%40ee-esmaeilkiani13877.iam.gserviceaccount.com"
}
with open(credentials_path, 'w') as f:
json.dump(credentials_dict, f)
credentials = ee.ServiceAccountCredentials(
"dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com",
credentials_path
)
ee.Initialize(credentials)
return True
except Exception as e:
st.error(f"خطا در اتصال به Earth Engine: {e}")
return False
earth_engine_initialized = initialize_earth_engine()
# نمونه داده
@st.cache_data
def generate_sample_data():
farm_data = {
'Farm_ID': [f'{i:02d}-{j:02d}' for i in range(1, 5) for j in range(20, 30)],
'Farm_Name': [f'مزرعه {i:02d}-{j:02d}' for i in range(1, 5) for j in range(20, 30)],
'Channel_ID': [f'کانال {(i-1)*10 + j%10 + 1}' for i in range(1, 5) for j in range(20, 30)],
'Administration_ID': [i for i in range(1, 5) for _ in range(10)],
'Administration_Name': [f'اداره {i}' for i in range(1, 5) for _ in range(10)],
'Production_Section': [1 if i <= 2 else 2 for i in range(1, 5) for _ in range(10)],
'Area': [np.random.randint(50, 200) for _ in range(40)],
'Variety': [np.random.choice(['CP69', 'CP73']) for _ in range(40)],
'Crop_Age': [np.random.randint(1, 5) for _ in range(40)]
}
farms_df = pd.DataFrame(farm_data)
height_data = []
today = datetime.now()
for week in range(1, 6):
date = today - timedelta(days=(5-week)*7)
for farm_id in farms_df['Farm_ID']:
stations = [np.random.randint(150, 220) for _ in range(5)]
avg_height = int(np.mean(stations))
groundwater = [np.random.randint(50, 100) for _ in range(2)]
sheath_moisture = np.random.randint(60, 90)
nitrogen = np.random.randint(20, 40)
height_data.append({
'Measurement_ID': f"{farm_id}-W{week}",
'Farm_ID': farm_id,
'Week': week,
'Measurement_Date': date.strftime('%Y-%m-%d'),
'Height': avg_height,
'Station1': stations[0],
'Station2': stations[1],
'Station3': stations[2],
'Station4': stations[3],
'Station5': stations[4],
'Groundwater1': groundwater[0],
'Groundwater2': groundwater[1],
'Sheath_Moisture': sheath_moisture,
'Nitrogen': nitrogen
})
heights_df = pd.DataFrame(height_data)
weekly_report_data = []
for farm_id in farms_df['Farm_ID']:
farm_heights = heights_df[heights_df['Farm_ID'] == farm_id].sort_values('Week')
for week in range(1, 6):
current_week_data = farm_heights[farm_heights['Week'] == week]
if current_week_data.empty:
continue
current_height = current_week_data['Height'].values[0]
if week > 1:
prev_week_data = farm_heights[farm_heights['Week'] == week-1]
if not prev_week_data.empty:
prev_height = prev_week_data['Height'].values[0]
growth_change = current_height - prev_height
else:
growth_change = 0
else:
growth_change = 0
all_farms_this_week = heights_df[heights_df['Week'] == week]
avg_height_all_farms = all_farms_this_week['Height'].mean()
if current_height > avg_height_all_farms + 5:
growth_status = 'خوب'
elif current_height < avg_height_all_farms - 5:
growth_status = 'ضعیف'
else:
growth_status = 'متوسط'
weekly_report_data.append({
'Report_ID': f"{farm_id}-R{week}",
'Farm_ID': farm_id,
'Week': week,
'Average_Height': current_height,
'Growth_Change': growth_change,
'Growth_Status': growth_status,
'Regional_Average': int(avg_height_all_farms)
})
weekly_report_df = pd.DataFrame(weekly_report_data)
return farms_df, heights_df, weekly_report_df
farms_df, heights_df, weekly_report_df = generate_sample_data()
report_with_details = pd.merge(weekly_report_df, farms_df, on='Farm_ID')
latest_week = heights_df['Week'].max()
latest_reports = report_with_details[report_with_details['Week'] == latest_week]
# مدیریت وضعیت برنامه
if 'selected_tab' not in st.session_state:
st.session_state.selected_tab = "ورود اطلاعات"
if 'selected_day' not in st.session_state:
st.session_state.selected_day = "شنبه"
if 'farm_data' not in st.session_state:
st.session_state.farm_data = {}
if 'ndvi_map' not in st.session_state:
st.session_state.ndvi_map = None
# تابع برای محاسبه میانگین ارتفاع
def calculate_average_height(row):
stations = [row[f'ایستگاه {i}'] for i in range(1, 6) if f'ایستگاه {i}' in row and row[f'ایستگاه {i}'] is not None]
valid_stations = [s for s in stations if s > 0]
return sum(valid_stations) / len(valid_stations) if valid_stations else 0
# تابع برای ایجاد نقشه NDVI (بدون تغییر)
def create_ndvi_map(farm_id=None, date=None):
if not earth_engine_initialized:
st.warning("اتصال به Earth Engine برقرار نشد. نقشه NDVI نمایش داده نمی‌شود.")
return None
try:
center_point = [31.3183, 48.6706]
m = folium.Map(location=center_point, zoom_start=10)
if farm_id:
farm = farms_df[farms_df['Farm_ID'] == farm_id].iloc[0]
lat = 31.3183 + (int(farm_id.split('-')[0]) - 1) * 0.05
lon = 48.6706 + (int(farm_id.split('-')[1]) - 20) * 0.05
if date:
date_obj = datetime.strptime(date, '%Y-%m-%d')
start_date = date_obj - timedelta(days=5)
end_date = date_obj + timedelta(days=5)
start_date_str = start_date.strftime('%Y-%m-%d')
end_date_str = end_date.strftime('%Y-%m-%d')
else:
start_date_str = '2023-01-01'
end_date_str = '2023-12-31'
region = ee.Geometry.Point([lon, lat]).buffer(1000)
s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
.filterDate(start_date_str, end_date_str) \
.filterBounds(region) \
.sort('CLOUD_COVERAGE_ASSESSMENT') \
.first()
ndvi = s2.normalizedDifference(['B8', 'B4']).rename('NDVI')
ndvi_viz = {
'min': -0.2,
'max': 0.8,
'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850']
}
map_id_dict = ee.Image(ndvi).getMapId(ndvi_viz)
folium.TileLayer(
tiles=map_id_dict['tile_fetcher'].url_format,
attr='Google Earth Engine',
name='NDVI',
overlay=True,
control=True
).add_to(m)
folium.Marker(
location=[lat, lon],
popup=f'مزرعه {farm_id}',
icon=folium.Icon(color='green', icon='leaf')
).add_to(m)
m.fit_bounds([[lat-0.05, lon-0.05], [lat+0.05, lon+0.05]])
folium.LayerControl().add_to(m)
return m
except Exception as e:
st.error(f"خطا در ایجاد نقشه NDVI: {e}")
return None
# منوی کناری (بدون تغییر)
with st.sidebar:
st.image("https://via.placeholder.com/150x150.png?text=لوگو", width=150)
st.title("سامانه پایش مزارع نیشکر")
st.subheader("شرکت کشت و صنعت نیشکر دهخدا")
menu = st.selectbox(
"انتخاب صفحه",
["داشبورد", "ورود اطلاعات", "گزارش‌گیری", "تحلیل داده‌ها", "تنظیمات"]
)
st.session_state.selected_tab = menu
st.markdown("---")
st.markdown("### راهنمای وضعیت رشد")
col1, col2 = st.columns(2)
with col1:
st.markdown('<div class="status-good">خوب</div>', unsafe_allow_html=True)
st.markdown('<div class="status-medium">متوسط</div>', unsafe_allow_html=True)
st.markdown('<div class="status-bad">ضعیف</div>', unsafe_allow_html=True)
with col2:
st.write("بالاتر از میانگین")
st.write("نزدیک به میانگین")
st.write("پایین‌تر از میانگین")
st.markdown("---")
st.info("نسخه آزمایشی 1.0")
# داشبورد (بدون تغییر)
if st.session_state.selected_tab == "داشبورد":
st.title("داشبورد پایش مزارع نیشکر")
st.subheader("وضعیت کلی مزارع")
status_counts = latest_reports['Growth_Status'].value_counts()
good_count = status_counts.get('خوب', 0)
medium_count = status_counts.get('متوسط', 0)
bad_count = status_counts.get('ضعیف', 0)
col1, col2, col3, col4 = st.columns(4)
with col1:
st.markdown('<div class="metric-card metric-green">'f'<h2>{good_count}</h2>''<p>مزارع با وضعیت خوب</p>''</div>', unsafe_allow_html=True)
with col2:
st.markdown('<div class="metric-card metric-yellow">'f'<h2>{medium_count}</h2>''<p>مزارع با وضعیت متوسط</p>''</div>', unsafe_allow_html=True)
with col3:
st.markdown('<div class="metric-card metric-red">'f'<h2>{bad_count}</h2>''<p>مزارع با وضعیت ضعیف</p>''</div>', unsafe_allow_html=True)
with col4:
avg_height = int(latest_reports['Average_Height'].mean())
st.markdown('<div class="metric-card metric-blue">'f'<h2>{avg_height} سانتی‌متر</h2>''<p>میانگین ارتفاع هفته جاری</p>''</div>', unsafe_allow_html=True)
st.markdown("---")
col1, col2 = st.columns(2)
with col1:
st.subheader("میانگین ارتفاع به تفکیک اداره")
admin_heights = latest_reports.groupby('Administration_Name')['Average_Height'].mean().reset_index()
admin_heights['Average_Height'] = admin_heights['Average_Height'].astype(int)
fig = px.bar(
admin_heights,
x='Administration_Name',
y='Average_Height',
color='Average_Height',
color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")],
labels={'Administration_Name': 'اداره', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
)
fig.update_layout(height=400, xaxis_title="اداره", yaxis_title="میانگین ارتفاع (سانتی‌متر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
st.plotly_chart(fig, use_container_width=True)
with col2:
st.subheader("مقایسه واریته‌ها")
variety_heights = latest_reports.groupby('Variety')['Average_Height'].mean().reset_index()
variety_heights['Average_Height'] = variety_heights['Average_Height'].astype(int)
fig = px.bar(
variety_heights,
x='Variety',
y='Average_Height',
color='Variety',
color_discrete_sequence=["#00c853", "#2196f3"],
labels={'Variety': 'واریته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
)
fig.update_layout(height=400, xaxis_title="واریته", yaxis_title="میانگین ارتفاع (سانتی‌متر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
st.plotly_chart(fig, use_container_width=True)
st.subheader("روند میانگین ارتفاع هفتگی")
weekly_avg = report_with_details.groupby('Week')['Average_Height'].mean().reset_index()
weekly_avg['Average_Height'] = weekly_avg['Average_Height'].astype(int)
fig = px.line(
weekly_avg,
x='Week',
y='Average_Height',
markers=True,
line_shape='spline',
labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)'}
)
fig.update_traces(line=dict(width=3, color="#1e6b45"), marker=dict(size=10))
fig.update_layout(height=400, xaxis_title="هفته", yaxis_title="میانگین ارتفاع (سانتی‌متر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
st.plotly_chart(fig, use_container_width=True)
st.markdown("---")
st.subheader("نقشه NDVI")
col1, col2 = st.columns(2)
with col1:
selected_farm_for_map = st.selectbox(
"انتخاب مزرعه",
farms_df['Farm_ID'].tolist(),
format_func=lambda x: farms_df[farms_df['Farm_ID'] == x]['Farm_Name'].iloc[0]
)
with col2:
latest_date = heights_df[heights_df['Farm_ID'] == selected_farm_for_map].sort_values('Week', ascending=False)['Measurement_Date'].iloc[0]
selected_date_for_map = st.date_input(
"انتخاب تاریخ",
value=datetime.strptime(latest_date, '%Y-%m-%d'),
max_value=datetime.now()
)
if st.button("نمایش نقشه NDVI"):
with st.spinner("در حال تولید نقشه..."):
ndvi_map = create_ndvi_map(selected_farm_for_map, selected_date_for_map.strftime('%Y-%m-%d'))
st.session_state.ndvi_map = ndvi_map
if st.session_state.ndvi_map:
st.markdown('<div class="map-container">', unsafe_allow_html=True)
folium_static(st.session_state.ndvi_map, width=1200, height=500)
st.markdown('</div>', unsafe_allow_html=True)
farm_info = farms_df[farms_df['Farm_ID'] == selected_farm_for_map].iloc[0]
farm_measurements = heights_df[(heights_df['Farm_ID'] == selected_farm_for_map) &
(heights_df['Measurement_Date'] == latest_date)].iloc[0]
st.markdown(f"""
### اطلاعات مزرعه {farm_info['Farm_Name']}
**اداره:** {farm_info['Administration_Name']} | **کانال:** {farm_info['Channel_ID']} | **واریته:** {farm_info['Variety']} | **سن کشت:** {farm_info['Crop_Age']} سال
**آخرین اندازه‌گیری:** {latest_date}
- **میانگین ارتفاع:** {farm_measurements['Height']} سانتی‌متر
- **رطوبت غلاف:** {farm_measurements['Sheath_Moisture']}%
- **نیتروژن:** {farm_measurements['Nitrogen']}%
""")
else:
st.info("برای نمایش نقشه NDVI، یک مزرعه و تاریخ انتخاب کنید.")
st.markdown("---")
st.subheader("مزارع با بیشترین و کمترین رشد")
col1, col2 = st.columns(2)
with col1:
st.markdown("#### بیشترین رشد هفتگی")
top_growth = report_with_details[report_with_details['Week'] == latest_week].sort_values('Growth_Change', ascending=False).head(5)
for _, row in top_growth.iterrows():
growth_color = "green" if row['Growth_Change'] > 0 else "red"
st.markdown(f"""
<div style="border: 1px solid #eee; padding: 10px; border-radius: 5px; margin-bottom: 10px;">
<h4 style="margin: 0;">{row['Farm_Name']} (اداره {row['Administration_ID']})</h4>
<p style="margin: 5px 0;">ارتفاع: {row['Average_Height']} سانتی‌متر</p>
<p style="color: {growth_color}; font-weight: bold; margin: 0;">
تغییر: {'+' if row['Growth_Change'] > 0 else ''}{row['Growth_Change']} سانتی‌متر
</p>
</div>
""", unsafe_allow_html=True)
with col2:
st.markdown("#### کمترین رشد هفتگی")
bottom_growth = report_with_details[report_with_details['Week'] == latest_week].sort_values('Growth_Change').head(5)
for _, row in bottom_growth.iterrows():
growth_color = "green" if row['Growth_Change'] > 0 else "red"
st.markdown(f"""
<div style="border: 1px solid #eee; padding: 10px; border-radius: 5px; margin-bottom: 10px;">
<h4 style="margin: 0;">{row['Farm_Name']} (اداره {row['Administration_ID']})</h4>
<p style="margin: 5px 0;">ارتفاع: {row['Average_Height']} سانتی‌متر</p>
<p style="color: {growth_color}; font-weight: bold; margin: 0;">
تغییر: {'+' if row['Growth_Change'] > 0 else ''}{row['Growth_Change']} سانتی‌متر
</p>
</div>
""", unsafe_allow_html=True)
# بخش ورود اطلاعات (بهبود یافته)
elif st.session_state.selected_tab == "ورود اطلاعات":
st.title("ورود اطلاعات روزانه")
# انتخاب روز هفته
days_of_week = ["شنبه", "یکشنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنجشنبه"]
selected_day = st.selectbox("انتخاب روز", days_of_week, index=days_of_week.index(st.session_state.selected_day))
st.session_state.selected_day = selected_day
# انتخاب اداره
selected_admin = st.selectbox("انتخاب اداره", sorted(farms_df['Administration_ID'].unique()))
# فیلتر مزارع بر اساس اداره انتخاب‌شده
filtered_farms = farms_df[farms_df['Administration_ID'] == selected_admin].copy()
# تعریف ستون‌های جدول
columns = {
'مزرعه': filtered_farms['Farm_Name'],
'ایستگاه 1': [0] * len(filtered_farms),
'ایستگاه 2': [0] * len(filtered_farms),
'ایستگاه 3': [0] * len(filtered_farms),
'ایستگاه 4': [0] * len(filtered_farms),
'ایستگاه 5': [0] * len(filtered_farms),
'چاهک 1': [0] * len(filtered_farms),
'چاهک 2': [0] * len(filtered_farms),
'رطوبت غلاف': [0] * len(filtered_farms),
'نیتروژن': [0] * len(filtered_farms),
'میانگین ارتفاع': [0] * len(filtered_farms)
}
data_df = pd.DataFrame(columns)
# بارگذاری داده‌های قبلی (اگر وجود داشته باشد)
if selected_day in st.session_state.farm_data:
for idx, farm in filtered_farms.iterrows():
farm_id = farm['Farm_ID']
if farm_id in st.session_state.farm_data[selected_day]:
saved_data = st.session_state.farm_data[selected_day][farm_id]
for col in ['ایستگاه 1', 'ایستگاه 2', 'ایستگاه 3', 'ایستگاه 4', 'ایستگاه 5', 'چاهک 1', 'چاهک 2', 'رطوبت غلاف', 'نیتروژن']:
if col in saved_data:
data_df.at[idx, col] = saved_data[col]
data_df.at[idx, 'میانگین ارتفاع'] = calculate_average_height(data_df.iloc[idx])
# نمایش جدول ورود داده‌ها
st.subheader(f"ورود داده‌های روز {selected_day} - اداره {selected_admin}")
edited_df = st.data_editor(
data_df,
num_rows="fixed",
key=f"data_editor_{selected_day}_{selected_admin}",
hide_index=True,
column_config={
"مزرعه": st.column_config.TextColumn("مزرعه", disabled=True),
"ایستگاه 1": st.column_config.NumberColumn("ایستگاه 1", min_value=0, max_value=300, step=1),
"ایستگاه 2": st.column_config.NumberColumn("ایستگاه 2", min_value=0, max_value=300, step=1),
"ایستگاه 3": st.column_config.NumberColumn("ایستگاه 3", min_value=0, max_value=300, step=1),
"ایستگاه 4": st.column_config.NumberColumn("ایستگاه 4", min_value=0, max_value=300, step=1),
"ایستگاه 5": st.column_config.NumberColumn("ایستگاه 5", min_value=0, max_value=300, step=1),
"چاهک 1": st.column_config.NumberColumn("چاهک 1", min_value=0, max_value=300, step=1),
"چاهک 2": st.column_config.NumberColumn("چاهک 2", min_value=0, max_value=300, step=1),
"رطوبت غلاف": st.column_config.NumberColumn("رطوبت غلاف", min_value=0, max_value=100, step=1),
"نیتروژن": st.column_config.NumberColumn("نیتروژن", min_value=0, max_value=100, step=1),
"میانگین ارتفاع": st.column_config.NumberColumn("میانگین ارتفاع", disabled=True)
},
use_container_width=True
)
# محاسبه داینامیک میانگین ارتفاع
for idx, row in edited_df.iterrows():
edited_df.at[idx, 'میانگین ارتفاع'] = calculate_average_height(row)
# نمایش خلاصه داینامیک
st.markdown("---")
st.subheader("خلاصه وضعیت داده‌های واردشده")
col1, col2, col3, col4 = st.columns(4)
with col1:
total_avg = edited_df['میانگین ارتفاع'].mean()
st.metric("میانگین کل ارتفاع", f"{total_avg:.1f} سانتی‌متر" if total_avg > 0 else "0")
with col2:
farms_entered = len(edited_df[edited_df['میانگین ارتفاع'] > 0])
st.metric("تعداد مزارع واردشده", f"{farms_entered} از {len(filtered_farms)}")
with col3:
avg_moisture = edited_df['رطوبت غلاف'].mean()
st.metric("میانگین رطوبت غلاف", f"{avg_moisture:.1f}%" if avg_moisture > 0 else "0")
with col4:
avg_nitrogen = edited_df['نیتروژن'].mean()
st.metric("میانگین نیتروژن", f"{avg_nitrogen:.1f}%" if avg_nitrogen > 0 else "0")
# دکمه ذخیره
if st.button("ذخیره اطلاعات"):
if selected_day not in st.session_state.farm_data:
st.session_state.farm_data[selected_day] = {}
for idx, row in edited_df.iterrows():
farm_id = filtered_farms.iloc[idx]['Farm_ID']
st.session_state.farm_data[selected_day][farm_id] = {
'ایستگاه 1': row['ایستگاه 1'],
'ایستگاه 2': row['ایستگاه 2'],
'ایستگاه 3': row['ایستگاه 3'],
'ایستگاه 4': row['ایستگاه 4'],
'ایستگاه 5': row['ایستگاه 5'],
'چاهک 1': row['چاهک 1'],
'چاهک 2': row['چاهک 2'],
'رطوبت غلاف': row['رطوبت غلاف'],
'نیتروژن': row['نیتروژن'],
'میانگین ارتفاع': row['میانگین ارتفاع']
}
st.success("داده‌ها با موفقیت ذخیره شدند.")
# گزارش‌گیری (بدون تغییر)
elif st.session_state.selected_tab == "گزارش‌گیری":
st.title("گزارش‌های هفتگی")
report_type = st.radio(
"نوع گزارش",
["گزارش به تفکیک مزرعه", "گزارش به تفکیک اداره", "گزارش به تفکیک کانال", "مزارع با وضعیت ضعیف"]
)
st.markdown("---")
if report_type == "گزارش به تفکیک مزرعه":
st.subheader("گزارش هفتگی به تفکیک مزرعه")
selected_admin = st.selectbox("انتخاب اداره", sorted(farms_df['Administration_ID'].unique()), key="admin_select_farm_report")
filtered_farms = farms_df[farms_df['Administration_ID'] == selected_admin]
selected_farm_id = st.selectbox(
"انتخاب مزرعه",
filtered_farms['Farm_ID'].tolist(),
format_func=lambda x: filtered_farms[filtered_farms['Farm_ID'] == x]['Farm_Name'].iloc[0]
)
farm_reports = weekly_report_df[weekly_report_df['Farm_ID'] == selected_farm_id].sort_values('Week')
if not farm_reports.empty:
farm_info = filtered_farms[filtered_farms['Farm_ID'] == selected_farm_id].iloc[0]
col1, col2, col3 = st.columns(3)
col1.metric("واریته", farm_info['Variety'])
col2.metric("سن کشت", f"{farm_info['Crop_Age']} سال")
col3.metric("مساحت", f"{farm_info['Area']} هکتار")
st.markdown("---")
st.subheader("روند رشد هفتگی")
fig = go.Figure()
fig.add_trace(go.Scatter(
x=farm_reports['Week'],
y=farm_reports['Regional_Average'],
mode='lines+markers',
name='میانگین منطقه',
line=dict(color='gray', width=2, dash='dot'),
marker=dict(size=8)
))
fig.add_trace(go.Scatter(
x=farm_reports['Week'],
y=farm_reports['Average_Height'],
mode='lines+markers',
name='ارتفاع مزرعه',
line=dict(color='#1e6b45', width=3),
marker=dict(size=10)
))
fig.update_layout(height=400, xaxis_title="هفته", yaxis_title="ارتفاع (سانتی‌متر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
st.plotly_chart(fig, use_container_width=True)
st.subheader("داده‌های هفتگی")
report_table = pd.DataFrame({
'هفته': farm_reports['Week'],
'تاریخ': farm_reports['Week'].apply(lambda x: heights_df[heights_df['Week'] == x]['Measurement_Date'].iloc[0]),
'ارتفاع': farm_reports['Average_Height'],
'میانگین منطقه': farm_reports['Regional_Average'],
'تغییرات': farm_reports['Growth_Change'],
'وضعیت': farm_reports['Growth_Status']
})
def status_with_color(status):
colors = {'خوب': '#00c853', 'متوسط': '#ffd600', 'ضعیف': '#ff3d00'}
return f'<span style="color: {colors.get(status, "black")};">{status}</span>'
report_table['وضعیت'] = report_table['وضعیت'].apply(status_with_color)
st.markdown(report_table.to_html(escape=False, index=False), unsafe_allow_html=True)
st.subheader("نقشه NDVI مزرعه")
if st.button("نمایش نقشه NDVI این مزرعه"):
with st.spinner("در حال تولید نقشه..."):
latest_date = farm_reports.iloc[-1]['Week']
farm_date = heights_df[heights_df['Week'] == latest_date]['Measurement_Date'].iloc[0]
ndvi_map = create_ndvi_map(selected_farm_id, farm_date)
if ndvi_map:
st.markdown('<div class="map-container">', unsafe_allow_html=True)
folium_static(ndvi_map, width=1200, height=500)
st.markdown('</div>', unsafe_allow_html=True)
st.markdown("---")
st.subheader("توصیه‌های کارشناسی")
latest_report = farm_reports.iloc[-1]
latest_status = latest_report['Growth_Status']
if latest_status == 'خوب':
st.success("وضعیت رشد مزرعه مطلوب است. ادامه برنامه فعلی توصیه می‌شود.")
elif latest_status == 'متوسط':
st.warning("وضعیت رشد مزرعه متوسط است. بررسی وضعیت آبیاری و تغذیه توصیه می‌شود.")
else:
st.error("وضعیت رشد مزرعه ضعیف است. مشکلات احتمالی:")
st.markdown("""
- بررسی سیستم آبیاری و عدم گرفتگی
- بررسی وضعیت تغذیه و نیاز به کود
- بررسی آفات و بیماری‌های احتمالی
- بررسی وضعیت زهکشی مزرعه
""")
# تحلیل داده‌ها (بدون تغییر)
elif st.session_state.selected_tab == "تحلیل داده‌ها":
st.title("تحلیل پیشرفته داده‌ها")
analysis_type = st.selectbox(
"انتخاب نوع تحلیل",
["تحلیل واریته‌ها", "تحلیل عوامل مؤثر بر رشد", "پیش‌بینی روند آینده", "مقایسه بخش‌های تولیدی"]
)
st.markdown("---")
if analysis_type == "تحلیل واریته‌ها":
st.subheader("مقایسه واریته‌های مختلف")
variety_weekly = report_with_details.groupby(['Variety', 'Week'])['Average_Height'].mean().reset_index()
variety_weekly['Average_Height'] = variety_weekly['Average_Height'].round().astype(int)
fig = px.line(
variety_weekly,
x='Week',
y='Average_Height',
color='Variety',
markers=True,
labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتی‌متر)', 'Variety': 'واریته'}
)
fig.update_traces(line=dict(width=3), marker=dict(size=10))
fig.update_layout(height=500, xaxis_title="هفته", yaxis_title="میانگین ارتفاع (سانتی‌متر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)')
st.plotly_chart(fig, use_container_width=True)