mirror of
https://github.com/hanzoai/insights.git
synced 2026-11-03 00:52:32 +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.
166 lines
6.6 KiB
Python
166 lines
6.6 KiB
Python
from enum import StrEnum
|
||
|
||
# Source-of-truth taxonomy for signals. Django-free (plain StrEnum) so it stays cheap to import
|
||
# from contracts.py, the model layer, and the frontend-types codegen alike. StrEnum members compare
|
||
# equal to their string value, so they drop into `==` checks and ORM filters unchanged.
|
||
|
||
|
||
class ReportPriority(StrEnum):
|
||
# P0–P4 importance/urgency scale shared by signal remediations, scout findings, and report
|
||
# priority assessments. One scale, one enum: replaces the codegen-synthesized schema.Priority /
|
||
# schema.Severity that used to leak out of the old TS-first pipeline.
|
||
P0 = "P0"
|
||
P1 = "P1"
|
||
P2 = "P2"
|
||
P3 = "P3"
|
||
P4 = "P4"
|
||
|
||
|
||
class SignalSourceProduct(StrEnum):
|
||
SESSION_REPLAY = "session_replay"
|
||
LLM_ANALYTICS = "llm_analytics"
|
||
GITHUB = "github"
|
||
LINEAR = "linear"
|
||
JIRA = "jira"
|
||
ZENDESK = "zendesk"
|
||
CONVERSATIONS = "conversations"
|
||
ERROR_TRACKING = "error_tracking"
|
||
ENDPOINTS = "endpoints"
|
||
PGANALYZE = "pganalyze"
|
||
SIGNALS_SCOUT = "signals_scout"
|
||
LOGS = "logs"
|
||
HEALTH_CHECKS = "health_checks"
|
||
REPLAY_VISION = "replay_vision"
|
||
ANALYTICS = "analytics"
|
||
# Tier-1 data-warehouse inbox sources (support / issue trackers / error tracking)
|
||
FRESHDESK = "freshdesk"
|
||
FRESHSERVICE = "freshservice"
|
||
FRONT = "front"
|
||
GORGIAS = "gorgias"
|
||
KUSTOMER = "kustomer"
|
||
DIXA = "dixa"
|
||
PLAIN = "plain"
|
||
GITLAB = "gitlab"
|
||
GITEA = "gitea"
|
||
SHORTCUT = "shortcut"
|
||
SENTRY = "sentry"
|
||
ROLLBAR = "rollbar"
|
||
BUGSNAG = "bugsnag"
|
||
HONEYBADGER = "honeybadger"
|
||
RAYGUN = "raygun"
|
||
# Tier-2 security scanners (record kind: scanner_finding)
|
||
SNYK = "snyk"
|
||
SONARQUBE = "sonarqube"
|
||
SEMGREP = "semgrep"
|
||
RAPID7_INSIGHTVM = "rapid7_insightvm"
|
||
# Tier-3 product feedback / feature requests / reviews
|
||
FEATUREBASE = "featurebase"
|
||
FRILL = "frill"
|
||
AHA = "aha"
|
||
USERVOICE = "uservoice"
|
||
PRODUCTBOARD = "productboard"
|
||
CANNY = "canny"
|
||
ASKNICELY = "asknicely"
|
||
RETENTLY = "retently"
|
||
APPFIGURES = "appfigures"
|
||
APPFOLLOW = "appfollow"
|
||
JUDGEME_REVIEWS = "judgeme_reviews"
|
||
# OAuth-connected support sources
|
||
INTERCOM = "intercom"
|
||
HUBSPOT = "hubspot"
|
||
ENGINEERING_ANALYTICS = "engineering_analytics"
|
||
# Search analytics (record kind: search_opportunity)
|
||
GOOGLE_SEARCH_CONSOLE = "google_search_console"
|
||
|
||
|
||
class SignalSourceType(StrEnum):
|
||
SESSION_ANALYSIS_CLUSTER = "session_analysis_cluster"
|
||
SESSION_PROBLEM = "session_problem"
|
||
EVALUATION = "evaluation"
|
||
EVALUATION_REPORT = "evaluation_report"
|
||
ISSUE = "issue"
|
||
TICKET = "ticket"
|
||
ISSUE_CREATED = "issue_created"
|
||
ISSUE_REOPENED = "issue_reopened"
|
||
ISSUE_SPIKING = "issue_spiking"
|
||
ENDPOINT_EXECUTION_FAILED = "endpoint_execution_failed"
|
||
ENDPOINT_BREAKDOWN_LIMIT_EXCEEDED = "endpoint_breakdown_limit_exceeded"
|
||
CROSS_SOURCE_ISSUE = "cross_source_issue"
|
||
ALERT_STATE_CHANGE = "alert_state_change"
|
||
HEALTH_ISSUE = "health_issue"
|
||
SCANNER_FINDING = "scanner_finding"
|
||
ANOMALY_INVESTIGATION = "anomaly_investigation"
|
||
FEEDBACK = "feedback"
|
||
REVIEW = "review"
|
||
CI_FLAKY_CHECK = "ci_flaky_check"
|
||
CI_BROKEN_DEFAULT_BRANCH = "ci_broken_default_branch"
|
||
CI_DURATION_REGRESSION = "ci_duration_regression"
|
||
SEARCH_OPPORTUNITY = "search_opportunity"
|
||
|
||
|
||
# Plain value lists for ENUM_NAME_OVERRIDES in web.py — drf-spectacular hashes ChoiceField
|
||
# choices as (value, value) pairs, which an Enum class path doesn't normalize to.
|
||
SIGNAL_SOURCE_PRODUCT_VALUES: list[str] = [product.value for product in SignalSourceProduct]
|
||
SIGNAL_SOURCE_TYPE_VALUES: list[str] = [source_type.value for source_type in SignalSourceType]
|
||
|
||
# Human-facing labels for the model's `source_product` choices. Kept beside the enum so a new
|
||
# source is a one-file change; values/labels must stay identical to the migration-frozen choices.
|
||
SIGNAL_SOURCE_PRODUCT_LABELS: dict[SignalSourceProduct, str] = {
|
||
SignalSourceProduct.SESSION_REPLAY: "Session replay",
|
||
SignalSourceProduct.LLM_ANALYTICS: "LLM analytics",
|
||
SignalSourceProduct.GITHUB: "GitHub",
|
||
SignalSourceProduct.LINEAR: "Linear",
|
||
SignalSourceProduct.JIRA: "Jira",
|
||
SignalSourceProduct.ZENDESK: "Zendesk",
|
||
SignalSourceProduct.CONVERSATIONS: "Conversations",
|
||
SignalSourceProduct.ERROR_TRACKING: "Error tracking",
|
||
SignalSourceProduct.PGANALYZE: "pganalyze",
|
||
SignalSourceProduct.SIGNALS_SCOUT: "Signals scout",
|
||
SignalSourceProduct.LOGS: "Logs",
|
||
SignalSourceProduct.HEALTH_CHECKS: "Health checks",
|
||
SignalSourceProduct.ENDPOINTS: "Endpoints",
|
||
SignalSourceProduct.REPLAY_VISION: "Replay Vision",
|
||
SignalSourceProduct.ANALYTICS: "Product analytics",
|
||
SignalSourceProduct.FRESHDESK: "Freshdesk",
|
||
SignalSourceProduct.FRESHSERVICE: "Freshservice",
|
||
SignalSourceProduct.FRONT: "Front",
|
||
SignalSourceProduct.GORGIAS: "Gorgias",
|
||
SignalSourceProduct.KUSTOMER: "Kustomer",
|
||
SignalSourceProduct.DIXA: "Dixa",
|
||
SignalSourceProduct.PLAIN: "Plain",
|
||
SignalSourceProduct.GITLAB: "GitLab",
|
||
SignalSourceProduct.GITEA: "Gitea",
|
||
SignalSourceProduct.SHORTCUT: "Shortcut",
|
||
SignalSourceProduct.SENTRY: "Sentry",
|
||
SignalSourceProduct.ROLLBAR: "Rollbar",
|
||
SignalSourceProduct.BUGSNAG: "Bugsnag",
|
||
SignalSourceProduct.HONEYBADGER: "Honeybadger",
|
||
SignalSourceProduct.RAYGUN: "Raygun",
|
||
SignalSourceProduct.SNYK: "Snyk",
|
||
SignalSourceProduct.SONARQUBE: "SonarQube",
|
||
SignalSourceProduct.SEMGREP: "Semgrep",
|
||
SignalSourceProduct.RAPID7_INSIGHTVM: "Rapid7 InsightVM",
|
||
SignalSourceProduct.FEATUREBASE: "Featurebase",
|
||
SignalSourceProduct.FRILL: "Frill",
|
||
SignalSourceProduct.AHA: "Aha",
|
||
SignalSourceProduct.USERVOICE: "UserVoice",
|
||
SignalSourceProduct.PRODUCTBOARD: "Productboard",
|
||
SignalSourceProduct.CANNY: "Canny",
|
||
SignalSourceProduct.ASKNICELY: "AskNicely",
|
||
SignalSourceProduct.RETENTLY: "Retently",
|
||
SignalSourceProduct.APPFIGURES: "Appfigures",
|
||
SignalSourceProduct.APPFOLLOW: "AppFollow",
|
||
SignalSourceProduct.JUDGEME_REVIEWS: "Judge.me",
|
||
SignalSourceProduct.INTERCOM: "Intercom",
|
||
SignalSourceProduct.HUBSPOT: "HubSpot",
|
||
SignalSourceProduct.ENGINEERING_ANALYTICS: "Engineering analytics",
|
||
SignalSourceProduct.GOOGLE_SEARCH_CONSOLE: "Google Search Console",
|
||
}
|
||
|
||
# The Django model's `source_product` choices, frozen-equivalent to the prior nested TextChoices so
|
||
# no migration is generated. Plain `str` values (not enum members) keep migration state stable; order
|
||
# follows SIGNAL_SOURCE_PRODUCT_LABELS, which matches the original declaration order.
|
||
SIGNAL_SOURCE_PRODUCT_CHOICES: list[tuple[str, str]] = [
|
||
(product.value, label) for product, label in SIGNAL_SOURCE_PRODUCT_LABELS.items()
|
||
]
|