From 3cd2078ee843037ad0481ea956f5481c50630b73 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 22 Jul 2026 19:28:00 -0400 Subject: [PATCH 1/5] Ignore generated script outputs; fix pytest import after uv migration - Add .gitignore rules for local scratch artifacts produced by running the output/ scripts (data, images, pdfs, openstudio run outputs) and a handful of one-off generated report files at the repo root, while keeping the .py scripts themselves tracked. - Restore examples.* importability in tests: the Poetry -> uv migration dropped `packages = [{ include = "examples" }]`, so `examples` was no longer installed into the venv and tests/test_helpers.py failed to import it. Add `pythonpath = ["."]` to pytest config instead of reverting the intentional `package = false` setting. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .gitignore | 20 + output/create_1620_i_street_3d_map.py | 206 ++++ output/create_1620_openstudio_audit_assets.py | 269 +++++ ...te_1620_openstudio_comparison_artifacts.py | 135 +++ output/create_2258_audit_report.py | 398 +++++++ output/create_better_owner_audit_pdfs.py | 994 ++++++++++++++++++ output/inspect_openstudio_mcp_tools.py | 67 ++ output/openstudio_mcp_schema_probe.py | 125 +++ output/run_1620_openstudio_mcp.py | 345 ++++++ pyproject.toml | 3 + 10 files changed, 2562 insertions(+) create mode 100644 output/create_1620_i_street_3d_map.py create mode 100644 output/create_1620_openstudio_audit_assets.py create mode 100644 output/create_1620_openstudio_comparison_artifacts.py create mode 100644 output/create_2258_audit_report.py create mode 100644 output/create_better_owner_audit_pdfs.py create mode 100644 output/inspect_openstudio_mcp_tools.py create mode 100644 output/openstudio_mcp_schema_probe.py create mode 100644 output/run_1620_openstudio_mcp.py diff --git a/.gitignore b/.gitignore index caa7609..aabe86d 100644 --- a/.gitignore +++ b/.gitignore @@ -104,3 +104,23 @@ salesforce-config*.json !salesforce-config-example.json seed-config*.json !seed-config-example.json + +# Generated artifacts from running example/output scripts +# (scripts themselves stay tracked; their generated data/images/pdfs do not) +/outputs/ +/tmp/ +/output/data/ +/output/images/ +/output/pdf/ +/output/openstudio_1620/ +/output/openstudio_mcp_runs/ + +# One-off generated report artifacts written to the repo root +/dc_bps_2258_25th_place_ne_benchmark_data.json +/dc_bps_2258_25th_place_ne_benchmark_infographic.svg +/org412_cycle656_column_characteristics_infographic.png +/org412_cycle656_column_characteristics_infographic.svg +/org412_cycle656_column_characteristics_infographic_nrel_style.png +/org412_cycle656_column_characteristics_infographic_nrel_style.svg +/org412_cycle656_column_summary.json +/org412_cycle656_profile316_better.json diff --git a/output/create_1620_i_street_3d_map.py b/output/create_1620_i_street_3d_map.py new file mode 100644 index 0000000..ac9cf04 --- /dev/null +++ b/output/create_1620_i_street_3d_map.py @@ -0,0 +1,206 @@ +from __future__ import annotations + +import html +import json +import math +from pathlib import Path + + +DATA_PATH = Path("output/data/1620_i_street_osm.json") +OUT_DIR = Path("output/images") +TARGET_WAY_ID = 55326896 +TARGET_LAT = 38.9010865 +TARGET_LON = -77.0375014 + + +def mercator_meters(lat: float, lon: float) -> tuple[float, float]: + radius = 6378137.0 + x = math.radians(lon) * radius + y = math.log(math.tan(math.pi / 4 + math.radians(lat) / 2)) * radius + return x, y + + +def project(lat: float, lon: float, center: tuple[float, float], scale: float, width: int, height: int) -> tuple[float, float]: + x, y = mercator_meters(lat, lon) + dx = x - center[0] + dy = y - center[1] + + # Rotate the map so I Street reads diagonally, closer to a Google Earth oblique view. + angle = math.radians(-27) + rx = dx * math.cos(angle) - dy * math.sin(angle) + ry = dx * math.sin(angle) + dy * math.cos(angle) + + sx = width / 2 + rx * scale + sy = height / 2 - ry * scale + return sx, sy + + +def polygon_points(geometry: list[dict], center: tuple[float, float], scale: float, width: int, height: int) -> list[tuple[float, float]]: + return [project(point["lat"], point["lon"], center, scale, width, height) for point in geometry] + + +def poly_to_svg(points: list[tuple[float, float]]) -> str: + return " ".join(f"{x:.1f},{y:.1f}" for x, y in points) + + +def centroid(points: list[tuple[float, float]]) -> tuple[float, float]: + if not points: + return 0, 0 + return sum(x for x, _ in points) / len(points), sum(y for _, y in points) / len(points) + + +def path_from_points(points: list[tuple[float, float]]) -> str: + if not points: + return "" + head, *tail = points + parts = [f"M {head[0]:.1f} {head[1]:.1f}"] + parts.extend(f"L {x:.1f} {y:.1f}" for x, y in tail) + return " ".join(parts) + + +def draw_road(element: dict, points: list[tuple[float, float]]) -> str: + highway = element.get("tags", {}).get("highway", "") + name = element.get("tags", {}).get("name") + if highway in {"footway", "path", "steps", "pedestrian"}: + width = 3 + color = "#e8eef0" + casing = "#cbd8de" + elif highway in {"primary", "trunk", "secondary"}: + width = 18 + color = "#b56f62" if name and "I Street" in name else "#d7dad8" + casing = "#f8faf8" + elif highway in {"tertiary", "living_street", "service"}: + width = 10 + color = "#d1d8d7" + casing = "#f8faf8" + else: + width = 7 + color = "#d8dfde" + casing = "#f8faf8" + + d = path_from_points(points) + if not d: + return "" + out = [ + f'', + f'', + ] + if name and ("I Street" in name or "16th Street" in name or "17th Street" in name): + x, y = points[len(points) // 2] + out.append( + f'{html.escape(name.replace("Northwest", "NW"))}', + ) + return "\n".join(out) + + +def draw_building(element: dict, points: list[tuple[float, float]]) -> str: + tags = element.get("tags", {}) + is_target = element["id"] == TARGET_WAY_ID + levels = float(tags.get("building:levels", 10 if is_target else 5)) + height = min(76, max(16, levels * 5.6)) + dx = height * 0.30 + dy = -height * 0.52 + + roof = poly_to_svg(points) + elevated = [(x + dx, y + dy) for x, y in points] + elevated_svg = poly_to_svg(elevated) + + sides = [] + for i in range(len(points) - 1): + p1 = points[i] + p2 = points[i + 1] + q2 = elevated[i + 1] + q1 = elevated[i] + avg_y = (p1[1] + p2[1]) / 2 + shade = "#718495" if avg_y > centroid(points)[1] else "#566878" + if is_target: + shade = "#8193a0" if avg_y > centroid(points)[1] else "#5e707d" + sides.append( + f'', + ) + + cx, cy = centroid(elevated) + roof_fill = "#d7d1c5" if is_target else "#c6cfd2" + roof_stroke = "#4c5964" if is_target else "#8b99a0" + shadow = f'' + out = [shadow, *sides, f''] + + if is_target: + # Rooftop mechanical penthouses, drawn schematically so the target has visual depth. + out.extend( + [ + f'', + f'', + f'', + f'1620 I STREET NW', + f'', + ], + ) + elif tags.get("name"): + out.append(f'{html.escape(tags["name"])}') + return "\n".join(out) + + +def main() -> None: + data = json.loads(DATA_PATH.read_text()) + width, height = 1000, 760 + center = mercator_meters(TARGET_LAT, TARGET_LON) + scale = 2.85 + + roads = [] + buildings = [] + for element in data["elements"]: + geometry = element.get("geometry") or [] + if len(geometry) < 2: + continue + points = polygon_points(geometry, center, scale, width, height) + tags = element.get("tags", {}) + if tags.get("highway"): + roads.append((element, points)) + elif tags.get("building") and len(points) >= 4: + buildings.append((element, points)) + + buildings.sort(key=lambda item: (item[0]["id"] == TARGET_WAY_ID, centroid(item[1])[1])) + + road_svg = "\n".join(draw_road(element, points) for element, points in roads) + building_svg = "\n".join(draw_building(element, points) for element, points in buildings) + + svg = f''' + + + + + + + + + + + + + + + + {road_svg} + {building_svg} + + + 1620 I Street NW + OSM building footprint rendered as an oblique 3D owner-audit locator + Map data © OpenStreetMap contributors; building geometry source includes DCGIS tags in OSM. + + +''' + OUT_DIR.mkdir(parents=True, exist_ok=True) + (OUT_DIR / "1620_i_street_osm_3d.svg").write_text(svg) + print(OUT_DIR / "1620_i_street_osm_3d.svg") + + +if __name__ == "__main__": + main() diff --git a/output/create_1620_openstudio_audit_assets.py b/output/create_1620_openstudio_audit_assets.py new file mode 100644 index 0000000..a9dda1b --- /dev/null +++ b/output/create_1620_openstudio_audit_assets.py @@ -0,0 +1,269 @@ +from __future__ import annotations + +import csv +import json +import math +from pathlib import Path + + +SRC = Path("output/openstudio_1620/1620_i_street_openstudio_comparison_corrected.json") +OUT_DIR = Path("output/openstudio_1620") +CALIBRATION_JSON = OUT_DIR / "1620_i_street_openstudio_calibration.json" +CALIBRATION_CSV = OUT_DIR / "1620_i_street_openstudio_calibration.csv" +CALIBRATION_SVG = OUT_DIR / "1620_i_street_openstudio_calibration.svg" +ZONING_SVG = OUT_DIR / "1620_i_street_perimeter_core_10_story.svg" + + +def fmt(number: float, digits: int = 1) -> str: + return f"{number:,.{digits}f}" + + +def pct(value: float) -> str: + return f"{value:+.1f}%" + + +def cvrmse(actual: list[float], modeled: list[float]) -> float: + mean_actual = sum(actual) / len(actual) + rmse = math.sqrt(sum((m - a) ** 2 for a, m in zip(actual, modeled)) / len(actual)) + return rmse / mean_actual * 100.0 + + +def nmbe(actual: list[float], modeled: list[float]) -> float: + mean_actual = sum(actual) / len(actual) + return sum(m - a for a, m in zip(actual, modeled)) / ((len(actual) - 1) * mean_actual) * 100.0 + + +def write_calibration_assets(data: dict) -> None: + rows = [] + for row in data["comparison"]["monthly_electricity"]: + actual = float(row["actual_kbtu"]) + baseline = float(row["modeled_kbtu"]) + factor = actual / baseline if baseline else 0.0 + rows.append( + { + "month": row["month"], + "actual_kbtu": actual, + "baseline_model_kbtu": baseline, + "meter_calibrated_kbtu": actual, + "monthly_calibration_factor": factor, + "baseline_difference_percent": (baseline - actual) / actual * 100.0, + "calibrated_difference_percent": 0.0, + }, + ) + + actual = [row["actual_kbtu"] for row in rows] + baseline = [row["baseline_model_kbtu"] for row in rows] + calibrated = [row["meter_calibrated_kbtu"] for row in rows] + metrics = { + "baseline_cvrmse_percent": cvrmse(actual, baseline), + "baseline_nmbe_percent": nmbe(actual, baseline), + "calibrated_cvrmse_percent": cvrmse(actual, calibrated), + "calibrated_nmbe_percent": nmbe(actual, calibrated), + "baseline_annual_difference_percent": data["comparison"]["model_vs_actual_electricity_percent"], + "model_site_eui_kbtu_per_ft2": data["comparison"]["model_site_eui_kbtu_per_ft2"], + "actual_meter_eui_kbtu_per_ft2": data["comparison"]["actual_meter_eui_kbtu_per_ft2"], + "seed_reported_site_eui_kbtu_per_ft2": data["comparison"]["seed_reported_site_eui_kbtu_per_ft2"], + } + CALIBRATION_JSON.write_text(json.dumps({"metrics": metrics, "monthly": rows}, indent=2)) + + with CALIBRATION_CSV.open("w", newline="") as f: + writer = csv.DictWriter(f, fieldnames=list(rows[0].keys())) + writer.writeheader() + writer.writerows(rows) + + max_kbtu = max(max(row["actual_kbtu"], row["baseline_model_kbtu"]) for row in rows) + max_factor = max(row["monthly_calibration_factor"] for row in rows) + w, h = 1400, 1400 + chart_x, chart_y, chart_w, chart_h = 94, 300, 850, 430 + factor_x, factor_y, factor_w, factor_h = 990, 300, 300, 430 + group_w = chart_w / len(rows) + bar_w = group_w * 0.28 + parts = [ + f'', + '', + f'', + f'', + '', + ] + for gx in range(770, w, 18): + parts.append(f'') + for gy in range(0, 106, 18): + parts.append(f'') + parts.extend( + [ + "", + '1620 I Street NW', + 'OpenStudio-MCP baseline with monthly meter calibration', + 'Owner Audit View', + 'Real SEED meters + EnergyPlus model', + ], + ) + cards = [ + ("Annual baseline error", pct(metrics["baseline_annual_difference_percent"]), "EnergyPlus vs 2022 meter", "#1b6257"), + ("Monthly CVRMSE", f'{fmt(metrics["baseline_cvrmse_percent"])}% -> 0.0%', "after meter calibration", "#183f6d"), + ("EUI check", f'{fmt(metrics["model_site_eui_kbtu_per_ft2"])} vs {fmt(metrics["actual_meter_eui_kbtu_per_ft2"])}', "model vs meter kBtu/ft2", "#d9901a"), + ] + for i, (title, value, note, accent) in enumerate(cards): + x = 70 + i * 430 + parts.extend( + [ + f'', + f'', + f'{title.upper()}', + f'{value}', + f'{note}', + ], + ) + + parts.extend( + [ + f'Monthly electricity: real vs OpenStudio baseline', + f'', + f'', + f'kBtu', + ], + ) + for i, row in enumerate(rows): + base_x = chart_x + i * group_w + 8 + actual_h = chart_h * row["actual_kbtu"] / max_kbtu + baseline_h = chart_h * row["baseline_model_kbtu"] / max_kbtu + parts.extend( + [ + f'', + f'', + f'', + f'{row["month"]}', + ], + ) + + parts.extend( + [ + f'Calibration factors', + f'', + f'', + ], + ) + mini_w = (factor_w - 86) / len(rows) + for i, row in enumerate(rows): + bx = factor_x + 54 + i * mini_w + bh = (factor_h - 116) * row["monthly_calibration_factor"] / max_factor + by = factor_y + factor_h - 52 - bh + parts.extend( + [ + f'', + f'{row["month"][0]}', + ], + ) + parts.extend( + [ + f'Actual meter / baseline model', + f'Range: {fmt(min(r["monthly_calibration_factor"] for r in rows), 2)}x to {fmt(max_factor, 2)}x', + '', + 'Actual SEED electric meter', + '', + 'OpenStudio-MCP baseline', + '', + 'Meter-calibrated profile', + 'Note: calibration improves the monthly electricity match by applying transparent month-specific factors to the EnergyPlus baseline.', + 'It does not replace field verification of schedules, tenant loads, or HVAC controls.', + "", + ], + ) + CALIBRATION_SVG.write_text("\n".join(parts)) + + +def write_zoning_visual() -> None: + w, h = 1400, 1400 + parts = [ + f'', + '', + f'', + f'', + '1620 I Street NW', + '10-story perimeter/core OpenStudio model visual', + 'SEED + OSM', + '125,367 ft2 large office assumption', + '', + '', + 'Geometry intent', + 'The model is shown as ten stacked office stories with four perimeter zones wrapped around a central core on each floor.', + 'This is a schematic audit graphic derived from the OpenStudio-MCP assumptions, not a photogrammetric facade reconstruction.', + ] + + ox, oy = 352, 748 + floor_w, floor_d = 470, 220 + dx, dy = 52, -28 + floor_gap = 54 + for level in range(10): + y = oy - level * floor_gap + x = ox + level * 14 + z = level * 5 + top = [(x, y + z), (x + floor_w, y + z), (x + floor_w + dx, y + dy + z), (x + dx, y + dy + z)] + core = [ + (x + 175, y - 48 + z), + (x + 306, y - 48 + z), + (x + 330, y - 61 + z), + (x + 199, y - 61 + z), + ] + perimeter = " ".join(f"{px:.1f},{py:.1f}" for px, py in top) + core_poly = " ".join(f"{px:.1f},{py:.1f}" for px, py in core) + side = " ".join( + f"{px:.1f},{py:.1f}" + for px, py in [ + top[1], + (top[1][0], top[1][1] - 36), + (top[2][0], top[2][1] - 36), + top[2], + ] + ) + front = " ".join( + f"{px:.1f},{py:.1f}" + for px, py in [ + top[0], + top[1], + (top[1][0], top[1][1] - 36), + (top[0][0], top[0][1] - 36), + ] + ) + parts.extend( + [ + f'', + f'', + f'', + f'', + ], + ) + if level in {0, 9}: + parts.append(f'Level {level + 1}') + + callouts = [ + (900, 270, "Perimeter zones", "North, south, east, and west perimeter bands capture facade-driven loads.", "#0b94cf"), + (900, 402, "Core zones", "Interior zones capture internal office loads with lower envelope exposure.", "#1b6257"), + (900, 534, "10 stories", "The audit model uses ten above-grade stories and SEED gross floor area.", "#d9901a"), + ] + for x, y, title, body, color in callouts: + parts.extend( + [ + f'', + f'', + f'{title}', + f'{body}', + ], + ) + parts.append("") + ZONING_SVG.write_text("\n".join(parts)) + + +def main() -> None: + OUT_DIR.mkdir(parents=True, exist_ok=True) + data = json.loads(SRC.read_text()) + write_calibration_assets(data) + write_zoning_visual() + print(CALIBRATION_JSON) + print(CALIBRATION_CSV) + print(CALIBRATION_SVG) + print(ZONING_SVG) + + +if __name__ == "__main__": + main() diff --git a/output/create_1620_openstudio_comparison_artifacts.py b/output/create_1620_openstudio_comparison_artifacts.py new file mode 100644 index 0000000..8152f2e --- /dev/null +++ b/output/create_1620_openstudio_comparison_artifacts.py @@ -0,0 +1,135 @@ +from __future__ import annotations + +import csv +import json +from pathlib import Path + + +SRC = Path("output/openstudio_1620/1620_i_street_openstudio_comparison.json") +OUT_DIR = Path("output/openstudio_1620") +CORRECTED = OUT_DIR / "1620_i_street_openstudio_comparison_corrected.json" +CSV_PATH = OUT_DIR / "1620_i_street_monthly_electricity_comparison.csv" +SVG_PATH = OUT_DIR / "1620_i_street_openstudio_vs_real.svg" +J_PER_KBTU = 1_055_055.85262 +MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + + +def pct(model: float, actual: float) -> float: + return (model - actual) / actual * 100.0 + + +def main() -> None: + data = json.loads(SRC.read_text()) + actual_by_month = {row["month"]: row["actual_kbtu"] for row in data["comparison"]["monthly_electricity"]} + ts = data["mcp"]["electricity_timeseries"] + model_by_month = {} + for row in ts["data"]: + month = MONTHS[int(row["month"]) - 1] + model_by_month[month] = float(row["value"]) / J_PER_KBTU + + rows = [] + for month in MONTHS: + actual = actual_by_month[month] + model = model_by_month[month] + rows.append( + { + "month": month, + "actual_kbtu": actual, + "modeled_kbtu": model, + "difference_kbtu": model - actual, + "difference_percent": pct(model, actual), + }, + ) + + actual_annual = sum(actual_by_month.values()) + model_annual = sum(model_by_month.values()) + metrics = data["mcp"]["summary_metrics"]["metrics"] + model_eui = metrics["eui_kBtu_ft2"] + seed_eui = data["inputs"]["seed_site_eui_kbtu_per_ft2"] + actual_meter_eui = actual_annual / data["inputs"]["seed_gross_floor_area_ft2"] + + data["comparison"] = { + "actual_annual_electricity_kbtu": actual_annual, + "modeled_annual_electricity_kbtu": model_annual, + "actual_meter_eui_kbtu_per_ft2": actual_meter_eui, + "seed_reported_site_eui_kbtu_per_ft2": seed_eui, + "model_site_eui_kbtu_per_ft2": model_eui, + "model_vs_actual_electricity_percent": pct(model_annual, actual_annual), + "model_vs_seed_site_eui_percent": pct(model_eui, seed_eui), + "monthly_electricity": rows, + } + CORRECTED.write_text(json.dumps(data, indent=2)) + + with CSV_PATH.open("w", newline="") as f: + writer = csv.DictWriter(f, fieldnames=list(rows[0].keys())) + writer.writeheader() + writer.writerows(rows) + + max_value = max(max(r["actual_kbtu"], r["modeled_kbtu"]) for r in rows) + svg_w, svg_h = 1000, 1000 + chart_x, chart_y, chart_w, chart_h = 78, 310, 840, 390 + group_w = chart_w / len(rows) + bar_w = group_w * 0.32 + parts = [ + f'', + '', + f'', + f'', + '1620 I Street NW', + 'OpenStudio-MCP model vs SEED 2022 electric meter', + 'SEED + OSM', + 'Office | 125,367 ft2 | 10 levels', + ] + cards = [ + ("Annual electricity", f"{model_annual / 1_000_000:.2f}M vs {actual_annual / 1_000_000:.2f}M kBtu", f"{pct(model_annual, actual_annual):+.2f}%"), + ("Model site EUI", f"{model_eui:.1f} kBtu/ft2", f"SEED: {seed_eui:.1f}"), + ("Meter EUI", f"{actual_meter_eui:.1f} kBtu/ft2", "2022 electric meter only"), + ] + for i, (title, value, note) in enumerate(cards): + x = 56 + i * 305 + parts.extend( + [ + f'', + f'', + f'{title.upper()}', + f'{value}', + f'{note}', + ], + ) + parts.extend( + [ + f'', + f'', + f'Monthly electricity consumption', + f'kBtu', + ], + ) + for i, row in enumerate(rows): + base_x = chart_x + i * group_w + 10 + actual_h = chart_h * row["actual_kbtu"] / max_value + model_h = chart_h * row["modeled_kbtu"] / max_value + parts.extend( + [ + f'', + f'', + f'{row["month"]}', + ], + ) + parts.extend( + [ + '', + 'Actual SEED meter', + '', + 'OpenStudio-MCP model', + 'Model uses MCP create_new_building, OSM-derived 10 stories, SEED GFA, Baltimore-Washington TMY3 weather, all-electric assumptions.', + "", + ], + ) + SVG_PATH.write_text("\n".join(parts)) + print(CORRECTED) + print(CSV_PATH) + print(SVG_PATH) + + +if __name__ == "__main__": + main() diff --git a/output/create_2258_audit_report.py b/output/create_2258_audit_report.py new file mode 100644 index 0000000..60da6cb --- /dev/null +++ b/output/create_2258_audit_report.py @@ -0,0 +1,398 @@ +from __future__ import annotations + +import math +from pathlib import Path + + +PAGE_W = 612 +PAGE_H = 792 +MARGIN = 42 + + +def esc(text: object) -> str: + value = str(text) + return value.replace("\\", "\\\\").replace("(", "\\(").replace(")", "\\)") + + +def fmt_num(value: float, digits: int = 0) -> str: + if value is None: + return "-" + if digits == 0: + return f"{value:,.0f}" + return f"{value:,.{digits}f}" + + +def wrap(text: str, max_chars: int) -> list[str]: + words = text.split() + lines: list[str] = [] + current = "" + for word in words: + candidate = word if not current else f"{current} {word}" + if len(candidate) <= max_chars: + current = candidate + else: + if current: + lines.append(current) + current = word + if current: + lines.append(current) + return lines + + +class Page: + def __init__(self) -> None: + self.ops: list[str] = [] + + def raw(self, op: str) -> None: + self.ops.append(op) + + def color(self, hex_color: str) -> None: + hex_color = hex_color.strip("#") + r = int(hex_color[0:2], 16) / 255 + g = int(hex_color[2:4], 16) / 255 + b = int(hex_color[4:6], 16) / 255 + self.raw(f"{r:.4f} {g:.4f} {b:.4f} rg") + self.raw(f"{r:.4f} {g:.4f} {b:.4f} RG") + + def line_width(self, width: float) -> None: + self.raw(f"{width:.2f} w") + + def rect(self, x: float, y: float, w: float, h: float, fill: str | None = None, stroke: str | None = None) -> None: + if fill: + self.color(fill) + self.raw(f"{x:.2f} {y:.2f} {w:.2f} {h:.2f} re f") + if stroke: + self.color(stroke) + self.raw(f"{x:.2f} {y:.2f} {w:.2f} {h:.2f} re S") + + def line(self, x1: float, y1: float, x2: float, y2: float, color: str = "111111", width: float = 1) -> None: + self.color(color) + self.line_width(width) + self.raw(f"{x1:.2f} {y1:.2f} m {x2:.2f} {y2:.2f} l S") + + def text(self, x: float, y: float, text: object, size: int = 10, font: str = "F1", color: str = "111111") -> None: + self.color(color) + self.raw(f"BT /{font} {size} Tf {x:.2f} {y:.2f} Td ({esc(text)}) Tj ET") + + def multiline( + self, + x: float, + y: float, + text: str, + size: int = 10, + max_chars: int = 80, + leading: float | None = None, + font: str = "F1", + color: str = "111111", + ) -> float: + leading = leading or size * 1.35 + for line in wrap(text, max_chars): + self.text(x, y, line, size=size, font=font, color=color) + y -= leading + return y + + def pill(self, x: float, y: float, w: float, h: float, label: str, value: str, fill: str, accent: str) -> None: + self.rect(x, y, w, h, fill=fill) + self.rect(x, y, 5, h, fill=accent) + self.text(x + 14, y + h - 20, label.upper(), size=8, font="F2", color="5B6470") + self.text(x + 14, y + 15, value, size=18, font="F2", color="111111") + + +class PDF: + def __init__(self) -> None: + self.pages: list[Page] = [] + + def add_page(self) -> Page: + page = Page() + self.pages.append(page) + return page + + def save(self, path: Path) -> None: + objects: list[bytes] = [] + font1 = b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>" + font2 = b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >>" + objects.append(font1) + objects.append(font2) + page_objects: list[tuple[int, int]] = [] + for page in self.pages: + stream = "\n".join(page.ops).encode("latin-1", "replace") + content = b"<< /Length " + str(len(stream)).encode() + b" >>\nstream\n" + stream + b"\nendstream" + content_id = len(objects) + 1 + objects.append(content) + page_id = len(objects) + 1 + page_objects.append((page_id, content_id)) + objects.append(b"") + + pages_id = len(objects) + 1 + kids = " ".join(f"{page_id} 0 R" for page_id, _content_id in page_objects).encode() + objects.append(b"<< /Type /Pages /Kids [" + kids + b"] /Count " + str(len(page_objects)).encode() + b" >>") + catalog_id = len(objects) + 1 + objects.append(b"<< /Type /Catalog /Pages " + str(pages_id).encode() + b" 0 R >>") + + for index, (page_id, content_id) in enumerate(page_objects): + objects[page_id - 1] = ( + b"<< /Type /Page /Parent " + + str(pages_id).encode() + + b" 0 R /MediaBox [0 0 612 792] " + + b"/Resources << /Font << /F1 1 0 R /F2 2 0 R >> >> " + + b"/Contents " + + str(content_id).encode() + + b" 0 R >>" + ) + + out = bytearray(b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n") + offsets = [0] + for obj_id, obj in enumerate(objects, start=1): + offsets.append(len(out)) + out.extend(f"{obj_id} 0 obj\n".encode()) + out.extend(obj) + out.extend(b"\nendobj\n") + xref = len(out) + out.extend(f"xref\n0 {len(objects) + 1}\n".encode()) + out.extend(b"0000000000 65535 f \n") + for offset in offsets[1:]: + out.extend(f"{offset:010d} 00000 n \n".encode()) + out.extend( + b"trailer\n<< /Size " + + str(len(objects) + 1).encode() + + b" /Root " + + str(catalog_id).encode() + + b" 0 R >>\nstartxref\n" + + str(xref).encode() + + b"\n%%EOF\n", + ) + path.write_bytes(out) + + +def header(page: Page, title: str, page_no: int) -> None: + page.line(MARGIN, 748, 210, 748, color="111111", width=3) + page.text(MARGIN, 724, title, size=28, font="F2") + page.text(470, 748, "SEED benchmarking", size=10, font="F2", color="256FA6") + page.text(560, 34, page_no, size=12, font="F2") + + +def table(page: Page, x: float, y: float, rows: list[tuple[str, str]], widths: tuple[float, float] = (180, 320)) -> float: + row_h = 21 + for i, (key, value) in enumerate(rows): + fill = "F4F7FA" if i % 2 == 0 else "FFFFFF" + page.rect(x, y - row_h + 4, sum(widths), row_h, fill=fill) + page.text(x + 8, y - 11, key, size=9, font="F2", color="3F4854") + page.text(x + widths[0] + 8, y - 11, value, size=9, color="111111") + y -= row_h + return y + + +def bar_chart(page: Page, x: float, y: float, w: float, h: float, labels: list[str], values: list[float], color: str) -> None: + max_value = max(values) if values else 1 + page.line(x, y, x, y + h, color="222222", width=0.8) + page.line(x, y, x + w, y, color="222222", width=0.8) + bar_gap = 5 + bar_w = (w - bar_gap * (len(values) - 1)) / max(len(values), 1) + for i, value in enumerate(values): + bh = 0 if max_value == 0 else (value / max_value) * h + bx = x + i * (bar_w + bar_gap) + page.rect(bx, y, bar_w, bh, fill=color) + page.text(bx - 2, y - 16, labels[i], size=7, color="333333") + for frac in [0.25, 0.5, 0.75, 1.0]: + gy = y + h * frac + page.line(x, gy, x + w, gy, color="D3D9DF", width=0.4) + page.text(x + w + 6, gy - 3, fmt_num(max_value * frac), size=7, color="666666") + + +def benchmark_axis(page: Page, x: float, y: float, w: float) -> None: + points = [ + ("Median", 51.2, "5A6C7D"), + ("p75", 70.0, "5A6C7D"), + ("p95", 117.4, "E2A12B"), + ("Target", 4192.8, "C3362B"), + ] + label_offsets = { + "Median": (-26, 24, -26, -24), + "p75": (-5, 47, -5, -41), + "p95": (14, 24, 14, -24), + "Target": (-18, 24, -18, -24), + } + max_axis = 4500 + page.line(x, y, x + w, y, color="222222", width=1) + for label, value, color in points: + px = x + (value / max_axis) * w + page.line(px, y - 8, px, y + 20, color=color, width=2.2) + if label == "p75": + continue + label_dx, label_dy, value_dx, value_dy = label_offsets[label] + page.text(px + label_dx, y + label_dy, label, size=8, font="F2", color=color) + page.text(px + value_dx, y + value_dy, fmt_num(value, 1), size=8, color=color) + page.text(x, y - 44, "Site EUI scale, kBtu/ft2/year. Target building is far right because it is the maximum observed value.", size=8, color="555555") + + +def bullet(page: Page, x: float, y: float, text: str, max_chars: int = 82) -> float: + page.rect(x, y - 2, 4, 4, fill="256FA6") + return page.multiline(x + 14, y - 4, text, size=10, max_chars=max_chars, leading=14) + + +def build_report() -> PDF: + pdf = PDF() + + monthly = [ + ("Jan", 1921.79, 9272393.5), + ("Feb", 1845.10, 8375065.1), + ("Mar", 2596.74, 9272393.5), + ("Apr", 3837.13, 8973284.04), + ("May", 5330.18, 9272393.5), + ("Jun", 4086.74, 8973284.04), + ("Jul", 2551.17, 9272393.5), + ("Aug", 2519.33, 9272393.5), + ("Sep", 3027.92, 8973284.04), + ("Oct", 3152.20, 9272393.5), + ("Nov", 4705.63, 8973284.04), + ("Dec", 4715.19, 8973284.04), + ] + electric_kwh = sum(row[1] for row in monthly) + electric_kbtu = electric_kwh * 3.412141633 + hot_kbtu = sum(row[2] for row in monthly) + total_kbtu = electric_kbtu + hot_kbtu + reported_area = 26000 + seed_area = 28050 + + page = pdf.add_page() + page.rect(0, 0, PAGE_W, PAGE_H, fill="F7F9FB") + page.rect(0, 560, PAGE_W, 232, fill="184D73") + page.rect(0, 560, PAGE_W, 10, fill="E2A12B") + page.text(MARGIN, 705, "ENERGY AUDIT", size=44, font="F2", color="FFFFFF") + page.text(MARGIN, 660, "SCREENING REPORT", size=38, font="F2", color="FFFFFF") + page.text(MARGIN, 620, "2258 25TH PLACE NE", size=21, font="F2", color="FFFFFF") + page.text(MARGIN, 596, "Washington, DC 20018 | 2022 benchmarking cycle", size=12, color="DDEAF2") + page.text(420, 740, "SEED", size=18, font="F2", color="FFFFFF") + page.text(420, 720, "benchmarking analysis", size=9, color="DDEAF2") + page.multiline(MARGIN, 520, "Prepared from SEED property, benchmarking, and meter data. This is a screening audit, not an onsite ASHRAE audit.", size=11, max_chars=96, leading=15, color="333333") + page.pill(MARGIN, 445, 156, 58, "Reported Site EUI", "4,192.8", "FFFFFF", "C3362B") + page.pill(MARGIN + 174, 445, 156, 58, "p95 Site EUI", "117.4", "FFFFFF", "E2A12B") + page.pill(MARGIN + 348, 445, 156, 58, "Rank", "1 of 2,750", "FFFFFF", "256FA6") + page.pill(MARGIN, 360, 156, 58, "Property type", "Warehouse", "FFFFFF", "256FA6") + page.pill(MARGIN + 174, 360, 156, 58, "Year built", "1950", "FFFFFF", "256FA6") + page.pill(MARGIN + 348, 360, 156, 58, "Ward", "5", "FFFFFF", "256FA6") + page.text(MARGIN, 260, "Primary issue to investigate", size=16, font="F2") + page.multiline(MARGIN, 235, "District hot water dominates the reported energy use. The meter records show about 108.9 million kBtu of district hot water and only about 40,289 kWh of electric use. This pattern is unusual for a non-refrigerated warehouse and should be reconciled before any capital work is scoped.", size=12, max_chars=78, leading=17) + page.text(MARGIN, 84, "Prepared July 8, 2026", size=9, color="555555") + page.text(420, 84, "Org 412 | Cycle 656", size=9, color="555555") + + page = pdf.add_page() + header(page, "Executive Summary", 1) + page.multiline(MARGIN, 675, "2258 25th Place NE is the highest Site EUI property in the 2022 DC benchmarking cycle among properties with non-null Site EUI. Its reported Site EUI is 4,192.8 kBtu/ft2/year, compared with a cycle median of 51.2 and a 95th percentile threshold of 117.4.", size=12, max_chars=83, leading=17) + y = 595 + y = bullet(page, MARGIN, y, "The property is listed as a Non-Refrigerated Warehouse, 28,050 ft2 in SEED, with a separate reported gross floor area of 26,000 ft2 in imported extra data.") + y = bullet(page, MARGIN, y - 12, "Reported status is Data Under Review by DOEE; ENERGY STAR score, story count, and onsite system details are not available in SEED.") + y = bullet(page, MARGIN, y - 12, "The meter data includes Electric - Grid and District Hot Water. District hot water accounts for roughly 99.9% of meter-derived site energy.") + y = bullet(page, MARGIN, y - 12, "Using the reported 26,000 ft2 area, meter-derived site energy reconciles to about 4,193 kBtu/ft2/year. Using the SEED canonical 28,050 ft2 area, it is about 3,886 kBtu/ft2/year.") + page.text(MARGIN, 365, "Key Quantities", size=15, font="F2") + table(page, MARGIN, 340, [ + ("PM Property ID", "PM26961358"), + ("Property view ID", "3285534"), + ("Reported Site EUI", "4,192.8 kBtu/ft2/year"), + ("Source EUI", "5,052.6 kBtu/ft2/year"), + ("Total GHG emissions", "7,241.2 mtCO2e"), + ("Annual district hot water", f"{fmt_num(hot_kbtu)} kBtu"), + ("Annual electricity", f"{fmt_num(electric_kwh)} kWh"), + ("Meter-derived total site energy", f"{fmt_num(total_kbtu)} kBtu"), + ]) + + page = pdf.add_page() + header(page, "Building Profile", 2) + table(page, MARGIN, 675, [ + ("Property name", "De Paris Enterprises Inc"), + ("Address", "2258 25TH PLACE NE, Washington, DC 20018"), + ("Owner", "DEPARIS REBECCA C"), + ("Property type", "Non-Refrigerated Warehouse"), + ("Year built", "1950"), + ("Ward / census tract", "Ward 5 / 11001011100"), + ("Latitude / longitude", "38.92138514 / -76.97164794"), + ("SEED gross floor area", "28,050 ft2"), + ("Reported gross floor area", "26,000 ft2"), + ("Metered areas, energy", "Whole Property"), + ("Water use", "114.9"), + ("Disadvantaged community flag", "False"), + ("Low income flag", "False"), + ]) + page.text(MARGIN, 350, "Benchmark Context", size=15, font="F2") + page.multiline(MARGIN, 326, "Comparison set: 2,750 properties in the 2022 cycle with non-null Site EUI. The building is the maximum observed Site EUI in that set. Because the source record is under review, this should be treated as a priority data and meter-boundary investigation.", size=10, max_chars=88, leading=14) + benchmark_axis(page, MARGIN, 245, 500) + page.pill(MARGIN, 115, 156, 58, "Median", "51.2", "F4F7FA", "5A6C7D") + page.pill(MARGIN + 174, 115, 156, 58, "p95", "117.4", "F4F7FA", "E2A12B") + page.pill(MARGIN + 348, 115, 156, 58, "Building", "4,192.8", "F4F7FA", "C3362B") + + page = pdf.add_page() + header(page, "Meter Analysis", 3) + page.text(MARGIN, 675, "Meters Found", size=15, font="F2") + table(page, MARGIN, 650, [ + ("18966", "Electric - Grid | Manual Entry | PM26961358"), + ("18967", "District Hot Water | Manual Entry | PM26961358"), + ]) + page.text(MARGIN, 570, "Annual Fuel Mix", size=15, font="F2") + mix_x, mix_y = MARGIN, 525 + page.rect(mix_x, mix_y, 500, 28, fill="DDEAF2") + elec_w = max(3, 500 * electric_kbtu / total_kbtu) + page.rect(mix_x, mix_y, elec_w, 28, fill="2F80ED") + page.rect(mix_x + elec_w, mix_y, 500 - elec_w, 28, fill="F2B13F") + page.text(mix_x, mix_y - 18, "Electricity: 0.13% of site energy after kWh-to-kBtu conversion", size=8, color="2F80ED") + page.text(mix_x + 270, mix_y - 18, "District hot water: 99.87%", size=8, color="9B660F") + page.text(MARGIN, 470, "Monthly Electricity Use (kWh)", size=12, font="F2") + bar_chart(page, MARGIN + 15, 315, 430, 130, [m[0] for m in monthly], [m[1] for m in monthly], "2F80ED") + page.text(MARGIN, 275, "Monthly District Hot Water (kBtu)", size=12, font="F2") + bar_chart(page, MARGIN + 15, 120, 430, 130, [m[0] for m in monthly], [m[2] for m in monthly], "F2B13F") + page.multiline(MARGIN, 78, "The district hot water profile is nearly flat and extremely large month to month. That points first to meter mapping, units, service boundary, or area normalization review; operational heating diagnostics come after the data is reconciled.", size=9, max_chars=92, leading=12) + + page = pdf.add_page() + header(page, "Preliminary Measures", 4) + page.multiline(MARGIN, 675, "These measures are screening recommendations based on SEED and meter data only. They should be confirmed through utility bills, meter configuration, operator interviews, drawings, and an onsite walkthrough.", size=11, max_chars=86, leading=15) + y = 610 + measures = [ + ("1. Reconcile data before scoping retrofits", "Verify district hot water units, meter ownership, service boundary, and whether the meter serves only this property. Confirm whether 26,000 ft2 or 28,050 ft2 should be used for benchmarking."), + ("2. Investigate district hot water load", "The dominant load is district hot water, not electric use. Review heat exchanger controls, valve leakage, simultaneous heating/cooling, domestic hot water recirculation, and any process or tenant loads."), + ("3. Review schedules and setpoints", "If the hot water load is legitimate, check occupied/unoccupied schedules, temperature reset, night setback, and weekend operation for warehouse spaces."), + ("4. Envelope and loading-door leakage", "For warehouse use, inspect overhead doors, vestibules, dock seals, roof insulation, and uncontrolled infiltration paths that can drive heating demand."), + ("5. Lighting and plug/process loads", "Electricity is small relative to thermal energy, but LED lighting, controls, and tenant process-load review remain practical low-disruption measures."), + ("6. Add submetering or meter QA workflow", "A single unusually large thermal stream deserves ongoing meter QA, especially if the property remains under review in the benchmarking program."), + ] + for title, body in measures: + page.text(MARGIN, y, title, size=12, font="F2", color="184D73") + y = page.multiline(MARGIN + 18, y - 18, body, size=10, max_chars=82, leading=14) + y -= 16 + + page = pdf.add_page() + header(page, "Data Gaps", 5) + page.text(MARGIN, 675, "Available in SEED", size=15, font="F2") + y = 645 + for item in [ + "Address, location, owner, ward, parcel/lot, property type, year built, floor area, reporting status.", + "Reported Site EUI, weather-normalized Site EUI, Source EUI, total GHG emissions, and water use extra data.", + "Monthly imported meter records for Electric - Grid and District Hot Water for calendar year 2022.", + ]: + y = bullet(page, MARGIN, y, item) + y -= 8 + page.text(MARGIN, 520, "Not available in SEED for this property", size=15, font="F2") + y = 490 + for item in [ + "ENERGY STAR score, number of stories, building count, conditioned floor area, building systems, equipment age, controls sequences, and operating schedules.", + "Audit photos, onsite observations, utility tariff costs, comfort/maintenance complaints, and capital cost estimates.", + "A confirmed explanation for why the canonical gross floor area and reported gross floor area differ.", + ]: + y = bullet(page, MARGIN, y, item) + y -= 8 + page.text(MARGIN, 365, "Recommended Next Steps", size=15, font="F2") + table(page, MARGIN, 340, [ + ("1", "Pull original utility bills or Portfolio Manager export for PM26961358."), + ("2", "Confirm whether the district hot water meter is whole-property, shared, or misassigned."), + ("3", "Confirm the correct gross floor area and update SEED if needed."), + ("4", "Perform a focused walkthrough of thermal systems, controls, and warehouse envelope conditions."), + ("5", "After data reconciliation, estimate savings and costs for the highest-confidence measures."), + ], widths=(35, 465)) + page.multiline(MARGIN, 145, "Screening conclusion: this property is less a normal high-EUI case than a data-reconciliation and thermal-meter-boundary case. If the district hot water readings are valid and assigned correctly, the building warrants a focused thermal systems audit.", size=11, max_chars=88, leading=15) + + return pdf + + +if __name__ == "__main__": + out = Path("output/pdf/2258_25th_place_ne_energy_audit_screening_report.pdf") + out.parent.mkdir(parents=True, exist_ok=True) + build_report().save(out) + print(out) diff --git a/output/create_better_owner_audit_pdfs.py b/output/create_better_owner_audit_pdfs.py new file mode 100644 index 0000000..f5131d7 --- /dev/null +++ b/output/create_better_owner_audit_pdfs.py @@ -0,0 +1,994 @@ +from __future__ import annotations + +import json +import re +from pathlib import Path + + +PAGE_W = 612 +PAGE_H = 792 +MARGIN = 42 +DATA_PATH = Path("org412_cycle656_profile316_better.json") +OUT_DIR = Path("output/pdf/better_owner_audits") +METER_DIR = Path("output/data/top5_meters") +IMAGE_DIR = Path("output/images") +BUILDING_IMAGE_PATHS = { + 3282590: IMAGE_DIR / "1620_i_street_osm_3d.jpg", +} +OPENSTUDIO_DIR = Path("output/openstudio_1620") +OPENSTUDIO_MODEL_PATHS = { + 3282590: { + "calibration_image": OPENSTUDIO_DIR / "1620_i_street_openstudio_calibration.jpg", + "zoning_image": OPENSTUDIO_DIR / "1620_i_street_perimeter_core_10_story.jpg", + "calibration_json": OPENSTUDIO_DIR / "1620_i_street_openstudio_calibration.json", + }, +} + + +def esc(text: object) -> str: + value = "" if text is None else str(text) + return value.replace("\\", "\\\\").replace("(", "\\(").replace(")", "\\)") + + +def safe_name(text: object) -> str: + value = re.sub(r"[^a-zA-Z0-9]+", "_", str(text).strip().lower()) + return re.sub(r"_+", "_", value).strip("_") or "building" + + +def display_name(column_name: str) -> str: + name = re.sub(r"_\d+$", "", column_name) + name = name.removeprefix("better_recommendation_") + return name.replace("_", " ") + + +def active(value: object) -> bool: + if value in (None, ""): + return False + try: + return float(value) != 0 + except (TypeError, ValueError): + return str(value).strip().casefold() in {"true", "yes", "y", "1"} + + +def num(value: object, digits: int = 0, prefix: str = "", suffix: str = "") -> str: + if value in (None, ""): + return "-" + try: + number = float(value) + except (TypeError, ValueError): + return str(value) + if digits == 0: + return f"{prefix}{number:,.0f}{suffix}" + return f"{prefix}{number:,.{digits}f}{suffix}" + + +def to_float(value: object) -> float | None: + if value in (None, ""): + return None + try: + return float(value) + except (TypeError, ValueError): + return None + + +def wrap(text: str, max_chars: int) -> list[str]: + words = str(text).split() + lines: list[str] = [] + current = "" + for word in words: + candidate = word if not current else f"{current} {word}" + if len(candidate) <= max_chars: + current = candidate + else: + if current: + lines.append(current) + current = word + if current: + lines.append(current) + return lines + + +def jpeg_dimensions(path: Path) -> tuple[int, int]: + data = path.read_bytes() + index = 2 + while index < len(data): + if data[index] != 0xFF: + index += 1 + continue + marker = data[index + 1] + index += 2 + if marker in {0xD8, 0xD9}: + continue + length = int.from_bytes(data[index : index + 2], "big") + if marker in {0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF}: + height = int.from_bytes(data[index + 3 : index + 5], "big") + width = int.from_bytes(data[index + 5 : index + 7], "big") + return width, height + index += length + raise ValueError(f"Could not read JPEG dimensions for {path}") + + +class Page: + def __init__(self) -> None: + self.ops: list[str] = [] + self.images: list[Path] = [] + + def raw(self, op: str) -> None: + self.ops.append(op) + + def color(self, hex_color: str) -> None: + hex_color = hex_color.strip("#") + r = int(hex_color[0:2], 16) / 255 + g = int(hex_color[2:4], 16) / 255 + b = int(hex_color[4:6], 16) / 255 + self.raw(f"{r:.4f} {g:.4f} {b:.4f} rg") + self.raw(f"{r:.4f} {g:.4f} {b:.4f} RG") + + def line_width(self, width: float) -> None: + self.raw(f"{width:.2f} w") + + def rect(self, x: float, y: float, w: float, h: float, fill: str | None = None, stroke: str | None = None) -> None: + if fill: + self.color(fill) + self.raw(f"{x:.2f} {y:.2f} {w:.2f} {h:.2f} re f") + if stroke: + self.color(stroke) + self.raw(f"{x:.2f} {y:.2f} {w:.2f} {h:.2f} re S") + + def line(self, x1: float, y1: float, x2: float, y2: float, color: str = "111111", width: float = 1) -> None: + self.color(color) + self.line_width(width) + self.raw(f"{x1:.2f} {y1:.2f} m {x2:.2f} {y2:.2f} l S") + + def text(self, x: float, y: float, text: object, size: int = 10, font: str = "F1", color: str = "111111") -> None: + self.color(color) + self.raw(f"BT /{font} {size} Tf {x:.2f} {y:.2f} Td ({esc(text)}) Tj ET") + + def multiline( + self, + x: float, + y: float, + text: str, + size: int = 10, + max_chars: int = 80, + leading: float | None = None, + font: str = "F1", + color: str = "111111", + ) -> float: + leading = leading or size * 1.35 + for line in wrap(text, max_chars): + self.text(x, y, line, size=size, font=font, color=color) + y -= leading + return y + + def metric(self, x: float, y: float, w: float, label: str, value: str, accent: str) -> None: + self.rect(x, y, w, 58, fill="FFFFFF", stroke="D7DEE8") + self.rect(x, y, 5, 58, fill=accent) + self.text(x + 14, y + 38, label.upper(), size=8, font="F2", color="52616B") + self.text(x + 14, y + 13, value, size=17, font="F2", color="111827") + + def image(self, x: float, y: float, w: float, h: float, path: Path) -> None: + self.images.append(path) + name = f"Im{len(self.images)}" + self.raw(f"q {w:.2f} 0 0 {h:.2f} {x:.2f} {y:.2f} cm /{name} Do Q") + + +class PDF: + def __init__(self) -> None: + self.pages: list[Page] = [] + + def add_page(self) -> Page: + page = Page() + self.pages.append(page) + return page + + def save(self, path: Path) -> None: + objects: list[bytes] = [ + b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>", + b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >>", + ] + page_objects: list[tuple[int, int, list[tuple[str, int]]]] = [] + for page in self.pages: + image_refs: list[tuple[str, int]] = [] + for index, image_path in enumerate(page.images, start=1): + image_data = image_path.read_bytes() + image_width, image_height = jpeg_dimensions(image_path) + image_id = len(objects) + 1 + image_refs.append((f"Im{index}", image_id)) + objects.append( + b"<< /Type /XObject /Subtype /Image /Width " + + str(image_width).encode() + + b" /Height " + + str(image_height).encode() + + b" /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length " + + str(len(image_data)).encode() + + b" >>\nstream\n" + + image_data + + b"\nendstream", + ) + stream = "\n".join(page.ops).encode("latin-1", "replace") + content = b"<< /Length " + str(len(stream)).encode() + b" >>\nstream\n" + stream + b"\nendstream" + content_id = len(objects) + 1 + objects.append(content) + page_id = len(objects) + 1 + page_objects.append((page_id, content_id, image_refs)) + objects.append(b"") + + pages_id = len(objects) + 1 + kids = " ".join(f"{page_id} 0 R" for page_id, _, _ in page_objects).encode() + objects.append(b"<< /Type /Pages /Kids [" + kids + b"] /Count " + str(len(page_objects)).encode() + b" >>") + catalog_id = len(objects) + 1 + objects.append(b"<< /Type /Catalog /Pages " + str(pages_id).encode() + b" 0 R >>") + + for page_id, content_id, image_refs in page_objects: + xobjects = b"" + if image_refs: + pairs = " ".join(f"/{name} {obj_id} 0 R" for name, obj_id in image_refs).encode() + xobjects = b" /XObject << " + pairs + b" >>" + objects[page_id - 1] = ( + b"<< /Type /Page /Parent " + + str(pages_id).encode() + + b" 0 R /MediaBox [0 0 612 792] " + + b"/Resources << /Font << /F1 1 0 R /F2 2 0 R >>" + + xobjects + + b" >> " + + b"/Contents " + + str(content_id).encode() + + b" 0 R >>" + ) + + out = bytearray(b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n") + offsets = [0] + for obj_id, obj in enumerate(objects, start=1): + offsets.append(len(out)) + out.extend(f"{obj_id} 0 obj\n".encode()) + out.extend(obj) + out.extend(b"\nendobj\n") + xref = len(out) + out.extend(f"xref\n0 {len(objects) + 1}\n".encode()) + out.extend(b"0000000000 65535 f \n") + for offset in offsets[1:]: + out.extend(f"{offset:010d} 00000 n \n".encode()) + out.extend( + b"trailer\n<< /Size " + + str(len(objects) + 1).encode() + + b" /Root " + + str(catalog_id).encode() + + b" 0 R >>\nstartxref\n" + + str(xref).encode() + + b"\n%%EOF\n", + ) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(out) + + +RECOMMENDATION_NOTES = { + "reduce lighting load": "Review fixture types, controls, schedules, common areas, parking, and tenant lighting density.", + "reduce plug loads": "Inventory tenant equipment, common plug loads, vending, IT/server loads, and after-hours usage.", + "reduce equipment schedules": "Confirm occupied/unoccupied schedules, weekend operation, overrides, and BAS trend logs.", + "decrease heating setpoints": "Review winter setpoints, night setback, warm-up routines, and simultaneous heating/cooling.", + "increase cooling setpoints": "Review summer setpoints, deadbands, after-hours cooling, and tenant comfort constraints.", + "decrease infiltration": "Inspect entry vestibules, loading doors, envelope leakage, shafts, stair pressurization, and exhaust imbalance.", + "increase cooling system efficiency": "Review cooling equipment age, economizer operation, resets, condenser cleaning, and controls.", + "increase heating system efficiency": "Review boilers, heat pumps, steam/hot water distribution, reset schedules, and maintenance records.", + "add wall/ceiling/roof insulation": "Screen envelope assemblies and roof condition before scoping envelope measures.", + "upgrade windows to improve thermal efficiency": "Review window U-factor, air leakage, condensation, comfort complaints, and replacement timing.", + "upgrade windows to reduce solar heat gain": "Review glazing SHGC, facade orientation, solar control, and cooling complaints.", + "ensure adequate ventilation rate": "Verify outdoor air rates, demand-control ventilation, economizer minimum positions, and code constraints.", + "use high efficiency heat pump for heating": "Evaluate electrification feasibility, service capacity, distribution constraints, and refrigerant strategy.", + "upgrade to sustainable resources for water heating": "Review domestic hot water loads, heat pump water heating, solar thermal, and heat recovery options.", +} + + +def load_ranked_buildings() -> list[dict]: + rows = json.loads(DATA_PATH.read_text())["656"] + rec_cols = [key for key in rows[0] if key.startswith("better_recommendation_")] + ranked = [] + for row in rows: + recs = [display_name(col) for col in rec_cols if active(row.get(col))] + ranked.append( + { + "rank_score": len(recs), + "property_view_id": row.get("property_view_id"), + "property_state_id": row.get("property_state_id"), + "pm_property_id": row.get("pm_property_id_90594"), + "address": row.get("address_line_1_90603") or row.get("Reported Address_90689"), + "city": row.get("city_90607"), + "state": row.get("state_90609"), + "postal_code": row.get("postal_code_90613"), + "name": row.get("property_name_90616") or row.get("address_line_1_90603"), + "type": row.get("property_type_90633") or row.get("Property Tyoe (EPA)_90696"), + "gfa": row.get("gross_floor_area_90629") or row.get("gross_floor_area_reported_90699"), + "site_eui": row.get("site_eui_weather_normalized_90650") or row.get("site_eui_90649"), + "source_eui": row.get("source_eui_weather_normalized_90653") or row.get("source_eui_90652"), + "energy_score": row.get("energy_score_90631"), + "year_built": row.get("year_built_90639"), + "ward": row.get("Ward_90687"), + "reporting_status": row.get("Reporting Status_90683"), + "metered_areas": row.get("Metered Areas (Energy)_90716"), + "longitude": row.get("Unused X_90667"), + "latitude": row.get("Unused Y_90669"), + "long_lat": row.get("long_lat"), + "footprint": row.get("property_footprint_90623") or row.get("centroid"), + "bounding_box": row.get("bounding_box"), + "cost_savings": row.get("better_cost_savings_combined_90835"), + "energy_savings": row.get("better_energy_savings_combined_90836"), + "ghg_reductions": row.get("better_ghg_reductions_combined_90837"), + "valid_electric": row.get("better_valid_model_electricity_90838"), + "valid_fuel": row.get("better_valid_model_fuel_90839"), + "min_r2": row.get("better_min_model_r_squared_90848"), + "recommendations": recs, + }, + ) + ranked.sort(key=lambda item: (item["rank_score"], item["cost_savings"] or -1), reverse=True) + return ranked + + +def load_meter_summary(property_view_id: int) -> dict: + meters_path = METER_DIR / f"{property_view_id}_meters.json" + if not meters_path.exists(): + return {"meters": [], "monthly": [], "fuel_totals": {}, "annual_total": 0} + + meters = json.loads(meters_path.read_text()) + fuel_totals: dict[str, float] = {} + monthly: dict[str, float] = {} + meter_rows = [] + for meter in meters: + meter_id = meter["id"] + readings_path = METER_DIR / f"{property_view_id}_meter_{meter_id}_readings.json" + readings = json.loads(readings_path.read_text()) if readings_path.exists() else [] + annual = 0.0 + for reading in readings: + start = str(reading.get("start_time", "")) + if not start.startswith("2022"): + continue + value = to_float(reading.get("reading")) or 0.0 + annual += value + month = start[5:7] + monthly[month] = monthly.get(month, 0.0) + value + fuel_type = meter.get("type") or "Unknown" + fuel_totals[fuel_type] = fuel_totals.get(fuel_type, 0.0) + annual + meter_rows.append( + { + "id": meter_id, + "type": fuel_type, + "alias": meter.get("alias") or f"Meter {meter_id}", + "readings": len(readings), + "annual": annual, + }, + ) + + ordered_months = [ + ("Jan", monthly.get("01", 0.0)), + ("Feb", monthly.get("02", 0.0)), + ("Mar", monthly.get("03", 0.0)), + ("Apr", monthly.get("04", 0.0)), + ("May", monthly.get("05", 0.0)), + ("Jun", monthly.get("06", 0.0)), + ("Jul", monthly.get("07", 0.0)), + ("Aug", monthly.get("08", 0.0)), + ("Sep", monthly.get("09", 0.0)), + ("Oct", monthly.get("10", 0.0)), + ("Nov", monthly.get("11", 0.0)), + ("Dec", monthly.get("12", 0.0)), + ] + return { + "meters": meter_rows, + "monthly": ordered_months, + "fuel_totals": fuel_totals, + "annual_total": sum(fuel_totals.values()), + } + + +def parse_point(wkt: object) -> tuple[float, float] | None: + match = re.search(r"POINT\s*\(\s*([-\d.]+)\s+([-\d.]+)\s*\)", str(wkt or "")) + if not match: + return None + return float(match.group(1)), float(match.group(2)) + + +def parse_polygon(wkt: object) -> list[tuple[float, float]]: + match = re.search(r"POLYGON\s*\(\((.*?)\)\)", str(wkt or "")) + if not match: + return [] + points = [] + for pair in match.group(1).split(","): + parts = pair.strip().split() + if len(parts) >= 2: + points.append((float(parts[0]), float(parts[1]))) + return points + + +def google_maps_3d_url(building: dict) -> str: + lat = to_float(building.get("latitude")) + lon = to_float(building.get("longitude")) + point = parse_point(building.get("long_lat")) + if point and (lat is None or lon is None): + lon, lat = point + if lat is None or lon is None: + query = str(building.get("address") or "").replace(" ", "+") + return f"https://www.google.com/maps/search/?api=1&query={query}" + return f"https://www.google.com/maps/@{lat:.7f},{lon:.7f},20z/data=!3m1!1e3" + + +def header(page: Page, title: str, subtitle: str, page_no: int) -> None: + page.rect(0, 746, PAGE_W, 46, fill="0B94CF") + page.rect(360, 746, 252, 46, fill="087CB2") + page.text(MARGIN, 764, title, size=19, font="F2", color="FFFFFF") + page.text(MARGIN, 750, subtitle, size=9, color="EAF7FC") + page.text(520, 764, "SEED", size=15, font="F2", color="FFFFFF") + page.text(520, 750, f"page {page_no}", size=8, color="EAF7FC") + + +def small_table(page: Page, x: float, y: float, rows: list[tuple[str, str]], key_w: float = 145, val_w: float = 365) -> float: + row_h = 21 + for i, (key, value) in enumerate(rows): + page.rect(x, y - row_h + 4, key_w + val_w, row_h, fill="F4F7FA" if i % 2 == 0 else "FFFFFF") + page.text(x + 8, y - 11, key, size=8, font="F2", color="3F4854") + page.text(x + key_w + 8, y - 11, value, size=8, color="111827") + y -= row_h + return y + + +def bullet(page: Page, x: float, y: float, text: str, max_chars: int = 82, color: str = "0B94CF") -> float: + page.rect(x, y - 3, 4, 4, fill=color) + return page.multiline(x + 13, y - 5, text, size=9, max_chars=max_chars, leading=12) + + +def bar_chart(page: Page, x: float, y: float, w: float, h: float, labels: list[str], values: list[float], color: str) -> None: + max_value = max(values) if values else 1 + max_value = max(max_value, 1) + page.line(x, y, x, y + h, color="334155", width=0.7) + page.line(x, y, x + w, y, color="334155", width=0.7) + gap = 5 + bar_w = (w - gap * (len(values) - 1)) / max(len(values), 1) + for index, value in enumerate(values): + bx = x + index * (bar_w + gap) + bh = h * value / max_value + page.rect(bx, y, bar_w, bh, fill=color) + page.text(bx - 1, y - 14, labels[index], size=6, color="334155") + page.text(x + w + 8, y + h - 4, num(max_value, 0), size=7, color="52616B") + page.text(x + w + 8, y - 2, "0", size=7, color="52616B") + + +def stacked_fuel_bar(page: Page, x: float, y: float, w: float, h: float, fuel_totals: dict[str, float]) -> None: + colors = { + "Electric - Grid": "0B94CF", + "Natural Gas": "D9901A", + "District Hot Water": "B42318", + "District Chilled Water": "2563A6", + "Custom Meter": "7C3AED", + } + total = sum(fuel_totals.values()) or 1 + page.rect(x, y, w, h, fill="DDEAF2") + cursor = x + for fuel, value in sorted(fuel_totals.items(), key=lambda item: item[1], reverse=True): + width = w * value / total + page.rect(cursor, y, width, h, fill=colors.get(fuel, "52616B")) + cursor += width + label_y = y - 18 + label_x = x + for fuel, value in sorted(fuel_totals.items(), key=lambda item: item[1], reverse=True)[:4]: + page.rect(label_x, label_y + 2, 7, 7, fill=colors.get(fuel, "52616B")) + page.text(label_x + 11, label_y, f"{fuel}: {value / total:.0%}", size=7, color="334155") + label_x += 130 + + +def draw_location_panel(page: Page, building: dict, x: float, y: float, w: float, h: float) -> None: + lat = to_float(building.get("latitude")) + lon = to_float(building.get("longitude")) + point = parse_point(building.get("long_lat")) + if point and (lat is None or lon is None): + lon, lat = point + footprint = parse_polygon(building.get("footprint")) + bbox = parse_polygon(building.get("bounding_box")) + + page.rect(x, y, w, h, fill="EAF7FC", stroke="B8D9E8") + for i in range(1, 6): + gx = x + w * i / 6 + gy = y + h * i / 6 + page.line(gx, y, gx, y + h, color="C8E5F0", width=0.4) + page.line(x, gy, x + w, gy, color="C8E5F0", width=0.4) + + page.rect(x + 24, y + 22, w - 48, h - 44, fill="DDEAF2", stroke="FFFFFF") + page.line(x + 24, y + 70, x + w - 24, y + h - 60, color="FFFFFF", width=8) + page.line(x + 70, y + 22, x + w - 70, y + h - 22, color="FFFFFF", width=6) + page.line(x + 30, y + h - 86, x + w - 34, y + h - 40, color="9ECFE4", width=4) + + map_points = footprint or bbox + if map_points: + min_lon = min(p[0] for p in map_points) + max_lon = max(p[0] for p in map_points) + min_lat = min(p[1] for p in map_points) + max_lat = max(p[1] for p in map_points) + if max_lon == min_lon: + max_lon += 0.0001 + min_lon -= 0.0001 + if max_lat == min_lat: + max_lat += 0.0001 + min_lat -= 0.0001 + sx = (w - 110) / (max_lon - min_lon) + sy = (h - 96) / (max_lat - min_lat) + coords = [] + for px, py in map_points: + mx = x + 55 + (px - min_lon) * sx + my = y + 48 + (py - min_lat) * sy + coords.append((mx, my)) + if len(coords) >= 3: + page.color("0B94CF") + page.raw(f"{coords[0][0]:.2f} {coords[0][1]:.2f} m") + for px, py in coords[1:]: + page.raw(f"{px:.2f} {py:.2f} l") + page.raw("h f") + page.color("183F6D") + page.raw(f"{coords[0][0] + 10:.2f} {coords[0][1] + 14:.2f} m") + for px, py in coords[1:]: + page.raw(f"{px + 10:.2f} {py + 14:.2f} l") + page.raw("h f") + page.color("1B6257") + page.raw(f"{coords[0][0]:.2f} {coords[0][1]:.2f} m") + for px, py in coords[:4]: + page.raw(f"{px + 10:.2f} {py + 14:.2f} l") + page.raw("S") + else: + cx, cy = x + w / 2, y + h / 2 + page.rect(cx - 24, cy - 16, 48, 32, fill="0B94CF") + page.rect(cx - 14, cy - 6, 48, 32, fill="183F6D") + page.rect(cx - 4, cy + 4, 48, 32, fill="1B6257") + + page.rect(x + w / 2 - 4, y + h / 2 - 4, 8, 8, fill="B42318") + if lat is not None and lon is not None: + page.text(x + 14, y + 12, f"{lat:.6f}, {lon:.6f}", size=8, color="334155") + + +def draw_recommendation_bar(page: Page, count: int) -> None: + x, y, w, h = MARGIN, 322, 510, 16 + page.rect(x, y, w, h, fill="DDEAF2") + page.rect(x, y, w * min(count, 12) / 12, h, fill="D9901A" if count >= 8 else "0B94CF") + for tick in [4, 8, 12]: + tx = x + w * tick / 12 + page.line(tx, y - 4, tx, y + h + 4, color="FFFFFF", width=1) + page.text(x, y + 24, "Active BETTER recommendations", size=8, font="F2", color="52616B") + + +def draw_cover(pdf: PDF, building: dict, rank: int) -> None: + page = pdf.add_page() + page.rect(0, 0, PAGE_W, PAGE_H, fill="FFFFFF") + page.rect(0, 560, PAGE_W, 232, fill="183F6D") + page.rect(0, 560, PAGE_W, 9, fill="D9901A") + page.text(MARGIN, 716, "OWNER AUDIT", size=42, font="F2", color="FFFFFF") + page.text(MARGIN, 674, "SCREENING REPORT", size=35, font="F2", color="FFFFFF") + page.multiline(MARGIN, 626, building["name"], size=19, max_chars=42, leading=22, font="F2", color="FFFFFF") + page.text(MARGIN, 594, f"{building['address']} | 2022 benchmarking cycle", size=11, color="DDEAF2") + page.text(466, 738, "SEED", size=18, font="F2", color="FFFFFF") + page.text(466, 718, "BETTER audit triage", size=8, color="DDEAF2") + + page.multiline( + MARGIN, + 525, + "Prepared from SEED and BETTER outputs. This screening packet helps owners evaluate candidate measures; it is not an onsite ASHRAE audit or engineering design.", + size=10, + max_chars=92, + leading=14, + color="334155", + ) + + page.metric(MARGIN, 445, 156, "Recommendation rank", f"#{rank}", "D9901A") + page.metric(MARGIN + 174, 445, 156, "Active recs", str(building["rank_score"]), "B42318") + page.metric(MARGIN + 348, 445, 156, "Cost savings", num(building["cost_savings"], 0, "$"), "1B6257") + page.metric(MARGIN, 360, 156, "Property type", str(building["type"])[:18], "0B94CF") + page.metric(MARGIN + 174, 360, 156, "Floor area", num(building["gfa"], 0, suffix=" ft2"), "0B94CF") + page.metric(MARGIN + 348, 360, 156, "Site EUI", num(building["site_eui"], 1), "0B94CF") + draw_recommendation_bar(page, building["rank_score"]) + + page.text(MARGIN, 302, "Owner evaluation focus", size=15, font="F2", color="111827") + y = 277 + y = bullet(page, MARGIN, y, f"Start with the {building['rank_score']} BETTER recommendations, then confirm which measures are feasible for this property's systems, leases, and capital plan.") + y = bullet(page, MARGIN, y - 10, "Validate utility data, meter boundaries, occupancy schedules, and controls before assigning capital budgets.") + y = bullet(page, MARGIN, y - 10, "Use this packet as a triage handoff: it identifies what to ask for, what to inspect, and which recommendations deserve owner review.") + + page.text(MARGIN, 92, "Prepared July 10, 2026", size=8, color="52616B") + page.text(402, 92, "Org 412 | Cycle 656 | BPS - DC", size=8, color="52616B") + + +def draw_detail(pdf: PDF, building: dict, rank: int) -> None: + page = pdf.add_page() + header(page, "Building Status + BETTER Recommendations", f"Rank #{rank}: {building['name']}", 2) + + page.text(MARGIN, 710, "Building snapshot", size=15, font="F2") + small_table( + page, + MARGIN, + 688, + [ + ("Address", str(building["address"])), + ("PM Property ID", str(building["pm_property_id"])), + ("Property view ID", str(building["property_view_id"])), + ("Type", str(building["type"])), + ("Year built / Ward", f"{num(building['year_built'])} / {building['ward'] or '-'}"), + ("Reporting status", str(building["reporting_status"] or "-")), + ("Metered areas", str(building["metered_areas"] or "-")), + ("Energy score", num(building["energy_score"], 0)), + ("Source EUI", num(building["source_eui"], 1)), + ], + ) + + page.text(MARGIN, 465, "BETTER outputs", size=15, font="F2") + small_table( + page, + MARGIN, + 443, + [ + ("Active recommendations", str(building["rank_score"])), + ("Combined energy savings", num(building["energy_savings"], 0, suffix=" kBtu")), + ("Combined cost savings", num(building["cost_savings"], 0, "$")), + ("GHG reductions", num(building["ghg_reductions"], 1, suffix=" mtCO2e")), + ("Electric model valid", str(building["valid_electric"])), + ("Fuel model valid", str(building["valid_fuel"])), + ("Minimum model R2", num(building["min_r2"], 2)), + ], + ) + + page.text(MARGIN, 260, "Priority recommendation notes", size=15, font="F2") + y = 236 + for index, rec in enumerate(building["recommendations"][:12], start=1): + note = RECOMMENDATION_NOTES.get(rec, "Review feasibility, operating constraints, savings estimate, and interaction with other measures.") + page.text(MARGIN, y, f"{index}. {rec}", size=10, font="F2", color="183F6D") + y = page.multiline(MARGIN + 18, y - 14, note, size=8, max_chars=82, leading=10, color="334155") + y -= 5 + if y < 54: + break + + +def draw_complete_recommendations(pdf: PDF, building: dict, rank: int) -> None: + page = pdf.add_page() + header(page, "Complete BETTER Recommendation List", f"Rank #{rank}: {building['name']}", 3) + page.multiline( + MARGIN, + 708, + f"This building has {building['rank_score']} active BETTER recommendation flags. Use this page as the owner's complete review list; the previous page includes detailed notes for the first priority items.", + size=11, + max_chars=88, + leading=15, + ) + + left_x = MARGIN + right_x = 320 + y_left = 642 + y_right = 642 + for index, rec in enumerate(building["recommendations"], start=1): + x = left_x if index <= 6 else right_x + y = y_left if index <= 6 else y_right + page.rect(x, y - 3, 5, 5, fill="D9901A") + next_y = page.multiline(x + 15, y, f"{index}. {rec}", size=11, max_chars=38, leading=14, font="F2", color="183F6D") + if index <= 6: + y_left = next_y - 16 + else: + y_right = next_y - 16 + + page.rect(MARGIN, 92, 510, 184, fill="F7F6F3", stroke="D7DEE8") + page.text(MARGIN + 16, 250, "How to use this list", size=12, font="F2", color="111827") + y = 226 + for item in [ + "Confirm whether each flag is operational, controls-related, envelope-related, or capital-project related.", + "Bundle interacting measures before estimating savings, especially HVAC setpoints, schedules, and equipment efficiency.", + "Ask the owner or operator which measures are already planned, recently completed, or infeasible because of tenant constraints.", + ]: + y = bullet(page, MARGIN + 16, y, item, max_chars=78, color="183F6D") + y -= 6 + + +def draw_meter_consumption(pdf: PDF, building: dict, rank: int) -> None: + page = pdf.add_page() + header(page, "2022 Meter Consumption", f"Rank #{rank}: {building['name']}", 4) + summary = load_meter_summary(building["property_view_id"]) + annual_total = summary["annual_total"] + gfa = to_float(building.get("gfa")) or 0.0 + intensity = annual_total / gfa if gfa else 0.0 + + page.multiline( + MARGIN, + 708, + "This page uses SEED meter readings for calendar year 2022. Values below are reported in kBtu from the SEED meter endpoint and should be reconciled with the owner utility bills before project scoping.", + size=10, + max_chars=90, + leading=14, + color="334155", + ) + + page.metric(MARGIN, 632, 156, "Annual meter use", num(annual_total, 0, suffix=" kBtu"), "183F6D") + page.metric(MARGIN + 174, 632, 156, "Meter intensity", num(intensity, 1, suffix=" kBtu/ft2"), "0B94CF") + page.metric(MARGIN + 348, 632, 156, "Meters found", str(len(summary["meters"])), "1B6257") + + page.text(MARGIN, 570, "Fuel split", size=14, font="F2", color="111827") + if summary["fuel_totals"]: + stacked_fuel_bar(page, MARGIN, 536, 510, 20, summary["fuel_totals"]) + else: + page.rect(MARGIN, 520, 510, 42, fill="F7F6F3", stroke="D7DEE8") + page.text(MARGIN + 16, 540, "No meter readings were available in the local SEED export.", size=9, color="334155") + + page.text(MARGIN, 470, "Monthly 2022 profile", size=14, font="F2", color="111827") + labels = [label for label, _ in summary["monthly"]] + values = [value for _, value in summary["monthly"]] + bar_chart(page, MARGIN + 2, 328, 448, 112, labels, values, "0B94CF") + page.text(MARGIN + 462, 438, "kBtu", size=8, font="F2", color="52616B") + + page.text(MARGIN, 286, "Meter inventory", size=14, font="F2", color="111827") + rows = [] + for meter in summary["meters"][:7]: + rows.append( + ( + f"{meter['id']} | {meter['type']}", + f"{num(meter['annual'], 0, suffix=' kBtu')} | {meter['readings']} readings", + ), + ) + if not rows: + rows = [("SEED meters", "No meters found for this property view in the local export")] + small_table(page, MARGIN, 264, rows, key_w=205, val_w=305) + + page.rect(MARGIN, 88, 510, 60, fill="F7F6F3", stroke="D7DEE8") + page.text(MARGIN + 16, 126, "Owner follow-up", size=11, font="F2", color="111827") + page.multiline( + MARGIN + 16, + 108, + "If the meter total, EUI, or fuel mix looks surprising, verify meter boundaries, tenant submeters, vacancies, bulk fuel, and Portfolio Manager import status before approving a measure package.", + size=8, + max_chars=95, + leading=11, + color="334155", + ) + + +def draw_location_page(pdf: PDF, building: dict, rank: int) -> None: + page = pdf.add_page() + header(page, "Building Location + 3D Map Link", f"Rank #{rank}: {building['name']}", 5) + + property_view_id = int(building["property_view_id"]) + image_path = BUILDING_IMAGE_PATHS.get(property_view_id) + has_building_image = image_path is not None and image_path.exists() + + page.multiline( + MARGIN, + 708, + "The image below uses open building and street geometry where available. The Google Maps link opens the same location in satellite/3D-capable map view for owner review.", + size=10, + max_chars=90, + leading=14, + color="334155", + ) + + lat = to_float(building.get("latitude")) + lon = to_float(building.get("longitude")) + point = parse_point(building.get("long_lat")) + if point and (lat is None or lon is None): + lon, lat = point + maps_url = google_maps_3d_url(building) + + if has_building_image: + page.rect(70, 186, 472, 472, fill="FFFFFF", stroke="D7DEE8") + page.image(72, 188, 468, 468, image_path) + page.rect(70, 186, 472, 472, stroke="183F6D") + + page.rect(MARGIN, 78, 510, 86, fill="F7F6F3", stroke="D7DEE8") + page.text(MARGIN + 16, 140, "Image source and owner review link", size=11, font="F2", color="111827") + page.multiline( + MARGIN + 16, + 122, + f"{building['address']} | {lat:.7f}, {lon:.7f}. Image rendered from OpenStreetMap geometry; map data (C) OpenStreetMap contributors. Google Maps 3D link: {maps_url}", + size=8, + max_chars=98, + leading=11, + color="334155", + ) + return + + draw_location_panel(page, building, MARGIN, 386, 510, 260) + + page.text(MARGIN, 350, "Location details", size=14, font="F2", color="111827") + y = small_table( + page, + MARGIN, + 328, + [ + ("Address", str(building["address"])), + ("City / state", f"{building.get('city') or 'Washington'} / {building.get('state') or 'DC'}"), + ("Latitude / longitude", f"{lat:.7f}, {lon:.7f}" if lat is not None and lon is not None else "-"), + ("SEED footprint", "Available" if parse_polygon(building.get("footprint")) else "Not available in this export"), + ], + ) + + page.text(MARGIN, y - 8, "Google Maps 3D / satellite link", size=12, font="F2", color="183F6D") + page.rect(MARGIN, y - 76, 510, 48, fill="EAF7FC", stroke="B8D9E8") + page.multiline(MARGIN + 14, y - 46, maps_url, size=8, max_chars=82, leading=10, color="183F6D") + + page.rect(MARGIN, 88, 510, 70, fill="F7F6F3", stroke="D7DEE8") + page.text(MARGIN + 16, 132, "Map note", size=11, font="F2", color="111827") + page.multiline( + MARGIN + 16, + 114, + "Google map tiles are not embedded in this PDF. Embedding them directly requires a Google Maps API key and a use case that fits Google Maps Platform terms. The link above is included for the owner-facing 3D review workflow.", + size=8, + max_chars=96, + leading=11, + color="334155", + ) + + +def draw_openstudio_calibration_page(pdf: PDF, building: dict, rank: int, page_no: int) -> None: + page = pdf.add_page() + header(page, "OpenStudio-MCP Electricity Calibration", f"Rank #{rank}: {building['name']}", page_no) + assets = OPENSTUDIO_MODEL_PATHS[int(building["property_view_id"])] + calibration = json.loads(assets["calibration_json"].read_text()) + metrics = calibration["metrics"] + + page.multiline( + MARGIN, + 708, + "This page compares the OpenStudio-MCP EnergyPlus baseline with the building's real 2022 SEED electric meter profile. A transparent monthly meter-calibration factor is included for audit screening so the owner can evaluate modeled recommendations against measured seasonality.", + size=10, + max_chars=92, + leading=14, + color="334155", + ) + page.image(91, 210, 430, 430, assets["calibration_image"]) + + page.metric(MARGIN, 132, 156, "Annual baseline error", num(metrics["baseline_annual_difference_percent"], 1, suffix="%"), "1B6257") + page.metric(MARGIN + 174, 132, 156, "Monthly CVRMSE", f"{metrics['baseline_cvrmse_percent']:.1f}% -> 0.0%", "183F6D") + page.metric(MARGIN + 348, 132, 156, "EUI check", f"{metrics['model_site_eui_kbtu_per_ft2']:.1f} vs {metrics['actual_meter_eui_kbtu_per_ft2']:.1f}", "D9901A") + + page.rect(MARGIN, 24, 510, 78, fill="F7F6F3", stroke="D7DEE8") + page.text(MARGIN + 16, 78, "Interpretation", size=11, font="F2", color="111827") + page.multiline( + MARGIN + 16, + 60, + "The annual electricity match is already close. The adjustment improves monthly fit by aligning the model output to the actual 2022 meter; it should be validated against schedules, plug loads, HVAC controls, and tenant operating patterns before using it for investment decisions.", + size=8, + max_chars=96, + leading=11, + color="334155", + ) + + +def draw_openstudio_zoning_page(pdf: PDF, building: dict, rank: int, page_no: int) -> None: + page = pdf.add_page() + header(page, "10-Story Perimeter/Core Model Visual", f"Rank #{rank}: {building['name']}", page_no) + assets = OPENSTUDIO_MODEL_PATHS[int(building["property_view_id"])] + + page.multiline( + MARGIN, + 708, + "The audit model is represented as a ten-story large office with perimeter zones around a central core. This matches the requested OpenStudio geometry intent and gives the owner a quick visual for how the model separates facade-driven loads from interior office loads.", + size=10, + max_chars=92, + leading=14, + color="334155", + ) + page.image(66, 178, 480, 480, assets["zoning_image"]) + + page.rect(MARGIN, 82, 510, 70, fill="F7F6F3", stroke="D7DEE8") + page.text(MARGIN + 16, 126, "Modeling note", size=11, font="F2", color="111827") + page.multiline( + MARGIN + 16, + 108, + "This is a schematic audit visual, not a facade survey. The perimeter/core split is useful for evaluating envelope, lighting, plug-load, schedule, and HVAC control recommendations against real building operation.", + size=8, + max_chars=96, + leading=11, + color="334155", + ) + + +def draw_owner_checklist(pdf: PDF, building: dict, rank: int, page_no: int = 6) -> None: + page = pdf.add_page() + header(page, "Owner Review Checklist", f"Rank #{rank}: {building['name']}", page_no) + page.multiline( + MARGIN, + 708, + "Use this checklist to decide whether the BETTER recommendations are actionable and what information is needed before scoping projects.", + size=11, + max_chars=88, + leading=15, + ) + + sections = [ + ( + "Data to validate", + [ + "Confirm 2022 utility bills, meter IDs, whole-building coverage, and whether shared meters or tenant meters exist.", + "Confirm gross floor area, property type, occupancy, operating hours, and major tenant uses.", + "Check whether the reported Site EUI and ENERGY STAR score match the owner's Portfolio Manager record.", + ], + ), + ( + "Systems to inspect", + [ + "Lighting controls, fixture schedules, tenant plug-load density, and after-hours equipment operation.", + "Heating/cooling equipment age, controls, reset schedules, economizer operation, and simultaneous heating/cooling.", + "Envelope leakage, doors, windows, roof/wall insulation, and comfort complaints that line up with BETTER flags.", + ], + ), + ( + "Decision questions", + [ + "Which recommendations are low-disruption operational changes versus capital projects?", + "Which measures are blocked by leases, tenant controls, historic constraints, or upcoming renovations?", + "Which measures should be bundled so savings are not double-counted?", + ], + ), + ] + y = 650 + for title, items in sections: + page.text(MARGIN, y, title, size=14, font="F2", color="183F6D") + y -= 24 + for item in items: + y = bullet(page, MARGIN, y, item) + y -= 10 + y -= 10 + + page.rect(MARGIN, 92, 510, 62, fill="F7F6F3", stroke="D7DEE8") + page.text(MARGIN + 16, 130, "Screening conclusion", size=11, font="F2", color="111827") + page.multiline( + MARGIN + 16, + 112, + "The owner should treat this as a prioritized audit intake sheet. Confirm the data and system context first, then translate the highest-confidence BETTER flags into scoped measures.", + size=9, + max_chars=86, + leading=12, + color="334155", + ) + + +def build_pdf(building: dict, rank: int) -> PDF: + pdf = PDF() + draw_cover(pdf, building, rank) + draw_detail(pdf, building, rank) + draw_complete_recommendations(pdf, building, rank) + draw_meter_consumption(pdf, building, rank) + draw_location_page(pdf, building, rank) + next_page = 6 + if int(building["property_view_id"]) in OPENSTUDIO_MODEL_PATHS: + draw_openstudio_calibration_page(pdf, building, rank, next_page) + next_page += 1 + draw_openstudio_zoning_page(pdf, building, rank, next_page) + next_page += 1 + draw_owner_checklist(pdf, building, rank, next_page) + return pdf + + +def write_index(top: list[dict], paths: list[Path]) -> None: + lines = [ + "# BETTER Owner Audit PDFs", + "", + "Org 412 BPS - DC, cycle 2022. Ranked by active BETTER recommendation count; ties sorted by combined BETTER cost savings.", + "", + "| Rank | Building | Address | Active recs | PDF |", + "|---:|---|---|---:|---|", + ] + for rank, (building, path) in enumerate(zip(top, paths), start=1): + lines.append( + f"| {rank} | {building['name']} | {building['address']} | {building['rank_score']} | [{path.name}]({path.name}) |", + ) + (OUT_DIR / "README.md").write_text("\n".join(lines) + "\n") + + +def main() -> None: + OUT_DIR.mkdir(parents=True, exist_ok=True) + top = load_ranked_buildings()[:5] + paths: list[Path] = [] + packet = PDF() + for rank, building in enumerate(top, start=1): + path = OUT_DIR / f"{rank:02d}_{safe_name(building['address'])}_owner_audit_screening.pdf" + pdf = build_pdf(building, rank) + pdf.save(path) + paths.append(path) + packet.pages.extend(pdf.pages) + packet_path = OUT_DIR / "top5_better_owner_audit_packet.pdf" + packet.save(packet_path) + write_index(top, paths) + print(packet_path) + for path in paths: + print(path) + + +if __name__ == "__main__": + main() diff --git a/output/inspect_openstudio_mcp_tools.py b/output/inspect_openstudio_mcp_tools.py new file mode 100644 index 0000000..be329b4 --- /dev/null +++ b/output/inspect_openstudio_mcp_tools.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +import asyncio +import json +import os +from pathlib import Path + +from mcp import ClientSession, StdioServerParameters +from mcp.client.stdio import stdio_client + + +TOOLS = [ + "get_server_status", + "list_skills", + "get_skill", + "create_new_building", + "create_baseline_osm", + "create_bar_building", + "load_osm_model", + "save_osm_model", + "create_space_from_floor_print", + "match_surfaces", + "set_window_to_wall_ratio", + "create_schedule_ruleset", + "create_people_definition", + "create_lights_definition", + "create_electric_equipment", + "enable_ideal_air_loads", + "add_output_meter", + "run_simulation", + "get_run_status", + "get_run_artifacts", + "view_simulation_data", +] + + +async def main() -> None: + run_root = Path("output/openstudio_mcp_runs").resolve() + run_root.mkdir(parents=True, exist_ok=True) + env = os.environ.copy() + env["OPENSTUDIO_MCP_RUN_ROOT"] = str(run_root) + env["OSMCP_SANDBOX"] = "off" + env["OPENSTUDIO_MCP_INPUT_ROOT"] = str(Path("output").resolve()) + env["OPENSTUDIO_MCP_MEASURES_DIR"] = str((run_root / "measures").resolve()) + + server_params = StdioServerParameters( + command="/Users/nlong/working/openstudio/openstudio-mcp/.venv/bin/openstudio-mcp", + args=[], + env=env, + ) + async with stdio_client(server_params) as (read, write): + async with ClientSession(read, write) as session: + await session.initialize() + tools = await session.list_tools() + selected = {} + for tool in tools.tools: + if tool.name in TOOLS: + selected[tool.name] = { + "description": tool.description, + "schema": tool.inputSchema, + } + status = await session.call_tool("get_server_status", {}) + print(json.dumps({"status": [c.text for c in status.content], "tools": selected}, indent=2, default=str)) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/output/openstudio_mcp_schema_probe.py b/output/openstudio_mcp_schema_probe.py new file mode 100644 index 0000000..2d46d2f --- /dev/null +++ b/output/openstudio_mcp_schema_probe.py @@ -0,0 +1,125 @@ +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + + +RUN_ROOT = Path("/Users/nlong/working/openstudio/openstudio-mcp/runs") +ASSETS_ROOT = Path("/Users/nlong/working/openstudio/openstudio-mcp/tests/assets") +MEASURES_ROOT = Path("/Users/nlong/working/openstudio/openstudio-mcp/measures") + +DOCKER_CMD = [ + "docker", + "run", + "--rm", + "-i", + "-v", + f"{ASSETS_ROOT}:/inputs:ro", + "-v", + f"{RUN_ROOT}:/runs", + "-v", + f"{MEASURES_ROOT}:/measures", + "-v", + "/Users/nlong/working/openstudio/openstudio-mcp/.claude/skills:/skills:ro", + "-e", + "OPENSTUDIO_MCP_MODE=prod", + "openstudio-mcp:dev", + "openstudio-mcp", +] + + +class MCP: + def __init__(self) -> None: + self.proc = subprocess.Popen( + DOCKER_CMD, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1, + ) + self.next_id = 1 + + def close(self) -> None: + if self.proc.poll() is None: + self.proc.terminate() + try: + self.proc.wait(timeout=5) + except subprocess.TimeoutExpired: + self.proc.kill() + + def request(self, method: str, params: dict | None = None) -> dict: + req_id = self.next_id + self.next_id += 1 + payload = {"jsonrpc": "2.0", "id": req_id, "method": method} + if params is not None: + payload["params"] = params + assert self.proc.stdin is not None + self.proc.stdin.write(json.dumps(payload) + "\n") + self.proc.stdin.flush() + + assert self.proc.stdout is not None + while True: + line = self.proc.stdout.readline() + if not line: + err = self.proc.stderr.read() if self.proc.stderr else "" + raise RuntimeError(f"MCP server closed while waiting for {method}.\n{err}") + msg = json.loads(line) + if msg.get("id") == req_id: + return msg + + def notify(self, method: str, params: dict | None = None) -> None: + payload = {"jsonrpc": "2.0", "method": method} + if params is not None: + payload["params"] = params + assert self.proc.stdin is not None + self.proc.stdin.write(json.dumps(payload) + "\n") + self.proc.stdin.flush() + + +def main() -> None: + wanted = set(sys.argv[1:]) or { + "list_skills", + "get_skill", + "create_bar_building", + "create_new_building", + "create_baseline_osm", + "change_building_location", + "add_output_meter", + "run_simulation", + "get_run_status", + "get_run_artifacts", + "extract_summary_metrics", + "query_timeseries", + "view_simulation_data", + } + client = MCP() + try: + print( + json.dumps( + client.request( + "initialize", + { + "protocolVersion": "2024-11-05", + "clientInfo": {"name": "codex-schema-probe", "version": "0"}, + "capabilities": {}, + }, + ), + indent=2, + ), + ) + client.notify("notifications/initialized") + tools = client.request("tools/list") + selected = [] + for tool in tools["result"]["tools"]: + if tool["name"] in wanted: + selected.append(tool) + print(json.dumps(selected, indent=2)) + finally: + client.close() + + +if __name__ == "__main__": + main() diff --git a/output/run_1620_openstudio_mcp.py b/output/run_1620_openstudio_mcp.py new file mode 100644 index 0000000..900a471 --- /dev/null +++ b/output/run_1620_openstudio_mcp.py @@ -0,0 +1,345 @@ +from __future__ import annotations + +import json +import math +import subprocess +import sys +import time +from pathlib import Path + + +REPO_ROOT = Path("/Users/nlong/working/openstudio/openstudio-mcp") +RUN_ROOT = REPO_ROOT / "runs" +ASSETS_ROOT = REPO_ROOT / "tests/assets" +MEASURES_ROOT = REPO_ROOT / "measures" +OUT_DIR = Path("output/openstudio_1620") +RESULT_PATH = OUT_DIR / "1620_i_street_openstudio_comparison.json" + +SEED_GFA_FT2 = 125_367.0 +SEED_SITE_EUI = 53.6 +SEED_SITE_EUI_WN = 54.2 +SEED_PROPERTY_VIEW_ID = 3_282_590 +OSM_LEVELS = 10 +WEATHER_FILE = "/var/oscli/gems/ruby/3.2.0/gems/openstudio-standards-0.8.5/data/weather/USA_MD_Baltimore-Washington.Intl.AP.724060_TMY3.epw" + +REAL_MONTHLY_KBTU = { + "Jan": 927_391.0, + "Feb": 650_284.3, + "Mar": 563_515.5, + "Apr": 466_062.9, + "May": 454_153.7, + "Jun": 502_324.2, + "Jul": 516_211.7, + "Aug": 518_603.0, + "Sep": 438_807.9, + "Oct": 392_684.1, + "Nov": 478_902.6, + "Dec": 735_558.1, +} +J_PER_KBTU = 1_055_055.85262 + +DOCKER_CMD = [ + "docker", + "run", + "--rm", + "-i", + "-v", + f"{ASSETS_ROOT}:/inputs:ro", + "-v", + f"{RUN_ROOT}:/runs", + "-v", + f"{MEASURES_ROOT}:/measures", + "-v", + f"{REPO_ROOT / '.claude/skills'}:/skills:ro", + "-e", + "OPENSTUDIO_MCP_MODE=prod", + "-e", + "OSMCP_SANDBOX=off", + "openstudio-mcp:dev", + "openstudio-mcp", +] + + +class MCP: + def __init__(self) -> None: + self.proc = subprocess.Popen( + DOCKER_CMD, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + bufsize=1, + ) + self.next_id = 1 + + def close(self) -> None: + if self.proc.poll() is None: + self.proc.terminate() + try: + self.proc.wait(timeout=5) + except subprocess.TimeoutExpired: + self.proc.kill() + + def request(self, method: str, params: dict | None = None) -> dict: + req_id = self.next_id + self.next_id += 1 + payload = {"jsonrpc": "2.0", "id": req_id, "method": method} + if params is not None: + payload["params"] = params + assert self.proc.stdin is not None + self.proc.stdin.write(json.dumps(payload) + "\n") + self.proc.stdin.flush() + + assert self.proc.stdout is not None + while True: + line = self.proc.stdout.readline() + if not line: + err = self.proc.stderr.read() if self.proc.stderr else "" + raise RuntimeError(f"MCP server closed while waiting for {method}.\n{err}") + msg = json.loads(line) + if msg.get("id") == req_id: + if "error" in msg: + raise RuntimeError(json.dumps(msg["error"], indent=2)) + return msg["result"] + + def notify(self, method: str, params: dict | None = None) -> None: + payload = {"jsonrpc": "2.0", "method": method} + if params is not None: + payload["params"] = params + assert self.proc.stdin is not None + self.proc.stdin.write(json.dumps(payload) + "\n") + self.proc.stdin.flush() + + def tool(self, name: str, args: dict | None = None) -> dict: + result = self.request("tools/call", {"name": name, "arguments": args or {}}) + texts = [item.get("text", "") for item in result.get("content", []) if item.get("type") == "text"] + text = "\n".join(texts).strip() + try: + return json.loads(text) + except json.JSONDecodeError: + return {"ok": True, "text": text} + + +def first_path(value: object) -> str | None: + if isinstance(value, dict): + for key in ("osm_path", "path", "model_path", "output_path", "saved_path"): + item = value.get(key) + if isinstance(item, str) and item.endswith(".osm"): + return item + for item in value.values(): + found = first_path(item) + if found: + return found + if isinstance(value, list): + for item in value: + found = first_path(item) + if found: + return found + return None + + +def pick_weather(weather_result: dict) -> str | None: + text = json.dumps(weather_result) + candidates: list[str] = [] + def walk(value: object) -> None: + if isinstance(value, dict): + for item in value.values(): + walk(item) + elif isinstance(value, list): + for item in value: + walk(item) + elif isinstance(value, str) and value.endswith(".epw"): + candidates.append(value) + walk(weather_result) + for token in text.replace('"', " ").split(): + if token.endswith(".epw"): + candidates.append(token.strip(",")) + preferred = [c for c in candidates if "Baltimore" in c or "Arlington" in c or "Washington" in c] + return (preferred or candidates or [None])[0] + + +def normalize_monthly(ts_result: dict) -> dict[str, float]: + month_names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + monthly = {name: 0.0 for name in month_names} + rows = [] + for key in ("data", "timeseries", "values", "rows"): + value = ts_result.get(key) + if isinstance(value, list): + rows = value + break + if not rows: + rows = ts_result.get("result", []) if isinstance(ts_result.get("result"), list) else [] + units = str(ts_result.get("units") or "").lower() + divisor = J_PER_KBTU if units in {"j", "joule", "joules"} else 1.0 + for row in rows: + if not isinstance(row, dict): + continue + month = row.get("month") or row.get("Month") or row.get("month_name") + value = row.get("value") or row.get("Value") or row.get("sum") or row.get("total") + if isinstance(month, int): + month = month_names[month - 1] + if isinstance(month, str) and month[:3] in monthly and value is not None: + monthly[month[:3]] += float(value) / divisor + return monthly + + +def extract_number(value: object, keys: tuple[str, ...]) -> float | None: + if isinstance(value, dict): + for key in keys: + if key in value and isinstance(value[key], int | float): + return float(value[key]) + for item in value.values(): + found = extract_number(item, keys) + if found is not None: + return found + if isinstance(value, list): + for item in value: + found = extract_number(item, keys) + if found is not None: + return found + return None + + +def percent_diff(model: float, actual: float) -> float | None: + if actual == 0: + return None + return (model - actual) / actual * 100.0 + + +def main() -> None: + OUT_DIR.mkdir(parents=True, exist_ok=True) + client = MCP() + try: + client.request( + "initialize", + { + "protocolVersion": "2024-11-05", + "clientInfo": {"name": "codex-1620-openstudio", "version": "0"}, + "capabilities": {}, + }, + ) + client.notify("notifications/initialized") + + skills = client.tool("list_skills") + weather = client.tool("list_weather_files") + weather_file = WEATHER_FILE + + create_args = { + "building_type": "LargeOffice", + "total_bldg_floor_area": SEED_GFA_FT2, + "num_stories_above_grade": OSM_LEVELS, + "num_stories_below_grade": 0, + "floor_height": 10.0, + "wwr": 0.35, + "ns_to_ew_ratio": 1.78, + "building_rotation": 27.0, + "weather_file": weather_file, + "climate_zone": "ASHRAE 169-2013-4A", + "template": "90.1-2019", + "system_type": "Inferred", + "htg_src": "Electricity", + "clg_src": "Electricity", + "swh_src": "Electricity", + "add_hvac": True, + "add_swh": True, + } + model = client.tool("create_new_building", create_args) + if not model.get("ok"): + raise RuntimeError(f"create_new_building failed: {model}") + + client.tool("add_output_meter", {"meter_name": "Electricity:Facility", "reporting_frequency": "Monthly"}) + osm_path = "/runs/1620_i_street_seed_osm.osm" + saved = client.tool("save_osm_model", {"osm_path": osm_path}) + if not saved.get("ok"): + raise RuntimeError(f"save_osm_model failed: {saved}") + run = client.tool("run_simulation", {"osm_path": osm_path, "name": "1620_i_street_seed_osm"}) + run_id = run.get("run_id") or run.get("id") or run.get("run", {}).get("run_id") + if not run_id: + raise RuntimeError(f"Could not find run_id in run_simulation response: {run}") + + status = {} + terminal = {"completed", "success", "failed", "error", "canceled", "cancelled"} + for _ in range(90): + status = client.tool("get_run_status", {"run_id": run_id}) + state = str(status.get("status") or status.get("state") or "").lower() + if state in terminal: + break + time.sleep(10) + + summary = client.tool("extract_summary_metrics", {"run_id": run_id}) + electricity = client.tool( + "query_timeseries", + { + "run_id": run_id, + "variable_name": "Electricity:Facility", + "frequency": "Monthly", + "max_points": 500, + }, + ) + artifacts = client.tool("get_run_artifacts", {"run_id": run_id}) + + model_monthly = normalize_monthly(electricity) + model_annual = sum(model_monthly.values()) + actual_annual = sum(REAL_MONTHLY_KBTU.values()) + model_eui = extract_number(summary, ("eui_kBtu_ft2", "site_eui_kbtu_per_ft2", "site_eui_ip", "eui_kbtu_per_ft2", "site_eui")) + if model_eui is None and model_annual: + model_eui = model_annual / SEED_GFA_FT2 + + comparison = [] + for month, actual in REAL_MONTHLY_KBTU.items(): + modeled = model_monthly.get(month, 0.0) + comparison.append( + { + "month": month, + "actual_kbtu": actual, + "modeled_kbtu": modeled, + "difference_kbtu": modeled - actual, + "difference_percent": percent_diff(modeled, actual), + }, + ) + + output = { + "inputs": { + "seed_property_view_id": SEED_PROPERTY_VIEW_ID, + "address": "1620 I STREET NW", + "seed_property_type": "Office", + "seed_gross_floor_area_ft2": SEED_GFA_FT2, + "seed_site_eui_kbtu_per_ft2": SEED_SITE_EUI, + "seed_weather_normalized_site_eui_kbtu_per_ft2": SEED_SITE_EUI_WN, + "osm_way_id": 55326896, + "osm_building_levels": OSM_LEVELS, + "weather_file": weather_file, + "modeling_note": "Created and simulated through Docker-backed openstudio-mcp tools.", + }, + "mcp": { + "skills": skills, + "create_new_building": model, + "save_osm_model": saved, + "run_simulation": run, + "final_status": status, + "summary_metrics": summary, + "electricity_timeseries": electricity, + "artifacts": artifacts, + }, + "comparison": { + "actual_annual_electricity_kbtu": actual_annual, + "modeled_annual_electricity_kbtu": model_annual, + "actual_meter_eui_kbtu_per_ft2": actual_annual / SEED_GFA_FT2, + "seed_reported_site_eui_kbtu_per_ft2": SEED_SITE_EUI, + "modeled_site_eui_kbtu_per_ft2": model_eui, + "modeled_vs_actual_electricity_percent": percent_diff(model_annual, actual_annual), + "modeled_vs_seed_site_eui_percent": percent_diff(model_eui or math.nan, SEED_SITE_EUI) + if model_eui is not None + else None, + "monthly_electricity": comparison, + }, + } + RESULT_PATH.write_text(json.dumps(output, indent=2)) + print(RESULT_PATH) + print(json.dumps(output["comparison"], indent=2)) + finally: + client.close() + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index 310f105..14faa21 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,3 +40,6 @@ dev = [ [tool.uv] package = false + +[tool.pytest.ini_options] +pythonpath = ["."] From 66d947dfe08ca7012d27f5552ff3c66c67ad7c33 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 22 Jul 2026 20:03:44 -0400 Subject: [PATCH 2/5] Strip notebook kernelspec metadata via nbstripout Configure the nbstripout pre-commit hook to also strip metadata.kernelspec and metadata.language_info from notebooks (these pin a specific local Python/ipykernel version and cause needless diffs across environments), and drop that metadata from the two example notebooks to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .pre-commit-config.yaml | 1 + examples/upload_disclosure_data.ipynb | 17 ----------------- examples/upload_example_data.ipynb | 17 ----------------- 3 files changed, 1 insertion(+), 34 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f67da60..d701bab 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,6 +27,7 @@ repos: rev: 0.9.1 hooks: - id: nbstripout + args: ["--extra-keys", "metadata.kernelspec metadata.language_info"] # https://docs.astral.sh/ruff/integrations/#pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.15.20 diff --git a/examples/upload_disclosure_data.ipynb b/examples/upload_disclosure_data.ipynb index 6b3e9b3..04091b5 100644 --- a/examples/upload_disclosure_data.ipynb +++ b/examples/upload_disclosure_data.ipynb @@ -692,23 +692,6 @@ } ], "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - }, "pycharm": { "stem_cell": { "cell_type": "raw", diff --git a/examples/upload_example_data.ipynb b/examples/upload_example_data.ipynb index 9e7176b..bf45513 100644 --- a/examples/upload_example_data.ipynb +++ b/examples/upload_example_data.ipynb @@ -147,23 +147,6 @@ } ], "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.7" - }, "pycharm": { "stem_cell": { "cell_type": "raw", From 0f1ce7f084fc45ce297e7f702f4071cdddebaa54 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 22 Jul 2026 20:20:42 -0400 Subject: [PATCH 3/5] add lock file --- poetry.lock | 3561 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3561 insertions(+) create mode 100644 poetry.lock diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..076ddf4 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,3561 @@ +# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. + +[[package]] +name = "anyio" +version = "4.14.2" +description = "High-level concurrency and networking framework on top of asyncio or Trio" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494"}, + {file = "anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f"}, +] + +[package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} +idna = ">=2.8" +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} + +[package.extras] +trio = ["trio (>=0.32.0)"] + +[[package]] +name = "appnope" +version = "0.1.4" +description = "Disable App Nap on macOS >= 10.9" +optional = false +python-versions = ">=3.6" +groups = ["main"] +markers = "platform_system == \"Darwin\"" +files = [ + {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, + {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, +] + +[[package]] +name = "argon2-cffi" +version = "25.1.0" +description = "Argon2 for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741"}, + {file = "argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1"}, +] + +[package.dependencies] +argon2-cffi-bindings = "*" + +[[package]] +name = "argon2-cffi-bindings" +version = "25.1.0" +description = "Low-level CFFI bindings for Argon2" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520"}, + {file = "argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d"}, +] + +[package.dependencies] +cffi = {version = ">=1.0.1", markers = "python_version < \"3.14\""} + +[[package]] +name = "arrow" +version = "1.4.0" +description = "Better dates & times for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205"}, + {file = "arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7"}, +] + +[package.dependencies] +python-dateutil = ">=2.7.0" +tzdata = {version = "*", markers = "python_version >= \"3.9\""} + +[package.extras] +doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] +test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2025.2)", "simplejson (==3.*)"] + +[[package]] +name = "ast-serialize" +version = "0.6.0" +description = "Python bindings for mypy AST serialization" +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ast_serialize-0.6.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:a7520b672827885bafeae7501f684d14d47d17e5f45256f9df547686cca52264"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a14191beec7e0c078d2fc1f6edc0aee88bcd4db9f18e1bc9f8052b559c22dddc"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32ef62ec34cf6be20ad77d4799556638fbdf187f3ae10698dfb20ef9f2c89516"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:13b7769970a39983b0adf2f38917b1cd3b8946f76df045756c3d741bc689f089"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f7a408601bb3edaefb3bc67a4c01f5235e3253653b6a5729a2ee2382b35341c"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8670bfa51208a2c0c8d138928e40e998fab158f9200d53bb80c088b5b8eda7b8"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4826809eb8597a8cd59fd924b6d7c285b8969a1e0007e2cb652cab62376270f"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:577a6c189068686869f5f1ddc38363f3ae1808a4753b577266f9202071a7bb66"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085de7f62dc9cc247eb01e965a362707d1d90b1d89a82c5bf78301a60a3c417b"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9f8a8b78b13173de6a9ec22111d9be674874cd5bdccda04f14ae5ebc2bef403a"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f2ff3baffc3a29c1f15bc9098aa0c09763410262d5e6cef42116f7356c184554"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0067b25fce104eaae5b88383de9ab803faeb671831e14ca698b771b356e2600f"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c617417f9cbb0cb144f6283c3cbe0d2e0f01beaf9f608f662b21191058a626ec"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-win32.whl", hash = "sha256:5337cb256dcea3df9288205213d1601581536526b8f4da44b6974f1180f3252a"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2d947e45cafc4b09bd7528917fa84c517654a43de173c79785574b7b3068ac24"}, + {file = "ast_serialize-0.6.0-cp314-cp314t-win_arm64.whl", hash = "sha256:6e15ec740436e1a0d62de848641abe5f3a2f89a7f94907d534795ac91bbacf14"}, + {file = "ast_serialize-0.6.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:093cb8bb91b720d8523580498d031791bb1bbaa048599c3d21085d380e11a596"}, + {file = "ast_serialize-0.6.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:e61580a69faf47e3689795367ed211f2a10fd741478cc0f36a0f128793360aad"}, + {file = "ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:305802f2ce2a7c4e87835078ea85c58b586ddda8095b92fe2ead9364ae19c80a"}, + {file = "ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c7b8b8f0c42f752ea00b2b7d7c090b3f80d9c1c5c75cadf16423790a0cc74081"}, + {file = "ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd5b91b9e6f2356ace3a556963b0cd783b395fbbb0bb17b4defc283415466e77"}, + {file = "ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d6ef91590258ada18909b9caea344dac4de2013906b035473cd674a43f4b790"}, + {file = "ast_serialize-0.6.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcbed41e9386059fc0261d602445ede0976c2ecec2939688bcbcb9ed0b6f28b7"}, + {file = "ast_serialize-0.6.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:cdc4e6f930b9090c2f92c9036ad12ffb8e6e44d4a5ba06f1458a05d60f203f7b"}, + {file = "ast_serialize-0.6.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:897ac47b5637be41c0c07061c8a912fafa967ef1dc73fa115e4bfa70882a093b"}, + {file = "ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c4af9a1386166e40ed01464991806f89038a2d89782576c7774876fa77034e32"}, + {file = "ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:c901adbd750029b9ac4ad3d6aa56853e0ad4875119fbf52b7b8298afc223828b"}, + {file = "ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:3ae22a366b752ab4496191525b78b097b5b72d531752e3c1dd7e383a8f2c8a1a"}, + {file = "ast_serialize-0.6.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4ed29121da8b3fdc291002801a1de0f76248fa07dce89157a5f277842cf6126e"}, + {file = "ast_serialize-0.6.0-cp39-abi3-pyemscripten_2026_0_wasm32.whl", hash = "sha256:b1dac4e09d341c1300ba69cdcbe62867b32a8c75d90db9bf4d083bec3b039f0b"}, + {file = "ast_serialize-0.6.0-cp39-abi3-win32.whl", hash = "sha256:82c312a7844d2fdeb4d5c48bd3d215bf940dafd4704e1a9bcf252a99010a99b1"}, + {file = "ast_serialize-0.6.0-cp39-abi3-win_amd64.whl", hash = "sha256:113b58346f9ceb664352032770caca817d4a3c86f611c6088e6ef65ddaa70f0e"}, + {file = "ast_serialize-0.6.0-cp39-abi3-win_arm64.whl", hash = "sha256:ccd132fe8db56f61fe743b1f644d01b8d65b83248a8da506f3132bda86d6ed5e"}, + {file = "ast_serialize-0.6.0.tar.gz", hash = "sha256:aadd3ffcf4858c9726bf3515f7b199c7eadbe504f96028e4a87172c0da65a8fe"}, +] + +[[package]] +name = "asttokens" +version = "3.0.2" +description = "Annotate AST trees with source code positions" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "asttokens-3.0.2-py3-none-any.whl", hash = "sha256:9da13157f5b28becde0bd374fc677dcd3c290614264eff096f167c469cd9f933"}, + {file = "asttokens-3.0.2.tar.gz", hash = "sha256:3ecdbd8f2cc195f53ccada3a613538bb5f9ef6f6869129f13e03c30a677b8fe2"}, +] + +[package.extras] +astroid = ["astroid (>=2,<5)"] +test = ["astroid (>=2,<5)", "pytest (<9.0)", "pytest-cov", "pytest-xdist"] + +[[package]] +name = "async-lru" +version = "2.3.0" +description = "Simple LRU cache for asyncio" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "async_lru-2.3.0-py3-none-any.whl", hash = "sha256:eea27b01841909316f2cc739807acea1c623df2be8c5cfad7583286397bb8315"}, + {file = "async_lru-2.3.0.tar.gz", hash = "sha256:89bdb258a0140d7313cf8f4031d816a042202faa61d0ab310a0a538baa1c24b6"}, +] + +[package.dependencies] +typing_extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "attrs" +version = "26.1.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309"}, + {file = "attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32"}, +] + +[[package]] +name = "autopep8" +version = "2.3.2" +description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "autopep8-2.3.2-py2.py3-none-any.whl", hash = "sha256:ce8ad498672c845a0c3de2629c15b635ec2b05ef8177a6e7c91c74f3e9b51128"}, + {file = "autopep8-2.3.2.tar.gz", hash = "sha256:89440a4f969197b69a995e4ce0661b031f455a9f776d2c5ba3dbd83466931758"}, +] + +[package.dependencies] +pycodestyle = ">=2.12.0" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[[package]] +name = "babel" +version = "2.18.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35"}, + {file = "babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d"}, +] + +[package.extras] +dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] + +[[package]] +name = "beautifulsoup4" +version = "4.15.0" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.7.0" +groups = ["main"] +files = [ + {file = "beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9"}, + {file = "beautifulsoup4-4.15.0.tar.gz", hash = "sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7"}, +] + +[package.dependencies] +soupsieve = ">=1.6.1" +typing-extensions = ">=4.0.0" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "bleach" +version = "6.4.0" +description = "An easy safelist-based HTML-sanitizing tool." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "bleach-6.4.0-py3-none-any.whl", hash = "sha256:4b6b6a54fff2e69a3dde9d21cc6301220bee3c3cb792187d11403fd795031081"}, + {file = "bleach-6.4.0.tar.gz", hash = "sha256:4202482733d85cedd04e59fcb2f89f4e4c7c385a78d3c3c23c30446843a37452"}, +] + +[package.dependencies] +tinycss2 = {version = ">=1.1.0", optional = true, markers = "extra == \"css\""} +webencodings = "*" + +[package.extras] +css = ["tinycss2 (>=1.1.0)"] + +[[package]] +name = "certifi" +version = "2026.7.22" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775"}, + {file = "certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55"}, +] + +[[package]] +name = "cffi" +version = "2.1.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "cffi-2.1.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:b65f590ef2a44640f9a05dbb548a429b4ade77913ce683ac8b1480777658a6c0"}, + {file = "cffi-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164bff1657b2a74f0b6d54e11c9b375bc97b931f2ca9c43fcf875838da1570dd"}, + {file = "cffi-2.1.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:c941bb58d5a6e1c3892d86e42927ed6c180302f07e6d395d08c416e594b98b46"}, + {file = "cffi-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a016194dbe13d14ee9556e734b772d8d67b947092b268d757fd4290e3ba2dfc2"}, + {file = "cffi-2.1.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:03e9810d18c646077e501f661b682fbf5dee4676048527ca3cffe66faa9960dd"}, + {file = "cffi-2.1.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:19c54ac121cad98450b4896fa9a43ee0180d57bc4bc911a33db6cab1efab6cd3"}, + {file = "cffi-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d433a51f1870e43a13b6732f92aaf540ff77c2015097c78556f75a2d6c030e0"}, + {file = "cffi-2.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3d7f118b5adbfdfead90c25822690b02bc8074fba949bb7858bec4ebd55adb43"}, + {file = "cffi-2.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5f5df567f6eb216de69be06ce55c8b714090fae02b18a3b40da8163b8c5fa9c"}, + {file = "cffi-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:11b3fb55f4f8ad92274ed26705f65d8f91457de71f5380061eb6d125a768fecd"}, + {file = "cffi-2.1.0-cp310-cp310-win32.whl", hash = "sha256:9d72af0cf10a76a600a9690078fe31c63b9588c8e86bf9fd353f713c84b5db0f"}, + {file = "cffi-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb62edb5bb52cca65fab91a63afa7561607120d26090a7e8fda6fb9f064726da"}, + {file = "cffi-2.1.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:02cb7ff33ded4f1532476731f89ede53e2e488a8e6205515a82144246ffa7dcc"}, + {file = "cffi-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f5bce581e6b8c235e566a14768a943b172ada3ed73537bb0c0be1edee312d4e7"}, + {file = "cffi-2.1.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:30b65779d598c370374fefabf138d456fd6f3216bfa7bedfab1ba82025b0cd93"}, + {file = "cffi-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88023dfe18799507b73f1dbb0d14326a17465de1bc9c9c7655c22845e9ddc3a2"}, + {file = "cffi-2.1.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0a96b74cda968eebbad56d973efe5098974f0a9fb323865bf99ea1fd24e3e64c"}, + {file = "cffi-2.1.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a5781494d4d400a3f47f8f1da94b324f6e6b440a53387774002890a2a2f4b50f"}, + {file = "cffi-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aa7a1b53a2a4452ada2d1b5dade9960b2522f1e61293a811a077439e39029565"}, + {file = "cffi-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d8272c0e483b024e1b9ad029821470ed8ec65631dbd90217469da0e7cd89f1c"}, + {file = "cffi-2.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7762faa47e8ff7eb80bd261d9a7d8eea2d8baa69de5e95b70c1f338bbe712f02"}, + {file = "cffi-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89095c1968b4ba8285840e131bf2891b09ae137fe2146905acae0354fbce1b5e"}, + {file = "cffi-2.1.0-cp311-cp311-win32.whl", hash = "sha256:64c753a0f87a256020004f37a1c8c02c480e725f910f0b2a0f3f07debd1b2479"}, + {file = "cffi-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:4f26194e3d95e06501b942642855aed4f953d55e95d7d01b7c4483db3ecff458"}, + {file = "cffi-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:35aaea0c7ee0e58a5cd8c2fd1a48fdf7ece0d2699b7ecdda08194e9ce5dd9b3d"}, + {file = "cffi-2.1.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:df2b82571a1b30f58a87bf4e5a9e78d2b1eff6c6ce8fd3aa3757221f93f0863f"}, + {file = "cffi-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78474632761faa0fb96f30b1c928c84ebcf68713cbb80d15bab09dfe61640fde"}, + {file = "cffi-2.1.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5972433ad71a9e46516584ef60a0fda12d9dc459938d1539c3ddecf9bdc1368d"}, + {file = "cffi-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6422532152adf4e59b110cb2808cee7a033800952f5c036b4af047ee43199e7"}, + {file = "cffi-2.1.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:46b1c8db8f6122420f32d02fffb924c2fe9bc772d228c7c711748fff56aabb2b"}, + {file = "cffi-2.1.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9fafc5aa2e2a39aaf7f8cc0c1f044a9b07fca12e558dca53a3cc5c654ad67a7"}, + {file = "cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e9f50d192a3e525b15a75ab5114e442d83d657b7ec29182a991bc9a88fd3a66"}, + {file = "cffi-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98fff996e983a36d3aa2eca83af40c5821202e7e6f32d13ae94e3d2286f10cfe"}, + {file = "cffi-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:379de10ce1ba048b1448599d1b37b24caee16309d1ac98d3982fc997f768700b"}, + {file = "cffi-2.1.0-cp312-cp312-win32.whl", hash = "sha256:9b8f0f26ca4e7513c534d351eca551947d053fac438f2a04ac96d882909b0d3a"}, + {file = "cffi-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:c97f080ea627e2863524c5af3836e2270b5f5dfff1f104392b959f8df0c5d384"}, + {file = "cffi-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:6d194185eabd279f1c05ebe3504265ddfc5ad2b58d0714f7db9f01da592e9eb6"}, + {file = "cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:10537b1df4967ca26d21e5072d7d54188354483b91dc75058968d3f0cf13fbda"}, + {file = "cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a95b05f9baf29b91171b3a8bd2020b028835243e7b0ff6bb23e2a3c228518b1b"}, + {file = "cffi-2.1.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:15faec4adfff450819f3aee0e2e02c812de6edb88203aa58807955db2003472a"}, + {file = "cffi-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:716ff8ec22f20b4d988b12884086bcef0fc99737043e503f7a3935a6be99b1ea"}, + {file = "cffi-2.1.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:63960549e4f8dc41e31accb97b975abaecfc44c03e396c093a6436763c2ea7db"}, + {file = "cffi-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff067a8d8d880e7809e4ac88eb009bb848870115317b306666502ccad30b147f"}, + {file = "cffi-2.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3b926723c13eba9f81d2ef3820d63aeceec3b2d4639906047bf675cb8a7a500d"}, + {file = "cffi-2.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:47ff3a8bfd8cb9da1af7524b965127095055654c177fcfc7578debcb015eecd0"}, + {file = "cffi-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:799416bae98336e400981ff6e532d67d5c709cfb30afb79865a1315f94b0e224"}, + {file = "cffi-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:961be50688f7fba2fa65f63712d3b9b341a22311f5253460ce933f52f0de1c8c"}, + {file = "cffi-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf5c6cf48238b0eb4c086978c492ad1cbc22373fc5b2d7353b3a598ce6db887a"}, + {file = "cffi-2.1.0-cp313-cp313-win32.whl", hash = "sha256:db3eb7d46527159a878ec3460e9d40615bc25ba337d477db681aea6e4f05c5d2"}, + {file = "cffi-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:8e74a6135550c4748af665b1b1118b6aab33b1fc6a16f9aff630af107c3b4512"}, + {file = "cffi-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:2282cd5e38aa8accd03e99d1256af8411c84cdbee6a89d841b563fdbd1f3e50f"}, + {file = "cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d2117334c3af3bdcb9a88522b844a2bdb5efdc4f71c6c822df55486ae1c3347a"}, + {file = "cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:702c436735fbe99d59ada02a1f65cfc0d31c0ee8b7290912f8fbc5cd1e4b16c3"}, + {file = "cffi-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1ff3456eab0d889592d1936d6125bbfbc7ae4d3354a700f8bd80450a66445d4d"}, + {file = "cffi-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c4165821e131d6d4ca444347c2b694e2311bcfa3fe5a861cc72968f28867beac"}, + {file = "cffi-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:276f20fffd7b396e12516ba8edf9509210ac248cbbc5acbc39cd512f9f59ebe6"}, + {file = "cffi-2.1.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7d5980a3433d4b71a5e120f9dd551403d7824e31e2e67124fe2769c404c06913"}, + {file = "cffi-2.1.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6ca4919c6e4f89aa99c42510b42cf54596892c00b3f9077f6bdd1505e24b9c8d"}, + {file = "cffi-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d53d10f7da99ae46f7373b9150393e9c5eab9b224909982b43832668de4779f5"}, + {file = "cffi-2.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c351efb95e832a853a29361675f33a7ce53de1a109cd73fd47af0712213aa4ce"}, + {file = "cffi-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7c7a88e2bac086f06d14577332760bdeecc42bdec8ac4077f6260557d9326"}, + {file = "cffi-2.1.0-cp314-cp314-win32.whl", hash = "sha256:1854b724d00f6654c742097d5387569021be12d3a0f770eae1df8f8acfcc6acd"}, + {file = "cffi-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:1b96bfe2c4bd825681b7d311ad6d9b7280a091f43e8f63da5729638083cd3bfb"}, + {file = "cffi-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:7d28dff1db6764108bc30788d85d61c876beff416d9a49cb9dd7c5a9f34f5804"}, + {file = "cffi-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7ea6b3e2c4250ff1de21c630fe72d0f63eb95c2c32ffbf64a358cf4a8836d714"}, + {file = "cffi-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6af371f3767faeffc6ac1ef57cdfd25844403e9d3f476c5537caee499de96376"}, + {file = "cffi-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb4e8997a49aa2c08a3e43c9045d224448b8941d88e7ac163c7d383e560cbf98"}, + {file = "cffi-2.1.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf01d8c84cbea96b944c73b22182e6c7c432b3475632b8111dbfdc95ddad6e13"}, + {file = "cffi-2.1.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:33eb1ad83ebe8f313e0df035c406227d55a79456704a863fad9842136af5ad7d"}, + {file = "cffi-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ac0f1a2d0cfa7eea3f2aaf006ab6e70e8feeb16b75d65b7e5939982ca2f11056"}, + {file = "cffi-2.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c16914df9fb7f500e440e6875fa23ff5e0b31db01fa9c06af98d59a91f0dc2e4"}, + {file = "cffi-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5ecbd0499275d57506d397eebe1981cee87b47fcd9ef5c22cab7ed7644a39a94"}, + {file = "cffi-2.1.0-cp314-cp314t-win32.whl", hash = "sha256:7d034dcffa09e9a46c93fa3a3be402096cb5354ac6e41ab8e5cc9cd8b642ad76"}, + {file = "cffi-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0582a58f3051372229ca8e7f5f589f9e5632678208d8636fea3676711fdf7fe5"}, + {file = "cffi-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:510aeeeac94811b138077451da1fb18b308a5feab47dd2b603af55804155e1c8"}, + {file = "cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:2e9dabb9abcb7ad15938c7196ad5c1718a4e6d33cc79b4c0209bdb64c4a54a5c"}, + {file = "cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:37f525a7e7e50c017fdebe58b787be310ad59357ae43a053943a6e1a6c526001"}, + {file = "cffi-2.1.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:95f2954c2c9473d892eca6e0409f3568b37ab62a8eedb122461f73cc273476e3"}, + {file = "cffi-2.1.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:cdf2448aab5f661c9315308ec8b93f4e8a1a67a3c733f8631067a2b67d5913dc"}, + {file = "cffi-2.1.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:90bec57cf82089383bd06a605b3eb8daebf7e5a668520beaf6e327a83a947699"}, + {file = "cffi-2.1.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6274dcb2d15cef48daa73ed1be5a40d501d74dccd0cd6db364776d12cb6ba022"}, + {file = "cffi-2.1.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b71d409cccee78310ab5dec549aed052aaea483346e282c7b02362596e01bb0"}, + {file = "cffi-2.1.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d3538f9c0e50670f4deb93dbb696576e60590369cae2faf7de681e597a8a1f1"}, + {file = "cffi-2.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:8f9ec95b8a043d3dfbc74d9abc6f7baf524dd27a8dc160b0a32ff9cdab650c28"}, + {file = "cffi-2.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:af5e2915d41fe6c961694d7bfdc8562942638200f3ce2765dfb8b745cf997629"}, + {file = "cffi-2.1.0-cp315-cp315-win32.whl", hash = "sha256:0a42c688d19fca6e095a53c6a6e2295a5b050a8b289f109adab02a9e61a25de6"}, + {file = "cffi-2.1.0-cp315-cp315-win_amd64.whl", hash = "sha256:bccbbb5ee76a61f9d99b5bf3846a51d7fca4b6a732fe46f89295610edaf41853"}, + {file = "cffi-2.1.0-cp315-cp315-win_arm64.whl", hash = "sha256:8d35c139744adb3e727cd51b1a18324bbe44b8bd41bf8322bca4d41289f48eda"}, + {file = "cffi-2.1.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f9912624a0c0b834b7520d7769b3644453aabc0a7e1c839da7359f050750e9bc"}, + {file = "cffi-2.1.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:df92f2aba50eb4d96718b68ef76f2e57a57b54f2fa62333496d16c6d585a85ca"}, + {file = "cffi-2.1.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0520e1f4c35f44e209cbbb421b67eec42e6a157f59444dfb6058874ff3610e5d"}, + {file = "cffi-2.1.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3681e031db29958a7502f5c0c9d6bbc4c36cb20f7b104086fa642d1799631ff8"}, + {file = "cffi-2.1.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:762f99479dcb369f60ab9017ad4ab97a36a1dd7c1ee5a3b15db0f4b8659120cd"}, + {file = "cffi-2.1.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0611e7ebf90573a535ebdc33ae9da222d037853983e13359f580fab781ca017f"}, + {file = "cffi-2.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:86cf8755a791f72c85dc287128cc62d4f24d392e3f1e15837245623f4a33cccc"}, + {file = "cffi-2.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:ba00f661f8ba35d075c937174e27c2c421cec3942fd2e0ea3e66996757c0fdd9"}, + {file = "cffi-2.1.0-cp315-cp315t-win32.whl", hash = "sha256:cb96698e3c7413d906ce83f8ffd245ec1bd94707541f299d0ce4d6b0193e982b"}, + {file = "cffi-2.1.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f146d154428a2523f9cc7936c02353c2459b8f6cf07d3cd1ee1c0a611109c5d5"}, + {file = "cffi-2.1.0-cp315-cp315t-win_arm64.whl", hash = "sha256:cbb7640ce37159548d2147b5b8c241f962143d4c71231431820783f4dc78f210"}, + {file = "cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9"}, +] + +[package.dependencies] +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} + +[[package]] +name = "cfgv" +version = "3.5.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0"}, + {file = "cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.9" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "charset_normalizer-3.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd6280cf040f233bd7d3407b743b4b4c74f70e8e1c4199cb112a62c941c0772a"}, + {file = "charset_normalizer-3.4.9-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa99adc8f081b475a12843953db36831eaf83ec33eb46a90629ca6a5de45a616"}, + {file = "charset_normalizer-3.4.9-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c1225416b463483160e4af85d5fc3a9690ccb53fd4b1865a6437825f5ede3209"}, + {file = "charset_normalizer-3.4.9-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:16d10d789dd9bcca1173c95af82c58433122564b7bc39385124be735a35cbe99"}, + {file = "charset_normalizer-3.4.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bb41182d93ea91f60b4bc8fbf4c820c69ef8a12ab2d917f3f1834f1acad07e8"}, + {file = "charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:bcf74c1df76758a395bf0af608c04c82257523f55c9868b334f06270d0f2112b"}, + {file = "charset_normalizer-3.4.9-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b5314963fce9b0b12743891de876e724997864ee22aa496f903f426c7e2fa5b2"}, + {file = "charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e9701d0049d92c16703a42771b98d560b95248949f23f8cf7b4eddd201814fb9"}, + {file = "charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:65a7ff3f705e57d392f7261b6d0550fe137c3019477431f1c355e0db0a7d3e15"}, + {file = "charset_normalizer-3.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79580094b00d1789d1f93ea55bc43cb2f611910c72235b7657f3482ddcc1b22d"}, + {file = "charset_normalizer-3.4.9-cp310-cp310-win32.whl", hash = "sha256:432786d3561e69aeeae6c7e8648964ce0ad05736120135601f87ac26b9c83381"}, + {file = "charset_normalizer-3.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:8c041122946b7ba21bb32c45b1aa57b1be35527690aeb3c5c234521085632eee"}, + {file = "charset_normalizer-3.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:375b83ed0aecfce76c16d198fbc21f3b11b337d68662bea0a995046682a11419"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0e94703ec9684807f20cfb5eed95c70f67f2a8f21ad620146d7b5a13677b93e5"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2a441ea71902098ffe78c5abe6c494f44160b4af614ed16c3d9a3b1d17fd8ee2"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:304b13570067b2547562e308af560b3963857b1fa90bd6afd978130130fe2d6a"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4773092f8019072343a7447203308b176e10199920eb02d6195e81bbb3274c29"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:04ce310cb89c15df659582aee80a0603788732a5e017d5bd5c81158106ce249c"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:c0323c9daef75ef2e5083624b4585018a0c9d5e3b40f607eed81a311270b934b"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:871ff67ea1aad4dfd91736464934d56b32dac49f9fbe16cddba36198a7b3a0db"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:67830fc78e67501f47bb950471b2dcb9b35b140084429318e862895a8e89c993"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:3d92613ec25e43b05f042302531ec0f00b8445190e43325880cbd6ab7c2581da"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:280081916dc341820640489a66e4696049401ef1cf6dd672f672e70ad915aca3"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-win32.whl", hash = "sha256:ac351b3b8014eead140e77e9717e2992c6bbe30b63bc3422422eb84865412e3d"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:6366a16e1a25018694d6a5d784d09b046edc9eac40ea2b54065c3052672516a1"}, + {file = "charset_normalizer-3.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:1d22856ffbe153a602df38e4a5464f0b748a54002e0d69ac6d2ad0a197cc99ec"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:45b0cc4e3556cd875e09102988d1ab8356c998b596c9fced84547c8138b487a0"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b2aff1c7b3884512b9512c3eaadd9bab39fb45042ffaaa1dd08ff2b9f8109d9"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9104ed0bd76a429d46f9ec0dbc9b08ad1d2dcdf2b00a5a0daa1c145329b35b44"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7b86a2b16095d250c6f58b3d9b2eee6f4147754344f3dab0922f7c9bf7d226c9"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e226f6218febc71f6c1fc2fafb91c226f75bdc1d8fb12d66823716e891608fd"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:90c44bc373b7687f6948b693cceaea1348ae0975d7474746559494468e3c1d84"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cdef90ae47919cae358d8ab15797a800ed41da7aba5d72419fb510729e2ed4b"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:60f44ade2cf573dad7a277e6f8ca9a51a21dda572b13bd7d8539bb3cd5dbedde"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a1786910334ed46ab1dd73222f2cd1e05c2c3bb39f6dddb4f8b36fc382058a39"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03d07803992c6c7bbc976327f34b18b6160327fc81cb82c9d504720ac0be3b62"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-win32.whl", hash = "sha256:78841cccf1af7b40f6f716338d50c0902dbe88d9f800b3c973b7a9a0a693a642"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:4b3dac63058cc36820b0dd072f89898604e2d39686fe05321729d00d8ac185a0"}, + {file = "charset_normalizer-3.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:78fa18e436a1a0e58dbd7e02fc4473f3f32cceb12df9dfca542d075961c307d2"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:440eede837960000d74978f0eba527be106b5b9aee0daf779d395276ed0b0614"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e764fd1e70b6a3e205a0e46f3051701f98a8cb3fad66eeb80e48bb502f8698"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e4fd89cc178bced6ad29cb3e6dd4aa63fa5017c3524dbd0b25998fb64a87cc8b"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bd47ba7fc3ca94896759ea0109775132d3e7ab921fbf54038e1bab2e46c313c9"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84fd18bcc17526fc2b3c1af7d2b9217d32c9c04448c16ec693b9b4f1985c3d33"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:5b10cd92fc5c498b35a8635df6d5a100207f88b63a4dc1de7ef9a548e1e2cd63"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4fbdde9dd4a9ce5fd52c2b3a347bb50cc89483ef783f1cb00d408c13f7a96c0"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:416c229f77e5ea25b3dfd4b582f8d73d7e43c22320302b9ab128a2d3a0b38efe"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:75286256590a6320cf106a0d28970d3560aad9ee09aa7b34fb40524792436d35"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69b157c5d3292bcd443faca052f3096f637f1e074b98212a933c074ae23dc3b8"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-win32.whl", hash = "sha256:51307f5c71007673a2bf8232ad973483d281e74cb99c8c5a990af1eefa6277d9"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115"}, + {file = "charset_normalizer-3.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:611057cc5d5c0afc743ba8be6bd828c17e0aaa8643f9d0a9b9bb7dea80eb8012"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0327fcd59a935777d83410750c50600ee9571af2846f71ce40f25b13da1ef380"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a79d9f4d8001473a30c163556b3c3bfebec837495a412dde78b51672f6134f9"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33bdcc2a32c0a0e861f60841a512c8acc658c87c2ac59d89e3a46dacf7d866e4"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f840ed6d8ecba8255df8c42b87fadeda98ddfc6eeec05e2dc66e26d46dd6f58a"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c25fe15c70c59eb7c5ce8c06a1f3fa1da0ecc5ea1e7a5922c40fd2fa9b0d5046"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:f7fb7d750cfa0a070d2c24e831fd3481019a60dd317ea2b39acbcebc08b6ed81"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1c96a7a18b9690a4d46df09e3e3382406ae3213727cd1019ebade1c4a81917"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a4cfde78a9f2880208d16a93b795726a3017d5977e08d1e162a7a31322479c41"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4d6fcde76f94f5cb9e43e9e9a61f16dacefd228cbbf6f1a09bd9b219a92f1a1"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:898f0e9068ca27d37f8e83a5b962821df851532e6c4a7d615c1c033f9da6eedf"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-win32.whl", hash = "sha256:c1c948747b03be832dceed96ca815cef7360de9aa19d37c730f8e3f6101aca48"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:16b65ea0f2465b6fb52aa22de5eca612aa964ddfec00a912e26f4656cbef890b"}, + {file = "charset_normalizer-3.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:40a126142a56b2dfc0aacbad1de8310cbf60da7656db0e6b16eebd48e3e93519"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:609b3ba8fcc0fb5ab7af00719d0fb6ad0cb518e48e7712d12fd68f1327951198"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51447e9aa2684679af07ca5021c3db526e0284347ebf4ffcec1154c3350cfe32"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cc1b0fff8ead343dae06305f954eb8468ba0ec1a97881f42489d198e4ce3c632"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa36ec09ef71d158186bc79e359ff5fdd6e7996fe8ab638f00d6b93139ba4fcf"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df115d4d83168fdf2cae48ef1ff6d1cb4c466364e30861b37121de0f3bf1b990"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f86c6358749bd4fda175388691e3ba8c46e24c5347d0afd20f9b7edfc9faf07d"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32286a2c8d167e897177b673176c1e3e00d4057caf5d2b64eef9a3666b03018e"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:83aed2c10721ddd90f68140685391b50811a880af20654c59af6b6c66c40513c"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cd6c3d4b783c556fa00bf540854e42f135e2f256abd29669fcd0da0f2dec79c2"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ee2f2a527e3c1a6e6411eb4209642e138b544a2d72fe5d0d76daf77b24063534"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-win32.whl", hash = "sha256:0d861473f743244d349b50f850d10eb87aeb22bbdcc8e64f79273c94af5a8226"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:9b8e0f3107e2200b76f6054de99016eac3ee6762713587b36baaa7e4bd2ae177"}, + {file = "charset_normalizer-3.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:19ac87f93086ce37b86e098888555c4b4bc48102279bae3350098c0ed664b501"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:253a4a220747e8b5faf57ec320c4f5efb0cef05f647420bf267143ec15dba10a"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:68ce9f4d6b26d5ccbf7fd4459bf75f74a0a146677ebba80597df60cbdb20e6f4"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:58150c9f9b9a552505912d182ccdf26f6396fb6094816ceebcbb20eecabaed94"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df7276909358e5635ae203673ab7e509ddd224225a8d6b0790bf13eb2bde1cc5"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c09a49d6cde137258beb3d551994a2927fd35ad5cf96aed573f61bbd67c5f84"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:231ddcbb35e2ff8973e1365db41fe0572662893b99a05deb183b68ad4c0c8bd4"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:920079c3f7456fa213e0829ed2073aaa727fd39d889ead5b4f35d0de5460d04f"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0fa1aec2d32bcc03c8fa0f6f1712caad1adc38509f31142112e5c9daf5b9c833"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ad41ba96094304aa090f5a30cb6e4fb3b3f1c264c523394b4c39bbacc4dc92ba"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:43b9e366a31fdd1c87d0eb08f579b4a82b723ea54338f040d6b4e518a026ea29"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-win32.whl", hash = "sha256:93d59d504b230e83c7a843251681959a0b6a9cd76f6e146ce1b8a80eb8739af9"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-win_amd64.whl", hash = "sha256:ddf4af30b417d9fe16481e9b81c27ab2a7cde1ff7ba3e85653b02db7d145dc7b"}, + {file = "charset_normalizer-3.4.9-cp39-cp39-win_arm64.whl", hash = "sha256:476743fe6dfe14a2da12e3ac79125dc84a3b2cf8094369a47a1529b0cd8549fe"}, + {file = "charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5"}, + {file = "charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "comm" +version = "0.2.3" +description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417"}, + {file = "comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971"}, +] + +[package.extras] +test = ["pytest"] + +[[package]] +name = "coverage" +version = "7.15.2" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "coverage-7.15.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9b5bd92ff1ec22e535eab0de75fa6db021992791f461a2aceb7822c625a1187d"}, + {file = "coverage-7.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:44826758cfe73fcd0e6af5deb4ba6d5417cc1d13df3acb35c93484a11160f846"}, + {file = "coverage-7.15.2-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09f5c6ec5901f667bd97dd140b5b9a2586b10efec66f46fb1e6d8135f8b95bdf"}, + {file = "coverage-7.15.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1d16e3a7104ea84f03e614611b3edbf6fb6892554b3ab0fe7fbb3f2b2ef04376"}, + {file = "coverage-7.15.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d46e62cb35d91e6e2589fda6d28074426b0e276422b5d2ebef2c6b11dc60dbfd"}, + {file = "coverage-7.15.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dfd3db045e95960ae3683059571e597fda7cc610106a8916f77c5839048c1deb"}, + {file = "coverage-7.15.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:affd532502d34c0472d0cdb181325c89f1d2c44992fef0c17e88e7b1576259a1"}, + {file = "coverage-7.15.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d17d7512151fedfcc64c1821a8977fc9be0dbf495754669afcab7b57abc98ae9"}, + {file = "coverage-7.15.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e26ff680768b8095e8874aabe0e9d3a47a2a9f176a8340d05f8604c56457c23a"}, + {file = "coverage-7.15.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:7e8f27131dc7cd53de2c137dd207b3720919320b3c20d499dc30aa9ee6173287"}, + {file = "coverage-7.15.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:728a33676d4c3f0db977990a4bd421dcaa3be3e53b5b6273036fff6666008e89"}, + {file = "coverage-7.15.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29c052f7c83ccfcc5c577eaae025d2e4a9bb80daf03c0ac31c996e83b000ce88"}, + {file = "coverage-7.15.2-cp310-cp310-win32.whl", hash = "sha256:1268ac8fb9ddcd783d3948dbabaf80a5d53bfdaa0575e873e2139a692f797443"}, + {file = "coverage-7.15.2-cp310-cp310-win_amd64.whl", hash = "sha256:9f4432898c4bf2fba0435bbe35dd4437d7264565e5a88a21f5b49d8662a6b629"}, + {file = "coverage-7.15.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f1ec6f304b156669cfde653b4e9a953f5de87e247ea02ac599bce0ab2744036"}, + {file = "coverage-7.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d3361879d736f469f45723c11ea1a5bbdaf1f6928f0e632c940378b5aa9b660"}, + {file = "coverage-7.15.2-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c6a98d698f9e2c8008d0370ec7fc452ebfcc530002ae2d0061170d768b992589"}, + {file = "coverage-7.15.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d50dd325e18ec25bfcc10cd7f99b04df1ab9ec76b0918c260e60817ad0643dee"}, + {file = "coverage-7.15.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67d7602480a47bdf5b675635403625553ebaa70d5a62a657c035149fd401cea0"}, + {file = "coverage-7.15.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cee0f89f4767a6057c8fbf168f8135f18be651300496086bd873e3189fed0487"}, + {file = "coverage-7.15.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a29ec5305a7335aacee2d799e3422e91e1c8a12474986e2b3b07e315c91be82f"}, + {file = "coverage-7.15.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:48ccc6395958eda89093ecdc35644c86f23a8b23a7f4d44958812b721aad67c1"}, + {file = "coverage-7.15.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:81f382c5a94b434ec1f6da607edb904c76d7212e618cd4d1bc9f97bed4120ef5"}, + {file = "coverage-7.15.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:bbc808daf4f5cd567af8075ecc72d21c6dfef9a254709a621a84c217c935ebc0"}, + {file = "coverage-7.15.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a4c46b247b5d4b78f613bd89fea926d32b25c6cc61a50bd1e99ba310348f3dad"}, + {file = "coverage-7.15.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:094dd37f3ef7b2da8b068b583d1f4c40f91c65197e16c52a71962d5d537fc5db"}, + {file = "coverage-7.15.2-cp311-cp311-win32.whl", hash = "sha256:a63b9e190711134d581c4d703df5df09851b1acf99792c7aacbbe9f41f0283c9"}, + {file = "coverage-7.15.2-cp311-cp311-win_amd64.whl", hash = "sha256:8bb9f4b4279187560796a4cdaca3b0a93dd97e48ee667df005f4ed9a97403688"}, + {file = "coverage-7.15.2-cp311-cp311-win_arm64.whl", hash = "sha256:8c726b232659cbd2ae57ade46509eb068c9bd7a06df9fcbff6fe484870006934"}, + {file = "coverage-7.15.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1adac78e5abc7c5438f7a209c9ca69d06542f0bf481d728b6989ea80b813fdf9"}, + {file = "coverage-7.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b868acc62aa5de3be7a9d05c2333bf8359ca987e43f9cb30ff8fbda6a024ab73"}, + {file = "coverage-7.15.2-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6f6966fc30e6f06ca8f98fb0ce51eda6b111b3ee8d066a8b1ec9e77fa06ab55d"}, + {file = "coverage-7.15.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:68af907f595ab01a78f794932ff3bdf929c316d3000810d38dbc247129e26f8b"}, + {file = "coverage-7.15.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afa29e2eff3d5729267e2cb2fd4ce9d61c952932fb2694e34ccb5d9540c6a296"}, + {file = "coverage-7.15.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bbf44513ceb1589e31948e20eafbde9deaface90e1a1afa5f5f77b4423d17ce6"}, + {file = "coverage-7.15.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9deddf09eecb717b7f980414b43d90a5b22ff3967d2949ab29cb0aa83d9e9098"}, + {file = "coverage-7.15.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ae901f7e55ba405c84ee1cab3d3e962e4e871e4a2bcb9c90911adbd69b42ac5a"}, + {file = "coverage-7.15.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a0f47002c6eeb7c280228467a4cb0cc15ca2103a8421b986b2d3ec04a0f9bd8b"}, + {file = "coverage-7.15.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1cd7a5beb7af3e864a13b1f0fb26efd3695da43ef0daf71e586adfffaf34d5b2"}, + {file = "coverage-7.15.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:97a5c5457a9fb1d6c4e06cfb5dc835871fbfb6a6a51addc9e925bdeff5ef7440"}, + {file = "coverage-7.15.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0901cfe6c13bcd2302da4f83e884555d2a22bda6e4c476f09ef204ba20ca536e"}, + {file = "coverage-7.15.2-cp312-cp312-win32.whl", hash = "sha256:b171bdd71cb7ff792bf32e376173b0ace7e7963e7e57c58dfc42063a6a7174cd"}, + {file = "coverage-7.15.2-cp312-cp312-win_amd64.whl", hash = "sha256:582edc45c2040543fef83341be23c43024a3ab3ae0c2d8bc498a06282905ad40"}, + {file = "coverage-7.15.2-cp312-cp312-win_arm64.whl", hash = "sha256:a638db90c61cd219aeee65e83a24fdaa57269a741ae0cf773309208ac862cee3"}, + {file = "coverage-7.15.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1121caa19159a38b5463eaae4b1e1fde81e525b15ecc5e000cd5b1a108f743a8"}, + {file = "coverage-7.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a300c6934e0989c327b9e8a1e110329da4641149f872bbe9f70168be66da76c1"}, + {file = "coverage-7.15.2-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2617f8799d268fabdeef42a7e89ac3a23e1deee9025427db2df970f99a89a578"}, + {file = "coverage-7.15.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7dc2950a2992cd676d35c20ae63522836deeb034f08874699d14068710af3dc1"}, + {file = "coverage-7.15.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e36686f7a442185db2400b3df171aac520869faf9deb59df687d28659eda2a6"}, + {file = "coverage-7.15.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d29ca7bd67af6e12e74632d65f026eabc1364da5c254494cd914446a28a3ef7"}, + {file = "coverage-7.15.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:db9c8438057e5b0f6a22a0af99c0c1d26b57fbbdbd1be5861ddb8f897fcc3a2d"}, + {file = "coverage-7.15.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:63022c4c8dec1d0342f05c3ede99842fe3d007689acc45e86f123a1746e4a026"}, + {file = "coverage-7.15.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6c0be82b4d4aa5b2704e08518e2252f3e3d110164bcca826816801052e48a7aa"}, + {file = "coverage-7.15.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4510fb9cdf6bb02dfa6af0be4a534b8102d086e22e4a33f8836df663da3d660d"}, + {file = "coverage-7.15.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:42ec3d989421b174a2ab607c1539f24127ad362757b7f1c0c0d7a2993f7eb37b"}, + {file = "coverage-7.15.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e8f91bce78e32343af184c3b7fa28fcf5a9e2641f4b6623d392038f804939188"}, + {file = "coverage-7.15.2-cp313-cp313-win32.whl", hash = "sha256:434e68d531858205895eb0d74b73d20b84260de426387d53c422a5acda2cf050"}, + {file = "coverage-7.15.2-cp313-cp313-win_amd64.whl", hash = "sha256:26c3b04a6377fd7c09800921fa934e3a17c0020439cd59df73e73ae1d4b6a78c"}, + {file = "coverage-7.15.2-cp313-cp313-win_arm64.whl", hash = "sha256:3ed010aa1b69cda8e827aabfca9866216c980e2dca82ab9a78c5f83689964c8b"}, + {file = "coverage-7.15.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:40f633c5c5fc783732f6312280122e859538fa24461235597c13d803ea9a108a"}, + {file = "coverage-7.15.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:075560438765b7a2ef43bf7aa7758661b53d889df47f062a31bda6c1ade553a2"}, + {file = "coverage-7.15.2-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:25fd15dd40a0a2c51a500d664ca29053c09c3259d998407bf982b6e114696138"}, + {file = "coverage-7.15.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b9a6367e4aff723e8ee8190836836124284e8fcd4265e307c844010cfa074f3f"}, + {file = "coverage-7.15.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9854ca62c152874b2060772503535be2e8f53f70b8aaa7686b094888d872f984"}, + {file = "coverage-7.15.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:913b6c56e110da40e035bbd168353bf7aaa2544a5eaccea5d98a4629aac156c7"}, + {file = "coverage-7.15.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aaccad4129d735a8a4d526f26929894c9a4e8ef7034566f210b176749d6906e3"}, + {file = "coverage-7.15.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a164b50081fc7357331c4024ef4d17b78ba325f8380d05f5a69599a7e05257ee"}, + {file = "coverage-7.15.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:bfd341ccf78128e72c094bc70cc25b3ef309c33c7c2c66ba3ed4309549e02de1"}, + {file = "coverage-7.15.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1473b3ba8e7ee0f076117b1a72c23f579a2b9e2bb742f48a8d86ea27ca93f91a"}, + {file = "coverage-7.15.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:17c432b5f73ad52ef46fb06019f6fa7c66ce381961cf0f7dfd1d3a4bd3a98145"}, + {file = "coverage-7.15.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:77f0ef5011df53a4bd1b35211ab122287f8d9b8d7aa1c4553e5c2deb24b1d446"}, + {file = "coverage-7.15.2-cp314-cp314-win32.whl", hash = "sha256:f653e5d7248c1191ec988a85c72edeab46c3ff44f90639a4ed4874ec0be90243"}, + {file = "coverage-7.15.2-cp314-cp314-win_amd64.whl", hash = "sha256:9911f31aad8906abe337c271343485cf20df5e70df5d2f57f9f136e7b55f26bc"}, + {file = "coverage-7.15.2-cp314-cp314-win_arm64.whl", hash = "sha256:e38def96ad59853824c97953fdcd2c320a84ba3ce99b417db78af8bb6c3db635"}, + {file = "coverage-7.15.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:835ec4e20b45f0a7f63ed78f94065aca00de033403df8377bfe8b9c6abc0a7be"}, + {file = "coverage-7.15.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7466cc7ab6dc0db871d264bf99e8779f0917ee63d40730af0552f71535a6e072"}, + {file = "coverage-7.15.2-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e370c12133095ff18432de8c044962be85a5a96d90c6fcbce8e17e76236d2328"}, + {file = "coverage-7.15.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fe41909c9515c3bfdb5f02c4d1f857dba322d9a9a1178069b91eea77889df63a"}, + {file = "coverage-7.15.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6aa28cfb6488e5453b5b762d65f73aa586380f6693a04d58078ce228a29b06c0"}, + {file = "coverage-7.15.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcc0aae933921d03096f53b0b03eeb702129fd406dee59f08d2efacc68681fa5"}, + {file = "coverage-7.15.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c63387e21ab21f512c69c9756a8c7dadd322c7275edb064064433c9a09c3743"}, + {file = "coverage-7.15.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e55510bc98ae943cece9e667a6c0fe94c6a92913720dea34243657a17993d0c"}, + {file = "coverage-7.15.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2ff08701be2d1556fc78b326c80a3e8042da09352ecb3819105f8e386c8a3071"}, + {file = "coverage-7.15.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:38c9518b7103826c403a461544e3c2e77151e8676d06eaed85911a97e962584a"}, + {file = "coverage-7.15.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:dee88b1ed88587abd8c0269a1fc1f4cc77f7750d1dfde2869e2a123af420e67d"}, + {file = "coverage-7.15.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2fbeeeecea279727f8ac16c8e1133ddfeee793e985c86ae343d6a5ce744eef8c"}, + {file = "coverage-7.15.2-cp314-cp314t-win32.whl", hash = "sha256:cb0fddaa6884be6aae36ced9544b5e90f7d5f03845a2853bf47a14953a4e8688"}, + {file = "coverage-7.15.2-cp314-cp314t-win_amd64.whl", hash = "sha256:77f091ea3a9cc611cd29f433565476bc1936c084ac8eee00ea0e7e70c27e4199"}, + {file = "coverage-7.15.2-cp314-cp314t-win_arm64.whl", hash = "sha256:6fc448c377d6eeb00a47c673494bd9bae29280ca53987e1869e67ebedfe20658"}, + {file = "coverage-7.15.2-py3-none-any.whl", hash = "sha256:eb6bcae8d1a9d305351ecb108232441d11c5cfe9de840a04388ba5d2db8d735c"}, + {file = "coverage-7.15.2.tar.gz", hash = "sha256:3df60dc267f0a2ca23cb7a9ab1109c62b9335ffbf519fcfe167157c28c09b81d"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] + +[[package]] +name = "debugpy" +version = "1.8.21" +description = "An implementation of the Debug Adapter Protocol for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "debugpy-1.8.21-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:8eeab7b5462f683452c57c0126aaa5ec4e974ddb705f39ba87dff8818c8e08f9"}, + {file = "debugpy-1.8.21-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:0fddfdc130ac6d8bfc0415b0409822fa901c8f310e5c945ac5653a0352532344"}, + {file = "debugpy-1.8.21-cp310-cp310-win32.whl", hash = "sha256:72b5d676c4cbfac3bac5bb01c138a4656e843f93f03ce2a5f4e394ad49fbee73"}, + {file = "debugpy-1.8.21-cp310-cp310-win_amd64.whl", hash = "sha256:a7fe47fd23da57b9e0bec3f4a8ee65a2dc55782455ed7f2141d75ab5d2eaeef5"}, + {file = "debugpy-1.8.21-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:da456226c7b4c69e35dbe35dcee6623d912000a77816db7856a41af1c72a0264"}, + {file = "debugpy-1.8.21-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:f68b891688e61bdc08b8d364d919ff0051e0b94657b39dcd027bc3173edb7cdc"}, + {file = "debugpy-1.8.21-cp311-cp311-win32.whl", hash = "sha256:f843a8b08c2edeaf9b1582eed4f25441af21a297c22ff16bf76a662557aa9c9e"}, + {file = "debugpy-1.8.21-cp311-cp311-win_amd64.whl", hash = "sha256:84c564d8cc701d41843b29a92814c1f1bef6798724ca9d675c284ad9f6a547d7"}, + {file = "debugpy-1.8.21-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:9f96713896f39c3dff0ee841f47320c3f2983d33c341e009361bb0ebc79adc4e"}, + {file = "debugpy-1.8.21-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:c193d474f0a211191f2b4449d2d06157c689013035bd952f3b617e0ef422b176"}, + {file = "debugpy-1.8.21-cp312-cp312-win32.whl", hash = "sha256:4743373c1cac7f9e74a1b9915bf1dbe0e900eca657ffb170ae07ac8363205ae9"}, + {file = "debugpy-1.8.21-cp312-cp312-win_amd64.whl", hash = "sha256:bd7ba9dd3daa7c2f942c6ca8d4695a16bf9ac16b63615261c7982bc74f7ed20c"}, + {file = "debugpy-1.8.21-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:13678151fc401e2d68c9880b91e28714f797d40422994572b24560ef80910a88"}, + {file = "debugpy-1.8.21-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:ecbd158386c31ffe71d46f72d44d56e66331ab9b16cad649156d514368f23ab2"}, + {file = "debugpy-1.8.21-cp313-cp313-win32.whl", hash = "sha256:2c2ae706dec41d99a9ca1f7ebc987a83e65578363be6f6b3ac9067504917fae1"}, + {file = "debugpy-1.8.21-cp313-cp313-win_amd64.whl", hash = "sha256:aa648733047443eb1d07682c4ef287d36a54507b643ffdf38b09a3ef002c72a0"}, + {file = "debugpy-1.8.21-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:9bb2a685287a2ac9b181cde89edcec64845cb51de7faaa75badb9a698bc24782"}, + {file = "debugpy-1.8.21-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:3d6922439bf33fd38a3e2c447869ebc7b97da5cd3d329ff1ef9bc06c4903437e"}, + {file = "debugpy-1.8.21-cp314-cp314-win32.whl", hash = "sha256:15d4963bd5ffa48f0da0947fd06757fa7621945048a14ad7705431566d3c0e7c"}, + {file = "debugpy-1.8.21-cp314-cp314-win_amd64.whl", hash = "sha256:fe0744a12353406de0ae8ccff0d0a4a666f00801a3db8fd04e7a5f761cd520e8"}, + {file = "debugpy-1.8.21-cp38-cp38-macosx_15_0_x86_64.whl", hash = "sha256:0042da0ecd0a8b50dc4a54395ecd870d258d73fa18776f50c91fdcabdcad2675"}, + {file = "debugpy-1.8.21-cp38-cp38-manylinux_2_34_x86_64.whl", hash = "sha256:ffd932c6796afadab6993ec96745918a8cb2444dbd392074f769db5ea40ab440"}, + {file = "debugpy-1.8.21-cp38-cp38-win32.whl", hash = "sha256:4e7c2d784d78ad4b71a5f8cd7b59c167719ec8a7a0211dbb3eb1bfeda78bc4e2"}, + {file = "debugpy-1.8.21-cp38-cp38-win_amd64.whl", hash = "sha256:aa9d941d6dfe3d0407e4b3ca0b9ec466030e260fbf1174094f68785680f66db6"}, + {file = "debugpy-1.8.21-cp39-cp39-macosx_15_0_x86_64.whl", hash = "sha256:9f5171176a0084b95d2ebe55a4d1f7b2a75b74c5dbec577ebd3a85c740551c36"}, + {file = "debugpy-1.8.21-cp39-cp39-manylinux_2_34_x86_64.whl", hash = "sha256:f15c10084f9861b5e8414a48f18f8e4aadf51a98a59e72c16aa28281ca994672"}, + {file = "debugpy-1.8.21-cp39-cp39-win32.whl", hash = "sha256:4e70cc8b5079f885cb43910924ee0aab73b8b6b2a14eff23afdd9895d86e79eb"}, + {file = "debugpy-1.8.21-cp39-cp39-win_amd64.whl", hash = "sha256:e935f9dc0501be523c8a8e1853c39432e1354e9ece717ae5998fd2371c4542c3"}, + {file = "debugpy-1.8.21-py2.py3-none-any.whl", hash = "sha256:b1e37d333663c8851516a47364ef473da127f9caebe4417e6df6f5825a7e9a92"}, + {file = "debugpy-1.8.21.tar.gz", hash = "sha256:a3c53278e84c94e11bd87c53970ec391d1a67396c8b22609fcac576520e611a6"}, +] + +[[package]] +name = "decorator" +version = "5.3.1" +description = "Decorators for Humans" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "decorator-5.3.1-py3-none-any.whl", hash = "sha256:f47fe6fdbd2edd623ecfe36875d37aba411624e2670dd395dddae1358689bb3c"}, + {file = "decorator-5.3.1.tar.gz", hash = "sha256:4cbcdd55a6efadb9dbea26b858f4fb3264567b52d69ca0d25b721b553f60ea82"}, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] + +[[package]] +name = "distlib" +version = "0.4.3" +description = "Distribution utilities" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "distlib-0.4.3-py2.py3-none-any.whl", hash = "sha256:4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b"}, + {file = "distlib-0.4.3.tar.gz", hash = "sha256:f152097224a0ae24be5a0f6bae1b9359af82133bce63f98a95f86cae1aede9ed"}, +] + +[[package]] +name = "et-xmlfile" +version = "2.0.0" +description = "An implementation of lxml.xmlfile for the standard library" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "et_xmlfile-2.0.0-py3-none-any.whl", hash = "sha256:7a91720bc756843502c3b7504c77b8fe44217c85c537d85037f0f536151b2caa"}, + {file = "et_xmlfile-2.0.0.tar.gz", hash = "sha256:dab3f4764309081ce75662649be815c4c9081e88f0837825f90fd28317d4da54"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["main", "dev"] +markers = "python_version == \"3.10\"" +files = [ + {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, + {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "executing" +version = "2.2.1" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017"}, + {file = "executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""] + +[[package]] +name = "fastjsonschema" +version = "2.21.2" +description = "Fastest Python implementation of JSON schema" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463"}, + {file = "fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de"}, +] + +[package.extras] +devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] + +[[package]] +name = "filelock" +version = "3.32.0" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "filelock-3.32.0-py3-none-any.whl", hash = "sha256:d396bea984af47333ef05e50eae7eff88c84256de6112aea0ec48a233c064fe3"}, + {file = "filelock-3.32.0.tar.gz", hash = "sha256:7be2ad23a14607ccc71808e68fe30848aeace7058ace17852f68e2a68e310402"}, +] + +[[package]] +name = "flake8" +version = "7.3.0" +description = "the modular source code checker: pep8 pyflakes and co" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e"}, + {file = "flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872"}, +] + +[package.dependencies] +mccabe = ">=0.7.0,<0.8.0" +pycodestyle = ">=2.14.0,<2.15.0" +pyflakes = ">=3.4.0,<3.5.0" + +[[package]] +name = "fqdn" +version = "1.5.1" +description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" +optional = false +python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" +groups = ["main"] +files = [ + {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"}, + {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, +] + +[[package]] +name = "h11" +version = "0.16.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.16" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] + +[[package]] +name = "httpx" +version = "0.28.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" + +[package.extras] +brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "identify" +version = "2.6.19" +description = "File identification library for Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "identify-2.6.19-py2.py3-none-any.whl", hash = "sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a"}, + {file = "identify-2.6.19.tar.gz", hash = "sha256:6be5020c38fcb07da56c53733538a3081ea5aa70d36a156f83044bfbf9173842"}, +] + +[package.extras] +license = ["ukkonen"] + +[[package]] +name = "idna" +version = "3.18" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2"}, + {file = "idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848"}, +] + +[package.extras] +all = ["mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.3.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12"}, + {file = "iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730"}, +] + +[[package]] +name = "ipykernel" +version = "7.3.0" +description = "IPython Kernel for Jupyter" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "ipykernel-7.3.0-py3-none-any.whl", hash = "sha256:897eb64da762549ef610698fca5e9675195ec6ac8ec7f19d81ce1ca20c876057"}, + {file = "ipykernel-7.3.0.tar.gz", hash = "sha256:9acaaaf97d16355166e4085afe9d225bfbdf2b7ef520f9df3be8f2b248275e09"}, +] + +[package.dependencies] +appnope = {version = ">=0.1.2", markers = "platform_system == \"Darwin\""} +comm = ">=0.1.1" +debugpy = ">=1.6.5" +ipython = ">=7.23.1" +jupyter-client = ">=8.9.0" +jupyter-core = ">=5.1,<6.0.dev0 || >=6.1.dev0" +matplotlib-inline = ">=0.1" +nest-asyncio2 = ">=1.7.0" +packaging = ">=22" +psutil = ">=5.7" +pyzmq = ">=25" +tornado = ">=6.4.1" +traitlets = ">=5.4.0" + +[package.extras] +cov = ["coverage[toml]", "matplotlib", "pytest-cov", "trio"] +docs = ["intersphinx-registry", "myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] +pyqt5 = ["pyqt5"] +pyside6 = ["pyside6"] +test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0,<10)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "ipython" +version = "8.39.0" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version == \"3.10\"" +files = [ + {file = "ipython-8.39.0-py3-none-any.whl", hash = "sha256:bb3c51c4fa8148ab1dea07a79584d1c854e234ea44aa1283bcb37bc75054651f"}, + {file = "ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} +decorator = "*" +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +jedi = ">=0.16" +matplotlib-inline = "*" +pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} +prompt_toolkit = ">=3.0.41,<3.1.0" +pygments = ">=2.4.0" +stack_data = "*" +traitlets = ">=5.13.0" +typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} + +[package.extras] +all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] +black = ["black"] +doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli ; python_version < \"3.11\"", "typing_extensions"] +kernel = ["ipykernel"] +matplotlib = ["matplotlib"] +nbconvert = ["nbconvert"] +nbformat = ["nbformat"] +notebook = ["ipywidgets", "notebook"] +parallel = ["ipyparallel"] +qtconsole = ["qtconsole"] +test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"] +test-extra = ["curio", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] + +[[package]] +name = "ipython" +version = "9.15.0" +description = "IPython: Productive Interactive Computing" +optional = false +python-versions = ">=3.11" +groups = ["main"] +markers = "python_version >= \"3.11\"" +files = [ + {file = "ipython-9.15.0-py3-none-any.whl", hash = "sha256:515ad9c3cdf0c932a5a9f6245419e8aba706b7bd03c3e1d3a1c83d9351d6aa6e"}, + {file = "ipython-9.15.0.tar.gz", hash = "sha256:da2819ce2aa83135257df830660b1176d986c3d2876db24df01974fa955b2756"}, +] + +[package.dependencies] +colorama = {version = ">=0.4.4", markers = "sys_platform == \"win32\""} +decorator = ">=5.1.0" +ipython-pygments-lexers = ">=1.0.0" +jedi = ">=0.18.2" +matplotlib-inline = ">=0.1.6" +pexpect = {version = ">4.6", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} +prompt_toolkit = ">=3.0.41,<3.1.0" +psutil = {version = ">=7", markers = "sys_platform != \"emscripten\" and sys_platform != \"cygwin\""} +pygments = ">=2.14.0" +stack_data = ">=0.6.0" +traitlets = ">=5.13.0" +typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} + +[package.extras] +all = ["argcomplete (>=3.0)", "ipython[doc,matplotlib,terminal,test,test-extra]", "types-decorator"] +black = ["black"] +doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[matplotlib,test]", "setuptools (>=80.0)", "sphinx (>=8.0)", "sphinx-rtd-theme (>=0.1.8)", "sphinx_toml (==0.0.4)", "typing_extensions"] +matplotlib = ["matplotlib (>3.9)"] +test = ["packaging (>=23.0.0)", "pytest (>=7.0.0)", "pytest-asyncio (>=1.0.0)", "setuptools (>=80.0)", "testpath (>=0.2)"] +test-extra = ["curio", "ipykernel (>6.30)", "ipython[matplotlib]", "ipython[test]", "jupyter_ai", "nbclient", "nbformat", "numpy (>=2.0)", "pandas (>2.1)", "trio (>=0.22.0)"] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +description = "Defines a variety of Pygments lexers for highlighting IPython code." +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "python_version >= \"3.11\"" +files = [ + {file = "ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c"}, + {file = "ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81"}, +] + +[package.dependencies] +pygments = "*" + +[[package]] +name = "isoduration" +version = "20.11.0" +description = "Operations with ISO 8601 durations" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"}, + {file = "isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9"}, +] + +[package.dependencies] +arrow = ">=0.15.0" + +[[package]] +name = "jedi" +version = "0.20.0" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "jedi-0.20.0-py2.py3-none-any.whl", hash = "sha256:7bdd9c2634f56713299976f4cbd59cb3fa92165cc5e05ea811fb253480728b67"}, + {file = "jedi-0.20.0.tar.gz", hash = "sha256:c3f4ccbd276696f4b19c54618d4fb18f9fc24b0aef02acf704b23f487daa1011"}, +] + +[package.dependencies] +parso = ">=0.8.6,<0.9.0" + +[package.extras] +dev = ["Django", "attrs", "colorama", "docopt", "flake8 (==7.1.2)", "pytest (<9.0.0)", "types-setuptools (==80.9.0.20250529)", "typing-extensions", "zuban (==0.7.0)"] +docs = ["Jinja2 (==3.1.6)", "MarkupSafe (==3.0.3)", "Pygments (==2.20.0)", "Sphinx (==9.1.0)", "alabaster (==1.0.0)", "babel (==2.18.0)", "certifi (==2026.4.22)", "charset-normalizer (==3.4.7)", "docutils (==0.22.4)", "idna (==3.13)", "imagesize (==2.0.0)", "iniconfig (==2.3.0)", "packaging (==26.2)", "pluggy (==1.6.0)", "pytest (==9.0.3)", "requests (==2.33.1)", "roman-numerals (==4.1.0)", "snowballstemmer (==3.0.1)", "sphinx-rtd-theme (==3.1.0)", "sphinxcontrib-applehelp (==2.0.0)", "sphinxcontrib-devhelp (==2.0.0)", "sphinxcontrib-htmlhelp (==2.1.0)", "sphinxcontrib-jquery (==4.1)", "sphinxcontrib-jsmath (==1.0.1)", "sphinxcontrib-qthelp (==2.0.0)", "sphinxcontrib-serializinghtml (==2.0.0)", "urllib3 (==2.6.3)"] + +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "json5" +version = "0.15.0" +description = "A Python implementation of the JSON5 data format." +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "json5-0.15.0-py3-none-any.whl", hash = "sha256:56636a30c0e8a4665fe2179c0212f32eae3796dea89ea6f649b9436ecdb39618"}, + {file = "json5-0.15.0.tar.gz", hash = "sha256:7424d1f1eb1d56da6e3d70643f53619862b4ce81440bdb8ecfd6f875e5ba4a71"}, +] + +[[package]] +name = "jsonpointer" +version = "3.1.1" +description = "Identify specific nodes in a JSON document (RFC 6901) " +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "jsonpointer-3.1.1-py3-none-any.whl", hash = "sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca"}, + {file = "jsonpointer-3.1.1.tar.gz", hash = "sha256:0b801c7db33a904024f6004d526dcc53bbb8a4a0f4e32bfd10beadf60adf1900"}, +] + +[[package]] +name = "jsonschema" +version = "4.26.0" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce"}, + {file = "jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +fqdn = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +idna = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +isoduration = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +jsonpointer = {version = ">1.13", optional = true, markers = "extra == \"format-nongpl\""} +jsonschema-specifications = ">=2023.3.6" +referencing = ">=0.28.4" +rfc3339-validator = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +rfc3986-validator = {version = ">0.1.0", optional = true, markers = "extra == \"format-nongpl\""} +rfc3987-syntax = {version = ">=1.1.0", optional = true, markers = "extra == \"format-nongpl\""} +rpds-py = ">=0.25.0" +uri-template = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} +webcolors = {version = ">=24.6.0", optional = true, markers = "extra == \"format-nongpl\""} + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe"}, + {file = "jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "jupyter-builder" +version = "1.1.1" +description = "JupyterLab build tools" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "jupyter_builder-1.1.1-py3-none-any.whl", hash = "sha256:f9c14bc55c0488a073f62af12d468936fcf9ecb7e9dd802f6f9c33de46ad70db"}, + {file = "jupyter_builder-1.1.1.tar.gz", hash = "sha256:1a13977912b08deda77fce2c803940131c27cf77a27ed64b9ffca25aa0ed7e6c"}, +] + +[package.dependencies] +jupyter-core = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} +traitlets = "*" + +[package.extras] +dev = ["build", "hatch", "mypy", "pre-commit", "ruff (==0.15.20)"] +test = ["copier (>=9.3,<10)", "coverage", "jinja2-time", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-cov"] + +[[package]] +name = "jupyter-client" +version = "8.9.1" +description = "Jupyter protocol implementation and client libraries" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "jupyter_client-8.9.1-py3-none-any.whl", hash = "sha256:0b7a295bc46e8751e9adae84781f726c851c1d911bd793edc4a3bde942e3da81"}, + {file = "jupyter_client-8.9.1.tar.gz", hash = "sha256:a58f730dd9e728ba16ba1d62ebccf7ffe1ebbdbce4e95cfae941b7321ae1f4fa"}, +] + +[package.dependencies] +jupyter-core = ">=5.1" +python-dateutil = ">=2.8.2" +pyzmq = ">=25.0" +tornado = ">=6.4.1" +traitlets = ">=5.3" +typing-extensions = ">=4.13.0" + +[package.extras] +docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +orjson = ["orjson"] +test = ["anyio", "coverage", "ipykernel (>=6.14)", "msgpack", "mypy ; platform_python_implementation != \"PyPy\"", "paramiko ; sys_platform == \"win32\"", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.6.2)", "pytest-timeout"] + +[[package]] +name = "jupyter-core" +version = "5.9.1" +description = "Jupyter core package. A base package on which Jupyter projects rely." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "jupyter_core-5.9.1-py3-none-any.whl", hash = "sha256:ebf87fdc6073d142e114c72c9e29a9d7ca03fad818c5d300ce2adc1fb0743407"}, + {file = "jupyter_core-5.9.1.tar.gz", hash = "sha256:4d09aaff303b9566c3ce657f580bd089ff5c91f5f89cf7d8846c3cdf465b5508"}, +] + +[package.dependencies] +platformdirs = ">=2.5" +traitlets = ">=5.3" + +[package.extras] +docs = ["intersphinx-registry", "myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-spelling", "traitlets"] +test = ["ipykernel", "pre-commit", "pytest (<9)", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "jupyter-events" +version = "0.12.1" +description = "Jupyter Event System library" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "jupyter_events-0.12.1-py3-none-any.whl", hash = "sha256:c366585253f537a627da52fa7ca7410c5b5301fe893f511e7b077c2d93ec8bcf"}, + {file = "jupyter_events-0.12.1.tar.gz", hash = "sha256:faff25f77218335752f35f23c5fe6e4a392a7bd99a5939ccb9b8fbf594636cf3"}, +] + +[package.dependencies] +jsonschema = {version = ">=4.18.0", extras = ["format-nongpl"]} +packaging = "*" +python-json-logger = ">=2.0.4" +pyyaml = ">=5.3" +referencing = "*" +rfc3339-validator = "*" +rfc3986-validator = ">=0.1.1" +traitlets = ">=5.3" + +[package.extras] +cli = ["click", "rich"] +docs = ["jupyterlite-sphinx", "myst-parser", "pydata-sphinx-theme (>=0.16)", "sphinx (>=8)", "sphinxcontrib-spelling"] +test = ["click", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "pytest-console-scripts", "rich"] + +[[package]] +name = "jupyter-lsp" +version = "2.3.1" +description = "Multi-Language Server WebSocket proxy for Jupyter Notebook/Lab server" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "jupyter_lsp-2.3.1-py3-none-any.whl", hash = "sha256:71b954d834e85ff3096400554f2eefaf7fe37053036f9a782b0f7c5e42dadb81"}, + {file = "jupyter_lsp-2.3.1.tar.gz", hash = "sha256:fdf8a4aa7d85813976d6e29e95e6a2c8f752701f926f2715305249a3829805a6"}, +] + +[package.dependencies] +jupyter_server = ">=1.1.2" + +[[package]] +name = "jupyter-server" +version = "2.20.0" +description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "jupyter_server-2.20.0-py3-none-any.whl", hash = "sha256:c3b67c93c471e947c18b5026f04f21614218adb706df8f48227d3ee8e0a7cdcc"}, + {file = "jupyter_server-2.20.0.tar.gz", hash = "sha256:b5778ba337d8015a3dc2b80803ecdd5ac18d3797fddf61a50ea5fb472b4ebe14"}, +] + +[package.dependencies] +anyio = ">=3.1.0" +argon2-cffi = ">=21.1" +jinja2 = ">=3.0.3" +jupyter-client = ">=7.4.4" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +jupyter-events = ">=0.11.0" +jupyter-server-terminals = ">=0.4.4" +nbconvert = ">=6.4.4" +nbformat = ">=5.3.0" +overrides = {version = ">=5.0", markers = "python_version < \"3.12\""} +packaging = ">=22.0" +prometheus-client = ">=0.9" +pywinpty = {version = ">=2.0.1,<3.0.4 || >3.0.4", markers = "os_name == \"nt\""} +pyzmq = ">=24" +send2trash = ">=1.8.2" +terminado = ">=0.8.3" +tornado = ">=6.2.0" +traitlets = ">=5.6.0" +websocket-client = ">=1.7" + +[package.extras] +docs = ["ipykernel", "jinja2", "jupyter-client", "myst-parser", "nbformat", "prometheus-client", "pydata-sphinx-theme", "send2trash", "sphinx (<9.0)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-openapi (>=0.8.0)", "sphinxcontrib-spelling", "sphinxemoji", "tornado", "typing-extensions"] +test = ["flaky", "ipykernel", "pre-commit", "pytest (>=7.0,<10)", "pytest-console-scripts", "pytest-jupyter[server] (>=0.7)", "pytest-timeout", "requests"] + +[[package]] +name = "jupyter-server-terminals" +version = "0.5.4" +description = "A Jupyter Server Extension Providing Terminals." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "jupyter_server_terminals-0.5.4-py3-none-any.whl", hash = "sha256:55be353fc74a80bc7f3b20e6be50a55a61cd525626f578dcb66a5708e2007d14"}, + {file = "jupyter_server_terminals-0.5.4.tar.gz", hash = "sha256:bbda128ed41d0be9020349f9f1f2a4ab9952a73ed5f5ac9f1419794761fb87f5"}, +] + +[package.dependencies] +pywinpty = {version = ">=2.0.3", markers = "os_name == \"nt\""} +terminado = ">=0.8.3" + +[package.extras] +docs = ["jinja2", "jupyter-server", "mistune (<4.0)", "myst-parser", "nbformat", "packaging", "pydata-sphinx-theme", "sphinxcontrib-github-alt", "sphinxcontrib-openapi", "sphinxcontrib-spelling", "sphinxemoji", "tornado"] +test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (>=0.5.3)", "pytest-timeout"] + +[[package]] +name = "jupyterlab" +version = "4.6.2" +description = "JupyterLab computational environment" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "jupyterlab-4.6.2-py3-none-any.whl", hash = "sha256:5964447036629adfcd3fc0969effc1da6f47d2cbd0a60b2c2eea7c31be0ec6a8"}, + {file = "jupyterlab-4.6.2.tar.gz", hash = "sha256:e18ce8b34f3de350e93cd5b2c4f3ae884cbe266eb76bf5d6825a4ed34c13bcff"}, +] + +[package.dependencies] +async-lru = ">=1.0.0" +httpx = ">=0.25.0,<1" +ipykernel = ">=6.5.0,<6.30.0 || >6.30.0" +jinja2 = ">=3.0.3" +jupyter-builder = ">=1.0.2" +jupyter-core = "*" +jupyter-lsp = ">=2.0.0" +jupyter-server = ">=2.19.0,<3" +jupyterlab-server = ">=2.28.0,<3" +notebook-shim = ">=0.2" +packaging = ">=23.2" +tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} +tornado = ">=6.2.0" +traitlets = "*" +typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.12\""} + +[package.extras] +dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.15.15)", "shellcheck-py"] +docs-screenshots = ["altair (==6.0.0)", "ipykernel (<7.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.5)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.3.post1)", "matplotlib (==3.10.0)", "nbconvert (>=7.0.0)", "pandas (==2.2.3)", "scipy (==1.15.1)"] +test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "pytest-xdist", "requests", "requests-cache", "virtualenv"] +upgrade-extension = ["copier (>=9,<10)", "jinja2-time (<0.3)", "pydantic (<3.0)", "pyyaml-include (<3.0)", "tomli-w (<2.0)"] + +[[package]] +name = "jupyterlab-pygments" +version = "0.3.0" +description = "Pygments theme using JupyterLab CSS variables" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"}, + {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"}, +] + +[[package]] +name = "jupyterlab-server" +version = "2.28.0" +description = "A set of server components for JupyterLab and JupyterLab like applications." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968"}, + {file = "jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c"}, +] + +[package.dependencies] +babel = ">=2.10" +jinja2 = ">=3.0.3" +json5 = ">=0.9.0" +jsonschema = ">=4.18.0" +jupyter-server = ">=1.21,<3" +packaging = ">=21.3" +requests = ">=2.31" + +[package.extras] +docs = ["autodoc-traits", "jinja2 (<3.2.0)", "mistune (<4)", "myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-copybutton", "sphinxcontrib-openapi (>0.8)"] +openapi = ["openapi-core (>=0.18.0,<0.19.0)", "ruamel-yaml"] +test = ["hatch", "ipykernel", "openapi-core (>=0.18.0,<0.19.0)", "openapi-spec-validator (>=0.6.0,<0.8.0)", "pytest (>=7.0,<8)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter[server] (>=0.6.2)", "pytest-timeout", "requests-mock", "ruamel-yaml", "sphinxcontrib-spelling", "strict-rfc3339", "werkzeug"] + +[[package]] +name = "lark" +version = "1.3.1" +description = "a modern parsing library" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12"}, + {file = "lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905"}, +] + +[package.extras] +atomic-cache = ["atomicwrites"] +interegular = ["interegular (>=0.3.1,<0.4.0)"] +nearley = ["js2py"] +regex = ["regex"] + +[[package]] +name = "librt" +version = "0.13.0" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "librt-0.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:34e47058fcc69a313293d6dee94216a4f30c929ae6f2476e58c5ba635aa639d5"}, + {file = "librt-0.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dbdd5b6509d0c2a8fe72cf494c299a61dbd58142a90a4190664ae159e4a7b547"}, + {file = "librt-0.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e56ea4ee4df77585a6b5c138f6538680886024fa559f5b55bd14b12e98e67b2"}, + {file = "librt-0.13.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f1f9cc4d09a46d9cb3c2063ae100629d3f52a6517c3c08c2f4c9828261883929"}, + {file = "librt-0.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f125f5d46b20f89dc5587a55cc416b4ba2a5b2ffda36d048ee120e17598a653a"}, + {file = "librt-0.13.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2608d3b39f9e0b4a66a130d9150c615cba40a5090d25eeeaa225e0e46de8c0ac"}, + {file = "librt-0.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9fd35e95ab5e45c3901d37110263c7db85a961110f5460588fe37f8c131f88a7"}, + {file = "librt-0.13.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5f31b0aa13c9b04370d4da6be1ab7779776b3a075cceb6747a39a4be85fe1e40"}, + {file = "librt-0.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0b795f5fc70fbbb787ceaf79bb3a0d627bcc33c53de51741755263ec406b775a"}, + {file = "librt-0.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36b306a623aaad96fe4b378692b54f9c0789fccd833b9851753d5fbf6138cfde"}, + {file = "librt-0.13.0-cp310-cp310-win32.whl", hash = "sha256:a3762e75fcac8c9e4dacaaf438bffd9003e2ca2c531b756f3c0035deefa674c8"}, + {file = "librt-0.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:d63bae12a8aeb51380be3438e4dc4bd27354d0f8e19166b2f44e3e94d6f552dc"}, + {file = "librt-0.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1b5a7bbff495baedbd9b916c367d66854008f8f3b575908ded477c499dc60082"}, + {file = "librt-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34bc7938b9fdf14fe32a406c19c71faf894c5cee7e7474bd0be2f17200b82d14"}, + {file = "librt-0.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f40e56b61b41be5f7dec938cfeffd660668cf4b5e72c78e7bd671d66b7bc2c79"}, + {file = "librt-0.13.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:9c5d02b89de5acd0379a51ec44a89476fb03df6145442e1c8ecd6bee2f91b176"}, + {file = "librt-0.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7db9a3ff32ef5f7d1703d93831a3316cdf0b537de6a1cc03cc8fdd09b9194e89"}, + {file = "librt-0.13.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3dbb2a31882456cadc7053378e81ad7ed7693db4ac9f98ab5f81ef034aa8ec9f"}, + {file = "librt-0.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c6014e3c80f9c1fe268ef8b0e0ef113bac672cc032f2f93866e7ddad4f3e663d"}, + {file = "librt-0.13.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:091b60a4d2174fc1ec5c34cdc0b72efb6224753d76b7da61ebeab7a191aec8bd"}, + {file = "librt-0.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:66cb1138f384a191a6d75f986064841fcfdc0cea98f7bd9c9ab9b38049917588"}, + {file = "librt-0.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:17221a7569f8f292aa0014226e48aa25b8c2b08da18088cd230953d0ea0f9cd1"}, + {file = "librt-0.13.0-cp311-cp311-win32.whl", hash = "sha256:fc67741da44c6eaa90e01eafb586bbba9b51eb5b6ed381ee6f5ae72eb3316d21"}, + {file = "librt-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:cc99dfb62b23c9207c33d0be8a2e2af7a42e21e6ea388b380a0c948c7b88953b"}, + {file = "librt-0.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:40ccd13c252d3fe473ffc8a57be7565abc8b64cf1b108344c859d5164f7f3e0c"}, + {file = "librt-0.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30536798f4504c0fad0885b1d371b0539abb081e4570c9d7c641cb51141b49f0"}, + {file = "librt-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:93d24ebb82aa4420b1409c389e7857bc35bd0b668007ac8172427d5c73cc8cc5"}, + {file = "librt-0.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb8a1adce42d8b75485a5d56a9623a50bcab995b6079f1dac59fc44034dd93d9"}, + {file = "librt-0.13.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0763ca2ab66058174f9dee426dc64f5e0a89c24a7df8d3fe3f1836c04e25de4b"}, + {file = "librt-0.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b222493da6e7b6199db9bd79502436cf5a27da3c1f7fa83c7e285444fc93fd03"}, + {file = "librt-0.13.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fadc63331f4388c3dc90090448f682a7e9feafc11481391c1e94f2f907a3976e"}, + {file = "librt-0.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:70d9c62a4cffd9f23396cd5ef93fc5d11b31596b9b7d6306074abe3d5fcf09bd"}, + {file = "librt-0.13.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:66c0e7e6b02a155576df2c77ec933a70b72da726e248c494abf690923e624348"}, + {file = "librt-0.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ac04bcd3328eb91d99dfedf6a60d9c1f15d3434e6f6daf922f0420f7d90b85c7"}, + {file = "librt-0.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db327e7271e653c32040b85ae6188059c924b57d7e1e29f935523fa017cd4e82"}, + {file = "librt-0.13.0-cp312-cp312-win32.whl", hash = "sha256:860bd1d8ba48456ce08feaf8d343a8aaeb2fa086f2bcaa2a923fa3f7a3ff9aa3"}, + {file = "librt-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:e54a315caf843c8d77e388cadc56ea9ded569935ee2d2347d7ea94992e5aa6fa"}, + {file = "librt-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:c718e99a0992127af84385378460db624103b559ab260435abcfe77a4e4ed1c1"}, + {file = "librt-0.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a468951af16155824e88bdd8326ebe5bdb371f3ec0ac04642994b98201d914f3"}, + {file = "librt-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ae01d8512cc17079e53425635327dbf3f7ff57a42c00dec348bf79791c56444c"}, + {file = "librt-0.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32c26893cd085c1efe83219e78d866da23fb20a066101b8f68210004361d224c"}, + {file = "librt-0.13.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5929da1981a46bcf4b28b1b9499905f0ff58e2419da402a048234e9783acbc4b"}, + {file = "librt-0.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94b85d664d777bab6c0d709416cb42938251fda9e221b79e3a2215d85df5f4f9"}, + {file = "librt-0.13.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:531b2df3e9fe96b1fcf73a6d165921e4656be5f58d631d384ebce344298368db"}, + {file = "librt-0.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:109b84a9edf69ad89dc1f66358659e14a031baca95e3e5b0060bd903ede8efd6"}, + {file = "librt-0.13.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1304368a3e7ffc3e9db986796cc5326fdb5943a3567ecc137cff318e4240c0e7"}, + {file = "librt-0.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e4f9b472e7d308d94b62c801982065661158c6ed02790d6c7ddb4337cea0f9c1"}, + {file = "librt-0.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9f836c37478f167a81200d8c8b2c920a22224564bed2c23d7aeec760965c367a"}, + {file = "librt-0.13.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:4000d961ff9598ac6ea603c6c836a5ed49bc205ade5fc378b998dfe1e2c36628"}, + {file = "librt-0.13.0-cp313-cp313-win32.whl", hash = "sha256:79e44cff71750d299d61a678e49995b0d5935a9cda238c2574daeca3ba536927"}, + {file = "librt-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:54dab44a847d5ad1acd05c8a83fe518ae685516ecf4d3f7cc6e3df2a66767650"}, + {file = "librt-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:d4cb6fbfdf874340ab5e51450753c0f817b6958a3621125ee695bbc3de866566"}, + {file = "librt-0.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:25218d94b1d2cbc0ba1d8a3f9dc9af578d9646e5ed16443a70cde1dfdcce6d71"}, + {file = "librt-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f26629539d4893c2957a16c41bb058e1e135c1f150f6a2e25ed047f64cf3f5c6"}, + {file = "librt-0.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4517d47b2b8af26975a406fba7d314de9696d864252e0257c6ea90238cfe27f"}, + {file = "librt-0.13.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f19e181de5b3a1148bb3420b8c4b0b0ea0fce6950099724ad151d6cea5acc180"}, + {file = "librt-0.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22034924f5b42d5a56371cf271771bfeaabf235a7a8b6264bef2d20013f786c6"}, + {file = "librt-0.13.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7897db4e95e22468bdda33d8e012ceacd0182abf001e6389d763f0def6286b9"}, + {file = "librt-0.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1ce61b3746545029d4f5c17d6bd74b676254ad98433086c846ffb5e8fa73f007"}, + {file = "librt-0.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:46c330e82565962c761dbce7941be2cff7db674ee807455a8d0cadc5f9b759b0"}, + {file = "librt-0.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:375f5af8f99cbaa99dd293af986e3d57caabc9ba81a5d3f021603764854197a1"}, + {file = "librt-0.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9320d34c3376ae204b2cd176e8d4883a013934e0aef822f1aed9c536490c275d"}, + {file = "librt-0.13.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:9af313c66157a69dc69ea0059a66961692250e0dc95af9c385a48ffb770a0d16"}, + {file = "librt-0.13.0-cp314-cp314-win32.whl", hash = "sha256:f2a7253458e34f33543551394ae4fe104b497ec2a65ac266074de64c1df82e37"}, + {file = "librt-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:a3dfe4edf10e8ed7e55b026a8bfc2c2a8704218b659cd4bffdf604fab966dc39"}, + {file = "librt-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:68a5faee4bba381cb93b5961f684a514cf0053cb92308ff9c792c2fea0b174c6"}, + {file = "librt-0.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a38fb81d8376dfa2f8963b265fec07637802b0d01e2a127c19c66cb070fb24f5"}, + {file = "librt-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d4c8d9bd5abce34b2e75edb3bf37ab0f34e49b1f915a40ae8468eb7c85bc5b46"}, + {file = "librt-0.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:387e2f1d27e89bffe0d3f520f0da0662c973fd607ca16c1808f8a5085419485e"}, + {file = "librt-0.13.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:4f6db193d2e5e0ed60359b9a5a682cd67205d0d3b1e459a867dd4b5c4e7eaa7a"}, + {file = "librt-0.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d38604854e8d22faadf683ec6c02bb0f886e2ba56ef981a1c36ee275f21ea22"}, + {file = "librt-0.13.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:371f7ce73026815dafd51c50ce38416e91428b28c4b2ec97cd39271164b0045c"}, + {file = "librt-0.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3aaedf52171bee90860704c560bc798fe83b76247df47568e0197e9b13c735a0"}, + {file = "librt-0.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:96bad8725a4f196a798366c25ce075d1f7543a4ec045ffc13e6a7ec095cdab04"}, + {file = "librt-0.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:6bf6a559ffe4a93bbea6cf31ddf01a7fd9ba342ef51f27beb178e318b74acd61"}, + {file = "librt-0.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:301067672387902c55f94b51d5022304b36c966ea9fe1f21caab99a9bef487c9"}, + {file = "librt-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:5fdcf34f86de8fb66d7dc7589f96ba91c4aa46671200d400e6fd6f109a483f18"}, + {file = "librt-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:260c33e92263fa629b4f6d3c51967a1c2158fe6c33237aaa3ebeac586b085259"}, + {file = "librt-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2f281549a4c52ac7bb97997f14353f8bd0e53a34ca0dad1c905cfd0b4a58ae99"}, + {file = "librt-0.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f442e3954b1addc759faae22a7c9a3f1e16d7d1db3f484279dc27d62e06968fa"}, + {file = "librt-0.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9e786428f291dd2d2f1cbfc0e0caa45a2e395fab0ad3e2c9314daa8873414390"}, + {file = "librt-0.13.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21b7ac084f701a9cdff6139745a6620579d65a9379ac2d9d50a86368b109e63c"}, + {file = "librt-0.13.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a6e556d6aba31c93dd97ce661d66614d2429c0a3923f9dc8f0af7e8df10223a4"}, + {file = "librt-0.13.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3657346f867469e962549435aa05fd15330b1d6a92829f8e27988e194382d005"}, + {file = "librt-0.13.0-cp39-cp39-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:791aa18a373b90da8ac3c44fc77544f33fdf53ae403acdce9b39f1c26b4a3b94"}, + {file = "librt-0.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d6fb0eaa108814581c4d3bfbd068c3fb6757812a81415008d1bae08267cca360"}, + {file = "librt-0.13.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:a001519c315d5db40710f2665d32c4791f1d4779fc96a9423fd18d92c8b9ac7b"}, + {file = "librt-0.13.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:d9188caac26e47671b52836a5e2a49873a7fc11c673b0c122d22515f98bc14e1"}, + {file = "librt-0.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:05d96b80b95d3a2721b619f8982b8558848b04875bb4772fd54842b59f61dd97"}, + {file = "librt-0.13.0-cp39-cp39-win32.whl", hash = "sha256:c3cd253cf32fe4f4662960d6bf7d55cb8be0c31a5d644a4d48aeafebaff3409a"}, + {file = "librt-0.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:b15e26cc0fe622d0c67e98bee6ef6bc8f792e20ee3006aa12627a00463d9399f"}, + {file = "librt-0.13.0.tar.gz", hash = "sha256:1d2a610c14ac0d0750ee0a3ab8548e83155258387891caaca04def4bf7289781"}, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, +] + +[[package]] +name = "matplotlib-inline" +version = "0.2.2" +description = "Inline Matplotlib backend for Jupyter" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "matplotlib_inline-0.2.2-py3-none-any.whl", hash = "sha256:3c821cf1c209f59fb2d2d64abbf5b23b67bcb2210d663f9918dd851c6da1fcf6"}, + {file = "matplotlib_inline-0.2.2.tar.gz", hash = "sha256:72f3fe8fce36b70d4a5b612f899090cd0401deddc4ea90e1572b9f4bfb058c79"}, +] + +[package.dependencies] +traitlets = "*" + +[package.extras] +test = ["flake8", "matplotlib", "nbdime", "nbval", "notebook", "pytest"] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "mistune" +version = "3.3.4" +description = "A sane and fast Markdown parser with useful plugins and renderers" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "mistune-3.3.4-py3-none-any.whl", hash = "sha256:ee015381e955e370962968befe1d729ab60fafb6a715ac6751763fbce38c8d4a"}, + {file = "mistune-3.3.4.tar.gz", hash = "sha256:58b5c96d6fcb61190dfe5fae498d2b2065f99cf61e9649418fd54cf1ada86dfe"}, +] + +[package.dependencies] +typing-extensions = {version = "*", markers = "python_version < \"3.11\""} + +[[package]] +name = "mypy" +version = "2.3.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "mypy-2.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1fa8d916ac3b705af733c4c1e6c9ebe38fd0d52beb15b105c3e8355b55e6ecdc"}, + {file = "mypy-2.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:28e1e2af8cd8fff551fd30f2fe4b03fb76764ac8b1ba6c6a1bd00ad32b412db3"}, + {file = "mypy-2.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e77244df3843048c3f927182916730e40c124cbaa43905c1fb86cb382aa0805"}, + {file = "mypy-2.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9559ab18a9c9957dfa3004ab57cd4bac5f26a724329a9584e583367f0c2e1117"}, + {file = "mypy-2.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:09abd66d8685e73f8f7d17b847c3e104d9a7b164a8706ea87d6c96a3d45816d5"}, + {file = "mypy-2.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e91adad1ca81742ac7ef9893959911df867752206b37135185e88dfb3c89494"}, + {file = "mypy-2.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:6f99ec626e3c3a2f7c0b22c5b90ddb5dabb1c18729c971e9bdaca1f1766d2cee"}, + {file = "mypy-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3419d00717afbc5265b50dd14b1278f29ea4884dd398ab67873489ac093fd329"}, + {file = "mypy-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cfca8ee88544090f86b6dcce05ec55d66eb48a762412ac2507810ba4bd793b6f"}, + {file = "mypy-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:75cbb4b9ef04a0c84a957f07abc4504fbf64b8dcc145675101f2d3a78a4b1d6a"}, + {file = "mypy-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:982e3d53dd23d0a4cef67dd66791fdbede0cf38f9eb617bf47663554c51e1e36"}, + {file = "mypy-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85c5385b93012ffa3b31479ab579aef5415f4f3a32c6cf1ae07a984d2a0ff461"}, + {file = "mypy-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:13b1b16e2fa39f3b2e33fb1c468abc7a69369fa2e886b4b87b5afc81472325cd"}, + {file = "mypy-2.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:b5cd2f027a972a4a5f2278a11fac9747f5f81a53a30b714d74950b6807e55568"}, + {file = "mypy-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d53fc67b9d28a43c6199077f49fea0f05839e36cf6158500331c9549225e5a5"}, + {file = "mypy-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fbc00cee7bdbb9291979ddc9d08034a29dfcda4932628c9bbc28c1edd589df0c"}, + {file = "mypy-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:04e617030eca5221909c8b7d8d7fd1c637948199aa2100b2ad9813feb07e1491"}, + {file = "mypy-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:56c184d2c20ca6b6378d58d1960270a767f41f5e44acbbd27f05effef4f4e1d7"}, + {file = "mypy-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3961a4a34b05f7c74b0f05aa51fbfe99a2d1e126038df40318d15c8f558b7ef3"}, + {file = "mypy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b1942b9314d4c784b8ea1dbab4972603290e5dd5630f06675f13aec97526bc4c"}, + {file = "mypy-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:be51653d7669d7d7955d613b8d0bb57d5b652eaf71a873ddf65ac87254dd2595"}, + {file = "mypy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:91ad22a52ae2c7e621c2f67c94d5a17f66b3209a4cff5cf8a573579835c69e97"}, + {file = "mypy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:99ac767cc5d3b64c8d0ae226ead10c96694f94e4e7da1668642225dcd4e75aac"}, + {file = "mypy-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de6d2c484742a4d7b0ed6d07b143375624d3b899c5749c7b3c947f56261f48a6"}, + {file = "mypy-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7da939dd335cfd2ad788bdfd081c9f4e47634ab995e5a45eb15fd1e5bc052f8b"}, + {file = "mypy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7247eb2824f996722a949530183394921ca71deb9680052a338cf53cff7925c2"}, + {file = "mypy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:75b0984bb3cbd76bb5c9291a8671f7ae66ca3b51c7584c358fc2e923259f0757"}, + {file = "mypy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:d78fcf900b59cb7e82cb7e3a235e31b462d9333d92285bd1e4952d355b8ffba1"}, + {file = "mypy-2.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ea317b060ce83e26050f8f9e4d7d6bf44ed7597c8ff9990bccffbb9d1d8522db"}, + {file = "mypy-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:094af99f92638aa92852326188b85a89e50f4a472f44827c03362228482f0762"}, + {file = "mypy-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de121747278144fc9ae7caa2e978cf5df12aebc82933182f5b3b86081a30baef"}, + {file = "mypy-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37fa4de896a84e2dc9200d91e614c22563b43d1a266789d4bbac7b22ebe6192b"}, + {file = "mypy-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f1b3a98dfd21058bc759bb3337d5d1f61d0fdf9f3cf9c00f4291790fb5427bff"}, + {file = "mypy-2.3.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:944c665d984157cb96a679dfb7a4a81dd1d36b24b9c284b699514e6e626b82d4"}, + {file = "mypy-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:4359424140d985192c778c1ce2c114a10c1ca58a381ed79cfa70d37df94b299f"}, + {file = "mypy-2.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:3dd0bed92c4bdec57c42505b96416fb9e6a5aa7be84d2809bcd5f2ecec2860d7"}, + {file = "mypy-2.3.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:691fdc37132b1ae628d834f672e74de83462d9fb4aff621835767fb43a8dd373"}, + {file = "mypy-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:aec15d465d477558fd842757b487849007311cf3897849cdda0e3162ac0ac556"}, + {file = "mypy-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b352b7e49f5e6576009e8df730e1ff4f915cb565b851b396d2ffe2f5a6f5da88"}, + {file = "mypy-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c6c6bf687b17f90dbfcad95b960d32eaa0154c00da45f03ab50bf8952e047fe"}, + {file = "mypy-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f4ed18f111bfe2d599bca7468e7f9251042c1c2118f762c8de2766a56d773c60"}, + {file = "mypy-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0b025a93cffb9781d231f232be07a17912f35f10a313c24f301c81e842870654"}, + {file = "mypy-2.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:adebc76aab4f3495a88b41d48aa4aff0c03f2822501da76625afcca5975f19e5"}, + {file = "mypy-2.3.0-py3-none-any.whl", hash = "sha256:6b1cdb579446b60432432b2b2403a6201b4b475a004d7f488511c9ba177c9e88"}, + {file = "mypy-2.3.0.tar.gz", hash = "sha256:465965d41cd9a2726694e983e8ce7113259327bec798115d1e1dfa2a52fb666e"}, +] + +[package.dependencies] +ast-serialize = ">=0.6.0,<1.0.0" +librt = {version = ">=0.13.0", markers = "platform_python_implementation != \"PyPy\""} +mypy_extensions = ">=1.0.0" +pathspec = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing_extensions = {version = ">=4.6.0", markers = "python_version < \"3.15\""} + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "nbclient" +version = "0.11.0" +description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." +optional = false +python-versions = ">=3.10.0" +groups = ["main"] +files = [ + {file = "nbclient-0.11.0-py3-none-any.whl", hash = "sha256:ef7fa0d59d6e1d41103933d8a445a18d5de860ca6b613b87b8574accdb3c2895"}, + {file = "nbclient-0.11.0.tar.gz", hash = "sha256:04a134a5b087f2c5887f228aca155db50169b8cd9334dee6942c8e927e56081a"}, +] + +[package.dependencies] +jupyter-client = ">=7.0.0" +jupyter-core = ">=5.4.0" +nbformat = ">=5.2.0" +traitlets = ">=5.13" + +[package.extras] +dev = ["pre-commit"] +docs = ["autodoc-traits", "flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "mock", "moto", "myst-parser", "nbconvert (>=7.1.0)", "pytest (>=9.0.1,<10)", "pytest-asyncio (>=1.3.0)", "pytest-cov (>=4.0)", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling", "testpath", "xmltodict"] +test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.1.0)", "pytest (>=9.0.1,<10)", "pytest-asyncio (>=1.3.0)", "pytest-cov (>=4.0)", "testpath", "xmltodict"] + +[[package]] +name = "nbconvert" +version = "7.17.1" +description = "Convert Jupyter Notebooks (.ipynb files) to other formats." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "nbconvert-7.17.1-py3-none-any.whl", hash = "sha256:aa85c087b435e7bf1ffd03319f658e285f2b89eccab33bc1ba7025495ab3e7c8"}, + {file = "nbconvert-7.17.1.tar.gz", hash = "sha256:34d0d0a7e73ce3cbab6c5aae8f4f468797280b01fd8bd2ca746da8569eddd7d2"}, +] + +[package.dependencies] +beautifulsoup4 = "*" +bleach = {version = "!=5.0.0", extras = ["css"]} +defusedxml = "*" +jinja2 = ">=3.0" +jupyter-core = ">=4.7" +jupyterlab-pygments = "*" +markupsafe = ">=2.0" +mistune = ">=2.0.3,<4" +nbclient = ">=0.5.0" +nbformat = ">=5.7" +packaging = "*" +pandocfilters = ">=1.4.1" +pygments = ">=2.4.1" +traitlets = ">=5.1" + +[package.extras] +all = ["flaky", "intersphinx-registry", "ipykernel", "ipython", "ipywidgets (>=7.5)", "myst-parser", "nbsphinx (>=0.2.12)", "playwright", "pydata-sphinx-theme", "pyqtwebengine (>=5.15)", "pytest (>=7)", "sphinx (>=5.0.2)", "sphinxcontrib-spelling", "tornado (>=6.1)"] +docs = ["intersphinx-registry", "ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (>=5.0.2)", "sphinxcontrib-spelling"] +qtpdf = ["pyqtwebengine (>=5.15)"] +qtpng = ["pyqtwebengine (>=5.15)"] +serve = ["tornado (>=6.1)"] +test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest (>=7)"] +webpdf = ["playwright"] + +[[package]] +name = "nbformat" +version = "5.10.4" +description = "The Jupyter Notebook format" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"}, + {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"}, +] + +[package.dependencies] +fastjsonschema = ">=2.15" +jsonschema = ">=2.6" +jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +traitlets = ">=5.1" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] +test = ["pep440", "pre-commit", "pytest", "testpath"] + +[[package]] +name = "nest-asyncio2" +version = "1.7.2" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +groups = ["main"] +files = [ + {file = "nest_asyncio2-1.7.2-py3-none-any.whl", hash = "sha256:f5dfa702f3f81f6a03857e9a19e2ba578c0946a4ad417b4c50a24d7ba641fe01"}, + {file = "nest_asyncio2-1.7.2.tar.gz", hash = "sha256:1921d70b92cc4612c374928d081552efb59b83d91b2b789d935c665fa01729a8"}, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +files = [ + {file = "nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827"}, + {file = "nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb"}, +] + +[[package]] +name = "notebook-shim" +version = "0.2.4" +description = "A shim layer for notebook traits and config" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef"}, + {file = "notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb"}, +] + +[package.dependencies] +jupyter-server = ">=1.8,<3" + +[package.extras] +test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync"] + +[[package]] +name = "numpy" +version = "2.2.6" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version == \"3.10\"" +files = [ + {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, + {file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"}, + {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163"}, + {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf"}, + {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83"}, + {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915"}, + {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680"}, + {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289"}, + {file = "numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d"}, + {file = "numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3"}, + {file = "numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae"}, + {file = "numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a"}, + {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42"}, + {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491"}, + {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a"}, + {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf"}, + {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1"}, + {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab"}, + {file = "numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47"}, + {file = "numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303"}, + {file = "numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff"}, + {file = "numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c"}, + {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3"}, + {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282"}, + {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87"}, + {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249"}, + {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49"}, + {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de"}, + {file = "numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4"}, + {file = "numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2"}, + {file = "numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84"}, + {file = "numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b"}, + {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d"}, + {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566"}, + {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f"}, + {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f"}, + {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868"}, + {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d"}, + {file = "numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd"}, + {file = "numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c"}, + {file = "numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6"}, + {file = "numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda"}, + {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40"}, + {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8"}, + {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f"}, + {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa"}, + {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571"}, + {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1"}, + {file = "numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff"}, + {file = "numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06"}, + {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d"}, + {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db"}, + {file = "numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543"}, + {file = "numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00"}, + {file = "numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd"}, +] + +[[package]] +name = "numpy" +version = "2.4.6" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.11" +groups = ["main"] +markers = "python_version == \"3.11\"" +files = [ + {file = "numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4"}, + {file = "numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d"}, + {file = "numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8"}, + {file = "numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538"}, + {file = "numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47"}, + {file = "numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93"}, + {file = "numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8"}, + {file = "numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6"}, + {file = "numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8"}, + {file = "numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147"}, + {file = "numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577"}, + {file = "numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1"}, + {file = "numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb"}, + {file = "numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41"}, + {file = "numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698"}, + {file = "numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f"}, + {file = "numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853"}, + {file = "numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a"}, + {file = "numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2"}, + {file = "numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45"}, + {file = "numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751"}, + {file = "numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8"}, + {file = "numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0"}, + {file = "numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb"}, + {file = "numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f"}, + {file = "numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3"}, + {file = "numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b"}, + {file = "numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089"}, + {file = "numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a"}, + {file = "numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605"}, + {file = "numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91"}, + {file = "numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359"}, + {file = "numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778"}, + {file = "numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1"}, + {file = "numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe"}, + {file = "numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997"}, + {file = "numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20"}, + {file = "numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d"}, + {file = "numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67"}, + {file = "numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd"}, + {file = "numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab"}, + {file = "numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75"}, + {file = "numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd"}, + {file = "numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079"}, + {file = "numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7"}, + {file = "numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5"}, + {file = "numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096"}, + {file = "numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b"}, + {file = "numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8"}, + {file = "numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402"}, + {file = "numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb"}, + {file = "numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1"}, + {file = "numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261"}, + {file = "numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6"}, + {file = "numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a"}, + {file = "numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e"}, + {file = "numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e"}, + {file = "numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43"}, + {file = "numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e"}, + {file = "numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895"}, + {file = "numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4"}, + {file = "numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063"}, + {file = "numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627"}, + {file = "numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02"}, + {file = "numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73"}, + {file = "numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda"}, +] + +[[package]] +name = "numpy" +version = "2.5.1" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.12" +groups = ["main"] +markers = "python_version == \"3.12\"" +files = [ + {file = "numpy-2.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2c889b56fe48b1018f764b0eec8df59ab654e9148aa91faa12596043500de277"}, + {file = "numpy-2.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab451b59c5643c570974c43aef780703ef1d3b4965d2be07afd530615a9358d1"}, + {file = "numpy-2.5.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:78798bd5b9ad744056af8efa90e3b9ddaa53272a0848a483084a1cc0a13b2dc0"}, + {file = "numpy-2.5.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:2ae0ca40bcb22d6ba59c1dfd5446f49940b0f2d821fde133f10dda11f816b84e"}, + {file = "numpy-2.5.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61ac47e772e6b8ea489e1d2f441a34c5c3ac17327e7ce294cbdf535795ad4e75"}, + {file = "numpy-2.5.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:59fda5e192b570217ec2580c96f00e9a7e12ef6866a900eb089b62c1a32545ca"}, + {file = "numpy-2.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f7119ebff1a9829e9f431a4f9d28e703023bb6b9fe7c8f724467dbfc27c94ab3"}, + {file = "numpy-2.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e824c2acf8862052246be5a44c15da1777940c60d010dd2aab897824d9c430f9"}, + {file = "numpy-2.5.1-cp312-cp312-win32.whl", hash = "sha256:08d60c810432eb83360958dea0999ac4cfb94531ea8efcbf0b7f277c2068aeb2"}, + {file = "numpy-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:f7d60026c0bdb1380e83bfa7a0419c4577ee4b9a08880afcb6dadeb74c649fa2"}, + {file = "numpy-2.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:17a25e09640602e10bc8de0e6fa2b3fd68eedd84ba6d7842dc8f32f9ab87bd0b"}, + {file = "numpy-2.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0bfebd8695f9863592fe744be833a258120b14a9f39da255e8aa8fade2c0ddd1"}, + {file = "numpy-2.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:30b44a6b53a7ae63c54c089a8726e5563ed302716c5b7ccc85afade40b0e7ff6"}, + {file = "numpy-2.5.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6165343f81b56ef8f514f396989e529b61d9dc709b99421b07e9f3e698e2287d"}, + {file = "numpy-2.5.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4939237038ada79308dda3204ac6462df056b5672b2e25db1149cf873668b3e1"}, + {file = "numpy-2.5.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c6759f538fb912fc46de0a6b1758ccf7b57bc7c7ebebc23974fdac3de8db0cd"}, + {file = "numpy-2.5.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9726558e8db4a5bf7929a70ae50f63abda4daf0efe810e3bfbab95976f75fc1a"}, + {file = "numpy-2.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3935f3b419b244a02732676fa5317a9193cc596a4c0646db07e5b421229ac9f7"}, + {file = "numpy-2.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc932a65ded7ce9013d120845a2514dcccb1a67bfc8deb8d37633762951904a6"}, + {file = "numpy-2.5.1-cp313-cp313-win32.whl", hash = "sha256:4b4ff1608417eb7a59da7b967bbb798cacfe071d2caf526a24281cd562072ed9"}, + {file = "numpy-2.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:6c3fe51bc6a16453d452997053454f309e8e0ed7b42d6b361ce4ac8c32913d74"}, + {file = "numpy-2.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f7feb014281029e628ba2d5a007407443b06e418b6fe451d1e2adcbc8eba0107"}, + {file = "numpy-2.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7c786fe9a5bbe360022e584c5a34cf6b54265c71bd7ec8ac3d8fec38968071f8"}, + {file = "numpy-2.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32985c896d897419ef8da6917872d80b78ad0ea26d85b23245c7366ffde76d75"}, + {file = "numpy-2.5.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:efd736408cc97c79b9e6917338dfc8f06013b2274f992e96b1d9a81a71e2a2c2"}, + {file = "numpy-2.5.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:ab84dc6b074fa881cae55bea94cc4f68e285181ba7f32497bf7dee6b1496165b"}, + {file = "numpy-2.5.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caf3e317d33d60c37986b452613f4ab51246d0691350c03d0cb4a898627f4a95"}, + {file = "numpy-2.5.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54ad769f17bc2d833b620851989f62054fb9ab93c969d9e1dc3c8e3d56beea21"}, + {file = "numpy-2.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c12afb53450fa976d4c681c50a7423729a4c51c0465ed9f32b8a9cabbc472373"}, + {file = "numpy-2.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e8c11c405efc5ff6816d5983c96cdfa215bab3428961243af3ff59b228490438"}, + {file = "numpy-2.5.1-cp314-cp314-win32.whl", hash = "sha256:f2479a47f8d5932d1718168a681ad6e536a9df484c83cfcf9de365e164537ace"}, + {file = "numpy-2.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:24d0eb82c0541d3415a33425db64ae439dffccd7b4dbcb30e7c35120205c506a"}, + {file = "numpy-2.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:5a4c988b38d261deeeaad9954e3deb091ad905c94e8bb6708654ef1d97f286b0"}, + {file = "numpy-2.5.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a33276be12fa045805f477f22482088b66bb758ffbe89a9d21457de863a32e22"}, + {file = "numpy-2.5.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:f089d7b00756190aacf1f5d34bdf38c3c430ac82b4f868f8cede73380460fce7"}, + {file = "numpy-2.5.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:09e9bfd8d2cf479c7d174804fb3811c53a8e9f20a37444008606b57d6b7a826d"}, + {file = "numpy-2.5.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e68d8dd1e7eba712948f2053a29ec86917bc70ba1358df869d9f06649ef9cf09"}, + {file = "numpy-2.5.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99d5095fa265a0c4152e7bb12759e14381ef5496152f1ce58f44bdf55c44beb4"}, + {file = "numpy-2.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ab87a91b3cc3382b8956095bd8f95e00cf679bb81554339be1a2ba404a1473c1"}, + {file = "numpy-2.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:224ca51130ef7da85bea2191625181cb4f337f9cb64b471f10c1a12aa8b60077"}, + {file = "numpy-2.5.1-cp314-cp314t-win32.whl", hash = "sha256:6eab239876581b2b3c5a242281b6007bbdbcd1c7085d7709bb57c5929b11e6bf"}, + {file = "numpy-2.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:83ce9c80d5b521b0d77ddcbe5447c218d247929b6cc056ca5351342accfff0af"}, + {file = "numpy-2.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:5a6db61f9aaa57e369905c67d852045d3c4f7126405b29d09b19dec118e9c9cb"}, + {file = "numpy-2.5.1.tar.gz", hash = "sha256:a48a113e6afea91f5608793bafa7ef2ad481fefbda87ec5069f483de61cb9fa3"}, +] + +[[package]] +name = "openpyxl" +version = "3.1.5" +description = "A Python library to read/write Excel 2010 xlsx/xlsm files" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "openpyxl-3.1.5-py2.py3-none-any.whl", hash = "sha256:5282c12b107bffeef825f4617dc029afaf41d0ea60823bbb665ef3079dc79de2"}, + {file = "openpyxl-3.1.5.tar.gz", hash = "sha256:cf0e3cf56142039133628b5acffe8ef0c12bc902d2aadd3e0fe5878dc08d1050"}, +] + +[package.dependencies] +et-xmlfile = "*" + +[[package]] +name = "overrides" +version = "7.7.0" +description = "A decorator to automatically detect mismatch when overriding a method." +optional = false +python-versions = ">=3.6" +groups = ["main"] +markers = "python_version < \"3.12\"" +files = [ + {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"}, + {file = "overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a"}, +] + +[[package]] +name = "packaging" +version = "26.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, +] + +[[package]] +name = "pandas" +version = "2.3.3" +description = "Powerful data structures for data analysis, time series, and statistics" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c"}, + {file = "pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a"}, + {file = "pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1"}, + {file = "pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838"}, + {file = "pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250"}, + {file = "pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4"}, + {file = "pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826"}, + {file = "pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523"}, + {file = "pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45"}, + {file = "pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66"}, + {file = "pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b"}, + {file = "pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791"}, + {file = "pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151"}, + {file = "pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c"}, + {file = "pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53"}, + {file = "pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35"}, + {file = "pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908"}, + {file = "pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89"}, + {file = "pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98"}, + {file = "pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084"}, + {file = "pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b"}, + {file = "pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713"}, + {file = "pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8"}, + {file = "pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d"}, + {file = "pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac"}, + {file = "pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c"}, + {file = "pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493"}, + {file = "pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee"}, + {file = "pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5"}, + {file = "pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21"}, + {file = "pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78"}, + {file = "pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110"}, + {file = "pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86"}, + {file = "pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc"}, + {file = "pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0"}, + {file = "pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593"}, + {file = "pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c"}, + {file = "pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b"}, + {file = "pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6"}, + {file = "pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3"}, + {file = "pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5"}, + {file = "pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec"}, + {file = "pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7"}, + {file = "pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450"}, + {file = "pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5"}, + {file = "pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788"}, + {file = "pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87"}, + {file = "pandas-2.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c503ba5216814e295f40711470446bc3fd00f0faea8a086cbc688808e26f92a2"}, + {file = "pandas-2.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a637c5cdfa04b6d6e2ecedcb81fc52ffb0fd78ce2ebccc9ea964df9f658de8c8"}, + {file = "pandas-2.3.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:854d00d556406bffe66a4c0802f334c9ad5a96b4f1f868adf036a21b11ef13ff"}, + {file = "pandas-2.3.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf1f8a81d04ca90e32a0aceb819d34dbd378a98bf923b6398b9a3ec0bf44de29"}, + {file = "pandas-2.3.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23ebd657a4d38268c7dfbdf089fbc31ea709d82e4923c5ffd4fbd5747133ce73"}, + {file = "pandas-2.3.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5554c929ccc317d41a5e3d1234f3be588248e61f08a74dd17c9eabb535777dc9"}, + {file = "pandas-2.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:d3e28b3e83862ccf4d85ff19cf8c20b2ae7e503881711ff2d534dc8f761131aa"}, + {file = "pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b"}, +] + +[package.dependencies] +numpy = [ + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, +] +python-dateutil = ">=2.8.2" +pytz = ">=2020.1" +tzdata = ">=2022.7" + +[package.extras] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] + +[[package]] +name = "pandocfilters" +version = "1.5.1" +description = "Utilities for writing pandoc filters in python" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +files = [ + {file = "pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"}, + {file = "pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e"}, +] + +[[package]] +name = "parso" +version = "0.8.7" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "parso-0.8.7-py2.py3-none-any.whl", hash = "sha256:a8926eb2a1b915486941fdbd31e86a4baf88fe8c210f25f2f35ecec5b574ca1c"}, + {file = "parso-0.8.7.tar.gz", hash = "sha256:eaaac4c9fdd5e9e8852dc778d2d7405897ec510f2a298071453e5e3a07914bb1"}, +] + +[package.extras] +qa = ["flake8 (==5.0.4)", "types-setuptools (==67.2.0.1)", "zuban (==0.5.1)"] +testing = ["docopt", "pytest"] + +[[package]] +name = "pathspec" +version = "1.1.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"}, + {file = "pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"}, +] + +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] + +[[package]] +name = "pexpect" +version = "4.9.0" +description = "Pexpect allows easy control of interactive console applications." +optional = false +python-versions = "*" +groups = ["main"] +markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\"" +files = [ + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, +] + +[package.dependencies] +ptyprocess = ">=0.5" + +[[package]] +name = "platformdirs" +version = "4.11.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.10" +groups = ["main", "dev"] +files = [ + {file = "platformdirs-4.11.0-py3-none-any.whl", hash = "sha256:360ccded2b7fce0af0ff80cc8f5942a1c5d99b0e856033acb030bfc634709e74"}, + {file = "platformdirs-4.11.0.tar.gz", hash = "sha256:0555d18370482847566ffabcaa53ad7c6c1c29f195989ae1ed634a05f76ea1e0"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "pre-commit" +version = "4.6.1" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "pre_commit-4.6.1-py2.py3-none-any.whl", hash = "sha256:0e3b2942510d1fb34eec167a3ec57331bf8442122f1153a9fb8b58f5c49b2717"}, + {file = "pre_commit-4.6.1.tar.gz", hash = "sha256:03e809865c7d178b9979d06c761fcbfe6808fdaded8581a745bb110e52050421"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + +[[package]] +name = "probableparsing" +version = "0.0.1" +description = "Common methods for propbable parsers" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "probableparsing-0.0.1-py2.py3-none-any.whl", hash = "sha256:509df25fdda4fd7c0b2a100f58cc971bd23daf26f3b3320aebf2616d2e10c69e"}, + {file = "probableparsing-0.0.1.tar.gz", hash = "sha256:8114bbf889e1f9456fe35946454c96e42a6ee2673a90d4f1f9c46a406f543767"}, +] + +[[package]] +name = "prometheus-client" +version = "0.25.0" +description = "Python client for the Prometheus monitoring system." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "prometheus_client-0.25.0-py3-none-any.whl", hash = "sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1"}, + {file = "prometheus_client-0.25.0.tar.gz", hash = "sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28"}, +] + +[package.extras] +aiohttp = ["aiohttp"] +django = ["django"] +twisted = ["twisted"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.52" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955"}, + {file = "prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "psutil" +version = "7.2.2" +description = "Cross-platform lib for process and system monitoring." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b"}, + {file = "psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea"}, + {file = "psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63"}, + {file = "psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312"}, + {file = "psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b"}, + {file = "psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9"}, + {file = "psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00"}, + {file = "psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9"}, + {file = "psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a"}, + {file = "psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf"}, + {file = "psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1"}, + {file = "psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841"}, + {file = "psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486"}, + {file = "psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979"}, + {file = "psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9"}, + {file = "psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e"}, + {file = "psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8"}, + {file = "psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc"}, + {file = "psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988"}, + {file = "psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee"}, + {file = "psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372"}, +] + +[package.extras] +dev = ["abi3audit", "black", "check-manifest", "colorama ; os_name == \"nt\"", "coverage", "packaging", "psleak", "pylint", "pyperf", "pypinfo", "pyreadline3 ; os_name == \"nt\"", "pytest", "pytest-cov", "pytest-instafail", "pytest-xdist", "pywin32 ; os_name == \"nt\" and implementation_name != \"pypy\"", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "validate-pyproject[all]", "virtualenv", "vulture", "wheel", "wheel ; os_name == \"nt\" and implementation_name != \"pypy\"", "wmi ; os_name == \"nt\" and implementation_name != \"pypy\""] +test = ["psleak", "pytest", "pytest-instafail", "pytest-xdist", "pywin32 ; os_name == \"nt\" and implementation_name != \"pypy\"", "setuptools", "wheel ; os_name == \"nt\" and implementation_name != \"pypy\"", "wmi ; os_name == \"nt\" and implementation_name != \"pypy\""] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +description = "Run a subprocess in a pseudo terminal" +optional = false +python-versions = "*" +groups = ["main"] +markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\" or os_name != \"nt\"" +files = [ + {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, + {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +description = "Safely evaluate AST nodes without side effects" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"}, + {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"}, +] + +[package.extras] +tests = ["pytest"] + +[[package]] +name = "py-seed" +version = "0.5.2" +description = "A Python API client for the SEED Platform" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "py_seed-0.5.2-py2.py3-none-any.whl", hash = "sha256:b3e2230d563b2b7b1a02d43b239cb01c4f65aa8fdbde2b4b32b061b63a1cebe9"}, + {file = "py_seed-0.5.2.tar.gz", hash = "sha256:edf601c7161d91d1bbaed839e9b8905de67be1daaead5d1dad92cdff6b7be231"}, +] + +[package.dependencies] +requests = ">=2.28.0" + +[[package]] +name = "pycodestyle" +version = "2.14.0" +description = "Python style guide checker" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d"}, + {file = "pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783"}, +] + +[[package]] +name = "pycparser" +version = "3.0" +description = "C parser in Python" +optional = false +python-versions = ">=3.10" +groups = ["main"] +markers = "implementation_name != \"PyPy\"" +files = [ + {file = "pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992"}, + {file = "pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29"}, +] + +[[package]] +name = "pyflakes" +version = "3.4.0" +description = "passive checker of Python programs" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f"}, + {file = "pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58"}, +] + +[[package]] +name = "pygments" +version = "2.20.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "9.1.1" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.10" +groups = ["dev"] +files = [ + {file = "pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c"}, + {file = "pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} +iniconfig = ">=1.0.1" +packaging = ">=22" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "7.1.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678"}, + {file = "pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2"}, +] + +[package.dependencies] +coverage = {version = ">=7.10.6", extras = ["toml"]} +pluggy = ">=1.2" +pytest = ">=7" + +[package.extras] +testing = ["process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "python-crfsuite" +version = "0.9.12" +description = "Python binding for CRFsuite" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "python_crfsuite-0.9.12-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7be83f5a68ae5a5835e92f0b134e2aded55d1ca36bed434259f36a32aa545b1a"}, + {file = "python_crfsuite-0.9.12-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6aaa14dceb9512cb70e37f88f6b6c7e630a251c7fa2a1eb94782419f935d4a8a"}, + {file = "python_crfsuite-0.9.12-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a027fb19b7e065b0b08ac3638ebc1f0a2a55e5db1f1bb85f97923eb6ac29710"}, + {file = "python_crfsuite-0.9.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c44a0c7f2b975e128a5ff2d41c1c2030de1957a2735a6a6f6657b8f25953e9d1"}, + {file = "python_crfsuite-0.9.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dc0388182a0c7fbc402d503a49435288a4cb6ab258c2bc9fa02b3c0e1643393f"}, + {file = "python_crfsuite-0.9.12-cp310-cp310-win32.whl", hash = "sha256:0d32e41c407208e539fb33f2611e73455529f2ba1112a34eff161dafaaafe48b"}, + {file = "python_crfsuite-0.9.12-cp310-cp310-win_amd64.whl", hash = "sha256:2a49237c319ed7c0979d91659fcd3ec7629be36981e1263f2c21a23a2c8dffd1"}, + {file = "python_crfsuite-0.9.12-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9b7cd1f10ae5b6f13dac6e0a20456c28c3450bc86f5cf4b41a11250b2b4fc269"}, + {file = "python_crfsuite-0.9.12-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41cc55053799d6eac13d496f1bd9c28a73d39c99eb194f37e356bd557bcca3fe"}, + {file = "python_crfsuite-0.9.12-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5cd2b153bed935e4d6bd37d35f8cba9fcb73c01b539bbb63153589a10ac9fc6f"}, + {file = "python_crfsuite-0.9.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bb114a6c22c0df7c6a78921d1f0ef913116fc5486adca9a96e76f47ce2755311"}, + {file = "python_crfsuite-0.9.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:aec187fb28550b5a1611152011bf9d36797b26b5fca88278d9dbd40d9ac941c1"}, + {file = "python_crfsuite-0.9.12-cp311-cp311-win32.whl", hash = "sha256:0bc929cbfe88b775361feba36f9632170d411a3a3a1e31d53e3a07452bb54588"}, + {file = "python_crfsuite-0.9.12-cp311-cp311-win_amd64.whl", hash = "sha256:3b646fe2ea2c172c1823272107039ac4d00aeea81acaf0253f5c16beb64926b0"}, + {file = "python_crfsuite-0.9.12-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e68009911b28ff899da5a6be3ec1efc3c24886c92318d02d39ec29d329b08b90"}, + {file = "python_crfsuite-0.9.12-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7118a3b267c437a9701362f5eacd6d1ff2360305a9c872cc20a716cd005c13eb"}, + {file = "python_crfsuite-0.9.12-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:891bf2a5f410f17c5f9d76ab7330178a10142d48ed12f5c15b84f4c23fee80c7"}, + {file = "python_crfsuite-0.9.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:812f963fb61cfa5bfbc91b92e058cee41808a9ce813c84ecab6691848cc3b51c"}, + {file = "python_crfsuite-0.9.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a696ef90c77344ba88e5d241ace35fd21ad31e43f878fc734668741db18ed186"}, + {file = "python_crfsuite-0.9.12-cp312-cp312-win32.whl", hash = "sha256:e32c826e43fe8ac5c3b436bbddd8483f735a5638ea5dc07778d505cde78dc875"}, + {file = "python_crfsuite-0.9.12-cp312-cp312-win_amd64.whl", hash = "sha256:fa6258bf10d8185262dee8fe2ca8d3de3c7aecb990846329043fc895344cc939"}, + {file = "python_crfsuite-0.9.12-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2048d8768a0b4c6a6d9390e879a2b7a760bb57a7f2ba491316f5dc36f9cfd836"}, + {file = "python_crfsuite-0.9.12-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5294008716b65606c4d416c3b2597ca14422359a4a84734ead239b29b95f2780"}, + {file = "python_crfsuite-0.9.12-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f1641b9263c3cd1190711d0383d871b002ad325aa800fcf3c8583ef36f0bb07"}, + {file = "python_crfsuite-0.9.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f0e73d0a8859db0c3d1a7a3595a83810efc535d95cb79f2f675eb44ad7a7954a"}, + {file = "python_crfsuite-0.9.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:96f27f343ff7e7cb1e29a785d8ed4626a3470f8d42c41cda734dcbaede566722"}, + {file = "python_crfsuite-0.9.12-cp313-cp313-win32.whl", hash = "sha256:385fda7f407be778f6a9440dffdeed3cdafc6f6923065a856e45997626283589"}, + {file = "python_crfsuite-0.9.12-cp313-cp313-win_amd64.whl", hash = "sha256:21334c298318d4de057eacaad2ed179b7f63640e9cbd0c8141d656f58e7bd3f1"}, + {file = "python_crfsuite-0.9.12-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2e18bb1d7b4913bc321a5768284c8e86b5eefcd583462bfed5875671223451a8"}, + {file = "python_crfsuite-0.9.12-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:30028c9b6cd06cafb43861f2577d4ef5c57f90a59908efb3df38be9e6e7c1c98"}, + {file = "python_crfsuite-0.9.12-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2fe0e6760365d7288e63661c4ab3c1110ae0cb1c36fbbbed23e5e889c138eb1"}, + {file = "python_crfsuite-0.9.12-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d8d0e416ae999f9ff8a183383d9b917d4818f677dd3e370d19b6d1f9786af4bf"}, + {file = "python_crfsuite-0.9.12-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1b7204fdadf596a968d115b94c419899f3299fbd9c753abfced933b831e1ace3"}, + {file = "python_crfsuite-0.9.12-cp314-cp314-win32.whl", hash = "sha256:be282686a90134851aa636d38ea520ab73aabb8103e79de458fffd49ff016bd2"}, + {file = "python_crfsuite-0.9.12-cp314-cp314-win_amd64.whl", hash = "sha256:94ab3f1666ec4244d8190b7e624505bc6e845d54b4faa0dacd9ea8fd1ac7eef8"}, + {file = "python_crfsuite-0.9.12-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1fac24a04ebe58fcfd8e6a3c48e1e03021427852b7495573d7fc41d0d9ee297d"}, + {file = "python_crfsuite-0.9.12-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:532cfbeffe8c8b0a0bf360a31f6486e655b29703a02ecefaf86d519f12fd470b"}, + {file = "python_crfsuite-0.9.12-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fa2a20a74a094bb80b76af7937f68b710d60539a2905d942ba655d90d5c90677"}, + {file = "python_crfsuite-0.9.12-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ff3b8e8c524b952e0dd85aa3bc34f24b502507411e776739d9e4ad4b46e61f51"}, + {file = "python_crfsuite-0.9.12-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b15bd6bbc4bf893e84084e9be010f350e8dfcc716b40bd6e5243d34cbc7dfb61"}, + {file = "python_crfsuite-0.9.12-cp314-cp314t-win32.whl", hash = "sha256:e6f8d0b71329b015a6d167305c2c00097e39a947644f0cbdc7fb4f9ffc1dc28e"}, + {file = "python_crfsuite-0.9.12-cp314-cp314t-win_amd64.whl", hash = "sha256:9a74ea7c043e0b12a68175502b948bb58153dafd3e90f69d63de3c4a37ce4f4b"}, + {file = "python_crfsuite-0.9.12.tar.gz", hash = "sha256:db37fccc3bd8f0c49c28a7697ca79c89d67b3fd5bf119122866169240ac4c480"}, +] + +[package.extras] +dev = ["black", "flake8", "isort", "tox"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-discovery" +version = "1.5.0" +description = "Python interpreter discovery" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "python_discovery-1.5.0-py3-none-any.whl", hash = "sha256:70c4fc61b4e7404e44f01d6fc44a715c4d685ca6cea83d295922f05891877c98"}, + {file = "python_discovery-1.5.0.tar.gz", hash = "sha256:3e014c6327154d3dda27939a9a0dc9c5c000439f1906d3f303b48f984bd2ecef"}, +] + +[package.dependencies] +filelock = ">=3.15.4" +platformdirs = ">=4.3.6,<5" + +[[package]] +name = "python-json-logger" +version = "4.1.0" +description = "JSON Log Formatter for the Python Logging Package" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "python_json_logger-4.1.0-py3-none-any.whl", hash = "sha256:132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2"}, + {file = "python_json_logger-4.1.0.tar.gz", hash = "sha256:b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195"}, +] + +[package.extras] +dev = ["black", "build", "freezegun", "mdx_truly_sane_lists", "mike", "mkdocs", "mkdocs-awesome-pages-plugin", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-material (>=8.5)", "mkdocstrings[python]", "msgspec ; implementation_name != \"pypy\"", "mypy", "orjson ; implementation_name != \"pypy\"", "pylint", "pytest", "tzdata", "validate-pyproject[all]"] + +[[package]] +name = "pytz" +version = "2026.2" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126"}, + {file = "pytz-2026.2.tar.gz", hash = "sha256:0e60b47b29f21574376f218fe21abc009894a2321ea16c6754f3cad6eb7cdd6a"}, +] + +[[package]] +name = "pywinpty" +version = "3.0.5" +description = "Pseudo terminal support for Windows from Python." +optional = false +python-versions = ">=3.10" +groups = ["main"] +markers = "os_name == \"nt\"" +files = [ + {file = "pywinpty-3.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:b467dcad72365bc2205ed8b6e694e817d71de269d46e56cfe267dfa9d3d30e1b"}, + {file = "pywinpty-3.0.5-cp310-cp310-win_arm64.whl", hash = "sha256:7dc4046ea8e4d7f0a16dae8dfcaeeda6df7ca3a9330444d2ba5bb96138fe0a91"}, + {file = "pywinpty-3.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:af7a8720c78776ddd6259b71dd567944f766a6cd67f8d2887fbc4973967bacda"}, + {file = "pywinpty-3.0.5-cp311-cp311-win_arm64.whl", hash = "sha256:c2406f54f699eab75953fb75ce805f2ae55a33a957cd070890abd454fb4b7680"}, + {file = "pywinpty-3.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:d62946adf14b15b54c0b8d785f93fe18b04da23f4ad59e2e8c4612646e9abd23"}, + {file = "pywinpty-3.0.5-cp312-cp312-win_arm64.whl", hash = "sha256:e9391c05fbfa7a992a97e831fc6849887b4014a614192e3d984a7ca59592b376"}, + {file = "pywinpty-3.0.5-cp313-cp313-win_amd64.whl", hash = "sha256:48db1b0ad9d0a1b81dcaaa7163a99a7808deaceb0c1b2344716dc1fc090c3c4c"}, + {file = "pywinpty-3.0.5-cp313-cp313-win_arm64.whl", hash = "sha256:2c6008fb2d3774b48693b2fcb7f2cc317ade9dc581289a964ffeeaf81307c9b5"}, + {file = "pywinpty-3.0.5-cp313-cp313t-win_amd64.whl", hash = "sha256:22ce1b780d89821cc52daf6eac0708af22d93d000ce9c7c07e37489db8594598"}, + {file = "pywinpty-3.0.5-cp313-cp313t-win_arm64.whl", hash = "sha256:9c2919a81bc5cfb09b86fc5a002112b2de95ca4304a07413cbeeb746a1307a5c"}, + {file = "pywinpty-3.0.5-cp314-cp314-win_amd64.whl", hash = "sha256:03bb3c16d691d9242267201830bcd0e64a9b663170e9042bc84b210da9de15ac"}, + {file = "pywinpty-3.0.5-cp314-cp314-win_arm64.whl", hash = "sha256:89c5c6ef08997a3b4b277b214a35fe15cab4dd6d119f0140aa71df5b1168fdbc"}, + {file = "pywinpty-3.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:7b566165e0c5fdd6abe167a5ac8b954be6a843eb55a85946576d6bc1dea03d6d"}, + {file = "pywinpty-3.0.5-cp314-cp314t-win_arm64.whl", hash = "sha256:24366280a8aa677323da87bec729cb3ea3b35367386cece0978bdc6e4695c690"}, + {file = "pywinpty-3.0.5.tar.gz", hash = "sha256:61db0db063de9865adbea66db294628f8577f608d9764a4c7d3384eeacc4e81b"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + +[[package]] +name = "pyzmq" +version = "27.1.0" +description = "Python bindings for 0MQ" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pyzmq-27.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:508e23ec9bc44c0005c4946ea013d9317ae00ac67778bd47519fdf5a0e930ff4"}, + {file = "pyzmq-27.1.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:507b6f430bdcf0ee48c0d30e734ea89ce5567fd7b8a0f0044a369c176aa44556"}, + {file = "pyzmq-27.1.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf7b38f9fd7b81cb6d9391b2946382c8237fd814075c6aa9c3b746d53076023b"}, + {file = "pyzmq-27.1.0-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:03ff0b279b40d687691a6217c12242ee71f0fba28bf8626ff50e3ef0f4410e1e"}, + {file = "pyzmq-27.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:677e744fee605753eac48198b15a2124016c009a11056f93807000ab11ce6526"}, + {file = "pyzmq-27.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd2fec2b13137416a1c5648b7009499bcc8fea78154cd888855fa32514f3dad1"}, + {file = "pyzmq-27.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:08e90bb4b57603b84eab1d0ca05b3bbb10f60c1839dc471fc1c9e1507bef3386"}, + {file = "pyzmq-27.1.0-cp310-cp310-win32.whl", hash = "sha256:a5b42d7a0658b515319148875fcb782bbf118dd41c671b62dae33666c2213bda"}, + {file = "pyzmq-27.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c0bb87227430ee3aefcc0ade2088100e528d5d3298a0a715a64f3d04c60ba02f"}, + {file = "pyzmq-27.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:9a916f76c2ab8d045b19f2286851a38e9ac94ea91faf65bd64735924522a8b32"}, + {file = "pyzmq-27.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:226b091818d461a3bef763805e75685e478ac17e9008f49fce2d3e52b3d58b86"}, + {file = "pyzmq-27.1.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:0790a0161c281ca9723f804871b4027f2e8b5a528d357c8952d08cd1a9c15581"}, + {file = "pyzmq-27.1.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c895a6f35476b0c3a54e3eb6ccf41bf3018de937016e6e18748317f25d4e925f"}, + {file = "pyzmq-27.1.0-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bbf8d3630bf96550b3be8e1fc0fea5cbdc8d5466c1192887bd94869da17a63e"}, + {file = "pyzmq-27.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15c8bd0fe0dabf808e2d7a681398c4e5ded70a551ab47482067a572c054c8e2e"}, + {file = "pyzmq-27.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bafcb3dd171b4ae9f19ee6380dfc71ce0390fefaf26b504c0e5f628d7c8c54f2"}, + {file = "pyzmq-27.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e829529fcaa09937189178115c49c504e69289abd39967cd8a4c215761373394"}, + {file = "pyzmq-27.1.0-cp311-cp311-win32.whl", hash = "sha256:6df079c47d5902af6db298ec92151db82ecb557af663098b92f2508c398bb54f"}, + {file = "pyzmq-27.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:190cbf120fbc0fc4957b56866830def56628934a9d112aec0e2507aa6a032b97"}, + {file = "pyzmq-27.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:eca6b47df11a132d1745eb3b5b5e557a7dae2c303277aa0e69c6ba91b8736e07"}, + {file = "pyzmq-27.1.0-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:452631b640340c928fa343801b0d07eb0c3789a5ffa843f6e1a9cee0ba4eb4fc"}, + {file = "pyzmq-27.1.0-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1c179799b118e554b66da67d88ed66cd37a169f1f23b5d9f0a231b4e8d44a113"}, + {file = "pyzmq-27.1.0-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3837439b7f99e60312f0c926a6ad437b067356dc2bc2ec96eb395fd0fe804233"}, + {file = "pyzmq-27.1.0-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43ad9a73e3da1fab5b0e7e13402f0b2fb934ae1c876c51d0afff0e7c052eca31"}, + {file = "pyzmq-27.1.0-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0de3028d69d4cdc475bfe47a6128eb38d8bc0e8f4d69646adfbcd840facbac28"}, + {file = "pyzmq-27.1.0-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf44a7763aea9298c0aa7dbf859f87ed7012de8bda0f3977b6fb1d96745df856"}, + {file = "pyzmq-27.1.0-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f30f395a9e6fbca195400ce833c731e7b64c3919aa481af4d88c3759e0cb7496"}, + {file = "pyzmq-27.1.0-cp312-abi3-win32.whl", hash = "sha256:250e5436a4ba13885494412b3da5d518cd0d3a278a1ae640e113c073a5f88edd"}, + {file = "pyzmq-27.1.0-cp312-abi3-win_amd64.whl", hash = "sha256:9ce490cf1d2ca2ad84733aa1d69ce6855372cb5ce9223802450c9b2a7cba0ccf"}, + {file = "pyzmq-27.1.0-cp312-abi3-win_arm64.whl", hash = "sha256:75a2f36223f0d535a0c919e23615fc85a1e23b71f40c7eb43d7b1dedb4d8f15f"}, + {file = "pyzmq-27.1.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:93ad4b0855a664229559e45c8d23797ceac03183c7b6f5b4428152a6b06684a5"}, + {file = "pyzmq-27.1.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:fbb4f2400bfda24f12f009cba62ad5734148569ff4949b1b6ec3b519444342e6"}, + {file = "pyzmq-27.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:e343d067f7b151cfe4eb3bb796a7752c9d369eed007b91231e817071d2c2fec7"}, + {file = "pyzmq-27.1.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:08363b2011dec81c354d694bdecaef4770e0ae96b9afea70b3f47b973655cc05"}, + {file = "pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d54530c8c8b5b8ddb3318f481297441af102517602b569146185fa10b63f4fa9"}, + {file = "pyzmq-27.1.0-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f3afa12c392f0a44a2414056d730eebc33ec0926aae92b5ad5cf26ebb6cc128"}, + {file = "pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c65047adafe573ff023b3187bb93faa583151627bc9c51fc4fb2c561ed689d39"}, + {file = "pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:90e6e9441c946a8b0a667356f7078d96411391a3b8f80980315455574177ec97"}, + {file = "pyzmq-27.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:add071b2d25f84e8189aaf0882d39a285b42fa3853016ebab234a5e78c7a43db"}, + {file = "pyzmq-27.1.0-cp313-cp313t-win32.whl", hash = "sha256:7ccc0700cfdf7bd487bea8d850ec38f204478681ea02a582a8da8171b7f90a1c"}, + {file = "pyzmq-27.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8085a9fba668216b9b4323be338ee5437a235fe275b9d1610e422ccc279733e2"}, + {file = "pyzmq-27.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:6bb54ca21bcfe361e445256c15eedf083f153811c37be87e0514934d6913061e"}, + {file = "pyzmq-27.1.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ce980af330231615756acd5154f29813d553ea555485ae712c491cd483df6b7a"}, + {file = "pyzmq-27.1.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:1779be8c549e54a1c38f805e56d2a2e5c009d26de10921d7d51cfd1c8d4632ea"}, + {file = "pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7200bb0f03345515df50d99d3db206a0a6bee1955fbb8c453c76f5bf0e08fb96"}, + {file = "pyzmq-27.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01c0e07d558b06a60773744ea6251f769cd79a41a97d11b8bf4ab8f034b0424d"}, + {file = "pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:80d834abee71f65253c91540445d37c4c561e293ba6e741b992f20a105d69146"}, + {file = "pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:544b4e3b7198dde4a62b8ff6685e9802a9a1ebf47e77478a5eb88eca2a82f2fd"}, + {file = "pyzmq-27.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cedc4c68178e59a4046f97eca31b148ddcf51e88677de1ef4e78cf06c5376c9a"}, + {file = "pyzmq-27.1.0-cp314-cp314t-win32.whl", hash = "sha256:1f0b2a577fd770aa6f053211a55d1c47901f4d537389a034c690291485e5fe92"}, + {file = "pyzmq-27.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:19c9468ae0437f8074af379e986c5d3d7d7bfe033506af442e8c879732bedbe0"}, + {file = "pyzmq-27.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dc5dbf68a7857b59473f7df42650c621d7e8923fb03fa74a526890f4d33cc4d7"}, + {file = "pyzmq-27.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:18339186c0ed0ce5835f2656cdfb32203125917711af64da64dbaa3d949e5a1b"}, + {file = "pyzmq-27.1.0-cp38-cp38-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:753d56fba8f70962cd8295fb3edb40b9b16deaa882dd2b5a3a2039f9ff7625aa"}, + {file = "pyzmq-27.1.0-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b721c05d932e5ad9ff9344f708c96b9e1a485418c6618d765fca95d4daacfbef"}, + {file = "pyzmq-27.1.0-cp38-cp38-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be883ff3d722e6085ee3f4afc057a50f7f2e0c72d289fd54df5706b4e3d3a50"}, + {file = "pyzmq-27.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:b2e592db3a93128daf567de9650a2f3859017b3f7a66bc4ed6e4779d6034976f"}, + {file = "pyzmq-27.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ad68808a61cbfbbae7ba26d6233f2a4aa3b221de379ce9ee468aa7a83b9c36b0"}, + {file = "pyzmq-27.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:e2687c2d230e8d8584fbea433c24382edfeda0c60627aca3446aa5e58d5d1831"}, + {file = "pyzmq-27.1.0-cp38-cp38-win32.whl", hash = "sha256:a1aa0ee920fb3825d6c825ae3f6c508403b905b698b6460408ebd5bb04bbb312"}, + {file = "pyzmq-27.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:df7cd397ece96cf20a76fae705d40efbab217d217897a5053267cd88a700c266"}, + {file = "pyzmq-27.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:96c71c32fff75957db6ae33cd961439f386505c6e6b377370af9b24a1ef9eafb"}, + {file = "pyzmq-27.1.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:49d3980544447f6bd2968b6ac913ab963a49dcaa2d4a2990041f16057b04c429"}, + {file = "pyzmq-27.1.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:849ca054d81aa1c175c49484afaaa5db0622092b5eccb2055f9f3bb8f703782d"}, + {file = "pyzmq-27.1.0-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3970778e74cb7f85934d2b926b9900e92bfe597e62267d7499acc39c9c28e345"}, + {file = "pyzmq-27.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:da96ecdcf7d3919c3be2de91a8c513c186f6762aa6cf7c01087ed74fad7f0968"}, + {file = "pyzmq-27.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9541c444cfe1b1c0156c5c86ece2bb926c7079a18e7b47b0b1b3b1b875e5d098"}, + {file = "pyzmq-27.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e30a74a39b93e2e1591b58eb1acef4902be27c957a8720b0e368f579b82dc22f"}, + {file = "pyzmq-27.1.0-cp39-cp39-win32.whl", hash = "sha256:b1267823d72d1e40701dcba7edc45fd17f71be1285557b7fe668887150a14b78"}, + {file = "pyzmq-27.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0c996ded912812a2fcd7ab6574f4ad3edc27cb6510349431e4930d4196ade7db"}, + {file = "pyzmq-27.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:346e9ba4198177a07e7706050f35d733e08c1c1f8ceacd5eb6389d653579ffbc"}, + {file = "pyzmq-27.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c17e03cbc9312bee223864f1a2b13a99522e0dc9f7c5df0177cd45210ac286e6"}, + {file = "pyzmq-27.1.0-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f328d01128373cb6763823b2b4e7f73bdf767834268c565151eacb3b7a392f90"}, + {file = "pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c1790386614232e1b3a40a958454bdd42c6d1811837b15ddbb052a032a43f62"}, + {file = "pyzmq-27.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:448f9cb54eb0cee4732b46584f2710c8bc178b0e5371d9e4fc8125201e413a74"}, + {file = "pyzmq-27.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:05b12f2d32112bf8c95ef2e74ec4f1d4beb01f8b5e703b38537f8849f92cb9ba"}, + {file = "pyzmq-27.1.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:18770c8d3563715387139060d37859c02ce40718d1faf299abddcdcc6a649066"}, + {file = "pyzmq-27.1.0-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ac25465d42f92e990f8d8b0546b01c391ad431c3bf447683fdc40565941d0604"}, + {file = "pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:53b40f8ae006f2734ee7608d59ed661419f087521edbfc2149c3932e9c14808c"}, + {file = "pyzmq-27.1.0-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f605d884e7c8be8fe1aa94e0a783bf3f591b84c24e4bc4f3e7564c82ac25e271"}, + {file = "pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355"}, + {file = "pyzmq-27.1.0-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:50081a4e98472ba9f5a02850014b4c9b629da6710f8f14f3b15897c666a28f1b"}, + {file = "pyzmq-27.1.0-pp38-pypy38_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:510869f9df36ab97f89f4cff9d002a89ac554c7ac9cadd87d444aa4cf66abd27"}, + {file = "pyzmq-27.1.0-pp38-pypy38_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1f8426a01b1c4098a750973c37131cf585f61c7911d735f729935a0c701b68d3"}, + {file = "pyzmq-27.1.0-pp38-pypy38_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:726b6a502f2e34c6d2ada5e702929586d3ac948a4dbbb7fed9854ec8c0466027"}, + {file = "pyzmq-27.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:bd67e7c8f4654bef471c0b1ca6614af0b5202a790723a58b79d9584dc8022a78"}, + {file = "pyzmq-27.1.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:722ea791aa233ac0a819fc2c475e1292c76930b31f1d828cb61073e2fe5e208f"}, + {file = "pyzmq-27.1.0-pp39-pypy39_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:01f9437501886d3a1dd4b02ef59fb8cc384fa718ce066d52f175ee49dd5b7ed8"}, + {file = "pyzmq-27.1.0-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4a19387a3dddcc762bfd2f570d14e2395b2c9701329b266f83dd87a2b3cbd381"}, + {file = "pyzmq-27.1.0-pp39-pypy39_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c618fbcd069e3a29dcd221739cacde52edcc681f041907867e0f5cc7e85f172"}, + {file = "pyzmq-27.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff8d114d14ac671d88c89b9224c63d6c4e5a613fe8acd5594ce53d752a3aafe9"}, + {file = "pyzmq-27.1.0.tar.gz", hash = "sha256:ac0765e3d44455adb6ddbf4417dcce460fc40a05978c08efdf2948072f6db540"}, +] + +[package.dependencies] +cffi = {version = "*", markers = "implementation_name == \"pypy\""} + +[[package]] +name = "referencing" +version = "0.37.0" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231"}, + {file = "referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" +typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} + +[[package]] +name = "requests" +version = "2.34.2" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0"}, + {file = "requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed"}, +] + +[package.dependencies] +certifi = ">=2023.5.7" +charset_normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.26,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] + +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +description = "A pure python RFC3339 validator" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] +files = [ + {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, + {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, +] + +[package.dependencies] +six = "*" + +[[package]] +name = "rfc3986-validator" +version = "0.1.1" +description = "Pure python rfc3986 validator" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +groups = ["main"] +files = [ + {file = "rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9"}, + {file = "rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055"}, +] + +[[package]] +name = "rfc3987-syntax" +version = "1.1.0" +description = "Helper functions to syntactically validate strings according to RFC 3987." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f"}, + {file = "rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d"}, +] + +[package.dependencies] +lark = ">=1.2.2" + +[package.extras] +testing = ["pytest (>=8.3.5)"] + +[[package]] +name = "rpds-py" +version = "0.30.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.10" +groups = ["main"] +markers = "python_version == \"3.10\"" +files = [ + {file = "rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288"}, + {file = "rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221"}, + {file = "rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7"}, + {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff"}, + {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7"}, + {file = "rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139"}, + {file = "rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464"}, + {file = "rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169"}, + {file = "rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425"}, + {file = "rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d"}, + {file = "rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038"}, + {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7"}, + {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed"}, + {file = "rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85"}, + {file = "rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c"}, + {file = "rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825"}, + {file = "rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229"}, + {file = "rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad"}, + {file = "rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6"}, + {file = "rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51"}, + {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5"}, + {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e"}, + {file = "rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394"}, + {file = "rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf"}, + {file = "rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b"}, + {file = "rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e"}, + {file = "rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2"}, + {file = "rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e"}, + {file = "rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d"}, + {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7"}, + {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31"}, + {file = "rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95"}, + {file = "rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d"}, + {file = "rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15"}, + {file = "rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1"}, + {file = "rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a"}, + {file = "rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9"}, + {file = "rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0"}, + {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94"}, + {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08"}, + {file = "rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27"}, + {file = "rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6"}, + {file = "rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d"}, + {file = "rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0"}, + {file = "rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07"}, + {file = "rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f"}, + {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65"}, + {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f"}, + {file = "rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53"}, + {file = "rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed"}, + {file = "rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950"}, + {file = "rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6"}, + {file = "rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb"}, + {file = "rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8"}, + {file = "rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5"}, + {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404"}, + {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856"}, + {file = "rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40"}, + {file = "rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0"}, + {file = "rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4"}, + {file = "rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e"}, + {file = "rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84"}, +] + +[[package]] +name = "rpds-py" +version = "2026.6.3" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.11" +groups = ["main"] +markers = "python_version >= \"3.11\"" +files = [ + {file = "rpds_py-2026.6.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7b689145a1485c335569bd056464f3243a29af7ed3871c7be31ad624ba239bc7"}, + {file = "rpds_py-2026.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db08f45aecde626498fb3df07bcf6d2ec040af42e859a4f5040d79c200342911"}, + {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc992ab27b15f852c76755eb2ab7dce86585ddadba6fa5946e58556088845b4"}, + {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f88d653e7b3b779d71ae7454e20dcc9b6bae903f33c269db9f2be41bda3f261"}, + {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e52655eaf81e32593abedaa4bfe33170c8cfedf3365ed9be6e11e07f148f0278"}, + {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dfcc8b909769d19db55c7cc9541eb64b9b774b1057ffffb4f1048070475bb9f9"}, + {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c1255b302953c86a486b81d330d5ee1d5bd937691ce271b6be0ef0e299eaab7"}, + {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:8d2294a31386bfa251d8c8a39472beee17db67d4f1a6eabea665d35c9a4461c3"}, + {file = "rpds_py-2026.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8f23ead891a3b762f35ab3b04623da7056545b48aa60d59957e6789914545da"}, + {file = "rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:421aba32367055614287a4292b6a17f1939c9452299f7a0209c117e990b646d4"}, + {file = "rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1e5822dfc2f0d4ab7e745eaa6d85945069329beeccef965af3f3bb26058fcab6"}, + {file = "rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83e35b57523816c8613fd0776b40cd8bb9f596b37ddd2692eb4a6bb5ab2f8c93"}, + {file = "rpds_py-2026.6.3-cp311-cp311-win32.whl", hash = "sha256:de3eceba0b683bcbb1ab93da016d0270df1f9ae7be716b40214c5dafac6ea45a"}, + {file = "rpds_py-2026.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:2c54a076ca4d370980ab57bc0e31df57bbe8d41340436a90ef8b1219a3cbb127"}, + {file = "rpds_py-2026.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:168c733a7112e071bb7a66460e667edfcff06c017a3c523f7a8a8e08d0140804"}, + {file = "rpds_py-2026.6.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0"}, + {file = "rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf"}, + {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24"}, + {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e"}, + {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975"}, + {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680"}, + {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6"}, + {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a"}, + {file = "rpds_py-2026.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4"}, + {file = "rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa"}, + {file = "rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc"}, + {file = "rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822"}, + {file = "rpds_py-2026.6.3-cp312-cp312-win32.whl", hash = "sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed"}, + {file = "rpds_py-2026.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f"}, + {file = "rpds_py-2026.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96"}, + {file = "rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223"}, + {file = "rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f"}, + {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f"}, + {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7"}, + {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6"}, + {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af"}, + {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf"}, + {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885"}, + {file = "rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4"}, + {file = "rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7"}, + {file = "rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d"}, + {file = "rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97"}, + {file = "rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0"}, + {file = "rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80"}, + {file = "rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb"}, + {file = "rpds_py-2026.6.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e"}, + {file = "rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd"}, + {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d"}, + {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda"}, + {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8"}, + {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53"}, + {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504"}, + {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc"}, + {file = "rpds_py-2026.6.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77"}, + {file = "rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698"}, + {file = "rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd"}, + {file = "rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d"}, + {file = "rpds_py-2026.6.3-cp314-cp314-win32.whl", hash = "sha256:882076c00c0a608b131187055ddc5ae29f2e7eaf870d6168980420d58528a5c8"}, + {file = "rpds_py-2026.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5"}, + {file = "rpds_py-2026.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:2a9c6f195058cb45335e8cc3802745c603d716eb96bc9625950c1aac71c0c703"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-win32.whl", hash = "sha256:6de4744d05bd1aa1be4ed7ea1189e3979196808008113bbbf899a460966b925e"}, + {file = "rpds_py-2026.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:c7b9a2f8f4d8e90af72571d3d495deebdd7e3c75451f5b41719aee166e940fc2"}, + {file = "rpds_py-2026.6.3-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13"}, + {file = "rpds_py-2026.6.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05"}, + {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba"}, + {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617"}, + {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9"}, + {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb"}, + {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885"}, + {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a"}, + {file = "rpds_py-2026.6.3-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868"}, + {file = "rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187"}, + {file = "rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107"}, + {file = "rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba"}, + {file = "rpds_py-2026.6.3-cp315-cp315-win32.whl", hash = "sha256:d483fe17f01ad64b7bf7cc38fcefff1ca9fb83f8c2b2542b68f97ffe0611b369"}, + {file = "rpds_py-2026.6.3-cp315-cp315-win_amd64.whl", hash = "sha256:67e3a721ffc5d8d2210d3671872298c4a84e4b8035cfe42ffd7cde35d772b146"}, + {file = "rpds_py-2026.6.3-cp315-cp315-win_arm64.whl", hash = "sha256:6e84adbcf4bf841aed8116a8264b9f50b4cb3e7bd89b516122e616ac56ca269e"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-win32.whl", hash = "sha256:913ca42ccad3f8cc6e292b587ae8ae49c8c823e5dce51a736252fc7c7cdfa577"}, + {file = "rpds_py-2026.6.3-cp315-cp315t-win_amd64.whl", hash = "sha256:ae3d4fe8c0b9213624fdce7279d70e3b148b682ca20719ebd193a23ebfa47324"}, + {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4cf2d36a2357e4d07bb5a4f98801265327b48256867816cfd2ceb001e9754a8f"}, + {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:30c6dc199b24a5e3e81d50da0f00858c5bbdb2617a750395687f4339c5818171"}, + {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9891e594296ab9dada6551c8e7b387b2721f27a67eecd528412e8906247a7b90"}, + {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5c2dc92304aa48a4a60443b548bb12f12e119d4b72f314015e67b9e1be97fca"}, + {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:127e08c0642d880cf32ca47ec2a4a77b901f7e2dd1ad9762adb13955d72ffcc9"}, + {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bb68f03f395eb793220b45c097bd4d8c32944393da0fad8b999efac0868fc8c"}, + {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3450b693fde92133e9f51060568a4c31fcca76d5e53bbd611e689ca446517e9"}, + {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:5e8d07bddee435a2ff6f1920e18feff28d0bc4533e42f4bf6927fbd073312c41"}, + {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3a83ae6c67b7676b9878378547ca8e93ed77a580037bcbcd1d32f739e1e6089c"}, + {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2bfd04c19ddbd6640de0b51894d764bd2758854d5b75bd102d2ef10cb9c293a9"}, + {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:ca6546b66be9dc4738b1b043d5ebd5488c66c578c5ff0fd0e8065313fe3afb76"}, + {file = "rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8e65860d238379ed982fd9ba690579b5e95af2f4840f99c772816dbe573cb826"}, + {file = "rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4"}, +] + +[[package]] +name = "send2trash" +version = "2.1.0" +description = "Send file to trash natively under Mac OS X, Windows and Linux" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "send2trash-2.1.0-py3-none-any.whl", hash = "sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c"}, + {file = "send2trash-2.1.0.tar.gz", hash = "sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459"}, +] + +[package.extras] +nativelib = ["pyobjc (>=9.0) ; sys_platform == \"darwin\"", "pywin32 (>=305) ; sys_platform == \"win32\""] +test = ["pytest (>=8)"] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "soupsieve" +version = "2.9.1" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "soupsieve-2.9.1-py3-none-any.whl", hash = "sha256:4f4477399246b7a0c720a88ca2454b11cd6bb9ae4c9d170140786e916776c14c"}, + {file = "soupsieve-2.9.1.tar.gz", hash = "sha256:c33e6605bbc71dd628b00c632d58ae607c22bade247e52553928f83bbb75b4ba"}, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +description = "Extract data from python stack frames and tracebacks for informative displays" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, +] + +[package.dependencies] +asttokens = ">=2.1.0" +executing = ">=1.2.0" +pure-eval = "*" + +[package.extras] +tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] + +[[package]] +name = "terminado" +version = "0.18.1" +description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0"}, + {file = "terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e"}, +] + +[package.dependencies] +ptyprocess = {version = "*", markers = "os_name != \"nt\""} +pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""} +tornado = ">=6.1.0" + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["pre-commit", "pytest (>=7.0)", "pytest-timeout"] +typing = ["mypy (>=1.6,<2.0)", "traitlets (>=5.11.1)"] + +[[package]] +name = "tinycss2" +version = "1.5.1" +description = "A tiny CSS parser" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "tinycss2-1.5.1-py3-none-any.whl", hash = "sha256:3415ba0f5839c062696996998176c4a3751d18b7edaaeeb658c9ce21ec150661"}, + {file = "tinycss2-1.5.1.tar.gz", hash = "sha256:d339d2b616ba90ccce58da8495a78f46e55d4d25f9fd71dfd526f07e7d53f957"}, +] + +[package.dependencies] +webencodings = ">=0.4" + +[package.extras] +doc = ["furo", "sphinx"] +test = ["pytest", "ruff"] + +[[package]] +name = "tomli" +version = "2.4.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +markers = "python_version == \"3.10\"" +files = [ + {file = "tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30"}, + {file = "tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a"}, + {file = "tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076"}, + {file = "tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9"}, + {file = "tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c"}, + {file = "tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc"}, + {file = "tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049"}, + {file = "tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e"}, + {file = "tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece"}, + {file = "tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a"}, + {file = "tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085"}, + {file = "tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9"}, + {file = "tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5"}, + {file = "tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585"}, + {file = "tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1"}, + {file = "tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917"}, + {file = "tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9"}, + {file = "tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257"}, + {file = "tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54"}, + {file = "tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a"}, + {file = "tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897"}, + {file = "tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f"}, + {file = "tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d"}, + {file = "tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5"}, + {file = "tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd"}, + {file = "tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36"}, + {file = "tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd"}, + {file = "tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf"}, + {file = "tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac"}, + {file = "tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662"}, + {file = "tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853"}, + {file = "tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15"}, + {file = "tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba"}, + {file = "tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6"}, + {file = "tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7"}, + {file = "tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232"}, + {file = "tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4"}, + {file = "tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c"}, + {file = "tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d"}, + {file = "tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41"}, + {file = "tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c"}, + {file = "tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f"}, + {file = "tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8"}, + {file = "tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26"}, + {file = "tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396"}, + {file = "tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe"}, + {file = "tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f"}, +] + +[[package]] +name = "tornado" +version = "6.5.7" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "tornado-6.5.7-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:148b2eb15c2c765a50796172c1e499649b35f30d2e3c3d3e15913cfa56bfb163"}, + {file = "tornado-6.5.7-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9da38de27f1da3b78a966f0dae12b5a1ea9afe72ca805d84ff06508272ddf100"}, + {file = "tornado-6.5.7-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8d759e71906ee783f8867b93bf26a265743da4c1e2f4a018464c1ba019862972"}, + {file = "tornado-6.5.7-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a46347a18f23fb92b396beebe0fb78f61dda0cc302445202c16203d8a18848b"}, + {file = "tornado-6.5.7-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7778b30bef919231265e91c69963ce0f49a1e9c07ac900bbe75b19ce2575ba92"}, + {file = "tornado-6.5.7-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e726f0c75da7726eec023aa62751ff8878bd2737e34fbdd33b1ae5897d2200f5"}, + {file = "tornado-6.5.7-cp39-abi3-win32.whl", hash = "sha256:f8de3bf12d3efdd0cbe7c8887868198f8a91415e3f29fcf258d9b8eb7b1d9ae4"}, + {file = "tornado-6.5.7-cp39-abi3-win_amd64.whl", hash = "sha256:de942f843533a039ef9fa3d9c88c7cd8a7c94553fb5ad0154270989b3d99a2c4"}, + {file = "tornado-6.5.7-cp39-abi3-win_arm64.whl", hash = "sha256:ff934fce95643af5f11efdae618eaa73d469dc588641e5c8d19295a0c65c4796"}, + {file = "tornado-6.5.7.tar.gz", hash = "sha256:66c513a76cda70d53907bc27cf1447557699c2e95aa48ba27a442ff61c3ddfc2"}, +] + +[[package]] +name = "traitlets" +version = "5.15.1" +description = "Traitlets Python configuration system" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "traitlets-5.15.1-py3-none-any.whl", hash = "sha256:770a53705f84b81ac107e83a1b3328ff2dae16094d8fc3cfc004e4b22dfd8e92"}, + {file = "traitlets-5.15.1.tar.gz", hash = "sha256:7b1c07854fe25acb39e009bae49f11b79ff6cbb2f27999104e9110e7a6b53722"}, +] + +[package.extras] +docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.17.0,<1.19)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8"}, + {file = "typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5"}, +] + +[[package]] +name = "tzdata" +version = "2026.3" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +groups = ["main"] +files = [ + {file = "tzdata-2026.3-py2.py3-none-any.whl", hash = "sha256:dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931"}, + {file = "tzdata-2026.3.tar.gz", hash = "sha256:4a1518b8993086a7982523e071643f3c0e5f213e75b21318e78bcabfff9d1415"}, +] + +[[package]] +name = "uri-template" +version = "1.3.0" +description = "RFC 6570 URI Template Processor" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7"}, + {file = "uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363"}, +] + +[package.extras] +dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake8-commas", "flake8-comprehensions", "flake8-continuation", "flake8-datetimez", "flake8-docstrings", "flake8-import-order", "flake8-literal", "flake8-modern-annotations", "flake8-noqa", "flake8-pyproject", "flake8-requirements", "flake8-typechecking-import", "flake8-use-fstring", "mypy", "pep8-naming", "types-PyYAML"] + +[[package]] +name = "urllib3" +version = "2.7.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"}, + {file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"}, +] + +[package.extras] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] + +[[package]] +name = "usaddress" +version = "0.5.16" +description = "Parse US addresses using conditional random fields" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "usaddress-0.5.16-py3-none-any.whl", hash = "sha256:ce2bd73e3a41176fa29f093e9ad153327a87862430f1b0b121f0cb230c182a6a"}, + {file = "usaddress-0.5.16.tar.gz", hash = "sha256:f7e614e2489c159cf85be1b6f86edbfaf329dbeedef4d002a025757afd1ec6c6"}, +] + +[package.dependencies] +probableparsing = "*" +python-crfsuite = ">=0.7" + +[package.extras] +dev = ["black", "flake8", "isort", "mypy", "parserator", "pytest"] + +[[package]] +name = "virtualenv" +version = "21.7.0" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "virtualenv-21.7.0-py3-none-any.whl", hash = "sha256:a8370c1c5530fbabf955e40b8fbbc68a431648b10f9433faa587db30a06e51dd"}, + {file = "virtualenv-21.7.0.tar.gz", hash = "sha256:7f9519b9432ff11b6e1a3e94061664efc2ff99ea21780e3cf4f6bd0a5da8b37c"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = {version = ">=3.24.2,<4", markers = "python_version >= \"3.10\""} +platformdirs = ">=3.9.1,<5" +python-discovery = ">=1.4.2" +typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""} + +[[package]] +name = "wcwidth" +version = "0.8.2" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "wcwidth-0.8.2-py3-none-any.whl", hash = "sha256:d63947694a0539a1d51e01eda7caf800c291020e6cdd7e28ad7b14dd33ad4f85"}, + {file = "wcwidth-0.8.2.tar.gz", hash = "sha256:91fbef97204b96a3d4d421609b80340b760cf33e26da123ff243d76b1fda8dda"}, +] + +[[package]] +name = "webcolors" +version = "25.10.0" +description = "A library for working with the color formats defined by HTML and CSS." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d"}, + {file = "webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf"}, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + +[[package]] +name = "websocket-client" +version = "1.9.0" +description = "WebSocket client for Python with low level API options" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef"}, + {file = "websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98"}, +] + +[package.extras] +docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx_rtd_theme (>=1.1.0)"] +optional = ["python-socks", "wsaccel"] +test = ["pytest", "websockets"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.10,<3.13" +content-hash = "31db0206cec080ad2894f9c4dd55e1bf28e7b28d8a23ae4b793aac17553cc046" From 03e525a953a82f7e1a0e546a830f9cb08d33bf70 Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 22 Jul 2026 20:26:31 -0400 Subject: [PATCH 4/5] none of those files should be in this repo --- output/create_1620_i_street_3d_map.py | 206 ---- output/create_1620_openstudio_audit_assets.py | 269 ----- ...te_1620_openstudio_comparison_artifacts.py | 135 --- output/create_2258_audit_report.py | 398 ------- output/create_better_owner_audit_pdfs.py | 994 ------------------ output/inspect_openstudio_mcp_tools.py | 67 -- output/openstudio_mcp_schema_probe.py | 125 --- output/run_1620_openstudio_mcp.py | 345 ------ 8 files changed, 2539 deletions(-) delete mode 100644 output/create_1620_i_street_3d_map.py delete mode 100644 output/create_1620_openstudio_audit_assets.py delete mode 100644 output/create_1620_openstudio_comparison_artifacts.py delete mode 100644 output/create_2258_audit_report.py delete mode 100644 output/create_better_owner_audit_pdfs.py delete mode 100644 output/inspect_openstudio_mcp_tools.py delete mode 100644 output/openstudio_mcp_schema_probe.py delete mode 100644 output/run_1620_openstudio_mcp.py diff --git a/output/create_1620_i_street_3d_map.py b/output/create_1620_i_street_3d_map.py deleted file mode 100644 index ac9cf04..0000000 --- a/output/create_1620_i_street_3d_map.py +++ /dev/null @@ -1,206 +0,0 @@ -from __future__ import annotations - -import html -import json -import math -from pathlib import Path - - -DATA_PATH = Path("output/data/1620_i_street_osm.json") -OUT_DIR = Path("output/images") -TARGET_WAY_ID = 55326896 -TARGET_LAT = 38.9010865 -TARGET_LON = -77.0375014 - - -def mercator_meters(lat: float, lon: float) -> tuple[float, float]: - radius = 6378137.0 - x = math.radians(lon) * radius - y = math.log(math.tan(math.pi / 4 + math.radians(lat) / 2)) * radius - return x, y - - -def project(lat: float, lon: float, center: tuple[float, float], scale: float, width: int, height: int) -> tuple[float, float]: - x, y = mercator_meters(lat, lon) - dx = x - center[0] - dy = y - center[1] - - # Rotate the map so I Street reads diagonally, closer to a Google Earth oblique view. - angle = math.radians(-27) - rx = dx * math.cos(angle) - dy * math.sin(angle) - ry = dx * math.sin(angle) + dy * math.cos(angle) - - sx = width / 2 + rx * scale - sy = height / 2 - ry * scale - return sx, sy - - -def polygon_points(geometry: list[dict], center: tuple[float, float], scale: float, width: int, height: int) -> list[tuple[float, float]]: - return [project(point["lat"], point["lon"], center, scale, width, height) for point in geometry] - - -def poly_to_svg(points: list[tuple[float, float]]) -> str: - return " ".join(f"{x:.1f},{y:.1f}" for x, y in points) - - -def centroid(points: list[tuple[float, float]]) -> tuple[float, float]: - if not points: - return 0, 0 - return sum(x for x, _ in points) / len(points), sum(y for _, y in points) / len(points) - - -def path_from_points(points: list[tuple[float, float]]) -> str: - if not points: - return "" - head, *tail = points - parts = [f"M {head[0]:.1f} {head[1]:.1f}"] - parts.extend(f"L {x:.1f} {y:.1f}" for x, y in tail) - return " ".join(parts) - - -def draw_road(element: dict, points: list[tuple[float, float]]) -> str: - highway = element.get("tags", {}).get("highway", "") - name = element.get("tags", {}).get("name") - if highway in {"footway", "path", "steps", "pedestrian"}: - width = 3 - color = "#e8eef0" - casing = "#cbd8de" - elif highway in {"primary", "trunk", "secondary"}: - width = 18 - color = "#b56f62" if name and "I Street" in name else "#d7dad8" - casing = "#f8faf8" - elif highway in {"tertiary", "living_street", "service"}: - width = 10 - color = "#d1d8d7" - casing = "#f8faf8" - else: - width = 7 - color = "#d8dfde" - casing = "#f8faf8" - - d = path_from_points(points) - if not d: - return "" - out = [ - f'', - f'', - ] - if name and ("I Street" in name or "16th Street" in name or "17th Street" in name): - x, y = points[len(points) // 2] - out.append( - f'{html.escape(name.replace("Northwest", "NW"))}', - ) - return "\n".join(out) - - -def draw_building(element: dict, points: list[tuple[float, float]]) -> str: - tags = element.get("tags", {}) - is_target = element["id"] == TARGET_WAY_ID - levels = float(tags.get("building:levels", 10 if is_target else 5)) - height = min(76, max(16, levels * 5.6)) - dx = height * 0.30 - dy = -height * 0.52 - - roof = poly_to_svg(points) - elevated = [(x + dx, y + dy) for x, y in points] - elevated_svg = poly_to_svg(elevated) - - sides = [] - for i in range(len(points) - 1): - p1 = points[i] - p2 = points[i + 1] - q2 = elevated[i + 1] - q1 = elevated[i] - avg_y = (p1[1] + p2[1]) / 2 - shade = "#718495" if avg_y > centroid(points)[1] else "#566878" - if is_target: - shade = "#8193a0" if avg_y > centroid(points)[1] else "#5e707d" - sides.append( - f'', - ) - - cx, cy = centroid(elevated) - roof_fill = "#d7d1c5" if is_target else "#c6cfd2" - roof_stroke = "#4c5964" if is_target else "#8b99a0" - shadow = f'' - out = [shadow, *sides, f''] - - if is_target: - # Rooftop mechanical penthouses, drawn schematically so the target has visual depth. - out.extend( - [ - f'', - f'', - f'', - f'1620 I STREET NW', - f'', - ], - ) - elif tags.get("name"): - out.append(f'{html.escape(tags["name"])}') - return "\n".join(out) - - -def main() -> None: - data = json.loads(DATA_PATH.read_text()) - width, height = 1000, 760 - center = mercator_meters(TARGET_LAT, TARGET_LON) - scale = 2.85 - - roads = [] - buildings = [] - for element in data["elements"]: - geometry = element.get("geometry") or [] - if len(geometry) < 2: - continue - points = polygon_points(geometry, center, scale, width, height) - tags = element.get("tags", {}) - if tags.get("highway"): - roads.append((element, points)) - elif tags.get("building") and len(points) >= 4: - buildings.append((element, points)) - - buildings.sort(key=lambda item: (item[0]["id"] == TARGET_WAY_ID, centroid(item[1])[1])) - - road_svg = "\n".join(draw_road(element, points) for element, points in roads) - building_svg = "\n".join(draw_building(element, points) for element, points in buildings) - - svg = f''' - - - - - - - - - - - - - - - - {road_svg} - {building_svg} - - - 1620 I Street NW - OSM building footprint rendered as an oblique 3D owner-audit locator - Map data © OpenStreetMap contributors; building geometry source includes DCGIS tags in OSM. - - -''' - OUT_DIR.mkdir(parents=True, exist_ok=True) - (OUT_DIR / "1620_i_street_osm_3d.svg").write_text(svg) - print(OUT_DIR / "1620_i_street_osm_3d.svg") - - -if __name__ == "__main__": - main() diff --git a/output/create_1620_openstudio_audit_assets.py b/output/create_1620_openstudio_audit_assets.py deleted file mode 100644 index a9dda1b..0000000 --- a/output/create_1620_openstudio_audit_assets.py +++ /dev/null @@ -1,269 +0,0 @@ -from __future__ import annotations - -import csv -import json -import math -from pathlib import Path - - -SRC = Path("output/openstudio_1620/1620_i_street_openstudio_comparison_corrected.json") -OUT_DIR = Path("output/openstudio_1620") -CALIBRATION_JSON = OUT_DIR / "1620_i_street_openstudio_calibration.json" -CALIBRATION_CSV = OUT_DIR / "1620_i_street_openstudio_calibration.csv" -CALIBRATION_SVG = OUT_DIR / "1620_i_street_openstudio_calibration.svg" -ZONING_SVG = OUT_DIR / "1620_i_street_perimeter_core_10_story.svg" - - -def fmt(number: float, digits: int = 1) -> str: - return f"{number:,.{digits}f}" - - -def pct(value: float) -> str: - return f"{value:+.1f}%" - - -def cvrmse(actual: list[float], modeled: list[float]) -> float: - mean_actual = sum(actual) / len(actual) - rmse = math.sqrt(sum((m - a) ** 2 for a, m in zip(actual, modeled)) / len(actual)) - return rmse / mean_actual * 100.0 - - -def nmbe(actual: list[float], modeled: list[float]) -> float: - mean_actual = sum(actual) / len(actual) - return sum(m - a for a, m in zip(actual, modeled)) / ((len(actual) - 1) * mean_actual) * 100.0 - - -def write_calibration_assets(data: dict) -> None: - rows = [] - for row in data["comparison"]["monthly_electricity"]: - actual = float(row["actual_kbtu"]) - baseline = float(row["modeled_kbtu"]) - factor = actual / baseline if baseline else 0.0 - rows.append( - { - "month": row["month"], - "actual_kbtu": actual, - "baseline_model_kbtu": baseline, - "meter_calibrated_kbtu": actual, - "monthly_calibration_factor": factor, - "baseline_difference_percent": (baseline - actual) / actual * 100.0, - "calibrated_difference_percent": 0.0, - }, - ) - - actual = [row["actual_kbtu"] for row in rows] - baseline = [row["baseline_model_kbtu"] for row in rows] - calibrated = [row["meter_calibrated_kbtu"] for row in rows] - metrics = { - "baseline_cvrmse_percent": cvrmse(actual, baseline), - "baseline_nmbe_percent": nmbe(actual, baseline), - "calibrated_cvrmse_percent": cvrmse(actual, calibrated), - "calibrated_nmbe_percent": nmbe(actual, calibrated), - "baseline_annual_difference_percent": data["comparison"]["model_vs_actual_electricity_percent"], - "model_site_eui_kbtu_per_ft2": data["comparison"]["model_site_eui_kbtu_per_ft2"], - "actual_meter_eui_kbtu_per_ft2": data["comparison"]["actual_meter_eui_kbtu_per_ft2"], - "seed_reported_site_eui_kbtu_per_ft2": data["comparison"]["seed_reported_site_eui_kbtu_per_ft2"], - } - CALIBRATION_JSON.write_text(json.dumps({"metrics": metrics, "monthly": rows}, indent=2)) - - with CALIBRATION_CSV.open("w", newline="") as f: - writer = csv.DictWriter(f, fieldnames=list(rows[0].keys())) - writer.writeheader() - writer.writerows(rows) - - max_kbtu = max(max(row["actual_kbtu"], row["baseline_model_kbtu"]) for row in rows) - max_factor = max(row["monthly_calibration_factor"] for row in rows) - w, h = 1400, 1400 - chart_x, chart_y, chart_w, chart_h = 94, 300, 850, 430 - factor_x, factor_y, factor_w, factor_h = 990, 300, 300, 430 - group_w = chart_w / len(rows) - bar_w = group_w * 0.28 - parts = [ - f'', - '', - f'', - f'', - '', - ] - for gx in range(770, w, 18): - parts.append(f'') - for gy in range(0, 106, 18): - parts.append(f'') - parts.extend( - [ - "", - '1620 I Street NW', - 'OpenStudio-MCP baseline with monthly meter calibration', - 'Owner Audit View', - 'Real SEED meters + EnergyPlus model', - ], - ) - cards = [ - ("Annual baseline error", pct(metrics["baseline_annual_difference_percent"]), "EnergyPlus vs 2022 meter", "#1b6257"), - ("Monthly CVRMSE", f'{fmt(metrics["baseline_cvrmse_percent"])}% -> 0.0%', "after meter calibration", "#183f6d"), - ("EUI check", f'{fmt(metrics["model_site_eui_kbtu_per_ft2"])} vs {fmt(metrics["actual_meter_eui_kbtu_per_ft2"])}', "model vs meter kBtu/ft2", "#d9901a"), - ] - for i, (title, value, note, accent) in enumerate(cards): - x = 70 + i * 430 - parts.extend( - [ - f'', - f'', - f'{title.upper()}', - f'{value}', - f'{note}', - ], - ) - - parts.extend( - [ - f'Monthly electricity: real vs OpenStudio baseline', - f'', - f'', - f'kBtu', - ], - ) - for i, row in enumerate(rows): - base_x = chart_x + i * group_w + 8 - actual_h = chart_h * row["actual_kbtu"] / max_kbtu - baseline_h = chart_h * row["baseline_model_kbtu"] / max_kbtu - parts.extend( - [ - f'', - f'', - f'', - f'{row["month"]}', - ], - ) - - parts.extend( - [ - f'Calibration factors', - f'', - f'', - ], - ) - mini_w = (factor_w - 86) / len(rows) - for i, row in enumerate(rows): - bx = factor_x + 54 + i * mini_w - bh = (factor_h - 116) * row["monthly_calibration_factor"] / max_factor - by = factor_y + factor_h - 52 - bh - parts.extend( - [ - f'', - f'{row["month"][0]}', - ], - ) - parts.extend( - [ - f'Actual meter / baseline model', - f'Range: {fmt(min(r["monthly_calibration_factor"] for r in rows), 2)}x to {fmt(max_factor, 2)}x', - '', - 'Actual SEED electric meter', - '', - 'OpenStudio-MCP baseline', - '', - 'Meter-calibrated profile', - 'Note: calibration improves the monthly electricity match by applying transparent month-specific factors to the EnergyPlus baseline.', - 'It does not replace field verification of schedules, tenant loads, or HVAC controls.', - "", - ], - ) - CALIBRATION_SVG.write_text("\n".join(parts)) - - -def write_zoning_visual() -> None: - w, h = 1400, 1400 - parts = [ - f'', - '', - f'', - f'', - '1620 I Street NW', - '10-story perimeter/core OpenStudio model visual', - 'SEED + OSM', - '125,367 ft2 large office assumption', - '', - '', - 'Geometry intent', - 'The model is shown as ten stacked office stories with four perimeter zones wrapped around a central core on each floor.', - 'This is a schematic audit graphic derived from the OpenStudio-MCP assumptions, not a photogrammetric facade reconstruction.', - ] - - ox, oy = 352, 748 - floor_w, floor_d = 470, 220 - dx, dy = 52, -28 - floor_gap = 54 - for level in range(10): - y = oy - level * floor_gap - x = ox + level * 14 - z = level * 5 - top = [(x, y + z), (x + floor_w, y + z), (x + floor_w + dx, y + dy + z), (x + dx, y + dy + z)] - core = [ - (x + 175, y - 48 + z), - (x + 306, y - 48 + z), - (x + 330, y - 61 + z), - (x + 199, y - 61 + z), - ] - perimeter = " ".join(f"{px:.1f},{py:.1f}" for px, py in top) - core_poly = " ".join(f"{px:.1f},{py:.1f}" for px, py in core) - side = " ".join( - f"{px:.1f},{py:.1f}" - for px, py in [ - top[1], - (top[1][0], top[1][1] - 36), - (top[2][0], top[2][1] - 36), - top[2], - ] - ) - front = " ".join( - f"{px:.1f},{py:.1f}" - for px, py in [ - top[0], - top[1], - (top[1][0], top[1][1] - 36), - (top[0][0], top[0][1] - 36), - ] - ) - parts.extend( - [ - f'', - f'', - f'', - f'', - ], - ) - if level in {0, 9}: - parts.append(f'Level {level + 1}') - - callouts = [ - (900, 270, "Perimeter zones", "North, south, east, and west perimeter bands capture facade-driven loads.", "#0b94cf"), - (900, 402, "Core zones", "Interior zones capture internal office loads with lower envelope exposure.", "#1b6257"), - (900, 534, "10 stories", "The audit model uses ten above-grade stories and SEED gross floor area.", "#d9901a"), - ] - for x, y, title, body, color in callouts: - parts.extend( - [ - f'', - f'', - f'{title}', - f'{body}', - ], - ) - parts.append("") - ZONING_SVG.write_text("\n".join(parts)) - - -def main() -> None: - OUT_DIR.mkdir(parents=True, exist_ok=True) - data = json.loads(SRC.read_text()) - write_calibration_assets(data) - write_zoning_visual() - print(CALIBRATION_JSON) - print(CALIBRATION_CSV) - print(CALIBRATION_SVG) - print(ZONING_SVG) - - -if __name__ == "__main__": - main() diff --git a/output/create_1620_openstudio_comparison_artifacts.py b/output/create_1620_openstudio_comparison_artifacts.py deleted file mode 100644 index 8152f2e..0000000 --- a/output/create_1620_openstudio_comparison_artifacts.py +++ /dev/null @@ -1,135 +0,0 @@ -from __future__ import annotations - -import csv -import json -from pathlib import Path - - -SRC = Path("output/openstudio_1620/1620_i_street_openstudio_comparison.json") -OUT_DIR = Path("output/openstudio_1620") -CORRECTED = OUT_DIR / "1620_i_street_openstudio_comparison_corrected.json" -CSV_PATH = OUT_DIR / "1620_i_street_monthly_electricity_comparison.csv" -SVG_PATH = OUT_DIR / "1620_i_street_openstudio_vs_real.svg" -J_PER_KBTU = 1_055_055.85262 -MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] - - -def pct(model: float, actual: float) -> float: - return (model - actual) / actual * 100.0 - - -def main() -> None: - data = json.loads(SRC.read_text()) - actual_by_month = {row["month"]: row["actual_kbtu"] for row in data["comparison"]["monthly_electricity"]} - ts = data["mcp"]["electricity_timeseries"] - model_by_month = {} - for row in ts["data"]: - month = MONTHS[int(row["month"]) - 1] - model_by_month[month] = float(row["value"]) / J_PER_KBTU - - rows = [] - for month in MONTHS: - actual = actual_by_month[month] - model = model_by_month[month] - rows.append( - { - "month": month, - "actual_kbtu": actual, - "modeled_kbtu": model, - "difference_kbtu": model - actual, - "difference_percent": pct(model, actual), - }, - ) - - actual_annual = sum(actual_by_month.values()) - model_annual = sum(model_by_month.values()) - metrics = data["mcp"]["summary_metrics"]["metrics"] - model_eui = metrics["eui_kBtu_ft2"] - seed_eui = data["inputs"]["seed_site_eui_kbtu_per_ft2"] - actual_meter_eui = actual_annual / data["inputs"]["seed_gross_floor_area_ft2"] - - data["comparison"] = { - "actual_annual_electricity_kbtu": actual_annual, - "modeled_annual_electricity_kbtu": model_annual, - "actual_meter_eui_kbtu_per_ft2": actual_meter_eui, - "seed_reported_site_eui_kbtu_per_ft2": seed_eui, - "model_site_eui_kbtu_per_ft2": model_eui, - "model_vs_actual_electricity_percent": pct(model_annual, actual_annual), - "model_vs_seed_site_eui_percent": pct(model_eui, seed_eui), - "monthly_electricity": rows, - } - CORRECTED.write_text(json.dumps(data, indent=2)) - - with CSV_PATH.open("w", newline="") as f: - writer = csv.DictWriter(f, fieldnames=list(rows[0].keys())) - writer.writeheader() - writer.writerows(rows) - - max_value = max(max(r["actual_kbtu"], r["modeled_kbtu"]) for r in rows) - svg_w, svg_h = 1000, 1000 - chart_x, chart_y, chart_w, chart_h = 78, 310, 840, 390 - group_w = chart_w / len(rows) - bar_w = group_w * 0.32 - parts = [ - f'', - '', - f'', - f'', - '1620 I Street NW', - 'OpenStudio-MCP model vs SEED 2022 electric meter', - 'SEED + OSM', - 'Office | 125,367 ft2 | 10 levels', - ] - cards = [ - ("Annual electricity", f"{model_annual / 1_000_000:.2f}M vs {actual_annual / 1_000_000:.2f}M kBtu", f"{pct(model_annual, actual_annual):+.2f}%"), - ("Model site EUI", f"{model_eui:.1f} kBtu/ft2", f"SEED: {seed_eui:.1f}"), - ("Meter EUI", f"{actual_meter_eui:.1f} kBtu/ft2", "2022 electric meter only"), - ] - for i, (title, value, note) in enumerate(cards): - x = 56 + i * 305 - parts.extend( - [ - f'', - f'', - f'{title.upper()}', - f'{value}', - f'{note}', - ], - ) - parts.extend( - [ - f'', - f'', - f'Monthly electricity consumption', - f'kBtu', - ], - ) - for i, row in enumerate(rows): - base_x = chart_x + i * group_w + 10 - actual_h = chart_h * row["actual_kbtu"] / max_value - model_h = chart_h * row["modeled_kbtu"] / max_value - parts.extend( - [ - f'', - f'', - f'{row["month"]}', - ], - ) - parts.extend( - [ - '', - 'Actual SEED meter', - '', - 'OpenStudio-MCP model', - 'Model uses MCP create_new_building, OSM-derived 10 stories, SEED GFA, Baltimore-Washington TMY3 weather, all-electric assumptions.', - "", - ], - ) - SVG_PATH.write_text("\n".join(parts)) - print(CORRECTED) - print(CSV_PATH) - print(SVG_PATH) - - -if __name__ == "__main__": - main() diff --git a/output/create_2258_audit_report.py b/output/create_2258_audit_report.py deleted file mode 100644 index 60da6cb..0000000 --- a/output/create_2258_audit_report.py +++ /dev/null @@ -1,398 +0,0 @@ -from __future__ import annotations - -import math -from pathlib import Path - - -PAGE_W = 612 -PAGE_H = 792 -MARGIN = 42 - - -def esc(text: object) -> str: - value = str(text) - return value.replace("\\", "\\\\").replace("(", "\\(").replace(")", "\\)") - - -def fmt_num(value: float, digits: int = 0) -> str: - if value is None: - return "-" - if digits == 0: - return f"{value:,.0f}" - return f"{value:,.{digits}f}" - - -def wrap(text: str, max_chars: int) -> list[str]: - words = text.split() - lines: list[str] = [] - current = "" - for word in words: - candidate = word if not current else f"{current} {word}" - if len(candidate) <= max_chars: - current = candidate - else: - if current: - lines.append(current) - current = word - if current: - lines.append(current) - return lines - - -class Page: - def __init__(self) -> None: - self.ops: list[str] = [] - - def raw(self, op: str) -> None: - self.ops.append(op) - - def color(self, hex_color: str) -> None: - hex_color = hex_color.strip("#") - r = int(hex_color[0:2], 16) / 255 - g = int(hex_color[2:4], 16) / 255 - b = int(hex_color[4:6], 16) / 255 - self.raw(f"{r:.4f} {g:.4f} {b:.4f} rg") - self.raw(f"{r:.4f} {g:.4f} {b:.4f} RG") - - def line_width(self, width: float) -> None: - self.raw(f"{width:.2f} w") - - def rect(self, x: float, y: float, w: float, h: float, fill: str | None = None, stroke: str | None = None) -> None: - if fill: - self.color(fill) - self.raw(f"{x:.2f} {y:.2f} {w:.2f} {h:.2f} re f") - if stroke: - self.color(stroke) - self.raw(f"{x:.2f} {y:.2f} {w:.2f} {h:.2f} re S") - - def line(self, x1: float, y1: float, x2: float, y2: float, color: str = "111111", width: float = 1) -> None: - self.color(color) - self.line_width(width) - self.raw(f"{x1:.2f} {y1:.2f} m {x2:.2f} {y2:.2f} l S") - - def text(self, x: float, y: float, text: object, size: int = 10, font: str = "F1", color: str = "111111") -> None: - self.color(color) - self.raw(f"BT /{font} {size} Tf {x:.2f} {y:.2f} Td ({esc(text)}) Tj ET") - - def multiline( - self, - x: float, - y: float, - text: str, - size: int = 10, - max_chars: int = 80, - leading: float | None = None, - font: str = "F1", - color: str = "111111", - ) -> float: - leading = leading or size * 1.35 - for line in wrap(text, max_chars): - self.text(x, y, line, size=size, font=font, color=color) - y -= leading - return y - - def pill(self, x: float, y: float, w: float, h: float, label: str, value: str, fill: str, accent: str) -> None: - self.rect(x, y, w, h, fill=fill) - self.rect(x, y, 5, h, fill=accent) - self.text(x + 14, y + h - 20, label.upper(), size=8, font="F2", color="5B6470") - self.text(x + 14, y + 15, value, size=18, font="F2", color="111111") - - -class PDF: - def __init__(self) -> None: - self.pages: list[Page] = [] - - def add_page(self) -> Page: - page = Page() - self.pages.append(page) - return page - - def save(self, path: Path) -> None: - objects: list[bytes] = [] - font1 = b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>" - font2 = b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >>" - objects.append(font1) - objects.append(font2) - page_objects: list[tuple[int, int]] = [] - for page in self.pages: - stream = "\n".join(page.ops).encode("latin-1", "replace") - content = b"<< /Length " + str(len(stream)).encode() + b" >>\nstream\n" + stream + b"\nendstream" - content_id = len(objects) + 1 - objects.append(content) - page_id = len(objects) + 1 - page_objects.append((page_id, content_id)) - objects.append(b"") - - pages_id = len(objects) + 1 - kids = " ".join(f"{page_id} 0 R" for page_id, _content_id in page_objects).encode() - objects.append(b"<< /Type /Pages /Kids [" + kids + b"] /Count " + str(len(page_objects)).encode() + b" >>") - catalog_id = len(objects) + 1 - objects.append(b"<< /Type /Catalog /Pages " + str(pages_id).encode() + b" 0 R >>") - - for index, (page_id, content_id) in enumerate(page_objects): - objects[page_id - 1] = ( - b"<< /Type /Page /Parent " - + str(pages_id).encode() - + b" 0 R /MediaBox [0 0 612 792] " - + b"/Resources << /Font << /F1 1 0 R /F2 2 0 R >> >> " - + b"/Contents " - + str(content_id).encode() - + b" 0 R >>" - ) - - out = bytearray(b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n") - offsets = [0] - for obj_id, obj in enumerate(objects, start=1): - offsets.append(len(out)) - out.extend(f"{obj_id} 0 obj\n".encode()) - out.extend(obj) - out.extend(b"\nendobj\n") - xref = len(out) - out.extend(f"xref\n0 {len(objects) + 1}\n".encode()) - out.extend(b"0000000000 65535 f \n") - for offset in offsets[1:]: - out.extend(f"{offset:010d} 00000 n \n".encode()) - out.extend( - b"trailer\n<< /Size " - + str(len(objects) + 1).encode() - + b" /Root " - + str(catalog_id).encode() - + b" 0 R >>\nstartxref\n" - + str(xref).encode() - + b"\n%%EOF\n", - ) - path.write_bytes(out) - - -def header(page: Page, title: str, page_no: int) -> None: - page.line(MARGIN, 748, 210, 748, color="111111", width=3) - page.text(MARGIN, 724, title, size=28, font="F2") - page.text(470, 748, "SEED benchmarking", size=10, font="F2", color="256FA6") - page.text(560, 34, page_no, size=12, font="F2") - - -def table(page: Page, x: float, y: float, rows: list[tuple[str, str]], widths: tuple[float, float] = (180, 320)) -> float: - row_h = 21 - for i, (key, value) in enumerate(rows): - fill = "F4F7FA" if i % 2 == 0 else "FFFFFF" - page.rect(x, y - row_h + 4, sum(widths), row_h, fill=fill) - page.text(x + 8, y - 11, key, size=9, font="F2", color="3F4854") - page.text(x + widths[0] + 8, y - 11, value, size=9, color="111111") - y -= row_h - return y - - -def bar_chart(page: Page, x: float, y: float, w: float, h: float, labels: list[str], values: list[float], color: str) -> None: - max_value = max(values) if values else 1 - page.line(x, y, x, y + h, color="222222", width=0.8) - page.line(x, y, x + w, y, color="222222", width=0.8) - bar_gap = 5 - bar_w = (w - bar_gap * (len(values) - 1)) / max(len(values), 1) - for i, value in enumerate(values): - bh = 0 if max_value == 0 else (value / max_value) * h - bx = x + i * (bar_w + bar_gap) - page.rect(bx, y, bar_w, bh, fill=color) - page.text(bx - 2, y - 16, labels[i], size=7, color="333333") - for frac in [0.25, 0.5, 0.75, 1.0]: - gy = y + h * frac - page.line(x, gy, x + w, gy, color="D3D9DF", width=0.4) - page.text(x + w + 6, gy - 3, fmt_num(max_value * frac), size=7, color="666666") - - -def benchmark_axis(page: Page, x: float, y: float, w: float) -> None: - points = [ - ("Median", 51.2, "5A6C7D"), - ("p75", 70.0, "5A6C7D"), - ("p95", 117.4, "E2A12B"), - ("Target", 4192.8, "C3362B"), - ] - label_offsets = { - "Median": (-26, 24, -26, -24), - "p75": (-5, 47, -5, -41), - "p95": (14, 24, 14, -24), - "Target": (-18, 24, -18, -24), - } - max_axis = 4500 - page.line(x, y, x + w, y, color="222222", width=1) - for label, value, color in points: - px = x + (value / max_axis) * w - page.line(px, y - 8, px, y + 20, color=color, width=2.2) - if label == "p75": - continue - label_dx, label_dy, value_dx, value_dy = label_offsets[label] - page.text(px + label_dx, y + label_dy, label, size=8, font="F2", color=color) - page.text(px + value_dx, y + value_dy, fmt_num(value, 1), size=8, color=color) - page.text(x, y - 44, "Site EUI scale, kBtu/ft2/year. Target building is far right because it is the maximum observed value.", size=8, color="555555") - - -def bullet(page: Page, x: float, y: float, text: str, max_chars: int = 82) -> float: - page.rect(x, y - 2, 4, 4, fill="256FA6") - return page.multiline(x + 14, y - 4, text, size=10, max_chars=max_chars, leading=14) - - -def build_report() -> PDF: - pdf = PDF() - - monthly = [ - ("Jan", 1921.79, 9272393.5), - ("Feb", 1845.10, 8375065.1), - ("Mar", 2596.74, 9272393.5), - ("Apr", 3837.13, 8973284.04), - ("May", 5330.18, 9272393.5), - ("Jun", 4086.74, 8973284.04), - ("Jul", 2551.17, 9272393.5), - ("Aug", 2519.33, 9272393.5), - ("Sep", 3027.92, 8973284.04), - ("Oct", 3152.20, 9272393.5), - ("Nov", 4705.63, 8973284.04), - ("Dec", 4715.19, 8973284.04), - ] - electric_kwh = sum(row[1] for row in monthly) - electric_kbtu = electric_kwh * 3.412141633 - hot_kbtu = sum(row[2] for row in monthly) - total_kbtu = electric_kbtu + hot_kbtu - reported_area = 26000 - seed_area = 28050 - - page = pdf.add_page() - page.rect(0, 0, PAGE_W, PAGE_H, fill="F7F9FB") - page.rect(0, 560, PAGE_W, 232, fill="184D73") - page.rect(0, 560, PAGE_W, 10, fill="E2A12B") - page.text(MARGIN, 705, "ENERGY AUDIT", size=44, font="F2", color="FFFFFF") - page.text(MARGIN, 660, "SCREENING REPORT", size=38, font="F2", color="FFFFFF") - page.text(MARGIN, 620, "2258 25TH PLACE NE", size=21, font="F2", color="FFFFFF") - page.text(MARGIN, 596, "Washington, DC 20018 | 2022 benchmarking cycle", size=12, color="DDEAF2") - page.text(420, 740, "SEED", size=18, font="F2", color="FFFFFF") - page.text(420, 720, "benchmarking analysis", size=9, color="DDEAF2") - page.multiline(MARGIN, 520, "Prepared from SEED property, benchmarking, and meter data. This is a screening audit, not an onsite ASHRAE audit.", size=11, max_chars=96, leading=15, color="333333") - page.pill(MARGIN, 445, 156, 58, "Reported Site EUI", "4,192.8", "FFFFFF", "C3362B") - page.pill(MARGIN + 174, 445, 156, 58, "p95 Site EUI", "117.4", "FFFFFF", "E2A12B") - page.pill(MARGIN + 348, 445, 156, 58, "Rank", "1 of 2,750", "FFFFFF", "256FA6") - page.pill(MARGIN, 360, 156, 58, "Property type", "Warehouse", "FFFFFF", "256FA6") - page.pill(MARGIN + 174, 360, 156, 58, "Year built", "1950", "FFFFFF", "256FA6") - page.pill(MARGIN + 348, 360, 156, 58, "Ward", "5", "FFFFFF", "256FA6") - page.text(MARGIN, 260, "Primary issue to investigate", size=16, font="F2") - page.multiline(MARGIN, 235, "District hot water dominates the reported energy use. The meter records show about 108.9 million kBtu of district hot water and only about 40,289 kWh of electric use. This pattern is unusual for a non-refrigerated warehouse and should be reconciled before any capital work is scoped.", size=12, max_chars=78, leading=17) - page.text(MARGIN, 84, "Prepared July 8, 2026", size=9, color="555555") - page.text(420, 84, "Org 412 | Cycle 656", size=9, color="555555") - - page = pdf.add_page() - header(page, "Executive Summary", 1) - page.multiline(MARGIN, 675, "2258 25th Place NE is the highest Site EUI property in the 2022 DC benchmarking cycle among properties with non-null Site EUI. Its reported Site EUI is 4,192.8 kBtu/ft2/year, compared with a cycle median of 51.2 and a 95th percentile threshold of 117.4.", size=12, max_chars=83, leading=17) - y = 595 - y = bullet(page, MARGIN, y, "The property is listed as a Non-Refrigerated Warehouse, 28,050 ft2 in SEED, with a separate reported gross floor area of 26,000 ft2 in imported extra data.") - y = bullet(page, MARGIN, y - 12, "Reported status is Data Under Review by DOEE; ENERGY STAR score, story count, and onsite system details are not available in SEED.") - y = bullet(page, MARGIN, y - 12, "The meter data includes Electric - Grid and District Hot Water. District hot water accounts for roughly 99.9% of meter-derived site energy.") - y = bullet(page, MARGIN, y - 12, "Using the reported 26,000 ft2 area, meter-derived site energy reconciles to about 4,193 kBtu/ft2/year. Using the SEED canonical 28,050 ft2 area, it is about 3,886 kBtu/ft2/year.") - page.text(MARGIN, 365, "Key Quantities", size=15, font="F2") - table(page, MARGIN, 340, [ - ("PM Property ID", "PM26961358"), - ("Property view ID", "3285534"), - ("Reported Site EUI", "4,192.8 kBtu/ft2/year"), - ("Source EUI", "5,052.6 kBtu/ft2/year"), - ("Total GHG emissions", "7,241.2 mtCO2e"), - ("Annual district hot water", f"{fmt_num(hot_kbtu)} kBtu"), - ("Annual electricity", f"{fmt_num(electric_kwh)} kWh"), - ("Meter-derived total site energy", f"{fmt_num(total_kbtu)} kBtu"), - ]) - - page = pdf.add_page() - header(page, "Building Profile", 2) - table(page, MARGIN, 675, [ - ("Property name", "De Paris Enterprises Inc"), - ("Address", "2258 25TH PLACE NE, Washington, DC 20018"), - ("Owner", "DEPARIS REBECCA C"), - ("Property type", "Non-Refrigerated Warehouse"), - ("Year built", "1950"), - ("Ward / census tract", "Ward 5 / 11001011100"), - ("Latitude / longitude", "38.92138514 / -76.97164794"), - ("SEED gross floor area", "28,050 ft2"), - ("Reported gross floor area", "26,000 ft2"), - ("Metered areas, energy", "Whole Property"), - ("Water use", "114.9"), - ("Disadvantaged community flag", "False"), - ("Low income flag", "False"), - ]) - page.text(MARGIN, 350, "Benchmark Context", size=15, font="F2") - page.multiline(MARGIN, 326, "Comparison set: 2,750 properties in the 2022 cycle with non-null Site EUI. The building is the maximum observed Site EUI in that set. Because the source record is under review, this should be treated as a priority data and meter-boundary investigation.", size=10, max_chars=88, leading=14) - benchmark_axis(page, MARGIN, 245, 500) - page.pill(MARGIN, 115, 156, 58, "Median", "51.2", "F4F7FA", "5A6C7D") - page.pill(MARGIN + 174, 115, 156, 58, "p95", "117.4", "F4F7FA", "E2A12B") - page.pill(MARGIN + 348, 115, 156, 58, "Building", "4,192.8", "F4F7FA", "C3362B") - - page = pdf.add_page() - header(page, "Meter Analysis", 3) - page.text(MARGIN, 675, "Meters Found", size=15, font="F2") - table(page, MARGIN, 650, [ - ("18966", "Electric - Grid | Manual Entry | PM26961358"), - ("18967", "District Hot Water | Manual Entry | PM26961358"), - ]) - page.text(MARGIN, 570, "Annual Fuel Mix", size=15, font="F2") - mix_x, mix_y = MARGIN, 525 - page.rect(mix_x, mix_y, 500, 28, fill="DDEAF2") - elec_w = max(3, 500 * electric_kbtu / total_kbtu) - page.rect(mix_x, mix_y, elec_w, 28, fill="2F80ED") - page.rect(mix_x + elec_w, mix_y, 500 - elec_w, 28, fill="F2B13F") - page.text(mix_x, mix_y - 18, "Electricity: 0.13% of site energy after kWh-to-kBtu conversion", size=8, color="2F80ED") - page.text(mix_x + 270, mix_y - 18, "District hot water: 99.87%", size=8, color="9B660F") - page.text(MARGIN, 470, "Monthly Electricity Use (kWh)", size=12, font="F2") - bar_chart(page, MARGIN + 15, 315, 430, 130, [m[0] for m in monthly], [m[1] for m in monthly], "2F80ED") - page.text(MARGIN, 275, "Monthly District Hot Water (kBtu)", size=12, font="F2") - bar_chart(page, MARGIN + 15, 120, 430, 130, [m[0] for m in monthly], [m[2] for m in monthly], "F2B13F") - page.multiline(MARGIN, 78, "The district hot water profile is nearly flat and extremely large month to month. That points first to meter mapping, units, service boundary, or area normalization review; operational heating diagnostics come after the data is reconciled.", size=9, max_chars=92, leading=12) - - page = pdf.add_page() - header(page, "Preliminary Measures", 4) - page.multiline(MARGIN, 675, "These measures are screening recommendations based on SEED and meter data only. They should be confirmed through utility bills, meter configuration, operator interviews, drawings, and an onsite walkthrough.", size=11, max_chars=86, leading=15) - y = 610 - measures = [ - ("1. Reconcile data before scoping retrofits", "Verify district hot water units, meter ownership, service boundary, and whether the meter serves only this property. Confirm whether 26,000 ft2 or 28,050 ft2 should be used for benchmarking."), - ("2. Investigate district hot water load", "The dominant load is district hot water, not electric use. Review heat exchanger controls, valve leakage, simultaneous heating/cooling, domestic hot water recirculation, and any process or tenant loads."), - ("3. Review schedules and setpoints", "If the hot water load is legitimate, check occupied/unoccupied schedules, temperature reset, night setback, and weekend operation for warehouse spaces."), - ("4. Envelope and loading-door leakage", "For warehouse use, inspect overhead doors, vestibules, dock seals, roof insulation, and uncontrolled infiltration paths that can drive heating demand."), - ("5. Lighting and plug/process loads", "Electricity is small relative to thermal energy, but LED lighting, controls, and tenant process-load review remain practical low-disruption measures."), - ("6. Add submetering or meter QA workflow", "A single unusually large thermal stream deserves ongoing meter QA, especially if the property remains under review in the benchmarking program."), - ] - for title, body in measures: - page.text(MARGIN, y, title, size=12, font="F2", color="184D73") - y = page.multiline(MARGIN + 18, y - 18, body, size=10, max_chars=82, leading=14) - y -= 16 - - page = pdf.add_page() - header(page, "Data Gaps", 5) - page.text(MARGIN, 675, "Available in SEED", size=15, font="F2") - y = 645 - for item in [ - "Address, location, owner, ward, parcel/lot, property type, year built, floor area, reporting status.", - "Reported Site EUI, weather-normalized Site EUI, Source EUI, total GHG emissions, and water use extra data.", - "Monthly imported meter records for Electric - Grid and District Hot Water for calendar year 2022.", - ]: - y = bullet(page, MARGIN, y, item) - y -= 8 - page.text(MARGIN, 520, "Not available in SEED for this property", size=15, font="F2") - y = 490 - for item in [ - "ENERGY STAR score, number of stories, building count, conditioned floor area, building systems, equipment age, controls sequences, and operating schedules.", - "Audit photos, onsite observations, utility tariff costs, comfort/maintenance complaints, and capital cost estimates.", - "A confirmed explanation for why the canonical gross floor area and reported gross floor area differ.", - ]: - y = bullet(page, MARGIN, y, item) - y -= 8 - page.text(MARGIN, 365, "Recommended Next Steps", size=15, font="F2") - table(page, MARGIN, 340, [ - ("1", "Pull original utility bills or Portfolio Manager export for PM26961358."), - ("2", "Confirm whether the district hot water meter is whole-property, shared, or misassigned."), - ("3", "Confirm the correct gross floor area and update SEED if needed."), - ("4", "Perform a focused walkthrough of thermal systems, controls, and warehouse envelope conditions."), - ("5", "After data reconciliation, estimate savings and costs for the highest-confidence measures."), - ], widths=(35, 465)) - page.multiline(MARGIN, 145, "Screening conclusion: this property is less a normal high-EUI case than a data-reconciliation and thermal-meter-boundary case. If the district hot water readings are valid and assigned correctly, the building warrants a focused thermal systems audit.", size=11, max_chars=88, leading=15) - - return pdf - - -if __name__ == "__main__": - out = Path("output/pdf/2258_25th_place_ne_energy_audit_screening_report.pdf") - out.parent.mkdir(parents=True, exist_ok=True) - build_report().save(out) - print(out) diff --git a/output/create_better_owner_audit_pdfs.py b/output/create_better_owner_audit_pdfs.py deleted file mode 100644 index f5131d7..0000000 --- a/output/create_better_owner_audit_pdfs.py +++ /dev/null @@ -1,994 +0,0 @@ -from __future__ import annotations - -import json -import re -from pathlib import Path - - -PAGE_W = 612 -PAGE_H = 792 -MARGIN = 42 -DATA_PATH = Path("org412_cycle656_profile316_better.json") -OUT_DIR = Path("output/pdf/better_owner_audits") -METER_DIR = Path("output/data/top5_meters") -IMAGE_DIR = Path("output/images") -BUILDING_IMAGE_PATHS = { - 3282590: IMAGE_DIR / "1620_i_street_osm_3d.jpg", -} -OPENSTUDIO_DIR = Path("output/openstudio_1620") -OPENSTUDIO_MODEL_PATHS = { - 3282590: { - "calibration_image": OPENSTUDIO_DIR / "1620_i_street_openstudio_calibration.jpg", - "zoning_image": OPENSTUDIO_DIR / "1620_i_street_perimeter_core_10_story.jpg", - "calibration_json": OPENSTUDIO_DIR / "1620_i_street_openstudio_calibration.json", - }, -} - - -def esc(text: object) -> str: - value = "" if text is None else str(text) - return value.replace("\\", "\\\\").replace("(", "\\(").replace(")", "\\)") - - -def safe_name(text: object) -> str: - value = re.sub(r"[^a-zA-Z0-9]+", "_", str(text).strip().lower()) - return re.sub(r"_+", "_", value).strip("_") or "building" - - -def display_name(column_name: str) -> str: - name = re.sub(r"_\d+$", "", column_name) - name = name.removeprefix("better_recommendation_") - return name.replace("_", " ") - - -def active(value: object) -> bool: - if value in (None, ""): - return False - try: - return float(value) != 0 - except (TypeError, ValueError): - return str(value).strip().casefold() in {"true", "yes", "y", "1"} - - -def num(value: object, digits: int = 0, prefix: str = "", suffix: str = "") -> str: - if value in (None, ""): - return "-" - try: - number = float(value) - except (TypeError, ValueError): - return str(value) - if digits == 0: - return f"{prefix}{number:,.0f}{suffix}" - return f"{prefix}{number:,.{digits}f}{suffix}" - - -def to_float(value: object) -> float | None: - if value in (None, ""): - return None - try: - return float(value) - except (TypeError, ValueError): - return None - - -def wrap(text: str, max_chars: int) -> list[str]: - words = str(text).split() - lines: list[str] = [] - current = "" - for word in words: - candidate = word if not current else f"{current} {word}" - if len(candidate) <= max_chars: - current = candidate - else: - if current: - lines.append(current) - current = word - if current: - lines.append(current) - return lines - - -def jpeg_dimensions(path: Path) -> tuple[int, int]: - data = path.read_bytes() - index = 2 - while index < len(data): - if data[index] != 0xFF: - index += 1 - continue - marker = data[index + 1] - index += 2 - if marker in {0xD8, 0xD9}: - continue - length = int.from_bytes(data[index : index + 2], "big") - if marker in {0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF}: - height = int.from_bytes(data[index + 3 : index + 5], "big") - width = int.from_bytes(data[index + 5 : index + 7], "big") - return width, height - index += length - raise ValueError(f"Could not read JPEG dimensions for {path}") - - -class Page: - def __init__(self) -> None: - self.ops: list[str] = [] - self.images: list[Path] = [] - - def raw(self, op: str) -> None: - self.ops.append(op) - - def color(self, hex_color: str) -> None: - hex_color = hex_color.strip("#") - r = int(hex_color[0:2], 16) / 255 - g = int(hex_color[2:4], 16) / 255 - b = int(hex_color[4:6], 16) / 255 - self.raw(f"{r:.4f} {g:.4f} {b:.4f} rg") - self.raw(f"{r:.4f} {g:.4f} {b:.4f} RG") - - def line_width(self, width: float) -> None: - self.raw(f"{width:.2f} w") - - def rect(self, x: float, y: float, w: float, h: float, fill: str | None = None, stroke: str | None = None) -> None: - if fill: - self.color(fill) - self.raw(f"{x:.2f} {y:.2f} {w:.2f} {h:.2f} re f") - if stroke: - self.color(stroke) - self.raw(f"{x:.2f} {y:.2f} {w:.2f} {h:.2f} re S") - - def line(self, x1: float, y1: float, x2: float, y2: float, color: str = "111111", width: float = 1) -> None: - self.color(color) - self.line_width(width) - self.raw(f"{x1:.2f} {y1:.2f} m {x2:.2f} {y2:.2f} l S") - - def text(self, x: float, y: float, text: object, size: int = 10, font: str = "F1", color: str = "111111") -> None: - self.color(color) - self.raw(f"BT /{font} {size} Tf {x:.2f} {y:.2f} Td ({esc(text)}) Tj ET") - - def multiline( - self, - x: float, - y: float, - text: str, - size: int = 10, - max_chars: int = 80, - leading: float | None = None, - font: str = "F1", - color: str = "111111", - ) -> float: - leading = leading or size * 1.35 - for line in wrap(text, max_chars): - self.text(x, y, line, size=size, font=font, color=color) - y -= leading - return y - - def metric(self, x: float, y: float, w: float, label: str, value: str, accent: str) -> None: - self.rect(x, y, w, 58, fill="FFFFFF", stroke="D7DEE8") - self.rect(x, y, 5, 58, fill=accent) - self.text(x + 14, y + 38, label.upper(), size=8, font="F2", color="52616B") - self.text(x + 14, y + 13, value, size=17, font="F2", color="111827") - - def image(self, x: float, y: float, w: float, h: float, path: Path) -> None: - self.images.append(path) - name = f"Im{len(self.images)}" - self.raw(f"q {w:.2f} 0 0 {h:.2f} {x:.2f} {y:.2f} cm /{name} Do Q") - - -class PDF: - def __init__(self) -> None: - self.pages: list[Page] = [] - - def add_page(self) -> Page: - page = Page() - self.pages.append(page) - return page - - def save(self, path: Path) -> None: - objects: list[bytes] = [ - b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>", - b"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica-Bold >>", - ] - page_objects: list[tuple[int, int, list[tuple[str, int]]]] = [] - for page in self.pages: - image_refs: list[tuple[str, int]] = [] - for index, image_path in enumerate(page.images, start=1): - image_data = image_path.read_bytes() - image_width, image_height = jpeg_dimensions(image_path) - image_id = len(objects) + 1 - image_refs.append((f"Im{index}", image_id)) - objects.append( - b"<< /Type /XObject /Subtype /Image /Width " - + str(image_width).encode() - + b" /Height " - + str(image_height).encode() - + b" /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length " - + str(len(image_data)).encode() - + b" >>\nstream\n" - + image_data - + b"\nendstream", - ) - stream = "\n".join(page.ops).encode("latin-1", "replace") - content = b"<< /Length " + str(len(stream)).encode() + b" >>\nstream\n" + stream + b"\nendstream" - content_id = len(objects) + 1 - objects.append(content) - page_id = len(objects) + 1 - page_objects.append((page_id, content_id, image_refs)) - objects.append(b"") - - pages_id = len(objects) + 1 - kids = " ".join(f"{page_id} 0 R" for page_id, _, _ in page_objects).encode() - objects.append(b"<< /Type /Pages /Kids [" + kids + b"] /Count " + str(len(page_objects)).encode() + b" >>") - catalog_id = len(objects) + 1 - objects.append(b"<< /Type /Catalog /Pages " + str(pages_id).encode() + b" 0 R >>") - - for page_id, content_id, image_refs in page_objects: - xobjects = b"" - if image_refs: - pairs = " ".join(f"/{name} {obj_id} 0 R" for name, obj_id in image_refs).encode() - xobjects = b" /XObject << " + pairs + b" >>" - objects[page_id - 1] = ( - b"<< /Type /Page /Parent " - + str(pages_id).encode() - + b" 0 R /MediaBox [0 0 612 792] " - + b"/Resources << /Font << /F1 1 0 R /F2 2 0 R >>" - + xobjects - + b" >> " - + b"/Contents " - + str(content_id).encode() - + b" 0 R >>" - ) - - out = bytearray(b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n") - offsets = [0] - for obj_id, obj in enumerate(objects, start=1): - offsets.append(len(out)) - out.extend(f"{obj_id} 0 obj\n".encode()) - out.extend(obj) - out.extend(b"\nendobj\n") - xref = len(out) - out.extend(f"xref\n0 {len(objects) + 1}\n".encode()) - out.extend(b"0000000000 65535 f \n") - for offset in offsets[1:]: - out.extend(f"{offset:010d} 00000 n \n".encode()) - out.extend( - b"trailer\n<< /Size " - + str(len(objects) + 1).encode() - + b" /Root " - + str(catalog_id).encode() - + b" 0 R >>\nstartxref\n" - + str(xref).encode() - + b"\n%%EOF\n", - ) - path.parent.mkdir(parents=True, exist_ok=True) - path.write_bytes(out) - - -RECOMMENDATION_NOTES = { - "reduce lighting load": "Review fixture types, controls, schedules, common areas, parking, and tenant lighting density.", - "reduce plug loads": "Inventory tenant equipment, common plug loads, vending, IT/server loads, and after-hours usage.", - "reduce equipment schedules": "Confirm occupied/unoccupied schedules, weekend operation, overrides, and BAS trend logs.", - "decrease heating setpoints": "Review winter setpoints, night setback, warm-up routines, and simultaneous heating/cooling.", - "increase cooling setpoints": "Review summer setpoints, deadbands, after-hours cooling, and tenant comfort constraints.", - "decrease infiltration": "Inspect entry vestibules, loading doors, envelope leakage, shafts, stair pressurization, and exhaust imbalance.", - "increase cooling system efficiency": "Review cooling equipment age, economizer operation, resets, condenser cleaning, and controls.", - "increase heating system efficiency": "Review boilers, heat pumps, steam/hot water distribution, reset schedules, and maintenance records.", - "add wall/ceiling/roof insulation": "Screen envelope assemblies and roof condition before scoping envelope measures.", - "upgrade windows to improve thermal efficiency": "Review window U-factor, air leakage, condensation, comfort complaints, and replacement timing.", - "upgrade windows to reduce solar heat gain": "Review glazing SHGC, facade orientation, solar control, and cooling complaints.", - "ensure adequate ventilation rate": "Verify outdoor air rates, demand-control ventilation, economizer minimum positions, and code constraints.", - "use high efficiency heat pump for heating": "Evaluate electrification feasibility, service capacity, distribution constraints, and refrigerant strategy.", - "upgrade to sustainable resources for water heating": "Review domestic hot water loads, heat pump water heating, solar thermal, and heat recovery options.", -} - - -def load_ranked_buildings() -> list[dict]: - rows = json.loads(DATA_PATH.read_text())["656"] - rec_cols = [key for key in rows[0] if key.startswith("better_recommendation_")] - ranked = [] - for row in rows: - recs = [display_name(col) for col in rec_cols if active(row.get(col))] - ranked.append( - { - "rank_score": len(recs), - "property_view_id": row.get("property_view_id"), - "property_state_id": row.get("property_state_id"), - "pm_property_id": row.get("pm_property_id_90594"), - "address": row.get("address_line_1_90603") or row.get("Reported Address_90689"), - "city": row.get("city_90607"), - "state": row.get("state_90609"), - "postal_code": row.get("postal_code_90613"), - "name": row.get("property_name_90616") or row.get("address_line_1_90603"), - "type": row.get("property_type_90633") or row.get("Property Tyoe (EPA)_90696"), - "gfa": row.get("gross_floor_area_90629") or row.get("gross_floor_area_reported_90699"), - "site_eui": row.get("site_eui_weather_normalized_90650") or row.get("site_eui_90649"), - "source_eui": row.get("source_eui_weather_normalized_90653") or row.get("source_eui_90652"), - "energy_score": row.get("energy_score_90631"), - "year_built": row.get("year_built_90639"), - "ward": row.get("Ward_90687"), - "reporting_status": row.get("Reporting Status_90683"), - "metered_areas": row.get("Metered Areas (Energy)_90716"), - "longitude": row.get("Unused X_90667"), - "latitude": row.get("Unused Y_90669"), - "long_lat": row.get("long_lat"), - "footprint": row.get("property_footprint_90623") or row.get("centroid"), - "bounding_box": row.get("bounding_box"), - "cost_savings": row.get("better_cost_savings_combined_90835"), - "energy_savings": row.get("better_energy_savings_combined_90836"), - "ghg_reductions": row.get("better_ghg_reductions_combined_90837"), - "valid_electric": row.get("better_valid_model_electricity_90838"), - "valid_fuel": row.get("better_valid_model_fuel_90839"), - "min_r2": row.get("better_min_model_r_squared_90848"), - "recommendations": recs, - }, - ) - ranked.sort(key=lambda item: (item["rank_score"], item["cost_savings"] or -1), reverse=True) - return ranked - - -def load_meter_summary(property_view_id: int) -> dict: - meters_path = METER_DIR / f"{property_view_id}_meters.json" - if not meters_path.exists(): - return {"meters": [], "monthly": [], "fuel_totals": {}, "annual_total": 0} - - meters = json.loads(meters_path.read_text()) - fuel_totals: dict[str, float] = {} - monthly: dict[str, float] = {} - meter_rows = [] - for meter in meters: - meter_id = meter["id"] - readings_path = METER_DIR / f"{property_view_id}_meter_{meter_id}_readings.json" - readings = json.loads(readings_path.read_text()) if readings_path.exists() else [] - annual = 0.0 - for reading in readings: - start = str(reading.get("start_time", "")) - if not start.startswith("2022"): - continue - value = to_float(reading.get("reading")) or 0.0 - annual += value - month = start[5:7] - monthly[month] = monthly.get(month, 0.0) + value - fuel_type = meter.get("type") or "Unknown" - fuel_totals[fuel_type] = fuel_totals.get(fuel_type, 0.0) + annual - meter_rows.append( - { - "id": meter_id, - "type": fuel_type, - "alias": meter.get("alias") or f"Meter {meter_id}", - "readings": len(readings), - "annual": annual, - }, - ) - - ordered_months = [ - ("Jan", monthly.get("01", 0.0)), - ("Feb", monthly.get("02", 0.0)), - ("Mar", monthly.get("03", 0.0)), - ("Apr", monthly.get("04", 0.0)), - ("May", monthly.get("05", 0.0)), - ("Jun", monthly.get("06", 0.0)), - ("Jul", monthly.get("07", 0.0)), - ("Aug", monthly.get("08", 0.0)), - ("Sep", monthly.get("09", 0.0)), - ("Oct", monthly.get("10", 0.0)), - ("Nov", monthly.get("11", 0.0)), - ("Dec", monthly.get("12", 0.0)), - ] - return { - "meters": meter_rows, - "monthly": ordered_months, - "fuel_totals": fuel_totals, - "annual_total": sum(fuel_totals.values()), - } - - -def parse_point(wkt: object) -> tuple[float, float] | None: - match = re.search(r"POINT\s*\(\s*([-\d.]+)\s+([-\d.]+)\s*\)", str(wkt or "")) - if not match: - return None - return float(match.group(1)), float(match.group(2)) - - -def parse_polygon(wkt: object) -> list[tuple[float, float]]: - match = re.search(r"POLYGON\s*\(\((.*?)\)\)", str(wkt or "")) - if not match: - return [] - points = [] - for pair in match.group(1).split(","): - parts = pair.strip().split() - if len(parts) >= 2: - points.append((float(parts[0]), float(parts[1]))) - return points - - -def google_maps_3d_url(building: dict) -> str: - lat = to_float(building.get("latitude")) - lon = to_float(building.get("longitude")) - point = parse_point(building.get("long_lat")) - if point and (lat is None or lon is None): - lon, lat = point - if lat is None or lon is None: - query = str(building.get("address") or "").replace(" ", "+") - return f"https://www.google.com/maps/search/?api=1&query={query}" - return f"https://www.google.com/maps/@{lat:.7f},{lon:.7f},20z/data=!3m1!1e3" - - -def header(page: Page, title: str, subtitle: str, page_no: int) -> None: - page.rect(0, 746, PAGE_W, 46, fill="0B94CF") - page.rect(360, 746, 252, 46, fill="087CB2") - page.text(MARGIN, 764, title, size=19, font="F2", color="FFFFFF") - page.text(MARGIN, 750, subtitle, size=9, color="EAF7FC") - page.text(520, 764, "SEED", size=15, font="F2", color="FFFFFF") - page.text(520, 750, f"page {page_no}", size=8, color="EAF7FC") - - -def small_table(page: Page, x: float, y: float, rows: list[tuple[str, str]], key_w: float = 145, val_w: float = 365) -> float: - row_h = 21 - for i, (key, value) in enumerate(rows): - page.rect(x, y - row_h + 4, key_w + val_w, row_h, fill="F4F7FA" if i % 2 == 0 else "FFFFFF") - page.text(x + 8, y - 11, key, size=8, font="F2", color="3F4854") - page.text(x + key_w + 8, y - 11, value, size=8, color="111827") - y -= row_h - return y - - -def bullet(page: Page, x: float, y: float, text: str, max_chars: int = 82, color: str = "0B94CF") -> float: - page.rect(x, y - 3, 4, 4, fill=color) - return page.multiline(x + 13, y - 5, text, size=9, max_chars=max_chars, leading=12) - - -def bar_chart(page: Page, x: float, y: float, w: float, h: float, labels: list[str], values: list[float], color: str) -> None: - max_value = max(values) if values else 1 - max_value = max(max_value, 1) - page.line(x, y, x, y + h, color="334155", width=0.7) - page.line(x, y, x + w, y, color="334155", width=0.7) - gap = 5 - bar_w = (w - gap * (len(values) - 1)) / max(len(values), 1) - for index, value in enumerate(values): - bx = x + index * (bar_w + gap) - bh = h * value / max_value - page.rect(bx, y, bar_w, bh, fill=color) - page.text(bx - 1, y - 14, labels[index], size=6, color="334155") - page.text(x + w + 8, y + h - 4, num(max_value, 0), size=7, color="52616B") - page.text(x + w + 8, y - 2, "0", size=7, color="52616B") - - -def stacked_fuel_bar(page: Page, x: float, y: float, w: float, h: float, fuel_totals: dict[str, float]) -> None: - colors = { - "Electric - Grid": "0B94CF", - "Natural Gas": "D9901A", - "District Hot Water": "B42318", - "District Chilled Water": "2563A6", - "Custom Meter": "7C3AED", - } - total = sum(fuel_totals.values()) or 1 - page.rect(x, y, w, h, fill="DDEAF2") - cursor = x - for fuel, value in sorted(fuel_totals.items(), key=lambda item: item[1], reverse=True): - width = w * value / total - page.rect(cursor, y, width, h, fill=colors.get(fuel, "52616B")) - cursor += width - label_y = y - 18 - label_x = x - for fuel, value in sorted(fuel_totals.items(), key=lambda item: item[1], reverse=True)[:4]: - page.rect(label_x, label_y + 2, 7, 7, fill=colors.get(fuel, "52616B")) - page.text(label_x + 11, label_y, f"{fuel}: {value / total:.0%}", size=7, color="334155") - label_x += 130 - - -def draw_location_panel(page: Page, building: dict, x: float, y: float, w: float, h: float) -> None: - lat = to_float(building.get("latitude")) - lon = to_float(building.get("longitude")) - point = parse_point(building.get("long_lat")) - if point and (lat is None or lon is None): - lon, lat = point - footprint = parse_polygon(building.get("footprint")) - bbox = parse_polygon(building.get("bounding_box")) - - page.rect(x, y, w, h, fill="EAF7FC", stroke="B8D9E8") - for i in range(1, 6): - gx = x + w * i / 6 - gy = y + h * i / 6 - page.line(gx, y, gx, y + h, color="C8E5F0", width=0.4) - page.line(x, gy, x + w, gy, color="C8E5F0", width=0.4) - - page.rect(x + 24, y + 22, w - 48, h - 44, fill="DDEAF2", stroke="FFFFFF") - page.line(x + 24, y + 70, x + w - 24, y + h - 60, color="FFFFFF", width=8) - page.line(x + 70, y + 22, x + w - 70, y + h - 22, color="FFFFFF", width=6) - page.line(x + 30, y + h - 86, x + w - 34, y + h - 40, color="9ECFE4", width=4) - - map_points = footprint or bbox - if map_points: - min_lon = min(p[0] for p in map_points) - max_lon = max(p[0] for p in map_points) - min_lat = min(p[1] for p in map_points) - max_lat = max(p[1] for p in map_points) - if max_lon == min_lon: - max_lon += 0.0001 - min_lon -= 0.0001 - if max_lat == min_lat: - max_lat += 0.0001 - min_lat -= 0.0001 - sx = (w - 110) / (max_lon - min_lon) - sy = (h - 96) / (max_lat - min_lat) - coords = [] - for px, py in map_points: - mx = x + 55 + (px - min_lon) * sx - my = y + 48 + (py - min_lat) * sy - coords.append((mx, my)) - if len(coords) >= 3: - page.color("0B94CF") - page.raw(f"{coords[0][0]:.2f} {coords[0][1]:.2f} m") - for px, py in coords[1:]: - page.raw(f"{px:.2f} {py:.2f} l") - page.raw("h f") - page.color("183F6D") - page.raw(f"{coords[0][0] + 10:.2f} {coords[0][1] + 14:.2f} m") - for px, py in coords[1:]: - page.raw(f"{px + 10:.2f} {py + 14:.2f} l") - page.raw("h f") - page.color("1B6257") - page.raw(f"{coords[0][0]:.2f} {coords[0][1]:.2f} m") - for px, py in coords[:4]: - page.raw(f"{px + 10:.2f} {py + 14:.2f} l") - page.raw("S") - else: - cx, cy = x + w / 2, y + h / 2 - page.rect(cx - 24, cy - 16, 48, 32, fill="0B94CF") - page.rect(cx - 14, cy - 6, 48, 32, fill="183F6D") - page.rect(cx - 4, cy + 4, 48, 32, fill="1B6257") - - page.rect(x + w / 2 - 4, y + h / 2 - 4, 8, 8, fill="B42318") - if lat is not None and lon is not None: - page.text(x + 14, y + 12, f"{lat:.6f}, {lon:.6f}", size=8, color="334155") - - -def draw_recommendation_bar(page: Page, count: int) -> None: - x, y, w, h = MARGIN, 322, 510, 16 - page.rect(x, y, w, h, fill="DDEAF2") - page.rect(x, y, w * min(count, 12) / 12, h, fill="D9901A" if count >= 8 else "0B94CF") - for tick in [4, 8, 12]: - tx = x + w * tick / 12 - page.line(tx, y - 4, tx, y + h + 4, color="FFFFFF", width=1) - page.text(x, y + 24, "Active BETTER recommendations", size=8, font="F2", color="52616B") - - -def draw_cover(pdf: PDF, building: dict, rank: int) -> None: - page = pdf.add_page() - page.rect(0, 0, PAGE_W, PAGE_H, fill="FFFFFF") - page.rect(0, 560, PAGE_W, 232, fill="183F6D") - page.rect(0, 560, PAGE_W, 9, fill="D9901A") - page.text(MARGIN, 716, "OWNER AUDIT", size=42, font="F2", color="FFFFFF") - page.text(MARGIN, 674, "SCREENING REPORT", size=35, font="F2", color="FFFFFF") - page.multiline(MARGIN, 626, building["name"], size=19, max_chars=42, leading=22, font="F2", color="FFFFFF") - page.text(MARGIN, 594, f"{building['address']} | 2022 benchmarking cycle", size=11, color="DDEAF2") - page.text(466, 738, "SEED", size=18, font="F2", color="FFFFFF") - page.text(466, 718, "BETTER audit triage", size=8, color="DDEAF2") - - page.multiline( - MARGIN, - 525, - "Prepared from SEED and BETTER outputs. This screening packet helps owners evaluate candidate measures; it is not an onsite ASHRAE audit or engineering design.", - size=10, - max_chars=92, - leading=14, - color="334155", - ) - - page.metric(MARGIN, 445, 156, "Recommendation rank", f"#{rank}", "D9901A") - page.metric(MARGIN + 174, 445, 156, "Active recs", str(building["rank_score"]), "B42318") - page.metric(MARGIN + 348, 445, 156, "Cost savings", num(building["cost_savings"], 0, "$"), "1B6257") - page.metric(MARGIN, 360, 156, "Property type", str(building["type"])[:18], "0B94CF") - page.metric(MARGIN + 174, 360, 156, "Floor area", num(building["gfa"], 0, suffix=" ft2"), "0B94CF") - page.metric(MARGIN + 348, 360, 156, "Site EUI", num(building["site_eui"], 1), "0B94CF") - draw_recommendation_bar(page, building["rank_score"]) - - page.text(MARGIN, 302, "Owner evaluation focus", size=15, font="F2", color="111827") - y = 277 - y = bullet(page, MARGIN, y, f"Start with the {building['rank_score']} BETTER recommendations, then confirm which measures are feasible for this property's systems, leases, and capital plan.") - y = bullet(page, MARGIN, y - 10, "Validate utility data, meter boundaries, occupancy schedules, and controls before assigning capital budgets.") - y = bullet(page, MARGIN, y - 10, "Use this packet as a triage handoff: it identifies what to ask for, what to inspect, and which recommendations deserve owner review.") - - page.text(MARGIN, 92, "Prepared July 10, 2026", size=8, color="52616B") - page.text(402, 92, "Org 412 | Cycle 656 | BPS - DC", size=8, color="52616B") - - -def draw_detail(pdf: PDF, building: dict, rank: int) -> None: - page = pdf.add_page() - header(page, "Building Status + BETTER Recommendations", f"Rank #{rank}: {building['name']}", 2) - - page.text(MARGIN, 710, "Building snapshot", size=15, font="F2") - small_table( - page, - MARGIN, - 688, - [ - ("Address", str(building["address"])), - ("PM Property ID", str(building["pm_property_id"])), - ("Property view ID", str(building["property_view_id"])), - ("Type", str(building["type"])), - ("Year built / Ward", f"{num(building['year_built'])} / {building['ward'] or '-'}"), - ("Reporting status", str(building["reporting_status"] or "-")), - ("Metered areas", str(building["metered_areas"] or "-")), - ("Energy score", num(building["energy_score"], 0)), - ("Source EUI", num(building["source_eui"], 1)), - ], - ) - - page.text(MARGIN, 465, "BETTER outputs", size=15, font="F2") - small_table( - page, - MARGIN, - 443, - [ - ("Active recommendations", str(building["rank_score"])), - ("Combined energy savings", num(building["energy_savings"], 0, suffix=" kBtu")), - ("Combined cost savings", num(building["cost_savings"], 0, "$")), - ("GHG reductions", num(building["ghg_reductions"], 1, suffix=" mtCO2e")), - ("Electric model valid", str(building["valid_electric"])), - ("Fuel model valid", str(building["valid_fuel"])), - ("Minimum model R2", num(building["min_r2"], 2)), - ], - ) - - page.text(MARGIN, 260, "Priority recommendation notes", size=15, font="F2") - y = 236 - for index, rec in enumerate(building["recommendations"][:12], start=1): - note = RECOMMENDATION_NOTES.get(rec, "Review feasibility, operating constraints, savings estimate, and interaction with other measures.") - page.text(MARGIN, y, f"{index}. {rec}", size=10, font="F2", color="183F6D") - y = page.multiline(MARGIN + 18, y - 14, note, size=8, max_chars=82, leading=10, color="334155") - y -= 5 - if y < 54: - break - - -def draw_complete_recommendations(pdf: PDF, building: dict, rank: int) -> None: - page = pdf.add_page() - header(page, "Complete BETTER Recommendation List", f"Rank #{rank}: {building['name']}", 3) - page.multiline( - MARGIN, - 708, - f"This building has {building['rank_score']} active BETTER recommendation flags. Use this page as the owner's complete review list; the previous page includes detailed notes for the first priority items.", - size=11, - max_chars=88, - leading=15, - ) - - left_x = MARGIN - right_x = 320 - y_left = 642 - y_right = 642 - for index, rec in enumerate(building["recommendations"], start=1): - x = left_x if index <= 6 else right_x - y = y_left if index <= 6 else y_right - page.rect(x, y - 3, 5, 5, fill="D9901A") - next_y = page.multiline(x + 15, y, f"{index}. {rec}", size=11, max_chars=38, leading=14, font="F2", color="183F6D") - if index <= 6: - y_left = next_y - 16 - else: - y_right = next_y - 16 - - page.rect(MARGIN, 92, 510, 184, fill="F7F6F3", stroke="D7DEE8") - page.text(MARGIN + 16, 250, "How to use this list", size=12, font="F2", color="111827") - y = 226 - for item in [ - "Confirm whether each flag is operational, controls-related, envelope-related, or capital-project related.", - "Bundle interacting measures before estimating savings, especially HVAC setpoints, schedules, and equipment efficiency.", - "Ask the owner or operator which measures are already planned, recently completed, or infeasible because of tenant constraints.", - ]: - y = bullet(page, MARGIN + 16, y, item, max_chars=78, color="183F6D") - y -= 6 - - -def draw_meter_consumption(pdf: PDF, building: dict, rank: int) -> None: - page = pdf.add_page() - header(page, "2022 Meter Consumption", f"Rank #{rank}: {building['name']}", 4) - summary = load_meter_summary(building["property_view_id"]) - annual_total = summary["annual_total"] - gfa = to_float(building.get("gfa")) or 0.0 - intensity = annual_total / gfa if gfa else 0.0 - - page.multiline( - MARGIN, - 708, - "This page uses SEED meter readings for calendar year 2022. Values below are reported in kBtu from the SEED meter endpoint and should be reconciled with the owner utility bills before project scoping.", - size=10, - max_chars=90, - leading=14, - color="334155", - ) - - page.metric(MARGIN, 632, 156, "Annual meter use", num(annual_total, 0, suffix=" kBtu"), "183F6D") - page.metric(MARGIN + 174, 632, 156, "Meter intensity", num(intensity, 1, suffix=" kBtu/ft2"), "0B94CF") - page.metric(MARGIN + 348, 632, 156, "Meters found", str(len(summary["meters"])), "1B6257") - - page.text(MARGIN, 570, "Fuel split", size=14, font="F2", color="111827") - if summary["fuel_totals"]: - stacked_fuel_bar(page, MARGIN, 536, 510, 20, summary["fuel_totals"]) - else: - page.rect(MARGIN, 520, 510, 42, fill="F7F6F3", stroke="D7DEE8") - page.text(MARGIN + 16, 540, "No meter readings were available in the local SEED export.", size=9, color="334155") - - page.text(MARGIN, 470, "Monthly 2022 profile", size=14, font="F2", color="111827") - labels = [label for label, _ in summary["monthly"]] - values = [value for _, value in summary["monthly"]] - bar_chart(page, MARGIN + 2, 328, 448, 112, labels, values, "0B94CF") - page.text(MARGIN + 462, 438, "kBtu", size=8, font="F2", color="52616B") - - page.text(MARGIN, 286, "Meter inventory", size=14, font="F2", color="111827") - rows = [] - for meter in summary["meters"][:7]: - rows.append( - ( - f"{meter['id']} | {meter['type']}", - f"{num(meter['annual'], 0, suffix=' kBtu')} | {meter['readings']} readings", - ), - ) - if not rows: - rows = [("SEED meters", "No meters found for this property view in the local export")] - small_table(page, MARGIN, 264, rows, key_w=205, val_w=305) - - page.rect(MARGIN, 88, 510, 60, fill="F7F6F3", stroke="D7DEE8") - page.text(MARGIN + 16, 126, "Owner follow-up", size=11, font="F2", color="111827") - page.multiline( - MARGIN + 16, - 108, - "If the meter total, EUI, or fuel mix looks surprising, verify meter boundaries, tenant submeters, vacancies, bulk fuel, and Portfolio Manager import status before approving a measure package.", - size=8, - max_chars=95, - leading=11, - color="334155", - ) - - -def draw_location_page(pdf: PDF, building: dict, rank: int) -> None: - page = pdf.add_page() - header(page, "Building Location + 3D Map Link", f"Rank #{rank}: {building['name']}", 5) - - property_view_id = int(building["property_view_id"]) - image_path = BUILDING_IMAGE_PATHS.get(property_view_id) - has_building_image = image_path is not None and image_path.exists() - - page.multiline( - MARGIN, - 708, - "The image below uses open building and street geometry where available. The Google Maps link opens the same location in satellite/3D-capable map view for owner review.", - size=10, - max_chars=90, - leading=14, - color="334155", - ) - - lat = to_float(building.get("latitude")) - lon = to_float(building.get("longitude")) - point = parse_point(building.get("long_lat")) - if point and (lat is None or lon is None): - lon, lat = point - maps_url = google_maps_3d_url(building) - - if has_building_image: - page.rect(70, 186, 472, 472, fill="FFFFFF", stroke="D7DEE8") - page.image(72, 188, 468, 468, image_path) - page.rect(70, 186, 472, 472, stroke="183F6D") - - page.rect(MARGIN, 78, 510, 86, fill="F7F6F3", stroke="D7DEE8") - page.text(MARGIN + 16, 140, "Image source and owner review link", size=11, font="F2", color="111827") - page.multiline( - MARGIN + 16, - 122, - f"{building['address']} | {lat:.7f}, {lon:.7f}. Image rendered from OpenStreetMap geometry; map data (C) OpenStreetMap contributors. Google Maps 3D link: {maps_url}", - size=8, - max_chars=98, - leading=11, - color="334155", - ) - return - - draw_location_panel(page, building, MARGIN, 386, 510, 260) - - page.text(MARGIN, 350, "Location details", size=14, font="F2", color="111827") - y = small_table( - page, - MARGIN, - 328, - [ - ("Address", str(building["address"])), - ("City / state", f"{building.get('city') or 'Washington'} / {building.get('state') or 'DC'}"), - ("Latitude / longitude", f"{lat:.7f}, {lon:.7f}" if lat is not None and lon is not None else "-"), - ("SEED footprint", "Available" if parse_polygon(building.get("footprint")) else "Not available in this export"), - ], - ) - - page.text(MARGIN, y - 8, "Google Maps 3D / satellite link", size=12, font="F2", color="183F6D") - page.rect(MARGIN, y - 76, 510, 48, fill="EAF7FC", stroke="B8D9E8") - page.multiline(MARGIN + 14, y - 46, maps_url, size=8, max_chars=82, leading=10, color="183F6D") - - page.rect(MARGIN, 88, 510, 70, fill="F7F6F3", stroke="D7DEE8") - page.text(MARGIN + 16, 132, "Map note", size=11, font="F2", color="111827") - page.multiline( - MARGIN + 16, - 114, - "Google map tiles are not embedded in this PDF. Embedding them directly requires a Google Maps API key and a use case that fits Google Maps Platform terms. The link above is included for the owner-facing 3D review workflow.", - size=8, - max_chars=96, - leading=11, - color="334155", - ) - - -def draw_openstudio_calibration_page(pdf: PDF, building: dict, rank: int, page_no: int) -> None: - page = pdf.add_page() - header(page, "OpenStudio-MCP Electricity Calibration", f"Rank #{rank}: {building['name']}", page_no) - assets = OPENSTUDIO_MODEL_PATHS[int(building["property_view_id"])] - calibration = json.loads(assets["calibration_json"].read_text()) - metrics = calibration["metrics"] - - page.multiline( - MARGIN, - 708, - "This page compares the OpenStudio-MCP EnergyPlus baseline with the building's real 2022 SEED electric meter profile. A transparent monthly meter-calibration factor is included for audit screening so the owner can evaluate modeled recommendations against measured seasonality.", - size=10, - max_chars=92, - leading=14, - color="334155", - ) - page.image(91, 210, 430, 430, assets["calibration_image"]) - - page.metric(MARGIN, 132, 156, "Annual baseline error", num(metrics["baseline_annual_difference_percent"], 1, suffix="%"), "1B6257") - page.metric(MARGIN + 174, 132, 156, "Monthly CVRMSE", f"{metrics['baseline_cvrmse_percent']:.1f}% -> 0.0%", "183F6D") - page.metric(MARGIN + 348, 132, 156, "EUI check", f"{metrics['model_site_eui_kbtu_per_ft2']:.1f} vs {metrics['actual_meter_eui_kbtu_per_ft2']:.1f}", "D9901A") - - page.rect(MARGIN, 24, 510, 78, fill="F7F6F3", stroke="D7DEE8") - page.text(MARGIN + 16, 78, "Interpretation", size=11, font="F2", color="111827") - page.multiline( - MARGIN + 16, - 60, - "The annual electricity match is already close. The adjustment improves monthly fit by aligning the model output to the actual 2022 meter; it should be validated against schedules, plug loads, HVAC controls, and tenant operating patterns before using it for investment decisions.", - size=8, - max_chars=96, - leading=11, - color="334155", - ) - - -def draw_openstudio_zoning_page(pdf: PDF, building: dict, rank: int, page_no: int) -> None: - page = pdf.add_page() - header(page, "10-Story Perimeter/Core Model Visual", f"Rank #{rank}: {building['name']}", page_no) - assets = OPENSTUDIO_MODEL_PATHS[int(building["property_view_id"])] - - page.multiline( - MARGIN, - 708, - "The audit model is represented as a ten-story large office with perimeter zones around a central core. This matches the requested OpenStudio geometry intent and gives the owner a quick visual for how the model separates facade-driven loads from interior office loads.", - size=10, - max_chars=92, - leading=14, - color="334155", - ) - page.image(66, 178, 480, 480, assets["zoning_image"]) - - page.rect(MARGIN, 82, 510, 70, fill="F7F6F3", stroke="D7DEE8") - page.text(MARGIN + 16, 126, "Modeling note", size=11, font="F2", color="111827") - page.multiline( - MARGIN + 16, - 108, - "This is a schematic audit visual, not a facade survey. The perimeter/core split is useful for evaluating envelope, lighting, plug-load, schedule, and HVAC control recommendations against real building operation.", - size=8, - max_chars=96, - leading=11, - color="334155", - ) - - -def draw_owner_checklist(pdf: PDF, building: dict, rank: int, page_no: int = 6) -> None: - page = pdf.add_page() - header(page, "Owner Review Checklist", f"Rank #{rank}: {building['name']}", page_no) - page.multiline( - MARGIN, - 708, - "Use this checklist to decide whether the BETTER recommendations are actionable and what information is needed before scoping projects.", - size=11, - max_chars=88, - leading=15, - ) - - sections = [ - ( - "Data to validate", - [ - "Confirm 2022 utility bills, meter IDs, whole-building coverage, and whether shared meters or tenant meters exist.", - "Confirm gross floor area, property type, occupancy, operating hours, and major tenant uses.", - "Check whether the reported Site EUI and ENERGY STAR score match the owner's Portfolio Manager record.", - ], - ), - ( - "Systems to inspect", - [ - "Lighting controls, fixture schedules, tenant plug-load density, and after-hours equipment operation.", - "Heating/cooling equipment age, controls, reset schedules, economizer operation, and simultaneous heating/cooling.", - "Envelope leakage, doors, windows, roof/wall insulation, and comfort complaints that line up with BETTER flags.", - ], - ), - ( - "Decision questions", - [ - "Which recommendations are low-disruption operational changes versus capital projects?", - "Which measures are blocked by leases, tenant controls, historic constraints, or upcoming renovations?", - "Which measures should be bundled so savings are not double-counted?", - ], - ), - ] - y = 650 - for title, items in sections: - page.text(MARGIN, y, title, size=14, font="F2", color="183F6D") - y -= 24 - for item in items: - y = bullet(page, MARGIN, y, item) - y -= 10 - y -= 10 - - page.rect(MARGIN, 92, 510, 62, fill="F7F6F3", stroke="D7DEE8") - page.text(MARGIN + 16, 130, "Screening conclusion", size=11, font="F2", color="111827") - page.multiline( - MARGIN + 16, - 112, - "The owner should treat this as a prioritized audit intake sheet. Confirm the data and system context first, then translate the highest-confidence BETTER flags into scoped measures.", - size=9, - max_chars=86, - leading=12, - color="334155", - ) - - -def build_pdf(building: dict, rank: int) -> PDF: - pdf = PDF() - draw_cover(pdf, building, rank) - draw_detail(pdf, building, rank) - draw_complete_recommendations(pdf, building, rank) - draw_meter_consumption(pdf, building, rank) - draw_location_page(pdf, building, rank) - next_page = 6 - if int(building["property_view_id"]) in OPENSTUDIO_MODEL_PATHS: - draw_openstudio_calibration_page(pdf, building, rank, next_page) - next_page += 1 - draw_openstudio_zoning_page(pdf, building, rank, next_page) - next_page += 1 - draw_owner_checklist(pdf, building, rank, next_page) - return pdf - - -def write_index(top: list[dict], paths: list[Path]) -> None: - lines = [ - "# BETTER Owner Audit PDFs", - "", - "Org 412 BPS - DC, cycle 2022. Ranked by active BETTER recommendation count; ties sorted by combined BETTER cost savings.", - "", - "| Rank | Building | Address | Active recs | PDF |", - "|---:|---|---|---:|---|", - ] - for rank, (building, path) in enumerate(zip(top, paths), start=1): - lines.append( - f"| {rank} | {building['name']} | {building['address']} | {building['rank_score']} | [{path.name}]({path.name}) |", - ) - (OUT_DIR / "README.md").write_text("\n".join(lines) + "\n") - - -def main() -> None: - OUT_DIR.mkdir(parents=True, exist_ok=True) - top = load_ranked_buildings()[:5] - paths: list[Path] = [] - packet = PDF() - for rank, building in enumerate(top, start=1): - path = OUT_DIR / f"{rank:02d}_{safe_name(building['address'])}_owner_audit_screening.pdf" - pdf = build_pdf(building, rank) - pdf.save(path) - paths.append(path) - packet.pages.extend(pdf.pages) - packet_path = OUT_DIR / "top5_better_owner_audit_packet.pdf" - packet.save(packet_path) - write_index(top, paths) - print(packet_path) - for path in paths: - print(path) - - -if __name__ == "__main__": - main() diff --git a/output/inspect_openstudio_mcp_tools.py b/output/inspect_openstudio_mcp_tools.py deleted file mode 100644 index be329b4..0000000 --- a/output/inspect_openstudio_mcp_tools.py +++ /dev/null @@ -1,67 +0,0 @@ -from __future__ import annotations - -import asyncio -import json -import os -from pathlib import Path - -from mcp import ClientSession, StdioServerParameters -from mcp.client.stdio import stdio_client - - -TOOLS = [ - "get_server_status", - "list_skills", - "get_skill", - "create_new_building", - "create_baseline_osm", - "create_bar_building", - "load_osm_model", - "save_osm_model", - "create_space_from_floor_print", - "match_surfaces", - "set_window_to_wall_ratio", - "create_schedule_ruleset", - "create_people_definition", - "create_lights_definition", - "create_electric_equipment", - "enable_ideal_air_loads", - "add_output_meter", - "run_simulation", - "get_run_status", - "get_run_artifacts", - "view_simulation_data", -] - - -async def main() -> None: - run_root = Path("output/openstudio_mcp_runs").resolve() - run_root.mkdir(parents=True, exist_ok=True) - env = os.environ.copy() - env["OPENSTUDIO_MCP_RUN_ROOT"] = str(run_root) - env["OSMCP_SANDBOX"] = "off" - env["OPENSTUDIO_MCP_INPUT_ROOT"] = str(Path("output").resolve()) - env["OPENSTUDIO_MCP_MEASURES_DIR"] = str((run_root / "measures").resolve()) - - server_params = StdioServerParameters( - command="/Users/nlong/working/openstudio/openstudio-mcp/.venv/bin/openstudio-mcp", - args=[], - env=env, - ) - async with stdio_client(server_params) as (read, write): - async with ClientSession(read, write) as session: - await session.initialize() - tools = await session.list_tools() - selected = {} - for tool in tools.tools: - if tool.name in TOOLS: - selected[tool.name] = { - "description": tool.description, - "schema": tool.inputSchema, - } - status = await session.call_tool("get_server_status", {}) - print(json.dumps({"status": [c.text for c in status.content], "tools": selected}, indent=2, default=str)) - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/output/openstudio_mcp_schema_probe.py b/output/openstudio_mcp_schema_probe.py deleted file mode 100644 index 2d46d2f..0000000 --- a/output/openstudio_mcp_schema_probe.py +++ /dev/null @@ -1,125 +0,0 @@ -from __future__ import annotations - -import json -import subprocess -import sys -from pathlib import Path - - -RUN_ROOT = Path("/Users/nlong/working/openstudio/openstudio-mcp/runs") -ASSETS_ROOT = Path("/Users/nlong/working/openstudio/openstudio-mcp/tests/assets") -MEASURES_ROOT = Path("/Users/nlong/working/openstudio/openstudio-mcp/measures") - -DOCKER_CMD = [ - "docker", - "run", - "--rm", - "-i", - "-v", - f"{ASSETS_ROOT}:/inputs:ro", - "-v", - f"{RUN_ROOT}:/runs", - "-v", - f"{MEASURES_ROOT}:/measures", - "-v", - "/Users/nlong/working/openstudio/openstudio-mcp/.claude/skills:/skills:ro", - "-e", - "OPENSTUDIO_MCP_MODE=prod", - "openstudio-mcp:dev", - "openstudio-mcp", -] - - -class MCP: - def __init__(self) -> None: - self.proc = subprocess.Popen( - DOCKER_CMD, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - bufsize=1, - ) - self.next_id = 1 - - def close(self) -> None: - if self.proc.poll() is None: - self.proc.terminate() - try: - self.proc.wait(timeout=5) - except subprocess.TimeoutExpired: - self.proc.kill() - - def request(self, method: str, params: dict | None = None) -> dict: - req_id = self.next_id - self.next_id += 1 - payload = {"jsonrpc": "2.0", "id": req_id, "method": method} - if params is not None: - payload["params"] = params - assert self.proc.stdin is not None - self.proc.stdin.write(json.dumps(payload) + "\n") - self.proc.stdin.flush() - - assert self.proc.stdout is not None - while True: - line = self.proc.stdout.readline() - if not line: - err = self.proc.stderr.read() if self.proc.stderr else "" - raise RuntimeError(f"MCP server closed while waiting for {method}.\n{err}") - msg = json.loads(line) - if msg.get("id") == req_id: - return msg - - def notify(self, method: str, params: dict | None = None) -> None: - payload = {"jsonrpc": "2.0", "method": method} - if params is not None: - payload["params"] = params - assert self.proc.stdin is not None - self.proc.stdin.write(json.dumps(payload) + "\n") - self.proc.stdin.flush() - - -def main() -> None: - wanted = set(sys.argv[1:]) or { - "list_skills", - "get_skill", - "create_bar_building", - "create_new_building", - "create_baseline_osm", - "change_building_location", - "add_output_meter", - "run_simulation", - "get_run_status", - "get_run_artifacts", - "extract_summary_metrics", - "query_timeseries", - "view_simulation_data", - } - client = MCP() - try: - print( - json.dumps( - client.request( - "initialize", - { - "protocolVersion": "2024-11-05", - "clientInfo": {"name": "codex-schema-probe", "version": "0"}, - "capabilities": {}, - }, - ), - indent=2, - ), - ) - client.notify("notifications/initialized") - tools = client.request("tools/list") - selected = [] - for tool in tools["result"]["tools"]: - if tool["name"] in wanted: - selected.append(tool) - print(json.dumps(selected, indent=2)) - finally: - client.close() - - -if __name__ == "__main__": - main() diff --git a/output/run_1620_openstudio_mcp.py b/output/run_1620_openstudio_mcp.py deleted file mode 100644 index 900a471..0000000 --- a/output/run_1620_openstudio_mcp.py +++ /dev/null @@ -1,345 +0,0 @@ -from __future__ import annotations - -import json -import math -import subprocess -import sys -import time -from pathlib import Path - - -REPO_ROOT = Path("/Users/nlong/working/openstudio/openstudio-mcp") -RUN_ROOT = REPO_ROOT / "runs" -ASSETS_ROOT = REPO_ROOT / "tests/assets" -MEASURES_ROOT = REPO_ROOT / "measures" -OUT_DIR = Path("output/openstudio_1620") -RESULT_PATH = OUT_DIR / "1620_i_street_openstudio_comparison.json" - -SEED_GFA_FT2 = 125_367.0 -SEED_SITE_EUI = 53.6 -SEED_SITE_EUI_WN = 54.2 -SEED_PROPERTY_VIEW_ID = 3_282_590 -OSM_LEVELS = 10 -WEATHER_FILE = "/var/oscli/gems/ruby/3.2.0/gems/openstudio-standards-0.8.5/data/weather/USA_MD_Baltimore-Washington.Intl.AP.724060_TMY3.epw" - -REAL_MONTHLY_KBTU = { - "Jan": 927_391.0, - "Feb": 650_284.3, - "Mar": 563_515.5, - "Apr": 466_062.9, - "May": 454_153.7, - "Jun": 502_324.2, - "Jul": 516_211.7, - "Aug": 518_603.0, - "Sep": 438_807.9, - "Oct": 392_684.1, - "Nov": 478_902.6, - "Dec": 735_558.1, -} -J_PER_KBTU = 1_055_055.85262 - -DOCKER_CMD = [ - "docker", - "run", - "--rm", - "-i", - "-v", - f"{ASSETS_ROOT}:/inputs:ro", - "-v", - f"{RUN_ROOT}:/runs", - "-v", - f"{MEASURES_ROOT}:/measures", - "-v", - f"{REPO_ROOT / '.claude/skills'}:/skills:ro", - "-e", - "OPENSTUDIO_MCP_MODE=prod", - "-e", - "OSMCP_SANDBOX=off", - "openstudio-mcp:dev", - "openstudio-mcp", -] - - -class MCP: - def __init__(self) -> None: - self.proc = subprocess.Popen( - DOCKER_CMD, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - bufsize=1, - ) - self.next_id = 1 - - def close(self) -> None: - if self.proc.poll() is None: - self.proc.terminate() - try: - self.proc.wait(timeout=5) - except subprocess.TimeoutExpired: - self.proc.kill() - - def request(self, method: str, params: dict | None = None) -> dict: - req_id = self.next_id - self.next_id += 1 - payload = {"jsonrpc": "2.0", "id": req_id, "method": method} - if params is not None: - payload["params"] = params - assert self.proc.stdin is not None - self.proc.stdin.write(json.dumps(payload) + "\n") - self.proc.stdin.flush() - - assert self.proc.stdout is not None - while True: - line = self.proc.stdout.readline() - if not line: - err = self.proc.stderr.read() if self.proc.stderr else "" - raise RuntimeError(f"MCP server closed while waiting for {method}.\n{err}") - msg = json.loads(line) - if msg.get("id") == req_id: - if "error" in msg: - raise RuntimeError(json.dumps(msg["error"], indent=2)) - return msg["result"] - - def notify(self, method: str, params: dict | None = None) -> None: - payload = {"jsonrpc": "2.0", "method": method} - if params is not None: - payload["params"] = params - assert self.proc.stdin is not None - self.proc.stdin.write(json.dumps(payload) + "\n") - self.proc.stdin.flush() - - def tool(self, name: str, args: dict | None = None) -> dict: - result = self.request("tools/call", {"name": name, "arguments": args or {}}) - texts = [item.get("text", "") for item in result.get("content", []) if item.get("type") == "text"] - text = "\n".join(texts).strip() - try: - return json.loads(text) - except json.JSONDecodeError: - return {"ok": True, "text": text} - - -def first_path(value: object) -> str | None: - if isinstance(value, dict): - for key in ("osm_path", "path", "model_path", "output_path", "saved_path"): - item = value.get(key) - if isinstance(item, str) and item.endswith(".osm"): - return item - for item in value.values(): - found = first_path(item) - if found: - return found - if isinstance(value, list): - for item in value: - found = first_path(item) - if found: - return found - return None - - -def pick_weather(weather_result: dict) -> str | None: - text = json.dumps(weather_result) - candidates: list[str] = [] - def walk(value: object) -> None: - if isinstance(value, dict): - for item in value.values(): - walk(item) - elif isinstance(value, list): - for item in value: - walk(item) - elif isinstance(value, str) and value.endswith(".epw"): - candidates.append(value) - walk(weather_result) - for token in text.replace('"', " ").split(): - if token.endswith(".epw"): - candidates.append(token.strip(",")) - preferred = [c for c in candidates if "Baltimore" in c or "Arlington" in c or "Washington" in c] - return (preferred or candidates or [None])[0] - - -def normalize_monthly(ts_result: dict) -> dict[str, float]: - month_names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] - monthly = {name: 0.0 for name in month_names} - rows = [] - for key in ("data", "timeseries", "values", "rows"): - value = ts_result.get(key) - if isinstance(value, list): - rows = value - break - if not rows: - rows = ts_result.get("result", []) if isinstance(ts_result.get("result"), list) else [] - units = str(ts_result.get("units") or "").lower() - divisor = J_PER_KBTU if units in {"j", "joule", "joules"} else 1.0 - for row in rows: - if not isinstance(row, dict): - continue - month = row.get("month") or row.get("Month") or row.get("month_name") - value = row.get("value") or row.get("Value") or row.get("sum") or row.get("total") - if isinstance(month, int): - month = month_names[month - 1] - if isinstance(month, str) and month[:3] in monthly and value is not None: - monthly[month[:3]] += float(value) / divisor - return monthly - - -def extract_number(value: object, keys: tuple[str, ...]) -> float | None: - if isinstance(value, dict): - for key in keys: - if key in value and isinstance(value[key], int | float): - return float(value[key]) - for item in value.values(): - found = extract_number(item, keys) - if found is not None: - return found - if isinstance(value, list): - for item in value: - found = extract_number(item, keys) - if found is not None: - return found - return None - - -def percent_diff(model: float, actual: float) -> float | None: - if actual == 0: - return None - return (model - actual) / actual * 100.0 - - -def main() -> None: - OUT_DIR.mkdir(parents=True, exist_ok=True) - client = MCP() - try: - client.request( - "initialize", - { - "protocolVersion": "2024-11-05", - "clientInfo": {"name": "codex-1620-openstudio", "version": "0"}, - "capabilities": {}, - }, - ) - client.notify("notifications/initialized") - - skills = client.tool("list_skills") - weather = client.tool("list_weather_files") - weather_file = WEATHER_FILE - - create_args = { - "building_type": "LargeOffice", - "total_bldg_floor_area": SEED_GFA_FT2, - "num_stories_above_grade": OSM_LEVELS, - "num_stories_below_grade": 0, - "floor_height": 10.0, - "wwr": 0.35, - "ns_to_ew_ratio": 1.78, - "building_rotation": 27.0, - "weather_file": weather_file, - "climate_zone": "ASHRAE 169-2013-4A", - "template": "90.1-2019", - "system_type": "Inferred", - "htg_src": "Electricity", - "clg_src": "Electricity", - "swh_src": "Electricity", - "add_hvac": True, - "add_swh": True, - } - model = client.tool("create_new_building", create_args) - if not model.get("ok"): - raise RuntimeError(f"create_new_building failed: {model}") - - client.tool("add_output_meter", {"meter_name": "Electricity:Facility", "reporting_frequency": "Monthly"}) - osm_path = "/runs/1620_i_street_seed_osm.osm" - saved = client.tool("save_osm_model", {"osm_path": osm_path}) - if not saved.get("ok"): - raise RuntimeError(f"save_osm_model failed: {saved}") - run = client.tool("run_simulation", {"osm_path": osm_path, "name": "1620_i_street_seed_osm"}) - run_id = run.get("run_id") or run.get("id") or run.get("run", {}).get("run_id") - if not run_id: - raise RuntimeError(f"Could not find run_id in run_simulation response: {run}") - - status = {} - terminal = {"completed", "success", "failed", "error", "canceled", "cancelled"} - for _ in range(90): - status = client.tool("get_run_status", {"run_id": run_id}) - state = str(status.get("status") or status.get("state") or "").lower() - if state in terminal: - break - time.sleep(10) - - summary = client.tool("extract_summary_metrics", {"run_id": run_id}) - electricity = client.tool( - "query_timeseries", - { - "run_id": run_id, - "variable_name": "Electricity:Facility", - "frequency": "Monthly", - "max_points": 500, - }, - ) - artifacts = client.tool("get_run_artifacts", {"run_id": run_id}) - - model_monthly = normalize_monthly(electricity) - model_annual = sum(model_monthly.values()) - actual_annual = sum(REAL_MONTHLY_KBTU.values()) - model_eui = extract_number(summary, ("eui_kBtu_ft2", "site_eui_kbtu_per_ft2", "site_eui_ip", "eui_kbtu_per_ft2", "site_eui")) - if model_eui is None and model_annual: - model_eui = model_annual / SEED_GFA_FT2 - - comparison = [] - for month, actual in REAL_MONTHLY_KBTU.items(): - modeled = model_monthly.get(month, 0.0) - comparison.append( - { - "month": month, - "actual_kbtu": actual, - "modeled_kbtu": modeled, - "difference_kbtu": modeled - actual, - "difference_percent": percent_diff(modeled, actual), - }, - ) - - output = { - "inputs": { - "seed_property_view_id": SEED_PROPERTY_VIEW_ID, - "address": "1620 I STREET NW", - "seed_property_type": "Office", - "seed_gross_floor_area_ft2": SEED_GFA_FT2, - "seed_site_eui_kbtu_per_ft2": SEED_SITE_EUI, - "seed_weather_normalized_site_eui_kbtu_per_ft2": SEED_SITE_EUI_WN, - "osm_way_id": 55326896, - "osm_building_levels": OSM_LEVELS, - "weather_file": weather_file, - "modeling_note": "Created and simulated through Docker-backed openstudio-mcp tools.", - }, - "mcp": { - "skills": skills, - "create_new_building": model, - "save_osm_model": saved, - "run_simulation": run, - "final_status": status, - "summary_metrics": summary, - "electricity_timeseries": electricity, - "artifacts": artifacts, - }, - "comparison": { - "actual_annual_electricity_kbtu": actual_annual, - "modeled_annual_electricity_kbtu": model_annual, - "actual_meter_eui_kbtu_per_ft2": actual_annual / SEED_GFA_FT2, - "seed_reported_site_eui_kbtu_per_ft2": SEED_SITE_EUI, - "modeled_site_eui_kbtu_per_ft2": model_eui, - "modeled_vs_actual_electricity_percent": percent_diff(model_annual, actual_annual), - "modeled_vs_seed_site_eui_percent": percent_diff(model_eui or math.nan, SEED_SITE_EUI) - if model_eui is not None - else None, - "monthly_electricity": comparison, - }, - } - RESULT_PATH.write_text(json.dumps(output, indent=2)) - print(RESULT_PATH) - print(json.dumps(output["comparison"], indent=2)) - finally: - client.close() - - -if __name__ == "__main__": - main() From cfbf11e17a4bad6c0fe9c3554cbfb740b00d9add Mon Sep 17 00:00:00 2001 From: Nicholas Long Date: Wed, 22 Jul 2026 20:27:20 -0400 Subject: [PATCH 5/5] ignore output(s) --- .gitignore | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index aabe86d..d04166d 100644 --- a/.gitignore +++ b/.gitignore @@ -109,18 +109,4 @@ seed-config*.json # (scripts themselves stay tracked; their generated data/images/pdfs do not) /outputs/ /tmp/ -/output/data/ -/output/images/ -/output/pdf/ -/output/openstudio_1620/ -/output/openstudio_mcp_runs/ - -# One-off generated report artifacts written to the repo root -/dc_bps_2258_25th_place_ne_benchmark_data.json -/dc_bps_2258_25th_place_ne_benchmark_infographic.svg -/org412_cycle656_column_characteristics_infographic.png -/org412_cycle656_column_characteristics_infographic.svg -/org412_cycle656_column_characteristics_infographic_nrel_style.png -/org412_cycle656_column_characteristics_infographic_nrel_style.svg -/org412_cycle656_column_summary.json -/org412_cycle656_profile316_better.json +/output/