import streamlit as st import folium from streamlit_folium import folium_static import streamlit.components.v1 as components from datetime import datetime import gspread from google.oauth2.service_account import Credentials import os import json import random import time # تنظیمات اولیهه st.set_page_config(layout="wide", page_title="راهیار - تحلیل انصاف قیمتی", page_icon="🚖") # ========== تنظیمات دیتا ========== SHEET_ID = "1mmdWAyOCYq4yXMgP53Duq712AnlqZWLkfIo76JqM7wM" SHEET_NAME = "Condition1" # ========== استایل‌های سفارشی یکپارچه ========== st.markdown(""" """, unsafe_allow_html=True) # ========== توابع اصلی ========== def enhanced_likert_scale(question_data): """لیکرت اسکیل با محدوده 0 تا scale و عدم نمایش نقطه برای 0""" question = question_data["question"] key = question_data["key"] scale = question_data["scale"] labels = question_data.get("labels", ["کاملاً مخالفم", "کاملاً موافقم"]) # مقداردهی اولیه if key not in st.session_state: st.session_state[key] = 0 # نمایش سوال st.markdown(f"
{question}
", unsafe_allow_html=True) # ایجاد خط و نقاط با HTML/CSS scale_html = f"""
{labels[1]} {labels[0]}
""" # اضافه کردن نقاط فقط برای مقادیر 1 تا scale current_value = st.session_state.get(key, 0) for i in range(1, scale+1): value = scale-i+1 # مقادیر از 1 تا scale active_class = "active" if current_value == value else "" scale_html += f"
" scale_html += "
" # نمایش کامپوننت HTML components.html(scale_html, height=60) # ورودی عددی با محدوده 0 تا scale (0 قابل انتخاب است) value = st.number_input( "پاسخ شما (0 برای پاسخ ندادن، 1 تا 7 برای پاسخ):", min_value=0, # 0 مجاز است max_value=scale, value=st.session_state.get(key, 0), step=1, key=f"{key}_input", placeholder="0 (پاسخ نداده) یا 1-7", format="%d", on_change=lambda: st.session_state.update({key: st.session_state[f"{key}_input"]}) ) # خط جداکننده بنفش st.markdown('
', unsafe_allow_html=True) return st.session_state.get(key) def create_ride_map(): """ایجاد نقشه سفر با Folium - نسخه اصلاح شده با مناطق عمومی""" # نقاط تقریبی برای مناطق عمومی جنوب و غرب تهران south_tehran = [35.65, 51.38] # منطقه عمومی جنوب تهران west_tehran = [35.72, 51.31] # منطقه عمومی غرب تهران # مرکز نقشه بین دو نقطه m = folium.Map(location=[35.685, 51.315], zoom_start=11) # ایجاد دایره برای مبدأ (جنوب تهران) folium.Circle( location=south_tehran, radius=2500, # شعاع 1.5 کیلومتر popup="مبدأ: منطقه جنوب تهران", color="#6a0dad", fill=True, fill_color="#6a0dad", fill_opacity=0.2, weight=2 ).add_to(m) # ایجاد دایره برای مقصد (غرب تهران) folium.Circle( location=west_tehran, radius=2500, # شعاع 1.5 کیلومتر popup="مقصد: منطقه غرب تهران", color="#ff0000", fill=True, fill_color="#ff0000", fill_opacity=0.2, weight=2 ).add_to(m) # خط ارتباطی بین دو منطقه folium.PolyLine( [south_tehran, west_tehran], color="#6a0dad", weight=3, opacity=0.7, dash_array='5, 5' # خط چین ).add_to(m) return m def show_explanation(exp_type): """نمایش توضیحات قیمت""" explanations = { "input": [ " سطح تقاضا در منطقه: زیاد (+)", " تعداد رانندگان فعال: کم (+)", " زمان روز: ساعت اوج ترافیک (+)", "شرایط جوی: هوای بارانی (++)" ], "counterfactual": [ "اگر این سفر را 1 ساعت دیرتر درخواست کنید، به دلیل سطح تقاضای کمتر، رانندگان فعال بیش‌تر، زمان بهتر روز و شرایط جوی بهتر، احتمالاً قیمت حدوداً 40٪ کمتر (120 هزار تومان) خواهد بود.", ] } if exp_type != "control": if exp_type == "input": st.markdown("

علت قیمت گذاری:

", unsafe_allow_html=True) st.markdown("""
(تعداد علامت + نشان دهنده شدت اثر عامل بر قیمت است)
""", unsafe_allow_html=True) for item in explanations.get(exp_type, []): st.markdown(f"

• {item}

", unsafe_allow_html=True) elif exp_type == "counterfactual": st.markdown("

علت قیمت گذاری:

", unsafe_allow_html=True) for item in explanations.get(exp_type, []): st.markdown(f"

• {item}

", unsafe_allow_html=True) # ========== توابع مدیریت داده‌ها ========== def get_credentials(): """دریافت اعتبارنامه از Secrets""" try: service_account_json = os.environ.get('GCP_SERVICE_ACCOUNT') if not service_account_json: st.error("مقدار GCP_SERVICE_ACCOUNT در محیط یافت نشد") return None service_account_info = json.loads(service_account_json) creds = Credentials.from_service_account_info( service_account_info, scopes=[ "https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/drive.file" ] ) return creds except Exception as e: st.error(f"خطا در دریافت اعتبارنامه: {str(e)}") return None def save_to_sheet(data): try: creds = get_credentials() if not creds: return False client = gspread.authorize(creds) spreadsheet = client.open_by_key(SHEET_ID) worksheet = spreadsheet.worksheet(SHEET_NAME) row_data = [ data.get("start_time", ""), # زمان شروع data.get("end_time", ""), # زمان پایان data.get("completion_time", ""), # مدت زمان تکمیل (ثانیه) data.get("scenario_type", ""), data.get("price", ""), data.get("age", ""), data.get("gender", ""), data.get("education", ""), data.get("ride_frequency", ""), data.get("related_education_job",""), data.get("city",""), data.get("user_contact", ""), data.get("price_accepted", ""), # سوالات توجه data.get("attention_check1", ""), data.get("attention_check2", ""), # سوالات distributive (7 گزینه‌ای) data.get("distributive_1", ""), data.get("distributive_2", ""), data.get("distributive_3", ""), # سوالات procedural (7 گزینه‌ای) data.get("procedural_1", ""), data.get("procedural_2", ""), data.get("procedural_3", ""), # سوالات informational (5 گزینه‌ای) data.get("informational_1", ""), data.get("informational_2", ""), data.get("informational_3", ""), data.get("informational_4", ""), data.get("informational_5", ""), # سوالات manipulation data.get ("trust", ""), data.get("pricing_method", ""), data.get("price_increase", ""), data.get("explanation_received", ""), data.get("explanation_type", "") ] worksheet.append_row(row_data) return True except Exception as e: st.error(f"خطا در ذخیره‌سازی: {str(e)}") return False # ========== بخش‌های فرم ========== def welcome_page(): """صفحه خوشامدگویی""" st.markdown("""
لوگو دانشگاه شریف

