Skip to content

Sheets

albert.resources.sheets

Attributes:

Name Type Description
CellAttributeValue

CellAttributeValue

CellAttributeValue = (
    str | float | int | dict[str, Any] | list[Any] | None
)

CellChangeId

Bases: TypedDict

Attributes:

Name Type Description
rowId str
colId str

rowId

rowId: str

colId

colId: str

CellChangePayload

Bases: TypedDict

Attributes:

Name Type Description
Id CellChangeId
data list[PatchDatum]

Id

data

data: list[PatchDatum]

CellColor

Bases: str, Enum

The allowed colors for a cell

Attributes:

Name Type Description
WHITE
RED
GREEN
BLUE
YELLOW
ORANGE
PURPLE

WHITE

WHITE = 'RGB(255, 255, 255)'

RED

RED = 'RGB(255, 161, 161)'

GREEN

GREEN = 'RGB(130, 222, 198)'

BLUE

BLUE = 'RGB(214, 233, 255)'

YELLOW

YELLOW = 'RGB(254, 240, 159)'

ORANGE

ORANGE = 'RGB(255, 227, 210)'

PURPLE

PURPLE = 'RGB(238, 215, 255)'

CellType

Bases: str, Enum

The type of information in the Cell

Attributes:

Name Type Description
INVENTORY
APP
BLANK
FORMULA
TAG
PRICE
PDC
BAT
TOTAL
TAS
DEF
LKP
FOR
EXTINV
BTI
PRM
PRG
RSL
FNC
WFL
DAC
INT
DAT
NDR
PIC

INVENTORY

INVENTORY = 'INV'

APP

APP = 'APP'

BLANK

BLANK = 'BLK'

FORMULA

FORMULA = 'Formula'

TAG

TAG = 'TAG'

PRICE

PRICE = 'PRC'

PDC

PDC = 'PDC'

BAT

BAT = 'BAT'

TOTAL

TOTAL = 'TOT'

TAS

TAS = 'TAS'

DEF

DEF = 'DEF'

LKP

LKP = 'LKP'

FOR

FOR = 'FOR'

EXTINV

EXTINV = 'EXTINV'

BTI

BTI = 'BTI'

PRM

PRM = 'PRM'

PRG

PRG = 'PRG'

RSL

RSL = 'RSL'

FNC

FNC = 'FNC'

WFL

WFL = 'WFL'

DAC

DAC = 'DAC'

INT

INT = 'INT'

DAT

DAT = 'DAT'

NDR

NDR = 'NDR'

PIC

PIC = 'PIC'

DesignType

Bases: str, Enum

The type of Design

Attributes:

Name Type Description
APPS
PRODUCTS
RESULTS
PROCESS

APPS

APPS = 'apps'

PRODUCTS

PRODUCTS = 'products'

RESULTS

RESULTS = 'results'

PROCESS

PROCESS = 'process'

ColumnPosition

Bases: str, Enum

Insertion position for a new column relative to a reference column.

Attributes:

Name Type Description
LEFT_OF
RIGHT_OF

LEFT_OF

LEFT_OF = 'leftOf'

RIGHT_OF

RIGHT_OF = 'rightOf'

RowPosition

Bases: str, Enum

Insertion position for a new row relative to a reference row.

Attributes:

Name Type Description
ABOVE
BELOW

ABOVE

ABOVE = 'above'

BELOW

BELOW = 'below'

Cell

Bases: BaseResource

A Cell in a Sheet

Attributes:

Name Type Description
column_id str

The column ID of the cell.

row_id str

The row ID of the cell.

value str | dict

The value of the cell. If the cell is an inventory item, this will be a dict.

min_value str | None

The minimum allowed value for inventory cells. Optional.

max_value str | None

The maximum allowed value for inventory cells. Optional.

row_label_name (str, optional)

The display name of the row.

type CellType | str

The type of the cell. Allowed values are the same as for CellType.

row_type (CellType, optional)

The type of the row containing this cell. Usually one of INV (inventory row), TOT (total row), TAS (task row), TAG, PRC, PDC, BAT or BLK.

name str | None

The name of the cell. Optional. Default is None.

calculation str

The calculation of the cell. Optional. Default is "".

design_id str

The design ID of the design this cell is in.

format dict

The format of the cell. Optional. Default is {}. The format is a dict with the keys bgColor and fontColor. The values are strings in the format RGB(255, 255, 255).

raw_value str

The raw value of the cell. If the cell is an inventory item, this will be the value of the inventory item. Read-only.

color str | None

The color of the cell. Read only.

column_id

column_id: str = Field(alias='colId')

row_id

row_id: str = Field(alias='rowId')

row_label_name

row_label_name: str | None = Field(
    default=None, alias="lableName"
)

value

value: str | dict | list = ''

min_value

min_value: str | None = Field(
    default=None, alias="minValue"
)

max_value

max_value: str | None = Field(
    default=None, alias="maxValue"
)

type

type: CellType | str

row_type

row_type: CellType | str | None = Field(default=None)

name

name: str | None = Field(default=None)

calculation

calculation: str = ''

design_id

design_id: str

format

format: dict = Field(
    default_factory=dict, alias="cellFormat"
)

inventory_id

inventory_id: str | None = Field(default=None)

raw_value

raw_value

color

color

Component

Bases: BaseResource

Represents an amount of an inventory item in a formulation.

Attributes:

Name Type Description
inventory_item InventoryItem | None

The inventory item in the component. Optional when inventory_id is provided.

inventory_id InventoryId | None

The inventory identifier backing the component. Automatically populated from inventory_item when present; required when inventory_item is omitted.

amount float

The amount of the inventory item in the component.

cell Cell

The cell that the component is in. Read-only.

inventory_item

inventory_item: InventoryItem | None = Field(default=None)

inventory_id

inventory_id: InventoryId | None = Field(default=None)

amount

amount: float

min_value

min_value: float | None = Field(default=None)

max_value

max_value: float | None = Field(default=None)

cell

cell

inventory_item_id

inventory_item_id: InventoryId

DesignState

Bases: BaseResource

The state of a Design

Attributes:

Name Type Description
collapsed bool | None

collapsed

collapsed: bool | None = False

RowConfig

Bases: BaseAlbertModel

Configuration for an APP or location-type row.

Show JSON schema:
{
  "description": "Configuration for an APP or location-type row.",
  "properties": {
    "option": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Option"
    },
    "value": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Value"
    }
  },
  "title": "RowConfig",
  "type": "object"
}

Fields:

option

option: str | None = None

value

value: str | None = None

RowGroup

Bases: BaseAlbertModel

A named group of rows within a Design.

Show JSON schema:
{
  "description": "A named group of rows within a Design.",
  "properties": {
    "rowId": {
      "title": "Rowid",
      "type": "string"
    },
    "name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Name"
    },
    "child_row_ids": {
      "items": {
        "type": "string"
      },
      "title": "Child Row Ids",
      "type": "array"
    }
  },
  "required": [
    "rowId"
  ],
  "title": "RowGroup",
  "type": "object"
}

