176 lines
7.7 KiB
Python
176 lines
7.7 KiB
Python
import json
|
|
|
|
from nicegui import ui
|
|
|
|
# --- Survey Options ---
|
|
FREQUENCY_OPTIONS = ["Daily", "Weekly", "Monthly", "Rarely"]
|
|
FEATURE_OPTIONS = ["News Feed", "Marketplace", "Groups", "Messenger", "Stories"]
|
|
OTHER_APP_OPTIONS = ["Instagram", "TikTok", "X/Twitter", "LinkedIn", "Snapchat", "Reddit"]
|
|
GENDER_OPTIONS = ["Male", "Female", "Non-binary", "Prefer not to say"]
|
|
|
|
|
|
class SurveyState:
|
|
def __init__(self):
|
|
self.frequency = None
|
|
self.selected_features = []
|
|
self.feature_feedback = {}
|
|
self.age = None
|
|
self.gender = None
|
|
self.location = ""
|
|
|
|
def save_to_json(self):
|
|
data = {
|
|
"usage": {"frequency": self.frequency, "features_used": self.selected_features},
|
|
"feature_feedback": self.feature_feedback,
|
|
"demographics": {"age": self.age, "gender": self.gender, "location": self.location},
|
|
}
|
|
with open("survey_results.json", "w") as f:
|
|
json.dump(data, f, indent=4)
|
|
|
|
|
|
state = SurveyState()
|
|
|
|
|
|
def create_survey():
|
|
container = ui.column().classes("w-full items-center q-pa-md")
|
|
|
|
# --- PAGE 1: USAGE ---
|
|
def show_page_1():
|
|
container.clear()
|
|
with container:
|
|
ui.label("Facebook Usage").classes("text-h4 mb-4")
|
|
with ui.card().classes("w-full max-w-lg p-6"):
|
|
ui.label("How frequently do you use Facebook?").classes("text-bold")
|
|
p1_radio = ui.radio(options=FREQUENCY_OPTIONS).bind_value(state, "frequency")
|
|
|
|
ui.label("What features do you use?").classes("text-bold mt-4")
|
|
p1_select = (
|
|
ui.select(options=FEATURE_OPTIONS, multiple=True)
|
|
.classes("w-full")
|
|
.bind_value(state, "selected_features")
|
|
)
|
|
|
|
next_btn = ui.button("Next", on_click=show_page_2).classes("mt-6 w-full")
|
|
|
|
# Validation
|
|
ui.timer(
|
|
0.1, lambda: next_btn.set_enabled(state.frequency is not None and len(state.selected_features) > 0)
|
|
)
|
|
|
|
# --- PAGE 2: STAR RATINGS & CONDITIONAL FEEDBACK ---
|
|
def show_page_2():
|
|
container.clear()
|
|
# Initialize dictionary for selected features
|
|
for f in state.selected_features:
|
|
if f not in state.feature_feedback:
|
|
state.feature_feedback[f] = {"rating": 5, "likes": "", "other_apps": [], "improvements": ""}
|
|
|
|
with container:
|
|
ui.label("Feature Feedback").classes("text-h4 mb-4")
|
|
|
|
# We define the button and validation logic first to ensure they are in scope
|
|
def is_page_2_valid():
|
|
for f in state.selected_features:
|
|
data = state.feature_feedback[f]
|
|
if data["rating"] < 4:
|
|
if len(data.get("improvements", "")) < 20:
|
|
return False
|
|
return True
|
|
|
|
for feature in state.selected_features:
|
|
with ui.card().classes("w-full max-w-lg p-6 mb-4"):
|
|
ui.label(f"Rate the {feature}").classes("text-h6 text-primary")
|
|
|
|
# Star Rating (Quasar based)
|
|
stars = (
|
|
ui.rating(value=5, icon="star")
|
|
.classes("text-3xl")
|
|
.bind_value(state.feature_feedback[feature], "rating")
|
|
)
|
|
|
|
# CASE: Rating 4 or 5
|
|
with ui.column().classes("w-full").bind_visibility_from(stars, "value", backward=lambda v: v >= 4):
|
|
ui.textarea("What do you like about this feature?").classes("w-full").bind_value(
|
|
state.feature_feedback[feature], "likes"
|
|
)
|
|
|
|
# CASE: Rating below 4
|
|
with ui.column().classes("w-full").bind_visibility_from(stars, "value", backward=lambda v: v < 4):
|
|
ui.label("What other apps do you use for this?")
|
|
ui.select(options=OTHER_APP_OPTIONS, multiple=True).classes("w-full").bind_value(
|
|
state.feature_feedback[feature], "other_apps"
|
|
)
|
|
|
|
ui.textarea("How can this be improved? (min 20 characters)").classes("w-full").bind_value(
|
|
state.feature_feedback[feature], "improvements"
|
|
)
|
|
|
|
with ui.row().classes("w-full max-w-lg justify-between mt-4"):
|
|
ui.button("Back", on_click=show_page_1).props("outline")
|
|
next_btn = ui.button("Next", on_click=show_page_3)
|
|
ui.tooltip("Please answer all questions (min 20 chars for improvements)").bind_visibility_from(
|
|
next_btn, "enabled", backward=lambda v: not v
|
|
)
|
|
|
|
# Reactive validation timer
|
|
ui.timer(0.5, lambda: next_btn.set_enabled(is_page_2_valid()))
|
|
|
|
# --- PAGE 3: DEMOGRAPHICS ---
|
|
def show_page_3():
|
|
container.clear()
|
|
with container:
|
|
ui.label("Demographics").classes("text-h4 mb-4")
|
|
with ui.card().classes("w-full max-w-lg p-6"):
|
|
ui.number("Age", format="%d").classes("w-full").bind_value(state, "age")
|
|
ui.select(label="Gender", options=GENDER_OPTIONS).classes("w-full").bind_value(state, "gender")
|
|
ui.input("Location").classes("w-full").bind_value(state, "location")
|
|
|
|
with ui.row().classes("w-full justify-between mt-6"):
|
|
ui.button("Back", on_click=show_page_2).props("outline")
|
|
ui.button("Review Survey", on_click=show_confirmation).classes("bg-blue")
|
|
|
|
# --- PAGE 4: CONFIRMATION (SUMMARY) ---
|
|
def show_confirmation():
|
|
container.clear()
|
|
with container:
|
|
ui.label("Review Your Answers").classes("text-h4 mb-4")
|
|
with ui.card().classes("w-full max-w-2xl p-6"):
|
|
with ui.column().classes("w-full gap-2"):
|
|
ui.label(f"**Frequency:** {state.frequency}").classes("text-md")
|
|
ui.label(f"**Features:** {', '.join(state.selected_features)}")
|
|
|
|
ui.separator().classes("my-2")
|
|
for f in state.selected_features:
|
|
d = state.feature_feedback[f]
|
|
ui.label(f"**{f}**: {d['rating']} Stars").classes("text-primary")
|
|
if d["rating"] >= 4:
|
|
ui.label(f"Likes: {d['likes'] or 'No comment'}").classes("ml-4 text-grey-8")
|
|
else:
|
|
ui.label(f"Improvements: {d['improvements']}").classes("ml-4 text-grey-8")
|
|
|
|
ui.separator().classes("my-2")
|
|
ui.label(
|
|
f"**Age:** {state.age or 'N/A'} | **Gender:** {state.gender} | **Location:** {state.location}"
|
|
)
|
|
|
|
with ui.row().classes("w-full justify-between mt-8"):
|
|
ui.button("Back to Edit", on_click=show_page_3).props("outline")
|
|
ui.button("Confirm & Submit", on_click=handle_final_submit).classes("bg-green")
|
|
|
|
# --- FINAL PAGE: ACKNOWLEDGMENT ---
|
|
def handle_final_submit():
|
|
state.save_to_json()
|
|
container.clear()
|
|
with container:
|
|
with ui.card().classes("w-full max-w-lg p-12 items-center text-center"):
|
|
ui.icon("verified", color="green").classes("text-6xl")
|
|
ui.label("Submission Successful").classes("text-h4 mt-2")
|
|
ui.label("Thank you for your time. Your responses have been saved.").classes("text-grey-7")
|
|
# No buttons here prevents the user from going back
|
|
|
|
show_page_1()
|
|
|
|
|
|
create_survey()
|
|
ui.run()
|