Skip to content

kpi_definition

kpi_definition

Typed classes for Domo card kpi definitions.

Models the kpi definition API response as structured dataclasses with from_dict classmethods, replacing raw dict access with typed properties and methods.

Structure

KpiDefinition ├── subscriptions: dict[str, Subscription] │ └── Subscription │ ├── columns: list[ColumnMapping] │ ├── filters: list[Filter] │ ├── orderBy: list[SortColumn] │ └── groupBy: list[GroupByColumn] ├── formulas: list[Formula] │ └── Formula │ ├── id, name, formula, status, dataType │ ├── columnPositions: list[ColumnRef] │ └── nonAggregatedColumns: list[str] ├── charts: dict └── segments: dict

ColumnSchema ├── id, name, type ├── isCalculation, isAggregatable └── sourceId

ColumnMapping dataclass

ColumnMapping(
    column: str | None = None,
    formula_id: str | None = None,
    mapping: str | None = None,
    aggregation: str | None = None,
    extras: dict[str, Any] = dict(),
)

A column mapped to a chart axis in a subscription.

Either column (raw dataset field) or formula_id (beast mode reference) will be set, not both.

Attributes:

Name Type Description
column str | None

Raw dataset column name (or None if formula_id)

formula_id str | None

Beast mode calculation ID (or None if raw column)

mapping str | None

Chart axis mapping (e.g., "ITEM", "VALUE", "LABEL")

is_formula property

is_formula: bool

Whether this mapping references a beast mode.

name property

name: str | None

Column name with backticks stripped, or None if formula reference.

to_dict

to_dict() -> dict[str, Any]

Reconstruct the API dict this ColumnMapping was parsed from.

Omits keys whose value is None; preserves any unmodeled keys (alias, format, calendar, ...) captured in extras.

Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
def to_dict(self) -> dict[str, Any]:
    """Reconstruct the API dict this ColumnMapping was parsed from.

    Omits keys whose value is None; preserves any unmodeled keys
    (``alias``, ``format``, ``calendar``, ...) captured in ``extras``.
    """
    out: dict[str, Any] = {}
    if self.column is not None:
        out["column"] = self.column
    if self.formula_id is not None:
        out["formulaId"] = self.formula_id
    if self.aggregation is not None:
        out["aggregation"] = self.aggregation
    if self.mapping is not None:
        out["mapping"] = self.mapping
    out.update(self.extras)
    return out

ColumnRef dataclass

ColumnRef(column_name: str, column_position: int)

A column reference within a formula's columnPositions.

Attributes:

Name Type Description
column_name str

Column name (may include backticks in raw form)

column_position int

Character position in the formula string

name property

name: str

Column name with backticks stripped.

to_dict

to_dict() -> dict[str, Any]

Reconstruct the API dict this ColumnRef was parsed from.

Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
93
94
95
96
97
98
def to_dict(self) -> dict[str, Any]:
    """Reconstruct the API dict this ColumnRef was parsed from."""
    return {
        "columnName": self.column_name,
        "columnPosition": self.column_position,
    }

ColumnSchema dataclass

ColumnSchema(
    id: str = "",
    name: str = "",
    type: str | None = None,
    is_calculation: bool = False,
    is_aggregatable: bool = True,
    source_id: str | None = None,
    hidden: bool = False,
    order: int = 0,
)

A column in the dataset schema from the kpi definition.

Attributes:

Name Type Description
id str

Column ID (usually same as name)

name str

Column display name

type str | None

Data type (e.g., "numeric", "string")

is_calculation bool

Whether this is a beast mode column

is_aggregatable bool

Whether the column can be aggregated

source_id str | None

Source dataset ID

hidden bool

Whether the column is hidden

order int

Column order in the dataset

to_dict

to_dict() -> dict[str, Any]

Reconstruct the API dict for this column schema (typed subset).

NOTE: the parser reads only a subset of the dataset schema fields, so this is intentionally lossy (isEncrypted, isControlled, value, templateId are not modeled and are not emitted).

Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
def to_dict(self) -> dict[str, Any]:
    """Reconstruct the API dict for this column schema (typed subset).

    NOTE: the parser reads only a subset of the dataset schema fields, so
    this is intentionally lossy (``isEncrypted``, ``isControlled``,
    ``value``, ``templateId`` are not modeled and are not emitted).
    """
    out: dict[str, Any] = {
        "id": self.id,
        "name": self.name,
        "isCalculation": self.is_calculation,
        "isAggregatable": self.is_aggregatable,
        "hidden": self.hidden,
        "order": self.order,
    }
    if self.type is not None:
        out["type"] = self.type
    if self.source_id is not None:
        out["sourceId"] = self.source_id
    return out

