Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion monday/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
UserResource, GroupResource, ComplexityResource, WorkspaceResource, NotificationResource, MeResource

_DEFAULT_HEADERS = {
"API-Version": "2023-10"
"API-Version": "2026-01"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any breaking changes?

}

DEFAULT_TIMEOUT = 60
Expand Down
35 changes: 35 additions & 0 deletions monday/query_joins.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,37 @@ def mutate_subitem_query(parent_item_id, subitem_name, column_values,
str(create_labels_if_missing).lower())


def mutate_multiple_items_query(items_data):
mutation_parts = []
for index, item_data in enumerate(items_data):
method = item_data.pop('method')
params = ', '.join([f"{key}: %s" for key in item_data.keys()])
values = [
monday_json_stringify(value) if not isinstance(value, (int, bool, str)) else
(str(value).lower() if isinstance(value, bool) else
f'"{value}"' if isinstance(value, str) else value)
for value in item_data.values()
]

mutation_part = f'''
item{index}: {method}({params}) {{
id
name
column_values {{
id
text
}}
}}
''' % tuple(values)

mutation_parts.append(mutation_part)

query = '''mutation {
%s
}''' % ' '.join(mutation_parts)
return query


def get_item_query(board_id, column_id, value, limit=None, cursor=None):
if not isinstance(value, list):
value = [value]
Expand Down Expand Up @@ -192,6 +223,10 @@ def create_column(
column_title,
)
else:
if isinstance(column_type, str):
column_type = ColumnType(column_type)

