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:
+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():
|
||||
|
||||
Reference in New Issue
Block a user