Fields:

row_id

row_id: str

name

name: str | None = None

child_row_ids

child_row_ids: list[str]

Design

Bases: BaseSessionResource

A Design in a Sheet. Designs are sheet subsections that are largly abstracted away from the user.

Attributes:

Name Type Description
id str

The Albert ID of the design.

design_type DesignType

The type of the design. Allowed values are the same as for DesignType.

state DesignState | None

The state of the design. Optional. Default is None.

grid DataFrame | None

The grid of the design. Optional. Default is None. Read-only.

rows list[Row] | None

The rows of the design. Optional. Default is None. Read-only.

columns list[Column] | None

The columns of the design. Optional. Default is None. Read-only.

Methods:

Name Description
group_rows

Create a named row group within this design.

get_groups

Get all row groups in this design.

state

state: DesignState | None = Field({})

id

id: str = Field(alias='albertId')

design_type

design_type: DesignType = Field(alias='designType')

sheet

sheet

grid

grid

columns

columns: list[Column]

rows

rows: list[Row]

group_rows

group_rows(
    *,
    name: str,
    child_row_ids: list[str],
    reference_id: str | None = None,
    position: str = "above",
) -> RowGroup

Create a row group within this design.

Parameters:

Name Type Description Default
name str

The name of the row group.

required
child_row_ids list[str]

Row IDs to include in the group. Must contain at least one ID.

required
reference_id str

The reference row ID for insertion. Defaults to the first child row.

None
position str

Position relative to reference_id. One of "above" or "below". Default is "above".

'above'

Returns:

Type Description
RowGroup

The created row group.

Source code in src/albert/resources/sheets.py
def group_rows(
    self,
    *,
    name: str,
    child_row_ids: list[str],
    reference_id: str | None = None,
    position: str = "above",
) -> RowGroup:
    """Create a row group within this design.

    Parameters
    ----------
    name : str
        The name of the row group.
    child_row_ids : list[str]
        Row IDs to include in the group. Must contain at least one ID.
    reference_id : str, optional
        The reference row ID for insertion. Defaults to the first child row.
    position : str, optional
        Position relative to ``reference_id``. One of ``"above"`` or ``"below"``.
        Default is ``"above"``.

    Returns
    -------
    RowGroup
        The created row group.
    """
    if not child_row_ids:
        raise AlbertException("child_row_ids must include at least one row ID")

    seen: set[str] = set()
    ids = [x for x in child_row_ids if not (x in seen or seen.add(x))]

    if reference_id and reference_id in ids:
        ids = [reference_id] + [x for x in ids if x != reference_id]
    else:
        reference_id = ids[0]

    payload = {
        "name": name,
        "referenceId": reference_id,
        "position": position,
        "ChildRows": [{"rowId": rid} for rid in ids],
    }
    response = self.session.put(f"/api/v3/worksheet/{self.id}/designs/groups", json=payload)
    data = response.json()
    group = RowGroup(
        rowId=data.get("rowId", reference_id),
        name=data.get("name", name),
    )
    child_rows = data.get("ChildRows") or []
    group.child_row_ids = [r["rowId"] for r in child_rows if r.get("rowId")]
    if not group.child_row_ids:
        group.child_row_ids = ids

    existing = {g.row_id: g for g in (self._groups_cache or [])}
    existing[group.row_id] = group
    self._groups_cache = list(existing.values())
    self._rows = None
    return group

get_groups

get_groups(*, refresh: bool = False) -> list[RowGroup]

Get all row groups in this design.

Parameters:

Name Type Description Default
refresh bool

When True, re-fetches the group list even if cached. Default is False.

False

Returns:

Type Description
list[RowGroup]

The row groups in this design.

Source code in src/albert/resources/sheets.py
def get_groups(self, *, refresh: bool = False) -> list[RowGroup]:
    """Get all row groups in this design.

    Parameters
    ----------
    refresh : bool, optional
        When True, re-fetches the group list even if cached. Default is False.

    Returns
    -------
    list[RowGroup]
        The row groups in this design.
    """
    if self._groups_cache is not None and not refresh:
        return self._groups_cache

    try:
        response = self.session.get(f"/api/v3/worksheet/design/{self.id}/rows/sequence")
    except AlbertHTTPError:
        self._groups_cache = []
        return []

    seq = response.json()
    if not isinstance(seq, list):
        self._groups_cache = []
        return []

    groups: list[RowGroup] = []
    for item in seq:
        rid = item.get("rowId") or item.get("id")
        child_dicts = (
            item.get("children") or item.get("childRows") or item.get("ChildRows") or []
        )
        if rid and child_dicts:
            child_ids = [
                (c.get("rowId") or c.get("id"))
                for c in child_dicts
                if isinstance(c, dict) and (c.get("rowId") or c.get("id"))
            ]
            groups.append(RowGroup(rowId=rid, name=item.get("name"), child_row_ids=child_ids))

    self._groups_cache = groups
    return groups

SheetFormulationRef

Bases: BaseAlbertModel

A reference to a formulation in a sheet

Show JSON schema:
{
  "description": "A reference to a formulation in a sheet",
  "properties": {
    "id": {
      "description": "The Albert ID of the inventory item that is the formulation",
      "title": "Id",
      "type": "string"
    },
    "name": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "description": "The name of the formulation",
      "title": "Name"
    },
    "hidden": {
      "description": "Whether the formulation is hidden",
      "title": "Hidden",
      "type": "boolean"
    }
  },
  "required": [
    "id",
    "hidden"
  ],
  "title": "SheetFormulationRef",
  "type": "object"
}

Fields:

id

id: str

The Albert ID of the inventory item that is the formulation

name

name: str | None = None

The name of the formulation

hidden

hidden: bool

Whether the formulation is hidden

Sheet

Bases: BaseSessionResource

A Sheet in Albert

Attributes:

Name Type Description
id str

The Albert ID of the sheet.

name str

The name of the sheet.

hidden bool

Whether the sheet is hidden.

is_column_right bool | None

When True, copied columns are placed to the right of the source column; when False, to the left.

col_size_mode str | None

Column width sizing mode. Allowed values are "minimum" and "fitToColumn". None resets to the default grid width.

designs list[Design]

The designs of the sheet.

project_id str

The Albert ID of the project the sheet is in.

grid DataFrame | None

The grid of the sheet. Optional. Default is None. Read-only.

columns list[Column]

The columns of the sheet. Read-only.

rows list[Row]

The rows of the sheet. Read-only.

Methods:

Name Description
set_session
set_sheet_fields
rename
add_formulation
add_components_to_formulation

Add components to an existing formulation column without clearing other cells.

add_formulation_columns
add_blank_row
add_inventory_row
add_lookup_row