به پرسشنامه ما خوش آمدید 🌟

با سلام و احترام،

از شما دعوت می‌شود در یک پژوهش دانشگاهی شرکت کنید که در قالب پایان‌نامه کارشناسی‌ارشد در دانشگاه صنعتی شریف انجام می‌شود. این تحقیق به بررسی ادراک مصرف‌کنندگان از انصاف در قیمت‌گذاریِ اپلیکیشن‌های تاکسی اینترنتی (مانند اسنپ و تپسی 🚖) می‌پردازد.

شرکت در این مطالعه کاملاً داوطلبانه است؛ پاسخ دقیقی برای سوالات وجود ندارد و پاسخ‌های صادقانه شما فقط برای پیش‌برد اهداف علمی تحلیل خواهد شد.

پر کردن این پرسشنامه کمتر از 5 دقیقه وقت شما را می‌گیرد و پاسخ‌های ارزشمند شما کمک شایانی به ارتقای دانش علمی خواهد کرد. پیشاپیش از مشارکت شما صمیمانه سپاسگزاریم 🙏

برای آغاز پرسشنامه، لطفاً روی دکمه زیر کلیک کنید 👇🏻

""", unsafe_allow_html=True) if st.button("شروع پرسشنامه", key="start_btn", type="primary"): st.session_state.current_page = "scenario_explanation" st.session_state.start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") st.rerun() def scenario_explanation(): """توضیح سناریو""" col1, col2 = st.columns([2, 4]) # افزایش نسبت ستون اول with col1: st.markdown('
', unsafe_allow_html=True) try: st.image("rahyar.png", width=80) except: st.image("https://via.placeholder.com/80/6a0dad/FFFFFF?text=LOGO", width=80) st.markdown('
', unsafe_allow_html=True) with col2: st.markdown("""

