├── .github ├── FUNDING.yml └── workflows │ ├── python-package.yml │ └── python-publish.yml ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── ms_graph ├── client.py ├── drive_items.py ├── drives.py ├── groups.py ├── mail.py ├── notes.py ├── personal_contacts.py ├── search.py ├── session.py ├── users.py ├── utils │ └── range.py └── workbooks_and_charts │ ├── application.py │ ├── comments.py │ ├── enums.py │ ├── range.py │ ├── table.py │ ├── workbook.py │ └── worksheets.py ├── requirements.txt ├── samples ├── configs │ └── write_config.py ├── images │ └── azure_directory.jpg ├── setting_up_an_app.md ├── use_client.py ├── use_drive_services.py ├── use_group_services.py ├── use_mail_services.py ├── use_notebook_services.py ├── use_personal_contacts_service.py ├── use_range_service.py ├── use_search_service.py ├── use_workbooks_service.py └── user_drive_items_services.py ├── setup.py └── tests └── test_client.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [areed1192] 2 | patreon: sigmacoding -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [master] 9 | pull_request: 10 | branches: [master] 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | python-version: ["3.8", "3.9", "3.10"] 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v2 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | 26 | - name: Install Dependencies from requirements.txt File. 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install flake8 pytest 30 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 31 | 32 | - name: Lint with flake8 33 | run: | 34 | # stop the build if there are Python syntax errors or undefined names 35 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 36 | 37 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 38 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 39 | 40 | - name: Test with Unittest. 41 | run: | 42 | # If we have a testing folder, then run with unittest. 43 | if [ -f tests/test_clients.py ]; then python -m unittest tests/test_client.py; fi 44 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | workflow_dispatch: 8 | release: 9 | types: [created] 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: "3.9" 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install setuptools wheel twine 25 | - name: Publish to TestPyPI 26 | env: 27 | TWINE_USERNAME: "__token__" 28 | TWINE_PASSWORD: ${{ secrets.test_pypi_token }} 29 | run: | 30 | python setup.py sdist bdist_wheel 31 | twine upload --repository testpypi --verbose dist/* 32 | - name: Publish to PyPI 33 | env: 34 | TWINE_USERNAME: "__token__" 35 | TWINE_PASSWORD: ${{ secrets.pypi_token }} 36 | run: | 37 | python setup.py sdist bdist_wheel 38 | twine upload --verbose dist/* 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Alex Folder. 132 | config/ 133 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Python: Run Client", 9 | "type": "python", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/samples/use_client.py", 12 | "console": "integratedTerminal" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.linting.enabled": true, 3 | "python.linting.pylintEnabled": true, 4 | // Ignore Module Doc Strings. 5 | "python.linting.pylintArgs": [ 6 | "--disable", 7 | "C0114,C0415" 8 | ], 9 | "python.testing.unittestArgs": [ 10 | "-v", 11 | "-s", 12 | "./tests", 13 | "-p", 14 | "test_*.py" 15 | ], 16 | "python.testing.pytestEnabled": false, 17 | "python.testing.unittestEnabled": true 18 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alex Reed 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Microsoft Graph Python Client Library 2 | 3 | ## Table of Contents 4 | 5 | - [Overview](#overview) 6 | - [Setup](#setup) 7 | - [Usage](#usage) 8 | - [Support These Projects](#support-these-projects) 9 | 10 | ## Overview 11 | 12 | Microsoft Graph is the gateway to data and intelligence in Microsoft 365. It provides 13 | a unified programmability model that you can use to access the tremendous amount of data 14 | in Microsoft 365, Windows 10, and Enterprise Mobility + Security. This project utilizes python 15 | to help users interact with and manage data on Microsoft Graph API. 16 | 17 | ## Setup 18 | 19 | **Setup - Requirements Install:** 20 | 21 | For this particular project, you only need to install the dependencies, to use the project. The dependencies 22 | are listed in the `requirements.txt` file and can be installed by running the following command: 23 | 24 | ```console 25 | pip install -r requirements.txt 26 | ``` 27 | 28 | After running that command, the dependencies should be installed. 29 | 30 | **Setup - Local Install:** 31 | 32 | If you are planning to make modifications to this project or you would like to access it 33 | before it has been indexed on `PyPi`. I would recommend you either install this project 34 | in `editable` mode or do a `local install`. For those of you, who want to make modifications 35 | to this project. I would recommend you install the library in `editable` mode. 36 | 37 | If you want to install the library in `editable` mode, make sure to run the `setup.py` 38 | file, so you can install any dependencies you may need. To run the `setup.py` file, 39 | run the following command in your terminal. 40 | 41 | ```console 42 | pip install -e . 43 | ``` 44 | 45 | If you don't plan to make any modifications to the project but still want to use it across 46 | your different projects, then do a local install. 47 | 48 | ```console 49 | pip install . 50 | ``` 51 | 52 | This will install all the dependencies listed in the `setup.py` file. Once done 53 | you can use the library wherever you want. 54 | 55 | **Setup - PyPi Install:** 56 | 57 | To **install** the library, run the following command from the terminal. 58 | 59 | ```console 60 | pip install 61 | ``` 62 | 63 | **Setup - PyPi Upgrade:** 64 | 65 | To **upgrade** the library, run the following command from the terminal. 66 | 67 | ```console 68 | pip install --upgrade 69 | ``` 70 | 71 | ## Usage 72 | 73 | Here is a simple example of using the `ms_graph` library. 74 | 75 | ```python 76 | from pprint import pprint 77 | from ms_graph.client import MicrosoftGraphClient 78 | from configparser import ConfigParser 79 | 80 | # Specify your scopes when you want access certain resources. 81 | scopes = [ 82 | "Calendars.ReadWrite", 83 | "Files.ReadWrite.All", 84 | "User.ReadWrite.All", 85 | "Notes.ReadWrite.All", 86 | "Directory.ReadWrite.All", 87 | "User.Read.All", 88 | "Directory.Read.All", 89 | "Directory.ReadWrite.All", 90 | "offline_access", 91 | "openid", 92 | "profile" 93 | ] 94 | 95 | # Initialize the Parser. 96 | config = ConfigParser() 97 | 98 | # Read the file. 99 | config.read("config/config.ini") 100 | 101 | # Get the specified credentials. 102 | client_id = config.get("graph_api", "client_id") 103 | client_secret = config.get("graph_api", "client_secret") 104 | redirect_uri = config.get("graph_api", "redirect_uri") 105 | 106 | # Initialize the Client. 107 | graph_client = MicrosoftGraphClient( 108 | client_id=client_id, 109 | client_secret=client_secret, 110 | redirect_uri=redirect_uri, 111 | scope=scopes, 112 | credentials="config/ms_graph_state.jsonc" 113 | ) 114 | 115 | # Login to the Client. 116 | graph_client.login() 117 | 118 | 119 | # Grab the User Services. 120 | user_services = graph_client.users() 121 | 122 | # List the Users. 123 | pprint(user_services.list_users()) 124 | 125 | 126 | # Grab the Drive Services. 127 | drive_services = graph_client.drives() 128 | 129 | # List the Root Drive. 130 | pprint(drive_services.get_root_drive()) 131 | ``` 132 | 133 | ## Support These Projects 134 | 135 | **Patreon:** 136 | Help support this project and future projects by donating to my [Patreon Page](https://www.patreon.com/sigmacoding). I'm 137 | always looking to add more content for individuals like yourself, unfortuantely some of the APIs I would require me to 138 | pay monthly fees. 139 | 140 | **YouTube:** 141 | If you'd like to watch more of my content, feel free to visit my YouTube channel [Sigma Coding](https://www.youtube.com/c/SigmaCoding). 142 | -------------------------------------------------------------------------------- /ms_graph/client.py: -------------------------------------------------------------------------------- 1 | import json 2 | import time 3 | import urllib 4 | import random 5 | import string 6 | import pathlib 7 | 8 | from typing import List 9 | from typing import Dict 10 | 11 | import msal 12 | 13 | from ms_graph.users import Users 14 | from ms_graph.drives import Drives 15 | from ms_graph.groups import Groups 16 | from ms_graph.notes import Notes 17 | from ms_graph.session import GraphSession 18 | from ms_graph.drive_items import DriveItems 19 | from ms_graph.search import Search 20 | from ms_graph.personal_contacts import PersonalContacts 21 | from ms_graph.mail import Mail 22 | 23 | from ms_graph.workbooks_and_charts.workbook import Workbooks 24 | from ms_graph.workbooks_and_charts.range import Range 25 | 26 | class MicrosoftGraphClient: 27 | 28 | """ 29 | ### Overview: 30 | ---- 31 | Used as the main entry point for the Microsoft Graph 32 | API Service. 33 | """ 34 | 35 | RESOURCE = "https://graph.microsoft.com/" 36 | 37 | AUTHORITY_URL = "https://login.microsoftonline.com/" 38 | AUTH_ENDPOINT = "/oauth2/v2.0/authorize?" 39 | TOKEN_ENDPOINT = "/oauth2/v2.0/token" 40 | 41 | OFFICE365_AUTHORITY_URL = "https://login.live.com" 42 | OFFICE365_AUTH_ENDPOINT = "/oauth20_authorize.srf?" 43 | OFFICE365_TOKEN_ENDPOINT = "/oauth20_token.srf" 44 | 45 | def __init__( 46 | self, 47 | client_id: str, 48 | client_secret: str, 49 | redirect_uri: str, 50 | scope: List[str], 51 | account_type: str = "consumers", 52 | office365: bool = False, 53 | credentials: str = None, 54 | ): 55 | """Initializes the Graph Client. 56 | 57 | ### Parameters 58 | ---- 59 | client_id : str 60 | The application Client ID assigned when 61 | creating a new Microsoft App. 62 | 63 | client_secret : str 64 | The application Client Secret assigned when 65 | creating a new Microsoft App. 66 | 67 | redirect_uri : str 68 | The application Redirect URI assigned when 69 | creating a new Microsoft App. 70 | 71 | scope : List[str] 72 | The list of scopes you want the application 73 | to have access to. 74 | 75 | account_type : str, optional 76 | [description], by default "common" 77 | 78 | office365 : bool, optional 79 | [description], by default False 80 | """ 81 | 82 | # printing lowercase 83 | letters = string.ascii_lowercase 84 | 85 | self.credentials = credentials 86 | self.token_dict = None 87 | 88 | self.client_id = client_id 89 | self.client_secret = client_secret 90 | self.api_version = "v1.0" 91 | self.account_type = account_type 92 | self.redirect_uri = redirect_uri 93 | 94 | self.scope = scope 95 | self.state = "".join(random.choice(letters) for i in range(10)) 96 | 97 | self.access_token = None 98 | self.refresh_token = None 99 | self.graph_session = None 100 | self.id_token = None 101 | 102 | self.base_url = self.RESOURCE + self.api_version + "/" 103 | self.office_url = self.OFFICE365_AUTHORITY_URL + self.OFFICE365_AUTH_ENDPOINT 104 | self.graph_url = self.AUTHORITY_URL + self.account_type + self.AUTH_ENDPOINT 105 | self.office365 = office365 106 | self._redirect_code = None 107 | 108 | # Initialize the Credential App. 109 | self.client_app = msal.ConfidentialClientApplication( 110 | client_id=self.client_id, 111 | authority=self.AUTHORITY_URL + self.account_type, 112 | client_credential=self.client_secret, 113 | ) 114 | 115 | def _state(self, action: str, token_dict: dict = None) -> bool: 116 | """Sets the session state for the Client Library. 117 | 118 | ### Arguments 119 | ---- 120 | action : str 121 | Defines what action to take when determining the state. Either 122 | `load` or `save`. 123 | 124 | token_dict : dict, optional 125 | If the state is defined as `save` then pass through the 126 | token dictionary you want to save, by default None. 127 | 128 | ### Returns 129 | ---- 130 | bool: 131 | If the state action was successful, then returns `True` 132 | otherwise it returns `False`. 133 | """ 134 | 135 | # Determine if the Credentials file exists. 136 | does_exists = pathlib.Path(self.credentials).exists() 137 | 138 | # If it exists and we are loading it then proceed. 139 | if does_exists and action == "load": 140 | 141 | # Load the file. 142 | with open(file=self.credentials, mode="r", encoding="utf-8") as state_file: 143 | credentials = json.load(fp=state_file) 144 | 145 | # Grab the Token if it exists. 146 | if "refresh_token" in credentials: 147 | 148 | self.refresh_token = credentials["refresh_token"] 149 | self.access_token = credentials["access_token"] 150 | self.id_token = credentials["id_token"] 151 | self.token_dict = credentials 152 | 153 | return True 154 | 155 | else: 156 | return False 157 | 158 | # If we are saving the state then open the file and dump the dictionary. 159 | elif action == "save": 160 | 161 | token_dict["expires_in"] = time.time() + int(token_dict["expires_in"]) 162 | token_dict["ext_expires_in"] = time.time() + int( 163 | token_dict["ext_expires_in"] 164 | ) 165 | 166 | self.refresh_token = token_dict["refresh_token"] 167 | self.access_token = token_dict["access_token"] 168 | self.id_token = token_dict["id_token"] 169 | self.token_dict = token_dict 170 | 171 | with open(file=self.credentials, mode="w+", encoding="utf-8") as state_file: 172 | json.dump(obj=token_dict, fp=state_file, indent=2) 173 | 174 | def _token_seconds(self, token_type: str = "access_token") -> int: 175 | """Determines time till expiration for a token. 176 | 177 | Return the number of seconds until the current access token or refresh token 178 | will expire. The default value is access token because this is the most commonly used 179 | token during requests. 180 | 181 | ### Arguments: 182 | ---- 183 | token_type {str} -- The type of token you would like to determine lifespan for. 184 | Possible values are ["access_token", "refresh_token"] (default: {access_token}) 185 | 186 | ### Returns: 187 | ---- 188 | {int} -- The number of seconds till expiration. 189 | """ 190 | 191 | # if needed check the access token. 192 | if token_type == "access_token": 193 | 194 | # if the time to expiration is less than or equal to 0, return 0. 195 | if not self.access_token or ( 196 | time.time() + 60 >= self.token_dict["expires_in"] 197 | ): 198 | return 0 199 | 200 | # else return the number of seconds until expiration. 201 | token_exp = int(self.token_dict["expires_in"] - time.time() - 60) 202 | 203 | # if needed check the refresh token. 204 | elif token_type == "refresh_token": 205 | 206 | # if the time to expiration is less than or equal to 0, return 0. 207 | if not self.refresh_token or ( 208 | time.time() + 60 >= self.token_dict["ext_expires_in"] 209 | ): 210 | return 0 211 | 212 | # else return the number of seconds until expiration. 213 | token_exp = int(self.token_dict["ext_expires_in"] - time.time() - 60) 214 | 215 | return token_exp 216 | 217 | def _token_validation(self, nseconds: int = 60): 218 | """Checks if a token is valid. 219 | 220 | Verify the current access token is valid for at least N seconds, and 221 | if not then attempt to refresh it. Can be used to assure a valid token 222 | before making a call to the TD Ameritrade API. 223 | 224 | Arguments: 225 | ---- 226 | nseconds {int} -- The minimum number of seconds the token has to be 227 | valid for before attempting to get a refresh token. (default: {5}) 228 | """ 229 | 230 | if self._token_seconds(token_type="access_token") < nseconds: 231 | self.grab_refresh_token() 232 | 233 | def _silent_sso(self) -> bool: 234 | """Attempts a Silent Authentication using the Access Token and Refresh Token. 235 | 236 | Returns 237 | ---- 238 | (bool) 239 | `True` if it was successful and `False` if it failed. 240 | """ 241 | 242 | # if the current access token is not expired then we are still authenticated. 243 | if self._token_seconds(token_type="access_token") > 0: 244 | return True 245 | 246 | # if the current access token is expired then try and refresh access token. 247 | elif self.refresh_token and self.grab_refresh_token(): 248 | return True 249 | 250 | # More than likely a first time login, so can"t do silent authenticaiton. 251 | return False 252 | 253 | def login(self) -> None: 254 | """Logs the user into the session.""" 255 | 256 | # Load the State. 257 | self._state(action="load") 258 | 259 | # Try a Silent SSO First. 260 | if self._silent_sso(): 261 | 262 | # Set the Session. 263 | self.graph_session = GraphSession(client=self) 264 | 265 | return True 266 | 267 | else: 268 | 269 | # Build the URL. 270 | url = self.authorization_url() 271 | 272 | # aks the user to go to the URL provided, they will be prompted 273 | # to authenticate themsevles. 274 | print(f"Please go to URL provided authorize your account: {url}") 275 | 276 | # ask the user to take the final URL after authentication and 277 | # paste here so we can parse. 278 | my_response = input("Paste the full URL redirect here: ") 279 | 280 | # store the redirect URL 281 | self._redirect_code = my_response 282 | 283 | # this will complete the final part of the authentication process. 284 | self.grab_access_token() 285 | 286 | # Set the session. 287 | self.graph_session = GraphSession(client=self) 288 | 289 | def authorization_url(self): 290 | """Builds the authorization URL used to get an Authorization Code. 291 | 292 | ### Returns: 293 | ---- 294 | A string. 295 | """ 296 | 297 | # Build the Auth URL. 298 | auth_url = self.client_app.get_authorization_request_url( 299 | scopes=self.scope, state=self.state, redirect_uri=self.redirect_uri 300 | ) 301 | 302 | return auth_url 303 | 304 | def grab_access_token(self) -> Dict: 305 | """Exchanges a code for an Access Token. 306 | 307 | ### Returns: 308 | ---- 309 | dict : A dictionary containing a new access token and refresh token. 310 | """ 311 | 312 | # Parse the Code. 313 | query_dict = urllib.parse.parse_qs(self._redirect_code) 314 | 315 | # Grab the Code. 316 | code = query_dict[self.redirect_uri + "?code"] 317 | 318 | # Grab the Token. 319 | token_dict = self.client_app.acquire_token_by_authorization_code( 320 | code=code, scopes=self.scope, redirect_uri=self.redirect_uri 321 | ) 322 | 323 | # Save the token dict. 324 | self._state(action="save", token_dict=token_dict) 325 | 326 | return token_dict 327 | 328 | def grab_refresh_token(self) -> Dict: 329 | """Grabs a new access token using a refresh token. 330 | 331 | ### Returns 332 | ---- 333 | dict : 334 | A token dictionary with a new access token. 335 | """ 336 | 337 | # Grab a new token using our refresh token. 338 | token_dict = self.client_app.acquire_token_by_refresh_token( 339 | refresh_token=self.refresh_token, scopes=self.scope 340 | ) 341 | 342 | if "error" in token_dict: 343 | print(token_dict) 344 | raise PermissionError( 345 | "Permissions not authorized, delete json file and run again." 346 | ) 347 | 348 | # Save the Token. 349 | self._state(action="save", token_dict=token_dict) 350 | 351 | return token_dict 352 | 353 | def users(self) -> Users: 354 | """Used to access the User Services and metadata. 355 | 356 | ### Returns 357 | --- 358 | Users: 359 | The `Users` services Object. 360 | """ 361 | 362 | # Grab the Users Object for the session. 363 | users_object: Users = Users(session=self.graph_session) 364 | 365 | return users_object 366 | 367 | def drives(self) -> Drives: 368 | """Used to access the Drives Services and metadata. 369 | 370 | ### Returns 371 | --- 372 | Drives: 373 | The `Drives` services Object. 374 | """ 375 | 376 | # Grab the Drives Object for the session. 377 | drives_object: Drives = Drives(session=self.graph_session) 378 | 379 | return drives_object 380 | 381 | def drive_item(self) -> DriveItems: 382 | """Used to access the Drive Items Services and metadata. 383 | 384 | ### Returns 385 | --- 386 | DriveItems: 387 | The `DriveItems` services Object. 388 | """ 389 | 390 | # Grab the Drive Items Object for the session. 391 | drive_items_object: Drives = DriveItems(session=self.graph_session) 392 | 393 | return drive_items_object 394 | 395 | def groups(self) -> Groups: 396 | """Used to access the Groups Services and metadata. 397 | 398 | ### Returns 399 | --- 400 | Groups: 401 | The `Groups` services Object. 402 | """ 403 | 404 | # Grab the `Groups` Object for the session. 405 | groups_object: Groups = Groups(session=self.graph_session) 406 | 407 | return groups_object 408 | 409 | def notes(self) -> Notes: 410 | """Used to access the OneNotes Services and metadata. 411 | 412 | ### Returns 413 | --- 414 | Notes: 415 | The `Notes` services Object. 416 | """ 417 | 418 | # Grab the `Notes` Object for the session. 419 | notes_object: Notes = Notes(session=self.graph_session) 420 | 421 | return notes_object 422 | 423 | def search(self) -> Search: 424 | """Used to access the Search Services and metadata. 425 | 426 | ### Returns 427 | --- 428 | Search: 429 | The `Search` services Object. 430 | """ 431 | 432 | # Grab the `Search` Object for the session. 433 | search_object: Search = Search(session=self.graph_session) 434 | 435 | return search_object 436 | 437 | def personal_contacts(self) -> PersonalContacts: 438 | """Used to access the PersonalContacts Services and metadata. 439 | 440 | ### Returns 441 | --- 442 | PersonalContacts: 443 | The `PersonalContacts` services Object. 444 | """ 445 | 446 | # Grab the `PersonalContacts` Object for the session. 447 | personal_contacts_object: PersonalContacts = PersonalContacts( 448 | session=self.graph_session 449 | ) 450 | 451 | return personal_contacts_object 452 | 453 | def mail(self) -> Mail: 454 | """Used to access the Mail Services and metadata. 455 | 456 | ### Returns 457 | --- 458 | Mail: 459 | The `Mail` services Object. 460 | """ 461 | 462 | # Grab the `Mail` Object for the session. 463 | mail_service: Mail = Mail(session=self.graph_session) 464 | 465 | return mail_service 466 | 467 | def workbooks(self) -> Workbooks: 468 | """Used to access the Workbooks Services and metadata. 469 | 470 | ### Returns 471 | --- 472 | Workbooks: 473 | The `Workbooks` services Object. 474 | """ 475 | 476 | # Grab the `Workbooks` Object for the session. 477 | workbook_service: Workbooks = Workbooks(session=self.graph_session) 478 | 479 | return workbook_service 480 | 481 | def range(self) -> Range: 482 | """Used to access the Range Services and metadata. 483 | 484 | ### Returns 485 | --- 486 | Range: 487 | The `Range` services Object. 488 | """ 489 | 490 | # Grab the `Range` Object for the session. 491 | range_service: Range = Range(session=self.graph_session) 492 | 493 | return range_service 494 | -------------------------------------------------------------------------------- /ms_graph/drive_items.py: -------------------------------------------------------------------------------- 1 | from ms_graph.session import GraphSession 2 | 3 | 4 | class DriveItems(): 5 | 6 | """ 7 | ## Overview: 8 | ---- 9 | The driveItem resource represents a file, folder, 10 | or other item stored in a drive. All file system 11 | objects in OneDrive and SharePoint are returned as 12 | driveItem resources. 13 | """ 14 | 15 | def __init__(self, session: object) -> None: 16 | """Initializes the `DriveItems` object. 17 | 18 | ### Parameters 19 | ---- 20 | session : object 21 | An authenticated session for our Microsoft Graph Client. 22 | """ 23 | 24 | # Set the session. 25 | self.graph_session: GraphSession = session 26 | 27 | # Set the endpoint. 28 | self.endpoint = "drive" 29 | self.collections_endpoint = "drives/" 30 | 31 | def get_drive_item(self, drive_id: str, item_id: str) -> dict: 32 | """Grab"s a DriveItem Resource using the Item ID and Drive ID. 33 | 34 | ### Parameters 35 | ---- 36 | drive_id : str 37 | The Drive ID in which the resource exist. 38 | 39 | item_id : str 40 | The item ID of the object you want to 41 | return. 42 | 43 | ### Returns 44 | ---- 45 | dict : 46 | A DriveItem resource object. 47 | """ 48 | 49 | content = self.graph_session.make_request( 50 | method="get", 51 | endpoint=self.collections_endpoint + f"/{drive_id}/items/{item_id}" 52 | ) 53 | 54 | return content 55 | 56 | def get_drive_item_by_path(self, drive_id: str, item_path: str) -> dict: 57 | """Grab"s a DriveItem Resource using the Item ID and Drive ID. 58 | 59 | ### Parameters 60 | ---- 61 | drive_id : str 62 | The Drive ID in which the resource exist. 63 | 64 | item_path : str 65 | The path to the Item. 66 | 67 | ### Returns 68 | ---- 69 | dict : 70 | A DriveItem resource object. 71 | """ 72 | 73 | content = self.graph_session.make_request( 74 | method="get", 75 | endpoint=self.collections_endpoint + f"/{drive_id}/root:/{item_path}" 76 | ) 77 | 78 | return content 79 | 80 | def get_group_drive_item(self, group_id: str, item_id: str) -> dict: 81 | """Grab"s a DriveItem Resource using the Item ID and Drive ID. 82 | 83 | ### Parameters 84 | ---- 85 | group_id : str 86 | The Group ID in which the resource exist. 87 | 88 | item_id : str 89 | The item ID of the object you want to 90 | return. 91 | 92 | ### Returns 93 | ---- 94 | dict : 95 | A DriveItem resource object. 96 | """ 97 | 98 | content = self.graph_session.make_request( 99 | method="get", 100 | endpoint=f"/groups/{group_id}/drive/items/{item_id}" 101 | ) 102 | 103 | return content 104 | 105 | def get_group_drive_item_by_path(self, group_id: str, item_path: str) -> dict: 106 | """Grab"s a DriveItem Resource using the Item ID and Drive ID. 107 | 108 | ### Parameters 109 | ---- 110 | drive_id : str 111 | The Drive ID in which the resource exist. 112 | 113 | item_path : str 114 | The path to the Item. 115 | 116 | ### Returns 117 | ---- 118 | dict : 119 | A DriveItem resource object. 120 | """ 121 | 122 | content = self.graph_session.make_request( 123 | method="get", 124 | endpoint=f"/groups/{group_id}/drive/root:/{item_path}" 125 | ) 126 | 127 | return content 128 | 129 | def get_my_drive_item(self, item_id: str) -> dict: 130 | """Grab"s a DriveItem Resource using the Item ID and Drive ID. 131 | 132 | ### Parameters 133 | ---- 134 | item_id : str 135 | The item ID of the object you want to 136 | return. 137 | 138 | ### Returns 139 | ---- 140 | dict : 141 | A DriveItem resource object. 142 | """ 143 | 144 | content = self.graph_session.make_request( 145 | method="get", 146 | endpoint=f"/me/drive/items/{item_id}" 147 | ) 148 | 149 | return content 150 | 151 | def get_my_drive_item_by_path(self, item_path: str) -> dict: 152 | """Grab"s a DriveItem Resource using the Item ID and Drive ID. 153 | 154 | ### Parameters 155 | ---- 156 | item_path : str 157 | The path to the Item. 158 | 159 | ### Returns 160 | ---- 161 | dict : 162 | A DriveItem resource object. 163 | """ 164 | 165 | content = self.graph_session.make_request( 166 | method="get", 167 | endpoint=f"/me/drive/root:/{item_path}" 168 | ) 169 | 170 | return content 171 | 172 | def get_site_drive_item(self, site_id: str, item_id: str) -> dict: 173 | """Grab"s a DriveItem Resource using the Item ID and Drive ID. 174 | 175 | ### Parameters 176 | ---- 177 | site_id : str 178 | The site ID which to query the item from. 179 | 180 | item_id : str 181 | The item ID of the object you want to 182 | return. 183 | 184 | ### Returns 185 | ---- 186 | dict : 187 | A DriveItem resource object. 188 | """ 189 | 190 | content = self.graph_session.make_request( 191 | method="get", 192 | endpoint=f"/sites/{site_id}/drive/items/{item_id}" 193 | ) 194 | 195 | return content 196 | 197 | def get_site_drive_item_by_path(self, site_id: str, item_path: str) -> dict: 198 | """Grab"s a DriveItem Resource using the Item ID and Drive ID. 199 | 200 | ### Parameters 201 | ---- 202 | site_id : str 203 | The site ID which to query the item from. 204 | 205 | item_path : str 206 | The path to the Item. 207 | 208 | ### Returns 209 | ---- 210 | dict : 211 | A DriveItem resource object. 212 | """ 213 | 214 | content = self.graph_session.make_request( 215 | method="get", 216 | endpoint=f"/sites/{site_id}/drive/root:/{item_path}" 217 | ) 218 | 219 | return content 220 | 221 | def get_site_drive_item_from_list(self, site_id: str, list_id: str, item_id: str) -> dict: 222 | """Grab"s a DriveItem Resource using the Item ID and Drive ID. 223 | 224 | ### Parameters 225 | ---- 226 | site_id : str 227 | The site ID which to query the item from. 228 | 229 | list_id : str 230 | The list ID which to query the item from. 231 | 232 | item_id : str 233 | The item ID of the object you want to 234 | return. 235 | 236 | ### Returns 237 | ---- 238 | dict : 239 | A DriveItem resource object. 240 | """ 241 | 242 | content = self.graph_session.make_request( 243 | method="get", 244 | endpoint=f"/sites/{site_id}/lists/{list_id}/items/{item_id}/driveItem" 245 | ) 246 | 247 | return content 248 | 249 | def get_user_drive_item(self, user_id: str, item_id: str) -> dict: 250 | """Grab"s a DriveItem Resource using the Item ID and Drive ID. 251 | 252 | ### Parameters 253 | ---- 254 | user_id : str 255 | The User ID which to query the item from. 256 | 257 | item_id : str 258 | The item ID of the object you want to 259 | return. 260 | 261 | ### Returns 262 | ---- 263 | dict : 264 | A DriveItem resource object. 265 | """ 266 | 267 | content = self.graph_session.make_request( 268 | method="get", 269 | endpoint=f"/users/{user_id}/drive/items/{item_id}" 270 | ) 271 | 272 | return content 273 | 274 | def get_user_drive_item_by_path(self, user_id: str, item_path: str) -> dict: 275 | """Grab"s a DriveItem Resource using the Item ID and Drive ID. 276 | 277 | ### Parameters 278 | ---- 279 | site_id : str 280 | The User ID which to query the item from. 281 | 282 | item_path : str 283 | The path to the Item. 284 | 285 | ### Returns 286 | ---- 287 | dict : 288 | A DriveItem resource object. 289 | """ 290 | 291 | content = self.graph_session.make_request( 292 | method="get", 293 | endpoint=f"/users/{user_id}/drive/root:/{item_path}" 294 | ) 295 | 296 | return content 297 | -------------------------------------------------------------------------------- /ms_graph/drives.py: -------------------------------------------------------------------------------- 1 | from ms_graph.session import GraphSession 2 | 3 | 4 | class Drives(): 5 | 6 | """ 7 | ## Overview: 8 | ---- 9 | The drive resource is the top level object representing a user"s OneDrive or a 10 | document library in SharePoint. OneDrive users will always have at least one drive 11 | available, their default drive. Users without a OneDrive license may not have a default 12 | drive available. 13 | """ 14 | 15 | def __init__(self, session: object) -> None: 16 | """Initializes the `Drives` object. 17 | 18 | ### Parameters 19 | ---- 20 | session : object 21 | An authenticated session for our Microsoft Graph Client. 22 | """ 23 | 24 | # Set the session. 25 | self.graph_session: GraphSession = session 26 | 27 | # Set the endpoint. 28 | self.endpoint = "drive" 29 | self.collections_endpoint = "drives" 30 | 31 | def get_root_drive(self) -> dict: 32 | """Get root folder for user"s default Drive. 33 | 34 | ### Returns 35 | ---- 36 | dict : 37 | A Drive Resource Object. 38 | """ 39 | 40 | content = self.graph_session.make_request( 41 | method="get", 42 | endpoint=self.endpoint + "/root" 43 | ) 44 | 45 | return content 46 | 47 | def get_root_drive_children(self) -> dict: 48 | """List children under the Drive for user"s default Drive. 49 | 50 | ### Returns 51 | ---- 52 | dict : 53 | A List of Drive Resource Object. 54 | """ 55 | 56 | content = self.graph_session.make_request( 57 | method="get", 58 | endpoint=self.endpoint + "/root/children" 59 | ) 60 | 61 | return content 62 | 63 | def get_root_drive_delta(self) -> dict: 64 | """List children under the Drive for user"s default Drive. 65 | 66 | ### Returns 67 | ---- 68 | dict : 69 | A List of Drive Resource Object. 70 | """ 71 | 72 | content = self.graph_session.make_request( 73 | method="get", 74 | endpoint=self.endpoint + "/root/delta" 75 | ) 76 | 77 | return content 78 | 79 | def get_root_drive_followed(self) -> dict: 80 | """List user"s followed driveItems. 81 | 82 | ### Returns 83 | ---- 84 | dict : 85 | A List of DriveItem Resource Object. 86 | """ 87 | 88 | content = self.graph_session.make_request( 89 | method="get", 90 | endpoint=self.endpoint + "/root/followed" 91 | ) 92 | 93 | return content 94 | 95 | def get_recent_files(self) -> dict: 96 | """List a set of items that have been recently used by the signed in user. 97 | 98 | ### Overview: 99 | ---- 100 | This collection includes items that are in the user"s drive 101 | as well as items they have access to from other drives. 102 | 103 | ### Returns 104 | ---- 105 | dict : 106 | A List of DriveItem Resource Objects. 107 | """ 108 | 109 | content = self.graph_session.make_request( 110 | method="get", 111 | endpoint="me/drive/recent" 112 | ) 113 | 114 | return content 115 | 116 | def get_shared_files(self) -> dict: 117 | """Retrieve a collection of DriveItem resources that have been 118 | shared with the owner of the Drive. 119 | 120 | ### Returns 121 | ---- 122 | dict : 123 | A List of DriveItem Resource Objects. 124 | """ 125 | 126 | content = self.graph_session.make_request( 127 | method="get", 128 | endpoint="me/drive/sharedWithMe" 129 | ) 130 | 131 | return content 132 | 133 | def get_special_folder_by_name(self, folder_name: str) -> dict: 134 | """Use the special collection to access a special folder by name. 135 | 136 | ### Overview: 137 | ---- 138 | Special folders provide simple aliases to access well-known folders 139 | in OneDrive without the need to look up the folder by path (which 140 | would require localization), or reference the folder with an ID. If 141 | a special folder is renamed or moved to another location within the 142 | drive, this syntax will continue to find that folder. Special folders 143 | are automatically created the first time an application attempts to write 144 | to one, if it doesn"t already exist. If a user deletes one, it is recreated 145 | when written to again. Note: If you have read-only permissions and request 146 | a special folder that doesn"t exist, you"ll receive a 403 Forbidden error. 147 | 148 | ### Returns 149 | ---- 150 | dict : 151 | A List of DriveItem Resource Objects. 152 | """ 153 | 154 | content = self.graph_session.make_request( 155 | method="get", 156 | endpoint=f"/me/drive/special/{folder_name}" 157 | ) 158 | 159 | return content 160 | 161 | def get_special_folder_children_by_name(self, folder_name: str) -> dict: 162 | """Use the special collection to access a collection of Children belonging to special folder 163 | by name. 164 | 165 | ### Returns 166 | ---- 167 | dict : 168 | A List of DriveItem Resource Objects. 169 | """ 170 | 171 | content = self.graph_session.make_request( 172 | method="get", 173 | endpoint=f"/me/drive/special/{folder_name}/children" 174 | ) 175 | 176 | return content 177 | 178 | def get_drive_by_id(self, drive_id: str) -> dict: 179 | """Grab"s a Drive Resource using the Drive ID. 180 | 181 | ### Returns 182 | ---- 183 | dict : 184 | A Drive Resource Object. 185 | """ 186 | 187 | content = self.graph_session.make_request( 188 | method="get", 189 | endpoint=self.collections_endpoint + f"/{drive_id}" 190 | ) 191 | 192 | return content 193 | 194 | def get_my_drive(self) -> dict: 195 | """Get"s the User"s Current OneDrive. 196 | 197 | ### Returns 198 | ---- 199 | dict : 200 | A Drive Resource Object. 201 | """ 202 | 203 | content = self.graph_session.make_request( 204 | method="get", 205 | endpoint=self.endpoint + "/me" 206 | ) 207 | 208 | return content 209 | 210 | def get_my_drive_children(self, item_id: str) -> dict: 211 | """Returns a list of DriveItem Resources for the User"s Current OneDrive. 212 | 213 | ### Returns 214 | ---- 215 | dict : 216 | A List of DriveChildren Resource Objects. 217 | """ 218 | 219 | content = self.graph_session.make_request( 220 | method="get", 221 | endpoint=self.endpoint + f"/me/drive/items/{item_id}/children" 222 | ) 223 | 224 | return content 225 | 226 | def get_my_drives(self) -> dict: 227 | """List children under the Drive for user"s default Drive. 228 | 229 | ### Returns 230 | ---- 231 | dict : 232 | A List of Drive Resource Objects. 233 | """ 234 | 235 | content = self.graph_session.make_request( 236 | method="get", 237 | endpoint=self.collections_endpoint + "/me" 238 | ) 239 | 240 | return content 241 | 242 | def get_user_drive(self, user_id: str) -> dict: 243 | """Returns the User"s default OneDrive. 244 | 245 | ### Returns 246 | ---- 247 | dict : 248 | A Drive Resource Object. 249 | """ 250 | 251 | content = self.graph_session.make_request( 252 | method="get", 253 | endpoint=f"users/{user_id}/drive" 254 | ) 255 | 256 | return content 257 | 258 | def get_user_drive_children(self, user_id: str, item_id: str) -> dict: 259 | """Returns a list of DriveItem Resources for the Default User Drive. 260 | 261 | ### Returns 262 | ---- 263 | dict : 264 | A List of DriveChildren Resource Objects. 265 | """ 266 | 267 | content = self.graph_session.make_request( 268 | method="get", 269 | endpoint=f"users/{user_id}/drive/items/{item_id}/children" 270 | ) 271 | 272 | return content 273 | 274 | def get_user_drives(self, user_id: str) -> dict: 275 | """Returns a List Drive Resource Objects for user"s default Drive. 276 | 277 | ### Returns 278 | ---- 279 | dict : 280 | A List of Drive Resource Objects. 281 | """ 282 | 283 | content = self.graph_session.make_request( 284 | method="get", 285 | endpoint=f"users/{user_id}/drives" 286 | ) 287 | 288 | return content 289 | 290 | def get_group_drive(self, group_id: str) -> dict: 291 | """Returns a Site Group default Drive.. 292 | 293 | ### Returns 294 | ---- 295 | dict : 296 | A Drive Resource Object. 297 | """ 298 | 299 | content = self.graph_session.make_request( 300 | method="get", 301 | endpoint=f"groups/{group_id}/drive" 302 | ) 303 | 304 | return content 305 | 306 | def get_group_drive_children(self, group_id: str, item_id: str) -> dict: 307 | """Returns a list of DriveItems for the Specified Drive ID for the Specified Group. 308 | 309 | ### Returns 310 | ---- 311 | dict : 312 | A List of DriveChildren Resource Objects. 313 | """ 314 | 315 | content = self.graph_session.make_request( 316 | method="get", 317 | endpoint=f"groups/{group_id}/drive/items/{item_id}/children" 318 | ) 319 | 320 | return content 321 | 322 | def get_group_drives(self, group_id: str) -> dict: 323 | """List children under the Drive for user"s default Drive. 324 | 325 | ### Returns 326 | ---- 327 | dict : 328 | A List of Drive Resource Objects. 329 | """ 330 | 331 | content = self.graph_session.make_request( 332 | method="get", 333 | endpoint=f"groups/{group_id}/drives" 334 | ) 335 | 336 | return content 337 | 338 | def get_sites_drive(self, site_id: str) -> dict: 339 | """Returns the Default Drive Resource For the Specified Site ID. 340 | 341 | ### Returns 342 | ---- 343 | dict : 344 | A Drive Resource Object. 345 | """ 346 | 347 | content = self.graph_session.make_request( 348 | method="get", 349 | endpoint=f"sites/{site_id}/drive" 350 | ) 351 | 352 | return content 353 | 354 | def get_sites_drive_children(self, site_id: str, item_id: str) -> dict: 355 | """Returns a list of DriveItems for the Specified Drive ID on the Specified Site. 356 | 357 | ### Returns 358 | ---- 359 | dict : 360 | A List of DriveChildren Resource Objects. 361 | """ 362 | 363 | content = self.graph_session.make_request( 364 | method="get", 365 | endpoint=f"sites/{site_id}/drive/items/{item_id}/children" 366 | ) 367 | 368 | return content 369 | 370 | def get_sites_drives(self, site_id: str) -> dict: 371 | """Returns a List of Drive Resources for the Specified Site ID. 372 | 373 | ### Returns 374 | ---- 375 | dict : 376 | A List of Drive Resource Objects. 377 | """ 378 | 379 | content = self.graph_session.make_request( 380 | method="get", 381 | endpoint=f"sites/{site_id}/drives" 382 | ) 383 | 384 | return content 385 | -------------------------------------------------------------------------------- /ms_graph/groups.py: -------------------------------------------------------------------------------- 1 | from ms_graph.session import GraphSession 2 | 3 | 4 | class Groups(): 5 | 6 | """ 7 | ## Overview: 8 | ---- 9 | Groups are collections of users and other principals who share 10 | access to resources in Microsoft services or in your app. Microsoft 11 | Graph provides APIs that you can use to create and manage different 12 | types of groups and group functionality according to your scenario. 13 | All group-related operations in Microsoft Graph require administrator 14 | consent. 15 | """ 16 | 17 | def __init__(self, session: object) -> None: 18 | """Initializes the `Group` service. 19 | 20 | ### Parameters 21 | ---- 22 | session : object 23 | An authenticated session for our Microsoft Graph Client. 24 | """ 25 | 26 | # Set the session. 27 | self.graph_session: GraphSession = session 28 | 29 | # Set the endpoint. 30 | self.endpoint = "group" 31 | self.collections_endpoint = "groups" 32 | 33 | def list_groups(self) -> dict: 34 | """List all the groups in an organization, including but 35 | not limited to Microsoft 365 groups. 36 | 37 | ### Returns 38 | ------- 39 | dict : 40 | If successful, this method returns a 200 OK 41 | response code and collection of group objects in 42 | the response body. The response includes only the 43 | default properties of each group. 44 | """ 45 | 46 | content = self.graph_session.make_request( 47 | method="get", 48 | endpoint=self.collections_endpoint 49 | ) 50 | 51 | return content 52 | -------------------------------------------------------------------------------- /ms_graph/notes.py: -------------------------------------------------------------------------------- 1 | from ms_graph.session import GraphSession 2 | 3 | 4 | class Notes(): 5 | 6 | """ 7 | ## Overview: 8 | ---- 9 | Microsoft Graph lets your app get authorized access to a user's 10 | OneNote notebooks, sections, and pages in a personal or organization 11 | account. With the appropriate delegated or application permissions, 12 | your app can access the OneNote data of the signed-in user or any 13 | user in a tenant. 14 | """ 15 | 16 | def __init__(self, session: object) -> None: 17 | """Initializes the `Notes` object. 18 | 19 | ### Parameters 20 | ---- 21 | session : object 22 | An authenticated session for our Microsoft Graph Client. 23 | """ 24 | 25 | # Set the session. 26 | self.graph_session: GraphSession = session 27 | 28 | # Set the endpoint. 29 | self.endpoint = "onenote" 30 | 31 | def list_my_notebooks(self) -> dict: 32 | """Retrieve a list of your notebook objects. 33 | 34 | ### Returns 35 | ---- 36 | dict : 37 | A List of `Notebook` Resource Object. 38 | """ 39 | 40 | # define the endpoints. 41 | endpoint = "me/" + self.endpoint + "/notebooks" 42 | 43 | content = self.graph_session.make_request( 44 | method="get", 45 | endpoint=endpoint 46 | ) 47 | 48 | return content 49 | 50 | def list_user_notebooks(self, user_id: str) -> dict: 51 | """Retrieve a list of notebook objects. 52 | 53 | ### Parameters 54 | ---- 55 | user_id (str): The User"s ID that is assoicated with 56 | their Graph account. 57 | 58 | ### Returns 59 | ---- 60 | dict : 61 | A List of `Notebook` Resource Object. 62 | """ 63 | 64 | # Define the endpoint. 65 | endpoint =f"users/{user_id}" + self.endpoint + "/notebooks" 66 | 67 | content = self.graph_session.make_request( 68 | method="get", 69 | endpoint=endpoint 70 | ) 71 | 72 | return content 73 | 74 | def list_group_notebooks(self, group_id: str) -> dict: 75 | """Retrieve a list of notebook objects. 76 | 77 | ### Parameters 78 | ---- 79 | group_id (str): The Group ID that you want to pull 80 | notebooks for. 81 | 82 | ### Returns 83 | ---- 84 | dict : 85 | A List of `Notebook` Resource Object. 86 | """ 87 | 88 | # Define the endpoint. 89 | endpoint = f"groups/{group_id}" + self.endpoint + "/notebooks" 90 | 91 | content = self.graph_session.make_request( 92 | method="get", 93 | endpoint=endpoint 94 | ) 95 | 96 | return content 97 | 98 | def list_site_notebooks(self, site_id: str) -> dict: 99 | """Retrieve a list of notebook objects. 100 | 101 | ### Parameters 102 | ---- 103 | site_id (str): The Site ID that you want to pull 104 | notebooks for. 105 | 106 | ### Returns 107 | ---- 108 | dict : 109 | A List of `Notebook` Resource Object. 110 | """ 111 | 112 | # Define the endpoint. 113 | endpoint = f"sites/{site_id}" + self.endpoint + "/notebooks" 114 | 115 | content = self.graph_session.make_request( 116 | method="get", 117 | endpoint=endpoint 118 | ) 119 | 120 | return content 121 | 122 | def get_my_notebook(self, notebook_id: str) -> dict: 123 | """Retrieve a list of notebook objects. 124 | 125 | ### Parameters 126 | ---- 127 | notebook_id (str): The User"s Notebook ID that you 128 | want to pull. 129 | 130 | ### Returns 131 | ---- 132 | dict : 133 | A List of `Notebook` Resource Object. 134 | """ 135 | 136 | # define the endpoints. 137 | endpoint = "me/" + self.endpoint + f"/notebooks/{notebook_id}" 138 | 139 | content = self.graph_session.make_request( 140 | method="get", 141 | endpoint=endpoint 142 | ) 143 | 144 | return content 145 | 146 | def get_user_notebook(self, user_id: str, notebook_id: str) -> dict: 147 | """Retrieve a notebook object from a user by it"s ID. 148 | 149 | ### Parameters 150 | ---- 151 | user_id (str): The User"s ID that is assoicated with 152 | their Graph account. 153 | 154 | notebook_id (str): The Notebook ID that you 155 | want to pull. 156 | 157 | ### Returns 158 | ---- 159 | dict : 160 | A List of `Notebook` Resource Object. 161 | """ 162 | 163 | # Define the endpoint. 164 | endpoint = f"users/{user_id}" + self.endpoint + f"/notebooks/{notebook_id}" 165 | 166 | content = self.graph_session.make_request( 167 | method="get", 168 | endpoint=endpoint 169 | ) 170 | 171 | return content 172 | 173 | def get_group_notebook(self, group_id: str, notebook_id: str) -> dict: 174 | """Retrieve a notebook object from a Group by it"s ID. 175 | 176 | ### Parameters 177 | ---- 178 | group_id (str): The Group ID that you want to pull 179 | notebooks for. 180 | 181 | notebook_id (str): The Notebook ID that you 182 | want to pull. 183 | 184 | ### Returns 185 | ---- 186 | dict : 187 | A List of `Notebook` Resource Object. 188 | """ 189 | 190 | # Define the endpoint. 191 | endpoint = f"groups/{group_id}" + self.endpoint + f"/notebooks/{notebook_id}" 192 | 193 | content = self.graph_session.make_request( 194 | method="get", 195 | endpoint=endpoint 196 | ) 197 | 198 | return content 199 | 200 | def get_site_notebook(self, site_id: str, notebook_id: str) -> dict: 201 | """Retrieve a notebook object from a SharePoint Site by it"s ID. 202 | 203 | ### Parameters 204 | ---- 205 | site_id (str): The Site ID that you want to pull 206 | notebooks for. 207 | 208 | notebook_id (str): The Notebook ID that you 209 | want to pull. 210 | 211 | ### Returns 212 | ---- 213 | dict : 214 | A List of `Notebook` Resource Object. 215 | """ 216 | 217 | # Define the endpoint. 218 | endpoint = f"sites/{site_id}" + self.endpoint + f"/notebooks/{notebook_id}" 219 | 220 | content = self.graph_session.make_request( 221 | method="get", 222 | endpoint=endpoint 223 | ) 224 | 225 | return content 226 | 227 | def list_my_notebook_sections(self, notebook_id: str) -> dict: 228 | """Retrieve a list of onenoteSection objects from one of your notebooks. 229 | 230 | ### Parameters 231 | ---- 232 | notebook_id (str): The Notebook ID that you 233 | want to pull. 234 | 235 | ### Returns 236 | ---- 237 | dict : 238 | A List of `Notebook` Resource Object. 239 | """ 240 | 241 | # Define the endpoint. 242 | endpoint = endpoint = "me/" + self.endpoint + f"/notebooks/{notebook_id}/sections" 243 | 244 | content = self.graph_session.make_request( 245 | method="get", 246 | endpoint=endpoint 247 | ) 248 | 249 | return content 250 | 251 | def list_my_notebook_pages(self, section_id: str) -> dict: 252 | """Retrieve a list of onenoteSection objects from one of your notebooks. 253 | 254 | ### Parameters 255 | ---- 256 | notebook_id (str): The Notebook ID that you 257 | want to pull. 258 | 259 | section (str): The Section ID that you 260 | want to pull. 261 | 262 | ### Returns 263 | ---- 264 | dict : 265 | A List of `Notebook` Resource Object. 266 | """ 267 | 268 | # Define the endpoint. 269 | endpoint = endpoint = f"me/{self.endpoint}/" + f"/sections/{section_id}" 270 | 271 | content = self.graph_session.make_request( 272 | method="get", 273 | endpoint=endpoint 274 | ) 275 | 276 | return content 277 | -------------------------------------------------------------------------------- /ms_graph/personal_contacts.py: -------------------------------------------------------------------------------- 1 | from ms_graph.session import GraphSession 2 | 3 | 4 | class PersonalContacts(): 5 | 6 | """ 7 | ## Overview: 8 | ---- 9 | A contact is an item in Outlook where you can organize and save 10 | information about the people and organizations you communicate 11 | with. Contacts are contained in contact folders. 12 | """ 13 | 14 | def __init__(self, session: object) -> None: 15 | """Initializes the `PersonalContacts` object. 16 | 17 | ### Parameters 18 | ---- 19 | session : object 20 | An authenticated session for our Microsoft Graph Client. 21 | """ 22 | 23 | # Set the session. 24 | self.graph_session: GraphSession = session 25 | 26 | # Set the endpoint. 27 | self.endpoint = "contacts" 28 | self.endpoint_folders = "contactFolders" 29 | 30 | def list_my_contacts(self) -> dict: 31 | """Retrieves all the contacts from the users mailbox. 32 | 33 | ### Returns 34 | ---- 35 | dict : 36 | A List of `Contact` Resource Object. 37 | """ 38 | 39 | # define the endpoints. 40 | endpoint = "me/" + self.endpoint 41 | 42 | content = self.graph_session.make_request( 43 | method="get", 44 | endpoint=endpoint 45 | ) 46 | 47 | return content 48 | 49 | def list_my_contacts_folder(self) -> dict: 50 | """Retrieves all the contacts folders from the users mailbox. 51 | 52 | ### Returns 53 | ---- 54 | dict : 55 | A List of `ContactFolders` Resource Object. 56 | """ 57 | 58 | # define the endpoints. 59 | endpoint = "me/" + self.endpoint_folders 60 | 61 | content = self.graph_session.make_request( 62 | method="get", 63 | endpoint=endpoint 64 | ) 65 | 66 | return content 67 | 68 | def list_contacts_folder_by_id(self, user_id: str, folder_id: str) -> dict: 69 | """Retrieves all the contacts folders from the users mailbox. 70 | 71 | ### Parameters 72 | ---- 73 | user_id : str 74 | The User ID that the folder belongs to. 75 | 76 | folder_id : str 77 | The folder ID you want to retrieve. 78 | 79 | ### Returns 80 | ---- 81 | dict : 82 | A List of `ContactFolders` Resource Object. 83 | """ 84 | 85 | # define the endpoints. 86 | endpoint = f"users/{user_id}/" + self.endpoint_folders + f"/{folder_id}" 87 | 88 | content = self.graph_session.make_request( 89 | method="get", 90 | endpoint=endpoint 91 | ) 92 | 93 | return content 94 | 95 | def create_my_contact_folder(self, folder_resource: dict) -> dict: 96 | """Creates a new Contact Folder under the default users profile. 97 | 98 | ### Parameters 99 | ---- 100 | folder_resource : dict 101 | A dictionary that specifies the folder resource 102 | attributes like the folder ID and folder display 103 | value. 104 | 105 | ### Returns 106 | ---- 107 | dict : 108 | A `ContactFolder` Resource Object. 109 | """ 110 | 111 | # define the endpoints. 112 | endpoint = "me/" + self.endpoint_folders 113 | 114 | content = self.graph_session.make_request( 115 | method="post", 116 | endpoint=endpoint, 117 | json=folder_resource 118 | ) 119 | 120 | return content 121 | 122 | def create_user_contact_folder(self, user_id: str, folder_resource: dict) -> dict: 123 | """Creates a new Contact Folder under the specified users profile. 124 | 125 | ### Parameters 126 | ---- 127 | user_id : str 128 | The User ID that the folder belongs to. 129 | 130 | folder_resource : dict 131 | A dictionary that specifies the folder resource 132 | attributes like the folder ID and folder display 133 | value. 134 | 135 | ### Returns 136 | ---- 137 | dict : 138 | A `ContactFolder` Resource Object. 139 | """ 140 | 141 | # define the endpoints. 142 | endpoint = f"users/{user_id}/" + self.endpoint_folders 143 | 144 | content = self.graph_session.make_request( 145 | method="post", 146 | endpoint=endpoint, 147 | json=folder_resource 148 | ) 149 | 150 | return content 151 | 152 | def get_my_contacts_folder_by_id(self, folder_id: str) -> dict: 153 | """Retrieves a contactsFolder resource using the specified ID. 154 | 155 | ### Parameters 156 | ---- 157 | folder_id : str 158 | The folder ID you want to retrieve. 159 | 160 | ### Returns 161 | ---- 162 | dict : 163 | A `ContactFolder` Resource Object. 164 | """ 165 | 166 | # define the endpoints. 167 | endpoint = "me/" + self.endpoint_folders + f"/{folder_id}" 168 | 169 | content = self.graph_session.make_request( 170 | method="get", 171 | endpoint=endpoint 172 | ) 173 | 174 | return content 175 | 176 | def get_contacts_folder_by_id(self, user_id: str, folder_id: str) -> dict: 177 | """Retrieves a contactsFolder resource using the specified ID for the 178 | specified user. 179 | 180 | ### Parameters 181 | ---- 182 | user_id : str 183 | The User ID that the folder belongs to. 184 | 185 | folder_id : str 186 | The folder ID you want to retrieve. 187 | 188 | ### Returns 189 | ---- 190 | dict : 191 | A `ContactFolder` Resource Object. 192 | """ 193 | 194 | # define the endpoints. 195 | endpoint = f"users/{user_id}/" + self.endpoint_folders + f"/{folder_id}" 196 | 197 | content = self.graph_session.make_request( 198 | method="get", 199 | endpoint=endpoint 200 | ) 201 | 202 | return content 203 | 204 | def get_my_contact_by_id(self, contact_id: str) -> dict: 205 | """Retrieves the Contact Resource for the specified contact ID. 206 | 207 | ### Parameters 208 | ---- 209 | contact_id : str 210 | An authenticated session for our Microsoft Graph Client. 211 | 212 | ### Returns 213 | ---- 214 | dict : 215 | A List of `Contact` Resource Object. 216 | """ 217 | 218 | # define the endpoints. 219 | endpoint = "me/" + self.endpoint + f"/{contact_id}" 220 | 221 | content = self.graph_session.make_request( 222 | method="get", 223 | endpoint=endpoint 224 | ) 225 | 226 | return content 227 | -------------------------------------------------------------------------------- /ms_graph/search.py: -------------------------------------------------------------------------------- 1 | from ms_graph.session import GraphSession 2 | 3 | 4 | class Search(): 5 | 6 | """ 7 | ## Overview: 8 | ---- 9 | You can use the Microsoft Search API to query Microsoft 365 data in your apps. 10 | Search requests run in the context of the signed-in user, identified using an 11 | access token with delegated permissions. 12 | """ 13 | 14 | def __init__(self, session: object) -> None: 15 | """Initializes the `Query` object. 16 | 17 | ### Parameters 18 | ---- 19 | session : object 20 | An authenticated session for our Microsoft Graph Client. 21 | """ 22 | 23 | # Set the session. 24 | self.graph_session: GraphSession = session 25 | 26 | # Set the endpoint. 27 | self.endpoint = "search" 28 | 29 | def query(self, search_request: dict) -> dict: 30 | """Runs the query specified in the request body. Search 31 | results are provided in the response. 32 | 33 | ### Returns 34 | ---- 35 | dict : 36 | A `SearchResponse` collection. 37 | """ 38 | 39 | # define the endpoints. 40 | endpoint = self.endpoint + "/query" 41 | 42 | content = self.graph_session.make_request( 43 | method="post", 44 | endpoint=endpoint, 45 | json=search_request 46 | ) 47 | 48 | return content 49 | -------------------------------------------------------------------------------- /ms_graph/session.py: -------------------------------------------------------------------------------- 1 | import json as json_lib 2 | import logging 3 | import pathlib 4 | 5 | from typing import Dict 6 | from typing import List 7 | from typing import Union 8 | 9 | import requests 10 | 11 | class GraphSession(): 12 | 13 | """Serves as the Session for the Current Microsoft 14 | Graph API.""" 15 | 16 | def __init__(self, client: object) -> None: 17 | """Initializes the `GraphSession` client. 18 | 19 | ### Overview: 20 | ---- 21 | The GraphSession object handles all the requests made 22 | for the different endpoints on the Microsoft Graph API. 23 | 24 | ### Arguments: 25 | ---- 26 | client (str): The Microsoft Graph API Python Client. 27 | 28 | ### Usage: 29 | ---- 30 | >>> graph_session = GraphSession() 31 | """ 32 | 33 | from ms_graph.client import MicrosoftGraphClient 34 | 35 | # We can also add custom formatting to our log messages. 36 | log_format = "%(asctime)-15s|%(filename)s|%(message)s" 37 | 38 | if not pathlib.Path("logs").exists(): 39 | pathlib.Path("logs").mkdir() 40 | pathlib.Path("logs/log_file_custom.log").touch() 41 | 42 | self.client: MicrosoftGraphClient = client 43 | logging.basicConfig( 44 | filename="logs/log_file_custom.log", 45 | level=logging.INFO, 46 | encoding="utf-8", 47 | format=log_format 48 | ) 49 | 50 | def build_headers(self, additional_args: dict = None) -> Dict: 51 | """Used to build the headers needed to make the request. 52 | 53 | ### Parameters 54 | ---- 55 | additional_args : dict (optional, Default=None) 56 | Any additional headers that need to be sent in the 57 | request. 58 | 59 | ### Returns 60 | ---- 61 | dict : 62 | A dictionary containing all the components. 63 | """ 64 | 65 | # Define the base headers. 66 | headers = { 67 | "Authorization": f"Bearer {self.client.access_token}" 68 | } 69 | 70 | if additional_args: 71 | headers.update(additional_args) 72 | 73 | return headers 74 | 75 | def build_url(self, endpoint: str) -> str: 76 | """Build the URL used the make string. 77 | 78 | ### Parameters 79 | ---- 80 | endpoint : str 81 | The endpoint used to make the full URL. 82 | 83 | ### Returns 84 | ---- 85 | str: 86 | The full URL with the endpoint needed. 87 | """ 88 | 89 | url = self.client.RESOURCE + self.client.api_version + "/" + endpoint 90 | 91 | return url 92 | 93 | def make_request( 94 | self, 95 | method: str, 96 | endpoint: str, 97 | params: dict = None, 98 | data: dict = None, 99 | json: dict = None, 100 | additional_headers: dict = None, 101 | expect_no_response: bool = False 102 | ) -> Union[Dict, List]: 103 | """Handles all the requests in the library. 104 | 105 | ### Overview: 106 | --- 107 | A central function used to handle all the requests made in the library, 108 | this function handles building the URL, defining Content-Type, passing 109 | through payloads, and handling any errors that may arise during the request. 110 | 111 | ### Arguments: 112 | ---- 113 | method : str 114 | The Request method, can be one of the 115 | following: ["get","post","put","delete","patch"] 116 | 117 | endpoint : str 118 | The API URL endpoint, example is "quotes" 119 | 120 | params : dict (optional, Default=None) 121 | The URL params for the request. 122 | 123 | data : dict (optional, Default=None) 124 | A data payload for a request. 125 | 126 | json : dict (optional, Default=None) 127 | A json data payload for a request 128 | 129 | expect_no_response: bool (optional, Default=False) 130 | Some responses will only return a status code, 131 | so if this is set to True it will only return 132 | the status code. 133 | 134 | ### Returns: 135 | ---- 136 | Union[List, Dict]: 137 | The resource object or objects. 138 | """ 139 | 140 | # Build the URL. 141 | url = self.build_url(endpoint=endpoint) 142 | 143 | # Define the headers. 144 | headers = self.build_headers(additional_args=additional_headers) 145 | 146 | logging.info( 147 | f"URL: {url}" 148 | ) 149 | 150 | # Define a new session. 151 | request_session = requests.Session() 152 | request_session.verify = True 153 | 154 | # Define a new request. 155 | request_request = requests.Request( 156 | method=method.upper(), 157 | headers=headers, 158 | url=url, 159 | params=params, 160 | data=data, 161 | json=json 162 | ).prepare() 163 | 164 | # Send the request. 165 | response: requests.Response = request_session.send( 166 | request=request_request 167 | ) 168 | 169 | # Close the session. 170 | request_session.close() 171 | 172 | # If it"s okay and no details. 173 | if response.ok and expect_no_response: 174 | return {"status_code": response.status_code} 175 | elif response.ok and len(response.content) > 0: 176 | return response.json() 177 | elif len(response.content) == 0 and response.ok: 178 | return { 179 | "message": "Request was successful, status code provided.", 180 | "status_code": response.status_code 181 | } 182 | elif not response.ok: 183 | 184 | # Define the error dict. 185 | error_dict = { 186 | "error_code": response.status_code, 187 | "response_url": response.url, 188 | "response_body": json_lib.loads(response.content.decode("ascii")), 189 | "response_request": dict(response.request.headers), 190 | "response_method": response.request.method, 191 | } 192 | 193 | # Log the error. 194 | logging.error( 195 | msg=json_lib.dumps(obj=error_dict, indent=4) 196 | ) 197 | 198 | raise requests.HTTPError() 199 | -------------------------------------------------------------------------------- /ms_graph/users.py: -------------------------------------------------------------------------------- 1 | from ms_graph.session import GraphSession 2 | 3 | 4 | class Users(): 5 | 6 | """ 7 | ## Overview: 8 | ---- 9 | You can use Microsoft Graph to build compelling app experiences 10 | based on users, their relationships with other users and groups, 11 | and their mail, calendar, and files. 12 | """ 13 | 14 | def __init__(self, session: object) -> None: 15 | """Initializes the `Users` object. 16 | 17 | ### Parameters 18 | ---- 19 | session : object 20 | An authenticated session for our Microsoft Graph Client. 21 | """ 22 | 23 | # Set the session. 24 | self.graph_session: GraphSession = session 25 | 26 | # Set the endpoint. 27 | self.endpoint = "users" 28 | 29 | def list_users(self) -> dict: 30 | """Retrieve a list of user objects. 31 | 32 | ### Returns 33 | ---- 34 | dict : 35 | If successful, this method returns a 200 OK response code 36 | and collection of user objects in the response body. If a 37 | large user collection is returned, you can use paging in your 38 | app. 39 | """ 40 | 41 | content = self.graph_session.make_request( 42 | method="get", 43 | endpoint=self.endpoint 44 | ) 45 | 46 | return content 47 | -------------------------------------------------------------------------------- /ms_graph/utils/range.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | from typing import Union 3 | from dataclasses import fields 4 | from dataclasses import dataclass 5 | from dataclasses import is_dataclass 6 | 7 | 8 | def _to_dict(data_class_obj: Union[dataclass, dict]) -> dict: 9 | """Converts a `dataclass` object to a normal python `dict`. 10 | 11 | ### Parameter 12 | ---- 13 | data_class_obj : Union[dataclass, dict] 14 | The python `dataclass` object or a normal 15 | python `dict` that may contain `dataclass` 16 | objects. 17 | 18 | ### Returns 19 | ---- 20 | dict : 21 | A python dict that can be sent to the Microsoft 22 | Graph API. 23 | """ 24 | 25 | class_dict = {} 26 | 27 | if is_dataclass(data_class_obj): 28 | 29 | # Loop through each field and grab the value and key. 30 | for field in fields(data_class_obj): 31 | 32 | key = field.name 33 | value = getattr(data_class_obj, field.name) 34 | 35 | # Handle values that could be Enums. 36 | if isinstance(value, Enum): 37 | value = value.value 38 | 39 | if isinstance(value, dict): 40 | value = _to_dict(data_class_obj=value) 41 | 42 | if isinstance(value, list): 43 | value = [_to_dict(data_class_obj=item) for item in value] 44 | 45 | if value is not None: 46 | class_dict[key] = value 47 | 48 | elif isinstance(data_class_obj, dict): 49 | 50 | for key, value in data_class_obj.items(): 51 | 52 | # Handle values that could be Enums. 53 | if isinstance(value, Enum): 54 | value = value.value 55 | 56 | if isinstance(value, dict): 57 | value = _to_dict(data_class_obj=value) 58 | 59 | if isinstance(value, list): 60 | value = [_to_dict(data_class_obj=item) for item in value] 61 | 62 | if value is not None: 63 | class_dict[key] = value 64 | 65 | return class_dict 66 | 67 | 68 | @dataclass 69 | class RangeProperties: 70 | 71 | """ 72 | ### Overview 73 | ---- 74 | A python dataclass which is used to represent Range Properties. 75 | The Microsoft Graph API allows users to update Range objects and 76 | this utility makes constructing those updates in a concise way that 77 | is python friendly. 78 | 79 | ### Parameters 80 | ---- 81 | column_hidden : bool (optional, Default=None) 82 | Represents if all columns of the current range are hidden. 83 | 84 | formulas : list (optional, Default=None) 85 | Represents the formula in A1-style notation. 86 | 87 | formulas_local : list (optional, Default=None) 88 | Represents the formula in A1-style notation, in the user's 89 | language and number-formatting locale. For example, the 90 | English "=SUM(A1, 1.5)" formula would become 91 | "=SUMME(A1; 1,5)" in German. 92 | 93 | formulas_r1c1 : list (optional, Default=None) 94 | Represents the formula in R1C1-style notation. 95 | 96 | number_format : str (optional, Default=None) 97 | Represents Excel's number format code for the given cell. 98 | 99 | row_hidden : bool (optional, Default=None) 100 | Represents if all rows of the current range are hidden. 101 | 102 | values : list (optional, Default=None) 103 | Represents the raw values of the specified range. The 104 | data returned could be of type string, number, or a 105 | boolean. Cell that contain an error will return the 106 | error string. 107 | """ 108 | 109 | column_hidden: bool 110 | row_hidden: bool 111 | formulas: list 112 | formulas_local: list 113 | formulas_r1c1: list 114 | number_format: str 115 | values: list 116 | 117 | def to_dict(self) -> dict: 118 | """Generates a dictionary containing all the field 119 | names and values. 120 | 121 | ### Returns 122 | ---- 123 | dict : 124 | A dictionary object where the Fieldnames 125 | are the keys and Field values are the 126 | values. 127 | """ 128 | 129 | return _to_dict(data_class_obj=self) 130 | 131 | 132 | @dataclass 133 | class RangeFormatProperties: 134 | 135 | """ 136 | ### Overview 137 | ---- 138 | A python dataclass which is used to represent Range Format 139 | Properties. A format object encapsulating the range's font, 140 | fill, borders, alignment, and other properties. 141 | 142 | ### Parameters 143 | ---- 144 | column_width : float (optional, Default=None) 145 | Sets the width of all columns within the range. If the 146 | column widths are not uniform, null will be returned. 147 | 148 | horizontal_alignment : Union[str, Enum] (optional, Default='General') 149 | Represents the horizontal alignment for the specified 150 | object. The possible values are: `General`, `Left`, `Center`, 151 | `Right`, `Fill`, `Justify`, `CenterAcrossSelection`, 152 | and `Distributed`. 153 | 154 | row_height : float (optional, Default=None) 155 | Sets the height of all rows in the range. If the 156 | row heights are not uniform, null will be returned. 157 | 158 | vertical_alignment : Union[str, Enum] (optional, Default='General') 159 | Represents the vertical alignment for the specified 160 | object. The possible values are: `Top`, `Center`, `Bottom`, 161 | `Justify`, and `Distributed`. 162 | 163 | wrap_text : bool (optional, Default=False) 164 | Indicates if Excel wraps the text in the object. 165 | A null value indicates that the entire range doesn't 166 | have uniform wrap setting 167 | """ 168 | 169 | column_width: float = None 170 | horizontal_alignment: Union[str, Enum] = "General" 171 | row_height: float = None 172 | vertical_alignment: Union[str, Enum] = "General" 173 | wrap_text: bool = False 174 | 175 | def to_dict(self) -> dict: 176 | """Generates a dictionary containing all the field 177 | names and values. 178 | 179 | ### Returns 180 | ---- 181 | dict : 182 | A dictionary object where the Fieldnames 183 | are the keys and Field values are the 184 | values. 185 | """ 186 | 187 | return _to_dict(data_class_obj=self) 188 | 189 | @dataclass 190 | class RangeFillProperties: 191 | 192 | """ 193 | ### Overview 194 | ---- 195 | A python dataclass which is used to represent Range Fill 196 | Properties. Represents the background of a range object. 197 | 198 | ### Parameters 199 | ---- 200 | color : str (optional, Default=None) 201 | HTML color code representing the color of the border 202 | line, of the form #RRGGBB (e.g. "FFA500") or as a 203 | named HTML color (e.g. "orange"). 204 | """ 205 | 206 | column_width: str = None 207 | 208 | def to_dict(self) -> dict: 209 | """Generates a dictionary containing all the field 210 | names and values. 211 | 212 | ### Returns 213 | ---- 214 | dict : 215 | A dictionary object where the Fieldnames 216 | are the keys and Field values are the 217 | values. 218 | """ 219 | 220 | return _to_dict(data_class_obj=self) 221 | 222 | @dataclass 223 | class RangeFontProperties: 224 | 225 | """ 226 | ### Overview 227 | ---- 228 | A python dataclass which is used to represent Range Font 229 | Properties. This object represents the font attributes 230 | (font name, font size, color, etc.) for an object. 231 | 232 | ### Parameters 233 | ---- 234 | bold : bool (optional, Default=False) 235 | Represents the bold status of font. If set to `True` 236 | font will be bold, `False` it will not. 237 | 238 | color : str (optional, Default=None) 239 | HTML color code representation of the text color. 240 | E.g. #FF0000 represents Red. 241 | 242 | italic : bool (optional, Default=False) 243 | Represents the italic status of font. If set to `True` 244 | font will be italic, `False` it will not. 245 | 246 | name : str (optional, Default=None) 247 | The font name. For example, `Calibri`. 248 | 249 | size : float (optional, Default=None) 250 | Sets the size of the font. 251 | 252 | underline : Union[str, Enum] (optional, Default='None') 253 | Type of underline applied to the font. The possible 254 | values are: `None`, `Single`, `Double`, 255 | `SingleAccountant`, `DoubleAccountant`. 256 | """ 257 | 258 | bold: bool = False 259 | color: str = None 260 | italic: bool = False 261 | name: str = None 262 | size: float = None 263 | underline: Union[str, Enum] = "None" 264 | 265 | def to_dict(self) -> dict: 266 | """Generates a dictionary containing all the field 267 | names and values. 268 | 269 | ### Returns 270 | ---- 271 | dict : 272 | A dictionary object where the Fieldnames 273 | are the keys and Field values are the 274 | values. 275 | """ 276 | 277 | return _to_dict(data_class_obj=self) 278 | 279 | @dataclass 280 | class RangeBorderProperties: 281 | 282 | """ 283 | ### Overview 284 | ---- 285 | A python dataclass which is used to represent Range Border 286 | Properties. Represents the border of an object. 287 | 288 | ### Parameters 289 | ---- 290 | color : str (optional, Default=None) 291 | HTML color code representing the color of the border 292 | line, of the form #RRGGBB (e.g. "FFA500") or as a 293 | named HTML color (e.g. "orange"). the text color. 294 | 295 | style : Union[str, Enum] (optional, Default="None") 296 | One of the constants of line style specifying the 297 | line style for the border. The possible values 298 | are: `None`, `Continuous`, `Dash`, `DashDot`, 299 | `DashDotDot`, `Dot`, `Double`, `SlantDashDot`. 300 | 301 | weight : Union[str, Enum] (optional, Default=None) 302 | Specifies the weight of the border around a 303 | range. The possible values are: `Hairline`, 304 | `Thin`, `Medium`, and `Thick`. 305 | """ 306 | 307 | color: str = None 308 | style: Union[str, Enum] = "None" 309 | weight: Union[str, Enum] = None 310 | 311 | def to_dict(self) -> dict: 312 | """Generates a dictionary containing all the field 313 | names and values. 314 | 315 | ### Returns 316 | ---- 317 | dict : 318 | A dictionary object where the Fieldnames 319 | are the keys and Field values are the 320 | values. 321 | """ 322 | 323 | return _to_dict(data_class_obj=self) 324 | 325 | @dataclass 326 | class RangeFormatProtectionProperties: 327 | 328 | """ 329 | ### Overview 330 | ---- 331 | A python dataclass which is used to represent Range Format 332 | Protection Properties. Represents the format protection 333 | of a range object. 334 | 335 | ### Parameters 336 | ---- 337 | formula_hidden : bool (optional, Default=False) 338 | Indicates if Excel hides the formula for the cells 339 | in the range. A null value indicates that the entire 340 | range doesn't have uniform formula hidden setting. 341 | 342 | locked : bool (optional, Default=False) 343 | Indicates if Excel locks the cells in the object. 344 | A null value indicates that the entire range 345 | doesn't have uniform lock setting. 346 | """ 347 | 348 | formula_hidden: bool = False 349 | locked: bool = False 350 | 351 | def to_dict(self) -> dict: 352 | """Generates a dictionary containing all the field 353 | names and values. 354 | 355 | ### Returns 356 | ---- 357 | dict : 358 | A dictionary object where the Fieldnames 359 | are the keys and Field values are the 360 | values. 361 | """ 362 | 363 | return _to_dict(data_class_obj=self) 364 | -------------------------------------------------------------------------------- /ms_graph/workbooks_and_charts/application.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | from typing import Union 3 | from ms_graph.session import GraphSession 4 | 5 | 6 | class WorkbookApplication: 7 | 8 | """ 9 | ## Overview: 10 | ---- 11 | Represents the Excel application that manages 12 | the workbook. 13 | """ 14 | 15 | def __init__(self, session: object) -> None: 16 | """Initializes the `WorkbookApplication` object. 17 | 18 | ### Parameters 19 | ---- 20 | session : object 21 | An authenticated session for our Microsoft Graph Client. 22 | """ 23 | 24 | # Set the session. 25 | self.graph_session: GraphSession = session 26 | 27 | def get(self, item_id: str = None, item_path: str = None) -> dict: 28 | """Retrieve the properties and relationships of a workbookApplication 29 | object using the Item ID or Item Path. 30 | 31 | ### Parameters 32 | ---- 33 | item_id : str (optional, Default=None) 34 | The Drive Item Resource ID. 35 | 36 | item_path : str (optional, Default=None) 37 | The Item Path. An Example would be the following: 38 | `/TestFolder/TestFile.txt` 39 | 40 | ### Returns 41 | ---- 42 | dict: 43 | A workbookApplication resource object. 44 | """ 45 | 46 | if item_id: 47 | content = self.graph_session.make_request( 48 | method="get", 49 | endpoint=f"/me/drive/items/{item_id}/workbook/application", 50 | ) 51 | elif item_path: 52 | content = self.graph_session.make_request( 53 | method="get", 54 | endpoint=f"/me/drive/root:/{item_path}:/workbook/application", 55 | ) 56 | 57 | return content 58 | 59 | def calculate( 60 | self, 61 | calculation_type: Union[str, Enum], 62 | item_id: str = None, 63 | item_path: str = None 64 | ) -> dict: 65 | """Recalculate all currently opened workbooks in Excel using the 66 | Item ID or Item Path. 67 | 68 | ### Parameters 69 | ---- 70 | calculation_type : 71 | Specifies the calculation type to use. Possible 72 | values are: `Recalculate`, `Full`, `FullRebuild`. 73 | 74 | item_id : str (optional, Default=None) 75 | The Drive Item Resource ID. 76 | 77 | item_path : str (optional, Default=None) 78 | The Item Path. An Example would be the following: 79 | `/TestFolder/TestFile.txt` 80 | 81 | ### Returns 82 | ---- 83 | dict: 84 | A response object status code, 200 for success. 85 | """ 86 | 87 | if isinstance(calculation_type, Enum): 88 | data = {"calculationType": calculation_type.value} 89 | else: 90 | data = {"calculationType": calculation_type} 91 | 92 | if item_id: 93 | content = self.graph_session.make_request( 94 | method="get", 95 | endpoint=f"/me/drive/items/{item_id}/workbook/application", 96 | json=data, 97 | additional_headers={"Content-type": "application/json"}, 98 | expect_no_response=True, 99 | ) 100 | elif item_path: 101 | content = self.graph_session.make_request( 102 | method="get", 103 | endpoint=f"/me/drive/root:/{item_path}:/workbook/application", 104 | json=data, 105 | additional_headers={"Content-type": "application/json"}, 106 | expect_no_response=True, 107 | ) 108 | 109 | return content 110 | -------------------------------------------------------------------------------- /ms_graph/workbooks_and_charts/comments.py: -------------------------------------------------------------------------------- 1 | from ms_graph.session import GraphSession 2 | 3 | 4 | class WorkbookComments: 5 | 6 | """ 7 | ## Overview: 8 | ---- 9 | Represents a comment in workbook. 10 | """ 11 | 12 | def __init__(self, session: object) -> None: 13 | """Initializes the `WorkbookComment` object. 14 | 15 | ### Parameters 16 | ---- 17 | session : object 18 | An authenticated session for our Microsoft Graph Client. 19 | """ 20 | 21 | # Set the session. 22 | self.graph_session: GraphSession = session 23 | 24 | def list(self, item_id: str) -> dict: 25 | """Retrieve a list of workbookComment objects using the 26 | Item ID. 27 | 28 | ### Parameters 29 | ---- 30 | item_id : str 31 | The drive item resource id. 32 | 33 | ### Returns 34 | ---- 35 | dict: 36 | A collection of WorkbookComment objects. 37 | """ 38 | 39 | content = self.graph_session.make_request( 40 | method="get", 41 | endpoint=f"/me/drive/items/{item_id}/workbook/comments", 42 | ) 43 | 44 | return content 45 | 46 | def get(self, item_id: str, comment_id: str) -> dict: 47 | """Retrieve the properties and relationships of 48 | a WorkbookComment object. 49 | 50 | ### Parameters 51 | ---- 52 | item_id : str 53 | The drive item resource id. 54 | 55 | comment_id : str 56 | The comment resource id. 57 | 58 | ### Returns 59 | ---- 60 | dict: 61 | A WorkbookComment object. 62 | """ 63 | 64 | content = self.graph_session.make_request( 65 | method="get", 66 | endpoint=f"/me/drive/items/{item_id}/workbook/comments/{comment_id}", 67 | ) 68 | 69 | return content 70 | 71 | def list_replies(self, item_id: str, comment_id) -> dict: 72 | """Retrieve a list of workbookCommentReply objects using the 73 | Item ID. 74 | 75 | ### Parameters 76 | ---- 77 | item_id : str 78 | The drive item resource id. 79 | 80 | comment_id : str 81 | The comment resource id. 82 | 83 | ### Returns 84 | ---- 85 | dict: 86 | A collection of WorkbookCommentReply objects. 87 | """ 88 | 89 | content = self.graph_session.make_request( 90 | method="get", 91 | endpoint=f"/me/drive/items/{item_id}/workbook/comments/{comment_id}/replies", 92 | ) 93 | 94 | return content 95 | 96 | def create_reply( 97 | self, item_id: str, comment_id: str, content: str, content_type: str = "plain" 98 | ) -> dict: 99 | """Creates a new WorkbookCommentReply object using the 100 | specified Item ID. 101 | 102 | ### Parameters 103 | ---- 104 | item_id : str 105 | The drive item resource id. 106 | 107 | comment_id : str 108 | The comment resource id. 109 | 110 | content : str 111 | The content of a comment reply. 112 | 113 | content_type : str (optional, Default='plain') 114 | Indicates the type for the comment reply. 115 | 116 | ### Returns 117 | ---- 118 | dict: 119 | A WorkbookCommentReply object. 120 | """ 121 | 122 | body = {"content": content, "contentType": content_type} 123 | 124 | content = self.graph_session.make_request( 125 | method="post", 126 | json=body, 127 | additional_headers={"Content-type": "application/json"}, 128 | endpoint=f"/me/drive/items/{item_id}/workbook/comments/{comment_id}/replies", 129 | ) 130 | 131 | return content 132 | 133 | def get_reply(self, item_id: str, comment_id: str, reply_id: str) -> dict: 134 | """Retrieve the properties and relationships of WorkbookCommentReply 135 | object using the specified Item ID. 136 | 137 | ### Parameters 138 | ---- 139 | item_id : str 140 | The drive item resource id. 141 | 142 | comment_id : str 143 | The comment resource id. 144 | 145 | reply_id : str 146 | The comment reply resource id. 147 | 148 | ### Returns 149 | ---- 150 | dict: 151 | A WorkbookCommentReply object. 152 | """ 153 | 154 | content = self.graph_session.make_request( 155 | method="get", 156 | endpoint=f"/me/drive/items/{item_id}/workbook/comments/{comment_id}/replies/{reply_id}", 157 | ) 158 | 159 | return content 160 | -------------------------------------------------------------------------------- /ms_graph/workbooks_and_charts/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class CalculationTypes(Enum): 5 | """Specifies the calculation types used in the 6 | `WorkbookApplication` calculate method. 7 | 8 | ### Usage: 9 | ---- 10 | >>> from ms_graph.workbooks_and_charts.enums import CalculationTypes 11 | >>> CalculationTypes.RECALCULATE.value 12 | """ 13 | 14 | RECALCULATE = "Recaulcaute" 15 | FULL = "Full" 16 | FULLREBUILD = "FullRebuild" 17 | 18 | 19 | class WorksheetVisibility(Enum): 20 | """Specifies the visibility types used in the 21 | `Worksheet` `update_worksheet` method. 22 | 23 | ### Usage: 24 | ---- 25 | >>> from ms_graph.workbooks_and_charts.enums import WorksheetVisibility 26 | >>> WorksheetVisibility.VISIBLE.value 27 | """ 28 | 29 | VISIBLE = "Visible" 30 | HIDDEN = "Hidden" 31 | VERYHIDDEN = "VeryHidden" 32 | 33 | 34 | class RangeShift(Enum): 35 | """Specifies the shift directions used in the 36 | `Range` `insert_range` and `delete` method. 37 | 38 | ### Usage: 39 | ---- 40 | >>> from ms_graph.workbooks_and_charts.enums import RangeShift 41 | >>> RangeShift.DOWN.value 42 | """ 43 | 44 | # These are for inserts. 45 | DOWN = "Down" 46 | RIGHT = "Right" 47 | 48 | # These are for deletes. 49 | UP = "Up" 50 | LEFT = "Left" 51 | 52 | 53 | class Underline(Enum): 54 | """Specifies the Underline property used in the 55 | `RangeFontProperties` object. 56 | 57 | ### Usage: 58 | ---- 59 | >>> from ms_graph.workbooks_and_charts.enums import Underline 60 | >>> RangeFontUnderline.SINGLE.value 61 | """ 62 | 63 | NONE = "None" 64 | SINGLE = "Single" 65 | DOUBLE = "Double" 66 | SINGLE_ACCOUNTANT = "SingleAccountant" 67 | DOUBLE_ACCOUNTANT = "DoubleAccountant" 68 | 69 | 70 | class VerticalAlignment(Enum): 71 | """Specifies the Vertical Alignment property used in the 72 | `RangeFormatProperties` object. 73 | 74 | ### Usage: 75 | ---- 76 | >>> from ms_graph.workbooks_and_charts.enums import VerticalAlignment 77 | >>> VerticalAlignment.TOP.value 78 | """ 79 | 80 | TOP = "Top" 81 | CENTER = "Center" 82 | BOTTOM = "Bottom" 83 | JUSTIFY = "Justify" 84 | DISTRIBUTED = "Distributed" 85 | 86 | 87 | class HorizontalAlignment(Enum): 88 | """Specifies the Horizontal Alignment property used in the 89 | `RangeFormatProperties` object. 90 | 91 | ### Usage: 92 | ---- 93 | >>> from ms_graph.workbooks_and_charts.enums import HorizontalAlignment 94 | >>> HorizontalAlignment.GENERAL.value 95 | """ 96 | 97 | GENERAL = "General" 98 | LEFT = "Left" 99 | CENTER = "Center" 100 | RIGHT = "Right" 101 | FILL = "Fill" 102 | JUSTIFY = "Justify" 103 | CENTER_ACCROSS_SELECTION = "CenterAcrossSelection" 104 | DISTRIBUTED = "Distributed" 105 | 106 | 107 | class BorderStyle(Enum): 108 | """Specifies the Border Style property used in the 109 | `RangeBorderProperties` object. 110 | 111 | ### Usage: 112 | ---- 113 | >>> from ms_graph.workbooks_and_charts.enums import BorderStyle 114 | >>> BorderStyle.CONTINUOUS.value 115 | """ 116 | 117 | NONE = "None" 118 | CONTINUOUS = "Continuous" 119 | DASH = "Dash" 120 | DASH_DOT = "DashDot" 121 | DASH_DOT_DOT = "DashDotDot" 122 | DOT = "Dot" 123 | DOUBLE = "Double" 124 | SLANT_DASH_DOT = "SlantDashDot" 125 | 126 | 127 | class BorderWeight(Enum): 128 | """Specifies the Border Weight property used in the 129 | `RangeBorderProperties` object. 130 | 131 | ### Usage: 132 | ---- 133 | >>> from ms_graph.workbooks_and_charts.enums import BorderWeight 134 | >>> BorderWeight.HAIRLINE.value 135 | """ 136 | 137 | HAIRLINE = "Hairline" 138 | THIN = "Thin" 139 | MEDIUM = "Medium" 140 | THICK = "Thick" 141 | 142 | 143 | class ApplyTo(Enum): 144 | """Specifies the type of Clear Action used in the `Range.clear()` 145 | method. 146 | 147 | ### Usage: 148 | ---- 149 | >>> from ms_graph.workbooks_and_charts.enums import ApplyTo 150 | >>> ApplyTo.ALL.value 151 | """ 152 | 153 | ALL = "All" 154 | FORMATS = "Formats" 155 | CONTENTS = "Contents" 156 | 157 | class TableStyle(Enum): 158 | """Specifies the TableStyle property used in the `Table.update()` 159 | method. 160 | 161 | ### Usage: 162 | ---- 163 | >>> from ms_graph.workbooks_and_charts.enums import TableStyle 164 | >>> TableStyle.TABLE_STYLE_LIGHT_1.value 165 | """ 166 | 167 | TABLE_STYLE_LIGHT_1 = "TableStyleLight1" 168 | TABLE_STYLE_LIGHT_2 = "TableStyleLight2" 169 | TABLE_STYLE_LIGHT_3 = "TableStyleLight3" 170 | TABLE_STYLE_LIGHT_4 = "TableStyleLight4" 171 | TABLE_STYLE_LIGHT_5 = "TableStyleLight5" 172 | TABLE_STYLE_LIGHT_6 = "TableStyleLight6" 173 | TABLE_STYLE_LIGHT_7 = "TableStyleLight7" 174 | TABLE_STYLE_LIGHT_8 = "TableStyleLight8" 175 | TABLE_STYLE_LIGHT_9 = "TableStyleLight9" 176 | TABLE_STYLE_LIGHT_10 = "TableStyleLight10" 177 | TABLE_STYLE_LIGHT_11 = "TableStyleLight11" 178 | TABLE_STYLE_LIGHT_12 = "TableStyleLight12" 179 | TABLE_STYLE_LIGHT_13 = "TableStyleLight13" 180 | TABLE_STYLE_LIGHT_14 = "TableStyleLight14" 181 | TABLE_STYLE_LIGHT_15 = "TableStyleLight15" 182 | TABLE_STYLE_LIGHT_16 = "TableStyleLight16" 183 | TABLE_STYLE_LIGHT_17 = "TableStyleLight17" 184 | TABLE_STYLE_LIGHT_18 = "TableStyleLight18" 185 | TABLE_STYLE_LIGHT_19 = "TableStyleLight19" 186 | TABLE_STYLE_LIGHT_20 = "TableStyleLight20" 187 | TABLE_STYLE_LIGHT_21 = "TableStyleLight21" 188 | TABLE_STYLE_LIGHT_22 = "TableStyleLight22" 189 | TABLE_STYLE_LIGHT_23 = "TableStyleLight23" 190 | TABLE_STYLE_LIGHT_24 = "TableStyleLight24" 191 | TABLE_STYLE_LIGHT_25 = "TableStyleLight25" 192 | TABLE_STYLE_LIGHT_26 = "TableStyleLight26" 193 | TABLE_STYLE_LIGHT_27 = "TableStyleLight27" 194 | TABLE_STYLE_LIGHT_28 = "TableStyleLight28" 195 | 196 | TABLE_STYLE_MEDIUM_1 = "TableStyleMedium1" 197 | TABLE_STYLE_MEDIUM_2 = "TableStyleMedium2" 198 | TABLE_STYLE_MEDIUM_3 = "TableStyleMedium3" 199 | TABLE_STYLE_MEDIUM_4 = "TableStyleMedium4" 200 | TABLE_STYLE_MEDIUM_5 = "TableStyleMedium5" 201 | TABLE_STYLE_MEDIUM_6 = "TableStyleMedium6" 202 | TABLE_STYLE_MEDIUM_7 = "TableStyleMedium7" 203 | TABLE_STYLE_MEDIUM_8 = "TableStyleMedium8" 204 | TABLE_STYLE_MEDIUM_9 = "TableStyleMedium9" 205 | TABLE_STYLE_MEDIUM_10 = "TableStyleMedium10" 206 | TABLE_STYLE_MEDIUM_11 = "TableStyleMedium11" 207 | TABLE_STYLE_MEDIUM_12 = "TableStyleMedium12" 208 | TABLE_STYLE_MEDIUM_13 = "TableStyleMedium13" 209 | TABLE_STYLE_MEDIUM_14 = "TableStyleMedium14" 210 | TABLE_STYLE_MEDIUM_15 = "TableStyleMedium15" 211 | TABLE_STYLE_MEDIUM_16 = "TableStyleMedium16" 212 | TABLE_STYLE_MEDIUM_17 = "TableStyleMedium17" 213 | TABLE_STYLE_MEDIUM_18 = "TableStyleMedium18" 214 | TABLE_STYLE_MEDIUM_19 = "TableStyleMedium19" 215 | TABLE_STYLE_MEDIUM_20 = "TableStyleMedium20" 216 | TABLE_STYLE_MEDIUM_21 = "TableStyleMedium21" 217 | TABLE_STYLE_MEDIUM_22 = "TableStyleMedium22" 218 | TABLE_STYLE_MEDIUM_23 = "TableStyleMedium23" 219 | TABLE_STYLE_MEDIUM_24 = "TableStyleMedium24" 220 | TABLE_STYLE_MEDIUM_25 = "TableStyleMedium25" 221 | TABLE_STYLE_MEDIUM_26 = "TableStyleMedium26" 222 | TABLE_STYLE_MEDIUM_27 = "TableStyleMedium27" 223 | TABLE_STYLE_MEDIUM_28 = "TableStyleMedium28" 224 | 225 | TABLE_STYLE_DARK_1 = "TableStyleDark1" 226 | TABLE_STYLE_DARK_2 = "TableStyleDark2" 227 | TABLE_STYLE_DARK_3 = "TableStyleDark3" 228 | TABLE_STYLE_DARK_4 = "TableStyleDark4" 229 | TABLE_STYLE_DARK_5 = "TableStyleDark5" 230 | TABLE_STYLE_DARK_6 = "TableStyleDark6" 231 | TABLE_STYLE_DARK_7 = "TableStyleDark7" 232 | TABLE_STYLE_DARK_8 = "TableStyleDark8" 233 | TABLE_STYLE_DARK_9 = "TableStyleDark9" 234 | TABLE_STYLE_DARK_10 = "TableStyleDark10" 235 | TABLE_STYLE_DARK_11 = "TableStyleDark11" 236 | TABLE_STYLE_DARK_12 = "TableStyleDark12" 237 | TABLE_STYLE_DARK_13 = "TableStyleDark13" 238 | TABLE_STYLE_DARK_14 = "TableStyleDark14" 239 | TABLE_STYLE_DARK_15 = "TableStyleDark15" 240 | TABLE_STYLE_DARK_16 = "TableStyleDark16" 241 | TABLE_STYLE_DARK_17 = "TableStyleDark17" 242 | TABLE_STYLE_DARK_18 = "TableStyleDark18" 243 | TABLE_STYLE_DARK_19 = "TableStyleDark19" 244 | TABLE_STYLE_DARK_20 = "TableStyleDark20" 245 | TABLE_STYLE_DARK_21 = "TableStyleDark21" 246 | TABLE_STYLE_DARK_22 = "TableStyleDark22" 247 | TABLE_STYLE_DARK_23 = "TableStyleDark23" 248 | TABLE_STYLE_DARK_24 = "TableStyleDark24" 249 | TABLE_STYLE_DARK_25 = "TableStyleDark25" 250 | TABLE_STYLE_DARK_26 = "TableStyleDark26" 251 | TABLE_STYLE_DARK_27 = "TableStyleDark27" 252 | TABLE_STYLE_DARK_28 = "TableStyleDark28" 253 | -------------------------------------------------------------------------------- /ms_graph/workbooks_and_charts/range.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | from typing import Union 3 | from ms_graph.session import GraphSession 4 | from ms_graph.utils.range import RangeProperties 5 | from ms_graph.utils.range import RangeFormatProperties 6 | 7 | def build_endpoint(inputs: dict) -> str: 8 | """Builds the endpoint for the Range object. 9 | 10 | ### Parameters 11 | ---- 12 | inputs : dict 13 | The `locals()` of the function. 14 | 15 | ### Raises 16 | ---- 17 | ValueError: 18 | If an item id or item path is not specified 19 | an error will be raised. 20 | 21 | ### Returns 22 | ---- 23 | str: 24 | The full URL path. 25 | """ 26 | 27 | item_id = inputs.get("item_id", None) 28 | item_path = inputs.get("item_path", None) 29 | worksheet_name_or_id = inputs.get("worksheet_name_or_id", None) 30 | address = inputs.get("address", None) 31 | name = inputs.get("name", None) 32 | table_name_or_id = inputs.get("table_name_or_id", None) 33 | column_name_or_id = inputs.get("column_name_or_id", None) 34 | 35 | if item_id: 36 | workbook_path = f"/me/drive/items/{item_id}/workbook/" 37 | elif item_path: 38 | workbook_path = f"/me/drive/root:/{item_path}:/workbook/" 39 | else: 40 | raise ValueError("Must specify an Item ID or Item Path.") 41 | 42 | if (worksheet_name_or_id and address): 43 | range_path = f"worksheets/{worksheet_name_or_id}/range(address='{address}')" 44 | elif name: 45 | range_path = f"names/{name}/range" 46 | elif (table_name_or_id and column_name_or_id): 47 | range_path = f"tables/{table_name_or_id}/columns/{column_name_or_id}/range" 48 | 49 | return workbook_path + range_path 50 | 51 | class Range: 52 | 53 | """ 54 | ### Overview: 55 | ---- 56 | Range represents a set of one or more contiguous cells 57 | such as a cell, a row, a column, block of cells, etc. 58 | """ 59 | 60 | def __init__(self, session: object) -> None: 61 | """Initializes the `Range` object. 62 | 63 | # Parameters 64 | ---- 65 | session : object 66 | An authenticated session for our Microsoft Graph Client. 67 | """ 68 | 69 | # Set the session. 70 | self.graph_session: GraphSession = session 71 | 72 | def get_range( 73 | self, 74 | address: str = None, 75 | name: str = None, 76 | table_name_or_id: str = None, 77 | worksheet_name_or_id: str = None, 78 | column_name_or_id: str = None, 79 | item_id: str = None, 80 | item_path: str = None, 81 | ) -> dict: 82 | """Retrieve the properties and relationships of range object. 83 | 84 | ### Parameters 85 | ---- 86 | address : str (optional, Default=None) 87 | The range address. 88 | 89 | name : str (optional, Default=None) 90 | 91 | table_name_or_id : str (optional, Default=None) 92 | The name of the table or the resource id. 93 | 94 | worksheet_name_or_id : str (optional, Default=None) 95 | The name of the worksheet or the resource id. 96 | 97 | column_name_or_id : str (optional, Default=None) 98 | The name of the table column or the resource id. 99 | This must be specified if you are grabbing a table 100 | range. 101 | 102 | item_id : str (optional, Default=None) 103 | The Drive Item Resource ID. 104 | 105 | item_path : str (optional, Default=None) 106 | The Item Path. An Example would be the following: 107 | `/TestFolder/TestFile.txt` 108 | 109 | ### Returns 110 | ---- 111 | dict: 112 | A Range object. 113 | """ 114 | 115 | inputs = { 116 | "address": address, 117 | "name": name, 118 | "table_name_or_id": table_name_or_id, 119 | "worksheet_name_or_id":worksheet_name_or_id, 120 | "column_name_or_id": column_name_or_id, 121 | "item_id": item_id, 122 | "item_path": item_path 123 | } 124 | 125 | endpoint = build_endpoint(inputs=inputs) 126 | 127 | content = self.graph_session.make_request( 128 | method="get", 129 | endpoint=endpoint 130 | ) 131 | 132 | return content 133 | 134 | def update_range( 135 | self, 136 | range_properties: Union[dict, RangeProperties], 137 | address: str = None, 138 | name: str = None, 139 | table_name_or_id: str = None, 140 | worksheet_name_or_id: str = None, 141 | column_name_or_id: str = None, 142 | item_id: str = None, 143 | item_path: str = None, 144 | ) -> dict: 145 | """Retrieve the properties and relationships of range object. 146 | 147 | ### Parameters 148 | ---- 149 | address : str (optional, Default=None) 150 | The range address. 151 | 152 | name : str (optional, Default=None) 153 | 154 | table_name_or_id : str (optional, Default=None) 155 | The name of the table or the resource id. 156 | 157 | worksheet_name_or_id : str (optional, Default=None) 158 | The name of the worksheet or the resource id. 159 | 160 | column_name_or_id : str (optional, Default=None) 161 | The name of the table column or the resource id. 162 | This must be specified if you are grabbing a table 163 | range. 164 | 165 | item_id : str (optional, Default=None) 166 | The Drive Item Resource ID. 167 | 168 | item_path : str (optional, Default=None) 169 | The Item Path. An Example would be the following: 170 | `/TestFolder/TestFile.txt` 171 | 172 | ### Returns 173 | ---- 174 | dict: 175 | A Range object. 176 | """ 177 | 178 | inputs = { 179 | "address": address, 180 | "name": name, 181 | "table_name_or_id": table_name_or_id, 182 | "worksheet_name_or_id":worksheet_name_or_id, 183 | "column_name_or_id": column_name_or_id, 184 | "item_id": item_id, 185 | "item_path": item_path 186 | } 187 | 188 | endpoint = build_endpoint(inputs=inputs) 189 | 190 | if isinstance(range_properties, RangeProperties): 191 | range_properties = range_properties.to_dict() 192 | 193 | content = self.graph_session.make_request( 194 | method="patch", 195 | json=range_properties, 196 | additional_headers={"Content-type": "application/json"}, 197 | endpoint=endpoint, 198 | ) 199 | 200 | return content 201 | 202 | def insert_range( 203 | self, 204 | shift: Union[str, Enum], 205 | address: str = None, 206 | name: str = None, 207 | table_name_or_id: str = None, 208 | worksheet_name_or_id: str = None, 209 | column_name_or_id: str = None, 210 | item_id: str = None, 211 | item_path: str = None, 212 | ) -> dict: 213 | """Retrieve the properties and relationships of range object. 214 | 215 | ### Parameters 216 | ---- 217 | shift : Union[str, Enum] 218 | Specifies which way to shift the cells. The 219 | possible values are: Down, Right. 220 | 221 | address : str (optional, Default=None) 222 | The range address. 223 | 224 | name : str (optional, Default=None) 225 | 226 | table_name_or_id : str (optional, Default=None) 227 | The name of the table or the resource id. 228 | 229 | worksheet_name_or_id : str (optional, Default=None) 230 | The name of the worksheet or the resource id. 231 | 232 | column_name_or_id : str (optional, Default=None) 233 | The name of the table column or the resource id. 234 | This must be specified if you are grabbing a table 235 | range. 236 | 237 | item_id : str (optional, Default=None) 238 | The Drive Item Resource ID. 239 | 240 | item_path : str (optional, Default=None) 241 | The Item Path. An Example would be the following: 242 | `/TestFolder/TestFile.txt` 243 | 244 | ### Returns 245 | ---- 246 | dict: 247 | A Range object. 248 | """ 249 | 250 | inputs = { 251 | "address": address, 252 | "name": name, 253 | "table_name_or_id": table_name_or_id, 254 | "worksheet_name_or_id":worksheet_name_or_id, 255 | "column_name_or_id": column_name_or_id, 256 | "item_id": item_id, 257 | "item_path": item_path 258 | } 259 | 260 | endpoint = build_endpoint(inputs=inputs) 261 | endpoint = endpoint + "/insert" 262 | 263 | if isinstance(shift, Enum): 264 | shift = shift.value 265 | 266 | content = self.graph_session.make_request( 267 | method="post", 268 | json={"shift": shift}, 269 | additional_headers={"Content-type": "application/json"}, 270 | endpoint=endpoint, 271 | ) 272 | 273 | return content 274 | 275 | def get_range_format( 276 | self, 277 | address: str = None, 278 | name: str = None, 279 | table_name_or_id: str = None, 280 | worksheet_name_or_id: str = None, 281 | column_name_or_id: str = None, 282 | item_id: str = None, 283 | item_path: str = None, 284 | ) -> dict: 285 | """Retrieve the properties and relationships of rangeformat object. 286 | 287 | ### Parameters 288 | ---- 289 | address : str (optional, Default=None) 290 | The range address. 291 | 292 | name : str (optional, Default=None) 293 | 294 | table_name_or_id : str (optional, Default=None) 295 | The name of the table or the resource id. 296 | 297 | worksheet_name_or_id : str (optional, Default=None) 298 | The name of the worksheet or the resource id. 299 | 300 | column_name_or_id : str (optional, Default=None) 301 | The name of the table column or the resource id. 302 | This must be specified if you are grabbing a table 303 | range. 304 | 305 | item_id : str (optional, Default=None) 306 | The Drive Item Resource ID. 307 | 308 | item_path : str (optional, Default=None) 309 | The Item Path. An Example would be the following: 310 | `/TestFolder/TestFile.txt` 311 | 312 | ### Returns 313 | ---- 314 | dict: 315 | A WorkbookFormatRange object. 316 | """ 317 | 318 | inputs = { 319 | "address": address, 320 | "name": name, 321 | "table_name_or_id": table_name_or_id, 322 | "worksheet_name_or_id":worksheet_name_or_id, 323 | "column_name_or_id": column_name_or_id, 324 | "item_id": item_id, 325 | "item_path": item_path 326 | } 327 | 328 | endpoint = build_endpoint(inputs=inputs) 329 | endpoint = endpoint + "/format" 330 | 331 | content = self.graph_session.make_request( 332 | method="get", endpoint=endpoint) 333 | 334 | return content 335 | 336 | def update_range_format( 337 | self, 338 | range_format_properties: Union[dict, RangeProperties], 339 | address: str = None, 340 | name: str = None, 341 | table_name_or_id: str = None, 342 | worksheet_name_or_id: str = None, 343 | column_name_or_id: str = None, 344 | item_id: str = None, 345 | item_path: str = None, 346 | ) -> dict: 347 | """Retrieve the properties and relationships of range object. 348 | 349 | ### Parameters 350 | ---- 351 | range_format_properties : Union[dict, RangeProperties] 352 | Supply the values for relevant fields that should be 353 | updated. Existing properties that are not included in 354 | the request body will maintain their previous values 355 | or be recalculated based on changes to other property 356 | values. For best performance you shouldn't include 357 | existing values that haven't changed. 358 | 359 | address : str (optional, Default=None) 360 | The range address. 361 | 362 | name : str (optional, Default=None) 363 | 364 | table_name_or_id : str (optional, Default=None) 365 | The name of the table or the resource id. 366 | 367 | worksheet_name_or_id : str (optional, Default=None) 368 | The name of the worksheet or the resource id. 369 | 370 | column_name_or_id : str (optional, Default=None) 371 | The name of the table column or the resource id. 372 | This must be specified if you are grabbing a table 373 | range. 374 | 375 | item_id : str (optional, Default=None) 376 | The Drive Item Resource ID. 377 | 378 | item_path : str (optional, Default=None) 379 | The Item Path. An Example would be the following: 380 | `/TestFolder/TestFile.txt` 381 | 382 | ### Returns 383 | ---- 384 | dict: 385 | A Range object. 386 | """ 387 | 388 | inputs = { 389 | "address": address, 390 | "name": name, 391 | "table_name_or_id": table_name_or_id, 392 | "worksheet_name_or_id":worksheet_name_or_id, 393 | "column_name_or_id": column_name_or_id, 394 | "item_id": item_id, 395 | "item_path": item_path 396 | } 397 | 398 | endpoint = build_endpoint(inputs=inputs) 399 | endpoint = endpoint + "/format" 400 | 401 | if isinstance(range_format_properties, RangeFormatProperties): 402 | range_format_properties = range_format_properties.to_dict() 403 | 404 | content = self.graph_session.make_request( 405 | method="patch", 406 | json=range_format_properties, 407 | additional_headers={"Content-type": "application/json"}, 408 | endpoint=endpoint, 409 | ) 410 | 411 | return content 412 | 413 | def merge( 414 | self, 415 | across: bool = False, 416 | address: str = None, 417 | name: str = None, 418 | table_name_or_id: str = None, 419 | worksheet_name_or_id: str = None, 420 | column_name_or_id: str = None, 421 | item_id: str = None, 422 | item_path: str = None, 423 | ) -> dict: 424 | """Merge the range cells into one region in the worksheet. 425 | 426 | # Parameters 427 | ---- 428 | across : bool (optional, Default=False) 429 | Set to `True` to merge cells in each row 430 | of the specified range as separate merged 431 | cells. 432 | 433 | address : str (optional, Default=None) 434 | The range address. 435 | 436 | name : str (optional, Default=None) 437 | 438 | table_name_or_id : str (optional, Default=None) 439 | The name of the table or the resource id. 440 | 441 | worksheet_name_or_id : str (optional, Default=None) 442 | The name of the worksheet or the resource id. 443 | 444 | column_name_or_id : str (optional, Default=None) 445 | The name of the table column or the resource id. 446 | This must be specified if you are grabbing a table 447 | range. 448 | 449 | item_id : str (optional, Default=None) 450 | The Drive Item Resource ID. 451 | 452 | item_path : str (optional, Default=None) 453 | The Item Path. An Example would be the following: 454 | `/TestFolder/TestFile.txt` 455 | 456 | ### Returns 457 | ---- 458 | dict: 459 | A Response object. 460 | """ 461 | 462 | inputs = { 463 | "address": address, 464 | "name": name, 465 | "table_name_or_id": table_name_or_id, 466 | "worksheet_name_or_id":worksheet_name_or_id, 467 | "column_name_or_id": column_name_or_id, 468 | "item_id": item_id, 469 | "item_path": item_path 470 | } 471 | 472 | endpoint = build_endpoint(inputs=inputs) 473 | endpoint = endpoint + "/merge" 474 | 475 | body = {"across": across} 476 | 477 | content = self.graph_session.make_request( 478 | method="post", 479 | json=body, 480 | additional_headers={"Content-type": "application/json"}, 481 | endpoint=endpoint, 482 | ) 483 | 484 | return content 485 | 486 | def unmerge( 487 | self, 488 | address: str = None, 489 | name: str = None, 490 | table_name_or_id: str = None, 491 | worksheet_name_or_id: str = None, 492 | column_name_or_id: str = None, 493 | item_id: str = None, 494 | item_path: str = None, 495 | ) -> dict: 496 | """Unmerge the range cells into seperate regions. 497 | 498 | ### Parameters 499 | ---- 500 | address : str (optional, Default=None) 501 | The range address. 502 | 503 | name : str (optional, Default=None) 504 | 505 | table_name_or_id : str (optional, Default=None) 506 | The name of the table or the resource id. 507 | 508 | worksheet_name_or_id : str (optional, Default=None) 509 | The name of the worksheet or the resource id. 510 | 511 | column_name_or_id : str (optional, Default=None) 512 | The name of the table column or the resource id. 513 | This must be specified if you are grabbing a table 514 | range. 515 | 516 | item_id : str (optional, Default=None) 517 | The Drive Item Resource ID. 518 | 519 | item_path : str (optional, Default=None) 520 | The Item Path. An Example would be the following: 521 | `/TestFolder/TestFile.txt` 522 | 523 | ### Returns 524 | ---- 525 | dict: 526 | A Response object. 527 | """ 528 | 529 | inputs = { 530 | "address": address, 531 | "name": name, 532 | "table_name_or_id": table_name_or_id, 533 | "worksheet_name_or_id":worksheet_name_or_id, 534 | "column_name_or_id": column_name_or_id, 535 | "item_id": item_id, 536 | "item_path": item_path 537 | } 538 | 539 | endpoint = build_endpoint(inputs=inputs) 540 | endpoint = endpoint + "/unmerge" 541 | 542 | content = self.graph_session.make_request( 543 | method="post", 544 | additional_headers={"Content-type": "application/json"}, 545 | endpoint=endpoint, 546 | ) 547 | 548 | return content 549 | 550 | def clear( 551 | self, 552 | apply_to: Union[str, Enum] = None, 553 | address: str = None, 554 | name: str = None, 555 | table_name_or_id: str = None, 556 | worksheet_name_or_id: str = None, 557 | column_name_or_id: str = None, 558 | item_id: str = None, 559 | item_path: str = None, 560 | ) -> dict: 561 | """Clear range values such as format, fill, and border. 562 | 563 | ### Parameters 564 | ---- 565 | apply_to : Union[str, Enum] (optional, Default=None) 566 | Determines the type of clear action. The possible 567 | values are: `All`, `Formats`, `Contents`. 568 | 569 | address : str (optional, Default=None) 570 | The range address. 571 | 572 | name : str (optional, Default=None) 573 | 574 | table_name_or_id : str (optional, Default=None) 575 | The name of the table or the resource id. 576 | 577 | worksheet_name_or_id : str (optional, Default=None) 578 | The name of the worksheet or the resource id. 579 | 580 | column_name_or_id : str (optional, Default=None) 581 | The name of the table column or the resource id. 582 | This must be specified if you are grabbing a table 583 | range. 584 | 585 | item_id : str (optional, Default=None) 586 | The Drive Item Resource ID. 587 | 588 | item_path : str (optional, Default=None) 589 | The Item Path. An Example would be the following: 590 | `/TestFolder/TestFile.txt` 591 | 592 | ### Returns 593 | ---- 594 | dict: 595 | A Response object. 596 | """ 597 | 598 | inputs = { 599 | "address": address, 600 | "name": name, 601 | "table_name_or_id": table_name_or_id, 602 | "worksheet_name_or_id":worksheet_name_or_id, 603 | "column_name_or_id": column_name_or_id, 604 | "item_id": item_id, 605 | "item_path": item_path 606 | } 607 | 608 | endpoint = build_endpoint(inputs=inputs) 609 | endpoint = endpoint + "/clear" 610 | 611 | if isinstance(apply_to, Enum): 612 | apply_to = apply_to.value 613 | 614 | content = self.graph_session.make_request( 615 | method="post", 616 | json={"applyTo": apply_to}, 617 | additional_headers={"Content-type": "application/json"}, 618 | endpoint=endpoint, 619 | ) 620 | 621 | return content 622 | 623 | def delete( 624 | self, 625 | shift: Union[str, Enum] = None, 626 | address: str = None, 627 | name: str = None, 628 | table_name_or_id: str = None, 629 | worksheet_name_or_id: str = None, 630 | column_name_or_id: str = None, 631 | item_id: str = None, 632 | item_path: str = None, 633 | ) -> dict: 634 | """Deletes the cells associated with the range. 635 | 636 | ### Parameters 637 | ---- 638 | shift : Union[str, Enum] (optional, Default=None) 639 | Specifies which way to shift the cells. The possible 640 | values are: `Up` and `Left`. 641 | 642 | address : str (optional, Default=None) 643 | The range address. 644 | 645 | name : str (optional, Default=None) 646 | 647 | table_name_or_id : str (optional, Default=None) 648 | The name of the table or the resource id. 649 | 650 | worksheet_name_or_id : str (optional, Default=None) 651 | The name of the worksheet or the resource id. 652 | 653 | column_name_or_id : str (optional, Default=None) 654 | The name of the table column or the resource id. 655 | This must be specified if you are grabbing a table 656 | range. 657 | 658 | item_id : str (optional, Default=None) 659 | The Drive Item Resource ID. 660 | 661 | item_path : str (optional, Default=None) 662 | The Item Path. An Example would be the following: 663 | `/TestFolder/TestFile.txt` 664 | 665 | ### Returns 666 | ---- 667 | dict: 668 | A Response object. 669 | """ 670 | 671 | inputs = { 672 | "address": address, 673 | "name": name, 674 | "table_name_or_id": table_name_or_id, 675 | "worksheet_name_or_id":worksheet_name_or_id, 676 | "column_name_or_id": column_name_or_id, 677 | "item_id": item_id, 678 | "item_path": item_path 679 | } 680 | 681 | endpoint = build_endpoint(inputs=inputs) 682 | endpoint = endpoint + "/delete" 683 | 684 | if isinstance(shift, Enum): 685 | shift = shift.value 686 | 687 | content = self.graph_session.make_request( 688 | method="post", 689 | json={"shift": shift}, 690 | additional_headers={"Content-type": "application/json"}, 691 | endpoint=endpoint, 692 | ) 693 | 694 | return content 695 | -------------------------------------------------------------------------------- /ms_graph/workbooks_and_charts/table.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | from typing import Union 3 | from ms_graph.session import GraphSession 4 | 5 | 6 | def build_endpoint(inputs: dict) -> str: 7 | """Builds the endpoint for the Range object. 8 | 9 | ### Parameters 10 | ---- 11 | inputs : dict 12 | The `locals()` of the function. 13 | 14 | ### Raises 15 | ---- 16 | ValueError: 17 | If an item id or item path is not specified 18 | an error will be raised. 19 | 20 | ### Returns 21 | ---- 22 | str: 23 | The full URL path. 24 | """ 25 | 26 | item_id = inputs.get("item_id", None) 27 | item_path = inputs.get("item_path", None) 28 | worksheet_name_or_id = inputs.get("worksheet_name_or_id", None) 29 | address = inputs.get("address", None) 30 | name = inputs.get("name", None) 31 | table_name_or_id = inputs.get("table_name_or_id", None) 32 | column_name_or_id = inputs.get("column_name_or_id", None) 33 | 34 | if item_id: 35 | workbook_path = f"/me/drive/items/{item_id}/workbook/" 36 | elif item_path: 37 | workbook_path = f"/me/drive/root:/{item_path}:/workbook/" 38 | else: 39 | raise ValueError("Must specify an Item ID or Item Path.") 40 | 41 | if (worksheet_name_or_id and address): 42 | range_path = f"worksheets/{worksheet_name_or_id}/range(address='{address}')" 43 | elif name: 44 | range_path = f"names/{name}/range" 45 | elif (table_name_or_id and column_name_or_id): 46 | range_path = f"tables/{table_name_or_id}/columns/{column_name_or_id}/range" 47 | 48 | return workbook_path + range_path 49 | 50 | 51 | class Table: 52 | 53 | """ 54 | ### Overview: 55 | ---- 56 | Represents an Excel Table Object, also called an 57 | Excel List Object. 58 | """ 59 | 60 | def __init__(self, session: object) -> None: 61 | """Initializes the `Table` object. 62 | 63 | # Parameters 64 | ---- 65 | session : object 66 | An authenticated session for our Microsoft Graph Client. 67 | """ 68 | 69 | # Set the session. 70 | self.graph_session: GraphSession = session 71 | 72 | def get_table( 73 | self, 74 | table_name_or_id: str = None, 75 | worksheet_name_or_id: str = None, 76 | item_id: str = None, 77 | item_path: str = None, 78 | ) -> dict: 79 | """Retrieve the properties and relationships of table object. 80 | 81 | ### Parameters 82 | ---- 83 | table_name_or_id : str (optional, Default=None) 84 | The name of the table or the resource id. 85 | 86 | worksheet_name_or_id : str (optional, Default=None) 87 | The name of the worksheet or the resource id. 88 | 89 | item_id : str (optional, Default=None) 90 | The Drive Item Resource ID. 91 | 92 | item_path : str (optional, Default=None) 93 | The Item Path. An Example would be the following: 94 | `/TestFolder/TestFile.txt` 95 | 96 | ### Returns 97 | ---- 98 | dict: 99 | A WorkbookTable object. 100 | """ 101 | 102 | if item_id: 103 | workbook_path = f"/me/drive/items/{item_id}/workbook/" 104 | elif item_path: 105 | workbook_path = f"/me/drive/root:/{item_path}:/workbook/" 106 | else: 107 | raise ValueError("Must specify an Item ID or Item Path.") 108 | 109 | if (worksheet_name_or_id and table_name_or_id): 110 | table_path = f"worksheets/{worksheet_name_or_id}/tables/{table_name_or_id}" 111 | elif table_name_or_id: 112 | table_path = f"tables/{table_name_or_id}" 113 | 114 | endpoint = workbook_path + table_path 115 | 116 | content = self.graph_session.make_request( 117 | method="get", 118 | endpoint=endpoint 119 | ) 120 | 121 | return content 122 | 123 | def add_table( 124 | self, 125 | address: str, 126 | has_headers: bool, 127 | table_name_or_id: str = None, 128 | worksheet_name_or_id: str = None, 129 | item_id: str = None, 130 | item_path: str = None, 131 | ) -> dict: 132 | """Create a new WorkbookTable Object. 133 | 134 | ### Overview 135 | ---- 136 | The range source address determines the worksheet under 137 | which the table will be added. If the table cannot be 138 | added (e.g., because the address is invalid, or the table 139 | would overlap with another table), an error will be thrown. 140 | 141 | ### Parameters 142 | ---- 143 | address : str 144 | Address or name of the range object representing the 145 | data source. If the address does not contain a sheet 146 | name, the currently-active sheet is used. 147 | 148 | has_headers : bool 149 | Boolean value that indicates whether the data being 150 | imported has column labels. If the source does not 151 | contain headers (i.e,. when this property set to 152 | `False`), Excel will automatically generate header 153 | shifting the data down by one row. 154 | 155 | table_name_or_id : str (optional, Default=None) 156 | The name of the table or the resource id. 157 | 158 | worksheet_name_or_id : str (optional, Default=None) 159 | The name of the worksheet or the resource id. 160 | 161 | item_id : str (optional, Default=None) 162 | The Drive Item Resource ID. 163 | 164 | item_path : str (optional, Default=None) 165 | The Item Path. An Example would be the following: 166 | `/TestFolder/TestFile.txt` 167 | 168 | ### Returns 169 | ---- 170 | dict: 171 | A WorkbookTable Object. 172 | """ 173 | 174 | if item_id: 175 | workbook_path = f"/me/drive/items/{item_id}/workbook/" 176 | elif item_path: 177 | workbook_path = f"/me/drive/root:/{item_path}:/workbook/" 178 | else: 179 | raise ValueError("Must specify an Item ID or Item Path.") 180 | 181 | if (worksheet_name_or_id and table_name_or_id): 182 | table_path = f"worksheets/{worksheet_name_or_id}/tables/{table_name_or_id}" 183 | elif table_name_or_id: 184 | table_path = f"tables/{table_name_or_id}" 185 | 186 | body = {"address": address, "hasHeaders": has_headers} 187 | endpoint = workbook_path + table_path + "/add" 188 | 189 | content = self.graph_session.make_request( 190 | method="post", 191 | json=body, 192 | additional_headers={"Content-type": "application/json"}, 193 | endpoint=endpoint 194 | ) 195 | 196 | return content 197 | 198 | def update_table( 199 | self, 200 | name: str = None, 201 | show_headers: bool = None, 202 | show_totals: bool = None, 203 | style: Union[str, Enum] = None, 204 | table_name_or_id: str = None, 205 | worksheet_name_or_id: str = None, 206 | item_id: str = None, 207 | item_path: str = None, 208 | ) -> dict: 209 | """Update the properties of table object. 210 | 211 | ### Parameters 212 | ---- 213 | name : str (optional, Default=None) 214 | The name of the table. 215 | 216 | show_headers : bool (optional, Default=None) 217 | Indicates whether the header row is visible or not. This 218 | value can be set to show or remove the header row. 219 | 220 | show_totals : bool (optional, Default=None) 221 | Indicates whether the total row is visible or not. This 222 | value can be set to show or remove the total row. 223 | 224 | style : Union[str, Enum] (optional, Default=None) 225 | Constant value that represents the Table style. The possible 226 | values are: `TableStyleLight1` through `TableStyleLight21`, 227 | `TableStyleMedium1` through `TableStyleMedium28`, `TableStyleDark1` 228 | through `TableStyleDark11`. A custom user-defined style 229 | present in the workbook can also be specified. 230 | 231 | table_name_or_id : str (optional, Default=None) 232 | The name of the table or the resource id. 233 | 234 | worksheet_name_or_id : str (optional, Default=None) 235 | The name of the worksheet or the resource id. 236 | 237 | item_id : str (optional, Default=None) 238 | The Drive Item Resource ID. 239 | 240 | item_path : str (optional, Default=None) 241 | The Item Path. An Example would be the following: 242 | `/TestFolder/TestFile.txt` 243 | 244 | ### Returns 245 | ---- 246 | dict: 247 | A WorkbookTable object. 248 | """ 249 | 250 | if item_id: 251 | workbook_path = f"/me/drive/items/{item_id}/workbook/" 252 | elif item_path: 253 | workbook_path = f"/me/drive/root:/{item_path}:/workbook/" 254 | else: 255 | raise ValueError("Must specify an Item ID or Item Path.") 256 | 257 | if (worksheet_name_or_id and table_name_or_id): 258 | table_path = f"worksheets/{worksheet_name_or_id}/tables/{table_name_or_id}" 259 | elif table_name_or_id: 260 | table_path = f"tables/{table_name_or_id}" 261 | 262 | endpoint = workbook_path + table_path 263 | 264 | if isinstance(style, Enum): 265 | style = style.value 266 | 267 | body = { 268 | "name": name, 269 | "showHeaders": show_headers, 270 | "showTotals": show_totals, 271 | "style": style 272 | } 273 | 274 | content = self.graph_session.make_request( 275 | method="patch", 276 | json=body, 277 | additional_headers={"Content-type": "application/json"}, 278 | endpoint=endpoint 279 | ) 280 | 281 | return content 282 | -------------------------------------------------------------------------------- /ms_graph/workbooks_and_charts/workbook.py: -------------------------------------------------------------------------------- 1 | from ms_graph.session import GraphSession 2 | 3 | 4 | class Workbooks: 5 | 6 | """ 7 | ## Overview: 8 | ---- 9 | You can use Microsoft Graph to allow web and mobile applications to 10 | read and modify Excel workbooks stored in OneDrive for Business, SharePoint 11 | site or Group drive. The Workbook (or Excel file) resource contains all the 12 | other Excel resources through relationships. You can access a workbook through 13 | the Drive API by identifying the location of the file in the URL. 14 | """ 15 | 16 | def __init__(self, session: object) -> None: 17 | """Initializes the `Workbooks` object. 18 | 19 | ### Parameters 20 | ---- 21 | session : object 22 | An authenticated session for our Microsoft Graph Client. 23 | """ 24 | 25 | # Set the session. 26 | self.graph_session: GraphSession = session 27 | 28 | def create_session(self, item_id: str = None, item_path: str = None) -> dict: 29 | """Create a new Workbook Session using the Item ID or Item Path 30 | 31 | ### Parameters 32 | ---- 33 | item_id : str (optional, Default=None) 34 | The Drive Item Resource ID. 35 | 36 | item_path : str (optional, Default=None) 37 | The Item Path. An Example would be the following: 38 | `/TestFolder/TestFile.txt` 39 | 40 | ### Returns 41 | ---- 42 | dict: 43 | A workbookSessionInfo resource object. 44 | """ 45 | 46 | if item_id: 47 | content = self.graph_session.make_request( 48 | method="post", 49 | endpoint=f"/me/drive/items/{item_id}/workbook/createSession", 50 | ) 51 | elif item_path: 52 | content = self.graph_session.make_request( 53 | method="post", 54 | endpoint=f"/me/drive/root:/{item_path}:/workbook/createSession", 55 | ) 56 | 57 | return content 58 | 59 | def refresh_session( 60 | self, session_id: str, item_id: str = None, item_path: str = None 61 | ) -> dict: 62 | """Used to Refresh an existing Workbook Session using the Item ID or 63 | Item Path. 64 | 65 | ### Parameters 66 | ---- 67 | session_id : str 68 | Workbook session Id to be refreshed 69 | 70 | item_id : str (optional, Default=None) 71 | The Drive Item Resource ID. 72 | 73 | item_path : str (optional, Default=None) 74 | The Item Path. An Example would be the following: 75 | `/TestFolder/TestFile.txt` 76 | 77 | ### Returns 78 | ---- 79 | dict: 80 | A response object, 204 for a success. 81 | """ 82 | 83 | if item_id: 84 | content = self.graph_session.make_request( 85 | method="post", 86 | endpoint=f"/me/drive/items/{item_id}/workbook/refreshSession", 87 | additional_headers={"workbook-session-id": session_id}, 88 | expect_no_response=True, 89 | ) 90 | elif item_path: 91 | content = self.graph_session.make_request( 92 | method="post", 93 | endpoint=f"/me/drive/root:/{item_path}:/workbook/refreshSession", 94 | additional_headers={"workbook-session-id": session_id}, 95 | expect_no_response=True, 96 | ) 97 | 98 | return content 99 | 100 | def close_session( 101 | self, session_id: str, item_id: str = None, item_path: str = None 102 | ) -> dict: 103 | """Used to close an existing Workbook Session using the Item ID or 104 | Item Path. 105 | 106 | ### Parameters 107 | ---- 108 | session_id : str 109 | Workbook session Id to be closed. 110 | 111 | item_id : str (optional, Default=None) 112 | The Drive Item Resource ID. 113 | 114 | item_path : str (optional, Default=None) 115 | The Item Path. An Example would be the following: 116 | `/TestFolder/TestFile.txt` 117 | 118 | ### Returns 119 | ---- 120 | dict: 121 | A response object, 204 for a success. 122 | """ 123 | 124 | if item_id: 125 | content = self.graph_session.make_request( 126 | method="post", 127 | endpoint=f"/me/drive/items/{item_id}/workbook/closeSession", 128 | additional_headers={"workbook-session-id": session_id}, 129 | expect_no_response=True, 130 | ) 131 | elif item_path: 132 | content = self.graph_session.make_request( 133 | method="post", 134 | endpoint=f"/me/drive/root:/{item_path}:/workbook/closeSession", 135 | additional_headers={"workbook-session-id": session_id}, 136 | expect_no_response=True, 137 | ) 138 | 139 | return content 140 | 141 | def list_tables(self, item_id: str = None, item_path: str = None) -> dict: 142 | """Retrieves a list of table objects using the Item ID or 143 | Item Path. 144 | 145 | ### Parameters 146 | ---- 147 | item_id : str (optional, Default=None) 148 | The Drive Item Resource ID. 149 | 150 | item_path : str (optional, Default=None) 151 | The Item Path. An Example would be the following: 152 | `/TestFolder/TestFile.txt` 153 | 154 | ### Returns 155 | ---- 156 | dict: 157 | A collection of Table Object resources. 158 | """ 159 | 160 | if item_id: 161 | content = self.graph_session.make_request( 162 | method="get", endpoint=f"/me/drive/items/{item_id}/workbook/tables" 163 | ) 164 | elif item_path: 165 | content = self.graph_session.make_request( 166 | method="get", endpoint=f"/me/drive/root:/{item_path}:/workbook/tables" 167 | ) 168 | 169 | return content 170 | 171 | def list_worksheets(self, item_id: str = None, item_path: str = None) -> dict: 172 | """Retrieves a list of worksheet objects using the Item ID or 173 | Item Path. 174 | 175 | ### Parameters 176 | ---- 177 | item_id : str (optional, Default=None) 178 | The Drive Item Resource ID. 179 | 180 | item_path : str (optional, Default=None) 181 | The Item Path. An Example would be the following: 182 | `/TestFolder/TestFile.txt` 183 | 184 | ### Returns 185 | ---- 186 | dict: 187 | A collection of Worksheet resource objects. 188 | """ 189 | 190 | if item_id: 191 | content = self.graph_session.make_request( 192 | method="get", endpoint=f"/me/drive/items/{item_id}/workbook/worksheets" 193 | ) 194 | elif item_path: 195 | content = self.graph_session.make_request( 196 | method="get", 197 | endpoint=f"/me/drive/root:/{item_path}:/workbook/worksheets", 198 | ) 199 | 200 | return content 201 | 202 | def list_names(self, item_id: str = None, item_path: str = None) -> dict: 203 | """Retrieves a list of named objects using the Item ID or 204 | Item Path. 205 | 206 | ### Parameters 207 | ---- 208 | item_id : str (optional, Default=None) 209 | The Drive Item Resource ID. 210 | 211 | item_path : str (optional, Default=None) 212 | The Item Path. An Example would be the following: 213 | `/TestFolder/TestFile.txt` 214 | 215 | ### Returns 216 | ---- 217 | dict: 218 | A collection of Named resource objects. 219 | """ 220 | 221 | if item_id: 222 | content = self.graph_session.make_request( 223 | method="get", endpoint=f"/me/drive/items/{item_id}/workbook/names" 224 | ) 225 | elif item_path: 226 | content = self.graph_session.make_request( 227 | method="get", endpoint=f"/me/drive/root:/{item_path}:/workbook/names" 228 | ) 229 | 230 | return content 231 | 232 | def get_operation_result(self, operation_id: str) -> dict: 233 | """This function is the last in a series of steps to create a 234 | `workbookTableRow` resource asynchronously. 235 | 236 | ### Parameters 237 | ---- 238 | operation_id : str 239 | The operationId provided in the `workbook_operation` response 240 | returned in the preceding `get_workbook_operation` request. 241 | 242 | ### Returns 243 | ---- 244 | dict: 245 | A workbookTableRow object. 246 | """ 247 | 248 | content = self.graph_session.make_request( 249 | method="get", 250 | endpoint=f"/driveItem/workbook/tableRowOperationResult(key={operation_id})", 251 | ) 252 | 253 | return content 254 | -------------------------------------------------------------------------------- /ms_graph/workbooks_and_charts/worksheets.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | from typing import Union 3 | from ms_graph.session import GraphSession 4 | 5 | 6 | class Worksheet: 7 | 8 | """ 9 | ## Overview: 10 | ---- 11 | Represents an Excel Worksheet object. An Excel Worksheet object 12 | is a grid of cells. It can contain data, tables, charts, etc. 13 | """ 14 | 15 | def __init__(self, session: object) -> None: 16 | """Initializes the `Worksheet` object. 17 | 18 | ### Parameters 19 | ---- 20 | session : object 21 | An authenticated session for our Microsoft Graph Client. 22 | """ 23 | 24 | # Set the session. 25 | self.graph_session: GraphSession = session 26 | 27 | def add_worksheet( 28 | self, 29 | item_id: str = None, 30 | item_path: str = None, 31 | name: str = None, 32 | workbook_session_id: str = None, 33 | ) -> dict: 34 | """Adds a new worksheet to the workbook. The worksheet will be added 35 | at the end of existing worksheets. If you wish to activate the newly 36 | added worksheet, call .activate() on it. 37 | 38 | ### Parameters 39 | ---- 40 | item_id : str (optional, Default=None) 41 | The Drive Item Resource ID. 42 | 43 | item_path : str (optional, Default=None) 44 | The Item Path. An Example would be the following: 45 | `/TestFolder/TestFile.txt` 46 | 47 | name : str 48 | The name of the worksheet to be added. If specified, 49 | name should be unqiue. If not specified, Excel 50 | determines the name of the new worksheet. 51 | 52 | workbook_session_id : str (optional, Default=None) 53 | Workbook session Id that determines if changes are 54 | persisted or not. 55 | 56 | ### Returns 57 | ---- 58 | dict: 59 | A WorkbookWorksheet object. 60 | """ 61 | 62 | if workbook_session_id: 63 | additional_headers = {"Workbook-Session-Id": workbook_session_id} 64 | else: 65 | additional_headers = None 66 | 67 | if name: 68 | body = {"name": name} 69 | else: 70 | body = None 71 | 72 | if item_id: 73 | content = self.graph_session.make_request( 74 | method="post", 75 | json=body, 76 | additional_headers=additional_headers, 77 | endpoint=f"/me/drive/items/{item_id}/workbook/worksheets/add", 78 | ) 79 | elif item_path: 80 | content = self.graph_session.make_request( 81 | method="post", 82 | json=body, 83 | additional_headers=additional_headers, 84 | endpoint=f"/me/drive/root:/{item_path}:/workbook/worksheets/add", 85 | ) 86 | 87 | return content 88 | 89 | def get_worksheet( 90 | self, worksheet_id_or_name: str, item_id: str = None, item_path: str = None 91 | ) -> dict: 92 | """Retrieve the properties and relationships of worksheet object. 93 | 94 | ### Parameters 95 | ---- 96 | worksheet_id_or_name : str 97 | The worksheet resource id or the worksheet name. 98 | 99 | item_id : str (optional, Default=None) 100 | The Drive Item Resource ID. 101 | 102 | item_path : str (optional, Default=None) 103 | The Item Path. An Example would be the following: 104 | `/TestFolder/TestFile.txt` 105 | 106 | ### Returns 107 | ---- 108 | dict: 109 | A WorkbookWorksheet object. 110 | """ 111 | 112 | if item_id: 113 | content = self.graph_session.make_request( 114 | method="get", 115 | endpoint=f"/me/drive/items/{item_id}/workbook/worksheets/{worksheet_id_or_name}", 116 | ) 117 | elif item_path: 118 | content = self.graph_session.make_request( 119 | method="get", 120 | endpoint=f"/me/drive/root:/{item_path}:/workbook/worksheets/{worksheet_id_or_name}", 121 | ) 122 | 123 | return content 124 | 125 | def get_used_range( 126 | self, 127 | worksheet_id_or_name: str, 128 | item_id: str = None, 129 | item_path: str = None, 130 | values_only: bool = True, 131 | ) -> dict: 132 | """The used range is the smallest range that encompasses any cells 133 | that have a value or formatting assigned to them. If the worksheet 134 | is blank, this function will return the top left cell. 135 | 136 | ### Parameters 137 | ---- 138 | worksheet_id_or_name : str 139 | The worksheet resource id or the worksheet name. 140 | 141 | item_id : str (optional, Default=None) 142 | The Drive Item Resource ID. 143 | 144 | item_path : str (optional, Default=None) 145 | The Item Path. An Example would be the following: 146 | `/TestFolder/TestFile.txt` 147 | 148 | values_only: bool (optional, Default=True) 149 | Considers only cells with values as used cells 150 | (ignores formatting). 151 | 152 | ### Returns 153 | ---- 154 | dict: 155 | A Range object. 156 | """ 157 | 158 | if values_only: 159 | params = {"valuesOnly": values_only} 160 | else: 161 | params = None 162 | 163 | if item_id: 164 | content = self.graph_session.make_request( 165 | method="get", 166 | params=params, 167 | endpoint=f"/me/drive/items/{item_id}/workbook/worksheets/" 168 | + f"{worksheet_id_or_name}/usedRange", 169 | ) 170 | elif item_path: 171 | content = self.graph_session.make_request( 172 | method="get", 173 | params=params, 174 | endpoint=f"/me/drive/root:/{item_path}:/workbook/worksheets/" 175 | + f"{worksheet_id_or_name}/usedRange", 176 | ) 177 | 178 | return content 179 | 180 | def update_worksheet( 181 | self, 182 | worksheet_id_or_name: str, 183 | item_id: str = None, 184 | item_path: str = None, 185 | name: str = None, 186 | position: int = None, 187 | visibility: Union[str, Enum] = None, 188 | workbook_session_id: str = None, 189 | ) -> dict: 190 | """Update the properties of worksheet object. 191 | 192 | ### Parameters 193 | ---- 194 | worksheet_id_or_name : str 195 | The worksheet resource id or the worksheet name. 196 | 197 | item_id : str (optional, Default=None) 198 | The Drive Item Resource ID. 199 | 200 | item_path : str (optional, Default=None) 201 | The Item Path. An Example would be the following: 202 | `/TestFolder/TestFile.txt` 203 | 204 | name : str (optional, Default=None) 205 | The display name of the worksheet. 206 | 207 | position : int (optional, Default=None) 208 | The zero-based position of the worksheet within the workbook. 209 | 210 | visibility : Union[str, Enum] (optional, Default=None) 211 | The Visibility of the worksheet. The possible values 212 | are: Visible, Hidden, VeryHidden. 213 | 214 | workbook_session_id : str (optional, Default=None) 215 | Workbook session Id that determines if changes are persisted 216 | or not. 217 | 218 | ### Returns 219 | ---- 220 | dict: 221 | The updated WorkbookWorksheet object. 222 | """ 223 | 224 | body = {} 225 | additional_headers = {"Content-type": "application/json"} 226 | 227 | if name: 228 | body["name"] = name 229 | 230 | if visibility: 231 | if isinstance(visibility, Enum): 232 | body["visibility"] = visibility.value 233 | else: 234 | body["visibility"] = visibility 235 | 236 | if position: 237 | body["position"] = position 238 | 239 | if body == {}: 240 | return { 241 | "message": "No properties were requested to be updated, no request made." 242 | } 243 | 244 | if workbook_session_id: 245 | additional_headers["Workbook-Session-Id"] = workbook_session_id 246 | 247 | if item_id: 248 | content = self.graph_session.make_request( 249 | method="patch", 250 | json=body, 251 | additional_headers=additional_headers, 252 | endpoint=f"/me/drive/items/{item_id}/workbook/worksheets/{worksheet_id_or_name}", 253 | ) 254 | elif item_path: 255 | content = self.graph_session.make_request( 256 | method="patch", 257 | json=body, 258 | additional_headers=additional_headers, 259 | endpoint=f"/me/drive/root:/{item_path}:/workbook/worksheets/{worksheet_id_or_name}", 260 | ) 261 | 262 | return content 263 | 264 | def delete_worksheet( 265 | self, 266 | worksheet_id_or_name: str, 267 | item_id: str = None, 268 | item_path: str = None, 269 | workbook_session_id: str = None, 270 | ) -> dict: 271 | """Deletes the worksheet from the workbook. 272 | 273 | ### Parameters 274 | ---- 275 | worksheet_id_or_name : str 276 | The worksheet resource id or the worksheet name. 277 | 278 | item_id : str (optional, Default=None) 279 | The Drive Item Resource ID. 280 | 281 | item_path : str (optional, Default=None) 282 | The Item Path. An Example would be the following: 283 | `/TestFolder/TestFile.txt` 284 | 285 | workbook_session_id : str (optional, Default=None) 286 | Workbook session Id that determines if changes are persisted 287 | or not. 288 | 289 | ### Returns 290 | ---- 291 | dict: 292 | The response status code, a 200 means success. 293 | """ 294 | 295 | additional_headers = {} 296 | 297 | if workbook_session_id: 298 | additional_headers["Workbook-Session-Id"] = workbook_session_id 299 | 300 | if item_id: 301 | content = self.graph_session.make_request( 302 | method="delete", 303 | additional_headers=additional_headers, 304 | endpoint=f"/me/drive/items/{item_id}/workbook/worksheets/{worksheet_id_or_name}", 305 | expect_no_response=True, 306 | ) 307 | elif item_path: 308 | content = self.graph_session.make_request( 309 | method="delete", 310 | additional_headers=additional_headers, 311 | endpoint=f"/me/drive/root:/{item_path}:/workbook/worksheets/{worksheet_id_or_name}", 312 | expect_no_response=True, 313 | ) 314 | 315 | return content 316 | 317 | def get_cell( 318 | self, 319 | worksheet_id_or_name: str, 320 | row: int, 321 | column: int, 322 | item_id: str = None, 323 | item_path: str = None, 324 | ) -> dict: 325 | """Gets the range object containing the single cell based on row and column 326 | numbers. The cell can be outside the bounds of its parent range, so long 327 | as it's stays within the worksheet grid. 328 | 329 | ### Parameters 330 | ---- 331 | worksheet_id_or_name : str 332 | The worksheet resource id or the worksheet name. 333 | 334 | row : int 335 | Row number of the cell to be retrieved. Zero-indexed. 336 | 337 | column : int 338 | Column number of the cell to be retrieved. Zero-indexed. 339 | 340 | item_id : str (optional, Default=None) 341 | The Drive Item Resource ID. 342 | 343 | item_path : str (optional, Default=None) 344 | The Item Path. An Example would be the following: 345 | `/TestFolder/TestFile.txt` 346 | 347 | ### Returns 348 | ---- 349 | dict: 350 | A Range object. 351 | """ 352 | 353 | if item_id: 354 | content = self.graph_session.make_request( 355 | method="get", 356 | endpoint=f"/me/drive/items/{item_id}/workbook/worksheets" 357 | + f"/{worksheet_id_or_name}/cell(row={row},column={column})", 358 | ) 359 | elif item_path: 360 | content = self.graph_session.make_request( 361 | method="get", 362 | endpoint=f"/me/drive/root:/{item_path}:/workbook/worksheets/" 363 | + f"{worksheet_id_or_name}/cell(row={row},column={column})", 364 | ) 365 | 366 | return content 367 | 368 | def get_range( 369 | self, 370 | worksheet_id_or_name: str, 371 | address: str = None, 372 | item_id: str = None, 373 | item_path: str = None, 374 | ) -> dict: 375 | """Gets the range object specified by the address or name. 376 | 377 | ### Parameters 378 | ---- 379 | worksheet_id_or_name : str 380 | The worksheet resource id or the worksheet name. 381 | 382 | address : str (optional, Default=None) 383 | The address or the name of the range. If not specified, 384 | the entire worksheet range is returned. 385 | 386 | item_id : str (optional, Default=None) 387 | The Drive Item Resource ID. 388 | 389 | item_path : str (optional, Default=None) 390 | The Item Path. An Example would be the following: 391 | `/TestFolder/TestFile.txt` 392 | 393 | ### Returns 394 | ---- 395 | dict: 396 | A Range object. 397 | """ 398 | 399 | if address: 400 | endpoint = r"/range(address={address})" 401 | else: 402 | endpoint = "/range" 403 | 404 | if item_id: 405 | content = self.graph_session.make_request( 406 | method="get", 407 | endpoint=f"/me/drive/items/{item_id}/workbook/worksheets" 408 | + f"/{worksheet_id_or_name}" 409 | + endpoint, 410 | ) 411 | elif item_path: 412 | content = self.graph_session.make_request( 413 | method="get", 414 | endpoint=f"/me/drive/root:/{item_path}:/workbook/worksheets/" 415 | + f"{worksheet_id_or_name}" 416 | + endpoint, 417 | ) 418 | 419 | return content 420 | 421 | def list_tables( 422 | self, 423 | worksheet_id_or_name: str, 424 | item_id: str = None, 425 | item_path: str = None, 426 | ) -> dict: 427 | """Retrieve a list of WorkbookTable objects. 428 | 429 | ### Parameters 430 | ---- 431 | worksheet_id_or_name : str 432 | The worksheet resource id or the worksheet name. 433 | 434 | item_id : str (optional, Default=None) 435 | The Drive Item Resource ID. 436 | 437 | item_path : str (optional, Default=None) 438 | The Item Path. An Example would be the following: 439 | `/TestFolder/TestFile.txt` 440 | 441 | ### Returns 442 | ---- 443 | dict: 444 | A collection of WorkbookTable Objects. 445 | """ 446 | 447 | if item_id: 448 | content = self.graph_session.make_request( 449 | method="get", 450 | endpoint=f"/me/drive/items/{item_id}/workbook/worksheets" 451 | + f"/{worksheet_id_or_name}/tables", 452 | ) 453 | elif item_path: 454 | content = self.graph_session.make_request( 455 | method="get", 456 | endpoint=f"/me/drive/root:/{item_path}:/workbook/worksheets/" 457 | + f"{worksheet_id_or_name}/tables", 458 | ) 459 | 460 | return content 461 | 462 | def add_table( 463 | self, 464 | worksheet_id_or_name: str, 465 | address: str, 466 | has_headers: bool, 467 | item_id: str = None, 468 | item_path: str = None, 469 | ) -> dict: 470 | """Creates a new WorkbookTable Object. 471 | 472 | ### Parameters 473 | ---- 474 | worksheet_id_or_name : str 475 | The worksheet resource id or the worksheet name. 476 | 477 | address : str 478 | The range address. 479 | 480 | has_headers : bool 481 | Boolean value that indicates whether the range has 482 | column labels. If the source does not contain headers 483 | (i.e,. when this property set to false), Excel will 484 | automatically generate header shifting the data down 485 | by one row. 486 | 487 | item_id : str (optional, Default=None) 488 | The Drive Item Resource ID. 489 | 490 | item_path : str (optional, Default=None) 491 | The Item Path. An Example would be the following: 492 | `/TestFolder/TestFile.txt` 493 | 494 | ### Returns 495 | ---- 496 | dict: 497 | A WorkbookTable Object. 498 | """ 499 | 500 | body = {"address": address, "hasHeaders": has_headers} 501 | 502 | if item_id: 503 | content = self.graph_session.make_request( 504 | method="post", 505 | json=body, 506 | additional_headers={"Content-type": "application/json"}, 507 | endpoint=f"/me/drive/items/{item_id}/workbook/worksheets" 508 | + f"/{worksheet_id_or_name}/tables/add", 509 | ) 510 | elif item_path: 511 | content = self.graph_session.make_request( 512 | method="post", 513 | json=body, 514 | additional_headers={"Content-type": "application/json"}, 515 | endpoint=f"/me/drive/root:/{item_path}:/workbook/worksheets/" 516 | + f"{worksheet_id_or_name}/tables/add", 517 | ) 518 | 519 | return content 520 | 521 | def list_charts( 522 | self, 523 | worksheet_id_or_name: str, 524 | item_id: str = None, 525 | item_path: str = None, 526 | ) -> dict: 527 | """Retrieve a list of WorkbookChart Objects. 528 | 529 | ### Parameters 530 | ---- 531 | worksheet_id_or_name : str 532 | The worksheet resource id or the worksheet name. 533 | 534 | item_id : str (optional, Default=None) 535 | The Drive Item Resource ID. 536 | 537 | item_path : str (optional, Default=None) 538 | The Item Path. An Example would be the following: 539 | `/TestFolder/TestFile.txt` 540 | 541 | ### Returns 542 | ---- 543 | dict: 544 | A collection of WorkbookChart Objects. 545 | """ 546 | 547 | if item_id: 548 | content = self.graph_session.make_request( 549 | method="get", 550 | endpoint=f"/me/drive/items/{item_id}/workbook/worksheets" 551 | + f"/{worksheet_id_or_name}/charts", 552 | ) 553 | elif item_path: 554 | content = self.graph_session.make_request( 555 | method="get", 556 | endpoint=f"/me/drive/root:/{item_path}:/workbook/worksheets/" 557 | + f"{worksheet_id_or_name}/charts", 558 | ) 559 | 560 | return content 561 | 562 | def add_chart( 563 | self, 564 | worksheet_id_or_name: str, 565 | name: str, 566 | height: float, 567 | top: float, 568 | left: float, 569 | width: float, 570 | item_id: str = None, 571 | item_path: str = None, 572 | ) -> dict: 573 | """Retrieve a list of table objects. 574 | 575 | ### Parameters 576 | ---- 577 | worksheet_id_or_name : str 578 | The worksheet resource id or the worksheet name. 579 | 580 | height : float 581 | Represents the height, in points, of the chart object. 582 | 583 | top : float 584 | Represents the distance, in points, from the top edge 585 | of the object to the top of row 1 (on a worksheet) or 586 | the top of the chart area (on a chart). 587 | 588 | left : float 589 | The distance, in points, from the left side of the chart 590 | to the worksheet origin. 591 | 592 | width : float 593 | Represents the width, in points, of the chart object. 594 | 595 | name : str 596 | Represents the name of a chart object. 597 | 598 | item_id : str (optional, Default=None) 599 | The Drive Item Resource ID. 600 | 601 | item_path : str (optional, Default=None) 602 | The Item Path. An Example would be the following: 603 | `/TestFolder/TestFile.txt` 604 | 605 | ### Returns 606 | ---- 607 | dict: 608 | A WorkbookChart object. 609 | """ 610 | 611 | body = { 612 | "height": height, 613 | "top": top, 614 | "left": left, 615 | "width": width, 616 | "name": name, 617 | } 618 | 619 | if item_id: 620 | content = self.graph_session.make_request( 621 | method="post", 622 | json=body, 623 | additional_headers={"Content-type": "application/json"}, 624 | endpoint=f"/me/drive/items/{item_id}/workbook/worksheets" 625 | + f"/{worksheet_id_or_name}/charts", 626 | ) 627 | elif item_path: 628 | content = self.graph_session.make_request( 629 | method="post", 630 | json=body, 631 | additional_headers={"Content-type": "application/json"}, 632 | endpoint=f"/me/drive/root:/{item_path}:/workbook/worksheets/" 633 | + f"{worksheet_id_or_name}/charts", 634 | ) 635 | 636 | return content 637 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | msal>=1.5.0 2 | requests>=2.24.0 3 | -------------------------------------------------------------------------------- /samples/configs/write_config.py: -------------------------------------------------------------------------------- 1 | from configparser import ConfigParser 2 | 3 | # Initialize the Parser. 4 | config = ConfigParser() 5 | 6 | # Add the Section. 7 | config.add_section("graph_api") 8 | 9 | # Set the Values. 10 | config.set("graph_api", "client_id", "") 11 | config.set("graph_api", "client_secret", "") 12 | config.set("graph_api", "redirect_uri", "") 13 | 14 | # Write the file. 15 | with open(file="samples/configs/config.ini", mode="w+", encoding="utf-8") as f: 16 | config.write(f) 17 | -------------------------------------------------------------------------------- /samples/images/azure_directory.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/areed1192/ms-graph-python-client/22c3386b9b1d5832fbedea5686dc0637b83127a8/samples/images/azure_directory.jpg -------------------------------------------------------------------------------- /samples/setting_up_an_app.md: -------------------------------------------------------------------------------- 1 | # Using the Microsoft Graph API 2 | 3 | ## Overview 4 | 5 | Microsoft Graph is the gateway to data and intelligence in Microsoft 365. It provides 6 | a unified programmability model that you can use to access the tremendous amount of data 7 | in Microsoft 365, Windows 10, and Enterprise Mobility + Security. Use the wealth of data 8 | n Microsoft Graph to build apps for organizations and consumers that interact with millions 9 | of users. 10 | 11 | ## Register an App 12 | 13 | ### Step 1: Go to your Azure Portal 14 | 15 | For step 1 you'll need to go to your [Azure Portal](https://portal.azure.com/) and login. 16 | 17 | ### Step 2: Go to the Azure Active Directory Service 18 | 19 | Once you've logged into Azure you'll need to register a new application so you can access 20 | the Microsoft Graph API. To register a new application go to your **Azure Active Directory** 21 | and once there go down to **App Registrations** a new window will pop up. 22 | 23 | ### Step 3: Register a New App 24 | 25 | Select **New registration**. On the Register an application page, set the values as follows. 26 | 27 | - Set Name to **Python Graph Tutorial**. 28 | - Set **Supported account** types to Accounts in any organizational directory and personal Microsoft accounts. 29 | - Under **Redirect URI**, set the first drop-down to Web and set the value to . 30 | 31 | ### Step 4: Grab your Client ID 32 | 33 | Select **Register**. On the *Python Graph Tutorial* page, copy the value of the **Application (client) ID** and 34 | save it, you will need it in the next step. 35 | 36 | ### Step 5: Create a Client Secret 37 | 38 | Select **Certificates & secrets** under Manage. Select the **New client secret** button. Enter a value in 39 | Description and select one of the options for Expires and select Add. **MAKE SURE TO COPY THE CLIENT SECRET** 40 | **BEFORE YOU LEAVE THE PAGE OR ELSE YOU WILL NOT BE ABLE TO GRAB IT AGAIN AND WILL NEED TO CREATE A NEW ONE.** 41 | 42 | ### Step 6: Save the Content to a Config File (Optional) 43 | 44 | If you want you can save the content to a config file using python and then when you need to grab your credentials 45 | you can simply read the config file into your script. Some alternative options is using an Azure Key Vault or storing 46 | the credentials in environment variables. To save the content in a config file use the sample file in the [configs folder](https://github.com/areed1192/ms-graph-python-client/tree/master/samples/configs) 47 | that is found in the samples folder. 48 | 49 | Make sure to fill out the values in the file: 50 | 51 | ```python 52 | from configparser import ConfigParser 53 | 54 | # Initialize the Parser. 55 | config = ConfigParser() 56 | 57 | # Add the Section. 58 | config.add_section("graph_api") 59 | 60 | # Set the Values. 61 | config.set("graph_api", "client_id", "") 62 | config.set("graph_api", "client_secret", "") 63 | config.set("graph_api", "redirect_uri", "") 64 | 65 | # Write the file. 66 | with open(file="samples/configs/config_video.ini", mode="w+") as f: 67 | config.write(f) 68 | ``` 69 | -------------------------------------------------------------------------------- /samples/use_client.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ms_graph.client import MicrosoftGraphClient 4 | 5 | scopes = [ 6 | "Calendars.ReadWrite", 7 | "Files.ReadWrite.All", 8 | "User.ReadWrite.All", 9 | "Notes.ReadWrite.All", 10 | "Directory.ReadWrite.All", 11 | "User.Read.All", 12 | "Directory.Read.All", 13 | "Directory.ReadWrite.All", 14 | ] 15 | 16 | # Initialize the Parser. 17 | config = ConfigParser() 18 | 19 | # Read the file. 20 | config.read("config/config.ini") 21 | 22 | # Get the specified credentials. 23 | client_id = config.get("graph_api", "client_id") 24 | client_secret = config.get("graph_api", "client_secret") 25 | redirect_uri = config.get("graph_api", "redirect_uri") 26 | 27 | # Initialize the Client. 28 | graph_client = MicrosoftGraphClient( 29 | client_id=client_id, 30 | client_secret=client_secret, 31 | redirect_uri=redirect_uri, 32 | scope=scopes, 33 | credentials="configs/ms_graph_state.jsonc", 34 | ) 35 | 36 | # Login to the Client. 37 | graph_client.login() 38 | 39 | # Grab the User Services. 40 | user_services = graph_client.users() 41 | 42 | # List the Users. 43 | pprint(user_services.list_users()) 44 | 45 | # Grab the Drive Services. 46 | drive_services = graph_client.drives() 47 | 48 | # List the Root Drive. 49 | pprint(drive_services.get_root_drive()) 50 | 51 | # List the Root Drive Deltas. 52 | pprint(drive_services.get_root_drive_delta()) 53 | -------------------------------------------------------------------------------- /samples/use_drive_services.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ms_graph.client import MicrosoftGraphClient 4 | 5 | scopes = [ 6 | "Calendars.ReadWrite", 7 | "Files.ReadWrite.All", 8 | "User.ReadWrite.All", 9 | "Notes.ReadWrite.All", 10 | "Directory.ReadWrite.All", 11 | "User.Read.All", 12 | "Directory.Read.All", 13 | "Directory.ReadWrite.All" 14 | ] 15 | 16 | # Initialize the Parser. 17 | config = ConfigParser() 18 | 19 | # Read the file. 20 | config.read("config/config.ini") 21 | 22 | # Get the specified credentials. 23 | client_id = config.get("graph_api", "client_id") 24 | client_secret = config.get("graph_api", "client_secret") 25 | redirect_uri = config.get("graph_api", "redirect_uri") 26 | 27 | # Initialize the Client. 28 | graph_client = MicrosoftGraphClient( 29 | client_id=client_id, 30 | client_secret=client_secret, 31 | redirect_uri=redirect_uri, 32 | scope=scopes, 33 | credentials="config/ms_graph_state.jsonc" 34 | ) 35 | 36 | # Login to the Client. 37 | graph_client.login() 38 | 39 | # Grab the Drive Services. 40 | drive_services = graph_client.drives() 41 | 42 | # List the Root Drive. 43 | pprint(drive_services.get_root_drive()) 44 | 45 | # List the Root Drive Deltas. 46 | pprint(drive_services.get_root_drive_delta()) 47 | 48 | # List the Root Drive Children. 49 | pprint(drive_services.get_root_drive_children()) 50 | 51 | # List the Root Drive Followers 52 | pprint(drive_services.get_root_drive_followed()) 53 | 54 | # Grab a Drive by id. 55 | pprint(drive_services.get_drive_by_id(drive_id="8bc640c57cda25b6")) 56 | 57 | # Grab MY Drives. 58 | pprint(drive_services.get_my_drives()) 59 | 60 | # Grab User Drives. 61 | pprint(drive_services.get_user_drives(user_id="8bc640c57cda25b6")) 62 | 63 | # Grab Group Drives. 64 | pprint(drive_services.get_user_drives(user_id="8bc640c57cda25b6")) 65 | -------------------------------------------------------------------------------- /samples/use_group_services.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ms_graph.client import MicrosoftGraphClient 4 | 5 | # Define the Scopes needed to Login. 6 | scopes = [ 7 | "Calendars.ReadWrite", 8 | "Files.ReadWrite.All", 9 | "User.ReadWrite.All", 10 | "Notes.ReadWrite.All", 11 | "Directory.ReadWrite.All", 12 | "User.Read.All", 13 | "Directory.Read.All", 14 | "Directory.ReadWrite.All", 15 | "Group.Read.All", 16 | "Group.ReadWrite.All", 17 | ] 18 | 19 | # Initialize the Parser. 20 | config = ConfigParser() 21 | 22 | # Read the file. 23 | config.read("configs/config.ini") 24 | 25 | # Get the specified credentials. 26 | client_id = config.get("graph_api", "client_id") 27 | client_secret = config.get("graph_api", "client_secret") 28 | redirect_uri = config.get("graph_api", "redirect_uri") 29 | 30 | # Initialize the Client. 31 | graph_client = MicrosoftGraphClient( 32 | client_id=client_id, 33 | client_secret=client_secret, 34 | redirect_uri=redirect_uri, 35 | scope=scopes, 36 | credentials="configs/ms_graph_state.jsonc", 37 | ) 38 | 39 | # Login to the Client. 40 | graph_client.login() 41 | 42 | # Grab the Groups Services. 43 | groups_services = graph_client.groups() 44 | 45 | # List all Groups. 46 | pprint(groups_services.list_groups()) 47 | -------------------------------------------------------------------------------- /samples/use_mail_services.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ms_graph.client import MicrosoftGraphClient 4 | 5 | # SCOPES NEEDED: 6 | # --------------- 7 | # "Mail.Send", 8 | # "MailboxSettings.Read", 9 | # "MailboxSettings.ReadWrite" 10 | 11 | # Define the Scopes needed to Login. 12 | scopes = [ 13 | "Calendars.ReadWrite", 14 | "Files.ReadWrite.All", 15 | "User.ReadWrite.All", 16 | "Notes.ReadWrite.All", 17 | "Directory.ReadWrite.All", 18 | "User.Read.All", 19 | "Directory.Read.All", 20 | "Directory.ReadWrite.All", 21 | "Group.Read.All", 22 | "Group.ReadWrite.All", 23 | "Notes.Create", 24 | "Notes.Read", 25 | "Notes.ReadWrite", 26 | "Notes.Read.All", 27 | "Notes.ReadWrite.All", 28 | "Mail.Send", 29 | "MailboxSettings.Read", 30 | "MailboxSettings.ReadWrite", 31 | ] 32 | 33 | # Initialize the Parser. 34 | config = ConfigParser() 35 | 36 | # Read the file. 37 | config.read("configs/config.ini") 38 | 39 | # Get the specified credentials. 40 | client_id = config.get("graph_api", "client_id") 41 | client_secret = config.get("graph_api", "client_secret") 42 | redirect_uri = config.get("graph_api", "redirect_uri") 43 | 44 | # Initialize the Client. 45 | graph_client = MicrosoftGraphClient( 46 | client_id=client_id, 47 | client_secret=client_secret, 48 | redirect_uri=redirect_uri, 49 | scope=scopes, 50 | credentials="configs/ms_graph_state.jsonc", 51 | ) 52 | 53 | # Login to the Client. 54 | graph_client.login() 55 | 56 | # Define a valid User ID. 57 | USER_ID = "8bc640c57cda25b6" 58 | 59 | # Define a mail Item ID. 60 | MAIL_ID = ( 61 | "AQMkADAwATZiZmYAZC1hMDI2LTE3NTgtMDACLTAwCgBGAAADpjqwNb_dak68rN7703u" 62 | + "ffQcAFNKsLOjbGUuHHmYnyKdJiAAAAgEhAAAAFNKsLOjbGUuHHmYnyKdJiAAFBMTneQ" 63 | + "AAAA==" 64 | ) 65 | 66 | # Define a mail item ID with Attachments. 67 | MAIL_ID_ATTACHMENTS = ( 68 | "AQMkADAwATZiZmYAZC1hMDI2LTE3NTgtMDACLTAwCgBGAAADpjqwNb+" 69 | + "dak68rN7703uffQcAFNKsLOjbGUuHHmYnyKdJiAAAAgEMAAAAFNKsLO" 70 | + "jbGUuHHmYnyKdJiAAE9ucV+AAAAA==" 71 | ) 72 | 73 | # Grab the Notes Services. 74 | mail_services = graph_client.mail() 75 | 76 | # Grab all my Messages. 77 | pprint(mail_services.list_my_messages()) 78 | 79 | # Grab a specific message for the default user. 80 | pprint(mail_services.get_my_messages(message_id=MAIL_ID)) 81 | 82 | # Get a Specific User"s Message. 83 | pprint(mail_services.get_user_messages(user_id=USER_ID, message_id=MAIL_ID)) 84 | 85 | # List the rules for a specific user.. 86 | pprint(mail_services.list_rules(user_id=USER_ID)) 87 | 88 | # List the rules for the default user. 89 | pprint(mail_services.list_my_rules()) 90 | 91 | # List the overrides for a specific user. 92 | pprint(mail_services.list_overrides(user_id=USER_ID)) 93 | 94 | # List the overrides for the default user. 95 | pprint(mail_services.list_my_overrides()) 96 | 97 | # List the attachments for a specific message. 98 | pprint(mail_services.list_my_attachements(message_id=MAIL_ID_ATTACHMENTS)) 99 | 100 | 101 | # Create a new message for the default user. Keep in mind this does not send the mail. 102 | new_message_draft = mail_services.create_my_message( 103 | message={ 104 | "subject": "Did you see last night's game?", 105 | "importance": "Low", 106 | "body": {"contentType": "HTML", "content": "They were awesome!"}, 107 | "toRecipients": [{"emailAddress": {"address": "alexreed1192@gmail.com"}}], 108 | } 109 | ) 110 | 111 | # Check it out. 112 | pprint(new_message_draft) 113 | 114 | # grab the ID. 115 | new_message_id = new_message_draft["id"] 116 | 117 | # Send the newly created message. 118 | mail_services.send_my_message(message_id=new_message_id) 119 | 120 | # Let"s create a new message rule, this will help with things like incoming mail. We can 121 | # control what happens to mail that meets certain conditions. 122 | my_new_message_rule = mail_services.create_my_message_rule( 123 | rule={ 124 | "displayName": "From partner", 125 | "sequence": 2, 126 | "isEnabled": True, 127 | "conditions": {"senderContains": ["youtube"]}, 128 | "actions": { 129 | "forwardTo": [ 130 | { 131 | "emailAddress": { 132 | "name": "Alex Reed", 133 | "address": "coding.sigma@gmail.com", 134 | } 135 | } 136 | ], 137 | "stopProcessingRules": True, 138 | }, 139 | } 140 | ) 141 | 142 | # Check it out. 143 | pprint(my_new_message_rule) 144 | -------------------------------------------------------------------------------- /samples/use_notebook_services.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ms_graph.client import MicrosoftGraphClient 4 | 5 | # SCOPES NEEDED: 6 | # --------------- 7 | # "Notes.Create 8 | # "Notes.Read" 9 | # "Notes.ReadWrite" 10 | # "Notes.Read.All", 11 | # "Notes.ReadWrite.All" 12 | 13 | # Define the Scopes needed to Login. 14 | scopes = [ 15 | "Calendars.ReadWrite", 16 | "Files.ReadWrite.All", 17 | "User.ReadWrite.All", 18 | "Notes.ReadWrite.All", 19 | "Directory.ReadWrite.All", 20 | "User.Read.All", 21 | "Directory.Read.All", 22 | "Directory.ReadWrite.All", 23 | "Group.Read.All", 24 | "Group.ReadWrite.All", 25 | "Notes.Create", 26 | "Notes.Read", 27 | "Notes.ReadWrite", 28 | "Notes.Read.All", 29 | "Notes.ReadWrite.All", 30 | ] 31 | 32 | # Initialize the Parser. 33 | config = ConfigParser() 34 | 35 | # Read the file. 36 | config.read("configs/config.ini") 37 | 38 | # Get the specified credentials. 39 | client_id = config.get("graph_api", "client_id") 40 | client_secret = config.get("graph_api", "client_secret") 41 | redirect_uri = config.get("graph_api", "redirect_uri") 42 | 43 | # Initialize the Client. 44 | graph_client = MicrosoftGraphClient( 45 | client_id=client_id, 46 | client_secret=client_secret, 47 | redirect_uri=redirect_uri, 48 | scope=scopes, 49 | credentials="configs/ms_graph_state.jsonc", 50 | ) 51 | 52 | # Login to the Client. 53 | graph_client.login() 54 | 55 | # Grab the Notes Services. 56 | notes_services = graph_client.notes() 57 | 58 | # List all Notes. 59 | pprint(notes_services.list_my_notebooks()) 60 | 61 | # Grab the Notebook Sections. 62 | notebook_sections = notes_services.list_my_notebook_sections( 63 | notebook_id="0-8BC640C57CDA25B6!71451" 64 | ) 65 | 66 | # List all the sections for the Notebook. 67 | pprint(notebook_sections) 68 | 69 | # Grab all the Notebook Pages. 70 | notebook_pages = notes_services.list_my_notebook_pages( 71 | section_id="0-8BC640C57CDA25B6!71455" 72 | ) 73 | 74 | # List all the Page for the Notebook for the particular session. 75 | pprint(notebook_sections) 76 | -------------------------------------------------------------------------------- /samples/use_personal_contacts_service.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ms_graph.client import MicrosoftGraphClient 4 | 5 | scopes = [ 6 | "Contacts.ReadWrite", 7 | "Calendars.ReadWrite", 8 | "Files.ReadWrite.All", 9 | "User.ReadWrite.All", 10 | "Notes.ReadWrite.All", 11 | "Directory.ReadWrite.All", 12 | "User.Read.All", 13 | "Directory.Read.All", 14 | "Directory.ReadWrite.All", 15 | "Mail.ReadWrite", 16 | "Sites.ReadWrite.All", 17 | "ExternalItem.Read.All", 18 | ] 19 | 20 | # Initialize the Parser. 21 | config = ConfigParser() 22 | 23 | # Read the file. 24 | config.read("configs/config.ini") 25 | 26 | # Get the specified credentials. 27 | client_id = config.get("graph_api", "client_id") 28 | client_secret = config.get("graph_api", "client_secret") 29 | redirect_uri = config.get("graph_api", "redirect_uri") 30 | 31 | # Initialize the Client. 32 | graph_client = MicrosoftGraphClient( 33 | client_id=client_id, 34 | client_secret=client_secret, 35 | redirect_uri=redirect_uri, 36 | scope=scopes, 37 | credentials="configs/ms_graph_state.jsonc", 38 | ) 39 | 40 | # Login to the Client. 41 | graph_client.login() 42 | 43 | # Define a valid User ID. 44 | USER_ID = "8bc640c57cda25b6" 45 | 46 | # Define a folder ID. 47 | FOLDER_ID = ( 48 | "AQMkADAwATZiZmYAZC1hMDI2LTE3NTgtMDACLTAwCgAuAAADpjqwNb_d" 49 | + "ak68rN7703uffQEAFNKsLOjbGUuHHmYnyKdJiAAFAP8ORwAAAA==" 50 | ) 51 | 52 | # Grab the Personal Contacts Service. 53 | personal_contacts_service = graph_client.personal_contacts() 54 | 55 | # Grab my contacts folders. 56 | pprint(personal_contacts_service.list_my_contacts_folder()) 57 | 58 | # Grab a contact folder for a specific user and a specific ID. 59 | pprint( 60 | personal_contacts_service.list_contacts_folder_by_id( 61 | user_id=USER_ID, folder_id=FOLDER_ID 62 | ) 63 | ) 64 | 65 | # Grab a contact folder for a specific user and a specific ID. 66 | pprint( 67 | personal_contacts_service.get_contacts_folder_by_id( 68 | user_id=USER_ID, folder_id=FOLDER_ID 69 | ) 70 | ) 71 | 72 | # Grab the Contacts. 73 | my_contacts = personal_contacts_service.list_my_contacts() 74 | 75 | # Get a random contact id. 76 | contact_id = my_contacts["value"][-1]["id"] 77 | 78 | # Grab a specific contact from my contacts folder. 79 | pprint(personal_contacts_service.get_my_contact_by_id(contact_id=contact_id)) 80 | 81 | # Create a new contact folder under the default profile. 82 | pprint( 83 | personal_contacts_service.create_my_contact_folder( 84 | folder_resource={ 85 | "parentFolderId": "sigma-coding-contacts", 86 | "displayName": "Sigma Coding - Contacts", 87 | } 88 | ) 89 | ) 90 | 91 | # Create a new contact folder under the specified user profile. 92 | pprint( 93 | personal_contacts_service.create_user_contact_folder( 94 | user_id=USER_ID, 95 | folder_resource={ 96 | "parentFolderId": "trading-robot-contacts", 97 | "displayName": "Trading Robot - Contacts", 98 | }, 99 | ) 100 | ) 101 | -------------------------------------------------------------------------------- /samples/use_range_service.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ms_graph.client import MicrosoftGraphClient 4 | 5 | # Needed Permissions 6 | # Files.ReadWrite 7 | 8 | scopes = [ 9 | "Calendars.ReadWrite", 10 | "Files.ReadWrite.All", 11 | "User.ReadWrite.All", 12 | "Notes.ReadWrite.All", 13 | "Directory.ReadWrite.All", 14 | "User.Read.All", 15 | "Directory.Read.All", 16 | "Directory.ReadWrite.All", 17 | "Mail.ReadWrite", 18 | "Sites.ReadWrite.All", 19 | "ExternalItem.Read.All", 20 | ] 21 | 22 | # Initialize the Parser. 23 | config = ConfigParser() 24 | 25 | # Read the file. 26 | config.read("config/config.ini") 27 | 28 | # Get the specified credentials. 29 | client_id = config.get("graph_api", "client_id") 30 | client_secret = config.get("graph_api", "client_secret") 31 | redirect_uri = config.get("graph_api", "redirect_uri") 32 | 33 | # Initialize the Client. 34 | graph_client = MicrosoftGraphClient( 35 | client_id=client_id, 36 | client_secret=client_secret, 37 | redirect_uri=redirect_uri, 38 | scope=scopes, 39 | credentials="config/ms_graph_state.jsonc", 40 | ) 41 | 42 | # Login to the Client. 43 | graph_client.login() 44 | 45 | # Grab the Range Service. 46 | range_service = graph_client.range() 47 | 48 | # Grab a range. 49 | range_object = range_service.get_range( 50 | item_path="Desktop/Personal Code/Repo - YouTube Channel Management/" 51 | + "youtube-channel-management/YouTube Video Description Database.xlsm", 52 | worksheet_name_or_id="Video_Database", 53 | address="A1:P374", 54 | ) 55 | pprint(range_object) 56 | -------------------------------------------------------------------------------- /samples/use_search_service.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ms_graph.client import MicrosoftGraphClient 4 | 5 | scopes = [ 6 | "Calendars.ReadWrite", 7 | "Files.ReadWrite.All", 8 | "User.ReadWrite.All", 9 | "Notes.ReadWrite.All", 10 | "Directory.ReadWrite.All", 11 | "User.Read.All", 12 | "Directory.Read.All", 13 | "Directory.ReadWrite.All", 14 | "Mail.ReadWrite", 15 | "Sites.ReadWrite.All", 16 | "ExternalItem.Read.All" 17 | ] 18 | 19 | # Initialize the Parser. 20 | config = ConfigParser() 21 | 22 | # Read the file. 23 | config.read("configs/config.ini") 24 | 25 | # Get the specified credentials. 26 | client_id = config.get("graph_api", "client_id") 27 | client_secret = config.get("graph_api", "client_secret") 28 | redirect_uri = config.get("graph_api", "redirect_uri") 29 | 30 | # Initialize the Client. 31 | graph_client = MicrosoftGraphClient( 32 | client_id=client_id, 33 | client_secret=client_secret, 34 | redirect_uri=redirect_uri, 35 | scope=scopes, 36 | credentials="configs/ms_graph_state.jsonc" 37 | ) 38 | 39 | # Login to the Client. 40 | graph_client.login() 41 | 42 | # Grab the Search Service. 43 | search_service = graph_client.search() 44 | 45 | # Search for some documents. 46 | search_response = search_service.query( 47 | search_request={ 48 | "requests": [ 49 | { 50 | "entityTypes": [ 51 | "message" 52 | ], 53 | "query": { 54 | "queryString": "sigma" 55 | }, 56 | "from": 0, 57 | "size": 25 58 | } 59 | ] 60 | } 61 | ) 62 | 63 | # Print the Output. 64 | pprint(search_response) 65 | -------------------------------------------------------------------------------- /samples/use_workbooks_service.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ms_graph.client import MicrosoftGraphClient 4 | 5 | # Needed Permissions 6 | # Files.ReadWrite 7 | 8 | scopes = [ 9 | "Calendars.ReadWrite", 10 | "Files.ReadWrite.All", 11 | "User.ReadWrite.All", 12 | "Notes.ReadWrite.All", 13 | "Directory.ReadWrite.All", 14 | "User.Read.All", 15 | "Directory.Read.All", 16 | "Directory.ReadWrite.All", 17 | "Mail.ReadWrite", 18 | "Sites.ReadWrite.All", 19 | "ExternalItem.Read.All", 20 | ] 21 | 22 | # Initialize the Parser. 23 | config = ConfigParser() 24 | 25 | # Read the file. 26 | config.read("config/config.ini") 27 | 28 | # Get the specified credentials. 29 | client_id = config.get("graph_api", "client_id") 30 | client_secret = config.get("graph_api", "client_secret") 31 | redirect_uri = config.get("graph_api", "redirect_uri") 32 | 33 | # Initialize the Client. 34 | graph_client = MicrosoftGraphClient( 35 | client_id=client_id, 36 | client_secret=client_secret, 37 | redirect_uri=redirect_uri, 38 | scope=scopes, 39 | credentials="config/ms_graph_state.jsonc", 40 | ) 41 | 42 | # Login to the Client. 43 | graph_client.login() 44 | 45 | # Grab the Workbooks Service. 46 | workbooks_service = graph_client.workbooks() 47 | 48 | # Create a new session for my Excel Workbook. 49 | session_response = workbooks_service.create_session( 50 | item_path="Desktop/Personal Code/Repo - YouTube Channel Management/" 51 | + "youtube-channel-management/YouTube Video Description Database.xlsm" 52 | ) 53 | pprint(session_response) 54 | 55 | # List all the Tables in a Workbook. 56 | table_objects = workbooks_service.list_tables( 57 | item_path="Desktop/Personal Code/Repo - YouTube Channel Management/" 58 | + "youtube-channel-management/YouTube Video Description Database.xlsm" 59 | ) 60 | pprint(table_objects) 61 | 62 | # List all the Worksheets in a Workbook. 63 | worksheet_objects = workbooks_service.list_worksheets( 64 | item_path="Desktop/Personal Code/Repo - YouTube Channel Management/" 65 | + "youtube-channel-management/YouTube Video Description Database.xlsm" 66 | ) 67 | pprint(worksheet_objects) 68 | 69 | # List all the Named Objects in a Workbook. 70 | name_objects = workbooks_service.list_names( 71 | item_path="Desktop/Personal Code/Repo - YouTube Channel Management/" 72 | + "youtube-channel-management/YouTube Video Description Database.xlsm" 73 | ) 74 | pprint(name_objects) 75 | -------------------------------------------------------------------------------- /samples/user_drive_items_services.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | from configparser import ConfigParser 3 | from ms_graph.client import MicrosoftGraphClient 4 | 5 | scopes = [ 6 | "Calendars.ReadWrite", 7 | "Files.ReadWrite.All", 8 | "User.ReadWrite.All", 9 | "Notes.ReadWrite.All", 10 | "Directory.ReadWrite.All", 11 | "User.Read.All", 12 | "Directory.Read.All", 13 | "Directory.ReadWrite.All", 14 | ] 15 | 16 | # Initialize the Parser. 17 | config = ConfigParser() 18 | 19 | # Read the file. 20 | config.read("configs/config.ini") 21 | 22 | # Get the specified credentials. 23 | client_id = config.get("graph_api", "client_id") 24 | client_secret = config.get("graph_api", "client_secret") 25 | redirect_uri = config.get("graph_api", "redirect_uri") 26 | 27 | # Initialize the Client. 28 | graph_client = MicrosoftGraphClient( 29 | client_id=client_id, 30 | client_secret=client_secret, 31 | redirect_uri=redirect_uri, 32 | scope=scopes, 33 | credentials="configs/ms_graph_state.jsonc", 34 | ) 35 | 36 | # Login to the Client. 37 | graph_client.login() 38 | 39 | # Grab the Drive Items Service. 40 | drive_item_services = graph_client.drive_item() 41 | 42 | # Define a valid User ID. 43 | USER_ID = "8bc640c57cda25b6" 44 | 45 | # Define a valid Drive ID. 46 | DRIVE_ID = "8bc640c57cda25b6" 47 | 48 | # Define a valid Drive Item ID. 49 | DRIVE_ITEM_ID = "8BC640C57CDA25B6!3837" 50 | 51 | # Grab a Drive Item, by ID. 52 | pprint(drive_item_services.get_drive_item(drive_id=DRIVE_ID, item_id=DRIVE_ITEM_ID)) 53 | 54 | # Grab a Drive Item, by path. 55 | pprint( 56 | drive_item_services.get_drive_item_by_path( 57 | drive_id=DRIVE_ID, item_path="/Career - Certifications & Exams" 58 | ) 59 | ) 60 | 61 | # Grab a Drive Item, for a specific user in a specific Drive. 62 | pprint(drive_item_services.get_user_drive_item(user_id=USER_ID, item_id=DRIVE_ITEM_ID)) 63 | 64 | # Grab a Drive Item, by path for a specific user in a specific Drive. 65 | pprint( 66 | drive_item_services.get_user_drive_item_by_path( 67 | user_id=USER_ID, item_path="/Career - Certifications & Exams" 68 | ) 69 | ) 70 | 71 | # Grab my Drive Item by ID. 72 | pprint(drive_item_services.get_my_drive_item(item_id=DRIVE_ITEM_ID)) 73 | 74 | # Grab my Drive Item, by path. 75 | pprint( 76 | drive_item_services.get_my_drive_item_by_path( 77 | item_path="/Career - Certifications & Exams" 78 | ) 79 | ) 80 | 81 | # Define a valid Group ID. 82 | GROUP_ID = "GROUP_ID_GOES_HERE" 83 | 84 | # Grab a group Drive Item by ID. 85 | pprint( 86 | drive_item_services.get_group_drive_item(group_id=GROUP_ID, item_id=DRIVE_ITEM_ID) 87 | ) 88 | 89 | # Grab a group Drive Item, by path. 90 | pprint( 91 | drive_item_services.get_group_drive_item_by_path( 92 | group_id=GROUP_ID, item_path="/Career - Certifications & Exams" 93 | ) 94 | ) 95 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from setuptools import find_namespace_packages 3 | 4 | # Open the README file. 5 | with open(file="README.md", mode="r", encoding="utf-8") as fh: 6 | long_description = fh.read() 7 | 8 | setup( 9 | name="ms-graph-python-client", 10 | author="Alex Reed", 11 | author_email="coding.sigma@gmail.com", 12 | version="0.1.0", 13 | description="A Python Client Application that allows interaction with the Microsoft Graph API.", 14 | long_description=long_description, 15 | long_description_content_type="text/markdown", 16 | url="https://github.com/areed1192/ms-graph-python-client", 17 | install_requires=["requests", "msal"], 18 | packages=find_namespace_packages(include=["ms_graph", "ms_graph.*"]), 19 | python_requires=">3.8", 20 | ) 21 | -------------------------------------------------------------------------------- /tests/test_client.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from unittest import TestCase 4 | from configparser import ConfigParser 5 | from ms_graph.client import MicrosoftGraphClient 6 | 7 | from ms_graph.mail import Mail 8 | from ms_graph.notes import Notes 9 | from ms_graph.users import Users 10 | from ms_graph.search import Search 11 | from ms_graph.drives import Drives 12 | from ms_graph.groups import Groups 13 | from ms_graph.drive_items import DriveItems 14 | from ms_graph.personal_contacts import PersonalContacts 15 | 16 | 17 | class MicrosoftGraphSessionTest(TestCase): 18 | 19 | """Will perform a unit test for the `MicrosoftGraphClient` session.""" 20 | 21 | def setUp(self) -> None: 22 | """Set up the `MicrosoftGraphClient` Client.""" 23 | 24 | scopes = [ 25 | "Calendars.ReadWrite", 26 | "Files.ReadWrite.All", 27 | "User.ReadWrite.All", 28 | "Notes.ReadWrite.All", 29 | "Directory.ReadWrite.All", 30 | "User.Read.All", 31 | "Directory.Read.All", 32 | "Directory.ReadWrite.All" 33 | ] 34 | 35 | # Initialize the Parser. 36 | config = ConfigParser() 37 | 38 | # Read the file. 39 | config.read("config/config.ini") 40 | 41 | # Get the specified credentials. 42 | client_id = config.get("graph_api", "client_id") 43 | client_secret = config.get("graph_api", "client_secret") 44 | redirect_uri = config.get("graph_api", "redirect_uri") 45 | 46 | # Initialize the Client. 47 | graph_client = MicrosoftGraphClient( 48 | client_id=client_id, 49 | client_secret=client_secret, 50 | redirect_uri=redirect_uri, 51 | scope=scopes, 52 | credentials="config/ms_graph_state.jsonc" 53 | ) 54 | 55 | self.client = graph_client 56 | 57 | def test_creates_instance_of_session(self): 58 | """Create an instance and make sure it"s a `MicrosoftGraphClient`.""" 59 | 60 | self.assertIsInstance(self.client, MicrosoftGraphClient) 61 | 62 | def test_creates_instance_of_mail(self): 63 | """Create an instance and make sure it"s a `MicrosoftGraphClient.Mail`.""" 64 | 65 | self.assertIsInstance(self.client.mail(), Mail) 66 | 67 | def test_creates_instance_of_drive_items(self): 68 | """Create an instance and make sure it"s a `MicrosoftGraphClient.DriveItems`.""" 69 | 70 | self.assertIsInstance(self.client.drive_item(), DriveItems) 71 | 72 | def test_creates_instance_of_drives(self): 73 | """Create an instance and make sure it"s a `MicrosoftGraphClient.Drives`.""" 74 | 75 | self.assertIsInstance(self.client.drives(), Drives) 76 | 77 | def test_creates_instance_of_users(self): 78 | """Create an instance and make sure it"s a `MicrosoftGraphClient.Users`.""" 79 | 80 | self.assertIsInstance(self.client.users(), Users) 81 | 82 | def test_creates_instance_of_groups(self): 83 | """Create an instance and make sure it"s a `MicrosoftGraphClient.Groups`.""" 84 | 85 | self.assertIsInstance(self.client.groups(), Groups) 86 | 87 | def test_creates_instance_of_notes(self): 88 | """Create an instance and make sure it"s a `MicrosoftGraphClient.Notes`.""" 89 | 90 | self.assertIsInstance(self.client.notes(), Notes) 91 | 92 | def test_creates_instance_of_search(self): 93 | """Create an instance and make sure it"s a `MicrosoftGraphClient.Search`.""" 94 | 95 | self.assertIsInstance(self.client.search(), Search) 96 | 97 | def test_creates_instance_of_personal_contacts(self): 98 | """Create an instance and make sure it"s a `MicrosoftGraphClient.PersonalContacts`.""" 99 | 100 | self.assertIsInstance( 101 | self.client.personal_contacts(), PersonalContacts) 102 | 103 | def tearDown(self) -> None: 104 | """Teardown the `MicrosoftGraphClient` Client.""" 105 | 106 | del self.client 107 | 108 | 109 | if __name__ == "__main__": 110 | unittest.main() 111 | --------------------------------------------------------------------------------