Add a lookup (LKP) row to a design.

add_app_row

Add an application (APP) row to a design.

update_cells
add_blank_column

Add a blank (BLK) column to this sheet.

add_lookup_column

Add a lookup (LKP) column to this sheet.

add_function_column

Add a function (FNC) column to this sheet.

add_property_column

Add a property/result (RSL) column to this sheet.

pin_columns

Pin one or more columns to the left or right edge of the sheet.

unpin_columns

Unpin one or more columns.

set_columns_width

Set the display width of one or more columns.

hide_column

Hide a column.

show_column

Show a hidden column.

delete_column
delete_row
get_column

Retrieve a Column by its colId, underlying inventory ID, or display header name.

lock_column

Lock or unlock a column in the sheet.

id

id: str = Field(alias='albertId')

name

name: str

formulations

formulations: list[SheetFormulationRef] = Field(
    default_factory=list, alias="Formulas"
)

hidden

hidden: bool

is_column_right

is_column_right: bool | None = Field(
    default=None, alias="isColumnRight"
)

col_size_mode

col_size_mode: str | None = Field(
    default=None, alias="colSizeMode"
)

designs

designs: list[Design] = Field(alias='Designs')

project_id

project_id: str = Field(alias='projectId')

app_design

app_design

product_design

product_design

result_design

result_design

process_design

process_design

grid

grid

leftmost_pinned_column

leftmost_pinned_column

The leftmost pinned column in the sheet

columns

columns: list[Column]

The columns of a given sheet

rows

rows: list[Row]

The rows of a given sheet

set_session

set_session()
Source code in src/albert/resources/sheets.py
@model_validator(mode="after")
def set_session(self):
    if self.session is not None:
        for d in self.designs:
            d._session = self.session
    return self

set_sheet_fields

set_sheet_fields() -> Sheet
Source code in src/albert/resources/sheets.py
@model_validator(mode="after")
def set_sheet_fields(self: Sheet) -> Sheet:
    for _idx, d in enumerate(self.designs):  # Instead of creating a new list
        d._sheet = self  # Set the reference to the sheet
        if d.design_type == DesignType.APPS:
            self._app_design = d
        elif d.design_type == DesignType.PRODUCTS:
            self._product_design = d
        elif d.design_type == DesignType.RESULTS:
            self._result_design = d
        elif d.design_type == DesignType.PROCESS:
            self._process_design = d
    return self

rename

rename(*, new_name: str)
Source code in src/albert/resources/sheets.py
def rename(self, *, new_name: str):
    endpoint = f"/api/v3/worksheet/sheet/{self.id}"

    payload = [{"attribute": "name", "operation": "update", "newValue": new_name}]

    self.session.patch(endpoint, json=payload)

    self.name = new_name
    return self

add_formulation

add_formulation(
    *,
    formulation_name: str,
    components: list[Component],
    inventory_id: InventoryId | None = None,
    enforce_order: bool = False,
    clear: bool = True,
) -> Column
Source code in src/albert/resources/sheets.py
def add_formulation(
    self,
    *,
    formulation_name: str,
    components: list[Component],
    inventory_id: InventoryId | None = None,
    enforce_order: bool = False,
    clear: bool = True,
) -> Column:
    all_cells: list[Cell] = []
    existing_formulation_names = [x.name for x in self.columns]
    if clear and formulation_name in existing_formulation_names:
        # get the existing column and clear it out to put the new formulation in
        col = self.get_column(column_name=formulation_name, inventory_id=inventory_id)
        self._clear_formulation_from_column(column=col)
    else:
        col = self.add_formulation_columns(formulation_names=[formulation_name])[0]
    column_id = col.column_id

    self.grid = None  # reset the grid for saftey
    product_rows = list(self.product_design.rows)
    initial_row_ids = {row.row_id for row in product_rows}

    for component in components:
        component_inventory_id = component.inventory_item_id
        row_id = self._get_row_id_for_component(
            inventory_id=component_inventory_id,
            existing_cells=all_cells,
            enforce_order=enforce_order,
            product_rows=product_rows,
        )
        if row_id is None:
            raise AlbertException(f"No Component with id {component_inventory_id}")

        value = str(component.amount)
        min_value = str(component.min_value) if component.min_value is not None else None
        max_value = str(component.max_value) if component.max_value is not None else None
        this_cell = Cell(
            column_id=column_id,
            row_id=row_id,
            value=value,
            calculation="",
            type=CellType.INVENTORY,
            design_id=self.product_design.id,
            name=formulation_name,
            inventory_id=col.inventory_id,
            min_value=min_value,
            max_value=max_value,
        )
        all_cells.append(this_cell)

    new_row_ids = [row.row_id for row in product_rows if row.row_id not in initial_row_ids]

    total_row = next((r for r in product_rows if r.type == CellType.TOTAL), None)
    if total_row is not None:
        ingredient_row_ids = [
            row.row_id
            for row in product_rows
            if row.inventory_id is not None and row.row_id != total_row.row_id
        ]
        calculation = "=" + "+".join(f"{column_id}{row_id}" for row_id in ingredient_row_ids)
        total_cell = Cell(
            column_id=column_id,
            row_id=total_row.row_id,
            value=str(sum(component.amount for component in components)),
            calculation=calculation,
            type=CellType.TOTAL,
            design_id=self.product_design.id,
            name=formulation_name,
            inventory_id=col.inventory_id,
        )
        all_cells.append(total_cell)

        # When new ingredient rows were added to the sheet, every other existing
        # inventory column's Total cell must also include those rows in its
        # calculation formula.
        if new_row_ids:
            all_ingredient_row_ids = [
                row.row_id
                for row in product_rows
                if row.inventory_id is not None and row.row_id != total_row.row_id
            ]
            for other_col in self.columns:
                if other_col.column_id == column_id or other_col.type != CellType.INVENTORY:
                    continue
                other_total_cell = next(
                    (
                        c
                        for c in other_col.cells
                        if isinstance(c, Cell) and c.row_id == total_row.row_id
                    ),
                    None,
                )
                if other_total_cell is None:
                    continue
                new_calculation = "=" + "+".join(
                    f"{other_col.column_id}{rid}" for rid in all_ingredient_row_ids
                )
                all_cells.append(
                    other_total_cell.model_copy(update={"calculation": new_calculation})
                )

    # Send ingredient cells first, then Total cells in a separate call.
    def _is_total_cell(c: Cell) -> bool:
        return c.row_type == CellType.TOTAL or c.type == CellType.TOTAL

    ingredient_cells = [c for c in all_cells if not _is_total_cell(c)]
    total_cells = [c for c in all_cells if _is_total_cell(c)]

    if ingredient_cells:
        self.update_cells(cells=ingredient_cells)

    if total_cells:
        # grid reset for safety
        self.grid = None
        self.update_cells(cells=total_cells)

    return self.get_column(column_id=column_id)