Filter dataclass

Filter(
    column: str | None = None,
    values: list[Any] = list(),
    filter_type: str | None = None,
    operand: str | None = None,
    data_type: str | None = None,
    extras: dict[str, Any] = dict(),
)

A filter applied to a subscription.

The column field can be a raw column name or a formula ID (starting with "calculation_").

Attributes:

Name Type Description
column str | None

Column name or formula ID

values list[Any]

Filter values

filter_type str | None

Filter type (e.g., "LEGACY")

operand str | None

Filter operand (e.g., "IN", "NOT_IN", "EQUALS")

data_type str | None

Data type of the column (e.g., "string", "numeric")

is_formula property

is_formula: bool

Whether this filter references a beast mode.

to_dict

to_dict() -> dict[str, Any]

Reconstruct the API dict this Filter was parsed from.

Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
217
218
219
220
221
222
223
224
225
226
227
228
229
230
def to_dict(self) -> dict[str, Any]:
    """Reconstruct the API dict this Filter was parsed from."""
    out: dict[str, Any] = {}
    if self.column is not None:
        out["column"] = self.column
    out["values"] = self.values
    if self.filter_type is not None:
        out["filterType"] = self.filter_type
    if self.operand is not None:
        out["operand"] = self.operand
    if self.data_type is not None:
        out["dataType"] = self.data_type
    out.update(self.extras)
    return out

Formula dataclass

Formula(
    id: str = "",
    name: str = "",
    formula: str = "",
    resolved_formula: str | None = None,
    status: str | None = None,
    data_type: str | None = None,
    column_positions: list[ColumnRef] = list(),
    non_aggregated_columns: list[str] = list(),
    template_id: int | None = None,
    variable: bool = False,
    persisted_on_data_source: bool = False,
    used_by_other_cards: bool = False,
    reference_count: int = 0,
    is_controlled: bool = False,
    is_aggregatable: bool = True,
)

A beast mode formula in a card's definition.

Attributes:

Name Type Description
id str

Unique formula ID (e.g., "calculation_272e5ef4-...")

name str

Display name

formula str

SQL expression

status str | None

Validation status (e.g., "VALID")

data_type str | None

Output data type (e.g., "DECIMAL", "STRING", "LONG")

column_positions list[ColumnRef]

Columns referenced in the formula

non_aggregated_columns list[str]

Columns used without aggregation

template_id int | None

Formula template ID

variable bool

Whether this is a variable

persisted_on_data_source bool

Whether persisted to the dataset

used_by_other_cards bool

Whether other cards reference this formula

reference_count int

Number of cards referencing this formula

is_controlled bool

Whether this is a controlled formula

is_aggregatable bool

Whether the formula result is aggregatable

referenced_columns property

referenced_columns: set[str]

All dataset columns referenced by this formula.

Combines columnPositions and nonAggregatedColumns, stripping backticks from column names.

to_dict

to_dict() -> dict[str, Any]

Reconstruct the API dict for this formula (typed subset).

NOTE: the parser reads only a subset of a beast mode's fields, so this is intentionally lossy relative to the original payload (fields such as cacheWindow, locked, owner, isAnalytic, bignumber are not modeled and are therefore not emitted).

Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
def to_dict(self) -> dict[str, Any]:
    """Reconstruct the API dict for this formula (typed subset).

    NOTE: the parser reads only a subset of a beast mode's fields, so this
    is intentionally lossy relative to the original payload (fields such as
    ``cacheWindow``, ``locked``, ``owner``, ``isAnalytic``, ``bignumber``
    are not modeled and are therefore not emitted).
    """
    out: dict[str, Any] = {
        "id": self.id,
        "name": self.name,
        "formula": self.formula,
        "variable": self.variable,
        "persistedOnDataSource": self.persisted_on_data_source,
        "usedByOtherCards": self.used_by_other_cards,
        "referenceCount": self.reference_count,
        "isControlled": self.is_controlled,
        "isAggregatable": self.is_aggregatable,
    }
    if self.status is not None:
        out["status"] = self.status
    if self.data_type is not None:
        out["dataType"] = self.data_type
    if self.template_id is not None:
        out["templateId"] = self.template_id
    if self.column_positions:
        out["columnPositions"] = [cp.to_dict() for cp in self.column_positions]
    if self.non_aggregated_columns:
        out["nonAggregatedColumns"] = self.non_aggregated_columns
    return out

