Skip to main content

:::note Generated reference Generated from honeyframeapi 0.1.8 — do not edit by hand. :::

honeyframeapi.dashboard

Dashboard + card handles — build dashboards from code.

The ergonomic replacement for a hand-rolled requests upload script:

dash = project.create_dashboard("Spending & Cost Saving")
dash.add_card("Total Spending", "kpi",
"SELECT SUM(net_value) AS total FROM marts.fact_purchase_order_item",
card_config={"color_theme": "ocean"}, size_x=6, size_y=2)
dash.add_card("Monthly Spend", "stacked_bar", SQL,
card_config={"xField": "year_month", "yField": "total"})
results = dash.execute_all() # {card_id: {columns, rows, ...}}

card_type ∈ table|bar|line|pie|kpi|donut|area|stacked_bar|horizontal_bar| combo|pivot|text|section_divider. config (a.k.a. card_config) is chart-specific: bar/horizontal_bar → {xField, yField}, pie/donut → {nameField, valueField}, kpi → {color_theme}. Card SQL may use Looker-style {{param}} tokens that execute_all(parameters=...) binds.

class Card​

Card(dashboard: Dashboard, data: dict)

A single card on a dashboard.

Card.card_id​

property

card_id: int

Card.title​

property

title: str

Card.card_type​

property

card_type: str

Card.execute()​

execute(params: Optional[dict] = None) -> dict

Run just this card; returns {columns, rows, row_count, …}.

Card.update()​

update(**fields) -> dict

Patch card fields (title, sql_query, card_config, grid_, size_, …).

Card.delete()​

delete() -> None
unlink_template() -> dict

Detach this card from its CardTemplate — future template edits no longer fan out to it. Idempotent. Returns {card_id, template_id: None, message}.

class Dashboard​

Dashboard.reload()​

reload() -> Dashboard

Fetch the dashboard detail (incl. cards) and cache it.

Dashboard.title​

property

title: str

Dashboard.cards​

property

cards: list[Card]

The dashboard's cards (call reload first if stale).

Dashboard.update()​

update(**fields) -> dict

Patch dashboard fields (title, description, is_public, config, …).

Dashboard.add_card()​

add_card(title: str, card_type: str, sql: str = '', *, config: Optional[dict] = None, card_config: Optional[dict] = None, description: Optional[str] = None, grid_col: int = 0, grid_row: int = -1, size_x: int = 12, size_y: int = 4, page_number: int = 1) -> Card

Add a card and return its handle.

card_config is the chart-specific settings dict. config is a deprecated alias for it (still honoured, with a DeprecationWarning). grid_row=-1 lets the server auto-place the card.

Dashboard.add_cards()​

add_cards(cards: list[dict]) -> list[Card]

Bulk-add cards from a list of dicts (each = add_card kwargs).

Accepts the plain card-dict shape (title, card_type, sql_query, …) ({"title", "card_type", "sql_query", "card_config", "grid_col", …}) so existing definitions port over unchanged.

Dashboard.execute_all()​

execute_all(*, parameters: Optional[dict] = None, filters: Optional[list] = None, timeout: Optional[float] = None) -> dict

Run every card; returns {results: {card_id: {...}}, …}.

timeout (seconds) overrides the client's default for this call — a large dashboard can take longer than one card.

Dashboard.execute_batch()​

execute_batch(card_ids: list[int], *, params: Optional[dict] = None, asset_id: Optional[int] = None) -> dict

Run a named SUBSET of this dashboard's cards in ONE round-trip.

POST /{id}/cards/execute-batch. Each card runs exactly the way Card.execute would — same gate, same :name bind merge, same masking — so this is the batched form of the single-card path, NOT of execute_all (which takes {{param}}/filters instead). params is one dict applied to every card; a card that doesn't reference a key just ignores it.

Returns {card_id: {card_id, columns, rows, row_count, execution_ms, error, bound_params}}. The keys arrive as JSON object keys, i.e. strings — index with res[str(cid)]. A failing card reports in-band via its own error and never sinks the batch.