add_components_to_formulation

add_components_to_formulation(
    *,
    formulation_name: str | None = None,
    column_id: str | None = None,
    inventory_id: InventoryId | None = None,
    components: list[Component],
    enforce_order: bool = False,
) -> Column

Add components to an existing formulation column without clearing other cells.

Exactly one of column_id, inventory_id, or formulation_name must be provided.

Parameters:

Name Type Description Default
formulation_name str

The name of the formulation column.

None
column_id str

The column ID of the formulation column.

None
inventory_id str

The inventory ID of the formulation column.

None
components list[Component]

The components to append.

required
enforce_order bool

When True, rows are inserted in the order of components. Default is False.

False

Returns:

Type Description
Column

The updated formulation column.

Source code in src/albert/resources/sheets.py
@validate_call
def add_components_to_formulation(
    self,
    *,
    formulation_name: str | None = None,
    column_id: str | None = None,
    inventory_id: InventoryId | None = None,
    components: list[Component],
    enforce_order: bool = False,
) -> Column:
    """Add components to an existing formulation column without clearing other cells.

    Exactly one of ``column_id``, ``inventory_id``, or ``formulation_name`` must be provided.

    Parameters
    ----------
    formulation_name : str, optional
        The name of the formulation column.
    column_id : str, optional
        The column ID of the formulation column.
    inventory_id : str, optional
        The inventory ID of the formulation column.
    components : list[Component]
        The components to append.
    enforce_order : bool, optional
        When True, rows are inserted in the order of ``components``. Default is False.

    Returns
    -------
    Column
        The updated formulation column.
    """
    col = self.get_column(
        column_id=column_id, inventory_id=inventory_id, column_name=formulation_name
    )
    col_id = col.column_id
    self.grid = None

    product_rows = list(self.product_design.rows)
    all_cells: list[Cell] = []
    for component in components:
        inv_item = component.inventory_item
        item_id: InventoryId = inv_item.id if inv_item is not None else component.inventory_id
        row_id = self._get_row_id_for_component(
            inventory_id=item_id,
            existing_cells=all_cells,
            enforce_order=enforce_order,
            product_rows=product_rows,
        )
        if row_id is None:
            raise AlbertException(f"No row found for inventory ID {item_id}")

        all_cells.append(
            Cell(
                column_id=col_id,
                row_id=row_id,
                value=str(component.amount),
                calculation="",
                type=CellType.INVENTORY,
                design_id=self.product_design.id,
                name=col.name or formulation_name or "",
                inventory_id=col.inventory_id,
                min_value=str(component.min_value)
                if component.min_value is not None
                else None,
                max_value=str(component.max_value)
                if component.max_value is not None
                else None,
            )
        )

    self.update_cells(cells=all_cells)
    return self.get_column(column_id=col_id)

add_formulation_columns

add_formulation_columns(
    *,
    formulation_names: list[str],
    starting_position: dict | None = None,
) -> list[Column]
Source code in src/albert/resources/sheets.py
def add_formulation_columns(
    self,
    *,
    formulation_names: list[str],
    starting_position: dict | None = None,
) -> list[Column]:
    if starting_position is None:
        # Ensure pinned-column state is resolved before computing the reference position.
        if self.app_design is not None:
            _ = self.app_design.grid
        starting_position = {
            "reference_id": self.leftmost_pinned_column,
            "position": "rightOf",
        }
    sheet_id = self.id

    endpoint = f"/api/v3/worksheet/sheet/{sheet_id}/columns"

    # In case a user supplied a single formulation name instead of a list
    formulation_names = (
        formulation_names if isinstance(formulation_names, list) else [formulation_names]
    )

    payload = []
    for formulation_name in (
        formulation_names
    ):  # IS there a limit to the number I can add at once? Need to check this.
        # define payload for this item
        payload.append(
            {
                "type": "INV",
                "name": formulation_name,
                "referenceId": starting_position["reference_id"],  # initially defined column
                "position": starting_position["position"],
            }
        )
    response = self.session.post(endpoint, json=payload)

    self.grid = None
    new_dicts = self._reformat_formulation_addition_payload(response_json=response.json())
    return [Column(**x) for x in new_dicts]

add_blank_row

add_blank_row(
    *,
    row_name: str,
    design: DesignType = PRODUCTS,
    position: dict | None = None,
)
Source code in src/albert/resources/sheets.py
def add_blank_row(
    self,
    *,
    row_name: str,
    design: DesignType = DesignType.PRODUCTS,
    position: dict | None = None,
):
    if design == DesignType.RESULTS:
        raise AlbertException("You cannot add rows to the results design")
    if position is None:
        position = {"reference_id": "ROW1", "position": "above"}
    endpoint = f"/api/v3/worksheet/design/{self._get_design_id(design=design)}/rows"

    payload = [
        {
            "type": "BLK",
            "name": row_name,
            "referenceId": position["reference_id"],
            "position": position["position"],
        }
    ]

    response = self.session.post(endpoint, json=payload)

    self.grid = None
    row_dict = response.json()[0]
    return Row(
        rowId=row_dict["rowId"],
        type=row_dict["type"],
        session=self.session,
        design=self._get_design(design=design),
        name=row_dict["name"],
        sheet=self,
    )

add_inventory_row

add_inventory_row(
    *, inventory_id: str, position: dict | None = None
)
Source code in src/albert/resources/sheets.py
def add_inventory_row(
    self,
    *,
    inventory_id: str,
    position: dict | None = None,
):
    if position is None:
        position = {"reference_id": "ROW1", "position": "above"}
    design_id = self.product_design.id
    endpoint = f"/api/v3/worksheet/design/{design_id}/rows"

    payload = {
        "type": "INV",
        "id": ("INV" + inventory_id if not inventory_id.startswith("INV") else inventory_id),
        "referenceId": position["reference_id"],
        "position": position["position"],
    }

    response = self.session.post(endpoint, json=payload)

    self.grid = None
    row_dict = response.json()
    return Row(
        rowId=row_dict["rowId"],
        inventory_id=inventory_id,
        type=row_dict["type"],
        session=self.session,
        design=self.product_design,
        sheet=self,
        name=row_dict["name"],
        id=row_dict["id"],
        manufacturer=row_dict["manufacturer"],
    )

add_lookup_row

add_lookup_row(
    *,
    name: str,
    design: DesignType | str | None = APPS,
    reference_id: str = "ROW1",
    position: RowPosition = ABOVE,
) -> Row

Add a lookup (LKP) row to a design.

Parameters:

Name Type Description Default
name str

The display name of the new row.

required
design DesignType or str

Which design to add the row to. Default is DesignType.APPS.

APPS
reference_id str

The row ID to insert relative to. Defaults to "ROW1".

'ROW1'
position RowPosition

Whether to insert ABOVE or BELOW the reference row. Default is ABOVE.

