import streamlit as st import folium from streamlit_folium import folium_static import streamlit.components.v1 as components from datetime import datetime import textwrap import gspread from google.oauth2.service_account import Credentials import os import json import random import time import requests # تنظیمات اولیهه st.set_page_config(layout="wide", page_title="راهیار - تحلیل انصاف قیمتی", page_icon="🚖") # ========== تنظیمات دیتا ========== SHEET_ID = "1gpv_Li_p8SqiG-wBLeb0Mvs0qXJXKOnC_UuCGgs0RJg" SHEET_NAME = "Sheet1" # ========== استایل‌های سفارشی یکپارچه ========== st.markdown(""" """, unsafe_allow_html=True) # ========== توابع اصلی ========== def enhanced_likert_scale(question_data): question = question_data["question"] key = question_data["key"] scale = question_data["scale"] labels = question_data.get("labels", ["کاملاً مخالفم", "کاملاً موافقم"]) # نمایش سوال st.markdown(f"
{question}
", unsafe_allow_html=True) # رادیو باتن اصلی (مخفی) selected_value = st.radio( "", options=list(range(1, scale+1)), index=st.session_state.get(key, 0) - 1 if st.session_state.get(key, 0) > 0 else None, label_visibility="collapsed", horizontal=True, key=f"{key}_radio" ) # مخفی کردن رادیو باتن st.markdown( """ """, unsafe_allow_html=True ) # ذخیره مقدار انتخاب شده if selected_value is not None: st.session_state[key] = selected_value # ایجاد JavaScript js_code = """ """ # ایجاد دکمه‌های سفارشی options_html = "".join([ f'
' f'{i+1}' '
' for i in range(scale) ]) # تنظیم لیبل وسط برای مقیاس‌های فرد middle_label = "" middle_label_text = "متوسط" if scale % 2 == 1: # اگر مقیاس فرد باشد middle_label = f"""
{middle_label_text}
""" # ترکیب تمام بخش‌ها با استایل بهبود یافته components.html( f""" {js_code}
{labels[0]}
{middle_label}
{labels[1]}
{options_html}
""", height=120 # ارتفاع برای جا دادن لیبل‌ها و خطوط ) # نمایش وضعیت انتخاب status = f"پاسخ شما: {st.session_state[key]}" if st.session_state.get(key) else "پاسخ شما: هنوز انتخاب نشده" st.markdown( f"""

{status}

""", unsafe_allow_html=True ) return st.session_state.get(key) def create_ride_map(): origin = [35.7665280, 51.3300394] # پونک شمالی destination = [35.7552343, 51.4204264] # پل طبیعت purple = "#6a0dad" # ساخت نقشه m = folium.Map( location=[(origin[0] + destination[0]) / 2, (origin[1] + destination[1]) / 2], zoom_start=12 # 👈 عدد زوم اینجاست ) # آیکون مبدأ origin_icon = folium.CustomIcon( icon_image='origin.png', icon_size=(80, 48) ) folium.Marker( location=origin, icon=origin_icon, tooltip="مبدأ: پونک شمالی" ).add_to(m) # آیکون مقصد destination_icon = folium.CustomIcon( icon_image='destination.png', icon_size=(80, 46) ) folium.Marker( location=destination, icon=destination_icon, tooltip="مقصد: پل طبیعت" ).add_to(m) # خط مسیر بنفش folium.PolyLine( [origin, destination], color=purple, weight=4, opacity=0.8, dash_array="5,5" ).add_to(m) return m def show_explanation(exp_type): """نمایش توضیحات قیمت""" explanations = { "input": [ " سطح تقاضا در منطقه: زیاد (+)", " تعداد رانندگان فعال: کم (+)", " زمان روز: ساعت اوج ترافیک (+)", "شرایط جوی: هوای بارانی (++)" ], "counterfactual": [ f"اگر عجله ندارید و درخواست خود را 1 ساعت بعد تکرار کنید، احتمالاً تقاضا کمتر خواهد بود، رانندگان فعال در اطراف شما بیش‌تر خواهد بود، زمان روز و شرایط جوی بهتر خواهد بود؛ پس قیمت حدوداً 40٪ کمتر ({int(st.session_state.price * 0.6):,} تومان) خواهد بود.", ] } if exp_type in ["input", "counterfactual"]: st.markdown("

توضیح رهیار درمورد علت قیمت گذاری:

", unsafe_allow_html=True) if exp_type == "input": 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("increase_amount", ""), 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) st.markdown("""

