sentinel / tests /test_demo.py
jeuko's picture
Sync from GitHub (main)
cc034ee verified
"""Regression tests for CLI/reporting helper utilities."""
from pathlib import Path
import pytest
import yaml
from sentinel.models import (
CancerRiskAssessment,
ContributingFactor,
ContributionStrength,
DxRecommendation,
InitialAssessment,
RiskFactor,
RiskFactorCategory,
)
from sentinel.reporting import generate_excel_report, generate_pdf_report
from sentinel.user_input import (
AlcoholConsumption,
Anthropometrics,
CancerType,
Demographics,
Ethnicity,
FamilyMemberCancer,
FamilyRelation,
FamilySide,
Lifestyle,
PersonalMedicalHistory,
RelationshipDegree,
Sex,
SmokingHistory,
SmokingStatus,
UserInput,
)
from sentinel.utils import load_user_file
def test_load_user_file_yaml(tmp_path):
"""Ensure YAML user profiles load into a ``UserInput`` instance.
Args:
tmp_path: Pytest-managed temporary directory path.
"""
data = {
"demographics": {
"age_years": 30,
"sex": "male",
"anthropometrics": {"height_cm": 175, "weight_kg": 70},
},
"lifestyle": {
"smoking": {"status": "never"},
"alcohol_consumption": "none",
},
"personal_medical_history": {},
"family_history": [],
}
path = tmp_path / "user.yaml"
path.write_text(yaml.dump(data))
user = load_user_file(str(path))
assert isinstance(user, UserInput)
assert user.demographics.age_years == 30
assert user.lifestyle.smoking.status == SmokingStatus.NEVER
assert user.symptoms == []
@pytest.mark.parametrize("save_files", [True, False])
def test_generate_reports(tmp_path, save_files):
"""Generate PDF and Excel reports and assert the outputs exist.
Args:
tmp_path: Pytest-managed temporary directory path.
save_files: Whether to write outputs to repo path or temporary path.
"""
# 1. Create mock UserInput data with all fields
user = UserInput(
demographics=Demographics(
age_years=45,
sex=Sex.FEMALE,
ethnicity=Ethnicity.WHITE,
anthropometrics=Anthropometrics(height_cm=165, weight_kg=70),
),
lifestyle=Lifestyle(
smoking=SmokingHistory(
status=SmokingStatus.FORMER,
pack_years=10,
),
alcohol_consumption=AlcoholConsumption.LIGHT,
),
personal_medical_history=PersonalMedicalHistory(
previous_cancers=[CancerType.MELANOMA],
),
family_history=[
FamilyMemberCancer(
relation=FamilyRelation.MOTHER,
cancer_type=CancerType.BREAST,
age_at_diagnosis=50,
degree=RelationshipDegree.FIRST,
side=FamilySide.MATERNAL,
)
],
)
# 2. Create mock InitialAssessment data
assessment = InitialAssessment(
response="This is a summary response.",
thinking="The user is a 45-year-old female with a BRCA2 mutation...",
reasoning="1. Assessed breast cancer risk as high due to BRCA2...",
overall_summary="This assessment indicates a significant and immediate need...",
overall_risk_score=68,
identified_risk_factors=[
RiskFactor(
description="Positive for BRCA2 genetic mutation",
category=RiskFactorCategory.PERSONAL_MEDICAL,
),
RiskFactor(
description="Personal history of Skin Cancer",
category=RiskFactorCategory.PERSONAL_MEDICAL,
),
RiskFactor(
description="First-degree relative (Mother) with Breast Cancer",
category=RiskFactorCategory.FAMILY_HISTORY,
),
RiskFactor(
description="Age of 45", category=RiskFactorCategory.DEMOGRAPHICS
),
RiskFactor(
description="Former smoker (10 pack-years)",
category=RiskFactorCategory.LIFESTYLE,
),
],
llm_risk_interpretations=[
CancerRiskAssessment(
cancer_type="Breast Cancer",
risk_level=4,
explanation="Risk is high due to the combination of a known BRCA2 mutation and a first-degree relative with breast cancer.",
recommended_steps=[
"Annual mammogram",
"Annual breast MRI",
"Consultation with a high-risk breast specialist",
],
contributing_factors=[
ContributingFactor(
description="Positive for BRCA2 genetic mutation",
category=RiskFactorCategory.PERSONAL_MEDICAL,
strength=ContributionStrength.MAJOR,
),
ContributingFactor(
description="First-degree relative (Mother) with Breast Cancer",
category=RiskFactorCategory.FAMILY_HISTORY,
strength=ContributionStrength.MAJOR,
),
ContributingFactor(
description="Age of 45",
category=RiskFactorCategory.DEMOGRAPHICS,
strength=ContributionStrength.MINOR,
),
],
),
CancerRiskAssessment(
cancer_type="Ovarian Cancer",
risk_level=4,
explanation="Risk is significantly elevated due to the known BRCA2 mutation.",
recommended_steps=[
"Consider risk-reducing surgery",
"Transvaginal ultrasound and CA-125 blood test for surveillance",
],
contributing_factors=[
ContributingFactor(
description="Positive for BRCA2 genetic mutation",
category=RiskFactorCategory.PERSONAL_MEDICAL,
strength=ContributionStrength.MAJOR,
)
],
),
CancerRiskAssessment(
cancer_type="Skin Cancer",
risk_level=3,
explanation="Risk is moderate-to-high due to a personal history of skin cancer, which is a strong predictor of future risk.",
recommended_steps=[
"Annual full-body dermatological examination.",
"Vigilant use of broad-spectrum sunscreen.",
],
contributing_factors=[
ContributingFactor(
description="Personal history of Skin Cancer",
category=RiskFactorCategory.PERSONAL_MEDICAL,
strength=ContributionStrength.MAJOR,
)
],
),
CancerRiskAssessment(
cancer_type="Colorectal Cancer",
risk_level=3,
explanation="Risk is moderate as you have reached the standard screening age. Some studies suggest a minor increased risk with BRCA2 mutations.",
recommended_steps=[
"Begin regular colonoscopy screenings as per standard guidelines (age 45)."
],
contributing_factors=[
ContributingFactor(
description="Age of 45",
category=RiskFactorCategory.DEMOGRAPHICS,
strength=ContributionStrength.MODERATE,
),
ContributingFactor(
description="Positive for BRCA2 genetic mutation",
category=RiskFactorCategory.PERSONAL_MEDICAL,
strength=ContributionStrength.MINOR,
),
],
),
CancerRiskAssessment(
cancer_type="Lung Cancer",
risk_level=2,
explanation="A history of smoking, even as a former smoker, confers a residual risk for lung cancer, though it is not high enough to warrant screening at this time.",
recommended_steps=["Monitor for symptoms like persistent cough."],
lifestyle_advice="Continue to avoid smoking.",
contributing_factors=[
ContributingFactor(
description="Former smoker (10 pack-years)",
category=RiskFactorCategory.LIFESTYLE,
strength=ContributionStrength.MODERATE,
)
],
),
],
dx_recommendations=[
DxRecommendation(
test_name="Mammogram",
frequency="Annually",
rationale="High risk for breast cancer due to BRCA2 and family history.",
recommendation_level=5,
),
DxRecommendation(
test_name="MRI", # Breast MRI
frequency="Annually",
rationale="Supplemental screening for high-risk individuals with BRCA mutations.",
recommendation_level=5,
),
DxRecommendation(
test_name="Colonoscopy",
frequency="Every 5-10 years",
rationale="Standard screening age reached; commence screening.",
recommendation_level=4,
),
],
)
# 3. Define output path
if save_files:
output_path = Path("outputs")
output_path.mkdir(exist_ok=True)
else:
output_path = tmp_path
# 4. Define output filenames
pdf_filename = output_path / "report.pdf"
excel_filename = output_path / "report.xlsx"
# 5. Generate and check PDF report
try:
generate_pdf_report(assessment, user, str(pdf_filename))
assert pdf_filename.exists()
assert pdf_filename.stat().st_size > 0 # Check file is not empty
except Exception as e:
# The test environment might not have PDF generation dependencies
print(f"PDF generation failed, likely due to missing dependencies: {e}")
pytest.fail(f"PDF generation failed with an unexpected error: {e}")
# 6. Generate and check Excel report
generate_excel_report(assessment, user, str(excel_filename))
assert excel_filename.exists()
assert excel_filename.stat().st_size > 0