ABOVE

Returns:

Type Description
Row

The created row.

Source code in src/albert/resources/sheets.py
@validate_call
def add_lookup_row(
    self,
    *,
    name: str,
    design: DesignType | str | None = DesignType.APPS,
    reference_id: str = "ROW1",
    position: RowPosition = RowPosition.ABOVE,
) -> Row:
    """Add a lookup (LKP) row to a design.

    Parameters
    ----------
    name : str
        The display name of the new row.
    design : DesignType or str, optional
        Which design to add the row to. Default is ``DesignType.APPS``.
    reference_id : str, optional
        The row ID to insert relative to. Defaults to ``"ROW1"``.
    position : RowPosition, optional
        Whether to insert ``ABOVE`` or ``BELOW`` the reference row.
        Default is ``ABOVE``.

    Returns
    -------
    Row
        The created row.
    """
    if design == DesignType.RESULTS:
        raise AlbertException("Cannot add rows to the results design")
    design_obj = self._get_design(design=design)
    payload = [
        {
            "type": "LKP",
            "name": name,
            "referenceId": reference_id,
            "position": position.value,
        }
    ]
    response = self.session.post(
        f"/api/v3/worksheet/design/{design_obj.id}/rows", json=payload
    )
    self.grid = None
    data = response.json()[0] if isinstance(response.json(), list) else response.json()
    return Row(
        rowId=data["rowId"],
        type=data["type"],
        session=self.session,
        design=design_obj,
        sheet=self,
        name=data.get("lableName") or data.get("name") or name,
        inventory_id=data.get("id"),
        manufacturer=data.get("manufacturer"),
    )

add_app_row

add_app_row(
    *,
    app_id: str,
    name: str,
    config: RowConfig | None = None,
    design: DesignType | str | None = APPS,
    reference_id: str = "ROW1",
    position: RowPosition = ABOVE,
) -> Row

Add an application (APP) row to a design.

Parameters:

Name Type Description Default
app_id str

The ID of the application. The APP prefix is added automatically if absent.

required
name str

The display name of the row.

required
config RowConfig

Row configuration (option and value). Used to scope the app to a location or region.

None
design DesignType or str

Which design to add the row to. Default is DesignType.APPS.

APPS
reference_id str

The row ID to insert relative to. Defaults to "ROW1".

'ROW1'
position RowPosition

Whether to insert ABOVE or BELOW the reference row. Default is ABOVE.

ABOVE

Returns:

Type Description
Row

The created row.

Source code in src/albert/resources/sheets.py
@validate_call
def add_app_row(
    self,
    *,
    app_id: str,
    name: str,
    config: RowConfig | None = None,
    design: DesignType | str | None = DesignType.APPS,
    reference_id: str = "ROW1",
    position: RowPosition = RowPosition.ABOVE,
) -> Row:
    """Add an application (APP) row to a design.

    Parameters
    ----------
    app_id : str
        The ID of the application. The ``APP`` prefix is added automatically if absent.
    name : str
        The display name of the row.
    config : RowConfig, optional
        Row configuration (``option`` and ``value``). Used to scope the app
        to a location or region.
    design : DesignType or str, optional
        Which design to add the row to. Default is ``DesignType.APPS``.
    reference_id : str, optional
        The row ID to insert relative to. Defaults to ``"ROW1"``.
    position : RowPosition, optional
        Whether to insert ``ABOVE`` or ``BELOW`` the reference row.
        Default is ``ABOVE``.

    Returns
    -------
    Row
        The created row.
    """
    if design == DesignType.RESULTS:
        raise AlbertException("Cannot add rows to the results design")
    design_obj = self._get_design(design=design)
    app_id = app_id if app_id.startswith("APP") else f"APP{app_id}"

    payload: dict = {
        "type": "APP",
        "id": app_id,
        "name": name,
        "referenceId": reference_id,
        "position": position.value,
    }
    if config is not None:
        payload["config"] = config.model_dump(by_alias=True, mode="json", exclude_none=True)

    response = self.session.post(
        f"/api/v3/worksheet/design/{design_obj.id}/rows", json=[payload]
    )
    self.grid = None
    data = response.json()[0] if isinstance(response.json(), list) else response.json()
    return Row(
        rowId=data["rowId"],
        type=data["type"],
        session=self.session,
        design=design_obj,
        sheet=self,
        name=data.get("name") or name,
        inventory_id=data.get("id"),
        manufacturer=data.get("manufacturer"),
        config=data.get("config"),
    )

update_cells

update_cells(*, cells: list[Cell])
Source code in src/albert/resources/sheets.py
def update_cells(self, *, cells: list[Cell]):
    request_path_dict: dict[str, list[Cell]] = {}
    updated: list[Cell] = []
    failed: list[Cell] = []
    # sort by design ID
    for c in cells:
        if c.design_id not in request_path_dict:
            request_path_dict[c.design_id] = [c]
        else:
            request_path_dict[c.design_id].append(c)

    for design_id, cell_list in request_path_dict.items():
        payload_entries: list[tuple[CellChangePayload, Cell]] = []
        for cell in cell_list:
            change_dict = self._get_cell_changes(cell=cell)
            if change_dict is None:
                continue

            is_calculation_cell = cell.calculation is not None and cell.calculation != ""
            max_items = 2 if is_calculation_cell else 1

            if len(change_dict["data"]) > max_items:
                for item in change_dict["data"]:
                    single_change: CellChangePayload = {
                        "Id": change_dict["Id"],
                        "data": [item],
                    }
                    payload_entries.append((single_change, cell))
            else:
                payload_entries.append((change_dict, cell))

        if not payload_entries:
            continue

        this_url = f"/api/v3/worksheet/{design_id}/values"
        pending_by_cell: dict[tuple[str, str], list[tuple[CellChangePayload, Cell]]] = {}
        for payload, cell in payload_entries:
            key = (payload["Id"]["rowId"], payload["Id"]["colId"])
            pending_by_cell.setdefault(key, []).append((payload, cell))

        ordered_keys = list(pending_by_cell.keys())

        def _unique_cells(cells: list[Cell]) -> list[Cell]:
            seen: set[tuple[str, str, str]] = set()
            result: list[Cell] = []
            for c in cells:
                key = (c.design_id, c.row_id, c.column_id)
                if key not in seen:
                    seen.add(key)
                    result.append(c)
            return result

        batch_index = 0
        while True:
            batch_payloads: list[CellChangePayload] = []
            batch_cells: list[Cell] = []
            for key in ordered_keys:
                queue = pending_by_cell.get(key)
                if queue:
                    payload, cell = queue.pop(0)
                    batch_payloads.append(payload)
                    batch_cells.append(cell)
            if not batch_payloads:
                break

            payload_body = [
                {
                    "Id": payload["Id"],
                    "data": [datum.model_dump(by_alias=True) for datum in payload["data"]],
                }
                for payload in batch_payloads
            ]
            response = self.session.patch(this_url, json=payload_body)
            target_cells = _unique_cells(batch_cells)

            if response.status_code == 204:
                for c in target_cells:
                    if c not in updated:
                        updated.append(c)
            elif response.status_code == 206:
                cell_results = self._filter_cells(
                    cells=target_cells, response_dict=response.json()
                )
                for c in cell_results[0]:
                    if c not in updated:
                        updated.append(c)
                for c in cell_results[1]:
                    if c not in failed:
                        failed.append(c)
            else:
                for c in target_cells:
                    if c not in failed:
                        failed.append(c)

            batch_index += 1

    # reset the in-memory grid after updates
    self.grid = None
    return (updated, failed)

