GSMEthesis commited on
Commit
18a10e7
·
verified ·
1 Parent(s): b256f5c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +61 -112
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
@@ -673,6 +674,34 @@ h3 {
673
  """, unsafe_allow_html=True)
674
 
675
  # ========== توابع اصلی ==========
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
676
  def create_ride_map():
677
  """ایجاد نقشه سفر با Folium - نسخه اصلاح شده با مناطق عمومی"""
678
  # نقاط تقریبی برای مناطق عمومی جنوب و غرب تهران
@@ -774,62 +803,7 @@ def create_likert_question(question, key, scale_type="5point"):
774
  return value
775
 
776
  # ========== توابع مدیریت داده‌ها ==========
777
- def apply_global_styles():
778
- st.markdown("""
779
- <style>
780
- /* ===== بازنویسی کامل استایل‌های پیش‌فرض ===== */
781
-
782
- /* پایه‌ترین سطوح */
783
- html, body, #root, .stApp {
784
- direction: rtl !important;
785
- text-align: right !important;
786
- font-family: 'B Nazanin' !important;
787
- overflow-x: hidden !important;
788
- }
789
-
790
- /* دکمه‌ها - بالاترین سطح Specificity */
791
- body .stButton>button,
792
- body .stButton>button:hover,
793
- body .stButton>button:focus {
794
- margin-right: auto !important;
795
- margin-left: 0 !important;
796
- float: right !important;
797
- transform: none !important;
798
- }
799
-
800
- /* اسلایدرها - بازنویسی کامل */
801
- body div[data-testid="stSlider"] > div:first-child,
802
- body div[data-testid="stSlider"] > div:first-child > div {
803
- direction: ltr !important;
804
- background:
805
- repeating-linear-gradient(
806
- to right,
807
- #6a0dad, #6a0dad 3px,
808
- transparent 3px, transparent calc(20% - 2px),
809
- #6a0dad calc(20%), #6a0dad calc(20% + 2px),
810
- transparent calc(20% + 2px), transparent calc(40% - 2px),
811
- #6a0dad calc(40%), #6a0dad calc(40% + 2px),
812
- transparent calc(40% + 2px), transparent calc(60% - 2px),
813
- #6a0dad calc(60%), #6a0dad calc(60% + 2px),
814
- transparent calc(60% + 2px), transparent calc(80% - 2px),
815
- #6a0dad calc(80%), #6a0dad calc(80% + 2px)
816
- ),
817
- linear-gradient(to right, #E6E6FA, #6A0DAD) !important;
818
- }
819
-
820
- /* بازنویسی کامل بلوک‌ها */
821
- body .stHorizontalBlock > div,
822
- body .stVerticalBlock > div {
823
- padding-right: 12px !important;
824
- padding-left: 0 !important;
825
- }
826
 
827
- /* مخفی کردن اسکرول افقی */
828
- body ::-webkit-scrollbar {
829
- display: none !important;
830
- }
831
- </style>
832
- """, unsafe_allow_html=True)
833
  def get_credentials():
834
  """دریافت اعتبارنامه از Secrets"""
835
  try:
@@ -944,23 +918,6 @@ def create_likert_question(question, key, scale_type="5point"):
944
  """,
945
  unsafe_allow_html=True
946
  )
947
- # اضافه کردن JavaScript مخصوص این اسلایدر
948
- st.components.v1.html(f"""
949
- <script>
950
- (function() {{
951
- const slider = parent.document.querySelector('[data-testid="stSlider"] [aria-valuenow="{value}"]');
952
- if(slider) {{
953
- slider.parentElement.style.background = `repeating-linear-gradient(
954
- to right,
955
- #6a0dad, #6a0dad 2px,
956
- transparent 2px, transparent calc(100%/{max_value-1} - 1px),
957
- #6a0dad calc(100%/{max_value-1}), #6a0dad calc(100%/{max_value-1} + 1px)
958
- )`;
959
- }}
960
- }})();
961
- </script>
962
- """, height=0)
963
-
964
  return value
965
 
966
  # ========== بخش‌های فرم ==========
@@ -1138,43 +1095,43 @@ def attention_check1():
1138
  st.warning("لطفاً یک گزینه را انتخاب کنید")
1139
 
1140
  def random_likert_questions():
