Refactor MedTrackerApp and UI components for improved structure and readability
- Simplified initialization logic in init.py - Consolidated testing_mode assignment - Removed unnecessary else statements - Created UIManager class to handle UI-related tasks - Modularized input frame creation, table frame creation, and graph frame creation - Enhanced edit window creation with better organization and error handling - Updated data management methods to improve clarity and maintainability - Improved logging for better debugging and tracking of application flow
This commit is contained in:
+179
-530
@@ -1,399 +1,134 @@
|
||||
import csv
|
||||
import logging
|
||||
import os
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox, ttk
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import pandas as pd
|
||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
|
||||
import sys
|
||||
from PIL import Image, ImageTk
|
||||
import tkinter as tk
|
||||
from tkinter import messagebox
|
||||
from typing import Dict, List, Tuple, Any, Callable, Union
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from init import logger
|
||||
from constants import LOG_LEVEL
|
||||
from data_manager import DataManager
|
||||
from graph_manager import GraphManager
|
||||
from ui_manager import UIManager
|
||||
|
||||
|
||||
class MedTrackerApp:
|
||||
def __init__(self, root):
|
||||
|
||||
self.root = root
|
||||
def __init__(self, root: tk.Tk) -> None:
|
||||
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.iconbitmap("app_icon.ico")
|
||||
# screen_width = self.root.winfo_screenwidth()
|
||||
# screen_height = self.root.winfo_screenheight()
|
||||
# self.root.geometry(f"{screen_width}x{screen_height}")
|
||||
# self.root.configure(background='gold')
|
||||
# self.root.lift()
|
||||
# self.root.attributes("-topmost", True)
|
||||
# self.root.geometry("800x600")
|
||||
|
||||
# Set up data file
|
||||
self.filename: str = "thechart_data.csv"
|
||||
if len(sys.argv) > 1:
|
||||
script_name = sys.argv[0]
|
||||
first_argument = sys.argv[1]
|
||||
|
||||
first_argument: str = sys.argv[1]
|
||||
if LOG_LEVEL == "DEBUG":
|
||||
logger.debug(f"Script name: {script_name}")
|
||||
logger.debug(f"Script name: {sys.argv[0]}")
|
||||
logger.debug(f"First argument: {first_argument}")
|
||||
|
||||
if os.path.exists(first_argument):
|
||||
self.filename = first_argument
|
||||
logger.info(f"Using data file: {first_argument}")
|
||||
else:
|
||||
logger.warning(
|
||||
f"Data file {first_argument} does not exist."
|
||||
f" Using default file: {self.filename}"
|
||||
f"Data file {first_argument} does not exist. Using default file: {self.filename}"
|
||||
)
|
||||
|
||||
self.make_icon(
|
||||
img="/home/will/Code/thechart/chart-671.png",
|
||||
logger=logger,
|
||||
# Initialize managers
|
||||
self.ui_manager: UIManager = UIManager(root, logger)
|
||||
self.data_manager: DataManager = DataManager(self.filename, logger)
|
||||
|
||||
# Set up application icon
|
||||
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)
|
||||
|
||||
# Set up the main application UI
|
||||
self._setup_main_ui()
|
||||
|
||||
def _setup_main_ui(self) -> None:
|
||||
"""Set up the main UI components."""
|
||||
import tkinter.ttk as ttk
|
||||
|
||||
# --- Main Frame ---
|
||||
main_frame: ttk.Frame = ttk.Frame(self.root, padding="10")
|
||||
main_frame.grid(row=0, column=0, sticky="nsew")
|
||||
|
||||
# Configure root window grid
|
||||
self.root.grid_rowconfigure(0, weight=1)
|
||||
self.root.grid_columnconfigure(0, weight=1)
|
||||
|
||||
# Configure main frame grid for scaling
|
||||
for i in range(2):
|
||||
main_frame.grid_rowconfigure(i, weight=1 if i == 1 else 0)
|
||||
main_frame.grid_columnconfigure(i, weight=3 if i == 1 else 1)
|
||||
logger.debug("Main frame and root grid configured for scaling.")
|
||||
|
||||
# --- Create Graph Frame ---
|
||||
graph_frame: ttk.Frame = self.ui_manager.create_graph_frame(main_frame)
|
||||
self.graph_manager: GraphManager = GraphManager(graph_frame)
|
||||
|
||||
# --- Create Input Frame ---
|
||||
input_ui: Dict[str, Any] = self.ui_manager.create_input_frame(
|
||||
main_frame
|
||||
)
|
||||
self.input_frame: ttk.Frame = input_ui["frame"]
|
||||
self.symptom_vars: Dict[str, tk.IntVar] = input_ui["symptom_vars"]
|
||||
self.medicine_vars: Dict[str, List[Union[tk.IntVar, ttk.Spinbox]]] = (
|
||||
input_ui["medicine_vars"]
|
||||
)
|
||||
self.note_var: tk.StringVar = input_ui["note_var"]
|
||||
self.date_var: tk.StringVar = input_ui["date_var"]
|
||||
|
||||
# Add buttons to input frame
|
||||
self.ui_manager.add_buttons(
|
||||
self.input_frame,
|
||||
[
|
||||
{
|
||||
"text": "Add Entry",
|
||||
"command": self.add_entry,
|
||||
"fill": "both",
|
||||
"expand": True,
|
||||
},
|
||||
{"text": "Quit", "command": self.on_closing},
|
||||
],
|
||||
)
|
||||
|
||||
self.filename = "thechart_data.csv"
|
||||
self.initialize_csv()
|
||||
|
||||
main_frame = ttk.Frame(self.root, padding="10")
|
||||
main_frame.grid(row=0, column=0)
|
||||
|
||||
# --- Input Frame ---
|
||||
input_frame = ttk.LabelFrame(main_frame, text="New Entry")
|
||||
input_frame.grid(row=1, column=0, padx=10, pady=10, sticky="nsew")
|
||||
|
||||
ttk.Label(input_frame, text="Depression (0-10):").grid(
|
||||
row=0, column=0, sticky="w", padx=5, pady=2
|
||||
# --- Create Table Frame ---
|
||||
table_ui: Dict[str, Any] = self.ui_manager.create_table_frame(
|
||||
main_frame
|
||||
)
|
||||
self.depression_var = tk.IntVar()
|
||||
ttk.Scale(
|
||||
input_frame,
|
||||
from_=0,
|
||||
to=10,
|
||||
orient=tk.HORIZONTAL,
|
||||
variable=self.depression_var,
|
||||
).grid(row=0, column=1, sticky="ew")
|
||||
|
||||
ttk.Label(input_frame, text="Anxiety (0-10):").grid(
|
||||
row=1, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
self.anxiety_var = tk.IntVar()
|
||||
ttk.Scale(
|
||||
input_frame,
|
||||
from_=0,
|
||||
to=10,
|
||||
orient=tk.HORIZONTAL,
|
||||
variable=self.anxiety_var,
|
||||
).grid(row=1, column=1, sticky="ew")
|
||||
|
||||
ttk.Label(input_frame, text="Sleep Quality (0-10):").grid(
|
||||
row=2, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
self.sleep_var = tk.IntVar()
|
||||
ttk.Scale(
|
||||
input_frame,
|
||||
from_=0,
|
||||
to=10,
|
||||
orient=tk.HORIZONTAL,
|
||||
variable=self.sleep_var,
|
||||
).grid(row=2, column=1, sticky="ew")
|
||||
|
||||
ttk.Label(input_frame, text="Appetite (0-10):").grid(
|
||||
row=3, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
self.appetite_var = tk.IntVar()
|
||||
ttk.Scale(
|
||||
input_frame,
|
||||
from_=0,
|
||||
to=10,
|
||||
orient=tk.HORIZONTAL,
|
||||
variable=self.appetite_var,
|
||||
).grid(row=3, column=1, sticky="ew")
|
||||
|
||||
ttk.Label(input_frame, text="Treatment:").grid(
|
||||
row=4, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
|
||||
medicine_frame = ttk.LabelFrame(input_frame, text="Medicine")
|
||||
medicine_frame.grid(row=4, column=1, padx=0, pady=10, sticky="nsew")
|
||||
|
||||
self.bupropion_var = tk.IntVar(value=0)
|
||||
self.hydroxyzine_var = tk.IntVar(value=0)
|
||||
self.gabapentin_var = tk.IntVar(value=0)
|
||||
self.propranolol_var = tk.IntVar(value=0)
|
||||
|
||||
ttk.Checkbutton(
|
||||
medicine_frame,
|
||||
text="Bupropion 150mg",
|
||||
variable=self.bupropion_var,
|
||||
name="bupropion_check",
|
||||
command=lambda: self.toggle_checkbox(obj_name="bupropion_check"),
|
||||
).grid(row=0, column=0, sticky="w", padx=5, pady=2)
|
||||
|
||||
ttk.Checkbutton(
|
||||
medicine_frame,
|
||||
text="Hydroxyzine 25mg",
|
||||
variable=self.hydroxyzine_var,
|
||||
name="hydroxyzine_check",
|
||||
command=lambda: self.toggle_checkbox(obj_name="hydroxyzine_check"),
|
||||
).grid(row=1, column=0, sticky="w", padx=5, pady=2)
|
||||
|
||||
ttk.Checkbutton(
|
||||
medicine_frame,
|
||||
text="Gabapentin 100mg",
|
||||
variable=self.gabapentin_var,
|
||||
name="gabapentin_check",
|
||||
command=lambda: self.toggle_checkbox(obj_name="gabapentin_check"),
|
||||
).grid(row=2, column=0, sticky="w", padx=5, pady=2)
|
||||
|
||||
ttk.Checkbutton(
|
||||
medicine_frame,
|
||||
text="Propranolol 10mg",
|
||||
name="propranolol_check",
|
||||
variable=self.propranolol_var,
|
||||
command=lambda: self.toggle_checkbox(obj_name="propranolol_check"),
|
||||
).grid(row=3, column=0, sticky="w", padx=5, pady=2)
|
||||
|
||||
ttk.Label(input_frame, text="Note:").grid(
|
||||
row=5, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
self.note_var = tk.StringVar()
|
||||
ttk.Entry(input_frame, textvariable=self.note_var).grid(
|
||||
row=5, column=1, sticky="ew", padx=5, pady=2
|
||||
)
|
||||
|
||||
ttk.Label(input_frame, text="Date (mm/dd/yyyy):").grid(
|
||||
row=6, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
self.date_var = tk.StringVar()
|
||||
ttk.Entry(
|
||||
input_frame, textvariable=self.date_var, justify="center"
|
||||
).grid(row=6, column=1, sticky="ew", padx=5, pady=2)
|
||||
|
||||
button_frame = ttk.Frame(input_frame)
|
||||
button_frame.grid(row=7, column=0, columnspan=2, pady=10)
|
||||
|
||||
ttk.Button(
|
||||
button_frame, text="Add Entry", command=self.add_entry
|
||||
).pack(side="left", padx=5, fill="both", expand=True)
|
||||
ttk.Button(button_frame, text="Quit", command=self.on_closing).pack(
|
||||
side="left", padx=5
|
||||
)
|
||||
|
||||
# --- Table Frame ---
|
||||
table_frame = ttk.LabelFrame(
|
||||
main_frame, text="Log (Double-click to edit)"
|
||||
)
|
||||
table_frame.grid(row=1, column=1, padx=10, pady=10, sticky="nsew")
|
||||
|
||||
self.tree = ttk.Treeview(
|
||||
table_frame,
|
||||
columns=(
|
||||
"Date",
|
||||
"Depression",
|
||||
"Anxiety",
|
||||
"Sleep",
|
||||
"Appetite",
|
||||
"Bupropion",
|
||||
"Hydroxyzine",
|
||||
"Gabapentin",
|
||||
"Propranolol",
|
||||
"Note",
|
||||
),
|
||||
show="headings",
|
||||
)
|
||||
self.tree.heading("Date", text="Date")
|
||||
self.tree.heading("Depression", text="Depression")
|
||||
self.tree.heading("Anxiety", text="Anxiety")
|
||||
self.tree.heading("Sleep", text="Sleep")
|
||||
self.tree.heading("Appetite", text="Appetite")
|
||||
self.tree.heading("Bupropion", text="Bupropion 150mg")
|
||||
self.tree.heading("Hydroxyzine", text="Hydroxyzine 25mg")
|
||||
self.tree.heading("Gabapentin", text="Gabapentin 100mg")
|
||||
self.tree.heading("Propranolol", text="Propranolol 10mg")
|
||||
self.tree.heading("Note", text="Note")
|
||||
|
||||
self.tree.column("Date", width=80, anchor="center")
|
||||
self.tree.column("Depression", width=80, anchor="center")
|
||||
self.tree.column("Anxiety", width=80, anchor="center")
|
||||
self.tree.column("Sleep", width=80, anchor="center")
|
||||
self.tree.column("Appetite", width=80, anchor="center")
|
||||
self.tree.column("Bupropion", width=120, anchor="center")
|
||||
self.tree.column("Hydroxyzine", width=120, anchor="center")
|
||||
self.tree.column("Gabapentin", width=120, anchor="center")
|
||||
self.tree.column("Propranolol", width=120, anchor="center")
|
||||
self.tree.column("Note", width=300, anchor="w")
|
||||
|
||||
# --- Bind double-click event ---
|
||||
self.tree: ttk.Treeview = table_ui["tree"]
|
||||
self.tree.bind("<Double-1>", self.on_double_click)
|
||||
self.tree.pack(side="left", fill="both", expand=True)
|
||||
|
||||
scrollbar = ttk.Scrollbar(
|
||||
table_frame, orient="vertical", command=self.tree.yview
|
||||
)
|
||||
self.tree.configure(yscrollcommand=scrollbar.set)
|
||||
scrollbar.pack(side="right", fill="y")
|
||||
|
||||
# --- Graph Frame ---
|
||||
graph_frame = ttk.LabelFrame(main_frame, text="Evolution")
|
||||
graph_frame.grid(
|
||||
row=0, column=0, columnspan=2, padx=10, pady=10, sticky="nsew"
|
||||
)
|
||||
|
||||
self.fig, self.ax = plt.subplots()
|
||||
self.canvas = FigureCanvasTkAgg(figure=self.fig, master=graph_frame)
|
||||
self.canvas.get_tk_widget().pack(fill="both", expand=True)
|
||||
|
||||
# Load data
|
||||
self.load_data()
|
||||
|
||||
def toggle_checkbox(obj_name: str) -> None:
|
||||
if ttk.Checkbutton.nametowidget(name=obj_name).get():
|
||||
ttk.Checkbutton.nametowidget(name=obj_name).set(False)
|
||||
else:
|
||||
ttk.Checkbutton.nametowidget(name=obj_name).set(True)
|
||||
|
||||
def on_double_click(self, event: any) -> None:
|
||||
def on_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:
|
||||
item_id = self.tree.selection()[0]
|
||||
item_values = self.tree.item(item_id, "values")
|
||||
self.create_edit_window(item_id, item_values)
|
||||
logger.debug(f"Editing item_id={item_id}, values={item_values}")
|
||||
self._create_edit_window(item_id, item_values)
|
||||
|
||||
def create_edit_window(self, item_id: str, values: tuple) -> None:
|
||||
def _create_edit_window(
|
||||
self, item_id: str, values: Tuple[str, ...]
|
||||
) -> None:
|
||||
"""Create a new Toplevel window for editing an entry."""
|
||||
edit_win = tk.Toplevel(master=self.root)
|
||||
edit_win.title("Edit Entry")
|
||||
# Define callbacks for edit window buttons
|
||||
callbacks: Dict[str, Callable] = {
|
||||
"save": self._save_edit,
|
||||
"delete": lambda win: self._delete_entry(win, item_id),
|
||||
}
|
||||
|
||||
# Unpack values
|
||||
date, dep, anx, slp, app, bup, hydro, gaba, prop, note = values
|
||||
# Create edit window using UI manager
|
||||
_: tk.Toplevel = self.ui_manager.create_edit_window(values, callbacks)
|
||||
|
||||
# Create variables for the widgets
|
||||
date_var = tk.StringVar(value=str(date))
|
||||
dep_var = tk.IntVar(value=int(dep))
|
||||
anx_var = tk.IntVar(value=int(anx))
|
||||
slp_var = tk.IntVar(value=int(slp))
|
||||
app_var = tk.IntVar(value=int(app))
|
||||
bup_var = tk.IntVar(value=int(bup))
|
||||
hydro_var = tk.IntVar(value=int(hydro))
|
||||
gaba_var = tk.IntVar(value=int(gaba))
|
||||
prop_var = tk.IntVar(value=int(prop))
|
||||
note_var = tk.StringVar(value=str(note))
|
||||
|
||||
# Create form widgets
|
||||
ttk.Label(edit_win, text="Depression:").grid(
|
||||
row=1, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
ttk.Scale(
|
||||
edit_win, from_=0, to=10, variable=dep_var, orient=tk.HORIZONTAL
|
||||
).grid(row=1, column=1, sticky="ew")
|
||||
|
||||
ttk.Label(edit_win, text="Anxiety:").grid(
|
||||
row=2, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
ttk.Scale(
|
||||
edit_win, from_=0, to=10, variable=anx_var, orient=tk.HORIZONTAL
|
||||
).grid(row=2, column=1, sticky="ew")
|
||||
|
||||
ttk.Label(edit_win, text="Sleep:").grid(
|
||||
row=3, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
ttk.Scale(
|
||||
edit_win, from_=0, to=10, variable=slp_var, orient=tk.HORIZONTAL
|
||||
).grid(row=3, column=1, sticky="ew")
|
||||
|
||||
ttk.Label(edit_win, text="Appetite:").grid(
|
||||
row=4, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
ttk.Scale(
|
||||
edit_win, from_=0, to=10, variable=app_var, orient=tk.HORIZONTAL
|
||||
).grid(row=4, column=1, sticky="ew")
|
||||
|
||||
ttk.Label(edit_win, text="Treatment:").grid(
|
||||
row=5, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
|
||||
medicine_frame = ttk.LabelFrame(edit_win, text="Medicine")
|
||||
medicine_frame.grid(row=5, column=1, padx=0, pady=10, sticky="nsew")
|
||||
|
||||
ttk.Checkbutton(
|
||||
medicine_frame,
|
||||
text="Bupropion 150mg",
|
||||
name="bupropion_check",
|
||||
variable=bup_var,
|
||||
command=lambda: self.toggle_checkbox(obj_name="bupropion_check"),
|
||||
).grid(row=0, column=0, sticky="w", padx=5, pady=2)
|
||||
|
||||
ttk.Checkbutton(
|
||||
medicine_frame,
|
||||
text="Hydroxyzine 25mg",
|
||||
name="hydroxyzine_check",
|
||||
variable=hydro_var,
|
||||
command=lambda: self.toggle_checkbox(obj_name="hydroxyzine_check"),
|
||||
).grid(row=1, column=0, sticky="w", padx=5, pady=2)
|
||||
|
||||
ttk.Checkbutton(
|
||||
medicine_frame,
|
||||
text="Gabapentin 100mg",
|
||||
name="gabapentin_check",
|
||||
variable=gaba_var,
|
||||
command=lambda: self.toggle_checkbox(obj_name="gabapentin_check"),
|
||||
).grid(row=2, column=0, sticky="w", padx=5, pady=2)
|
||||
|
||||
ttk.Checkbutton(
|
||||
medicine_frame,
|
||||
text="Propranolol 10mg",
|
||||
name="propranolol_check",
|
||||
variable=prop_var,
|
||||
command=lambda: self.toggle_checkbox(obj_name="propranolol_check"),
|
||||
).grid(row=3, column=0, sticky="w", padx=5, pady=2)
|
||||
|
||||
ttk.Label(edit_win, text="Note:").grid(
|
||||
row=6, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
ttk.Entry(edit_win, textvariable=note_var).grid(
|
||||
row=6, column=1, sticky="ew"
|
||||
)
|
||||
|
||||
ttk.Label(edit_win, text="Date:").grid(
|
||||
row=7, column=0, sticky="w", padx=5, pady=2
|
||||
)
|
||||
ttk.Entry(edit_win, textvariable=date_var).grid(
|
||||
row=7, column=1, sticky="ew"
|
||||
)
|
||||
|
||||
# Save and Cancel buttons
|
||||
save_btn = ttk.Button(
|
||||
edit_win,
|
||||
text="Save",
|
||||
command=lambda: self.save_edit(
|
||||
edit_win,
|
||||
date_var.get(),
|
||||
dep_var.get(),
|
||||
anx_var.get(),
|
||||
slp_var.get(),
|
||||
app_var.get(),
|
||||
bup_var.get(),
|
||||
hydro_var.get(),
|
||||
gaba_var.get(),
|
||||
prop_var.get(),
|
||||
note_var.get(),
|
||||
),
|
||||
)
|
||||
save_btn.grid(row=8, column=0, padx=5, pady=10)
|
||||
|
||||
cancel_btn = ttk.Button(
|
||||
edit_win, text="Cancel", command=edit_win.destroy
|
||||
)
|
||||
cancel_btn.grid(row=8, column=1, padx=5, pady=10)
|
||||
delete_btn = ttk.Button(
|
||||
edit_win,
|
||||
text="Delete",
|
||||
command=lambda: self.delete_entry(edit_win, item_id),
|
||||
)
|
||||
delete_btn.grid(row=8, column=2, padx=5, pady=10)
|
||||
|
||||
def save_edit(
|
||||
def _save_edit(
|
||||
self,
|
||||
edit_win: tk.Toplevel,
|
||||
date: str,
|
||||
@@ -407,207 +142,121 @@ class MedTrackerApp:
|
||||
prop: int,
|
||||
note: str,
|
||||
) -> None:
|
||||
"""
|
||||
Save the edited data to the CSV file.
|
||||
"""
|
||||
df = pd.read_csv(self.filename)
|
||||
# Find the row to update using the date as a unique identifier
|
||||
df.loc[
|
||||
df["date"] == date,
|
||||
[
|
||||
"date",
|
||||
"depression",
|
||||
"anxiety",
|
||||
"sleep",
|
||||
"appetite",
|
||||
"bupropion",
|
||||
"hydroxyzine",
|
||||
"gabapentin",
|
||||
"propranolol",
|
||||
"note",
|
||||
],
|
||||
] = [date, dep, anx, slp, app, bup, hydro, gaba, prop, note]
|
||||
# Write the updated dataframe back to the CSV
|
||||
df.to_csv(self.filename, index=False)
|
||||
"""Save the edited data to the CSV file."""
|
||||
values: List[Union[str, int]] = [
|
||||
date,
|
||||
dep,
|
||||
anx,
|
||||
slp,
|
||||
app,
|
||||
bup,
|
||||
hydro,
|
||||
gaba,
|
||||
prop,
|
||||
note,
|
||||
]
|
||||
|
||||
edit_win.destroy()
|
||||
messagebox.showinfo(
|
||||
"Success", "Entry updated successfully!", parent=self.root
|
||||
)
|
||||
self.clear_entries()
|
||||
self.load_data()
|
||||
if self.data_manager.update_entry(date, values):
|
||||
edit_win.destroy()
|
||||
messagebox.showinfo(
|
||||
"Success", "Entry updated successfully!", parent=self.root
|
||||
)
|
||||
self._clear_entries()
|
||||
self.load_data()
|
||||
else:
|
||||
messagebox.showerror(
|
||||
"Error", "Failed to save changes", parent=edit_win
|
||||
)
|
||||
|
||||
def on_closing(self) -> None:
|
||||
if messagebox.askokcancel(
|
||||
"Quit", "Do you want to quit the application?", parent=self.root
|
||||
):
|
||||
plt.close(self.fig)
|
||||
self.graph_manager.close()
|
||||
self.root.destroy()
|
||||
|
||||
def initialize_csv(self) -> None:
|
||||
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",
|
||||
"hydroxyzine",
|
||||
"gabapentin",
|
||||
"propranolol",
|
||||
"note",
|
||||
]
|
||||
)
|
||||
|
||||
def add_entry(self) -> None:
|
||||
with open(self.filename, mode="a", newline="") as file:
|
||||
writer = csv.writer(file)
|
||||
writer.writerow(
|
||||
[
|
||||
self.date_var.get(),
|
||||
self.depression_var.get(),
|
||||
self.anxiety_var.get(),
|
||||
self.sleep_var.get(),
|
||||
self.appetite_var.get(),
|
||||
self.bupropion_var.get(),
|
||||
self.hydroxyzine_var.get(),
|
||||
self.gabapentin_var.get(),
|
||||
self.propranolol_var.get(),
|
||||
self.note_var.get(),
|
||||
]
|
||||
"""Add a new entry to the CSV file."""
|
||||
entry: List[Union[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(),
|
||||
self.medicine_vars["hydroxyzine"][0].get(),
|
||||
self.medicine_vars["gabapentin"][0].get(),
|
||||
self.medicine_vars["propranolol"][0].get(),
|
||||
self.note_var.get(),
|
||||
]
|
||||
logger.debug(f"Adding entry: {entry}")
|
||||
|
||||
if self.data_manager.add_entry(entry):
|
||||
messagebox.showinfo(
|
||||
"Success", "Entry added successfully!", parent=self.root
|
||||
)
|
||||
self._clear_entries()
|
||||
self.load_data()
|
||||
else:
|
||||
messagebox.showerror(
|
||||
"Error", "Failed to add entry", parent=self.root
|
||||
)
|
||||
|
||||
messagebox.showinfo(
|
||||
"Success", "Entry added successfully!", parent=self.root
|
||||
)
|
||||
self.clear_entries()
|
||||
self.load_data()
|
||||
|
||||
def delete_entry(self, edit_win: tk.Toplevel, item_id: str) -> None:
|
||||
"""
|
||||
Delete the selected entry from the CSV file.
|
||||
"""
|
||||
def _delete_entry(self, edit_win: tk.Toplevel, item_id: str) -> None:
|
||||
"""Delete the selected entry from the CSV file."""
|
||||
logger.debug(f"Delete requested for item_id={item_id}")
|
||||
if messagebox.askyesno(
|
||||
"Delete Entry",
|
||||
"Are you sure you want to delete this entry?",
|
||||
parent=edit_win,
|
||||
):
|
||||
df = pd.read_csv(self.filename)
|
||||
# Get the date of the entry to delete
|
||||
date = self.tree.item(item_id, "values")[0]
|
||||
# Remove the row with the matching date
|
||||
df = df[df["date"] != date]
|
||||
# Write the updated dataframe back to the CSV
|
||||
df.to_csv(self.filename, index=False)
|
||||
date: str = self.tree.item(item_id, "values")[0]
|
||||
logger.debug(f"Deleting entry with date={date}")
|
||||
|
||||
edit_win.destroy()
|
||||
messagebox.showinfo(
|
||||
"Success", "Entry deleted successfully!", parent=edit_win
|
||||
)
|
||||
self.load_data()
|
||||
if self.data_manager.delete_entry(date):
|
||||
edit_win.destroy()
|
||||
messagebox.showinfo(
|
||||
"Success", "Entry deleted successfully!", parent=edit_win
|
||||
)
|
||||
self.load_data()
|
||||
else:
|
||||
messagebox.showerror(
|
||||
"Error", "Failed to delete entry", parent=edit_win
|
||||
)
|
||||
|
||||
def clear_entries(self) -> None:
|
||||
def _clear_entries(self) -> None:
|
||||
"""Clear all input fields."""
|
||||
logger.debug("Clearing input fields.")
|
||||
self.date_var.set("")
|
||||
self.depression_var.set(0)
|
||||
self.anxiety_var.set(0)
|
||||
self.sleep_var.set(0)
|
||||
self.appetite_var.set(0)
|
||||
self.bupropion_var.set(False)
|
||||
self.hydroxyzine_var.set(False)
|
||||
self.gabapentin_var.set(False)
|
||||
self.propranolol_var.set(False)
|
||||
for key in self.symptom_vars:
|
||||
self.symptom_vars[key].set(0)
|
||||
for key in self.medicine_vars:
|
||||
self.medicine_vars[key][0].set(0)
|
||||
self.note_var.set("")
|
||||
|
||||
def load_data(self) -> None:
|
||||
"""Load data from the CSV file into the table and graph."""
|
||||
logger.debug("Loading data from CSV.")
|
||||
|
||||
# Clear existing data in the treeview
|
||||
for i in self.tree.get_children():
|
||||
self.tree.delete(i)
|
||||
|
||||
if (
|
||||
os.path.exists(self.filename)
|
||||
and os.path.getsize(self.filename) > 0
|
||||
):
|
||||
try:
|
||||
df = pd.read_csv(
|
||||
self.filename,
|
||||
dtype={
|
||||
"depression": int,
|
||||
"anxiety": int,
|
||||
"sleep": int,
|
||||
"appetite": int,
|
||||
"bupropion": int,
|
||||
"hydroxyzine": int,
|
||||
"gabapentin": int,
|
||||
"propranolol": int,
|
||||
"note": str,
|
||||
"date": str,
|
||||
},
|
||||
).fillna("")
|
||||
df = df.sort_values(by="date").reset_index(drop=True)
|
||||
for index, row in df.iterrows():
|
||||
self.tree.insert(parent="", index="end", values=list(row))
|
||||
self.update_graph(df)
|
||||
except pd.errors.EmptyDataError:
|
||||
self.update_graph(pd.DataFrame())
|
||||
# Load data from the CSV file
|
||||
df: pd.DataFrame = self.data_manager.load_data()
|
||||
|
||||
def update_graph(self, df: pd.DataFrame) -> None:
|
||||
self.ax.clear()
|
||||
# Update the treeview with the data
|
||||
if not df.empty:
|
||||
df["date"] = pd.to_datetime(df["date"])
|
||||
df = df.sort_values(by="date")
|
||||
df.set_index(keys="date", inplace=True)
|
||||
self.ax.plot(
|
||||
df.index,
|
||||
df["depression"],
|
||||
marker="o",
|
||||
linestyle="-",
|
||||
label="Depression (0:good, 10:bad)",
|
||||
)
|
||||
self.ax.plot(
|
||||
df.index,
|
||||
df["anxiety"],
|
||||
marker="o",
|
||||
linestyle="-",
|
||||
label="Anxiety (0:good, 10:bad)",
|
||||
)
|
||||
self.ax.plot(
|
||||
df.index,
|
||||
df["sleep"],
|
||||
marker="o",
|
||||
linestyle="dashed",
|
||||
label="Sleep (0:bad, 10:good)",
|
||||
)
|
||||
self.ax.plot(
|
||||
df.index,
|
||||
df["appetite"],
|
||||
marker="o",
|
||||
linestyle="dashed",
|
||||
label="Appetite (0:bad, 10:good)",
|
||||
)
|
||||
self.ax.legend()
|
||||
self.ax.set_title("Medication Effects Over Time")
|
||||
self.ax.set_xlabel("Date")
|
||||
self.ax.set_ylabel("Rating (0-10)")
|
||||
self.fig.autofmt_xdate()
|
||||
self.canvas.draw()
|
||||
for index, row in df.iterrows():
|
||||
self.tree.insert(parent="", index="end", values=list(row))
|
||||
logger.debug(f"Loaded {len(df)} entries into treeview.")
|
||||
|
||||
def make_icon(self, img: str, logger: logging.Logger) -> None:
|
||||
try:
|
||||
icon_image = Image.open(img)
|
||||
icon_image = icon_image.resize(
|
||||
size=(32, 32), resample=Image.Resampling.NEAREST
|
||||
)
|
||||
icon_photo = ImageTk.PhotoImage(image=icon_image)
|
||||
self.root.iconphoto(True, icon_photo)
|
||||
self.root.wm_iconphoto(True, icon_photo)
|
||||
except FileNotFoundError:
|
||||
logger.warning("Icon file not found.")
|
||||
# Update the graph
|
||||
self.graph_manager.update_graph(df)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
root = tk.Tk()
|
||||
app = MedTrackerApp(root)
|
||||
root: tk.Tk = tk.Tk()
|
||||
app: MedTrackerApp = MedTrackerApp(root)
|
||||
root.mainloop()
|
||||
|
||||
Reference in New Issue
Block a user