add_blank_column

add_blank_column(
    *,
    name: str,
    reference_id: str | None = None,
    position: ColumnPosition = RIGHT_OF,
) -> Column

Add a blank (BLK) column to this sheet.

Parameters:

Name Type Description Default
name str

The display name of the new column.

required
reference_id str

The column ID to insert relative to. Defaults to the last column in the sheet.

None
position ColumnPosition

Whether to insert LEFT_OF or RIGHT_OF the reference column. Default is RIGHT_OF.

RIGHT_OF

Returns:

Type Description
Column

The created column.

Source code in src/albert/resources/sheets.py
@validate_call
def add_blank_column(
    self,
    *,
    name: str,
    reference_id: str | None = None,
    position: ColumnPosition = ColumnPosition.RIGHT_OF,
) -> Column:
    """Add a blank (BLK) column to this sheet.

    Parameters
    ----------
    name : str
        The display name of the new column.
    reference_id : str, optional
        The column ID to insert relative to. Defaults to the last column in the sheet.
    position : ColumnPosition, optional
        Whether to insert ``LEFT_OF`` or ``RIGHT_OF`` the reference column.
        Default is ``RIGHT_OF``.

    Returns
    -------
    Column
        The created column.
    """
    return self._add_column(
        type="BLK", name=name, reference_id=reference_id, position=position
    )

add_lookup_column

add_lookup_column(
    *,
    name: str,
    reference_id: str | None = None,
    position: ColumnPosition = RIGHT_OF,
) -> Column

Add a lookup (LKP) column to this sheet.

Parameters:

Name Type Description Default
name str

The display name of the new column.

required
reference_id str

The column ID to insert relative to. Defaults to the last column in the sheet.

None
position ColumnPosition

Whether to insert LEFT_OF or RIGHT_OF the reference column. Default is RIGHT_OF.

RIGHT_OF

Returns:

Type Description
Column

The created column.

Source code in src/albert/resources/sheets.py
@validate_call
def add_lookup_column(
    self,
    *,
    name: str,
    reference_id: str | None = None,
    position: ColumnPosition = ColumnPosition.RIGHT_OF,
) -> Column:
    """Add a lookup (LKP) column to this sheet.

    Parameters
    ----------
    name : str
        The display name of the new column.
    reference_id : str, optional
        The column ID to insert relative to. Defaults to the last column in the sheet.
    position : ColumnPosition, optional
        Whether to insert ``LEFT_OF`` or ``RIGHT_OF`` the reference column.
        Default is ``RIGHT_OF``.

    Returns
    -------
    Column
        The created column.
    """
    return self._add_column(
        type="LKP", name=name, reference_id=reference_id, position=position
    )

add_function_column

add_function_column(
    *,
    name: str,
    reference_id: str | None = None,
    position: ColumnPosition = RIGHT_OF,
) -> Column

Add a function (FNC) column to this sheet.

Parameters:

Name Type Description Default
name str

The display name of the new column.

required
reference_id str

The column ID to insert relative to. Defaults to the last column in the sheet.

None
position ColumnPosition

Whether to insert LEFT_OF or RIGHT_OF the reference column. Default is RIGHT_OF.

RIGHT_OF

Returns:

Type Description
Column

The created column.

Source code in src/albert/resources/sheets.py
@validate_call
def add_function_column(
    self,
    *,
    name: str,
    reference_id: str | None = None,
    position: ColumnPosition = ColumnPosition.RIGHT_OF,
) -> Column:
    """Add a function (FNC) column to this sheet.

    Parameters
    ----------
    name : str
        The display name of the new column.
    reference_id : str, optional
        The column ID to insert relative to. Defaults to the last column in the sheet.
    position : ColumnPosition, optional
        Whether to insert ``LEFT_OF`` or ``RIGHT_OF`` the reference column.
        Default is ``RIGHT_OF``.

    Returns
    -------
    Column
        The created column.
    """
    return self._add_column(
        type="FNC", name=name, reference_id=reference_id, position=position
    )

add_property_column

add_property_column(
    *,
    name: str,
    attribute_id: str,
    data_column_id: DataColumnId | None = None,
    data_column_name: str | None = None,
    reference_id: str | None = None,
    position: ColumnPosition = RIGHT_OF,
) -> Column

Add a property/result (RSL) column to this sheet.

Exactly one of data_column_id or data_column_name must be provided; the other is fetched automatically.

Parameters:

Name Type Description Default
name str

The display name of the new column.

required
attribute_id str

The ID of the attribute (e.g. "ATR2020").

required
data_column_id DataColumnId

The data column ID (e.g. "DAC2900"). Fetched from the API if omitted.

None
data_column_name str

The data column name. Fetched from the API if omitted.

None
reference_id str

The column ID to insert relative to. Defaults to the last column in the sheet.

None
position ColumnPosition

Whether to insert LEFT_OF or RIGHT_OF the reference column. Default is RIGHT_OF.

RIGHT_OF

Returns:

Type Description
Column

The created column.

Source code in src/albert/resources/sheets.py
@validate_call
def add_property_column(
    self,
    *,
    name: str,
    attribute_id: str,
    data_column_id: DataColumnId | None = None,
    data_column_name: str | None = None,
    reference_id: str | None = None,
    position: ColumnPosition = ColumnPosition.RIGHT_OF,
) -> Column:
    """Add a property/result (RSL) column to this sheet.

    Exactly one of ``data_column_id`` or ``data_column_name`` must be provided;
    the other is fetched automatically.

    Parameters
    ----------
    name : str
        The display name of the new column.
    attribute_id : str
        The ID of the attribute (e.g. ``"ATR2020"``).
    data_column_id : DataColumnId, optional
        The data column ID (e.g. ``"DAC2900"``). Fetched from the API if omitted.
    data_column_name : str, optional
        The data column name. Fetched from the API if omitted.
    reference_id : str, optional
        The column ID to insert relative to. Defaults to the last column in the sheet.
    position : ColumnPosition, optional
        Whether to insert ``LEFT_OF`` or ``RIGHT_OF`` the reference column.
        Default is ``RIGHT_OF``.

    Returns
    -------
    Column
        The created column.
    """
    if not data_column_id and not data_column_name:
        raise AlbertException("Provide at least one of data_column_id or data_column_name.")
    if not data_column_id or not data_column_name:
        from albert.collections.data_columns import DataColumnCollection

        dc_collection = DataColumnCollection(session=self.session)
        if data_column_id and not data_column_name:
            dc = dc_collection.get_by_id(id=data_column_id)
            data_column_name = dc.name
        else:
            dc = dc_collection.get_by_name(name=data_column_name)
            if dc is None:
                raise AlbertException(f"No data column found with name '{data_column_name}'.")
            data_column_id = dc.id

    return self._add_column(
        type="RSL",
        name=name,
        reference_id=reference_id,
        position=position,
        extra={
            "id": attribute_id,
            "datacolumnId": data_column_id,
            "datacolumnName": data_column_name,
        },
    )

