Update app.py
Browse files
app.py
CHANGED
|
@@ -11,7 +11,7 @@ from helpers import (
|
|
| 11 |
text_to_speech, speech_to_text, geocode_location,
|
| 12 |
export_to_jsonl, export_to_csv, search_entries,
|
| 13 |
register_user, authenticate_user, get_user_info, update_user_entry_count,
|
| 14 |
-
translate_text, detect_language
|
| 15 |
)
|
| 16 |
|
| 17 |
# Set page config
|
|
@@ -29,66 +29,68 @@ if 'authenticated' not in st.session_state:
|
|
| 29 |
st.session_state.authenticated = False
|
| 30 |
if 'username' not in st.session_state:
|
| 31 |
st.session_state.username = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
# --- AUTHENTICATION PAGE ---
|
| 34 |
if not st.session_state.authenticated:
|
| 35 |
-
st.title("
|
| 36 |
-
st.markdown("#### *
|
| 37 |
st.markdown("---")
|
| 38 |
-
|
| 39 |
col1, col2 = st.columns([1, 1])
|
| 40 |
-
|
| 41 |
with col1:
|
| 42 |
st.image("https://images.pexels.com/photos/265216/pexels-photo-265216.jpeg",
|
| 43 |
caption="Photo by Pixabay", use_container_width=True)
|
| 44 |
-
st.markdown(""
|
| 45 |
-
|
| 46 |
-
This archive is a community-driven effort to document and share the rich, time-tested farming practices of India. By contributing, you help create a valuable open-source corpus for agricultural research and AI.
|
| 47 |
-
""")
|
| 48 |
-
|
| 49 |
with col2:
|
| 50 |
-
tab1, tab2 = st.tabs(["**
|
| 51 |
-
|
| 52 |
with tab1:
|
| 53 |
-
st.subheader(
|
| 54 |
with st.form("login_form"):
|
| 55 |
-
username = st.text_input(
|
| 56 |
-
password = st.text_input(
|
| 57 |
-
login_button = st.form_submit_button(
|
| 58 |
-
|
| 59 |
if login_button:
|
| 60 |
if username and password:
|
| 61 |
if authenticate_user(username, password):
|
| 62 |
st.session_state.authenticated = True
|
| 63 |
st.session_state.username = username
|
| 64 |
-
st.success(
|
| 65 |
st.rerun()
|
| 66 |
else:
|
| 67 |
-
st.error(
|
| 68 |
else:
|
| 69 |
-
st.error(
|
| 70 |
-
|
| 71 |
with tab2:
|
| 72 |
-
st.subheader(
|
| 73 |
with st.form("register_form"):
|
| 74 |
-
full_name = st.text_input(
|
| 75 |
-
email = st.text_input(
|
| 76 |
-
reg_username = st.text_input(
|
| 77 |
-
reg_password = st.text_input(
|
| 78 |
-
confirm_password = st.text_input(
|
| 79 |
-
register_button = st.form_submit_button(
|
| 80 |
-
|
| 81 |
if register_button:
|
| 82 |
if full_name and email and reg_username and reg_password and confirm_password:
|
| 83 |
if reg_password == confirm_password:
|
| 84 |
if register_user(reg_username, email, reg_password, full_name):
|
| 85 |
-
st.success(
|
| 86 |
else:
|
| 87 |
-
st.error(
|
| 88 |
else:
|
| 89 |
-
st.error(
|
| 90 |
else:
|
| 91 |
-
st.error(
|
| 92 |
st.stop()
|
| 93 |
|
| 94 |
# --- MAIN APPLICATION (FOR AUTHENTICATED USERS) ---
|
|
@@ -97,131 +99,138 @@ user_info = get_user_info(st.session_state.username)
|
|
| 97 |
# App header with user info
|
| 98 |
col1, col2 = st.columns([3, 1])
|
| 99 |
with col1:
|
| 100 |
-
st.title("
|
| 101 |
with col2:
|
| 102 |
st.markdown(f"""
|
| 103 |
<div style="text-align: right;">
|
| 104 |
-
|
| 105 |
-
<small>
|
| 106 |
</div>
|
| 107 |
""", unsafe_allow_html=True)
|
| 108 |
-
if st.button(
|
| 109 |
st.session_state.authenticated = False
|
| 110 |
st.session_state.username = None
|
| 111 |
st.rerun()
|
| 112 |
|
| 113 |
# --- NAVIGATION LOGIC ---
|
| 114 |
-
st.sidebar.title(
|
| 115 |
def set_page(page_name):
|
| 116 |
st.session_state.page_selection = page_name
|
| 117 |
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
"
|
| 121 |
-
"
|
| 122 |
-
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
"Go to",
|
| 125 |
page_options,
|
| 126 |
key="page_selection",
|
| 127 |
label_visibility="collapsed"
|
| 128 |
)
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
# --- Home Page ---
|
| 131 |
-
if
|
| 132 |
-
st.header("
|
| 133 |
st.markdown("---")
|
| 134 |
-
|
| 135 |
with st.container(border=True):
|
| 136 |
-
st.subheader("
|
| 137 |
total_entries = len(st.session_state.entries)
|
| 138 |
languages = len(set(entry.get('language', 'Unknown') for entry in st.session_state.entries))
|
| 139 |
categories = len(set(entry.get('category', 'Unknown') for entry in st.session_state.entries))
|
| 140 |
|
| 141 |
stat_cols = st.columns(3)
|
| 142 |
-
stat_cols[0].metric("
|
| 143 |
-
stat_cols[1].metric("
|
| 144 |
-
stat_cols[2].metric("
|
| 145 |
|
| 146 |
-
st.markdown("###
|
| 147 |
gs_cols = st.columns(3)
|
| 148 |
with gs_cols[0]:
|
| 149 |
with st.container(border=True, height=180):
|
| 150 |
-
st.markdown("####
|
| 151 |
-
st.markdown(
|
| 152 |
-
st.button(
|
| 153 |
with gs_cols[1]:
|
| 154 |
with st.container(border=True, height=180):
|
| 155 |
-
st.markdown("####
|
| 156 |
-
st.markdown(
|
| 157 |
-
st.button(
|
| 158 |
with gs_cols[2]:
|
| 159 |
with st.container(border=True, height=180):
|
| 160 |
-
st.markdown("####
|
| 161 |
-
st.markdown(
|
| 162 |
-
st.button(
|
| 163 |
|
| 164 |
-
# --- Submit Wisdom Page
|
| 165 |
-
elif
|
| 166 |
-
st.header("
|
| 167 |
-
st.markdown("
|
| 168 |
|
| 169 |
if 'form_data' not in st.session_state:
|
| 170 |
st.session_state.form_data = {
|
| 171 |
-
'title': '', 'description': '', 'language': 'English', 'category': 'Other',
|
| 172 |
'location_name': '', 'manual_lat': 20.5937, 'manual_lon': 78.9629
|
| 173 |
}
|
| 174 |
|
| 175 |
-
|
| 176 |
-
st.subheader("Step 1: Use Helper Tools (Optional)")
|
| 177 |
with st.container(border=True):
|
| 178 |
-
st.markdown("
|
| 179 |
-
st.caption("
|
| 180 |
geocode_cols = st.columns([3, 1])
|
| 181 |
-
geocode_input = geocode_cols[0].text_input("
|
| 182 |
-
if geocode_cols[1].button("
|
| 183 |
if geocode_input:
|
| 184 |
coords = geocode_location(geocode_input)
|
| 185 |
if coords:
|
| 186 |
st.session_state.form_data['manual_lat'], st.session_state.form_data['manual_lon'] = coords
|
| 187 |
-
st.success(
|
| 188 |
else:
|
| 189 |
-
st.error(
|
| 190 |
|
| 191 |
st.markdown("---")
|
| 192 |
-
st.markdown("
|
| 193 |
-
st.caption("
|
| 194 |
speech_cols = st.columns([3, 1])
|
| 195 |
-
|
| 196 |
-
if speech_cols[1].button("
|
| 197 |
with st.spinner("Listening..."):
|
| 198 |
-
recorded_text = speech_to_text(
|
| 199 |
if recorded_text:
|
| 200 |
st.session_state.form_data['description'] = recorded_text
|
| 201 |
-
st.success(
|
| 202 |
st.rerun()
|
| 203 |
else:
|
| 204 |
-
st.error("
|
| 205 |
|
| 206 |
st.markdown("---")
|
| 207 |
|
| 208 |
-
|
| 209 |
-
st.subheader("Step 2: Complete and Submit the Form")
|
| 210 |
with st.form("entry_form", clear_on_submit=True):
|
| 211 |
-
with st.expander("
|
| 212 |
-
st.caption("
|
| 213 |
-
title = st.text_input("
|
| 214 |
-
description = st.text_area("
|
| 215 |
|
| 216 |
col1, col2 = st.columns(2)
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
language = col2.selectbox("Language of this Entry*", get_languages(), index=get_languages().index(st.session_state.form_data.get('language', 'English')))
|
| 220 |
-
|
| 221 |
-
with st.expander("**Part 2: Geographic Context**"):
|
| 222 |
-
st.caption("Specify where this knowledge comes from. Use the helper tool above to search, or click the map to fine-tune.")
|
| 223 |
-
location_name = st.text_input("Location Name (e.g., Village, District, State)", value=st.session_state.form_data.get('location_name', ''))
|
| 224 |
|
|
|
|
|
|
|
|
|
|
| 225 |
map_center = [st.session_state.form_data['manual_lat'], st.session_state.form_data['manual_lon']]
|
| 226 |
m = folium.Map(location=map_center, zoom_start=7)
|
| 227 |
folium.Marker(map_center, popup="Your Selected Location", tooltip="Current Location").add_to(m)
|
|
@@ -233,21 +242,19 @@ elif page == "✍️ Submit Wisdom":
|
|
| 233 |
st.rerun()
|
| 234 |
|
| 235 |
loc_cols = st.columns(2)
|
| 236 |
-
manual_lat = loc_cols[0].text_input("
|
| 237 |
-
manual_lon = loc_cols[1].text_input("
|
| 238 |
|
| 239 |
-
with st.expander("
|
| 240 |
-
st.caption("
|
| 241 |
-
uploaded_image = st.file_uploader("
|
| 242 |
-
uploaded_audio = st.file_uploader("
|
| 243 |
|
| 244 |
-
# FIXED: This submit button must be indented inside the 'with st.form(...)' block
|
| 245 |
st.markdown("---")
|
| 246 |
-
submitted = st.form_submit_button("
|
| 247 |
|
| 248 |
if submitted:
|
| 249 |
if title and description:
|
| 250 |
-
# ... (submission logic remains the same) ...
|
| 251 |
image_path, audio_path = None, None
|
| 252 |
media_dir = "data_entries/media"
|
| 253 |
os.makedirs(media_dir, exist_ok=True)
|
|
@@ -262,7 +269,7 @@ elif page == "✍️ Submit Wisdom":
|
|
| 262 |
|
| 263 |
entry = {
|
| 264 |
'id': len(st.session_state.entries) + 1, 'title': title, 'description': description,
|
| 265 |
-
'language':
|
| 266 |
'latitude': st.session_state.form_data['manual_lat'],
|
| 267 |
'longitude': st.session_state.form_data['manual_lon'],
|
| 268 |
'image_path': image_path, 'audio_path': audio_path,
|
|
@@ -274,35 +281,36 @@ elif page == "✍️ Submit Wisdom":
|
|
| 274 |
if save_entry(entry):
|
| 275 |
st.session_state.entries.append(entry)
|
| 276 |
update_user_entry_count(st.session_state.username)
|
| 277 |
-
st.success("
|
| 278 |
st.session_state.pop('form_data', None)
|
| 279 |
else:
|
| 280 |
-
st.error("
|
| 281 |
else:
|
| 282 |
-
st.error("
|
| 283 |
|
| 284 |
# --- Browse Knowledge Page ---
|
| 285 |
-
elif
|
| 286 |
-
st.header("
|
| 287 |
-
st.markdown("
|
| 288 |
|
| 289 |
with st.container(border=True):
|
| 290 |
col1, col2, col3 = st.columns(3)
|
| 291 |
with col1:
|
| 292 |
-
filter_language = st.selectbox("
|
| 293 |
with col2:
|
| 294 |
-
filter_category = st.selectbox("
|
| 295 |
with col3:
|
| 296 |
-
sort_by = st.selectbox("
|
| 297 |
|
| 298 |
filtered_entries = st.session_state.entries.copy()
|
| 299 |
-
if filter_language != "
|
| 300 |
-
if filter_category != "
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
elif sort_by == "
|
|
|
|
| 304 |
|
| 305 |
-
st.markdown(
|
| 306 |
st.markdown("---")
|
| 307 |
|
| 308 |
for entry in filtered_entries:
|
|
@@ -310,31 +318,31 @@ elif page == "📖 Browse Knowledge":
|
|
| 310 |
st.subheader(f"📖 {entry.get('title', 'Untitled')}")
|
| 311 |
|
| 312 |
meta_cols = st.columns(4)
|
| 313 |
-
meta_cols[0].markdown(
|
| 314 |
-
meta_cols[1].markdown(
|
| 315 |
-
meta_cols[2].markdown(
|
| 316 |
-
meta_cols[3].markdown(
|
| 317 |
|
| 318 |
-
with st.expander("
|
| 319 |
col1, col2 = st.columns([2, 1])
|
| 320 |
with col1:
|
| 321 |
-
st.markdown(
|
| 322 |
st.write(entry.get('description', 'No description'))
|
| 323 |
-
if st.button(
|
| 324 |
text_to_speech(entry.get('description', ''), entry.get('language'))
|
| 325 |
|
| 326 |
with col2:
|
| 327 |
if entry.get('image_path') and os.path.exists(entry['image_path']):
|
| 328 |
-
st.image(Image.open(entry['image_path']), caption="
|
| 329 |
if entry.get('audio_path') and os.path.exists(entry['audio_path']):
|
| 330 |
st.audio(entry['audio_path'])
|
| 331 |
if entry.get('latitude') and entry.get('longitude'):
|
| 332 |
-
st.markdown(
|
| 333 |
|
| 334 |
# --- Knowledge Map Page ---
|
| 335 |
-
elif
|
| 336 |
-
st.header("
|
| 337 |
-
st.markdown("
|
| 338 |
|
| 339 |
geo_entries = [e for e in st.session_state.entries if e.get('latitude') and e.get('longitude')]
|
| 340 |
|
|
@@ -343,8 +351,8 @@ elif page == "🗺️ Knowledge Map":
|
|
| 343 |
for entry in geo_entries:
|
| 344 |
popup_html = f"""
|
| 345 |
<b>{entry.get('title', 'Untitled')}</b><br>
|
| 346 |
-
<b>
|
| 347 |
-
<b>
|
| 348 |
"""
|
| 349 |
folium.Marker(
|
| 350 |
[entry['latitude'], entry['longitude']],
|
|
@@ -352,132 +360,132 @@ elif page == "🗺️ Knowledge Map":
|
|
| 352 |
tooltip=entry.get('title', 'Untitled')
|
| 353 |
).add_to(m)
|
| 354 |
st_folium(m, width=1200, height=600, returned_objects=[])
|
| 355 |
-
st.info(
|
| 356 |
else:
|
| 357 |
-
st.warning("
|
| 358 |
|
| 359 |
# --- Search Page ---
|
| 360 |
-
elif
|
| 361 |
-
st.header("
|
| 362 |
-
st.markdown("
|
| 363 |
|
| 364 |
with st.container(border=True):
|
| 365 |
-
search_query = st.text_input("
|
| 366 |
-
with st.expander("
|
| 367 |
col1, col2 = st.columns(2)
|
| 368 |
-
search_language = col1.selectbox("
|
| 369 |
-
search_category = col2.selectbox("
|
| 370 |
-
has_media = col1.checkbox("
|
| 371 |
-
has_location = col2.checkbox("
|
| 372 |
|
| 373 |
if search_query:
|
| 374 |
results = search_entries(
|
| 375 |
st.session_state.entries, search_query,
|
| 376 |
-
language=search_language
|
| 377 |
-
category=search_category
|
| 378 |
has_media=has_media, has_location=has_location
|
| 379 |
)
|
| 380 |
-
st.markdown(
|
| 381 |
for entry in results:
|
| 382 |
with st.container(border=True):
|
| 383 |
st.subheader(f"📖 {entry.get('title', 'Untitled')}")
|
| 384 |
-
st.markdown(f"**
|
| 385 |
st.write(entry.get('description', 'No description'))
|
| 386 |
|
| 387 |
# --- Translation Hub Page ---
|
| 388 |
-
elif
|
| 389 |
-
st.header("
|
| 390 |
-
st.markdown("
|
| 391 |
|
| 392 |
with st.container(border=True):
|
| 393 |
col1, col2 = st.columns(2)
|
| 394 |
with col1:
|
| 395 |
-
st.subheader("
|
| 396 |
-
source_language = st.selectbox("
|
| 397 |
-
source_text = st.text_area("
|
| 398 |
|
| 399 |
with col2:
|
| 400 |
-
st.subheader("
|
| 401 |
-
target_language = st.selectbox("
|
| 402 |
-
if st.button("
|
| 403 |
with st.spinner("Translating..."):
|
| 404 |
source_lang_code = source_language if source_language != "Auto-detect" else "auto"
|
| 405 |
translated_text = translate_text(source_text, target_language, source_lang_code)
|
| 406 |
-
st.text_area("
|
| 407 |
else:
|
| 408 |
-
st.text_area("
|
| 409 |
|
| 410 |
# --- Export Data Page ---
|
| 411 |
-
elif
|
| 412 |
-
st.header("
|
| 413 |
-
st.markdown("
|
| 414 |
|
| 415 |
with st.container(border=True):
|
| 416 |
-
st.subheader("
|
| 417 |
col1, col2 = st.columns(2)
|
| 418 |
-
export_format = col1.selectbox("
|
| 419 |
-
export_language = col2.selectbox("
|
| 420 |
-
export_category = col1.selectbox("
|
| 421 |
|
| 422 |
-
if st.button("
|
| 423 |
filtered_entries = st.session_state.entries.copy()
|
| 424 |
-
if export_language != "
|
| 425 |
-
if export_category != "
|
| 426 |
|
| 427 |
if not filtered_entries:
|
| 428 |
-
st.warning("
|
| 429 |
else:
|
| 430 |
timestamp = datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%d')
|
| 431 |
if export_format == "CSV":
|
| 432 |
export_data = export_to_csv(filtered_entries, True, True)
|
| 433 |
st.download_button(
|
| 434 |
-
label="
|
| 435 |
file_name=f"farming_wisdom_{timestamp}.csv", mime="text/csv", use_container_width=True
|
| 436 |
)
|
| 437 |
else: # JSONL
|
| 438 |
export_data = export_to_jsonl(filtered_entries, True, True)
|
| 439 |
st.download_button(
|
| 440 |
-
label="
|
| 441 |
file_name=f"farming_wisdom_{timestamp}.jsonl", mime="application/jsonl", use_container_width=True
|
| 442 |
)
|
| 443 |
-
st.success(
|
| 444 |
|
| 445 |
# --- Profile Page ---
|
| 446 |
-
elif
|
| 447 |
-
st.header(
|
| 448 |
st.markdown("---")
|
| 449 |
|
| 450 |
-
tab1, tab2, tab3 = st.tabs(["**
|
| 451 |
|
| 452 |
with tab1:
|
| 453 |
with st.container(border=True):
|
| 454 |
-
st.subheader("
|
| 455 |
info_cols = st.columns(2)
|
| 456 |
-
info_cols[0].markdown(
|
| 457 |
-
info_cols[0].markdown(
|
| 458 |
-
info_cols[1].markdown(
|
| 459 |
-
info_cols[1].markdown(
|
| 460 |
-
st.metric("
|
| 461 |
|
| 462 |
with tab2:
|
| 463 |
with st.container(border=True):
|
| 464 |
-
st.subheader("
|
| 465 |
my_entries = [e for e in st.session_state.entries if e.get('contributor') == st.session_state.username]
|
| 466 |
if my_entries:
|
| 467 |
-
st.write(
|
| 468 |
for entry in reversed(my_entries[-5:]):
|
| 469 |
st.markdown(f"- **{entry.get('title', 'Untitled')}** (*{entry.get('category', 'N/A')}*)")
|
| 470 |
else:
|
| 471 |
-
st.info("
|
| 472 |
|
| 473 |
with tab3:
|
| 474 |
with st.container(border=True):
|
| 475 |
-
st.subheader("
|
| 476 |
-
st.selectbox("
|
| 477 |
-
st.checkbox("
|
| 478 |
-
if st.button("
|
| 479 |
-
st.success("
|
| 480 |
|
| 481 |
# --- Footer ---
|
| 482 |
st.sidebar.markdown("---")
|
| 483 |
-
st.sidebar.info("
|
|
|
|
| 11 |
text_to_speech, speech_to_text, geocode_location,
|
| 12 |
export_to_jsonl, export_to_csv, search_entries,
|
| 13 |
register_user, authenticate_user, get_user_info, update_user_entry_count,
|
| 14 |
+
translate_text, detect_language, get_ui_text
|
| 15 |
)
|
| 16 |
|
| 17 |
# Set page config
|
|
|
|
| 29 |
st.session_state.authenticated = False
|
| 30 |
if 'username' not in st.session_state:
|
| 31 |
st.session_state.username = None
|
| 32 |
+
# NEW: Initialize language selection
|
| 33 |
+
if 'language' not in st.session_state:
|
| 34 |
+
st.session_state.language = "English"
|
| 35 |
+
|
| 36 |
+
# --- LANGUAGE SELECTOR WIDGET ---
|
| 37 |
+
# This needs to be defined early so the lang variable is available globally
|
| 38 |
+
lang = st.sidebar.selectbox(
|
| 39 |
+
label="Language / भाषा",
|
| 40 |
+
options=["English", "Hindi"],
|
| 41 |
+
key='language'
|
| 42 |
+
)
|
| 43 |
|
| 44 |
# --- AUTHENTICATION PAGE ---
|
| 45 |
if not st.session_state.authenticated:
|
| 46 |
+
st.title(get_ui_text("app_title", lang))
|
| 47 |
+
st.markdown(f"#### *{get_ui_text('app_tagline', lang)}*")
|
| 48 |
st.markdown("---")
|
|
|
|
| 49 |
col1, col2 = st.columns([1, 1])
|
|
|
|
| 50 |
with col1:
|
| 51 |
st.image("https://images.pexels.com/photos/265216/pexels-photo-265216.jpeg",
|
| 52 |
caption="Photo by Pixabay", use_container_width=True)
|
| 53 |
+
st.markdown(f"### {get_ui_text('about_platform_header', lang)}")
|
| 54 |
+
st.markdown(get_ui_text('about_platform_text', lang))
|
|
|
|
|
|
|
|
|
|
| 55 |
with col2:
|
| 56 |
+
tab1, tab2 = st.tabs([f"**{get_ui_text('login_tab', lang)}**", f"**{get_ui_text('register_tab', lang)}**"])
|
|
|
|
| 57 |
with tab1:
|
| 58 |
+
st.subheader(get_ui_text('login_subheader', lang))
|
| 59 |
with st.form("login_form"):
|
| 60 |
+
username = st.text_input(get_ui_text('username_label', lang))
|
| 61 |
+
password = st.text_input(get_ui_text('password_label', lang), type="password")
|
| 62 |
+
login_button = st.form_submit_button(get_ui_text('login_button', lang), type="primary", use_container_width=True)
|
|
|
|
| 63 |
if login_button:
|
| 64 |
if username and password:
|
| 65 |
if authenticate_user(username, password):
|
| 66 |
st.session_state.authenticated = True
|
| 67 |
st.session_state.username = username
|
| 68 |
+
st.success(get_ui_text('login_success', lang))
|
| 69 |
st.rerun()
|
| 70 |
else:
|
| 71 |
+
st.error(get_ui_text('invalid_credentials', lang))
|
| 72 |
else:
|
| 73 |
+
st.error(get_ui_text('enter_credentials', lang))
|
|
|
|
| 74 |
with tab2:
|
| 75 |
+
st.subheader(get_ui_text('register_subheader', lang))
|
| 76 |
with st.form("register_form"):
|
| 77 |
+
full_name = st.text_input(get_ui_text('full_name_label', lang))
|
| 78 |
+
email = st.text_input(get_ui_text('email_label', lang))
|
| 79 |
+
reg_username = st.text_input(get_ui_text('choose_username_label', lang))
|
| 80 |
+
reg_password = st.text_input(get_ui_text('choose_password_label', lang), type="password")
|
| 81 |
+
confirm_password = st.text_input(get_ui_text('confirm_password_label', lang), type="password")
|
| 82 |
+
register_button = st.form_submit_button(get_ui_text('register_button', lang), type="primary", use_container_width=True)
|
|
|
|
| 83 |
if register_button:
|
| 84 |
if full_name and email and reg_username and reg_password and confirm_password:
|
| 85 |
if reg_password == confirm_password:
|
| 86 |
if register_user(reg_username, email, reg_password, full_name):
|
| 87 |
+
st.success(get_ui_text('registration_success', lang))
|
| 88 |
else:
|
| 89 |
+
st.error(get_ui_text('registration_failed', lang))
|
| 90 |
else:
|
| 91 |
+
st.error(get_ui_text('passwords_no_match', lang))
|
| 92 |
else:
|
| 93 |
+
st.error(get_ui_text('fill_all_fields', lang))
|
| 94 |
st.stop()
|
| 95 |
|
| 96 |
# --- MAIN APPLICATION (FOR AUTHENTICATED USERS) ---
|
|
|
|
| 99 |
# App header with user info
|
| 100 |
col1, col2 = st.columns([3, 1])
|
| 101 |
with col1:
|
| 102 |
+
st.title(get_ui_text("app_title", lang))
|
| 103 |
with col2:
|
| 104 |
st.markdown(f"""
|
| 105 |
<div style="text-align: right;">
|
| 106 |
+
{get_ui_text('welcome_message', lang)}, <strong>{user_info.get('full_name', st.session_state.username)}</strong>!<br>
|
| 107 |
+
<small>{get_ui_text('entries_submitted', lang)}: {user_info.get('entries_submitted', 0)}</small>
|
| 108 |
</div>
|
| 109 |
""", unsafe_allow_html=True)
|
| 110 |
+
if st.button(get_ui_text('logout_button', lang), use_container_width=True):
|
| 111 |
st.session_state.authenticated = False
|
| 112 |
st.session_state.username = None
|
| 113 |
st.rerun()
|
| 114 |
|
| 115 |
# --- NAVIGATION LOGIC ---
|
| 116 |
+
st.sidebar.title(get_ui_text('navigation_header', lang))
|
| 117 |
def set_page(page_name):
|
| 118 |
st.session_state.page_selection = page_name
|
| 119 |
|
| 120 |
+
# Map keys to translated page names
|
| 121 |
+
page_keys = {
|
| 122 |
+
"home": get_ui_text("home_page", lang),
|
| 123 |
+
"submit": get_ui_text("submit_page", lang),
|
| 124 |
+
"browse": get_ui_text("browse_page", lang),
|
| 125 |
+
"map": get_ui_text("map_page", lang),
|
| 126 |
+
"search": get_ui_text("search_page", lang),
|
| 127 |
+
"translate": get_ui_text("translate_page", lang),
|
| 128 |
+
"export": get_ui_text("export_page", lang),
|
| 129 |
+
"profile": get_ui_text("profile_page", lang),
|
| 130 |
+
}
|
| 131 |
+
page_options = list(page_keys.values())
|
| 132 |
+
|
| 133 |
+
# Get the key of the selected page to use in the if/elif block
|
| 134 |
+
selected_page_name = st.sidebar.radio(
|
| 135 |
"Go to",
|
| 136 |
page_options,
|
| 137 |
key="page_selection",
|
| 138 |
label_visibility="collapsed"
|
| 139 |
)
|
| 140 |
+
# Find the key corresponding to the selected page name
|
| 141 |
+
page_key = [key for key, value in page_keys.items() if value == selected_page_name][0]
|
| 142 |
+
|
| 143 |
|
| 144 |
# --- Home Page ---
|
| 145 |
+
if page_key == "home":
|
| 146 |
+
st.header(get_ui_text("home_header", lang))
|
| 147 |
st.markdown("---")
|
|
|
|
| 148 |
with st.container(border=True):
|
| 149 |
+
st.subheader(get_ui_text("stats_header", lang))
|
| 150 |
total_entries = len(st.session_state.entries)
|
| 151 |
languages = len(set(entry.get('language', 'Unknown') for entry in st.session_state.entries))
|
| 152 |
categories = len(set(entry.get('category', 'Unknown') for entry in st.session_state.entries))
|
| 153 |
|
| 154 |
stat_cols = st.columns(3)
|
| 155 |
+
stat_cols[0].metric(get_ui_text("total_entries_metric", lang), total_entries)
|
| 156 |
+
stat_cols[1].metric(get_ui_text("languages_metric", lang), languages)
|
| 157 |
+
stat_cols[2].metric(get_ui_text("categories_metric", lang), categories)
|
| 158 |
|
| 159 |
+
st.markdown(f"### {get_ui_text('getting_started_header', lang)}")
|
| 160 |
gs_cols = st.columns(3)
|
| 161 |
with gs_cols[0]:
|
| 162 |
with st.container(border=True, height=180):
|
| 163 |
+
st.markdown(f"#### {get_ui_text('share_knowledge_card_header', lang)}")
|
| 164 |
+
st.markdown(get_ui_text('share_knowledge_card_text', lang))
|
| 165 |
+
st.button(get_ui_text('share_now_button', lang), on_click=set_page, args=(page_keys["submit"],), use_container_width=True)
|
| 166 |
with gs_cols[1]:
|
| 167 |
with st.container(border=True, height=180):
|
| 168 |
+
st.markdown(f"#### {get_ui_text('explore_entries_card_header', lang)}")
|
| 169 |
+
st.markdown(get_ui_text('explore_entries_card_text', lang))
|
| 170 |
+
st.button(get_ui_text('explore_now_button', lang), on_click=set_page, args=(page_keys["browse"],), use_container_width=True)
|
| 171 |
with gs_cols[2]:
|
| 172 |
with st.container(border=True, height=180):
|
| 173 |
+
st.markdown(f"#### {get_ui_text('discover_map_card_header', lang)}")
|
| 174 |
+
st.markdown(get_ui_text('discover_map_card_text', lang))
|
| 175 |
+
st.button(get_ui_text('view_map_button', lang), on_click=set_page, args=(page_keys["map"],), use_container_width=True)
|
| 176 |
|
| 177 |
+
# --- Submit Wisdom Page ---
|
| 178 |
+
elif page_key == "submit":
|
| 179 |
+
st.header(get_ui_text("submit_header", lang))
|
| 180 |
+
st.markdown(get_ui_text("submit_tagline", lang))
|
| 181 |
|
| 182 |
if 'form_data' not in st.session_state:
|
| 183 |
st.session_state.form_data = {
|
| 184 |
+
'title': '', 'description': '', 'language': 'English', 'category': 'Other',
|
| 185 |
'location_name': '', 'manual_lat': 20.5937, 'manual_lon': 78.9629
|
| 186 |
}
|
| 187 |
|
| 188 |
+
st.subheader(get_ui_text("step1_header", lang))
|
|
|
|
| 189 |
with st.container(border=True):
|
| 190 |
+
st.markdown(get_ui_text("pin_location_header", lang))
|
| 191 |
+
st.caption(get_ui_text("pin_location_caption", lang))
|
| 192 |
geocode_cols = st.columns([3, 1])
|
| 193 |
+
geocode_input = geocode_cols[0].text_input("search_location", label_visibility="collapsed", placeholder=get_ui_text("find_location_placeholder", lang))
|
| 194 |
+
if geocode_cols[1].button(get_ui_text("find_location_button", lang), use_container_width=True):
|
| 195 |
if geocode_input:
|
| 196 |
coords = geocode_location(geocode_input)
|
| 197 |
if coords:
|
| 198 |
st.session_state.form_data['manual_lat'], st.session_state.form_data['manual_lon'] = coords
|
| 199 |
+
st.success(get_ui_text("location_found_success", lang).format(location=geocode_input))
|
| 200 |
else:
|
| 201 |
+
st.error(get_ui_text("location_not_found_error", lang).format(location=geocode_input))
|
| 202 |
|
| 203 |
st.markdown("---")
|
| 204 |
+
st.markdown(get_ui_text("record_voice_header", lang))
|
| 205 |
+
st.caption(get_ui_text("record_voice_caption", lang))
|
| 206 |
speech_cols = st.columns([3, 1])
|
| 207 |
+
speech_language_for_stt = speech_cols[0].selectbox("Language for Speech Recognition", get_languages(), key="speech_lang")
|
| 208 |
+
if speech_cols[1].button(get_ui_text("record_audio_button", lang), use_container_width=True):
|
| 209 |
with st.spinner("Listening..."):
|
| 210 |
+
recorded_text = speech_to_text(speech_language_for_stt)
|
| 211 |
if recorded_text:
|
| 212 |
st.session_state.form_data['description'] = recorded_text
|
| 213 |
+
st.success(get_ui_text("speech_recorded_success", lang))
|
| 214 |
st.rerun()
|
| 215 |
else:
|
| 216 |
+
st.error(get_ui_text("speech_record_error", lang))
|
| 217 |
|
| 218 |
st.markdown("---")
|
| 219 |
|
| 220 |
+
st.subheader(get_ui_text("step2_header", lang))
|
|
|
|
| 221 |
with st.form("entry_form", clear_on_submit=True):
|
| 222 |
+
with st.expander(get_ui_text("part1_header", lang), expanded=True):
|
| 223 |
+
st.caption(get_ui_text("part1_caption", lang))
|
| 224 |
+
title = st.text_input(get_ui_text("title_label", lang), value=st.session_state.form_data.get('title', ''), placeholder=get_ui_text("title_placeholder", lang))
|
| 225 |
+
description = st.text_area(get_ui_text("description_label", lang), value=st.session_state.form_data.get('description', ''), height=250, placeholder=get_ui_text("description_placeholder", lang))
|
| 226 |
|
| 227 |
col1, col2 = st.columns(2)
|
| 228 |
+
category = col1.selectbox(get_ui_text("category_label", lang), get_categories(), index=get_categories().index(st.session_state.form_data.get('category', 'Other')))
|
| 229 |
+
language_of_entry = col2.selectbox(get_ui_text("language_entry_label", lang), get_languages(), index=get_languages().index(st.session_state.form_data.get('language', 'English')))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
|
| 231 |
+
with st.expander(get_ui_text("part2_header", lang)):
|
| 232 |
+
st.caption(get_ui_text("part2_caption", lang))
|
| 233 |
+
location_name = st.text_input(get_ui_text("location_name_label", lang), value=st.session_state.form_data.get('location_name', ''))
|
| 234 |
map_center = [st.session_state.form_data['manual_lat'], st.session_state.form_data['manual_lon']]
|
| 235 |
m = folium.Map(location=map_center, zoom_start=7)
|
| 236 |
folium.Marker(map_center, popup="Your Selected Location", tooltip="Current Location").add_to(m)
|
|
|
|
| 242 |
st.rerun()
|
| 243 |
|
| 244 |
loc_cols = st.columns(2)
|
| 245 |
+
manual_lat = loc_cols[0].text_input(get_ui_text("latitude_label", lang), value=f"{st.session_state.form_data['manual_lat']:.6f}", disabled=True)
|
| 246 |
+
manual_lon = loc_cols[1].text_input(get_ui_text("longitude_label", lang), value=f"{st.session_state.form_data['manual_lon']:.6f}", disabled=True)
|
| 247 |
|
| 248 |
+
with st.expander(get_ui_text("part3_header", lang)):
|
| 249 |
+
st.caption(get_ui_text("part3_caption", lang))
|
| 250 |
+
uploaded_image = st.file_uploader(get_ui_text("image_upload_label", lang), type=['jpg', 'jpeg', 'png'])
|
| 251 |
+
uploaded_audio = st.file_uploader(get_ui_text("audio_upload_label", lang), type=['mp3', 'wav', 'ogg'])
|
| 252 |
|
|
|
|
| 253 |
st.markdown("---")
|
| 254 |
+
submitted = st.form_submit_button(get_ui_text("submit_contribution_button", lang), type="primary", use_container_width=True)
|
| 255 |
|
| 256 |
if submitted:
|
| 257 |
if title and description:
|
|
|
|
| 258 |
image_path, audio_path = None, None
|
| 259 |
media_dir = "data_entries/media"
|
| 260 |
os.makedirs(media_dir, exist_ok=True)
|
|
|
|
| 269 |
|
| 270 |
entry = {
|
| 271 |
'id': len(st.session_state.entries) + 1, 'title': title, 'description': description,
|
| 272 |
+
'language': language_of_entry, 'category': category, 'location_name': location_name,
|
| 273 |
'latitude': st.session_state.form_data['manual_lat'],
|
| 274 |
'longitude': st.session_state.form_data['manual_lon'],
|
| 275 |
'image_path': image_path, 'audio_path': audio_path,
|
|
|
|
| 281 |
if save_entry(entry):
|
| 282 |
st.session_state.entries.append(entry)
|
| 283 |
update_user_entry_count(st.session_state.username)
|
| 284 |
+
st.success(get_ui_text("submit_success", lang))
|
| 285 |
st.session_state.pop('form_data', None)
|
| 286 |
else:
|
| 287 |
+
st.error(get_ui_text("submit_failed", lang))
|
| 288 |
else:
|
| 289 |
+
st.error(get_ui_text("submit_validation_error", lang))
|
| 290 |
|
| 291 |
# --- Browse Knowledge Page ---
|
| 292 |
+
elif page_key == "browse":
|
| 293 |
+
st.header(get_ui_text("browse_header", lang))
|
| 294 |
+
st.markdown(get_ui_text("browse_tagline", lang))
|
| 295 |
|
| 296 |
with st.container(border=True):
|
| 297 |
col1, col2, col3 = st.columns(3)
|
| 298 |
with col1:
|
| 299 |
+
filter_language = st.selectbox(get_ui_text("filter_lang_label", lang), [get_ui_text("all_option", lang)] + get_languages())
|
| 300 |
with col2:
|
| 301 |
+
filter_category = st.selectbox(get_ui_text("filter_cat_label", lang), [get_ui_text("all_option", lang)] + get_categories())
|
| 302 |
with col3:
|
| 303 |
+
sort_by = st.selectbox(get_ui_text("sort_by_label", lang), [get_ui_text("newest_first_option", lang), get_ui_text("oldest_first_option", lang), get_ui_text("title_az_option", lang)])
|
| 304 |
|
| 305 |
filtered_entries = st.session_state.entries.copy()
|
| 306 |
+
if filter_language != get_ui_text("all_option", lang): filtered_entries = [e for e in filtered_entries if e.get('language') == filter_language]
|
| 307 |
+
if filter_category != get_ui_text("all_option", lang): filtered_entries = [e for e in filtered_entries if e.get('category') == filter_category]
|
| 308 |
+
|
| 309 |
+
if sort_by == get_ui_text("newest_first_option", lang): filtered_entries.sort(key=lambda x: x.get('timestamp', ''), reverse=True)
|
| 310 |
+
elif sort_by == get_ui_text("oldest_first_option", lang): filtered_entries.sort(key=lambda x: x.get('timestamp', ''))
|
| 311 |
+
elif sort_by == get_ui_text("title_az_option", lang): filtered_entries.sort(key=lambda x: x.get('title', '').lower())
|
| 312 |
|
| 313 |
+
st.markdown(get_ui_text("showing_entries", lang).format(count=len(filtered_entries), total=len(st.session_state.entries)))
|
| 314 |
st.markdown("---")
|
| 315 |
|
| 316 |
for entry in filtered_entries:
|
|
|
|
| 318 |
st.subheader(f"📖 {entry.get('title', 'Untitled')}")
|
| 319 |
|
| 320 |
meta_cols = st.columns(4)
|
| 321 |
+
meta_cols[0].markdown(get_ui_text("category_display", lang).format(category=entry.get('category', 'N/A')))
|
| 322 |
+
meta_cols[1].markdown(get_ui_text("language_display", lang).format(language=entry.get('language', 'N/A')))
|
| 323 |
+
meta_cols[2].markdown(get_ui_text("location_display", lang).format(location=entry.get('location_name', 'N/A')))
|
| 324 |
+
meta_cols[3].markdown(get_ui_text("submitted_display", lang).format(date=entry.get('timestamp', 'N/A')[:10]))
|
| 325 |
|
| 326 |
+
with st.expander(get_ui_text("view_details_expander", lang)):
|
| 327 |
col1, col2 = st.columns([2, 1])
|
| 328 |
with col1:
|
| 329 |
+
st.markdown(get_ui_text("description_display", lang))
|
| 330 |
st.write(entry.get('description', 'No description'))
|
| 331 |
+
if st.button(get_ui_text("listen_button", lang), key=f"tts_{entry.get('id')}"):
|
| 332 |
text_to_speech(entry.get('description', ''), entry.get('language'))
|
| 333 |
|
| 334 |
with col2:
|
| 335 |
if entry.get('image_path') and os.path.exists(entry['image_path']):
|
| 336 |
+
st.image(Image.open(entry['image_path']), caption=get_ui_text("image_caption", lang), use_container_width=True)
|
| 337 |
if entry.get('audio_path') and os.path.exists(entry['audio_path']):
|
| 338 |
st.audio(entry['audio_path'])
|
| 339 |
if entry.get('latitude') and entry.get('longitude'):
|
| 340 |
+
st.markdown(get_ui_text("coords_display", lang).format(lat=f"{entry['latitude']:.4f}", lon=f"{entry['longitude']:.4f}"))
|
| 341 |
|
| 342 |
# --- Knowledge Map Page ---
|
| 343 |
+
elif page_key == "map":
|
| 344 |
+
st.header(get_ui_text("map_header", lang))
|
| 345 |
+
st.markdown(get_ui_text("map_tagline", lang))
|
| 346 |
|
| 347 |
geo_entries = [e for e in st.session_state.entries if e.get('latitude') and e.get('longitude')]
|
| 348 |
|
|
|
|
| 351 |
for entry in geo_entries:
|
| 352 |
popup_html = f"""
|
| 353 |
<b>{entry.get('title', 'Untitled')}</b><br>
|
| 354 |
+
<b>{get_ui_text("map_popup_category", lang)}</b> {entry.get('category', 'N/A')}<br>
|
| 355 |
+
<b>{get_ui_text("map_popup_language", lang)}</b> {entry.get('language', 'N/A')}
|
| 356 |
"""
|
| 357 |
folium.Marker(
|
| 358 |
[entry['latitude'], entry['longitude']],
|
|
|
|
| 360 |
tooltip=entry.get('title', 'Untitled')
|
| 361 |
).add_to(m)
|
| 362 |
st_folium(m, width=1200, height=600, returned_objects=[])
|
| 363 |
+
st.info(get_ui_text("map_info", lang).format(count=len(geo_entries)))
|
| 364 |
else:
|
| 365 |
+
st.warning(get_ui_text("map_warning", lang))
|
| 366 |
|
| 367 |
# --- Search Page ---
|
| 368 |
+
elif page_key == "search":
|
| 369 |
+
st.header(get_ui_text("search_header", lang))
|
| 370 |
+
st.markdown(get_ui_text("search_tagline", lang))
|
| 371 |
|
| 372 |
with st.container(border=True):
|
| 373 |
+
search_query = st.text_input("search", label_visibility="collapsed", placeholder=get_ui_text("search_placeholder", lang))
|
| 374 |
+
with st.expander(get_ui_text("advanced_filters_expander", lang)):
|
| 375 |
col1, col2 = st.columns(2)
|
| 376 |
+
search_language = col1.selectbox(get_ui_text("filter_lang_label", lang), [get_ui_text("all_option", lang)] + get_languages(), key="search_lang")
|
| 377 |
+
search_category = col2.selectbox(get_ui_text("filter_cat_label", lang), [get_ui_text("all_option", lang)] + get_categories(), key="search_cat")
|
| 378 |
+
has_media = col1.checkbox(get_ui_text("has_media_checkbox", lang))
|
| 379 |
+
has_location = col2.checkbox(get_ui_text("has_location_checkbox", lang))
|
| 380 |
|
| 381 |
if search_query:
|
| 382 |
results = search_entries(
|
| 383 |
st.session_state.entries, search_query,
|
| 384 |
+
language=search_language,
|
| 385 |
+
category=search_category,
|
| 386 |
has_media=has_media, has_location=has_location
|
| 387 |
)
|
| 388 |
+
st.markdown(get_ui_text("search_results_header", lang).format(count=len(results), query=search_query))
|
| 389 |
for entry in results:
|
| 390 |
with st.container(border=True):
|
| 391 |
st.subheader(f"📖 {entry.get('title', 'Untitled')}")
|
| 392 |
+
st.markdown(f"**{get_ui_text('map_popup_category', lang)}** {entry.get('category', 'N/A')} | **{get_ui_text('map_popup_language', lang)}** {entry.get('language', 'N/A')} | **{get_ui_text('location_display', lang).format(location=entry.get('location_name', 'N/A'))}**")
|
| 393 |
st.write(entry.get('description', 'No description'))
|
| 394 |
|
| 395 |
# --- Translation Hub Page ---
|
| 396 |
+
elif page_key == "translate":
|
| 397 |
+
st.header(get_ui_text("translation_hub_header", lang))
|
| 398 |
+
st.markdown(get_ui_text("translation_hub_tagline", lang))
|
| 399 |
|
| 400 |
with st.container(border=True):
|
| 401 |
col1, col2 = st.columns(2)
|
| 402 |
with col1:
|
| 403 |
+
st.subheader(get_ui_text("original_text_header", lang))
|
| 404 |
+
source_language = st.selectbox(get_ui_text("source_language_label", lang), ["Auto-detect"] + get_languages())
|
| 405 |
+
source_text = st.text_area(get_ui_text("text_to_translate_label", lang), height=200, placeholder=get_ui_text("text_to_translate_placeholder", lang), label_visibility="collapsed")
|
| 406 |
|
| 407 |
with col2:
|
| 408 |
+
st.subheader(get_ui_text("translated_text_header", lang))
|
| 409 |
+
target_language = st.selectbox(get_ui_text("target_language_label", lang), get_languages())
|
| 410 |
+
if st.button(get_ui_text("translate_button", lang), type="primary", use_container_width=True) and source_text:
|
| 411 |
with st.spinner("Translating..."):
|
| 412 |
source_lang_code = source_language if source_language != "Auto-detect" else "auto"
|
| 413 |
translated_text = translate_text(source_text, target_language, source_lang_code)
|
| 414 |
+
st.text_area(get_ui_text("translation_output_label", lang), value=translated_text, height=200, key="translated_output", label_visibility="collapsed")
|
| 415 |
else:
|
| 416 |
+
st.text_area(get_ui_text("translation_output_label", lang), value="", height=200, label_visibility="collapsed")
|
| 417 |
|
| 418 |
# --- Export Data Page ---
|
| 419 |
+
elif page_key == "export":
|
| 420 |
+
st.header(get_ui_text("export_header", lang))
|
| 421 |
+
st.markdown(get_ui_text("export_tagline", lang))
|
| 422 |
|
| 423 |
with st.container(border=True):
|
| 424 |
+
st.subheader(get_ui_text("export_config_header", lang))
|
| 425 |
col1, col2 = st.columns(2)
|
| 426 |
+
export_format = col1.selectbox(get_ui_text("format_label", lang), ["CSV", "JSONL"])
|
| 427 |
+
export_language = col2.selectbox(get_ui_text("filter_lang_label", lang), [get_ui_text("all_option", lang)] + get_languages(), key="export_lang")
|
| 428 |
+
export_category = col1.selectbox(get_ui_text("filter_cat_label", lang), [get_ui_text("all_option", lang)] + get_categories(), key="export_cat")
|
| 429 |
|
| 430 |
+
if st.button(get_ui_text("generate_export_button", lang), type="primary", use_container_width=True):
|
| 431 |
filtered_entries = st.session_state.entries.copy()
|
| 432 |
+
if export_language != get_ui_text("all_option", lang): filtered_entries = [e for e in filtered_entries if e.get('language') == export_language]
|
| 433 |
+
if export_category != get_ui_text("all_option", lang): filtered_entries = [e for e in filtered_entries if e.get('category') == export_category]
|
| 434 |
|
| 435 |
if not filtered_entries:
|
| 436 |
+
st.warning(get_ui_text("export_no_match_warning", lang))
|
| 437 |
else:
|
| 438 |
timestamp = datetime.datetime.now(datetime.timezone.utc).strftime('%Y%m%d')
|
| 439 |
if export_format == "CSV":
|
| 440 |
export_data = export_to_csv(filtered_entries, True, True)
|
| 441 |
st.download_button(
|
| 442 |
+
label=get_ui_text("export_download_csv_button", lang), data=export_data,
|
| 443 |
file_name=f"farming_wisdom_{timestamp}.csv", mime="text/csv", use_container_width=True
|
| 444 |
)
|
| 445 |
else: # JSONL
|
| 446 |
export_data = export_to_jsonl(filtered_entries, True, True)
|
| 447 |
st.download_button(
|
| 448 |
+
label=get_ui_text("export_download_jsonl_button", lang), data=export_data,
|
| 449 |
file_name=f"farming_wisdom_{timestamp}.jsonl", mime="application/jsonl", use_container_width=True
|
| 450 |
)
|
| 451 |
+
st.success(get_ui_text("export_ready_success", lang).format(count=len(filtered_entries)))
|
| 452 |
|
| 453 |
# --- Profile Page ---
|
| 454 |
+
elif page_key == "profile":
|
| 455 |
+
st.header(get_ui_text("profile_header", lang).format(name=user_info.get('full_name', st.session_state.username)))
|
| 456 |
st.markdown("---")
|
| 457 |
|
| 458 |
+
tab1, tab2, tab3 = st.tabs([f"**{get_ui_text('my_info_tab', lang)}**", f"**{get_ui_text('my_contributions_tab', lang)}**", f"**{get_ui_text('settings_tab', lang)}**"])
|
| 459 |
|
| 460 |
with tab1:
|
| 461 |
with st.container(border=True):
|
| 462 |
+
st.subheader(get_ui_text("profile_info_header", lang))
|
| 463 |
info_cols = st.columns(2)
|
| 464 |
+
info_cols[0].markdown(get_ui_text("full_name_display", lang).format(name=user_info.get('full_name', 'Not provided')))
|
| 465 |
+
info_cols[0].markdown(get_ui_text("username_display", lang).format(username=st.session_state.username))
|
| 466 |
+
info_cols[1].markdown(get_ui_text("email_display", lang).format(email=user_info.get('email', 'Not provided')))
|
| 467 |
+
info_cols[1].markdown(get_ui_text("member_since_display", lang).format(date=user_info.get('registration_date', 'Unknown')[:10]))
|
| 468 |
+
st.metric(get_ui_text("total_entries_metric_profile", lang), user_info.get('entries_submitted', 0))
|
| 469 |
|
| 470 |
with tab2:
|
| 471 |
with st.container(border=True):
|
| 472 |
+
st.subheader(get_ui_text("my_recent_contributions_header", lang))
|
| 473 |
my_entries = [e for e in st.session_state.entries if e.get('contributor') == st.session_state.username]
|
| 474 |
if my_entries:
|
| 475 |
+
st.write(get_ui_text("contributions_count_message", lang).format(count=len(my_entries)))
|
| 476 |
for entry in reversed(my_entries[-5:]):
|
| 477 |
st.markdown(f"- **{entry.get('title', 'Untitled')}** (*{entry.get('category', 'N/A')}*)")
|
| 478 |
else:
|
| 479 |
+
st.info(get_ui_text("no_contributions_message", lang))
|
| 480 |
|
| 481 |
with tab3:
|
| 482 |
with st.container(border=True):
|
| 483 |
+
st.subheader(get_ui_text("account_prefs_header", lang))
|
| 484 |
+
st.selectbox(get_ui_text("preferred_language_label", lang), get_languages(), index=1)
|
| 485 |
+
st.checkbox(get_ui_text("email_notifications_checkbox", lang), value=False)
|
| 486 |
+
if st.button(get_ui_text("update_profile_button", lang), use_container_width=True):
|
| 487 |
+
st.success(get_ui_text("profile_update_success", lang))
|
| 488 |
|
| 489 |
# --- Footer ---
|
| 490 |
st.sidebar.markdown("---")
|
| 491 |
+
st.sidebar.info(get_ui_text("app_version_footer", lang))
|