nicegui-survey/secret_survey.py
2025-12-29 07:37:08 +05:30

183 lines
8.0 KiB
Python

import json
import secrets
from nicegui import app, ui
# --- 1. User Database & Token Generation ---
# In a real app, you'd save these to a file or DB.
# Generating unique tokens for your three users:
USER_MAP = {"k9a2_xJv1": "Ken", "Lz78_pQn9": "Liz", "Rb55_mTk2": "Ruben"}
# --- Survey Options & State ---
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, name):
self.user_name = name
self.frequency = None
self.selected_features = []
self.feature_feedback = {}
self.age = None
self.gender = None
self.location = ""
def save_to_json(self):
data = {
"user": self.user_name,
"usage": {"frequency": self.frequency, "features_used": self.selected_features},
"feature_feedback": self.feature_feedback,
"demographics": {"age": self.age, "gender": self.gender, "location": self.location},
}
filename = f"survey_{self.user_name.lower()}.json"
with open(filename, "w") as f:
json.dump(data, f, indent=4)
# --- 2. The Main Page Function ---
@ui.page("/")
def main_page(token: str):
# Extract 'id' from the URL query parameters
# user_token = app.native.main_window.get_url() if hasattr(app, "native") else ""
# Standard way in web mode:
# token = ui.query_params.get("id")
if token is None:
with ui.column().classes("w-full items-center mt-20"):
ui.icon("error", color="red").classes("text-6xl")
ui.label("User not found").classes("text-h4 text-negative")
ui.label("Please check your unique link and try again.").classes("text-grey")
return
if token not in USER_MAP:
with ui.column().classes("w-full items-center mt-20"):
ui.icon("error", color="red").classes("text-6xl")
ui.label("User not found").classes("text-h4 text-negative")
ui.label("Please check your unique link and try again.").classes("text-grey")
return
# Valid User Found
user_name = USER_MAP[token]
state = SurveyState(user_name)
container = ui.column().classes("w-full items-center q-pa-md")
# --- PAGE 1: GREETING & USAGE ---
def show_page_1():
container.clear()
with container:
ui.label(f"Welcome, {state.user_name}!").classes("text-h3 text-primary mb-2")
ui.label("Facebook Usage Survey").classes("text-h5 mb-6 text-grey-7")
with ui.card().classes("w-full max-w-lg p-6"):
ui.label("How frequently do you use Facebook?").classes("text-bold")
ui.radio(options=FREQUENCY_OPTIONS).bind_value(state, "frequency")
ui.label("What features do you use?").classes("text-bold mt-4")
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")
ui.timer(
0.5, lambda: next_btn.set_enabled(state.frequency is not None and len(state.selected_features) > 0)
)
# --- PAGE 2: STAR RATINGS (With fix for terminal errors) ---
def show_page_2():
container.clear()
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")
def is_page_2_valid():
for f in state.selected_features:
d = state.feature_feedback[f]
if d["rating"] < 4 and len(d.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")
stars = (
ui.rating(value=5, icon="star")
.classes("text-3xl")
.bind_value(state.feature_feedback[feature], "rating")
)
with ui.column().classes("w-full").bind_visibility_from(stars, "value", backward=lambda v: v >= 4):
ui.textarea("What do you like about it?").classes("w-full").bind_value(
state.feature_feedback[feature], "likes"
)
with ui.column().classes("w-full").bind_visibility_from(stars, "value", backward=lambda v: v < 4):
ui.label("Other apps you use:").classes("text-caption")
ui.select(options=OTHER_APP_OPTIONS, multiple=True).classes("w-full").bind_value(
state.feature_feedback[feature], "other_apps"
)
ui.textarea("Improvements (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 fully").bind_visibility_from(
next_btn, "enabled", backward=lambda v: not v
)
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 ---
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"**Name:** {state.user_name}")
ui.label(f"**Frequency:** {state.frequency}")
ui.separator()
for f in state.selected_features:
d = state.feature_feedback[f]
ui.label(f"**{f}**: {d['rating']} Stars")
ui.separator()
ui.label(f"**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")
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("check_circle", color="green").classes("text-6xl")
ui.label("Thank You!").classes("text-h4 mt-2")
ui.label(f"Your response has been saved as survey_{state.user_name.lower()}.json")
show_page_1()
ui.run()