card_ids is the scope: pass the cards of the one page you are rendering. An unscoped batch would run every card on the dashboard, and a dashboard behind a multi-page webapp can carry hundreds. asset_id serves the pinned version of a published asset (the same snapshot the app serves); a cross-org or unknown id degrades server-side to a live, unpinned run rather than erroring.

page = [c.card_id for c in dash.cards[:5]]
res = dash.execute_batch(page, params={"start": "2026-01-01"})
print(res[str(page[0])]["row_count"])
public_links() -> list[dict]

List this dashboard's public share links (metadata only — never the token). Ephemeral server-side-PDF render tokens are hidden.

create_public_link(*, label: Optional[str] = None, expires_days: Optional[int] = PUBLIC_LINK_DEFAULT_DAYS) -> dict

Mint a token-gated public share link. The raw token and a relative share_url are in the returned dict and are shown once — store them.

The link expires after expires_days (default 30; 1..365 allowed). expires_days=0 makes a link that never expires — opt in only when you mean it. Any other value raises ValueError (the server would silently make it never-expiring). None still means never but is deprecated in favour of 0.

revoke_public_link(link_id: int) -> None

Revoke a share link (idempotent; 404 if it belongs to another dashboard).

Dashboard.move_to_collection()​

move_to_collection(collection_id: Optional[int]) -> dict

Move this dashboard into a collection (collection_id), or pass None/0 to uncategorize it.

Dashboard.shares()​

shares() -> list[dict]

This dashboard's share grants (owner-only).

Dashboard.add_share()​

add_share(*, user_id: Optional[int] = None, role: Optional[str] = None, access_level: str = 'view') -> dict

Grant access to a user OR a role (exactly one). access_level ∈ view | edit. Upserts by target. Returns {share_id}.

Dashboard.update_share()​

update_share(share_id: int, access_level: str) -> dict

Change a share's access_level (view | edit).

Dashboard.remove_share()​

remove_share(share_id: int) -> dict

Dashboard.revisions()​

revisions() -> list[dict]

Revision history, newest first ({revision_number, author_user_id, change_note, created_at, author_name} — no snapshot payload).

Dashboard.create_revision()​

create_revision(change_note: Optional[str] = None) -> dict

Force a named snapshot now (bypasses the autosave rate limit). Returns {revision_number}.

Dashboard.get_revision()​

get_revision(revision_number: int) -> dict

One revision incl. its snapshot (the full card/page layout).

Dashboard.diff_revisions()​

diff_revisions(a: int, b: int) -> dict

Diff two revisions — {a, b, cards: {added, removed, changed, …}, dashboard: {fields}}.

Dashboard.restore_revision()​

restore_revision(revision_number: int) -> dict