رهیار 🚖

همراه سفرهای درون‌شهری شما، راهی مطمئن، راهی روشن، رهیار

""", unsafe_allow_html=True) st.markdown("### سناریوی تحقیق") st.markdown("""

فرض کنید در روزی از روزها شما مهمان یکی از اقوام‌تان در جنوب تهران هستید. قصد دارید برای خریدی به غرب تهران بروید...

گوشی‌تان را از کیف درمی‌آورید. چشم‌تان به آیکون جدیدی می‌افتد؛ اپلیکیشنی به نام رهیار — نه اسنپ است و نه تپسی، اما خیلی شبیه آن‌هاست. رنگ بنفش جذابی دارد و شعارش توی ذهن‌تان می‌نشیند:
«همراه سفرهای شما، راهی مطمئن، راهی روشن، رهیار»

با کنجکاوی اپ را باز می‌کنید. ظاهر ساده و روانی دارد. فعلاً فقط گزینه‌ی «سفر معمولی» فعال است. خبری از امکانات اضافه مثل «سفر دو‌مسیـره»، «توقف در مسیر»، «اکوپلاس» یا «موتورسوار» نیست — اما خب، رهیار تازه‌کار است و قرار است توسعه پیدا کند!.

مبدأ و مقصد را انتخاب می‌کنید و با قیمت مواجه می‌شوید.

با کلیک روی «ادامه»، اطلاعات سفر را مشاهده کنید 👇🏻

""", unsafe_allow_html=True) if st.button("ادامه", key="continue_btn", type="primary"): st.session_state.current_page = "map_view" st.rerun() def map_view(): col1, col2 = st.columns([2, 4]) # افزایش نسبت ستون اول with col1: st.markdown('
', unsafe_allow_html=True) try: st.image("rahyar.png", width=80) except: st.image("https://via.placeholder.com/80/6a0dad/FFFFFF?text=LOGO", width=80) st.markdown('
', unsafe_allow_html=True) with col2: st.markdown("""

رهیار 🚖

همراه سفرهای درون‌شهری شما، راهی مطمئن، راهی روشن، رهیار

""", unsafe_allow_html=True) st.markdown("""

مسیر سفر شما به صورت حدودی، از جنوب به غرب تهران است.

با توجه به اطلاعاتی که بعد از نقشه دریافت می‌کنید، تصمیم بگیرید که سفر را می‌پذیرید را رد می‌کنید.

سپس با کلیک بر دکمه مربوطه به بخش بعدی بروید.

