├── .github └── workflows │ └── pypa-publish.yml ├── .gitignore ├── .idea ├── .gitignore ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── monday-api-python-sdk.iml └── vcs.xml ├── DEVELOPMENT.md ├── LICENSE ├── README.md ├── pylintrc ├── pyproject.toml ├── setup.cfg ├── setup.py └── src └── monday_sdk ├── __init__.py ├── client.py ├── constants.py ├── exceptions.py ├── graphql_handler.py ├── modules ├── __init__.py ├── activity_logs.py ├── boards.py ├── custom.py ├── items.py └── updates.py ├── query_templates.py ├── types ├── __init__.py ├── api_response_types.py ├── client_settings.py └── monday_enums.py └── utils.py /.github/workflows/pypa-publish.yml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package to PyPI when a Release is Created 2 | 3 | on: 4 | release: 5 | types: [created] 6 | jobs: 7 | pypi-publish: 8 | name: Upload release to PyPI 9 | runs-on: ubuntu-latest 10 | permissions: 11 | id-token: write # IMPORTANT: this permission is mandatory for trusted publishing 12 | steps: 13 | - uses: actions/create-github-app-token@v1 14 | id: app-token 15 | with: 16 | # required 17 | app-id: ${{ vars.APP_ID }} 18 | private-key: ${{ secrets.PRIVATE_KEY }} 19 | - uses: actions/checkout@v4 20 | with: 21 | token: ${{ steps.app-token.outputs.token }} 22 | ref: ${{ github.head_ref }} 23 | # Make sure the value of GITHUB_TOKEN will not be persisted in repo's config 24 | persist-credentials: false 25 | - name: Set up Python 26 | uses: actions/setup-python@v4 27 | with: 28 | python-version: "3.x" 29 | - name: Install dependencies 30 | run: | 31 | python -m pip install --upgrade pip 32 | pip install setuptools wheel 33 | - name: Build package 34 | run: | 35 | python setup.py sdist bdist_wheel # Could also be python -m build 36 | - name: Publish package distributions to PyPI 37 | uses: pypa/gh-action-pypi-publish@release/v1 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *$py.class 4 | *.so 5 | .Python 6 | build/ 7 | develop-eggs/ 8 | dist/ 9 | downloads/ 10 | eggs/ 11 | .eggs/ 12 | lib/ 13 | lib64/ 14 | parts/ 15 | sdist/ 16 | var/ 17 | wheels/ 18 | share/python-wheels/ 19 | *.egg-info/ 20 | .installed.cfg 21 | *.egg 22 | MANIFEST 23 | *.manifest 24 | *.spec 25 | pip-log.txt 26 | pip-delete-this-directory.txt 27 | htmlcov/ 28 | .tox/ 29 | .nox/ 30 | .coverage 31 | .coverage.* 32 | .cache 33 | nosetests.xml 34 | coverage.xml 35 | *.cover 36 | *.py,cover 37 | .hypothesis/ 38 | .pytest_cache/ 39 | cover/ 40 | *.mo 41 | *.pot 42 | .ipynb_checkpoints 43 | profile_default/ 44 | ipython_config.py 45 | .env 46 | .venv 47 | env/ 48 | venv/ 49 | ENV/ 50 | env.bak/ 51 | venv.bak/ 52 | .pyre/ 53 | .pytype/ 54 | cython_debug/ 55 | .idea/ 56 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 64 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/monday-api-python-sdk.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # Development documentation 2 | 3 | Source of information: https://packaging.python.org/ 4 | 5 | ## Prepare the local environment 6 | 7 | * https://packaging.python.org/en/latest/tutorials/installing-packages/ 8 | 9 | ## Install package from the local copy 10 | 11 | * https://packaging.python.org/en/latest/tutorials/installing-packages/#installing-from-a-local-src-tree 12 | * https://setuptools.pypa.io/en/latest/userguide/development_mode.html 13 | 14 | ``` 15 | python -m venv .venv 16 | source .venv/bin/activate 17 | pip install --editable . 18 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Developer Terms 2 | Welcome to our API and SDK! These Terms along with any other terms and policies referenced and incorporated by reference herein, as may be amended from time to time (the “Terms”) constitute a legally binding agreement as of the Effective Date (as defined below), governing your access to, and the use of, our application program interfaces, software, or accompanying documentation or materials (collectively, the “API”) and our software development kit or accompanying documentation or materials (collectively, the “SDK”) so please read these Terms carefully before accessing or using the API or downloading, installing or using the SDK. The Terms is between monday.com Ltd. (“monday.com”, “us”, “we” or “our”) and you, either individually, or on behalf of your employer or any other entity which you represent (“you” or “your”). In case you represent your employer or another entity, you hereby represent that (a) you have full legal authority to bind your employer or such entity (as applicable) to the Terms; and (b) after reading and understanding the Terms, you agree to the Terms on behalf of your employer or the respective entity (as applicable), and the Terms will bind your employer or such entity (as the case may be). YOU ACKNOWLEDGE THAT THE TERMS ARE BINDING, AND YOU AFFIRM AND SIGNIFY YOUR CONSENT TO THE TERMS, BY EITHER: (I) CLICKING ON A BUTTON OR CHECKING A CHECKBOX FOR THE ACCEPTANCE OF THE TERMS; OR (II) ACCESSING OR USING THE API AND DOWNLOADING, INSTALLING OR USING THE SDK, WHICHEVER IS EARLIER (THE “EFFECTIVE DATE”). IF YOU DO NOT AGREE TO COMPLY WITH, AND BE BOUND BY, THE TERMS OR DO NOT HAVE AUTHORITY TO BIND YOUR EMPLOYER OR ANY OTHER ENTITY (AS APPLICABLE), PLEASE DO NOT ACCEPT THE TERMS OR USE THE API AND SDK. 3 | 4 | 1. Permissions – What you are allowed to do with our API and SDK 5 | API License. Subject to your compliance with the Terms, monday.com grants you a limited, non-transferable, nonexclusive, revocable royalty-free license, without a right to sublicense, to use the API to develop, implement, integrate, and interface your application (“App“) with the monday.com Services as defined in our Terms of Service and distribute Your App to end users that are also customers of monday.com Service. 6 | 7 | SDK License. Subject to your compliance with the Terms, monday.com grants you a limited, non-transferrable, non-exclusive, revocable royalty-free license (a) without a right to sublicense, to download, install, and use the SDK to develop your App and if required by the functionality of your App to embed the monday.com software contained in the SDK into your App that communicates with the Services; and (b) to distribute and sub-license the monday.com software, only as embedded through use of the SDK, only as included in your App. 8 | 9 | License Restrictions. Except as expressly authorized under these Terms, you may not: (a) use, copy, modify, display, distribute, transfer, or sublicense any portion of the API or SDK; (b) make the functionality of the API or SDK available to any third party through any means, including, without limitation, any hosting, application services provider, service bureau, or other type of service; or (c) use the monday.com name, trademarks logos, anything confusingly similar or anything that may create a connotation of false-endorsement by monday.com, in connection with any App created by you, or in a manner that creates a sense of endorsement, sponsorship, or false association with monday.com. You acknowledge and agree that portions of the API, SDK and Services, including, without limitation, the source code and the specific design and structure of individual modules or programs, constitute or contain trade secrets of monday.com and its licensors. Accordingly, you agree not to disassemble, decompile, or otherwise reverse engineer the API, SDK or Services, in whole or in part, or to permit or authorize a third party to do so, except to the extent that such activities are expressly permitted by law notwithstanding this prohibition. You agree to fully comply with all U.S. export laws and regulations or any other applicable export laws and regulations to ensure that neither the API, SDK and Services, any technical data related thereto, nor any direct product thereof are exported or re-exported directly or indirectly in violation of, or used for any purposes prohibited by, such laws and regulations. 10 | 11 | 2. API Access Requirements. 12 | Each App must maintain absolute compatibility with the API in order to be granted access to the API and Services, including by (a) applying all SDK and API updates provided by monday.com, (b) providing all functionalities identified as critical by monday.com, and (c) supporting any standards (including encryption standards) required by monday.com. You agree to abide by any limitations on access, calls, or use of the API or Services (any “Service Limits”) that may be set by monday.com and will not attempt to circumvent such Service Limits without the separate prior written consent of monday.com. You may not use or access the API or Services for the purposes of monitoring the availability, performance, or functionality of the API or Services or for any other benchmarking or competitive purpose. 13 | 14 | 3. Updates and Support. 15 | monday.com may, but is under no obligation to maintain, support, update, or provide error corrections for the API or SDK. If monday.com provides you with an update or maintenance release for the API or SDK, unless you receive a separate license from monday.com for that update or release that expressly supersedes these Terms, such update or release will be subject to the terms and conditions of these Terms. 16 | 17 | 4. Your App. 18 | If you offer your App for use by others outside your organization, you agree to do so only subject to the terms of a user agreement (“EULA”) and privacy policy (“Privacy Policy”) for your App, which will be presented to users before they download or access your App. You agree that the EULA and Privacy Policy will comply with all applicable law, and that you will obtain and maintain all consents and permissions required in connection with the collection, use, storage and sharing of data in clear, understandable and accurate terms. You agree that each EULA and Privacy Policy will be at least as protective of monday.com’s proprietary rights in the API and SDK as those contained in the Terms and which will include, without limitation, terms for monday.com’s benefit regarding (a) restrictions on reverse engineering (to the maximum extent permitted by applicable law); (b) disclaimer of warranties; and (c) limitation of liability. You must obtain the explicit consent of the end user before collecting, using, posting, or sharing any Customer Data obtained through the API on an end user’s behalf. Mere authorization of your application by the end user does not constitute consent. In no event shall you or the App engage in any transmission, storage, accessor other use of Customer Data outside the scope of what the end user consented. In respect of any third party “open source” software or other third party intellectual property in any App (“Third Party Code”): (1) You will fully comply with any terms and conditions governing such Third Party Code, including without limitations, displaying any attributions, copyright information and other notices, terms and conditions that may be required to be provided to end users based on your use of such Third Party Code; (2) you shall use only Third Party Code that does not impose any obligation on, or affect monday.com, the end users, or any aspect of our Services (including our Marketplace) and related intellectual property, on an ordinary use and exploitation of your App (including as indicated in any terms applicable between you and monday.com). 19 | 20 | 5. Services Data. 21 | Results, usage statistics, data, or information (in the aggregate or otherwise) derived from your or your App’s use of the API, SDK or Services (“Services Data”) may be used only for your internal business purposes. 22 | 23 | 6. Acceptable Use. 24 | You may not use or access the API, SDK or Services for any unlawful purpose, for any purpose not expressly authorized hereunder, or in any manner that is inconsistent with the Terms. In addition, you agree that your Apps will not: 25 | 26 | contain any content that: (i) infringes, misappropriates or violates a third party’s patent, copyright, trademark, trade secret, moral rights or other intellectual property rights, or rights of publicity or privacy; (ii) violates, or encourages any conduct that would violate, any applicable law or regulation or would give rise to civil liability; (iii) is fraudulent, false, misleading or deceptive; (iv) is defamatory, obscene, pornographic, vulgar or offensive; (v) promotes discrimination, bigotry, racism, hatred, harassment or harm against any individual or group; (vi) is violent or threatening or promotes violence or actions that are threatening to any person or entity; or (vii) promotes illegal or harmful activities or substances; 27 | 28 | use, display, mirror or frame the Services or any individual element within the Services, without monday.com’s express written consent; 29 | 30 | access, tamper with, or use non-public areas of the Services, monday.com’s computer systems, or the technical delivery systems of monday.com’s providers; 31 | 32 | attempt to probe, scan or test the vulnerability of any monday.com system or network or breach any security or authentication measures; 33 | 34 | avoid, bypass, remove, deactivate, impair, descramble or otherwise circumvent any technological measure implemented by monday.com or any of monday.com’s providers or any other third party (including another user) to protect the Services; 35 | 36 | collect or store any personally identifiable information from the Services from other users of the Services without their express permission; or 37 | 38 | violate any applicable law or regulation. 39 | 40 | use Third Party Code that impose any obligation on, or affect monday.com, the end users, or any aspect of our Services, and related intellectual property, on an ordinary use or any exploitation of your App. 41 | 42 | 7. End User Data Protection 43 | (a) Data Collection. In connection with your use of the API, we may provide you with access to data or information (including personal data) associated with your Apps, and you may also collect other information, content, Customer Data or any other data from the end users who have installed your App, either as part of the App’s performance and activities, or data you collect from the end users in connection with their usage of the App or any other lawful reason, which may include personal data. Any data, content, Customer Data or information provided or collected pursuant to this clause (a) is collectively “End User Data“. 44 | 45 | (b) Use of End User Data. You agree that you will: (i) use, process, share, transfer and limit data collected from API calls to End User Data as has been specifically authorized by the end user or that is necessary and lawfully authorized for usage for the purposes of providing and improving the functionality of your App, all as specifically detailed in your Privacy Policy or any other Data Protection Agreement with the end user, governing your collection and use of the End User Data; and (ii) treat, store, transmit, use, or otherwise process the End User Data only in accordance with this Agreement, your Privacy Policy and your agreements with each respective end users and all applicable laws, rules, regulations, orders, and other requirements of governmental agencies the “Laws“); and (iii) comply with any and all data subject rights of the end users, including without limitation, the right to delete, rescind and review the End User Data. monday.com shall not be liable for, or have any responsibility in connection with, End User Data processed, used, shared or transferred by you or your App. 46 | 47 | (c) Security. You shall maintain and handle all End User Data in accordance with: (i) privacy and security measures adequate to preserve its confidentiality and security and (ii) all applicable Laws. You agree to implement appropriate technical and organizational measures (1) to ensure a level of security appropriate for the processing operations you undertake, and (2) that are, in any case, no less than measures consistent with industry standard practices. In addition, you shall adhere to the security requirements set forth in the app developer documentation and any other requirements provided to you by monday.com. 48 | 49 | (d) Incident Notification. In the event of any actual, alleged or suspected (i) unauthorized access, acquisition, use, disclosure, modification, loss or destruction of End User Data, whether intentional or accidental, (ii) security vulnerability or compromise of an App, or (iii) an issue that materially degrades monday.com’s systems or networks (collectively, an “Incident”), you shall promptly notify monday.com, and in no event later than 24 hours of first being aware of such Incident, and such notification must be prior to any notification to the end users. At our request, you will provide monday.com with further information and prompt assistance related to the Incident, including information regarding how it may affect monday.com and the end users. You shall investigate and remediate the Incidence, at your sole expense, in accordance with all applicable Laws and contractual obligations, including notification obligations, and you shall fully indemnify monday.com for any cost, expense or damage that monday.com may incur in connection with such Incident. 50 | 51 | (e) Deletion of End User Data upon App Deactivation. When we or an end user de-authorizes, deactivates, uninstalls or otherwise terminates an App, you shall either: (1) permanently delete all End User Data and any meta-data that was collected, transmitted, created or received by the App, within 30 days; or (2) Obtain express, written consent from the end user to retain End User Data longer than 30 days, provided such consent is clear and explicit. In the event that you retain End User Data, you must continue to maintain the such End User Data according to the your Privacy Policy and the EULA with the end user and continue to maintain any security measures that were in effect at the time of collection. End User Data must be deleted at any time an end user withdraws their consent to the collection and use of the End User Data. You must notify monday.com at marketplace@monday.com of your compliance with End User Data deletion or when further consent is obtained from the end user. 52 | 53 | 8. Ownership. 54 | The API and SDK are licensed, not sold, and monday.com retains ownership thereof, including all intellectual property rights therein and reserves all rights not expressly granted to you in these Terms. 55 | 56 | 9. Feedback. 57 | As a developer, you may provide suggestions, comments, feature requests or other feedback to any of monday.com Materials, the monday.com Service or the API (“Feedback”). Such Feedback is deemed an integral part of monday.com Materials, and as such, it is the sole property of monday.com without restrictions or limitations on use of any kind. monday.com may either implement or reject such Feedback, without any restriction or obligation of any kind. 58 | 59 | 10. Term and Termination. 60 | The Terms remains effective as of the Effective Date until terminated. You may terminate the Terms at any time by providing notice of such termination to monday.com. If you breach any term or condition of this Terms, monday.com may, at its sole discretion, without notice: (a) suspend your and your App’s access to the API, SKI or Services; (b) terminate all licenses or permissions granted hereunder; or (c) terminate these Terms. monday.com may, at its sole discretion, terminate this Terms or any licenses or permissions granted hereunder, by providing you with thirty (30) days’ notice of such termination. Upon termination, (x) all licenses or permissions granted to you hereunder will terminate; (y) you will terminate your App’s access to and use of the API and SDK; and (z) you will, within fifteen (15) days of termination, destroy all copies of the API, SDK and Services Data and any confidential information of monday.com. Sections 1 (with respect to License Restrictions only), 6, 7, 8, 10, 11, 12, 13, 14, 16 and 17 will survive termination of these Terms. 61 | 62 | 11. No Warranty. 63 | THE API AND SDK ARE PROVIDED ON AN “AS IS” AND “AS AVAILABLE” BASIS, WITHOUT WARRANTY OF ANY KIND. monday.com EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT, AND ANY WARRANTIES AND CONDITIONS ARISING OUT OF COURSE OF DEALING OR USAGE OF TRADE RELATIVE TO THE API OR SDK. NO ADVICE OR INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED FROM ELSEWHERE WILL CREATE ANY WARRANTY OR CONDITION NOT EXPRESSLY STATED IN THESE TEMS. 64 | 65 | 12. Limitation of Liability. 66 | monday.com’S TOTAL LIABILITY TO YOU FROM ALL CAUSES OF ACTION AND UNDER ALL THEORIES OF LIABILITY UNDER THESE TERMS WILL BE LIMITED TO AND WILL NOT EXCEED ONE HUNDERD DOLLARS. EXCEPT FOR BREACH OF SECTION ‎11 AND YOUR INDEMNITY OBIGATIONS UNDER SECTION ‎12, IN NO EVENT WILL EITHER PARTY BE LIABLE TO THE OTHER FOR ANY SPECIAL, INCIDENTAL, EXEMPLARY, PUNITIVE OR CONSEQUENTIAL DAMAGES (INCLUDING LOSS OF USE, DATA, BUSINESS OR PROFITS) OR FOR THE COST OF PROCURING SUBSTITUTE PRODUCTS ARISING OUT OF OR IN CONNECTION WITH THESE TERMS OR THE USE OR PERFORMANCE OF THE API OR SDK, WHETHER SUCH LIABILITY ARISES FROM A CLAIM BASED UPON CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY, OR OTHERWISE, AND WHETHER OR NOT SUCH PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE. THE FOREGOING LIMITATIONS WILL SURVIVE AND APPLY EVEN IF ANY LIMITED REMEDY SPECIFIED IN THESE TERMS IS FOUND TO HAVE FAILED OF ITS ESSENTIAL PURPOSE. 67 | 68 | 13. Indemnity. 69 | You agree to defend, indemnify, and hold monday.com harmless from and against any liabilities, losses, damages, judgments, fines, penalties, costs and expenses (including reasonable attorneys’ fees and court costs), as incurred, arising out of or resulting from any third-party claim, action, or proceeding brought against monday.com (any “Claim”) arising from your App or use of the API of SDK (including without limitations, any alleged infringement, violation, misappropriation of any third party right (e.g. intellectual property rights and privacy rights) or your breach of these Terms. Upon receiving a Claim, monday.com being shall provide written notice to you and allow you to assume control over the defense and/or settlement of the claim, provided that monday.com reserves the right to retain counsel, at its own expense, to participate in the defense and settlement of the Claim. 70 | 71 | 14. Confidentiality. 72 | The API and SDK are the confidential information of monday.com and you agree not to use the API and SDK except as expressly authorized hereunder and as necessary to exercise your rights under these Terms. Except as expressly authorized hereunder, you will not disclose monday.com’s confidential information to any third party. Notwithstanding the forgoing, you may disclose monday.com’s confidential information to those of your employees and subcontractors that need to know such confidential information for the purpose of performing under these Terms, provided that each such employee or subcontractor is subject to a written agreement that includes binding use and disclosure restrictions that are at least as protective as those set forth herein. You agree to use all reasonable efforts to maintain the confidentiality of monday.com’s confidential information, but in no event less than the efforts that you ordinarily use with respect to your own proprietary information of similar importance. 73 | 74 | 15. Modification. 75 | monday.com may modify the terms of these Terms, at any time and in its sole discretion, by providing you with written notice of such modification, including by posting the new terms on the monday.com’s website. If any noticed modification to these Terms is unacceptable to you, your sole recourse will be to terminate these Terms in accordance with Section 8 above. Your access and/or use of the API or SDK after receiving such notice will constitute your acceptance of the noticed modifications. 76 | 77 | 16. Governing Law and Jurisdiction. 78 | These Terms and any action related thereto will be governed and interpreted by and under the laws of the State of Israel without giving effect to any conflicts of laws principles that require the application of the law of a different jurisdiction. Courts of competent jurisdiction located in Tel Aviv-Jaffa, Israel, will have the sole and exclusive jurisdiction and venue over all controversies and claims arising out of, or relating to, these Terms. You and us mutually agree that the United Nations Convention on Contracts for the International Sale of Goods does not apply to this Terms. 79 | 80 | 17. General. 81 | These Terms is written in English, and translated into other languages for your convenience. If a translated (non-English) version of these Terms conflicts in any way with the English version, the provisions of the English version will prevail. Neither us nor you will be liable by reason of any failure or delay in the performance of its obligations on account of events beyond the reasonable control of a party, which may include denial-of-service attacks, interruption or failure of the Internet or any utility service, failures in third-party hosting services, strikes, shortages, riots, fires, acts of God, war, pandemics, terrorism, and governmental action. The parties are independent contractors. These Terms does not create a partnership, franchise, joint venture, agency, fiduciary or employment relationship between the parties. There are no third party beneficiaries to these Terms. We will use your contact details that we have in our records, in connection with providing you notices, as provided herein. Our contact details for any notices are detailed below. You acknowledge that notices that we provide you, in connection with these Terms, will be provided via e-mail or otherwise in connection with your use of the API and SDK. Any notice to you will be deemed given upon the earlier of: (i) receipt; or (ii) 24 hours of delivery. Notices to us will be provided to monday.com Ltd., attn: General Counsel, at legal@monday.com, or sent to 52 Menachem Begin Road, Tel Aviv 6713701, Israel. These Terms, and any and all rights and obligations hereunder, may not be transferred or assigned by you without our written approval. We may assign our rights and/or obligations hereunder without your consent or prior notice to you. Subject to the foregoing conditions, this Terms will bind and inure to the benefit of the parties, their respective successors, and permitted assigns. Any assignment not authorized herein will be null and void. These Terms will be enforced to the fullest extent permitted under applicable law. If any provision of these Terms is held by a court of competent jurisdiction to be contrary to law, the provision will be modified by the court and interpreted so as best to accomplish the objectives of the original provision to the fullest extent permitted by law, and the remaining provisions of these Terms will remain in effect. No failure or delay by either party in exercising any right under these Terms will constitute a waiver of that right. No waiver under these Terms will be effective unless made in writing and signed by an authorized representative of the party being deemed to have granted the waiver. 82 | 83 | Last Updated: October 16, 2020 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # monday-api-python-sdk 2 | 3 | A Python SDK for interacting with Monday"s GraphQL API. 4 | 5 | ## Table of Contents 6 | 7 | - [Installation](#installation) 8 | - [Usage](#usage) 9 | - [Examples](#examples) 10 | - [Authentication](#authentication) 11 | - [Response Types](#response-types) 12 | 13 | ## Installation 14 | 15 | To install the SDK, use pip: 16 | 17 | ```bash 18 | pip install monday-api-python-sdk 19 | ``` 20 | ## Authentication 21 | To use the SDK, you need to authenticate with your Monday API token: 22 | 23 | ```python 24 | from monday_sdk import MondayClient, MondayApiResponse, Board 25 | 26 | client = MondayClient(token="your_token") 27 | ``` 28 | 29 | ## Examples 30 | 31 | Here are some examples of how to use the SDK: 32 | 33 | ### Example 1: Create a new item 34 | ```python 35 | from monday_sdk import MondayClient 36 | 37 | client = MondayClient(token="your_token") 38 | 39 | column_values = { 40 | "status_column_id": "In Progress", # Replace with your actual status column ID and value 41 | "date_column_id": "2025-01-06", # Replace with your actual date column ID and date (YYYY-MM-DD format) 42 | "text_column_id": "Important task" # Replace with your actual text column ID and value 43 | } 44 | 45 | item = client.items.create_item( 46 | board_id="your_board_id", 47 | group_id="your_group_id", 48 | item_name="New Item", 49 | column_values=column_values 50 | ) 51 | 52 | print(item) 53 | ``` 54 | ### Example 2: Create an Update and Update Column Values 55 | ```python 56 | from monday_sdk import MondayClient, StatusColumnValue, DateColumnValue 57 | 58 | client = MondayClient(token="your_token") 59 | 60 | # Create an update for an item 61 | update_response = client.updates.create_update( 62 | item_id="your_item_id", 63 | update_value="This is a new update message for the item." 64 | ) 65 | 66 | # Change a status column value 67 | status_response = client.items.change_status_column_value( 68 | board_id="your_board_id", 69 | item_id="your_item_id", 70 | column_id="status_column_id", # Replace with the actual column ID 71 | value="Done" # Replace with the desired status value 72 | ) 73 | print(f"Status column updated: {status_response}") 74 | 75 | # Change a date column value 76 | date_response = client.items.change_date_column_value( 77 | board_id="your_board_id", 78 | item_id="your_item_id", 79 | column_id="date_column_id", 80 | timestamp="2025-01-06" 81 | ) 82 | ``` 83 | 84 | ## Response Types 85 | 86 | The SDK provides structured types to help you work with API responses more effectively. These types allow you to easily access and manipulate the data returned by the API. 87 | 88 | ### Available Types 89 | 90 | - `MondayApiResponse`: Represents the full response from a Monday API query, including data and account information. 91 | - `Data`: Holds the core data returned from the API, such as boards, items, and complexity details. 92 | - `Board`: Represents a Monday board, including items, updates, and activity logs. 93 | - `Item`: Represents an item on a board, including its details and associated subitems. 94 | - `Column`, `ColumnValue`: Represents columns and their values for an item. 95 | - `Group`: Represents a group within a board. 96 | - `User`: Represents a user associated with an update or activity log. 97 | - `Update`: Represents an update on an item. 98 | - `ActivityLog`: Represents an activity log entry on a board. 99 | - `ItemsPage`: Represents a paginated collection of items. 100 | 101 | ### Example Usage 102 | 103 | Here is an example of how to use these types with the SDK to deserialize API responses: 104 | ```python 105 | from monday_sdk import MondayClient 106 | 107 | client = MondayClient(token="your_token") 108 | items = client.boards.fetch_all_items_by_board_id(board_id="your_board_id") 109 | first_item_name = items[0].name 110 | print(f"First item name: {first_item_name}") 111 | ``` 112 | By using these types, you can ensure type safety and better code completion support in your IDE, making your work with the Monday API more efficient and error-free. 113 | -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | [MAIN] 2 | max-args = 6 3 | py-version = 3.8 4 | [FORMAT] 5 | max-line-length=120 -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools >= 61.0"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [project] 6 | name = "monday-api-python-sdk" 7 | requires-python = ">=3.8" 8 | dynamic = ["version"] 9 | dependencies = [ 10 | "dacite", 11 | "requests", 12 | ] 13 | authors = [ 14 | {name = "Michael Imas", email = "michaelim@monday.com"}, 15 | ] 16 | maintainers = [ 17 | {name = "Rafał Zawadzki", email = "rafalza@monday.com"}, 18 | ] 19 | readme = "README.md" 20 | description = "A Python SDK for interacting with Monday's GraphQL API." 21 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [options] 2 | package_dir = 3 | = src 4 | [metadata] 5 | license_files = LICENSE 6 | [flake8] 7 | max-line-length=120 8 | 9 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """A setuptools based setup module. 2 | """ 3 | 4 | # Always prefer setuptools over distutils 5 | from setuptools import setup, find_packages 6 | import pathlib 7 | 8 | here = pathlib.Path(__file__).parent.resolve() 9 | 10 | # Get the long description from the README file 11 | long_description = (here / "README.md").read_text(encoding="utf-8") 12 | 13 | # Arguments marked as "Required" below must be included for upload to PyPI. 14 | # Fields marked as "Optional" may be commented out. 15 | 16 | setup( 17 | name="monday-api-python-sdk", # Required 18 | version="1.3.1", # Required 19 | description="A Python SDK for interacting with Monday's GraphQL API", # Optional 20 | long_description=long_description, # Optional 21 | long_description_content_type="text/markdown", # Optional (see note above) 22 | url="https://github.com/mondaycom/monday-api-python-sdk", # Optional 23 | author="Michael Imas", 24 | author_email="michaelim@monday.com", 25 | license_files=("LICENSE",), 26 | classifiers=[ # Optional 27 | "Development Status :: 5 - Production/Stable", 28 | "Intended Audience :: Developers", 29 | "Topic :: Software Development :: Build Tools", 30 | # Pick your license as you wish 31 | # "License :: OSI Approved :: MIT License", 32 | # Specify the Python versions you support here. In particular, ensure 33 | # that you indicate you support Python 3. These classifiers are *not* 34 | # checked by 'pip install'. See instead 'python_requires' below. 35 | "Programming Language :: Python :: 3", 36 | "Programming Language :: Python :: 3.8", 37 | "Programming Language :: Python :: 3.9", 38 | "Programming Language :: Python :: 3.10", 39 | "Programming Language :: Python :: 3 :: Only", 40 | ], 41 | # This field adds keywords for your project which will appear on the 42 | # project page. What does your project relate to? 43 | # 44 | # Note that this is a list of additional keywords, separated 45 | # by commas, to be used to assist searching for the distribution in a 46 | # larger catalog. 47 | keywords="sample, setuptools, development", # Optional 48 | # When your source code is in a subdirectory under the project root, e.g. 49 | # `src/`, it is necessary to specify the `package_dir` argument. 50 | package_dir={"": "src"}, # Optional 51 | # You can just specify package directories manually here if your project is 52 | # simple. Or you can use find_packages(). 53 | # 54 | # Alternatively, if you just want to distribute a single Python file, use 55 | # the `py_modules` argument instead as follows, which will expect a file 56 | # called `my_module.py` to exist: 57 | # 58 | # py_modules=["my_module"], 59 | # 60 | packages=find_packages(where="src"), # Required 61 | # Specify which Python versions you support. In contrast to the 62 | # 'Programming Language' classifiers above, 'pip install' will check this 63 | # and refuse to install the project if the version does not match. See 64 | # https://packaging.python.org/guides/distributing-packages-using-setuptools/#python-requires 65 | python_requires=">=3.7, <4", 66 | # This field lists other packages that your project depends on to run. 67 | # Any package you put here will be installed by pip when your project is 68 | # installed, so they must be valid existing projects. 69 | # 70 | # For an analysis of "install_requires" vs pip's requirements files see: 71 | # https://packaging.python.org/discussions/install-requires-vs-requirements/ 72 | install_requires=["dacite", "requests"], # Optional 73 | # List additional groups of dependencies here (e.g. development 74 | # dependencies). Users will be able to install these using the "extras" 75 | # syntax, for example: 76 | # 77 | # $ pip install sampleproject[dev] 78 | # 79 | # Similar to `install_requires` above, these must be valid existing 80 | # projects. 81 | # extras_require={ # Optional 82 | # "dev": ["check-manifest"], 83 | # "test": ["coverage"], 84 | # }, 85 | # If there are data files included in your packages that need to be 86 | # installed, specify them here. 87 | # package_data={ # Optional 88 | # "sample": ["package_data.dat"], 89 | # }, 90 | # Entry points. The following would provide a command called `sample` which 91 | # executes the function `main` from this package when invoked: 92 | # entry_points={ # Optional 93 | # "console_scripts": [ 94 | # "sample=sample:main", 95 | # ], 96 | # }, 97 | # List additional URLs that are relevant to your project as a dict. 98 | # 99 | # This field corresponds to the "Project-URL" metadata fields: 100 | # https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use 101 | # 102 | # Examples listed include a pattern for specifying where the package tracks 103 | # issues, where the source is hosted, where to say thanks to the package 104 | # maintainers, and where to support the project financially. The key is 105 | # what's used to render the link text on PyPI. 106 | # project_urls={ # Optional 107 | # "Bug Reports": "https://github.com/pypa/sampleproject/issues", 108 | # "Funding": "https://donate.pypi.org", 109 | # "Say Thanks!": "http://saythanks.io/to/example", 110 | # "Source": "https://github.com/pypa/sampleproject/", 111 | # }, 112 | ) 113 | -------------------------------------------------------------------------------- /src/monday_sdk/__init__.py: -------------------------------------------------------------------------------- 1 | from .client import MondayClient 2 | from .types import * 3 | from .utils import * -------------------------------------------------------------------------------- /src/monday_sdk/client.py: -------------------------------------------------------------------------------- 1 | from .types import MondayClientSettings 2 | from .modules import BoardModule, ItemModule, UpdateModule, CustomModule, ActivityLogModule 3 | from .constants import API_VERSION, DEFAULT_MAX_RETRY_ATTEMPTS 4 | 5 | BASE_HEADERS = {"API-Version": API_VERSION} 6 | 7 | 8 | class MondayClient: 9 | def __init__(self, token, headers=None, debug_mode=False, max_retry_attempts=DEFAULT_MAX_RETRY_ATTEMPTS): 10 | 11 | headers = headers or BASE_HEADERS.copy() 12 | self.settings = MondayClientSettings(token, headers, debug_mode, max_retry_attempts) 13 | 14 | self.boards = BoardModule(self.settings) 15 | self.items = ItemModule(self.settings) 16 | self.updates = UpdateModule(self.settings) 17 | self.activity_logs = ActivityLogModule(self.settings) 18 | self.custom = CustomModule(self.settings) 19 | -------------------------------------------------------------------------------- /src/monday_sdk/constants.py: -------------------------------------------------------------------------------- 1 | API_URL = "https://api.monday.com/v2" 2 | API_VERSION = "2023-10" 3 | TOKEN_HEADER = "Authorization" 4 | MAX_COMPLEXITY = 10000000 # Monday's API complexity limit per minute 5 | BASE_HEADERS = {"API-Version": API_VERSION} 6 | 7 | DEFAULT_MAX_RETRY_ATTEMPTS = 3 8 | DEFAULT_PAGE_LIMIT_ITEMS = 500 9 | DEFAULT_PAGE_LIMIT_UPDATES = 1000 10 | DEFAULT_PAGE_LIMIT_BOARDS = 50 11 | DEFAULT_PAGE_LIMIT_ACTIVITY_LOGS = 1000 12 | -------------------------------------------------------------------------------- /src/monday_sdk/exceptions.py: -------------------------------------------------------------------------------- 1 | class MondayError(Exception): 2 | pass 3 | 4 | 5 | class MondayQueryError(MondayError): 6 | def __init__(self, message, original_errors=None): 7 | super().__init__(message) 8 | self.original_errors = original_errors or [] 9 | -------------------------------------------------------------------------------- /src/monday_sdk/graphql_handler.py: -------------------------------------------------------------------------------- 1 | import dacite 2 | import requests 3 | import json 4 | import time 5 | 6 | from .exceptions import MondayQueryError 7 | from .constants import API_URL, TOKEN_HEADER 8 | from .types import MondayApiResponse, MondayClientSettings 9 | 10 | 11 | class MondayGraphQL: 12 | """ 13 | GraphQL client that handles API interactions, response serialization, and error handling. 14 | """ 15 | 16 | def __init__(self, settings: MondayClientSettings): 17 | self.endpoint = API_URL 18 | self.token = settings.token 19 | self.headers = settings.headers 20 | self.debug_mode = settings.debug_mode 21 | self.max_retry_attempts = settings.max_retry_attempts 22 | 23 | def execute(self, query: str) -> MondayApiResponse: 24 | """ 25 | Executes a GraphQL query and handles errors and rate limits. 26 | 27 | Args: 28 | query (str): The GraphQL query to execute. 29 | 30 | Returns: 31 | MondayApiResponse: The deserialized response from the Monday API. 32 | """ 33 | current_attempt = 0 34 | 35 | while current_attempt < self.max_retry_attempts: 36 | 37 | if self.debug_mode: 38 | print(f"[debug_mode] about to execute query: {query}") 39 | 40 | try: 41 | response = self._send(query) 42 | 43 | if self.debug_mode: 44 | print(f"[debug_mode] response: {response}") 45 | 46 | if response.status_code == 429: 47 | print("Rate limit exceeded, response code 429 - sleeping for 30 seconds") 48 | time.sleep(30) 49 | current_attempt += 1 50 | continue 51 | 52 | response.raise_for_status() 53 | response_data = response.json() 54 | 55 | if response_data.get("error_code") == "ComplexityException": 56 | time.sleep(2) 57 | print("ComplexityException: retrying query") 58 | current_attempt += 1 59 | continue 60 | 61 | if "errors" in response_data: 62 | raise MondayQueryError(response_data["errors"][0]["message"], response_data["errors"]) 63 | 64 | try: 65 | serialized_result = dacite.from_dict(data_class=MondayApiResponse, data=response_data) 66 | return serialized_result 67 | 68 | except dacite.DaciteError as e: 69 | print(f"Error while deserializing response: {e}") 70 | raise MondayQueryError("Error while deserializing response", response_data) 71 | 72 | except (requests.HTTPError, json.JSONDecodeError, MondayQueryError) as e: 73 | print(f"Error while executing query: {e}") 74 | current_attempt += 1 75 | 76 | def _send(self, query: str): 77 | payload = {"query": query} 78 | headers = self.headers.copy() 79 | 80 | if self.token is not None: 81 | headers[TOKEN_HEADER] = self.token 82 | 83 | return requests.request("POST", self.endpoint, headers=headers, json=payload) 84 | -------------------------------------------------------------------------------- /src/monday_sdk/modules/__init__.py: -------------------------------------------------------------------------------- 1 | from .boards import BoardModule 2 | from .items import ItemModule 3 | from .updates import UpdateModule 4 | from .custom import CustomModule 5 | from .activity_logs import ActivityLogModule 6 | -------------------------------------------------------------------------------- /src/monday_sdk/modules/activity_logs.py: -------------------------------------------------------------------------------- 1 | from ..query_templates import get_activity_logs_query 2 | from ..types import MondayApiResponse, ActivityLog 3 | from ..graphql_handler import MondayGraphQL 4 | from ..constants import DEFAULT_PAGE_LIMIT_ACTIVITY_LOGS 5 | from typing import Optional, Union, List 6 | 7 | 8 | class ActivityLogModule(MondayGraphQL): 9 | 10 | def fetch_activity_logs_from_board( 11 | self, 12 | board_ids: Union[int, str], 13 | page: Optional[int] = 1, 14 | limit: Optional[int] = DEFAULT_PAGE_LIMIT_ACTIVITY_LOGS, 15 | from_date: Optional[str] = None, 16 | to_date: Optional[str] = None, 17 | ) -> MondayApiResponse: 18 | query = get_activity_logs_query(board_ids, limit, page, from_date, to_date) 19 | return self.execute(query) 20 | 21 | def fetch_all_activity_logs_from_board( 22 | self, 23 | board_ids: Union[int, str], 24 | from_date: Optional[str] = None, 25 | to_date: Optional[str] = None, 26 | limit: Optional[int] = DEFAULT_PAGE_LIMIT_ACTIVITY_LOGS, 27 | events_filter: Optional[List[str]] = None, 28 | ) -> List[ActivityLog]: 29 | page = 1 30 | activity_logs = [] 31 | while True: 32 | response = self.fetch_activity_logs_from_board( 33 | board_ids=board_ids, limit=limit, page=page, from_date=from_date, to_date=to_date 34 | ) 35 | current_activity_logs = response.data.boards[0].activity_logs 36 | if not current_activity_logs: # ATM is the only way to check if there are no more activity logs 37 | break 38 | else: 39 | relevant_activity_logs = ( 40 | current_activity_logs 41 | if events_filter is None 42 | else [log for log in current_activity_logs if log.event in events_filter] 43 | ) 44 | activity_logs.extend(relevant_activity_logs) 45 | page += 1 46 | 47 | return activity_logs 48 | -------------------------------------------------------------------------------- /src/monday_sdk/modules/boards.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional, Union, Any, Mapping 2 | 3 | from ..graphql_handler import MondayGraphQL 4 | from ..query_templates import get_boards_query, get_board_by_id_query, get_board_items_query, get_columns_by_board_query 5 | from ..types import MondayApiResponse, Item, ItemsPage, BoardKind, BoardState, BoardsOrderBy 6 | from ..utils import sleep_according_to_complexity, construct_updated_at_query_params 7 | from ..constants import DEFAULT_PAGE_LIMIT_BOARDS, DEFAULT_PAGE_LIMIT_ITEMS 8 | 9 | 10 | class BoardModule(MondayGraphQL): 11 | def fetch_boards( 12 | self, 13 | limit: Optional[int] = DEFAULT_PAGE_LIMIT_BOARDS, 14 | page: Optional[int] = None, 15 | ids: Optional[List[int]] = None, 16 | board_kind: Optional[BoardKind] = None, 17 | state: Optional[BoardState] = None, 18 | order_by: Optional[BoardsOrderBy] = None, 19 | ) -> MondayApiResponse: 20 | query = get_boards_query(ids=ids, limit=limit, page=page, board_kind=board_kind, state=state, order_by=order_by) 21 | return self.execute(query) 22 | 23 | def fetch_boards_by_id(self, board_id: Union[int, str]) -> MondayApiResponse: 24 | query = get_board_by_id_query(board_id) 25 | return self.execute(query) 26 | 27 | def fetch_all_items_by_board_id( 28 | self, 29 | board_id: Union[int, str], 30 | query_params: Optional[Mapping[str, Any]] = None, 31 | limit: Optional[int] = DEFAULT_PAGE_LIMIT_ITEMS, 32 | ) -> List[Item]: 33 | """ 34 | Fetches all items from a board by board ID, includes paginating 35 | todo: add support for multiple board IDs 36 | """ 37 | items: List[Item] = [] 38 | cursor = None 39 | 40 | while True: 41 | query = get_board_items_query(board_id, query_params=query_params, cursor=cursor, limit=limit) 42 | response = self.execute(query) 43 | items_page = response.data.boards[0].items_page if cursor is None else response.data.next_items_page 44 | 45 | items.extend(items_page.items) 46 | complexity = response.data.complexity.query 47 | cursor = items_page.cursor 48 | 49 | if cursor: 50 | sleep_according_to_complexity(complexity) 51 | else: 52 | break 53 | 54 | return items 55 | 56 | def fetch_item_by_board_id_by_update_date( 57 | self, 58 | board_id: Union[int, str], 59 | updated_after: str, 60 | updated_before: str, 61 | limit: Optional[int] = DEFAULT_PAGE_LIMIT_ITEMS, 62 | ) -> List[Item]: 63 | """ 64 | Fetches items from a board by board ID by update date, useful for incremental fetching 65 | todo: add type hints for updated_after and updated_before and validate them 66 | """ 67 | if not updated_after and not updated_before: 68 | raise ValueError( 69 | "Either updated_after or updated_before must be provided when fetching items by update date" 70 | ) 71 | query_params = construct_updated_at_query_params(updated_after, updated_before) 72 | return self.fetch_all_items_by_board_id(board_id, query_params=query_params, limit=limit) 73 | 74 | def fetch_columns_by_board_id(self, board_id: Union[int, str]) -> MondayApiResponse: 75 | query = get_columns_by_board_query(board_id) 76 | return self.execute(query) 77 | -------------------------------------------------------------------------------- /src/monday_sdk/modules/custom.py: -------------------------------------------------------------------------------- 1 | from ..graphql_handler import MondayGraphQL 2 | 3 | 4 | class CustomModule(MondayGraphQL): 5 | def execute_custom_query(self, custom_query): 6 | return self.execute(custom_query) 7 | -------------------------------------------------------------------------------- /src/monday_sdk/modules/items.py: -------------------------------------------------------------------------------- 1 | from typing import Union, Dict, Any, List 2 | import datetime 3 | 4 | from ..graphql_handler import MondayGraphQL 5 | from ..query_templates import create_item_query, get_item_query, change_column_value_query, get_item_by_id_query, update_multiple_column_values_query, create_subitem_query, delete_item_query, archive_item_query, move_item_to_group_query, change_simple_column_value_query 6 | from ..types import Item 7 | 8 | 9 | class ItemModule(MondayGraphQL): 10 | """ " 11 | todo: add types for this module 12 | """ 13 | 14 | def change_custom_column_value( 15 | self, board_id: Union[str, int], item_id: Union[str, int], column_id: str, value: Dict[str, Any] 16 | ): 17 | """ 18 | for text columns, use change_simple_column_value 19 | for status columns, use change_status_column_value 20 | for date columns, use change_date_column_value 21 | for other columns, use this method, for example, for checkbox columns pass {'checked': True} 22 | """ 23 | query = change_column_value_query(board_id, item_id, column_id, value) 24 | return self.execute(query) 25 | 26 | def change_simple_column_value( 27 | self, board_id: Union[str, int], item_id: Union[str, int], column_id: str, value: str 28 | ): 29 | query = change_simple_column_value_query(board_id, item_id, column_id, value) 30 | return self.execute(query) 31 | 32 | def change_status_column_value( 33 | self, board_id: Union[str, int], item_id: Union[str, int], column_id: str, value: str 34 | ): 35 | dict_value = {"label": value} 36 | return self.change_custom_column_value(board_id, item_id, column_id, dict_value) 37 | 38 | def change_date_column_value( 39 | self, board_id: Union[str, int], item_id: Union[str, int], column_id: str, timestamp: datetime 40 | ): 41 | dict_value = {"date": timestamp.strftime("%Y-%m-%d"), "time": timestamp.strftime("%H:%M:%S")} 42 | return self.change_custom_column_value(board_id, item_id, column_id, dict_value) 43 | 44 | def create_item( 45 | self, 46 | board_id: Union[str, int], 47 | group_id: Union[str, int], 48 | item_name: str, 49 | column_values: Dict[str, Any] = None, 50 | create_labels_if_missing=False, 51 | ): 52 | query = create_item_query(board_id, group_id, item_name, column_values, create_labels_if_missing) 53 | return self.execute(query) 54 | 55 | def create_subitem( 56 | self, 57 | parent_item_id: Union[str, int], 58 | subitem_name: str, 59 | column_values: Dict[str, Any] = None, 60 | create_labels_if_missing=False, 61 | ): 62 | query = create_subitem_query(parent_item_id, subitem_name, column_values, create_labels_if_missing) 63 | return self.execute(query) 64 | 65 | def fetch_items_by_column_value( 66 | self, board_id: Union[str, int], column_id: str, value: str, limit: int = None, cursor: str = None 67 | ): 68 | query = get_item_query(board_id, column_id, value, limit, cursor) 69 | return self.execute(query) 70 | 71 | def fetch_items_by_id(self, ids: Union[str, int, List[Union[str, int]]]) -> List[Item]: 72 | if isinstance(ids, (list, set)): 73 | ids_str = ", ".join(map(str, ids)) 74 | ids_str = f"[{ids_str}]" 75 | else: 76 | ids_str = str(ids) 77 | query = get_item_by_id_query(ids_str) 78 | response = self.execute(query) 79 | return response.data.items 80 | 81 | def change_multiple_column_values( 82 | self, 83 | board_id: Union[str, int], 84 | item_id: Union[str, int], 85 | column_values: Dict[str, Any], 86 | create_labels_if_missing: bool = False, 87 | ): 88 | query = update_multiple_column_values_query(board_id, item_id, column_values, create_labels_if_missing) 89 | return self.execute(query) 90 | 91 | def move_item_to_group(self, item_id: Union[str, int], group_id: Union[str, int]): 92 | query = move_item_to_group_query(item_id, group_id) 93 | return self.execute(query) 94 | 95 | def archive_item_by_id(self, item_id: Union[str, int]): 96 | query = archive_item_query(item_id) 97 | return self.execute(query) 98 | 99 | def delete_item_by_id(self, item_id: Union[str, int]): 100 | query = delete_item_query(item_id) 101 | return self.execute(query) 102 | -------------------------------------------------------------------------------- /src/monday_sdk/modules/updates.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import List, Optional 3 | 4 | from ..query_templates import create_update_query, delete_update_query, get_update_query, get_updates_for_item_query, get_updates_for_board 5 | from ..types import MondayApiResponse, Update 6 | from ..graphql_handler import MondayGraphQL 7 | 8 | class UpdateModule(MondayGraphQL): 9 | def create_update(self, item_id, update_value) -> MondayApiResponse: 10 | query = create_update_query(item_id, update_value) 11 | return self.execute(query) 12 | 13 | def delete_update(self, item_id) -> MondayApiResponse: 14 | query = delete_update_query(item_id) 15 | return self.execute(query) 16 | 17 | def fetch_updates(self, limit, page=None) -> MondayApiResponse: 18 | query = get_update_query(limit, page) 19 | return self.execute(query) 20 | 21 | def fetch_updates_for_item(self, item_id, limit=100) -> MondayApiResponse: 22 | query = get_updates_for_item_query(item_id=item_id, limit=limit) 23 | return self.execute(query) 24 | 25 | def fetch_board_updates_page(self, board_id, limit=100, page=1) -> List[Update]: 26 | query = get_updates_for_board(board_id, limit, page) 27 | response: MondayApiResponse = self.execute(query) 28 | return response.data.boards[0].updates 29 | 30 | def fetch_board_updates( 31 | self, 32 | board_ids: str, 33 | updated_after: Optional[str] = None, 34 | updated_before: Optional[str] = None, 35 | ) -> List[Update]: 36 | """ 37 | Fetches all updates from a board (with optional date filtering). 38 | - Paginates through all pages until no more updates. 39 | - If from_date or to_date are provided, filters out updates outside that window. 40 | """ 41 | start_dt = datetime.fromisoformat(updated_after) if updated_after else None 42 | end_dt = datetime.fromisoformat(updated_before) if updated_before else None 43 | 44 | all_updates = [] 45 | page = 1 46 | 47 | while True: 48 | updates = self.fetch_board_updates_page(board_ids, page=page) 49 | if not updates: 50 | break 51 | 52 | if start_dt or end_dt: 53 | updates = [ 54 | u for u in updates 55 | if ( 56 | u.updated_at 57 | and (start_dt is None or datetime.fromisoformat(u.updated_at) >= start_dt) 58 | and (end_dt is None or datetime.fromisoformat(u.updated_at) <= end_dt) 59 | ) 60 | ] 61 | 62 | all_updates.extend(updates) 63 | page += 1 64 | 65 | return all_updates -------------------------------------------------------------------------------- /src/monday_sdk/query_templates.py: -------------------------------------------------------------------------------- 1 | import json 2 | from enum import Enum 3 | from typing import List, Union, Optional, Mapping, Any 4 | 5 | from .types import BoardKind, BoardState, BoardsOrderBy 6 | from .utils import monday_json_stringify, gather_params 7 | 8 | 9 | # todo: divide into separate files, take into account that multiple resources can use the same query 10 | 11 | 12 | def get_board_items_query( 13 | board_ids: Union[int, str], 14 | limit: int, 15 | query_params: Optional[Mapping[str, Any]] = None, 16 | cursor: Optional[str] = None, 17 | ) -> str: 18 | if cursor is None: 19 | return get_board_items_first_page_query(board_ids, query_params=query_params, limit=limit) 20 | else: 21 | return get_board_items_pagination_query(cursor=cursor, limit=limit) 22 | 23 | 24 | def get_board_items_first_page_query( 25 | board_id: Union[str, int], limit: int, query_params: Optional[Mapping[str, Any]] = None 26 | ) -> str: 27 | raw_params = locals().items() 28 | items_page_params = gather_params(raw_params, excluded_params=["board_id"]) 29 | wrapped_params = f"({items_page_params})" if items_page_params else "" 30 | 31 | query = """{ 32 | complexity { 33 | query 34 | after 35 | } 36 | boards(ids: %s){ 37 | name 38 | items_page %s { 39 | cursor 40 | items { 41 | id 42 | name 43 | group { 44 | id 45 | title 46 | } 47 | column_values { 48 | ... on MirrorValue { 49 | display_value 50 | } 51 | ... on BoardRelationValue { 52 | display_value 53 | } 54 | ... on DependencyValue { 55 | display_value 56 | } 57 | value 58 | text 59 | type 60 | column { 61 | id 62 | title 63 | } 64 | } 65 | subitems { 66 | name 67 | id 68 | parent_item { 69 | id 70 | } 71 | group { 72 | id 73 | title 74 | } 75 | column_values { 76 | ... on MirrorValue { 77 | display_value 78 | } 79 | ... on DependencyValue { 80 | display_value 81 | } 82 | ... on BoardRelationValue { 83 | display_value 84 | } 85 | text 86 | type 87 | column { 88 | id 89 | title 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | }""" % ( 97 | board_id, 98 | wrapped_params, 99 | ) 100 | return query 101 | 102 | 103 | def get_board_items_pagination_query(cursor: str, limit: int) -> str: 104 | 105 | query = """{ 106 | complexity { 107 | query 108 | after 109 | } 110 | next_items_page(limit: %s, cursor: "%s") { 111 | cursor 112 | items { 113 | id 114 | name 115 | group { 116 | id 117 | title 118 | } 119 | column_values { 120 | ... on MirrorValue { 121 | display_value 122 | } 123 | ... on BoardRelationValue { 124 | display_value 125 | } 126 | ... on DependencyValue { 127 | display_value 128 | } 129 | value 130 | text 131 | type 132 | column { 133 | id 134 | title 135 | } 136 | } 137 | subitems { 138 | name 139 | id 140 | group { 141 | id 142 | title 143 | } 144 | column_values { 145 | ... on MirrorValue { 146 | display_value 147 | } 148 | ... on DependencyValue { 149 | display_value 150 | } 151 | ... on BoardRelationValue { 152 | display_value 153 | } 154 | text 155 | type 156 | column { 157 | id 158 | title 159 | } 160 | } 161 | } 162 | } 163 | } 164 | }""" % ( 165 | limit, 166 | cursor, 167 | ) 168 | return query 169 | 170 | 171 | # ITEM RESOURCE QUERIES 172 | def create_item_query(board_id, group_id, item_name, column_values, create_labels_if_missing): 173 | # Monday does not allow passing through non-JSON null values here, 174 | # so if you choose not to specify column values, need to set column_values to empty object. 175 | column_values = column_values or {} 176 | 177 | query = """mutation 178 | { 179 | create_item ( 180 | board_id: %s, 181 | group_id: "%s", 182 | item_name: "%s", 183 | column_values: %s, 184 | create_labels_if_missing: %s 185 | ) { 186 | id 187 | } 188 | }""" % ( 189 | board_id, 190 | group_id, 191 | item_name, 192 | monday_json_stringify(column_values), 193 | str(create_labels_if_missing).lower(), 194 | ) 195 | 196 | return query 197 | 198 | 199 | def create_subitem_query(parent_item_id, subitem_name, column_values, create_labels_if_missing): 200 | column_values = column_values or {} 201 | 202 | return """mutation 203 | { 204 | create_subitem ( 205 | parent_item_id: %s, 206 | item_name: "%s", 207 | column_values: %s, 208 | create_labels_if_missing: %s 209 | ) { 210 | id, 211 | name, 212 | column_values { 213 | id, 214 | text 215 | }, 216 | board { 217 | id, 218 | name 219 | } 220 | } 221 | }""" % ( 222 | parent_item_id, 223 | subitem_name, 224 | monday_json_stringify(column_values), 225 | str(create_labels_if_missing).lower(), 226 | ) 227 | 228 | 229 | def get_item_query(board_id, column_id, value, limit, cursor=None): 230 | columns = [{"column_id": str(column_id), "column_values": [str(value)]}] if not cursor else None 231 | 232 | raw_params = locals().items() 233 | items_page_params = gather_params(raw_params, excluded_params=["column_id", "value"]) 234 | 235 | query = ( 236 | """query 237 | { 238 | items_page_by_column_values (%s) { 239 | cursor 240 | items { 241 | id 242 | name 243 | updates { 244 | id 245 | body 246 | } 247 | group { 248 | id 249 | title 250 | } 251 | column_values { 252 | id 253 | text 254 | value 255 | } 256 | } 257 | } 258 | }""" 259 | % items_page_params 260 | ) 261 | 262 | return query 263 | 264 | 265 | def get_item_by_id_query(ids): 266 | query = """{ 267 | complexity { 268 | query 269 | after 270 | } 271 | items(ids: %s){ 272 | id 273 | name 274 | state 275 | group { 276 | id 277 | title 278 | } 279 | column_values { 280 | ... on MirrorValue { 281 | display_value 282 | } 283 | ... on BoardRelationValue { 284 | display_value 285 | } 286 | ... on DependencyValue { 287 | display_value 288 | } 289 | value 290 | text 291 | type 292 | column { 293 | id 294 | title 295 | } 296 | } 297 | subitems { 298 | name 299 | id 300 | parent_item { 301 | id 302 | } 303 | group { 304 | id 305 | title 306 | } 307 | column_values { 308 | ... on MirrorValue { 309 | display_value 310 | } 311 | ... on DependencyValue { 312 | display_value 313 | } 314 | ... on BoardRelationValue { 315 | display_value 316 | } 317 | text 318 | type 319 | column { 320 | id 321 | title 322 | } 323 | } 324 | } 325 | } 326 | }""" % ( 327 | ids 328 | ) 329 | 330 | return query 331 | 332 | 333 | def change_column_value_query(board_id, item_id, column_id, value): 334 | query = """mutation 335 | { 336 | change_column_value( 337 | board_id: %s, 338 | item_id: %s, 339 | column_id: "%s", 340 | value: %s 341 | ) { 342 | id 343 | name 344 | column_values { 345 | id 346 | text 347 | value 348 | } 349 | } 350 | }""" % ( 351 | board_id, 352 | item_id, 353 | column_id, 354 | monday_json_stringify(value), 355 | ) 356 | 357 | return query 358 | 359 | 360 | def change_simple_column_value_query(board_id, item_id, column_id, value): 361 | query = """mutation 362 | { 363 | change_simple_column_value ( 364 | board_id: %s, 365 | item_id: %s, 366 | column_id: "%s", 367 | value: "%s" 368 | ) { 369 | id 370 | } 371 | }""" % ( 372 | board_id, 373 | item_id, 374 | column_id, 375 | value, 376 | ) 377 | 378 | return query 379 | 380 | 381 | def move_item_to_group_query(item_id, group_id): 382 | query = """ 383 | mutation 384 | { 385 | move_item_to_group (item_id: %s, group_id: "%s") 386 | { 387 | id 388 | } 389 | }""" % ( 390 | item_id, 391 | group_id, 392 | ) 393 | return query 394 | 395 | 396 | def archive_item_query(item_id): 397 | query = ( 398 | """ 399 | mutation 400 | { 401 | archive_item (item_id: %s) 402 | { 403 | id 404 | } 405 | }""" 406 | % item_id 407 | ) 408 | return query 409 | 410 | 411 | def delete_item_query(item_id): 412 | query = ( 413 | """ 414 | mutation 415 | { 416 | delete_item (item_id: %s) 417 | { 418 | id 419 | } 420 | }""" 421 | % item_id 422 | ) 423 | return query 424 | 425 | 426 | def get_columns_by_board_query(board_id): 427 | return ( 428 | """query 429 | { 430 | boards(ids: %s) { 431 | id 432 | name 433 | groups { 434 | id 435 | title 436 | } 437 | columns { 438 | title 439 | id 440 | type 441 | settings_str 442 | } 443 | } 444 | }""" 445 | % board_id 446 | ) 447 | 448 | 449 | def update_multiple_column_values_query(board_id, item_id, column_values, create_labels_if_missing=False): 450 | query = """mutation 451 | { 452 | change_multiple_column_values ( 453 | board_id: %s, 454 | item_id: %s, 455 | column_values: %s, 456 | create_labels_if_missing: %s 457 | ) { 458 | id 459 | name 460 | column_values { 461 | id 462 | text 463 | } 464 | } 465 | }""" % ( 466 | board_id, 467 | item_id, 468 | monday_json_stringify(column_values), 469 | str(create_labels_if_missing).lower(), 470 | ) 471 | 472 | return query 473 | 474 | 475 | # UPDATE RESOURCE QUERIES 476 | def create_update_query(item_id, update_value): 477 | query = """mutation 478 | { 479 | create_update( 480 | item_id: %s, 481 | body: %s 482 | ) { 483 | id 484 | } 485 | }""" % ( 486 | item_id, 487 | json.dumps(update_value), 488 | ) 489 | 490 | return query 491 | 492 | 493 | def delete_update_query(item_id): 494 | query = ( 495 | """mutation { 496 | delete_update (id: %s) { 497 | id 498 | } 499 | }""" 500 | % item_id 501 | ) 502 | 503 | return query 504 | 505 | 506 | def get_updates_for_item_query(item_id, limit: int): 507 | query = """query{ 508 | items(ids: %s){ 509 | updates (limit: %s) { 510 | id, 511 | body, 512 | created_at, 513 | updated_at, 514 | creator { 515 | id, 516 | name, 517 | email 518 | }, 519 | assets { 520 | id, 521 | name, 522 | url, 523 | file_extension, 524 | file_size 525 | }, 526 | replies { 527 | id, 528 | body, 529 | creator{ 530 | id, 531 | name, 532 | email 533 | }, 534 | created_at, 535 | updated_at 536 | } 537 | } 538 | } 539 | }""" % ( 540 | item_id, 541 | limit, 542 | ) 543 | 544 | return query 545 | 546 | 547 | def get_updates_for_board(board_id, limit: int, page=1): 548 | query = """query 549 | { 550 | boards(ids: %s) { 551 | updates(limit: %s, page: %s) { 552 | id, 553 | text_body, 554 | item_id, 555 | updated_at, 556 | created_at, 557 | creator { 558 | name, 559 | id 560 | } 561 | } 562 | } 563 | }""" % ( 564 | board_id, 565 | limit, 566 | page, 567 | ) 568 | 569 | return query 570 | 571 | 572 | def get_update_query(limit, page=1): 573 | query = """query 574 | { 575 | updates ( 576 | limit: %s, 577 | page: %s 578 | ) { 579 | id, 580 | body 581 | } 582 | }""" % ( 583 | limit, 584 | page, 585 | ) 586 | 587 | return query 588 | 589 | 590 | def get_boards_query( 591 | ids: List[int], 592 | limit: int, 593 | page: int = 1, 594 | board_kind: BoardKind = None, 595 | state: BoardState = None, 596 | order_by: BoardsOrderBy = None, 597 | ): 598 | parameters = locals().items() 599 | query_params = [] 600 | for k, v in parameters: 601 | if v is not None: 602 | value = v 603 | if isinstance(v, Enum): 604 | value = v.value 605 | query_params.append("%s: %s" % (k, value)) 606 | joined_params = f"({', '.join(query_params)})" if query_params else "" 607 | 608 | query = ( 609 | """query 610 | { 611 | boards %s { 612 | id 613 | name 614 | permissions 615 | tags { 616 | id 617 | name 618 | } 619 | groups { 620 | id 621 | title 622 | } 623 | columns { 624 | id 625 | title 626 | type 627 | } 628 | } 629 | }""" 630 | % joined_params 631 | ) 632 | 633 | return query 634 | 635 | 636 | def get_board_by_id_query(board_id: Union[int, str]): 637 | return ( 638 | """query 639 | { 640 | boards (ids: %s) { 641 | id 642 | name 643 | permissions 644 | tags { 645 | id 646 | name 647 | } 648 | groups { 649 | id 650 | title 651 | } 652 | columns { 653 | id 654 | title 655 | type 656 | settings_str 657 | } 658 | } 659 | }""" 660 | % board_id 661 | ) 662 | 663 | 664 | def get_items_by_group_query(board_id: Union[int, str], group_id: str, limit: int, cursor: Optional[str] = None): 665 | raw_params = locals().items() 666 | items_page_params = gather_params(raw_params, excluded_params=["board_id", "group_id"]) 667 | wrapped_params = f"({items_page_params})" if items_page_params else "" 668 | 669 | query = """query 670 | { 671 | boards(ids: %s) { 672 | groups(ids: "%s") { 673 | id 674 | title 675 | items_page %s { 676 | cursor 677 | items { 678 | id 679 | name 680 | } 681 | } 682 | } 683 | } 684 | }""" % ( 685 | board_id, 686 | group_id, 687 | wrapped_params, 688 | ) 689 | return query 690 | 691 | 692 | def get_complexity_query(): 693 | query = """ 694 | query 695 | { 696 | complexity { 697 | after, 698 | reset_in_x_seconds 699 | } 700 | }""" 701 | 702 | return query 703 | 704 | 705 | def get_activity_logs_query( 706 | board_id: Union[int, str], 707 | limit: int, 708 | page: Optional[int] = 1, 709 | from_date: Optional[str] = None, 710 | to_date: Optional[str] = None, 711 | ): 712 | raw_params = locals().items() 713 | activity_logs_params = gather_params(raw_params, excluded_params=["board_id", "from_date", "to_date"]) 714 | 715 | # Monday API excepts "from" and "to" and the function parameters couldn't be named that way because of Python's reserved words 716 | if from_date: 717 | activity_logs_params += f', from: "{from_date}"' 718 | if to_date: 719 | activity_logs_params += f', to: "{to_date}"' 720 | 721 | wrapped_params = f"({activity_logs_params})" if activity_logs_params else "" 722 | 723 | query = """{ 724 | complexity { 725 | query 726 | after 727 | } 728 | boards(ids: %s) { 729 | activity_logs %s { 730 | id 731 | account_id 732 | created_at 733 | data 734 | entity 735 | event 736 | user_id 737 | } 738 | } 739 | }""" % ( 740 | board_id, 741 | wrapped_params, 742 | ) 743 | 744 | return query 745 | -------------------------------------------------------------------------------- /src/monday_sdk/types/__init__.py: -------------------------------------------------------------------------------- 1 | from .api_response_types import ( 2 | MondayApiResponse, 3 | Data, 4 | Complexity, 5 | Board, 6 | ItemsPage, 7 | Item, 8 | Column, 9 | ColumnValue, 10 | Group, 11 | Update, 12 | ActivityLog, 13 | User, 14 | ) 15 | from .monday_enums import BoardKind, BoardState, BoardsOrderBy, Operator 16 | from .client_settings import MondayClientSettings 17 | -------------------------------------------------------------------------------- /src/monday_sdk/types/api_response_types.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | from typing import Optional, List 3 | 4 | 5 | @dataclass 6 | class Column: 7 | id: Optional[str] = field(default=None) 8 | title: Optional[str] = field(default=None) 9 | 10 | 11 | @dataclass 12 | class ColumnValue: 13 | value: Optional[str] = field(default=None) 14 | text: Optional[str] = field(default=None) 15 | type: Optional[str] = field(default=None) 16 | column: Optional[Column] = field(default=None) 17 | 18 | 19 | @dataclass 20 | class Group: 21 | id: Optional[str] = field(default=None) 22 | title: Optional[str] = field(default=None) 23 | 24 | 25 | @dataclass 26 | class Item: 27 | id: Optional[str] = field(default=None) 28 | state: Optional[str] = field(default=None) 29 | name: Optional[str] = field(default=None) 30 | updated_at: Optional[str] = field(default=None) 31 | group: Optional[Group] = field(default=None) 32 | subitems: Optional[List["Item"]] = field(default_factory=list) 33 | parent_item: Optional["Item"] = field(default=None) # only relevant for subitems 34 | column_values: Optional[List[ColumnValue]] = field(default_factory=list) 35 | 36 | 37 | @dataclass 38 | class CreatedItem: 39 | id: Optional[str] = field(default=None) 40 | 41 | 42 | @dataclass 43 | class User: 44 | name: Optional[str] = field(default=None) 45 | id: Optional[str] = field(default=None) 46 | 47 | 48 | @dataclass 49 | class Update: 50 | id: Optional[str] = field(default=None) 51 | text_body: Optional[str] = field(default=None) 52 | item_id: Optional[str] = field(default=None) 53 | created_at: Optional[str] = field(default=None) 54 | updated_at: Optional[str] = field(default=None) 55 | creator: Optional[User] = field(default=None) 56 | 57 | 58 | @dataclass 59 | class ItemsPage: 60 | cursor: Optional[str] = field(default=None) 61 | items: Optional[List[Item]] = field(default_factory=list) 62 | 63 | 64 | @dataclass 65 | class Complexity: 66 | query: Optional[int] = field(default=None) 67 | after: Optional[int] = field(default=None) 68 | 69 | 70 | @dataclass 71 | class ActivityLog: 72 | id: str 73 | account_id: str 74 | created_at: str 75 | data: str 76 | entity: str 77 | event: str 78 | user_id: str 79 | 80 | 81 | @dataclass 82 | class Board: 83 | name: Optional[str] = field(default=None) 84 | items_page: Optional[ItemsPage] = field(default=None) 85 | updates: Optional[List[Update]] = field(default_factory=list) 86 | activity_logs: Optional[List[ActivityLog]] = field(default_factory=list) 87 | 88 | 89 | @dataclass 90 | class Data: 91 | complexity: Optional[Complexity] = field(default=None) 92 | boards: Optional[List[Board]] = field(default_factory=list) 93 | items: Optional[List[Item]] = field(default_factory=list) 94 | next_items_page: Optional[ItemsPage] = field(default=None) 95 | items_page_by_column_values: Optional[ItemsPage] = field(default=None) 96 | create_item: Optional[CreatedItem] = field(default=None) 97 | 98 | 99 | @dataclass 100 | class MondayApiResponse: 101 | data: Data 102 | account_id: int -------------------------------------------------------------------------------- /src/monday_sdk/types/client_settings.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Dict 3 | 4 | 5 | @dataclass 6 | class MondayClientSettings: 7 | token: str 8 | headers: Dict[str, str] 9 | debug_mode: bool 10 | max_retry_attempts: int 11 | -------------------------------------------------------------------------------- /src/monday_sdk/types/monday_enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class ColumnType(Enum): 5 | AUTO_NUMBER = "auto_number" # Number items according to their order in the group/board 6 | CHECKBOX = "checkbox" # Check off items and see what's done at a glance 7 | COUNTRY = "country" # Choose a country 8 | COLOR_PICKER = "color_picker" # Manage a design system using a color palette 9 | CREATION_LOG = "creation_log" # Add the item's creator and creation date automatically 10 | DATE = "date" # Add dates like deadlines to ensure you never drop the ball 11 | DEPENDENCY = "dependency" # Set up dependencies between items in the board 12 | DROPDOWN = "dropdown" # Create a dropdown list of options 13 | EMAIL = "email" # Email team members and clients directly from your board 14 | FILE = "file" # Add files & docs to your item 15 | HOUR = "hour" # Add times to manage and schedule tasks, shifts and more 16 | ITEM_ID = "item_id" # Show a unique ID for each item 17 | LAST_UPDATED = "last_updated" # Add the person that last updated the item and the date 18 | LINK = "link" # Simply hyperlink to any website 19 | LOCATION = "location" # Place multiple locations on a geographic map 20 | LONG_TEXT = "long_text" # Add large amounts of text without changing column width 21 | NUMBERS = "numbers" # Add revenue, costs, time estimations and more 22 | PEOPLE = "people" # Assign people to improve team work 23 | PHONE = "phone" # Call your contacts directly from monday.com 24 | PROGRESS = "progress" # Show progress by combining status columns in a battery 25 | RATING = "rating" # Rate or rank anything visually 26 | STATUS = "status" # Get an instant overview of where things stand 27 | TEAM = "team" # Assign a full team to an item 28 | TAGS = "tags" # Add tags to categorize items across multiple boards 29 | TEXT = "text" # Add textual information e.g. addresses, names or keywords 30 | TIMELINE = "timeline" # Visually see a breakdown of your team's workload by time 31 | TIME_TRACKING = "time_tracking" # Easily track time spent on each item, group, and board 32 | VOTE = "vote" # Vote on an item e.g. pick a new feature or a favorite lunch place 33 | WEEK = "week" # Select the week on which each item should be completed 34 | WORLD_CLOCK = "world_clock" # Keep track of the time anywhere in the world 35 | 36 | 37 | class Operator(Enum): 38 | GREATER_THAN_OR_EQUALS = "greater_than_or_equals" 39 | LESS_THAN_OR_EQUALS = "lower_than_or_equal" 40 | 41 | 42 | class BoardKind(Enum): 43 | """Board kinds""" 44 | 45 | PUBLIC = "public" 46 | PRIVATE = "private" 47 | SHARE = "share" 48 | 49 | 50 | class BoardState(Enum): 51 | """Board available states""" 52 | 53 | ALL = "all" 54 | ACTIVE = "active" 55 | ARCHIVED = "archived" 56 | DELETED = "deleted" 57 | 58 | 59 | class BoardsOrderBy(Enum): 60 | """Order to retrieve boards""" 61 | 62 | CREATED_AT = "created_at" 63 | USED_AT = "used_at" 64 | -------------------------------------------------------------------------------- /src/monday_sdk/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | import time 3 | 4 | from enum import Enum 5 | from typing import List, Iterable, Tuple, Any, Optional, Union 6 | from .types import Item, Operator 7 | from .constants import MAX_COMPLEXITY 8 | 9 | 10 | def extract_column_value_by_title(item: Item, column_name: str) -> Union[str, bool]: 11 | item_column_values = item.column_values 12 | for column_value in item_column_values: 13 | if column_value.column.title == column_name: 14 | if column_value.type == "checkbox": 15 | # Parse the JSON string into a dictionary 16 | value_dict = json.loads(column_value.value) 17 | return value_dict["checked"] 18 | else: 19 | return column_value.text 20 | return "" 21 | 22 | 23 | def extract_column_value_by_id(item: Item, column_id: str) -> Union[str, bool]: 24 | item_column_values = item.column_values 25 | for column_value in item_column_values: 26 | if column_value.column.id == column_id: 27 | if column_value.type == "checkbox": 28 | # Parse the JSON string into a dictionary 29 | value_dict = json.loads(column_value.value) 30 | return value_dict["checked"] 31 | else: 32 | return column_value.text 33 | return "" 34 | 35 | 36 | def extract_column_id_by_title(item: Item, column_name: str) -> Union[str, None]: 37 | item_column_values = item.column_values 38 | for column_value in item_column_values: 39 | if column_value.column.title == column_name: 40 | return column_value.column.id 41 | return None 42 | 43 | 44 | def monday_json_stringify(value): 45 | # Monday's required format: "{\"label\":\"Done\"}" 46 | return json.dumps(json.dumps(value)) 47 | 48 | 49 | def gather_params(params: Iterable[Tuple[str, Any]], excluded_params: Optional[List[str]] = None, exclude_none: bool = True) -> str: 50 | valid_params = [ 51 | f"{param}: {format_param_value(value)}" 52 | for param, value in params 53 | if not ((excluded_params and param in excluded_params) or (value is None and exclude_none)) 54 | ] 55 | return ", ".join(valid_params) 56 | 57 | 58 | def format_param_value(value: Any) -> str: 59 | if value is None: 60 | return "null" 61 | if isinstance(value, str): 62 | return f'"{value}"' 63 | if isinstance(value, Enum): 64 | return str(value.value) 65 | if isinstance(value, dict): 66 | return f"{{{gather_params(value.items(), exclude_none=False)}}}" 67 | if isinstance(value, list): 68 | return f"[{', '.join(format_param_value(val) for val in value)}]" 69 | return str(value) 70 | 71 | 72 | def sleep_according_to_complexity(query_complexity) -> None: 73 | """ 74 | monday complexity is limited per minute, so the sleep is according to the "cost" of the current query 75 | """ 76 | sleep_time = (query_complexity / MAX_COMPLEXITY) * 60 77 | time.sleep(sleep_time) 78 | 79 | 80 | def construct_updated_at_query_params(start_date: str, end_date: str) -> dict: 81 | query_params = {"rules": []} 82 | if start_date: 83 | query_params["rules"].append( 84 | { 85 | "column_id": "__last_updated__", 86 | "compare_value": ["EXACT", start_date], 87 | "operator": Operator.GREATER_THAN_OR_EQUALS, 88 | "compare_attribute": "UPDATED_AT", 89 | } 90 | ) 91 | if end_date: 92 | query_params["rules"].append( 93 | { 94 | "column_id": "__last_updated__", 95 | "compare_value": ["EXACT", end_date], 96 | "operator": Operator.LESS_THAN_OR_EQUALS, 97 | "compare_attribute": "UPDATED_AT", 98 | } 99 | ) 100 | return query_params 101 | --------------------------------------------------------------------------------