Restore the dashboard to a revision (auto-snapshots current state first, so it's reversible). Returns {ok, restored_revision}.

Dashboard.undo()​

undo() -> dict

Undo the last change (restore the most recent prior revision). 409 if there's nothing to undo. Returns {ok, restored_revision, undone_change_note}.

Dashboard.undo_card()​

undo_card(card_id: int, *, revision_number: Optional[int] = None) -> dict

Undo the last change to ONE card (restore/delete/revert it from the most recent Before: snapshot, or an explicit revision_number). Returns {ok, card_id, action, restored_from_revision, new_revision}.

Dashboard.export_pdf()​

export_pdf(path, *, paper_size: str = 'A4', orientation: str = 'portrait', current_page_only: bool = False, page_number: Optional[int] = None, file_type: str = 'pdf', width: Optional[int] = None, height: Optional[int] = None, scale: Optional[float] = None, timeout: Optional[float] = None) -> str

Render this dashboard via the server's headless Chromium and write it to local path (returns the path).

paper_size ∈ a4|a3|letter|screen_16_9, orientation ∈ portrait|landscape (case-insensitive). current_page_only + optional page_number export a single dashboard page.

file_type ∈ pdf|png, width/height are render-window pixels (320–4096; either one alone keeps the paper's other axis) and scale is the device-pixel-ratio (0.5–3.0, raster only) — the same options as the UI's export dialog. All four default to the pre-existing A4 PDF behavior, so an unchanged call sends an unchanged request body.

Raises HoneyframeError when the platform host has no headless Chromium (the server returns 503) — fall back to the browser's in-page export there.

Dashboard.delete()​

delete() -> None

Dashboard.url​

property

url: str

The browser URL for this dashboard.

class CardTemplate​

An org-level reusable card template (the Card Library).

Create one from a proven card, then stamp it onto dashboards — either one-shot (insert_into) or as a live link (link_into) whose cards track future template edits. Reach it via the client:

tpl = client.create_card_template("Margin KPI", "kpi",
sql="SELECT margin_pct FROM marts.fact_margin")
tpl.link_into(dashboard_id=10) # edits to tpl fan out to this card
client.list_card_templates()

CardTemplate.name​

property

name: Optional[str]

CardTemplate.update()​

update(**fields) -> dict

Patch the template (name, description, card_type, sql_query, card_config, tags, param_defaults, folder, visibility). Changes to card_type/sql_query/card_config/description fan out to every linked card. Returns {**template, linked_cards_updated}.

CardTemplate.delete()​

delete() -> dict

CardTemplate.insert_into()​

insert_into(dashboard_id: int, *, title: Optional[str] = None, grid_col: int = 0, grid_row: int = -1, page_number: int = 1) -> dict

Stamp a ONE-SHOT copy onto a dashboard (the card is NOT linked — later template edits won't touch it). Returns the new card row.

link_into(dashboard_id: int, *, title: Optional[str] = None, grid_col: int = 0, grid_row: int = -1, page_number: int = 1) -> dict

Stamp a LINKED card onto a dashboard — future update edits fan out to it (Card.unlink_template severs the link). Returns the new card row.

CardTemplate.preview()​

preview(*, params: Optional[dict] = None) -> dict

Execute the template's SQL (masked, row-limited) with optional params merged over its defaults. Returns {template_id, card_type, card_config, columns, rows, row_count, execution_ms, error}. Needs a project-scoped client (its query runs under the caller's project).

CardTemplate.duplicate()​

duplicate(*, name: Optional[str] = None) -> dict

Copy this template into the caller's org (owner = caller, render_count reset). Returns {template_id, name, source_template_id, message}.

CardTemplate.versions()​

versions() -> list[dict]

Edit history, newest first ({version_num, edited_by, edited_at, edit_summary, editor_email, editor_name} — no snapshot payload).

CardTemplate.get_version()​

get_version(version_num: int) -> dict

One version incl. its snapshot (the template state at that point).

CardTemplate.restore_version()​

restore_version(version_num: int) -> dict

Restore the template to a prior version (records a new history entry; fans out to linked cards). Returns {template_id, restored_from_version, template, message}.

Functions​

dashboard_filter()​

dashboard_filter(column: str, *, operator: str = 'between', value: Optional[str] = None, start: Optional[str] = None, end: Optional[str] = None, values: Optional[list] = None, ftype: Optional[str] = None) -> dict

A global dashboard filter. operator ∈ between (needs start+end) | eq | gte | lte (need value) | in (needs values). ftype ∈ categorical | explicit | numeric_range | date_relative coerces the bind value's type.

filter_between()​

filter_between(column: str, start: str, end: str, *, ftype: Optional[str] = None) -> dict

A range filter: start <= column <= end.

filter_compare()​

filter_compare(column: str, operator: str, value: str, *, ftype: Optional[str] = None) -> dict

A scalar comparison filter (operator ∈ eq | gte | lte).

filter_in()​

filter_in(column: str, values: list, *, ftype: Optional[str] = None) -> dict

A set-membership filter: column IN (values…).


Verified against honeyframeapi 0.1.8.