1141
- """نمایش سوالات لیکرت به ترتیب مشخص"""
1142
- # تعریف گروه‌های سوالات با ترتیب جدید
1143
  question_groups = [
1144
  {
1145
  "title": "عدالت توزیعی",
1146
  "key": "distributive",
1147
  "questions": [
1148
- {"key": "distributive_1", "question": "قیمتی که به شما ارائه شد، منصفانه است.", "scale": "7point"},
1149
- {"key": "distributive_2", "question": "قیمتی که به شما ارائه شد، معقول است.", "scale": "7point"},
1150
- {"key": "distributive_3", "question": "قیمتی که به شما ارائه شد، قابل قبول است.", "scale": "7point"}
1151
  ]
1152
  },
1153
  {
1154
  "title": "سوال توجه",
1155
  "key": "attention_check",
1156
  "questions": [
1157
- {"key": "attention_check2", "question": "لطفاً برای اطمینان از دقت پاسخ‌هایتان، گزینه 'کاملاً موافقم' را انتخاب کنید.", "scale": "7point"}
1158
  ]
1159
  },
1160
  {
1161
  "title": "عدالت رویه‌ای",
1162
  "key": "procedural",
1163
  "questions": [
1164
- {"key": "procedural_1", "question": "فرآیند و رویه قیمت‌گذاری پلتفرم قابل قبول است.", "scale": "7point"},
1165
- {"key": "procedural_2", "question": "فرآیند و رویه قیمت‌گذاری پلتفرم منصفانه است.", "scale": "7point"},
1166
- {"key": "procedural_3", "question": "فرآیند و رویه قیمت‌گذاری پلتفرم معقول است.", "scale": "7point"}
1167
  ]
1168
  },
1169
  {
1170
  "title": "عدالت اطلاعاتی",
1171
  "key": "informational",
1172
  "questions": [
1173
- {"key": "informational_1", "question": "پلتفرم به صورت صادقانه دلایل تعیین قیمت را توضیح داد.", "scale": "5point"},
1174
- {"key": "informational_2", "question": "پلتفرم به طور کامل عوامل مؤثر بر تعیین قیمت را شرح داد.", "scale": "5point"},
1175
- {"key": "informational_3", "question": "دلایل ارائه‌شده برای تعیین قیمت منطقی و قابل قبول بود.", "scale": "5point"},
1176
- {"key": "informational_4", "question": "توضیحات درباره تعیین قیمت بلافاصله و در زمان مناسب نمایش داده شد.", "scale": "5point"},
1177
- {"key": "informational_5", "question": "توضیحات پلتفرم درباره تعیین قیمت، متناسب با شرایط سفر من بود.", "scale": "5point"}
1178
  ]
1179
  }
1180
  ]
@@ -1183,36 +1140,31 @@ def random_likert_questions():
1183
  if 'current_likert_group' not in st.session_state:
1184
  st.session_state.current_likert_group = 0
1185
  st.session_state.answers = st.session_state.get('answers', {})
1186
-
1187
  # دریافت گروه جاری
1188
  current_group = question_groups[st.session_state.current_likert_group]
1189
-
1190
  # نمایش عنوان گروه
1191
  st.markdown(f"## {current_group['title']}")
1192
  st.markdown("---")
1193
-
1194
- # نمایش تمام سوالات این گروه در یک صفحه
1195
- answers = {}
1196
  for question in current_group['questions']:
1197
- answer = create_likert_question(
1198
- question["question"],
1199
- question["key"],
1200
- question["scale"]
1201
  )
1202
- answers[question["key"]] = answer
1203
-
1204
- # بررسی آیا همه سوالات پاسخ داده شده‌اند
1205
- all_answered = all(answers.values())
1206
-
1207
- # دکمه ادامه/اتمام
1208
- button_label = "ادامه به گروه بعدی" if st.session_state.current_likert_group < len(question_groups)-1 else "اتمام پرسشنامه"
1209
 
1210
  if st.button(button_label, disabled=not all_answered):
1211
- # ذخیره پاسخ‌های این گروه
1212
- for key, value in answers.items():
1213
- st.session_state.answers[key] = value
1214
-
1215
- # رفتن به گروه بعدی یا صفحه پایانی
1216
  if st.session_state.current_likert_group < len(question_groups) - 1:
1217
  st.session_state.current_likert_group += 1
1218
  st.rerun()
@@ -1221,8 +1173,8 @@ def random_likert_questions():
1221
  st.rerun()
