aboutsummaryrefslogtreecommitdiffstats
path: root/egometrics.py
diff options
context:
space:
mode:
authorThomas Vanbesien <tvanbesi@proton.me>2026-03-18 15:18:46 +0100
committerThomas Vanbesien <tvanbesi@proton.me>2026-03-18 15:18:46 +0100
commit81c3cbf634e1e6929317d3ffcd87df6426808417 (patch)
tree3c4045f2ae4329983c3fabe6710a1caa3168e06c /egometrics.py
parent7ceb22f1e12e3a040874a43b5e1177db83be15ed (diff)
downloadEgoMetrics-81c3cbf634e1e6929317d3ffcd87df6426808417.tar.gz
EgoMetrics-81c3cbf634e1e6929317d3ffcd87df6426808417.zip
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.
Diffstat (limited to 'egometrics.py')
-rw-r--r--egometrics.py578
1 files changed, 8 insertions, 570 deletions
diff --git a/egometrics.py b/egometrics.py
index d882a59..f45fa6d 100644
--- a/egometrics.py
+++ b/egometrics.py
@@ -1,11 +1,17 @@
import sqlite3
from db import get_connection, init_db
-import models
-import ui
+from screens import (
+ log_workout,
+ manage_workout_exercises,
+ manage_workout_templates,
+ view_workout_sessions,
+)
def main_menu(conn: sqlite3.Connection) -> None:
+ import ui
+
while True:
ui.clear_screen()
print("=== EgoMetrics ===\n")
@@ -27,574 +33,6 @@ def main_menu(conn: sqlite3.Connection) -> None:
break
-# --- Workout Exercises ---
-
-
-def manage_workout_exercises(conn: sqlite3.Connection) -> None:
- while True:
- ui.clear_screen()
- ui.print_header("Manage Workout 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_workout_exercises(conn)
- elif choice == "2":
- add_workout_exercise(conn)
- elif choice == "3":
- edit_workout_exercise(conn)
- elif choice == "4":
- delete_workout_exercise(conn)
- elif choice == "5":
- break
-
-
-def list_workout_exercises(conn: sqlite3.Connection, pause: bool = True) -> None:
- exercises = models.list_workout_exercises(conn)
- ui.print_header("All Workout Exercises")
- ui.print_table(
- ["ID", "Name", "BW?", "Note"],
- [
- [
- str(e["id"]),
- e["name"],
- "Yes" if e["bw_relative"] else "No",
- e["note"] or "",
- ]
- for e in exercises
- ],
- )
- if pause:
- ui.pause()
-
-
-def add_workout_exercise(conn: sqlite3.Connection) -> None:
- name = ui.prompt_str("Name: ")
- assert name is not None
- bw_relative = ui.confirm("Is weight relative to body weight?")
- note = ui.prompt_str("Note (optional): ", required=False)
- try:
- models.add_workout_exercise(conn, name, bw_relative, note)
- print(f'Exercise "{name}" added.')
- except sqlite3.IntegrityError:
- print(f'Exercise "{name}" already exists.')
- ui.pause()
-
-
-def edit_workout_exercise(conn: sqlite3.Connection) -> None:
- list_workout_exercises(conn, pause=False)
- eid = ui.prompt_int("\nExercise ID to edit: ")
- assert eid is not None
- ex = models.get_workout_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)
- bw_cur = "Yes" if ex["bw_relative"] else "No"
- print(f"BW-relative [{bw_cur}]: ", end="")
- bw_input = input().strip().lower()
- if bw_input in ("y", "yes"):
- bw_relative: bool | None = True
- elif bw_input in ("n", "no"):
- bw_relative = False
- else:
- bw_relative = None # keep current
- note = ui.prompt_str(f"Note [{ex['note'] or ''}]: ", required=False)
- try:
- models.update_workout_exercise(conn, eid, name, bw_relative, note)
- print("Exercise updated.")
- except sqlite3.IntegrityError:
- print(f'An exercise named "{name}" already exists.')
- ui.pause()
-
-
-def delete_workout_exercise(conn: sqlite3.Connection) -> None:
- list_workout_exercises(conn, pause=False)
- eid = ui.prompt_int("\nExercise ID to delete: ")
- assert eid is not None
- ex = models.get_workout_exercise(conn, eid)
- if not ex:
- print("Exercise not found.")
- return
- if not ui.confirm(f'Delete "{ex["name"]}"?'):
- return
- try:
- models.delete_workout_exercise(conn, eid)
- print("Exercise deleted.")
- except sqlite3.IntegrityError:
- print(
- f'Cannot delete "{ex["name"]}" — it is used in existing sessions or templates.'
- )
- ui.pause()
-
-
-# --- Workout Sessions ---
-
-
-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()
-
-
-# --- Workout Templates ---
-
-
-def manage_workout_templates(conn: sqlite3.Connection) -> None:
- while True:
- ui.clear_screen()
- ui.print_header("Manage Workout Templates")
- print("1. List Templates")
- print("2. Create Template")
- print("3. View/Edit Template")
- print("4. Delete Template")
- print("5. Back")
- choice = input("\n> ").strip()
- if choice == "1":
- list_workout_templates(conn)
- elif choice == "2":
- create_workout_template(conn)
- elif choice == "3":
- edit_workout_template(conn)
- elif choice == "4":
- delete_workout_template(conn)
- elif choice == "5":
- break
-
-
-def list_workout_templates(conn: sqlite3.Connection, pause: bool = True) -> None:
- templates = models.list_workout_templates(conn)
- ui.print_header("All Workout Templates")
- ui.print_table(
- ["ID", "Name", "Exercises"],
- [[str(t["id"]), t["name"], t["exercises"] or ""] for t in templates],
- )
- if pause:
- ui.pause()
-
-
-def create_workout_template(conn: sqlite3.Connection) -> None:
- name = ui.prompt_str("Template name: ")
- assert name is not None
- try:
- template_id = models.create_workout_template(conn, name)
- print(f'Template "{name}" created.')
- except sqlite3.IntegrityError:
- print(f'Template "{name}" already exists.')
- ui.pause()
- return
- # Immediately enter exercise editor
- print("Now add exercises to the template.")
- ui.pause()
- edit_template_exercises(conn, template_id)
-
-
-def edit_workout_template(conn: sqlite3.Connection) -> None:
- templates = models.list_workout_templates(conn)
- if not templates:
- print("\nNo templates defined.")
- return
- list_workout_templates(conn, pause=False)
- tid = ui.prompt_int("\nTemplate ID to edit: ")
- assert tid is not None
- template = models.get_workout_template(conn, tid)
- if not template:
- print("Template not found.")
- return
- edit_template_exercises(conn, tid)
-
-
-def edit_template_exercises(conn: sqlite3.Connection, template_id: int) -> None:
- """Interactive editor for a template's exercise list."""
- while True:
- ui.clear_screen()
- template, entries = models.get_workout_template_detail(conn, template_id)
- assert template is not None
- ui.print_header(f'Template: {template["name"]}')
-
- # Build in-memory list from current DB state
- exercises_data: list[dict] = [
- {
- "exercise_id": e["exercise_id"],
- "exercise_name": e["exercise_name"],
- "bw_relative": bool(e["bw_relative"]),
- "sets": e["sets"],
- "reps": e["reps"],
- "lsrpe": e["lsrpe"],
- "rest_time": e["rest_time"],
- "note": e["note"],
- }
- for e in entries
- ]
-
- if exercises_data:
- ui.print_table(
- ["#", "Exercise", "Sets", "Reps", "LSRPE", "Rest", "Note"],
- [
- [
- str(i),
- e["exercise_name"],
- str(e["sets"]),
- str(e["reps"]),
- str(e["lsrpe"]),
- f"{e['rest_time']}s",
- e["note"] or "",
- ]
- for i, e in enumerate(exercises_data, 1)
- ],
- )
- else:
- print(" (no exercises)")
-
- print(
- "\nActions: (a)dd exercise, (r)emove, (m)ove, (e)dit defaults, (n)ame, (b)ack"
- )
- action = input("> ").strip().lower()
-
- if action == "b":
- break
-
- elif action == "n":
- new_name = ui.prompt_str("New template name: ")
- assert new_name is not None
- try:
- models.update_workout_template_name(conn, template_id, new_name)
- print(f'Template renamed to "{new_name}".')
- except sqlite3.IntegrityError:
- print(f'Template "{new_name}" already exists.')
- ui.pause()
-
- elif action == "a":
- available = models.list_workout_exercises(conn)
- if not available:
- print("No exercises defined. Create some first.")
- continue
- print("\nAvailable exercises:")
- for e in available:
- print(f" {e['id']}. {e['name']}")
- eid = ui.prompt_int("\nExercise ID to add: ")
- assert eid is not None
- ex = models.get_workout_exercise(conn, eid)
- if not ex:
- print("Exercise not found.")
- continue
- print(f"\n -- {ex['name']} (defaults) --")
- sets = ui.prompt_int(" Sets: ", min_val=1)
- reps = ui.prompt_int(" Reps: ", min_val=1)
- lsrpe = ui.prompt_int(" LSRPE: ", min_val=1, max_val=10)
- rest_time = ui.prompt_int(" Rest time in seconds: ", min_val=0)
- note = ui.prompt_str(" Note (optional): ", required=False)
- exercises_data.append(
- {
- "exercise_id": eid,
- "sets": sets,
- "reps": reps,
- "lsrpe": lsrpe,
- "rest_time": rest_time,
- "note": note,
- }
- )
- models.save_workout_template_exercises(conn, template_id, exercises_data)
- print(f'"{ex["name"]}" added to template.')
- ui.pause()
-
- elif action == "r":
- if not exercises_data:
- print("No exercises to remove.")
- continue
- pos = ui.prompt_int("Position # to remove: ", min_val=1)
- assert pos is not None
- if pos > len(exercises_data):
- print("Invalid position.")
- continue
- removed = exercises_data.pop(pos - 1)
- models.save_workout_template_exercises(conn, template_id, exercises_data)
- print(f'Removed "{removed["exercise_name"]}".')
- ui.pause()
-
- elif action == "m":
- if len(exercises_data) < 2:
- print("Need at least 2 exercises to move.")
- continue
- from_pos = ui.prompt_int(
- "Move from position #: ", min_val=1, max_val=len(exercises_data)
- )
- to_pos = ui.prompt_int(
- "Move to position #: ", min_val=1, max_val=len(exercises_data)
- )
- assert from_pos is not None and to_pos is not None
- item = exercises_data.pop(from_pos - 1)
- exercises_data.insert(to_pos - 1, item)
- models.save_workout_template_exercises(conn, template_id, exercises_data)
- print("Exercise moved.")
- ui.pause()
-
- elif action == "e":
- if not exercises_data:
- print("No exercises to edit.")
- continue
- pos = ui.prompt_int("Position # to edit: ", min_val=1)
- assert pos is not None
- if pos > len(exercises_data):
- print("Invalid position.")
- continue
- entry = exercises_data[pos - 1]
- print(f'\n -- {entry["exercise_name"]} (edit defaults) --')
- entry["sets"] = ui.prompt_int(
- f" Sets [{entry['sets'] or ''}]: ", min_val=1, default=entry["sets"]
- )
- entry["reps"] = ui.prompt_int(
- f" Reps [{entry['reps'] or ''}]: ", min_val=1, default=entry["reps"]
- )
- entry["lsrpe"] = ui.prompt_int(
- f" LSRPE [{entry['lsrpe']}]: ",
- min_val=1,
- max_val=10,
- default=entry["lsrpe"],
- )
- entry["rest_time"] = ui.prompt_int(
- f" Rest time in seconds [{entry['rest_time']}]: ",
- min_val=0,
- default=entry["rest_time"],
- )
- entry["note"] = ui.prompt_str(
- f" Note [{entry['note'] or ''}]: ",
- required=False,
- default=entry["note"],
- )
- models.save_workout_template_exercises(conn, template_id, exercises_data)
- print("Defaults updated.")
- ui.pause()
-
-
-def delete_workout_template(conn: sqlite3.Connection) -> None:
- templates = models.list_workout_templates(conn)
- if not templates:
- print("\nNo templates defined.")
- return
- list_workout_templates(conn, pause=False)
- tid = ui.prompt_int("\nTemplate ID to delete: ")
- assert tid is not None
- template = models.get_workout_template(conn, tid)
- if not template:
- print("Template not found.")
- return
- if not ui.confirm(f'Delete template "{template["name"]}"?'):
- return
- models.delete_workout_template(conn, tid)
- print("Template deleted.")
- ui.pause()
-
-
-# --- Entry Point ---
-
-
def main() -> None:
init_db()
conn = get_connection()