Skill: nomadrex/genome-report (version 1.0.0)

--- name: genome-report description: Analyze 23andMe raw genome data to generate comprehensive health, trait, and family comparison reports. Supports cardiovascular, cognitive, metabolic, pharmacogenomic, athletic, and ancestry analysis. Use when user asks to analyze genome data, 23andMe files, genetic reports, SNP analysis, health risk from DNA, or family genetic comparison. --- # Genome Report Analyze 23andMe v5 raw data files and generate health/trait reports with risk scoring. ## Usage ```bash python3 skills/genome-report/scripts/genome_report.py <genome_file.txt> [options] ``` ### Options | Flag | Values | Default | Description | |------|--------|---------|-------------| | `--category` | `all\|cardio\|cognitive\|metabolic\|pharma\|athletic\|traits` | `all` | Category filter | | `--output` | `text\|json\|html` | `text` | Output format | | `--output-path` | filepath | auto | HTML output path | | `--family DIR` | directory path | — | Family comparison mode | ### Examples ```bash # Full report to console python3 skills/genome-report/scripts/genome_report.py ~/my_genome.txt # HTML report for one category python3 skills/genome-report/scripts/genome_report.py ~/my_genome.txt --category cardio --output html # Family comparison python3 skills/genome-report/scripts/genome_report.py --family ~/genomes/ --output html ``` ## Input Format 23andMe v5 raw data (tab-separated): `rsid chromosome position genotype` Lines starting with `#` are skipped. Genotypes marked `--` (no-call) are excluded. ## Output - **Text**: Console output with risk bars and color-coded SNP details - **JSON**: Structured data with scores and per-SNP results - **HTML**: Styled report with risk score cards and color-coded tables ## Categories & Coverage ~55 curated SNPs across 6 categories: - **Cardiovascular** — blood pressure, cholesterol, cardiac rhythm, CAD risk - **Cognitive** — memory, dopamine, BDNF, social cognition, brain volume - **Metabolic** — diabetes risk, MTHFR, lactose, alcohol, iron metabolism - **Pharmacogenomics** — warfarin, clopidogrel, CYP2D6, CYP2C19 drug metabolism - **Athletic** — muscle fiber type, endurance, recovery, injury risk - **Traits** — eye color, hair color, earwax, bitter taste, asparagus smell ## SNP Database The SNP reference data lives in `references/snp_database.json`. Edit this file to add/update SNPs without changing the script. Each entry has: rsid, gene, category, trait, risk_allele, and genotype-specific effect descriptions. ## Constraints - Pure Python 3.9+ — no external dependencies - **Not medical advice** — educational/informational only