1222
 
1223
  if not all_answered:
1224
- st.warning("لطفاً به تمام سوالات این بخش پاسخ دهید")
1225
-
1226
  def explanation_questions():
1227
  """سوالات درباره توضیحات قیمت"""
1228
  st.markdown("""
@@ -1423,14 +1375,11 @@ def thank_you_page():
1423
 
1424
  # ========== مدیریت وضعیت و صفحه‌بندی ==========
1425
  def main():
1426
-
1427
- apply_global_styles()
1428
  # تشخیص دستگاه
1429
  user_agent = st.query_params.get("user_agent", [""])[0]
1430
  st.session_state.is_desktop = "mobile" not in user_agent.lower()
1431
 
1432
 
1433
- # اینجا می‌توانید از is_desktop برای تعیین اینکه نمایشگر موبایل اعمال شود، استفاده کنید
1434
  if st.session_state.is_desktop:
1435
  # اطمینان از نمایش همان حالت موبایل برای همه دستگاه‌ها
1436
  st.session_state.is_desktop = False
 
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
 
674
  """, unsafe_allow_html=True)
675
 
676
  # ========== توابع اصلی ==========
677
+ def custom_likert_slider(question, key, points=7):
678
+ options = {
679
+ 5: ["کاملاً مخالفم", "مخالفم", "خنثی", "موافقم", "کاملاً موافقم"],
680
+ 7: ["کاملاً مخالفم", "نسبتاً مخالفم", "کمی مخالفم", "خنثی", "کمی موافقم", "نسبتاً موافقم", "کاملاً موافقم"]
681
+ }
682
+ labels = options.get(points)
683
+ slider_id = f"slider_{key}"
684
+
685
+ # ساخت HTML برای اسلایدر + ارسال مقدار با postMessage
686
+ html = f"""
687
+ <div id="container_{key}" style="direction: rtl; font-family: 'B Nazanin'; margin-bottom: 30px;">
688
+ <label style="font-size: 16px; font-weight: bold;">{question}</label>
689
+ <div style="display: flex; justify-content: space-between; font-size: 12px; margin-bottom: 5px;">
690
+ {''.join(f"<span>{label}</span>" for label in labels)}
691
+ </div>
692
+ <input type="range" id="{slider_id}" min="1" max="{points}" step="1" value="{(points+1)//2}"
693
+ style="width: 100%; accent-color: #6a0dad;"
694
+ oninput="document.getElementById('output_{key}').innerText=this.value;
695
+ window.parent.postMessage({{type: 'streamlit:setComponentValue', value: parseInt(this.value)}}, '*')">
696
+ <p style="text-align: center; margin-top: 10px;">پاسخ انتخاب‌شده: <strong><span id="output_{key}">{(points+1)//2}</span></strong></p>
697
+ </div>
698
+ """
699
+
700
+ # مقدار برگشتی (از JS)
701
+ selected_value = components.html(html, height=150, key=f"component_{key}")
702
+
703
+ return selected_value
704
+
705
  def create_ride_map():
706
  """ایجاد نقشه سفر با Folium - نسخه اصلاح شده با مناطق عمومی"""
707
  # نقاط تقریبی برای مناطق عمومی جنوب و غرب تهران
 
803
  return value
804
 
805
  # ========== توابع مدیریت داده‌ها ==========
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
806
 
 
 
 
 
 
 
807
  def get_credentials():
808
  """دریافت اعتبارنامه از Secrets"""
809
  try:
 
