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