From 29ccc5fbd94754768814aa07e8ff01be287b94e3 Mon Sep 17 00:00:00 2001 From: William Valentin Date: Thu, 17 Jul 2025 12:20:20 -0700 Subject: [PATCH] Refactored... --- src/main.py | 297 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 255 insertions(+), 42 deletions(-) diff --git a/src/main.py b/src/main.py index b4cfb91..34e35ad 100644 --- a/src/main.py +++ b/src/main.py @@ -1,7 +1,6 @@ import csv import os import tkinter as tk -from datetime import datetime from tkinter import messagebox, ttk import matplotlib.pyplot as plt @@ -12,18 +11,21 @@ from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg class MedTrackerApp: def __init__(self, root): self.root = root + self.root.resizable(True, True) + # self.root.iconbitmap("./snog_skype.jpg") self.root.title("Thechart - medication tracker") self.root.protocol("WM_DELETE_WINDOW", self.on_closing) + # self.root.geometry("800x600") self.filename = "thechart_data.csv" self.initialize_csv() main_frame = ttk.Frame(self.root, padding="10") - main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + main_frame.grid(row=0, column=0) # --- Input Frame --- input_frame = ttk.LabelFrame(main_frame, text="New Entry") - input_frame.grid(row=0, column=0, padx=10, pady=10, sticky="ew") + 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 @@ -65,16 +67,69 @@ class MedTrackerApp: variable=self.appetite_var, ).grid(row=3, column=1, sticky="ew") - ttk.Label(input_frame, text="Note:").grid( + 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=4, column=1, sticky="ew", padx=5, pady=2 + 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=5, column=0, columnspan=2, pady=10) + 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 ) @@ -84,20 +139,46 @@ class MedTrackerApp: # --- Table Frame --- table_frame = ttk.LabelFrame(main_frame, text="Log (Double-click to edit)") - table_frame.grid(row=1, column=0, padx=10, pady=10, sticky="nsew") + table_frame.grid(row=1, column=1, padx=10, pady=10, sticky="nsew") self.tree = ttk.Treeview( table_frame, - columns=("Timestamp", "Depression", "Anxiety", "Sleep", "Appetite", "Note"), + columns=( + "Date", + "Depression", + "Anxiety", + "Sleep", + "Appetite", + "Bupropion", + "Hydroxyzine", + "Gabapentin", + "Propranolol", + "Note", + ), show="headings", ) - self.tree.heading("Timestamp", text="Timestamp") + 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") + # --- NEW: Bind double-click event --- self.tree.bind("", self.on_double_click) self.tree.pack(side="left", fill="both", expand=True) @@ -110,44 +191,48 @@ class MedTrackerApp: # --- Graph Frame --- graph_frame = ttk.LabelFrame(main_frame, text="Evolution") - graph_frame.grid(row=0, column=1, rowspan=2, padx=10, pady=10, sticky="nsew") + graph_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=10, sticky="nsew") self.fig, self.ax = plt.subplots() - self.canvas = FigureCanvasTkAgg(self.fig, master=graph_frame) + self.canvas = FigureCanvasTkAgg(figure=self.fig, master=graph_frame) self.canvas.get_tk_widget().pack(fill="both", expand=True) - main_frame.columnconfigure(1, weight=3) - main_frame.rowconfigure(1, weight=1) - self.load_data() - def on_double_click(self, event) -> None: + 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: """Handle double-click event to edit an entry.""" 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) - def create_edit_window(self, item_id, values) -> None: + def create_edit_window(self, item_id: str, values: tuple) -> None: """Create a new Toplevel window for editing an entry.""" edit_win = tk.Toplevel(self.root) edit_win.title("Edit Entry") # Unpack values - ts, dep, anx, slp, app, note = values + date, dep, anx, slp, app, bup, hydro, gaba, prop, note = values # 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)) - note_var = tk.StringVar(value=note) + 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=f"Timestamp: {ts}").grid( - row=0, column=0, columnspan=2, padx=5, pady=5 - ) - ttk.Label(edit_win, text="Depression:").grid( row=1, column=0, sticky="w", padx=5, pady=2 ) @@ -176,10 +261,54 @@ class MedTrackerApp: edit_win, from_=1, to=10, variable=app_var, orient=tk.HORIZONTAL ).grid(row=4, column=1, sticky="ew") - ttk.Label(edit_win, text="Note:").grid( + ttk.Label(edit_win, text="Treatment:").grid( row=5, column=0, sticky="w", padx=5, pady=2 ) - ttk.Entry(edit_win, textvariable=note_var).grid(row=5, column=1, sticky="ew") + + 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( @@ -187,36 +316,69 @@ class MedTrackerApp: text="Save", command=lambda: self.save_edit( edit_win, - ts, + 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=6, column=0, padx=5, pady=10) + 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=6, column=1, padx=5, pady=10) + 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( - self, edit_win, timestamp, dep: int, anx: int, slp: int, app: int, note: str + self, + edit_win: tk.Toplevel, + date: str, + dep: int, + anx: int, + slp: int, + app: int, + bup: int, + hydro: int, + gaba: int, + 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 timestamp as a unique identifier + # Find the row to update using the date as a unique identifier df.loc[ - df["timestamp"] == timestamp, - ["depression", "anxiety", "sleep", "appetite", "note"], - ] = [dep, anx, slp, app, note] + 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) edit_win.destroy() messagebox.showinfo("Success", "Entry updated successfully!") + self.clear_entries() self.load_data() def on_closing(self) -> None: @@ -229,21 +391,35 @@ class MedTrackerApp: with open(self.filename, mode="w", newline="") as file: writer = csv.writer(file) writer.writerow( - ["timestamp", "depression", "anxiety", "sleep", "appetite", "note"] + [ + "date", + "depression", + "anxiety", + "sleep", + "appetite", + "bupropion", + "hydroxyzine", + "gabapentin", + "propranolol", + "note", + ] ) def add_entry(self) -> None: - timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") with open(self.filename, mode="a", newline="") as file: writer = csv.writer(file) writer.writerow( [ - timestamp, 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(), + self.date_var.get(), ] ) @@ -251,11 +427,35 @@ class MedTrackerApp: 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. + """ + if messagebox.askyesno( + "Delete Entry", "Are you sure you want to delete this entry?" + ): + 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) + + edit_win.destroy() + messagebox.showinfo("Success", "Entry deleted successfully!") + self.load_data() + def clear_entries(self) -> None: + 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) self.note_var.set("") def load_data(self) -> None: @@ -264,11 +464,24 @@ class MedTrackerApp: if os.path.exists(self.filename) and os.path.getsize(self.filename) > 0: try: - df = pd.read_csv(self.filename, dtype={"note": str}).fillna("") - # Sort by timestamp to keep order consistent - df = df.sort_values(by="timestamp").reset_index(drop=True) + 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("", "end", values=list(row)) + self.tree.insert(parent="", index="end", values=list(row)) self.update_graph(df) except pd.errors.EmptyDataError: self.update_graph(pd.DataFrame()) @@ -276,8 +489,8 @@ class MedTrackerApp: def update_graph(self, df: pd.DataFrame) -> None: self.ax.clear() if not df.empty: - df["timestamp"] = pd.to_datetime(df["timestamp"]) - df.set_index("timestamp", inplace=True) + df = df.sort_values(by="date") + df.set_index(keys="date", inplace=True) self.ax.plot( df.index, df["depression"], @@ -297,7 +510,7 @@ class MedTrackerApp: self.ax.legend() self.ax.set_title("Medication Effects Over Time") self.ax.set_xlabel("Date") - self.ax.set_ylabel("Rating (1-10)") + self.ax.set_ylabel("Rating (0-10)") self.fig.autofmt_xdate() self.canvas.draw()