import sqlite3 import models import ui def _load_from_template(conn: sqlite3.Connection, entries: list[dict]) -> None: """Offer to pre-fill session exercises from a template.""" templates = models.list_workout_templates(conn) if not templates: return 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): 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 def _prompt_exercises( conn: sqlite3.Connection, exercises: list[sqlite3.Row], entries: list[dict], ) -> bool: """Prompt user to add exercises manually. Returns False if aborted.""" 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 False return True 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.') def _confirm_and_save( conn: sqlite3.Connection, date_time: str, session_note: str | None, entries: list[dict], ) -> None: """Show session summary with option to reorder, then save or cancel.""" while True: 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) ], ) action = input("\nActions: (s)ave, (m)ove, (c)ancel\n> ").strip().lower() if action == "s": models.save_workout_session(conn, date_time, session_note, entries) print("Session saved!") ui.pause() return elif action == "m": if len(entries) < 2: print("Need at least 2 exercises to move.") ui.pause() continue from_pos = ui.prompt_int( "Move exercise #: ", min_val=1, max_val=len(entries) ) to_pos = ui.prompt_int( "Move to position #: ", min_val=1, max_val=len(entries) ) assert from_pos is not None and to_pos is not None item = entries.pop(from_pos - 1) entries.insert(to_pos - 1, item) elif action == "c": print("Session discarded.") ui.pause() return 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 ui.clear_screen() ui.print_header("Log Workout") print("(You can reorder exercises before saving.)\n") date_time = ui.prompt_datetime("Date/Time") session_note = ui.prompt_str("Session note (optional): ", required=False) entries: list[dict] = [] _load_from_template(conn, entries) if not _prompt_exercises(conn, exercises, entries): return _confirm_and_save(conn, date_time, session_note, entries) def _view_session_detail(conn: sqlite3.Connection, session_id: int) -> None: """Display a single session's exercises and handle deletion.""" ui.clear_screen() 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() def view_workout_sessions(conn: sqlite3.Connection) -> None: while True: ui.clear_screen() 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) ], ) 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 _view_session_detail(conn, sessions[idx]["id"])