first commit
This commit is contained in:
commit
d4127e878b
169
app.py
Normal file
169
app.py
Normal file
@ -0,0 +1,169 @@
|
||||
import json
|
||||
|
||||
from nicegui import ui
|
||||
|
||||
# --- Configuration & 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("Step 1: 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")
|
||||
ui.radio(options=FREQUENCY_OPTIONS, on_change=lambda: update_p1_btn()).bind_value(state, "frequency")
|
||||
|
||||
ui.label("What features do you use?").classes("text-bold mt-4")
|
||||
ui.select(options=FEATURE_OPTIONS, multiple=True, on_change=lambda: update_p1_btn()).bind_value(
|
||||
state, "selected_features"
|
||||
).classes("w-full")
|
||||
|
||||
next_btn = ui.button("Next", on_click=show_page_2).classes("mt-6 w-full")
|
||||
|
||||
def update_p1_btn():
|
||||
next_btn.set_enabled(state.frequency is not None and len(state.selected_features) > 0)
|
||||
|
||||
update_p1_btn()
|
||||
|
||||
# --- PAGE 2: RATINGS & FEEDBACK ---
|
||||
def show_page_2():
|
||||
container.clear()
|
||||
# Ensure data structure exists 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("Step 2: Feedback").classes("text-h4 mb-4")
|
||||
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 the questions (min 20 chars for improvements)").bind_visibility_from(
|
||||
next_btn, "enabled", backward=lambda v: not v
|
||||
)
|
||||
|
||||
def update_p2_btn():
|
||||
valid = True
|
||||
for f in state.selected_features:
|
||||
f_data = state.feature_feedback[f]
|
||||
if f_data["rating"] < 4:
|
||||
if len(f_data.get("improvements", "")) < 20:
|
||||
valid = False
|
||||
next_btn.set_enabled(valid)
|
||||
|
||||
update_p2_btn()
|
||||
|
||||
for feature in state.selected_features:
|
||||
with ui.card().classes("w-full max-w-lg p-6 mb-4"):
|
||||
ui.label(f"Feature: {feature}").classes("text-h6 text-primary")
|
||||
|
||||
slider = ui.slider(min=1, max=5, step=1, on_change=lambda: update_p2_btn()).bind_value(
|
||||
state.feature_feedback[feature], "rating"
|
||||
)
|
||||
|
||||
# High Rating Logic
|
||||
with ui.column().classes("w-full").bind_visibility_from(slider, "value", backward=lambda v: v >= 4):
|
||||
ui.textarea("What do you like about it?").classes("w-full").bind_value(
|
||||
state.feature_feedback[feature], "likes"
|
||||
)
|
||||
|
||||
# Low Rating Logic
|
||||
with ui.column().classes("w-full").bind_visibility_from(slider, "value", backward=lambda v: v < 4):
|
||||
ui.label("Other apps 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("Improvements (min 20 chars)", on_change=lambda: update_p2_btn()).classes(
|
||||
"w-full"
|
||||
).bind_value(state.feature_feedback[feature], "improvements")
|
||||
|
||||
# --- PAGE 3: DEMOGRAPHICS ---
|
||||
def show_page_3():
|
||||
container.clear()
|
||||
with container:
|
||||
ui.label("Step 3: 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 Responses", on_click=show_confirmation).classes("bg-blue")
|
||||
|
||||
# --- PAGE 4: CONFIRMATION (SUMMARY) ---
|
||||
def show_confirmation():
|
||||
container.clear()
|
||||
with container:
|
||||
ui.label("Review Your Responses").classes("text-h4 mb-4")
|
||||
with ui.card().classes("w-full max-w-2xl p-6"):
|
||||
with ui.column().classes("gap-1"):
|
||||
ui.label("Usage Info").classes("text-bold text-lg")
|
||||
ui.label(f"Frequency: {state.frequency}")
|
||||
ui.label(f"Features: {', '.join(state.selected_features)}")
|
||||
|
||||
ui.separator().classes("my-4")
|
||||
ui.label("Feedback").classes("text-bold text-lg")
|
||||
for f in state.selected_features:
|
||||
data = state.feature_feedback[f]
|
||||
ui.label(f"• {f}: Rating {data['rating']}/5")
|
||||
if data["rating"] >= 4:
|
||||
ui.label(f" Likes: {data['likes'] or 'N/A'}").classes("text-grey-7 ml-4")
|
||||
else:
|
||||
ui.label(f" Improvements: {data['improvements']}").classes("text-grey-7 ml-4")
|
||||
|
||||
ui.separator().classes("my-4")
|
||||
ui.label("Demographics").classes("text-bold text-lg")
|
||||
ui.label(f"Age: {state.age} | 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("Final Confirm & Submit", on_click=handle_final_submit).classes("bg-green")
|
||||
|
||||
# --- PAGE 5: FINAL 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("check_circle", color="green").classes("text-6xl")
|
||||
ui.label("Thank You!").classes("text-h3 mt-4")
|
||||
ui.label("Your responses have been recorded successfully.").classes("text-lg text-grey-6")
|
||||
ui.label("This survey is now closed.").classes("mt-4 italic")
|
||||
|
||||
show_page_1()
|
||||
|
||||
|
||||
create_survey()
|
||||
ui.run()
|
||||
46
requirements.txt
Normal file
46
requirements.txt
Normal file
@ -0,0 +1,46 @@
|
||||
aiofiles==25.1.0
|
||||
aiohappyeyeballs==2.6.1
|
||||
aiohttp==3.13.2
|
||||
aiosignal==1.4.0
|
||||
annotated-doc==0.0.4
|
||||
annotated-types==0.7.0
|
||||
anyio==4.12.0
|
||||
attrs==25.4.0
|
||||
bidict==0.23.1
|
||||
certifi==2025.11.12
|
||||
click==8.3.1
|
||||
docutils==0.22.4
|
||||
fastapi==0.128.0
|
||||
frozenlist==1.8.0
|
||||
h11==0.16.0
|
||||
httpcore==1.0.9
|
||||
httptools==0.7.1
|
||||
httpx==0.28.1
|
||||
idna==3.11
|
||||
ifaddr==0.2.0
|
||||
itsdangerous==2.2.0
|
||||
Jinja2==3.1.6
|
||||
markdown2==2.5.4
|
||||
MarkupSafe==3.0.3
|
||||
multidict==6.7.0
|
||||
nicegui==3.4.1
|
||||
orjson==3.11.5
|
||||
propcache==0.4.1
|
||||
pydantic==2.12.5
|
||||
pydantic_core==2.41.5
|
||||
Pygments==2.19.2
|
||||
python-dotenv==1.2.1
|
||||
python-engineio==4.13.0
|
||||
python-multipart==0.0.21
|
||||
python-socketio==5.16.0
|
||||
PyYAML==6.0.3
|
||||
simple-websocket==1.1.0
|
||||
starlette==0.50.0
|
||||
typing-inspection==0.4.2
|
||||
typing_extensions==4.15.0
|
||||
uvicorn==0.40.0
|
||||
uvloop==0.22.1
|
||||
watchfiles==1.1.1
|
||||
websockets==15.0.1
|
||||
wsproto==1.3.2
|
||||
yarl==1.22.0
|
||||
182
secret_survey.py
Normal file
182
secret_survey.py
Normal file
@ -0,0 +1,182 @@
|
||||
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()
|
||||
175
survey.py
Normal file
175
survey.py
Normal file
@ -0,0 +1,175 @@
|
||||
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()
|
||||
31
survey_results.json
Normal file
31
survey_results.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"usage": {
|
||||
"frequency": "Weekly",
|
||||
"features_used": [
|
||||
"News Feed",
|
||||
"Marketplace"
|
||||
]
|
||||
},
|
||||
"feature_feedback": {
|
||||
"News Feed": {
|
||||
"rating": 2,
|
||||
"likes": "",
|
||||
"other_apps": [
|
||||
"TikTok",
|
||||
"LinkedIn"
|
||||
],
|
||||
"improvements": "I do not like this features"
|
||||
},
|
||||
"Marketplace": {
|
||||
"rating": 5,
|
||||
"likes": "very useful",
|
||||
"other_apps": [],
|
||||
"improvements": ""
|
||||
}
|
||||
},
|
||||
"demographics": {
|
||||
"age": 32.0,
|
||||
"gender": "Female",
|
||||
"location": "kolkata"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user