GroupByColumn dataclass

GroupByColumn(
    column: str | None = None,
    formula_id: str | None = None,
    extras: dict[str, Any] = dict(),
)

A group-by column in a subscription.

Attributes:

Name Type Description
column str | None

Raw column name (or None if formula_id)

formula_id str | None

Beast mode calculation ID (or None if raw column)

to_dict

to_dict() -> dict[str, Any]

Reconstruct the API dict this GroupByColumn was parsed from.

Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
312
313
314
315
316
317
318
319
320
def to_dict(self) -> dict[str, Any]:
    """Reconstruct the API dict this GroupByColumn was parsed from."""
    out: dict[str, Any] = {}
    if self.column is not None:
        out["column"] = self.column
    if self.formula_id is not None:
        out["formulaId"] = self.formula_id
    out.update(self.extras)
    return out

KpiDefinition dataclass

KpiDefinition(
    subscriptions: dict[str, Subscription] = dict(),
    formulas: list[Formula] = list(),
    charts: dict[str, Any] = dict(),
    segments: dict[str, Any] = dict(),
    conditional_formats: list[dict] = list(),
    annotations: list[dict] = list(),
    slicers: list[dict] = list(),
    title: str | None = None,
    description: str | None = None,
    chart_version: str | None = None,
    allow_table_drill: bool = False,
    input_table: bool = False,
    modified: int | None = None,
    columns_schema: list[ColumnSchema] = list(),
)

Typed representation of a card's kpi definition.

Provides structured access to subscriptions, formulas, and column schema with convenience properties for common queries.

Usage

kpi = KpiDefinition.from_dict(api_response) kpi.main.columns # list[ColumnMapping] kpi.formula_by_id[...] # Formula lookup kpi.used_columns # set[str] of all columns used kpi.available_columns # list[ColumnSchema] of all dataset columns

available_columns property

available_columns: list[str]

All available column names from the dataset schema.

beast_modes property

beast_modes: list[Formula]

All beast mode formulas (non-variable calculations).

definition property

definition: dict[str, Any]

The inner definition dict of the create/GET envelope.

formula_by_id property

formula_by_id: dict[str, Formula]

Lookup dict of formulas by their calculation ID.

main property

The main subscription (most common use case).

unused_columns property

unused_columns: set[str]

Dataset columns NOT used by this card.

used_columns property

used_columns: set[str]

All dataset columns used by this card.

Resolves formula references to their underlying columns. Combines columns from: - Subscription column mappings - Subscription filters - Subscription orderBy - Subscription groupBy - All formula columnPositions and nonAggregatedColumns

from_dict classmethod

from_dict(obj: dict[str, Any]) -> KpiDefinition

Build a KpiDefinition from the kpi definition API response.

Parameters:

Name Type Description Default
obj dict[str, Any]

Full API response dict (includes top-level 'definition' and 'columns')

required
Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
@classmethod
def from_dict(cls, obj: dict[str, Any]) -> KpiDefinition:
    """Build a KpiDefinition from the kpi definition API response.

    Args:
        obj: Full API response dict (includes top-level 'definition' and 'columns')
    """
    definition = obj.get("definition", obj)

    # Parse subscriptions
    subscriptions = {
        name: Subscription.from_dict(sub_data)
        for name, sub_data in definition.get("subscriptions", {}).items()
        if isinstance(sub_data, dict)
    }

    # Parse formulas
    formulas = [Formula.from_dict(f) for f in definition.get("formulas", [])]

    # Parse column schema (top-level 'columns' in API response)
    columns_schema = [ColumnSchema.from_dict(c) for c in obj.get("columns", [])]

    return cls(
        subscriptions=subscriptions,
        formulas=formulas,
        charts=definition.get("charts", {}),
        segments=definition.get("segments", {}),
        conditional_formats=definition.get("conditionalFormats", []),
        annotations=definition.get("annotations", []),
        slicers=definition.get("slicers", []),
        title=definition.get("title"),
        description=definition.get("description"),
        chart_version=definition.get("chartVersion"),
        allow_table_drill=definition.get("allowTableDrill", False),
        input_table=definition.get("inputTable", False),
        modified=definition.get("modified"),
        columns_schema=columns_schema,
    )

