aboutsummaryrefslogtreecommitdiffstats
path: root/ui.py
diff options
context:
space:
mode:
Diffstat (limited to 'ui.py')
-rw-r--r--ui.py140
1 files changed, 139 insertions, 1 deletions
diff --git a/ui.py b/ui.py
index 2d63c06..ec4c1ee 100644
--- a/ui.py
+++ b/ui.py
@@ -1,6 +1,11 @@
+import os
from datetime import datetime
+def clear_screen() -> None:
+ os.system("cls" if os.name == "nt" else "clear")
+
+
def print_header(title: str) -> None:
print(f"\n--- {title} ---\n")
@@ -23,10 +28,13 @@ def prompt_int(
required: bool = True,
min_val: int | None = None,
max_val: int | None = None,
+ default: int | None = None,
) -> int | None:
while True:
val = input(prompt).strip()
if not val:
+ if default is not None:
+ return default
if not required:
return None
print("This field is required.")
@@ -45,10 +53,44 @@ def prompt_int(
return n
-def prompt_str(prompt: str, required: bool = True) -> str | None:
+def prompt_float(
+ prompt: str,
+ required: bool = True,
+ min_val: float | None = None,
+ max_val: float | None = None,
+ default: float | None = None,
+) -> float | None:
while True:
val = input(prompt).strip()
if not val:
+ if default is not None:
+ return default
+ if not required:
+ return None
+ print("This field is required.")
+ continue
+ try:
+ n = float(val)
+ except ValueError:
+ print("Please enter a valid number.")
+ continue
+ if min_val is not None and n < min_val:
+ print(f"Must be at least {min_val}.")
+ continue
+ if max_val is not None and n > max_val:
+ print(f"Must be at most {max_val}.")
+ continue
+ return round(n, 1)
+
+
+def prompt_str(
+ prompt: str, required: bool = True, default: str | None = None
+) -> str | None:
+ while True:
+ val = input(prompt).strip()
+ if not val:
+ if default is not None:
+ return default
if not required:
return None
print("This field is required.")
@@ -56,6 +98,98 @@ def prompt_str(prompt: str, required: bool = True) -> str | None:
return val
+def _fmt_kg(val: str) -> str:
+ """Format a single weight value, dropping '.0' for whole numbers."""
+ n = float(val)
+ return str(int(n)) if n == int(n) else f"{n:.1f}"
+
+
+def format_weight(weight_str: str, bw_relative: bool) -> str:
+ """Format comma-separated weight values for display."""
+ values = weight_str.split(",")
+ if bw_relative:
+ parts = []
+ for v in values:
+ n = float(v)
+ label = _fmt_kg(v)
+ if n >= 0:
+ parts.append(f"BW+{label}")
+ else:
+ parts.append(f"BW{label}")
+ return "/".join(parts) + "kg"
+ return "/".join(_fmt_kg(v) for v in values) + "kg"
+
+
+def prompt_sets_detail(
+ sets: int,
+ default_reps: int | None = None,
+ default_weight: float | None = None,
+ default_rest: int | None = None,
+ bw_relative: bool = False,
+) -> tuple[str, str, str]:
+ """Prompt for reps, weight (kg), and rest time on each set, return comma-separated strings."""
+ weight_label = "Weight relative to BW (kg)" if bw_relative else "Weight kg"
+ weight_min: float | None = None if bw_relative else 0.0
+ reps_list = []
+ weight_list = []
+ rest_list = []
+ for i in range(1, sets + 1):
+ print(f" Set {i}:")
+ if default_reps is not None:
+ r = prompt_int(
+ f" Reps [{default_reps}]: ", min_val=1, default=default_reps
+ )
+ else:
+ r = prompt_int(" Reps: ", min_val=1)
+ assert r is not None
+ reps_list.append(str(r))
+ if default_weight is not None:
+ w = prompt_float(
+ f" {weight_label} [{_fmt_kg(str(default_weight))}]: ",
+ min_val=weight_min,
+ default=default_weight,
+ )
+ else:
+ w = prompt_float(f" {weight_label}: ", min_val=weight_min)
+ assert w is not None
+ weight_list.append(f"{w:.1f}")
+ if default_rest is not None:
+ rest_val = input(f" Rest seconds [{default_rest}] (- = skip): ").strip()
+ if rest_val == "-":
+ rest_list.append("")
+ elif rest_val == "":
+ rest_list.append(str(default_rest))
+ else:
+ try:
+ n = int(rest_val)
+ if n < 0:
+ print(" Must be at least 0, using default.")
+ rest_list.append(str(default_rest))
+ else:
+ rest_list.append(str(n))
+ except ValueError:
+ print(" Invalid input, using default.")
+ rest_list.append(str(default_rest))
+ else:
+ rt = prompt_int(" Rest seconds (optional): ", required=False, min_val=0)
+ rest_list.append(str(rt) if rt is not None else "")
+ return ",".join(reps_list), ",".join(weight_list), ",".join(rest_list)
+
+
+def format_rest_time(rest_str: str | None) -> str:
+ """Format comma-separated rest values for display, showing '-' for skipped sets."""
+ if not rest_str:
+ return "-"
+ parts = []
+ for v in rest_str.split(","):
+ v = v.strip()
+ if v:
+ parts.append(f"{v}s")
+ else:
+ parts.append("-")
+ return "/".join(parts)
+
+
def prompt_datetime(prompt: str, default_now: bool = True) -> str:
while True:
default = datetime.now().strftime("%Y-%m-%d %H")
@@ -72,6 +206,10 @@ def prompt_datetime(prompt: str, default_now: bool = True) -> str:
continue
+def pause() -> None:
+ input("\nPress Enter to continue...")
+
+
def confirm(prompt: str) -> bool:
val = input(f"{prompt} [y/N]: ").strip().lower()
return val == "y"