study2 / app.py
Maryam Ilka
Update app.py
c64bdb0 verified
raw
history blame
36.1 kB
import streamlit as st
import folium
from streamlit_folium import folium_static
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("""
<style>
@font-face {
font-family: 'Vazir';
src: url('https://cdn.jsdelivr.net/gh/rastikerdar/vazir-font@v30.1.0/dist/Vazir.woff') format('woff');
}
:root {
--primary: #6a0dad;
--text: #333333;
--background: #ffffff;
--border: #dddddd;
--input-bg: #ffffff;
--secondary-bg: #f8f9fa;
--green: #f0ff0;
--dgreen: #006400
}
* {
font-family: 'Vazir', sans-serif !important;
text-align: right !important;
direction: rtl !important;
}
/* تنظیمات اصلی */
body, .stApp, [data-testid="stAppViewContainer"] {
background-color: var(--background) !important;
color: var(--text) !important;
}
/* هدر راهیار */
.rahyar-title {
color: var(--primary) !important;
margin: 0 !important;
}
.rahyar-subtitle {
color: var(--primary) !important;
margin: 0 !important;
font-size: 14px !important;
}
/* توضیحات */
.explanation-title {
color: var(--primary) !important;
font-weight: bold !important;
margin: 20px 0 10px 0 !important;
font-size: 18px !important;
}
.explanation-item {
background-color: var(--secondary-bg) !important;
border-radius: 8px !important;
padding: 12px 15px !important;
margin: 8px 0 !important;
border-right: 3px solid var(--primary) !important;
}
/* ========== استایل‌های ورودی یکپارچه ========== */
/* استایل پایه برای تمام عناصر ورودی */
.stTextInput input,
.stNumberInput input,
.stSelectbox select,
.stTextArea textarea,
.stDateInput input,
.stTimeInput input,
.stMultiSelect div[role="combobox"],
div[data-testid="stVerticalBlock"] > div[data-testid="stHorizontalBlock"] > div > div > div > div > input,
/* لیست dropdown */
.st-bq, .st-br, .st-bs, .st-bt, .st-bu, .st-bv, .st-bw, .st-bx, .st-by, .st-bz {
color: var(--text) !important;
background-color: var(--input-bg) !important;
border: 1px var(--primary) !important;
}
/* استایل لیبل‌ها */
.stTextInput > label,
.stNumberInput > label,
.stSelectbox > label,
.stRadio > label,
.stSlider > label {
color: var(--text) !important;
font-weight: bold !important;
margin-bottom: 4px !important;
}
/* استایل placeholder */
::placeholder {
color: var(--text) !important;
opacity: 0.7 !important;
}
/* ========== استایل‌های خاص Selectbox ========== */
/* حذف کامل فلش پیشفرض در تمام مرورگرها */
div[data-baseweb="select"] > div:first-child {
-webkit-appearance: none !important;
-moz-appearance: none !important;
appearance: none !important;
background-image: none !important;
}
/* برای اینترنت اکسپلورر */
div[data-baseweb="select"] > div:first-child::-ms-expand {
display: none !important;
}
/* ایجاد فلش سفارشی */
div[data-baseweb="select"] {
position: relative;
}
div[data-baseweb="select"]::after {
content: "▼";
position: absolute;
left: 8px;
top: 50%;
transform: translateY(-50%);
color: var(--primary);
background-color: white !important; /* پسزمینه سفید برای پوشاندن فلش پیشفرض */
font-size: 14px;
pointer-events: none;
}
/* ===== راه حل تضمینی برای استایل dropdown ===== */
/* آیتم‌های لیست */
div[data-baseweb="popover"] [role="option"] {
color: var(--text) !important;
background-color: white !important;
border: 1px var(--primary) !important;
}
/* آیتم انتخاب شده */
div[data-baseweb="popover"] [role="option"][aria-selected="true"] {
background-color: #f0e6ff !important;
color: black !important;
}
/* ========== استایل‌های Number Input ========== */
.stNumberInput button {
color: var(--primary) !important;
background-color: transparent !important;
border: none !important;
width: 30px !important;
font-weight: bold !important;
}
.stNumberInput button:hover {
background-color: #f0e6ff !important;
}
/* کامپوننت‌های سفارشی */
.rahyar-header {
background-color: var(--primary) !important;
color: white !important;
padding: 15px !important;
border-radius: 10px !important;
margin-bottom: 20px !important;
text-align: center !important;
}
.price-container {
background-color: var(--secondary-bg) !important;
border-radius: 10px !important;
padding: 15px !important;
margin: 15px 0 !important;
border-right: 5px solid var(--primary) !important;
}
/* دکمه اصلی (بنفش با متن سفید) */
.stButton>button,
[data-testid="baseButton-primary"],
.accept-btn,
div[data-testid="stVerticalBlock"] > div[data-testid="stHorizontalBlock"] > div > div > button {
background-color: var(--primary) !important;
color: white !important;
border: none !important;
border-radius: 8px !important;
padding: 10px 20px !important;
font-weight: bold !important;
}
/* دکمه ثانویه (سفید با حاشیه بنفش) */
.stFormSubmitButton>button,
[data-testid="baseButton-secondary"],
.reject-btn {
background-color: var(--primary) !important;
color: var(--input-bg) !important;
border: 1px solid var(--primary) !important;
border-radius: 8px !important;
padding: 10px 20px !important;
font-weight: bold !important;
}
/* ========== استایل تضمینی برای گزینه‌های رادیویی ========== */
/* تمام سطوح لیبل‌ها */
.stRadio > label,
.stRadio > div > label,
.stRadio > div > div > label,
.stRadio > div > div > div > label {
color: #000000 !important; /* مشکی خالص */
font-weight: normal !important;
margin-right: 8px !important; /* فاصله از دکمه رادیویی */
}
/* دایره رادیویی */
.stRadio input[type="radio"] + span {
border-color: #000000 !important; /* حاشیه مشکی */
}
/* دایره رادیویی هنگام انتخاب */
.stRadio input[type="radio"]:checked + span {
background-color: #000000 !important; /* پس‌زمینه مشکی */
border-color: #000000 !important;
}
/* متن گزینه‌ها */
.stRadio span {
color: #000000 !important; /* مشکی خالص */
font-size: 14px !important;
padding-right: 5px !important;
}
/* حالت hover */
.stRadio label:hover span {
color: #333333 !important; /* مشکی کمی روشن‌تر */
}
/* کانتینر اصلی */
.stRadio > div {
margin-bottom: 10px !important;
}
/* تنظیمات مخصوص موبایل */
@media (max-width: 768px) {
.folium-map {
height: 300px !important;
}
/* فرم‌ها در موبایل */
.stTextInput>label,
.stNumberInput>label,
.stSelectbox>label {
font-size: 14px !important;
padding: 4px 0 !important;
}
.stTextInput input,
.stNumberInput input,
.stSelectbox select {
font-size: 16px !important;
padding: 12px !important;
height: auto !important;
}
.stButton>button {
font-size: 14px !important;
padding: 8px 16px !important;
}
.stMarkdown h3 {
font-size: 16px !important;
}
.stMarkdown p {
font-size: 13px !important;
}
.stSelectbox [role="listbox"] {
font-size: 16px !important;
}
}
</style>
""", unsafe_allow_html=True)
# ========== توابع اصلی ==========
def create_ride_map():
"""ایجاد نقشه سفر با Folium"""
start_point = [35.7698, 51.4116] # میدان ونک
end_point = [35.8044, 51.4258] # میدان تجریش
m = folium.Map(location=[35.7871, 51.4187], zoom_start=13)
folium.Marker(
start_point,
popup="<b>مبدأ:</b> میدان ونک",
icon=folium.Icon(color="green", icon="flag", prefix="fa")
).add_to(m)
folium.Marker(
end_point,
popup="<b>مقصد:</b> میدان تجریش",
icon=folium.Icon(color="red", icon="flag", prefix="fa")
).add_to(m)
folium.PolyLine(
[start_point, end_point],
color="#6a0dad",
weight=3,
opacity=1
).add_to(m)
return m
def show_explanation(exp_type):
"""نمایش توضیحات قیمت"""
explanations = {
"input": [
"سطح تقاضا در منطقه: زیاد",
"تعداد رانندگان فعال: کم",
"زمان روز: ساعت اوج ترافیک",
"شرایط جوی: هوای بارانی"
],
"counterfactual": [
"اگر این سفر را ۳۰ دقیقه دیرتر درخواست می‌کردید، به دلیل سطح تقاضای کمتر، رانندگان فعال بیش‌تر، زمان بهتر روز و شرایط جوی بهتر، ممکن بود قیمت ۱۵٪ کمتر باشد.",
]
}
if exp_type != "control":
st.markdown("<p class='explanation-title'>علت قیمت گذاری:</p>", unsafe_allow_html=True)
for item in explanations.get(exp_type, []):
st.markdown(f"<p class='explanation-item'>• {item}</p>", unsafe_allow_html=True)
def create_likert_question(question, key, scale_type="5point"):
"""سوال لیکرت با دکمه‌ها"""
if scale_type == "5point":
options = {
1: "کاملاً مخالفم",
2: "مخالفم",
3: "نظری ندارم",
4: "موافقم",
5: "کاملاً موافقم"
}
else: # 7-point scale
options = {
1: "کاملاً مخالفم",
2: "مخالفم",
3: "تا حدی مخالفم",
4: "نظری ندارم",
5: "تا حدی موافقم",
6: "موافقم",
7: "کاملاً موافقم"
}
st.markdown(f"<p style='margin-bottom: 10px;'>{question}</p>", unsafe_allow_html=True)
cols = st.columns(len(options))
selected = st.session_state.get(key, None)
# نمایش گزینه‌ها از کاملاً موافقم (راست) تا کاملاً مخالفم (چپ)
for value, label in reversed(options.items()):
with cols[len(options) - value]:
if st.button(
label,
key=f"{key}_{value}",
on_click=lambda v=value: st.session_state.update({key: v}),
type="primary" if selected == value else "secondary"
):
pass
if selected:
st.markdown(f"<p style='color: #6a0dad;'>پاسخ شما: {options[selected]}</p>", unsafe_allow_html=True)
return selected
# ========== توابع مدیریت داده‌ها ==========
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("scenario_type", ""),
data.get("price", ""),
data.get("age", ""),
data.get("gender", ""),
data.get("education", ""),
data.get("ride_frequency", ""),
data.get("user_contact", ""),
data.get("price_accepted", ""),
# سوالات توجه
data.get("attention_check1", ""),
data.get("attention_check2", ""),
# سوالات جدید
data.get("pricing_method", ""),
data.get("price_increase", ""),
# سوالات informational (5 گزینه‌ای)
data.get("informational_1", ""),
data.get("informational_2", ""),
data.get("informational_3", ""),
data.get("informational_4", ""),
data.get("informational_5", ""),
# سوالات 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", ""),
# سوالات explanation
data.get("explanation_received", ""),
data.get("explanation_type", ""),
# زمان‌سنجی
data.get("end_time", ""), # زمان پایان
data.get("completion_time", "") # مدت زمان تکمیل (ثانیه)
]
worksheet.append_row(row_data)
return True
except Exception as e:
st.error(f"خطا در ذخیره‌سازی: {str(e)}")
return False
# ========== بخش‌های فرم ==========
def welcome_page():
"""صفحه خوشامدگویی"""
st.markdown("""
<div style="text-align: center; margin-bottom: 30px;">
<h2> 🚖 قیمت‌گذاری در پلتفرم‌های درخواست تاکسی اینترنتی</h2>
<p>👋با سلام و درود</p>
<p>پیشاپیش بابت زمانی که برای پاسخ به سوالات این پرسشنامه و پیش‌برد اهداف علمی دانشجویان می‌گذارید، متشکرم.</p>
<p>جهت تقدیر از شرکت‌کنندگان، به دو نفر به قید قرعه جایزه 5 میلیون ریالی تقدیم خواهد شد.</p>
</div>
<div style="display: flex; align-items: center; background-color: #f0f2f6; border-radius: 10px; padding: 20px; gap: 20px;">
<div style="flex: 0 0 100px;">
<img src="https://huggingface.co/spaces/maryamilka/surge-pricing/resolve/main/shariflogo.png" alt="لوگو دانشگاه شریف" style="width: 100%; max-width: 100px;">
</div>
<div style="flex: 1;">
<h3>درباره تحقیق:</h3>
<p>این پرسشنامه بخشی از یک پایان‌نامه کارشناسی ارشد در دانشگاه صنعتی شریف است که به بررسی ادراک انصاف در قیمت‌گذاری در پلتفرم‌های درخواست تاکسی اینترنتی می‌پردازد.</p>
<p>پاسخ‌های شما به سوالات کاملاً محرمانه خواهد بود و فقط در جهت اهداف علمی استفاده خواهد شذ.</p>
<p>جهت شروع روی دکمه زیر کلیک کنید 👇🏻</p>
</div>
</div>
""", unsafe_allow_html=True)
if st.button("شروع پرسشنامه", key="start_btn", type="primary"):
st.session_state.current_page = "contact"
st.session_state.start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
st.rerun()
def user_contact():
"""راه ارتباطی ساده"""
st.markdown("""
<div style="text-align: center; margin-bottom: 30px;">
<h3>📩 راه ارتباطی شما (اختیاری)</h3>
<p>در صورت تمایل به شرکت در قرعه‌کشی می‌توانید آیدی تلگرام، شماره تماس یا ایمیل خود را وارد کنید:</p>
</div>
""", unsafe_allow_html=True)
contact_info = st.text_input(
"راه ارتباطی (اختیاری)",
placeholder="مثال: @username یا 09123456789 یا example@email.com",
key="user_contact_input"
)
if st.button("ادامه", key="continue_btn", type="primary"):
st.session_state.user_contact = contact_info
st.session_state.current_page = "demographic"
st.rerun()
def demographic_form():
"""فرم اطلاعات دموگرافیک"""
st.markdown("### 📝 اطلاعات دموگرافیک")
with st.form("demographic_form"):
age = st.number_input("سن", min_value=18, max_value=100)
gender = st.selectbox("جنسیت", ["مرد", "زن", "سایر"])
education = st.selectbox("تحصیلات", ["دیپلم", "لیسانس", "فوق لیسانس", "دکترا"])
ride_frequency = st.selectbox("دفعات استفاده از سرویس‌های اشتراک سفر در ماه",
["کمتر از 5 بار", "5-10 بار", "بیش از 10 بار"])
related_education_job = st.text_input(
"اگر تحصیلات یا شغل مرتبط با مدیریت/بازاریابی دارید، لطفاً مشخص کنید (اختیاری):",
placeholder="مثال: مدیریت بازرگانی، بازاریابی دیجیتال، MBA و...",
key="related_education_job"
)
if st.form_submit_button("ادامه"):
st.session_state.demographic_data = {
"age": age,
"gender": gender,
"education": education,
"ride_frequency": ride_frequency
}
st.session_state.current_page = "scenario_explanation"
st.rerun()
def scenario_explanation():
"""توضیح سناریو"""
col1, col2 = st.columns([1, 4])
with col1:
try:
st.image("rahyar.png", width=80)
except:
st.image("https://via.placeholder.com/80/6a0dad/FFFFFF?text=LOGO", width=80)
with col2:
st.markdown("""
<h2 class="rahyar-title">رهیار 🚖</h2>
<p class="rahyar-subtitle">همراه سفرهای درون‌شهری شما، راهی مطمئن، راهی روشن، رهیار</p>
""", unsafe_allow_html=True)
st.markdown("### سناریوی تحقیق")
st.markdown("""
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 10px;">
<p>فرض کنید یک اپلیکیشن حمل‌ونقل آنلاین ایرانی به اسم رهیار طراحی شده، چیزی شبیه اسنپ یا تپسی، اما جدیدتر و با شعار "همراه سفرهای شما، راهی مطمئن، راهی روشن، رهیار"</p>
<p>در یک روز عادی، شما قصد دارید برای سفری از طریق این پلتفرم اقدام کنید..</p>
<p>با کلیک بر دکمه ادامه، اطلاعات سفر را مشاهده کنید.</p>
</div>
""", 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([1, 4])
with col1:
st.image("https://via.placeholder.com/80/6a0dad/FFFFFF?text=LOGO", width=80)
with col2:
st.markdown("""
<h2 style="color: #6a0dad; margin: 0;">رهیار 🚖</h2>
<p style="color: #6a0dad; margin: 0;">همراه سفرهای درون‌شهری شما، راهی مطمئن، راهی روشن، رهیار</p>
""", 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"""
<div class="price-container">
<div style="display: flex; justify-content: space-between; align-items: center;">
<span>راهیار <span style="background-color: #e6e6fa; color: #6a0dad; padding: 2px 8px; border-radius: 12px; font-size: 14px;">به صرفه</span></span>
<span class="rahyar-price">{st.session_state.price:,} تومان</span>
</div>
</div>
""", 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():
"""سوال توجه اول (بدون بررسی پاسخ صحیح)"""
st.markdown("""
<style>
/* تضمین رنگ متن برای تمام سطوح */
.st-ec, .st-ed, .st-ee, .st-ef, .st-eg, .st-eh, .st-ei, .st-ej {
color: black !important;
}
</style>
""", unsafe_allow_html=True)
st.markdown("### سوال توجه")
# استفاده از st.radio با key منحصر به فرد
answer = st.radio(
"رنگ سازمانی اپلیکیشن رهیار چه رنگی بود؟",
["قرمز", "سبز", "بنفش", "آبی", "زرد"],
index=None,
key="att1_radio"
)
if st.button("ادامه", key="att1_btn"):
if answer:
st.session_state.attention_check1 = answer # ذخیره پاسخ در session_state
st.session_state.current_page = "random_likert_questions"
st.rerun()
else:
st.warning("لطفاً یک گزینه را انتخاب کنید")
def attention_check2():
"""سوال توجه دوم (بدون بررسی پاسخ صحیح)"""
st.markdown("""
<style>
/* تضمین رنگ متن برای تمام سطوح */
.st-ec, .st-ed, .st-ee, .st-ef, .st-eg, .st-eh, .st-ei, .st-ej {
color: black !important;
}
</style>
""", unsafe_allow_html=True)
st.markdown("### سوال توجه")
answer = st.radio(
"نام اپلیکیشنی که در این تحقیق بررسی می‌شود چیست؟",
["اسنپ", "تپسی", "راهیار", "ماکسیم", "دیگر"],
index=None,
key="att2_radio"
)
if st.button("ادامه", key="att2_btn"):
if answer:
st.session_state.attention_check2 = answer
st.session_state.current_page = "explanation_questions"
st.rerun()
else:
st.warning("لطفاً یک گزینه را انتخاب کنید")
def random_likert_questions():
"""نمایش تصادفی سوالات لیکرت"""
if 'all_questions' not in st.session_state:
# تعریف تمام سوالات با نوع مقیاس و لیبل مربوطه
st.session_state.all_questions = [
# سوالات informational (5 گزینه‌ای)
{"key": "informational_1", "question": "پلتفرم به صورت صادقانه دلایل تغییر قیمت (مثل افزایش تقاضا یا شرایط جوی) را توضیح داد.", "scale": "5point", "label": "informational_1"},
{"key": "informational_2", "question": "پلتفرم به طور کامل عوامل مؤثر بر قیمت (مثل ترافیک، تعداد رانندگان) را شرح داد.", "scale": "5point", "label": "informational_2"},
{"key": "informational_3", "question": "دلایل ارائه‌شده برای تغییر قیمت منطقی و قابل قبول بود.", "scale": "5point", "label": "informational_3"},
{"key": "informational_4", "question": "توضیحات درباره قیمت بلافاصله و در زمان مناسب نمایش داده شد.", "scale": "5point", "label": "informational_4"},
{"key": "informational_5", "question": "توضیحات پلتفرم متناسب با شرایط سفر من (مثل مسیر یا ساعت درخواست) بود.", "scale": "5point", "label": "informational_5"},
# سوالات distributive (7 گزینه‌ای)
{"key": "distributive_1", "question": "قیمتی که به شما ارائه شد، منصفانه است.", "scale": "7point", "label": "distributive_1"},
{"key": "distributive_2", "question": "قیمتی که به شما ارائه شد، معقول است.", "scale": "7point", "label": "distributive_2"},
{"key": "distributive_3", "question": "قیمتی که به شما ارائه شد، قابل قبول است.", "scale": "7point", "label": "distributive_3"},
# سوالات procedural (7 گزینه‌ای)
{"key": "procedural_1", "question": "فرآیند و رویه قیمت‌گذاری پلتفرم قابل قبول است.", "scale": "7point", "label": "procedural_1"},
{"key": "procedural_2", "question": "فرآیند و رویه قیمت‌گذاری پلتفرم منصفانه است.", "scale": "7point", "label": "procedural_2"},
{"key": "procedural_3", "question": "فرآیند و رویه قیمت‌گذاری پلتفرم معقول است.", "scale": "7point", "label": "procedural_3"}
]
# تصادفی‌سازی ترتیب سوالات
random.shuffle(st.session_state.all_questions)
st.session_state.current_question_index = 0
if st.session_state.current_question_index < len(st.session_state.all_questions):
q = st.session_state.all_questions[st.session_state.current_question_index]
# نمایش سوال بدون شماره
answer = create_likert_question(q["question"], q["key"], q["scale"])
if answer:
st.session_state.current_question_index += 1
time.sleep(0.5)
st.rerun()
else:
st.session_state.current_page = "attention_check2"
st.rerun()
def explanation_questions():
"""سوالات درباره توضیحات قیمت"""
st.markdown("""
<style>
/* تضمین رنگ متن برای تمام سطوح */
.st-ec, .st-ed, .st-ee, .st-ef, .st-eg, .st-eh, .st-ei, .st-ej {
color: black !important;
}
</style>
""", unsafe_allow_html=True)
st.markdown("### 📋 سوالات تکمیلی")
# مقداردهی اولیه متغیرهای session_state اگر وجود ندارند
if 'pricing_method' not in st.session_state:
st.session_state.pricing_method = None
if 'price_increase' not in st.session_state:
st.session_state.price_increase = None
if 'explanation_received' not in st.session_state:
st.session_state.explanation_received = None
if 'explanation_type' not in st.session_state:
st.session_state.explanation_type = None
# سوال جدید 1: روش قیمت‌گذاری
pricing_method = st.radio(
"به نظر شما پلتفرم قیمت را چگونه تعیین می‌کند؟",
[
"به صورت دستی توسط تیم پلتفرم",
"به صورت خودکار توسط هوش مصنوعی و الگوریتم‌ها",
"ترکیبی از هر دو روش",
"نظری ندارم"
],
index=None,
key="pricing_method_radio"
)
# سوال جدید 2: افزایش قیمت
price_increase = None
if pricing_method is not None: # فقط اگر به سوال اول پاسخ داده شده باشد
price_increase = st.radio(
"آیا به نظر شما در این سفر افزایش قیمت نسبت به حالت طبیعی وجود داشته است؟",
["بله", "خیر", "مطمئن نیستم"],
index=None,
key="price_increase_radio"
)
# سوالات قبلی
explanation_received = None
explanation_type = None
if price_increase is not None: # فقط اگر به سوال دوم پاسخ داده شده باشد
explanation_received = st.radio(
"آیا برای قیمت پیشنهادی این سفر، توضیحی به شما ارائه شد؟",
["بله", "خیر"],
index=None,
key="explanation_received_radio"
)
# سوال دوم (فقط اگر پاسخ بله باشد)
if explanation_received == "بله":
explanation_type = st.radio(
"اگر توضیحی دریافت کردید، این توضیح بیشتر به کدام مورد شباهت داشت؟",
[
"بر اساس عواملی که در قیمت‌گذاری لحاظ شده‌اند",
"شامل سناریوهای جایگزین که می‌توانستند قیمت متفاوتی ایجاد کنند",
"توضیحی دریافت نکردم"
],
index=None,
key="explanation_type_radio"
)
if st.button("ثبت پاسخ‌ها", type="primary", key="submit_explanation"):
# استفاده از مقادیر مستقیماً از session_state
st.session_state.pricing_method = st.session_state.get("pricing_method_radio")
st.session_state.price_increase = st.session_state.get("price_increase_radio")
st.session_state.explanation_received = st.session_state.get("explanation_received_radio")
st.session_state.explanation_type = st.session_state.get("explanation_type_radio")
# بررسی کامل بودن پاسخ‌ها
if st.session_state.pricing_method is None:
st.warning("لطفاً به سوال روش قیمت‌گذاری پاسخ دهید")
elif st.session_state.price_increase is None:
st.warning("لطفاً به سوال افزایش قیمت پاسخ دهید")
elif st.session_state.explanation_received is None:
st.warning("لطفاً به سوال دریافت توضیح پاسخ دهید")
elif st.session_state.explanation_received == "بله" and st.session_state.explanation_type is None:
st.warning("لطفاً به سوال نوع توضیح پاسخ دهید")
else:
# جمع‌آوری تمام داده‌ها برای ذخیره‌سازی
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.attention_check1,
"attention_check2": st.session_state.attention_check2,
"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.explanation_type if st.session_state.explanation_received == "بله" else "N/A",
**st.session_state.demographic_data
}
# اضافه کردن پاسخ‌های لیکرت
for q in st.session_state.all_questions:
save_data[q["label"]] = st.session_state.get(q["key"], None)
if save_to_sheet(save_data):
st.session_state.current_page = "thank_you"
st.rerun()
else:
st.error("خطا در ذخیره‌سازی داده‌ها. لطفاً دوباره تلاش کنید.")
def thank_you_page():
"""صفحه تشکر"""
st.success("✅ پاسخ‌های شما با موفقیت ثبت شد. با تشکر از مشارکت شما در این تحقیق!")
st.balloons()
if st.button("بازگشت به ابتدا"):
st.session_state.clear()
st.rerun()
# ========== مدیریت وضعیت و صفحه‌بندی ==========
def main():
# تشخیص دستگاه
user_agent = st.query_params.get("user_agent", [""])[0]
st.session_state.is_desktop = "mobile" not in user_agent.lower()
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
pages = {
"welcome": welcome_page,
"contact": user_contact,
"demographic": demographic_form,
"scenario_explanation": scenario_explanation,
"map_view": map_view,
"attention_check1": attention_check1,
"random_likert_questions": random_likert_questions,
"attention_check2": attention_check2,
"explanation_questions": explanation_questions,
"thank_you": thank_you_page
}
pages[st.session_state.current_page]()
if __name__ == "__main__":
main()