Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -736,55 +736,103 @@ h3 {
|
|
| 736 |
}
|
| 737 |
</style>
|
| 738 |
""", unsafe_allow_html=True)
|
|
|
|
|
|
|
| 739 |
def custom_likert_slider(question_data):
|
| 740 |
-
"""نمایش سوال لیکرت با اسلایدر
|
| 741 |
question = question_data["question"]
|
| 742 |
key = question_data["key"]
|
| 743 |
points = question_data["scale"]
|
| 744 |
labels = question_data.get("labels", ["کمترین", "بیشترین"])
|
| 745 |
|
| 746 |
-
# مقدار پیشفرض
|
| 747 |
-
default_value =
|
|
|
|
| 748 |
|
| 749 |
-
#
|
| 750 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 751 |
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
|
|
|
|
| 762 |
|
| 763 |
-
#
|
| 764 |
-
|
| 765 |
|
| 766 |
-
#
|
| 767 |
-
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 771 |
|
| 772 |
-
# نمایش لی
|
| 773 |
-
st.
|
| 774 |
-
|
| 775 |
-
|
| 776 |
-
<span>{labels[1]}</span>
|
| 777 |
-
<span>{labels[0]}</span>
|
| 778 |
-
</div>
|
| 779 |
-
<p style='text-align:center; direction:rtl;'>
|
| 780 |
-
پاسخ انتخابشده: <strong>{value}</strong>
|
| 781 |
-
</p>
|
| 782 |
-
""",
|
| 783 |
-
unsafe_allow_html=True
|
| 784 |
-
)
|
| 785 |
|
| 786 |
-
return value
|
| 787 |
-
|
| 788 |
def create_ride_map():
|
| 789 |
"""ایجاد نقشه سفر با Folium - نسخه اصلاح شده با مناطق عمومی"""
|
| 790 |
# نقاط تقریبی برای مناطق عمومی جنوب و غرب تهران
|
|
@@ -1194,7 +1242,8 @@ def attention_check1():
|
|
| 1194 |
st.warning("لطفاً یک گزینه را انتخاب کنید")
|
| 1195 |
|
| 1196 |
def random_likert_questions():
|
| 1197 |
-
"""نمایش سوالات لیکرت به ترتیب مشخص"""
|
|
|
|
| 1198 |
question_groups = [
|
| 1199 |
{
|
| 1200 |
"title": "عدالت توزیعی",
|
|
@@ -1204,7 +1253,7 @@ def random_likert_questions():
|
|
| 1204 |
"key": "distributive_1",
|
| 1205 |
"question": "قیمتی که به شما ارائه شد، چگونه بود؟",
|
| 1206 |
"scale": 7,
|
| 1207 |
-
"labels": ["کاملاً نامنصفانه", "کاملاً منصفانه"]
|
| 1208 |
},
|
| 1209 |
{
|
| 1210 |
"key": "distributive_2",
|
|
@@ -1224,7 +1273,7 @@ def random_likert_questions():
|
|
| 1224 |
"title": "سوال توجه",
|
| 1225 |
"key": "attention_check",
|
| 1226 |
"questions": [
|
| 1227 |
-
{"key": "attention_check2", "question": "تا چه مقدار با دقت به سوالات پاسخ میدهید؟", "scale": 7,
|
| 1228 |
]
|
| 1229 |
},
|
| 1230 |
{
|
|
@@ -1249,6 +1298,7 @@ def random_likert_questions():
|
|
| 1249 |
}
|
| 1250 |
]
|
| 1251 |
|
|
|
|
| 1252 |
# مقداردهی اولیه
|
| 1253 |
if 'current_likert_group' not in st.session_state:
|
| 1254 |
st.session_state.current_likert_group = 0
|
|
@@ -1256,30 +1306,48 @@ def random_likert_questions():
|
|
| 1256 |
# دریافت گروه جاری
|
| 1257 |
current_group = question_groups[st.session_state.current_likert_group]
|
| 1258 |
|
| 1259 |
-
# نمایش
|
| 1260 |
st.markdown("""
|
| 1261 |
-
<
|
| 1262 |
-
|
| 1263 |
-
|
| 1264 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1265 |
</div>
|
| 1266 |
""", unsafe_allow_html=True)
|
| 1267 |
|
| 1268 |
-
# نمایش تمام سوالات گروه
|
| 1269 |
for question in current_group['questions']:
|
| 1270 |
answer = custom_likert_slider(question)
|
|
|
|
|
|
|
| 1271 |
|
| 1272 |
# دکمه ادامه/اتمام
|
| 1273 |
-
|
| 1274 |
-
|
| 1275 |
-
|
| 1276 |
-
|
| 1277 |
-
|
| 1278 |
-
|
| 1279 |
-
else:
|
| 1280 |
-
st.session_state.current_page = "explanation_questions"
|
| 1281 |
st.rerun()
|
| 1282 |
-
|
|
|
|
|
|
|
|
|
|
| 1283 |
def explanation_questions():
|
| 1284 |
"""نمایش سوالات تکمیلی به صورت مرحلهای با دکمه ادامه"""
|
| 1285 |
st.markdown("### 📋 سوالات تکمیلی")
|
|
@@ -1369,7 +1437,7 @@ def explanation_questions():
|
|
| 1369 |
|
| 1370 |
# رفرش صفحه برای نمایش سوال بعدی
|
| 1371 |
st.rerun()
|
| 1372 |
-
|
| 1373 |
def demographic_form():
|
| 1374 |
"""فرم اطلاعات دموگرافیک"""
|
| 1375 |
st.markdown("### 📝 اطلاعات دموگرافیک")
|
|
@@ -1477,7 +1545,11 @@ def main():
|
|
| 1477 |
user_agent = st.query_params.get("user_agent", [""])[0]
|
| 1478 |
st.session_state.is_desktop = "mobile" not in user_agent.lower()
|
| 1479 |
|
| 1480 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1481 |
if 'answers' not in st.session_state:
|
| 1482 |
st.session_state.answers = {}
|
| 1483 |
|
|
@@ -1490,7 +1562,6 @@ def main():
|
|
| 1490 |
st.session_state.price_accepted = 0
|
| 1491 |
st.session_state.attention_check1 = None
|
| 1492 |
|
| 1493 |
-
# تعریف صفحات
|
| 1494 |
pages = {
|
| 1495 |
"welcome": welcome_page,
|
| 1496 |
"scenario_explanation": scenario_explanation,
|
|
@@ -1498,12 +1569,11 @@ def main():
|
|
| 1498 |
"attention_check1": attention_check1,
|
| 1499 |
"random_likert_questions": random_likert_questions,
|
| 1500 |
"explanation_questions": explanation_questions,
|
| 1501 |
-
"demographic": demographic_form,
|
| 1502 |
-
"contact": user_contact,
|
| 1503 |
"thank_you": thank_you_page
|
| 1504 |
}
|
| 1505 |
|
| 1506 |
-
# نمایش صفحه جاری
|
| 1507 |
pages[st.session_state.current_page]()
|
| 1508 |
|
| 1509 |
if __name__ == "__main__":
|
|
|
|
| 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 = (points + 1) // 2
|
| 750 |
+
current_value = st.session_state.get(key, default_value)
|
| 751 |
|
| 752 |
+
# HTML و JavaScript برای اسلایدر
|
| 753 |
+
html = f"""
|
| 754 |
+
<div id="container_{key}" style="direction: ltr; font-family: 'B Nazanin'; margin-bottom: 30px;">
|
| 755 |
+
<label style="font-size: 16px; font-weight: bold; display: block; text-align: right;">{question}</label>
|
| 756 |
+
<div style="display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 5px;">
|
| 757 |
+
<span>{labels[0]}</span>
|
| 758 |
+
<span>{labels[1]}</span>
|
| 759 |
+
</div>
|
| 760 |
+
<input type="range" id="{key}" min="1" max="{points}" step="1" value="{current_value}"
|
| 761 |
+
style="width: 100%; height: 10px; accent-color: #6a0dad; margin-bottom: 15px;"
|
| 762 |
+
oninput="updateSlider('{key}')"
|
| 763 |
+
onchange="updateSlider('{key}')">
|
| 764 |
+
<div style="text-align: center; margin-top: 10px; direction: rtl;">
|
| 765 |
+
پاسخ انتخابشده: <strong><span id="output_{key}">{current_value}</span></strong>
|
| 766 |
+
</div>
|
| 767 |
+
</div>
|
| 768 |
+
|
| 769 |
+
<script>
|
| 770 |
+
function updateSlider(key) {{
|
| 771 |
+
const slider = document.getElementById(key);
|
| 772 |
+
const value = parseInt(slider.value);
|
| 773 |
+
document.getElementById('output_' + key).innerText = value;
|
| 774 |
+
|
| 775 |
+
// ذخیره مقدار در یک متغیر مخفی برای دسترسی Streamlit
|
| 776 |
+
window.sliderValues = window.sliderValues || {{}};
|
| 777 |
+
window.sliderValues[key] = value;
|
| 778 |
+
|
| 779 |
+
// ارسال مقدار به Streamlit
|
| 780 |
+
try {{
|
| 781 |
+
window.parent.postMessage({{
|
| 782 |
+
type: 'streamlit:setComponentValue',
|
| 783 |
+
key: key,
|
| 784 |
+
value: value
|
| 785 |
+
}}, '*');
|
| 786 |
+
}} catch (e) {{
|
| 787 |
+
console.error('Error sending message to Streamlit:', e);
|
| 788 |
+
}}
|
| 789 |
+
|
| 790 |
+
// ذخیره مقدار در localStorage برای اطمینان
|
| 791 |
+
localStorage.setItem(key, value);
|
| 792 |
+
}}
|
| 793 |
|
| 794 |
+
// بازیابی مقدار اولیه از localStorage اگر وجود داشته باشد
|
| 795 |
+
window.addEventListener('load', () => {{
|
| 796 |
+
const key = '{key}';
|
| 797 |
+
const savedValue = localStorage.getItem(key);
|
| 798 |
+
if (savedValue) {{
|
| 799 |
+
document.getElementById(key).value = savedValue;
|
| 800 |
+
document.getElementById('output_' + key).innerText = savedValue;
|
| 801 |
+
}}
|
| 802 |
+
});
|
| 803 |
+
</script>
|
| 804 |
+
"""
|
| 805 |
|
| 806 |
+
# رندر کامپوننت HTML
|
| 807 |
+
components.html(html, height=150)
|
| 808 |
|
| 809 |
+
# خواندن مقدار از localStorage یا session_state
|
| 810 |
+
if key in st.session_state:
|
| 811 |
+
current_value = st.session_state[key]
|
| 812 |
+
else:
|
| 813 |
+
# تلاش برای خواندن از localStorage از طریق JavaScript
|
| 814 |
+
st.markdown(
|
| 815 |
+
f"""
|
| 816 |
+
<script>
|
| 817 |
+
const savedValue = localStorage.getItem('{key}');
|
| 818 |
+
if (savedValue) {{
|
| 819 |
+
window.parent.postMessage({{
|
| 820 |
+
type: 'streamlit:setComponentValue',
|
| 821 |
+
key: '{key}',
|
| 822 |
+
value: parseInt(savedValue)
|
| 823 |
+
}}, '*');
|
| 824 |
+
}}
|
| 825 |
+
</script>
|
| 826 |
+
""",
|
| 827 |
+
unsafe_allow_html=True
|
| 828 |
+
)
|
| 829 |
+
current_value = st.session_state.get(key, default_value)
|
| 830 |
|
| 831 |
+
# برای دیباگ: نمایش مقدار فعلی
|
| 832 |
+
st.write(f"مقدار فعلی برای {key}: {current_value}")
|
| 833 |
+
|
| 834 |
+
return current_value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 835 |
|
|
|
|
|
|
|
| 836 |
def create_ride_map():
|
| 837 |
"""ایجاد نقشه سفر با Folium - نسخه اصلاح شده با مناطق عمومی"""
|
| 838 |
# نقاط تقریبی برای مناطق عمومی جنوب و غرب تهران
|
|
|
|
| 1242 |
st.warning("لطفاً یک گزینه را انتخاب کنید")
|
| 1243 |
|
| 1244 |
def random_likert_questions():
|
| 1245 |
+
"""نمایش سوالات لیکرت به ترتیب مشخص با اسلایدر سفارشی"""
|
| 1246 |
+
# تعریف گروههای سوالات با لیبلهای سفارشی
|
| 1247 |
question_groups = [
|
| 1248 |
{
|
| 1249 |
"title": "عدالت توزیعی",
|
|
|
|
| 1253 |
"key": "distributive_1",
|
| 1254 |
"question": "قیمتی که به شما ارائه شد، چگونه بود؟",
|
| 1255 |
"scale": 7,
|
| 1256 |
+
"labels": ["کاملاً نامنصفانه", "کاملاً منصفانه"] # لیبلهای سفارشی برای این سوال
|
| 1257 |
},
|
| 1258 |
{
|
| 1259 |
"key": "distributive_2",
|
|
|
|
| 1273 |
"title": "سوال توجه",
|
| 1274 |
"key": "attention_check",
|
| 1275 |
"questions": [
|
| 1276 |
+
{"key": "attention_check2", "question": "تا چه مقدار با دقت به سوالات پاسخ میدهید؟", "scale": 7,"labels": ["خیلی کم", "خیلی زیاد"]}
|
| 1277 |
]
|
| 1278 |
},
|
| 1279 |
{
|
|
|
|
| 1298 |
}
|
| 1299 |
]
|
| 1300 |
|
| 1301 |
+
|
| 1302 |
# مقداردهی اولیه
|
| 1303 |
if 'current_likert_group' not in st.session_state:
|
| 1304 |
st.session_state.current_likert_group = 0
|
|
|
|
| 1306 |
# دریافت گروه جاری
|
| 1307 |
current_group = question_groups[st.session_state.current_likert_group]
|
| 1308 |
|
| 1309 |
+
# نمایش عنوان گروه
|
| 1310 |
st.markdown("""
|
| 1311 |
+
<style>
|
| 1312 |
+
.guide-text p {
|
| 1313 |
+
font-size: 14px !important;
|
| 1314 |
+
}
|
| 1315 |
+
</style>
|
| 1316 |
+
""", unsafe_allow_html=True)
|
| 1317 |
+
st.markdown("""
|
| 1318 |
+
<div class="guide-text" style="display: flex; flex-direction: column; align-items: center; background-color: #f0f2f6; border-radius: 10px; padding: 20px; gap: 15px;">
|
| 1319 |
+
<div style="flex: 1;">
|
| 1320 |
+
<h3>راهنمای پاسخ:</h3>
|
| 1321 |
+
<p>برای پاسخ به سوالات، با یک طیف کشویی مواجه خواهید شد.</p>
|
| 1322 |
+
<p>در این طیف 7 نقطه وجود دارد:
|
| 1323 |
+
<br>- سمت چپ: کمترین مقدار
|
| 1324 |
+
<br>- سمت راست: بیشترین مقدار
|
| 1325 |
+
</p>
|
| 1326 |
+
<p>نقطه روی کشو را جابجا کنید و در مقداری که پاسخ شماست متوقف شوید.</p>
|
| 1327 |
+
<p>پاسخ شما زیر طیف نمایش داده خواهد شد.</p>
|
| 1328 |
+
<p>اگر از پاسخهایتان مطمئن هستید، روی دکمه «ادامه» کلیک کنید.</p>
|
| 1329 |
+
</div>
|
| 1330 |
</div>
|
| 1331 |
""", unsafe_allow_html=True)
|
| 1332 |
|
| 1333 |
+
# نمایش تمام سوالات این گروه در یک صفحه
|
| 1334 |
for question in current_group['questions']:
|
| 1335 |
answer = custom_likert_slider(question)
|
| 1336 |
+
st.session_state.answers[question["key"]] = answer
|
| 1337 |
+
|
| 1338 |
|
| 1339 |
# دکمه ادامه/اتمام
|
| 1340 |
+
button_label = "ادامه به گروه بعد��" if st.session_state.current_likert_group < len(question_groups)-1 else "اتمام پرسشنامه"
|
| 1341 |
+
|
| 1342 |
+
if st.button(button_label):
|
| 1343 |
+
# رفتن به گروه بعدی یا صفحه پایانی
|
| 1344 |
+
if st.session_state.current_likert_group < len(question_groups) - 1:
|
| 1345 |
+
st.session_state.current_likert_group += 1
|
|
|
|
|
|
|
| 1346 |
st.rerun()
|
| 1347 |
+
else:
|
| 1348 |
+
st.session_state.current_page = "explanation_questions"
|
| 1349 |
+
st.rerun()
|
| 1350 |
+
|
| 1351 |
def explanation_questions():
|
| 1352 |
"""نمایش سوالات تکمیلی به صورت مرحلهای با دکمه ادامه"""
|
| 1353 |
st.markdown("### 📋 سوالات تکمیلی")
|
|
|
|
| 1437 |
|
| 1438 |
# رفرش صفحه برای نمایش سوال بعدی
|
| 1439 |
st.rerun()
|
| 1440 |
+
|
| 1441 |
def demographic_form():
|
| 1442 |
"""فرم اطلاعات دموگرافیک"""
|
| 1443 |
st.markdown("### 📝 اطلاعات دموگرافیک")
|
|
|
|
| 1545 |
user_agent = st.query_params.get("user_agent", [""])[0]
|
| 1546 |
st.session_state.is_desktop = "mobile" not in user_agent.lower()
|
| 1547 |
|
| 1548 |
+
|
| 1549 |
+
if st.session_state.is_desktop:
|
| 1550 |
+
# اطمینان از نمایش همان حالت موبایل برای همه دستگاهها
|
| 1551 |
+
st.session_state.is_desktop = False
|
| 1552 |
+
|
| 1553 |
if 'answers' not in st.session_state:
|
| 1554 |
st.session_state.answers = {}
|
| 1555 |
|
|
|
|
| 1562 |
st.session_state.price_accepted = 0
|
| 1563 |
st.session_state.attention_check1 = None
|
| 1564 |
|
|
|
|
| 1565 |
pages = {
|
| 1566 |
"welcome": welcome_page,
|
| 1567 |
"scenario_explanation": scenario_explanation,
|
|
|
|
| 1569 |
"attention_check1": attention_check1,
|
| 1570 |
"random_likert_questions": random_likert_questions,
|
| 1571 |
"explanation_questions": explanation_questions,
|
| 1572 |
+
"demographic": demographic_form, # دموگرافیک قبل از کانتکت
|
| 1573 |
+
"contact": user_contact, # کانتکت در انتها
|
| 1574 |
"thank_you": thank_you_page
|
| 1575 |
}
|
| 1576 |
|
|
|
|
| 1577 |
pages[st.session_state.current_page]()
|
| 1578 |
|
| 1579 |
if __name__ == "__main__":
|