pin_columns

pin_columns(
    *, col_ids: list[str], side: Literal["left", "right"]
) -> None

Pin one or more columns to the left or right edge of the sheet.

Parameters:

Name Type Description Default
col_ids list[str]

The column IDs to pin.

required
side 'left' or 'right'

Which edge to pin to.

required
Source code in src/albert/resources/sheets.py
@validate_call
def pin_columns(
    self,
    *,
    col_ids: list[str],
    side: Literal["left", "right"],
) -> None:
    """Pin one or more columns to the left or right edge of the sheet.

    Parameters
    ----------
    col_ids : list[str]
        The column IDs to pin.
    side : "left" or "right"
        Which edge to pin to.
    """
    payload = {
        "data": [
            {
                "operation": "update",
                "attribute": "pinned",
                "colIds": col_ids,
                "newValue": side,
            }
        ]
    }
    self.session.patch(f"/api/v3/worksheet/sheet/{self.id}/columns", json=payload)
    self.grid = None

unpin_columns

unpin_columns(*, col_ids: list[str]) -> None

Unpin one or more columns.

Parameters:

Name Type Description Default
col_ids list[str]

The column IDs to unpin.

required
Source code in src/albert/resources/sheets.py
@validate_call
def unpin_columns(self, *, col_ids: list[str]) -> None:
    """Unpin one or more columns.

    Parameters
    ----------
    col_ids : list[str]
        The column IDs to unpin.
    """
    payload = {
        "data": [
            {
                "operation": "update",
                "attribute": "pinned",
                "colIds": col_ids,
                "newValue": None,
            }
        ]
    }
    self.session.patch(f"/api/v3/worksheet/sheet/{self.id}/columns", json=payload)
    self.grid = None

set_columns_width

set_columns_width(
    *, col_ids: list[str], width: str
) -> None

Set the display width of one or more columns.

Parameters:

Name Type Description Default
col_ids list[str]

Column IDs to update.

required
width str

Width value, e.g. "142px".

required
Source code in src/albert/resources/sheets.py
@validate_call
def set_columns_width(self, *, col_ids: list[str], width: str) -> None:
    """Set the display width of one or more columns.

    Parameters
    ----------
    col_ids : list[str]
        Column IDs to update.
    width : str
        Width value, e.g. ``"142px"``.
    """
    payload = {
        "data": [
            {
                "operation": "update",
                "attribute": "columnWidth",
                "colIds": col_ids,
                "newValue": width,
            }
        ]
    }
    self.session.patch(f"/api/v3/worksheet/sheet/{self.id}/columns", json=payload)
    self.grid = None

hide_column

hide_column(*, col_id: str) -> None

Hide a column.

Parameters:

Name Type Description Default
col_id str

The column ID to hide.

required
Source code in src/albert/resources/sheets.py
@validate_call
def hide_column(self, *, col_id: str) -> None:
    """Hide a column.

    Parameters
    ----------
    col_id : str
        The column ID to hide.
    """
    self.session.patch(
        f"/api/v3/worksheet/sheet/{self.id}/columns",
        json={
            "data": [
                {
                    "operation": "update",
                    "attribute": "hidden",
                    "colId": col_id,
                    "newValue": True,
                }
            ]
        },
    )
    self.grid = None

show_column

show_column(*, col_id: str) -> None

Show a hidden column.

Parameters:

Name Type Description Default
col_id str

The column ID to show.

required
Source code in src/albert/resources/sheets.py
@validate_call
def show_column(self, *, col_id: str) -> None:
    """Show a hidden column.

    Parameters
    ----------
    col_id : str
        The column ID to show.
    """
    self.session.patch(
        f"/api/v3/worksheet/sheet/{self.id}/columns",
        json={
            "data": [
                {
                    "operation": "update",
                    "attribute": "hidden",
                    "colId": col_id,
                    "newValue": False,
                }
            ]
        },
    )
    self.grid = None

delete_column

delete_column(*, column_id: str) -> None
Source code in src/albert/resources/sheets.py
def delete_column(self, *, column_id: str) -> None:
    endpoint = f"/api/v3/worksheet/sheet/{self.id}/columns"
    payload = [{"colId": column_id}]
    self.session.delete(endpoint, json=payload)

    if self._grid is not None:  # if I have a grid loaded into memory, adjust it.
        self.grid = None

delete_row

delete_row(*, row_id: str, design_id: str) -> None
Source code in src/albert/resources/sheets.py
def delete_row(self, *, row_id: str, design_id: str) -> None:
    endpoint = f"/api/v3/worksheet/design/{design_id}/rows"
    payload = [{"rowId": row_id}]
    self.session.delete(endpoint, json=payload)

    if self._grid is not None:  # if I have a grid loaded into memory, adjust it.
        self.grid = None

get_column

get_column(
    *,
    column_id: str | None = None,
    inventory_id: InventoryId | None = None,
    column_name: str | None = None,
) -> Column

Retrieve a Column by its colId, underlying inventory ID, or display header name.

Parameters:

Name Type Description Default
column_id str | None

The sheet column ID to match (e.g. "COL5").

None
inventory_id str | None

The internal inventory identifier to match (e.g. "INVP015-001").

None
column_name str | None

The human-readable header name of the column (e.g. "p1").

None

Returns:

Type Description
Column

The matching Column object.

Raises:

Type Description
AlbertException

If no matching column is found or if multiple matches exist.

