diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 08df2cd..68abd03 100755 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -55,15 +55,15 @@ repos: # Run the formatter. - id: ruff-format - - repo: local - hooks: - - id: ty-check - name: ty-check - language: python - entry: ty check - pass_filenames: false - args: [--python=.venv/] - additional_dependencies: [ty] + # - repo: local + # hooks: + # - id: ty-check + # name: ty-check + # language: python + # entry: ty check + # pass_filenames: false + # args: [--python=.venv/] + # additional_dependencies: [ty] - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.17.0 diff --git a/API_ARCHITECTURE.md b/API_ARCHITECTURE.md new file mode 100644 index 0000000..81a2791 --- /dev/null +++ b/API_ARCHITECTURE.md @@ -0,0 +1,524 @@ +# Vitals API Architecture - Lightweight Serverless Biomarker Processing + +## Overview + +A simple serverless API system designed for low-frequency biomarker processing (1-2 blood tests per user per year). The architecture prioritizes simplicity and cost-effectiveness over high-throughput optimization. + +## Key Design Considerations + +Given the low usage frequency: + +- **No caching needed** - With 1-2 requests per user per year, caching adds unnecessary complexity +- **Simple database queries** - No need for complex indexing or query optimization +- **Minimal infrastructure** - Single Cloud Run service with combined endpoints +- **On-demand processing** - Calculate algorithms in real-time rather than pre-computing +- **Pay-per-use model** - Serverless scales to zero between requests + +## Simplified Architecture + +### Single Service Design + +**One Cloud Run Service: `vitals-api`** + +Combines all functionality into a single lightweight service with three main endpoints: + +#### 1. Process & Store Endpoint + +**`POST /api/v1/biomarkers`** + +- Receives raw biomarker JSON from mobile app +- Performs unit conversions in-memory +- Calculates all applicable algorithms immediately +- Stores both raw data and results in one MongoDB document +- Returns all results in a single response + +#### 2. Retrieve Historical Data Endpoint + +**`GET /api/v1/biomarkers/{patient_id}`** + +- Simple query to fetch user's historical test results +- Returns maximum 10-20 documents (given 1-2 tests/year) +- No pagination needed due to low data volume + +#### 3. Health Check Endpoint + +**`GET /api/v1/health`** + +- Simple endpoint for monitoring service availability + +## Simplified Data Flow + +```mermaid +graph TD + A[Mobile App] -->|Blood Test Data| B[Single API Endpoint] + B -->|Process & Calculate| C[In-Memory Processing] + C -->|Store Everything| D[MongoDB Document] + D -->|Return All Results| A + + A -->|Get History| E[History Endpoint] + E -->|Simple Query| D + E -->|Return Tests| A +``` + +## MongoDB Schema - Single Collection + +### Collection: `blood_tests` + +```javascript +{ + _id: ObjectId, + patient_id: String, + test_date: Date, + created_at: Date, + + // Original data from mobile app + raw_data: { + metadata: { /* as received */ }, + raw_biomarkers: { /* as received */ }, + clinical_data: { /* as received */ } + }, + + // Processed biomarkers with unit conversions + processed_biomarkers: { + albumin_g_dl: { value: Number, unit: String }, + albumin_g_l: { value: Number, unit: String }, + // ... other biomarkers with conversions + }, + + // Algorithm results (only populated if requirements met) + results: { + phenoage: { + calculated: Boolean, + phenoage: Number, + mortality_score: Number, + missing_markers: [String] // If not calculated + }, + score2: { + calculated: Boolean, + risk_percentage: Number, + risk_category: String, + missing_data: [String] // If not calculated + }, + score2_diabetes: { + calculated: Boolean, + risk_percentage: Number, + risk_category: String, + missing_data: [String] // If not calculated + } + } +} +``` + +## Implementation Approach + +### Single Python Service + +```python +# main.py structure +from fastapi import FastAPI +from vitals.biomarkers.helpers import process_biomarkers +from vitals.models import phenoage, score2, score2_diabetes + +app = FastAPI() + +@app.post("/api/v1/biomarkers") +async def process_blood_test(data: dict): + # 1. Process biomarkers (unit conversions) + processed = process_biomarkers(data) + + # 2. Try each algorithm + results = { + "phenoage": try_phenoage(processed), + "score2": try_score2(processed), + "score2_diabetes": try_score2_diabetes(processed) + } + + # 3. Store everything in one document + doc = { + "patient_id": data["metadata"]["patient_id"], + "test_date": data["metadata"]["test_date"], + "raw_data": data, + "processed_biomarkers": processed, + "results": results + } + + # 4. Save to MongoDB and return + db.blood_tests.insert_one(doc) + return {"success": True, "results": results} +``` + +## Deployment Configuration + +### Minimal Cloud Run Setup + +```yaml +# Single service configuration +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + name: vitals-api +spec: + template: + metadata: + annotations: + run.googleapis.com/execution-environment: gen2 + spec: + containers: + - image: gcr.io/project-id/vitals-api:latest + resources: + limits: + cpu: "1" + memory: "512Mi" + env: + - name: MONGODB_URI + value: "mongodb+srv://..." + serviceAccountName: vitals-api-sa + traffic: + - percent: 100 + latestRevision: true +``` + +### Cost Optimization + +With 1-2 requests per user per year: + +- **Cloud Run**: ~$0 (scales to zero, minimal invocations) +- **MongoDB Atlas**: M0 (free tier) or M2 (~$9/month) sufficient for thousands of users +- **No API Gateway needed**: Direct Cloud Run URL with built-in authentication +- **No caching layer**: Unnecessary for this frequency +- **No message queues**: Synchronous processing is fine + +## Security - Simple Approach + +1. **API Key Authentication**: Simple header-based API key +2. **HTTPS Only**: Cloud Run provides this by default +3. **Basic Rate Limiting**: Cloud Run's built-in quotas sufficient +4. **Input Validation**: Pydantic models for request validation + +## Monitoring - Lightweight + +1. **Cloud Run Metrics**: Default metrics sufficient +2. **Error Logging**: Python logging to Cloud Logging +3. **Uptime Monitoring**: Simple health check every 5 minutes +4. **No complex dashboards needed** for this usage pattern + +## Development Phases - Simplified + +### Phase 1: MVP (1-2 weeks) + +- Single FastAPI service +- MongoDB connection +- All three algorithms integrated +- Basic error handling + +### Phase 2: Production Ready (1 week) + +- Add authentication +- Deploy to Cloud Run +- Connect MongoDB Atlas +- Basic monitoring + +### Phase 3: Mobile Integration (1 week) + +- API documentation +- Mobile SDK/examples +- End-to-end testing + +## Example API Usage + +### Submit Blood Test + +```bash +curl -X POST https://vitals-api-xxx.run.app/api/v1/biomarkers \ + -H "X-API-Key: your-api-key" \ + -H "Content-Type: application/json" \ + -d @blood_test.json +``` + +Response includes all calculated results immediately. + +### Get History + +```bash +curl https://vitals-api-xxx.run.app/api/v1/biomarkers/P001-2024-001 \ + -H "X-API-Key: your-api-key" +``` + +Returns array of 1-2 test results per year. + +## Why This Architecture? + +1. **Simplicity**: One service, one database collection, straightforward logic +2. **Cost-effective**: Minimal resources for low-frequency usage +3. **Fast implementation**: Can be built in 2-3 weeks +4. **Easy maintenance**: Less moving parts = less to break +5. **Appropriate scale**: Designed for actual usage pattern, not hypothetical scale + +## Next Steps + +1. Create simple FastAPI service +2. Set up MongoDB Atlas free tier +3. Deploy to Cloud Run +4. Test with mobile app +5. Monitor actual usage patterns and adjust if needed + +--- + +# Alternative Architecture: Stateless API with Local Phone Storage + +## Overview + +A privacy-first, stateless architecture where all personal health data remains on the user's device. The API serves purely as a computation service without storing any user data, eliminating GDPR compliance requirements and giving users complete ownership of their health information. + +## Why Consider This Approach? + +Given that users only perform 1-2 blood tests per year, storing data locally on the phone is highly practical: + +- **Complete Privacy**: Health data never leaves the user's control +- **Zero Compliance Burden**: No GDPR, HIPAA, or other regulatory requirements +- **No Storage Costs**: Eliminate database infrastructure entirely +- **User Data Ownership**: Users have full control over their health records +- **No Data Breach Risk**: Can't leak data you don't store + +## Stateless Architecture Design + +### Single Compute Endpoint + +**`POST /api/v1/analyze`** + +- **Purpose**: Process biomarkers and calculate all applicable algorithms +- **Input**: Raw biomarker data (no user identification needed) +- **Processing**: Unit conversions + algorithm calculations +- **Output**: Processed data + all algorithm results +- **Storage**: None - completely stateless + +### Stateless Data Flow + +```mermaid +graph TD + A[Mobile App] -->|Blood Test Data| B[Stateless API] + B -->|Process & Calculate| C[In-Memory Only] + C -->|Return Results| A + A -->|Store Locally| D[Phone Storage] + + E[User Views History] -->|Query| D + D -->|Display| E +``` + +## API Request/Response Format + +### Request Structure + +```json +{ + "biomarkers": { + "albumin": { "value": 4.05, "unit": "g/dL" }, + "creatinine": { "value": 1.17, "unit": "mg/dL" }, + "glucose": { "value": 70.5, "unit": "mg/dL" }, + // ... other biomarkers + }, + "clinical_data": { + "age": 45, + "sex": "female", + "smoking": false, + "systolic_bp": 120 + // ... other clinical data + } +} +``` + +### Response Structure + +```json +{ + "processed_biomarkers": { + "albumin_g_dl": { "value": 4.05, "unit": "g/dL" }, + "albumin_g_l": { "value": 40.5, "unit": "g/L" }, + // ... all conversions + }, + "algorithms": { + "phenoage": { + "available": true, + "results": { + "phenoage": 42.3, + "mortality_score": 0.023 + } + }, + "score2": { + "available": false, + "missing_requirements": ["hdl_cholesterol", "total_cholesterol"] + }, + "score2_diabetes": { + "available": false, + "missing_requirements": ["diabetes_status", "hba1c"] + } + }, + "processing_timestamp": "2024-01-15T10:30:00Z" +} +``` + +## Implementation - Pure Functions + +```python +from fastapi import FastAPI +from vitals.biomarkers.helpers import convert_units +from vitals.models import phenoage, score2, score2_diabetes + +app = FastAPI() + +@app.post("/api/v1/analyze") +async def analyze_biomarkers(data: dict): + # Pure function - no side effects, no storage + + # 1. Convert units + processed = convert_units(data["biomarkers"]) + + # 2. Check algorithm requirements and calculate + results = {} + + # Try PhenoAge + phenoage_markers = extract_phenoage_markers(processed, data["clinical_data"]) + if phenoage_markers: + results["phenoage"] = { + "available": True, + "results": phenoage.calculate(phenoage_markers) + } + else: + results["phenoage"] = { + "available": False, + "missing_requirements": get_missing_phenoage_markers(processed) + } + + # Similar for SCORE2 and SCORE2-Diabetes... + + return { + "processed_biomarkers": processed, + "algorithms": results, + "processing_timestamp": datetime.utcnow().isoformat() + } + +# No database connections, no user management, no sessions +``` + +## Mobile App Storage Strategy + +The mobile app handles all data persistence: + +```javascript +// Example mobile storage schema +{ + "blood_tests": [ + { + "id": "local-uuid", + "date": "2024-01-15", + "raw_input": { /* original data */ }, + "api_response": { /* full API response */ }, + "notes": "Annual checkup" + } + ], + "user_profile": { + "name": "John Doe", + "date_of_birth": "1980-01-01" + } +} +``` + +## Deployment - Even Simpler + +```yaml +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + name: vitals-compute-api +spec: + template: + spec: + containers: + - image: gcr.io/project-id/vitals-compute:latest + resources: + limits: + cpu: "1" + memory: "256Mi" # Even less memory needed + env: + - name: LOG_LEVEL + value: "info" + # No database connection strings needed! +``` + +## Architecture Comparison + +| Aspect | Server Storage (Option 1) | Local Storage (Option 2) | +|--------|--------------------------|-------------------------| +| **Monthly Cost** | $9-55 (DB + hosting) | ~$0 (hosting only) | +| **GDPR Compliance** | Required | Not applicable | +| **Data Breach Risk** | Possible | None | +| **Implementation Time** | 2-3 weeks | 3-5 days | +| **User Privacy** | Good | Excellent | +| **Offline Access** | No | Yes | +| **Cross-Device Sync** | Automatic | Manual (user exports) | +| **Historical Queries** | Server-side | Client-side only | + +## Trade-offs to Consider + +### Advantages of Local Storage + +- **Maximum privacy and user control** +- **Zero compliance overhead** +- **Minimal infrastructure costs** +- **Works offline** +- **No user accounts needed** + +### Disadvantages + +- **No automatic cross-device sync** (users must export/import) +- **Data loss if phone lost** (unless user backs up) +- **No server-side analytics** (aggregate health trends) +- **Mobile app must handle data management** + +## Security Considerations + +1. **API Security**: + - Rate limiting to prevent abuse + - Input validation only + - No authentication needed (no user data) + +2. **Mobile Security**: + - Encrypted local storage + - Biometric protection for app + - Optional cloud backup (user's choice) + +## Which Architecture to Choose? + +**Choose Server Storage (Option 1) if:** + +- You need cross-device synchronization +- You want to provide population health insights +- Users expect automatic backups +- You're willing to handle compliance + +**Choose Local Storage (Option 2) if:** + +- Privacy is the top priority +- You want zero compliance burden +- You prefer minimal infrastructure +- Users are comfortable managing their own data + +## Implementation Timeline - Stateless Option + +### Phase 1: API Development (2-3 days) + +- Create stateless FastAPI service +- Integrate algorithm calculations +- Deploy to Cloud Run + +### Phase 2: Mobile Integration (3-5 days) + +- Implement local storage +- Create data export/import +- Add backup reminders + +### Total: Less than 1 week to production + +## Conclusion + +For an app with 1-2 blood tests per user per year, the stateless architecture with local storage offers compelling advantages in terms of privacy, cost, and simplicity. The trade-off of manual data management is minimal given the low frequency of use, while the benefits of zero compliance burden and complete user data ownership are substantial. diff --git a/CHANGELOG.md b/CHANGELOG.md index c61d57c..9c948c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.2.0] - 2025-01-19 ### Added + - SCORE2-Diabetes cardiovascular risk algorithm implementation - Comprehensive risk calculation for diabetic patients - Support for multiple risk regions and calibration @@ -22,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Standardized biomarker schemas with Pydantic ### Changed + - Comprehensive type hints throughout the codebase - Enhanced type safety and IDE support - Better code documentation through types @@ -35,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Improved test naming conventions ### Fixed + - Type hint issues with proper TypedDict approach - Import consistency across the package - Test function naming conventions @@ -42,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - MyPy errors resolved ### Technical Improvements + - Added pre-commit hooks for code quality - Configured GitHub Actions for automated code review - Enhanced development workflow with Makefile @@ -50,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.1.0] - Initial Release ### Added + - PhenoAge algorithm implementation (Levine's method) - Basic biomarker processing functionality - Initial project structure and setup diff --git a/CLAUDE.md b/CLAUDE.md index f5cd1f6..ef8d0ea 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,10 +1,13 @@ # CLAUDE.md - Project Guidelines for Claude Code ## Project Overview + This repository contains biomarker algorithms for health assessment, including PhenoAge, SCORE2, and SCORE2-Diabetes cardiovascular risk calculations. ## Environment Setup + **IMPORTANT**: This project uses UV for dependency management. Before starting work: + ```bash # Sync dependencies and activate virtual environment uv sync @@ -14,32 +17,39 @@ source .venv/bin/activate ``` ## Python Style Guidelines + **CRITICAL**: Follow the principles from `/vitals/specs/coding_style.md`. DO NOT OVERENGINEER. Always find the right balance between clarity and complexity. ### Core Principles (from specs/coding_style.md) + 1. **Favor Simplicity Over Complexity** + - Always choose simple, straightforward solutions - Avoid over-engineering and elaborate abstractions - No premature optimization - If there are two ways to solve a problem, choose the easier to understand 2. **Clarity is Key** + - Readable code beats clever code - Use clear, descriptive names - Reduce cognitive load - Code should express intent clearly at a glance 3. **Write Pythonic Code** + - Follow Python community standards and idioms - Use list comprehensions, generators, context managers appropriately - Write code that looks like Python wrote it 4. **Don't Repeat Yourself (DRY)** + - Avoid code duplication - Use functions and modules for common logic - But don't abstract too early 5. **Focus on Readability First** + - PEP8 is a guide, not a law - Readability trumps mechanical adherence to style rules - Consider the human reader first @@ -49,7 +59,9 @@ source .venv/bin/activate - Use PEP8 as baseline but prioritize readability ### Type Hints Guidelines + **IMPORTANT**: Do not overengineer type hints. Find the right balance: + - Use type hints for function signatures and class attributes - Keep type hints simple and readable - Don't create complex type aliases unless they add clarity @@ -57,6 +69,7 @@ source .venv/bin/activate - If a type hint makes code harder to read, reconsider it ## Project Structure + ``` vitals/ ├── biomarkers/ # Common biomarker utilities @@ -78,11 +91,13 @@ vitals/ ## Development Workflow ### Before Starting Work + 1. Sync dependencies: `uv sync` 2. Activate virtual environment: `source .venv/bin/activate` (if not auto-activated) 3. Ensure git hooks are installed: `make install` (this also installs pre-commit hooks) ### Running Tests + ```bash # Run tests with coverage report make test @@ -92,7 +107,9 @@ make lint ``` ### Git Commit Process + **CRITICAL**: Before ANY commit: + 1. Ensure pre-commit hooks are active (installed via `make install`) 2. If pre-commit hooks are not running automatically: - STOP and inform that git hooks need to be activated @@ -104,7 +121,9 @@ make lint - Other configured checks ### Code Quality Checks + Before committing changes, ensure: + - [ ] Virtual environment is activated - [ ] Code follows the style guidelines in `/vitals/specs/coding_style.md` - [ ] Type hints are balanced (not overengineered) @@ -116,6 +135,7 @@ Before committing changes, ensure: - [ ] Pre-commit hooks pass ## Common Patterns + - Use Pydantic BaseModel for data validation - Extract biomarkers using `biomarkers.helpers.extract_biomarkers_from_json()` - Algorithm implementations go in `models/` directory @@ -124,7 +144,9 @@ Before committing changes, ensure: - Keep type hints simple and practical ## Testing Approach + When implementing new features: + 1. Check for existing test patterns in the codebase 2. Write tests that are simple and clear 3. Ensure edge cases are handled properly @@ -132,6 +154,7 @@ When implementing new features: 5. Run tests before committing: `make test` ## Important Notes + - The SCORE2 implementation uses Belgium (Low Risk region) calibration by default - The SCORE2-Diabetes implementation includes diabetes-specific risk adjustments - Binary values (sex, smoking, diabetes) should use boolean types in schemas diff --git a/README.md b/README.md old mode 100755 new mode 100644 index e1db376..0e9884e --- a/README.md +++ b/README.md @@ -48,15 +48,16 @@ from vitals.models.score2_diabetes import compute Biological age calculation using Levine's PhenoAge algorithm. This algorithm estimates biological aging based on 10 biomarkers and chronological age. **Required biomarkers:** + - Albumin (g/dL or g/L) -- Creatinine (mg/dL or ¼mol/L) +- Creatinine (mg/dL or umol/L) - Glucose (mg/dL or mmol/L) - C-reactive protein (mg/L or mg/dL) - Lymphocyte percentage (%) - Mean cell volume (fL) - Red cell distribution width (%) - Alkaline phosphatase (U/L) -- White blood cell count (10³/¼L or 10y/L) +- White blood cell count (10³/L or 10y/L) - Age (years) ```python @@ -87,6 +88,7 @@ print(f"Accelerated Aging: {result.accelerated_aging:.1f}") 10-year cardiovascular disease risk assessment for non-diabetic European patients aged 40-69 years. **Required parameters:** + - Age (40-69 years) - Sex (male/female) - Systolic blood pressure (mmHg) @@ -118,10 +120,11 @@ print(f"Risk Category: {result.risk_category}") CVD risk assessment for diabetic patients, including diabetes-specific risk factors. **Additional parameters for diabetic patients:** + - Diabetes status (boolean) - Age at diabetes diagnosis (years) - HbA1c (% or mmol/mol) -- Estimated glomerular filtration rate (mL/min/1.73m²) +- Estimated glomerular filtration rate (mL/min/1.73m²) ```python from vitals.models.score2_diabetes import compute @@ -179,16 +182,19 @@ result = compute(biomarkers) ## Algorithms Implemented ### PhenoAge (Levine et al., 2018) + Biological age estimation based on 10 clinical biomarkers. The algorithm was developed using NHANES data and validated across multiple cohorts. **Reference:** Levine, M.E. et al. An epigenetic biomarker of aging for lifespan and healthspan. Aging (2018). ### SCORE2 (European Society of Cardiology, 2021) + Updated cardiovascular risk prediction algorithm for European populations, calibrated for different risk regions. **Reference:** SCORE2 working group. SCORE2 risk prediction algorithms. European Heart Journal (2021). ### SCORE2-Diabetes (European Society of Cardiology, 2023) + Diabetes-specific cardiovascular risk assessment incorporating diabetes duration, glycemic control, and kidney function. **Reference:** SCORE2-Diabetes working group. European Heart Journal (2023). @@ -212,6 +218,7 @@ make lint ``` The project uses: + - **UV** for dependency management - **pytest** for testing with coverage reporting - **pre-commit** hooks for code quality diff --git a/app.py b/app.py new file mode 100755 index 0000000..b3bda55 --- /dev/null +++ b/app.py @@ -0,0 +1,202 @@ +from typing import Any + +from fastapi import FastAPI +from pydantic import BaseModel, Field + +from vitals.biomarkers import helpers +from vitals.biomarkers.helpers import RiskCategory +from vitals.models import phenoage, score2, score2_diabetes +from vitals.schemas.phenoage import Markers as PhenoAgeMarkers +from vitals.schemas.phenoage import Units as PhenoAgeUnits +from vitals.schemas.score2 import DiabetesMarkers +from vitals.schemas.score2 import Markers as Score2Markers +from vitals.schemas.score2 import Units as Score2Units + + +class PatientMetadata(BaseModel): + """Patient metadata information.""" + + patient_id: str + sex: str + timestamp: str + test_date: str + laboratory: str + + +class RawBiomarkerData(BaseModel): + """Raw biomarker data payload from mobile app.""" + + metadata: PatientMetadata + raw_biomarkers: dict[str, dict[str, Any]] + + +class PhenoAgeResult(BaseModel): + """PhenoAge calculation results.""" + + algorithm: str = "phenoage" + chronological_age: float + predicted_age: float + accelerated_aging: float + + +class Score2Result(BaseModel): + """SCORE2 calculation results.""" + + algorithm: str = "score2" + age: float + calibrated_risk_percent: float + risk_category: RiskCategory + + +class Score2DiabetesResult(BaseModel): + """SCORE2-Diabetes calculation results.""" + + algorithm: str = "scores2_diabetes" + age: float + calibrated_risk_percent: float + risk_category: RiskCategory + + +class BiomarkerResponse(BaseModel): + """Response containing calculated biomarker results.""" + + patient_id: str + results: dict[str, Any] = Field(default_factory=dict) + processed_algorithms: list[str] = Field(default_factory=list) + errors: list[str] = Field(default_factory=list) + + +class ErrorResponse(BaseModel): + """Error response for validation failures.""" + + error: str + detail: str + + +# Create FastAPI application +app = FastAPI( + title="Vitals Biomarker API", + description="API for processing biomarker data and calculating health scores (PhenoAge, SCORE2, SCORE2-Diabetes)", + version="1.0.0", +) + + +@app.post("/process_data", response_model=BiomarkerResponse) +async def process_data(data: RawBiomarkerData) -> BiomarkerResponse: + """ + Process biomarker data and calculate health scores. + + This endpoint accepts biomarker data from mobile applications and processes it + through available algorithms (PhenoAge, SCORE2, SCORE2-Diabetes) based on + the biomarkers present in the payload. + + Args: + data: Raw biomarker data with metadata and biomarker values + + Returns: + BiomarkerResponse with calculated results from applicable algorithms + """ + response = BiomarkerResponse( + patient_id=data.metadata.patient_id, + results={}, + processed_algorithms=[], + errors=[], + ) + + # Add converted biomarkers (e.g., mg/dL to mmol/L conversions) + converted_biomarkers = helpers.add_converted_biomarkers(data.raw_biomarkers) + + # ---- PHENOAGE + phenoage_markers = helpers.validate_biomarkers_for_algorithm( + raw_biomarkers=converted_biomarkers, + biomarker_class=PhenoAgeMarkers, + biomarker_units=PhenoAgeUnits(), + ) + if phenoage_markers is not None: + chrono_age, pred_age, accl_age = phenoage.compute(phenoage_markers) + phenoage_data: PhenoAgeResult = PhenoAgeResult( + chronological_age=chrono_age, + predicted_age=pred_age, + accelerated_aging=accl_age, + ) + response.results["phenoage"] = phenoage_data.model_dump() + response.processed_algorithms.append(phenoage_data.algorithm) + else: + response.results["phenoage"] = None + response.errors.append("PhenoAge not computer: Missing required biomarkers") + + # ---- SCORE2 (all variants) + score2_markers = helpers.validate_biomarkers_for_algorithm( + raw_biomarkers=converted_biomarkers, + biomarker_class=Score2Markers, + biomarker_units=Score2Units(), + ) + + score2_data: Score2DiabetesResult | Score2Result | None = None + if score2_markers is not None: + age: float = score2_markers.age + scores2_with_diabetes_markers: DiabetesMarkers | None = ( + DiabetesMarkers.try_from_markers(score2_markers) + ) + + if age >= 70 and scores2_with_diabetes_markers is not None: + # Future implementation for older people + response.errors.append( + "SCORE2 for older people (age ≥ 70) not yet implemented" + ) + elif 40 <= age <= 69: + if scores2_with_diabetes_markers is not None: + age, calibrated_risk, risk_category = score2_diabetes.compute( + scores2_with_diabetes_markers + ) + score2_data = Score2DiabetesResult( + age=age, # Note: First value is age, not risk_score + calibrated_risk_percent=calibrated_risk, + risk_category=risk_category, + ) + else: + # Use standard SCORE2 algorithm + age, calibrated_risk, risk_category = score2.compute(score2_markers) + score2_data = Score2Result( + age=age, # Note: First value is age, not risk_score + calibrated_risk_percent=calibrated_risk, + risk_category=risk_category, + ) + + # Store result if calculation was successful + if score2_data: + response.results[score2_data.algorithm] = score2_data.model_dump() + response.processed_algorithms.append(score2_data.algorithm) + else: + response.results["score2"] = None + response.errors.append( + "SCORE2 not: Missing required biomarkers or Age requirements no" + ) + + return response + + +@app.get("/") +async def root(): + """Root endpoint with API information.""" + return { + "message": "Vitals Biomarker API", + "version": "1.0.0", + "endpoints": { + "/process_data": "POST - Process biomarker data", + "/docs": "GET - API documentation", + "/redoc": "GET - Alternative API documentation", + }, + } + + +@app.get("/health") +async def health_check(): + """Health check endpoint.""" + return {"status": "healthy"} + + +if __name__ == "__main__": + import uvicorn + + uvicorn.run(app, host="127.0.0.1", port=8000) diff --git a/main.py b/main.py deleted file mode 100755 index 9315ad2..0000000 --- a/main.py +++ /dev/null @@ -1,6 +0,0 @@ -def main(): - print("Hello from python-phenoage!") - - -if __name__ == "__main__": - main() diff --git a/pyproject.toml b/pyproject.toml index 37e8448..2701f30 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "python_phenoage" -version = "0.2.0" +version = "1.0.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.13" @@ -10,6 +10,8 @@ dependencies = [ "numpy", "requests>=2.32.4", "coverage>=7.9.1", + "fastapi>=0.104.1", + "uvicorn[standard]>=0.24.0", ] [dependency-groups] diff --git a/tests/inputs/invalid/test__phenoage_missing_albumin.json b/tests/inputs/invalid/test__phenoage_missing_albumin.json new file mode 100644 index 0000000..7fbe196 --- /dev/null +++ b/tests/inputs/invalid/test__phenoage_missing_albumin.json @@ -0,0 +1,41 @@ +{ + "metadata": { + "patient_id": "P999-2024-999", + "sex": "female", + "timestamp": "2024-06-15T08:30:00Z", + "test_date": "2024-06-15", + "laboratory": "Test Lab" + }, + "raw_biomarkers": { + "creatinine": { + "mg/dL": 1.17, + "umol/L": 103.428 + }, + "glucose": { + "mg/dL": 70.5, + "mmol/L": 3.9167 + }, + "crp": { + "mg/dL": 0.5, + "mg/L": 5.0 + }, + "lymphocyte_percent": { + "%": 40.3 + }, + "mean_cell_volume": { + "fL": 89.1 + }, + "red_cell_distribution_width": { + "%": 11.9 + }, + "alkaline_phosphatase": { + "U/L": 63.5 + }, + "white_blood_cell_count": { + "1000 cells/uL": 6.05 + }, + "age": { + "years": 39 + } + } +} diff --git a/tests/inputs/invalid/test__score2_missing_sbp.json b/tests/inputs/invalid/test__score2_missing_sbp.json new file mode 100644 index 0000000..75ead49 --- /dev/null +++ b/tests/inputs/invalid/test__score2_missing_sbp.json @@ -0,0 +1,26 @@ +{ + "metadata": { + "patient_id": "P998-2024-998", + "sex": "female", + "timestamp": "2024-07-04T13:45:00Z", + "test_date": "2024-07-04", + "laboratory": "Test Lab" + }, + "raw_biomarkers": { + "age": { + "years": 50 + }, + "smoking": { + "yes/no": true + }, + "total_cholesterol": { + "mmol/L": 6.3 + }, + "hdl_cholesterol": { + "mmol/L": 1.4 + }, + "is_male": { + "yes/no": false + } + } +} diff --git a/tests/inputs/phenoage/test__input__patient_01.json b/tests/inputs/phenoage/test__input__patient_01.json index 3ca6264..2a4a8a8 100755 --- a/tests/inputs/phenoage/test__input__patient_01.json +++ b/tests/inputs/phenoage/test__input__patient_01.json @@ -7,61 +7,39 @@ "laboratory": "Quest Diagnostics" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.05, - "unit": "g/dL" + "albumin": { + "g/dL": 4.05, + "g/L": 40.5 }, - "creatinine_mg_dl": { - "value": 1.17, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 1.17, + "umol/L": 103.428 }, - "glucose_mg_dl": { - "value": 70.5, - "unit": "mg/dL" + "glucose": { + "mg/dL": 70.5, + "mmol/L": 3.9167 }, - "crp_mg_dl": { - "value": 0.5, - "unit": "mg/dL" + "crp": { + "mg/dL": 0.5, + "mg/L": 5.0 }, - "lymphocyte_percent_%": { - "value": 40.3, - "unit": "%" + "lymphocyte_percent": { + "%": 40.3 }, - "mean_cell_volume_fl": { - "value": 89.1, - "unit": "fL" + "mean_cell_volume": { + "fL": 89.1 }, - "red_cell_distribution_width_%": { - "value": 11.9, - "unit": "%" + "red_cell_distribution_width": { + "%": 11.9 }, - "alkaline_phosphatase_u_l": { - "value": 63.5, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 63.5 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 6.05, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 6.05 }, - "age_years": { - "value": 39, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 3.9167, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 103.428, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 40.5, - "unit": "g/L" - }, - "crp_mg_l": { - "value": 5.0, - "unit": "mg/L" + "age": { + "years": 39 } } } diff --git a/tests/inputs/phenoage/test__input__patient_02.json b/tests/inputs/phenoage/test__input__patient_02.json index 2590587..8eec546 100755 --- a/tests/inputs/phenoage/test__input__patient_02.json +++ b/tests/inputs/phenoage/test__input__patient_02.json @@ -7,61 +7,39 @@ "laboratory": "LabCorp" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.0, - "unit": "g/dL" + "albumin": { + "g/dL": 4.0, + "g/L": 40.0 }, - "creatinine_mg_dl": { - "value": 0.584, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.584, + "umol/L": 51.6256 }, - "glucose_mg_dl": { - "value": 109.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 109.0, + "mmol/L": 6.0556 }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "crp": { + "mg/dL": 0.21, + "mg/L": 2.1 }, - "lymphocyte_percent_%": { - "value": 32.35, - "unit": "%" + "lymphocyte_percent": { + "%": 32.35 }, - "mean_cell_volume_fl": { - "value": 92.4, - "unit": "fL" + "mean_cell_volume": { + "fL": 92.4 }, - "red_cell_distribution_width_%": { - "value": 12.05, - "unit": "%" + "red_cell_distribution_width": { + "%": 12.05 }, - "alkaline_phosphatase_u_l": { - "value": 59, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 59 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 4.95, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 4.95 }, - "age_years": { - "value": 40, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 6.0556, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 51.6256, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 40.0, - "unit": "g/L" - }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "age": { + "years": 40 } } } diff --git a/tests/inputs/phenoage/test__input__patient_03.json b/tests/inputs/phenoage/test__input__patient_03.json index ec7d846..f0d6ce4 100755 --- a/tests/inputs/phenoage/test__input__patient_03.json +++ b/tests/inputs/phenoage/test__input__patient_03.json @@ -7,61 +7,39 @@ "laboratory": "Mayo Clinic Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.1, - "unit": "g/dL" + "albumin": { + "g/dL": 4.1, + "g/L": 41.0 }, - "creatinine_mg_dl": { - "value": 0.584, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.584, + "umol/L": 51.6256 }, - "glucose_mg_dl": { - "value": 89.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 89.0, + "mmol/L": 4.9444 }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "crp": { + "mg/dL": 0.21, + "mg/L": 2.1 }, - "lymphocyte_percent_%": { - "value": 43.85, - "unit": "%" + "lymphocyte_percent": { + "%": 43.85 }, - "mean_cell_volume_fl": { - "value": 91.9, - "unit": "fL" + "mean_cell_volume": { + "fL": 91.9 }, - "red_cell_distribution_width_%": { - "value": 12.7, - "unit": "%" + "red_cell_distribution_width": { + "%": 12.7 }, - "alkaline_phosphatase_u_l": { - "value": 96, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 96 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 4.7, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 4.7 }, - "age_years": { - "value": 80, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.9444, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 51.6256, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 41.0, - "unit": "g/L" - }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "age": { + "years": 80 } } } diff --git a/tests/inputs/phenoage/test__input__patient_04.json b/tests/inputs/phenoage/test__input__patient_04.json index 37af92e..3a47121 100755 --- a/tests/inputs/phenoage/test__input__patient_04.json +++ b/tests/inputs/phenoage/test__input__patient_04.json @@ -7,61 +7,39 @@ "laboratory": "Northwell Health Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.4, - "unit": "g/dL" + "albumin": { + "g/dL": 4.4, + "g/L": 44.0 }, - "creatinine_mg_dl": { - "value": 0.776, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.776, + "umol/L": 68.5984 }, - "glucose_mg_dl": { - "value": 89.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 89.0, + "mmol/L": 4.9444 }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "crp": { + "mg/dL": 0.21, + "mg/L": 2.1 }, - "lymphocyte_percent_%": { - "value": 29.0, - "unit": "%" + "lymphocyte_percent": { + "%": 29.0 }, - "mean_cell_volume_fl": { - "value": 78.4, - "unit": "fL" + "mean_cell_volume": { + "fL": 78.4 }, - "red_cell_distribution_width_%": { - "value": 12.05, - "unit": "%" + "red_cell_distribution_width": { + "%": 12.05 }, - "alkaline_phosphatase_u_l": { - "value": 35, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 35 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 5.55, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 5.55 }, - "age_years": { - "value": 36, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.9444, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 68.5984, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 44.0, - "unit": "g/L" - }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "age": { + "years": 36 } } } diff --git a/tests/inputs/phenoage/test__input__patient_05.json b/tests/inputs/phenoage/test__input__patient_05.json index bde1c9c..5a22171 100755 --- a/tests/inputs/phenoage/test__input__patient_05.json +++ b/tests/inputs/phenoage/test__input__patient_05.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.5, - "unit": "g/dL" + "albumin": { + "g/dL": 4.5, + "g/L": 45.0 }, - "creatinine_mg_dl": { - "value": 0.968, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.968, + "umol/L": 85.5712 }, - "glucose_mg_dl": { - "value": 85.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 85.0, + "mmol/L": 4.7222 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 27.2, - "unit": "%" + "lymphocyte_percent": { + "%": 27.2 }, - "mean_cell_volume_fl": { - "value": 90.4, - "unit": "fL" + "mean_cell_volume": { + "fL": 90.4 }, - "red_cell_distribution_width_%": { - "value": 13.0, - "unit": "%" + "red_cell_distribution_width": { + "%": 13.0 }, - "alkaline_phosphatase_u_l": { - "value": 74.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 74.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 5.9, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 5.9 }, - "age_years": { - "value": 35, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.7222, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 85.5712, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 45.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 35 } } } diff --git a/tests/inputs/phenoage/test__input__patient_06.json b/tests/inputs/phenoage/test__input__patient_06.json index 8cfe820..27f0ab2 100755 --- a/tests/inputs/phenoage/test__input__patient_06.json +++ b/tests/inputs/phenoage/test__input__patient_06.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 3.9, - "unit": "g/dL" + "albumin": { + "g/dL": 3.9, + "g/L": 39.0 }, - "creatinine_mg_dl": { - "value": 0.584, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.584, + "umol/L": 51.6256 }, - "glucose_mg_dl": { - "value": 88.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 88.0, + "mmol/L": 4.8889 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 43.7, - "unit": "%" + "lymphocyte_percent": { + "%": 43.7 }, - "mean_cell_volume_fl": { - "value": 66.3, - "unit": "fL" + "mean_cell_volume": { + "fL": 66.3 }, - "red_cell_distribution_width_%": { - "value": 18.1, - "unit": "%" + "red_cell_distribution_width": { + "%": 18.1 }, - "alkaline_phosphatase_u_l": { - "value": 84.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 84.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 5.05, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 5.05 }, - "age_years": { - "value": 42, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.8889, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 51.6256, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 39.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 42 } } } diff --git a/tests/inputs/phenoage/test__input__patient_07.json b/tests/inputs/phenoage/test__input__patient_07.json index dc8cd07..c434d05 100755 --- a/tests/inputs/phenoage/test__input__patient_07.json +++ b/tests/inputs/phenoage/test__input__patient_07.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.4, - "unit": "g/dL" + "albumin": { + "g/dL": 4.4, + "g/L": 44.0 }, - "creatinine_mg_dl": { - "value": 0.776, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.776, + "umol/L": 68.5984 }, - "glucose_mg_dl": { - "value": 89.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 89.0, + "mmol/L": 4.9444 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 29.0, - "unit": "%" + "lymphocyte_percent": { + "%": 29.0 }, - "mean_cell_volume_fl": { - "value": 78.4, - "unit": "fL" + "mean_cell_volume": { + "fL": 78.4 }, - "red_cell_distribution_width_%": { - "value": 12.05, - "unit": "%" + "red_cell_distribution_width": { + "%": 12.05 }, - "alkaline_phosphatase_u_l": { - "value": 35.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 35.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 5.55, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 5.55 }, - "age_years": { - "value": 36, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.9444, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 68.5984, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 44.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 36 } } } diff --git a/tests/inputs/phenoage/test__input__patient_08.json b/tests/inputs/phenoage/test__input__patient_08.json index da026d9..6b6071a 100755 --- a/tests/inputs/phenoage/test__input__patient_08.json +++ b/tests/inputs/phenoage/test__input__patient_08.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.4, - "unit": "g/dL" + "albumin": { + "g/dL": 4.4, + "g/L": 44.0 }, - "creatinine_mg_dl": { - "value": 0.776, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.776, + "umol/L": 68.5984 }, - "glucose_mg_dl": { - "value": 84.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 84.0, + "mmol/L": 4.6667 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 43.15, - "unit": "%" + "lymphocyte_percent": { + "%": 43.15 }, - "mean_cell_volume_fl": { - "value": 93.9, - "unit": "fL" + "mean_cell_volume": { + "fL": 93.9 }, - "red_cell_distribution_width_%": { - "value": 12.95, - "unit": "%" + "red_cell_distribution_width": { + "%": 12.95 }, - "alkaline_phosphatase_u_l": { - "value": 67.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 67.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 3.85, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 3.85 }, - "age_years": { - "value": 31, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.6667, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 68.5984, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 44.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 31 } } } diff --git a/tests/inputs/phenoage/test__input__patient_09.json b/tests/inputs/phenoage/test__input__patient_09.json index 5cb7b59..f43e95b 100755 --- a/tests/inputs/phenoage/test__input__patient_09.json +++ b/tests/inputs/phenoage/test__input__patient_09.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.3, - "unit": "g/dL" + "albumin": { + "g/dL": 4.3, + "g/L": 43.0 }, - "creatinine_mg_dl": { - "value": 0.872, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.872, + "umol/L": 77.0848 }, - "glucose_mg_dl": { - "value": 89.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 89.0, + "mmol/L": 4.9444 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 26.9, - "unit": "%" + "lymphocyte_percent": { + "%": 26.9 }, - "mean_cell_volume_fl": { - "value": 87.95, - "unit": "fL" + "mean_cell_volume": { + "fL": 87.95 }, - "red_cell_distribution_width_%": { - "value": 13.15, - "unit": "%" + "red_cell_distribution_width": { + "%": 13.15 }, - "alkaline_phosphatase_u_l": { - "value": 90.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 90.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 8.15, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 8.15 }, - "age_years": { - "value": 32, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.9444, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 77.0848, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 43.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 32 } } } diff --git a/tests/inputs/phenoage/test__input__patient_10.json b/tests/inputs/phenoage/test__input__patient_10.json index 2088750..b7d5fc0 100755 --- a/tests/inputs/phenoage/test__input__patient_10.json +++ b/tests/inputs/phenoage/test__input__patient_10.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.5, - "unit": "g/dL" + "albumin": { + "g/dL": 4.5, + "g/L": 45.0 }, - "creatinine_mg_dl": { - "value": 0.584, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.584, + "umol/L": 51.6256 }, - "glucose_mg_dl": { - "value": 76.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 76.0, + "mmol/L": 4.2222 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 36.5, - "unit": "%" + "lymphocyte_percent": { + "%": 36.5 }, - "mean_cell_volume_fl": { - "value": 92.05, - "unit": "fL" + "mean_cell_volume": { + "fL": 92.05 }, - "red_cell_distribution_width_%": { - "value": 13.3, - "unit": "%" + "red_cell_distribution_width": { + "%": 13.3 }, - "alkaline_phosphatase_u_l": { - "value": 36.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 36.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 5.9, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 5.9 }, - "age_years": { - "value": 31, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.2222, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 51.6256, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 45.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 31 } } } diff --git a/tests/inputs/phenoage/test__input__patient_11.json b/tests/inputs/phenoage/test__input__patient_11.json index b4da578..a3fa71c 100755 --- a/tests/inputs/phenoage/test__input__patient_11.json +++ b/tests/inputs/phenoage/test__input__patient_11.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.8, - "unit": "g/dL" + "albumin": { + "g/dL": 4.8, + "g/L": 48.0 }, - "creatinine_mg_dl": { - "value": 1.064, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 1.064, + "umol/L": 94.0576 }, - "glucose_mg_dl": { - "value": 77.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 77.0, + "mmol/L": 4.2778 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 34.2, - "unit": "%" + "lymphocyte_percent": { + "%": 34.2 }, - "mean_cell_volume_fl": { - "value": 96.6, - "unit": "fL" + "mean_cell_volume": { + "fL": 96.6 }, - "red_cell_distribution_width_%": { - "value": 12.2, - "unit": "%" + "red_cell_distribution_width": { + "%": 12.2 }, - "alkaline_phosphatase_u_l": { - "value": 69.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 69.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 5.05, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 5.05 }, - "age_years": { - "value": 32, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.2778, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 94.0576, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 48.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 32 } } } diff --git a/tests/inputs/phenoage/test__input__patient_12.json b/tests/inputs/phenoage/test__input__patient_12.json index 0d44ef6..ef1d3e3 100755 --- a/tests/inputs/phenoage/test__input__patient_12.json +++ b/tests/inputs/phenoage/test__input__patient_12.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.3, - "unit": "g/dL" + "albumin": { + "g/dL": 4.3, + "g/L": 43.0 }, - "creatinine_mg_dl": { - "value": 0.776, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.776, + "umol/L": 68.5984 }, - "glucose_mg_dl": { - "value": 79.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 79.0, + "mmol/L": 4.3889 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 40.75, - "unit": "%" + "lymphocyte_percent": { + "%": 40.75 }, - "mean_cell_volume_fl": { - "value": 90.95, - "unit": "fL" + "mean_cell_volume": { + "fL": 90.95 }, - "red_cell_distribution_width_%": { - "value": 13.15, - "unit": "%" + "red_cell_distribution_width": { + "%": 13.15 }, - "alkaline_phosphatase_u_l": { - "value": 39.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 39.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 4.7, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 4.7 }, - "age_years": { - "value": 33, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.3889, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 68.5984, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 43.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 33 } } } diff --git a/tests/inputs/phenoage/test__input__patient_13.json b/tests/inputs/phenoage/test__input__patient_13.json index 3029523..25da6d8 100755 --- a/tests/inputs/phenoage/test__input__patient_13.json +++ b/tests/inputs/phenoage/test__input__patient_13.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.7, - "unit": "g/dL" + "albumin": { + "g/dL": 4.7, + "g/L": 47.0 }, - "creatinine_mg_dl": { - "value": 0.776, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.776, + "umol/L": 68.5984 }, - "glucose_mg_dl": { - "value": 84.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 84.0, + "mmol/L": 4.6667 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 23.7, - "unit": "%" + "lymphocyte_percent": { + "%": 23.7 }, - "mean_cell_volume_fl": { - "value": 89.35, - "unit": "fL" + "mean_cell_volume": { + "fL": 89.35 }, - "red_cell_distribution_width_%": { - "value": 11.95, - "unit": "%" + "red_cell_distribution_width": { + "%": 11.95 }, - "alkaline_phosphatase_u_l": { - "value": 66.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 66.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 10.0, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 10.0 }, - "age_years": { - "value": 35, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.6667, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 68.5984, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 47.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 35 } } } diff --git a/tests/inputs/phenoage/test__input__patient_14.json b/tests/inputs/phenoage/test__input__patient_14.json index bfbf334..fce4429 100755 --- a/tests/inputs/phenoage/test__input__patient_14.json +++ b/tests/inputs/phenoage/test__input__patient_14.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 3.9, - "unit": "g/dL" + "albumin": { + "g/dL": 3.9, + "g/L": 39.0 }, - "creatinine_mg_dl": { - "value": 0.776, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.776, + "umol/L": 68.5984 }, - "glucose_mg_dl": { - "value": 94.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 94.0, + "mmol/L": 5.2222 }, - "crp_mg_l": { - "value": 9.9, - "unit": "mg/L" + "crp": { + "mg/L": 9.9, + "mg/dL": 0.99 }, - "lymphocyte_percent_%": { - "value": 36.5, - "unit": "%" + "lymphocyte_percent": { + "%": 36.5 }, - "mean_cell_volume_fl": { - "value": 91.5, - "unit": "fL" + "mean_cell_volume": { + "fL": 91.5 }, - "red_cell_distribution_width_%": { - "value": 13.6, - "unit": "%" + "red_cell_distribution_width": { + "%": 13.6 }, - "alkaline_phosphatase_u_l": { - "value": 68.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 68.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 5.55, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 5.55 }, - "age_years": { - "value": 43, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 5.2222, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 68.5984, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 39.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.99, - "unit": "mg/dL" + "age": { + "years": 43 } } } diff --git a/tests/inputs/phenoage/test__input__patient_15.json b/tests/inputs/phenoage/test__input__patient_15.json index 0dd09f4..e0bb396 100755 --- a/tests/inputs/phenoage/test__input__patient_15.json +++ b/tests/inputs/phenoage/test__input__patient_15.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 3.9, - "unit": "g/dL" + "albumin": { + "g/dL": 3.9, + "g/L": 39.0 }, - "creatinine_mg_dl": { - "value": 0.776, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.776, + "umol/L": 68.5984 }, - "glucose_mg_dl": { - "value": 95.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 95.0, + "mmol/L": 5.2778 }, - "crp_mg_l": { - "value": 5.5, - "unit": "mg/L" + "crp": { + "mg/L": 5.5, + "mg/dL": 0.55 }, - "lymphocyte_percent_%": { - "value": 27.75, - "unit": "%" + "lymphocyte_percent": { + "%": 27.75 }, - "mean_cell_volume_fl": { - "value": 96.35, - "unit": "fL" + "mean_cell_volume": { + "fL": 96.35 }, - "red_cell_distribution_width_%": { - "value": 13.3, - "unit": "%" + "red_cell_distribution_width": { + "%": 13.3 }, - "alkaline_phosphatase_u_l": { - "value": 95.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 95.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 7.45, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 7.45 }, - "age_years": { - "value": 47, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 5.2778, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 68.5984, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 39.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.55, - "unit": "mg/dL" + "age": { + "years": 47 } } } diff --git a/tests/inputs/phenoage/test__input__patient_16.json b/tests/inputs/phenoage/test__input__patient_16.json index 5228ff4..a6df760 100755 --- a/tests/inputs/phenoage/test__input__patient_16.json +++ b/tests/inputs/phenoage/test__input__patient_16.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.1, - "unit": "g/dL" + "albumin": { + "g/dL": 4.1, + "g/L": 41.0 }, - "creatinine_mg_dl": { - "value": 0.68, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.68, + "umol/L": 60.112 }, - "glucose_mg_dl": { - "value": 88.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 88.0, + "mmol/L": 4.8889 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 31.5, - "unit": "%" + "lymphocyte_percent": { + "%": 31.5 }, - "mean_cell_volume_fl": { - "value": 89.75, - "unit": "fL" + "mean_cell_volume": { + "fL": 89.75 }, - "red_cell_distribution_width_%": { - "value": 12.85, - "unit": "%" + "red_cell_distribution_width": { + "%": 12.85 }, - "alkaline_phosphatase_u_l": { - "value": 73.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 73.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 6.85, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 6.85 }, - "age_years": { - "value": 30, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.8889, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 60.112, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 41.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 30 } } } diff --git a/tests/inputs/phenoage/test__input__patient_17.json b/tests/inputs/phenoage/test__input__patient_17.json index 1a5cd20..fc1e206 100755 --- a/tests/inputs/phenoage/test__input__patient_17.json +++ b/tests/inputs/phenoage/test__input__patient_17.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.5, - "unit": "g/dL" + "albumin": { + "g/dL": 4.5, + "g/L": 45.0 }, - "creatinine_mg_dl": { - "value": 0.968, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.968, + "umol/L": 85.5712 }, - "glucose_mg_dl": { - "value": 101.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 101.0, + "mmol/L": 5.6111 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 40.9, - "unit": "%" + "lymphocyte_percent": { + "%": 40.9 }, - "mean_cell_volume_fl": { - "value": 90.7, - "unit": "fL" + "mean_cell_volume": { + "fL": 90.7 }, - "red_cell_distribution_width_%": { - "value": 12.45, - "unit": "%" + "red_cell_distribution_width": { + "%": 12.45 }, - "alkaline_phosphatase_u_l": { - "value": 85.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 85.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 4.25, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 4.25 }, - "age_years": { - "value": 53, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 5.6111, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 85.5712, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 45.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 53 } } } diff --git a/tests/inputs/phenoage/test__input__patient_18.json b/tests/inputs/phenoage/test__input__patient_18.json index 9fd8ff5..d22c131 100755 --- a/tests/inputs/phenoage/test__input__patient_18.json +++ b/tests/inputs/phenoage/test__input__patient_18.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.6, - "unit": "g/dL" + "albumin": { + "g/dL": 4.6, + "g/L": 46.0 }, - "creatinine_mg_dl": { - "value": 0.584, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.584, + "umol/L": 51.6256 }, - "glucose_mg_dl": { - "value": 83.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 83.0, + "mmol/L": 4.6111 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 37.55, - "unit": "%" + "lymphocyte_percent": { + "%": 37.55 }, - "mean_cell_volume_fl": { - "value": 87.95, - "unit": "fL" + "mean_cell_volume": { + "fL": 87.95 }, - "red_cell_distribution_width_%": { - "value": 12.45, - "unit": "%" + "red_cell_distribution_width": { + "%": 12.45 }, - "alkaline_phosphatase_u_l": { - "value": 52.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 52.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 5.85, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 5.85 }, - "age_years": { - "value": 32, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.6111, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 51.6256, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 46.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 32 } } } diff --git a/tests/inputs/phenoage/test__input__patient_19.json b/tests/inputs/phenoage/test__input__patient_19.json index 9e6eb0e..06f1242 100755 --- a/tests/inputs/phenoage/test__input__patient_19.json +++ b/tests/inputs/phenoage/test__input__patient_19.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 3.9, - "unit": "g/dL" + "albumin": { + "g/dL": 3.9, + "g/L": 39.0 }, - "creatinine_mg_dl": { - "value": 0.872, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.872, + "umol/L": 77.0848 }, - "glucose_mg_dl": { - "value": 105.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 105.0, + "mmol/L": 5.8333 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 38.65, - "unit": "%" + "lymphocyte_percent": { + "%": 38.65 }, - "mean_cell_volume_fl": { - "value": 84.4, - "unit": "fL" + "mean_cell_volume": { + "fL": 84.4 }, - "red_cell_distribution_width_%": { - "value": 15.35, - "unit": "%" + "red_cell_distribution_width": { + "%": 15.35 }, - "alkaline_phosphatase_u_l": { - "value": 59.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 59.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 5.05, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 5.05 }, - "age_years": { - "value": 70, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 5.8333, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 77.0848, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 39.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 70 } } } diff --git a/tests/inputs/phenoage/test__input__patient_20.json b/tests/inputs/phenoage/test__input__patient_20.json index 1df0bcb..1a0d84e 100755 --- a/tests/inputs/phenoage/test__input__patient_20.json +++ b/tests/inputs/phenoage/test__input__patient_20.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 5.0, - "unit": "g/dL" + "albumin": { + "g/dL": 5.0, + "g/L": 50.0 }, - "creatinine_mg_dl": { - "value": 0.68, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.68, + "umol/L": 60.112 }, - "glucose_mg_dl": { - "value": 71.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 71.0, + "mmol/L": 3.9444 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 29.25, - "unit": "%" + "lymphocyte_percent": { + "%": 29.25 }, - "mean_cell_volume_fl": { - "value": 88.75, - "unit": "fL" + "mean_cell_volume": { + "fL": 88.75 }, - "red_cell_distribution_width_%": { - "value": 12.7, - "unit": "%" + "red_cell_distribution_width": { + "%": 12.7 }, - "alkaline_phosphatase_u_l": { - "value": 91.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 91.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 4.45, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 4.45 }, - "age_years": { - "value": 32, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 3.9444, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 60.112, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 50.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 32 } } } diff --git a/tests/inputs/phenoage/test__input__patient_21.json b/tests/inputs/phenoage/test__input__patient_21.json index 8b4b15c..24f99f7 100755 --- a/tests/inputs/phenoage/test__input__patient_21.json +++ b/tests/inputs/phenoage/test__input__patient_21.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.5, - "unit": "g/dL" + "albumin": { + "g/dL": 4.5, + "g/L": 45.0 }, - "creatinine_mg_dl": { - "value": 0.872, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.872, + "umol/L": 77.0848 }, - "glucose_mg_dl": { - "value": 86.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 86.0, + "mmol/L": 4.7778 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 31.0, - "unit": "%" + "lymphocyte_percent": { + "%": 31.0 }, - "mean_cell_volume_fl": { - "value": 90.1, - "unit": "fL" + "mean_cell_volume": { + "fL": 90.1 }, - "red_cell_distribution_width_%": { - "value": 12.3, - "unit": "%" + "red_cell_distribution_width": { + "%": 12.3 }, - "alkaline_phosphatase_u_l": { - "value": 55.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 55.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 6.55, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 6.55 }, - "age_years": { - "value": 42, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 4.7778, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 77.0848, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 45.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 42 } } } diff --git a/tests/inputs/phenoage/test__input__patient_22.json b/tests/inputs/phenoage/test__input__patient_22.json index 9fd549f..63e773f 100755 --- a/tests/inputs/phenoage/test__input__patient_22.json +++ b/tests/inputs/phenoage/test__input__patient_22.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.3, - "unit": "g/dL" + "albumin": { + "g/dL": 4.3, + "g/L": 43.0 }, - "creatinine_mg_dl": { - "value": 1.16, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 1.16, + "umol/L": 102.544 }, - "glucose_mg_dl": { - "value": 106.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 106.0, + "mmol/L": 5.8889 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 35.45, - "unit": "%" + "lymphocyte_percent": { + "%": 35.45 }, - "mean_cell_volume_fl": { - "value": 85.15, - "unit": "fL" + "mean_cell_volume": { + "fL": 85.15 }, - "red_cell_distribution_width_%": { - "value": 14.15, - "unit": "%" + "red_cell_distribution_width": { + "%": 14.15 }, - "alkaline_phosphatase_u_l": { - "value": 86.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 86.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 10.55, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 10.55 }, - "age_years": { - "value": 39, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 5.8889, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 102.544, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 43.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 39 } } } diff --git a/tests/inputs/phenoage/test__input__patient_23.json b/tests/inputs/phenoage/test__input__patient_23.json index 9d36cdf..97d1a60 100755 --- a/tests/inputs/phenoage/test__input__patient_23.json +++ b/tests/inputs/phenoage/test__input__patient_23.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.1, - "unit": "g/dL" + "albumin": { + "g/dL": 4.1, + "g/L": 41.0 }, - "creatinine_mg_dl": { - "value": 0.68, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.68, + "umol/L": 60.112 }, - "glucose_mg_dl": { - "value": 100.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 100.0, + "mmol/L": 5.5556 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 52.5, - "unit": "%" + "lymphocyte_percent": { + "%": 52.5 }, - "mean_cell_volume_fl": { - "value": 93.1, - "unit": "fL" + "mean_cell_volume": { + "fL": 93.1 }, - "red_cell_distribution_width_%": { - "value": 13.65, - "unit": "%" + "red_cell_distribution_width": { + "%": 13.65 }, - "alkaline_phosphatase_u_l": { - "value": 49.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 49.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 3.2, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 3.2 }, - "age_years": { - "value": 62, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 5.5556, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 60.112, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 41.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 62 } } } diff --git a/tests/inputs/phenoage/test__input__patient_24.json b/tests/inputs/phenoage/test__input__patient_24.json index 41ba5bd..c069f99 100755 --- a/tests/inputs/phenoage/test__input__patient_24.json +++ b/tests/inputs/phenoage/test__input__patient_24.json @@ -7,61 +7,39 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "albumin_g_dl": { - "value": 4.4, - "unit": "g/dL" + "albumin": { + "g/dL": 4.4, + "g/L": 44.0 }, - "creatinine_mg_dl": { - "value": 0.584, - "unit": "mg/dL" + "creatinine": { + "mg/dL": 0.584, + "umol/L": 51.6256 }, - "glucose_mg_dl": { - "value": 90.0, - "unit": "mg/dL" + "glucose": { + "mg/dL": 90.0, + "mmol/L": 5.0 }, - "crp_mg_l": { - "value": 2.1, - "unit": "mg/L" + "crp": { + "mg/L": 2.1, + "mg/dL": 0.21 }, - "lymphocyte_percent_%": { - "value": 34.75, - "unit": "%" + "lymphocyte_percent": { + "%": 34.75 }, - "mean_cell_volume_fl": { - "value": 93.0, - "unit": "fL" + "mean_cell_volume": { + "fL": 93.0 }, - "red_cell_distribution_width_%": { - "value": 12.65, - "unit": "%" + "red_cell_distribution_width": { + "%": 12.65 }, - "alkaline_phosphatase_u_l": { - "value": 73.0, - "unit": "U/L" + "alkaline_phosphatase": { + "U/L": 73.0 }, - "white_blood_cell_count_1000_cells_ul": { - "value": 7.75, - "unit": "1000 cells/uL" + "white_blood_cell_count": { + "1000 cells/uL": 7.75 }, - "age_years": { - "value": 41, - "unit": "years" - }, - "glucose_mmol_l": { - "value": 5.0, - "unit": "mmol/L" - }, - "creatinine_umol_l": { - "value": 51.6256, - "unit": "umol/L" - }, - "albumin_g_l": { - "value": 44.0, - "unit": "g/L" - }, - "crp_mg_dl": { - "value": 0.21, - "unit": "mg/dL" + "age": { + "years": 41 } } } diff --git a/tests/inputs/score2/test__input__patient_25.json b/tests/inputs/score2/test__input__patient_25.json index c14c61d..b39c7e3 100755 --- a/tests/inputs/score2/test__input__patient_25.json +++ b/tests/inputs/score2/test__input__patient_25.json @@ -7,29 +7,23 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "age_years": { - "value": 50, - "unit": "years" + "age": { + "years": 50 }, - "smoking_yes_no": { - "value": true, - "unit": "yes/no" + "smoking": { + "yes/no": true }, - "systolic_blood_pressure_mmhg": { - "value": 140, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 140 }, - "total_cholesterol_mmol_l": { - "value": 6.3, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 6.3 }, - "hdl_cholesterol_mmol_l": { - "value": 1.4, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.4 }, - "is_male_yes_no": { - "value": false, - "unit": "yes/no" + "is_male": { + "yes/no": false } } } diff --git a/tests/inputs/score2/test__input__patient_26.json b/tests/inputs/score2/test__input__patient_26.json index 47ca4c5..30bfab5 100755 --- a/tests/inputs/score2/test__input__patient_26.json +++ b/tests/inputs/score2/test__input__patient_26.json @@ -7,29 +7,23 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "age_years": { - "value": 50, - "unit": "years" + "age": { + "years": 50 }, - "smoking_yes_no": { - "value": true, - "unit": "yes/no" + "smoking": { + "yes/no": true }, - "systolic_blood_pressure_mmhg": { - "value": 140, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 140 }, - "total_cholesterol_mmol_l": { - "value": 6.3, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 6.3 }, - "hdl_cholesterol_mmol_l": { - "value": 1.4, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.4 }, - "is_male_yes_no": { - "value": true, - "unit": "yes/no" + "is_male": { + "yes/no": true } } } diff --git a/tests/inputs/score2/test__input__patient_27.json b/tests/inputs/score2/test__input__patient_27.json index 23b76ea..fa77078 100644 --- a/tests/inputs/score2/test__input__patient_27.json +++ b/tests/inputs/score2/test__input__patient_27.json @@ -7,29 +7,23 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "age_years": { - "value": 55, - "unit": "years" + "age": { + "years": 55 }, - "smoking_yes_no": { - "value": false, - "unit": "yes/no" + "smoking": { + "yes/no": false }, - "systolic_blood_pressure_mmhg": { - "value": 125, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 125 }, - "total_cholesterol_mmol_l": { - "value": 5.2, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 5.2 }, - "hdl_cholesterol_mmol_l": { - "value": 1.6, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.6 }, - "is_male_yes_no": { - "value": false, - "unit": "yes/no" + "is_male": { + "yes/no": false } } } diff --git a/tests/inputs/score2/test__input__patient_28.json b/tests/inputs/score2/test__input__patient_28.json index ef74275..d9f659e 100644 --- a/tests/inputs/score2/test__input__patient_28.json +++ b/tests/inputs/score2/test__input__patient_28.json @@ -7,29 +7,23 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "age_years": { - "value": 45, - "unit": "years" + "age": { + "years": 45 }, - "smoking_yes_no": { - "value": false, - "unit": "yes/no" + "smoking": { + "yes/no": false }, - "systolic_blood_pressure_mmhg": { - "value": 130, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 130 }, - "total_cholesterol_mmol_l": { - "value": 5.8, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 5.8 }, - "hdl_cholesterol_mmol_l": { - "value": 1.3, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.3 }, - "is_male_yes_no": { - "value": true, - "unit": "yes/no" + "is_male": { + "yes/no": true } } } diff --git a/tests/inputs/score2/test__input__patient_29.json b/tests/inputs/score2/test__input__patient_29.json index 3b072bc..652a94d 100644 --- a/tests/inputs/score2/test__input__patient_29.json +++ b/tests/inputs/score2/test__input__patient_29.json @@ -7,29 +7,23 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "age_years": { - "value": 40, - "unit": "years" + "age": { + "years": 40 }, - "smoking_yes_no": { - "value": true, - "unit": "yes/no" + "smoking": { + "yes/no": true }, - "systolic_blood_pressure_mmhg": { - "value": 135, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 135 }, - "total_cholesterol_mmol_l": { - "value": 6.0, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 6.0 }, - "hdl_cholesterol_mmol_l": { - "value": 1.2, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.2 }, - "is_male_yes_no": { - "value": true, - "unit": "yes/no" + "is_male": { + "yes/no": true } } } diff --git a/tests/inputs/score2/test__input__patient_30.json b/tests/inputs/score2/test__input__patient_30.json index d0d551a..f07fe3e 100644 --- a/tests/inputs/score2/test__input__patient_30.json +++ b/tests/inputs/score2/test__input__patient_30.json @@ -7,29 +7,23 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "age_years": { - "value": 60, - "unit": "years" + "age": { + "years": 60 }, - "smoking_yes_no": { - "value": false, - "unit": "yes/no" + "smoking": { + "yes/no": false }, - "systolic_blood_pressure_mmhg": { - "value": 145, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 145 }, - "total_cholesterol_mmol_l": { - "value": 6.5, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 6.5 }, - "hdl_cholesterol_mmol_l": { - "value": 1.5, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.5 }, - "is_male_yes_no": { - "value": false, - "unit": "yes/no" + "is_male": { + "yes/no": false } } } diff --git a/tests/inputs/score2/test__input__patient_31.json b/tests/inputs/score2/test__input__patient_31.json index cce3e57..254968b 100644 --- a/tests/inputs/score2/test__input__patient_31.json +++ b/tests/inputs/score2/test__input__patient_31.json @@ -7,29 +7,23 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "age_years": { - "value": 65, - "unit": "years" + "age": { + "years": 65 }, - "smoking_yes_no": { - "value": true, - "unit": "yes/no" + "smoking": { + "yes/no": true }, - "systolic_blood_pressure_mmhg": { - "value": 150, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 150 }, - "total_cholesterol_mmol_l": { - "value": 7.0, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 7.0 }, - "hdl_cholesterol_mmol_l": { - "value": 1.1, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.1 }, - "is_male_yes_no": { - "value": true, - "unit": "yes/no" + "is_male": { + "yes/no": true } } } diff --git a/tests/inputs/score2/test__input__patient_32.json b/tests/inputs/score2/test__input__patient_32.json index 14ea302..605a60a 100644 --- a/tests/inputs/score2/test__input__patient_32.json +++ b/tests/inputs/score2/test__input__patient_32.json @@ -7,29 +7,23 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "age_years": { - "value": 69, - "unit": "years" + "age": { + "years": 69 }, - "smoking_yes_no": { - "value": false, - "unit": "yes/no" + "smoking": { + "yes/no": false }, - "systolic_blood_pressure_mmhg": { - "value": 155, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 155 }, - "total_cholesterol_mmol_l": { - "value": 7.2, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 7.2 }, - "hdl_cholesterol_mmol_l": { - "value": 1.3, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.3 }, - "is_male_yes_no": { - "value": false, - "unit": "yes/no" + "is_male": { + "yes/no": false } } } diff --git a/tests/inputs/score2/test__input__patient_33.json b/tests/inputs/score2/test__input__patient_33.json index f71c377..800df6f 100644 --- a/tests/inputs/score2/test__input__patient_33.json +++ b/tests/inputs/score2/test__input__patient_33.json @@ -7,29 +7,23 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "age_years": { - "value": 49, - "unit": "years" + "age": { + "years": 49 }, - "smoking_yes_no": { - "value": true, - "unit": "yes/no" + "smoking": { + "yes/no": true }, - "systolic_blood_pressure_mmhg": { - "value": 138, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 138 }, - "total_cholesterol_mmol_l": { - "value": 6.1, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 6.1 }, - "hdl_cholesterol_mmol_l": { - "value": 1.4, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.4 }, - "is_male_yes_no": { - "value": true, - "unit": "yes/no" + "is_male": { + "yes/no": true } } } diff --git a/tests/inputs/score2/test__input__patient_34.json b/tests/inputs/score2/test__input__patient_34.json index 41ebd11..125860f 100644 --- a/tests/inputs/score2/test__input__patient_34.json +++ b/tests/inputs/score2/test__input__patient_34.json @@ -7,29 +7,23 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "age_years": { - "value": 50, - "unit": "years" + "age": { + "years": 50 }, - "smoking_yes_no": { - "value": false, - "unit": "yes/no" + "smoking": { + "yes/no": false }, - "systolic_blood_pressure_mmhg": { - "value": 120, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 120 }, - "total_cholesterol_mmol_l": { - "value": 4.8, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 4.8 }, - "hdl_cholesterol_mmol_l": { - "value": 1.8, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.8 }, - "is_male_yes_no": { - "value": false, - "unit": "yes/no" + "is_male": { + "yes/no": false } } } diff --git a/tests/inputs/score2/test__input__patient_35.json b/tests/inputs/score2/test__input__patient_35.json index 6f111b0..d069a82 100644 --- a/tests/inputs/score2/test__input__patient_35.json +++ b/tests/inputs/score2/test__input__patient_35.json @@ -7,29 +7,23 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "age_years": { - "value": 55, - "unit": "years" + "age": { + "years": 55 }, - "smoking_yes_no": { - "value": true, - "unit": "yes/no" + "smoking": { + "yes/no": true }, - "systolic_blood_pressure_mmhg": { - "value": 142, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 142 }, - "total_cholesterol_mmol_l": { - "value": 6.4, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 6.4 }, - "hdl_cholesterol_mmol_l": { - "value": 1.2, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.2 }, - "is_male_yes_no": { - "value": true, - "unit": "yes/no" + "is_male": { + "yes/no": true } } } diff --git a/tests/inputs/score2/test__input__patient_36.json b/tests/inputs/score2/test__input__patient_36.json index 4d2e48e..29f35ed 100644 --- a/tests/inputs/score2/test__input__patient_36.json +++ b/tests/inputs/score2/test__input__patient_36.json @@ -7,29 +7,23 @@ "laboratory": "NHANES Reference Labs" }, "raw_biomarkers": { - "age_years": { - "value": 45, - "unit": "years" + "age": { + "years": 45 }, - "smoking_yes_no": { - "value": true, - "unit": "yes/no" + "smoking": { + "yes/no": true }, - "systolic_blood_pressure_mmhg": { - "value": 132, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 132 }, - "total_cholesterol_mmol_l": { - "value": 5.5, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 5.5 }, - "hdl_cholesterol_mmol_l": { - "value": 1.5, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.5 }, - "is_male_yes_no": { - "value": false, - "unit": "yes/no" + "is_male": { + "yes/no": false } } } diff --git a/tests/inputs/score2_diabetes/test__input__patient_01.json b/tests/inputs/score2_diabetes/test__input__patient_01.json index c5b14b6..d5f4557 100644 --- a/tests/inputs/score2_diabetes/test__input__patient_01.json +++ b/tests/inputs/score2_diabetes/test__input__patient_01.json @@ -7,45 +7,35 @@ "laboratory": "SCORE2-Diabetes Labs" }, "raw_biomarkers": { - "age_years": { - "value": 45, - "unit": "years" + "age": { + "years": 45 }, - "smoking_yes_no": { - "value": false, - "unit": "yes/no" + "smoking": { + "yes/no": false }, - "systolic_blood_pressure_mmhg": { - "value": 130, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 130 }, - "total_cholesterol_mmol_l": { - "value": 5.5, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 5.5 }, - "hdl_cholesterol_mmol_l": { - "value": 1.2, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.2 }, - "is_male_yes_no": { - "value": true, - "unit": "yes/no" + "is_male": { + "yes/no": true }, - "diabetes_yes_no": { - "value": true, - "unit": "yes/no" + "diabetes": { + "yes/no": true }, - "age_at_diabetes_diagnosis_years": { - "value": 40, - "unit": "years" + "age_at_diabetes_diagnosis": { + "years": 40 }, - "hba1c_mmol_mol": { - "value": 48, - "unit": "mmol/mol" + "hba1c": { + "mmol/mol": 48 }, - "egfr_ml_min_1_73m2": { - "value": 90, - "unit": "mL/min/1.73m\u00b2" + "egfr": { + "mL/min/1.73m\u00b2": 90 } } } diff --git a/tests/inputs/score2_diabetes/test__input__patient_02.json b/tests/inputs/score2_diabetes/test__input__patient_02.json index 73b780d..f6df5a6 100644 --- a/tests/inputs/score2_diabetes/test__input__patient_02.json +++ b/tests/inputs/score2_diabetes/test__input__patient_02.json @@ -7,45 +7,35 @@ "laboratory": "SCORE2-Diabetes Labs" }, "raw_biomarkers": { - "age_years": { - "value": 55, - "unit": "years" + "age": { + "years": 55 }, - "smoking_yes_no": { - "value": true, - "unit": "yes/no" + "smoking": { + "yes/no": true }, - "systolic_blood_pressure_mmhg": { - "value": 145, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 145 }, - "total_cholesterol_mmol_l": { - "value": 6.8, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 6.8 }, - "hdl_cholesterol_mmol_l": { - "value": 1.1, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.1 }, - "is_male_yes_no": { - "value": false, - "unit": "yes/no" + "is_male": { + "yes/no": false }, - "diabetes_yes_no": { - "value": true, - "unit": "yes/no" + "diabetes": { + "yes/no": true }, - "age_at_diabetes_diagnosis_years": { - "value": 50, - "unit": "years" + "age_at_diabetes_diagnosis": { + "years": 50 }, - "hba1c_mmol_mol": { - "value": 58, - "unit": "mmol/mol" + "hba1c": { + "mmol/mol": 58 }, - "egfr_ml_min_1_73m2": { - "value": 75, - "unit": "mL/min/1.73m\u00b2" + "egfr": { + "mL/min/1.73m\u00b2": 75 } } } diff --git a/tests/inputs/score2_diabetes/test__input__patient_03.json b/tests/inputs/score2_diabetes/test__input__patient_03.json index 44d6161..e56d30f 100644 --- a/tests/inputs/score2_diabetes/test__input__patient_03.json +++ b/tests/inputs/score2_diabetes/test__input__patient_03.json @@ -7,45 +7,35 @@ "laboratory": "SCORE2-Diabetes Labs" }, "raw_biomarkers": { - "age_years": { - "value": 65, - "unit": "years" + "age": { + "years": 65 }, - "smoking_yes_no": { - "value": false, - "unit": "yes/no" + "smoking": { + "yes/no": false }, - "systolic_blood_pressure_mmhg": { - "value": 155, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 155 }, - "total_cholesterol_mmol_l": { - "value": 7.2, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 7.2 }, - "hdl_cholesterol_mmol_l": { - "value": 0.9, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 0.9 }, - "is_male_yes_no": { - "value": true, - "unit": "yes/no" + "is_male": { + "yes/no": true }, - "diabetes_yes_no": { - "value": true, - "unit": "yes/no" + "diabetes": { + "yes/no": true }, - "age_at_diabetes_diagnosis_years": { - "value": 55, - "unit": "years" + "age_at_diabetes_diagnosis": { + "years": 55 }, - "hba1c_mmol_mol": { - "value": 68, - "unit": "mmol/mol" + "hba1c": { + "mmol/mol": 68 }, - "egfr_ml_min_1_73m2": { - "value": 60, - "unit": "mL/min/1.73m\u00b2" + "egfr": { + "mL/min/1.73m\u00b2": 60 } } } diff --git a/tests/inputs/score2_diabetes/test__input__patient_04.json b/tests/inputs/score2_diabetes/test__input__patient_04.json index a7d5e88..194e32f 100644 --- a/tests/inputs/score2_diabetes/test__input__patient_04.json +++ b/tests/inputs/score2_diabetes/test__input__patient_04.json @@ -7,45 +7,35 @@ "laboratory": "SCORE2-Diabetes Labs" }, "raw_biomarkers": { - "age_years": { - "value": 48, - "unit": "years" + "age": { + "years": 48 }, - "smoking_yes_no": { - "value": false, - "unit": "yes/no" + "smoking": { + "yes/no": false }, - "systolic_blood_pressure_mmhg": { - "value": 120, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 120 }, - "total_cholesterol_mmol_l": { - "value": 4.8, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 4.8 }, - "hdl_cholesterol_mmol_l": { - "value": 1.5, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.5 }, - "is_male_yes_no": { - "value": false, - "unit": "yes/no" + "is_male": { + "yes/no": false }, - "diabetes_yes_no": { - "value": true, - "unit": "yes/no" + "diabetes": { + "yes/no": true }, - "age_at_diabetes_diagnosis_years": { - "value": 45, - "unit": "years" + "age_at_diabetes_diagnosis": { + "years": 45 }, - "hba1c_mmol_mol": { - "value": 42, - "unit": "mmol/mol" + "hba1c": { + "mmol/mol": 42 }, - "egfr_ml_min_1_73m2": { - "value": 100, - "unit": "mL/min/1.73m\u00b2" + "egfr": { + "mL/min/1.73m\u00b2": 100 } } } diff --git a/tests/inputs/score2_diabetes/test__input__patient_05.json b/tests/inputs/score2_diabetes/test__input__patient_05.json index c33969d..3319553 100644 --- a/tests/inputs/score2_diabetes/test__input__patient_05.json +++ b/tests/inputs/score2_diabetes/test__input__patient_05.json @@ -7,45 +7,35 @@ "laboratory": "SCORE2-Diabetes Labs" }, "raw_biomarkers": { - "age_years": { - "value": 60, - "unit": "years" + "age": { + "years": 60 }, - "smoking_yes_no": { - "value": true, - "unit": "yes/no" + "smoking": { + "yes/no": true }, - "systolic_blood_pressure_mmhg": { - "value": 150, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 150 }, - "total_cholesterol_mmol_l": { - "value": 6.5, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 6.5 }, - "hdl_cholesterol_mmol_l": { - "value": 1.0, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.0 }, - "is_male_yes_no": { - "value": true, - "unit": "yes/no" + "is_male": { + "yes/no": true }, - "diabetes_yes_no": { - "value": true, - "unit": "yes/no" + "diabetes": { + "yes/no": true }, - "age_at_diabetes_diagnosis_years": { - "value": 48, - "unit": "years" + "age_at_diabetes_diagnosis": { + "years": 48 }, - "hba1c_mmol_mol": { - "value": 75, - "unit": "mmol/mol" + "hba1c": { + "mmol/mol": 75 }, - "egfr_ml_min_1_73m2": { - "value": 60, - "unit": "mL/min/1.73m\u00b2" + "egfr": { + "mL/min/1.73m\u00b2": 60 } } } diff --git a/tests/inputs/score2_diabetes/test__input__patient_06.json b/tests/inputs/score2_diabetes/test__input__patient_06.json index 07694a3..7cd0412 100644 --- a/tests/inputs/score2_diabetes/test__input__patient_06.json +++ b/tests/inputs/score2_diabetes/test__input__patient_06.json @@ -7,45 +7,35 @@ "laboratory": "SCORE2-Diabetes Labs" }, "raw_biomarkers": { - "age_years": { - "value": 42, - "unit": "years" + "age": { + "years": 42 }, - "smoking_yes_no": { - "value": true, - "unit": "yes/no" + "smoking": { + "yes/no": true }, - "systolic_blood_pressure_mmhg": { - "value": 135, - "unit": "mmHg" + "systolic_blood_pressure": { + "mmHg": 135 }, - "total_cholesterol_mmol_l": { - "value": 6.0, - "unit": "mmol/L" + "total_cholesterol": { + "mmol/L": 6.0 }, - "hdl_cholesterol_mmol_l": { - "value": 1.3, - "unit": "mmol/L" + "hdl_cholesterol": { + "mmol/L": 1.3 }, - "is_male_yes_no": { - "value": false, - "unit": "yes/no" + "is_male": { + "yes/no": false }, - "diabetes_yes_no": { - "value": true, - "unit": "yes/no" + "diabetes": { + "yes/no": true }, - "age_at_diabetes_diagnosis_years": { - "value": 38, - "unit": "years" + "age_at_diabetes_diagnosis": { + "years": 38 }, - "hba1c_mmol_mol": { - "value": 53, - "unit": "mmol/mol" + "hba1c": { + "mmol/mol": 53 }, - "egfr_ml_min_1_73m2": { - "value": 85, - "unit": "mL/min/1.73m\u00b2" + "egfr": { + "mL/min/1.73m\u00b2": 85 } } } diff --git a/tests/raw/phenoage/test__raw__patient_01.json b/tests/raw/phenoage/test__raw__patient_01.json index 5253431..d9d98cc 100755 --- a/tests/raw/phenoage/test__raw__patient_01.json +++ b/tests/raw/phenoage/test__raw__patient_01.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.05, - "unit": "g/dL" + "g/dL": 4.05 }, "creatinine": { - "value": 1.17, - "unit": "mg/dL" + "mg/dL": 1.17 }, "glucose": { - "value": 70.5, - "unit": "mg/dL" + "mg/dL": 70.5 }, "crp": { - "value": 0.5, - "unit": "mg/dL" + "mg/dL": 0.5 }, "lymphocyte_percent": { - "value": 40.3, - "unit": "%" + "%": 40.3 }, "mean_cell_volume": { - "value": 89.1, - "unit": "fL" + "fL": 89.1 }, "red_cell_distribution_width": { - "value": 11.9, - "unit": "%" + "%": 11.9 }, "alkaline_phosphatase": { - "value": 63.5, - "unit": "U/L" + "U/L": 63.5 }, "white_blood_cell_count": { - "value": 6.05, - "unit": "1000 cells/uL" + "1000 cells/uL": 6.05 }, "age": { - "value": 39, - "unit": "years" + "years": 39 } } } diff --git a/tests/raw/phenoage/test__raw__patient_02.json b/tests/raw/phenoage/test__raw__patient_02.json index 5e6a9b4..523a20a 100755 --- a/tests/raw/phenoage/test__raw__patient_02.json +++ b/tests/raw/phenoage/test__raw__patient_02.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.0, - "unit": "g/dL" + "g/dL": 4.0 }, "creatinine": { - "value": 0.584, - "unit": "mg/dL" + "mg/dL": 0.584 }, "glucose": { - "value": 109.0, - "unit": "mg/dL" + "mg/dL": 109.0 }, "crp": { - "value": 0.21, - "unit": "mg/dL" + "mg/dL": 0.21 }, "lymphocyte_percent": { - "value": 32.35, - "unit": "%" + "%": 32.35 }, "mean_cell_volume": { - "value": 92.4, - "unit": "fL" + "fL": 92.4 }, "red_cell_distribution_width": { - "value": 12.05, - "unit": "%" + "%": 12.05 }, "alkaline_phosphatase": { - "value": 59, - "unit": "U/L" + "U/L": 59 }, "white_blood_cell_count": { - "value": 4.95, - "unit": "1000 cells/uL" + "1000 cells/uL": 4.95 }, "age": { - "value": 40, - "unit": "years" + "years": 40 } } } diff --git a/tests/raw/phenoage/test__raw__patient_03.json b/tests/raw/phenoage/test__raw__patient_03.json index 27fe722..8d49e07 100755 --- a/tests/raw/phenoage/test__raw__patient_03.json +++ b/tests/raw/phenoage/test__raw__patient_03.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.1, - "unit": "g/dL" + "g/dL": 4.1 }, "creatinine": { - "value": 0.584, - "unit": "mg/dL" + "mg/dL": 0.584 }, "glucose": { - "value": 89.0, - "unit": "mg/dL" + "mg/dL": 89.0 }, "crp": { - "value": 0.21, - "unit": "mg/dL" + "mg/dL": 0.21 }, "lymphocyte_percent": { - "value": 43.85, - "unit": "%" + "%": 43.85 }, "mean_cell_volume": { - "value": 91.9, - "unit": "fL" + "fL": 91.9 }, "red_cell_distribution_width": { - "value": 12.7, - "unit": "%" + "%": 12.7 }, "alkaline_phosphatase": { - "value": 96, - "unit": "U/L" + "U/L": 96 }, "white_blood_cell_count": { - "value": 4.7, - "unit": "1000 cells/uL" + "1000 cells/uL": 4.7 }, "age": { - "value": 80, - "unit": "years" + "years": 80 } } } diff --git a/tests/raw/phenoage/test__raw__patient_04.json b/tests/raw/phenoage/test__raw__patient_04.json index dc27e44..c601506 100755 --- a/tests/raw/phenoage/test__raw__patient_04.json +++ b/tests/raw/phenoage/test__raw__patient_04.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.4, - "unit": "g/dL" + "g/dL": 4.4 }, "creatinine": { - "value": 0.776, - "unit": "mg/dL" + "mg/dL": 0.776 }, "glucose": { - "value": 89.0, - "unit": "mg/dL" + "mg/dL": 89.0 }, "crp": { - "value": 0.21, - "unit": "mg/dL" + "mg/dL": 0.21 }, "lymphocyte_percent": { - "value": 29.0, - "unit": "%" + "%": 29.0 }, "mean_cell_volume": { - "value": 78.4, - "unit": "fL" + "fL": 78.4 }, "red_cell_distribution_width": { - "value": 12.05, - "unit": "%" + "%": 12.05 }, "alkaline_phosphatase": { - "value": 35, - "unit": "U/L" + "U/L": 35 }, "white_blood_cell_count": { - "value": 5.55, - "unit": "1000 cells/uL" + "1000 cells/uL": 5.55 }, "age": { - "value": 36, - "unit": "years" + "years": 36 } } } diff --git a/tests/raw/phenoage/test__raw__patient_05.json b/tests/raw/phenoage/test__raw__patient_05.json index c5530ff..e224aed 100755 --- a/tests/raw/phenoage/test__raw__patient_05.json +++ b/tests/raw/phenoage/test__raw__patient_05.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.5, - "unit": "g/dL" + "g/dL": 4.5 }, "creatinine": { - "value": 0.968, - "unit": "mg/dL" + "mg/dL": 0.968 }, "glucose": { - "value": 85.0, - "unit": "mg/dL" + "mg/dL": 85.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 27.2, - "unit": "%" + "%": 27.2 }, "mean_cell_volume": { - "value": 90.4, - "unit": "fL" + "fL": 90.4 }, "red_cell_distribution_width": { - "value": 13.0, - "unit": "%" + "%": 13.0 }, "alkaline_phosphatase": { - "value": 74.0, - "unit": "U/L" + "U/L": 74.0 }, "white_blood_cell_count": { - "value": 5.9, - "unit": "1000 cells/uL" + "1000 cells/uL": 5.9 }, "age": { - "value": 35, - "unit": "years" + "years": 35 } } } diff --git a/tests/raw/phenoage/test__raw__patient_06.json b/tests/raw/phenoage/test__raw__patient_06.json index 128bd7c..2ea4fde 100755 --- a/tests/raw/phenoage/test__raw__patient_06.json +++ b/tests/raw/phenoage/test__raw__patient_06.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 3.9, - "unit": "g/dL" + "g/dL": 3.9 }, "creatinine": { - "value": 0.584, - "unit": "mg/dL" + "mg/dL": 0.584 }, "glucose": { - "value": 88.0, - "unit": "mg/dL" + "mg/dL": 88.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 43.7, - "unit": "%" + "%": 43.7 }, "mean_cell_volume": { - "value": 66.3, - "unit": "fL" + "fL": 66.3 }, "red_cell_distribution_width": { - "value": 18.1, - "unit": "%" + "%": 18.1 }, "alkaline_phosphatase": { - "value": 84.0, - "unit": "U/L" + "U/L": 84.0 }, "white_blood_cell_count": { - "value": 5.05, - "unit": "1000 cells/uL" + "1000 cells/uL": 5.05 }, "age": { - "value": 42, - "unit": "years" + "years": 42 } } } diff --git a/tests/raw/phenoage/test__raw__patient_07.json b/tests/raw/phenoage/test__raw__patient_07.json index 145101e..7cba991 100755 --- a/tests/raw/phenoage/test__raw__patient_07.json +++ b/tests/raw/phenoage/test__raw__patient_07.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.4, - "unit": "g/dL" + "g/dL": 4.4 }, "creatinine": { - "value": 0.776, - "unit": "mg/dL" + "mg/dL": 0.776 }, "glucose": { - "value": 89.0, - "unit": "mg/dL" + "mg/dL": 89.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 29.0, - "unit": "%" + "%": 29.0 }, "mean_cell_volume": { - "value": 78.4, - "unit": "fL" + "fL": 78.4 }, "red_cell_distribution_width": { - "value": 12.05, - "unit": "%" + "%": 12.05 }, "alkaline_phosphatase": { - "value": 35.0, - "unit": "U/L" + "U/L": 35.0 }, "white_blood_cell_count": { - "value": 5.55, - "unit": "1000 cells/uL" + "1000 cells/uL": 5.55 }, "age": { - "value": 36, - "unit": "years" + "years": 36 } } } diff --git a/tests/raw/phenoage/test__raw__patient_08.json b/tests/raw/phenoage/test__raw__patient_08.json index 5636060..cf75b76 100755 --- a/tests/raw/phenoage/test__raw__patient_08.json +++ b/tests/raw/phenoage/test__raw__patient_08.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.4, - "unit": "g/dL" + "g/dL": 4.4 }, "creatinine": { - "value": 0.776, - "unit": "mg/dL" + "mg/dL": 0.776 }, "glucose": { - "value": 84.0, - "unit": "mg/dL" + "mg/dL": 84.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 43.15, - "unit": "%" + "%": 43.15 }, "mean_cell_volume": { - "value": 93.9, - "unit": "fL" + "fL": 93.9 }, "red_cell_distribution_width": { - "value": 12.95, - "unit": "%" + "%": 12.95 }, "alkaline_phosphatase": { - "value": 67.0, - "unit": "U/L" + "U/L": 67.0 }, "white_blood_cell_count": { - "value": 3.85, - "unit": "1000 cells/uL" + "1000 cells/uL": 3.85 }, "age": { - "value": 31, - "unit": "years" + "years": 31 } } } diff --git a/tests/raw/phenoage/test__raw__patient_09.json b/tests/raw/phenoage/test__raw__patient_09.json index 805d1d7..9d2bdac 100755 --- a/tests/raw/phenoage/test__raw__patient_09.json +++ b/tests/raw/phenoage/test__raw__patient_09.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.3, - "unit": "g/dL" + "g/dL": 4.3 }, "creatinine": { - "value": 0.872, - "unit": "mg/dL" + "mg/dL": 0.872 }, "glucose": { - "value": 89.0, - "unit": "mg/dL" + "mg/dL": 89.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 26.9, - "unit": "%" + "%": 26.9 }, "mean_cell_volume": { - "value": 87.95, - "unit": "fL" + "fL": 87.95 }, "red_cell_distribution_width": { - "value": 13.15, - "unit": "%" + "%": 13.15 }, "alkaline_phosphatase": { - "value": 90.0, - "unit": "U/L" + "U/L": 90.0 }, "white_blood_cell_count": { - "value": 8.15, - "unit": "1000 cells/uL" + "1000 cells/uL": 8.15 }, "age": { - "value": 32, - "unit": "years" + "years": 32 } } } diff --git a/tests/raw/phenoage/test__raw__patient_10.json b/tests/raw/phenoage/test__raw__patient_10.json index d03f12f..069e941 100755 --- a/tests/raw/phenoage/test__raw__patient_10.json +++ b/tests/raw/phenoage/test__raw__patient_10.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.5, - "unit": "g/dL" + "g/dL": 4.5 }, "creatinine": { - "value": 0.584, - "unit": "mg/dL" + "mg/dL": 0.584 }, "glucose": { - "value": 76.0, - "unit": "mg/dL" + "mg/dL": 76.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 36.5, - "unit": "%" + "%": 36.5 }, "mean_cell_volume": { - "value": 92.05, - "unit": "fL" + "fL": 92.05 }, "red_cell_distribution_width": { - "value": 13.3, - "unit": "%" + "%": 13.3 }, "alkaline_phosphatase": { - "value": 36.0, - "unit": "U/L" + "U/L": 36.0 }, "white_blood_cell_count": { - "value": 5.9, - "unit": "1000 cells/uL" + "1000 cells/uL": 5.9 }, "age": { - "value": 31, - "unit": "years" + "years": 31 } } } diff --git a/tests/raw/phenoage/test__raw__patient_11.json b/tests/raw/phenoage/test__raw__patient_11.json index 401c427..2a0814d 100755 --- a/tests/raw/phenoage/test__raw__patient_11.json +++ b/tests/raw/phenoage/test__raw__patient_11.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.8, - "unit": "g/dL" + "g/dL": 4.8 }, "creatinine": { - "value": 1.064, - "unit": "mg/dL" + "mg/dL": 1.064 }, "glucose": { - "value": 77.0, - "unit": "mg/dL" + "mg/dL": 77.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 34.2, - "unit": "%" + "%": 34.2 }, "mean_cell_volume": { - "value": 96.6, - "unit": "fL" + "fL": 96.6 }, "red_cell_distribution_width": { - "value": 12.2, - "unit": "%" + "%": 12.2 }, "alkaline_phosphatase": { - "value": 69.0, - "unit": "U/L" + "U/L": 69.0 }, "white_blood_cell_count": { - "value": 5.05, - "unit": "1000 cells/uL" + "1000 cells/uL": 5.05 }, "age": { - "value": 32, - "unit": "years" + "years": 32 } } } diff --git a/tests/raw/phenoage/test__raw__patient_12.json b/tests/raw/phenoage/test__raw__patient_12.json index 5213c0f..c91519c 100755 --- a/tests/raw/phenoage/test__raw__patient_12.json +++ b/tests/raw/phenoage/test__raw__patient_12.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.3, - "unit": "g/dL" + "g/dL": 4.3 }, "creatinine": { - "value": 0.776, - "unit": "mg/dL" + "mg/dL": 0.776 }, "glucose": { - "value": 79.0, - "unit": "mg/dL" + "mg/dL": 79.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 40.75, - "unit": "%" + "%": 40.75 }, "mean_cell_volume": { - "value": 90.95, - "unit": "fL" + "fL": 90.95 }, "red_cell_distribution_width": { - "value": 13.15, - "unit": "%" + "%": 13.15 }, "alkaline_phosphatase": { - "value": 39.0, - "unit": "U/L" + "U/L": 39.0 }, "white_blood_cell_count": { - "value": 4.7, - "unit": "1000 cells/uL" + "1000 cells/uL": 4.7 }, "age": { - "value": 33, - "unit": "years" + "years": 33 } } } diff --git a/tests/raw/phenoage/test__raw__patient_13.json b/tests/raw/phenoage/test__raw__patient_13.json index 5b66b68..6717e7d 100755 --- a/tests/raw/phenoage/test__raw__patient_13.json +++ b/tests/raw/phenoage/test__raw__patient_13.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.7, - "unit": "g/dL" + "g/dL": 4.7 }, "creatinine": { - "value": 0.776, - "unit": "mg/dL" + "mg/dL": 0.776 }, "glucose": { - "value": 84.0, - "unit": "mg/dL" + "mg/dL": 84.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 23.7, - "unit": "%" + "%": 23.7 }, "mean_cell_volume": { - "value": 89.35, - "unit": "fL" + "fL": 89.35 }, "red_cell_distribution_width": { - "value": 11.95, - "unit": "%" + "%": 11.95 }, "alkaline_phosphatase": { - "value": 66.0, - "unit": "U/L" + "U/L": 66.0 }, "white_blood_cell_count": { - "value": 10.0, - "unit": "1000 cells/uL" + "1000 cells/uL": 10.0 }, "age": { - "value": 35, - "unit": "years" + "years": 35 } } } diff --git a/tests/raw/phenoage/test__raw__patient_14.json b/tests/raw/phenoage/test__raw__patient_14.json index e491d79..eff61d4 100755 --- a/tests/raw/phenoage/test__raw__patient_14.json +++ b/tests/raw/phenoage/test__raw__patient_14.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 3.9, - "unit": "g/dL" + "g/dL": 3.9 }, "creatinine": { - "value": 0.776, - "unit": "mg/dL" + "mg/dL": 0.776 }, "glucose": { - "value": 94.0, - "unit": "mg/dL" + "mg/dL": 94.0 }, "crp": { - "value": 9.9, - "unit": "mg/L" + "mg/L": 9.9 }, "lymphocyte_percent": { - "value": 36.5, - "unit": "%" + "%": 36.5 }, "mean_cell_volume": { - "value": 91.5, - "unit": "fL" + "fL": 91.5 }, "red_cell_distribution_width": { - "value": 13.6, - "unit": "%" + "%": 13.6 }, "alkaline_phosphatase": { - "value": 68.0, - "unit": "U/L" + "U/L": 68.0 }, "white_blood_cell_count": { - "value": 5.55, - "unit": "1000 cells/uL" + "1000 cells/uL": 5.55 }, "age": { - "value": 43, - "unit": "years" + "years": 43 } } } diff --git a/tests/raw/phenoage/test__raw__patient_15.json b/tests/raw/phenoage/test__raw__patient_15.json index 27e96ea..95b1b34 100755 --- a/tests/raw/phenoage/test__raw__patient_15.json +++ b/tests/raw/phenoage/test__raw__patient_15.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 3.9, - "unit": "g/dL" + "g/dL": 3.9 }, "creatinine": { - "value": 0.776, - "unit": "mg/dL" + "mg/dL": 0.776 }, "glucose": { - "value": 95.0, - "unit": "mg/dL" + "mg/dL": 95.0 }, "crp": { - "value": 5.5, - "unit": "mg/L" + "mg/L": 5.5 }, "lymphocyte_percent": { - "value": 27.75, - "unit": "%" + "%": 27.75 }, "mean_cell_volume": { - "value": 96.35, - "unit": "fL" + "fL": 96.35 }, "red_cell_distribution_width": { - "value": 13.3, - "unit": "%" + "%": 13.3 }, "alkaline_phosphatase": { - "value": 95.0, - "unit": "U/L" + "U/L": 95.0 }, "white_blood_cell_count": { - "value": 7.45, - "unit": "1000 cells/uL" + "1000 cells/uL": 7.45 }, "age": { - "value": 47, - "unit": "years" + "years": 47 } } } diff --git a/tests/raw/phenoage/test__raw__patient_16.json b/tests/raw/phenoage/test__raw__patient_16.json index 6b79cc7..ae0c355 100755 --- a/tests/raw/phenoage/test__raw__patient_16.json +++ b/tests/raw/phenoage/test__raw__patient_16.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.1, - "unit": "g/dL" + "g/dL": 4.1 }, "creatinine": { - "value": 0.68, - "unit": "mg/dL" + "mg/dL": 0.68 }, "glucose": { - "value": 88.0, - "unit": "mg/dL" + "mg/dL": 88.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 31.5, - "unit": "%" + "%": 31.5 }, "mean_cell_volume": { - "value": 89.75, - "unit": "fL" + "fL": 89.75 }, "red_cell_distribution_width": { - "value": 12.85, - "unit": "%" + "%": 12.85 }, "alkaline_phosphatase": { - "value": 73.0, - "unit": "U/L" + "U/L": 73.0 }, "white_blood_cell_count": { - "value": 6.85, - "unit": "1000 cells/uL" + "1000 cells/uL": 6.85 }, "age": { - "value": 30, - "unit": "years" + "years": 30 } } } diff --git a/tests/raw/phenoage/test__raw__patient_17.json b/tests/raw/phenoage/test__raw__patient_17.json index 6102f91..4465f1d 100755 --- a/tests/raw/phenoage/test__raw__patient_17.json +++ b/tests/raw/phenoage/test__raw__patient_17.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.5, - "unit": "g/dL" + "g/dL": 4.5 }, "creatinine": { - "value": 0.968, - "unit": "mg/dL" + "mg/dL": 0.968 }, "glucose": { - "value": 101.0, - "unit": "mg/dL" + "mg/dL": 101.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 40.9, - "unit": "%" + "%": 40.9 }, "mean_cell_volume": { - "value": 90.7, - "unit": "fL" + "fL": 90.7 }, "red_cell_distribution_width": { - "value": 12.45, - "unit": "%" + "%": 12.45 }, "alkaline_phosphatase": { - "value": 85.0, - "unit": "U/L" + "U/L": 85.0 }, "white_blood_cell_count": { - "value": 4.25, - "unit": "1000 cells/uL" + "1000 cells/uL": 4.25 }, "age": { - "value": 53, - "unit": "years" + "years": 53 } } } diff --git a/tests/raw/phenoage/test__raw__patient_18.json b/tests/raw/phenoage/test__raw__patient_18.json index be3035e..564f547 100755 --- a/tests/raw/phenoage/test__raw__patient_18.json +++ b/tests/raw/phenoage/test__raw__patient_18.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.6, - "unit": "g/dL" + "g/dL": 4.6 }, "creatinine": { - "value": 0.584, - "unit": "mg/dL" + "mg/dL": 0.584 }, "glucose": { - "value": 83.0, - "unit": "mg/dL" + "mg/dL": 83.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 37.55, - "unit": "%" + "%": 37.55 }, "mean_cell_volume": { - "value": 87.95, - "unit": "fL" + "fL": 87.95 }, "red_cell_distribution_width": { - "value": 12.45, - "unit": "%" + "%": 12.45 }, "alkaline_phosphatase": { - "value": 52.0, - "unit": "U/L" + "U/L": 52.0 }, "white_blood_cell_count": { - "value": 5.85, - "unit": "1000 cells/uL" + "1000 cells/uL": 5.85 }, "age": { - "value": 32, - "unit": "years" + "years": 32 } } } diff --git a/tests/raw/phenoage/test__raw__patient_19.json b/tests/raw/phenoage/test__raw__patient_19.json index 1f43989..bea01c9 100755 --- a/tests/raw/phenoage/test__raw__patient_19.json +++ b/tests/raw/phenoage/test__raw__patient_19.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 3.9, - "unit": "g/dL" + "g/dL": 3.9 }, "creatinine": { - "value": 0.872, - "unit": "mg/dL" + "mg/dL": 0.872 }, "glucose": { - "value": 105.0, - "unit": "mg/dL" + "mg/dL": 105.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 38.65, - "unit": "%" + "%": 38.65 }, "mean_cell_volume": { - "value": 84.4, - "unit": "fL" + "fL": 84.4 }, "red_cell_distribution_width": { - "value": 15.35, - "unit": "%" + "%": 15.35 }, "alkaline_phosphatase": { - "value": 59.0, - "unit": "U/L" + "U/L": 59.0 }, "white_blood_cell_count": { - "value": 5.05, - "unit": "1000 cells/uL" + "1000 cells/uL": 5.05 }, "age": { - "value": 70, - "unit": "years" + "years": 70 } } } diff --git a/tests/raw/phenoage/test__raw__patient_20.json b/tests/raw/phenoage/test__raw__patient_20.json index 2344aed..b44fcbd 100755 --- a/tests/raw/phenoage/test__raw__patient_20.json +++ b/tests/raw/phenoage/test__raw__patient_20.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 5.0, - "unit": "g/dL" + "g/dL": 5.0 }, "creatinine": { - "value": 0.68, - "unit": "mg/dL" + "mg/dL": 0.68 }, "glucose": { - "value": 71.0, - "unit": "mg/dL" + "mg/dL": 71.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 29.25, - "unit": "%" + "%": 29.25 }, "mean_cell_volume": { - "value": 88.75, - "unit": "fL" + "fL": 88.75 }, "red_cell_distribution_width": { - "value": 12.7, - "unit": "%" + "%": 12.7 }, "alkaline_phosphatase": { - "value": 91.0, - "unit": "U/L" + "U/L": 91.0 }, "white_blood_cell_count": { - "value": 4.45, - "unit": "1000 cells/uL" + "1000 cells/uL": 4.45 }, "age": { - "value": 32, - "unit": "years" + "years": 32 } } } diff --git a/tests/raw/phenoage/test__raw__patient_21.json b/tests/raw/phenoage/test__raw__patient_21.json index e9c149d..3ba57d9 100755 --- a/tests/raw/phenoage/test__raw__patient_21.json +++ b/tests/raw/phenoage/test__raw__patient_21.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.5, - "unit": "g/dL" + "g/dL": 4.5 }, "creatinine": { - "value": 0.872, - "unit": "mg/dL" + "mg/dL": 0.872 }, "glucose": { - "value": 86.0, - "unit": "mg/dL" + "mg/dL": 86.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 31.0, - "unit": "%" + "%": 31.0 }, "mean_cell_volume": { - "value": 90.1, - "unit": "fL" + "fL": 90.1 }, "red_cell_distribution_width": { - "value": 12.3, - "unit": "%" + "%": 12.3 }, "alkaline_phosphatase": { - "value": 55.0, - "unit": "U/L" + "U/L": 55.0 }, "white_blood_cell_count": { - "value": 6.55, - "unit": "1000 cells/uL" + "1000 cells/uL": 6.55 }, "age": { - "value": 42, - "unit": "years" + "years": 42 } } } diff --git a/tests/raw/phenoage/test__raw__patient_22.json b/tests/raw/phenoage/test__raw__patient_22.json index fd18929..db350fd 100755 --- a/tests/raw/phenoage/test__raw__patient_22.json +++ b/tests/raw/phenoage/test__raw__patient_22.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.3, - "unit": "g/dL" + "g/dL": 4.3 }, "creatinine": { - "value": 1.16, - "unit": "mg/dL" + "mg/dL": 1.16 }, "glucose": { - "value": 106.0, - "unit": "mg/dL" + "mg/dL": 106.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 35.45, - "unit": "%" + "%": 35.45 }, "mean_cell_volume": { - "value": 85.15, - "unit": "fL" + "fL": 85.15 }, "red_cell_distribution_width": { - "value": 14.15, - "unit": "%" + "%": 14.15 }, "alkaline_phosphatase": { - "value": 86.0, - "unit": "U/L" + "U/L": 86.0 }, "white_blood_cell_count": { - "value": 10.55, - "unit": "1000 cells/uL" + "1000 cells/uL": 10.55 }, "age": { - "value": 39, - "unit": "years" + "years": 39 } } } diff --git a/tests/raw/phenoage/test__raw__patient_23.json b/tests/raw/phenoage/test__raw__patient_23.json index c0d1ebc..de7fa28 100755 --- a/tests/raw/phenoage/test__raw__patient_23.json +++ b/tests/raw/phenoage/test__raw__patient_23.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.1, - "unit": "g/dL" + "g/dL": 4.1 }, "creatinine": { - "value": 0.68, - "unit": "mg/dL" + "mg/dL": 0.68 }, "glucose": { - "value": 100.0, - "unit": "mg/dL" + "mg/dL": 100.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 52.5, - "unit": "%" + "%": 52.5 }, "mean_cell_volume": { - "value": 93.1, - "unit": "fL" + "fL": 93.1 }, "red_cell_distribution_width": { - "value": 13.65, - "unit": "%" + "%": 13.65 }, "alkaline_phosphatase": { - "value": 49.0, - "unit": "U/L" + "U/L": 49.0 }, "white_blood_cell_count": { - "value": 3.2, - "unit": "1000 cells/uL" + "1000 cells/uL": 3.2 }, "age": { - "value": 62, - "unit": "years" + "years": 62 } } } diff --git a/tests/raw/phenoage/test__raw__patient_24.json b/tests/raw/phenoage/test__raw__patient_24.json index 329caad..271f05b 100755 --- a/tests/raw/phenoage/test__raw__patient_24.json +++ b/tests/raw/phenoage/test__raw__patient_24.json @@ -8,44 +8,34 @@ }, "raw_biomarkers": { "albumin": { - "value": 4.4, - "unit": "g/dL" + "g/dL": 4.4 }, "creatinine": { - "value": 0.584, - "unit": "mg/dL" + "mg/dL": 0.584 }, "glucose": { - "value": 90.0, - "unit": "mg/dL" + "mg/dL": 90.0 }, "crp": { - "value": 2.1, - "unit": "mg/L" + "mg/L": 2.1 }, "lymphocyte_percent": { - "value": 34.75, - "unit": "%" + "%": 34.75 }, "mean_cell_volume": { - "value": 93.0, - "unit": "fL" + "fL": 93.0 }, "red_cell_distribution_width": { - "value": 12.65, - "unit": "%" + "%": 12.65 }, "alkaline_phosphatase": { - "value": 73.0, - "unit": "U/L" + "U/L": 73.0 }, "white_blood_cell_count": { - "value": 7.75, - "unit": "1000 cells/uL" + "1000 cells/uL": 7.75 }, "age": { - "value": 41, - "unit": "years" + "years": 41 } } } diff --git a/tests/raw/score2/test__raw__patient_25.json b/tests/raw/score2/test__raw__patient_25.json index 45cdeee..b39c7e3 100755 --- a/tests/raw/score2/test__raw__patient_25.json +++ b/tests/raw/score2/test__raw__patient_25.json @@ -8,28 +8,22 @@ }, "raw_biomarkers": { "age": { - "value": 50, - "unit": "years" + "years": 50 }, "smoking": { - "value": true, - "unit": "yes/no" + "yes/no": true }, "systolic_blood_pressure": { - "value": 140, - "unit": "mmHg" + "mmHg": 140 }, "total_cholesterol": { - "value": 6.3, - "unit": "mmol/L" + "mmol/L": 6.3 }, "hdl_cholesterol": { - "value": 1.4, - "unit": "mmol/L" + "mmol/L": 1.4 }, "is_male": { - "value": false, - "unit": "yes/no" + "yes/no": false } } } diff --git a/tests/raw/score2/test__raw__patient_26.json b/tests/raw/score2/test__raw__patient_26.json index 2872e8f..30bfab5 100755 --- a/tests/raw/score2/test__raw__patient_26.json +++ b/tests/raw/score2/test__raw__patient_26.json @@ -8,28 +8,22 @@ }, "raw_biomarkers": { "age": { - "value": 50, - "unit": "years" + "years": 50 }, "smoking": { - "value": true, - "unit": "yes/no" + "yes/no": true }, "systolic_blood_pressure": { - "value": 140, - "unit": "mmHg" + "mmHg": 140 }, "total_cholesterol": { - "value": 6.3, - "unit": "mmol/L" + "mmol/L": 6.3 }, "hdl_cholesterol": { - "value": 1.4, - "unit": "mmol/L" + "mmol/L": 1.4 }, "is_male": { - "value": true, - "unit": "yes/no" + "yes/no": true } } } diff --git a/tests/raw/score2/test__raw__patient_27.json b/tests/raw/score2/test__raw__patient_27.json index c5ea1a0..fa77078 100644 --- a/tests/raw/score2/test__raw__patient_27.json +++ b/tests/raw/score2/test__raw__patient_27.json @@ -8,28 +8,22 @@ }, "raw_biomarkers": { "age": { - "value": 55, - "unit": "years" + "years": 55 }, "smoking": { - "value": false, - "unit": "yes/no" + "yes/no": false }, "systolic_blood_pressure": { - "value": 125, - "unit": "mmHg" + "mmHg": 125 }, "total_cholesterol": { - "value": 5.2, - "unit": "mmol/L" + "mmol/L": 5.2 }, "hdl_cholesterol": { - "value": 1.6, - "unit": "mmol/L" + "mmol/L": 1.6 }, "is_male": { - "value": false, - "unit": "yes/no" + "yes/no": false } } } diff --git a/tests/raw/score2/test__raw__patient_28.json b/tests/raw/score2/test__raw__patient_28.json index 2acaa03..d9f659e 100644 --- a/tests/raw/score2/test__raw__patient_28.json +++ b/tests/raw/score2/test__raw__patient_28.json @@ -8,28 +8,22 @@ }, "raw_biomarkers": { "age": { - "value": 45, - "unit": "years" + "years": 45 }, "smoking": { - "value": false, - "unit": "yes/no" + "yes/no": false }, "systolic_blood_pressure": { - "value": 130, - "unit": "mmHg" + "mmHg": 130 }, "total_cholesterol": { - "value": 5.8, - "unit": "mmol/L" + "mmol/L": 5.8 }, "hdl_cholesterol": { - "value": 1.3, - "unit": "mmol/L" + "mmol/L": 1.3 }, "is_male": { - "value": true, - "unit": "yes/no" + "yes/no": true } } } diff --git a/tests/raw/score2/test__raw__patient_29.json b/tests/raw/score2/test__raw__patient_29.json index f6fcf3f..652a94d 100644 --- a/tests/raw/score2/test__raw__patient_29.json +++ b/tests/raw/score2/test__raw__patient_29.json @@ -8,28 +8,22 @@ }, "raw_biomarkers": { "age": { - "value": 40, - "unit": "years" + "years": 40 }, "smoking": { - "value": true, - "unit": "yes/no" + "yes/no": true }, "systolic_blood_pressure": { - "value": 135, - "unit": "mmHg" + "mmHg": 135 }, "total_cholesterol": { - "value": 6.0, - "unit": "mmol/L" + "mmol/L": 6.0 }, "hdl_cholesterol": { - "value": 1.2, - "unit": "mmol/L" + "mmol/L": 1.2 }, "is_male": { - "value": true, - "unit": "yes/no" + "yes/no": true } } } diff --git a/tests/raw/score2/test__raw__patient_30.json b/tests/raw/score2/test__raw__patient_30.json index ca90af4..f07fe3e 100644 --- a/tests/raw/score2/test__raw__patient_30.json +++ b/tests/raw/score2/test__raw__patient_30.json @@ -8,28 +8,22 @@ }, "raw_biomarkers": { "age": { - "value": 60, - "unit": "years" + "years": 60 }, "smoking": { - "value": false, - "unit": "yes/no" + "yes/no": false }, "systolic_blood_pressure": { - "value": 145, - "unit": "mmHg" + "mmHg": 145 }, "total_cholesterol": { - "value": 6.5, - "unit": "mmol/L" + "mmol/L": 6.5 }, "hdl_cholesterol": { - "value": 1.5, - "unit": "mmol/L" + "mmol/L": 1.5 }, "is_male": { - "value": false, - "unit": "yes/no" + "yes/no": false } } } diff --git a/tests/raw/score2/test__raw__patient_31.json b/tests/raw/score2/test__raw__patient_31.json index 95fa65a..254968b 100644 --- a/tests/raw/score2/test__raw__patient_31.json +++ b/tests/raw/score2/test__raw__patient_31.json @@ -8,28 +8,22 @@ }, "raw_biomarkers": { "age": { - "value": 65, - "unit": "years" + "years": 65 }, "smoking": { - "value": true, - "unit": "yes/no" + "yes/no": true }, "systolic_blood_pressure": { - "value": 150, - "unit": "mmHg" + "mmHg": 150 }, "total_cholesterol": { - "value": 7.0, - "unit": "mmol/L" + "mmol/L": 7.0 }, "hdl_cholesterol": { - "value": 1.1, - "unit": "mmol/L" + "mmol/L": 1.1 }, "is_male": { - "value": true, - "unit": "yes/no" + "yes/no": true } } } diff --git a/tests/raw/score2/test__raw__patient_32.json b/tests/raw/score2/test__raw__patient_32.json index 9a90055..605a60a 100644 --- a/tests/raw/score2/test__raw__patient_32.json +++ b/tests/raw/score2/test__raw__patient_32.json @@ -8,28 +8,22 @@ }, "raw_biomarkers": { "age": { - "value": 69, - "unit": "years" + "years": 69 }, "smoking": { - "value": false, - "unit": "yes/no" + "yes/no": false }, "systolic_blood_pressure": { - "value": 155, - "unit": "mmHg" + "mmHg": 155 }, "total_cholesterol": { - "value": 7.2, - "unit": "mmol/L" + "mmol/L": 7.2 }, "hdl_cholesterol": { - "value": 1.3, - "unit": "mmol/L" + "mmol/L": 1.3 }, "is_male": { - "value": false, - "unit": "yes/no" + "yes/no": false } } } diff --git a/tests/raw/score2/test__raw__patient_33.json b/tests/raw/score2/test__raw__patient_33.json index 9a19590..800df6f 100644 --- a/tests/raw/score2/test__raw__patient_33.json +++ b/tests/raw/score2/test__raw__patient_33.json @@ -8,28 +8,22 @@ }, "raw_biomarkers": { "age": { - "value": 49, - "unit": "years" + "years": 49 }, "smoking": { - "value": true, - "unit": "yes/no" + "yes/no": true }, "systolic_blood_pressure": { - "value": 138, - "unit": "mmHg" + "mmHg": 138 }, "total_cholesterol": { - "value": 6.1, - "unit": "mmol/L" + "mmol/L": 6.1 }, "hdl_cholesterol": { - "value": 1.4, - "unit": "mmol/L" + "mmol/L": 1.4 }, "is_male": { - "value": true, - "unit": "yes/no" + "yes/no": true } } } diff --git a/tests/raw/score2/test__raw__patient_34.json b/tests/raw/score2/test__raw__patient_34.json index 3f0153c..125860f 100644 --- a/tests/raw/score2/test__raw__patient_34.json +++ b/tests/raw/score2/test__raw__patient_34.json @@ -8,28 +8,22 @@ }, "raw_biomarkers": { "age": { - "value": 50, - "unit": "years" + "years": 50 }, "smoking": { - "value": false, - "unit": "yes/no" + "yes/no": false }, "systolic_blood_pressure": { - "value": 120, - "unit": "mmHg" + "mmHg": 120 }, "total_cholesterol": { - "value": 4.8, - "unit": "mmol/L" + "mmol/L": 4.8 }, "hdl_cholesterol": { - "value": 1.8, - "unit": "mmol/L" + "mmol/L": 1.8 }, "is_male": { - "value": false, - "unit": "yes/no" + "yes/no": false } } } diff --git a/tests/raw/score2/test__raw__patient_35.json b/tests/raw/score2/test__raw__patient_35.json index d176cae..d069a82 100644 --- a/tests/raw/score2/test__raw__patient_35.json +++ b/tests/raw/score2/test__raw__patient_35.json @@ -8,28 +8,22 @@ }, "raw_biomarkers": { "age": { - "value": 55, - "unit": "years" + "years": 55 }, "smoking": { - "value": true, - "unit": "yes/no" + "yes/no": true }, "systolic_blood_pressure": { - "value": 142, - "unit": "mmHg" + "mmHg": 142 }, "total_cholesterol": { - "value": 6.4, - "unit": "mmol/L" + "mmol/L": 6.4 }, "hdl_cholesterol": { - "value": 1.2, - "unit": "mmol/L" + "mmol/L": 1.2 }, "is_male": { - "value": true, - "unit": "yes/no" + "yes/no": true } } } diff --git a/tests/raw/score2/test__raw__patient_36.json b/tests/raw/score2/test__raw__patient_36.json index f618344..29f35ed 100644 --- a/tests/raw/score2/test__raw__patient_36.json +++ b/tests/raw/score2/test__raw__patient_36.json @@ -8,28 +8,22 @@ }, "raw_biomarkers": { "age": { - "value": 45, - "unit": "years" + "years": 45 }, "smoking": { - "value": true, - "unit": "yes/no" + "yes/no": true }, "systolic_blood_pressure": { - "value": 132, - "unit": "mmHg" + "mmHg": 132 }, "total_cholesterol": { - "value": 5.5, - "unit": "mmol/L" + "mmol/L": 5.5 }, "hdl_cholesterol": { - "value": 1.5, - "unit": "mmol/L" + "mmol/L": 1.5 }, "is_male": { - "value": false, - "unit": "yes/no" + "yes/no": false } } } diff --git a/tests/test_helpers.py b/tests/test_helpers.py new file mode 100644 index 0000000..1ff220b --- /dev/null +++ b/tests/test_helpers.py @@ -0,0 +1,84 @@ +import json +from pathlib import Path + +from vitals.biomarkers import helpers +from vitals.schemas import phenoage, score2 + +INP_FILEPATH_PHENOAGE: Path = Path(__file__).parent / "inputs" / "phenoage" +INP_FILEPATH_SCORE2: Path = Path(__file__).parent / "inputs" / "score2" +INP_FILEPATH_INVALID: Path = Path(__file__).parent / "inputs" / "invalid" + + +def test_validate_biomarkers_for_algorithm_valid_phenoage(): + """Test successful validation of PhenoAge biomarkers.""" + filepath = INP_FILEPATH_PHENOAGE / "test__input__patient_01.json" + units = phenoage.Units() + + with open(filepath) as f: + test__biomarkers = json.load(f) + + result = helpers.validate_biomarkers_for_algorithm( + test__biomarkers, phenoage.Markers, units + ) + + if result is not None: + assert result.albumin == 40.5 # g/L + assert result.creatinine == 103.428 # umol/L + assert result.glucose == 3.9167 # mmol/L + assert result.crp == 0.5 # mg/dL + assert result.lymphocyte_percent == 40.3 # % + assert result.mean_cell_volume == 89.1 # fL + assert result.red_cell_distribution_width == 11.9 # % + assert result.alkaline_phosphatase == 63.5 # U/L + assert result.white_blood_cell_count == 6.05 # 1000 cells/uL + assert result.age == 39 # years + + +def test_validate_biomarkers_for_algorithm_valid_score2(): + """Test successful validation of SCORE2 biomarkers.""" + filepath = INP_FILEPATH_SCORE2 / "test__input__patient_25.json" + units = score2.Units() + + with open(filepath) as f: + test__biomarkers = json.load(f) + + result = helpers.validate_biomarkers_for_algorithm( + test__biomarkers, score2.Markers, units + ) + + if result is not None: + assert result.age == 50 + assert result.systolic_blood_pressure == 140 + assert result.total_cholesterol == 6.3 + assert result.hdl_cholesterol == 1.4 + assert result.smoking is True + assert result.is_male is False + + +def test_validate_biomarkers_for_algorithm_missing_phenoage_biomarker(): + """Test validation returns None when required PhenoAge biomarker is missing.""" + filepath = INP_FILEPATH_INVALID / "test__phenoage_missing_albumin.json" + units = phenoage.Units() + + with open(filepath) as f: + test__biomarkers = json.load(f) + + result = helpers.validate_biomarkers_for_algorithm( + test__biomarkers, phenoage.Markers, units + ) + + assert result is None + + +def test_validate_biomarkers_for_algorithm_missing_score2_biomarker(): + """Test validation returns None when required SCORE2 biomarker is missing.""" + filepath = INP_FILEPATH_INVALID / "test__score2_missing_sbp.json" + units = score2.Units() + + with open(filepath) as f: + test__biomarkers = json.load(f) + + result = helpers.validate_biomarkers_for_algorithm( + test__biomarkers, score2.Markers, units + ) + assert result is None diff --git a/tests/test_io.py b/tests/test_io.py index 6f42c92..5aef0bd 100755 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -5,8 +5,8 @@ from vitals.biomarkers import io -INP_FILEPATH = Path(__file__).parent / "raw" / "phenoage" -OUT_FILEPATH = Path(__file__).parent / "inputs" / "phenoage" +RAW_FILEPATH: Path = Path(__file__).parent / "raw" / "phenoage" +INP_FILEPATH: Path = Path(__file__).parent / "inputs" / "phenoage" @pytest.mark.parametrize( @@ -15,7 +15,7 @@ ) def test_process_json_files(input_filename, output_filename): # Process files in the tests directory - output = io.update(INP_FILEPATH / input_filename) - with open(OUT_FILEPATH / output_filename) as f: + output = io.update(RAW_FILEPATH / input_filename) + with open(INP_FILEPATH / output_filename) as f: expected_result = json.load(f) assert output == expected_result diff --git a/tests/test_phenoage.py b/tests/test_phenoage.py index 60cf7f2..29faa47 100755 --- a/tests/test_phenoage.py +++ b/tests/test_phenoage.py @@ -1,8 +1,11 @@ +import json from pathlib import Path import pytest +from vitals.biomarkers import helpers from vitals.models import phenoage +from vitals.schemas.phenoage import Markers, Units OUT_FILEPATH = Path(__file__).parent / "inputs" / "phenoage" @@ -25,12 +28,18 @@ ) def test_phenoage(filename, expected): # Get the actual fixture value using request.getfixturevalue - age, pred_age, accl_age = phenoage.compute(OUT_FILEPATH / filename) - expected_age, expected_pred_age, expected_accl_age = expected - - assert age == expected_age - assert pytest.approx(pred_age, abs=0.5) == expected_pred_age - assert pytest.approx(accl_age, abs=0.02) == expected_accl_age + with open(OUT_FILEPATH / filename) as f: + test__input = json.load(f) + test_biomarkers = helpers.validate_biomarkers_for_algorithm( + raw_biomarkers=test__input, biomarker_class=Markers, biomarker_units=Units() + ) + if test_biomarkers is not None: + age, pred_age, accl_age = phenoage.compute(test_biomarkers) + expected_age, expected_pred_age, expected_accl_age = expected + + assert age == expected_age + assert pytest.approx(pred_age, abs=0.5) == expected_pred_age + assert pytest.approx(accl_age, abs=0.02) == expected_accl_age if __name__ == "__main__": diff --git a/tests/test_score2.py b/tests/test_score2.py index 32adee3..ca0c2b3 100644 --- a/tests/test_score2.py +++ b/tests/test_score2.py @@ -1,8 +1,11 @@ +import json from pathlib import Path import pytest +from vitals.biomarkers import helpers from vitals.models import score2 +from vitals.schemas.score2 import Markers, Units OUT_FILEPATH = Path(__file__).parent / "inputs" / "score2" @@ -26,12 +29,19 @@ ) def test_score2(filename, expected): # Get the actual fixture value using request.getfixturevalue - age, pred_risk, pred_risk_category = score2.compute(OUT_FILEPATH / filename) - expected_age, expected_risk, expected_category = expected - - assert age == expected_age - assert pred_risk_category == expected_category - assert pytest.approx(pred_risk, abs=0.1) == expected_risk + with open(OUT_FILEPATH / filename) as f: + test__input = json.load(f) + test_biomarkers = helpers.validate_biomarkers_for_algorithm( + raw_biomarkers=test__input, biomarker_class=Markers, biomarker_units=Units() + ) + + if test_biomarkers is not None: + age, pred_risk, pred_risk_category = score2.compute(test_biomarkers) + expected_age, expected_risk, expected_category = expected + + assert age == expected_age + assert pred_risk_category == expected_category + assert pytest.approx(pred_risk, abs=0.1) == expected_risk if __name__ == "__main__": diff --git a/tests/test_score2_diabetes.py b/tests/test_score2_diabetes.py index 1a57311..dca3a6a 100644 --- a/tests/test_score2_diabetes.py +++ b/tests/test_score2_diabetes.py @@ -1,8 +1,11 @@ +import json from pathlib import Path import pytest +from vitals.biomarkers import helpers from vitals.models import score2_diabetes +from vitals.schemas.score2 import DiabetesMarkers, Markers, Units OUT_FILEPATH = Path(__file__).parent / "inputs" / "score2_diabetes" @@ -19,21 +22,24 @@ ], ) def test_score2_diabetes(filename, expected): - """ - Test SCORE2-Diabetes cardiovascular risk calculation. - - NOTE: The expected risk values and categories are placeholders. - They need to be calculated using MDCalc and updated before running tests. - """ # Get the actual fixture value - age, pred_risk, pred_risk_category = score2_diabetes.compute( - OUT_FILEPATH / filename - ) - expected_age, expected_risk, expected_category = expected - - assert age == expected_age - assert pred_risk_category == expected_category - assert pytest.approx(pred_risk, abs=0.1) == expected_risk + with open(OUT_FILEPATH / filename) as f: + test__input = json.load(f) + test_biomarkers = helpers.validate_biomarkers_for_algorithm( + raw_biomarkers=test__input, biomarker_class=Markers, biomarker_units=Units() + ) + + if test_biomarkers is not None: + diabetes_biomarkers = DiabetesMarkers.try_from_markers(test_biomarkers) + if diabetes_biomarkers is not None: + age, pred_risk, pred_risk_category = score2_diabetes.compute( + diabetes_biomarkers + ) + expected_age, expected_risk, expected_category = expected + + assert age == expected_age + assert pred_risk_category == expected_category + assert pytest.approx(pred_risk, abs=0.1) == expected_risk if __name__ == "__main__": diff --git a/uv.lock b/uv.lock index cd7e5fd..07388ea 100644 --- a/uv.lock +++ b/uv.lock @@ -12,41 +12,34 @@ wheels = [ ] [[package]] -name = "asttokens" -version = "3.0.0" +name = "anyio" +version = "4.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978, upload-time = "2024-11-30T04:30:14.439Z" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918, upload-time = "2024-11-30T04:30:10.946Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, ] [[package]] -name = "black" -version = "25.1.0" +name = "asttokens" +version = "3.0.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "mypy-extensions" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/94/49/26a7b0f3f35da4b5a65f081943b7bcd22d7002f5f0fb8098ec1ff21cb6ef/black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666", size = 649449, upload-time = "2025-01-29T04:15:40.373Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/e7/82da0a03e7ba5141f05cce0d302e6eed121ae055e0456ca228bf693984bc/asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", size = 61978, upload-time = "2024-11-30T04:30:14.439Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/87/0edf98916640efa5d0696e1abb0a8357b52e69e82322628f25bf14d263d1/black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f", size = 1650673, upload-time = "2025-01-29T05:37:20.574Z" }, - { url = "https://files.pythonhosted.org/packages/52/e5/f7bf17207cf87fa6e9b676576749c6b6ed0d70f179a3d812c997870291c3/black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3", size = 1453190, upload-time = "2025-01-29T05:37:22.106Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ee/adda3d46d4a9120772fae6de454c8495603c37c4c3b9c60f25b1ab6401fe/black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171", size = 1782926, upload-time = "2025-01-29T04:18:58.564Z" }, - { url = "https://files.pythonhosted.org/packages/cc/64/94eb5f45dcb997d2082f097a3944cfc7fe87e071907f677e80788a2d7b7a/black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18", size = 1442613, upload-time = "2025-01-29T04:19:27.63Z" }, - { url = "https://files.pythonhosted.org/packages/09/71/54e999902aed72baf26bca0d50781b01838251a462612966e9fc4891eadd/black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717", size = 207646, upload-time = "2025-01-29T04:15:38.082Z" }, + { url = "https://files.pythonhosted.org/packages/25/8a/c46dcc25341b5bce5472c718902eb3d38600a903b14fa6aeecef3f21a46f/asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2", size = 26918, upload-time = "2024-11-30T04:30:10.946Z" }, ] [[package]] name = "certifi" -version = "2025.7.9" +version = "2025.7.14" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/de/8a/c729b6b60c66a38f590c4e774decc4b2ec7b0576be8f1aa984a53ffa812a/certifi-2025.7.9.tar.gz", hash = "sha256:c1d2ec05395148ee10cf672ffc28cd37ea0ab0d99f9cc74c43e588cbd111b079", size = 160386, upload-time = "2025-07-09T02:13:58.874Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/76/52c535bcebe74590f296d6c77c86dabf761c41980e1347a2422e4aa2ae41/certifi-2025.7.14.tar.gz", hash = "sha256:8ea99dbdfaaf2ba2f9bac77b9249ef62ec5218e7c2b2e903378ed5fccf765995", size = 163981, upload-time = "2025-07-14T03:29:28.449Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/66/f3/80a3f974c8b535d394ff960a11ac20368e06b736da395b551a49ce950cce/certifi-2025.7.9-py3-none-any.whl", hash = "sha256:d842783a14f8fdd646895ac26f719a061408834473cfc10203f6a575beb15d39", size = 159230, upload-time = "2025-07-09T02:13:57.007Z" }, + { url = "https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl", hash = "sha256:6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2", size = 162722, upload-time = "2025-07-14T03:29:26.863Z" }, ] [[package]] @@ -143,11 +136,11 @@ wheels = [ [[package]] name = "distlib" -version = "0.3.9" +version = "0.4.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923, upload-time = "2024-10-09T18:35:47.551Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973, upload-time = "2024-10-09T18:35:44.272Z" }, + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] [[package]] @@ -159,6 +152,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/8f/c4d9bafc34ad7ad5d8dc16dd1347ee0e507a52c3adb6bfa8887e1c6a26ba/executing-2.2.0-py2.py3-none-any.whl", hash = "sha256:11387150cad388d62750327a53d3339fad4888b39a6fe233c3afbb54ecffd3aa", size = 26702, upload-time = "2025-01-22T15:41:25.929Z" }, ] +[[package]] +name = "fastapi" +version = "0.116.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "starlette" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/d7/6c8b3bfe33eeffa208183ec037fee0cce9f7f024089ab1c5d12ef04bd27c/fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143", size = 296485, upload-time = "2025-07-11T16:22:32.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/47/d63c60f59a59467fda0f93f46335c9d18526d7071f025cb5b89d5353ea42/fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565", size = 95631, upload-time = "2025-07-11T16:22:30.485Z" }, +] + [[package]] name = "filelock" version = "3.18.0" @@ -168,6 +175,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4d/36/2a115987e2d8c300a974597416d9de88f2444426de9571f4b59b2cca3acc/filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de", size = 16215, upload-time = "2025-03-14T07:11:39.145Z" }, ] +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "httptools" +version = "0.6.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639, upload-time = "2024-10-16T19:45:08.902Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214, upload-time = "2024-10-16T19:44:38.738Z" }, + { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431, upload-time = "2024-10-16T19:44:39.818Z" }, + { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121, upload-time = "2024-10-16T19:44:41.189Z" }, + { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805, upload-time = "2024-10-16T19:44:42.384Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858, upload-time = "2024-10-16T19:44:43.959Z" }, + { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042, upload-time = "2024-10-16T19:44:45.071Z" }, + { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682, upload-time = "2024-10-16T19:44:46.46Z" }, +] + [[package]] name = "identify" version = "2.6.12" @@ -252,15 +283,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899, upload-time = "2024-04-15T13:44:43.265Z" }, ] -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, -] - [[package]] name = "nodeenv" version = "1.9.1" @@ -345,15 +367,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650, upload-time = "2024-04-05T09:43:53.299Z" }, ] -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, -] - [[package]] name = "pexpect" version = "4.9.0" @@ -510,43 +523,58 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] +[[package]] +name = "python-dotenv" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/b0/4bc07ccd3572a2f9df7e6782f52b0c6c90dcbb803ac4a167702d7d0dfe1e/python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab", size = 41978, upload-time = "2025-06-24T04:21:07.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/ed/539768cf28c661b5b068d66d96a2f155c4971a5d55684a514c1a0e0dec2f/python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc", size = 20556, upload-time = "2025-06-24T04:21:06.073Z" }, +] + [[package]] name = "python-phenoage" -version = "0.1.0" +version = "0.2.0" source = { editable = "." } dependencies = [ { name = "coverage" }, + { name = "fastapi" }, { name = "numpy" }, { name = "pandas" }, { name = "pydantic" }, { name = "requests" }, + { name = "uvicorn", extra = ["standard"] }, ] [package.dev-dependencies] dev = [ - { name = "black" }, { name = "coverage" }, { name = "ipython" }, { name = "pre-commit" }, { name = "pytest" }, + { name = "ruff" }, + { name = "ty" }, ] [package.metadata] requires-dist = [ { name = "coverage", specifier = ">=7.9.1" }, + { name = "fastapi", specifier = ">=0.104.1" }, { name = "numpy" }, { name = "pandas" }, { name = "pydantic" }, { name = "requests", specifier = ">=2.32.4" }, + { name = "uvicorn", extras = ["standard"], specifier = ">=0.24.0" }, ] [package.metadata.requires-dev] dev = [ - { name = "black" }, { name = "coverage" }, { name = "ipython", specifier = ">=9.3.0" }, { name = "pre-commit", specifier = ">=4.2.0" }, { name = "pytest" }, + { name = "ruff" }, + { name = "ty" }, ] [[package]] @@ -590,6 +618,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", size = 64847, upload-time = "2025-06-09T16:43:05.728Z" }, ] +[[package]] +name = "ruff" +version = "0.12.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/ce/8d7dbedede481245b489b769d27e2934730791a9a82765cb94566c6e6abd/ruff-0.12.4.tar.gz", hash = "sha256:13efa16df6c6eeb7d0f091abae50f58e9522f3843edb40d56ad52a5a4a4b6873", size = 5131435, upload-time = "2025-07-17T17:27:19.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/9f/517bc5f61bad205b7f36684ffa5415c013862dee02f55f38a217bdbe7aa4/ruff-0.12.4-py3-none-linux_armv6l.whl", hash = "sha256:cb0d261dac457ab939aeb247e804125a5d521b21adf27e721895b0d3f83a0d0a", size = 10188824, upload-time = "2025-07-17T17:26:31.412Z" }, + { url = "https://files.pythonhosted.org/packages/28/83/691baae5a11fbbde91df01c565c650fd17b0eabed259e8b7563de17c6529/ruff-0.12.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:55c0f4ca9769408d9b9bac530c30d3e66490bd2beb2d3dae3e4128a1f05c7442", size = 10884521, upload-time = "2025-07-17T17:26:35.084Z" }, + { url = "https://files.pythonhosted.org/packages/d6/8d/756d780ff4076e6dd035d058fa220345f8c458391f7edfb1c10731eedc75/ruff-0.12.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a8224cc3722c9ad9044da7f89c4c1ec452aef2cfe3904365025dd2f51daeae0e", size = 10277653, upload-time = "2025-07-17T17:26:37.897Z" }, + { url = "https://files.pythonhosted.org/packages/8d/97/8eeee0f48ece153206dce730fc9e0e0ca54fd7f261bb3d99c0a4343a1892/ruff-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9949d01d64fa3672449a51ddb5d7548b33e130240ad418884ee6efa7a229586", size = 10485993, upload-time = "2025-07-17T17:26:40.68Z" }, + { url = "https://files.pythonhosted.org/packages/49/b8/22a43d23a1f68df9b88f952616c8508ea6ce4ed4f15353b8168c48b2d7e7/ruff-0.12.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:be0593c69df9ad1465e8a2d10e3defd111fdb62dcd5be23ae2c06da77e8fcffb", size = 10022824, upload-time = "2025-07-17T17:26:43.564Z" }, + { url = "https://files.pythonhosted.org/packages/cd/70/37c234c220366993e8cffcbd6cadbf332bfc848cbd6f45b02bade17e0149/ruff-0.12.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7dea966bcb55d4ecc4cc3270bccb6f87a337326c9dcd3c07d5b97000dbff41c", size = 11524414, upload-time = "2025-07-17T17:26:46.219Z" }, + { url = "https://files.pythonhosted.org/packages/14/77/c30f9964f481b5e0e29dd6a1fae1f769ac3fd468eb76fdd5661936edd262/ruff-0.12.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:afcfa3ab5ab5dd0e1c39bf286d829e042a15e966b3726eea79528e2e24d8371a", size = 12419216, upload-time = "2025-07-17T17:26:48.883Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/af7fe0a4202dce4ef62c5e33fecbed07f0178f5b4dd9c0d2fcff5ab4a47c/ruff-0.12.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c057ce464b1413c926cdb203a0f858cd52f3e73dcb3270a3318d1630f6395bb3", size = 11976756, upload-time = "2025-07-17T17:26:51.754Z" }, + { url = "https://files.pythonhosted.org/packages/09/d1/33fb1fc00e20a939c305dbe2f80df7c28ba9193f7a85470b982815a2dc6a/ruff-0.12.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e64b90d1122dc2713330350626b10d60818930819623abbb56535c6466cce045", size = 11020019, upload-time = "2025-07-17T17:26:54.265Z" }, + { url = "https://files.pythonhosted.org/packages/64/f4/e3cd7f7bda646526f09693e2e02bd83d85fff8a8222c52cf9681c0d30843/ruff-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2abc48f3d9667fdc74022380b5c745873499ff827393a636f7a59da1515e7c57", size = 11277890, upload-time = "2025-07-17T17:26:56.914Z" }, + { url = "https://files.pythonhosted.org/packages/5e/d0/69a85fb8b94501ff1a4f95b7591505e8983f38823da6941eb5b6badb1e3a/ruff-0.12.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:2b2449dc0c138d877d629bea151bee8c0ae3b8e9c43f5fcaafcd0c0d0726b184", size = 10348539, upload-time = "2025-07-17T17:26:59.381Z" }, + { url = "https://files.pythonhosted.org/packages/16/a0/91372d1cb1678f7d42d4893b88c252b01ff1dffcad09ae0c51aa2542275f/ruff-0.12.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:56e45bb11f625db55f9b70477062e6a1a04d53628eda7784dce6e0f55fd549eb", size = 10009579, upload-time = "2025-07-17T17:27:02.462Z" }, + { url = "https://files.pythonhosted.org/packages/23/1b/c4a833e3114d2cc0f677e58f1df6c3b20f62328dbfa710b87a1636a5e8eb/ruff-0.12.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:478fccdb82ca148a98a9ff43658944f7ab5ec41c3c49d77cd99d44da019371a1", size = 10942982, upload-time = "2025-07-17T17:27:05.343Z" }, + { url = "https://files.pythonhosted.org/packages/ff/ce/ce85e445cf0a5dd8842f2f0c6f0018eedb164a92bdf3eda51984ffd4d989/ruff-0.12.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0fc426bec2e4e5f4c4f182b9d2ce6a75c85ba9bcdbe5c6f2a74fcb8df437df4b", size = 11343331, upload-time = "2025-07-17T17:27:08.652Z" }, + { url = "https://files.pythonhosted.org/packages/35/cf/441b7fc58368455233cfb5b77206c849b6dfb48b23de532adcc2e50ccc06/ruff-0.12.4-py3-none-win32.whl", hash = "sha256:4de27977827893cdfb1211d42d84bc180fceb7b72471104671c59be37041cf93", size = 10267904, upload-time = "2025-07-17T17:27:11.814Z" }, + { url = "https://files.pythonhosted.org/packages/ce/7e/20af4a0df5e1299e7368d5ea4350412226afb03d95507faae94c80f00afd/ruff-0.12.4-py3-none-win_amd64.whl", hash = "sha256:fe0b9e9eb23736b453143d72d2ceca5db323963330d5b7859d60d101147d461a", size = 11209038, upload-time = "2025-07-17T17:27:14.417Z" }, + { url = "https://files.pythonhosted.org/packages/11/02/8857d0dfb8f44ef299a5dfd898f673edefb71e3b533b3b9d2db4c832dd13/ruff-0.12.4-py3-none-win_arm64.whl", hash = "sha256:0618ec4442a83ab545e5b71202a5c0ed7791e8471435b94e655b570a5031a98e", size = 10469336, upload-time = "2025-07-17T17:27:16.913Z" }, +] + [[package]] name = "six" version = "1.17.0" @@ -599,6 +652,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + [[package]] name = "stack-data" version = "0.6.3" @@ -613,6 +675,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, ] +[[package]] +name = "starlette" +version = "0.47.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/57/d062573f391d062710d4088fa1369428c38d51460ab6fedff920efef932e/starlette-0.47.2.tar.gz", hash = "sha256:6ae9aa5db235e4846decc1e7b79c4f346adf41e9777aebeb49dfd09bbd7023d8", size = 2583948, upload-time = "2025-07-20T17:31:58.522Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/1f/b876b1f83aef204198a42dc101613fefccb32258e5428b5f9259677864b4/starlette-0.47.2-py3-none-any.whl", hash = "sha256:c5847e96134e5c5371ee9fac6fdf1a67336d5815e09eb2a01fdb57a351ef915b", size = 72984, upload-time = "2025-07-20T17:31:56.738Z" }, +] + [[package]] name = "traitlets" version = "5.14.3" @@ -622,6 +696,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, ] +[[package]] +name = "ty" +version = "0.0.1a15" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/28/ba/abedc672a4d706241106923595d68573e995f85aced13aa3ef2e6d5069cf/ty-0.0.1a15.tar.gz", hash = "sha256:b601eb50e981bd3fb857eb17b473cad3728dab67f53370b6790dfc342797eb20", size = 3886937, upload-time = "2025-07-18T13:02:20.017Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/86/4846900f8b7f3dc7c2ec4e0bbd6bc4a4797f27443d3c9878ece5dfcb1446/ty-0.0.1a15-py3-none-linux_armv6l.whl", hash = "sha256:6110b5afee7ae1b0c8d00770eef4937ed0b700b823da04db04486bc661dc0f80", size = 7807444, upload-time = "2025-07-18T13:01:47.81Z" }, + { url = "https://files.pythonhosted.org/packages/e7/bd/b4ee15ffbf0fda9853aefb6cdfaa8d15a07af6ab1c6c874f7ad9adcdc2bd/ty-0.0.1a15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:855401e2fc1d4376f007ef7684dd9173e6a408adc2bc4610013f40c2a1d68d0f", size = 7913908, upload-time = "2025-07-18T13:01:50.877Z" }, + { url = "https://files.pythonhosted.org/packages/cb/5e/c37942782de2ed347ea24227fab61ad80383cee7f339af2be65a7732c4a9/ty-0.0.1a15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a20b21ea9683d92d541de4a534b68b4b595c2d04bf77be0ebfe05c9768ef47e7", size = 7526774, upload-time = "2025-07-18T13:01:52.403Z" }, + { url = "https://files.pythonhosted.org/packages/a4/11/8fa1eba381f2bc70eb8eccb2f93aa6f674b9578a1281cdf4984100de8009/ty-0.0.1a15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7648b0931177233e31d952723f068f2925696e464c436ed8bd820b775053474b", size = 7648872, upload-time = "2025-07-18T13:01:54.166Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/b12e34103638089848d58bb4a2813e9e77969fa7b4479212c9a263e7a176/ty-0.0.1a15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c9c6b70ae331585984b79a4574f28619d5ff755515b93b5454d04f5c521ca864", size = 7647674, upload-time = "2025-07-18T13:01:56.013Z" }, + { url = "https://files.pythonhosted.org/packages/2c/b9/c1d8c8e268fe46a65e77b8a61ef5e76ebf6ce5eec2beeb6a063ab23042fb/ty-0.0.1a15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38cae5d28b2882e66f4786825e87d500cfbb806c30bbcac745f20e459cf92482", size = 8470612, upload-time = "2025-07-18T13:01:57.526Z" }, + { url = "https://files.pythonhosted.org/packages/16/ba/c4a246026dbdd9f537d882aa51fa34e3a43288b493952724f71a59fb93cc/ty-0.0.1a15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1a8de6d3185afbf7cc199932d7fc508887e7ddad95a15c930efc4b5445eae6de", size = 8928257, upload-time = "2025-07-18T13:01:59.705Z" }, + { url = "https://files.pythonhosted.org/packages/e1/53/7958aa2a730fea926f992cd217f33363c9d0dd0cb688a7c9afa5d083863e/ty-0.0.1a15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5a38db0c2ceb2f0c20241ef6a1a5b0996dad45532bb50661faf46f28b64b9f0", size = 8576435, upload-time = "2025-07-18T13:02:01.535Z" }, + { url = "https://files.pythonhosted.org/packages/e7/77/6b65b83e28d162951e72212f31a1f9fdf7d30023a37702cb35d451df9fb8/ty-0.0.1a15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0045fe7905813296fa821dad4aaabbe0f011ce34915fdfabf651db5b5f7b9d72", size = 8411987, upload-time = "2025-07-18T13:02:03.394Z" }, + { url = "https://files.pythonhosted.org/packages/40/2f/c58c08165edb2e13b5c10f81fa2fc3f9c576992e7abb2c56d636245a49f6/ty-0.0.1a15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32deff2b8b05e8a8b8bf0f48ca1eef72ec299b9cc546ef9aba7185a033de28b1", size = 8211299, upload-time = "2025-07-18T13:02:05.662Z" }, + { url = "https://files.pythonhosted.org/packages/0d/0b/959d4186d87fc99af7c0cb1c425d351d7204d4ed54638925c21915c338ba/ty-0.0.1a15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:60c330a9f37b260ebdf7d3e7e05ec483fab15116f11317ffd76b0e09598038b0", size = 7550119, upload-time = "2025-07-18T13:02:07.804Z" }, + { url = "https://files.pythonhosted.org/packages/89/08/28b33a1125128f57b09a71d043e6ee06502c773ef0fab03fb54bd58dcfa4/ty-0.0.1a15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:745355c15574d50229c3644e47bad1192e261faaf3a11870641b4902a8d9d8fe", size = 7672278, upload-time = "2025-07-18T13:02:09.339Z" }, + { url = "https://files.pythonhosted.org/packages/2b/ba/5569b0a1a90d302e0636718a73a7c3d7029cfa03670f6cc716a4ab318709/ty-0.0.1a15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:07d53cb7c9c322be41dc79c373024422f6c6cd9e96f658e4b1b3289fe6130274", size = 8092872, upload-time = "2025-07-18T13:02:10.871Z" }, + { url = "https://files.pythonhosted.org/packages/2e/f2/a6e94b8b0189af49e871210b7244c4d49c5ac9cc1167f16dd0f28e026745/ty-0.0.1a15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26b28ed6e6ea80766fdd2608ea6e4daeb211e8de2b4b88376f574667bb90f489", size = 8278734, upload-time = "2025-07-18T13:02:13.059Z" }, + { url = "https://files.pythonhosted.org/packages/2e/ae/90d6008d3afe0762d089b5b363be62c3e19d9730c0b04f823448a56aa5fa/ty-0.0.1a15-py3-none-win32.whl", hash = "sha256:42f8d40aa30ef0c2187b70528151e740b74db47eb84a568fbc636c7294a1046e", size = 7390797, upload-time = "2025-07-18T13:02:14.898Z" }, + { url = "https://files.pythonhosted.org/packages/d4/14/fc292587c6e85e0b2584562c2cee26ece7c86e0679a690de86f53ad367bf/ty-0.0.1a15-py3-none-win_amd64.whl", hash = "sha256:2563111b072ea132443629a5fe0ec0cefed94c610cc694fc1bd2f48e179ca966", size = 7978840, upload-time = "2025-07-18T13:02:16.74Z" }, + { url = "https://files.pythonhosted.org/packages/24/e9/4d8c22801c7348ce79456c9c071914d94783c5f575ddddb30161b98a7c34/ty-0.0.1a15-py3-none-win_arm64.whl", hash = "sha256:9ea13096dda97437284b61915da92384d283cd096dbe730a3f63ee644721d2d5", size = 7561340, upload-time = "2025-07-18T13:02:18.629Z" }, +] + [[package]] name = "typing-extensions" version = "4.14.1" @@ -661,18 +760,110 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, ] +[[package]] +name = "uvicorn" +version = "0.35.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/42/e0e305207bb88c6b8d3061399c6a961ffe5fbb7e2aa63c9234df7259e9cd/uvicorn-0.35.0.tar.gz", hash = "sha256:bc662f087f7cf2ce11a1d7fd70b90c9f98ef2e2831556dd078d131b96cc94a01", size = 78473, upload-time = "2025-06-28T16:15:46.058Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406, upload-time = "2025-06-28T16:15:44.816Z" }, +] + +[package.optional-dependencies] +standard = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "httptools" }, + { name = "python-dotenv" }, + { name = "pyyaml" }, + { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" }, + { name = "watchfiles" }, + { name = "websockets" }, +] + +[[package]] +name = "uvloop" +version = "0.21.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741, upload-time = "2024-10-14T23:38:35.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/8d/2cbef610ca21539f0f36e2b34da49302029e7c9f09acef0b1c3b5839412b/uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281", size = 1468123, upload-time = "2024-10-14T23:38:00.688Z" }, + { url = "https://files.pythonhosted.org/packages/93/0d/b0038d5a469f94ed8f2b2fce2434a18396d8fbfb5da85a0a9781ebbdec14/uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af", size = 819325, upload-time = "2024-10-14T23:38:02.309Z" }, + { url = "https://files.pythonhosted.org/packages/50/94/0a687f39e78c4c1e02e3272c6b2ccdb4e0085fda3b8352fecd0410ccf915/uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6", size = 4582806, upload-time = "2024-10-14T23:38:04.711Z" }, + { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816", size = 4701068, upload-time = "2024-10-14T23:38:06.385Z" }, + { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc", size = 4454428, upload-time = "2024-10-14T23:38:08.416Z" }, + { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018, upload-time = "2024-10-14T23:38:10.888Z" }, +] + [[package]] name = "virtualenv" -version = "20.31.2" +version = "20.32.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, { name = "filelock" }, { name = "platformdirs" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/56/2c/444f465fb2c65f40c3a104fd0c495184c4f2336d65baf398e3c75d72ea94/virtualenv-20.31.2.tar.gz", hash = "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af", size = 6076316, upload-time = "2025-05-08T17:58:23.811Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/96/0834f30fa08dca3738614e6a9d42752b6420ee94e58971d702118f7cfd30/virtualenv-20.32.0.tar.gz", hash = "sha256:886bf75cadfdc964674e6e33eb74d787dff31ca314ceace03ca5810620f4ecf0", size = 6076970, upload-time = "2025-07-21T04:09:50.985Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/40/b1c265d4b2b62b58576588510fc4d1fe60a86319c8de99fd8e9fec617d2c/virtualenv-20.31.2-py3-none-any.whl", hash = "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11", size = 6057982, upload-time = "2025-05-08T17:58:21.15Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c6/f8f28009920a736d0df434b52e9feebfb4d702ba942f15338cb4a83eafc1/virtualenv-20.32.0-py3-none-any.whl", hash = "sha256:2c310aecb62e5aa1b06103ed7c2977b81e042695de2697d01017ff0f1034af56", size = 6057761, upload-time = "2025-07-21T04:09:48.059Z" }, +] + +[[package]] +name = "watchfiles" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406, upload-time = "2025-06-15T19:06:59.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004, upload-time = "2025-06-15T19:05:38.499Z" }, + { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671, upload-time = "2025-06-15T19:05:39.52Z" }, + { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772, upload-time = "2025-06-15T19:05:40.897Z" }, + { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789, upload-time = "2025-06-15T19:05:42.045Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551, upload-time = "2025-06-15T19:05:43.781Z" }, + { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420, upload-time = "2025-06-15T19:05:45.244Z" }, + { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950, upload-time = "2025-06-15T19:05:46.332Z" }, + { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706, upload-time = "2025-06-15T19:05:47.459Z" }, + { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814, upload-time = "2025-06-15T19:05:48.654Z" }, + { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820, upload-time = "2025-06-15T19:05:50.088Z" }, + { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194, upload-time = "2025-06-15T19:05:51.186Z" }, + { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349, upload-time = "2025-06-15T19:05:52.201Z" }, + { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836, upload-time = "2025-06-15T19:05:53.265Z" }, + { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343, upload-time = "2025-06-15T19:05:54.252Z" }, + { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916, upload-time = "2025-06-15T19:05:55.264Z" }, + { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582, upload-time = "2025-06-15T19:05:56.317Z" }, + { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752, upload-time = "2025-06-15T19:05:57.359Z" }, + { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436, upload-time = "2025-06-15T19:05:58.447Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016, upload-time = "2025-06-15T19:05:59.59Z" }, + { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727, upload-time = "2025-06-15T19:06:01.086Z" }, + { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864, upload-time = "2025-06-15T19:06:02.144Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626, upload-time = "2025-06-15T19:06:03.578Z" }, + { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744, upload-time = "2025-06-15T19:06:05.066Z" }, + { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114, upload-time = "2025-06-15T19:06:06.186Z" }, + { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879, upload-time = "2025-06-15T19:06:07.369Z" }, + { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026, upload-time = "2025-06-15T19:06:08.476Z" }, + { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917, upload-time = "2025-06-15T19:06:09.988Z" }, + { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602, upload-time = "2025-06-15T19:06:11.088Z" }, + { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758, upload-time = "2025-06-15T19:06:12.197Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601, upload-time = "2025-06-15T19:06:13.391Z" }, + { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936, upload-time = "2025-06-15T19:06:14.656Z" }, + { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243, upload-time = "2025-06-15T19:06:16.232Z" }, + { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073, upload-time = "2025-06-15T19:06:17.457Z" }, + { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872, upload-time = "2025-06-15T19:06:18.57Z" }, + { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877, upload-time = "2025-06-15T19:06:19.55Z" }, + { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645, upload-time = "2025-06-15T19:06:20.66Z" }, + { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424, upload-time = "2025-06-15T19:06:21.712Z" }, + { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584, upload-time = "2025-06-15T19:06:22.777Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675, upload-time = "2025-06-15T19:06:24.226Z" }, + { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363, upload-time = "2025-06-15T19:06:25.42Z" }, + { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240, upload-time = "2025-06-15T19:06:26.552Z" }, + { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607, upload-time = "2025-06-15T19:06:27.606Z" }, + { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315, upload-time = "2025-06-15T19:06:29.076Z" }, ] [[package]] @@ -683,3 +874,23 @@ sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc wheels = [ { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166, upload-time = "2024-01-06T02:10:55.763Z" }, ] + +[[package]] +name = "websockets" +version = "15.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, +] diff --git a/vitals/biomarkers/exceptions.py b/vitals/biomarkers/exceptions.py new file mode 100644 index 0000000..8f63352 --- /dev/null +++ b/vitals/biomarkers/exceptions.py @@ -0,0 +1,2 @@ +class BiomarkerNotFound(Exception): + pass diff --git a/vitals/biomarkers/helpers.py b/vitals/biomarkers/helpers.py index 15657f5..2235f94 100755 --- a/vitals/biomarkers/helpers.py +++ b/vitals/biomarkers/helpers.py @@ -1,6 +1,5 @@ from collections.abc import Callable -from pathlib import Path -from typing import Any, Literal, TypeAlias, TypedDict, TypeVar +from typing import Any, Literal, TypeAlias, TypeVar import numpy as np from pydantic import BaseModel @@ -9,197 +8,88 @@ RiskCategory: TypeAlias = Literal["Low to moderate", "High", "Very high"] Biomarkers = TypeVar("Biomarkers", bound=BaseModel) -Units = phenoage.Units | score2.Units | score2.UnitsDiabetes - - -class ConversionInfo(TypedDict): - """Type definition for biomarker conversion information.""" - - target_name: str - target_unit: str - conversion: Callable[[float], float] - - -def format_unit_suffix(unit: str) -> str: - """Convert unit string to a valid suffix format. - - Args: - unit: Unit string (e.g., "mg/dL", "1000 cells/uL") - - Returns: - Formatted suffix (e.g., "mg_dl", "1000_cells_ul") - """ - # Replace special characters with underscores - suffix = unit.lower() - suffix = suffix.replace("/", "_") - suffix = suffix.replace(" ", "_") - suffix = suffix.replace("^", "") - return suffix - - -def update_biomarker_names(biomarkers: dict[str, Any]) -> dict[str, Any]: - """Update biomarker names to include unit suffixes. - - Args: - biomarkers: Dictionary of biomarker data with value and unit - - Returns: - Updated dictionary with unit-suffixed biomarker names - """ - updated_biomarkers = {} - - for name, data in biomarkers.items(): - if isinstance(data, dict) and "unit" in data: - unit_suffix = format_unit_suffix(data["unit"]) - new_name = f"{name}_{unit_suffix}" - updated_biomarkers[new_name] = data - else: - # Keep as is if not in expected format - updated_biomarkers[name] = data - - return updated_biomarkers - - -def find_biomarker_value( - raw_biomarkers: dict[str, Any], biomarker_name: str, expected_unit: str -) -> float | None: - """ - Find biomarker value by name prefix and expected unit. - - Args: - raw_biomarkers: Dictionary of biomarker data - biomarker_name: Name of biomarker to find (without unit suffix) - expected_unit: Expected unit for this biomarker - - Returns: - Biomarker value if found, None otherwise - """ - for key, biomarker_data in raw_biomarkers.items(): - if key.startswith(biomarker_name) and isinstance(biomarker_data, dict): - unit = biomarker_data.get("unit", "") - if unit == expected_unit: - return biomarker_data.get("value") - - return None +Units = phenoage.Units | score2.Units def add_converted_biomarkers(biomarkers: dict[str, Any]) -> dict[str, Any]: """Add converted biomarker entries for glucose, creatinine, albumin, and CRP. Args: - biomarkers: Dictionary of biomarkers with unit-suffixed names + biomarkers: Dictionary of biomarkers with unit-keyed values Returns: Dictionary with original and converted biomarkers """ - # Copy original biomarkers - result = biomarkers.copy() - - # Conversion mappings - conversions: dict[str, ConversionInfo] = { - "glucose_mg_dl": { - "target_name": "glucose_mmol_l", - "target_unit": "mmol/L", - "conversion": lambda x: x / 18.0, - }, - "glucose_mmol_l": { - "target_name": "glucose_mg_dl", - "target_unit": "mg/dL", - "conversion": lambda x: x * 18.0, - }, - "creatinine_mg_dl": { - "target_name": "creatinine_umol_l", - "target_unit": "umol/L", - "conversion": lambda x: x * 88.4, - }, - "creatinine_umol_l": { - "target_name": "creatinine_mg_dl", - "target_unit": "mg/dL", - "conversion": lambda x: x / 88.4, - }, - "albumin_g_dl": { - "target_name": "albumin_g_l", - "target_unit": "g/L", - "conversion": lambda x: x * 10.0, - }, - "albumin_g_l": { - "target_name": "albumin_g_dl", - "target_unit": "g/dL", - "conversion": lambda x: x / 10.0, - }, - "crp_mg_l": { - "target_name": "crp_mg_dl", - "target_unit": "mg/dL", - "conversion": lambda x: x / 10.0, - }, - "crp_mg_dl": { - "target_name": "crp_mg_l", - "target_unit": "mg/L", - "conversion": lambda x: x * 10.0, - }, + # Deep copy to avoid modifying original + result = {k: v.copy() for k, v in biomarkers.items()} + + # Conversion mappings: biomarker -> [(source_unit, target_unit, conversion_func)] + conversions: dict[str, list[tuple[str, str, Callable]]] = { + "glucose": [ + ("mg/dL", "mmol/L", lambda x: x / 18.0), + ("mmol/L", "mg/dL", lambda x: x * 18.0), + ], + "creatinine": [ + ("mg/dL", "umol/L", lambda x: x * 88.4), + ("umol/L", "mg/dL", lambda x: x / 88.4), + ], + "albumin": [ + ("g/dL", "g/L", lambda x: x * 10.0), + ("g/L", "g/dL", lambda x: x / 10.0), + ], + "crp": [ + ("mg/L", "mg/dL", lambda x: x / 10.0), + ("mg/dL", "mg/L", lambda x: x * 10.0), + ], } # Add converted entries - for source_name, conversion_info in conversions.items(): - if source_name in biomarkers: - source_value = biomarkers[source_name]["value"] - target_name = conversion_info["target_name"] - - # Skip if target already exists - if target_name not in result: - converted_value = conversion_info["conversion"](source_value) - result[target_name] = { - "value": round(converted_value, 4), - "unit": conversion_info["target_unit"], - } + for biomarker_name, conversion_list in conversions.items(): + if biomarker_name in result: + for source_unit, target_unit, conversion_func in conversion_list: + if ( + source_unit in result[biomarker_name] + and target_unit not in result[biomarker_name] + ): + source_value = result[biomarker_name][source_unit] + converted_value = round(conversion_func(source_value), 4) + result[biomarker_name][target_unit] = converted_value return result -def extract_biomarkers_from_json( - filepath: str | Path, +def validate_biomarkers_for_algorithm( + raw_biomarkers: dict[str, Any], biomarker_class: type[Biomarkers], biomarker_units: Units, -) -> Biomarkers: +) -> Biomarkers | None: """ - Generic function to extract biomarkers from JSON file based on a Pydantic model. + Validate if all required biomarkers are available for a specific algorithm. Args: - filepath: Path to JSON file containing biomarker data - model_class: Pydantic model class defining required biomarkers - units_model: Pydantic model instance containing expected units + raw_biomarkers: Dictionary containing biomarker data with unit-keyed values + biomarker_class: Pydantic model class defining required biomarkers + biomarker_units: Pydantic model instance containing expected units Returns: - Instance of model_class with extracted biomarker values - - Raises: - ValueError: If required biomarker is not found with expected unit + Instance of biomarker_class with extracted biomarker values if all required + biomarkers are present, None if any are missing """ - import json - - with open(filepath) as f: - data = json.load(f) - - raw_biomarkers = data.get("raw_biomarkers", {}) expected_units_dict = biomarker_units.model_dump() - - # Get required biomarker field names from model required_fields = biomarker_class.model_fields - extracted_values = {} - for field_name in required_fields: - expected_unit = expected_units_dict.get(field_name) - if expected_unit is None: - raise ValueError(f"No expected unit defined for {field_name}") + # Build biomarkers dictionary using comprehension + biomarkers_for_scoring = { + field: raw_biomarkers.get(field, {}).get(expected_units_dict[field], None) + for field in required_fields + } - value = find_biomarker_value(raw_biomarkers, field_name, expected_unit) + # Check for missing biomarkers and return None if any are missing + for field, value in biomarkers_for_scoring.items(): if value is None: - raise ValueError( - f"Could not find {field_name} biomarker with unit {expected_unit}" - ) - extracted_values[field_name] = value + return None - return biomarker_class(**extracted_values) + return biomarker_class(**biomarkers_for_scoring) def determine_risk_category(age: float, calibrated_risk: float) -> RiskCategory: diff --git a/vitals/biomarkers/io.py b/vitals/biomarkers/io.py index 8a22037..fb5b9e7 100755 --- a/vitals/biomarkers/io.py +++ b/vitals/biomarkers/io.py @@ -15,11 +15,8 @@ def update(input_file: Path) -> dict[str, Any]: with open(input_file) as f: data = json.load(f) - # Update biomarker names with unit suffixes + # Add converted biomarkers if "raw_biomarkers" in data: - data["raw_biomarkers"] = helpers.update_biomarker_names(data["raw_biomarkers"]) - - # Add converted biomarkers data["raw_biomarkers"] = helpers.add_converted_biomarkers( data["raw_biomarkers"] ) diff --git a/vitals/models/phenoage.py b/vitals/models/phenoage.py index 7454608..04d2180 100755 --- a/vitals/models/phenoage.py +++ b/vitals/models/phenoage.py @@ -1,54 +1,36 @@ -from pathlib import Path - import numpy as np from vitals.biomarkers import helpers -from vitals.schemas.phenoage import Gompertz, LinearModel, Markers, Units +from vitals.schemas.phenoage import Gompertz, LinearModel, Markers -def compute(filepath: str | Path) -> tuple[float, float, float]: +def compute(biomarkers: Markers) -> tuple[float, float, float]: """ The Phenoage score is calculated as a weighted (coefficients available in Levine et al 2018) linear combination of these variables, which was then transformed into units of years using 2 parametric (Gompertz distribution) proportional hazard models—one for the linearly combined score for all 10 variables and another for chronological age. Thus, PhenoAge represents the expected age within the population that - corresponds to a person’s estimated hazard of mortality as a function of his/her biological profile. + corresponds to a person's estimated hazard of mortality as a function of his/her biological profile. """ - # Extract biomarkers from JSON file - biomarkers = helpers.extract_biomarkers_from_json( - filepath=filepath, - biomarker_class=Markers, - biomarker_units=Units(), - ) - age = biomarkers.age - coef = LinearModel() + age: float = biomarkers.age + coef: LinearModel = LinearModel() - if isinstance(biomarkers, Markers): - weighted_risk_score = ( - coef.intercept - + (coef.albumin * biomarkers.albumin) - + (coef.creatinine * biomarkers.creatinine) - + (coef.glucose * biomarkers.glucose) - + (coef.log_crp * np.log(biomarkers.crp)) - + (coef.lymphocyte_percent * biomarkers.lymphocyte_percent) - + (coef.mean_cell_volume * biomarkers.mean_cell_volume) - + ( - coef.red_cell_distribution_width - * biomarkers.red_cell_distribution_width - ) - + (coef.alkaline_phosphatase * biomarkers.alkaline_phosphatase) - + (coef.white_blood_cell_count * biomarkers.white_blood_cell_count) - + (coef.age * biomarkers.age) - ) - gompertz = helpers.gompertz_mortality_model( - weighted_risk_score=weighted_risk_score - ) - model = Gompertz() - pred_age = ( - model.coef1 + np.log(model.coef2 * np.log(1 - gompertz)) / model.coef3 - ) - accl_age = pred_age - age - return (age, pred_age, accl_age) - else: - raise ValueError(f"Invalid biomarker class used: {biomarkers}") + weighted_risk_score = ( + coef.intercept + + (coef.albumin * biomarkers.albumin) + + (coef.creatinine * biomarkers.creatinine) + + (coef.glucose * biomarkers.glucose) + + (coef.log_crp * np.log(biomarkers.crp)) + + (coef.lymphocyte_percent * biomarkers.lymphocyte_percent) + + (coef.mean_cell_volume * biomarkers.mean_cell_volume) + + (coef.red_cell_distribution_width * biomarkers.red_cell_distribution_width) + + (coef.alkaline_phosphatase * biomarkers.alkaline_phosphatase) + + (coef.white_blood_cell_count * biomarkers.white_blood_cell_count) + + (coef.age * biomarkers.age) + ) + gompertz = helpers.gompertz_mortality_model(weighted_risk_score=weighted_risk_score) + model = Gompertz() + pred_age = model.coef1 + np.log(model.coef2 * np.log(1 - gompertz)) / model.coef3 + accl_age = pred_age - age + return (age, pred_age, accl_age) diff --git a/vitals/models/score2.py b/vitals/models/score2.py index 51ca1e3..6c96103 100644 --- a/vitals/models/score2.py +++ b/vitals/models/score2.py @@ -5,8 +5,6 @@ in apparently healthy individuals aged 40-69 years in Europe. """ -from pathlib import Path - import numpy as np from vitals.biomarkers import helpers @@ -16,12 +14,11 @@ FemaleCoefficientsBaseModel, MaleCoefficientsBaseModel, Markers, - Units, ) def compute( - filepath: str | Path, + biomarkers: Markers, ) -> tuple[float, float, helpers.RiskCategory]: """ Calculate the 10-year cardiovascular disease risk using the SCORE2 algorithm. @@ -31,27 +28,15 @@ def compute( coefficients and applies regional calibration for Belgium (Low Risk region). Args: - filepath: Path to JSON file containing biomarker data including age, sex, - systolic blood pressure, total cholesterol, HDL cholesterol, and smoking status. + biomarkers: Validated Markers object containing age, sex, systolic blood pressure, + total cholesterol, HDL cholesterol, and smoking status. Returns: A tuple containing: - age: The patient's chronological age - risk_percentage: The calibrated 10-year CVD risk as a percentage - risk_category: Risk stratification category ("Low to moderate", "High", or "Very high") - - Raises: - ValueError: If invalid biomarker class is used """ - # Extract biomarkers from JSON file - biomarkers = helpers.extract_biomarkers_from_json( - filepath=filepath, - biomarker_class=Markers, - biomarker_units=Units(), - ) - - if not isinstance(biomarkers, Markers): - raise ValueError(f"Invalid biomarker class used: {biomarkers}") age: float = biomarkers.age is_male: bool = biomarkers.is_male # True for male, False for female diff --git a/vitals/models/score2_diabetes.py b/vitals/models/score2_diabetes.py index 47aa158..226b0cc 100644 --- a/vitals/models/score2_diabetes.py +++ b/vitals/models/score2_diabetes.py @@ -6,7 +6,6 @@ """ import math -from pathlib import Path import numpy as np @@ -14,15 +13,14 @@ from vitals.schemas.score2 import ( BaselineSurvival, CalibrationScales, + DiabetesMarkers, FemaleCoefficientsDiabeticModel, MaleCoefficientsDiabeticModel, - MarkersDiabetes, - UnitsDiabetes, ) def compute( - filepath: str | Path, + biomarkers: DiabetesMarkers, ) -> tuple[float, float, helpers.RiskCategory]: """ Calculate the 10-year cardiovascular disease risk using the SCORE2-Diabetes algorithm. @@ -32,28 +30,15 @@ def compute( calibration for Belgium (Low Risk region). Args: - filepath: Path to JSON file containing biomarker data including age, sex, - systolic blood pressure, total cholesterol, HDL cholesterol, smoking status, - diabetes status, age at diabetes diagnosis, HbA1c, and eGFR. + biomarkers: DiabetesMarkers object with guaranteed non-None diabetes-specific fields + (diabetes status, age at diabetes diagnosis, HbA1c, and eGFR). Returns: A tuple containing: - age: The patient's chronological age - risk_percentage: The calibrated 10-year CVD risk as a percentage - risk_category: Risk stratification category ("Low to moderate", "High", or "Very high") - - Raises: - ValueError: If invalid biomarker class is used """ - # Extract biomarkers from JSON file - biomarkers = helpers.extract_biomarkers_from_json( - filepath=filepath, - biomarker_class=MarkersDiabetes, - biomarker_units=UnitsDiabetes(), - ) - - if not isinstance(biomarkers, MarkersDiabetes): - raise ValueError(f"Invalid biomarker class used: {biomarkers}") age: float = biomarkers.age is_male: bool = biomarkers.is_male # True for male, False for female diff --git a/vitals/schemas/score2.py b/vitals/schemas/score2.py index 336d8e0..8ce2d69 100644 --- a/vitals/schemas/score2.py +++ b/vitals/schemas/score2.py @@ -1,3 +1,5 @@ +from typing import Optional + from pydantic import BaseModel # Common for all models @@ -44,6 +46,39 @@ class Markers(BaseModel): hdl_cholesterol: float smoking: bool is_male: bool + diabetes: bool | None + age_at_diabetes_diagnosis: float | None + hba1c: float | None + egfr: float | None + + +class DiabetesMarkers(Markers): + """Markers with guaranteed non-None diabetes fields for SCORE2-Diabetes algorithm.""" + + # Override the optional diabetes fields to be required + diabetes: bool + age_at_diabetes_diagnosis: float + hba1c: float + egfr: float + + @classmethod + def try_from_markers(cls, markers: Markers) -> Optional["DiabetesMarkers"]: + """Factory method to safely create DiabetesMarkers from unified Markers. + + Args: + markers: Unified Markers instance with potentially None diabetes fields + + Returns: + DiabetesMarkers instance if all diabetes fields are present and not None, + None otherwise + """ + marker_dict = markers.model_dump() + + # Check if all diabetes fields are present and not None + diabetes_fields = ["diabetes", "age_at_diabetes_diagnosis", "hba1c", "egfr"] + if all(marker_dict.get(field) is not None for field in diabetes_fields): + return cls(**marker_dict) + return None class Units(BaseModel): @@ -57,6 +92,10 @@ class Units(BaseModel): hdl_cholesterol: str = "mmol/L" smoking: str = "yes/no" is_male: str = "yes/no" + diabetes: str = "yes/no" + age_at_diabetes_diagnosis: str = "years" + hba1c: str = "mmol/mol" + egfr: str = "mL/min/1.73m²" class MaleCoefficientsBaseModel(BaseModel): @@ -100,36 +139,36 @@ class FemaleCoefficientsBaseModel(BaseModel): # ----- For Diabetic score2 model -class MarkersDiabetes(BaseModel): - """Processed Score2-Diabetes biomarkers with standardized units.""" - - age: float - systolic_blood_pressure: float - total_cholesterol: float - hdl_cholesterol: float - smoking: bool - is_male: bool - diabetes: bool - age_at_diabetes_diagnosis: float - hba1c: float - egfr: float - - -class UnitsDiabetes(BaseModel): - """ - The expected unit to be used for Score2-Diabetes computation - """ - - age: str = "years" - systolic_blood_pressure: str = "mmHg" - total_cholesterol: str = "mmol/L" - hdl_cholesterol: str = "mmol/L" - smoking: str = "yes/no" - is_male: str = "yes/no" - diabetes: str = "yes/no" - age_at_diabetes_diagnosis: str = "years" - hba1c: str = "mmol/mol" - egfr: str = "mL/min/1.73m²" +# class MarkersDiabetes(BaseModel): +# """Processed Score2-Diabetes biomarkers with standardized units.""" + +# age: float +# systolic_blood_pressure: float +# total_cholesterol: float +# hdl_cholesterol: float +# smoking: bool +# is_male: bool +# diabetes: bool +# age_at_diabetes_diagnosis: float +# hba1c: float +# egfr: float + + +# class UnitsDiabetes(BaseModel): +# """ +# The expected unit to be used for Score2-Diabetes computation +# """ + +# age: str = "years" +# systolic_blood_pressure: str = "mmHg" +# total_cholesterol: str = "mmol/L" +# hdl_cholesterol: str = "mmol/L" +# smoking: str = "yes/no" +# is_male: str = "yes/no" +# diabetes: str = "yes/no" +# age_at_diabetes_diagnosis: str = "years" +# hba1c: str = "mmol/mol" +# egfr: str = "mL/min/1.73m²" class MaleCoefficientsDiabeticModel(MaleCoefficientsBaseModel):