Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -5,7 +5,6 @@ import streamlit.components.v1 as components
|
|
| 5 |
from datetime import datetime
|
| 6 |
import gspread
|
| 7 |
from google.oauth2.service_account import Credentials
|
| 8 |
-
import plotly.graph_objects as go
|
| 9 |
import os
|
| 10 |
import json
|
| 11 |
import random
|
|
@@ -734,106 +733,63 @@ h3 {
|
|
| 734 |
[data-testid="stAppViewContainer"] p:not(.stButton p):not(.stDownloadButton p):not(.stFormSubmitButton p) {
|
| 735 |
margin-bottom: 1em !important;
|
| 736 |
line-height: 1.8 !important;
|
| 737 |
-
}
|
| 738 |
</style>
|
| 739 |
""", unsafe_allow_html=True)
|
| 740 |
|
| 741 |
# ========== توابع اصلی ==========
|
| 742 |
def custom_likert_slider(question_data):
|
| 743 |
-
"""نمایش سوال لیکرت با
|
| 744 |
question = question_data["question"]
|
| 745 |
key = question_data["key"]
|
| 746 |
points = question_data["scale"]
|
| 747 |
-
labels = question_data.get("labels", ["ک
|
| 748 |
|
| 749 |
# مقدار پیشفرض (وسط طیف)
|
| 750 |
-
default_value = st.session_state.
|
| 751 |
-
|
| 752 |
-
st.markdown(f"**{question}**")
|
| 753 |
-
|
| 754 |
-
# ایجاد یک container برای نمایش طیف
|
| 755 |
-
container = st.container()
|
| 756 |
-
|
| 757 |
-
# نمایش طیف با رنگ بنفش روی گزینه انتخابشده
|
| 758 |
-
fig = go.Figure()
|
| 759 |
-
|
| 760 |
-
positions = list(range(points))
|
| 761 |
-
|
| 762 |
-
# اضافه کردن نقاط به نمودار
|
| 763 |
-
fig.add_trace(go.Scatter(
|
| 764 |
-
x=positions,
|
| 765 |
-
y=[0]*points,
|
| 766 |
-
mode='markers+text',
|
| 767 |
-
marker=dict(
|
| 768 |
-
size=30,
|
| 769 |
-
color=['#d1c4e9' if i != st.session_state.answers.get(key, default_value) else '#6a0dad' for i in positions],
|
| 770 |
-
line=dict(width=2, color='#6a0dad')
|
| 771 |
-
),
|
| 772 |
-
text=labels if len(labels) == points else [str(i+1) for i in positions],
|
| 773 |
-
textposition="top center",
|
| 774 |
-
textfont=dict(size=14)
|
| 775 |
-
))
|
| 776 |
-
|
| 777 |
-
# خط اتصال نقاط
|
| 778 |
-
fig.add_trace(go.Scatter(
|
| 779 |
-
x=positions,
|
| 780 |
-
y=[0]*points,
|
| 781 |
-
mode='lines',
|
| 782 |
-
line=dict(color='#d1c4e9', width=2),
|
| 783 |
-
hoverinfo='none'
|
| 784 |
-
))
|
| 785 |
-
|
| 786 |
-
# تنظیمات layout
|
| 787 |
-
fig.update_layout(
|
| 788 |
-
xaxis=dict(
|
| 789 |
-
tickmode='array',
|
| 790 |
-
tickvals=positions,
|
| 791 |
-
ticktext=labels if len(labels) == points else [str(i+1) for i in positions],
|
| 792 |
-
showgrid=False,
|
| 793 |
-
zeroline=False,
|
| 794 |
-
range=[-0.5, points-0.5]
|
| 795 |
-
),
|
| 796 |
-
yaxis=dict(showticklabels=False, showgrid=False, zeroline=False, range=[-0.1, 0.1]),
|
| 797 |
-
margin=dict(l=20, r=20, t=40, b=20),
|
| 798 |
-
height=150,
|
| 799 |
-
showlegend=False,
|
| 800 |
-
hovermode=False,
|
| 801 |
-
clickmode='event+select'
|
| 802 |
-
)
|
| 803 |
|
| 804 |
-
#
|
| 805 |
-
|
| 806 |
-
|
| 807 |
-
|
| 808 |
-
|
| 809 |
-
|
| 810 |
-
|
| 811 |
-
|
| 812 |
-
|
| 813 |
-
|
| 814 |
-
|
| 815 |
-
""
|
| 816 |
-
|
| 817 |
-
|
| 818 |
-
|
| 819 |
-
|
| 820 |
-
|
| 821 |
-
|
| 822 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 823 |
|
| 824 |
-
#
|
| 825 |
-
|
| 826 |
-
st.session_state[key] = selected_value
|
| 827 |
|
| 828 |
-
#
|
| 829 |
-
|
| 830 |
-
st.markdown(
|
| 831 |
-
f"<p style='text-align:center; color:#6a0dad; font-weight:bold;'>پاسخ شما: <strong>{selected_label}</strong></p>",
|
| 832 |
-
unsafe_allow_html=True
|
| 833 |
-
)
|
| 834 |
|
| 835 |
-
|
|
|
|
| 836 |
|
|
|
|
|
|
|
| 837 |
def create_ride_map():
|
| 838 |
"""ایجاد نقشه سفر با Folium - نسخه اصلاح شده با مناطق عمومی"""
|
| 839 |
# نقاط تقریبی برای مناطق عمومی جنوب و غرب تهران
|
|
@@ -906,7 +862,10 @@ def show_explanation(exp_type):
|
|
| 906 |
elif exp_type == "counterfactual":
|
| 907 |
st.markdown("<p class='explanation-title'>علت قیمت گذاری:</p>", unsafe_allow_html=True)
|
| 908 |
for item in explanations.get(exp_type, []):
|
| 909 |
-
st.markdown(f"<p class='explanation-item'>• {item}</p>", unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
| 910 |
|
| 911 |
def create_likert_question(question, key, scale_type="5point"):
|
| 912 |
"""نمایش سوال لیکرت با اسلایدر نقطهای"""
|
|
@@ -1251,7 +1210,7 @@ def random_likert_questions():
|
|
| 1251 |
"key": "distributive_1",
|
| 1252 |
"question": "قیمتی که به شما ارائه شد، چگونه بود؟",
|
| 1253 |
"scale": 7,
|
| 1254 |
-
"labels": ["کاملاً نامنصفانه", "کاملاً منصفانه"]
|
| 1255 |
},
|
| 1256 |
{
|
| 1257 |
"key": "distributive_2",
|
|
@@ -1271,7 +1230,7 @@ def random_likert_questions():
|
|
| 1271 |
"title": "سوال توجه",
|
| 1272 |
"key": "attention_check",
|
| 1273 |
"questions": [
|
| 1274 |
-
{"key": "attention_check2", "question": "تا چه مقدار با دقت به سوالات پاسخ میدهید؟", "scale": 7,
|
| 1275 |
]
|
| 1276 |
},
|
| 1277 |
{
|
|
@@ -1296,6 +1255,7 @@ def random_likert_questions():
|
|
| 1296 |
}
|
| 1297 |
]
|
| 1298 |
|
|
|
|
| 1299 |
# مقداردهی اولیه
|
| 1300 |
if 'current_likert_group' not in st.session_state:
|
| 1301 |
st.session_state.current_likert_group = 0
|
|
@@ -1331,16 +1291,12 @@ def random_likert_questions():
|
|
| 1331 |
for question in current_group['questions']:
|
| 1332 |
answer = custom_likert_slider(question)
|
| 1333 |
st.session_state.answers[question["key"]] = answer
|
|
|
|
| 1334 |
|
| 1335 |
# دکمه ادامه/اتمام
|
| 1336 |
button_label = "ادامه به گروه بعدی" if st.session_state.current_likert_group < len(question_groups)-1 else "اتمام پرسشنامه"
|
| 1337 |
|
| 1338 |
if st.button(button_label):
|
| 1339 |
-
# ذخیره پاسخها قبل از رفتن به گروه بعدی
|
| 1340 |
-
for question in current_group['questions']:
|
| 1341 |
-
if question["key"] in st.session_state:
|
| 1342 |
-
st.session_state.answers[question["key"]] = st.session_state[question["key"]]
|
| 1343 |
-
|
| 1344 |
# رفتن به گروه بعدی یا صفحه پایانی
|
| 1345 |
if st.session_state.current_likert_group < len(question_groups) - 1:
|
| 1346 |
st.session_state.current_likert_group += 1
|
|
|
|
| 5 |
from datetime import datetime
|
| 6 |
import gspread
|
| 7 |
from google.oauth2.service_account import Credentials
|
|
|
|
| 8 |
import os
|
| 9 |
import json
|
| 10 |
import random
|
|
|
|
| 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 |
+
value = st.session_state.get(key, default_value)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 787 |
|
| 788 |
+
# ذخیره مقدار جدید در session_state
|
| 789 |
+
st.session_state[key] = value
|
| 790 |
|
| 791 |
+
return value
|
| 792 |
+
|
| 793 |
def create_ride_map():
|
| 794 |
"""ایجاد نقشه سفر با Folium - نسخه اصلاح شده با مناطق عمومی"""
|
| 795 |
# نقاط تقریبی برای مناطق عمومی جنوب و غرب تهران
|
|
|
|
| 862 |
elif exp_type == "counterfactual":
|
| 863 |
st.markdown("<p class='explanation-title'>علت قیمت گذاری:</p>", unsafe_allow_html=True)
|
| 864 |
for item in explanations.get(exp_type, []):
|
| 865 |
+
st.markdown(f"<p class='explanation-item'>• {item}</p>", unsafe_allow_html=True)
|
| 866 |
+
|
| 867 |
+
|
| 868 |
+
|
| 869 |
|
| 870 |
def create_likert_question(question, key, scale_type="5point"):
|
| 871 |
"""نمایش سوال لیکرت با اسلایدر نقطهای"""
|
|
|
|
| 1210 |
"key": "distributive_1",
|
| 1211 |
"question": "قیمتی که به شما ارائه شد، چگونه بود؟",
|
| 1212 |
"scale": 7,
|
| 1213 |
+
"labels": ["کاملاً نامنصفانه", "کاملاً منصفانه"] # لیبلهای سفارشی برای این سوال
|
| 1214 |
},
|
| 1215 |
{
|
| 1216 |
"key": "distributive_2",
|
|
|
|
| 1230 |
"title": "سوال توجه",
|
| 1231 |
"key": "attention_check",
|
| 1232 |
"questions": [
|
| 1233 |
+
{"key": "attention_check2", "question": "تا چه مقدار با دقت به سوالات پاسخ میدهید؟", "scale": 7,"labels": ["خیلی کم", "خیلی زیاد"]}
|
| 1234 |
]
|
| 1235 |
},
|
| 1236 |
{
|
|
|
|
| 1255 |
}
|
| 1256 |
]
|
| 1257 |
|
| 1258 |
+
|
| 1259 |
# مقداردهی اولیه
|
| 1260 |
if 'current_likert_group' not in st.session_state:
|
| 1261 |
st.session_state.current_likert_group = 0
|
|
|
|
| 1291 |
for question in current_group['questions']:
|
| 1292 |
answer = custom_likert_slider(question)
|
| 1293 |
st.session_state.answers[question["key"]] = answer
|
| 1294 |
+
|
| 1295 |
|
| 1296 |
# دکمه ادامه/اتمام
|
| 1297 |
button_label = "ادامه به گروه بعدی" if st.session_state.current_likert_group < len(question_groups)-1 else "اتمام پرسشنامه"
|
| 1298 |
|
| 1299 |
if st.button(button_label):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1300 |
# رفتن به گروه بعدی یا صفحه پایانی
|
| 1301 |
if st.session_state.current_likert_group < len(question_groups) - 1:
|
| 1302 |
st.session_state.current_likert_group += 1
|