""", unsafe_allow_html=True) st.markdown("### مسیر سفر شما") folium_static(create_ride_map(), width=1000 if st.session_state.is_desktop else 800, height=500 if st.session_state.is_desktop else 400) # قیمت st.markdown(f"""
رهیار معمولی {st.session_state.price:,} تومان
""", unsafe_allow_html=True) show_explanation(st.session_state.scenario_type) # دکمه‌ها col1, col2 = st.columns(2) with col1: if st.button("درخواست سفر", key="accept_btn", use_container_width=True): st.session_state.price_accepted = 1 st.session_state.current_page = "attention_check1" st.rerun() with col2: if st.button("رد سفر", key="reject_btn", use_container_width=True): st.session_state.price_accepted = 0 st.session_state.current_page = "attention_check1" st.rerun() def attention_check1(): """سوال توجه اول با دکمه سبز کاملاً عملی""" # 1. تزریق استایل‌های سفارشی st.markdown(""" """, unsafe_allow_html=True) st.markdown("### سوال") answer = st.radio( "رنگ لوگو و تم رنگی اپلیکیشن رهیار چگونه بود؟", ["قرمز", "سبز", "بنفش", "آبی", "فراموش کردم"], index=None, key="att1_radio" ) # کامپوننت HTML با دکمه سبز اصلی st.components.v1.html(f""" """, height=70) # 3. منطق اصلی دکمه (مخفی) if st.button("ادامه", key="att1_real_btn", type="primary"): if answer: st.session_state.attention_check1 = answer st.session_state.current_page = "random_likert_questions" st.rerun() else: st.warning("لطفاً یک گزینه را انتخاب کنید") def random_likert_questions(): """نمایش سوالات لیکرت با دکمه‌های دایره‌ای""" question_groups = [ { "title": "عدالت توزیعی", "key": "distributive", "questions": [ { "key": "distributive_1", "question": "قیمتی که به شما ارائه شد، چگونه بود؟", "scale": 7, "labels": ["کاملاً نامنصفانه", "کاملاً منصفانه"] }, { "key": "distributive_2", "question": "قیمتی که به شما ارائه شد، چگونه بود؟", "scale": 7, "labels": ["کاملاً غیرمعقول", "کاملاً معقول"] }, { "key": "distributive_3", "question": "قیمتی که به شما ارائه شد، چگونه بود؟", "scale": 7, "labels": ["کاملاً غیرقابل قبول", "کاملاً قابل قبول"] } ] }, { "title": "سوال توجه", "key": "attention_check", "questions": [ {"key": "attention_check2", "question": "تا چه مقدار با دقت به سوالات پاسخ می‌دهید؟", "scale": 7, "labels": ["خیلی کم", "خیلی زیاد"]} ] }, { "title": "عدالت رویه‌ای", "key": "procedural", "questions": [ {"key": "procedural_1", "question": "فرآیند و رویه قیمت‌گذاری پلتفرم قابل قبول است", "scale": 7, "labels": ["کاملاً مخالفم", "کاملاً موافقم"]}, {"key": "procedural_2", "question": "فرآیند و رویه قیمت‌گذاری پلتفرم منصفانه است", "scale": 7, "labels": ["کاملاً مخالفم", "کاملاً موافقم"]}, {"key": "procedural_3", "question": "فرآیند و رویه قیمت‌گذاری پلتفرم معقول است", "scale": 7, "labels": ["کاملاً مخالفم", "کاملاً موافقم"]} ] }, { "title": "عدالت اطلاعاتی", "key": "informational", "questions": [ {"key": "informational_1", "question": "تا چه حد رهیار دلایل تعیین قیمت را به صورت صادقانه توضیح داد؟", "scale": 7, "labels": ["هیچ", "خیلی زیاد"]}, {"key": "informational_2", "question": "تا چه حد رهیار عوامل مؤثر بر تعیین قیمت را به طور کامل شرح داد؟", "scale": 7, "labels": ["هیچ", "خیلی زیاد"]}, {"key": "informational_3", "question": "تا چه حد دلایل ارائه‌شده توسط رهیار برای تعیین قیمت منطقی و قابل قبول بود؟", "scale": 7, "labels": ["هیچ", "خیلی زیاد"]}, {"key": "informational_4", "question": "تا چه حد توضیحات درباره تعیین قیمت بلافاصله و در زمان مناسب نمایش داده شد؟", "scale": 7, "labels": ["هیچ", "خیلی زیاد"]}, {"key": "informational_5", "question": "تا چه حد توضیحات رهیار درباره تعیین قیمت، متناسب با شرایط سفر شما بود؟", "scale": 7, "labels": ["هیچ", "خیلی زیاد"]} ] } ] # مقداردهی اولیه if 'current_likert_group' not in st.session_state: st.session_state.current_likert_group = 0 current_group = question_groups[st.session_state.current_likert_group] st.markdown("""

