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.
150 lines
4.0 KiB
Python
150 lines
4.0 KiB
Python
"""
|
|
Contract types for notebooks.
|
|
|
|
Stable, framework-free frozen dataclasses that define what this product exposes
|
|
to the rest of the codebase. No Django imports.
|
|
|
|
These use ``pydantic.dataclasses.dataclass`` rather than the stdlib variant — same
|
|
syntax, same ``is_dataclass()`` compatibility, but with runtime validation on
|
|
construction so structural mistakes from mappers surface at the facade boundary
|
|
instead of producing a malformed payload further down the call stack.
|
|
"""
|
|
|
|
from dataclasses import field
|
|
from datetime import datetime
|
|
from typing import Any
|
|
from uuid import UUID
|
|
|
|
from pydantic.dataclasses import dataclass
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class NotebookData:
|
|
"""A notebook's persisted state, as other products read it."""
|
|
|
|
id: UUID
|
|
short_id: str
|
|
title: str | None
|
|
# ProseMirror/TipTap content: usually a `{type: "doc", content: [...]}` dict, but some
|
|
# notebooks (e.g. group/account templates) persist a bare list of nodes. JSONField allows
|
|
# either, so the contract must too.
|
|
content: dict[str, Any] | list[Any] | None
|
|
text_content: str | None
|
|
deleted: bool
|
|
visibility: str
|
|
version: int
|
|
created_at: datetime
|
|
last_modified_at: datetime
|
|
created_by_id: int | None = None
|
|
last_modified_by_id: int | None = None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class NotebookRecent:
|
|
"""A single entry in a team's recently-modified notebooks list."""
|
|
|
|
short_id: str
|
|
title: str | None
|
|
last_modified_at: datetime | None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class NotebookActivitySummary:
|
|
"""Aggregate notebook activity for a team — total count plus the most recent few."""
|
|
|
|
total_count: int
|
|
recent: list[NotebookRecent] = field(default_factory=list)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class NotebookUserInfo:
|
|
"""The creator/modifier of a notebook — raw user values, mirroring UserBasicSerializer's input."""
|
|
|
|
id: int
|
|
uuid: UUID
|
|
distinct_id: str | None
|
|
first_name: str
|
|
last_name: str
|
|
email: str
|
|
is_email_verified: bool | None
|
|
mascot_config: Any
|
|
role_at_organization: str | None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class TeamAccountNote:
|
|
"""A row of a team-wide account-notes list: an internal notebook plus the account it's linked to."""
|
|
|
|
short_id: str
|
|
title: str | None
|
|
created_at: datetime
|
|
last_modified_at: datetime
|
|
account_id: UUID
|
|
account_name: str
|
|
created_by: NotebookUserInfo | None = None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MarkdownNotebookMigrationStats:
|
|
"""Markdown migration status for notebooks in an optional team scope."""
|
|
|
|
total: int
|
|
converted: int
|
|
pending: int
|
|
team_id: int | None = None
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MarkdownNotebookMigrationPreview:
|
|
"""A dry-run preview of one notebook conversion."""
|
|
|
|
short_id: str
|
|
title: str | None
|
|
before_version: int
|
|
markdown_preview: str
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MarkdownNotebookMigrationResult:
|
|
"""Result of a markdown notebook migration run."""
|
|
|
|
dry_run: bool
|
|
team_id: int | None
|
|
batch_size: int | None
|
|
total: int
|
|
already_converted: int
|
|
pending_before: int
|
|
pending_after: int
|
|
converted: int
|
|
skipped: int
|
|
errored: int
|
|
previews: list[MarkdownNotebookMigrationPreview] = field(default_factory=list)
|
|
errors: list[str] = field(default_factory=list)
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AccountNote:
|
|
"""An internal notebook linked to a customer-analytics account, for context rendering."""
|
|
|
|
title: str | None
|
|
short_id: str
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class AccountNotebook:
|
|
"""A full internal notebook linked to an account, with creator/modifier info.
|
|
|
|
The richer counterpart to :class:`AccountNote` — backs the account-notebooks CRUD
|
|
endpoints, where consumers render who created/edited each notebook.
|
|
"""
|
|
|
|
id: UUID
|
|
short_id: str
|
|
title: str | None
|
|
content: dict[str, Any] | list[Any] | None
|
|
text_content: str | None
|
|
created_at: datetime
|
|
last_modified_at: datetime
|
|
created_by: NotebookUserInfo | None = None
|
|
last_modified_by: NotebookUserInfo | None = None
|