Source code in src/albert/resources/sheets.py
@validate_call
def get_column(
    self,
    *,
    column_id: str | None = None,
    inventory_id: InventoryId | None = None,
    column_name: str | None = None,
) -> Column:
    """
    Retrieve a Column by its colId, underlying inventory ID, or display header name.

    Parameters
    ----------
    column_id : str | None
        The sheet column ID to match (e.g. "COL5").
    inventory_id : str | None
        The internal inventory identifier to match (e.g. "INVP015-001").
    column_name : str | None
        The human-readable header name of the column (e.g. "p1").

    Returns
    -------
    Column
        The matching Column object.

    Raises
    ------
    AlbertException
        If no matching column is found or if multiple matches exist.
    """

    if not (column_id or inventory_id or column_name):
        raise AlbertException(
            "Must provide at least one of column_id, inventory_id or column_name"
        )
    # Gather candidates matching your filters
    candidates: list[Column] = []
    for col in self.columns:
        if column_id and col.column_id != column_id:
            continue
        if inventory_id and col.inventory_id != inventory_id:
            continue
        if column_name and col.name != column_name:
            continue
        candidates.append(col)

    if not candidates:
        raise AlbertException(
            f"No column found matching id={column_id}, "
            f"inventory_id={inventory_id}, column_name={column_name}"
        )
    if len(candidates) > 1:
        raise AlbertException("Ambiguous column match; please be more specific.")

    return candidates[0]

lock_column

lock_column(
    *,
    column_id: str | None = None,
    inventory_id: InventoryId | None = None,
    column_name: str | None = None,
    locked: bool = True,
) -> Column

Lock or unlock a column in the sheet.

The column can be specified by its sheet column ID (e.g. "COL5"), by the underlying inventory identifier of a formulation/product, or by the displayed header name. By default the column will be locked; pass locked=False to unlock it.

Parameters:

Name Type Description Default
column_id str | None

The sheet column ID to match.

None
inventory_id str | None

The inventory identifier of the formulation or product to match.

None
column_name str | None

The displayed header name of the column.

None
locked bool

Whether to lock (True) or unlock (False) the column. Defaults to True.

True

Returns:

Type Description
Column

The column that was updated.

Source code in src/albert/resources/sheets.py
def lock_column(
    self,
    *,
    column_id: str | None = None,
    inventory_id: InventoryId | None = None,
    column_name: str | None = None,
    locked: bool = True,
) -> Column:
    """Lock or unlock a column in the sheet.

    The column can be specified by its sheet column ID (e.g. ``"COL5"``),
    by the underlying inventory identifier of a formulation/product, or by
    the displayed header name. By default the column will be locked; pass
    ``locked=False`` to unlock it.

    Parameters
    ----------
    column_id : str | None
        The sheet column ID to match.
    inventory_id : str | None
        The inventory identifier of the formulation or product to match.
    column_name : str | None
        The displayed header name of the column.
    locked : bool
        Whether to lock (``True``) or unlock (``False``) the column. Defaults to
        ``True``.

    Returns
    -------
    Column
        The column that was updated.
    """

    column = self.get_column(
        column_id=column_id, inventory_id=inventory_id, column_name=column_name
    )

    payload = {
        "data": [
            {
                "operation": "update",
                "attribute": "locked",
                "colIds": [column.column_id],
                "newValue": locked,
            }
        ]
    }

    self.session.patch(
        url=f"/api/v3/worksheet/sheet/{self.id}/columns",
        json=payload,
    )

    self.grid = None

    return self.get_column(column_id=column.column_id)

Column

Bases: BaseSessionResource

A column in a Sheet

Attributes:

Name Type Description
column_id str

The column ID of the column.

name str | None

The name of the column. Optional. Default is None.

type CellType | str

The type of the column. Allowed values are the same as for CellType.

sheet Sheet

The sheet the column is in.

cells list[Cell]

The cells in the column. Read-only.

df_name str

The name of the column in the DataFrame. Read-only

Methods:

Name Description
rename
recolor_cells

column_id

column_id: str = Field(alias='colId')

name

name: str | None = Field(default=None)

type

type: CellType | str

sheet

sheet: Sheet

inventory_id

inventory_id: str | None = Field(default=None, exclude=True)

locked

locked: bool = Field(default=False)

hidden

hidden: bool | None = Field(default=None)

pinned

pinned: str | None = Field(default=None)

column_width

column_width: str | None = Field(default=None)

df_name

df_name: str

cells

cells: list[Cell]

rename

rename(new_name)
Source code in src/albert/resources/sheets.py
def rename(self, new_name):
    payload = {
        "data": [
            {
                "operation": "update",
                "attribute": "name",
                "colId": self.column_id,
                "oldValue": self.name,
                "newValue": new_name,
            }
        ]
    }

    self.session.patch(
        url=f"/api/v3/worksheet/sheet/{self.sheet.id}/columns",
        json=payload,
    )

    if self.sheet._grid is not None:  # if I have a grid loaded into memory, adjust it.
        self.sheet.grid = None
        # self.sheet._grid.rename(axis=1, mapper={self.name:new_name})
    self.name = new_name
    return self

recolor_cells

recolor_cells(color: CellColor)
Source code in src/albert/resources/sheets.py
def recolor_cells(self, color: CellColor):
    new_cells = []
    for c in self.cells:
        cell_copy = c.model_copy(update={"format": {"bgColor": color.value}})
        new_cells.append(cell_copy)
    return self.sheet.update_cells(cells=new_cells)

Row

Bases: BaseSessionResource

A row in a Sheet

Attributes:

Name Type Description
row_id str

The row ID of the row.

type CellType | str

The type of the row. Allowed values are the same as for CellType.

design Design

The design the row is in.

sheet Sheet

The sheet the row is in.

name str | None

The name of the row. Optional. Default is None.

inventory_id str | None

The inventory ID of the row. Optional. Default is None.

manufacturer str | None

The manufacturer of the row. Optional. Default is None.

config RowConfig | None

Configuration for APP or location-scoped rows. Optional. Default is None.

parent_row_id str | None

The row ID of the group header this row belongs to. None if not grouped.

child_row_ids list[str]

Row IDs of rows grouped under this row. Non-empty only on group header rows.

is_group_header bool

True when this row is the header of a row group. Read-only.

row_unique_id str

The unique ID of the row. Read-only.

cells list[Cell]

The cells in the row. Read-only.

Methods:

Name Description
recolor_cells

row_id

row_id: str = Field(alias='rowId')

type

type: CellType | str

design

design: Design

sheet

sheet: Sheet

name

name: str | None = Field(default=None)

inventory_id

inventory_id: str | None = Field(default=None, alias='id')

manufacturer

manufacturer: str | None = Field(default=None)

config

config: RowConfig | None = Field(default=None)

parent_row_id

parent_row_id: str | None = Field(default=None)

child_row_ids

child_row_ids: list[str] = Field(default_factory=list)

row_unique_id

row_unique_id

is_group_header

is_group_header: bool

True when this row is the header of a collapsed row group.

cells

cells: list[Cell]

recolor_cells

recolor_cells(color: CellColor)
Source code in src/albert/resources/sheets.py
def recolor_cells(self, color: CellColor):
    new_cells = []
    for c in self.cells:
        cell_copy = c.model_copy(update={"format": {"bgColor": color.value}})
        cell_copy.format = {"bgColor": color.value}
        new_cells.append(cell_copy)
    return self.sheet.update_cells(cells=new_cells)