path: references/snp_database.json
content: {
  "snps": [
    {"rsid": "rs699", "gene": "AGT", "category": "cardiovascular", "trait": "Angiotensinogen - blood pressure regulation", "risk_allele": "C", "genotypes": {"CC": "Higher angiotensinogen levels; increased hypertension risk", "CT": "Moderately elevated angiotensinogen; mild hypertension risk", "TT": "Normal angiotensinogen levels"}},
    {"rsid": "rs4961", "gene": "ADD1", "category": "cardiovascular", "trait": "Alpha-adducin - salt sensitivity", "risk_allele": "T", "genotypes": {"TT": "Salt-sensitive hypertension risk", "GT": "Moderate salt sensitivity", "GG": "Normal salt sensitivity"}},
    {"rsid": "rs4343", "gene": "ACE", "category": "cardiovascular", "trait": "ACE activity - cardiovascular risk", "risk_allele": "G", "genotypes": {"GG": "Higher ACE activity; increased cardiovascular risk", "AG": "Moderate ACE activity", "AA": "Normal ACE activity"}},
    {"rsid": "rs515135", "gene": "APOB", "category": "cardiovascular", "trait": "LDL cholesterol levels", "risk_allele": "T", "genotypes": {"TT": "Higher LDL cholesterol", "CT": "Moderately elevated LDL", "CC": "Normal LDL levels"}},
    {"rsid": "rs562338", "gene": "APOB", "category": "cardiovascular", "trait": "Apolipoprotein B levels", "risk_allele": "A", "genotypes": {"AA": "Higher ApoB levels", "AG": "Moderately elevated ApoB", "GG": "Normal ApoB levels"}},
    {"rsid": "rs7193343", "gene": "ZFHX3", "category": "cardiovascular", "trait": "Atrial fibrillation risk", "risk_allele": "T", "genotypes": {"TT": "Increased atrial fibrillation risk", "CT": "Moderate AF risk", "CC": "Normal AF risk"}},
    {"rsid": "rs1260326", "gene": "GCKR", "category": "cardiovascular", "trait": "Triglyceride levels", "risk_allele": "T", "genotypes": {"TT": "Higher triglycerides", "CT": "Moderately elevated triglycerides", "CC": "Normal triglyceride levels"}},
    {"rsid": "rs780094", "gene": "GCKR", "category": "cardiovascular", "trait": "Fasting glucose and triglycerides", "risk_allele": "T", "genotypes": {"TT": "Higher fasting glucose/triglycerides", "CT": "Moderate elevation", "CC": "Normal levels"}},
    {"rsid": "rs10494366", "gene": "NOS1AP", "category": "cardiovascular", "trait": "QT interval - cardiac rhythm", "risk_allele": "T", "genotypes": {"TT": "Prolonged QT interval risk", "GT": "Mildly prolonged QT", "GG": "Normal QT interval"}},
    {"rsid": "rs10757274", "gene": "9p21", "category": "cardiovascular", "trait": "Coronary artery disease risk", "risk_allele": "G", "genotypes": {"GG": "1.6x increased CAD risk", "AG": "1.3x increased CAD risk", "AA": "Normal CAD risk"}},
    {"rsid": "rs1333049", "gene": "9p21", "category": "cardiovascular", "trait": "Myocardial infarction risk", "risk_allele": "C", "genotypes": {"CC": "Increased MI risk", "CG": "Moderate MI risk", "GG": "Normal MI risk"}},
    {"rsid": "rs1801133", "gene": "MTHFR", "category": "cardiovascular", "trait": "Homocysteine metabolism (C677T)", "risk_allele": "A", "genotypes": {"AA": "70% reduced enzyme activity; elevated homocysteine", "AG": "35% reduced activity; mildly elevated homocysteine", "GG": "Normal MTHFR activity"}},
    {"rsid": "rs363050", "gene": "SNAP25", "category": "cognitive", "trait": "Synaptic plasticity and IQ", "risk_allele": "G", "genotypes": {"GG": "Associated with higher IQ scores", "AG": "Moderate cognitive benefit", "AA": "Typical cognitive profile"}},
    {"rsid": "rs2760118", "gene": "ALDH5A1", "category": "cognitive", "trait": "SSADH - GABA metabolism and cognition", "risk_allele": "T", "genotypes": {"TT": "Enhanced GABA metabolism; cognitive benefit", "CT": "Moderate effect", "CC": "Typical GABA metabolism"}},
    {"rsid": "rs6265", "gene": "BDNF", "category": "cognitive", "trait": "Brain-derived neurotrophic factor (Val66Met)", "risk_allele": "T", "genotypes": {"TT": "Met/Met: reduced BDNF secretion; memory concerns", "CT": "Val/Met: moderate BDNF reduction", "CC": "Val/Val: normal BDNF secretion"}},
    {"rsid": "rs4680", "gene": "COMT", "category": "cognitive", "trait": "Dopamine metabolism (Val158Met)", "risk_allele": "A", "genotypes": {"AA": "Met/Met: higher prefrontal dopamine; better working memory, more stress sensitivity", "AG": "Val/Met: balanced dopamine metabolism", "GG": "Val/Val: lower prefrontal dopamine; stress resilient, lower baseline cognition"}},
    {"rsid": "rs4851266", "gene": "KIBRA", "category": "cognitive", "trait": "Episodic memory performance", "risk_allele": "T", "genotypes": {"TT": "Enhanced episodic memory", "CT": "Moderate memory benefit", "CC": "Typical memory performance"}},
    {"rsid": "rs17070145", "gene": "KIBRA", "category": "cognitive", "trait": "Memory consolidation", "risk_allele": "T", "genotypes": {"TT": "Better memory consolidation", "CT": "Moderate benefit", "CC": "Typical memory consolidation"}},
    {"rsid": "rs1800497", "gene": "DRD2/ANKK1", "category": "cognitive", "trait": "Dopamine receptor density (Taq1A)", "risk_allele": "T", "genotypes": {"TT": "Reduced D2 receptor density; reward sensitivity", "CT": "Moderate D2 reduction", "CC": "Normal D2 receptor density"}},
    {"rsid": "rs53576", "gene": "OXTR", "category": "cognitive", "trait": "Oxytocin receptor - empathy and social behavior", "risk_allele": "A", "genotypes": {"AA": "Lower empathy scores; reduced social sensitivity", "AG": "Moderate social sensitivity", "GG": "Higher empathy; better social cognition"}},
    {"rsid": "rs7294919", "gene": "TESC", "category": "cognitive", "trait": "Hippocampal volume", "risk_allele": "T", "genotypes": {"TT": "Smaller hippocampal volume", "CT": "Moderate reduction", "CC": "Larger hippocampal volume"}},
    {"rsid": "rs10784502", "gene": "HMGA2", "category": "cognitive", "trait": "Intracranial volume and cognition", "risk_allele": "C", "genotypes": {"CC": "Larger intracranial volume", "CT": "Moderate effect", "TT": "Typical intracranial volume"}},
    {"rsid": "rs10119", "gene": "APOE-region", "category": "cognitive", "trait": "Alzheimer's disease risk proxy", "risk_allele": "T", "genotypes": {"TT": "Elevated Alzheimer's risk", "CT": "Moderate risk", "CC": "Lower Alzheimer's risk"}},
    {"rsid": "rs1801282", "gene": "PPARG", "category": "metabolic", "trait": "Insulin sensitivity (Pro12Ala)", "risk_allele": "C", "genotypes": {"CC": "Pro/Pro: higher type 2 diabetes risk", "CG": "Pro/Ala: improved insulin sensitivity", "GG": "Ala/Ala: best insulin sensitivity"}},
    {"rsid": "rs5219", "gene": "KCNJ11", "category": "metabolic", "trait": "Potassium channel - insulin secretion (E23K)", "risk_allele": "T", "genotypes": {"TT": "Reduced insulin secretion; T2D risk", "CT": "Moderate effect", "CC": "Normal insulin secretion"}},
    {"rsid": "rs7903146", "gene": "TCF7L2", "category": "metabolic", "trait": "Strongest T2D genetic risk factor", "risk_allele": "T", "genotypes": {"TT": "1.8x increased T2D risk", "CT": "1.4x increased T2D risk", "CC": "Normal T2D risk"}},
    {"rsid": "rs1801131", "gene": "MTHFR", "category": "metabolic", "trait": "Folate metabolism (A1298C)", "risk_allele": "G", "genotypes": {"GG": "Reduced MTHFR activity; impaired folate metabolism", "GT": "Mildly reduced activity", "TT": "Normal folate metabolism"}},
    {"rsid": "rs4988235", "gene": "LCT/MCM6", "category": "metabolic", "trait": "Lactose tolerance", "risk_allele": "G", "genotypes": {"GG": "Lactose intolerant (ancestral)", "AG": "Lactose tolerant (one persistence allele)", "AA": "Lactose tolerant (full persistence)"}},
    {"rsid": "rs1229984", "gene": "ADH1B", "category": "metabolic", "trait": "Alcohol metabolism", "risk_allele": "T", "genotypes": {"TT": "Ultra-rapid alcohol metabolism; flush reaction; protective against alcoholism", "CT": "Faster alcohol metabolism", "CC": "Normal alcohol metabolism"}},
    {"rsid": "rs1800562", "gene": "HFE", "category": "metabolic", "trait": "Hereditary hemochromatosis (C282Y)", "risk_allele": "A", "genotypes": {"AA": "Homozygous: high hemochromatosis risk", "AG": "Carrier: mild iron elevation", "GG": "Normal iron metabolism"}},
    {"rsid": "rs855791", "gene": "TMPRSS6", "category": "metabolic", "trait": "Iron levels and anemia risk", "risk_allele": "T", "genotypes": {"TT": "Lower iron/hemoglobin; anemia risk", "CT": "Mildly lower iron", "CC": "Normal iron levels"}},
    {"rsid": "rs662799", "gene": "APOA5", "category": "metabolic", "trait": "Triglyceride metabolism", "risk_allele": "G", "genotypes": {"GG": "Significantly elevated triglycerides", "AG": "Moderately elevated triglycerides", "AA": "Normal triglyceride metabolism"}},
    {"rsid": "rs1799853", "gene": "CYP2C9", "category": "pharma", "trait": "Warfarin sensitivity (*2 allele)", "risk_allele": "T", "genotypes": {"TT": "Poor metabolizer: significantly reduced warfarin dose needed", "CT": "Intermediate metabolizer: reduced dose needed", "CC": "Normal metabolizer"}},
    {"rsid": "rs1057910", "gene": "CYP2C9", "category": "pharma", "trait": "Warfarin sensitivity (*3 allele)", "risk_allele": "C", "genotypes": {"CC": "Poor metabolizer: major warfarin dose reduction", "AC": "Intermediate metabolizer", "AA": "Normal metabolizer"}},
    {"rsid": "rs9923231", "gene": "VKORC1", "category": "pharma", "trait": "Warfarin dose requirement", "risk_allele": "T", "genotypes": {"TT": "Low-dose warfarin needed (1-2mg/day)", "CT": "Intermediate warfarin dose", "CC": "Standard warfarin dose (5-7mg/day)"}},
    {"rsid": "rs4244285", "gene": "CYP2C19", "category": "pharma", "trait": "Clopidogrel activation (*2 allele)", "risk_allele": "A", "genotypes": {"AA": "Poor metabolizer: clopidogrel ineffective", "AG": "Intermediate metabolizer: reduced efficacy", "GG": "Normal metabolizer: clopidogrel effective"}},
    {"rsid": "rs4986893", "gene": "CYP2C19", "category": "pharma", "trait": "Clopidogrel activation (*3 allele)", "risk_allele": "A", "genotypes": {"AA": "Poor metabolizer: clopidogrel ineffective", "AG": "Intermediate metabolizer", "GG": "Normal metabolizer"}},
    {"rsid": "rs12248560", "gene": "CYP2C19", "category": "pharma", "trait": "CYP2C19 ultra-rapid metabolizer (*17)", "risk_allele": "T", "genotypes": {"TT": "Ultra-rapid metabolizer: increased drug clearance", "CT": "Rapid metabolizer", "CC": "Normal metabolizer"}},
    {"rsid": "rs1065852", "gene": "CYP2D6", "category": "pharma", "trait": "Drug metabolism (codeine, tamoxifen, many antidepressants)", "risk_allele": "A", "genotypes": {"AA": "Poor metabolizer: many drugs ineffective or toxic", "AG": "Intermediate metabolizer", "GG": "Normal metabolizer"}},
    {"rsid": "rs3892097", "gene": "CYP2D6", "category": "pharma", "trait": "CYP2D6*4 - most common poor metabolizer allele", "risk_allele": "A", "genotypes": {"AA": "Poor metabolizer", "AG": "Intermediate metabolizer", "GG": "Normal/extensive metabolizer"}},
    {"rsid": "rs776746", "gene": "CYP3A5", "category": "pharma", "trait": "Tacrolimus and many drug metabolism", "risk_allele": "T", "genotypes": {"TT": "CYP3A5 expresser: faster metabolism, higher doses needed", "CT": "Intermediate", "CC": "Non-expresser: standard dosing"}},
    {"rsid": "rs1815739", "gene": "ACTN3", "category": "athletic", "trait": "Muscle fiber type (R577X)", "risk_allele": "T", "genotypes": {"TT": "XX: no alpha-actinin-3; endurance advantage", "CT": "RX: mixed fiber type", "CC": "RR: fast-twitch dominant; sprint/power advantage"}},
    {"rsid": "rs8192678", "gene": "PPARGC1A", "category": "athletic", "trait": "Endurance capacity (Gly482Ser)", "risk_allele": "A", "genotypes": {"AA": "Ser/Ser: reduced mitochondrial biogenesis", "AG": "Gly/Ser: moderate endurance", "GG": "Gly/Gly: enhanced endurance capacity"}},
    {"rsid": "rs1042713", "gene": "ADRB2", "category": "athletic", "trait": "Beta-2 adrenergic receptor - exercise response", "risk_allele": "G", "genotypes": {"GG": "Arg/Arg: better bronchodilation response", "AG": "Intermediate", "AA": "Gly/Gly: reduced exercise bronchodilation"}},
    {"rsid": "rs4994", "gene": "ADRB3", "category": "athletic", "trait": "Fat metabolism during exercise", "risk_allele": "T", "genotypes": {"TT": "Reduced lipolysis during exercise", "CT": "Moderate effect", "CC": "Normal fat mobilization during exercise"}},
    {"rsid": "rs1800795", "gene": "IL6", "category": "athletic", "trait": "Recovery and inflammation", "risk_allele": "G", "genotypes": {"GG": "Higher IL-6 response; slower recovery", "GC": "Moderate inflammatory response", "CC": "Lower IL-6; faster recovery"}},
    {"rsid": "rs7460", "gene": "COL5A1", "category": "athletic", "trait": "Tendon and ligament injury risk", "risk_allele": "T", "genotypes": {"TT": "Higher injury risk", "CT": "Moderate risk", "CC": "Lower injury risk"}},
    {"rsid": "rs1426654", "gene": "SLC24A5", "category": "traits", "trait": "Skin pigmentation", "risk_allele": "A", "genotypes": {"AA": "Lighter skin pigmentation (European variant)", "AG": "Intermediate pigmentation", "GG": "Darker skin pigmentation (ancestral)"}},
    {"rsid": "rs12913832", "gene": "HERC2/OCA2", "category": "traits", "trait": "Eye color", "risk_allele": "G", "genotypes": {"GG": "Blue/green eyes (77% probability)", "AG": "Green/hazel eyes likely", "AA": "Brown eyes (dominant)"}},
    {"rsid": "rs1805007", "gene": "MC1R", "category": "traits", "trait": "Red hair and fair skin", "risk_allele": "T", "genotypes": {"TT": "Red hair likely; very fair skin; sun sensitivity", "CT": "Carrier: may have reddish tints; increased sun sensitivity", "CC": "Non-carrier: typical hair color range"}},
    {"rsid": "rs4481887", "gene": "OR2M7", "category": "traits", "trait": "Asparagus anosmia (smell detection)", "risk_allele": "A", "genotypes": {"AA": "Cannot smell asparagus metabolites in urine", "AG": "Reduced ability to detect asparagus smell", "GG": "Can smell asparagus metabolites"}},
    {"rsid": "rs713598", "gene": "TAS2R38", "category": "traits", "trait": "Bitter taste perception (PTC/PROP)", "risk_allele": "G", "genotypes": {"GG": "Non-taster: cannot taste bitter compounds", "CG": "Moderate bitter taste perception", "CC": "Super-taster: strong bitter taste perception"}},
    {"rsid": "rs7495174", "gene": "OCA2", "category": "traits", "trait": "Eye color modifier", "risk_allele": "A", "genotypes": {"AA": "Increased probability of blue/green eyes", "AG": "Intermediate effect", "GG": "Brown eyes more likely"}},
    {"rsid": "rs1800407", "gene": "OCA2", "category": "traits", "trait": "Eye color - green/hazel modifier", "risk_allele": "T", "genotypes": {"TT": "Green/hazel eyes more likely", "CT": "Moderate effect", "CC": "Less likely green/hazel"}},
    {"rsid": "rs17822931", "gene": "ABCC11", "category": "traits", "trait": "Earwax type and body odor", "risk_allele": "C", "genotypes": {"CC": "Wet earwax; typical body odor (Western type)", "CT": "Intermediate", "TT": "Dry earwax; reduced body odor (East Asian type)"}},
    {"rsid": "rs4680", "gene": "COMT", "category": "traits", "trait": "Pain sensitivity", "risk_allele": "A", "genotypes": {"AA": "Met/Met: higher pain sensitivity", "AG": "Intermediate pain sensitivity", "GG": "Val/Val: lower pain sensitivity"}},
    {"rsid": "rs1805009", "gene": "MC1R", "category": "traits", "trait": "Red hair variant D294H", "risk_allele": "C", "genotypes": {"CC": "Red hair/fair skin variant", "CG": "Carrier", "GG": "Non-carrier"}}
  ]
}


