Refactor method names for clarity and consistency across the application
Build and Push Docker Image / build-and-push (push) Has been cancelled

- Renamed `initialize_csv` to `_initialize_csv_file` in `DataManager` for better clarity.
- Updated method calls in `GraphManager` from `_create_toggle_controls` to `_create_chart_toggles` and `_on_toggle_changed` to `_handle_toggle_changed`.
- Changed method names in `MedTrackerApp` from `on_closing` to `handle_window_closing`, `add_entry` to `add_new_entry`, and `load_data` to `refresh_data_display`.
- Adjusted corresponding test method names in `TestMedTrackerApp` to reflect the new method names.
- Updated `UIManager` method names from `setup_icon` to `setup_application_icon` and adjusted related tests accordingly.
This commit is contained in:
William Valentin
2025-07-30 12:32:17 -07:00
parent e0faf20a56
commit b7c01bc373
6 changed files with 106 additions and 575 deletions
+2 -2
View File
@@ -11,9 +11,9 @@ class DataManager:
def __init__(self, filename: str, logger: logging.Logger) -> None:
self.filename: str = filename
self.logger: logging.Logger = logger
self.initialize_csv()
self._initialize_csv_file()
def initialize_csv(self) -> None:
def _initialize_csv_file(self) -> None:
"""Create CSV file with headers if it doesn't exist."""
if not os.path.exists(self.filename):
with open(self.filename, mode="w", newline="") as file:
+4 -4
View File
@@ -31,7 +31,7 @@ class GraphManager:
self.control_frame.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
# Create toggle checkboxes
self._create_toggle_controls()
self._create_chart_toggles()
# Create graph frame
self.graph_frame: ttk.Frame = ttk.Frame(self.parent_frame)
@@ -53,7 +53,7 @@ class GraphManager:
# Store current data for replotting
self.current_data: pd.DataFrame = pd.DataFrame()
def _create_toggle_controls(self) -> None:
def _create_chart_toggles(self) -> None:
"""Create toggle controls for chart elements."""
ttk.Label(self.control_frame, text="Show/Hide Elements:").pack(
side="left", padx=5
@@ -71,11 +71,11 @@ class GraphManager:
self.control_frame,
text=label,
variable=self.toggle_vars[key],
command=self._on_toggle_changed,
command=self._handle_toggle_changed,
)
checkbox.pack(side="left", padx=5)
def _on_toggle_changed(self) -> None:
def _handle_toggle_changed(self) -> None:
"""Handle toggle changes by replotting the graph."""
if not self.current_data.empty:
self._plot_graph_data(self.current_data)
+14 -14
View File
@@ -19,7 +19,7 @@ class MedTrackerApp:
self.root: tk.Tk = root
self.root.resizable(True, True)
self.root.title("Thechart - medication tracker")
self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
self.root.protocol("WM_DELETE_WINDOW", self.handle_window_closing)
# Set up data file
self.filename: str = "thechart_data.csv"
@@ -49,7 +49,7 @@ class MedTrackerApp:
icon_path: str = "chart-671.png"
if not os.path.exists(icon_path) and os.path.exists("./chart-671.png"):
icon_path = "./chart-671.png"
self.ui_manager.setup_icon(img_path=icon_path)
self.ui_manager.setup_application_icon(img_path=icon_path)
# Set up the main application UI
self._setup_main_ui()
@@ -85,28 +85,28 @@ class MedTrackerApp:
self.date_var: tk.StringVar = input_ui["date_var"]
# Add buttons to input frame
self.ui_manager.add_buttons(
self.ui_manager.add_action_buttons(
self.input_frame,
[
{
"text": "Add Entry",
"command": self.add_entry,
"command": self.add_new_entry,
"fill": "both",
"expand": True,
},
{"text": "Quit", "command": self.on_closing},
{"text": "Quit", "command": self.handle_window_closing},
],
)
# --- Create Table Frame ---
table_ui: dict[str, Any] = self.ui_manager.create_table_frame(main_frame)
self.tree: ttk.Treeview = table_ui["tree"]
self.tree.bind("<Double-1>", self.on_double_click)
self.tree.bind("<Double-1>", self.handle_double_click)
# Load data
self.load_data()
self.refresh_data_display()
def on_double_click(self, event: tk.Event) -> None:
def handle_double_click(self, event: tk.Event) -> None:
"""Handle double-click event to edit an entry."""
logger.debug("Double-click event triggered on treeview.")
if len(self.tree.get_children()) > 0:
@@ -198,7 +198,7 @@ class MedTrackerApp:
"Success", "Entry updated successfully!", parent=self.root
)
self._clear_entries()
self.load_data()
self.refresh_data_display()
else:
# Check if it's a duplicate date issue
df = self.data_manager.load_data()
@@ -212,14 +212,14 @@ class MedTrackerApp:
else:
messagebox.showerror("Error", "Failed to save changes", parent=edit_win)
def on_closing(self) -> None:
def handle_window_closing(self) -> None:
if messagebox.askokcancel(
"Quit", "Do you want to quit the application?", parent=self.root
):
self.graph_manager.close()
self.root.destroy()
def add_entry(self) -> None:
def add_new_entry(self) -> None:
"""Add a new entry to the CSV file."""
# Get current doses for today
today = self.date_var.get()
@@ -278,7 +278,7 @@ class MedTrackerApp:
"Success", "Entry added successfully!", parent=self.root
)
self._clear_entries()
self.load_data()
self.refresh_data_display()
else:
# Check if it's a duplicate date by trying to load existing data
df = self.data_manager.load_data()
@@ -309,7 +309,7 @@ class MedTrackerApp:
messagebox.showinfo(
"Success", "Entry deleted successfully!", parent=self.root
)
self.load_data()
self.refresh_data_display()
else:
messagebox.showerror("Error", "Failed to delete entry", parent=edit_win)
@@ -323,7 +323,7 @@ class MedTrackerApp:
self.medicine_vars[key][0].set(0)
self.note_var.set("")
def load_data(self) -> None:
def refresh_data_display(self) -> None:
"""Load data from the CSV file into the table and graph."""
logger.debug("Loading data from CSV.")
+24 -480
View File
@@ -17,7 +17,7 @@ class UIManager:
self.root: tk.Tk = root
self.logger: logging.Logger = logger
def setup_icon(self, img_path: str) -> bool:
def setup_application_icon(self, img_path: str) -> bool:
"""Set up the application icon."""
try:
self.logger.info(f"Trying to load icon from: {img_path}")
@@ -118,14 +118,10 @@ class UIManager:
# Set focus to canvas to ensure it receives scroll events
canvas.focus_set()
# Add mouse enter/leave events to manage focus for scrolling
# Add mouse enter event to manage focus for scrolling
def on_mouse_enter(event):
canvas.focus_set()
def on_mouse_leave(event):
# Don't change focus when leaving to avoid disrupting user interaction
pass
main_container.bind("<Enter>", on_mouse_enter)
canvas.bind("<Enter>", on_mouse_enter)
@@ -291,7 +287,7 @@ class UIManager:
graph_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=10, sticky="nsew")
return graph_frame
def add_buttons(
def add_action_buttons(
self, frame: ttk.Frame, buttons_config: list[dict[str, Any]]
) -> ttk.Frame:
"""Add buttons to a frame based on configuration."""
@@ -380,14 +376,10 @@ class UIManager:
# Set focus to canvas to ensure it receives scroll events
canvas.focus_set()
# Add mouse enter/leave events to manage focus for scrolling
# Add mouse enter event to manage focus for scrolling
def on_mouse_enter(event):
canvas.focus_set()
def on_mouse_leave(event):
# Don't change focus when leaving to avoid disrupting user interaction
pass
edit_win.bind("<Enter>", on_mouse_enter)
canvas.bind("<Enter>", on_mouse_enter)
@@ -467,7 +459,7 @@ class UIManager:
) = values_list[:16]
# Create improved UI sections
vars_dict = self._create_improved_edit_ui(
vars_dict = self._create_edit_ui(
main_container,
date,
dep,
@@ -490,7 +482,7 @@ class UIManager:
)
# Add action buttons
self._add_improved_edit_buttons(main_container, vars_dict, callbacks, edit_win)
self._add_edit_buttons(main_container, vars_dict, callbacks, edit_win)
# Update scroll region after adding all content
edit_win.update_idletasks()
@@ -505,7 +497,7 @@ class UIManager:
return edit_win
def _create_improved_edit_ui(
def _create_edit_ui(
self,
parent: ttk.Frame,
date: str,
@@ -521,7 +513,7 @@ class UIManager:
note: str,
dose_data: dict[str, str],
) -> dict[str, Any]:
"""Create improved UI layout for edit window with better organization."""
"""Create UI layout for edit window with organized sections."""
vars_dict = {}
row = 0
@@ -561,9 +553,7 @@ class UIManager:
]
for i, (label, key, value) in enumerate(symptoms):
self._create_improved_symptom_scale(
symptoms_frame, i, label, key, value, vars_dict
)
self._create_symptom_scale(symptoms_frame, i, label, key, value, vars_dict)
row += 1
@@ -573,7 +563,7 @@ class UIManager:
meds_frame.grid_columnconfigure(0, weight=1)
# Create medicine checkboxes with better styling
med_vars = self._create_improved_medicine_section(
med_vars = self._create_medicine_section(
meds_frame, bup, hydro, gaba, prop, quet
)
vars_dict.update(med_vars)
@@ -585,7 +575,7 @@ class UIManager:
dose_frame.grid(row=row, column=0, sticky="ew", pady=(0, 15))
dose_frame.grid_columnconfigure(0, weight=1)
dose_vars = self._create_improved_dose_tracking(dose_frame, dose_data)
dose_vars = self._create_dose_tracking(dose_frame, dose_data)
vars_dict.update(dose_vars)
row += 1
@@ -612,7 +602,7 @@ class UIManager:
return vars_dict
def _create_improved_symptom_scale(
def _create_symptom_scale(
self,
parent: ttk.Frame,
row: int,
@@ -621,7 +611,7 @@ class UIManager:
value: int,
vars_dict: dict[str, Any],
) -> None:
"""Create an improved symptom scale with better visual feedback."""
"""Create a symptom scale with visual feedback."""
# Ensure value is properly converted
try:
value = int(float(value)) if value not in ["", None] else 0
@@ -698,10 +688,10 @@ class UIManager:
scale.bind("<KeyRelease>", update_value_label)
update_value_label() # Set initial color
def _create_improved_medicine_section(
def _create_medicine_section(
self, parent: ttk.Frame, bup: int, hydro: int, gaba: int, prop: int, quet: int
) -> dict[str, tk.IntVar]:
"""Create improved medicine checkboxes with better layout."""
"""Create medicine checkboxes with organized layout."""
vars_dict = {}
# Create a grid layout for medicines
@@ -739,10 +729,10 @@ class UIManager:
return vars_dict
def _create_improved_dose_tracking(
def _create_dose_tracking(
self, parent: ttk.Frame, dose_data: dict[str, str]
) -> dict[str, Any]:
"""Create improved dose tracking interface."""
"""Create dose tracking interface."""
vars_dict = {}
# Create notebook for organized dose tracking
@@ -797,7 +787,7 @@ class UIManager:
# Take dose button
def create_take_dose_command(med_name, entry_var, med_key):
def take_dose():
self._take_dose_improved(med_name, entry_var, med_key, vars_dict)
self._take_dose(med_name, entry_var, med_key, vars_dict)
return take_dose
@@ -887,14 +877,14 @@ class UIManager:
# Always keep text widget enabled for user editing
def _take_dose_improved(
def _take_dose(
self,
med_name: str,
entry_var: tk.StringVar,
med_key: str,
vars_dict: dict[str, Any],
) -> None:
"""Handle taking a dose with improved feedback and state management."""
"""Handle taking a dose with feedback and state management."""
dose = entry_var.get().strip()
# Get the dose text widget - this is what the save function reads from
@@ -956,20 +946,20 @@ class UIManager:
parent=parent_window,
)
def _add_improved_edit_buttons(
def _add_edit_buttons(
self,
parent: ttk.Frame,
vars_dict: dict[str, Any],
callbacks: dict[str, Callable],
edit_win: tk.Toplevel,
) -> None:
"""Add improved action buttons to edit window."""
"""Add action buttons to edit window."""
button_frame = ttk.Frame(parent)
button_frame.grid(row=999, column=0, sticky="ew", pady=(20, 0))
button_frame.grid_columnconfigure((0, 1, 2), weight=1)
# Save button
def save_with_improved_data():
def save_with_data():
self.logger.debug("=== SAVE FUNCTION CALLED ===")
# Get note text from Text widget
@@ -1028,7 +1018,7 @@ class UIManager:
button_frame,
text="💾 Save Changes",
style="Accent.TButton",
command=save_with_improved_data,
command=save_with_data,
)
save_btn.grid(row=0, column=0, sticky="ew", padx=(0, 5))
@@ -1189,449 +1179,3 @@ class UIManager:
except tk.TclError:
# Handle potential errors when accessing children
pass
def _create_edit_fields(
self,
parent: tk.Toplevel,
date: str,
dep: int,
anx: int,
slp: int,
app: int,
) -> dict[str, tk.StringVar | tk.IntVar]:
"""Create fields for editing entry values."""
vars_dict: dict[str, tk.StringVar | tk.IntVar] = {}
# Ensure values are converted to appropriate types
try:
app = int(app) if app != "" else 0
except (ValueError, TypeError):
self.logger.warning(f"Invalid appetite value: {app}, defaulting to 0")
app = 0
value_map = {
"date": date,
"depression": dep,
"anxiety": anx,
"sleep": slp,
"appetite": app,
}
fields = [
("Date", tk.StringVar, "date"),
("Depression (0-10)", tk.IntVar, "depression"),
("Anxiety (0-10)", tk.IntVar, "anxiety"),
("Sleep (0-10)", tk.IntVar, "sleep"),
("Appetite (0-10)", tk.IntVar, "appetite"),
]
for idx, (label, var_type, key) in enumerate(fields):
try:
value = value_map[key]
if var_type == tk.IntVar:
try:
value = int(float(value))
except (ValueError, TypeError):
value = 0
self.logger.warning(
f"Failed to convert {key} value: {value}, defaulting to 0"
)
else:
value = str(value)
except (ValueError, TypeError, KeyError):
value = 0 if var_type == tk.IntVar else ""
self.logger.warning(
f"Missing or invalid value for {key}, defaulting to {value}"
)
vars_dict[key] = var_type(value=value)
ttk.Label(parent, text=f"{label}:").grid(
row=idx + 1, column=0, sticky="w", padx=5, pady=2
)
if var_type == tk.IntVar:
self._create_scale_with_label(parent, idx + 1, vars_dict[key], value)
else:
ttk.Entry(parent, textvariable=vars_dict[key]).grid(
row=idx + 1, column=1, sticky="ew"
)
return vars_dict
def _create_scale_with_label(
self, parent: tk.Toplevel, row: int, var: tk.IntVar, value: int
) -> None:
"""Create a scale with a value label."""
scale_frame: ttk.Frame = ttk.Frame(parent)
scale_frame.grid(row=row, column=1, sticky="ew", padx=5, pady=2)
scale_frame.grid_columnconfigure(0, weight=1)
scale = ttk.Scale(
scale_frame, from_=0, to=10, variable=var, orient=tk.HORIZONTAL
)
scale.grid(row=0, column=0, sticky="ew", padx=5)
# Add a value label to show the current value
value_label = ttk.Label(scale_frame, width=3)
value_label.grid(row=0, column=1, padx=(5, 0))
# Update label when scale value changes
def update_label(event=None):
value_label.configure(text=str(var.get()))
scale.bind("<Motion>", update_label)
scale.bind("<ButtonRelease-1>", update_label)
update_label() # Set initial value
scale.set(value) # Explicitly set scale value
def _create_medicine_checkboxes(
self,
parent: tk.Toplevel,
row: int,
bup: int,
hydro: int,
gaba: int,
prop: int,
quet: int,
) -> dict[str, tk.IntVar]:
"""Create medicine checkboxes in the edit window."""
ttk.Label(parent, text="Treatment:").grid(
row=row, column=0, sticky="w", padx=5, pady=2
)
medicine_frame: ttk.LabelFrame = ttk.LabelFrame(parent, text="Medicine")
medicine_frame.grid(row=row, column=1, padx=0, pady=10, sticky="nsew")
medicine_vars: dict[str, tuple[int, str]] = {
"bupropion": (bup, "Bupropion 150/300 mg"),
"hydroxyzine": (hydro, "Hydroxyzine 25mg"),
"gabapentin": (gaba, "Gabapentin 100mg"),
"propranolol": (prop, "Propranolol 10mg"),
"quetiapine": (quet, "Quetiapine 25mg"),
}
vars_dict: dict[str, tk.IntVar] = {}
for idx, (key, (value, label)) in enumerate(medicine_vars.items()):
vars_dict[key] = tk.IntVar(value=int(value))
ttk.Checkbutton(medicine_frame, text=label, variable=vars_dict[key]).grid(
row=idx, column=0, sticky="w", padx=5, pady=2
)
return vars_dict
def _add_edit_window_buttons(
self,
parent: tk.Toplevel,
row: int,
vars_dict: dict[str, Any],
callbacks: dict[str, Callable],
) -> None:
"""Add buttons to the edit window."""
button_frame: ttk.Frame = ttk.Frame(parent)
button_frame.grid(row=row, column=0, columnspan=2, pady=10)
# Save button - create a custom callback to handle dose data
def save_with_doses():
self.logger.debug("save_with_doses called")
# Extract dose data from the text widgets
dose_data = {}
for medicine in [
"bupropion",
"hydroxyzine",
"gabapentin",
"propranolol",
"quetiapine",
]:
dose_text_key = f"{medicine}_doses_text"
self.logger.debug(f"Looking for key: {dose_text_key}")
if dose_text_key in vars_dict and isinstance(
vars_dict[dose_text_key], tk.Text
):
raw_text = vars_dict[dose_text_key].get(1.0, tk.END).strip()
self.logger.debug(f"Raw text for {medicine}: '{raw_text}'")
dose_data[medicine] = self._parse_dose_history_for_saving(
raw_text, vars_dict["date"].get()
)
self.logger.debug(
f"Parsed dose data for {medicine}: '{dose_data[medicine]}'"
)
else:
self.logger.debug(
f"Key {dose_text_key} not found in vars_dict or not a Text "
"widget"
)
dose_data[medicine] = ""
callbacks["save"](
parent,
vars_dict["date"].get(),
vars_dict["depression"].get(),
vars_dict["anxiety"].get(),
vars_dict["sleep"].get(),
vars_dict["appetite"].get(),
vars_dict["bupropion"].get(),
vars_dict["hydroxyzine"].get(),
vars_dict["gabapentin"].get(),
vars_dict["propranolol"].get(),
vars_dict["quetiapine"].get(),
vars_dict["note"].get(),
dose_data,
)
ttk.Button(
button_frame,
text="Save",
command=save_with_doses,
).pack(side="left", padx=5)
# Cancel button
ttk.Button(button_frame, text="Cancel", command=parent.destroy).pack(
side="left", padx=5
)
# Delete button
ttk.Button(
button_frame,
text="Delete",
command=lambda: callbacks["delete"](parent),
).pack(side="left", padx=5)
def _add_dose_display_to_edit(
self, parent: tk.Toplevel, row: int, dose_data: dict[str, str]
) -> dict[str, tk.Text]:
"""Add comprehensive dose tracking to edit window with punch buttons."""
ttk.Label(parent, text="Dose Tracking:").grid(
row=row, column=0, sticky="w", padx=5, pady=2
)
dose_frame = ttk.LabelFrame(parent, text="Medicine Doses")
dose_frame.grid(row=row, column=1, padx=5, pady=2, sticky="ew")
dose_frame.grid_columnconfigure(2, weight=1)
dose_vars = {}
for idx, (medicine, doses_str) in enumerate(dose_data.items()):
# Medicine label
med_label = ttk.Label(dose_frame, text=f"{medicine.title()}:")
med_label.grid(row=idx, column=0, sticky="w", padx=5, pady=2)
# Dose entry field for new doses
dose_entry_var = tk.StringVar()
dose_entry = ttk.Entry(dose_frame, textvariable=dose_entry_var, width=12)
dose_entry.grid(row=idx, column=1, sticky="w", padx=5, pady=2)
# Store entry variable in dose_vars for access from punch button
dose_vars[f"{medicine}_entry_var"] = dose_entry_var
# Display area for existing doses (editable)
dose_text = tk.Text(dose_frame, height=3, width=40, wrap=tk.WORD)
dose_text.grid(row=idx, column=2, sticky="ew", padx=5, pady=2)
# Store text widget in dose_vars
dose_vars[f"{medicine}_doses_text"] = dose_text
# Punch button to record dose immediately
def create_punch_command(med_name, entry_var, text_widget):
"""Create a punch command that captures the specific widgets."""
def punch_command():
self._punch_dose_direct(med_name, entry_var, text_widget)
return punch_command
punch_button = ttk.Button(
dose_frame,
text=f"Take {medicine.title()}",
width=15,
command=create_punch_command(medicine, dose_entry_var, dose_text),
)
punch_button.grid(row=idx, column=3, sticky="w", padx=5, pady=2)
# Parse and format doses for editing
if doses_str and str(doses_str) != "nan":
doses_str = str(doses_str) # Convert to string in case it's a float/NaN
formatted_doses = []
for dose_entry_str in doses_str.split("|"):
if ":" in dose_entry_str:
timestamp, dose = dose_entry_str.split(":", 1)
# Format timestamp for display
try:
dt = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S")
time_str = dt.strftime("%H:%M")
formatted_doses.append(f"{time_str}: {dose}")
except ValueError:
formatted_doses.append(dose_entry_str)
if formatted_doses:
dose_text.insert(1.0, "\n".join(formatted_doses))
else:
dose_text.insert(1.0, "No doses recorded")
else:
dose_text.insert(1.0, "No doses recorded")
# Add help text below the dose display
help_label = ttk.Label(
dose_frame,
text="Format: HH:MM: dose",
font=("TkDefaultFont", 8),
foreground="gray",
)
help_label.grid(row=idx, column=4, sticky="w", padx=5, pady=2)
return dose_vars
def _punch_dose_direct(
self,
medicine_name: str,
dose_entry_var: tk.StringVar,
dose_text_widget: tk.Text,
) -> None:
"""Handle punch dose button with direct widget references."""
dose = dose_entry_var.get().strip()
# Find the parent edit window
parent_window = dose_text_widget.winfo_toplevel()
if not dose:
messagebox.showerror(
"Error",
f"Please enter a dose amount for {medicine_name}",
parent=parent_window,
)
return
# Get current time
now = datetime.now()
time_str = now.strftime("%H:%M")
# Get current content
current_content = dose_text_widget.get(1.0, tk.END).strip()
# Add new dose entry
new_dose_line = f"{time_str}: {dose}"
if current_content == "No doses recorded" or not current_content:
dose_text_widget.delete(1.0, tk.END)
dose_text_widget.insert(1.0, new_dose_line)
else:
dose_text_widget.insert(tk.END, f"\n{new_dose_line}")
# Clear the entry field
dose_entry_var.set("")
# Show success message
messagebox.showinfo(
"Success",
f"{medicine_name.title()} dose recorded: {dose} at {time_str}",
parent=parent_window,
)
def _punch_dose_in_edit(self, medicine_name: str, dose_vars: dict) -> None:
"""Handle punch dose button in edit window."""
dose_entry_var = dose_vars.get(f"{medicine_name}_entry_var")
dose_text_widget = dose_vars.get(f"{medicine_name}_doses_text")
if not dose_entry_var or not dose_text_widget:
return
dose = dose_entry_var.get().strip()
# Find the parent edit window
parent_window = dose_text_widget.winfo_toplevel()
if not dose:
messagebox.showerror(
"Error",
f"Please enter a dose amount for {medicine_name}",
parent=parent_window,
)
return
# Get current time
now = datetime.now()
time_str = now.strftime("%H:%M")
# Get current content
current_content = dose_text_widget.get(1.0, tk.END).strip()
# Add new dose entry
new_dose_line = f"{time_str}: {dose}"
if current_content == "No doses recorded" or not current_content:
dose_text_widget.delete(1.0, tk.END)
dose_text_widget.insert(1.0, new_dose_line)
else:
dose_text_widget.insert(tk.END, f"\n{new_dose_line}")
# Clear the entry field
dose_entry_var.set("")
# Show success message
messagebox.showinfo(
"Success",
f"{medicine_name.title()} dose recorded: {dose} at {time_str}",
parent=parent_window,
)
def _parse_dose_text(self, text: str, date: str) -> str:
"""Parse dose text from edit window back to CSV format."""
self.logger.debug(
f"_parse_dose_text called with text: '{text}' and date: '{date}'"
)
if not text or text == "No doses recorded":
self.logger.debug(
"Text is empty or 'No doses recorded', returning empty string"
)
return ""
lines = text.strip().split("\n")
dose_entries = []
for line in lines:
line = line.strip()
if ":" in line and line != "No doses recorded":
try:
# Try to parse HH:MM: dose format
# Split on ': ' (colon followed by space) to separate time from dose
if ": " in line:
time_part, dose_part = line.split(": ", 1)
else:
# Fallback: split on first colon after HH:MM pattern
colon_indices = [
i for i, char in enumerate(line) if char == ":"
]
if len(colon_indices) >= 2:
# Take everything up to the second colon as time
second_colon_idx = colon_indices[1]
time_part = line[:second_colon_idx]
dose_part = line[second_colon_idx + 1 :].strip()
else:
continue
dose_part = dose_part.strip()
# Create timestamp for today
from datetime import datetime
time_str = time_part.strip()
# Parse just the time (HH:MM format)
time_obj = datetime.strptime(time_str, "%H:%M")
# Create full timestamp with today's date
today = datetime.strptime(date, "%m/%d/%Y")
full_timestamp = today.replace(
hour=time_obj.hour, minute=time_obj.minute, second=0
)
timestamp_str = full_timestamp.strftime("%Y-%m-%d %H:%M:%S")
dose_entries.append(f"{timestamp_str}:{dose_part}")
except ValueError:
# If parsing fails, skip this line
self.logger.debug(f"Failed to parse line: '{line}'")
continue
result = "|".join(dose_entries)
self.logger.debug(f"_parse_dose_text returning: '{result}'")
return result