mirror of
https://github.com/hanzoai/insights.git
synced 2026-10-30 16:15:31 +00:00
The tree was a debranded snapshot of upstream taken around 2026-02-20, and upstream has moved about five months since. This replaces it with upstream master, mechanically re-derived: bin/debrand does the rename, and the pipeline and squashed migrations are restored from the shipped tree. Not a merge. The previous import replaced the tree wholesale in a single commit, so there is no shared content history to merge against. Reproducing the rename on the new tree is what actually converges. What survives: .hanzo/workflows, the Dockerfiles and bin/docker* entrypoints, compose files, LLM.md, and insights/migrations. The squashed initial migration is load-bearing — the live database records it by name, so upstream's ~918 individual migrations cannot come back. Nine products are dropped as unreferenced. Twenty-one more that we do not run are kept anyway because the core app still imports them; unpicking those is a refactor and does not belong in a rebase. This base does not build yet, and the reasons are not in the rename: upstream now wants Python 3.13.13 against our 3.12.12 and Django 5.2 against our 4.2, and the dependency manifests still name upstream packages that we republish under our own names. Those are decisions, not renames.
122 lines
4.7 KiB
Python
Executable File
122 lines
4.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Preflight dashboard widget OpenAPI codegen from the backend registry.
|
|
|
|
Same pattern as ``bin/build-mcp-oauth-scopes.py``: a ``bin/*`` codegen script
|
|
invoked from ``insightscli build:*``, reading backend SSOT and writing frontend JSON.
|
|
|
|
Checks every ``WIDGET_SPECS`` type has an ``ENUM_NAME_OVERRIDES`` entry (Orval /
|
|
``build:openapi-schema`` fails otherwise) and emits frontend SSOT JSON for date
|
|
presets and modal form field picks. Step 2 of ``insightscli build:widget-types`` runs
|
|
``generate-widget-config-zod.mjs`` (Orval ``generateReusableSchemas`` on the
|
|
catalog OpenAPI slice).
|
|
|
|
Run via insightscli: `insightscli build:widget-types` (also runs as part of `build:openapi`).
|
|
"""
|
|
# ruff: noqa: T201
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
import json
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
GENERATED_DIR = REPO_ROOT / "products" / "dashboards" / "frontend" / "generated"
|
|
DATE_FROM_OPTIONS_JSON = GENERATED_DIR / "widget-date-from-options.json"
|
|
FORM_FIELDS_JSON = GENERATED_DIR / "widget-form-fields.json"
|
|
|
|
|
|
def _friendly_config_schema_export(config_model_name: str) -> str:
|
|
type_name = config_model_name
|
|
if type_name.endswith("ListWidgetConfig"):
|
|
type_name = type_name.replace("ListWidgetConfig", "WidgetConfig")
|
|
return type_name[0].lower() + type_name[1:] + "Schema"
|
|
|
|
|
|
def _friendly_config_type_export(config_model_name: str) -> str:
|
|
type_name = config_model_name
|
|
if type_name.endswith("ListWidgetConfig"):
|
|
type_name = type_name.replace("ListWidgetConfig", "WidgetConfig")
|
|
return type_name
|
|
|
|
|
|
def main() -> int:
|
|
# Django imports stay local: django.setup() must run before registry/models load.
|
|
sys.path.insert(0, str(REPO_ROOT))
|
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "insights.settings")
|
|
import django # noqa: PLC0415
|
|
|
|
django.setup()
|
|
|
|
from django.conf import settings # noqa: PLC0415
|
|
|
|
from products.dashboards.backend.constants import ( # noqa: PLC0415
|
|
WIDGET_DATE_FROM_LABELS,
|
|
WIDGET_DATE_FROM_VALUES_ORDERED,
|
|
)
|
|
from products.dashboards.backend.widget_specs.registry import WIDGET_SPECS # noqa: PLC0415
|
|
|
|
enum_overrides = settings.SPECTACULAR_SETTINGS.get("ENUM_NAME_OVERRIDES", {})
|
|
override_values = [value for value in enum_overrides.values() if isinstance(value, list)]
|
|
missing_overrides = [
|
|
widget_type for widget_type, _spec in sorted(WIDGET_SPECS.items()) if [widget_type] not in override_values
|
|
]
|
|
if missing_overrides:
|
|
print(
|
|
"Missing ENUM_NAME_OVERRIDES entries for widget_type(s): "
|
|
+ ", ".join(missing_overrides)
|
|
+ ". See insights/settings/web.py and python manage.py find_enum_collisions.",
|
|
file=sys.stderr,
|
|
)
|
|
return 1
|
|
|
|
if not WIDGET_SPECS:
|
|
print("No widget specs found in WIDGET_SPECS", file=sys.stderr)
|
|
return 1
|
|
|
|
GENERATED_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
date_from_options = [
|
|
{"value": value, "label": WIDGET_DATE_FROM_LABELS[value]}
|
|
for value in WIDGET_DATE_FROM_VALUES_ORDERED
|
|
if value in WIDGET_DATE_FROM_LABELS
|
|
]
|
|
if len(date_from_options) != len(WIDGET_DATE_FROM_VALUES_ORDERED):
|
|
print("WIDGET_DATE_FROM_LABELS is missing entries for WIDGET_DATE_FROM_VALUES_ORDERED", file=sys.stderr)
|
|
return 1
|
|
|
|
DATE_FROM_OPTIONS_JSON.write_text(json.dumps({"options": date_from_options}, indent=2) + "\n", encoding="utf-8")
|
|
print(
|
|
f"Wrote {DATE_FROM_OPTIONS_JSON.relative_to(REPO_ROOT)} ({len(date_from_options)} date presets)",
|
|
)
|
|
|
|
form_fields_manifest: dict[str, dict[str, object]] = {}
|
|
for widget_type, spec in sorted(WIDGET_SPECS.items()):
|
|
missing_form_fields = set(spec.form_fields) - set(spec.config_model.model_fields)
|
|
if missing_form_fields:
|
|
print(
|
|
f"{widget_type}: form_fields not on config model: {sorted(missing_form_fields)}",
|
|
file=sys.stderr,
|
|
)
|
|
return 1
|
|
|
|
config_model_name = spec.config_model.__name__
|
|
config_schema_export = _friendly_config_schema_export(config_model_name)
|
|
form_fields_manifest[widget_type] = {
|
|
"configSchemaExport": config_schema_export,
|
|
"configTypeExport": _friendly_config_type_export(config_model_name),
|
|
"formSchemaExport": config_schema_export.replace("ConfigSchema", "FormSchema"),
|
|
"formFields": list(spec.form_fields),
|
|
}
|
|
|
|
FORM_FIELDS_JSON.write_text(json.dumps({"widgets": form_fields_manifest}, indent=2) + "\n", encoding="utf-8")
|
|
print(
|
|
f"Wrote {FORM_FIELDS_JSON.relative_to(REPO_ROOT)} ({len(form_fields_manifest)} widget form manifests)",
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|