راهنمای پاسخ:

برای پاسخ به سوالات، با یک طیف کشویی مواجه خواهید شد.

در این طیف 7 نقطه وجود دارد:
- سمت چپ: کمترین مقدار
- سمت راست: بیشترین مقدار

نقطه روی کشو را جابجا کنید و در مقداری که پاسخ شماست متوقف شوید.

پاسخ شما زیر طیف نمایش داده خواهد شد.

اگر از پاسخ‌هایتان مطمئن هستید، روی دکمه «ادامه» کلیک کنید.

""", unsafe_allow_html=True) # نمایش تمام سوالات این گروه for question in current_group['questions']: answer = enhanced_likert_scale(question) st.session_state.answers[question["key"]] = answer # دکمه ادامه/اتمام button_label = "ادامه به گروه بعدی" if st.session_state.current_likert_group < len(question_groups)-1 else "اتمام پرسشنامه" if st.button(button_label): # بررسی آیا همه سوالات این گروه پاسخ داده شده‌اند و هیچ کدام صفر نیستند all_answered = all( question["key"] in st.session_state.answers and st.session_state.answers[question["key"]] is not None for question in current_group['questions'] ) has_zero = any( st.session_state.answers.get(question["key"]) == 0 for question in current_group['questions'] ) if not all_answered or has_zero: st.error("لطفاً به تمام سوالات این بخش پاسخ دهید (مقدار صفر مجاز نیست)") else: if st.session_state.current_likert_group < len(question_groups) - 1: st.session_state.current_likert_group += 1 st.rerun() else: st.session_state.current_page = "explanation_questions" st.rerun() def explanation_questions(): """نمایش سوالات تکمیلی به صورت مرحله‌ای با دکمه ادامه""" st.markdown("### 📋 سوالات تکمیلی") # لیست سوالات به ترتیب نمایش questions = [ { "key": "price_increase", "label": "در سناریوی کیس رهیار، آیا به نظر شما قیمت ارائه‌شده برای مسیر موردنظر، در مقایسه با قیمت‌های معمول برای همان مسیر یا مسیرهای مشابه، افزایش غیرمعمول یا نامتعارفی داشته است یا خیر؟ (با صرف‌نظر از تغییرات قیمتی ناشی از تورم یا نوسانات عمومی قیمت)‌", "options": ["بله", "خیر", "مطمئن نیستم"], "required": True }, { "key": "pricing_method", "label": "به نظر شما رهیار چگونه قیمت‌های پویا و متغیر را اعمال می‌کند؟", "options": [ "به صورت دستی توسط تیم رهیار", "به صورت خودکار توسط هوش مصنوعی و الگوریتم‌ها", "ترکیبی از هر دو روش", "نظری ندارم" ], "required": True }, { "key": "trust", "label": "در زندگی روزمره ممکن است با تصمیمات هوش مصنوعی مواجه شوید، مانند پیشنهاد مسیر برای اجتناب از ترافیک، ارائه دستور پخت بر اساس مواد غذایی، توصیه پوشش یا نکات ارائه برای جلسات کاری، پیشنهاد سهام برای سرمایه‌گذاری، یا معرفی افراد در برنامه‌های دوستیابی. فارغ از تصمیمات حیاتی (مثل تشخیص پزشکی یا احکام قضایی)، آیا به تصمیم‌گیری‌های هوش مصنوعی در این موارد روزمره اعتماد دارید؟", "options": ["بله", "تا حدودی", "خیر", "نظری ندارم"], "required": True }, { "key": "explanation_received", "label": "آیا برای قیمت پیشنهادی این سفر، توضیحی به شما ارائه شد؟", "options": ["بله", "خیر"], "required": True }, { "key": "explanation_type", "label": "اگر توضیحی دریافت کردید، این توضیح بیشتر به کدام مورد شباهت داشت؟", "options": [ "با توجه به عوامل مؤثر در تعیین قیمت و شدت تأثیر آن‌ها", "به من اطلاع دادند که در صورت انتظار و درخواست سفر در شرایط متفاوتِ زمانی دیگر، ممکن است قیمت دیگری دریافت کنم", "توضیحی دریافت نکردم" ], "required": False, "condition": lambda: st.session_state.get("explanation_received") == "بله" } ] # مقداردهی اولیه step اگر وجود ندارد if "explanation_step" not in st.session_state: st.session_state.explanation_step = 0 # اگر همه سوالات پاسخ داده شده‌اند، به صفحه بعدی برو if st.session_state.explanation_step >= len(questions): st.session_state.current_page = "demographic" st.rerun() return # دریافت سوال جاری current_q = questions[st.session_state.explanation_step] # بررسی شرط نمایش برای سوالات اختیاری if "condition" in current_q and not current_q["condition"](): st.session_state[current_q["key"]] = "N/A" st.session_state.explanation_step += 1 st.rerun() return # نمایش سوال جاری answer = st.radio( current_q["label"], current_q["options"], index=None, key=f"explanation_q_{current_q['key']}" ) # دکمه ادامه if st.button("ادامه", key=f"continue_{current_q['key']}"): if answer is None and current_q["required"]: st.warning("لطفاً یک گزینه را انتخاب کنید") else: # ذخیره پاسخ st.session_state[current_q["key"]] = answer if answer is not None else "N/A" # افزایش شماره مرحله st.session_state.explanation_step += 1 # رفرش صفحه برای نمایش سوال بعدی st.rerun() def demographic_form(): """فرم اطلاعات دموگرافیک""" st.markdown("### 📝 اطلاعات دموگرافیک") st.markdown("""

