aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--screens/exercises.py20
-rw-r--r--screens/sessions.py223
-rw-r--r--screens/templates.py31
3 files changed, 149 insertions, 125 deletions
diff --git a/screens/exercises.py b/screens/exercises.py
index 983f50f..d9a9cda 100644
--- a/screens/exercises.py
+++ b/screens/exercises.py
@@ -15,18 +15,18 @@ def manage_workout_exercises(conn: sqlite3.Connection) -> None:
print("5. Back")
choice = input("\n> ").strip()
if choice == "1":
- list_workout_exercises(conn)
+ _list_workout_exercises(conn)
elif choice == "2":
- add_workout_exercise(conn)
+ _add_workout_exercise(conn)
elif choice == "3":
- edit_workout_exercise(conn)
+ _edit_workout_exercise(conn)
elif choice == "4":
- delete_workout_exercise(conn)
+ _delete_workout_exercise(conn)
elif choice == "5":
break
-def list_workout_exercises(conn: sqlite3.Connection, pause: bool = True) -> None:
+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(
@@ -45,7 +45,7 @@ def list_workout_exercises(conn: sqlite3.Connection, pause: bool = True) -> None
ui.pause()
-def add_workout_exercise(conn: sqlite3.Connection) -> None:
+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?")
@@ -58,8 +58,8 @@ def add_workout_exercise(conn: sqlite3.Connection) -> None:
ui.pause()
-def edit_workout_exercise(conn: sqlite3.Connection) -> None:
- list_workout_exercises(conn, pause=False)
+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)
@@ -86,8 +86,8 @@ def edit_workout_exercise(conn: sqlite3.Connection) -> None:
ui.pause()
-def delete_workout_exercise(conn: sqlite3.Connection) -> None:
- list_workout_exercises(conn, pause=False)
+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)
diff --git a/screens/sessions.py b/screens/sessions.py
index 58b2f71..67ef9ba 100644
--- a/screens/sessions.py
+++ b/screens/sessions.py
@@ -4,73 +4,67 @@ 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] = []
+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 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
+ 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:
@@ -81,10 +75,9 @@ def log_workout(conn: sqlite3.Connection) -> None:
if choice.lower() == "d":
if not entries:
print("No exercises added. Aborting.")
- return
- break
+ return False
+ return True
- # Select existing exercise and prompt for set details
try:
eid = int(choice)
except ValueError:
@@ -119,7 +112,14 @@ def log_workout(conn: sqlite3.Connection) -> None:
)
print(f' "{ex["name"]}" added to session.')
- # Show summary and confirm before writing to DB
+
+def _confirm_and_save(
+ conn: sqlite3.Connection,
+ date_time: str,
+ session_note: str | None,
+ entries: list[dict],
+) -> None:
+ """Show session summary and save if confirmed."""
ui.clear_screen()
ui.print_header("Session Summary")
print(f"Date: {date_time}")
@@ -150,10 +150,59 @@ def log_workout(conn: sqlite3.Connection) -> None:
ui.pause()
+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")
+ 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()
- # List all sessions with exercise name preview
sessions = models.list_workout_sessions(conn)
if not sessions:
print("\nNo sessions recorded yet.")
@@ -167,7 +216,6 @@ def view_workout_sessions(conn: sqlite3.Connection) -> None:
],
)
- # Select a session to view details
choice = input("\nSelect # for details ('b' = back): ").strip()
if choice.lower() == "b":
break
@@ -180,33 +228,4 @@ def view_workout_sessions(conn: sqlite3.Connection) -> None:
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()
+ _view_session_detail(conn, sessions[idx]["id"])
diff --git a/screens/templates.py b/screens/templates.py
index 7c8ff60..fe122ce 100644
--- a/screens/templates.py
+++ b/screens/templates.py
@@ -15,18 +15,18 @@ def manage_workout_templates(conn: sqlite3.Connection) -> None:
print("5. Back")
choice = input("\n> ").strip()
if choice == "1":
- list_workout_templates(conn)
+ _list_workout_templates(conn)
elif choice == "2":
- create_workout_template(conn)
+ _create_workout_template(conn)
elif choice == "3":
- edit_workout_template(conn)
+ _edit_workout_template(conn)
elif choice == "4":
- delete_workout_template(conn)
+ _delete_workout_template(conn)
elif choice == "5":
break
-def list_workout_templates(conn: sqlite3.Connection, pause: bool = True) -> None:
+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(
@@ -37,7 +37,7 @@ def list_workout_templates(conn: sqlite3.Connection, pause: bool = True) -> None
ui.pause()
-def create_workout_template(conn: sqlite3.Connection) -> None:
+def _create_workout_template(conn: sqlite3.Connection) -> None:
name = ui.prompt_str("Template name: ")
assert name is not None
try:
@@ -50,25 +50,25 @@ def create_workout_template(conn: sqlite3.Connection) -> None:
# Immediately enter exercise editor
print("Now add exercises to the template.")
ui.pause()
- edit_template_exercises(conn, template_id)
+ _edit_template_exercises(conn, template_id)
-def edit_workout_template(conn: sqlite3.Connection) -> None:
+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)
+ _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)
+ _edit_template_exercises(conn, tid)
-def edit_template_exercises(conn: sqlite3.Connection, template_id: int) -> None:
+def _edit_template_exercises(conn: sqlite3.Connection, template_id: int) -> None:
"""Interactive editor for a template's exercise list."""
while True:
ui.clear_screen()
@@ -118,6 +118,7 @@ def edit_template_exercises(conn: sqlite3.Connection, template_id: int) -> None:
if action == "b":
break
+ # Rename the template
elif action == "n":
new_name = ui.prompt_str("New template name: ")
assert new_name is not None
@@ -128,6 +129,7 @@ def edit_template_exercises(conn: sqlite3.Connection, template_id: int) -> None:
print(f'Template "{new_name}" already exists.')
ui.pause()
+ # Add an exercise: pick from available, prompt for defaults, append and save
elif action == "a":
available = models.list_workout_exercises(conn)
if not available:
@@ -162,6 +164,7 @@ def edit_template_exercises(conn: sqlite3.Connection, template_id: int) -> None:
print(f'"{ex["name"]}" added to template.')
ui.pause()
+ # Remove an exercise by position
elif action == "r":
if not exercises_data:
print("No exercises to remove.")
@@ -176,6 +179,7 @@ def edit_template_exercises(conn: sqlite3.Connection, template_id: int) -> None:
print(f'Removed "{removed["exercise_name"]}".')
ui.pause()
+ # Reorder: pop from one position and insert at another
elif action == "m":
if len(exercises_data) < 2:
print("Need at least 2 exercises to move.")
@@ -193,6 +197,7 @@ def edit_template_exercises(conn: sqlite3.Connection, template_id: int) -> None:
print("Exercise moved.")
ui.pause()
+ # Edit default values for an existing exercise (Enter keeps current)
elif action == "e":
if not exercises_data:
print("No exercises to edit.")
@@ -231,12 +236,12 @@ def edit_template_exercises(conn: sqlite3.Connection, template_id: int) -> None:
ui.pause()
-def delete_workout_template(conn: sqlite3.Connection) -> None:
+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)
+ _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)