path: scripts/genome_report.py
content: #!/usr/bin/env python3
"""
Genome Report — Analyze 23andMe raw data and generate health/trait reports.
Pure Python, no external dependencies. Python 3.9+.

DISCLAIMER: This is NOT medical advice. Results are for educational/informational
purposes only. Consult a healthcare professional for medical decisions.
"""

import argparse
import json
import os
import sys
from pathlib import Path
from datetime import datetime
from collections import defaultdict

SCRIPT_DIR = Path(__file__).parent
SNP_DB_PATH = SCRIPT_DIR.parent / "references" / "snp_database.json"

CATEGORY_LABELS = {
    "cardiovascular": "❤️ Cardiovascular",
    "cognitive": "🧠 Cognitive",
    "metabolic": "🔥 Metabolic",
    "pharma": "💊 Pharmacogenomics",
    "athletic": "🏃 Athletic",
    "traits": "🧬 Traits",
}

CATEGORY_ALIASES = {
    "cardio": "cardiovascular",
    "pharma": "pharma",
    "cognitive": "cognitive",
    "metabolic": "metabolic",
    "athletic": "athletic",
    "traits": "traits",
    "cardiovascular": "cardiovascular",
}


def load_snp_database():
    with open(SNP_DB_PATH) as f:
        data = json.load(f)
    db = {}
    for snp in data["snps"]:
        db[snp["rsid"]] = snp
    return db