لطفاً اطلاعات زیر را صادقانه و به دقت وارد نمایید.

همانطور که گفته شد،این اطلاعات کاملاً محرمانه نزد محقق خواهد ماند و در جهت اهداف پژوهشی استفاده خواهد شد.

""", unsafe_allow_html=True) with st.form("demographic_form"): age = st.number_input("سن", min_value=18, max_value=100, value=None, placeholder="سن خود را وارد کنید") gender = st.selectbox("جنسیت", ["", "مرد", "زن", "سایر"], index=0) education = st.selectbox("تحصیلات", ["", "دیپلم", "لیسانس", "فوق لیسانس", "دکترا"], index=0) city = st.selectbox("لطفاً استان محل سکونت خود را انتخاب بفرمایید.", ["", "آذربایجان شرقی", "آذربایجان غربی", "اردبیل", "اصفهان", "البرز", "ایلام", "بوشهر", "تهران", "چهارمحال و بختیاری", "خراسان جنوبی", "خراسان رضوی", "خراسان شمالی", "خوزستان", "زنجان", "سمنان", "سیستان و بلوچستان", "فارس", "قزوین", "قم", "کردستان", "کرمان", "کرمانشاه", "کهگیلویه و بویراحمد", "گلستان", "گیلان", "لرستان", "مازندران", "مرکزی", "هرمزگان", "همدان", "یزد"], index=0) related_education_job = st.selectbox("رشته تحصیلی/شغل شما در کدام‌یک از دسته‌های زیر قرار دارد؟", ["", "مهندسی", "درمانی", "فرهنگی", "مدیریتی (مالی)", "مدیریتی (بازاریابی)", "مدیریتی (سایر)", "روانشناسی", "اقتصادی", "حقوقی", "هنری", "ورزشی", "زبان", "غیره"], index=0) ride_frequency = st.selectbox("دفعات استفاده از سرویس‌های اشتراک سفر در ماه", ["", "هیچوقت", "کمتر از 5 بار", "5-10 بار", "بیش از 10 بار"], index=0) submitted = st.form_submit_button("ادامه") if submitted: if not all([age, gender, education, city, related_education_job, ride_frequency]): st.error("لطفاً تمام فیلدها را پر کنید") else: st.session_state.demographic_data = { "age": age, "gender": gender, "education": education, "city": city, "ride_frequency": ride_frequency, "related_education_job": related_education_job } st.session_state.current_page = "contact" st.rerun() def user_contact(): """راه ارتباطی ساده""" st.markdown("""

