From 81c3cbf634e1e6929317d3ffcd87df6426808417 Mon Sep 17 00:00:00 2001 From: Thomas Vanbesien Date: Wed, 18 Mar 2026 15:18:46 +0100 Subject: Refactor screen logic into screens/ package Split egometrics.py (610 lines) into domain-specific modules: exercises, sessions, and templates. Entry point now just wires the main menu to the screen modules. --- screens/sessions.py | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 screens/sessions.py (limited to 'screens/sessions.py') diff --git a/screens/sessions.py b/screens/sessions.py new file mode 100644 index 0000000..58b2f71 --- /dev/null +++ b/screens/sessions.py @@ -0,0 +1,212 @@ +import sqlite3 + +import models +import ui + + +def log_workout(conn: sqlite3.Connection) -> None: + exercises = models.list_workout_exercises(conn) + if not exercises: + print("No exercises defined. Add some first via Manage Workout Exercises.") + return + + # Session metadata + ui.clear_screen() + ui.print_header("Log Workout") + date_time = ui.prompt_datetime("Date/Time") + session_note = ui.prompt_str("Session note (optional): ", required=False) + + # Check if user wants to start from a template + entries: list[dict] = [] + templates = models.list_workout_templates(conn) + if templates: + print("\nStart from a template?") + for i, t in enumerate(templates, 1): + print(f" {i}. {t['name']}") + print(" 0. No template (blank session)") + choice = input("\n> ").strip() + try: + idx = int(choice) + if 1 <= idx <= len(templates): + # Load template exercises as defaults + template_id = templates[idx - 1]["id"] + _, tmpl_entries = models.get_workout_template_detail(conn, template_id) + print(f'\nUsing template "{templates[idx - 1]["name"]}"') + print("Press Enter to accept defaults, or type new values.\n") + for te in tmpl_entries: + print(f" -- {te['exercise_name']} --") + sets = ui.prompt_int( + f" Sets [{te['sets'] or ''}]: ", + min_val=1, + default=te["sets"], + ) + assert sets is not None + reps, weight, rest_time = ui.prompt_sets_detail( + sets, + default_reps=te["reps"], + default_rest=te["rest_time"], + bw_relative=bool(te["bw_relative"]), + ) + lsrpe = ui.prompt_int(" Last Set RPE: ", min_val=1, max_val=10) + note = ui.prompt_str( + f" Note [{te['note'] or ''}]: ", + required=False, + default=te["note"], + ) + entries.append( + { + "exercise_id": te["exercise_id"], + "exercise_name": te["exercise_name"], + "bw_relative": bool(te["bw_relative"]), + "sets": sets, + "reps": reps, + "weight": weight, + "rest_time": rest_time, + "lsrpe": lsrpe, + "note": note, + } + ) + print(f' "{te["exercise_name"]}" added to session.') + except (ValueError, IndexError): + pass # Invalid input or 0 — proceed without template + + # Build exercise list — collect everything in memory before saving + while True: + print("\nAvailable exercises:") + for e in exercises: + print(f" {e['id']}. {e['name']}") + print() + choice = input("Select exercise ID ('d' = done): ").strip() + + if choice.lower() == "d": + if not entries: + print("No exercises added. Aborting.") + return + break + + # Select existing exercise and prompt for set details + try: + eid = int(choice) + except ValueError: + print("Invalid input.") + continue + ex = models.get_workout_exercise(conn, eid) + if not ex: + print("Exercise not found.") + continue + + print(f"\n -- {ex['name']} --") + sets = ui.prompt_int(" Sets: ", min_val=1) + assert sets is not None + reps, weight, rest_time = ui.prompt_sets_detail( + sets, bw_relative=bool(ex["bw_relative"]) + ) + lsrpe = ui.prompt_int(" Last Set RPE: ", min_val=1, max_val=10) + note = ui.prompt_str(" Note (optional): ", required=False) + + entries.append( + { + "exercise_id": eid, + "exercise_name": ex["name"], + "bw_relative": bool(ex["bw_relative"]), + "sets": sets, + "reps": reps, + "weight": weight, + "rest_time": rest_time, + "lsrpe": lsrpe, + "note": note, + } + ) + print(f' "{ex["name"]}" added to session.') + + # Show summary and confirm before writing to DB + ui.clear_screen() + ui.print_header("Session Summary") + print(f"Date: {date_time}") + if session_note: + print(f"Note: {session_note}") + ui.print_table( + ["#", "Exercise", "Sets", "Reps", "Weight", "Rest", "LSRPE", "Note"], + [ + [ + str(i), + e["exercise_name"], + str(e["sets"]), + str(e["reps"]).replace(",", "/"), + ui.format_weight(e["weight"], e["bw_relative"]), + ui.format_rest_time(e["rest_time"]), + str(e["lsrpe"]), + e["note"] or "", + ] + for i, e in enumerate(entries, 1) + ], + ) + + if ui.confirm("\nSave this session?"): + models.save_workout_session(conn, date_time, session_note, entries) + print("Session saved!") + else: + print("Session discarded.") + ui.pause() + + +def view_workout_sessions(conn: sqlite3.Connection) -> None: + while True: + ui.clear_screen() + # List all sessions with exercise name preview + sessions = models.list_workout_sessions(conn) + if not sessions: + print("\nNo sessions recorded yet.") + return + ui.print_header("Past Workout Sessions") + ui.print_table( + ["#", "Date", "Exercises", "Note"], + [ + [str(i), s["date_time"], s["exercises"] or "", s["note"] or ""] + for i, s in enumerate(sessions, 1) + ], + ) + + # Select a session to view details + choice = input("\nSelect # for details ('b' = back): ").strip() + if choice.lower() == "b": + break + try: + idx = int(choice) - 1 + if idx < 0 or idx >= len(sessions): + print("Invalid selection.") + continue + except ValueError: + print("Invalid input.") + continue + + # Show full session detail with all exercise entries + ui.clear_screen() + session_id = sessions[idx]["id"] + session, entries = models.get_workout_session_detail(conn, session_id) + assert session is not None + ui.print_header(f"Session: {session['date_time']}") + if session["note"]: + print(f"Note: {session['note']}\n") + ui.print_table( + ["#", "Exercise", "Sets", "Reps", "Weight", "Rest", "LSRPE", "Note"], + [ + [ + str(e["position"]), + e["exercise_name"], + str(e["sets"]), + str(e["reps"]).replace(",", "/"), + ui.format_weight(str(e["weight"]), bool(e["bw_relative"])), + ui.format_rest_time(e["rest_time"]), + str(e["lsrpe"]), + e["note"] or "", + ] + for e in entries + ], + ) + action = input("\nActions: (b)ack, (d)elete session\n> ").strip().lower() + if action == "d": + if ui.confirm("Delete this session?"): + models.delete_workout_session(conn, session_id) + print("Session deleted.") + ui.pause() -- cgit v1.2.3