فرض کنید در روزی از روزها شما قصد دارید از محل زندگی‌تان در پونک شمالی به پاتوق همیشگی‌تان در اطراف پل طبیعت بروید.

گوشی‌تان را از کیف درمی‌آورید. اپلیکیشنی که معمولاً برای حمل و نقل استفاده می‌کنید را باز می‌کنید؛ اپلیکیشنی به نام رهیار — نه اسنپ است و نه تپسی، اما خیلی شبیه آن‌ها و رقیب جدید آن‌هاست. رنگ بنفش جذابی هم دارد. تصمیم می‌گیرید این بار هم برای این سفر از این اپلیکیشن استفاده کنید.

شما همیشه و البته این بار نیز از حالت سفر معمولی رهیار استفاده می‌کنید.

از آن‌جایی که این مسیر را معمولاً با این اپلیکیشن طی می‌کنید، متوجه شده‌اید که در شرایط معمول که ترافیک شدیدی وجود ندارد، زمان شلوغی در روز نیست، هوا مناسب است، رویداد خاصی نیست و.. قیمت این مسیر حدود 70 هزار تومان است. اما وقتی شرایط متفاوت است، رهیار با کمک هوش مصنوعی و تشخیص شرایط و شدت آن‌ها، قیمت‌ها را افزایش می‌دهد تا رانندگان انگیزه بیشتری برای قبول سفر داشته باشند و عرضه و تقاضا تعدیل شود.

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

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

""", 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("""

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

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

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

""", 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", "guide": textwrap.dedent("""

راهنمای پاسخ به سری اول:

در این بخش، با یک سری سؤال درمورد قیمتی که در صفحه اطلاعات سفر و در زیر نقشه دیدید، مواجه خواهید شد. در زیر سوالات طیفی قرار دارد:
- سمت راست (۱): کاملاً نامنصفانه، غیرمعقول یا غیرقابل قبول
- سمت چپ (۷): کاملاً منصفانه، معقول یا قابل قبول
لطفاً با دقت عدد مناسب را بین ۱ تا ۷ را انتخاب نمایید. بدین گونه شما انتخاب خواهید کرد که چقدر قیمت به نظرتان منصفانه بوده. چقدر با توجه به شرایط منطقی بوده و چقدر قابل قبول بوده.

"""), "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": "لطفاً خیلی زیاد (عدد 7) را انتخاب کنید.", "scale": 7, "labels": ["خیلی کم", "خیلی زیاد"]} ] }, { "title": "سری دوم سؤالات", "key": "procedural", "guide": textwrap.dedent("""

راهنمای پاسخ به سری دوم:

در این بخش با یک سری جمله خبری درمورد فرآیند و رویه قیمت‌گذاری رهیار مواجه خواهید شد. در زیر جملات یک طیف قرار دارد:
- سمت راست (۱): کاملاً مخالفم
- سمت چپ (۷): کاملاً موافقم
لطفاً نظر خود را با انتخاب عدد مناسب بیان کنید.

"""), "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", "guide": textwrap.dedent("""

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

در این بخش، با یک سری سؤال درمورد توضیحاتی که در صفحه اطلاعات سفر و در زیر نقشه درمورد قیمت به شما ارائه شد، مواجه خواهید شد. در زیر سوالات طیفی قرار دارد:
- سمت راست (۱): به هیچ وجه
- سمت چپ (۷): خیلی زیاد
لطفاً با دقت عدد مناسب را بین ۱ تا ۷ را انتخاب نمایید. بدین گونه شما انتخاب خواهید کرد که از هیچ مقدار تا خیلی زیاد به چه مقدار به شما توضیح با ویژگی‌های سوال ارائه شده است.