resolve_domo_beast_mode_refs async

resolve_domo_beast_mode_refs(
    auth: Any, *, context: Any = None, **context_kwargs
) -> None

Resolve DOMO_BEAST_MODE(nnn) references in all formulas.

Domo beast modes can reference other beast modes using the syntax DOMO_BEAST_MODE(template_id). This method fetches the referenced templates and populates resolved_formula on each Formula that contains such references.

Parameters:

Name Type Description Default
auth Any

Authentication object for API requests

required
context Any

Optional RouteContext for request configuration

None
Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
async def resolve_domo_beast_mode_refs(
    self,
    auth: Any,
    *,
    context: Any = None,
    **context_kwargs,
) -> None:
    """Resolve DOMO_BEAST_MODE(nnn) references in all formulas.

    Domo beast modes can reference other beast modes using the syntax
    ``DOMO_BEAST_MODE(template_id)``. This method fetches the referenced
    templates and populates ``resolved_formula`` on each Formula that
    contains such references.

    Args:
        auth: Authentication object for API requests
        context: Optional RouteContext for request configuration
    """
    import re
    from ...routes import beastmode as beastmode_routes
    from ...client.context import RouteContext

    _DOMO_BM_RE = re.compile(r"DOMO_BEAST_MODE\((\d+)\)")
    context = RouteContext.build_context(context=context, **context_kwargs)

    # Collect all unique template IDs referenced across all formulas
    all_refs: dict[int, Any] = {}  # template_id -> BeastModeTemplate
    for formula in self.formulas:
        for match in _DOMO_BM_RE.findall(formula.formula or ""):
            tid = int(match)
            if tid not in all_refs:
                all_refs[tid] = None

    if not all_refs:
        return

    # Fetch all referenced templates
    for tid in all_refs:
        try:
            res = await beastmode_routes.get_beastmode_by_id(
                auth=auth,
                beastmode_id=str(tid),
                include_hidden=True,
                context=context,
            )
            if res.is_success:
                from ..DomoBeastMode.template_definition import BeastModeTemplate

                all_refs[tid] = BeastModeTemplate.from_dict(res.response)
        except Exception:  # noqa: BLE001
            pass

    # Populate resolved_formula on each formula
    for formula in self.formulas:
        refs_in_formula = _DOMO_BM_RE.findall(formula.formula or "")
        if not refs_in_formula:
            continue
        resolved = formula.formula
        for tid_str in refs_in_formula:
            tid = int(tid_str)
            template = all_refs.get(tid)
            if template:
                resolved = resolved.replace(
                    f"DOMO_BEAST_MODE({tid})",
                    f"({template.expression})",
                )
        formula.resolved_formula = resolved

resolve_unresolved_formulas

resolve_unresolved_formulas(dataset_formulas: Any) -> None

Resolve formula IDs that aren't in the card's own formulas list.

Card definitions only include card-level beast modes. Dataset-level beast modes are referenced by their calculation_xxx legacy ID but their definitions live in the template API. This method resolves them using the dataset's Formulas manager.

After calling this, formula_by_id will include dataset-level formulas, and used_columns will correctly resolve them.

Parameters:

Name Type Description Default
dataset_formulas Any

DomoDataset_FormulasManager with loaded beast modes