def parse_23andme(filepath):
    """Parse 23andMe raw data file. Returns dict of rsid -> genotype."""
    genotypes = {}
    with open(filepath) as f:
        for line in f:
            line = line.strip()
            if not line or line.startswith("#"):
                continue
            parts = line.split("\t")
            if len(parts) >= 4:
                rsid, _chrom, _pos, genotype = parts[0], parts[1], parts[2], parts[3]
                if rsid.startswith("rs") and genotype != "--":
                    genotypes[rsid] = genotype
    return genotypes


def analyze_genome(genotypes, snp_db, categories=None):
    """Analyze genotypes against SNP database. Returns results by category."""
    results = defaultdict(list)
    
    for rsid, snp_info in snp_db.items():
        cat = snp_info["category"]
        if categories and cat not in categories:
            continue
        genotype = genotypes.get(rsid)
        if not genotype:
            continue
        
        # Normalize genotype for lookup (try both orientations)
        gt = genotype
        effect = snp_info["genotypes"].get(gt)
        if effect is None and len(gt) == 2:
            gt_rev = gt[1] + gt[0]
            effect = snp_info["genotypes"].get(gt_rev)
            if effect:
                gt = gt_rev
        
        # Count risk alleles
        risk_allele = snp_info.get("risk_allele", "")
        risk_count = genotype.count(risk_allele) if risk_allele else 0
        
        results[cat].append({
            "rsid": rsid,
            "gene": snp_info["gene"],
            "trait": snp_info["trait"],
            "genotype": genotype,
            "risk_allele": risk_allele,
            "risk_count": risk_count,
            "effect": effect or "Genotype not in database",
        })
    
    return dict(results)


