From aee4c33b3028a7d57a26288e8a1fb8e183d3a8b2 Mon Sep 17 00:00:00 2001 From: Gourav Kumar Date: Thu, 1 Jan 2026 12:26:18 +0530 Subject: [PATCH] gyjhg --- constants.py | 26 +++++ survey.py | 291 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 248 insertions(+), 69 deletions(-) create mode 100644 constants.py diff --git a/constants.py b/constants.py new file mode 100644 index 0000000..3fdae3d --- /dev/null +++ b/constants.py @@ -0,0 +1,26 @@ +PAGE_TITLE = "ARQ Usage Survey" + +USAGE_FREQUENCY_OPTIONS = ["daily", "weekly", "monthly", "Never used it"] + +TASK_OPTIONS = { + "analysis": "Stock Analysis", + "portfolio": "Portfolio Analysis", + "consensus": "Consensus Data", + "screening": "Stock Screening", + "Others": "Others", +} + +EXCEL_FEATURE_LIST = ["vlookup", "formulae", "others"] + +ABANDONMENT_OPTIONS = ["data incomplete", "calculations not possible", "others"] + +PRIORITY_TASKS = ["speed", "accuracy", "others"] + +ARQ_HELPED = [ + "By sending reminders", + "Helped extract data", + "Consensus data/Base rate analysis", + "Helped with screening", + "Helped with preliminary analysis of company/portfolio", + "Others", +] diff --git a/survey.py b/survey.py index b5c950a..d540a01 100644 --- a/survey.py +++ b/survey.py @@ -2,6 +2,8 @@ import json from nicegui import ui +import constants as const + # --- Survey Options --- FREQUENCY_OPTIONS = ["Daily", "Weekly", "Monthly", "Rarely"] FEATURE_OPTIONS = ["News Feed", "Marketplace", "Groups", "Messenger", "Stories"] @@ -12,19 +14,29 @@ GENDER_OPTIONS = ["Male", "Female", "Non-binary", "Prefer not to say"] class SurveyState: def __init__(self): self.frequency = None + self.no_use_reason = "" self.selected_features = [] + self.other_tasks = "" self.feature_feedback = {} - self.age = None - self.gender = None - self.location = "" + self.suggestions = "" + self.priority_tasks = [] + + def convert_to_json(self): + return { + "usage": { + "frequency": self.frequency, + "features_used": self.selected_features, + "other_tasks": self.other_tasks, + }, + "feature_feedback": self.feature_feedback, + "Reason for not using": self.no_use_reason, + "suggestions": self.suggestions, + "priority_tasks": self.priority_tasks, + } 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: + data = self.convert_to_json() + with open("results.json", "w") as f: json.dump(data, f, indent=4) @@ -38,77 +50,180 @@ def create_survey(): 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("ARQ Usage").classes("text-h4 mb-4") - ui.label("What features do you use?").classes("text-bold mt-4") + with ui.card().classes("w-full max-w-lg p-6"): + ui.label("How frequently do you use ARQ?").classes("text-bold") + p1_radio = ui.radio(options=const.USAGE_FREQUENCY_OPTIONS).bind_value(state, "frequency") + + with ui.column().classes("w-full").bind_visibility_from( + p1_radio, "value", backward=lambda v: v == "Never used it" + ): + ui.label("What are the reasons for not using ARQ?") + no_use_reason = ( + ui.input(validation={"Minimum 20 characters": lambda value: len(value) >= 20}) + .classes("w-full") + .bind_value(state, "no_use_reason") + ) + + ui.label("What tasks have you performed using this app?").classes("text-bold mt-4") p1_select = ( - ui.select(options=FEATURE_OPTIONS, multiple=True) + ui.select(options=const.TASK_OPTIONS, multiple=True) + .props("use-chips") .classes("w-full") .bind_value(state, "selected_features") ) - next_btn = ui.button("Next", on_click=show_page_2).classes("mt-6 w-full") + with ui.column().classes("w-full").bind_visibility_from( + p1_select, "value", backward=lambda v: "Others" in v + ): + ui.label("What other tasks have you performed using ARQ?") + ui.input().classes("w-full").bind_value(state, "other_tasks") + + with ui.row().classes("w-full"): + next_btn = ui.button("Next", on_click=show_page_2).classes("mt-6 w-full") + ui.tooltip( + "Please answer all questions (min 20 chars for 'Reasons for failure')" + ).bind_visibility_from(next_btn, "enabled", backward=lambda v: not v) # Validation - ui.timer( - 0.1, lambda: next_btn.set_enabled(state.frequency is not None and len(state.selected_features) > 0) - ) + def enable_next(): + if p1_radio.value == "Never used it": + if len(no_use_reason.value) < 20: + return False + return state.frequency is not None and len(state.selected_features) > 0 + + ui.timer(0.1, lambda: next_btn.set_enabled(enable_next())) # --- 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 == "Others": + continue if f not in state.feature_feedback: - state.feature_feedback[f] = {"rating": 5, "likes": "", "other_apps": [], "improvements": ""} + state.feature_feedback[f] = { + "completed_on_arq": None, + "rating": 0, + "completed_how": None, + "arq_helped": [], + "excel_features": [], + "other_apps": "", + "abandoned_why": [], + "abandoned_why_others": "", + } 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 + valid = True + for f, ob in state.feature_feedback.items(): + if ob["completed_on_arq"] is None: + valid = False + if ob["completed_on_arq"] == "Yes" and ob["rating"] == 0: + valid = False + if ob["completed_on_arq"] == "No" and ob["completed_how"] is None: + valid = False + if ob["completed_how"] == "both" and not ob["arq_helped"]: + valid = False + if ob["completed_how"] in ["both", "excel"] and not ob["excel_features"]: + valid = False + if ob["completed_how"] == "abandoned" and not ob["abandoned_why"]: + valid = False + if ob["completed_how"] == "other" and not ob["other_apps"]: + valid = False + return valid for feature in state.selected_features: + if feature == "Others": + continue with ui.card().classes("w-full max-w-lg p-6 mb-4"): - ui.label(f"Rate the {feature}").classes("text-h6 text-primary") + ui.label(f"{const.TASK_OPTIONS[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") + ui.label("Were you able to complete this task on ARQ").classes("text-bold mt-4") + p2q1_select = ( + ui.radio(["Yes", "No"]) + .props("inline") + .bind_value(state.feature_feedback[feature], "completed_on_arq") ) - # 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" + # Star Rating (Quasar based) + with ui.column().classes("w-full").bind_visibility_from( + p2q1_select, "value", backward=lambda v: v == "Yes" + ): + ui.label("How would you rate the features available to complete this task").classes( + "text-bold mt-4" + ) + rating = ( + ui.rating(value=5, icon="star") + .classes("text-3xl") + .bind_value(state.feature_feedback[feature], "rating") ) - # 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" + with ui.column().classes("w-full").bind_visibility_from( + p2q1_select, "value", backward=lambda v: v == "No" + ): + ui.label("How did you complete this task?") + p2q2_select = ( + ui.radio( + options={ + "both": "Using both ARQ and Excel", + "excel": "Entirely on Excel", + "other": "Using other applications", + "abandoned": "Task abandoned", + } + ) + .props("inline") + .bind_value(state.feature_feedback[feature], "completed_how") ) - ui.textarea("How can this be improved? (min 20 characters)").classes("w-full").bind_value( - state.feature_feedback[feature], "improvements" + with ui.column().classes("w-full").bind_visibility_from( + p2q2_select, "value", backward=lambda v: v == "both" + ): + ui.label("How did ARQ help you with this task?") + ui.select(options=const.ARQ_HELPED, multiple=True).props("use-chips").classes( + "w-full" + ).bind_value(state.feature_feedback[feature], "arq_helped") + + with ui.column().classes("w-full").bind_visibility_from( + p2q2_select, "value", backward=lambda v: v == "both" or v == "excel" + ): + ui.label("Which Excel features did you require to complete this task?") + ui.select(options=const.EXCEL_FEATURE_LIST, multiple=True).props("use-chips").classes( + "w-full" + ).bind_value(state.feature_feedback[feature], "excel_features") + + with ui.column().classes("w-full").bind_visibility_from( + p2q2_select, "value", backward=lambda v: v == "other" + ): + ui.input(label="Which other applications helped you with this task?").classes( + "w-full" + ).bind_value(state.feature_feedback[feature], "other_apps") + + with ui.column().classes("w-full").bind_visibility_from( + p2q2_select, "value", backward=lambda v: v == "abandoned" + ): + ui.label("What were the reasons for abandoning this task?") + p2q3_select = ( + ui.select(options=const.ABANDONMENT_OPTIONS, multiple=True) + .props("use-chips") + .classes("w-full") + .bind_value(state.feature_feedback[feature], "abandoned_why") + ) + with ui.column().classes("w-full").bind_visibility_from( + p2q3_select, "value", backward=lambda v: "others" in v + ): + ui.input(label="Describe the reasons for abandoning this task?").classes("w-full").bind_value( + state.feature_feedback[feature], "abandoned_why_others" ) 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( + ui.tooltip("Please answer all questions").bind_visibility_from( next_btn, "enabled", backward=lambda v: not v ) @@ -119,43 +234,64 @@ def create_survey(): def show_page_3(): container.clear() with container: - ui.label("Demographics").classes("text-h4 mb-4") + ui.label("Final thoughts").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") + suggestions = ( + ui.textarea("Do you have any suggestions for the app") + .classes("w-full") + .bind_value(state, "suggestions") + ) + ui.label("Select exactly 2 features to prioritize:") + priority_tasks = ( + ui.select( + options=const.PRIORITY_TASKS, + multiple=True, + validation={"Please select only 2 features": lambda value: len(value) <= 2}, + ) + .props("use-chips") + .classes("w-full") + .bind_value(state, "priority_tasks") + ) 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") + next_btn = ui.button("Review Survey", on_click=show_confirmation).classes("bg-blue") + ui.tooltip("Please answer all questions").bind_visibility_from( + next_btn, "enabled", backward=lambda v: not v + ) + + ui.timer( + 0.5, + lambda: next_btn.set_enabled(len(state.suggestions) > 0 and len(state.priority_tasks) == 2), + ) # --- 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)}") + # 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") + # 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}" - ) + # 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") + # 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(): @@ -171,5 +307,22 @@ def create_survey(): show_page_1() +with ui.right_drawer(value=True, fixed=True).classes("bg-blue-50 p-4").props("elevated width=450") as right_drawer: + + ui.label("Live Response Tracker").classes("text-grey font-bold") + debug_display = ui.markdown().classes("font-mono w-full") + + def update_debug(): + # Convert dictionary to a pretty-printed JSON string wrapped in markdown code blocks + data = state.convert_to_json() + formatted_json = json.dumps(data, indent=2) + debug_display.content = f"```json\n{formatted_json}\n```" + + create_survey() -ui.run() + + +ui.timer(0.1, update_debug) + + +ui.run(port=8081)