required
Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
def resolve_unresolved_formulas(self, dataset_formulas: Any) -> None:  # noqa: C901
    """Resolve formula IDs that aren't in the card's own formulas list.

    Card definitions only include card-level beast modes. Dataset-level
    beast modes are referenced by their ``calculation_xxx`` legacy ID
    but their definitions live in the template API. This method resolves
    them using the dataset's Formulas manager.

    After calling this, ``formula_by_id`` will include dataset-level
    formulas, and ``used_columns`` will correctly resolve them.

    Args:
        dataset_formulas: DomoDataset_FormulasManager with loaded beast modes
    """
    if not dataset_formulas or not dataset_formulas.beast_modes:
        return

    # Build lookup from dataset-level beast modes
    dataset_lookup = dataset_formulas.formula_by_legacy_id
    if not dataset_lookup:
        return

    existing_ids = {f.id for f in self.formulas}

    # Find unresolved formula IDs in subscriptions
    unresolved_ids: set[str] = set()

    for sub in self.subscriptions.values():
        for mapping in sub.columns:
            if mapping.formula_id and mapping.formula_id not in existing_ids:
                unresolved_ids.add(mapping.formula_id)

        for gb in sub.group_by:
            if gb.formula_id and gb.formula_id not in existing_ids:
                unresolved_ids.add(gb.formula_id)

        for o in sub.order_by:
            if o.formula_id and o.formula_id not in existing_ids:
                unresolved_ids.add(o.formula_id)

        for f in sub.filters:
            if (
                f.column
                and f.column.startswith("calculation_")
                and f.column not in existing_ids
            ):
                unresolved_ids.add(f.column)

    # Resolve and add
    for calc_id in unresolved_ids:
        template = dataset_lookup.get(calc_id)
        if template:
            self.formulas.append(template.to_formula())

to_dict

to_dict() -> dict[str, Any]

Reconstruct the full create/definition envelope.

Returns the {"definition": {...}, "columns": [...]} envelope.

Important: The Domo Content API has different formats for GET vs CREATE (PUT /content/v3/cards/kpi):

  • GET (read existing card): formulas, annotations, and conditionalFormats are returned as arrays.
  • CREATE (PUT new card): formulas, annotations, and conditionalFormats must be objects with change-tracking sub-keys:

  • formulas: {"dsUpdated": [], "dsDeleted": [], "card": [...]}

  • annotations: {"new": [], "modified": [], "deleted": []}
  • conditionalFormats: {"card": [...], "datasource": []}

This method produces the CREATE format. If you need the GET format (e.g. for round-trip comparison), use :meth:to_get_dict.

The subscriptions, charts and "chrome" fields round-trip exactly; the formulas and dataset columns are reconstructed from the typed subset the parser reads and are therefore lossy (see Formula.to_dict and ColumnSchema.to_dict).

Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
def to_dict(self) -> dict[str, Any]:
    """Reconstruct the full create/definition envelope.

    Returns the ``{"definition": {...}, "columns": [...]}`` envelope.

    **Important**: The Domo Content API has different formats for GET vs
    CREATE (PUT /content/v3/cards/kpi):

    - **GET** (read existing card): ``formulas``, ``annotations``, and
      ``conditionalFormats`` are returned as **arrays**.
    - **CREATE** (PUT new card): ``formulas``, ``annotations``, and
      ``conditionalFormats`` must be **objects** with change-tracking
      sub-keys:

      - ``formulas``: ``{"dsUpdated": [], "dsDeleted": [], "card": [...]}``
      - ``annotations``: ``{"new": [], "modified": [], "deleted": []}``
      - ``conditionalFormats``: ``{"card": [...], "datasource": []}``

    This method produces the **CREATE** format.  If you need the GET format
    (e.g. for round-trip comparison), use :meth:`to_get_dict`.

    The subscriptions, charts and "chrome" fields round-trip exactly; the
    ``formulas`` and dataset ``columns`` are reconstructed from the typed
    subset the parser reads and are therefore lossy (see ``Formula.to_dict``
    and ``ColumnSchema.to_dict``).
    """
    definition: dict[str, Any] = {
        "subscriptions": {
            name: sub.to_dict() for name, sub in self.subscriptions.items()
        },
        # CREATE format: formulas/annotations/conditionalFormats are objects
        # with change-tracking sub-keys, NOT arrays.
        "formulas": {
            "dsUpdated": [],
            "dsDeleted": [],
            "card": [f.to_dict() for f in self.formulas],
        },
        "conditionalFormats": {
            "card": self.conditional_formats,
            "datasource": [],
        },
        "annotations": {
            "new": self.annotations,
            "modified": [],
            "deleted": [],
        },
        "slicers": self.slicers,
        "title": self.title,
        "chartVersion": self.chart_version,
        "charts": self.charts,
        "allowTableDrill": self.allow_table_drill,
        "segments": self.segments,
        "inputTable": self.input_table,
    }
    if self.description is not None:
        definition["description"] = self.description
    # Do NOT include 'modified' on create — it's server-assigned.

    return {
        "definition": definition,
        "columns": [c.to_dict() for c in self.columns_schema],
    }