column_type.is_defaults_have_recommended_keys(defaults)
query = """mutation{
create_column(board_id: %s, title: "%s", description: "%s", column_type: %s, defaults: %s) {
id
Expand Down
8 changes: 6 additions & 2 deletions monday/resources/items.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from monday.query_joins import mutate_item_query, get_item_query, update_item_query, get_item_by_id_query, \
update_multiple_column_values_query, mutate_subitem_query, add_file_to_column_query, delete_item_query, \
archive_item_query, move_item_to_group_query
archive_item_query, move_item_to_group_query, mutate_multiple_items_query
from monday.resources.base import BaseResource


Expand All @@ -17,6 +17,10 @@ def create_subitem(self, parent_item_id, subitem_name, column_values=None,
create_labels_if_missing)
return self.client.execute(query)

def mutate_multiple_items(self, items_data):
query = mutate_multiple_items_query(items_data)
return self.client.execute(query)

def fetch_items_by_column_value(self, board_id, column_id, value, limit=None, cursor=None):
query = get_item_query(board_id, column_id, value, limit, cursor)
return self.client.execute(query)
Expand Down Expand Up @@ -45,7 +49,7 @@ def move_item_to_group(self, item_id, group_id):
def archive_item_by_id(self, item_id):
query = archive_item_query(item_id)
return self.client.execute(query)

def delete_item_by_id(self, item_id):
query = delete_item_query(item_id)
return self.client.execute(query)
42 changes: 38 additions & 4 deletions monday/resources/types.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,41 @@
from enum import Enum
from typing import Mapping
from warnings import warn


class DuplicateType(Enum):
"""Board duplication types"""

WITH_STRUCTURE = "duplicate_board_with_structure"
WITH_PULSES = "duplicate_board_with_pulses"
WITH_PULSES_AND_UPDATES = "duplicate_board_with_pulses_and_updates"


class ColumnType(Enum):
AUTO_NUMBER = "auto_number" # Number items according to their order in the group/board
AUTO_NUMBER = (
"auto_number" # Number items according to their order in the group/board
)
CHECKBOX = "checkbox" # Check off items and see what's done at a glance
COUNTRY = "country" # Choose a country
COLOR_PICKER = "color_picker" # Manage a design system using a color palette
CREATION_LOG = "creation_log" # Add the item's creator and creation date automatically
CREATION_LOG = (
"creation_log" # Add the item's creator and creation date automatically
)
DATE = "date" # Add dates like deadlines to ensure you never drop the ball
DEPENDENCY = "dependency" # Set up dependencies between items in the board
DROPDOWN = "dropdown" # Create a dropdown list of options
EMAIL = "email" # Email team members and clients directly from your board
FILE = "file" # Add files & docs to your item
HOUR = "hour" # Add times to manage and schedule tasks, shifts and more
ITEM_ID = "item_id" # Show a unique ID for each item
LAST_UPDATED = "last_updated" # Add the person that last updated the item and the date
LAST_UPDATED = (
"last_updated" # Add the person that last updated the item and the date
)
LINK = "link" # Simply hyperlink to any website
BOARD_RELATION = "board_relation" # Relationship with another board
LOCATION = "location" # Place multiple locations on a geographic map
LONG_TEXT = "long_text" # Add large amounts of text without changing column width
MIRROR = "mirror" # Display a value from another board through a linked item. If linked item is in another board, BOARD_RELATION also needs to be set up in the board
NUMBERS = "numbers" # Add revenue, costs, time estimations and more
PEOPLE = "people" # Assign people to improve team work
PHONE = "phone" # Call your contacts directly from monday.com
Expand All @@ -35,11 +46,34 @@ class ColumnType(Enum):
TAGS = "tags" # Add tags to categorize items across multiple boards
TEXT = "text" # Add textual information e.g. addresses, names or keywords
TIMELINE = "timeline" # Visually see a breakdown of your team's workload by time
TIME_TRACKING = "time_tracking" # Easily track time spent on each item, group, and board
TIME_TRACKING = (
"time_tracking" # Easily track time spent on each item, group, and board
)
VOTE = "vote" # Vote on an item e.g. pick a new feature or a favorite lunch place
WEEK = "week" # Select the week on which each item should be completed
WORLD_CLOCK = "world_clock" # Keep track of the time anywhere in the world

def is_defaults_have_recommended_keys(self, defaults: Mapping[str, any] = None):
defaults = defaults or {}
if self == ColumnType.MIRROR:
mirror_keys = ("relation_column", "displayed_linked_columns")
missing_keys = [key for key in mirror_keys if key not in defaults]
if missing_keys:
warn(
"Defaults for mirror column type missing recommended "
f"keys: {missing_keys}. Column will appear blank.",
UserWarning,
)
elif self == ColumnType.BOARD_RELATION:
relation_keys = ("boardId", "boardIds")
if not any(key in defaults for key in relation_keys):
warn(
"Defaults for board_relation column type missing "
"recommended keys: 'boardId' or 'boardIds'. "
"Items from the related board(s) will not be linkable.",
UserWarning,
)


class BoardKind(Enum):
"""Board kinds"""
Expand Down
67 changes: 66 additions & 1 deletion monday/tests/test_item_resource.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from monday.tests.test_case_resource import BaseTestCase
from monday.query_joins import mutate_item_query, get_item_query, update_item_query, get_item_by_id_query, \
update_multiple_column_values_query, mutate_subitem_query, add_file_to_column_query, delete_item_query, \
archive_item_query, move_item_to_group_query
archive_item_query, move_item_to_group_query, mutate_multiple_items_query
from monday.utils import monday_json_stringify


Expand Down Expand Up @@ -104,3 +104,68 @@ def test_move_item_to_group_query(self):
query = move_item_to_group_query(item_id=self.item_id, group_id=self.group_id)
self.assertIn(str(self.item_id), query)
self.assertIn(str(self.group_id), query)

def test_mutate_multiple_items_query(self):
items_data = [
{
'method': 'create_item',
'board_id': self.board_id,
'group_id': self.group_id,
'item_name': 'Test Item 1',
'column_values': {'text': 'Test Value 1'}
},
{
'method': 'create_item',
'board_id': self.board_id,
'group_id': self.group_id,
'item_name': 'Test Item 2',
'column_values': {'text': 'Test Value 2'}
}
]
query = mutate_multiple_items_query(items_data)

# Test for item 1
item1_format = (
'item0: create_item(board_id: %s, group_id: %s, '
'item_name: "%s", column_values: %s)'
)
item1_args = (
self.board_id, self.group_id, 'Test Item 1',
monday_json_stringify({'text': 'Test Value 1'})
)
item1_query = item1_format % item1_args

# Test for item 2
item2_format = (
'item1: create_item(board_id: %s, group_id: %s, '
'item_name: "%s", column_values: %s)'
)
item2_args = (
self.board_id, self.group_id, 'Test Item 2',
monday_json_stringify({'text': 'Test Value 2'})
)
item2_query = item2_format % item2_args

self.assertIn(item1_query, query)
self.assertIn(item2_query, query)

def test_get_item_by_id_query_with_multiple_ids(self):
item_ids = [123, 456, 789]
query = get_item_by_id_query(item_ids)
expected_query = '''query
{
items (ids: [123, 456, 789]) {
id,
name,
group {
id
title
}
column_values {
id,
text,
value
}
}
}'''
self.assertEqual(expected_query.strip(), query.strip())
50 changes: 50 additions & 0 deletions monday/tests/test_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import unittest
from unittest.mock import patch
from monday.resources.types import ColumnType


class TypesTestCase(unittest.TestCase):

def setUp(self):
self.defaults_mirror_missing = {}
self.defaults_mirror_correct = {
"relation_column": "some_id",
"displayed_linked_columns": ["name"]
}
self.defaults_board_relation_missing = {}
self.defaults_board_relation_correct = {
"boardId": 123
}

@patch('monday.resources.types.warn')
def test_is_defaults_have_recommended_keys_mirror_missing(self, mock_warn):
ColumnType.MIRROR.is_defaults_have_recommended_keys(
self.defaults_mirror_missing)
mock_warn.assert_called_once()
self.assertIn("missing recommended keys", mock_warn.call_args[0][0])

@patch('monday.resources.types.warn')
def test_is_defaults_have_recommended_keys_mirror_correct(self, mock_warn):
ColumnType.MIRROR.is_defaults_have_recommended_keys(
self.defaults_mirror_correct)
mock_warn.assert_not_called()

@patch('monday.resources.types.warn')
def test_is_defaults_have_recommended_keys_board_relation_missing(
self, mock_warn):
ColumnType.BOARD_RELATION.is_defaults_have_recommended_keys(
self.defaults_board_relation_missing)
mock_warn.assert_called_once()
self.assertIn("missing recommended keys", mock_warn.call_args[0][0])

@patch('monday.resources.types.warn')
def test_is_defaults_have_recommended_keys_board_relation_correct(
self, mock_warn):
ColumnType.BOARD_RELATION.is_defaults_have_recommended_keys(
self.defaults_board_relation_correct)
mock_warn.assert_not_called()

@patch('monday.resources.types.warn')
def test_is_defaults_have_recommended_keys_other_type(self, mock_warn):
ColumnType.TEXT.is_defaults_have_recommended_keys({})
mock_warn.assert_not_called()