918
  """,
919
  unsafe_allow_html=True
920
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
921
  return value
922
 
923
  # ========== بخش‌های فرم ==========
 
1095
  st.warning("لطفاً یک گزینه را انتخاب کنید")
1096
 
1097
  def random_likert_questions():
1098
+ """نمایش سوالات لیکرت با اسلایدرهای سفارشی قابل ذخیره‌سازی"""
1099
+ # تعریف گروه‌های سوالات با ترتیب مشخص
1100
  question_groups = [
1101
  {
1102
  "title": "عدالت توزیعی",
1103
  "key": "distributive",
1104
  "questions": [
1105
+ {"key": "distributive_1", "question": "قیمتی که به شما ارائه شد، منصفانه است.", "scale": 7},
1106
+ {"key": "distributive_2", "question": "قیمتی که به شما ارائه شد، معقول است.", "scale": 7},
1107
+ {"key": "distributive_3", "question": "قیمتی که به شما ارائه شد، قابل قبول است.", "scale": 7}
1108
  ]
1109
  },
1110
  {
1111
  "title": "سوال توجه",
1112
  "key": "attention_check",
1113
  "questions": [
1114
+ {"key": "attention_check2", "question": "لطفاً برای اطمینان از دقت پاسخ‌هایتان، گزینه 'کاملاً موافقم' را انتخاب کنید.", "scale": 7}
1115
  ]
1116
  },
1117
  {
1118
  "title": "عدالت رویه‌ای",
1119
  "key": "procedural",
1120
  "questions": [
1121
+ {"key": "procedural_1", "question": "فرآیند و رویه قیمت‌گذاری پلتفرم قابل قبول است.", "scale": 7},
1122
+ {"key": "procedural_2", "question": "فرآیند و رویه قیمت‌گذاری پلتفرم منصفانه است.", "scale": 7},
1123
+ {"key": "procedural_3", "question": "فرآیند و رویه قیمت‌گذاری پلتفرم معقول است.", "scale": 7}
1124
  ]
1125
  },
1126
  {
1127
  "title": "عدالت اطلاعاتی",
1128
  "key": "informational",
1129
  "questions": [
1130
+ {"key": "informational_1", "question": "پلتفرم به صورت صادقانه دلایل تعیین قیمت را توضیح داد.", "scale": 5},
1131
+ {"key": "informational_2", "question": "پلتفرم به طور کامل عوامل مؤثر بر تعیین قیمت را شرح داد.", "scale": 5},
1132
+ {"key": "informational_3", "question": "دلایل ارائه‌شده برای تعیین قیمت منطقی و قابل قبول بود.", "scale": 5},
1133
+ {"key": "informational_4", "question": "توضیحات درباره تعیین قیمت بلافاصله و در زمان مناسب نمایش داده شد.", "scale": 5},
1134
+ {"key": "informational_5", "question": "توضیحات پلتفرم درباره تعیین قیمت، متناسب با شرایط سفر من بود.", "scale": 5}
1135
  ]
1136
  }
1137
  ]
 
1140
  if 'current_likert_group' not in st.session_state:
1141
  st.session_state.current_likert_group = 0
1142
  st.session_state.answers = st.session_state.get('answers', {})
1143
+
1144
  # دریافت گروه جاری
1145
  current_group = question_groups[st.session_state.current_likert_group]
1146
+
1147
  # نمایش عنوان گروه
1148
  st.markdown(f"## {current_group['title']}")
1149
  st.markdown("---")
1150
+
1151
+ # ثبت پاسخ‌های جاری
1152
+ all_answered = True
1153
  for question in current_group['questions']:
1154
+ value = custom_likert_slider(
1155
+ question=question["question"],
1156
+ key=question["key"],
1157
+ points=question["scale"]
1158
  )
1159
+ if value is None:
1160
+ all_answered = False
1161
+ else:
1162
+ st.session_state.answers[question["key"]] = value
1163
+
1164
+ # دکمه ادامه یا اتمام
1165
+ button_label = "ادامه به گروه بعدی" if st.session_state.current_likert_group < len(question_groups) - 1 else "اتمام پرسشنامه"
1166
 
1167
  if st.button(button_label, disabled=not all_answered):
 
 
 
 
 
1168
  if st.session_state.current_likert_group < len(question_groups) - 1:
1169
  st.session_state.current_likert_group += 1
1170
  st.rerun()
 
1173
  st.rerun()
1174
 
1175
  if not all_answered:
1176
+ st.warning("لطفاً به تمام سوالات این بخش پاسخ دهید.")
1177
+
1178
  def explanation_questions():
1179
  """سوالات درباره توضیحات قیمت"""
1180
  st.markdown("""
 
1375
 
1376
  # ========== مدیریت وضعیت و صفحه‌بندی ==========
1377
  def main():
 
 
1378
  # تشخیص دستگاه
1379
  user_agent = st.query_params.get("user_agent", [""])[0]
1380
  st.session_state.is_desktop = "mobile" not in user_agent.lower()
1381
 
1382
 
 
1383
  if st.session_state.is_desktop:
1384
  # اطمینان از نمایش همان حالت موبایل برای همه دستگاه‌ها
1385
  st.session_state.is_desktop = False