def calculate_scores(results):
    """Calculate risk score (0-100) per category."""
    scores = {}
    for cat, snps in results.items():
        if not snps:
            scores[cat] = 0
            continue
        total_possible = len(snps) * 2  # max 2 risk alleles per SNP
        total_risk = sum(s["risk_count"] for s in snps)
        scores[cat] = round((total_risk / total_possible) * 100) if total_possible else 0
    return scores


def risk_color(score):
    if score < 30:
        return "green"
    elif score < 60:
        return "orange"
    return "red"


def risk_label(score):
    if score < 30:
        return "Low"
    elif score < 60:
        return "Moderate"
    return "Elevated"


# ── Output formatters ──

def output_text(results, scores, filename):
    print(f"\n{'='*60}")
    print(f"  GENOME REPORT — {Path(filename).stem}")
    print(f"  Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
    print(f"{'='*60}")
    print("\n⚠️  DISCLAIMER: This is NOT medical advice.\n")
    
    # Summary
    print("── RISK SCORE SUMMARY ──\n")
    for cat in sorted(scores, key=scores.get, reverse=True):
        label = CATEGORY_LABELS.get(cat, cat)
        score = scores[cat]
        bar = "█" * (score // 5) + "░" * (20 - score // 5)
        print(f"  {label:<30} {bar} {score:>3}% ({risk_label(score)})")
    
    # Details
    for cat, snps in sorted(results.items()):
        label = CATEGORY_LABELS.get(cat, cat)
        print(f"\n── {label} ──\n")
        for s in sorted(snps, key=lambda x: -x["risk_count"]):
            risk_indicator = "🔴" if s["risk_count"] == 2 else "🟡" if s["risk_count"] == 1 else "🟢"
            print(f"  {risk_indicator} {s['rsid']} ({s['gene']}) — {s['genotype']}")
            print(f"     {s['trait']}")
            print(f"     {s['effect']}")
            print()


def output_json(results, scores, filename):
    output = {
        "file": str(filename),
        "generated": datetime.now().isoformat(),
        "disclaimer": "This is NOT medical advice. For educational purposes only.",
        "scores": scores,
        "details": results,
    }
    print(json.dumps(output, indent=2))


def output_html(results, scores, filename, output_path=None):
    cats_html = ""
    for cat in sorted(scores, key=scores.get, reverse=True):
        label = CATEGORY_LABELS.get(cat, cat)
        score = scores[cat]
        color = risk_color(score)
        cats_html += f'<div class="score-card"><h3>{label}</h3><div class="score-bar"><div class="score-fill" style="width:{score}%;background:{color}"></div></div><span class="score-val">{score}% — {risk_label(score)}</span></div>\n'
    
    details_html = ""
    for cat, snps in sorted(results.items()):
        label = CATEGORY_LABELS.get(cat, cat)
        rows = ""
        for s in sorted(snps, key=lambda x: -x["risk_count"]):
            cls = "risk-high" if s["risk_count"] == 2 else "risk-med" if s["risk_count"] == 1 else "risk-low"
            rows += f'<tr class="{cls}"><td>{s["rsid"]}</td><td>{s["gene"]}</td><td>{s["genotype"]}</td><td>{s["trait"]}</td><td>{s["effect"]}</td></tr>\n'
        details_html += f'<h2>{label}</h2><table><tr><th>SNP</th><th>Gene</th><th>Genotype</th><th>Trait</th><th>Effect</th></tr>{rows}</table>\n'
    
    html = f"""<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Genome Report — {Path(filename).stem}</title>
<style>
body{{font-family:system-ui,sans-serif;max-width:960px;margin:0 auto;padding:20px;background:#f5f5f5;color:#333}}
h1{{color:#2c3e50}}
.disclaimer{{background:#fff3cd;border:1px solid #ffc107;padding:12px;border-radius:6px;margin:16px 0}}
.score-card{{background:white;padding:16px;border-radius:8px;margin:8px 0;box-shadow:0 1px 3px rgba(0,0,0,.1)}}
.score-bar{{background:#e0e0e0;border-radius:10px;height:20px;overflow:hidden;margin:8px 0}}
.score-fill{{height:100%;border-radius:10px;transition:width .3s}}
.score-val{{font-weight:600}}
table{{width:100%;border-collapse:collapse;background:white;border-radius:8px;overflow:hidden;margin:12px 0;box-shadow:0 1px 3px rgba(0,0,0,.1)}}
th{{background:#2c3e50;color:white;padding:10px;text-align:left}}
td{{padding:8px 10px;border-bottom:1px solid #eee}}
.risk-high{{background:#ffebee}} .risk-med{{background:#fff8e1}} .risk-low{{background:#e8f5e9}}
</style></head><body>
<h1>🧬 Genome Report — {Path(filename).stem}</h1>
<p>Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}</p>
<div class="disclaimer">⚠️ <strong>DISCLAIMER:</strong> This report is for educational and informational purposes only. It is NOT medical advice. Consult a healthcare professional before making any health decisions based on genetic data.</div>
<h2>Risk Score Summary</h2>
{cats_html}
{details_html}
</body></html>"""
    
    if output_path is None:
        output_path = Path(filename).stem + "_report.html"
    with open(output_path, "w") as f:
        f.write(html)
    print(f"HTML report saved to: {output_path}")


def family_comparison(genome_dir, snp_db, categories, output_format):
    """Compare multiple genome files in a directory."""
    genome_files = sorted(Path(genome_dir).glob("*.txt"))
    if not genome_files:
        print(f"No .txt genome files found in {genome_dir}")
        sys.exit(1)
    
    print(f"\nFound {len(genome_files)} genome file(s): {', '.join(f.stem for f in genome_files)}\n")
    
    all_results = {}
    all_scores = {}
    for gf in genome_files:
        genotypes = parse_23andme(gf)
        results = analyze_genome(genotypes, snp_db, categories)
        scores = calculate_scores(results)
        all_results[gf.stem] = results
        all_scores[gf.stem] = scores
    
    if output_format == "json":
        output = {
            "family_comparison": True,
            "generated": datetime.now().isoformat(),
            "disclaimer": "This is NOT medical advice.",
            "members": {name: {"scores": all_scores[name], "details": all_results[name]} for name in all_scores},
        }
        print(json.dumps(output, indent=2))
        return
    
    if output_format == "html":
        _family_html(all_results, all_scores, genome_dir)
        return
    
    # Text output
    print(f"{'='*60}")
    print("  FAMILY GENOME COMPARISON")
    print(f"{'='*60}\n")
    
    # Score comparison table
    members = list(all_scores.keys())
    all_cats = sorted(set(c for s in all_scores.values() for c in s))
    
    header = f"{'Category':<25}" + "".join(f"{m:<20}" for m in members)
    print(header)
    print("-" * len(header))
    for cat in all_cats:
        label = CATEGORY_LABELS.get(cat, cat)
        row = f"{label:<25}"
        for m in members:
            score = all_scores[m].get(cat, 0)
            row += f"{score:>3}% ({risk_label(score):<8})   "
        print(row)
    
    # Inheritance patterns
    if len(members) >= 2:
        print(f"\n── Shared Risk Alleles ──\n")
        # Find SNPs where all members share the same risk genotype
        snp_db_by_rsid = snp_db
        for rsid in snp_db_by_rsid:
            member_genotypes = {}
            for m in members:
                for cat_results in all_results[m].values():
                    for snp in cat_results:
                        if snp["rsid"] == rsid:
                            member_genotypes[m] = snp
            if len(member_genotypes) == len(members):
                risk_counts = [mg["risk_count"] for mg in member_genotypes.values()]
                if all(r >= 1 for r in risk_counts):
                    info = list(member_genotypes.values())[0]
                    gts = " / ".join(f"{m}:{member_genotypes[m]['genotype']}" for m in members)
                    print(f"  {info['rsid']} ({info['gene']}) — {info['trait']}")
                    print(f"    {gts}\n")


def _family_html(all_results, all_scores, genome_dir):
    members = list(all_scores.keys())
    all_cats = sorted(set(c for s in all_scores.values() for c in s))
    
    table_rows = ""
    for cat in all_cats:
        label = CATEGORY_LABELS.get(cat, cat)
        cells = f"<td><strong>{label}</strong></td>"
        for m in members:
            score = all_scores[m].get(cat, 0)
            color = risk_color(score)
            cells += f'<td><span style="color:{color};font-weight:600">{score}% ({risk_label(score)})</span></td>'
        table_rows += f"<tr>{cells}</tr>\n"
    
    html = f"""<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Family Genome Comparison</title>
<style>
body{{font-family:system-ui,sans-serif;max-width:1100px;margin:0 auto;padding:20px;background:#f5f5f5}}
h1{{color:#2c3e50}}
.disclaimer{{background:#fff3cd;border:1px solid #ffc107;padding:12px;border-radius:6px;margin:16px 0}}
table{{width:100%;border-collapse:collapse;background:white;border-radius:8px;overflow:hidden;box-shadow:0 1px 3px rgba(0,0,0,.1);margin:16px 0}}
th{{background:#2c3e50;color:white;padding:10px}} td{{padding:10px;border-bottom:1px solid #eee}}
</style></head><body>
<h1>👨‍👩‍👧‍👦 Family Genome Comparison</h1>
<p>Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}</p>
<div class="disclaimer">⚠️ This report is for educational purposes only. NOT medical advice.</div>
<table><tr><th>Category</th>{"".join(f"<th>{m}</th>" for m in members)}</tr>
{table_rows}</table>
</body></html>"""
    
    out = Path(genome_dir) / "family_comparison.html"
    with open(out, "w") as f:
        f.write(html)
    print(f"Family comparison saved to: {out}")


def main():
    parser = argparse.ArgumentParser(
        description="Analyze 23andMe raw genome data and generate health/trait reports.",
        epilog="DISCLAIMER: This is NOT medical advice. For educational purposes only.",
    )
    parser.add_argument("genome_file", nargs="?", help="Path to 23andMe raw data file (.txt)")
    parser.add_argument("--family", metavar="DIR", help="Directory with multiple genome files for family comparison")
    parser.add_argument("--category", default="all",
                        choices=["all", "cardio", "cardiovascular", "cognitive", "metabolic", "pharma", "athletic", "traits"],
                        help="Category to analyze (default: all)")
    parser.add_argument("--output", default="text", choices=["text", "json", "html"],
                        help="Output format (default: text)")
    parser.add_argument("--output-path", help="Output file path (for html)")
    
    args = parser.parse_args()
    
    if not args.genome_file and not args.family:
        parser.print_help()
        sys.exit(1)
    
    snp_db = load_snp_database()
    
    # Resolve categories
    categories = None
    if args.category != "all":
        resolved = CATEGORY_ALIASES.get(args.category, args.category)
        categories = {resolved}
    
    if args.family:
        family_comparison(args.family, snp_db, categories, args.output)
        return
    
    if not os.path.exists(args.genome_file):
        print(f"Error: File not found: {args.genome_file}")
        sys.exit(1)
    
    genotypes = parse_23andme(args.genome_file)
    print(f"Parsed {len(genotypes)} SNPs from {args.genome_file}")
    
    results = analyze_genome(genotypes, snp_db, categories)
    scores = calculate_scores(results)
    
    if args.output == "json":
        output_json(results, scores, args.genome_file)
    elif args.output == "html":
        output_html(results, scores, args.genome_file, args.output_path)
    else:
        output_text(results, scores, args.genome_file)


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)