Files
thechart/src/data_manager.py
T
William Valentin b7c01bc373
Build and Push Docker Image / build-and-push (push) Has been cancelled
Refactor method names for clarity and consistency across the application
- 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.
2025-07-30 12:32:17 -07:00

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 []