to_get_dict

to_get_dict() -> dict[str, Any]

Reconstruct the GET-format envelope (arrays for formulas/annotations/conditionalFormats).

Use this when you want to compare against the raw API GET response. For card creation, use :meth:to_dict (the CREATE format).

Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
def to_get_dict(self) -> dict[str, Any]:
    """Reconstruct the GET-format envelope (arrays for formulas/annotations/conditionalFormats).

    Use this when you want to compare against the raw API GET response.
    For card creation, use :meth:`to_dict` (the CREATE format).
    """
    definition: dict[str, Any] = {
        "subscriptions": {
            name: sub.to_dict() for name, sub in self.subscriptions.items()
        },
        "formulas": [f.to_dict() for f in self.formulas],
        "conditionalFormats": self.conditional_formats,
        "annotations": self.annotations,
        "slicers": self.slicers,
        "title": self.title,
        "chartVersion": self.chart_version,
        "charts": self.charts,
        "allowTableDrill": self.allow_table_drill,
        "segments": self.segments,
        "inputTable": self.input_table,
    }
    if self.description is not None:
        definition["description"] = self.description
    if self.modified is not None:
        definition["modified"] = self.modified

    return {
        "definition": definition,
        "columns": [c.to_dict() for c in self.columns_schema],
    }

SortColumn dataclass

SortColumn(
    column: str | None = None,
    formula_id: str | None = None,
    order: str | None = None,
    aggregation: str | None = None,
    extras: dict[str, Any] = dict(),
)

A sort specification in a subscription.

Attributes:

Name Type Description
column str | None

Raw column name (or None if formula_id)

formula_id str | None

Beast mode calculation ID (or None if raw column)

order str | None

Sort direction ("ASCENDING" or "DESCENDING")

to_dict

to_dict() -> dict[str, Any]

Reconstruct the API dict this SortColumn was parsed from.

Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
270
271
272
273
274
275
276
277
278
279
280
281
282
def to_dict(self) -> dict[str, Any]:
    """Reconstruct the API dict this SortColumn was parsed from."""
    out: dict[str, Any] = {}
    if self.column is not None:
        out["column"] = self.column
    if self.formula_id is not None:
        out["formulaId"] = self.formula_id
    if self.aggregation is not None:
        out["aggregation"] = self.aggregation
    if self.order is not None:
        out["order"] = self.order
    out.update(self.extras)
    return out

Subscription dataclass

Subscription(
    name: str = "",
    columns: list[ColumnMapping] = list(),
    filters: list[Filter] = list(),
    order_by: list[SortColumn] = list(),
    group_by: list[GroupByColumn] = list(),
    fiscal: bool = False,
    projection: bool = False,
    distinct: bool = False,
    extras: dict[str, Any] = dict(),
)

A subscription within a kpi definition (typically "main").

Attributes:

Name Type Description
name str

Subscription name (e.g., "main")

columns list[ColumnMapping]

Column-to-axis mappings

filters list[Filter]

Applied filters

order_by list[SortColumn]

Sort specifications

group_by list[GroupByColumn]

Group-by columns

fiscal bool

Whether fiscal calendar is used

projection bool

Whether projection is enabled

distinct bool

Whether DISTINCT is applied

to_dict

to_dict() -> dict[str, Any]

Reconstruct the API dict this Subscription was parsed from.

Preserves unmodeled subscription-level keys (limit, dateGrain, dateRangeFilter, ...) captured in extras so the round-trip is exact.

Source code in src/crew_dcs/classes/DomoCard/kpi_definition.py
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
def to_dict(self) -> dict[str, Any]:
    """Reconstruct the API dict this Subscription was parsed from.

    Preserves unmodeled subscription-level keys (``limit``, ``dateGrain``,
    ``dateRangeFilter``, ...) captured in ``extras`` so the round-trip is
    exact.
    """
    out: dict[str, Any] = {
        "name": self.name,
        "columns": [c.to_dict() for c in self.columns],
        "filters": [f.to_dict() for f in self.filters],
        "orderBy": [o.to_dict() for o in self.order_by],
        "groupBy": [g.to_dict() for g in self.group_by],
        "fiscal": self.fiscal,
        "projection": self.projection,
        "distinct": self.distinct,
    }
    out.update(self.extras)
    return out