"""), "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 st.session_state.current_question_index = 0 st.session_state.show_guide = True current_group = question_groups[st.session_state.current_likert_group] current_question = current_group['questions'][st.session_state.current_question_index] # نمایش راهنما فقط برای اولین سوال هر گروه if st.session_state.show_guide and 'guide' in current_group: st.markdown(f"## {current_group['title']}") guide_html = textwrap.dedent("""
{}
""").format(current_group['guide']) st.markdown(guide_html, unsafe_allow_html=True) else: st.markdown(f"## {current_group['title']}") # نمایش سوال جاری answer = enhanced_likert_scale(current_question) # اگر پاسخ داده شد، به سوال بعدی برو if answer is not None: st.session_state.answers[current_question["key"]] = answer st.session_state.show_guide = False # تاخیر برای نمایش پاسخ قبل از رفتن به سوال بعدی time.sleep(0.5) # بررسی آیا سوالات این گروه تمام شده یا نه if st.session_state.current_question_index < len(current_group['questions']) - 1: st.session_state.current_question_index += 1 else: # رفتن به گروه بعدی st.session_state.current_likert_group += 1 st.session_state.current_question_index = 0 st.session_state.show_guide = True # اگر همه گروه‌ها تمام شدند، به صفحه بعدی برو if st.session_state.current_likert_group >= len(question_groups): st.session_state.current_page = "explanation_questions" st.rerun() def explanation_questions(): """نمایش سوالات تکمیلی به صورت مرحله‌ای با دکمه ادامه""" st.markdown("### 📋 سؤالات تکمیلی") # لیست سوالات به ترتیب نمایش questions = [ { "key": "price_increase", "label": "در سناریوی کیس رهیار، آیا به نظر شما قیمت ارائه‌شده برای مسیر موردنظر، در مقایسه با قیمت‌ در شرایط معمولی روز (ترافیک عادی، تایم غیر شلوغی، عرضه و تقاضا مناسب، روز غیرخاص و..)، افزایش داشته است یا خیر؟ ", "options": ["بله", "خیر", "مطمئن نیستم"], "required": True }, { "key": "increase_amount", "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 current_q["key"] == "explanation_received": with st.expander("نمونه توضیحات قیمت‌گذاری", expanded=False): # ایجاد یک کانتینر برای وسط چین کردن col1, col2, col3 = st.columns([1, 6, 1]) with col2: try: st.image("control.png", width=400, # اندازه بزرگتر caption="نمونه بدون توضیح", use_container_width=True) # پارامتر جدید جایگزین st.image("input.png", width=400, caption="نمونه توضیح", use_container_width=True) st.image("counterfactual.png", width=400, caption="نمونه توضیح", use_container_width=True) except Exception as e: st.warning(f"تصاویر نمونه یافت نشدند. خطا: {str(e)}") # دکمه ادامه 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("""

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

جهت قدردانی از شما بابت زمانی که به پر کردن این پرسشنامه اختصاص دادید، به دو نفر از عزیزان به قید قرعه جایزه نقدی 5 میلیون ریالی تقدیم خواهد شد.

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

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

درصورتی که تمایل ندارید این فیلد را پر کنید لطفاً کلیک بر دکمه ثبت پاسخ را فراموش نکنید.

""", 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, "increase_amount": st.session_state.increase_amount, "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" # ایجاد 4 شرط مختلف conditions = [ {"price": 120000, "scenario_type": "input"}, {"price": 120000, "scenario_type": "counterfactual"}, {"price": 200000, "scenario_type": "input"}, {"price": 200000, "scenario_type": "counterfactual"} ] # انتخاب تصادفی یکی از شرایط selected_condition = random.choice(conditions) st.session_state.price = selected_condition["price"] st.session_state.scenario_type = selected_condition["scenario_type"] 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()