b7c01bc373
Build and Push Docker Image / build-and-push (push) Has been cancelled
- Renamed `initialize_csv` to `_initialize_csv_file` in `DataManager` for better clarity. - Updated method calls in `GraphManager` from `_create_toggle_controls` to `_create_chart_toggles` and `_on_toggle_changed` to `_handle_toggle_changed`. - Changed method names in `MedTrackerApp` from `on_closing` to `handle_window_closing`, `add_entry` to `add_new_entry`, and `load_data` to `refresh_data_display`. - Adjusted corresponding test method names in `TestMedTrackerApp` to reflect the new method names. - Updated `UIManager` method names from `setup_icon` to `setup_application_icon` and adjusted related tests accordingly.
268 lines
9.7 KiB
Python
268 lines
9.7 KiB
Python
import csv
|
|
import logging
|
|
import os
|
|
|
|
import pandas as pd
|
|
|
|
|
|
class DataManager:
|
|
"""Handle all data operations for the application."""
|
|
|
|
def __init__(self, filename: str, logger: logging.Logger) -> None:
|
|
self.filename: str = filename
|
|
self.logger: logging.Logger = logger
|
|
self._initialize_csv_file()
|
|
|
|
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",
|
|
]
|
|
)
|
|
|
|
def load_data(self) -> pd.DataFrame:
|
|
"""Load data from CSV file."""
|
|
if not os.path.exists(self.filename) or os.path.getsize(self.filename) == 0:
|
|
self.logger.warning("CSV file is empty or doesn't exist. No data to load.")
|
|
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("")
|
|
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.")
|
|
return pd.DataFrame()
|
|
except Exception as e:
|
|
self.logger.error(f"Error loading data: {str(e)}")
|
|
return pd.DataFrame()
|
|
|
|
def add_entry(self, entry_data: list[str | int]) -> bool:
|
|
"""Add a new entry to the CSV file."""
|
|
try:
|
|
# Check if date already exists
|
|
df: pd.DataFrame = self.load_data()
|
|
date_to_add: str = str(entry_data[0])
|
|
|
|
if not df.empty and date_to_add in df["date"].values:
|
|
self.logger.warning(f"Entry with date {date_to_add} already exists.")
|
|
return False
|
|
|
|
with open(self.filename, mode="a", newline="") as file:
|
|
writer = csv.writer(file)
|
|
writer.writerow(entry_data)
|
|
return True
|
|
except Exception as e:
|
|
self.logger.error(f"Error adding entry: {str(e)}")
|
|
return False
|
|
|
|
def update_entry(self, original_date: str, values: list[str | int]) -> bool:
|
|
"""Update an existing entry identified by original_date."""
|
|
try:
|
|
df: pd.DataFrame = self.load_data()
|
|
new_date: str = str(values[0])
|
|
|
|
# If the date is being changed, check if the new date already exists
|
|
if original_date != new_date and new_date in df["date"].values:
|
|
self.logger.warning(
|
|
f"Cannot update: entry with date {new_date} already exists."
|
|
)
|
|
return False
|
|
|
|
# Find the row to update using original_date as a unique identifier
|
|
# Handle both old format (10 columns) and new format (16 columns)
|
|
if len(values) == 16:
|
|
# New format with all dose columns including quetiapine
|
|
df.loc[
|
|
df["date"] == original_date,
|
|
[
|
|
"date",
|
|
"depression",
|
|
"anxiety",
|
|
"sleep",
|
|
"appetite",
|
|
"bupropion",
|
|
"bupropion_doses",
|
|
"hydroxyzine",
|
|
"hydroxyzine_doses",
|
|
"gabapentin",
|
|
"gabapentin_doses",
|
|
"propranolol",
|
|
"propranolol_doses",
|
|
"quetiapine",
|
|
"quetiapine_doses",
|
|
"note",
|
|
],
|
|
] = values
|
|
elif len(values) == 14:
|
|
# Format without quetiapine
|
|
df.loc[
|
|
df["date"] == original_date,
|
|
[
|
|
"date",
|
|
"depression",
|
|
"anxiety",
|
|
"sleep",
|
|
"appetite",
|
|
"bupropion",
|
|
"bupropion_doses",
|
|
"hydroxyzine",
|
|
"hydroxyzine_doses",
|
|
"gabapentin",
|
|
"gabapentin_doses",
|
|
"propranolol",
|
|
"propranolol_doses",
|
|
"note",
|
|
],
|
|
] = values
|
|
else:
|
|
# Old format - only update the user-editable columns
|
|
df.loc[
|
|
df["date"] == original_date,
|
|
[
|
|
"date",
|
|
"depression",
|
|
"anxiety",
|
|
"sleep",
|
|
"appetite",
|
|
"bupropion",
|
|
"hydroxyzine",
|
|
"gabapentin",
|
|
"propranolol",
|
|
"note",
|
|
],
|
|
] = values
|
|
df.to_csv(self.filename, index=False)
|
|
return True
|
|
except Exception as e:
|
|
self.logger.error(f"Error updating entry: {str(e)}")
|
|
return False
|
|
|
|
def delete_entry(self, date: str) -> bool:
|
|
"""Delete an entry identified by date."""
|
|
try:
|
|
df: pd.DataFrame = self.load_data()
|
|
# 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)
|
|
return True
|
|
except Exception as e:
|
|
self.logger.error(f"Error deleting entry: {str(e)}")
|
|
return False
|
|
|
|
def add_medicine_dose(self, date: str, medicine_name: str, dose: str) -> bool:
|
|
"""Add a medicine dose to today's entry."""
|
|
from datetime import datetime
|
|
|
|
try:
|
|
df: pd.DataFrame = self.load_data()
|
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
dose_entry = f"{timestamp}:{dose}"
|
|
|
|
# Find or create entry for the given date
|
|
if df.empty or date not in df["date"].values:
|
|
# Create new entry for today with default values
|
|
new_entry = {
|
|
"date": date,
|
|
"depression": 0,
|
|
"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": "",
|
|
}
|
|
df = pd.concat([df, pd.DataFrame([new_entry])], ignore_index=True)
|
|
|
|
# Add dose to the appropriate medicine
|
|
dose_column = f"{medicine_name}_doses"
|
|
mask = df["date"] == date
|
|
current_doses = df.loc[mask, dose_column].iloc[0]
|
|
|
|
if current_doses:
|
|
df.loc[mask, dose_column] = current_doses + "|" + dose_entry
|
|
else:
|
|
df.loc[mask, dose_column] = dose_entry
|
|
|
|
# Mark medicine as taken (set to 1)
|
|
df.loc[mask, medicine_name] = 1
|
|
|
|
df.to_csv(self.filename, index=False)
|
|
return True
|
|
except Exception as e:
|
|
self.logger.error(f"Error adding medicine dose: {str(e)}")
|
|
return False
|
|
|
|
def get_today_medicine_doses(
|
|
self, date: str, medicine_name: str
|
|
) -> list[tuple[str, str]]:
|
|
"""Get list of (timestamp, dose) tuples for a medicine on a given date."""
|
|
try:
|
|
df: pd.DataFrame = self.load_data()
|
|
if df.empty or date not in df["date"].values:
|
|
return []
|
|
|
|
dose_column = f"{medicine_name}_doses"
|
|
doses_str = df.loc[df["date"] == date, dose_column].iloc[0]
|
|
|
|
if not doses_str:
|
|
return []
|
|
|
|
doses = []
|
|
for dose_entry in doses_str.split("|"):
|
|
if ":" in dose_entry:
|
|
timestamp, dose = dose_entry.split(":", 1)
|
|
doses.append((timestamp, dose))
|
|
|
|
return doses
|
|
except Exception as e:
|
|
self.logger.error(f"Error getting medicine doses: {str(e)}")
|
|
return []
|