{"spec_id":"audiogram-clinical","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\naudiogram-clinical: Clinical Audiogram\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 82/100 | Created: 2026-06-15\n\"\"\"\n\nimport os\nimport sys\n\n\n# Remove the script's own directory from sys.path to prevent shadowing the pygal package\n_script_dir = os.path.dirname(os.path.abspath(__file__))\nif _script_dir in sys.path:\n    sys.path.remove(_script_dir)\n\nimport pygal\nfrom pygal.style import Style\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Semantic exception: audiology convention overrides Imprint categorical colors\nRIGHT_COLOR = \"#AE3030\"  # Imprint matte red — right ear\nLEFT_COLOR = \"#4467A3\"  # Imprint blue — left ear\n\n# Muted severity band fills — (light_hex, dark_hex), worst to best\n_BAND_COLOR_PAIRS = (\n    (\"#E8C8C8\", \"#2A1818\"),  # Profound (>90 dB)\n    (\"#EDD6C0\", \"#2A2018\"),  # Severe (71–90 dB)\n    (\"#EDE8C0\", \"#2A2A18\"),  # Mod-Severe (56–70 dB)\n    (\"#DEE8C0\", \"#1E2A18\"),  # Moderate (41–55 dB)\n    (\"#C8E4C8\", \"#182A18\"),  # Mild (26–40 dB)\n    (\"#C0E0D4\", \"#182A22\"),  # Normal (≤25 dB)\n)\nband_fills = tuple(pair[0] if THEME == \"light\" else pair[1] for pair in _BAND_COLOR_PAIRS)\n\n# Full palette: 6 band fills then the two ear colors\nPALETTE = band_fills + (RIGHT_COLOR, LEFT_COLOR)\n\ntitle_str = \"audiogram-clinical · python · pygal · anyplot.ai\"\nn_chars = len(title_str)\nratio = 67 / n_chars if n_chars > 67 else 1.0\ntitle_font_size = max(44, round(66 * ratio))\n\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=PALETTE,\n    title_font_size=title_font_size,\n    label_font_size=56,\n    major_label_font_size=44,\n    legend_font_size=36,\n    value_font_size=36,\n    stroke_width=3.0,\n)\n\n# Standard audiometric octave frequencies — each is exactly 2× the prior, so equal\n# categorical spacing in pygal.Line matches logarithmic visual spacing precisely\nfreq_labels = [\"125\", \"250\", \"500\", \"1k\", \"2k\", \"4k\", \"8k\"]\nn_freqs = len(freq_labels)\n\n# Noise-induced sensorineural hearing loss — bilateral 4 kHz notch\nthreshold_right = [10, 15, 20, 20, 25, 65, 55]  # right ear, red O markers\nthreshold_left = [15, 20, 25, 25, 35, 55, 50]  # left ear, blue X markers\n\n# Negate: 0 dB HL (best hearing) sits at y=0 (top); increasing loss goes negative\nright_neg = [-t for t in threshold_right]\nleft_neg = [-t for t in threshold_left]\n\n# Y-axis: absolute-value labels displayed at their negated positions every 10 dB\ny_label_items = [{\"label\": str(abs(v)), \"value\": v} for v in range(-120, 1, 10)]\n\nchart = pygal.Line(\n    width=2400,\n    height=2400,\n    style=custom_style,\n    title=title_str,\n    x_title=\"Frequency (Hz)\",\n    y_title=\"Hearing Level (dB HL)\",\n    show_dots=True,\n    dots_size=6,\n    show_x_guides=True,\n    show_y_guides=True,\n    range=(-120, 5),\n    legend_at_bottom=True,\n    legend_at_bottom_columns=4,\n)\n\nchart.x_labels = freq_labels\nchart.y_labels = y_label_items\n\n# Stacked fill technique for severity bands:\n# Each band is a flat horizontal line with fill=True, which fills from that y-value\n# upward to y=0.  Adding from deepest (worst, y=−120) to shallowest (best, y=−25)\n# means each later fill overwrites the upper portion, so every band retains its own\n# color only between its two surrounding boundary lines.\nband_specs = [\n    (\"Profound (>90 dB)\", -120),  # initial fill covers entire chart height\n    (\"Severe (71–90 dB)\", -90),  # overwrites from −90 to 0, exposing profound below\n    (\"Mod-Severe (56–70 dB)\", -70),\n    (\"Moderate (41–55 dB)\", -55),\n    (\"Mild (26–40 dB)\", -40),\n    (\"Normal (≤25 dB)\", -25),\n]\n\nfor label, y_val in band_specs:\n    chart.add(label, [y_val] * n_freqs, fill=True, show_dots=False, stroke_style={\"width\": 0.5})\n\n# Audiogram lines — drawn on top of all band fills\nchart.add(\"Right Ear (O)\", right_neg, fill=False, stroke_style={\"width\": 5.0})\nchart.add(\"Left Ear (X)\", left_neg, fill=False, stroke_style={\"width\": 5.0})\n\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}