# This file is part of monday-client.
#
# Copyright (C) 2024 Leet Cyber Security <https://leetcybersecurity.com/>
#
# monday-client is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# monday-client is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with monday-client. If not, see <https://www.gnu.org/licenses/>.
"""
Monday.com API board type definitions and structures.
This module contains dataclasses that represent Monday.com board objects,
including boards, board views, activity logs, and related operations.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Literal
if TYPE_CHECKING:
from monday.types.column import Column
from monday.types.group import Group
from monday.types.item import Item
from monday.types.user import User
from monday.types.workspace import Workspace
[docs]
@dataclass
class Board:
"""
Represents a Monday.com board with all its properties and relationships.
This dataclass maps to the Monday.com API board object structure, containing
fields like name, description, columns, groups, and associated users.
See Also:
https://developer.monday.com/api-reference/reference/boards#fields
"""
activity_logs: list[dict[str, Any]] | None = None
"""The board's activity logs"""
board_folder_id: str = ''
"""The board's folder unique identifier"""
board_kind: str = ''
"""The board's kind"""
columns: list[Column] | None = None
"""The board's columns"""
communication: str = ''
"""The board's communication"""
created_at: str = ''
"""The board's creation date. Returned as ``YYYY-MM-DDTHH:MM:SS``"""
creator: User | None = None
"""The board's creator"""
creator_id: str = ''
"""The board's creator unique identifier"""
description: str = ''
"""The board's description"""
groups: list[Group] | None = None
"""The board's groups"""
id: str = ''
"""The board's unique identifier"""
items: list[Item] | None = None
"""The board's items"""
items_count: int = 0
"""The number of items on the board"""
name: str = ''
"""The board's name"""
owners: list[User] | None = None
"""The board's owners"""
permissions: str = ''
"""The board's permissions"""
pos: str = ''
"""The board's position"""
state: str = ''
"""The board's state"""
subscribers: list[User] | None = None
"""The board's subscribers"""
tags: list[dict[str, Any]] | None = None
"""The board's tags"""
top_group: Group | None = None
"""The board's top group"""
type: str = ''
"""The board's type"""
updated_at: str = ''
"""The board's last update date. Returned as ``YYYY-MM-DDTHH:MM:SS``"""
workspace: Workspace | None = None
"""The board's workspace"""
workspace_id: str = ''
"""The board's workspace unique identifier"""
[docs]
def to_dict(self) -> dict[str, Any]:
"""Convert to dictionary for API requests."""
def _convert_list(items: list, converter_name: str = 'to_dict') -> list:
"""Convert list items using their converter method if available."""
return [
getattr(item, converter_name)()
if hasattr(item, converter_name)
else item
for item in items
]
data = {
'activity_logs': self.activity_logs,
'board_folder_id': self.board_folder_id,
'board_kind': self.board_kind,
'columns': _convert_list(self.columns) if self.columns else None,
'communication': self.communication,
'created_at': self.created_at,
'creator': self.creator.to_dict() if self.creator else None,
'creator_id': self.creator_id,
'description': self.description,
'groups': _convert_list(self.groups) if self.groups else None,
'id': self.id,
'items': _convert_list(self.items) if self.items else None,
'items_count': self.items_count,
'name': self.name,
'owners': _convert_list(self.owners) if self.owners else None,
'permissions': self.permissions,
'pos': self.pos,
'state': self.state,
'subscribers': _convert_list(self.subscribers)
if self.subscribers
else None,
'tags': self.tags,
'top_group': self.top_group.to_dict() if self.top_group else None,
'type': self.type,
'updated_at': self.updated_at,
'workspace': self.workspace.to_dict() if self.workspace else None,
'workspace_id': self.workspace_id,
}
return {k: v for k, v in data.items() if v}
[docs]
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Board:
"""Create from dictionary."""
from monday.types.column import Column # noqa: PLC0415
from monday.types.group import Group # noqa: PLC0415
from monday.types.item import Item # noqa: PLC0415
from monday.types.user import User # noqa: PLC0415
from monday.types.workspace import Workspace # noqa: PLC0415
items = None
if data.get('items'):
items = [
Item.from_dict(item) if isinstance(item, dict) else item
for item in data.get('items', [])
]
return cls(
activity_logs=data.get('activity_logs'),
board_folder_id=str(data.get('board_folder_id', '')),
board_kind=str(data.get('board_kind', '')),
columns=[
Column.from_dict(column) if hasattr(Column, 'from_dict') else column
for column in data.get('columns', [])
]
if data.get('columns')
else None,
communication=str(data.get('communication', '')),
created_at=str(data.get('created_at', '')),
creator=User.from_dict(data['creator']) if data.get('creator') else None,
creator_id=str(data.get('creator_id', '')),
description=str(data.get('description', '')),
groups=[
Group.from_dict(group) if hasattr(Group, 'from_dict') else group
for group in data.get('groups', [])
]
if data.get('groups')
else None,
id=str(data.get('id', '')),
items=items,
items_count=int(data.get('items_count', 0)),
name=str(data.get('name', '')),
owners=[
User.from_dict(owner) if hasattr(User, 'from_dict') else owner
for owner in data.get('owners', [])
]
if data.get('owners')
else None,
permissions=str(data.get('permissions', '')),
pos=str(data.get('pos', '')),
state=str(data.get('state', '')),
subscribers=[
User.from_dict(subscriber) if hasattr(User, 'from_dict') else subscriber
for subscriber in data.get('subscribers', [])
]
if data.get('subscribers')
else None,
tags=data.get('tags'),
top_group=Group.from_dict(data['top_group'])
if data.get('top_group')
else None,
type=str(data.get('type', '')),
updated_at=str(data.get('updated_at', '')),
workspace=Workspace.from_dict(data['workspace'])
if data.get('workspace')
else None,
workspace_id=str(data.get('workspace_id', '')),
)
[docs]
@dataclass
class ActivityLog:
"""
Type definitions for monday.com API activity log structures.
These types correspond to Monday.com's activity log view fields as documented in their API reference:
https://developer.monday.com/api-reference/reference/activity-logs#fields
"""
account_id: str = ''
"""The unique identifier of the account that initiated the event"""
data: str = ''
"""The item's column values"""
entity: Literal['board', 'pulse'] | None = None
"""The entity of the event that was changed"""
event: str = ''
"""The action that took place"""
id: str = ''
"""The unique identifier of the activity log event"""
user_id: str = ''
"""The unique identifier of the user who initiated the event"""
created_at: str = ''
"""The time of the event in 17-digit unix time"""
[docs]
def to_dict(self) -> dict[str, Any]:
"""Convert to dictionary for API requests."""
result = {}
if self.account_id:
result['account_id'] = self.account_id
if self.data:
result['data'] = self.data
if self.entity:
result['entity'] = self.entity
if self.event:
result['event'] = self.event
if self.id:
result['id'] = self.id
if self.user_id:
result['user_id'] = self.user_id
if self.created_at:
result['created_at'] = self.created_at
return result
[docs]
@classmethod
def from_dict(cls, data: dict[str, Any]) -> ActivityLog:
"""Create from dictionary."""
return cls(
account_id=str(data.get('account_id', '')),
data=str(data.get('data', '')),
entity=data.get('entity'),
event=str(data.get('event', '')),
id=str(data.get('id', '')),
user_id=str(data.get('user_id', '')),
created_at=str(data.get('created_at', '')),
)
[docs]
@dataclass
class BoardView:
"""
Type definitions for monday.com API board view structures.
These types correspond to Monday.com's board view fields as documented in their API reference:
https://developer.monday.com/api-reference/reference/board-views#fields
"""
id: str = ''
"""The view's unique identifier"""
name: str = ''
"""The view's name"""
settings_str: str = ''
"""The view's settings"""
type: str = ''
"""The view's type"""
view_specific_data_str: str = ''
"""Specific board view data (only supported for forms)"""
[docs]
def to_dict(self) -> dict[str, Any]:
"""Convert to dictionary for API requests."""
result = {}
if self.id:
result['id'] = self.id
if self.name:
result['name'] = self.name
if self.settings_str:
result['settings_str'] = self.settings_str
if self.type:
result['type'] = self.type
if self.view_specific_data_str:
result['view_specific_data_str'] = self.view_specific_data_str
return result
[docs]
@classmethod
def from_dict(cls, data: dict[str, Any]) -> BoardView:
"""Create from dictionary."""
return cls(
id=str(data.get('id', '')),
name=str(data.get('name', '')),
settings_str=str(data.get('settings_str', '')),
type=str(data.get('type', '')),
view_specific_data_str=str(data.get('view_specific_data_str', '')),
)
[docs]
@dataclass
class UndoData:
"""
Structure containing undo information for board operations.
Example:
.. code-block:: python
undo_data = {
'undo_record_id': 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
'action_type': 'modify_project',
'entity_type': 'Board',
'entity_id': 987654321,
'count': 1,
}
"""
undo_record_id: str = ''
"""Unique identifier for the undo record"""
action_type: str = ''
"""Type of action performed (e.g., 'modify_project')"""
entity_type: str = ''
"""Type of entity modified (e.g., 'Board')"""
entity_id: str = ''
"""ID of the entity that was modified"""
count: int = 0
"""Number of entities affected by the operation"""
[docs]
def to_dict(self) -> dict[str, Any]:
"""Convert to dictionary for API requests."""
result = {}
if self.undo_record_id:
result['undo_record_id'] = self.undo_record_id
if self.action_type:
result['action_type'] = self.action_type
if self.entity_type:
result['entity_type'] = self.entity_type
if self.entity_id:
result['entity_id'] = str(self.entity_id)
if self.count:
result['count'] = self.count
return result
[docs]
@classmethod
def from_dict(cls, data: dict[str, Any]) -> UndoData:
"""Create from dictionary."""
return cls(
undo_record_id=str(data.get('undo_record_id', '')),
action_type=str(data.get('action_type', '')),
entity_type=str(data.get('entity_type', '')),
entity_id=str(data.get('entity_id', '')),
count=int(data.get('count', 0)),
)
[docs]
@dataclass
class UpdateBoard:
"""
Response structure for board update operations.
Example:
.. code-block:: python
response = {
'success': True,
'undo_data': {
'undo_record_id': 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
'action_type': 'modify_project',
'entity_type': 'Board',
'entity_id': 987654321,
'count': 1,
},
}
"""
id: str = ''
"""The id of the updated board"""
name: str = ''
"""The name of the updated board"""
updated_attribute: str = ''
"""The value of the updated attribute"""
previous_attribute: str | None = None
"""The value of the attribute before it was updated"""
success: bool = False
"""Whether the update operation was successful"""
undo_data: UndoData | None = None
"""Information needed to undo the update operation"""
[docs]
def to_dict(self) -> dict[str, Any]:
"""Convert to dictionary for API requests."""
result = {}
if self.id:
result['id'] = self.id
if self.name:
result['name'] = self.name
if self.updated_attribute:
result['updated_attribute'] = self.updated_attribute
if self.previous_attribute:
result['previous_attribute'] = self.previous_attribute
if self.success:
result['success'] = self.success
if self.undo_data:
result['undo_data'] = self.undo_data.to_dict()
return result
[docs]
@classmethod
def from_dict(cls, data: dict[str, Any]) -> UpdateBoard:
"""Create from dictionary."""
return cls(
id=data.get('id', ''),
name=data.get('name', ''),
updated_attribute=data.get('updated_attribute', ''),
previous_attribute=data.get('previous_attribute', ''),
success=data.get('success', False),
undo_data=UndoData.from_dict(data['undo_data'])
if data.get('undo_data')
else None,
)