Add medicine management functionality with UI and data handling

- Implemented MedicineManagementWindow for adding, editing, and removing medicines.
- Created MedicineManager to handle medicine configurations, including loading and saving to JSON.
- Updated UIManager to dynamically generate medicine-related UI components based on the MedicineManager.
- Enhanced test suite with mock objects for MedicineManager to ensure proper functionality in DataManager tests.
- Added validation for medicine input fields in the UI.
- Introduced default medicine configurations for initial setup.
This commit is contained in:
William Valentin
2025-07-30 16:01:02 -07:00
parent ea30cb88c9
commit d7d4b332d4
34 changed files with 1370 additions and 2576 deletions

View File

@@ -3,10 +3,7 @@ Tests for the DataManager class.
"""
import os
import csv
import pytest
import pandas as pd
from unittest.mock import Mock, patch
import tempfile
from unittest.mock import patch
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))
@@ -17,20 +14,21 @@ from src.data_manager import DataManager
class TestDataManager:
"""Test cases for the DataManager class."""
def test_init(self, temp_csv_file, mock_logger):
def test_init(self, temp_csv_file, mock_logger, mock_medicine_manager):
"""Test DataManager initialization."""
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
assert dm.filename == temp_csv_file
assert dm.logger == mock_logger
assert dm.medicine_manager == mock_medicine_manager
assert os.path.exists(temp_csv_file)
def test_initialize_csv_creates_file_with_headers(self, temp_csv_file, mock_logger):
def test_initialize_csv_creates_file_with_headers(self, temp_csv_file, mock_logger, mock_medicine_manager):
"""Test that initialize_csv creates a file with proper headers."""
# Remove the file if it exists
if os.path.exists(temp_csv_file):
os.unlink(temp_csv_file)
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
# Check file exists and has correct headers
assert os.path.exists(temp_csv_file)
@@ -45,33 +43,33 @@ class TestDataManager:
]
assert headers == expected_headers
def test_initialize_csv_does_not_overwrite_existing_file(self, temp_csv_file, mock_logger):
def test_initialize_csv_does_not_overwrite_existing_file(self, temp_csv_file, mock_logger, mock_medicine_manager):
"""Test that initialize_csv does not overwrite existing file."""
# Write some data to the file first
with open(temp_csv_file, 'w') as f:
f.write("existing,data\n1,2\n")
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
# Check that existing data is preserved
with open(temp_csv_file, 'r') as f:
content = f.read()
assert "existing,data" in content
def test_load_data_empty_file(self, temp_csv_file, mock_logger):
def test_load_data_empty_file(self, temp_csv_file, mock_logger, mock_medicine_manager):
"""Test loading data from an empty file."""
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
df = dm.load_data()
assert df.empty
def test_load_data_nonexistent_file(self, mock_logger):
def test_load_data_nonexistent_file(self, mock_logger, mock_medicine_manager):
"""Test loading data from a nonexistent file."""
dm = DataManager("nonexistent.csv", mock_logger)
dm = DataManager("nonexistent.csv", mock_logger, mock_medicine_manager)
df = dm.load_data()
assert df.empty
mock_logger.warning.assert_called()
def test_load_data_with_valid_data(self, temp_csv_file, mock_logger, sample_data):
def test_load_data_with_valid_data(self, temp_csv_file, mock_logger, mock_medicine_manager, sample_data):
"""Test loading valid data from CSV file."""
# Write sample data to file
with open(temp_csv_file, 'w', newline='') as f:
@@ -86,7 +84,7 @@ class TestDataManager:
# Write sample data
writer.writerows(sample_data)
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
df = dm.load_data()
assert not df.empty
@@ -102,7 +100,7 @@ class TestDataManager:
assert df["anxiety"].dtype == int
assert df["note"].dtype == object
def test_load_data_sorted_by_date(self, temp_csv_file, mock_logger):
def test_load_data_sorted_by_date(self, temp_csv_file, mock_logger, mock_medicine_manager):
"""Test that loaded data is sorted by date."""
# Write data in random order
unsorted_data = [
@@ -121,7 +119,7 @@ class TestDataManager:
])
writer.writerows(unsorted_data)
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
df = dm.load_data()
# Check that data is sorted by date
@@ -129,9 +127,9 @@ class TestDataManager:
assert df.iloc[1]["note"] == "second"
assert df.iloc[2]["note"] == "third"
def test_add_entry_success(self, temp_csv_file, mock_logger):
def test_add_entry_success(self, temp_csv_file, mock_logger, mock_medicine_manager):
"""Test successfully adding an entry."""
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
entry = ["2024-01-01", 3, 2, 4, 3, 1, 0, 2, 1, "Test note"]
result = dm.add_entry(entry)
@@ -143,7 +141,7 @@ class TestDataManager:
assert df.iloc[0]["date"] == "2024-01-01"
assert df.iloc[0]["note"] == "Test note"
def test_add_entry_duplicate_date(self, temp_csv_file, mock_logger, sample_data):
def test_add_entry_duplicate_date(self, temp_csv_file, mock_logger, mock_medicine_manager, sample_data):
"""Test adding entry with duplicate date."""
# Add initial data
with open(temp_csv_file, 'w', newline='') as f:
@@ -156,7 +154,7 @@ class TestDataManager:
])
writer.writerows(sample_data)
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
# Try to add entry with existing date
duplicate_entry = ["2024-01-01", 5, 5, 5, 5, 1, "", 1, "", 1, "", 1, "", 0, "", "Duplicate"]
@@ -164,7 +162,7 @@ class TestDataManager:
assert result is False
mock_logger.warning.assert_called_with("Entry with date 2024-01-01 already exists.")
def test_update_entry_success(self, temp_csv_file, mock_logger, sample_data):
def test_update_entry_success(self, temp_csv_file, mock_logger, mock_medicine_manager, sample_data):
"""Test successfully updating an entry."""
# Add initial data
with open(temp_csv_file, 'w', newline='') as f:
@@ -177,7 +175,7 @@ class TestDataManager:
])
writer.writerows(sample_data)
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
updated_values = ["2024-01-01", 5, 5, 5, 5, 2, "", 2, "", 2, "", 2, "", 1, "", "Updated note"]
result = dm.update_entry("2024-01-01", updated_values)
@@ -189,7 +187,7 @@ class TestDataManager:
assert updated_row["depression"] == 5
assert updated_row["note"] == "Updated note"
def test_update_entry_change_date(self, temp_csv_file, mock_logger, sample_data):
def test_update_entry_change_date(self, temp_csv_file, mock_logger, mock_medicine_manager, sample_data):
"""Test updating an entry with a date change."""
# Add initial data
with open(temp_csv_file, 'w', newline='') as f:
@@ -202,7 +200,7 @@ class TestDataManager:
])
writer.writerows(sample_data)
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
updated_values = ["2024-01-05", 5, 5, 5, 5, 2, "", 2, "", 2, "", 2, "", 1, "", "Updated note"]
result = dm.update_entry("2024-01-01", updated_values)
@@ -213,7 +211,7 @@ class TestDataManager:
assert not any(df["date"] == "2024-01-01")
assert any(df["date"] == "2024-01-05")
def test_update_entry_duplicate_date(self, temp_csv_file, mock_logger, sample_data):
def test_update_entry_duplicate_date(self, temp_csv_file, mock_logger, mock_medicine_manager, sample_data):
"""Test updating entry to a date that already exists."""
# Add initial data
with open(temp_csv_file, 'w', newline='') as f:
@@ -226,7 +224,7 @@ class TestDataManager:
])
writer.writerows(sample_data)
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
# Try to change date to one that already exists
updated_values = ["2024-01-02", 5, 5, 5, 5, 2, "", 2, "", 2, "", 2, "", 1, "", "Updated note"]
@@ -236,7 +234,7 @@ class TestDataManager:
"Cannot update: entry with date 2024-01-02 already exists."
)
def test_delete_entry_success(self, temp_csv_file, mock_logger, sample_data):
def test_delete_entry_success(self, temp_csv_file, mock_logger, mock_medicine_manager, sample_data):
"""Test successfully deleting an entry."""
# Add initial data
with open(temp_csv_file, 'w', newline='') as f:
@@ -249,7 +247,7 @@ class TestDataManager:
])
writer.writerows(sample_data)
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
result = dm.delete_entry("2024-01-02")
assert result is True
@@ -259,7 +257,7 @@ class TestDataManager:
assert len(df) == 2
assert not any(df["date"] == "2024-01-02")
def test_delete_entry_nonexistent(self, temp_csv_file, mock_logger, sample_data):
def test_delete_entry_nonexistent(self, temp_csv_file, mock_logger, mock_medicine_manager, sample_data):
"""Test deleting a nonexistent entry."""
# Add initial data
with open(temp_csv_file, 'w', newline='') as f:
@@ -272,7 +270,7 @@ class TestDataManager:
])
writer.writerows(sample_data)
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
result = dm.delete_entry("2024-01-10")
assert result is True # Should return True even if no matching entry
@@ -282,22 +280,22 @@ class TestDataManager:
assert len(df) == 3
@patch('pandas.read_csv')
def test_load_data_exception_handling(self, mock_read_csv, temp_csv_file, mock_logger):
def test_load_data_exception_handling(self, mock_read_csv, temp_csv_file, mock_logger, mock_medicine_manager):
"""Test exception handling in load_data."""
mock_read_csv.side_effect = Exception("Test error")
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
df = dm.load_data()
assert df.empty
mock_logger.error.assert_called_with("Error loading data: Test error")
@patch('builtins.open')
def test_add_entry_exception_handling(self, mock_open, temp_csv_file, mock_logger):
def test_add_entry_exception_handling(self, mock_open, temp_csv_file, mock_logger, mock_medicine_manager):
"""Test exception handling in add_entry."""
mock_open.side_effect = Exception("Test error")
dm = DataManager(temp_csv_file, mock_logger)
dm = DataManager(temp_csv_file, mock_logger, mock_medicine_manager)
entry = ["2024-01-01", 3, 2, 4, 3, 1, 0, 2, 1, "Test note"]
result = dm.add_entry(entry)