Add medicine management functionality with UI and data handling
- Implemented MedicineManagementWindow for adding, editing, and removing medicines. - Created MedicineManager to handle medicine configurations, including loading and saving to JSON. - Updated UIManager to dynamically generate medicine-related UI components based on the MedicineManager. - Enhanced test suite with mock objects for MedicineManager to ensure proper functionality in DataManager tests. - Added validation for medicine input fields in the UI. - Introduced default medicine configurations for initial setup.
This commit is contained in:
+40
-52
@@ -4,40 +4,37 @@ import os
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from medicine_manager import MedicineManager
|
||||
|
||||
|
||||
class DataManager:
|
||||
"""Handle all data operations for the application."""
|
||||
|
||||
def __init__(self, filename: str, logger: logging.Logger) -> None:
|
||||
def __init__(
|
||||
self, filename: str, logger: logging.Logger, medicine_manager: MedicineManager
|
||||
) -> None:
|
||||
self.filename: str = filename
|
||||
self.logger: logging.Logger = logger
|
||||
self.medicine_manager = medicine_manager
|
||||
self._initialize_csv_file()
|
||||
|
||||
def _get_csv_headers(self) -> list[str]:
|
||||
"""Get CSV headers based on current medicine configuration."""
|
||||
base_headers = ["date", "depression", "anxiety", "sleep", "appetite"]
|
||||
|
||||
# Add medicine headers
|
||||
medicine_headers = []
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
medicine_headers.extend([medicine_key, f"{medicine_key}_doses"])
|
||||
|
||||
return base_headers + medicine_headers + ["note"]
|
||||
|
||||
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:
|
||||
writer = csv.writer(file)
|
||||
writer.writerow(
|
||||
[
|
||||
"date",
|
||||
"depression",
|
||||
"anxiety",
|
||||
"sleep",
|
||||
"appetite",
|
||||
"bupropion",
|
||||
"bupropion_doses",
|
||||
"hydroxyzine",
|
||||
"hydroxyzine_doses",
|
||||
"gabapentin",
|
||||
"gabapentin_doses",
|
||||
"propranolol",
|
||||
"propranolol_doses",
|
||||
"quetiapine",
|
||||
"quetiapine_doses",
|
||||
"note",
|
||||
]
|
||||
)
|
||||
writer.writerow(self._get_csv_headers())
|
||||
|
||||
def load_data(self) -> pd.DataFrame:
|
||||
"""Load data from CSV file."""
|
||||
@@ -46,27 +43,22 @@ class DataManager:
|
||||
return pd.DataFrame()
|
||||
|
||||
try:
|
||||
df: pd.DataFrame = pd.read_csv(
|
||||
self.filename,
|
||||
dtype={
|
||||
"depression": int,
|
||||
"anxiety": int,
|
||||
"sleep": int,
|
||||
"appetite": int,
|
||||
"bupropion": int,
|
||||
"bupropion_doses": str,
|
||||
"hydroxyzine": int,
|
||||
"hydroxyzine_doses": str,
|
||||
"gabapentin": int,
|
||||
"gabapentin_doses": str,
|
||||
"propranolol": int,
|
||||
"propranolol_doses": str,
|
||||
"quetiapine": int,
|
||||
"quetiapine_doses": str,
|
||||
"note": str,
|
||||
"date": str,
|
||||
},
|
||||
).fillna("")
|
||||
# Build dtype dictionary dynamically
|
||||
dtype_dict = {
|
||||
"depression": int,
|
||||
"anxiety": int,
|
||||
"sleep": int,
|
||||
"appetite": int,
|
||||
"date": str,
|
||||
"note": str,
|
||||
}
|
||||
|
||||
# Add medicine types
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
dtype_dict[medicine_key] = int
|
||||
dtype_dict[f"{medicine_key}_doses"] = str
|
||||
|
||||
df: pd.DataFrame = pd.read_csv(self.filename, dtype=dtype_dict).fillna("")
|
||||
return df.sort_values(by="date").reset_index(drop=True)
|
||||
except pd.errors.EmptyDataError:
|
||||
self.logger.warning("CSV file is empty. No data to load.")
|
||||
@@ -207,18 +199,14 @@ class DataManager:
|
||||
"anxiety": 0,
|
||||
"sleep": 0,
|
||||
"appetite": 0,
|
||||
"bupropion": 0,
|
||||
"bupropion_doses": "",
|
||||
"hydroxyzine": 0,
|
||||
"hydroxyzine_doses": "",
|
||||
"gabapentin": 0,
|
||||
"gabapentin_doses": "",
|
||||
"propranolol": 0,
|
||||
"propranolol_doses": "",
|
||||
"quetiapine": 0,
|
||||
"quetiapine_doses": "",
|
||||
"note": "",
|
||||
}
|
||||
|
||||
# Add medicine columns dynamically
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
new_entry[medicine_key] = 0
|
||||
new_entry[f"{medicine_key}_doses"] = ""
|
||||
|
||||
df = pd.concat([df, pd.DataFrame([new_entry])], ignore_index=True)
|
||||
|
||||
# Add dose to the appropriate medicine
|
||||
|
||||
+33
-31
@@ -7,30 +7,43 @@ import pandas as pd
|
||||
from matplotlib.axes import Axes
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
|
||||
from medicine_manager import MedicineManager
|
||||
|
||||
|
||||
class GraphManager:
|
||||
"""Handle all graph-related operations for the application."""
|
||||
|
||||
def __init__(self, parent_frame: ttk.LabelFrame) -> None:
|
||||
def __init__(
|
||||
self, parent_frame: ttk.LabelFrame, medicine_manager: MedicineManager
|
||||
) -> None:
|
||||
self.parent_frame: ttk.LabelFrame = parent_frame
|
||||
self.medicine_manager = medicine_manager
|
||||
|
||||
# Configure graph frame to expand
|
||||
self.parent_frame.grid_rowconfigure(0, weight=1)
|
||||
self.parent_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# Initialize toggle variables for chart elements
|
||||
self._initialize_toggle_vars()
|
||||
self._setup_ui()
|
||||
|
||||
def _initialize_toggle_vars(self) -> None:
|
||||
"""Initialize toggle variables for chart elements."""
|
||||
# Initialize symptom toggles (always shown by default)
|
||||
self.toggle_vars: dict[str, tk.BooleanVar] = {
|
||||
"depression": tk.BooleanVar(value=True),
|
||||
"anxiety": tk.BooleanVar(value=True),
|
||||
"sleep": tk.BooleanVar(value=True),
|
||||
"appetite": tk.BooleanVar(value=True),
|
||||
"bupropion": tk.BooleanVar(value=False),
|
||||
"hydroxyzine": tk.BooleanVar(value=False),
|
||||
"gabapentin": tk.BooleanVar(value=False),
|
||||
"propranolol": tk.BooleanVar(value=False),
|
||||
"quetiapine": tk.BooleanVar(value=False),
|
||||
}
|
||||
|
||||
# Add medicine toggles dynamically
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
medicine = self.medicine_manager.get_medicine(medicine_key)
|
||||
default_value = medicine.default_enabled if medicine else False
|
||||
self.toggle_vars[medicine_key] = tk.BooleanVar(value=default_value)
|
||||
|
||||
def _setup_ui(self) -> None:
|
||||
"""Set up the UI components."""
|
||||
# Create control frame for toggles
|
||||
self.control_frame: ttk.Frame = ttk.Frame(self.parent_frame)
|
||||
self.control_frame.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
|
||||
@@ -84,26 +97,20 @@ class GraphManager:
|
||||
)
|
||||
checkbox.pack(side="left", padx=3)
|
||||
|
||||
# Medicines toggles
|
||||
# Medicines toggles - dynamic based on medicine manager
|
||||
medicines_frame = ttk.LabelFrame(self.control_frame, text="Medicines")
|
||||
medicines_frame.pack(side="left", padx=5, pady=2)
|
||||
|
||||
medicine_configs = [
|
||||
("bupropion", "Bupropion"),
|
||||
("hydroxyzine", "Hydroxyzine"),
|
||||
("gabapentin", "Gabapentin"),
|
||||
("propranolol", "Propranolol"),
|
||||
("quetiapine", "Quetiapine"),
|
||||
]
|
||||
|
||||
for key, label in medicine_configs:
|
||||
checkbox = ttk.Checkbutton(
|
||||
medicines_frame,
|
||||
text=label,
|
||||
variable=self.toggle_vars[key],
|
||||
command=self._handle_toggle_changed,
|
||||
)
|
||||
checkbox.pack(side="left", padx=3)
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
medicine = self.medicine_manager.get_medicine(medicine_key)
|
||||
if medicine:
|
||||
checkbox = ttk.Checkbutton(
|
||||
medicines_frame,
|
||||
text=medicine.display_name,
|
||||
variable=self.toggle_vars[medicine_key],
|
||||
command=self._handle_toggle_changed,
|
||||
)
|
||||
checkbox.pack(side="left", padx=3)
|
||||
|
||||
def _handle_toggle_changed(self) -> None:
|
||||
"""Handle toggle changes by replotting the graph."""
|
||||
@@ -147,13 +154,8 @@ class GraphManager:
|
||||
has_plotted_series = True
|
||||
|
||||
# Plot medicine dose data
|
||||
medicine_colors = {
|
||||
"bupropion": "#FF6B6B", # Red
|
||||
"hydroxyzine": "#4ECDC4", # Teal
|
||||
"gabapentin": "#45B7D1", # Blue
|
||||
"propranolol": "#96CEB4", # Green
|
||||
"quetiapine": "#FFEAA7", # Yellow
|
||||
}
|
||||
# Get medicine colors from medicine manager
|
||||
medicine_colors = self.medicine_manager.get_graph_colors()
|
||||
|
||||
medicines = [
|
||||
"bupropion",
|
||||
|
||||
+116
-81
@@ -2,7 +2,7 @@ import os
|
||||
import sys
|
||||
import tkinter as tk
|
||||
from collections.abc import Callable
|
||||
from tkinter import messagebox
|
||||
from tkinter import messagebox, ttk
|
||||
from typing import Any
|
||||
|
||||
import pandas as pd
|
||||
@@ -11,6 +11,8 @@ from constants import LOG_LEVEL, LOG_PATH
|
||||
from data_manager import DataManager
|
||||
from graph_manager import GraphManager
|
||||
from init import logger
|
||||
from medicine_management_window import MedicineManagementWindow
|
||||
from medicine_manager import MedicineManager
|
||||
from ui_manager import UIManager
|
||||
|
||||
|
||||
@@ -42,8 +44,11 @@ class MedTrackerApp:
|
||||
logger.debug(f"First argument: {first_argument}")
|
||||
|
||||
# Initialize managers
|
||||
self.ui_manager: UIManager = UIManager(root, logger)
|
||||
self.data_manager: DataManager = DataManager(self.filename, logger)
|
||||
self.medicine_manager: MedicineManager = MedicineManager(logger=logger)
|
||||
self.ui_manager: UIManager = UIManager(root, logger, self.medicine_manager)
|
||||
self.data_manager: DataManager = DataManager(
|
||||
self.filename, logger, self.medicine_manager
|
||||
)
|
||||
|
||||
# Set up application icon
|
||||
icon_path: str = "chart-671.png"
|
||||
@@ -54,6 +59,9 @@ class MedTrackerApp:
|
||||
# Set up the main application UI
|
||||
self._setup_main_ui()
|
||||
|
||||
# Add menu bar
|
||||
self._setup_menu()
|
||||
|
||||
def _setup_main_ui(self) -> None:
|
||||
"""Set up the main UI components."""
|
||||
import tkinter.ttk as ttk
|
||||
@@ -74,7 +82,9 @@ class MedTrackerApp:
|
||||
|
||||
# --- Create Graph Frame ---
|
||||
graph_frame: ttk.Frame = self.ui_manager.create_graph_frame(main_frame)
|
||||
self.graph_manager: GraphManager = GraphManager(graph_frame)
|
||||
self.graph_manager: GraphManager = GraphManager(
|
||||
graph_frame, self.medicine_manager
|
||||
)
|
||||
|
||||
# --- Create Input Frame ---
|
||||
input_ui: dict[str, Any] = self.ui_manager.create_input_frame(main_frame)
|
||||
@@ -106,6 +116,59 @@ class MedTrackerApp:
|
||||
# Load data
|
||||
self.refresh_data_display()
|
||||
|
||||
def _setup_menu(self) -> None:
|
||||
"""Set up the menu bar."""
|
||||
menubar = tk.Menu(self.root)
|
||||
self.root.config(menu=menubar)
|
||||
|
||||
# Tools menu
|
||||
tools_menu = tk.Menu(menubar, tearoff=0)
|
||||
menubar.add_cascade(label="Tools", menu=tools_menu)
|
||||
tools_menu.add_command(
|
||||
label="Manage Medicines...", command=self._open_medicine_manager
|
||||
)
|
||||
|
||||
def _open_medicine_manager(self) -> None:
|
||||
"""Open the medicine management window."""
|
||||
MedicineManagementWindow(
|
||||
self.root, self.medicine_manager, self._refresh_ui_after_medicine_change
|
||||
)
|
||||
|
||||
def _refresh_ui_after_medicine_change(self) -> None:
|
||||
"""Refresh UI components after medicine configuration changes."""
|
||||
# Recreate the input frame with new medicines
|
||||
self.input_frame.destroy()
|
||||
input_ui: dict[str, Any] = self.ui_manager.create_input_frame(
|
||||
self.input_frame.master
|
||||
)
|
||||
self.input_frame: ttk.Frame = input_ui["frame"]
|
||||
self.medicine_vars: dict[str, tuple[tk.IntVar, str]] = input_ui["medicine_vars"]
|
||||
|
||||
# Add buttons to input frame
|
||||
self.ui_manager.add_action_buttons(
|
||||
self.input_frame,
|
||||
[
|
||||
{
|
||||
"text": "Add Entry",
|
||||
"command": self.add_new_entry,
|
||||
"fill": "both",
|
||||
"expand": True,
|
||||
},
|
||||
{"text": "Quit", "command": self.handle_window_closing},
|
||||
],
|
||||
)
|
||||
|
||||
# Recreate the table with new columns
|
||||
self.tree.destroy()
|
||||
table_ui: dict[str, Any] = self.ui_manager.create_table_frame(
|
||||
self.tree.master.master
|
||||
)
|
||||
self.tree: ttk.Treeview = table_ui["tree"]
|
||||
self.tree.bind("<Double-1>", self.handle_double_click)
|
||||
|
||||
# Refresh data display
|
||||
self.refresh_data_display()
|
||||
|
||||
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.")
|
||||
@@ -124,24 +187,24 @@ class MedTrackerApp:
|
||||
if not df.empty and original_date in df["date"].values:
|
||||
full_row = df[df["date"] == original_date].iloc[0]
|
||||
# Convert to tuple in the expected order for the edit window
|
||||
full_values = (
|
||||
full_values = [
|
||||
full_row["date"],
|
||||
full_row["depression"],
|
||||
full_row["anxiety"],
|
||||
full_row["sleep"],
|
||||
full_row["appetite"],
|
||||
full_row["bupropion"],
|
||||
full_row["bupropion_doses"],
|
||||
full_row["hydroxyzine"],
|
||||
full_row["hydroxyzine_doses"],
|
||||
full_row["gabapentin"],
|
||||
full_row["gabapentin_doses"],
|
||||
full_row["propranolol"],
|
||||
full_row["propranolol_doses"],
|
||||
full_row["quetiapine"],
|
||||
full_row["quetiapine_doses"],
|
||||
full_row["note"],
|
||||
)
|
||||
]
|
||||
|
||||
# Add medicine data dynamically
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
if medicine_key in full_row:
|
||||
full_values.append(full_row[medicine_key])
|
||||
full_values.append(full_row.get(f"{medicine_key}_doses", ""))
|
||||
else:
|
||||
full_values.extend([0, ""])
|
||||
|
||||
full_values.append(full_row["note"])
|
||||
full_values = tuple(full_values)
|
||||
else:
|
||||
# Fallback to the table values if full data not found
|
||||
full_values = values
|
||||
@@ -164,11 +227,7 @@ class MedTrackerApp:
|
||||
anx: int,
|
||||
slp: int,
|
||||
app: int,
|
||||
bup: int,
|
||||
hydro: int,
|
||||
gaba: int,
|
||||
prop: int,
|
||||
quet: int,
|
||||
medicine_values: dict[str, int],
|
||||
note: str,
|
||||
dose_data: dict[str, str],
|
||||
) -> None:
|
||||
@@ -179,19 +238,15 @@ class MedTrackerApp:
|
||||
anx,
|
||||
slp,
|
||||
app,
|
||||
bup,
|
||||
dose_data.get("bupropion", ""),
|
||||
hydro,
|
||||
dose_data.get("hydroxyzine", ""),
|
||||
gaba,
|
||||
dose_data.get("gabapentin", ""),
|
||||
prop,
|
||||
dose_data.get("propranolol", ""),
|
||||
quet,
|
||||
dose_data.get("quetiapine", ""),
|
||||
note,
|
||||
]
|
||||
|
||||
# Add medicine data dynamically
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
values.append(medicine_values.get(medicine_key, 0))
|
||||
values.append(dose_data.get(medicine_key, ""))
|
||||
|
||||
values.append(note)
|
||||
|
||||
if self.data_manager.update_entry(original_date, values):
|
||||
edit_win.destroy()
|
||||
messagebox.showinfo(
|
||||
@@ -223,49 +278,35 @@ class MedTrackerApp:
|
||||
"""Add a new entry to the CSV file."""
|
||||
# Get current doses for today
|
||||
today = self.date_var.get()
|
||||
bupropion_doses = ""
|
||||
hydroxyzine_doses = ""
|
||||
gabapentin_doses = ""
|
||||
propranolol_doses = ""
|
||||
quetiapine_doses = ""
|
||||
dose_values = {}
|
||||
|
||||
if today:
|
||||
bup_doses = self.data_manager.get_today_medicine_doses(today, "bupropion")
|
||||
hydroxyzine_doses_list = self.data_manager.get_today_medicine_doses(
|
||||
today, "hydroxyzine"
|
||||
)
|
||||
gaba_doses = self.data_manager.get_today_medicine_doses(today, "gabapentin")
|
||||
prop_doses = self.data_manager.get_today_medicine_doses(
|
||||
today, "propranolol"
|
||||
)
|
||||
quet_doses = self.data_manager.get_today_medicine_doses(today, "quetiapine")
|
||||
|
||||
bupropion_doses = "|".join([f"{ts}:{dose}" for ts, dose in bup_doses])
|
||||
hydroxyzine_doses = "|".join(
|
||||
[f"{ts}:{dose}" for ts, dose in hydroxyzine_doses_list]
|
||||
)
|
||||
gabapentin_doses = "|".join([f"{ts}:{dose}" for ts, dose in gaba_doses])
|
||||
propranolol_doses = "|".join([f"{ts}:{dose}" for ts, dose in prop_doses])
|
||||
quetiapine_doses = "|".join([f"{ts}:{dose}" for ts, dose in quet_doses])
|
||||
# Get doses for all medicines dynamically
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
doses = self.data_manager.get_today_medicine_doses(today, medicine_key)
|
||||
dose_values[f"{medicine_key}_doses"] = "|".join(
|
||||
[f"{ts}:{dose}" for ts, dose in doses]
|
||||
)
|
||||
else:
|
||||
# Set empty doses for all medicines
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
dose_values[f"{medicine_key}_doses"] = ""
|
||||
|
||||
# Build entry dynamically
|
||||
entry: list[str | int] = [
|
||||
self.date_var.get(),
|
||||
self.symptom_vars["depression"].get(),
|
||||
self.symptom_vars["anxiety"].get(),
|
||||
self.symptom_vars["sleep"].get(),
|
||||
self.symptom_vars["appetite"].get(),
|
||||
self.medicine_vars["bupropion"][0].get(),
|
||||
bupropion_doses,
|
||||
self.medicine_vars["hydroxyzine"][0].get(),
|
||||
hydroxyzine_doses,
|
||||
self.medicine_vars["gabapentin"][0].get(),
|
||||
gabapentin_doses,
|
||||
self.medicine_vars["propranolol"][0].get(),
|
||||
propranolol_doses,
|
||||
self.medicine_vars["quetiapine"][0].get(),
|
||||
quetiapine_doses,
|
||||
self.note_var.get(),
|
||||
]
|
||||
|
||||
# Add medicine data
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
entry.append(self.medicine_vars[medicine_key][0].get())
|
||||
entry.append(dose_values[f"{medicine_key}_doses"])
|
||||
|
||||
entry.append(self.note_var.get())
|
||||
logger.debug(f"Adding entry: {entry}")
|
||||
|
||||
# Check if date is empty
|
||||
@@ -336,26 +377,20 @@ class MedTrackerApp:
|
||||
|
||||
# Update the treeview with the data
|
||||
if not df.empty:
|
||||
# Only show user-friendly columns in the table (not the dose columns)
|
||||
display_columns = [
|
||||
"date",
|
||||
"depression",
|
||||
"anxiety",
|
||||
"sleep",
|
||||
"appetite",
|
||||
"bupropion",
|
||||
"hydroxyzine",
|
||||
"gabapentin",
|
||||
"propranolol",
|
||||
"quetiapine",
|
||||
"note",
|
||||
]
|
||||
# Build display columns dynamically (exclude dose columns for table view)
|
||||
display_columns = ["date", "depression", "anxiety", "sleep", "appetite"]
|
||||
|
||||
# Add medicine columns (without dose columns)
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
display_columns.append(medicine_key)
|
||||
|
||||
display_columns.append("note")
|
||||
|
||||
# Filter to only the columns we want to display
|
||||
if all(col in df.columns for col in display_columns):
|
||||
display_df = df[display_columns]
|
||||
else:
|
||||
# Fallback for old CSV format - just use all columns
|
||||
# Fallback - just use all columns
|
||||
display_df = df
|
||||
|
||||
for _index, row in display_df.iterrows():
|
||||
|
||||
@@ -0,0 +1,401 @@
|
||||
"""
|
||||
Medicine management window for adding, editing, and removing medicines.
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox, ttk
|
||||
|
||||
from medicine_manager import Medicine, MedicineManager
|
||||
|
||||
|
||||
class MedicineManagementWindow:
|
||||
"""Window for managing medicine configurations."""
|
||||
|
||||
def __init__(
|
||||
self, parent: tk.Tk, medicine_manager: MedicineManager, refresh_callback
|
||||
):
|
||||
self.parent = parent
|
||||
self.medicine_manager = medicine_manager
|
||||
self.refresh_callback = refresh_callback
|
||||
|
||||
# Create the window
|
||||
self.window = tk.Toplevel(parent)
|
||||
self.window.title("Manage Medicines")
|
||||
self.window.geometry("600x500")
|
||||
self.window.resizable(True, True)
|
||||
|
||||
# Make window modal
|
||||
self.window.transient(parent)
|
||||
self.window.grab_set()
|
||||
|
||||
self._setup_ui()
|
||||
self._populate_medicine_list()
|
||||
|
||||
# Center window
|
||||
self.window.update_idletasks()
|
||||
x = (self.window.winfo_screenwidth() // 2) - (600 // 2)
|
||||
y = (self.window.winfo_screenheight() // 2) - (500 // 2)
|
||||
self.window.geometry(f"600x500+{x}+{y}")
|
||||
|
||||
def _setup_ui(self):
|
||||
"""Set up the user interface."""
|
||||
main_frame = ttk.Frame(self.window, padding="10")
|
||||
main_frame.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
self.window.grid_rowconfigure(0, weight=1)
|
||||
self.window.grid_columnconfigure(0, weight=1)
|
||||
main_frame.grid_rowconfigure(1, weight=1)
|
||||
main_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# Title
|
||||
title_label = ttk.Label(
|
||||
main_frame, text="Medicine Management", font=("Arial", 14, "bold")
|
||||
)
|
||||
title_label.grid(row=0, column=0, columnspan=2, pady=(0, 10))
|
||||
|
||||
# Medicine list
|
||||
list_frame = ttk.LabelFrame(main_frame, text="Current Medicines")
|
||||
list_frame.grid(row=1, column=0, columnspan=2, sticky="nsew", pady=(0, 10))
|
||||
list_frame.grid_rowconfigure(0, weight=1)
|
||||
list_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# Treeview for medicines
|
||||
columns = ("key", "name", "dosage", "quick_doses", "color", "default")
|
||||
self.tree = ttk.Treeview(list_frame, columns=columns, show="headings")
|
||||
|
||||
# Column headings
|
||||
self.tree.heading("key", text="Key")
|
||||
self.tree.heading("name", text="Name")
|
||||
self.tree.heading("dosage", text="Dosage Info")
|
||||
self.tree.heading("quick_doses", text="Quick Doses")
|
||||
self.tree.heading("color", text="Color")
|
||||
self.tree.heading("default", text="Default Enabled")
|
||||
|
||||
# Column widths
|
||||
self.tree.column("key", width=80)
|
||||
self.tree.column("name", width=100)
|
||||
self.tree.column("dosage", width=100)
|
||||
self.tree.column("quick_doses", width=120)
|
||||
self.tree.column("color", width=70)
|
||||
self.tree.column("default", width=100)
|
||||
|
||||
self.tree.grid(row=0, column=0, sticky="nsew", padx=5, pady=5)
|
||||
|
||||
# Scrollbar for treeview
|
||||
scrollbar = ttk.Scrollbar(
|
||||
list_frame, orient="vertical", command=self.tree.yview
|
||||
)
|
||||
scrollbar.grid(row=0, column=1, sticky="ns")
|
||||
self.tree.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
# Buttons
|
||||
button_frame = ttk.Frame(main_frame)
|
||||
button_frame.grid(row=2, column=0, columnspan=2, pady=(10, 0))
|
||||
|
||||
ttk.Button(button_frame, text="Add Medicine", command=self._add_medicine).grid(
|
||||
row=0, column=0, padx=(0, 5)
|
||||
)
|
||||
|
||||
ttk.Button(
|
||||
button_frame, text="Edit Medicine", command=self._edit_medicine
|
||||
).grid(row=0, column=1, padx=5)
|
||||
|
||||
ttk.Button(
|
||||
button_frame, text="Remove Medicine", command=self._remove_medicine
|
||||
).grid(row=0, column=2, padx=5)
|
||||
|
||||
ttk.Button(button_frame, text="Close", command=self._close_window).grid(
|
||||
row=0, column=3, padx=(5, 0)
|
||||
)
|
||||
|
||||
def _populate_medicine_list(self):
|
||||
"""Populate the medicine list."""
|
||||
# Clear existing items
|
||||
for item in self.tree.get_children():
|
||||
self.tree.delete(item)
|
||||
|
||||
# Add medicines
|
||||
for medicine in self.medicine_manager.get_all_medicines().values():
|
||||
self.tree.insert(
|
||||
"",
|
||||
"end",
|
||||
values=(
|
||||
medicine.key,
|
||||
medicine.display_name,
|
||||
medicine.dosage_info,
|
||||
", ".join(medicine.quick_doses),
|
||||
medicine.color,
|
||||
"Yes" if medicine.default_enabled else "No",
|
||||
),
|
||||
)
|
||||
|
||||
def _add_medicine(self):
|
||||
"""Add a new medicine."""
|
||||
MedicineEditDialog(
|
||||
self.window, self.medicine_manager, None, self._on_medicine_changed
|
||||
)
|
||||
|
||||
def _edit_medicine(self):
|
||||
"""Edit selected medicine."""
|
||||
selection = self.tree.selection()
|
||||
if not selection:
|
||||
messagebox.showwarning("No Selection", "Please select a medicine to edit.")
|
||||
return
|
||||
|
||||
item = self.tree.item(selection[0])
|
||||
medicine_key = item["values"][0]
|
||||
medicine = self.medicine_manager.get_medicine(medicine_key)
|
||||
|
||||
if medicine:
|
||||
MedicineEditDialog(
|
||||
self.window, self.medicine_manager, medicine, self._on_medicine_changed
|
||||
)
|
||||
|
||||
def _remove_medicine(self):
|
||||
"""Remove selected medicine."""
|
||||
selection = self.tree.selection()
|
||||
if not selection:
|
||||
messagebox.showwarning(
|
||||
"No Selection", "Please select a medicine to remove."
|
||||
)
|
||||
return
|
||||
|
||||
item = self.tree.item(selection[0])
|
||||
medicine_key = item["values"][0]
|
||||
medicine_name = item["values"][1]
|
||||
|
||||
if messagebox.askyesno(
|
||||
"Confirm Removal",
|
||||
f"Are you sure you want to remove '{medicine_name}'?\n\n"
|
||||
"This will also remove all associated data from your records!",
|
||||
):
|
||||
if self.medicine_manager.remove_medicine(medicine_key):
|
||||
messagebox.showinfo(
|
||||
"Success", f"'{medicine_name}' removed successfully!"
|
||||
)
|
||||
self._populate_medicine_list()
|
||||
self._refresh_main_app()
|
||||
else:
|
||||
messagebox.showerror("Error", f"Failed to remove '{medicine_name}'.")
|
||||
|
||||
def _on_medicine_changed(self):
|
||||
"""Called when a medicine is added or edited."""
|
||||
self._populate_medicine_list()
|
||||
self._refresh_main_app()
|
||||
|
||||
def _refresh_main_app(self):
|
||||
"""Refresh the main application after medicine changes."""
|
||||
if self.refresh_callback:
|
||||
self.refresh_callback()
|
||||
|
||||
def _close_window(self):
|
||||
"""Close the window."""
|
||||
self.window.destroy()
|
||||
|
||||
|
||||
class MedicineEditDialog:
|
||||
"""Dialog for adding/editing a medicine."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
parent: tk.Toplevel,
|
||||
medicine_manager: MedicineManager,
|
||||
medicine: Medicine | None,
|
||||
callback,
|
||||
):
|
||||
self.parent = parent
|
||||
self.medicine_manager = medicine_manager
|
||||
self.medicine = medicine
|
||||
self.callback = callback
|
||||
self.is_edit = medicine is not None
|
||||
|
||||
# Create dialog
|
||||
self.dialog = tk.Toplevel(parent)
|
||||
self.dialog.title("Edit Medicine" if self.is_edit else "Add Medicine")
|
||||
self.dialog.geometry("400x350")
|
||||
self.dialog.resizable(False, False)
|
||||
|
||||
# Make modal
|
||||
self.dialog.transient(parent)
|
||||
self.dialog.grab_set()
|
||||
|
||||
self._setup_dialog()
|
||||
self._populate_fields()
|
||||
|
||||
# Center dialog
|
||||
self.dialog.update_idletasks()
|
||||
x = parent.winfo_x() + (parent.winfo_width() // 2) - (400 // 2)
|
||||
y = parent.winfo_y() + (parent.winfo_height() // 2) - (350 // 2)
|
||||
self.dialog.geometry(f"400x350+{x}+{y}")
|
||||
|
||||
def _setup_dialog(self):
|
||||
"""Set up the dialog UI."""
|
||||
main_frame = ttk.Frame(self.dialog, padding="15")
|
||||
main_frame.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
self.dialog.grid_rowconfigure(0, weight=1)
|
||||
self.dialog.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# Fields
|
||||
fields_frame = ttk.Frame(main_frame)
|
||||
fields_frame.grid(row=0, column=0, sticky="ew", pady=(0, 15))
|
||||
fields_frame.grid_columnconfigure(1, weight=1)
|
||||
|
||||
row = 0
|
||||
|
||||
# Key
|
||||
ttk.Label(fields_frame, text="Key:").grid(row=row, column=0, sticky="w", pady=5)
|
||||
self.key_var = tk.StringVar()
|
||||
key_entry = ttk.Entry(fields_frame, textvariable=self.key_var)
|
||||
key_entry.grid(row=row, column=1, sticky="ew", padx=(10, 0), pady=5)
|
||||
if self.is_edit:
|
||||
key_entry.configure(state="readonly")
|
||||
row += 1
|
||||
|
||||
# Display Name
|
||||
ttk.Label(fields_frame, text="Display Name:").grid(
|
||||
row=row, column=0, sticky="w", pady=5
|
||||
)
|
||||
self.name_var = tk.StringVar()
|
||||
ttk.Entry(fields_frame, textvariable=self.name_var).grid(
|
||||
row=row, column=1, sticky="ew", padx=(10, 0), pady=5
|
||||
)
|
||||
row += 1
|
||||
|
||||
# Dosage Info
|
||||
ttk.Label(fields_frame, text="Dosage Info:").grid(
|
||||
row=row, column=0, sticky="w", pady=5
|
||||
)
|
||||
self.dosage_var = tk.StringVar()
|
||||
ttk.Entry(fields_frame, textvariable=self.dosage_var).grid(
|
||||
row=row, column=1, sticky="ew", padx=(10, 0), pady=5
|
||||
)
|
||||
row += 1
|
||||
|
||||
# Quick Doses
|
||||
ttk.Label(fields_frame, text="Quick Doses:").grid(
|
||||
row=row, column=0, sticky="w", pady=5
|
||||
)
|
||||
self.doses_var = tk.StringVar()
|
||||
ttk.Entry(fields_frame, textvariable=self.doses_var).grid(
|
||||
row=row, column=1, sticky="ew", padx=(10, 0), pady=5
|
||||
)
|
||||
ttk.Label(
|
||||
fields_frame, text="(comma-separated, e.g. 25,50,100)", font=("Arial", 8)
|
||||
).grid(row=row + 1, column=1, sticky="w", padx=(10, 0))
|
||||
row += 2
|
||||
|
||||
# Color
|
||||
ttk.Label(fields_frame, text="Graph Color:").grid(
|
||||
row=row, column=0, sticky="w", pady=5
|
||||
)
|
||||
self.color_var = tk.StringVar()
|
||||
ttk.Entry(fields_frame, textvariable=self.color_var).grid(
|
||||
row=row, column=1, sticky="ew", padx=(10, 0), pady=5
|
||||
)
|
||||
ttk.Label(
|
||||
fields_frame, text="(hex color, e.g. #FF6B6B)", font=("Arial", 8)
|
||||
).grid(row=row + 1, column=1, sticky="w", padx=(10, 0))
|
||||
row += 2
|
||||
|
||||
# Default Enabled
|
||||
self.default_var = tk.BooleanVar()
|
||||
ttk.Checkbutton(
|
||||
fields_frame,
|
||||
text="Show in graph by default",
|
||||
variable=self.default_var,
|
||||
).grid(row=row, column=0, columnspan=2, sticky="w", pady=5)
|
||||
|
||||
# Buttons
|
||||
button_frame = ttk.Frame(main_frame)
|
||||
button_frame.grid(row=1, column=0)
|
||||
|
||||
ttk.Button(button_frame, text="Save", command=self._save_medicine).grid(
|
||||
row=0, column=0, padx=(0, 10)
|
||||
)
|
||||
|
||||
ttk.Button(button_frame, text="Cancel", command=self.dialog.destroy).grid(
|
||||
row=0, column=1
|
||||
)
|
||||
|
||||
def _populate_fields(self):
|
||||
"""Populate fields if editing."""
|
||||
if self.medicine:
|
||||
self.key_var.set(self.medicine.key)
|
||||
self.name_var.set(self.medicine.display_name)
|
||||
self.dosage_var.set(self.medicine.dosage_info)
|
||||
self.doses_var.set(",".join(self.medicine.quick_doses))
|
||||
self.color_var.set(self.medicine.color)
|
||||
self.default_var.set(self.medicine.default_enabled)
|
||||
|
||||
def _save_medicine(self):
|
||||
"""Save the medicine."""
|
||||
# Validate fields
|
||||
key = self.key_var.get().strip()
|
||||
name = self.name_var.get().strip()
|
||||
dosage = self.dosage_var.get().strip()
|
||||
doses_str = self.doses_var.get().strip()
|
||||
color = self.color_var.get().strip()
|
||||
|
||||
if not all([key, name, dosage, doses_str, color]):
|
||||
messagebox.showerror("Error", "All fields are required.")
|
||||
return
|
||||
|
||||
# Validate key format (alphanumeric and underscores only)
|
||||
if not key.replace("_", "").replace("-", "").isalnum():
|
||||
messagebox.showerror(
|
||||
"Error",
|
||||
"Key must contain only letters, numbers, underscores, and hyphens.",
|
||||
)
|
||||
return
|
||||
|
||||
# Parse quick doses
|
||||
try:
|
||||
quick_doses = [dose.strip() for dose in doses_str.split(",")]
|
||||
quick_doses = [dose for dose in quick_doses if dose] # Remove empty strings
|
||||
if not quick_doses:
|
||||
raise ValueError("At least one quick dose is required.")
|
||||
except Exception:
|
||||
messagebox.showerror("Error", "Quick doses must be comma-separated values.")
|
||||
return
|
||||
|
||||
# Validate color format
|
||||
if not color.startswith("#") or len(color) != 7:
|
||||
messagebox.showerror(
|
||||
"Error", "Color must be in hex format (e.g., #FF6B6B)."
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
int(color[1:], 16) # Validate hex color
|
||||
except ValueError:
|
||||
messagebox.showerror("Error", "Invalid hex color format.")
|
||||
return
|
||||
|
||||
# Create medicine object
|
||||
new_medicine = Medicine(
|
||||
key=key,
|
||||
display_name=name,
|
||||
dosage_info=dosage,
|
||||
quick_doses=quick_doses,
|
||||
color=color,
|
||||
default_enabled=self.default_var.get(),
|
||||
)
|
||||
|
||||
# Save medicine
|
||||
success = False
|
||||
if self.is_edit:
|
||||
success = self.medicine_manager.update_medicine(
|
||||
self.medicine.key, new_medicine
|
||||
)
|
||||
else:
|
||||
success = self.medicine_manager.add_medicine(new_medicine)
|
||||
|
||||
if success:
|
||||
action = "updated" if self.is_edit else "added"
|
||||
messagebox.showinfo("Success", f"Medicine {action} successfully!")
|
||||
self.callback()
|
||||
self.dialog.destroy()
|
||||
else:
|
||||
action = "update" if self.is_edit else "add"
|
||||
messagebox.showerror("Error", f"Failed to {action} medicine.")
|
||||
@@ -0,0 +1,195 @@
|
||||
"""
|
||||
Medicine configuration manager for the MedTracker application.
|
||||
Handles dynamic loading and saving of medicine configurations.
|
||||
"""
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from dataclasses import asdict, dataclass
|
||||
from typing import Any
|
||||
|
||||
|
||||
@dataclass
|
||||
class Medicine:
|
||||
"""Data class representing a medicine."""
|
||||
|
||||
key: str # Internal key (e.g., "bupropion")
|
||||
display_name: str # Display name (e.g., "Bupropion")
|
||||
dosage_info: str # Dosage information (e.g., "150/300 mg")
|
||||
quick_doses: list[str] # Common dose amounts for quick selection
|
||||
color: str # Color for graph display
|
||||
default_enabled: bool = False # Whether to show in graph by default
|
||||
|
||||
|
||||
class MedicineManager:
|
||||
"""Manages medicine configurations and provides access to medicine data."""
|
||||
|
||||
def __init__(
|
||||
self, config_file: str = "medicines.json", logger: logging.Logger = None
|
||||
):
|
||||
self.config_file = config_file
|
||||
self.logger = logger or logging.getLogger(__name__)
|
||||
self.medicines: dict[str, Medicine] = {}
|
||||
self._load_medicines()
|
||||
|
||||
def _get_default_medicines(self) -> list[Medicine]:
|
||||
"""Get the default medicine configuration."""
|
||||
return [
|
||||
Medicine(
|
||||
key="bupropion",
|
||||
display_name="Bupropion",
|
||||
dosage_info="150/300 mg",
|
||||
quick_doses=["150", "300"],
|
||||
color="#FF6B6B",
|
||||
default_enabled=True,
|
||||
),
|
||||
Medicine(
|
||||
key="hydroxyzine",
|
||||
display_name="Hydroxyzine",
|
||||
dosage_info="25 mg",
|
||||
quick_doses=["25", "50"],
|
||||
color="#4ECDC4",
|
||||
default_enabled=False,
|
||||
),
|
||||
Medicine(
|
||||
key="gabapentin",
|
||||
display_name="Gabapentin",
|
||||
dosage_info="100 mg",
|
||||
quick_doses=["100", "300", "600"],
|
||||
color="#45B7D1",
|
||||
default_enabled=False,
|
||||
),
|
||||
Medicine(
|
||||
key="propranolol",
|
||||
display_name="Propranolol",
|
||||
dosage_info="10 mg",
|
||||
quick_doses=["10", "20", "40"],
|
||||
color="#96CEB4",
|
||||
default_enabled=True,
|
||||
),
|
||||
Medicine(
|
||||
key="quetiapine",
|
||||
display_name="Quetiapine",
|
||||
dosage_info="25 mg",
|
||||
quick_doses=["25", "50", "100"],
|
||||
color="#FFEAA7",
|
||||
default_enabled=False,
|
||||
),
|
||||
]
|
||||
|
||||
def _load_medicines(self) -> None:
|
||||
"""Load medicines from configuration file."""
|
||||
if os.path.exists(self.config_file):
|
||||
try:
|
||||
with open(self.config_file) as f:
|
||||
data = json.load(f)
|
||||
|
||||
self.medicines = {}
|
||||
for medicine_data in data.get("medicines", []):
|
||||
medicine = Medicine(**medicine_data)
|
||||
self.medicines[medicine.key] = medicine
|
||||
|
||||
self.logger.info(
|
||||
f"Loaded {len(self.medicines)} medicines from {self.config_file}"
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error loading medicines config: {e}")
|
||||
self._create_default_config()
|
||||
else:
|
||||
self._create_default_config()
|
||||
|
||||
def _create_default_config(self) -> None:
|
||||
"""Create default medicine configuration."""
|
||||
default_medicines = self._get_default_medicines()
|
||||
self.medicines = {med.key: med for med in default_medicines}
|
||||
self.save_medicines()
|
||||
self.logger.info("Created default medicine configuration")
|
||||
|
||||
def save_medicines(self) -> bool:
|
||||
"""Save current medicines to configuration file."""
|
||||
try:
|
||||
data = {
|
||||
"medicines": [asdict(medicine) for medicine in self.medicines.values()]
|
||||
}
|
||||
|
||||
with open(self.config_file, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
self.logger.info(
|
||||
f"Saved {len(self.medicines)} medicines to {self.config_file}"
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error saving medicines config: {e}")
|
||||
return False
|
||||
|
||||
def get_all_medicines(self) -> dict[str, Medicine]:
|
||||
"""Get all medicines."""
|
||||
return self.medicines.copy()
|
||||
|
||||
def get_medicine(self, key: str) -> Medicine | None:
|
||||
"""Get a specific medicine by key."""
|
||||
return self.medicines.get(key)
|
||||
|
||||
def add_medicine(self, medicine: Medicine) -> bool:
|
||||
"""Add a new medicine."""
|
||||
if medicine.key in self.medicines:
|
||||
self.logger.warning(f"Medicine with key '{medicine.key}' already exists")
|
||||
return False
|
||||
|
||||
self.medicines[medicine.key] = medicine
|
||||
return self.save_medicines()
|
||||
|
||||
def update_medicine(self, key: str, medicine: Medicine) -> bool:
|
||||
"""Update an existing medicine."""
|
||||
if key not in self.medicines:
|
||||
self.logger.warning(f"Medicine with key '{key}' does not exist")
|
||||
return False
|
||||
|
||||
# If key is changing, remove old entry
|
||||
if key != medicine.key:
|
||||
del self.medicines[key]
|
||||
|
||||
self.medicines[medicine.key] = medicine
|
||||
return self.save_medicines()
|
||||
|
||||
def remove_medicine(self, key: str) -> bool:
|
||||
"""Remove a medicine."""
|
||||
if key not in self.medicines:
|
||||
self.logger.warning(f"Medicine with key '{key}' does not exist")
|
||||
return False
|
||||
|
||||
del self.medicines[key]
|
||||
return self.save_medicines()
|
||||
|
||||
def get_medicine_keys(self) -> list[str]:
|
||||
"""Get list of all medicine keys."""
|
||||
return list(self.medicines.keys())
|
||||
|
||||
def get_display_names(self) -> dict[str, str]:
|
||||
"""Get mapping of keys to display names."""
|
||||
return {key: med.display_name for key, med in self.medicines.items()}
|
||||
|
||||
def get_quick_doses(self, key: str) -> list[str]:
|
||||
"""Get quick dose options for a medicine."""
|
||||
medicine = self.medicines.get(key)
|
||||
return medicine.quick_doses if medicine else ["25", "50"]
|
||||
|
||||
def get_graph_colors(self) -> dict[str, str]:
|
||||
"""Get mapping of medicine keys to graph colors."""
|
||||
return {key: med.color for key, med in self.medicines.items()}
|
||||
|
||||
def get_default_enabled_medicines(self) -> list[str]:
|
||||
"""Get list of medicines that should be enabled by default in graphs."""
|
||||
return [key for key, med in self.medicines.items() if med.default_enabled]
|
||||
|
||||
def get_medicine_vars_dict(self) -> dict[str, tuple[Any, str]]:
|
||||
"""Get medicine variables dictionary for UI compatibility."""
|
||||
# This maintains compatibility with existing UI code
|
||||
import tkinter as tk
|
||||
|
||||
return {
|
||||
key: (tk.IntVar(value=0), f"{med.display_name} {med.dosage_info}")
|
||||
for key, med in self.medicines.items()
|
||||
}
|
||||
+36
-56
@@ -9,13 +9,18 @@ from typing import Any
|
||||
|
||||
from PIL import Image, ImageTk
|
||||
|
||||
from medicine_manager import MedicineManager
|
||||
|
||||
|
||||
class UIManager:
|
||||
"""Handle UI creation and management for the application."""
|
||||
|
||||
def __init__(self, root: tk.Tk, logger: logging.Logger) -> None:
|
||||
def __init__(
|
||||
self, root: tk.Tk, logger: logging.Logger, medicine_manager: MedicineManager
|
||||
) -> None:
|
||||
self.root: tk.Tk = root
|
||||
self.logger: logging.Logger = logger
|
||||
self.medicine_manager = medicine_manager
|
||||
|
||||
def setup_application_icon(self, img_path: str) -> bool:
|
||||
"""Set up the application icon."""
|
||||
@@ -157,14 +162,15 @@ class UIManager:
|
||||
medicine_frame.grid(row=4, column=1, padx=0, pady=10, sticky="nsew")
|
||||
medicine_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# Store medicine variables (checkboxes only)
|
||||
medicine_vars: dict[str, tuple[tk.IntVar, str]] = {
|
||||
"bupropion": (tk.IntVar(value=0), "Bupropion 150/300 mg"),
|
||||
"hydroxyzine": (tk.IntVar(value=0), "Hydroxyzine 25mg"),
|
||||
"gabapentin": (tk.IntVar(value=0), "Gabapentin 100mg"),
|
||||
"propranolol": (tk.IntVar(value=0), "Propranolol 10mg"),
|
||||
"quetiapine": (tk.IntVar(value=0), "Quetiapine 25mg"),
|
||||
}
|
||||
# Store medicine variables (checkboxes only) - dynamic based on medicine manager
|
||||
medicine_vars: dict[str, tuple[tk.IntVar, str]] = {}
|
||||
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
medicine = self.medicine_manager.get_medicine(medicine_key)
|
||||
if medicine:
|
||||
var = tk.IntVar(value=0)
|
||||
text = f"{medicine.display_name} {medicine.dosage_info}"
|
||||
medicine_vars[medicine_key] = (var, text)
|
||||
|
||||
for idx, (_med_name, (var, text)) in enumerate(medicine_vars.items()):
|
||||
# Just checkbox for medicine taken
|
||||
@@ -218,53 +224,34 @@ class UIManager:
|
||||
table_frame.grid_rowconfigure(0, weight=1)
|
||||
table_frame.grid_columnconfigure(0, weight=1)
|
||||
|
||||
columns: list[str] = [
|
||||
"Date",
|
||||
"Depression",
|
||||
"Anxiety",
|
||||
"Sleep",
|
||||
"Appetite",
|
||||
"Bupropion",
|
||||
"Hydroxyzine",
|
||||
"Gabapentin",
|
||||
"Propranolol",
|
||||
"Quetiapine",
|
||||
"Note",
|
||||
]
|
||||
|
||||
tree: ttk.Treeview = ttk.Treeview(table_frame, columns=columns, show="headings")
|
||||
|
||||
col_labels: list[str] = [
|
||||
"Date",
|
||||
"Depression",
|
||||
"Anxiety",
|
||||
"Sleep",
|
||||
"Appetite",
|
||||
"Bupropion 150/300 mg",
|
||||
"Hydroxyzine 25mg",
|
||||
"Gabapentin 100mg",
|
||||
"Propranolol 10mg",
|
||||
"Quetiapine 25mg",
|
||||
"Note",
|
||||
]
|
||||
|
||||
for col, label in zip(columns, col_labels, strict=False):
|
||||
tree.heading(col, text=label)
|
||||
|
||||
# Build columns dynamically
|
||||
columns: list[str] = ["Date", "Depression", "Anxiety", "Sleep", "Appetite"]
|
||||
col_labels: list[str] = ["Date", "Depression", "Anxiety", "Sleep", "Appetite"]
|
||||
col_settings: list[tuple[str, int, str]] = [
|
||||
("Date", 80, "center"),
|
||||
("Depression", 80, "center"),
|
||||
("Anxiety", 80, "center"),
|
||||
("Sleep", 80, "center"),
|
||||
("Appetite", 80, "center"),
|
||||
("Bupropion", 120, "center"),
|
||||
("Hydroxyzine", 120, "center"),
|
||||
("Gabapentin", 120, "center"),
|
||||
("Propranolol", 120, "center"),
|
||||
("Quetiapine", 120, "center"),
|
||||
("Note", 300, "w"),
|
||||
]
|
||||
|
||||
# Add medicine columns dynamically
|
||||
for medicine_key in self.medicine_manager.get_medicine_keys():
|
||||
medicine = self.medicine_manager.get_medicine(medicine_key)
|
||||
if medicine:
|
||||
columns.append(medicine.display_name)
|
||||
col_labels.append(f"{medicine.display_name} {medicine.dosage_info}")
|
||||
col_settings.append((medicine.display_name, 120, "center"))
|
||||
|
||||
columns.append("Note")
|
||||
col_labels.append("Note")
|
||||
col_settings.append(("Note", 300, "w"))
|
||||
|
||||
tree: ttk.Treeview = ttk.Treeview(table_frame, columns=columns, show="headings")
|
||||
|
||||
for col, label in zip(columns, col_labels, strict=False):
|
||||
tree.heading(col, text=label)
|
||||
|
||||
for col, width, anchor in col_settings:
|
||||
tree.column(col, width=width, anchor=anchor)
|
||||
|
||||
@@ -918,14 +905,7 @@ class UIManager:
|
||||
|
||||
def _get_quick_doses(self, medicine_key: str) -> list[str]:
|
||||
"""Get common dose amounts for quick selection."""
|
||||
dose_map = {
|
||||
"bupropion": ["150", "300"],
|
||||
"hydroxyzine": ["25", "50"],
|
||||
"gabapentin": ["100", "300", "600"],
|
||||
"propranolol": ["10", "20", "40"],
|
||||
"quetiapine": ["25", "50", "100"],
|
||||
}
|
||||
return dose_map.get(medicine_key, ["25", "50"])
|
||||
return self.medicine_manager.get_quick_doses(medicine_key)
|
||||
|
||||
def _populate_dose_history(self, text_widget: tk.Text, doses_str: str) -> None:
|
||||
"""Populate dose history text widget with formatted dose data."""
|
||||
|
||||
Reference in New Issue
Block a user