{"spec_id":"gauge-activity-rings","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\ngauge-activity-rings: Activity Rings Progress Chart\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 81/100 | Created: 2026-06-14\n\"\"\"\n\nimport math\nimport os\nimport sys\n\n\n# Remove this file from sys.path so cairosvg (a pygal dep) resolves the real package\nsys.path = [p for p in sys.path if os.path.abspath(p) != os.path.dirname(os.path.abspath(__file__))]\n\nimport cairosvg\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\nRING_COLORS = [\"#009E73\", \"#C475FD\", \"#4467A3\"]\nmetrics = [\"Move\", \"Exercise\", \"Stand\"]\nvalues = [420, 25, 9]\ngoals = [600, 30, 12]\nunits = [\"kcal\", \"min\", \"hr\"]\nfractions = [min(v / g, 1.0) for v, g in zip(values, goals, strict=False)]\n\nW, H = 2400, 2400\nSTROKE = 130\nGAP = 22\nOUTER_R = 880\ncx, cy = W / 2, H / 2 + 60\n\nradii = [OUTER_R - i * (STROKE + GAP) for i in range(3)]\n\n\ndef arc_d(r, frac, ox, oy):\n    if frac <= 0:\n        return None\n    if frac >= 1.0:\n        return f\"M {ox:.1f},{oy - r:.1f} A {r},{r} 0 1,1 {ox:.1f},{oy + r:.1f} A {r},{r} 0 1,1 {ox:.1f},{oy - r:.1f}\"\n    sweep = frac * 360\n    a1, a2 = math.radians(-90), math.radians(-90 + sweep)\n    x1, y1 = ox + r * math.cos(a1), oy + r * math.sin(a1)\n    x2, y2 = ox + r * math.cos(a2), oy + r * math.sin(a2)\n    lf = 1 if sweep > 180 else 0\n    return f\"M {x1:.1f},{y1:.1f} A {r},{r} 0 {lf},1 {x2:.1f},{y2:.1f}\"\n\n\ntitle = \"Daily Fitness Goals · gauge-activity-rings · python · pygal · anyplot.ai\"\n\nels = [\n    f'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{W}\" height=\"{H}\" viewBox=\"0 0 {W} {H}\">',\n    f'<rect width=\"{W}\" height=\"{H}\" fill=\"{PAGE_BG}\"/>',\n    (\n        f'<text x=\"{W / 2:.0f}\" y=\"110\" text-anchor=\"middle\" '\n        f'font-family=\"Arial,sans-serif\" font-size=\"44\" fill=\"{INK}\">{title}</text>'\n    ),\n]\n\n# Background tracks (ring color at 20% opacity) + progress arcs with rounded caps\nfor frac, color, r in zip(fractions, RING_COLORS, radii, strict=False):\n    els.append(\n        f'<circle cx=\"{cx:.0f}\" cy=\"{cy:.0f}\" r=\"{r}\" fill=\"none\" '\n        f'stroke=\"{color}\" stroke-opacity=\"0.20\" stroke-width=\"{STROKE}\"/>'\n    )\n    d = arc_d(r, frac, cx, cy)\n    if d:\n        els.append(f'<path d=\"{d}\" fill=\"none\" stroke=\"{color}\" stroke-width=\"{STROKE}\" stroke-linecap=\"round\"/>')\n\n# Center labels — colored percentage (bold for best) + muted metric name\nbest = fractions.index(max(fractions))\nty0 = cy - 130\nfor i, (metric, frac, color) in enumerate(zip(metrics, fractions, RING_COLORS, strict=False)):\n    py = ty0 + i * 110\n    weight = \"bold\" if i == best else \"400\"\n    els.extend(\n        [\n            (\n                f'<text x=\"{cx:.0f}\" y=\"{py:.0f}\" text-anchor=\"middle\" '\n                f'font-family=\"Arial,sans-serif\" font-size=\"62\" '\n                f'fill=\"{color}\" font-weight=\"{weight}\">{frac * 100:.0f}%</text>'\n            ),\n            (\n                f'<text x=\"{cx:.0f}\" y=\"{py + 42:.0f}\" text-anchor=\"middle\" '\n                f'font-family=\"Arial,sans-serif\" font-size=\"30\" fill=\"{INK_MUTED}\">{metric}</text>'\n            ),\n        ]\n    )\n\n# Legend at bottom\nlw = 740\nlx0 = (W - lw * len(metrics)) / 2\nfor i, (m, v, g, u, c) in enumerate(zip(metrics, values, goals, units, RING_COLORS, strict=False)):\n    lx = lx0 + i * lw\n    ly = H - 100\n    els.extend(\n        [\n            f'<circle cx=\"{lx + 22:.0f}\" cy=\"{ly:.0f}\" r=\"20\" fill=\"{c}\"/>',\n            (\n                f'<text x=\"{lx + 54:.0f}\" y=\"{ly + 14:.0f}\" '\n                f'font-family=\"Arial,sans-serif\" font-size=\"40\" fill=\"{INK}\">'\n                f\"{m}  {v}/{g} {u}</text>\"\n            ),\n        ]\n    )\n\nels.append(\"</svg>\")\nsvg_str = \"\\n\".join(els)\n\ncairosvg.svg2png(bytestring=svg_str.encode(), write_to=f\"plot-{THEME}.png\")\n\nwith open(f\"plot-{THEME}.html\", \"w\", encoding=\"utf-8\") as f:\n    f.write(\n        f'<!DOCTYPE html><html><head><meta charset=\"utf-8\"></head>'\n        f'<body style=\"margin:0;background:{PAGE_BG}\">{svg_str}</body></html>'\n    )\n"}