📩 راه ارتباطی شما (اختیاری)

در صورت تمایل به شرکت در قرعه‌کشی می‌توانید آیدی تلگرام، شماره تماس یا ایمیل خود را وارد کنید:

""", unsafe_allow_html=True) contact_info = st.text_input( "راه ارتباطی (اختیاری)", placeholder="مثال: @username یا 09123456789 یا example@email.com", key="user_contact_input" ) if st.button("ثبت پاسخ‌ها", type="primary", key="submit_explanation"): st.session_state.user_contact = contact_info end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") start_time = datetime.strptime(st.session_state.start_time, "%Y-%m-%d %H:%M:%S") completion_time = (datetime.now() - start_time).total_seconds() save_data = { "start_time": st.session_state.start_time, "end_time": end_time, "completion_time": completion_time, "scenario_type": st.session_state.scenario_type, "price": st.session_state.price, "user_contact": st.session_state.get("user_contact", ""), "price_accepted": st.session_state.get("price_accepted", 0), "attention_check1": st.session_state.get("attention_check1", None), "trust": st.session_state.trust, "pricing_method": st.session_state.pricing_method, "price_increase": st.session_state.price_increase, "explanation_received": st.session_state.explanation_received, "explanation_type": st.session_state.get("explanation_type", "N/A"), **st.session_state.demographic_data, **st.session_state.answers # اضافه کردن تمام پاسخ‌های لیکرت } if save_to_sheet(save_data): st.session_state.current_page = "thank_you" st.rerun() else: st.error("خطا در ذخیره‌سازی داده‌ها. لطفاً دوباره تلاش کنید.") def thank_you_page(): """صفحه تشکر""" st.success(""" ✅ پاسخ‌های شما با موفقیت ثبت شد. سپاسگزاریم که وقت ارزشمند خود را به این پژوهش اختصاص دادید. در صورت وجود هرگونه سوال، ابهام یا پیشنهاد می‌توانید با محقق تماس بگیرید: ✉ ایمیل: maryam.ilka2000@gmail.com """) st.balloons() # ========== مدیریت وضعیت و صفحه‌بندی ========== def main(): # تشخیص دستگاه user_agent = st.query_params.get("user_agent", [""])[0] st.session_state.is_desktop = "mobile" not in user_agent.lower() if st.session_state.is_desktop: # اطمینان از نمایش همان حالت موبایل برای همه دستگاه‌ها st.session_state.is_desktop = False if 'answers' not in st.session_state: st.session_state.answers = {} if 'current_page' not in st.session_state: st.session_state.current_page = "welcome" st.session_state.scenario_type = random.choice(["control", "input", "counterfactual"]) st.session_state.price = 200000 st.session_state.user_contact = None st.session_state.demographic_data = None st.session_state.price_accepted = 0 st.session_state.attention_check1 = None pages = { "welcome": welcome_page, "scenario_explanation": scenario_explanation, "map_view": map_view, "attention_check1": attention_check1, "random_likert_questions": random_likert_questions, "explanation_questions": explanation_questions, "demographic": demographic_form, # دموگرافیک قبل از کانتکت "contact": user_contact, # کانتکت در انتها "thank_you": thank_you_page } pages[st.session_state.current_page]() if __name__ == "__main__": main()