Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
import folium
|
| 3 |
from streamlit_folium import folium_static
|
|
|
|
| 4 |
from datetime import datetime
|
| 5 |
import gspread
|
| 6 |
from google.oauth2.service_account import Credentials
|
|
@@ -74,7 +75,7 @@ html, body, .stApp {
|
|
| 74 |
[data-testid="stAppViewContainer"] {
|
| 75 |
width: 475px !important;
|
| 76 |
height: 667px !important;
|
| 77 |
-
margin: 0 auto !important;
|
| 78 |
border: 12px solid #000 !important;
|
| 79 |
border-radius: 40px !important;
|
| 80 |
box-shadow:
|
|
@@ -156,7 +157,6 @@ html, body, .stApp {
|
|
| 156 |
[data-testid="stAppViewContainer"] .stDateInput>div>div>input,
|
| 157 |
[data-testid="stAppViewContainer"] .stTimeInput>div>div>input,
|
| 158 |
[data-testid="stAppViewContainer"] .price-container,
|
| 159 |
-
[data-testid="stAppViewContainer"] .explanation-item,
|
| 160 |
[data-testid="stAppViewContainer"] .stAlert,
|
| 161 |
[data-testid="stAppViewContainer"] .stMarkdown,
|
| 162 |
[data-testid="stAppViewContainer"] .folium-map,
|
|
@@ -196,7 +196,7 @@ html, body, .stApp {
|
|
| 196 |
.rahyar-subtitle {
|
| 197 |
color: var(--primary) !important;
|
| 198 |
margin: 0 !important;
|
| 199 |
-
font-size:
|
| 200 |
}
|
| 201 |
|
| 202 |
/* توضیحات */
|
|
@@ -213,6 +213,7 @@ html, body, .stApp {
|
|
| 213 |
padding: 12px 15px !important;
|
| 214 |
margin: 8px 0 !important;
|
| 215 |
border-right: 3px solid var(--primary) !important;
|
|
|
|
| 216 |
}
|
| 217 |
|
| 218 |
|
|
@@ -308,7 +309,7 @@ div[data-baseweb="textarea"] > div:first-child:focus-within {
|
|
| 308 |
color: var(--text) !important;
|
| 309 |
font-weight: bold !important;
|
| 310 |
margin-bottom: 8px !important;
|
| 311 |
-
font-size:
|
| 312 |
display: block !important;
|
| 313 |
}
|
| 314 |
|
|
@@ -461,7 +462,7 @@ div[data-testid="stVerticalBlock"] > div[data-testid="stHorizontalBlock"] > div
|
|
| 461 |
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2) !important; /* سایه بیشتر */
|
| 462 |
}
|
| 463 |
|
| 464 |
-
|
| 465 |
.stRadio > div > div > div > div > div > label,
|
| 466 |
.stRadio > div > div > div > div > label,
|
| 467 |
.stRadio > div > div > div > label,
|
|
@@ -470,11 +471,13 @@ div[data-testid="stVerticalBlock"] > div[data-testid="stHorizontalBlock"] > div
|
|
| 470 |
.stRadio > label {
|
| 471 |
color: #000000 !important;
|
| 472 |
-webkit-text-fill-color: #000000 !important; /* برای مرورگرهای وبکیت */
|
|
|
|
| 473 |
}
|
| 474 |
|
| 475 |
.stRadio span {
|
| 476 |
color: #000000 !important;
|
| 477 |
-webkit-text-fill-color: #000000 !important;
|
|
|
|
| 478 |
}
|
| 479 |
|
| 480 |
/* اگر باز هم مشکل داشت این را اضافه کنید */
|
|
@@ -486,6 +489,7 @@ div[data-testid="stVerticalBlock"] > div[data-testid="stHorizontalBlock"] > div
|
|
| 486 |
.stRadio > label > div:first-child {
|
| 487 |
color: #000000 !important;
|
| 488 |
-webkit-text-fill-color: #000000 !important;
|
|
|
|
| 489 |
}
|
| 490 |
|
| 491 |
/* استایل پایه برای هشدارها */
|
|
@@ -507,7 +511,7 @@ div[data-testid="stVerticalBlock"] > div[data-testid="stHorizontalBlock"] > div
|
|
| 507 |
.stAlert .st-ag {
|
| 508 |
color: #856404 !important;
|
| 509 |
font-family: 'B Nazanin' !important;
|
| 510 |
-
font-size:
|
| 511 |
margin-right: 8px !important;
|
| 512 |
}
|
| 513 |
|
|
@@ -527,7 +531,7 @@ div[data-testid="stVerticalBlock"] > div[data-testid="stHorizontalBlock"] > div
|
|
| 527 |
@media (max-width: 768px) {
|
| 528 |
.stAlert .st-ae {
|
| 529 |
padding: 12px !important;
|
| 530 |
-
font-size:
|
| 531 |
}
|
| 532 |
}
|
| 533 |
|
|
@@ -535,19 +539,16 @@ div[data-testid="stVerticalBlock"] > div[data-testid="stHorizontalBlock"] > div
|
|
| 535 |
/* متن گزینهها */
|
| 536 |
.stRadio span {
|
| 537 |
color: #000000 !important; /* مشکی خالص */
|
| 538 |
-
font-size:
|
| 539 |
padding-right: 5px !important;
|
| 540 |
}
|
| 541 |
|
| 542 |
/* حالت hover */
|
| 543 |
.stRadio label:hover span {
|
| 544 |
color: #333333 !important; /* مشکی کمی روشنتر */
|
|
|
|
| 545 |
}
|
| 546 |
|
| 547 |
-
/* کانتینر اصلی */
|
| 548 |
-
.stRadio > div {
|
| 549 |
-
margin-bottom: 10px !important;
|
| 550 |
-
}
|
| 551 |
|
| 552 |
|
| 553 |
/* تنظیمات مخصوص موبایل */
|
|
@@ -560,7 +561,7 @@ div[data-testid="stVerticalBlock"] > div[data-testid="stHorizontalBlock"] > div
|
|
| 560 |
.stTextInput>label,
|
| 561 |
.stNumberInput>label,
|
| 562 |
.stSelectbox>label {
|
| 563 |
-
font-size:
|
| 564 |
padding: 4px 0 !important;
|
| 565 |
}
|
| 566 |
|
|
@@ -573,30 +574,45 @@ div[data-testid="stVerticalBlock"] > div[data-testid="stHorizontalBlock"] > div
|
|
| 573 |
}
|
| 574 |
|
| 575 |
.stButton>button {
|
| 576 |
-
font-size:
|
| 577 |
padding: 8px 16px !important;
|
| 578 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 579 |
|
| 580 |
.stMarkdown h3 {
|
| 581 |
-
font-size:
|
| 582 |
}
|
| 583 |
|
| 584 |
.stMarkdown p {
|
| 585 |
-
font-size:
|
| 586 |
}
|
| 587 |
|
| 588 |
.stSelectbox [role="listbox"] {
|
| 589 |
-
font-size:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 590 |
}
|
| 591 |
}
|
| 592 |
|
| 593 |
/* ======== تنظیمات پایه واکنشگرا ======== */
|
| 594 |
:root {
|
| 595 |
/* اندازههای پایه بر اساس عرض 375px (آیفون X) */
|
| 596 |
-
--base-font-size:
|
| 597 |
--base-heading1-size: 24px;
|
| 598 |
-
--base-heading2-size:
|
| 599 |
-
--base-heading3-size:
|
| 600 |
--base-input-font: 16px;
|
| 601 |
--base-button-font: 16px;
|
| 602 |
}
|
|
@@ -604,7 +620,7 @@ div[data-testid="stVerticalBlock"] > div[data-testid="stHorizontalBlock"] > div
|
|
| 604 |
/* ======== تنظیمات پویا بر اساس عرض دستگاه ======== */
|
| 605 |
@media (max-width: 400px) {
|
| 606 |
:root {
|
| 607 |
-
--base-font-size:
|
| 608 |
--base-heading1-size: 22px;
|
| 609 |
--base-heading2-size: 18px;
|
| 610 |
--base-heading3-size: 16px;
|
|
@@ -615,7 +631,7 @@ div[data-testid="stVerticalBlock"] > div[data-testid="stHorizontalBlock"] > div
|
|
| 615 |
|
| 616 |
@media (min-width: 401px) and (max-width: 768px) {
|
| 617 |
:root {
|
| 618 |
-
--base-font-size:
|
| 619 |
--base-heading1-size: 24px;
|
| 620 |
--base-heading2-size: 20px;
|
| 621 |
--base-heading3-size: 18px;
|
|
@@ -669,10 +685,106 @@ h3 {
|
|
| 669 |
.stButton>button {
|
| 670 |
padding: calc(var(--base-font-size) * 0.7) calc(var(--base-font-size) * 1.2) !important;
|
| 671 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 672 |
</style>
|
| 673 |
""", unsafe_allow_html=True)
|
| 674 |
|
| 675 |
# ========== توابع اصلی ==========
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 676 |
def create_ride_map():
|
| 677 |
"""ایجاد نقشه سفر با Folium - نسخه اصلاح شده با مناطق عمومی"""
|
| 678 |
# نقاط تقریبی برای مناطق عمومی جنوب و غرب تهران
|
|
@@ -721,64 +833,74 @@ def show_explanation(exp_type):
|
|
| 721 |
"""نمایش توضیحات قیمت"""
|
| 722 |
explanations = {
|
| 723 |
"input": [
|
| 724 |
-
"سطح تقاضا در منطقه: زیاد",
|
| 725 |
-
"تعداد رانندگان فعال: کم",
|
| 726 |
-
"زمان روز: ساعت اوج ترافیک",
|
| 727 |
-
"شرایط جوی: هوای بارانی"
|
| 728 |
],
|
| 729 |
"counterfactual": [
|
| 730 |
-
"اگر این سفر را
|
| 731 |
]
|
| 732 |
}
|
| 733 |
|
| 734 |
if exp_type != "control":
|
| 735 |
-
|
| 736 |
-
|
| 737 |
-
st.markdown(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 738 |
|
| 739 |
def create_likert_question(question, key, scale_type="5point"):
|
| 740 |
-
"""سوال لیکرت با د
|
| 741 |
-
if scale_type == "
|
| 742 |
-
|
| 743 |
-
1: "کاملاً مخالفم",
|
| 744 |
-
2: "مخالفم",
|
| 745 |
-
3: "نظری ندارم",
|
| 746 |
-
4: "موافقم",
|
| 747 |
-
5: "کاملاً موافقم"
|
| 748 |
-
}
|
| 749 |
-
else: # 7-point scale
|
| 750 |
-
options = {
|
| 751 |
-
1: "کاملاً مخالفم",
|
| 752 |
-
2: "مخالفم",
|
| 753 |
-
3: "تا حدی مخالفم",
|
| 754 |
-
4: "نظری ندارم",
|
| 755 |
-
5: "تا حدی موافقم",
|
| 756 |
-
6: "موافقم",
|
| 757 |
-
7: "کاملاً موافقم"
|
| 758 |
-
}
|
| 759 |
-
|
| 760 |
-
st.markdown(f"<p style='margin-bottom: 10px;'>{question}</p>", unsafe_allow_html=True)
|
| 761 |
|
| 762 |
-
|
| 763 |
-
selected = st.session_state.get(key, None)
|
| 764 |
|
| 765 |
-
|
| 766 |
-
for value, label in reversed(options.items()):
|
| 767 |
-
with cols[len(options) - value]:
|
| 768 |
-
if st.button(
|
| 769 |
-
label,
|
| 770 |
-
key=f"{key}_{value}",
|
| 771 |
-
on_click=lambda v=value: st.session_state.update({key: v}),
|
| 772 |
-
type="primary" if selected == value else "secondary"
|
| 773 |
-
):
|
| 774 |
-
pass
|
| 775 |
|
| 776 |
-
|
| 777 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 778 |
|
| 779 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 780 |
|
| 781 |
# ========== توابع مدیریت دادهها ==========
|
|
|
|
| 782 |
def get_credentials():
|
| 783 |
"""دریافت اعتبارنامه از Secrets"""
|
| 784 |
try:
|
|
@@ -821,6 +943,7 @@ def save_to_sheet(data):
|
|
| 821 |
data.get("education", ""),
|
| 822 |
data.get("ride_frequency", ""),
|
| 823 |
data.get("related_education_job",""),
|
|
|
|
| 824 |
data.get("user_contact", ""),
|
| 825 |
data.get("price_accepted", ""),
|
| 826 |
|
|
@@ -828,24 +951,24 @@ def save_to_sheet(data):
|
|
| 828 |
data.get("attention_check1", ""),
|
| 829 |
data.get("attention_check2", ""),
|
| 830 |
|
| 831 |
-
|
| 832 |
-
# سوالات informational (5 گزینهای)
|
| 833 |
-
data.get("informational_1", ""),
|
| 834 |
-
data.get("informational_2", ""),
|
| 835 |
-
data.get("informational_3", ""),
|
| 836 |
-
data.get("informational_4", ""),
|
| 837 |
-
data.get("informational_5", ""),
|
| 838 |
-
|
| 839 |
# سوالات distributive (7 گزینهای)
|
| 840 |
data.get("distributive_1", ""),
|
| 841 |
data.get("distributive_2", ""),
|
| 842 |
-
data.get("distributive_3", ""),
|
| 843 |
|
| 844 |
# سوالات procedural (7 گزینهای)
|
| 845 |
data.get("procedural_1", ""),
|
| 846 |
data.get("procedural_2", ""),
|
| 847 |
data.get("procedural_3", ""),
|
| 848 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 849 |
# سوالات manipulation
|
| 850 |
data.get ("trust", ""),
|
| 851 |
data.get("pricing_method", ""),
|
|
@@ -861,30 +984,61 @@ def save_to_sheet(data):
|
|
| 861 |
st.error(f"خطا در ذخیرهسازی: {str(e)}")
|
| 862 |
return False
|
| 863 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 864 |
# ========== بخشهای فرم ==========
|
| 865 |
|
| 866 |
def welcome_page():
|
| 867 |
"""صفحه خوشامدگویی"""
|
| 868 |
-
st.markdown("""
|
| 869 |
-
<div style="
|
| 870 |
-
<
|
| 871 |
-
|
| 872 |
-
|
| 873 |
-
|
| 874 |
-
</div>
|
| 875 |
-
|
| 876 |
-
<div style="display: flex; align-items: center; background-color: #f0f2f6; border-radius: 10px; padding: 20px; gap: 20px;">
|
| 877 |
-
<div style="flex: 0 0 100px;">
|
| 878 |
-
<img src="https://huggingface.co/spaces/maryamilka/surge-pricing/resolve/main/shariflogo.png" alt="لوگو دانشگاه شریف" style="width: 100%; max-width: 100px;">
|
| 879 |
</div>
|
| 880 |
<div style="flex: 1;">
|
| 881 |
-
<h3>
|
| 882 |
-
<p>
|
| 883 |
-
<
|
| 884 |
-
<p>
|
| 885 |
-
<p>
|
|
|
|
| 886 |
</div>
|
| 887 |
</div>
|
|
|
|
| 888 |
""", unsafe_allow_html=True)
|
| 889 |
|
| 890 |
if st.button("شروع پرسشنامه", key="start_btn", type="primary"):
|
|
@@ -895,12 +1049,14 @@ def welcome_page():
|
|
| 895 |
|
| 896 |
def scenario_explanation():
|
| 897 |
"""توضیح سناریو"""
|
| 898 |
-
col1, col2 = st.columns([
|
| 899 |
with col1:
|
|
|
|
| 900 |
try:
|
| 901 |
st.image("rahyar.png", width=80)
|
| 902 |
except:
|
| 903 |
st.image("https://via.placeholder.com/80/6a0dad/FFFFFF?text=LOGO", width=80)
|
|
|
|
| 904 |
with col2:
|
| 905 |
st.markdown("""
|
| 906 |
<h2 class="rahyar-title">رهیار 🚖</h2>
|
|
@@ -911,9 +1067,12 @@ def scenario_explanation():
|
|
| 911 |
|
| 912 |
st.markdown("""
|
| 913 |
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 10px;">
|
| 914 |
-
<p>فرض کنید
|
| 915 |
-
<p>
|
| 916 |
-
<
|
|
|
|
|
|
|
|
|
|
| 917 |
</div>
|
| 918 |
""", unsafe_allow_html=True)
|
| 919 |
|
|
@@ -922,24 +1081,23 @@ def scenario_explanation():
|
|
| 922 |
st.rerun()
|
| 923 |
|
| 924 |
def map_view():
|
| 925 |
-
|
| 926 |
-
col1, col2 = st.columns([1, 4])
|
| 927 |
with col1:
|
|
|
|
| 928 |
try:
|
| 929 |
st.image("rahyar.png", width=80)
|
| 930 |
except:
|
| 931 |
st.image("https://via.placeholder.com/80/6a0dad/FFFFFF?text=LOGO", width=80)
|
|
|
|
| 932 |
with col2:
|
| 933 |
st.markdown("""
|
| 934 |
-
<h2
|
| 935 |
-
<p
|
| 936 |
""", unsafe_allow_html=True)
|
| 937 |
|
| 938 |
st.markdown("""
|
| 939 |
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 10px;">
|
| 940 |
<p>مسیر سفر شما به صورت حدودی، از جنوب به غرب تهران است.</p>
|
| 941 |
-
<p>فرض کنید در رهیار گزینههای مختلف سفر مثل بهصرفه،اکوپلاس یا بایک وجود ندارد و فقط میتوانید رهیار معمولی بگیرید.</p>
|
| 942 |
-
<p>همچنین فرض کنید ویژگیهایی مانند رهیار با همسفر ، سفر دو مسیره، توقف در سفر و.. نیز در رهیار وجود ندارد.</p>
|
| 943 |
<p> با توجه به اطلاعاتی که بعد از نقشه دریافت میکنید، تصمیم بگیرید که سفر را میپذیرید را رد میکنید.</p>
|
| 944 |
<p>سپس با کلیک بر دکمه مربوطه به بخش بعدی بروید.</p>
|
| 945 |
</div>
|
|
@@ -963,13 +1121,13 @@ def map_view():
|
|
| 963 |
# دکمهها
|
| 964 |
col1, col2 = st.columns(2)
|
| 965 |
with col1:
|
| 966 |
-
if st.button("درخواست ر
|
| 967 |
st.session_state.price_accepted = 1
|
| 968 |
st.session_state.current_page = "attention_check1"
|
| 969 |
st.rerun()
|
| 970 |
|
| 971 |
with col2:
|
| 972 |
-
if st.button("رد ر
|
| 973 |
st.session_state.price_accepted = 0
|
| 974 |
st.session_state.current_page = "attention_check1"
|
| 975 |
st.rerun()
|
|
@@ -989,7 +1147,7 @@ def attention_check1():
|
|
| 989 |
st.markdown("### سوال")
|
| 990 |
|
| 991 |
answer = st.radio(
|
| 992 |
-
"رنگ
|
| 993 |
["قرمز", "سبز", "بنفش", "آبی", "فراموش کردم"],
|
| 994 |
index=None,
|
| 995 |
key="att1_radio"
|
|
@@ -1035,198 +1193,202 @@ def attention_check1():
|
|
| 1035 |
else:
|
| 1036 |
st.warning("لطفاً یک گزینه را انتخاب کنید")
|
| 1037 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1038 |
|
| 1039 |
|
| 1040 |
-
|
| 1041 |
-
|
| 1042 |
-
|
| 1043 |
-
# تعریف تمام سوالات با نوع مقیاس و لیبل مربوطه
|
| 1044 |
-
st.session_state.all_questions = [
|
| 1045 |
-
# سوالات informational (5 گزینهای)
|
| 1046 |
-
{"key": "informational_1", "question": "پلتفرم به صورت صادقانه دلایل تعیین قیمت را توضیح داد.", "scale": "5point", "label": "informational_1"},
|
| 1047 |
-
{"key": "informational_2", "question": "پلتفرم به طور کامل عوامل مؤثر بر تعیین قیمت را شرح داد.", "scale": "5point", "label": "informational_2"},
|
| 1048 |
-
{"key": "informational_3", "question": "دلایل ارائهشده برای تعیین قیمت منطقی و قابل قبول بود.", "scale": "5point", "label": "informational_3"},
|
| 1049 |
-
{"key": "informational_4", "question": "توضیحات درباره تعیین قیمت بلافاصله و در زمان مناسب نمایش داده شد.", "scale": "5point", "label": "informational_4"},
|
| 1050 |
-
{"key": "informational_5", "question": "توضیحات پلتفرم درباره تعیین قیمت، متناسب با شرایط سفر من بود.", "scale": "5point", "label": "informational_5"},
|
| 1051 |
-
|
| 1052 |
-
# سوالات distributive (7 گزینهای)
|
| 1053 |
-
{"key": "distributive_1", "question": "قیمتی که به شما ارائه شد، منصفانه است.", "scale": "7point", "label": "distributive_1"},
|
| 1054 |
-
{"key": "distributive_2", "question": "قیمتی که به شما ارائه شد، معقول است.", "scale": "7point", "label": "distributive_2"},
|
| 1055 |
-
{"key": "distributive_3", "question": "قیمتی که به شما ارائه شد، قابل قبول است.", "scale": "7point", "label": "distributive_3"},
|
| 1056 |
-
|
| 1057 |
-
# سوالات procedural (7 گزینهای)
|
| 1058 |
-
{"key": "procedural_1", "question": "فرآیند و رویه قیمتگذاری پلتفرم قابل قبول است.", "scale": "7point", "label": "procedural_1"},
|
| 1059 |
-
{"key": "procedural_2", "question": "فرآیند و رویه قیمتگذاری پلتفرم منصفانه است.", "scale": "7point", "label": "procedural_2"},
|
| 1060 |
-
{"key": "procedural_3", "question": "فرآیند و رویه قیمتگذاری پلتفرم معقول است.", "scale": "7point", "label": "procedural_3"},
|
| 1061 |
-
|
| 1062 |
-
# سوال attention_check2 به صورت رندوم در میان سوالات دیگر
|
| 1063 |
-
{"key": "attention_check2", "question": "حاصل ۵ × ۳ به اضافه ۲ چقدر میشود؟", "scale": "attention", "label": "attention_check2"}
|
| 1064 |
-
]
|
| 1065 |
-
|
| 1066 |
-
# تصادفیسازی ترتیب سوالات
|
| 1067 |
-
random.shuffle(st.session_state.all_questions)
|
| 1068 |
-
st.session_state.current_question_index = 0
|
| 1069 |
|
| 1070 |
-
#
|
| 1071 |
-
|
| 1072 |
-
(st.session_state.all_questions[st.session_state.current_question_index]["scale"] != "attention"):
|
| 1073 |
-
st.markdown("""
|
| 1074 |
-
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 10px; margin-bottom: 20px;">
|
| 1075 |
-
<p style="font-weight: bold;">راهنمای پاسخگویی:</p>
|
| 1076 |
-
<p>لطفا موافقت خود را با سوالات زیر طبق گزینهها اعلام کنید.</p>
|
| 1077 |
-
<p>با کلیک روی هر گزینه پاسخ شما انتخاب خواهد شد. لطفا با صبوری و بررسی سوالات را پاسخ دهید و تا بارگزاری سوال بعدی کمی صبر کنید.</p>
|
| 1078 |
-
</div>
|
| 1079 |
-
""", unsafe_allow_html=True)
|
| 1080 |
|
| 1081 |
-
|
| 1082 |
-
q = st.session_state.all_questions[st.session_state.current_question_index]
|
| 1083 |
-
|
| 1084 |
-
# نمایش سوال attention_check2 به صورت خاص
|
| 1085 |
-
if q["scale"] == "attention":
|
| 1086 |
-
|
| 1087 |
-
st.markdown("""
|
| 1088 |
-
<style>
|
| 1089 |
-
/* تضمین رنگ متن برای تمام سطوح */
|
| 1090 |
-
.st-ec, .st-ed, .st-ee, .st-ef, .st-eg, .st-eh, .st-ei, .st-ej {
|
| 1091 |
-
color: black !important;
|
| 1092 |
-
}
|
| 1093 |
-
</style>
|
| 1094 |
-
""", unsafe_allow_html=True)
|
| 1095 |
-
st.markdown("### سوال")
|
| 1096 |
-
|
| 1097 |
-
answer = st.radio(
|
| 1098 |
-
"حاصل ۵ × ۳ به اضافه ۲ چقدر میشود؟",
|
| 1099 |
-
["1", "13", "15", "17", "دیگر"],
|
| 1100 |
-
index=None,
|
| 1101 |
-
key="att2_radio"
|
| 1102 |
-
)
|
| 1103 |
-
|
| 1104 |
-
if st.button("ادامه", key="att2_btn"):
|
| 1105 |
-
if answer:
|
| 1106 |
-
st.session_state.attention_check2 = answer
|
| 1107 |
-
st.session_state.current_question_index += 1
|
| 1108 |
-
time.sleep(0.5)
|
| 1109 |
-
st.rerun()
|
| 1110 |
-
else:
|
| 1111 |
-
st.warning("لطفاً یک گزینه را انتخاب کنید")
|
| 1112 |
-
else:
|
| 1113 |
-
# نمایش سوالات لیکرت به صورت معمول
|
| 1114 |
-
answer = create_likert_question(q["question"], q["key"], q["scale"])
|
| 1115 |
-
|
| 1116 |
-
if answer:
|
| 1117 |
-
st.session_state.current_question_index += 1
|
| 1118 |
-
time.sleep(0.5)
|
| 1119 |
-
st.rerun()
|
| 1120 |
-
else:
|
| 1121 |
-
st.session_state.current_page = "explanation_questions"
|
| 1122 |
-
st.rerun()
|
| 1123 |
-
|
| 1124 |
-
def explanation_questions():
|
| 1125 |
-
"""سوالات درباره توضیحات قیمت"""
|
| 1126 |
st.markdown("""
|
| 1127 |
<style>
|
| 1128 |
-
|
| 1129 |
-
|
| 1130 |
-
color: black !important;
|
| 1131 |
}
|
| 1132 |
</style>
|
| 1133 |
""", unsafe_allow_html=True)
|
| 1134 |
-
st.markdown("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1135 |
|
| 1136 |
-
# مقداردهی اولیه متغیرهای session_state اگر وجود ندارند
|
| 1137 |
-
if 'trust' not in st.session_state:
|
| 1138 |
-
st.session_state.trust = None
|
| 1139 |
-
if 'pricing_method' not in st.session_state:
|
| 1140 |
-
st.session_state.pricing_method = None
|
| 1141 |
-
if 'price_increase' not in st.session_state:
|
| 1142 |
-
st.session_state.price_increase = None
|
| 1143 |
-
if 'explanation_received' not in st.session_state:
|
| 1144 |
-
st.session_state.explanation_received = None
|
| 1145 |
-
if 'explanation_type' not in st.session_state:
|
| 1146 |
-
st.session_state.explanation_type = None
|
| 1147 |
-
|
| 1148 |
-
trust = st.radio(
|
| 1149 |
-
"آیا شما به تصمیم گیری هایی که توسط هوش مصنوعی انجام می شود اعتماد دارید؟",
|
| 1150 |
-
[
|
| 1151 |
-
"بله",
|
| 1152 |
-
"خیر",
|
| 1153 |
-
"نظری ندارم"
|
| 1154 |
-
],
|
| 1155 |
-
index=None,
|
| 1156 |
-
key="trust_radio"
|
| 1157 |
-
)
|
| 1158 |
|
| 1159 |
-
|
| 1160 |
-
if
|
| 1161 |
-
|
| 1162 |
-
|
| 1163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1164 |
"به صورت دستی توسط تیم پلتفرم",
|
| 1165 |
"به صورت خودکار توسط هوش مصنوعی و الگوریتمها",
|
| 1166 |
"ترکیبی از هر دو روش",
|
| 1167 |
"نظری ندارم"
|
| 1168 |
],
|
| 1169 |
-
|
| 1170 |
-
|
| 1171 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1172 |
|
| 1173 |
-
#
|
| 1174 |
-
|
| 1175 |
-
|
| 1176 |
-
price_increase = st.radio(
|
| 1177 |
-
"آیا به نظر شما در این سفر افزایش قیمت نسبت به حالت طبیعی وجود داشته است؟",
|
| 1178 |
-
["بله", "خیر", "مطمئن نیستم"],
|
| 1179 |
-
index=None,
|
| 1180 |
-
key="price_increase_radio"
|
| 1181 |
-
)
|
| 1182 |
|
| 1183 |
-
# سوالات
|
| 1184 |
-
|
| 1185 |
-
|
| 1186 |
-
|
| 1187 |
-
|
| 1188 |
-
"آیا برای قیمت پیشنهادی این سفر، توضیحی به شما ارائه شد؟",
|
| 1189 |
-
["بله", "خیر"],
|
| 1190 |
-
index=None,
|
| 1191 |
-
key="explanation_received_radio"
|
| 1192 |
-
)
|
| 1193 |
-
|
| 1194 |
-
# سوال دوم (فقط اگر پاسخ بله باشد)
|
| 1195 |
-
if explanation_received == "بله":
|
| 1196 |
-
explanation_type = st.radio(
|
| 1197 |
-
"اگر توضیحی دریافت کردید، این توضیح بیشتر به کدام مورد شباهت داشت؟",
|
| 1198 |
-
[
|
| 1199 |
-
"بر اساس عواملی که در قیمتگذاری لحاظ شدهاند",
|
| 1200 |
-
"شامل سناریوهای جایگزین که میتوانستند قیمت متفاوتی ایجاد کنند",
|
| 1201 |
-
"توضیحی دریافت نکردم"
|
| 1202 |
-
],
|
| 1203 |
-
index=None,
|
| 1204 |
-
key="explanation_type_radio"
|
| 1205 |
-
)
|
| 1206 |
-
|
| 1207 |
-
if st.button("ادامه", type="primary", key="submit_explanation"):
|
| 1208 |
-
# استفاده از مقادیر مستقیماً از session_state
|
| 1209 |
-
st.session_state.trust = st.session_state.get("trust_radio")
|
| 1210 |
-
st.session_state.pricing_method = st.session_state.get("pricing_method_radio")
|
| 1211 |
-
st.session_state.price_increase = st.session_state.get("price_increase_radio")
|
| 1212 |
-
st.session_state.explanation_received = st.session_state.get("explanation_received_radio")
|
| 1213 |
-
st.session_state.explanation_type = st.session_state.get("explanation_type_radio")
|
| 1214 |
-
|
| 1215 |
-
# بررسی کامل بودن پاسخها
|
| 1216 |
-
if st.session_state.trust is None:
|
| 1217 |
-
st.warning("لطفاً به سوال تکمیلی اول پاسخ دهید")
|
| 1218 |
-
elif st.session_state.pricing_method is None:
|
| 1219 |
-
st.warning("لطفاً به سوال تکمیلی دوم پاسخ دهید")
|
| 1220 |
-
elif st.session_state.price_increase is None:
|
| 1221 |
-
st.warning("لطفاً به سوال تکمیلی سوم پاسخ دهید")
|
| 1222 |
-
elif st.session_state.explanation_received is None:
|
| 1223 |
-
st.warning("لطفاً به سوال تکمیلی چهارم پاسخ دهید")
|
| 1224 |
-
elif st.session_state.explanation_received == "بله" and st.session_state.explanation_type is None:
|
| 1225 |
-
st.warning("لطفاً به سوال تکمیلی پنجم پاسخ دهید")
|
| 1226 |
-
else:
|
| 1227 |
-
st.session_state.current_page = "demographic"
|
| 1228 |
-
st.rerun()
|
| 1229 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1230 |
|
| 1231 |
def demographic_form():
|
| 1232 |
"""فرم اطلاعات دموگرافیک"""
|
|
@@ -1239,27 +1401,37 @@ def demographic_form():
|
|
| 1239 |
""", unsafe_allow_html=True)
|
| 1240 |
|
| 1241 |
with st.form("demographic_form"):
|
| 1242 |
-
age = st.number_input("سن", min_value=18, max_value=100)
|
| 1243 |
-
gender = st.selectbox("جنسیت", ["مرد", "زن", "سایر"])
|
| 1244 |
-
education = st.selectbox("تحصیلات", ["دیپلم", "لیسانس", "فوق لیسانس", "دکترا"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1245 |
ride_frequency = st.selectbox("دفعات استفاده از سرویسهای اشتراک سفر در ماه",
|
| 1246 |
-
["کمتر از 5 بار", "5-10 بار", "بیش از 10 بار"])
|
| 1247 |
-
|
| 1248 |
-
|
| 1249 |
-
|
| 1250 |
-
|
| 1251 |
-
|
| 1252 |
-
|
| 1253 |
-
|
| 1254 |
-
|
| 1255 |
-
|
| 1256 |
-
|
| 1257 |
-
|
| 1258 |
-
|
| 1259 |
-
|
| 1260 |
-
|
| 1261 |
-
|
| 1262 |
-
|
| 1263 |
|
| 1264 |
def user_contact():
|
| 1265 |
"""راه ارتباطی ساده"""
|
|
@@ -1290,20 +1462,16 @@ def user_contact():
|
|
| 1290 |
"price": st.session_state.price,
|
| 1291 |
"user_contact": st.session_state.get("user_contact", ""),
|
| 1292 |
"price_accepted": st.session_state.get("price_accepted", 0),
|
| 1293 |
-
"attention_check1": st.session_state.attention_check1,
|
| 1294 |
-
"attention_check2": st.session_state.attention_check2,
|
| 1295 |
"trust": st.session_state.trust,
|
| 1296 |
"pricing_method": st.session_state.pricing_method,
|
| 1297 |
"price_increase": st.session_state.price_increase,
|
| 1298 |
"explanation_received": st.session_state.explanation_received,
|
| 1299 |
-
"explanation_type": st.session_state.explanation_type
|
| 1300 |
-
**st.session_state.demographic_data
|
|
|
|
| 1301 |
}
|
| 1302 |
|
| 1303 |
-
# اضافه کردن پاسخهای لیکرت
|
| 1304 |
-
for q in st.session_state.all_questions:
|
| 1305 |
-
save_data[q["label"]] = st.session_state.get(q["key"], None)
|
| 1306 |
-
|
| 1307 |
if save_to_sheet(save_data):
|
| 1308 |
st.session_state.current_page = "thank_you"
|
| 1309 |
st.rerun()
|
|
@@ -1330,10 +1498,12 @@ def main():
|
|
| 1330 |
st.session_state.is_desktop = "mobile" not in user_agent.lower()
|
| 1331 |
|
| 1332 |
|
| 1333 |
-
# اینجا میتوانید از is_desktop برای تعیین اینکه نمایشگر موبایل اعمال شود، استفاده کنید
|
| 1334 |
if st.session_state.is_desktop:
|
| 1335 |
# اطمینان از نمایش همان حالت موبایل برای همه دستگاهها
|
| 1336 |
st.session_state.is_desktop = False
|
|
|
|
|
|
|
|
|
|
| 1337 |
|
| 1338 |
if 'current_page' not in st.session_state:
|
| 1339 |
st.session_state.current_page = "welcome"
|
|
@@ -1342,7 +1512,7 @@ def main():
|
|
| 1342 |
st.session_state.user_contact = None
|
| 1343 |
st.session_state.demographic_data = None
|
| 1344 |
st.session_state.price_accepted = 0
|
| 1345 |
-
|
| 1346 |
|
| 1347 |
pages = {
|
| 1348 |
"welcome": welcome_page,
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
import folium
|
| 3 |
from streamlit_folium import folium_static
|
| 4 |
+
import streamlit.components.v1 as components
|
| 5 |
from datetime import datetime
|
| 6 |
import gspread
|
| 7 |
from google.oauth2.service_account import Credentials
|
|
|
|
| 75 |
[data-testid="stAppViewContainer"] {
|
| 76 |
width: 475px !important;
|
| 77 |
height: 667px !important;
|
| 78 |
+
margin: 0 auto !important;
|
| 79 |
border: 12px solid #000 !important;
|
| 80 |
border-radius: 40px !important;
|
| 81 |
box-shadow:
|
|
|
|
| 157 |
[data-testid="stAppViewContainer"] .stDateInput>div>div>input,
|
| 158 |
[data-testid="stAppViewContainer"] .stTimeInput>div>div>input,
|
| 159 |
[data-testid="stAppViewContainer"] .price-container,
|
|
|
|
| 160 |
[data-testid="stAppViewContainer"] .stAlert,
|
| 161 |
[data-testid="stAppViewContainer"] .stMarkdown,
|
| 162 |
[data-testid="stAppViewContainer"] .folium-map,
|
|
|
|
| 196 |
.rahyar-subtitle {
|
| 197 |
color: var(--primary) !important;
|
| 198 |
margin: 0 !important;
|
| 199 |
+
font-size: 16px !important;
|
| 200 |
}
|
| 201 |
|
| 202 |
/* توضیحات */
|
|
|
|
| 213 |
padding: 12px 15px !important;
|
| 214 |
margin: 8px 0 !important;
|
| 215 |
border-right: 3px solid var(--primary) !important;
|
| 216 |
+
font-size: 18px !important;
|
| 217 |
}
|
| 218 |
|
| 219 |
|
|
|
|
| 309 |
color: var(--text) !important;
|
| 310 |
font-weight: bold !important;
|
| 311 |
margin-bottom: 8px !important;
|
| 312 |
+
font-size: 16px !important;
|
| 313 |
display: block !important;
|
| 314 |
}
|
| 315 |
|
|
|
|
| 462 |
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2) !important; /* سایه بیشتر */
|
| 463 |
}
|
| 464 |
|
| 465 |
+
/* استایل تضمینی برای رادیو باتنها - نسخه نهایی */
|
| 466 |
.stRadio > div > div > div > div > div > label,
|
| 467 |
.stRadio > div > div > div > div > label,
|
| 468 |
.stRadio > div > div > div > label,
|
|
|
|
| 471 |
.stRadio > label {
|
| 472 |
color: #000000 !important;
|
| 473 |
-webkit-text-fill-color: #000000 !important; /* برای مرورگرهای وبکیت */
|
| 474 |
+
font-size: 20px !important
|
| 475 |
}
|
| 476 |
|
| 477 |
.stRadio span {
|
| 478 |
color: #000000 !important;
|
| 479 |
-webkit-text-fill-color: #000000 !important;
|
| 480 |
+
font-size: 20px !important
|
| 481 |
}
|
| 482 |
|
| 483 |
/* اگر باز هم مشکل داشت این را اضافه کنید */
|
|
|
|
| 489 |
.stRadio > label > div:first-child {
|
| 490 |
color: #000000 !important;
|
| 491 |
-webkit-text-fill-color: #000000 !important;
|
| 492 |
+
font-size: 20px !important
|
| 493 |
}
|
| 494 |
|
| 495 |
/* استایل پایه برای هشدارها */
|
|
|
|
| 511 |
.stAlert .st-ag {
|
| 512 |
color: #856404 !important;
|
| 513 |
font-family: 'B Nazanin' !important;
|
| 514 |
+
font-size: 16px !important;
|
| 515 |
margin-right: 8px !important;
|
| 516 |
}
|
| 517 |
|
|
|
|
| 531 |
@media (max-width: 768px) {
|
| 532 |
.stAlert .st-ae {
|
| 533 |
padding: 12px !important;
|
| 534 |
+
font-size: 15px !important;
|
| 535 |
}
|
| 536 |
}
|
| 537 |
|
|
|
|
| 539 |
/* متن گزینهها */
|
| 540 |
.stRadio span {
|
| 541 |
color: #000000 !important; /* مشکی خالص */
|
| 542 |
+
font-size: 20px !important;
|
| 543 |
padding-right: 5px !important;
|
| 544 |
}
|
| 545 |
|
| 546 |
/* حالت hover */
|
| 547 |
.stRadio label:hover span {
|
| 548 |
color: #333333 !important; /* مشکی کمی روشنتر */
|
| 549 |
+
font-size: 20px !important;
|
| 550 |
}
|
| 551 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 552 |
|
| 553 |
|
| 554 |
/* تنظیمات مخصوص موبایل */
|
|
|
|
| 561 |
.stTextInput>label,
|
| 562 |
.stNumberInput>label,
|
| 563 |
.stSelectbox>label {
|
| 564 |
+
font-size: 16px !important;
|
| 565 |
padding: 4px 0 !important;
|
| 566 |
}
|
| 567 |
|
|
|
|
| 574 |
}
|
| 575 |
|
| 576 |
.stButton>button {
|
| 577 |
+
font-size: 17px !important;
|
| 578 |
padding: 8px 16px !important;
|
| 579 |
}
|
| 580 |
+
|
| 581 |
+
.stMarkdown h2 {
|
| 582 |
+
font-size: 22px !important;
|
| 583 |
+
}
|
| 584 |
|
| 585 |
.stMarkdown h3 {
|
| 586 |
+
font-size: 19px !important;
|
| 587 |
}
|
| 588 |
|
| 589 |
.stMarkdown p {
|
| 590 |
+
font-size: 17px !important;
|
| 591 |
}
|
| 592 |
|
| 593 |
.stSelectbox [role="listbox"] {
|
| 594 |
+
font-size: 18px !important;
|
| 595 |
+
}
|
| 596 |
+
.stRadio span {
|
| 597 |
+
color: #000000 !important; /* مشکی خالص */
|
| 598 |
+
font-size: 20px !important;
|
| 599 |
+
padding-right: 5px !important;
|
| 600 |
+
}
|
| 601 |
+
|
| 602 |
+
/* حالت hover */
|
| 603 |
+
.stRadio label:hover span {
|
| 604 |
+
color: #333333 !important; /* مشکی کمی روشنتر */
|
| 605 |
+
font-size: 20px !important;
|
| 606 |
}
|
| 607 |
}
|
| 608 |
|
| 609 |
/* ======== تنظیمات پایه واکنشگرا ======== */
|
| 610 |
:root {
|
| 611 |
/* اندازههای پایه بر اساس عرض 375px (آیفون X) */
|
| 612 |
+
--base-font-size: 20px;
|
| 613 |
--base-heading1-size: 24px;
|
| 614 |
+
--base-heading2-size: 22px;
|
| 615 |
+
--base-heading3-size: 20px;
|
| 616 |
--base-input-font: 16px;
|
| 617 |
--base-button-font: 16px;
|
| 618 |
}
|
|
|
|
| 620 |
/* ======== تنظیمات پویا بر اساس عرض دستگاه ======== */
|
| 621 |
@media (max-width: 400px) {
|
| 622 |
:root {
|
| 623 |
+
--base-font-size: 17px;
|
| 624 |
--base-heading1-size: 22px;
|
| 625 |
--base-heading2-size: 18px;
|
| 626 |
--base-heading3-size: 16px;
|
|
|
|
| 631 |
|
| 632 |
@media (min-width: 401px) and (max-width: 768px) {
|
| 633 |
:root {
|
| 634 |
+
--base-font-size: 17px;
|
| 635 |
--base-heading1-size: 24px;
|
| 636 |
--base-heading2-size: 20px;
|
| 637 |
--base-heading3-size: 18px;
|
|
|
|
| 685 |
.stButton>button {
|
| 686 |
padding: calc(var(--base-font-size) * 0.7) calc(var(--base-font-size) * 1.2) !important;
|
| 687 |
}
|
| 688 |
+
/* اضافه کردن این بخش جدید */
|
| 689 |
+
[data-testid="stAppViewContainer"] img {
|
| 690 |
+
max-width: 100% !important;
|
| 691 |
+
height: auto !important;
|
| 692 |
+
display: block !important;
|
| 693 |
+
padding: 0 !important;
|
| 694 |
+
margin: 0 auto !important;
|
| 695 |
+
}
|
| 696 |
+
|
| 697 |
+
/* برای تضمین نمایش در حالت موبایل شبیهسازی شده */
|
| 698 |
+
@media (min-width: 768px) {
|
| 699 |
+
[data-testid="stAppViewContainer"] img {
|
| 700 |
+
max-width: 200px !important;
|
| 701 |
+
}
|
| 702 |
+
}
|
| 703 |
+
|
| 704 |
+
|
| 705 |
+
/* تنظیم justify برای تمام متنها */
|
| 706 |
+
[data-testid="stAppViewContainer"] * {
|
| 707 |
+
text-align: justify !important;
|
| 708 |
+
text-justify: inter-word !important;
|
| 709 |
+
}
|
| 710 |
+
|
| 711 |
+
/* استثناها برای عناصری که نباید justify شوند */
|
| 712 |
+
[data-testid="stAppViewContainer"] .stRadio>label,
|
| 713 |
+
[data-testid="stAppViewContainer"] .stCheckbox>label,
|
| 714 |
+
[data-testid="stAppViewContainer"] .stSelectbox>label,
|
| 715 |
+
[data-testid="stAppViewContainer"] .stTextInput>label,
|
| 716 |
+
[data-testid="stAppViewContainer"] .stNumberInput>label,
|
| 717 |
+
[data-testid="stAppViewContainer"] input,
|
| 718 |
+
[data-testid="stAppViewContainer"] select,
|
| 719 |
+
[data-testid="stAppViewContainer"] textarea,
|
| 720 |
+
[data-testid="stAppViewContainer"] .rahyar-title,
|
| 721 |
+
[data-testid="stAppViewContainer"] .rahyar-subtitle {
|
| 722 |
+
direction: rtl !important;
|
| 723 |
+
text-align: right !important;
|
| 724 |
+
text-justify: auto !important;
|
| 725 |
+
}
|
| 726 |
+
|
| 727 |
+
/* تنظیمات برای لیستها */
|
| 728 |
+
[data-testid="stAppViewContainer"] ul,
|
| 729 |
+
[data-testid="stAppViewContainer"] ol {
|
| 730 |
+
padding-right: 25px !important;
|
| 731 |
+
}
|
| 732 |
+
|
| 733 |
+
[data-testid="stAppViewContainer"] p:not(.stButton p):not(.stDownloadButton p):not(.stFormSubmitButton p) {
|
| 734 |
+
margin-bottom: 1em !important;
|
| 735 |
+
line-height: 1.8 !important;
|
| 736 |
+
}
|
| 737 |
</style>
|
| 738 |
""", unsafe_allow_html=True)
|
| 739 |
|
| 740 |
# ========== توابع اصلی ==========
|
| 741 |
+
def custom_likert_slider(question_data):
|
| 742 |
+
"""نمایش سوال لیکرت با اسلایدر نقطهای و لیبلهای سفارشی"""
|
| 743 |
+
question = question_data["question"]
|
| 744 |
+
key = question_data["key"]
|
| 745 |
+
points = question_data["scale"]
|
| 746 |
+
labels = question_data.get("labels", ["کمترین", "بیشترین"]) # لیبلهای پیشفرض
|
| 747 |
+
|
| 748 |
+
# مقدار پیشفرض (وسط طیف)
|
| 749 |
+
default_value = st.session_state.get(key, (points + 1) // 2)
|
| 750 |
+
|
| 751 |
+
# HTML و JavaScript
|
| 752 |
+
html = f"""
|
| 753 |
+
<div id="container_{key}" style="direction: ltr; font-family: 'B Nazanin'; margin-bottom: 30px;">
|
| 754 |
+
<label style="font-size: 16px; font-weight: bold; display: block; text-align: right;">{question}</label>
|
| 755 |
+
<div style="display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 5px;">
|
| 756 |
+
<span>{labels[0]}</span>
|
| 757 |
+
<span>{labels[1]}</span>
|
| 758 |
+
</div>
|
| 759 |
+
<input type="range" id="{key}" min="1" max="{points}" step="1" value="{default_value}"
|
| 760 |
+
style="width: 100%; height: 10px; accent-color: #6a0dad; margin-bottom: 15px;"
|
| 761 |
+
oninput="updateSlider('{key}')">
|
| 762 |
+
<div style="text-align: center; margin-top: 10px; direction: rtl;">
|
| 763 |
+
پاسخ انتخابشده: <strong><span id="output_{key}">{default_value}</span></strong>
|
| 764 |
+
</div>
|
| 765 |
+
</div>
|
| 766 |
+
|
| 767 |
+
<script>
|
| 768 |
+
function updateSlider(key) {{
|
| 769 |
+
const value = parseInt(document.getElementById(key).value);
|
| 770 |
+
document.getElementById('output_' + key).innerText = value;
|
| 771 |
+
|
| 772 |
+
// ارسال مقدار به Streamlit
|
| 773 |
+
window.parent.postMessage({{
|
| 774 |
+
type: 'streamlit:setComponentValue',
|
| 775 |
+
key: key,
|
| 776 |
+
value: value
|
| 777 |
+
}}, '*');
|
| 778 |
+
}}
|
| 779 |
+
</script>
|
| 780 |
+
"""
|
| 781 |
+
|
| 782 |
+
# نمایش کامپوننت
|
| 783 |
+
components.html(html, height=150)
|
| 784 |
+
|
| 785 |
+
# مقدار نهایی
|
| 786 |
+
return st.session_state.get(key, default_value)
|
| 787 |
+
|
| 788 |
def create_ride_map():
|
| 789 |
"""ایجاد نقشه سفر با Folium - نسخه اصلاح شده با مناطق عمومی"""
|
| 790 |
# نقاط تقریبی برای مناطق عمومی جنوب و غرب تهران
|
|
|
|
| 833 |
"""نمایش توضیحات قیمت"""
|
| 834 |
explanations = {
|
| 835 |
"input": [
|
| 836 |
+
" سطح تقاضا در منطقه: زیاد (+)",
|
| 837 |
+
" تعداد رانندگان فعال: کم (+)",
|
| 838 |
+
" زمان روز: ساعت اوج ترافیک (+)",
|
| 839 |
+
"شرایط جوی: هوای بارانی (++)"
|
| 840 |
],
|
| 841 |
"counterfactual": [
|
| 842 |
+
"اگر این سفر را 1 ساعت دیرتر درخواست کنید، به دلیل سطح تقاضای کمتر، رانندگان فعال بیشتر، زمان بهتر روز و شرایط جوی بهتر، احتمالاً قیمت حدوداً 40٪ کمتر (120 هزار تومان) خواهد بود.",
|
| 843 |
]
|
| 844 |
}
|
| 845 |
|
| 846 |
if exp_type != "control":
|
| 847 |
+
if exp_type == "input":
|
| 848 |
+
st.markdown("<p class='explanation-title'>علت قیمت گذاری:</p>", unsafe_allow_html=True)
|
| 849 |
+
st.markdown("""
|
| 850 |
+
<div style="direction: rtl; text-align: right;">
|
| 851 |
+
<span style="font-size: 0.9em; color: #666;">(تعداد علامت + نشان دهنده شدت اثر عامل بر قیمت است)</span>
|
| 852 |
+
</div>
|
| 853 |
+
""", unsafe_allow_html=True)
|
| 854 |
+
for item in explanations.get(exp_type, []):
|
| 855 |
+
st.markdown(f"<p class='explanation-item'>• {item}</p>", unsafe_allow_html=True)
|
| 856 |
+
|
| 857 |
+
elif exp_type == "counterfactual":
|
| 858 |
+
st.markdown("<p class='explanation-title'>علت قیمت گذاری:</p>", unsafe_allow_html=True)
|
| 859 |
+
for item in explanations.get(exp_type, []):
|
| 860 |
+
st.markdown(f"<p class='explanation-item'>• {item}</p>", unsafe_allow_html=True)
|
| 861 |
+
|
| 862 |
+
|
| 863 |
+
|
| 864 |
|
| 865 |
def create_likert_question(question, key, scale_type="5point"):
|
| 866 |
+
"""نمایش سوال لیکرت با اسلایدر نقطهای"""
|
| 867 |
+
left_label = "کاملاً مخالفم" if scale_type == "7point" else "کاملاً مخالفم"
|
| 868 |
+
right_label = "کاملاً موافقم" if scale_type == "7point" else "کاملاً موافقم"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 869 |
|
| 870 |
+
st.markdown(f"<p style='font-size:16px; margin-bottom:5px;'>{question}</p>", unsafe_allow_html=True)
|
|
|
|
| 871 |
|
| 872 |
+
max_value = 7 if scale_type == "7point" else 5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 873 |
|
| 874 |
+
# اضافه کردن attribute برای انتخاب استایل مناسب
|
| 875 |
+
slider_container = st.empty()
|
| 876 |
+
with slider_container:
|
| 877 |
+
st.markdown(f'<div data-testid="stSlider" data-discrete="{"seven-point" if scale_type=="7point" else "five-point"}">', unsafe_allow_html=True)
|
| 878 |
+
value = st.slider(
|
| 879 |
+
"",
|
| 880 |
+
min_value=1,
|
| 881 |
+
max_value=max_value,
|
| 882 |
+
value=(max_value+1)//2,
|
| 883 |
+
step=1,
|
| 884 |
+
key=f"slider_{key}"
|
| 885 |
+
)
|
| 886 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 887 |
|
| 888 |
+
st.markdown(
|
| 889 |
+
f"""
|
| 890 |
+
<div class="slider-labels">
|
| 891 |
+
<span>{left_label}</span>
|
| 892 |
+
<span>{right_label}</span>
|
| 893 |
+
</div>
|
| 894 |
+
<p style='text-align:center; color:#6a0dad; font-weight:bold;'>
|
| 895 |
+
پاسخ شما: {value} ({'کاملاً مخالفم' if value==1 else 'کاملاً موافقم' if value==max_value else 'خنثی' if value==((max_value+1)//2) else 'موافقم' if value>((max_value+1)//2) else 'مخالفم'})
|
| 896 |
+
</p>
|
| 897 |
+
""",
|
| 898 |
+
unsafe_allow_html=True
|
| 899 |
+
)
|
| 900 |
+
return value
|
| 901 |
|
| 902 |
# ========== توابع مدیریت دادهها ==========
|
| 903 |
+
|
| 904 |
def get_credentials():
|
| 905 |
"""دریافت اعتبارنامه از Secrets"""
|
| 906 |
try:
|
|
|
|
| 943 |
data.get("education", ""),
|
| 944 |
data.get("ride_frequency", ""),
|
| 945 |
data.get("related_education_job",""),
|
| 946 |
+
data.get("city",""),
|
| 947 |
data.get("user_contact", ""),
|
| 948 |
data.get("price_accepted", ""),
|
| 949 |
|
|
|
|
| 951 |
data.get("attention_check1", ""),
|
| 952 |
data.get("attention_check2", ""),
|
| 953 |
|
| 954 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 955 |
# سوالات distributive (7 گزینهای)
|
| 956 |
data.get("distributive_1", ""),
|
| 957 |
data.get("distributive_2", ""),
|
| 958 |
+
data.get("distributive_3", ""),
|
| 959 |
|
| 960 |
# سوالات procedural (7 گزینهای)
|
| 961 |
data.get("procedural_1", ""),
|
| 962 |
data.get("procedural_2", ""),
|
| 963 |
data.get("procedural_3", ""),
|
| 964 |
|
| 965 |
+
# سوالات informational (5 گزینهای)
|
| 966 |
+
data.get("informational_1", ""),
|
| 967 |
+
data.get("informational_2", ""),
|
| 968 |
+
data.get("informational_3", ""),
|
| 969 |
+
data.get("informational_4", ""),
|
| 970 |
+
data.get("informational_5", ""),
|
| 971 |
+
|
| 972 |
# سوالات manipulation
|
| 973 |
data.get ("trust", ""),
|
| 974 |
data.get("pricing_method", ""),
|
|
|
|
| 984 |
st.error(f"خطا در ذخیرهسازی: {str(e)}")
|
| 985 |
return False
|
| 986 |
|
| 987 |
+
def create_likert_question(question, key, scale_type="5point"):
|
| 988 |
+
"""نمایش سوال لیکرت با اسلایدر نقطهای"""
|
| 989 |
+
# تعریف لیبلهای دو طرف
|
| 990 |
+
left_label = "کاملاً مخالفم" if scale_type == "7point" else "کاملاً مخالفم"
|
| 991 |
+
right_label = "کاملاً موافقم" if scale_type == "7point" else "کاملاً موافقم"
|
| 992 |
+
|
| 993 |
+
st.markdown(f"<p style='font-size:16px; margin-bottom:5px;'>{question}</p>", unsafe_allow_html=True)
|
| 994 |
+
|
| 995 |
+
# ایجاد اسلایدر
|
| 996 |
+
max_value = 7 if scale_type == "7point" else 5
|
| 997 |
+
value = st.slider(
|
| 998 |
+
"",
|
| 999 |
+
min_value=1,
|
| 1000 |
+
max_value=max_value,
|
| 1001 |
+
value=(max_value+1)//2, # مقدار میانی
|
| 1002 |
+
step=1,
|
| 1003 |
+
key=f"slider_{key}"
|
| 1004 |
+
)
|
| 1005 |
+
|
| 1006 |
+
# نمایش متن دو طرف اسلایدر
|
| 1007 |
+
st.markdown(
|
| 1008 |
+
f"""
|
| 1009 |
+
<div class="slider-labels">
|
| 1010 |
+
<span>{left_label}</span>
|
| 1011 |
+
<span>{right_label}</span>
|
| 1012 |
+
</div>
|
| 1013 |
+
<p style='text-align:center; color:#6a0dad; font-weight:bold;'>
|
| 1014 |
+
پاسخ شما: {value}
|
| 1015 |
+
</p>
|
| 1016 |
+
""",
|
| 1017 |
+
unsafe_allow_html=True
|
| 1018 |
+
)
|
| 1019 |
+
return value
|
| 1020 |
+
|
| 1021 |
# ========== بخشهای فرم ==========
|
| 1022 |
|
| 1023 |
def welcome_page():
|
| 1024 |
"""صفحه خوشامدگویی"""
|
| 1025 |
+
st.markdown("""
|
| 1026 |
+
<div style="display: flex; flex-direction: column; align-items: center; background-color: #f0f2f6; border-radius: 10px; padding: 20px; gap: 15px;">
|
| 1027 |
+
<div style="text-align: center;">
|
| 1028 |
+
<img src="https://huggingface.co/spaces/maryamilka/surge-pricing/resolve/main/shariflogo.png"
|
| 1029 |
+
alt="لوگو دانشگاه شریف"
|
| 1030 |
+
style="width: 200px; height: auto; margin-bottom: 10px;">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1031 |
</div>
|
| 1032 |
<div style="flex: 1;">
|
| 1033 |
+
<h3>به پرسشنامه ما خوش آمدید 🌟</h3>
|
| 1034 |
+
<p>با سلام و احترام،</p>
|
| 1035 |
+
<p>از شما دعوت میشود در یک پژوهش دانشگاهی شرکت کنید که در قالب پایاننامه کارشناسیارشد در دانشگاه صنعتی شریف انجام میشود. این تحقیق به بررسی ادراک مصرفکنندگان از انصاف در قیمتگذاریِ اپلیکیشنهای تاکسی اینترنتی (مانند اسنپ و تپسی 🚖) میپردازد.</p>
|
| 1036 |
+
<p>شرکت در این مطالعه کاملاً داوطلبانه است؛ پاسخ دقیقی برای سوالات وجود ندارد و پاسخهای صادقانه شما فقط برای پیشبرد اهداف علمی تحلیل خواهد شد.</p>
|
| 1037 |
+
<p>پر کردن این پرسشنامه کمتر از 5 دقیقه وقت شما را میگیرد و پاسخهای ارزشمند شما کمک شایانی به ارتقای دانش علمی خواهد کرد. پیشاپیش از مشارکت شما صمیمانه سپاسگزاریم 🙏</p>
|
| 1038 |
+
<p>برای آغاز پرسشنامه، لطفاً روی دکمه زیر کلیک کنید 👇🏻</p>
|
| 1039 |
</div>
|
| 1040 |
</div>
|
| 1041 |
+
</div>
|
| 1042 |
""", unsafe_allow_html=True)
|
| 1043 |
|
| 1044 |
if st.button("شروع پرسشنامه", key="start_btn", type="primary"):
|
|
|
|
| 1049 |
|
| 1050 |
def scenario_explanation():
|
| 1051 |
"""توضیح سناریو"""
|
| 1052 |
+
col1, col2 = st.columns([2, 4]) # افزایش نسبت ستون اول
|
| 1053 |
with col1:
|
| 1054 |
+
st.markdown('<div style="padding-right: 25px;">', unsafe_allow_html=True)
|
| 1055 |
try:
|
| 1056 |
st.image("rahyar.png", width=80)
|
| 1057 |
except:
|
| 1058 |
st.image("https://via.placeholder.com/80/6a0dad/FFFFFF?text=LOGO", width=80)
|
| 1059 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 1060 |
with col2:
|
| 1061 |
st.markdown("""
|
| 1062 |
<h2 class="rahyar-title">رهیار 🚖</h2>
|
|
|
|
| 1067 |
|
| 1068 |
st.markdown("""
|
| 1069 |
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 10px;">
|
| 1070 |
+
<p>فرض کنید در روزی از روزها شما مهمان یکی از اقوامتان در جنوب تهران هستید. قصد دارید برای خریدی به غرب تهران بروید...</p>
|
| 1071 |
+
<p> گوشیتان را از کیف درمیآورید. چشمتان به آیکون جدیدی میافتد؛ اپلیکیشنی به نام <strong>رهیار</strong> — نه اسنپ است و نه تپسی، اما خیلی شبیه آنهاست. رنگ بنفش جذابی دارد و شعارش توی ذهنتان مینشیند:
|
| 1072 |
+
<br>«همراه سفرهای شما، راهی مطمئن، راهی روشن، رهیار»</p>
|
| 1073 |
+
<p>با کنجکاوی اپ را باز میکنید. ظاهر ساده و روانی دارد. فعلاً فقط گزینهی «سفر معمولی» فعال است. خبری از امکانات اضافه مثل «سفر دومسیـره»، «توقف در مسیر»، «اکوپلاس» یا «موتورسوار» نیست — اما خب، رهیار تازهکار است و قرار است توسعه پیدا کند!.</p>
|
| 1074 |
+
<p>مبدأ و مقصد را انتخاب میکنید و با قیمت مواجه میشوید.</p>
|
| 1075 |
+
<p>با کلیک روی «ادامه»، اطلاعات سفر را مشاهده کنید 👇🏻</p>
|
| 1076 |
</div>
|
| 1077 |
""", unsafe_allow_html=True)
|
| 1078 |
|
|
|
|
| 1081 |
st.rerun()
|
| 1082 |
|
| 1083 |
def map_view():
|
| 1084 |
+
col1, col2 = st.columns([2, 4]) # افزایش نسبت ستون اول
|
|
|
|
| 1085 |
with col1:
|
| 1086 |
+
st.markdown('<div style="padding-right: 25px;">', unsafe_allow_html=True)
|
| 1087 |
try:
|
| 1088 |
st.image("rahyar.png", width=80)
|
| 1089 |
except:
|
| 1090 |
st.image("https://via.placeholder.com/80/6a0dad/FFFFFF?text=LOGO", width=80)
|
| 1091 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
| 1092 |
with col2:
|
| 1093 |
st.markdown("""
|
| 1094 |
+
<h2 class="rahyar-title">رهیار 🚖</h2>
|
| 1095 |
+
<p class="rahyar-subtitle">همراه سفرهای درونشهری شما، راهی مطمئن، راهی روشن، رهیار</p>
|
| 1096 |
""", unsafe_allow_html=True)
|
| 1097 |
|
| 1098 |
st.markdown("""
|
| 1099 |
<div style="background-color: #f8f9fa; padding: 15px; border-radius: 10px;">
|
| 1100 |
<p>مسیر سفر شما به صورت حدودی، از جنوب به غرب تهران است.</p>
|
|
|
|
|
|
|
| 1101 |
<p> با توجه به اطلاعاتی که بعد از نقشه دریافت میکنید، تصمیم بگیرید که سفر را میپذیرید را رد میکنید.</p>
|
| 1102 |
<p>سپس با کلیک بر دکمه مربوطه به بخش بعدی بروید.</p>
|
| 1103 |
</div>
|
|
|
|
| 1121 |
# دکمهها
|
| 1122 |
col1, col2 = st.columns(2)
|
| 1123 |
with col1:
|
| 1124 |
+
if st.button("درخواست سفر", key="accept_btn", use_container_width=True):
|
| 1125 |
st.session_state.price_accepted = 1
|
| 1126 |
st.session_state.current_page = "attention_check1"
|
| 1127 |
st.rerun()
|
| 1128 |
|
| 1129 |
with col2:
|
| 1130 |
+
if st.button("رد سفر", key="reject_btn", use_container_width=True):
|
| 1131 |
st.session_state.price_accepted = 0
|
| 1132 |
st.session_state.current_page = "attention_check1"
|
| 1133 |
st.rerun()
|
|
|
|
| 1147 |
st.markdown("### سوال")
|
| 1148 |
|
| 1149 |
answer = st.radio(
|
| 1150 |
+
"رنگ لوگو و تم رنگی اپلیکیشن رهیار چگونه بود؟",
|
| 1151 |
["قرمز", "سبز", "بنفش", "آبی", "فراموش کردم"],
|
| 1152 |
index=None,
|
| 1153 |
key="att1_radio"
|
|
|
|
| 1193 |
else:
|
| 1194 |
st.warning("لطفاً یک گزینه را انتخاب کنید")
|
| 1195 |
|
| 1196 |
+
def random_likert_questions():
|
| 1197 |
+
"""نمایش سوالات لیکرت به ترتیب مشخص با اسلایدر سفارشی"""
|
| 1198 |
+
# تعریف گروههای سوالات با لیبلهای سفارشی
|
| 1199 |
+
question_groups = [
|
| 1200 |
+
{
|
| 1201 |
+
"title": "عدالت توزیعی",
|
| 1202 |
+
"key": "distributive",
|
| 1203 |
+
"questions": [
|
| 1204 |
+
{
|
| 1205 |
+
"key": "distributive_1",
|
| 1206 |
+
"question": "قیمتی که به شما ارائه شد، چگونه بود؟",
|
| 1207 |
+
"scale": 7,
|
| 1208 |
+
"labels": ["کاملاً نامنصفانه", "کاملاً منصفانه"] # لیبلهای سفارشی برای این سوال
|
| 1209 |
+
},
|
| 1210 |
+
{
|
| 1211 |
+
"key": "distributive_2",
|
| 1212 |
+
"question": "قیمتی که به شما ارائه شد، چگونه بود؟",
|
| 1213 |
+
"scale": 7,
|
| 1214 |
+
"labels": ["کاملاً غیرمعقول", "کاملاً معقول"]
|
| 1215 |
+
},
|
| 1216 |
+
{
|
| 1217 |
+
"key": "distributive_3",
|
| 1218 |
+
"question": "قیمتی که به شما ارائه شد، چگونه بود؟",
|
| 1219 |
+
"scale": 7,
|
| 1220 |
+
"labels": ["کاملاً غیرقابل قبول", "کاملاً قابل قبول"]
|
| 1221 |
+
}
|
| 1222 |
+
]
|
| 1223 |
+
},
|
| 1224 |
+
{
|
| 1225 |
+
"title": "سوال توجه",
|
| 1226 |
+
"key": "attention_check",
|
| 1227 |
+
"questions": [
|
| 1228 |
+
{"key": "attention_check2", "question": "تا چه مقدار با دقت به سوالات پاسخ میدهید؟", "scale": 7,"labels": ["خیلی کم", "خیلی زیاد"]}
|
| 1229 |
+
]
|
| 1230 |
+
},
|
| 1231 |
+
{
|
| 1232 |
+
"title": "عدالت رویهای",
|
| 1233 |
+
"key": "procedural",
|
| 1234 |
+
"questions": [
|
| 1235 |
+
{"key": "procedural_1", "question": ".فرآیند و رویه قیمتگذاری پلتفرم قابل قبول است", "scale": 7, "labels": ["کاملاً مخالفم", "کاملاً موافقم"]},
|
| 1236 |
+
{"key": "procedural_2", "question": ".فرآیند و رویه قیمتگذاری پلتفرم منصفانه است", "scale": 7, "labels": ["کاملاً مخالفم", "کاملاً موافقم"]},
|
| 1237 |
+
{"key": "procedural_3", "question": ".فرآیند و رویه قیمتگذاری پلتفرم معقول است", "scale": 7, "labels": ["کاملاً مخالفم", "کاملاً موافقم"]}
|
| 1238 |
+
]
|
| 1239 |
+
},
|
| 1240 |
+
{
|
| 1241 |
+
"title": "عدالت اطلاعاتی",
|
| 1242 |
+
"key": "informational",
|
| 1243 |
+
"questions": [
|
| 1244 |
+
{"key": "informational_1", "question": "تا چه حد رهیار دلایل تعیین قیمت را به صورت صادقانه توضیح داد؟", "scale": 7, "labels": ["هیچ", "خیلی زیاد"]},
|
| 1245 |
+
{"key": "informational_2", "question": "تا چه حد رهیار عوامل مؤثر بر تعیین قیمت را به طور کامل شرح داد؟", "scale": 7, "labels": ["هیچ", "خیلی زیاد"]},
|
| 1246 |
+
{"key": "informational_3", "question": "تا چه حد دلایل ارائهشده توسط رهیار برای تعیین قیمت منطقی و قابل قبول بود؟", "scale": 7, "labels": ["هیچ", "خیلی زیاد"]},
|
| 1247 |
+
{"key": "informational_4", "question": "تا چه حد توضیحات درباره تعیین قیمت بلافاصله و در زمان مناسب نمایش داده شد؟", "scale": 7, "labels": ["هیچ", "خیلی زیاد"]},
|
| 1248 |
+
{"key": "informational_5", "question": "تا چه حد توضیحات رهیار درباره تعیین قیمت، متناسب با شرایط سفر شما بود؟", "scale": 7, "labels": ["هیچ", "خیلی زیاد"]}
|
| 1249 |
+
]
|
| 1250 |
+
}
|
| 1251 |
+
]
|
| 1252 |
|
| 1253 |
|
| 1254 |
+
# مقداردهی اولیه
|
| 1255 |
+
if 'current_likert_group' not in st.session_state:
|
| 1256 |
+
st.session_state.current_likert_group = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1257 |
|
| 1258 |
+
# دریافت گروه جاری
|
| 1259 |
+
current_group = question_groups[st.session_state.current_likert_group]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1260 |
|
| 1261 |
+
# نمایش عنوان گروه
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1262 |
st.markdown("""
|
| 1263 |
<style>
|
| 1264 |
+
.guide-text p {
|
| 1265 |
+
font-size: 14px !important;
|
|
|
|
| 1266 |
}
|
| 1267 |
</style>
|
| 1268 |
""", unsafe_allow_html=True)
|
| 1269 |
+
st.markdown("""
|
| 1270 |
+
<div class="guide-text" style="display: flex; flex-direction: column; align-items: center; background-color: #f0f2f6; border-radius: 10px; padding: 20px; gap: 15px;">
|
| 1271 |
+
<div style="flex: 1;">
|
| 1272 |
+
<h3>راهنمای پاسخ:</h3>
|
| 1273 |
+
<p>برای پاسخ به سوالات، با یک طیف کشویی مواجه خواهید شد.</p>
|
| 1274 |
+
<p>در این طیف 7 نقطه وجود دارد:
|
| 1275 |
+
<br>- سمت چپ: کمترین مقد��ر
|
| 1276 |
+
<br>- سمت راست: بیشترین مقدار
|
| 1277 |
+
</p>
|
| 1278 |
+
<p>نقطه روی کشو را جابجا کنید و در مقداری که پاسخ شماست متوقف شوید.</p>
|
| 1279 |
+
<p>پاسخ شما زیر طیف نمایش داده خواهد شد.</p>
|
| 1280 |
+
<p>اگر از پاسخهایتان مطمئن هستید، روی دکمه «ادامه» کلیک کنید.</p>
|
| 1281 |
+
</div>
|
| 1282 |
+
</div>
|
| 1283 |
+
""", unsafe_allow_html=True)
|
| 1284 |
+
|
| 1285 |
+
# نمایش تمام سوالات این گروه در یک صفحه
|
| 1286 |
+
for question in current_group['questions']:
|
| 1287 |
+
answer = custom_likert_slider(question)
|
| 1288 |
+
st.session_state.answers[question["key"]] = answer
|
| 1289 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1290 |
|
| 1291 |
+
# دکمه ادامه/اتمام
|
| 1292 |
+
button_label = "ادامه به گروه بعدی" if st.session_state.current_likert_group < len(question_groups)-1 else "اتمام پرسشنامه"
|
| 1293 |
+
|
| 1294 |
+
if st.button(button_label):
|
| 1295 |
+
# رفتن به گروه بعدی یا صفحه پایانی
|
| 1296 |
+
if st.session_state.current_likert_group < len(question_groups) - 1:
|
| 1297 |
+
st.session_state.current_likert_group += 1
|
| 1298 |
+
st.rerun()
|
| 1299 |
+
else:
|
| 1300 |
+
st.session_state.current_page = "explanation_questions"
|
| 1301 |
+
st.rerun()
|
| 1302 |
+
|
| 1303 |
+
def explanation_questions():
|
| 1304 |
+
"""نمایش سوالات تکمیلی به صورت مرحلهای با دکمه ادامه"""
|
| 1305 |
+
st.markdown("### 📋 سوالات تکمیلی")
|
| 1306 |
+
|
| 1307 |
+
# لیست سوالات به ترتیب نمایش
|
| 1308 |
+
questions = [
|
| 1309 |
+
{
|
| 1310 |
+
"key": "trust",
|
| 1311 |
+
"label": "آیا شما به تصمیمگیریهایی که توسط هوش مصنوعی انجام میشود اعتماد دارید؟",
|
| 1312 |
+
"options": ["بله", "خیر", "نظری ندارم"],
|
| 1313 |
+
"required": True
|
| 1314 |
+
},
|
| 1315 |
+
{
|
| 1316 |
+
"key": "pricing_method",
|
| 1317 |
+
"label": "به نظر شما پلتفرم قیمت را چگونه تعیین میکند؟",
|
| 1318 |
+
"options": [
|
| 1319 |
"به صورت دستی توسط تیم پلتفرم",
|
| 1320 |
"به صورت خودکار توسط هوش مصنوعی و الگوریتمها",
|
| 1321 |
"ترکیبی از هر دو روش",
|
| 1322 |
"نظری ندارم"
|
| 1323 |
],
|
| 1324 |
+
"required": True
|
| 1325 |
+
},
|
| 1326 |
+
{
|
| 1327 |
+
"key": "price_increase",
|
| 1328 |
+
"label": "آیا به نظر شما در این سفر افزایش قیمت نسبت به حالت طبیعی وجود داشته است؟",
|
| 1329 |
+
"options": ["بله", "خیر", "مطمئن نیستم"],
|
| 1330 |
+
"required": True
|
| 1331 |
+
},
|
| 1332 |
+
{
|
| 1333 |
+
"key": "explanation_received",
|
| 1334 |
+
"label": "آیا برای قیمت پیشنهادی این سفر، توضیحی به شما ارائه شد؟",
|
| 1335 |
+
"options": ["بله", "خیر"],
|
| 1336 |
+
"required": True
|
| 1337 |
+
},
|
| 1338 |
+
{
|
| 1339 |
+
"key": "explanation_type",
|
| 1340 |
+
"label": "اگر توضیحی دریافت کردید، این توضیح بیشتر به کدام مورد شباهت داشت؟",
|
| 1341 |
+
"options": [
|
| 1342 |
+
"بر اساس عواملی که در قیمتگذاری لحاظ شدهاند",
|
| 1343 |
+
"شامل سناریوهای جایگزین که میتوانستند قیمت متفاوتی ایجاد کنند",
|
| 1344 |
+
"توضیحی دریافت نکردم"
|
| 1345 |
+
],
|
| 1346 |
+
"required": False,
|
| 1347 |
+
"condition": lambda: st.session_state.get("explanation_received") == "بله"
|
| 1348 |
+
}
|
| 1349 |
+
]
|
| 1350 |
|
| 1351 |
+
# مقداردهی اولیه step اگر وجود ندارد
|
| 1352 |
+
if "explanation_step" not in st.session_state:
|
| 1353 |
+
st.session_state.explanation_step = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1354 |
|
| 1355 |
+
# اگر همه سوالات پاسخ داده شدهاند، به صفحه بعدی برو
|
| 1356 |
+
if st.session_state.explanation_step >= len(questions):
|
| 1357 |
+
st.session_state.current_page = "demographic"
|
| 1358 |
+
st.rerun()
|
| 1359 |
+
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1360 |
|
| 1361 |
+
# دریافت سوال جاری
|
| 1362 |
+
current_q = questions[st.session_state.explanation_step]
|
| 1363 |
+
|
| 1364 |
+
# بررسی شرط نمایش برای سوالات اختیاری
|
| 1365 |
+
if "condition" in current_q and not current_q["condition"]():
|
| 1366 |
+
st.session_state[current_q["key"]] = "N/A"
|
| 1367 |
+
st.session_state.explanation_step += 1
|
| 1368 |
+
st.rerun()
|
| 1369 |
+
return
|
| 1370 |
+
|
| 1371 |
+
# نمایش سوال جاری
|
| 1372 |
+
answer = st.radio(
|
| 1373 |
+
current_q["label"],
|
| 1374 |
+
current_q["options"],
|
| 1375 |
+
index=None,
|
| 1376 |
+
key=f"explanation_q_{current_q['key']}"
|
| 1377 |
+
)
|
| 1378 |
+
|
| 1379 |
+
# دکمه ادامه
|
| 1380 |
+
if st.button("ادامه", key=f"continue_{current_q['key']}"):
|
| 1381 |
+
if answer is None and current_q["required"]:
|
| 1382 |
+
st.warning("لطفاً یک گزینه را انتخاب کنید")
|
| 1383 |
+
else:
|
| 1384 |
+
# ذخیره پاسخ
|
| 1385 |
+
st.session_state[current_q["key"]] = answer if answer is not None else "N/A"
|
| 1386 |
+
|
| 1387 |
+
# افزایش شماره مرحله
|
| 1388 |
+
st.session_state.explanation_step += 1
|
| 1389 |
+
|
| 1390 |
+
# رفرش صفحه برای نمایش سوال بعدی
|
| 1391 |
+
st.rerun()
|
| 1392 |
|
| 1393 |
def demographic_form():
|
| 1394 |
"""فرم اطلاعات دموگرافیک"""
|
|
|
|
| 1401 |
""", unsafe_allow_html=True)
|
| 1402 |
|
| 1403 |
with st.form("demographic_form"):
|
| 1404 |
+
age = st.number_input("سن", min_value=18, max_value=100, value=None, placeholder="سن خود را وارد کنید")
|
| 1405 |
+
gender = st.selectbox("جنسیت", ["", "مرد", "زن", "سایر"], index=0)
|
| 1406 |
+
education = st.selectbox("تحصیلات", ["", "دیپلم", "لیسانس", "فوق لیسانس", "دکترا"], index=0)
|
| 1407 |
+
city = st.selectbox("لطفاً استان محل سکونت خود را انتخاب بفرمایید.",
|
| 1408 |
+
["", "آذربایجان شرقی", "آذربایجان غربی", "اردبیل", "اصفهان", "البرز", "ایلام",
|
| 1409 |
+
"بوشهر", "تهران", "چهارمحال و بختیاری", "خراسان جنوبی", "خراسان رضوی", "خراسان شمالی",
|
| 1410 |
+
"خوزستان", "زنجان", "سمنان", "سیستان و بلوچستان", "فارس", "قزوین", "قم", "کردستان",
|
| 1411 |
+
"کرمان", "کرمانشاه", "کهگیلویه و بویراحمد", "گلستان", "گیلان", "لرستان", "مازندران",
|
| 1412 |
+
"مرکزی", "هرمزگان", "همدان", "یزد"], index=0)
|
| 1413 |
+
related_education_job = st.selectbox("رشته تحصیلی/شغل شما در کدامیک از دستههای زیر قرار دارد؟",
|
| 1414 |
+
["", "مهندسی", "درمانی", "فرهنگی", "مدیریتی (مالی)",
|
| 1415 |
+
"مدیریتی (بازاریابی)", "مدیریتی (سایر)", "روانشناسی",
|
| 1416 |
+
"اقتصادی", "حقوقی", "هنری", "ورزشی", "زبان", "غیره"], index=0)
|
| 1417 |
ride_frequency = st.selectbox("دفعات استفاده از سرویسهای اشتراک سفر در ماه",
|
| 1418 |
+
["", "هیچوقت", "کمتر از 5 بار", "5-10 بار", "بیش از 10 بار"], index=0)
|
| 1419 |
+
|
| 1420 |
+
submitted = st.form_submit_button("ادامه")
|
| 1421 |
+
if submitted:
|
| 1422 |
+
if not all([age, gender, education, city, related_education_job, ride_frequency]):
|
| 1423 |
+
st.error("لطفاً تمام فیلدها را پر کنید")
|
| 1424 |
+
else:
|
| 1425 |
+
st.session_state.demographic_data = {
|
| 1426 |
+
"age": age,
|
| 1427 |
+
"gender": gender,
|
| 1428 |
+
"education": education,
|
| 1429 |
+
"city": city,
|
| 1430 |
+
"ride_frequency": ride_frequency,
|
| 1431 |
+
"related_education_job": related_education_job
|
| 1432 |
+
}
|
| 1433 |
+
st.session_state.current_page = "contact"
|
| 1434 |
+
st.rerun()
|
| 1435 |
|
| 1436 |
def user_contact():
|
| 1437 |
"""راه ارتباطی ساده"""
|
|
|
|
| 1462 |
"price": st.session_state.price,
|
| 1463 |
"user_contact": st.session_state.get("user_contact", ""),
|
| 1464 |
"price_accepted": st.session_state.get("price_accepted", 0),
|
| 1465 |
+
"attention_check1": st.session_state.get("attention_check1", None),
|
|
|
|
| 1466 |
"trust": st.session_state.trust,
|
| 1467 |
"pricing_method": st.session_state.pricing_method,
|
| 1468 |
"price_increase": st.session_state.price_increase,
|
| 1469 |
"explanation_received": st.session_state.explanation_received,
|
| 1470 |
+
"explanation_type": st.session_state.get("explanation_type", "N/A"),
|
| 1471 |
+
**st.session_state.demographic_data,
|
| 1472 |
+
**st.session_state.answers # اضافه کردن تمام پاسخهای لیکرت
|
| 1473 |
}
|
| 1474 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1475 |
if save_to_sheet(save_data):
|
| 1476 |
st.session_state.current_page = "thank_you"
|
| 1477 |
st.rerun()
|
|
|
|
| 1498 |
st.session_state.is_desktop = "mobile" not in user_agent.lower()
|
| 1499 |
|
| 1500 |
|
|
|
|
| 1501 |
if st.session_state.is_desktop:
|
| 1502 |
# اطمینان از نمایش همان حالت موبایل برای همه دستگاهها
|
| 1503 |
st.session_state.is_desktop = False
|
| 1504 |
+
|
| 1505 |
+
if 'answers' not in st.session_state:
|
| 1506 |
+
st.session_state.answers = {}
|
| 1507 |
|
| 1508 |
if 'current_page' not in st.session_state:
|
| 1509 |
st.session_state.current_page = "welcome"
|
|
|
|
| 1512 |
st.session_state.user_contact = None
|
| 1513 |
st.session_state.demographic_data = None
|
| 1514 |
st.session_state.price_accepted = 0
|
| 1515 |
+
st.session_state.attention_check1 = None
|
| 1516 |
|
| 1517 |
pages = {
|
| 1518 |
"welcome": welcome_page,
|