import sqlite3 from db import get_connection, init_db import models import ui def main_menu(conn: sqlite3.Connection) -> None: while True: print("\n=== EgoMetrics ===\n") print("1. Log Workout") print("2. View Sessions") print("3. Manage Exercises") print("4. Quit") choice = input("\n> ").strip() if choice == "1": log_workout(conn) elif choice == "2": view_sessions(conn) elif choice == "3": manage_exercises(conn) elif choice == "4": break def manage_exercises(conn: sqlite3.Connection) -> None: while True: ui.print_header("Manage Exercises") print("1. List Exercises") print("2. Add Exercise") print("3. Edit Exercise") print("4. Delete Exercise") print("5. Back") choice = input("\n> ").strip() if choice == "1": list_exercises(conn) elif choice == "2": add_exercise(conn) elif choice == "3": edit_exercise(conn) elif choice == "4": delete_exercise(conn) elif choice == "5": break def list_exercises(conn: sqlite3.Connection) -> None: exercises = models.list_exercises(conn) ui.print_header("All Exercises") ui.print_table( ["ID", "Name", "Note"], [[str(e["id"]), e["name"], e["note"] or ""] for e in exercises], ) def add_exercise(conn: sqlite3.Connection) -> None: name = ui.prompt_str("Name: ") assert name is not None note = ui.prompt_str("Note (optional): ", required=False) try: models.add_exercise(conn, name, note) print(f'Exercise "{name}" added.') except sqlite3.IntegrityError: print(f'Exercise "{name}" already exists.') def edit_exercise(conn: sqlite3.Connection) -> None: list_exercises(conn) eid = ui.prompt_int("\nExercise ID to edit: ") assert eid is not None ex = models.get_exercise(conn, eid) if not ex: print("Exercise not found.") return print(f'Editing "{ex["name"]}" (leave blank to keep current value)') name = ui.prompt_str(f"Name [{ex['name']}]: ", required=False) note = ui.prompt_str(f"Note [{ex['note'] or ''}]: ", required=False) try: models.update_exercise(conn, eid, name, note) print("Exercise updated.") except sqlite3.IntegrityError: print(f'An exercise named "{name}" already exists.') def delete_exercise(conn: sqlite3.Connection) -> None: list_exercises(conn) eid = ui.prompt_int("\nExercise ID to delete: ") assert eid is not None ex = models.get_exercise(conn, eid) if not ex: print("Exercise not found.") return if not ui.confirm(f'Delete "{ex["name"]}"?'): return try: models.delete_exercise(conn, eid) print("Exercise deleted.") except sqlite3.IntegrityError: print(f'Cannot delete "{ex["name"]}" — it is used in existing sessions.') def log_workout(conn: sqlite3.Connection) -> None: exercises = models.list_exercises(conn) if not exercises: print("No exercises defined. Add some first via Manage Exercises.") return # Session metadata ui.print_header("Log Workout") date_time = ui.prompt_datetime("Date/Time") session_note = ui.prompt_str("Session note (optional): ", required=False) # Build exercise list — collect everything in memory before saving entries = [] while True: print("\nAvailable exercises:") for e in exercises: print(f" {e['id']}. {e['name']}") print() choice = input("Select exercise ID ('n' = new exercise, 'd' = done): ").strip() if choice.lower() == "d": if not entries: print("No exercises added. Aborting.") return break # Inline exercise creation elif choice.lower() == "n": name = ui.prompt_str("Exercise name: ") assert name is not None note = ui.prompt_str("Note (optional): ", required=False) try: models.add_exercise(conn, name, note) exercises = models.list_exercises(conn) print(f'Exercise "{name}" added.') except sqlite3.IntegrityError: print(f'Exercise "{name}" already exists.') continue # Select existing exercise and prompt for set details try: eid = int(choice) except ValueError: print("Invalid input.") continue ex = models.get_exercise(conn, eid) if not ex: print("Exercise not found.") continue print(f"\n -- {ex['name']} --") sets = ui.prompt_int(" Sets: ", min_val=1) reps = ui.prompt_int(" Reps: ", min_val=1) rpe = ui.prompt_int(" RPE (optional): ", required=False, min_val=1, max_val=10) rest_time = ui.prompt_int( " Rest time in seconds (optional): ", required=False, min_val=0 ) lsrpe = ui.prompt_int( " Last Set RPE (optional): ", required=False, min_val=1, max_val=10 ) note = ui.prompt_str(" Note (optional): ", required=False) entries.append( { "exercise_id": eid, "exercise_name": ex["name"], "sets": sets, "reps": reps, "rpe": rpe, "rest_time": rest_time, "lsrpe": lsrpe, "note": note, } ) print(f' "{ex["name"]}" added to session.') # Show summary and confirm before writing to DB ui.print_header("Session Summary") print(f"Date: {date_time}") if session_note: print(f"Note: {session_note}") ui.print_table( ["#", "Exercise", "Sets", "Reps", "RPE", "Rest", "LSRPE", "Note"], [ [ str(i), e["exercise_name"], str(e["sets"]), str(e["reps"]), str(e["rpe"] or ""), f"{e['rest_time']}s" if e["rest_time"] else "", str(e["lsrpe"] or ""), e["note"] or "", ] for i, e in enumerate(entries, 1) ], ) if ui.confirm("\nSave this session?"): models.save_session(conn, date_time, session_note, entries) print("Session saved!") else: print("Session discarded.") def view_sessions(conn: sqlite3.Connection) -> None: while True: # List all sessions with exercise name preview sessions = models.list_sessions(conn) if not sessions: print("\nNo sessions recorded yet.") return ui.print_header("Past 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 session_id = sessions[idx]["id"] session, entries = models.get_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", "RPE", "Rest", "LSRPE", "Note"], [ [ str(e["position"]), e["exercise_name"], str(e["sets"]), str(e["reps"]), str(e["rpe"] or ""), f"{e['rest_time']}s" if e["rest_time"] else "", str(e["lsrpe"] or ""), 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_session(conn, session_id) print("Session deleted.") def main() -> None: init_db() conn = get_connection() try: main_menu(conn) except KeyboardInterrupt: print("\nGoodbye!") finally: conn.close() if __name__ == "__main__": main()