diff --git a/.gemini/GEMINI.md b/.gemini/GEMINI.md new file mode 100644 index 00000000..0f175c59 --- /dev/null +++ b/.gemini/GEMINI.md @@ -0,0 +1,12 @@ +# Overview + +This codebase is part of the Google Workspace GitHub organization, https://github.com/googleworkspace. + +## Style Guide + +Use open source best practices for code style and formatting with a preference for Google's style guides. + +## Tools + +- Verify against Google Workspace documentation with the `workspace-developer` MCP server tools. +- Use `gh` for GitHub interactions. diff --git a/.gemini/config.yaml b/.gemini/config.yaml new file mode 100644 index 00000000..a4814a5f --- /dev/null +++ b/.gemini/config.yaml @@ -0,0 +1,12 @@ +# Config for the Gemini Pull Request Review Bot. +# https://github.com/marketplace/gemini-code-assist +have_fun: false +code_review: + disable: false + comment_severity_threshold: "HIGH" + max_review_comments: -1 + pull_request_opened: + help: false + summary: true + code_review: true +ignore_patterns: [] diff --git a/.gemini/settings.json b/.gemini/settings.json new file mode 100644 index 00000000..ec3565d5 --- /dev/null +++ b/.gemini/settings.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "workspace-developer": { + "httpUrl": "https://workspace-developer.goog/mcp", + "trust": true + } + } +} \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..804a0939 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,17 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners + +.github/ @googleworkspace/workspace-devrel-dpe diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..19768a88 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,116 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/admin_sdk/directory" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/admin_sdk/reports" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/admin_sdk/reseller" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/apps_script/quickstart" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/calendar/quickstart" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/classroom/quickstart" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/docs/mail-merge" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/docs/quickstart" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/drive/activity-v2" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/drive/quickstart" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/forms/quickstart" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/gmail/quickstart" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/gmail/snippet" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/people/quickstart" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/sheets/quickstart" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/slides/quickstart" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/tasks/quickstart" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" + - package-ecosystem: "pip" + directory: "/vault/quickstart" + schedule: + interval: "weekly" + commit-message: + prefix: "chore(deps):" diff --git a/.github/linters/.htmlhintrc b/.github/linters/.htmlhintrc new file mode 100644 index 00000000..70391a46 --- /dev/null +++ b/.github/linters/.htmlhintrc @@ -0,0 +1,25 @@ +{ + "tagname-lowercase": true, + "attr-lowercase": true, + "attr-value-double-quotes": true, + "attr-value-not-empty": false, + "attr-no-duplication": true, + "doctype-first": false, + "tag-pair": true, + "tag-self-close": false, + "spec-char-escape": false, + "id-unique": true, + "src-not-empty": true, + "title-require": false, + "alt-require": true, + "doctype-html5": true, + "id-class-value": false, + "style-disabled": false, + "inline-style-disabled": false, + "inline-script-disabled": false, + "space-tab-mixed-disabled": "space", + "id-class-ad-disabled": false, + "href-abs-or-rel": false, + "attr-unsafe-chars": true, + "head-script-disabled": false +} diff --git a/.github/linters/.yaml-lint.yml b/.github/linters/.yaml-lint.yml new file mode 100644 index 00000000..e8394fd5 --- /dev/null +++ b/.github/linters/.yaml-lint.yml @@ -0,0 +1,59 @@ +--- +########################################### +# These are the rules used for # +# linting all the yaml files in the stack # +# NOTE: # +# You can disable line with: # +# # yamllint disable-line # +########################################### +rules: + braces: + level: warning + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: 1 + max-spaces-inside-empty: 5 + brackets: + level: warning + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: 1 + max-spaces-inside-empty: 5 + colons: + level: warning + max-spaces-before: 0 + max-spaces-after: 1 + commas: + level: warning + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: disable + comments-indentation: disable + document-end: disable + document-start: + level: warning + present: true + empty-lines: + level: warning + max: 2 + max-start: 0 + max-end: 0 + hyphens: + level: warning + max-spaces-after: 1 + indentation: + level: warning + spaces: consistent + indent-sequences: true + check-multi-line-strings: false + key-duplicates: enable + line-length: + level: warning + max: 120 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: true + new-line-at-end-of-file: disable + new-lines: + type: unix + trailing-spaces: disable \ No newline at end of file diff --git a/.github/linters/sun_checks.xml b/.github/linters/sun_checks.xml new file mode 100644 index 00000000..76d0840d --- /dev/null +++ b/.github/linters/sun_checks.xml @@ -0,0 +1,374 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.github/scripts/authorize.sh b/.github/scripts/authorize.sh index 165b8c1e..a6503421 100755 --- a/.github/scripts/authorize.sh +++ b/.github/scripts/authorize.sh @@ -20,6 +20,7 @@ SCOPES=( "https://www.googleapis.com/auth/drive" "https://www.googleapis.com/auth/drive.activity" + "https://www.googleapis.com/auth/drive.appdata" "https://mail.google.com/" "https://www.googleapis.com/auth/classroom.courses" "https://www.googleapis.com/auth/classroom.announcements" @@ -41,7 +42,7 @@ fi printf -v EXPANDED_SCOPES '%s,' "${SCOPES[@]}" gcloud auth application-default login \ - --client-id-file=client_secret.json \ + --client-id-file="$CLIENT_ID_FILE" \ --scopes="${EXPANDED_SCOPES}" cat "${HOME}/.config/gcloud/application_default_credentials.json" \ No newline at end of file diff --git a/.github/snippet-bot.yml b/.github/snippet-bot.yml new file mode 100644 index 00000000..bb488a81 --- /dev/null +++ b/.github/snippet-bot.yml @@ -0,0 +1,14 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml new file mode 100644 index 00000000..7b363bc4 --- /dev/null +++ b/.github/sync-repo-settings.yaml @@ -0,0 +1,41 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# .github/sync-repo-settings.yaml +# See https://github.com/googleapis/repo-automation-bots/tree/main/packages/sync-repo-settings for app options. +rebaseMergeAllowed: true +squashMergeAllowed: true +mergeCommitAllowed: false +deleteBranchOnMerge: true +branchProtectionRules: + - pattern: main + isAdminEnforced: false + requiresStrictStatusChecks: false + requiredStatusCheckContexts: + # .github/workflows/test.yml with a job called "test" + - "test" + # .github/workflows/lint.yml with a job called "lint" + - "lint" + # Google bots below + - "cla/google" + - "snippet-bot check" + - "header-check" + - "conventionalcommits.org" + requiredApprovingReviewCount: 1 + requiresCodeOwnerReviews: true +permissionRules: + - team: workspace-devrel-dpe + permission: admin + - team: workspace-devrel + permission: push diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml new file mode 100644 index 00000000..95f323bf --- /dev/null +++ b/.github/workflows/automation.yml @@ -0,0 +1,69 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +--- +name: Automation +on: [ push, pull_request, workflow_dispatch ] +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request' }} + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GOOGLEWORKSPACE_BOT_TOKEN}} + steps: + - name: approve + run: gh pr review --approve "$PR_URL" + - name: merge + run: gh pr merge --auto --squash --delete-branch "$PR_URL" + default-branch-migration: + # this job helps with migrating the default branch to main + # it pushes main to master if master exists and main is the default branch + # it pushes master to main if master is the default branch + runs-on: ubuntu-latest + if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' }} + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + # required otherwise GitHub blocks infinite loops in pushes originating in an action + token: ${{ secrets.GOOGLEWORKSPACE_BOT_TOKEN }} + - name: Set env + run: | + # set DEFAULT BRANCH + echo "DEFAULT_BRANCH=$(gh repo view --json defaultBranchRef --jq '.defaultBranchRef.name')" >> "$GITHUB_ENV"; + + # set HAS_MASTER_BRANCH + if [ -n "$(git ls-remote --heads origin master)" ]; then + echo "HAS_MASTER_BRANCH=true" >> "$GITHUB_ENV" + else + echo "HAS_MASTER_BRANCH=false" >> "$GITHUB_ENV" + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: configure git + run: | + git config --global user.name 'googleworkspace-bot' + git config --global user.email 'googleworkspace-bot@google.com' + - if: ${{ env.DEFAULT_BRANCH == 'main' && env.HAS_MASTER_BRANCH == 'true' }} + name: Update master branch from main + run: | + git checkout -B master + git reset --hard origin/main + git push origin master + - if: ${{ env.DEFAULT_BRANCH == 'master'}} + name: Update main branch from master + run: | + git checkout -B main + git reset --hard origin/master + git push origin main diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yml similarity index 86% rename from .github/workflows/lint.yaml rename to .github/workflows/lint.yml index 7b80e396..3dcd20f7 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yml @@ -12,13 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. name: Lint -on: - push: - branches: - - master - pull_request: - branches: - - master +on: [push, pull_request, workflow_dispatch] + jobs: lint: concurrency: @@ -26,14 +21,15 @@ jobs: cancel-in-progress: true runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2.4.0 + - uses: actions/checkout@v3.0.0 with: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 - - uses: github/super-linter/slim@v4.8.7 + - uses: github/super-linter/slim@v4.9.0 env: ERROR_ON_MISSING_EXEC_BIT: true VALIDATE_JSCPD: false VALIDATE_PYTHON_BLACK: false + VALIDATE_PYTHON_FLAKE8: false VALIDATE_ALL_CODEBASE: ${{ github.event_name == 'push' }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yaml b/.github/workflows/test.yml similarity index 61% rename from .github/workflows/ci.yaml rename to .github/workflows/test.yml index 2eecea34..2258e628 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/test.yml @@ -1,29 +1,43 @@ -name: CI +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] +name: Test + +on: [push, pull_request, workflow_dispatch] jobs: - test: + matrix: + concurrency: + group: ${{ github.head_ref || github.ref }} + cancel-in-progress: true # Only run for internal PRs or after a merge if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }} runs-on: ubuntu-latest strategy: matrix: # TODO - expand matrix once stable - python-version: [3.6] + python-version: + - "3.10" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Fetch and Diff PR with base from which it was cloned if: ${{ github.event.pull_request.base.sha }} run: | git fetch origin master "${{ github.event.pull_request.base.sha }}" git diff --diff-filter=ACM --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.sha }}" > "${HOME}/changed_files.txt" - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -42,3 +56,8 @@ jobs: CLIENT_ID_FILE: ${{secrets.SNIPPETS_CLIENT_ID_FILE}} - name: Run tests run: ./.github/scripts/test.sh + test: + needs: [matrix] + runs-on: ubuntu-latest + steps: + - run: echo "Test matrix finished" diff --git a/.pylintrc b/.pylintrc index e67e73ca..ef66b689 100644 --- a/.pylintrc +++ b/.pylintrc @@ -1,35 +1,33 @@ -[MASTER] +# This Pylint rcfile contains a best-effort configuration to uphold the +# best-practices and style described in the Google Python style guide: +# https://google.github.io/styleguide/pyguide.html +# +# Its canonical open-source location is: +# https://google.github.io/styleguide/pylintrc -# Specify a configuration file. -#rcfile= +[MAIN] -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= +# Files or directories to be skipped. They should be base names, not paths. +ignore=third_party -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS +# Files or directories matching the regex patterns are skipped. The regex +# matches against base names, not paths. +ignore-patterns= # Pickle collected data for later comparisons. -persistent=yes +persistent=no # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. load-plugins= # Use multiple processes to speed up Pylint. -jobs=1 +jobs=4 # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - [MESSAGES CONTROL] @@ -39,7 +37,8 @@ confidence= # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option -# multiple time. See also the "--disable" option for examples. +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. #enable= # Disable the message, report, category or checker with the given id(s). You @@ -51,78 +50,86 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -# -# ----------------------------------------------------------------------- -# 2015-01-12 - What follows is the list of all disabled items necessary -# to get a clean run of lint across CourseBuilder. These are separated -# into three tiers: -# -# - Fix-worthy. This includes: -# - Probable bugs -# - Easily-addressed hygiene issues, -# - Real warnings which we may mark as suppressed on a case-by-case basis. -# - Items that are questionable practice, but not necessarily economical to fix. -# - Items that we intend to ignore, as we do not consider them bad practice. -# -# Warning messages are documented at http://docs.pylint.org/features.html -# -# ---------------------------------------------------------------------- -# Fix-worthy: -# -# ---- Possible bugs: -# disable=super-on-old-class -# disable=arguments-differ (# of arguments to overriding/overridden method) -# disable=signature-differs -# disable=method-hidden -# disable=abstract-method (Abstract method not overridden in derived class) -# disable=no-member (self.foo used when foo not declared in class) -# -# ---- Easy-to-fix and improves readability, cleanliness: -# disable=relative-import -# -# ---- Probably legitimate, but needs markup to indicate intentionality -# disable=no-init (Class does not have __init__, nor do ancestor classes) -# disable=import-error -# disable=attribute-defined-outside-init -# -# ---------------------------------------------------------------------- -# Fix when economical: -# -# ---- Minor code cleanliness problems; fix when encountered. -# disable=unused-argument -# disable=unused-variable -# disable=invalid-name (Variable name does not meet coding standard) -# disable=duplicate-code -# -# ---- Laundry list of tunable parameters for when things are too big/small -# disable=abstract-class-little-used -# disable=too-few-public-methods -# disable=too-many-instance-attributes -# disable=too-many-ancestors -# disable=too-many-return-statements -# disable=too-many-lines -# disable=too-many-locals -# disable=too-many-function-args -# disable=too-many-public-methods -# disable=too-many-arguments -# -# ---------------------------------------------------------------------- -# Ignored; OK by our coding standard: -# -# disable=bad-continuation (Bad whitespace on following line) -# disable=no-self-use (Member function never uses 'self' parameter) -# disable=missing-docstring -# disable=fixme -# disable=star-args -# disable=locally-disabled (Notes local suppression of warning) -# disable=locally-enabled (Notes re-enable of suppressed warning) -# disable=bad-option-value (Notes suppression of unknown warning) -# disable=abstract-class-not-used (Warns when not used in same file) -# -# Unfortunately, since the options parsing does not support multi-line entries -# nor line continuation, all of the above items are redundantly specified here -# in a way that pylint is willing to parse. -disable=super-on-old-class,arguments-differ,signature-differs,method-hidden,abstract-method,no-member,relative-import,no-init,import-error,attribute-defined-outside-init,abstract-class-not-used,unused-argument,unused-variable,invalid-name,duplicate-code,abstract-class-little-used,too-few-public-methods,too-many-instance-attributes,too-many-ancestors,too-many-return-statements,too-many-lines,too-many-locals,too-many-function-args,too-many-public-methods,too-many-arguments,bad-continuation,no-self-use,missing-docstring,fixme,star-args,locally-disabled,locally-enabled,bad-option-value +disable=R, + abstract-method, + apply-builtin, + arguments-differ, + attribute-defined-outside-init, + backtick, + bad-option-value, + basestring-builtin, + buffer-builtin, + c-extension-no-member, + consider-using-enumerate, + cmp-builtin, + cmp-method, + coerce-builtin, + coerce-method, + delslice-method, + div-method, + eq-without-hash, + execfile-builtin, + file-builtin, + filter-builtin-not-iterating, + fixme, + getslice-method, + global-statement, + hex-method, + idiv-method, + implicit-str-concat, + import-error, + import-self, + import-star-module-level, + input-builtin, + intern-builtin, + invalid-str-codec, + locally-disabled, + long-builtin, + long-suffix, + map-builtin-not-iterating, + misplaced-comparison-constant, + missing-function-docstring, + metaclass-assignment, + next-method-called, + next-method-defined, + no-absolute-import, + no-init, # added + no-member, + no-name-in-module, + no-self-use, + nonzero-method, + oct-method, + old-division, + old-ne-operator, + old-octal-literal, + old-raise-syntax, + parameter-unpacking, + print-statement, + raising-string, + range-builtin-not-iterating, + raw_input-builtin, + rdiv-method, + reduce-builtin, + relative-import, + reload-builtin, + round-builtin, + setslice-method, + signature-differs, + standarderror-builtin, + suppressed-message, + sys-max-int, + trailing-newlines, + unichr-builtin, + unicode-builtin, + unnecessary-pass, + unpacking-in-except, + useless-else-on-loop, + useless-suppression, + using-cmp-argument, + wrong-import-order, + xrange-builtin, + zip-builtin-not-iterating, + [REPORTS] @@ -131,11 +138,6 @@ disable=super-on-old-class,arguments-differ,signature-differs,method-hidden,abst # mypackage.mymodule.MyReporterClass. output-format=text -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - # Tells whether to display a full report or only the messages reports=no @@ -151,210 +153,203 @@ evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / stateme #msg-template= -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - +[BASIC] -[SIMILARITIES] +# Good variable names which should always be accepted, separated by a comma +good-names=main,_ -# Minimum lines number of a similarity. -min-similarity-lines=4 +# Bad variable names which should always be refused, separated by a comma +bad-names= -# Ignore comments when computing similarities. -ignore-comments=yes +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= -# Ignore docstrings when computing similarities. -ignore-docstrings=yes +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no -# Ignore imports when computing similarities. -ignore-imports=no +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty,cached_property.cached_property,cached_property.threaded_cached_property,cached_property.cached_property_with_ttl,cached_property.threaded_cached_property_with_ttl +# Regular expression matching correct function names +function-rgx=^(?:(?PsetUp|tearDown|setUpModule|tearDownModule)|(?P_?[A-Z][a-zA-Z0-9]*)|(?P_?[a-z][a-z0-9_]*))$ -[LOGGING] +# Regular expression matching correct variable names +variable-rgx=^[a-z][a-z0-9_]*$ -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging +# Regular expression matching correct constant names +const-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ +# Regular expression matching correct attribute names +attr-rgx=^_{0,2}[a-z][a-z0-9_]*$ -[MISCELLANEOUS] +# Regular expression matching correct argument names +argument-rgx=^[a-z][a-z0-9_]*$ -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO +# Regular expression matching correct class attribute names +class-attribute-rgx=^(_?[A-Z][A-Z0-9_]*|__[a-z0-9_]+__|_?[a-z][a-z0-9_]*)$ +# Regular expression matching correct inline iteration names +inlinevar-rgx=^[a-z][a-z0-9_]*$ -[VARIABLES] +# Regular expression matching correct class names +class-rgx=^_?[A-Z][a-zA-Z0-9]*$ -# Tells whether we should check for unused import in __init__ files. -init-import=yes +# Regular expression matching correct module names +module-rgx=^(_?[a-z][a-z0-9_]*|__init__)$ -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_$|dummy +# Regular expression matching correct method names +method-rgx=(?x)^(?:(?P_[a-z0-9_]+__|runTest|setUp|tearDown|setUpTestCase|tearDownTestCase|setupSelf|tearDownClass|setUpClass|(test|assert)_*[A-Z0-9][a-zA-Z0-9_]*|next)|(?P_{0,2}[A-Z][a-zA-Z0-9_]*)|(?P_{0,2}[a-z][a-z0-9_]*))$ -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=(__.*__|main|test.*|.*test|.*Test)$ -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=12 [TYPECHECK] -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager,contextlib2.contextmanager # List of module names for which member attributes should not be checked # (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. ignored-modules= -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). -ignored-classes=SQLObject +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local # List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E0201 when accessed. Python regular +# system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. -generated-members=REQUEST,acl_users,aq_parent - +generated-members= -[BASIC] -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,input +[FORMAT] -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,ex,Run,_ +# Maximum number of characters on a single line. +max-line-length=80 -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata +# TODO(https://github.com/pylint-dev/pylint/issues/3352): Direct pylint to exempt +# lines made too long by directives to pytype. -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=(?x)( + ^\s*(\#\ )??$| + ^\s*(from\s+\S+\s+)?import\s+.+$) -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=yes -# Regular expression matching correct function names -function-rgx=[a-z_][a-z0-9_]{2,50}$ +# Maximum number of lines in a module +max-module-lines=99999 -# Naming hint for function names -function-name-hint=[a-z_][a-z0-9_]{2,50}$ +# String used as indentation unit. The internal Google style guide mandates 2 +# spaces. Google's externaly-published style guide says 4, consistent with +# PEP 8. Here, we use 2 spaces, for conformity with many open-sourced Google +# projects (like TensorFlow). +indent-string=' ' -# Regular expression matching correct variable names -variable-rgx=[a-z_][a-z0-9_]{1,30}$ +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 -# Naming hint for variable names -variable-name-hint=[a-z_][a-z0-9_]{2,30}$ +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ +[MISCELLANEOUS] -# Regular expression matching correct attribute names -attr-rgx=[a-z_][a-z0-9_]{2,30}$ +# List of note tags to take in consideration, separated by a comma. +notes=TODO -# Naming hint for attribute names -attr-name-hint=[a-z_][a-z0-9_]{2,30}$ -# Regular expression matching correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ +[STRING] -# Naming hint for argument names -argument-name-hint=[a-z_][a-z0-9_]{2,30}$ +# This flag controls whether inconsistent-quotes generates a warning when the +# character used as a quote delimiter is used inconsistently within a module. +check-quote-consistency=yes -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ +[VARIABLES] -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ +# Tells whether we should check for unused import in __init__ files. +init-import=no -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=^\*{0,2}(_$|unused_|dummy_) -# Regular expression matching correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= -# Naming hint for class names -class-name-hint=[A-Z_][a-zA-Z0-9]+$ +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six,six.moves,past.builtins,future.builtins,functools -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ -# Regular expression matching correct method names -method-rgx=[a-z_][a-z0-9_]{2,30}$ +[LOGGING] -# Naming hint for method names -method-name-hint=[a-z_][a-z0-9_]{2,30}$ +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging,absl.logging,tensorflow.io.logging -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=__.*__ -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 +[SIMILARITIES] +# Minimum lines number of a similarity. +min-similarity-lines=4 -[FORMAT] +# Ignore comments when computing similarities. +ignore-comments=yes -# Maximum number of characters on a single line. -max-line-length=80 +# Ignore docstrings when computing similarities. +ignore-docstrings=yes -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ +# Ignore imports when computing similarities. +ignore-imports=no -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no -# List of optional constructs for which whitespace checking is disabled -no-space-check=trailing-comma,dict-separator +[SPELLING] -# Maximum number of lines in a module -max-module-lines=2000 +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' +# List of comma separated words that should not be checked. +spelling-ignore-words= -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no [IMPORTS] # Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,TERMIOS,Bastion,rexec +deprecated-modules=regsub, + TERMIOS, + Bastion, + rexec, + sets # Create a graph of every (i.e. internal and external) dependencies in the # given file (report RP0402 must not be disabled) @@ -368,64 +363,37 @@ ext-import-graph= # not be disabled) int-import-graph= +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= -[CLASSES] +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant, absl -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__, + __new__, + setUp # List of member names, which should be excluded from the protected access # warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=12 - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.* - -# Maximum number of locals for function / method body -max-locals=25 - -# Maximum number of return / yield for function / method body -max-returns=6 +exclude-protected=_asdict, + _fields, + _replace, + _source, + _make -# Maximum number of branch for function / method body -max-branches=40 - -# Maximum number of statements in function / method body -max-statements=105 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=50 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception - -# Python 2/3 compatibility -disable=useless-object-inheritance +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls, + class_ -redefining-builtins-modules=oauth2client # Allow oauth2client to redefine file +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..4a9deaa4 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "google-workspace.google-workspace-developer-tools" + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 808c811a..6c65498b 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,11 @@ Perform mail merges from plain text or Google Sheets data sources - [Quickstart](https://developers.google.com/drive/v3/web/quickstart/python) - [DriveApp](drive/driveapp): A simple app that uploads a file to Google Drive. +### Forms + +- [Quickstart](https://developers.google.com/forms/api/quickstart/python) +- [Snippets](https://developers.google.com/forms/api/guides) + ### Gmail - [Quickstart](https://developers.google.com/gmail/api/quickstart/python) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..07bc436f --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,6 @@ +# Report a security issue + +To report a security issue, please use [https://g.co/vulnz](https://g.co/vulnz). We use +[https://g.co/vulnz](https://g.co/vulnz) for our intake, and do coordination and disclosure here on +GitHub (including using GitHub Security Advisory). The Google Security Team will +respond within 5 working days of your report on [https://g.co/vulnz](https://g.co/vulnz). diff --git a/admin_sdk/directory/quickstart.py b/admin_sdk/directory/quickstart.py index 074a545b..f4ba960f 100644 --- a/admin_sdk/directory/quickstart.py +++ b/admin_sdk/directory/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START admin_sdk_directory_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -23,48 +21,51 @@ from googleapiclient.discovery import build # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/admin.directory.user'] +SCOPES = ["https://www.googleapis.com/auth/admin.directory.user"] def main(): - """Shows basic usage of the Admin SDK Directory API. - Prints the emails and names of the first 10 users in the domain. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Shows basic usage of the Admin SDK Directory API. + Prints the emails and names of the first 10 users in the domain. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - service = build('admin', 'directory_v1', credentials=creds) + service = build("admin", "directory_v1", credentials=creds) - # Call the Admin SDK Directory API - print('Getting the first 10 users in the domain') - results = service.users().list(customer='my_customer', maxResults=10, - orderBy='email').execute() - users = results.get('users', []) + # Call the Admin SDK Directory API + print("Getting the first 10 users in the domain") + results = ( + service.users() + .list(customer="my_customer", maxResults=10, orderBy="email") + .execute() + ) + users = results.get("users", []) - if not users: - print('No users in the domain.') - else: - print('Users:') - for user in users: - print(u'{0} ({1})'.format(user['primaryEmail'], - user['name']['fullName'])) + if not users: + print("No users in the domain.") + else: + print("Users:") + for user in users: + print(f"{user['primaryEmail']} ({user['name']['fullName']})") -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END admin_sdk_directory_quickstart] diff --git a/admin_sdk/reports/quickstart.py b/admin_sdk/reports/quickstart.py index d609abbd..cce9f415 100644 --- a/admin_sdk/reports/quickstart.py +++ b/admin_sdk/reports/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START admin_sdk_reports_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -23,48 +21,57 @@ from googleapiclient.discovery import build # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/admin.reports.audit.readonly'] +SCOPES = ["https://www.googleapis.com/auth/admin.reports.audit.readonly"] def main(): - """Shows basic usage of the Admin SDK Reports API. - Prints the time, email, and name of the last 10 login events in the domain. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Shows basic usage of the Admin SDK Reports API. + Prints the time, email, and name of the last 10 login events in the domain. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - service = build('admin', 'reports_v1', credentials=creds) + service = build("admin", "reports_v1", credentials=creds) - # Call the Admin SDK Reports API - print('Getting the last 10 login events') - results = service.activities().list(userKey='all', applicationName='login', - maxResults=10).execute() - activities = results.get('items', []) + # Call the Admin SDK Reports API + print("Getting the last 10 login events") + results = ( + service.activities() + .list(userKey="all", applicationName="login", maxResults=10) + .execute() + ) + activities = results.get("items", []) - if not activities: - print('No logins found.') - else: - print('Logins:') - for activity in activities: - print(u'{0}: {1} ({2})'.format(activity['id']['time'], - activity['actor']['email'], activity['events'][0]['name'])) + if not activities: + print("No logins found.") + else: + print("Logins:") + for activity in activities: + print( + "{0}: {1} ({2})".format( + activity["id"]["time"], + activity["actor"]["email"], + activity["events"][0]["name"], + ) + ) -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END admin_sdk_reports_quickstart] diff --git a/admin_sdk/reseller/quickstart.py b/admin_sdk/reseller/quickstart.py index 1e6133a3..cdcbd663 100644 --- a/admin_sdk/reseller/quickstart.py +++ b/admin_sdk/reseller/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START admin_sdk_reseller_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -23,46 +21,52 @@ from googleapiclient.discovery import build # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/apps.order'] +SCOPES = ["https://www.googleapis.com/auth/apps.order"] def main(): - """Calls the Admin SDK Reseller API. Prints the customer ID, SKU ID, - and plan name of the first 10 subscriptions managed by the domain. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Calls the Admin SDK Reseller API. Prints the customer ID, SKU ID, + and plan name of the first 10 subscriptions managed by the domain. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - service = build('reseller', 'v1', credentials=creds) + service = build("reseller", "v1", credentials=creds) - # Call the Admin SDK Reseller API - print('Getting the first 10 subscriptions') - results = service.subscriptions().list(maxResults=10).execute() - subscriptions = results.get('subscriptions', []) - if not subscriptions: - print('No subscriptions found.') - else: - print('Subscriptions:') - for subscription in subscriptions: - print(u'{0} ({1}, {2})'.format(subscription['customerId'], - subscription['skuId'], subscription['plan']['planName'])) + # Call the Admin SDK Reseller API + print("Getting the first 10 subscriptions") + results = service.subscriptions().list(maxResults=10).execute() + subscriptions = results.get("subscriptions", []) + if not subscriptions: + print("No subscriptions found.") + else: + print("Subscriptions:") + for subscription in subscriptions: + print( + "{0} ({1}, {2})".format( + subscription["customerId"], + subscription["skuId"], + subscription["plan"]["planName"], + ) + ) -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END admin_sdk_reseller_quickstart] diff --git a/apps_script/execute/execute.py b/apps_script/execute/execute.py index 9d5e7fd7..3a0eabf3 100644 --- a/apps_script/execute/execute.py +++ b/apps_script/execute/execute.py @@ -1,86 +1,72 @@ -# Copyright Google Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. +""" +Copyright Google Inc. -# [START apps_script_api_execute] -from __future__ import print_function +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at -from googleapiclient import errors -from googleapiclient.discovery import build -from oauth2client import client -from oauth2client import file as oauth_file -from oauth2client import tools + https://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" -def main(): - """Runs the sample. - """ - SCRIPT_ID = 'ENTER_YOUR_SCRIPT_ID_HERE' +# [START apps_script_api_execute] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError - # Set up the Apps Script API - SCOPES = [ - 'https://www.googleapis.com/auth/script.scriptapp', - 'https://www.googleapis.com/auth/drive.readonly', - ] - store = oauth_file.Storage('token.json') - creds = store.get() - if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('credentials.json', SCOPES) - creds = tools.run_flow(flow, store) - service = build('script', 'v1', credentials=creds) - # Create an execution request object. - request = {"function": "getFoldersUnderRoot"} +def main(): + """Runs the sample.""" + # pylint: disable=maybe-no-member + script_id = "1VFBDoJFy6yb9z7-luOwRv3fCmeNOzILPnR4QVmR0bGJ7gQ3QMPpCW-yt" - try: - # Make the API request. - response = service.scripts().run(body=request, - scriptId=SCRIPT_ID).execute() + creds, _ = google.auth.default() + service = build("script", "v1", credentials=creds) - if 'error' in response: - # The API executed, but the script returned an error. + # Create an execution request object. + request = {"function": "getFoldersUnderRoot"} - # Extract the first (and only) set of error details. The values of - # this object are the script's 'errorMessage' and 'errorType', and - # an list of stack trace elements. - error = response['error']['details'][0] - print("Script error message: {0}".format(error['errorMessage'])) + try: + # Make the API request. + response = service.scripts().run(scriptId=script_id, body=request).execute() + if "error" in response: + # The API executed, but the script returned an error. + # Extract the first (and only) set of error details. The values of + # this object are the script's 'errorMessage' and 'errorType', and + # a list of stack trace elements. + error = response["error"]["details"][0] + print(f"Script error message: {0}.{format(error['errorMessage'])}") - if 'scriptStackTraceElements' in error: - # There may not be a stacktrace if the script didn't start - # executing. - print("Script error stacktrace:") - for trace in error['scriptStackTraceElements']: - print("\t{0}: {1}".format(trace['function'], - trace['lineNumber'])) - else: - # The structure of the result depends upon what the Apps Script - # function returns. Here, the function returns an Apps Script Object - # with String keys and values, and so the result is treated as a - # Python dictionary (folderSet). - folderSet = response['response'].get('result', {}) - if not folderSet: - print('No folders returned!') - else: - print('Folders under your root folder:') - for (folderId, folder) in folderSet.items(): - print("\t{0} ({1})".format(folder, folderId)) + if "scriptStackTraceElements" in error: + # There may not be a stacktrace if the script didn't start + # executing. + print("Script error stacktrace:") + for trace in error["scriptStackTraceElements"]: + print(f"\t{0}: {1}.{format(trace['function'], trace['lineNumber'])}") + else: + # The structure of the result depends upon what the Apps Script + # function returns. Here, the function returns an Apps Script + # Object with String keys and values, and so the result is + # treated as a Python dictionary (folder_set). + folder_set = response["response"].get("result", {}) + if not folder_set: + print("No folders returned!") + else: + print("Folders under your root folder:") + for folder_id, folder in folder_set.items(): + print(f"\t{0} ({1}).{format(folder, folder_id)}") - except errors.HttpError as e: - # The API encountered a problem before the script started executing. - print(e.content) + except HttpError as error: + # The API encountered a problem before the script started executing. + print(f"An error occurred: {error}") + print(error.content) -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END apps_script_api_execute] diff --git a/apps_script/quickstart/quickstart.py b/apps_script/quickstart/quickstart.py index 0718b449..e55aabb4 100644 --- a/apps_script/quickstart/quickstart.py +++ b/apps_script/quickstart/quickstart.py @@ -18,8 +18,6 @@ Call the Apps Script API to create a new script project, upload a file to the project, and log the script's URL to the user. """ -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -29,72 +27,73 @@ from googleapiclient.discovery import build # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/script.projects'] +SCOPES = ["https://www.googleapis.com/auth/script.projects"] -SAMPLE_CODE = ''' +SAMPLE_CODE = """ function helloWorld() { console.log("Hello, world!"); } -'''.strip() +""".strip() -SAMPLE_MANIFEST = ''' +SAMPLE_MANIFEST = """ { "timeZone": "America/New_York", "exceptionLogging": "CLOUD" } -'''.strip() +""".strip() def main(): - """Calls the Apps Script API. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Calls the Apps Script API.""" + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - try: - service = build('script', 'v1', credentials=creds) + try: + service = build("script", "v1", credentials=creds) - # Call the Apps Script API - # Create a new project - request = {'title': 'My Script'} - response = service.projects().create(body=request).execute() + # Call the Apps Script API + # Create a new project + request = {"title": "My Script"} + response = service.projects().create(body=request).execute() - # Upload two files to the project - request = { - 'files': [{ - 'name': 'hello', - 'type': 'SERVER_JS', - 'source': SAMPLE_CODE - }, { - 'name': 'appsscript', - 'type': 'JSON', - 'source': SAMPLE_MANIFEST - }] - } - response = service.projects().updateContent( - body=request, - scriptId=response['scriptId']).execute() - print('https://script.google.com/d/' + response['scriptId'] + '/edit') - except errors.HttpError as error: - # The API encountered a problem. - print(error.content) + # Upload two files to the project + request = { + "files": [ + {"name": "hello", "type": "SERVER_JS", "source": SAMPLE_CODE}, + { + "name": "appsscript", + "type": "JSON", + "source": SAMPLE_MANIFEST, + }, + ] + } + response = ( + service.projects() + .updateContent(body=request, scriptId=response["scriptId"]) + .execute() + ) + print("https://script.google.com/d/" + response["scriptId"] + "/edit") + except errors.HttpError as error: + # The API encountered a problem. + print(error.content) -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END apps_script_api_quickstart] diff --git a/calendar/quickstart/quickstart.py b/calendar/quickstart/quickstart.py index d3d827cf..18b94995 100644 --- a/calendar/quickstart/quickstart.py +++ b/calendar/quickstart/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START calendar_quickstart] -from __future__ import print_function - import datetime import os.path @@ -25,55 +23,64 @@ from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'] +SCOPES = ["https://www.googleapis.com/auth/calendar.readonly"] def main(): - """Shows basic usage of the Google Calendar API. - Prints the start and name of the next 10 events on the user's calendar. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Shows basic usage of the Google Calendar API. + Prints the start and name of the next 10 events on the user's calendar. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - try: - service = build('calendar', 'v3', credentials=creds) + try: + service = build("calendar", "v3", credentials=creds) - # Call the Calendar API - now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time - print('Getting the upcoming 10 events') - events_result = service.events().list(calendarId='primary', timeMin=now, - maxResults=10, singleEvents=True, - orderBy='startTime').execute() - events = events_result.get('items', []) + # Call the Calendar API + now = datetime.datetime.now(tz=datetime.timezone.utc).isoformat() + print("Getting the upcoming 10 events") + events_result = ( + service.events() + .list( + calendarId="primary", + timeMin=now, + maxResults=10, + singleEvents=True, + orderBy="startTime", + ) + .execute() + ) + events = events_result.get("items", []) - if not events: - print('No upcoming events found.') - return + if not events: + print("No upcoming events found.") + return - # Prints the start and name of the next 10 events - for event in events: - start = event['start'].get('dateTime', event['start'].get('date')) - print(start, event['summary']) + # Prints the start and name of the next 10 events + for event in events: + start = event["start"].get("dateTime", event["start"].get("date")) + print(start, event["summary"]) - except HttpError as error: - print('An error occurred: %s' % error) + except HttpError as error: + print(f"An error occurred: {error}") -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END calendar_quickstart] diff --git a/chat/client-libraries/cloud/README.md b/chat/client-libraries/cloud/README.md new file mode 100644 index 00000000..9fbdcfdf --- /dev/null +++ b/chat/client-libraries/cloud/README.md @@ -0,0 +1,21 @@ +# Google Chat API - Cloud Client library samples + +## Set up + +1. Add `service_account.json` and/or `client_secrets.json` to the current + folder depending on the credentials used by the samples to run: + + 1. `service_account.json` for + [app credentials](https://developers.google.com/workspace/chat/authenticate-authorize-chat-app) + + 1. `client_secrets.json` for + [user credentials](https://developers.google.com/workspace/chat/authenticate-authorize-chat-user) + +1. Execute `pip install -r requirements.txt` + +## Run + +Execute `python replace-with-the-sample-file.py` wih the sample file path of the sample. + +For example, to run the sample `create-message-app-cred`, you should run +`python create-message-app-cred.py`. diff --git a/chat/client-libraries/cloud/authentication_utils.py b/chat/client-libraries/cloud/authentication_utils.py new file mode 100644 index 00000000..d35ef798 --- /dev/null +++ b/chat/client-libraries/cloud/authentication_utils.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_authentication_utils] +import json + +import google.oauth2.credentials + +from google_auth_oauthlib.flow import InstalledAppFlow +from google.apps import chat_v1 as google_chat + +CLIENT_SECRETS_FILE = 'client_secrets.json' + +SERVICE_ACCOUNT_FILE = 'service_account.json' + +APP_AUTH_OAUTH_SCOPE = ["https://www.googleapis.com/auth/chat.bot"] + +def create_client_with_app_credentials(): + # For more information on app authentication, see + # https://developers.google.com/workspace/chat/authenticate-authorize-chat-app + creds = google.oauth2.service_account.Credentials.from_service_account_file( + SERVICE_ACCOUNT_FILE) + + return google_chat.ChatServiceClient( + credentials = creds, + client_options={ + "scopes": APP_AUTH_OAUTH_SCOPE + }) + +def create_client_with_user_credentials(scopes): + # For more information on user authentication, see + # https://developers.google.com/workspace/chat/authenticate-authorize-chat-user + flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, scopes) + cred = flow.run_local_server() + installed = json.load(open(CLIENT_SECRETS_FILE))["installed"] + + creds = google.oauth2.credentials.Credentials( + token = cred.token, + refresh_token = cred.refresh_token, + token_uri = installed["token_uri"], + client_id = installed["client_id"], + client_secret = installed["client_secret"], + scopes = scopes + ) + + # Create a client + client = google_chat.ChatServiceClient( + credentials = creds, + client_options = { + "scopes" : scopes + } + ) + + return client + +# [END chat_authentication_utils] diff --git a/chat/client-libraries/cloud/create_membership_user_cred.py b/chat/client-libraries/cloud/create_membership_user_cred.py new file mode 100644 index 00000000..fec4fc90 --- /dev/null +++ b/chat/client-libraries/cloud/create_membership_user_cred.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + + +# [START chat_create_membership_user_cred] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.memberships"] + +# This sample shows how to create membership with user credential for a human +# user +def create_membership_with_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.CreateMembershipRequest( + # Replace SPACE_NAME here + parent = "spaces/SPACE_NAME", + membership = { + "member": { + # Replace USER_NAME here + "name": "users/USER_NAME", + # user type for the membership + "type_": "HUMAN" + } + } + ) + + # Make the request + response = client.create_membership(request) + + # Handle the response + print(response) + +create_membership_with_user_cred() + +# [END chat_create_membership_user_cred] diff --git a/chat/client-libraries/cloud/create_membership_user_cred_for_app.py b/chat/client-libraries/cloud/create_membership_user_cred_for_app.py new file mode 100644 index 00000000..57840f5c --- /dev/null +++ b/chat/client-libraries/cloud/create_membership_user_cred_for_app.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_create_membership_user_cred_for_app] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.memberships.app"] + +# This sample shows how to create membership with app credential for an app +def create_membership_with_user_cred_for_app(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.CreateMembershipRequest( + # Replace SPACE_NAME here + parent = "spaces/SPACE_NAME", + membership = { + "member": { + # member name for app membership, do not change this. + "name": "users/app", + # user type for the membership + "type_": "BOT" + } + } + ) + + # Make the request + response = client.create_membership(request) + + # Handle the response + print(response) + +create_membership_with_user_cred_for_app() + +# [END chat_create_membership_user_cred_for_app] diff --git a/chat/client-libraries/cloud/create_membership_user_cred_for_group.py b/chat/client-libraries/cloud/create_membership_user_cred_for_group.py new file mode 100644 index 00000000..fe90c4d3 --- /dev/null +++ b/chat/client-libraries/cloud/create_membership_user_cred_for_group.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + + +# [START chat_create_membership_user_cred_for_group] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.memberships"] + +# This sample shows how to create membership with user credential for a group +def create_membership_with_user_cred_for_group(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.CreateMembershipRequest( + # Replace SPACE_NAME here + parent = "spaces/SPACE_NAME", + membership = { + "groupMember": { + # Replace GROUP_NAME here + "name": "groups/GROUP_NAME" + } + } + ) + + # Make the request + response = client.create_membership(request) + + # Handle the response + print(response) + +create_membership_with_user_cred_for_group() + +# [END chat_create_membership_user_cred_for_group] diff --git a/chat/client-libraries/cloud/create_message_app_cred.py b/chat/client-libraries/cloud/create_message_app_cred.py new file mode 100644 index 00000000..05db8d04 --- /dev/null +++ b/chat/client-libraries/cloud/create_message_app_cred.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_create_message_app_cred] +from authentication_utils import create_client_with_app_credentials +from google.apps import chat_v1 as google_chat + +# This sample shows how to create message with app credential +def create_message_with_app_cred(): + # Create a client + client = create_client_with_app_credentials() + + # Initialize request argument(s) + request = google_chat.CreateMessageRequest( + # Replace SPACE_NAME here. + parent = "spaces/SPACE_NAME", + message = { + "text": '👋🌎 Hello world! I created this message by calling ' + + 'the Chat API\'s `messages.create()` method.', + "cards_v2" : [{ "card": { + "header": { + "title": 'About this message', + "image_url": 'https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/info/default/24px.svg' + }, + "sections": [{ + "header": "Contents", + "widgets": [{ "text_paragraph": { + "text": '🔡 Text which can include ' + + 'hyperlinks 🔗, emojis 😄🎉, and @mentions 🗣️.' + }}, { "text_paragraph": { + "text": '🖼️ A card to display visual elements' + + 'and request information such as text 🔤, ' + + 'dates and times 📅, and selections ☑️.' + }}, { "text_paragraph": { + "text": '👉🔘 An accessory widget which adds ' + + 'a button to the bottom of a message.' + }} + ]}, { + "header": "What's next", + "collapsible": True, + "widgets": [{ "text_paragraph": { + "text": "❤️ Add a reaction." + }}, { "text_paragraph": { + "text": "🔄 Update " + + "or ❌ delete " + + "the message." + } + }] + } + ] + }}], + "accessory_widgets": [{ "button_list": { "buttons": [{ + "text": 'View documentation', + "icon": { "material_icon": { "name": 'link' }}, + "on_click": { "open_link": { + "url": 'https://developers.google.com/workspace/chat/create-messages' + }} + }]}}] + } + ) + + # Make the request + response = client.create_message(request) + + # Handle the response + print(response) + +create_message_with_app_cred() + +# [END chat_create_message_app_cred] diff --git a/chat/client-libraries/cloud/create_message_user_cred.py b/chat/client-libraries/cloud/create_message_user_cred.py new file mode 100644 index 00000000..a697b97e --- /dev/null +++ b/chat/client-libraries/cloud/create_message_user_cred.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_create_message_user_cred] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.messages.create"] + +def create_message_with_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.CreateMessageRequest( + # Replace SPACE_NAME here. + parent = "spaces/SPACE_NAME", + message = { + "text": '👋🌎 Hello world!' + + 'Text messages can contain things like:\n\n' + + '* Hyperlinks 🔗\n' + + '* Emojis 😄🎉\n' + + '* Mentions of other Chat users `@` \n\n' + + 'For details, see the ' + + '.' + } + ) + + # Make the request + response = client.create_message(request) + + # Handle the response + print(response) + +create_message_with_user_cred() + +# [END chat_create_message_user_cred] diff --git a/chat/client-libraries/cloud/create_message_user_cred_at_mention.py b/chat/client-libraries/cloud/create_message_user_cred_at_mention.py new file mode 100644 index 00000000..6dde1344 --- /dev/null +++ b/chat/client-libraries/cloud/create_message_user_cred_at_mention.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + + +# [START chat_create_message_user_cred_at_mention] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.messages.create"] + +# This sample shows how to create message with user credential with at mention +def create_message_with_user_cred_at_mention(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.CreateMessageRequest( + # replace SPACE_NAME here + parent = "spaces/SPACE_NAME", + message = { + # The user with USER_NAME will be at mentioned if they are in the + # space. + # Replace USER_NAME here + "text": "Hello !" + } + ) + + # Make the request + response = client.create_message(request) + + # Handle the response + print(response) + +create_message_with_user_cred_at_mention() + +# [END chat_create_message_user_cred_at_mention] diff --git a/chat/client-libraries/cloud/create_message_user_cred_message_id.py b/chat/client-libraries/cloud/create_message_user_cred_message_id.py new file mode 100644 index 00000000..6e52cde5 --- /dev/null +++ b/chat/client-libraries/cloud/create_message_user_cred_message_id.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + + +# [START chat_create_message_user_cred_message_id] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.messages.create"] + +# This sample shows how to create message with user credential with message id +def create_message_with_user_cred_message_id(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.CreateMessageRequest( + # Replace SPACE_NAME here + parent = "spaces/SPACE_NAME", + # Message id let chat apps get, update or delete a message without needing + # to store the system assigned ID in the message's resource name. + message_id = "client-MESSAGE-ID", + message = { + "text": "Hello with user credential!" + } + ) + + # Make the request + response = client.create_message(request) + + # Handle the response + print(response) + +create_message_with_user_cred_message_id() + +# [END chat_create_message_user_cred_message_id] diff --git a/chat/client-libraries/cloud/create_message_user_cred_request_id.py b/chat/client-libraries/cloud/create_message_user_cred_request_id.py new file mode 100644 index 00000000..58c67b25 --- /dev/null +++ b/chat/client-libraries/cloud/create_message_user_cred_request_id.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + + +# [START chat_create_message_user_cred_request_id] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.messages.create"] + +# This sample shows how to create message with user credential with request id +def create_message_with_user_cred_request_id(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.CreateMessageRequest( + # Replace SPACE_NAME here + parent = "spaces/SPACE_NAME", + # Specifying an existing request ID returns the message created with + # that ID instead of creating a new message. + request_id = "REQUEST_ID", + message = { + "text": "Hello with user credential!" + } + ) + + # Make the request + response = client.create_message(request) + + # Handle the response + print(response) + +create_message_with_user_cred_request_id() + +# [END chat_create_message_user_cred_request_id] diff --git a/chat/client-libraries/cloud/create_message_user_cred_thread_key.py b/chat/client-libraries/cloud/create_message_user_cred_thread_key.py new file mode 100644 index 00000000..75cda5f1 --- /dev/null +++ b/chat/client-libraries/cloud/create_message_user_cred_thread_key.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_create_message_user_cred_thread_key] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +import google.apps.chat_v1.CreateMessageRequest.MessageReplyOption + +SCOPES = ["https://www.googleapis.com/auth/chat.messages.create"] + +# This sample shows how to create message with user credential with thread key +def create_message_with_user_cred_thread_key(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.CreateMessageRequest( + # Replace SPACE_NAME here + parent = "spaces/SPACE_NAME", + # Creates the message as a reply to the thread specified by thread_key. + # If it fails, the message starts a new thread instead. + message_reply_option = MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD, + message = { + "text": "Hello with user credential!", + "thread": { + # Thread key specifies a thread and is unique to the chat app + # that sets it. + "thread_key": "THREAD_KEY" + } + } + ) + + # Make the request + response = client.create_message(request) + + # Handle the response + print(response) + +create_message_with_user_cred_thread_key() + +# [END chat_create_message_user_cred_thread_key] diff --git a/chat/client-libraries/cloud/create_message_user_cred_thread_name.py b/chat/client-libraries/cloud/create_message_user_cred_thread_name.py new file mode 100644 index 00000000..c608261e --- /dev/null +++ b/chat/client-libraries/cloud/create_message_user_cred_thread_name.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + + +# [START chat_create_message_user_cred_thread_name] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +import google.apps.chat_v1.CreateMessageRequest.MessageReplyOption + +SCOPES = ["https://www.googleapis.com/auth/chat.messages.create"] + +# This sample shows how to create message with user credential with thread name +def create_message_with_user_cred_thread_name(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.CreateMessageRequest( + # Replace SPACE_NAME here + parent = "spaces/SPACE_NAME", + # Creates the message as a reply to the thread specified by thread.name. + # If it fails, the message starts a new thread instead. + message_reply_option = MessageReplyOption.REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD, + message = { + "text": "Hello with user credential!", + "thread": { + # Resource name of a thread that uniquely identify a thread. + # Replace SPACE_NAME and THREAD_NAME here + "name": "spaces/SPACE_NAME/threads/THREAD_NAME" + } + } + ) + + # Make the request + response = client.create_message(request) + + # Handle the response + print(response) + +create_message_with_user_cred_thread_name() + +# [END chat_create_message_user_cred_thread_name] diff --git a/chat/client-libraries/cloud/create_space_user_cred.py b/chat/client-libraries/cloud/create_space_user_cred.py new file mode 100644 index 00000000..7ff01d45 --- /dev/null +++ b/chat/client-libraries/cloud/create_space_user_cred.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_create_space_user_cred] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.spaces.create"] + +def create_space_with_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.CreateSpaceRequest( + space = { + "space_type": 'SPACE', + # Replace DISPLAY_NAME here. + "display_name": 'DISPLAY_NAME' + } + ) + + # Make the request + response = client.create_space(request) + + # Handle the response + print(response) + +create_space_with_user_cred() + +# [END chat_create_space_user_cred] diff --git a/chat/client-libraries/cloud/delete_message_app_cred.py b/chat/client-libraries/cloud/delete_message_app_cred.py new file mode 100644 index 00000000..a4706255 --- /dev/null +++ b/chat/client-libraries/cloud/delete_message_app_cred.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_delete_message_app_cred] +from authentication_utils import create_client_with_app_credentials +from google.apps import chat_v1 as google_chat + +# This sample shows how to delete a message with app credential +def delete_message_with_app_cred(): + # Create a client + client = create_client_with_app_credentials() + + # Initialize request argument(s) + request = google_chat.DeleteMessageRequest( + # Replace SPACE_NAME and MESSAGE_NAME here + name = "spaces/SPACE_NAME/messages/MESSAGE_NAME", + ) + + # Make the request + response = client.delete_message(request) + + # Handle the response + print(response) + +delete_message_with_app_cred() + +# [END chat_delete_message_app_cred] diff --git a/chat/client-libraries/cloud/delete_message_user_cred.py b/chat/client-libraries/cloud/delete_message_user_cred.py new file mode 100644 index 00000000..d8a046a2 --- /dev/null +++ b/chat/client-libraries/cloud/delete_message_user_cred.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_delete_message_user_cred] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.messages"] + +# This sample shows how to delete a message with user credential +def delete_message_with_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.DeleteMessageRequest( + # Replace SPACE_NAME and MESSAGE_NAME here + name = "spaces/SPACE_NAME/messages/MESSAGE_NAME", + ) + + # Make the request + response = client.delete_message(request) + + # Handle the response + print(response) + +delete_message_with_user_cred() + +# [END chat_delete_message_user_cred] diff --git a/chat/client-libraries/cloud/get_membership_app_cred.py b/chat/client-libraries/cloud/get_membership_app_cred.py new file mode 100644 index 00000000..12325a1d --- /dev/null +++ b/chat/client-libraries/cloud/get_membership_app_cred.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_get_membership_app_cred] +from authentication_utils import create_client_with_app_credentials +from google.apps import chat_v1 as google_chat + +# This sample shows how to get membership with app credential +def get_membership_with_app_cred(): + # Create a client + client = create_client_with_app_credentials() + + # Initialize request argument(s) + request = google_chat.GetMembershipRequest( + # Replace SPACE_NAME and MEMBER_NAME here + name = 'spaces/SPACE_NAME/members/MEMBER_NAME', + ) + + # Make the request + response = client.get_membership(request) + + # Handle the response + print(response) + +get_membership_with_app_cred() + +# [END chat_get_membership_app_cred] diff --git a/chat/client-libraries/cloud/get_membership_user_cred.py b/chat/client-libraries/cloud/get_membership_user_cred.py new file mode 100644 index 00000000..bab553a4 --- /dev/null +++ b/chat/client-libraries/cloud/get_membership_user_cred.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_get_membership_user_cred] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.memberships.readonly"] + +# This sample shows how to get membership with user credential +def get_membership_with_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.GetMembershipRequest( + # Replace SPACE_NAME and MEMBER_NAME here + name = 'spaces/SPACE_NAME/members/MEMBER_NAME', + ) + + # Make the request + response = client.get_membership(request) + + # Handle the response + print(response) + +get_membership_with_user_cred() + +# [END chat_get_membership_user_cred] diff --git a/chat/client-libraries/cloud/get_message_app_cred.py b/chat/client-libraries/cloud/get_message_app_cred.py new file mode 100644 index 00000000..ca8e7888 --- /dev/null +++ b/chat/client-libraries/cloud/get_message_app_cred.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_get_message_app_cred] +from authentication_utils import create_client_with_app_credentials +from google.apps import chat_v1 as google_chat + +# This sample shows how to get message with app credential +def get_message_with_app_cred(): + # Create a client + client = create_client_with_app_credentials() + + # Initialize request argument(s) + request = google_chat.GetMessageRequest( + # Replace SPACE_NAME and MESSAGE_NAME here + name = 'spaces/SPACE_NAME/messages/MESSAGE_NAME', + ) + + # Make the request + response = client.get_message(request=request) + + # Handle the response + print(response) + +get_message_with_app_cred() + +# [END chat_get_message_app_cred] diff --git a/chat/client-libraries/cloud/get_message_user_cred.py b/chat/client-libraries/cloud/get_message_user_cred.py new file mode 100644 index 00000000..8f160b35 --- /dev/null +++ b/chat/client-libraries/cloud/get_message_user_cred.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_get_message_user_cred] +from authentication_utils import create_client_with_user_credentials +import google.oauth2.credentials + +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.messages.readonly"] + +# This sample shows how to get message with user credential +def get_message_with_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.GetMessageRequest( + # Replace SPACE_NAME and MESSAGE_NAME here + name = "spaces/SPACE_NAME/messages/MESSAGE_NAME", + ) + + # Make the request + response = client.get_message(request) + + # Handle the response + print(response) + +get_message_with_user_cred() + +# [END chat_get_message_user_cred] diff --git a/chat/client-libraries/cloud/get_space_app_cred.py b/chat/client-libraries/cloud/get_space_app_cred.py new file mode 100644 index 00000000..1f5bb341 --- /dev/null +++ b/chat/client-libraries/cloud/get_space_app_cred.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_get_space_app_cred] +from authentication_utils import create_client_with_app_credentials +from google.apps import chat_v1 as google_chat + +# This sample shows how to get space with app credential +def get_space_with_app_cred(): + # Create a client + client = create_client_with_app_credentials() + + # Initialize request argument(s) + request = google_chat.GetSpaceRequest( + # Replace SPACE_NAME here + name = "spaces/SPACE_NAME", + ) + + # Make the request + response = client.get_space(request) + + # Handle the response + print(response) + +get_space_with_app_cred() + +# [END chat_get_space_app_cred] diff --git a/chat/client-libraries/cloud/get_space_user_cred.py b/chat/client-libraries/cloud/get_space_user_cred.py new file mode 100644 index 00000000..3287762d --- /dev/null +++ b/chat/client-libraries/cloud/get_space_user_cred.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Snippet for GetSpace + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_get_space_user_cred] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.spaces.readonly"] + +# This sample shows how to get space with user credential +def get_space_with_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.GetSpaceRequest( + # Replace SPACE_NAME here + name = "spaces/SPACE_NAME", + ) + + # Make the request + response = client.get_space(request) + + # Handle the response + print(response) + +get_space_with_user_cred() + +# [END chat_get_space_user_cred] diff --git a/chat/client-libraries/cloud/list_memberships_app_cred.py b/chat/client-libraries/cloud/list_memberships_app_cred.py new file mode 100644 index 00000000..6ebe1eb3 --- /dev/null +++ b/chat/client-libraries/cloud/list_memberships_app_cred.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_list_memberships_app_cred] +from authentication_utils import create_client_with_app_credentials +from google.apps import chat_v1 as google_chat + +# This sample shows how to list memberships with app credential +def list_memberships_app_cred(): + # Create a client + client = create_client_with_app_credentials() + + # Initialize request argument(s) + request = google_chat.ListMembershipsRequest( + # Replace SPACE_NAME here + parent = 'spaces/SPACE_NAME', + # Filter membership by type (HUMAN or BOT) or role (ROLE_MEMBER or + # ROLE_MANAGER) + filter = 'member.type = "HUMAN"', + # Number of results that will be returned at once + page_size = 100 + ) + + # Make the request + page_result = client.list_memberships(request) + + # Handle the response. Iterating over page_result will yield results and + # resolve additional pages automatically. + for response in page_result: + print(response) + +list_memberships_app_cred() + +# [END chat_list_memberships_app_cred] diff --git a/chat/client-libraries/cloud/list_memberships_user_cred.py b/chat/client-libraries/cloud/list_memberships_user_cred.py new file mode 100644 index 00000000..4d50c959 --- /dev/null +++ b/chat/client-libraries/cloud/list_memberships_user_cred.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_list_memberships_user_cred] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.memberships.readonly"] + +# This sample shows how to list memberships with user credential +def list_memberships_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.ListMembershipsRequest( + # Replace SPACE_NAME here + parent = 'spaces/SPACE_NAME', + # Filter membership by type (HUMAN or BOT) or role (ROLE_MEMBER or + # ROLE_MANAGER) + filter = 'member.type = "HUMAN"', + # Number of results that will be returned at once + page_size = 100 + ) + + # Make the request + page_result = client.list_memberships(request) + + # Handle the response. Iterating over page_result will yield results and + # resolve additional pages automatically. + for response in page_result: + print(response) + +list_memberships_user_cred() + +# [END chat_list_memberships_user_cred] diff --git a/chat/client-libraries/cloud/list_messages_user_cred.py b/chat/client-libraries/cloud/list_messages_user_cred.py new file mode 100644 index 00000000..cacef343 --- /dev/null +++ b/chat/client-libraries/cloud/list_messages_user_cred.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_list_messages_user_cred] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.messages.readonly"] + +# This sample shows how to list messages with user credential +def list_messages_with_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.ListMessagesRequest( + # Replace SPACE_NAME here + parent = 'spaces/SPACE_NAME', + # Number of results that will be returned at once + page_size = 100 + ) + + # Make the request + page_result = client.list_messages(request) + + # Handle the response. Iterating over page_result will yield results and + # resolve additional pages automatically. + for response in page_result: + print(response) + +list_messages_with_user_cred() + +# [END chat_list_messages_user_cred] diff --git a/chat/client-libraries/cloud/list_spaces_app_cred.py b/chat/client-libraries/cloud/list_spaces_app_cred.py new file mode 100644 index 00000000..0c41ae20 --- /dev/null +++ b/chat/client-libraries/cloud/list_spaces_app_cred.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_list_spaces_app_cred] +from authentication_utils import create_client_with_app_credentials +from google.apps import chat_v1 as google_chat + +# This sample shows how to list spaces with app credential +def list_spaces_app_cred(): + # Create a client + client = create_client_with_app_credentials() + + # Initialize request argument(s) + request = google_chat.ListSpacesRequest( + # Filter spaces by space type (SPACE or GROUP_CHAT or DIRECT_MESSAGE) + filter = 'space_type = "SPACE"', + # Number of results that will be returned at once + page_size = 100 + ) + + # Make the request + page_result = client.list_spaces(request) + + # Handle the response. Iterating over page_result will yield results and + # resolve additional pages automatically. + for response in page_result: + print(response) + +list_spaces_app_cred() + +# [END chat_list_spaces_app_cred] diff --git a/chat/client-libraries/cloud/list_spaces_user_cred.py b/chat/client-libraries/cloud/list_spaces_user_cred.py new file mode 100644 index 00000000..5ae906cf --- /dev/null +++ b/chat/client-libraries/cloud/list_spaces_user_cred.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_list_spaces_user_cred] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.spaces.readonly"] + +# This sample shows how to list spaces with user credential +def list_spaces_with_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.ListSpacesRequest( + # Filter spaces by space type (SPACE or GROUP_CHAT or DIRECT_MESSAGE) + filter = 'space_type = "SPACE"', + # Number of results that will be returned at once + page_size = 100 + ) + + # Make the request + page_result = client.list_spaces(request) + + # Handle the response. Iterating over page_result will yield results and + # resolve additional pages automatically. + for response in page_result: + print(response) + +list_spaces_with_user_cred() + +# [END chat_list_spaces_user_cred] diff --git a/chat/client-libraries/cloud/requirements.txt b/chat/client-libraries/cloud/requirements.txt new file mode 100644 index 00000000..bf63a182 --- /dev/null +++ b/chat/client-libraries/cloud/requirements.txt @@ -0,0 +1,3 @@ +google_auth_oauthlib==1.2.0 +protobuf==4.21.12 +google-apps-chat==0.1.9 diff --git a/chat/client-libraries/cloud/set_up_space_user_cred.py b/chat/client-libraries/cloud/set_up_space_user_cred.py new file mode 100644 index 00000000..da20fc80 --- /dev/null +++ b/chat/client-libraries/cloud/set_up_space_user_cred.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_set_up_space_user_cred] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.spaces.create"] + +def set_up_space_with_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.SetUpSpaceRequest( + space = { + "space_type": 'SPACE', + # Replace DISPLAY_NAME here. + "display_name": 'DISPLAY_NAME' + }, + memberships = [{ + "member": { + # Replace USER_NAME here. + "name": 'users/USER_NAME', + "type_": 'HUMAN' + } + }] + ) + + # Make the request + response = client.set_up_space(request) + + # Handle the response + print(response) + +set_up_space_with_user_cred() + +# [END chat_set_up_space_user_cred] diff --git a/chat/client-libraries/cloud/update_message_app_cred.py b/chat/client-libraries/cloud/update_message_app_cred.py new file mode 100644 index 00000000..40dda760 --- /dev/null +++ b/chat/client-libraries/cloud/update_message_app_cred.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + +# [START chat_update_message_app_cred] +from authentication_utils import create_client_with_app_credentials +from google.apps import chat_v1 as google_chat + +# This sample shows how to update a message with app credential +def update_message_with_app_cred(): + # Create a client + client = create_client_with_app_credentials() + + # Initialize request argument(s) + request = google_chat.UpdateMessageRequest( + message = { + # Replace SPACE_NAME and MESSAGE_NAME here + "name": "spaces/SPACE_NAME/messages/MESSAGE_NAME", + "text": "Text updated with app credential!", + "cards_v2" : [{ "card": { "header": { + "title": 'Card updated with app credential!', + "image_url": 'https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/info/default/24px.svg' + }}}] + }, + # The field paths to update. Separate multiple values with commas or use + # `*` to update all field paths. + update_mask = "text,cardsV2" + ) + + # Make the request + response = client.update_message(request) + + # Handle the response + print(response) + +update_message_with_app_cred() + +# [END chat_update_message_app_cred] diff --git a/chat/client-libraries/cloud/update_message_user_cred.py b/chat/client-libraries/cloud/update_message_user_cred.py new file mode 100644 index 00000000..18146e3d --- /dev/null +++ b/chat/client-libraries/cloud/update_message_user_cred.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + + +# [START chat_update_message_user_cred] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.messages"] + +# This sample shows how to update a message with user credential +def update_message_with_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.UpdateMessageRequest( + message = { + # Replace SPACE_NAME and MESSAGE_NAME here + "name": "spaces/SPACE_NAME/messages/MESSAGE_NAME", + "text": "Updated with user credential!" + }, + # The field paths to update. Separate multiple values with commas or use + # `*` to update all field paths. + update_mask = "text" + ) + + # Make the request + response = client.update_message(request) + + # Handle the response + print(response) + +update_message_with_user_cred() + +# [END chat_update_message_user_cred] diff --git a/chat/client-libraries/cloud/update_space_user_cred.py b/chat/client-libraries/cloud/update_space_user_cred.py new file mode 100644 index 00000000..6c3540a6 --- /dev/null +++ b/chat/client-libraries/cloud/update_space_user_cred.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-apps-chat + + +# [START chat_update_space_user_cred] +from authentication_utils import create_client_with_user_credentials +from google.apps import chat_v1 as google_chat + +SCOPES = ["https://www.googleapis.com/auth/chat.spaces"] + +# This sample shows how to update a space with user credential +def update_space_with_user_cred(): + # Create a client + client = create_client_with_user_credentials(SCOPES) + + # Initialize request argument(s) + request = google_chat.UpdateSpaceRequest( + space = { + # Replace SPACE_NAME here + 'name': 'spaces/SPACE_NAME', + 'display_name': 'New space display name' + }, + # The field paths to update. Separate multiple values with commas. + update_mask = 'displayName' + ) + + # Make the request + response = client.update_space(request) + + # Handle the response + print(response) + +update_space_with_user_cred() + +# [END chat_update_space_user_cred] diff --git a/chat/quickstart/README.md b/chat/quickstart/README.md new file mode 100644 index 00000000..ef44f9c8 --- /dev/null +++ b/chat/quickstart/README.md @@ -0,0 +1,16 @@ +# Google Chat Python Quickstart + +Complete the steps described in the [quickstart instructions]( +https://developers.google.com/workspace/chat/api/guides/quickstart/python), +and in about five minutes you'll have a simple Python command-line +application that makes requests to the Google Chat API. + +## Install + +`pip install -r requirements` + +## Run + +After following the quickstart setup instructions, run the sample: + +`python3 quickstart.py` diff --git a/chat/quickstart/quickstart.py b/chat/quickstart/quickstart.py new file mode 100644 index 00000000..a29c6836 --- /dev/null +++ b/chat/quickstart/quickstart.py @@ -0,0 +1,80 @@ +# Copyright 2024 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START chat_quickstart] +from __future__ import print_function + +import os.path + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from google.apps import chat_v1 as google_chat + + +# If modifying these scopes, delete the file token.json. +SCOPES = ['https://www.googleapis.com/auth/chat.spaces.readonly'] + + +def main(): + """Shows basic usage of the Google Chat API. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists('token.json'): + creds = Credentials.from_authorized_user_file('token.json', SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + 'credentials.json', SCOPES) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open('token.json', 'w') as token: + token.write(creds.to_json()) + + try: + # Create a client + client = google_chat.ChatServiceClient( + credentials = creds, + client_options = { + "scopes" : SCOPES + } + ) + + # Initialize request argument(s) + request = google_chat.ListSpacesRequest( + # Filter spaces by space type (SPACE or GROUP_CHAT or DIRECT_MESSAGE) + filter = 'space_type = "SPACE"' + ) + + # Make the request + page_result = client.list_spaces(request) + + # Handle the response. Iterating over page_result will yield results and + # resolve additional pages automatically. + for response in page_result: + print(response) + except Exception as error: + # TODO(developer) - Handle errors from Chat API. + print(f'An error occurred: {error}') + + +if __name__ == '__main__': + main() +# [END chat_quickstart] diff --git a/chat/quickstart/requirements.txt b/chat/quickstart/requirements.txt new file mode 100644 index 00000000..aeabc164 --- /dev/null +++ b/chat/quickstart/requirements.txt @@ -0,0 +1,3 @@ +google-apps-chat==0.1.0 +google-auth-httplib2==0.1.0 +google-auth-oauthlib==0.4.0 diff --git a/classroom/quickstart/quickstart.py b/classroom/quickstart/quickstart.py index 9f1d65e9..3708c0a2 100644 --- a/classroom/quickstart/quickstart.py +++ b/classroom/quickstart/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START classroom_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -24,50 +22,51 @@ from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/classroom.courses.readonly'] +SCOPES = ["https://www.googleapis.com/auth/classroom.courses.readonly"] def main(): - """Shows basic usage of the Classroom API. - Prints the names of the first 10 courses the user has access to. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Shows basic usage of the Classroom API. + Prints the names of the first 10 courses the user has access to. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - try: - service = build('classroom', 'v1', credentials=creds) + try: + service = build("classroom", "v1", credentials=creds) - # Call the Classroom API - results = service.courses().list(pageSize=10).execute() - courses = results.get('courses', []) + # Call the Classroom API + results = service.courses().list(pageSize=10).execute() + courses = results.get("courses", []) - if not courses: - print('No courses found.') - return - # Prints the names of the first 10 courses. - print('Courses:') - for course in courses: - print(course['name']) + if not courses: + print("No courses found.") + return + # Prints the names of the first 10 courses. + print("Courses:") + for course in courses: + print(course["name"]) - except HttpError as error: - print('An error occurred: %s' % error) + except HttpError as error: + print(f"An error occurred: {error}") -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END classroom_quickstart] diff --git a/classroom/snippets/base_test.py b/classroom/snippets/base_test.py index a241f644..34432364 100644 --- a/classroom/snippets/base_test.py +++ b/classroom/snippets/base_test.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import print_function - import sys import unittest @@ -22,44 +20,45 @@ from googleapiclient.discovery import build from oauth2client.client import GoogleCredentials -SCOPES = 'https://www.googleapis.com/auth/classroom.courses' +SCOPES = "https://www.googleapis.com/auth/classroom.courses" class BaseTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.credentials = cls.create_credentials() - http = cls.credentials.authorize(httplib2.Http()) - cls.credentials.refresh(http) - cls.service = build('classroom', 'v1', http=http) - cls.stdout = sys.stdout - sys.stdout = None - @classmethod - def tearDownClass(cls): - # Restore STDOUT. - sys.stdout = cls.stdout + @classmethod + def setUpClass(cls): + cls.credentials = cls.create_credentials() + http = cls.credentials.authorize(httplib2.Http()) + cls.credentials.refresh(http) + cls.service = build("classroom", "v1", http=http) + cls.stdout = sys.stdout + sys.stdout = None + + @classmethod + def tearDownClass(cls): + # Restore STDOUT. + sys.stdout = cls.stdout - @classmethod - def create_credentials(cls): - cls.credentials = GoogleCredentials.get_application_default() - scope = ['https://www.googleapis.com/auth/drive'] - return cls.credentials.create_scoped(scope) + @classmethod + def create_credentials(cls): + cls.credentials = GoogleCredentials.get_application_default() + scope = ["https://www.googleapis.com/auth/drive"] + return cls.credentials.create_scoped(scope) - def setUp(self): - self.courses_to_delete = [] - print("Meow" + str(self.courses_to_delete)) + def setUp(self): + self.courses_to_delete = [] + print("Meow" + str(self.courses_to_delete)) - def tearDown(self): - for course_id in self.courses_to_delete: - try: - self.service.courses().delete(id=course_id).execute() - except errors.HttpError: - print('Unable to delete file %s' % course_id) + def tearDown(self): + for course_id in self.courses_to_delete: + try: + self.service.courses().delete(id=course_id).execute() + except errors.HttpError: + print(f"Unable to delete file {course_id}") - def delete_course_on_cleanup(self, course_id): - self.courses_to_delete.append(course_id) + def delete_course_on_cleanup(self, course_id): + self.courses_to_delete.append(course_id) -if __name__ == '__main__': - unittest.main() +if __name__ == "__main__": + unittest.main() diff --git a/classroom/snippets/classroom_add_alias_existing.py b/classroom/snippets/classroom_add_alias_existing.py new file mode 100644 index 00000000..cb777291 --- /dev/null +++ b/classroom/snippets/classroom_add_alias_existing.py @@ -0,0 +1,54 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +# [START classroom_add_alias_existing] +def classroom_add_alias_existing(course_id): + """ + Adds alias to existing course with specific course_id. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + # [START classroom_existing_alias] + service = build("classroom", "v1", credentials=creds) + alias = "d:school_math_101" + course_alias = {"alias": alias} + try: + course_alias = ( + service.courses() + .aliases() + .create(courseId=course_id, body=course_alias) + .execute() + ) + return course_alias + except HttpError as error: + print(f"An error occurred: {error}") + print("Alias Creation Failed") + return course_alias + # [END classroom_existing_alias] + + +if __name__ == "__main__": + # Put the course_id of course whose alias needs to be added. + classroom_add_alias_existing(456058313539) + +# [END classroom_existing_alias] diff --git a/classroom/snippets/classroom_add_alias_new.py b/classroom/snippets/classroom_add_alias_new.py new file mode 100644 index 00000000..65253653 --- /dev/null +++ b/classroom/snippets/classroom_add_alias_new.py @@ -0,0 +1,81 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import os.path + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + +# [START classroom_new_alias] +SCOPES = ["https://www.googleapis.com/auth/classroom.courses"] + + +def classroom_add_alias_new(): + """ + Creates a course with alias specification the user has access to. + The file token.json stores the user's access and refresh tokens, and is + created automatically when the authorization flow completes for + the first time. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity for + guides on implementing OAuth2 for the application. + """ + # pylint: disable=maybe-no-member + creds = None + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w", encoding="utf8") as token: + token.write(creds.to_json()) + + alias = "d:school_physics_333" + course = { + "id": alias, + "name": "English", + "section": "Period 2", + "description": "Course Description", + "room": "301", + "ownerId": "me", + } + try: + print("-------------") + service = build("classroom", "v1", credentials=creds) + course = service.courses().create(body=course).execute() + print("====================================") + + except HttpError as error: + print(f"An error occurred: {error}") + return course + + +if __name__ == "__main__": + # pylint: disable=too-many-arguments + # Put the course_id of course whose alias needs to be created. + classroom_add_alias_new() + +# [END classroom_new_alias] diff --git a/classroom/snippets/classroom_add_attachment.py b/classroom/snippets/classroom_add_attachment.py new file mode 100644 index 00000000..e65dfabb --- /dev/null +++ b/classroom/snippets/classroom_add_attachment.py @@ -0,0 +1,58 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +# [START classroom_add_attachment] +def classroom_add_attachment(course_id, coursework_id, submission_id): + """ + Adds attachment to existing course with specific course_id. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + request = { + "addAttachments": [ + {"link": {"url": "http://example.com/quiz-results"}}, + {"link": {"url": "http://example.com/quiz-reading"}}, + ] + } + + try: + service = build("classroom", "v1", credentials=creds) + while True: + coursework = service.courses().courseWork() + coursework.studentSubmissions().modifyAttachments( + courseId=course_id, + courseWorkId=coursework_id, + id=submission_id, + body=request, + ).execute() + + except HttpError as error: + print(f"An error occurred: {error}") + + +if __name__ == "__main__": + # Put the course_id, coursework_id and submission_id of course in which + # attachment needs to be added. + classroom_add_attachment("course_id", "coursework_id", "me") +# [END classroom_add_attachment] diff --git a/classroom/snippets/classroom_add_student.py b/classroom/snippets/classroom_add_student.py new file mode 100644 index 00000000..3f7ee5f7 --- /dev/null +++ b/classroom/snippets/classroom_add_student.py @@ -0,0 +1,83 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.""" + +# [START classroom_add_student] +import os + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + +SCOPES = ["https://www.googleapis.com/auth/classroom.coursework.students"] + + +def classroom_add_student_new(course_id): + """ + Adds a student to a course, the teacher has access to. + The file token.json stores the user's access and refresh tokens, and is + created automatically when the authorization flow completes for the first + time. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity for + guides on implementing OAuth2 for the application. + """ + + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w", encoding="utf8") as token: + token.write(creds.to_json()) + + enrollment_code = "abc-def" + student = {"userId": "gduser1@workspacesamples.dev"} + try: + service = build("classroom", "v1", credentials=creds) + student = ( + service.courses() + .students() + .create( + courseId=course_id, enrollmentCode=enrollment_code, body=student + ) + .execute() + ) + print( + '''User {%s} was enrolled as a student in + the course with ID "{%s}"''' + % (student.get("profile").get("name").get("fullName"), course_id) + ) + return student + except HttpError as error: + print(error) + return error + + +if __name__ == "__main__": + # Put the course_id of course for which student needs to be added. + classroom_add_student_new(478800920837) +# [END classroom_add_student] diff --git a/classroom/snippets/classroom_add_teacher.py b/classroom/snippets/classroom_add_teacher.py new file mode 100644 index 00000000..9b86b715 --- /dev/null +++ b/classroom/snippets/classroom_add_teacher.py @@ -0,0 +1,51 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.""" + +# [START classroom_add_teacher] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def classroom_add_teacher(course_id): + """ + Adds a teacher to a course with specific course_id. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + service = build("classroom", "v1", credentials=creds) + + teacher_email = "gduser1@workspacesamples.dev" + teacher = {"userId": teacher_email} + + try: + teachers = service.courses().teachers() + teacher = teachers.create(courseId=course_id, body=teacher).execute() + print( + "User %s was added as a teacher to the course with ID %s" + % (teacher.get("profile").get("name").get("fullName"), course_id) + ) + except HttpError as error: + print('User "{%s}" is already a member of this course.' % teacher_email) + return error + return teachers + + +if __name__ == "__main__": + # Put the course_id of course for which Teacher needs to be added. + classroom_add_teacher(453686957652) +# [END classroom_add_teacher] diff --git a/classroom/snippets/classroom_all_submissions.py b/classroom/snippets/classroom_all_submissions.py new file mode 100644 index 00000000..1c505fc2 --- /dev/null +++ b/classroom/snippets/classroom_all_submissions.py @@ -0,0 +1,75 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +# [START classroom_all_submissions] +def classroom_all_submissions(course_id, user_id): + """ + Creates the list of all submissions of the courses the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + submissions = [] + page_token = None + + try: + service = build("classroom", "v1", credentials=creds) + while True: + coursework = service.courses().courseWork() + response = ( + coursework.studentSubmissions() + .list( + pageToken=page_token, + courseId=course_id, + courseWorkId="-", + userId=user_id, + ) + .execute() + ) + submissions.extend(response.get("studentSubmissions", [])) + page_token = response.get("nextPageToken", None) + if not page_token: + break + + if not submissions: + print("No student submissions found.") + else: + print("Complete list of student Submissions:") + for submission in submissions: + print( + f"{submission.get('id')} was submitted at" + f" {submission.get('creationTime')}" + ) + + except HttpError as error: + print(f"An error occurred: {error}") + submissions = None + return submissions + + +if __name__ == "__main__": + # Put the course_id and user_id of course whose list needs to be + # submitted. + classroom_all_submissions(453686957652, 466086979658) +# [END classroom_all_submissions] diff --git a/classroom/snippets/classroom_create_course.py b/classroom/snippets/classroom_create_course.py new file mode 100644 index 00000000..c471d1d8 --- /dev/null +++ b/classroom/snippets/classroom_create_course.py @@ -0,0 +1,61 @@ +""" +Copyright 2018 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START classroom_create_course] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def classroom_create_course(): + """ + Creates the courses the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + + try: + service = build("classroom", "v1", credentials=creds) + course = { + "name": "10th Grade Mathematics Probability-2", + "section": "Period 3", + "descriptionHeading": "Welcome to 10th Grade Mathematics", + "description": """We'll be learning about about the + polynomials from a + combination of textbooks and guest lectures. + Expect to be excited!""", + "room": "302", + "ownerId": "me", + "courseState": "PROVISIONED", + } + # pylint: disable=maybe-no-member + course = service.courses().create(body=course).execute() + print(f"Course created: {(course.get('name'), course.get('id'))}") + return course + + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + classroom_create_course() + +# [END classroom_create_course] diff --git a/classroom/snippets/classroom_create_coursework.py b/classroom/snippets/classroom_create_coursework.py new file mode 100644 index 00000000..7a41392a --- /dev/null +++ b/classroom/snippets/classroom_create_coursework.py @@ -0,0 +1,65 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START classroom_create_coursework] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def classroom_create_coursework(course_id): + """ + Creates the coursework the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + + try: + service = build("classroom", "v1", credentials=creds) + coursework = { + "title": "Ant colonies", + "description": """Read the article about ant colonies + and complete the quiz.""", + "materials": [ + {"link": {"url": "http://example.com/ant-colonies"}}, + {"link": {"url": "http://example.com/ant-quiz"}}, + ], + "workType": "ASSIGNMENT", + "state": "PUBLISHED", + } + coursework = ( + service.courses() + .courseWork() + .create(courseId=course_id, body=coursework) + .execute() + ) + print(f"Assignment created with ID {coursework.get('id')}") + return coursework + + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Put the course_id of course whose coursework needs to be created, + # the user has access to. + classroom_create_coursework(453686957652) +# [END classroom_create_coursework] diff --git a/classroom/snippets/classroom_get_course.py b/classroom/snippets/classroom_get_course.py new file mode 100644 index 00000000..f91f31c5 --- /dev/null +++ b/classroom/snippets/classroom_get_course.py @@ -0,0 +1,49 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START classroom_get_course] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def classroom_get_course(course_id): + """ + Prints the name of the with specific course_id. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + course = None + try: + service = build("classroom", "v1", credentials=creds) + course = service.courses().get(id=course_id).execute() + print(f"Course found : {course.get('name')}") + except HttpError as error: + print(f"An error occurred: {error}") + print(f"Course not found: {course_id}") + return error + return course + + +if __name__ == "__main__": + # Put the course_id of course whose information needs to be fetched. + classroom_get_course("course_id") + +# [END classroom_get_courses] diff --git a/classroom/snippets/classroom_invite_guardian.py b/classroom/snippets/classroom_invite_guardian.py new file mode 100644 index 00000000..8b260c39 --- /dev/null +++ b/classroom/snippets/classroom_invite_guardian.py @@ -0,0 +1,59 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +# [START classroom_invite_guardian] +def classroom_invite_guardian(): + """ + Creates the courses the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + guardian_invitation = { + "invitedEmailAddress": "guardian@gmail.com", + } + + try: + service = build("classroom", "v1", credentials=creds) + while True: + guardian_invitations = service.userProfiles().guardianInvitations() + guardian_invitation = guardian_invitations.create( + # You can use a user ID or an email address. + studentId="student@mydomain.edu", + body=guardian_invitation, + ).execute() + print( + "Invitation created with id: {%s}" + % guardian_invitation.get("invitationId") + ) + + except HttpError as error: + print(f"An error occurred: {error}") + + +if __name__ == "__main__": + # Put the course_id, coursework_id and user_id of course whose list needs + # to be submitted. + classroom_invite_guardian() +# [END classroom_invite_guardian] diff --git a/classroom/snippets/classroom_list_course_aliases.py b/classroom/snippets/classroom_list_course_aliases.py new file mode 100644 index 00000000..562e13c6 --- /dev/null +++ b/classroom/snippets/classroom_list_course_aliases.py @@ -0,0 +1,64 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START classroom_list_course_aliases] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def classroom_list_course_aliases(course_id): + """ + Prints the list of the aliases of a specified course the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + creds, _ = google.auth.default() + try: + service = build("classroom", "v1", credentials=creds) + course_aliases = [] + page_token = None + + while True: + response = ( + service.courses() + .aliases() + .list(pageToken=page_token, courseId=course_id) + .execute() + ) + course_aliases.extend(response.get("aliases", [])) + page_token = response.get("nextPageToken", None) + if not page_token: + break + + if not course_aliases: + print("No course aliases found.") + + print("Course aliases:") + for course_alias in course_aliases: + print(f"{course_alias.get('alias')}") + return course_aliases + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + classroom_list_course_aliases("course_id") + +# [END classroom_list_course_aliases] diff --git a/classroom/snippets/classroom_list_courses.py b/classroom/snippets/classroom_list_courses.py new file mode 100644 index 00000000..6e8c97cd --- /dev/null +++ b/classroom/snippets/classroom_list_courses.py @@ -0,0 +1,63 @@ +""" +Copyright 2018 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START classroom_list_courses] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def classroom_list_courses(): + """ + Prints the list of the courses the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + creds, _ = google.auth.default() + try: + service = build("classroom", "v1", credentials=creds) + courses = [] + page_token = None + + while True: + # pylint: disable=maybe-no-member + response = ( + service.courses().list(pageToken=page_token, pageSize=100).execute() + ) + courses.extend(response.get("courses", [])) + page_token = response.get("nextPageToken", None) + if not page_token: + break + + if not courses: + print("No courses found.") + return + print("Courses:") + for course in courses: + print(f"{course.get('name'), course.get('id')}") + return courses + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + print("Courses available are-------") + classroom_list_courses() + +# [END classroom_list_courses] diff --git a/classroom/snippets/classroom_list_student_submissions.py b/classroom/snippets/classroom_list_student_submissions.py new file mode 100644 index 00000000..c56ea8de --- /dev/null +++ b/classroom/snippets/classroom_list_student_submissions.py @@ -0,0 +1,74 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START classroom_list_student_submissions] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def classroom_list_student_submissions(course_id, coursework_id, user_id): + """ + Creates the courses the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + submissions = [] + page_token = None + + try: + service = build("classroom", "v1", credentials=creds) + while True: + coursework = service.courses().courseWork() + response = ( + coursework.studentSubmissions() + .list( + pageToken=page_token, + courseId=course_id, + courseWorkId=coursework_id, + userId=user_id, + ) + .execute() + ) + submissions.extend(response.get("studentSubmissions", [])) + page_token = response.get("nextPageToken", None) + if not page_token: + break + + if not submissions: + print("No student submissions found.") + + print("Student Submissions:") + for submission in submissions: + print( + "Submitted at:" + f"{(submission.get('id'), submission.get('creationTime'))}" + ) + + except HttpError as error: + print(f"An error occurred: {error}") + return submissions + + +if __name__ == "__main__": + # Put the course_id, coursework_id and user_id of course whose list needs + # to be submitted. + classroom_list_student_submissions(453686957652, 466086979658, "me") +# [END classroom_list_student_submissions] diff --git a/classroom/snippets/classroom_list_submissions.py b/classroom/snippets/classroom_list_submissions.py new file mode 100644 index 00000000..e7040954 --- /dev/null +++ b/classroom/snippets/classroom_list_submissions.py @@ -0,0 +1,75 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START classroom_list_submissions] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def classroom_list_submissions(course_id, coursework_id): + """ + Creates the courses the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + submissions = [] + page_token = None + + try: + service = build("classroom", "v1", credentials=creds) + while True: + coursework = service.courses().courseWork() + response = ( + coursework.studentSubmissions() + .list( + pageToken=page_token, + courseId=course_id, + courseWorkId=coursework_id, + pageSize=10, + ) + .execute() + ) + submissions.extend(response.get("studentSubmissions", [])) + page_token = response.get("nextPageToken", None) + if not page_token: + break + + if not submissions: + print("No student submissions found.") + + print("Student Submissions:") + for submission in submissions: + print( + "Submitted at:" + f"{(submission.get('id'), submission.get('creationTime'))}" + ) + + except HttpError as error: + print(f"An error occurred: {error}") + submissions = None + return submissions + + +if __name__ == "__main__": + # Put the course_id and coursework_id of course whose list needs to be + # submitted. + classroom_list_submissions(453686957652, 466086979658) +# [END classroom_list_submissions] diff --git a/classroom/snippets/classroom_patch_course.py b/classroom/snippets/classroom_patch_course.py new file mode 100644 index 00000000..e1e02420 --- /dev/null +++ b/classroom/snippets/classroom_patch_course.py @@ -0,0 +1,52 @@ +"""Copyright 2018 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START classroom_patch_course] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def classroom_patch_course(course_id): + """ + Patch new course with existing course in the account the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + # pylint: disable=maybe-no-member + + creds, _ = google.auth.default() + + try: + service = build("classroom", "v1", credentials=creds) + course = {"section": "Period 3", "room": "313"} + course = ( + service.courses() + .patch(id=course_id, updateMask="section,room", body=course) + .execute() + ) + print(f" Course updated are: {course.get('name')}") + return course + except HttpError as error: + print(f"An error occurred: {error}") + + +if __name__ == "__main__": + # Put the course_id of course with whom we need to patch some extra + # information. + classroom_patch_course("course_id") + +# [END classroom_patch_course] diff --git a/classroom/snippets/classroom_snippets.py b/classroom/snippets/classroom_snippets.py deleted file mode 100644 index c5a00498..00000000 --- a/classroom/snippets/classroom_snippets.py +++ /dev/null @@ -1,326 +0,0 @@ -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import print_function - -from googleapiclient import errors - - -class ClassroomSnippets(object): - def __init__(self, service): - self.service = service - - def create_course(self): - """ Creates a single Classroom course. """ - service = self.service - # [START classroom_create_course] - course = { - 'name': '10th Grade Biology', - 'section': 'Period 2', - 'descriptionHeading': 'Welcome to 10th Grade Biology', - 'description': """We'll be learning about about the - structure of living creatures from a - combination of textbooks, guest lectures, - and lab work. Expect to be excited!""", - 'room': '301', - 'ownerId': 'me', - 'courseState': 'PROVISIONED' - } - course = service.courses().create(body=course).execute() - print('Course created: %s %s' % (course.get('name'), course.get('id'))) - # [END classroom_create_course] - return course - - def get_course(self, course_id): - """ Retrieves a classroom course by its id. """ - service = self.service - # [START classroom_get_course] - try: - course = service.courses().get(id=course_id).execute() - print('Course "{%s}" found.' % course.get('name')) - except errors.HttpError as error: - print('Course with ID "{%s}" not found.' % course_id) - # [END classroom_get_course] - return error - return course - - def list_courses(self): - """ Lists all classroom courses. """ - service = self.service - # [START classroom_list_courses] - courses = [] - page_token = None - - while True: - response = service.courses().list(pageToken=page_token, - pageSize=100).execute() - courses.extend(response.get('courses', [])) - page_token = response.get('nextPageToken', None) - if not page_token: - break - - if not courses: - print('No courses found.') - else: - print('Courses:') - for course in courses: - print(course.get('name'), course.get('id')) - # [END classroom_list_courses] - - def update_course(self, course_id): - """ Updates the section and room of Google Classroom. """ - service = self.service - # [START classroom_update_course] - course = service.courses().get(id=course_id).execute() - course['section'] = 'Period 3' - course['room'] = '302' - course = service.courses().update(id=course_id, body=course).execute() - print('Course %s updated.' % course.get('name')) - # [END classroom_update_course] - - def patch_course(self, course_id): - """ Creates a course with alias specification. """ - service = self.service - # [START classroom_patch_course] - course = { - 'section': 'Period 3', - 'room': '302' - } - course = service.courses().patch(id=course_id, - updateMask='section,room', - body=course).execute() - print('Course "%s" updated.' % course.get('name')) - # [END classroom_patch_course] - - def add_alias_new(self): - """ Creates a course with alias specification. """ - service = self.service - # [START classroom_new_alias] - alias = 'd:school_math_101' - course = { - 'id': alias, - 'name': 'Math 101', - 'section': 'Period 2', - 'description': 'Course Description', - 'room': '301', - 'ownerId': 'me' - } - try: - course = service.courses().create( - body=course).execute() - except errors.HttpError: - print('Course Creation Failed') - # [END classroom_new_alias] - - def add_alias_existing(self, course_id): - """ Adds alias to existing course. """ - service = self.service - # [START classroom_existing_alias] - alias = 'd:school_math_101' - course_alias = { - 'alias': alias - } - try: - course_alias = service.courses().aliases().create( - courseId=course_id, - body=course_alias).execute() - except errors.HttpError: - print('Alias Creation Failed') - # [END classroom_existing_alias] - - def add_teacher(self, course_id): - """ Adds a teacher to a course. """ - service = self.service - # [START classroom_add_teacher] - teacher_email = 'alice@example.edu' - teacher = { - 'userId': teacher_email - } - try: - teachers = service.courses().teachers() - teacher = teachers.create(courseId=course_id, - body=teacher).execute() - print('User %s was added as a teacher to the course with ID %s' - % (teacher.get('profile').get('name').get('fullName'), - course_id)) - except errors.HttpError as error: - print('User "{%s}" is already a member of this course.' - % teacher_email) - # [END classroom_add_teacher] - return error - return teachers - - def add_student(self, course_id): - """ Adds a student to a course. """ - service = self.service - # [START classroom_add_student] - enrollment_code = 'abcdef' - student = { - 'userId': 'me' - } - try: - student = service.courses().students().create( - courseId=course_id, - enrollmentCode=enrollment_code, - body=student).execute() - print( - '''User {%s} was enrolled as a student in - the course with ID "{%s}"''' - % (student.get('profile').get('name').get('fullName'), - course_id)) - except errors.HttpError as error: - print('You are already a member of this course.') - # [END classroom_add_student] - return error - return student - - def create_coursework(self, course_id): - """ Creates a coursework. """ - service = self.service - # [START classroom_create_coursework] - coursework = { - 'title': 'Ant colonies', - 'description': '''Read the article about ant colonies - and complete the quiz.''', - 'materials': [ - {'link': {'url': 'http://example.com/ant-colonies'}}, - {'link': {'url': 'http://example.com/ant-quiz'}} - ], - 'workType': 'ASSIGNMENT', - 'state': 'PUBLISHED', - } - coursework = service.courses().courseWork().create( - courseId=course_id, body=coursework).execute() - print('Assignment created with ID {%s}' % coursework.get('id')) - # [END classroom_create_coursework] - - def list_submissions(self, course_id, coursework_id): - """ Lists all student submissions for a given coursework. """ - service = self.service - # [START classroom_list_submissions] - submissions = [] - page_token = None - - while True: - coursework = service.courses().courseWork() - response = coursework.studentSubmissions().list( - pageToken=page_token, - courseId=course_id, - courseWorkId=coursework_id, - pageSize=10).execute() - submissions.extend(response.get('studentSubmissions', [])) - page_token = response.get('nextPageToken', None) - if not page_token: - break - - if not submissions: - print('No student submissions found.') - else: - print('Student Submissions:') - for submission in submissions: - print("%s was submitted at %s" % - (submission.get('id'), - submission.get('creationTime'))) - # [END classroom_list_submissions] - - def list_student_submissions(self, course_id, coursework_id, user_id): - """ Lists all coursework submissions for a given student. """ - service = self.service - # [START classroom_list_student_submissions] - submissions = [] - page_token = None - - while True: - coursework = service.courses().courseWork() - response = coursework.studentSubmissions().list( - pageToken=page_token, - courseId=course_id, - courseWorkId=coursework_id, - userId=user_id).execute() - submissions.extend(response.get('studentSubmissions', [])) - page_token = response.get('nextPageToken', None) - if not page_token: - break - - if not submissions: - print('No student submissions found.') - else: - print('Student Submissions:') - for submission in submissions: - print("%s was submitted at %s" % - (submission.get('id'), - submission.get('creationTime'))) - # [END classroom_list_student_submissions] - - def list_all_submissions(self, course_id, user_id): - """ Lists all coursework submissions for a given student. """ - service = self.service - # [START classroom_list_submissions] - submissions = [] - page_token = None - - while True: - coursework = service.courses().courseWork() - response = coursework.studentSubmissions().list( - pageToken=page_token, - courseId=course_id, - courseWorkId="-", - userId=user_id).execute() - submissions.extend(response.get('studentSubmissions', [])) - page_token = response.get('nextPageToken', None) - if not page_token: - break - - if not submissions: - print('No student submissions found.') - else: - print('Complete list of student Submissions:') - for submission in submissions: - print("%s was submitted at %s" % - (submission.get('id'), - submission.get('creationTime'))) - # [END classroom_list_submissions] - - def add_attachment(self, course_id, coursework_id, submission_id): - """ Adds an attachment to a student submission. """ - service = self.service - # [START classroom_add_attachment] - request = { - 'addAttachments': [ - {'link': {'url': 'http://example.com/quiz-results'}}, - {'link': {'url': 'http://example.com/quiz-reading'}} - ] - } - coursework = service.courses().courseWork() - coursework.studentSubmissions().modifyAttachments( - courseId=course_id, - courseWorkId=coursework_id, - id=submission_id, - body=request).execute() - # [END classroom_add_attachment] - - def invite_guardian(self): - """ Send an invite to a guardian. """ - service = self.service - # [START classroom_add_attachment] - guardian_invitation = { - 'invitedEmailAddress': 'guardian@gmail.com', - } - guardian_invitations = service.userProfiles().guardianInvitations() - guardian_invitation = guardian_invitations.create( - # You can use a user ID or an email address. - studentId='student@mydomain.edu', - body=guardian_invitation).execute() - print("Invitation created with id: {%s}" - % guardian_invitation.get('invitationId')) diff --git a/classroom/snippets/classroom_update_course.py b/classroom/snippets/classroom_update_course.py new file mode 100644 index 00000000..25ac1f3e --- /dev/null +++ b/classroom/snippets/classroom_update_course.py @@ -0,0 +1,55 @@ +""" +Copyright 2018 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START classroom_update_course] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def classroom_update_course(course_id): + """ + Updates the courses names the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + # pylint: disable=maybe-no-member + + creds, _ = google.auth.default() + + try: + service = build("classroom", "v1", credentials=creds) + + # Updates the section and room of Google Classroom. + course = service.courses().get(id=course_id).execute() + course["name"] = "10th Grade Physics - Light" + course["section"] = "Period 4" + course["room"] = "410" + course = service.courses().update(id=course_id, body=course).execute() + print(f" Updated Course is: {course.get('name')}") + return course + + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Put the course_id of course whose course needs to be updated. + classroom_update_course("course_id") + +# [END classroom_update_course] diff --git a/classroom/snippets/test_classroom_create_course.py b/classroom/snippets/test_classroom_create_course.py new file mode 100644 index 00000000..8d5c76da --- /dev/null +++ b/classroom/snippets/test_classroom_create_course.py @@ -0,0 +1,31 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import classroom_create_course +from base_test import BaseTest + + +class TestClassroomCreateCourse(BaseTest): + """Unit test class for Create course snippet""" + + def test_classroom_create_course(self): + """Class function for Create course snippet""" + course = classroom_create_course.classroom_create_course() + self.assertIsNotNone(course) + self.delete_course_on_cleanup(course.get("id")) + + +if __name__ == "__main__": + unittest.main() diff --git a/classroom/snippets/test_classroom_get_course.py b/classroom/snippets/test_classroom_get_course.py new file mode 100644 index 00000000..0ca05299 --- /dev/null +++ b/classroom/snippets/test_classroom_get_course.py @@ -0,0 +1,34 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import classroom_create_course +import classroom_get_course +from base_test import BaseTest + + +class TestClassroomGetCourse(BaseTest): + """Unit test class for Get course snippet""" + + def test_classroom_get_course(self): + """Unit test method for Get course snippet""" + course = classroom_create_course.classroom_create_course() + self.assertIsNotNone(course) + self.delete_course_on_cleanup(course.get("id")) + course_id = classroom_get_course.classroom_get_course(course.get("id")) + self.assertIsNotNone(course_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/classroom/snippets/test_classroom_list_course_aliases.py b/classroom/snippets/test_classroom_list_course_aliases.py new file mode 100644 index 00000000..f68b81ea --- /dev/null +++ b/classroom/snippets/test_classroom_list_course_aliases.py @@ -0,0 +1,34 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from base_test import BaseTest +from classroom_create_course import classroom_create_course +from classroom_list_course_aliases import classroom_list_course_aliases + + +class TestClassroomListCourseAliases(BaseTest): + """Unit test class for List course aliases snippet""" + + def test_classroom_list_course_aliases(self): + """Unit test method for List course snippet""" + course = classroom_create_course() + self.assertIsNotNone(course) + self.delete_course_on_cleanup(course.get("id")) + course_aliases = classroom_list_course_aliases(course.get("id")) + self.assertIsNotNone(course_aliases) + + +if __name__ == "__main__": + unittest.main() diff --git a/classroom/snippets/test_classroom_list_courses.py b/classroom/snippets/test_classroom_list_courses.py new file mode 100644 index 00000000..81fc27f1 --- /dev/null +++ b/classroom/snippets/test_classroom_list_courses.py @@ -0,0 +1,34 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import classroom_create_course +import classroom_list_courses +from base_test import BaseTest + + +class TestClassroomListCourses(BaseTest): + """Unit test class for List course snippet""" + + def test_classroom_list_courses(self): + """Unit test method for List course snippet""" + course = classroom_create_course.classroom_create_course() + self.assertIsNotNone(course) + self.delete_course_on_cleanup(course.get("id")) + courses = classroom_list_courses.classroom_list_courses() + self.assertIsNotNone(courses) + + +if __name__ == "__main__": + unittest.main() diff --git a/classroom/snippets/test_classroom_patch_course.py b/classroom/snippets/test_classroom_patch_course.py new file mode 100644 index 00000000..43a4862f --- /dev/null +++ b/classroom/snippets/test_classroom_patch_course.py @@ -0,0 +1,34 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import classroom_create_course +import classroom_patch_course +from base_test import BaseTest + + +class TestClassroomPatchCourse(BaseTest): + """Unit test class for Patch course snippet""" + + def test_classroom_patch_course(self): + """Unit test method for Patch course snippet""" + course = classroom_create_course.classroom_create_course() + self.assertIsNotNone(course) + self.delete_course_on_cleanup(course.get("id")) + course = classroom_patch_course.classroom_patch_course(course.get("id")) + self.assertIsNotNone(course) + + +if __name__ == "__main__": + unittest.main() diff --git a/classroom/snippets/test_classroom_update_course.py b/classroom/snippets/test_classroom_update_course.py new file mode 100644 index 00000000..ccfcdd4b --- /dev/null +++ b/classroom/snippets/test_classroom_update_course.py @@ -0,0 +1,34 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import classroom_create_course +import classroom_update_course +from base_test import BaseTest + + +class TestClassroomUpdateCourse(BaseTest): + """Unit test class for Get course snippet""" + + def test_classroom_update_course(self): + """Unit test method for Get course snippet""" + course = classroom_create_course.classroom_create_course() + self.assertIsNotNone(course) + self.delete_course_on_cleanup(course.get("id")) + course = classroom_update_course.classroom_update_course(course.get("id")) + self.assertIsNotNone(course) + + +if __name__ == "__main__": + unittest.main() diff --git a/classroom/snippets/test_snippets.py b/classroom/snippets/test_snippets.py deleted file mode 100644 index ffb3c3d6..00000000 --- a/classroom/snippets/test_snippets.py +++ /dev/null @@ -1,36 +0,0 @@ - -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest - -from base_test import BaseTest -from classroom_snippets import ClassroomSnippets - - -class SnippetsTest(BaseTest): - - @classmethod - def setUpClass(cls): - super(SnippetsTest, cls).setUpClass() - cls.snippets = ClassroomSnippets(cls.service) - - def test_create_course(self): - course = self.snippets.create_course() - self.assertIsNotNone(course) - self.delete_course_on_cleanup(course.get('id')) - - -if __name__ == '__main__': - unittest.main() diff --git a/docs/mail-merge/docs_mail_merge.py b/docs/mail-merge/docs_mail_merge.py index 2af4d89e..45d68840 100644 --- a/docs/mail-merge/docs_mail_merge.py +++ b/docs/mail-merge/docs_mail_merge.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# pylint: disable=(consider-using-f-string) """ docs-mail-merge.py (Python 2.x or 3.x) @@ -18,145 +19,170 @@ Google Docs (REST) API mail-merge sample app """ # [START mail_merge_python] -from __future__ import print_function - import time -from googleapiclient import discovery -from httplib2 import Http -from oauth2client import client, file, tools +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError # Fill-in IDs of your Docs template & any Sheets data source -DOCS_FILE_ID = 'YOUR_TMPL_DOC_FILE_ID' -SHEETS_FILE_ID = 'YOUR_SHEET_DATA_FILE_ID' +DOCS_FILE_ID = "195j9eDD3ccgjQRttHhJPymLJUCOUjs-jmwTrekvdjFE" +SHEETS_FILE_ID = "11pPEzi1vCMNbdpqaQx4N43rKmxvZlgEHE9GqpYoEsWw" # authorization constants -CLIENT_ID_FILE = 'credentials.json' -TOKEN_STORE_FILE = 'token.json' + SCOPES = ( # iterable or space-delimited string - 'https://www.googleapis.com/auth/drive', - 'https://www.googleapis.com/auth/documents', - 'https://www.googleapis.com/auth/spreadsheets.readonly', + "https://www.googleapis.com/auth/drive", + "https://www.googleapis.com/auth/documents", + "https://www.googleapis.com/auth/spreadsheets.readonly", ) # application constants -SOURCES = ('text', 'sheets') -SOURCE = 'text' # Choose one of the data SOURCES -COLUMNS = ['to_name', 'to_title', 'to_company', 'to_address'] +SOURCES = ("text", "sheets") +SOURCE = "text" # Choose one of the data SOURCES +COLUMNS = ["to_name", "to_title", "to_company", "to_address"] TEXT_SOURCE_DATA = ( - ('Ms. Lara Brown', 'Googler', 'Google NYC', '111 8th Ave\n' - 'New York, NY 10011-5201'), - ('Mr. Jeff Erson', 'Googler', 'Google NYC', '76 9th Ave\n' - 'New York, NY 10011-4962'), + ( + "Ms. Lara Brown", + "Googler", + "Google NYC", + "111 8th Ave\nNew York, NY 10011-5201", + ), + ( + "Mr. Jeff Erson", + "Googler", + "Google NYC", + "76 9th Ave\nNew York, NY 10011-4962", + ), ) - -def get_http_client(): - """Uses project credentials in CLIENT_ID_FILE along with requested OAuth2 - scopes for authorization, and caches API tokens in TOKEN_STORE_FILE. - """ - store = file.Storage(TOKEN_STORE_FILE) - creds = store.get() - if not creds or creds.invalid: - flow = client.flow_from_clientsecrets(CLIENT_ID_FILE, SCOPES) - creds = tools.run_flow(flow, store) - return creds.authorize(Http()) - +# fill-in your data to merge into document template variables +merge = { + # sender data + "my_name": "Ayme A. Coder", + "my_address": "1600 Amphitheatre Pkwy\nMountain View, CA 94043-1351", + "my_email": "http://google.com/", + "my_phone": "+1-650-253-0000", + # - - - - - - - - - - - - - - - - - - - - - - - - - - + # recipient data (supplied by 'text' or 'sheets' data source) + "to_name": None, + "to_title": None, + "to_company": None, + "to_address": None, + # - - - - - - - - - - - - - - - - - - - - - - - - - - + "date": time.strftime("%Y %B %d"), + # - - - - - - - - - - - - - - - - - - - - - - - - - - + "body": ( + "Google, headquartered in Mountain View, unveiled the new " + "Android phone at the Consumer Electronics Show. CEO Sundar " + "Pichai said in his keynote that users love their new phones." + ), +} + +creds, _ = google.auth.default() +# pylint: disable=maybe-no-member # service endpoints to Google APIs -HTTP = get_http_client() -DRIVE = discovery.build('drive', 'v3', http=HTTP) -DOCS = discovery.build('docs', 'v1', http=HTTP) -SHEETS = discovery.build('sheets', 'v4', http=HTTP) + +DRIVE = build("drive", "v2", credentials=creds) +DOCS = build("docs", "v1", credentials=creds) +SHEETS = build("sheets", "v4", credentials=creds) def get_data(source): - """Gets mail merge data from chosen data source. - """ - if source not in {'sheets', 'text'}: - raise ValueError('ERROR: unsupported source %r; choose from %r' % ( - source, SOURCES)) + """Gets mail merge data from chosen data source.""" + try: + if source not in {"sheets", "text"}: + raise ValueError( + f"ERROR: unsupported source {source}; choose from {SOURCES}" + ) return SAFE_DISPATCH[source]() + except HttpError as error: + print(f"An error occurred: {error}") + return error def _get_text_data(): - """(private) Returns plain text data; can alter to read from CSV file. - """ - return TEXT_SOURCE_DATA + """(private) Returns plain text data; can alter to read from CSV file.""" + return TEXT_SOURCE_DATA def _get_sheets_data(service=SHEETS): - """(private) Returns data from Google Sheets source. It gets all rows of - 'Sheet1' (the default Sheet in a new spreadsheet), but drops the first - (header) row. Use any desired data range (in standard A1 notation). - """ - return service.spreadsheets().values().get(spreadsheetId=SHEETS_FILE_ID, - range='Sheet1').execute().get('values')[1:] # skip header row + """(private) Returns data from Google Sheets source. It gets all rows of + 'Sheet1' (the default Sheet in a new spreadsheet), but drops the first + (header) row. Use any desired data range (in standard A1 notation). + """ + return ( + service.spreadsheets() + .values() + .get(spreadsheetId=SHEETS_FILE_ID, range="Sheet1") + .execute() + .get("values")[1:] + ) + # skip header row # data source dispatch table [better alternative vs. eval()] -SAFE_DISPATCH = {k: globals().get('_get_%s_data' % k) for k in SOURCES} +SAFE_DISPATCH = {k: globals().get(f"_get_{k}_data") for k in SOURCES} def _copy_template(tmpl_id, source, service): - """(private) Copies letter template document using Drive API then - returns file ID of (new) copy. - """ - body = {'name': 'Merged form letter (%s)' % source} - return service.files().copy(body=body, fileId=tmpl_id, - fields='id').execute().get('id') + """(private) Copies letter template document using Drive API then + returns file ID of (new) copy. + """ + try: + body = {"name": f"Merged form letter ({source})"} + return ( + service.files() + .copy(body=body, fileId=tmpl_id, fields="id") + .execute() + .get("id") + ) + except HttpError as error: + print(f"An error occurred: {error}") + return error def merge_template(tmpl_id, source, service): - """Copies template document and merges data into newly-minted copy then - returns its file ID. - """ + """Copies template document and merges data into newly-minted copy then + returns its file ID. + """ + try: # copy template and set context data struct for merging template values copy_id = _copy_template(tmpl_id, source, service) - context = merge.iteritems() if hasattr({}, 'iteritems') else merge.items() + context = merge.iteritems() if hasattr({}, "iteritems") else merge.items() # "search & replace" API requests for mail merge substitutions - reqs = [{'replaceAllText': { - 'containsText': { - 'text': '{{%s}}' % key.upper(), # {{VARS}} are uppercase - 'matchCase': True, - }, - 'replaceText': value, - }} for key, value in context] + reqs = [ + { + "replaceAllText": { + "containsText": { + "text": "{{%s}}" % key.upper(), # {{VARS}} are uppercase + "matchCase": True, + }, + "replaceText": value, + } + } + for key, value in context + ] # send requests to Docs API to do actual merge - DOCS.documents().batchUpdate(body={'requests': reqs}, - documentId=copy_id, fields='').execute() + DOCS.documents().batchUpdate( + body={"requests": reqs}, documentId=copy_id, fields="" + ).execute() return copy_id - - -if __name__ == '__main__': - # fill-in your data to merge into document template variables - merge = { - # sender data - 'my_name': 'Ayme A. Coder', - 'my_address': '1600 Amphitheatre Pkwy\n' - 'Mountain View, CA 94043-1351', - 'my_email': 'http://google.com/', - 'my_phone': '+1-650-253-0000', - # - - - - - - - - - - - - - - - - - - - - - - - - - - - # recipient data (supplied by 'text' or 'sheets' data source) - 'to_name': None, - 'to_title': None, - 'to_company': None, - 'to_address': None, - # - - - - - - - - - - - - - - - - - - - - - - - - - - - 'date': time.strftime('%Y %B %d'), - # - - - - - - - - - - - - - - - - - - - - - - - - - - - 'body': 'Google, headquartered in Mountain View, unveiled the new ' - 'Android phone at the Consumer Electronics Show. CEO Sundar ' - 'Pichai said in his keynote that users love their new phones.' - } - - # get row data, then loop through & process each form letter - data = get_data(SOURCE) # get data from data source - for i, row in enumerate(data): - merge.update(dict(zip(COLUMNS, row))) - print('Merged letter %d: docs.google.com/document/d/%s/edit' % ( - i + 1, merge_template(DOCS_FILE_ID, SOURCE, DRIVE))) + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # get row data, then loop through & process each form letter + data = get_data(SOURCE) # get data from data source + for i, row in enumerate(data): + merge.update(dict(zip(COLUMNS, row))) + print( + "Merged letter %d: docs.google.com/document/d/%s/edit" + % (i + 1, merge_template(DOCS_FILE_ID, SOURCE, DRIVE)) + ) # [END mail_merge_python] diff --git a/docs/mail-merge/docs_mail_merge_test.py b/docs/mail-merge/docs_mail_merge_test.py index 7020b58f..276d8f14 100644 --- a/docs/mail-merge/docs_mail_merge_test.py +++ b/docs/mail-merge/docs_mail_merge_test.py @@ -25,76 +25,66 @@ import unittest import google.auth -from docs_mail_merge import _copy_template, get_data, get_http_client +from docs_mail_merge import _copy_template, get_data from googleapiclient import discovery +creds, _ = google.auth.default() -class TestDocsMailMerge(unittest.TestCase): - 'Unit tests for Mail Merge sample' - def test_project(self): - self.assertTrue(project_test()) +class TestDocsMailMerge(unittest.TestCase): + "Unit tests for Mail Merge sample" - def test_gapis(self): - self.assertTrue(gapis_test()) + def test_project(self): + self.assertTrue(project_test()) - def test_create_doc(self): - self.assertTrue(create_doc_test()) + def test_create_doc(self): + self.assertTrue(create_doc_test()) - def test_copy_doc(self): - self.assertTrue(copy_doc_test()) + def test_copy_doc(self): + self.assertTrue(copy_doc_test()) - def test_get_text_data(self): - self.assertTrue(bool(get_text_data_test())) + def test_get_text_data(self): + self.assertTrue(bool(get_text_data_test())) - def test_get_sheets_data(self): - self.assertTrue(bool(get_sheets_data_test())) + def test_get_sheets_data(self): + self.assertTrue(bool(get_sheets_data_test())) def project_test(): - 'Tests whether project credentials file was downloaded from project.' - credentials, project = google.auth.default() - - -def gapis_test(): - 'Tests whether project can connect to all 3 APIs used in the sample.' - HTTP = get_http_client() - discovery.build('drive', 'v3', http=HTTP) - discovery.build('docs', 'v1', http=HTTP) - discovery.build('sheets', 'v4', http=HTTP) - return True + "Tests whether project credentials file was downloaded from project." + credentials, project = google.auth.default() def create_doc_test(): - 'Tests whether project can create and delete a Google Docs file.' - DRIVE = discovery.build('drive', 'v3', http=get_http_client()) - DATA = { - 'name': 'Test Doc', - 'mimeType': 'application/vnd.google-apps.document', - } - doc_id = DRIVE.files().create(body=DATA, fields='id').execute().get('id') - DRIVE.files().delete(fileId=doc_id, fields='').execute() - return True + "Tests whether project can create and delete a Google Docs file." + DRIVE = discovery.build("drive", "v3", credentials=creds) + DATA = { + "name": "Test Doc", + "mimeType": "application/vnd.google-apps.document", + } + doc_id = DRIVE.files().create(body=DATA, fields="id").execute().get("id") + DRIVE.files().delete(fileId=doc_id, fields="").execute() + return True def copy_doc_test(): - 'Tests whether project can copy and delete a Google Docs file.' - DRIVE = discovery.build('drive', 'v3', http=get_http_client()) - DOCS_FILE_ID = '1Xycxuuv7OhEQUuzbt_Mw0TPMq02MseSD1vZdBJ3nLjk' - doc_id = _copy_template(DOCS_FILE_ID, 'text', DRIVE) - DRIVE.files().delete(fileId=doc_id, fields='').execute() - return True + "Tests whether project can copy and delete a Google Docs file." + DRIVE = discovery.build("drive", "v3", credentials=creds) + DOCS_FILE_ID = "1Xycxuuv7OhEQUuzbt_Mw0TPMq02MseSD1vZdBJ3nLjk" + doc_id = _copy_template(DOCS_FILE_ID, "text", DRIVE) + DRIVE.files().delete(fileId=doc_id, fields="").execute() + return True def get_text_data_test(): - 'Tests reading plain text data.' - return get_data('text') + "Tests reading plain text data." + return get_data("text") def get_sheets_data_test(): - 'Tests reading Google Sheets data.' - return get_data('sheets') + "Tests reading Google Sheets data." + return get_data("sheets") -if __name__ == '__main__': - unittest.main() +if __name__ == "__main__": + unittest.main() diff --git a/docs/output-json/output-json.py b/docs/output-json/output-json.py index 71b88e93..082b08d4 100644 --- a/docs/output-json/output-json.py +++ b/docs/output-json/output-json.py @@ -17,8 +17,6 @@ Google Docs (REST) API output-json sample app """ # [START output_json_python] -from __future__ import print_function - import json from apiclient import discovery @@ -26,23 +24,26 @@ from oauth2client import client, file, tools # Set doc ID, as found at `https://docs.google.com/document/d/YOUR_DOC_ID/edit` -DOCUMENT_ID = 'YOUR_DOC_ID' +DOCUMENT_ID = "YOUR_DOC_ID" # Set the scopes and discovery info -SCOPES = 'https://www.googleapis.com/auth/documents.readonly' -DISCOVERY_DOC = ('https://docs.googleapis.com/$discovery/rest?' - 'version=v1') +SCOPES = "https://www.googleapis.com/auth/documents.readonly" +DISCOVERY_DOC = "https://docs.googleapis.com/$discovery/rest?version=v1" # Initialize credentials and instantiate Docs API service -store = file.Storage('token.json') +store = file.Storage("token.json") creds = store.get() if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('credentials.json', SCOPES) - creds = tools.run_flow(flow, store) -service = discovery.build('docs', 'v1', http=creds.authorize( - Http()), discoveryServiceUrl=DISCOVERY_DOC) + flow = client.flow_from_clientsecrets("credentials.json", SCOPES) + creds = tools.run_flow(flow, store) +service = discovery.build( + "docs", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, +) # Do a document "get" request and print the results as formatted JSON -result = service.documents().get(documentId=DOCUMENT_ID).execute() +result = service.documents().get(documentId=DOCUMENT_ID, includeTabsContent=True).execute() print(json.dumps(result, indent=4, sort_keys=True)) # [END output_json_python] diff --git a/docs/output-json/output_json.py b/docs/output-json/output_json.py new file mode 100644 index 00000000..caebb10e --- /dev/null +++ b/docs/output-json/output_json.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# +# Copyright ©2018-2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at apache.org/licenses/LICENSE-2.0. +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +ouput-json.py (Python 2.x or 3.x) +Google Docs (REST) API output-json sample app +""" +# [START output_json_python] +import json + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + +# Set doc ID, as found at `https://docs.google.com/document/d/YOUR_DOC_ID/edit` +DOCUMENT_ID = "195j9eDD3ccgjQRttHhJPymLJUCOUjs-jmwTrekvdjFE" + +# Set the scopes and discovery info +SCOPES = "https://www.googleapis.com/auth/documents.readonly" +DISCOVERY_DOC = "https://docs.googleapis.com/$discovery/rest?version=v1" + +# Initialize credentials and instantiate Docs API service +creds, _ = google.auth.default() +# pylint: disable=maybe-no-member +try: + service = build("docs", "v1", credentials=creds) + + # Do a document "get" request and print the results as formatted JSON + + result = service.documents().get(documentId=DOCUMENT_ID).execute() + print(json.dumps(result, indent=4, sort_keys=True)) +except HttpError as error: + print(f"An error occurred: {error}") + +# [END output_json_python] diff --git a/docs/quickstart/quickstart.py b/docs/quickstart/quickstart.py index 3e4b3e45..60324159 100644 --- a/docs/quickstart/quickstart.py +++ b/docs/quickstart/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START docs_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -24,45 +22,46 @@ from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/documents.readonly'] +SCOPES = ["https://www.googleapis.com/auth/documents.readonly"] # The ID of a sample document. -DOCUMENT_ID = '195j9eDD3ccgjQRttHhJPymLJUCOUjs-jmwTrekvdjFE' +DOCUMENT_ID = "195j9eDD3ccgjQRttHhJPymLJUCOUjs-jmwTrekvdjFE" def main(): - """Shows basic usage of the Docs API. - Prints the title of a sample document. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Shows basic usage of the Docs API. + Prints the title of a sample document. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - try: - service = build('docs', 'v1', credentials=creds) + try: + service = build("docs", "v1", credentials=creds) - # Retrieve the documents contents from the Docs service. - document = service.documents().get(documentId=DOCUMENT_ID).execute() + # Retrieve the documents contents from the Docs service. + document = service.documents().get(documentId=DOCUMENT_ID).execute() - print('The title of the document is: {}'.format(document.get('title'))) - except HttpError as err: - print(err) + print(f"The title of the document is: {document.get('title')}") + except HttpError as err: + print(err) -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END docs_quickstart] diff --git a/drive/activity-v2/quickstart.py b/drive/activity-v2/quickstart.py index 8e5ef54a..6e1b7769 100644 --- a/drive/activity-v2/quickstart.py +++ b/drive/activity-v2/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START drive_activity_v2_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -24,114 +22,113 @@ from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/drive.activity.readonly'] +SCOPES = ["https://www.googleapis.com/auth/drive.activity.readonly"] def main(): - """Shows basic usage of the Drive Activity API. - - Prints information about the last 10 events that occured the user's Drive. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) - - service = build('driveactivity', 'v2', credentials=creds) - - # Call the Drive Activity API - try: - results = service.activity().query(body={ - 'pageSize': 10 - }).execute() - activities = results.get('activities', []) - - if not activities: - print('No activity.') - else: - print('Recent activity:') - for activity in activities: - time = getTimeInfo(activity) - action = getActionInfo(activity['primaryActionDetail']) - actors = map(getActorInfo, activity['actors']) - targets = map(getTargetInfo, activity['targets']) - actors_str, targets_str = "", "" - actor_name = actors_str.join(actors) - target_name = targets_str.join(targets) - - # Print the action occurred on drive with actor, target item and timestamp - print(u'{0}: {1}, {2}, {3}'.format(time, action, actor_name, target_name)) - - except HttpError as error: - # TODO(developer) - Handleerrors from drive activity API. - print(f'An error occurred: {error}') + """Shows basic usage of the Drive Activity API. + + Prints information about the last 10 events that occured the user's Drive. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) + + service = build("driveactivity", "v2", credentials=creds) + + # Call the Drive Activity API + try: + results = service.activity().query(body={"pageSize": 10}).execute() + activities = results.get("activities", []) + + if not activities: + print("No activity.") + else: + print("Recent activity:") + for activity in activities: + time = getTimeInfo(activity) + action = getActionInfo(activity["primaryActionDetail"]) + actors = map(getActorInfo, activity["actors"]) + targets = map(getTargetInfo, activity["targets"]) + actors_str, targets_str = "", "" + actor_name = actors_str.join(actors) + target_name = targets_str.join(targets) + + # Print the action occurred on drive with actor, target item and timestamp + print(f"{time}: {action}, {actor_name}, {target_name}") + + except HttpError as error: + # TODO(developer) - Handleerrors from drive activity API. + print(f"An error occurred: {error}") # Returns the name of a set property in an object, or else "unknown". def getOneOf(obj): - for key in obj: - return key - return 'unknown' + for key in obj: + return key + return "unknown" # Returns a time associated with an activity. def getTimeInfo(activity): - if 'timestamp' in activity: - return activity['timestamp'] - if 'timeRange' in activity: - return activity['timeRange']['endTime'] - return 'unknown' + if "timestamp" in activity: + return activity["timestamp"] + if "timeRange" in activity: + return activity["timeRange"]["endTime"] + return "unknown" # Returns the type of action. def getActionInfo(actionDetail): - return getOneOf(actionDetail) + return getOneOf(actionDetail) # Returns user information, or the type of user if not a known user. def getUserInfo(user): - if 'knownUser' in user: - knownUser = user['knownUser'] - isMe = knownUser.get('isCurrentUser', False) - return u'people/me' if isMe else knownUser['personName'] - return getOneOf(user) + if "knownUser" in user: + knownUser = user["knownUser"] + isMe = knownUser.get("isCurrentUser", False) + return "people/me" if isMe else knownUser["personName"] + return getOneOf(user) # Returns actor information, or the type of actor if not a user. def getActorInfo(actor): - if 'user' in actor: - return getUserInfo(actor['user']) - return getOneOf(actor) + if "user" in actor: + return getUserInfo(actor["user"]) + return getOneOf(actor) # Returns the type of a target and an associated title. def getTargetInfo(target): - if 'driveItem' in target: - title = target['driveItem'].get('title', 'unknown') - return 'driveItem:"{0}"'.format(title) - if 'drive' in target: - title = target['drive'].get('title', 'unknown') - return 'drive:"{0}"'.format(title) - if 'fileComment' in target: - parent = target['fileComment'].get('parent', {}) - title = parent.get('title', 'unknown') - return 'fileComment:"{0}"'.format(title) - return '{0}:unknown'.format(getOneOf(target)) - - -if __name__ == '__main__': - main() + if "driveItem" in target: + title = target["driveItem"].get("title", "unknown") + return f'driveItem:"{title}"' + if "drive" in target: + title = target["drive"].get("title", "unknown") + return f'drive:"{title}"' + if "fileComment" in target: + parent = target["fileComment"].get("parent", {}) + title = parent.get("title", "unknown") + return f'fileComment:"{title}"' + return f"{getOneOf(target)}:unknown" + + +if __name__ == "__main__": + main() # [END drive_activity_v2_quickstart] diff --git a/drive/driveapp/main.py b/drive/driveapp/main.py index 03da783f..fa5b1437 100644 --- a/drive/driveapp/main.py +++ b/drive/driveapp/main.py @@ -1,12 +1,24 @@ #!/usr/bin/python +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Google Drive Quickstart in Python. This script uploads a single file to Google Drive. """ -from __future__ import print_function - import googleapiclient.http import httplib2 import oauth2client.client @@ -16,57 +28,57 @@ # OAuth 2.0 scope that will be authorized. # Check https://developers.google.com/drive/scopes for all available scopes. -OAUTH2_SCOPE = 'https://www.googleapis.com/auth/drive' +OAUTH2_SCOPE = "https://www.googleapis.com/auth/drive" # Location of the client secrets. -CLIENT_SECRETS = 'client_secrets.json' +CLIENT_SECRETS = "client_secrets.json" # Path to the file to upload. -FILENAME = 'document.txt' +FILENAME = "document.txt" # Metadata about the file. -MIMETYPE = 'text/plain' -TITLE = 'My New Text Document' -DESCRIPTION = 'A shiny new text document about hello world.' +MIMETYPE = "text/plain" +TITLE = "My New Text Document" +DESCRIPTION = "A shiny new text document about hello world." # Perform OAuth2.0 authorization flow. -flow = oauth2client.client.flow_from_clientsecrets( - CLIENT_SECRETS, OAUTH2_SCOPE) +flow = oauth2client.client.flow_from_clientsecrets(CLIENT_SECRETS, OAUTH2_SCOPE) flow.redirect_uri = oauth2client.client.OOB_CALLBACK_URN authorize_url = flow.step1_get_authorize_url() -print('Go to the following link in your browser: ' + authorize_url) +print("Go to the following link in your browser: " + authorize_url) # `six` library supports Python2 and Python3 without redefining builtin input() -code = six.moves.input('Enter verification code: ').strip() +code = six.moves.input("Enter verification code: ").strip() credentials = flow.step2_exchange(code) # Create an authorized Drive API client. http = httplib2.Http() credentials.authorize(http) -drive_service = build('drive', 'v2', http=http) +drive_service = build("drive", "v2", http=http) # Insert a file. Files are comprised of contents and metadata. # MediaFileUpload abstracts uploading file contents from a file on disk. media_body = googleapiclient.http.MediaFileUpload( - FILENAME, - mimetype=MIMETYPE, - resumable=True + FILENAME, mimetype=MIMETYPE, resumable=True ) # The body contains the metadata for the file. body = { - 'title': TITLE, - 'description': DESCRIPTION, + "title": TITLE, + "description": DESCRIPTION, } # Perform the request and print the result. try: - new_file = drive_service.files().insert( - body=body, media_body=media_body).execute() - file_title = new_file.get('title') - file_desc = new_file.get('description') - if file_title == TITLE and file_desc == DESCRIPTION: - print(f"File is uploaded \nTitle : {file_title} \nDescription : {file_desc}") + new_file = ( + drive_service.files().insert(body=body, media_body=media_body).execute() + ) + file_title = new_file.get("title") + file_desc = new_file.get("description") + if file_title == TITLE and file_desc == DESCRIPTION: + print( + f"File is uploaded \nTitle : {file_title} \nDescription : {file_desc}" + ) except HttpError as error: - # TODO(developer) - Handle errors from drive API. - print(f'An error occurred: {error}') + # TODO(developer) - Handle errors from drive API. + print(f"An error occurred: {error}") diff --git a/drive/quickstart/README.md b/drive/quickstart/README.md index 2aabc91a..0c283378 100644 --- a/drive/quickstart/README.md +++ b/drive/quickstart/README.md @@ -9,7 +9,7 @@ makes requests to the Drive V3 API. - Python - Create a project - Activate the Drive API in the Google API Console([the detail page](https://developers.google.com/workspace/guides/create-project)) -- Create a OAuth client ID credential and download the json file ([the datil page](https://developers.google.com/workspace/guides/create-credentials)) +- Create a OAuth client ID credential and download the json file ([the detail page](https://developers.google.com/workspace/guides/create-credentials)) - Rename the json file ## Install diff --git a/drive/quickstart/quickstart.py b/drive/quickstart/quickstart.py index f8d0cb6c..117ba4f8 100644 --- a/drive/quickstart/quickstart.py +++ b/drive/quickstart/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START drive_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -24,50 +22,54 @@ from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'] +SCOPES = ["https://www.googleapis.com/auth/drive.metadata.readonly"] def main(): - """Shows basic usage of the Drive v3 API. - Prints the names and ids of the first 10 files the user has access to. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Shows basic usage of the Drive v3 API. + Prints the names and ids of the first 10 files the user has access to. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - try: - service = build('drive', 'v3', credentials=creds) + try: + service = build("drive", "v3", credentials=creds) - # Call the Drive v3 API - results = service.files().list( - pageSize=10, fields="nextPageToken, files(id, name)").execute() - items = results.get('files', []) + # Call the Drive v3 API + results = ( + service.files() + .list(pageSize=10, fields="nextPageToken, files(id, name)") + .execute() + ) + items = results.get("files", []) - if not items: - print('No files found.') - return - print('Files:') - for item in items: - print(u'{0} ({1})'.format(item['name'], item['id'])) - except HttpError as error: - # TODO(developer) - Handle errors from drive API. - print(f'An error occurred: {error}') + if not items: + print("No files found.") + return + print("Files:") + for item in items: + print(f"{item['name']} ({item['id']})") + except HttpError as error: + # TODO(developer) - Handle errors from drive API. + print(f"An error occurred: {error}") -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END drive_quickstart] diff --git a/drive/snippets/drive-v2/app data snippet/fetch_appdata_folder.py b/drive/snippets/drive-v2/app data snippet/fetch_appdata_folder.py new file mode 100644 index 00000000..94008a33 --- /dev/null +++ b/drive/snippets/drive-v2/app data snippet/fetch_appdata_folder.py @@ -0,0 +1,49 @@ +"""Copyright 2018 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_fetch_appdata_folder] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def fetch_appdata_folder(): + """List out application data folder and prints folder ID. + Returns : Folder ID + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # call drive api client + service = build("drive", "v2", credentials=creds) + + # pylint: disable=maybe-no-member + file = service.files().get(fileId="appDataFolder", fields="id").execute() + print(f'Folder ID: {file.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.get("id") + + +if __name__ == "__main__": + fetch_appdata_folder() +# [END drive_fetch_appdata_folder] diff --git a/drive/snippets/drive-v2/app data snippet/list_appdata.py b/drive/snippets/drive-v2/app data snippet/list_appdata.py new file mode 100644 index 00000000..3930d30b --- /dev/null +++ b/drive/snippets/drive-v2/app data snippet/list_appdata.py @@ -0,0 +1,60 @@ +"""Copyright 2018 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_list_appdata] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def list_appdata(): + """List all files inserted in the application data folder + prints file titles with Ids. + Returns : List of items + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # call drive api client + service = build("drive", "v2", credentials=creds) + + # pylint: disable=maybe-no-member + response = ( + service.files() + .list( + spaces="appDataFolder", + fields="nextPageToken, items(id, title)", + maxResults=10, + ) + .execute() + ) + for file in response.get("items", []): + # Process change + print(f'Found file: {file.get("title")} ,{file.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + response = None + + return response.get("items") + + +if __name__ == "__main__": + list_appdata() +# [END drive_list_appdata] diff --git a/drive/snippets/drive-v2/app data snippet/test_fetch_appdata_folder.py b/drive/snippets/drive-v2/app data snippet/test_fetch_appdata_folder.py new file mode 100644 index 00000000..5eddf5f8 --- /dev/null +++ b/drive/snippets/drive-v2/app data snippet/test_fetch_appdata_folder.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import fetch_appdata_folder + + +class TestFetchAppdataFolder(unittest.TestCase): + """Unit test class for Appdata snippet""" + + @classmethod + def test_list_appdata(cls): + """Test list_appdata""" + file_id = fetch_appdata_folder.fetch_appdata_folder() + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/app data snippet/test_list_appdata.py b/drive/snippets/drive-v2/app data snippet/test_list_appdata.py new file mode 100644 index 00000000..f4a959a2 --- /dev/null +++ b/drive/snippets/drive-v2/app data snippet/test_list_appdata.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import list_appdata + + +class TestListAppdata(unittest.TestCase): + """Unit test class for Appdata snippet""" + + @classmethod + def test_list_appdata(cls): + """Test list_appdata""" + files = list_appdata.list_appdata() + cls.assertNotEqual(cls, 0, len(files)) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/app data snippet/test_upload_appdata.py b/drive/snippets/drive-v2/app data snippet/test_upload_appdata.py new file mode 100644 index 00000000..e4835c35 --- /dev/null +++ b/drive/snippets/drive-v2/app data snippet/test_upload_appdata.py @@ -0,0 +1,36 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import upload_app_data + + +class TestUploadAppdata(unittest.TestCase): + """ + Unit test class for Appdata snippet + """ + + @classmethod + def test_upload_adddata(cls): + """Test upload_appdata + create a text file titled "abc.txt" in order to pass this test + """ + file_id = upload_app_data.upload_appdata() + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/app data snippet/upload_app_data.py b/drive/snippets/drive-v2/app data snippet/upload_app_data.py new file mode 100644 index 00000000..0514c07a --- /dev/null +++ b/drive/snippets/drive-v2/app data snippet/upload_app_data.py @@ -0,0 +1,59 @@ +"""Copyright 2018 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_upload_appdata] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaFileUpload + + +def upload_appdata(): + """Insert a file in the application data folder and prints file Id. + Returns : ID's of the inserted files + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # call drive api client + service = build("drive", "v2", credentials=creds) + + file_metadata = { + "title": "abc.txt", + "parents": [{"id": "appDataFolder"}], + } + media = MediaFileUpload("abc.txt", mimetype="text/txt", resumable=True) + # pylint: disable=maybe-no-member + file = ( + service.files() + .insert(body=file_metadata, media_body=media, fields="id") + .execute() + ) + print(f'File ID: {file.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.get("id") + + +if __name__ == "__main__": + upload_appdata() +# [END drive_upload_appdata] diff --git a/drive/snippets/drive-v2/change snippet/fetch_changes.py b/drive/snippets/drive-v2/change snippet/fetch_changes.py new file mode 100644 index 00000000..904132fc --- /dev/null +++ b/drive/snippets/drive-v2/change snippet/fetch_changes.py @@ -0,0 +1,62 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_fetch_changes] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def fetch_changes(saved_start_page_token): + """Retrieve the list of changes for the currently authenticated user. + prints changed file's ID + Args: + saved_start_page_token : StartPageToken for the current state of the + account. + Returns: saved start page token. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + + # Begin with our last saved start token for this user or the + # current token from getStartPageToken() + page_token = saved_start_page_token + while page_token is not None: + # pylint: disable=maybe-no-member + response = ( + service.changes().list(pageToken=page_token, spaces="drive").execute() + ) + for change in response.get("items"): + # Process change + print(f'Change found for file: {change.get("fileId")}') + if "newStartPageToken" in response: + # Last page, save this token for the next polling interval + saved_start_page_token = response.get("newStartPageToken") + page_token = response.get("nextPageToken") + + except HttpError as error: + print(f"An error occurred: {error}") + saved_start_page_token = None + + return saved_start_page_token + + +if __name__ == "__main__": + # saved_start_page_token is the token number + fetch_changes(saved_start_page_token=15) +# [END drive_fetch_changes] diff --git a/drive/snippets/drive-v2/change snippet/fetch_start_page_token.py b/drive/snippets/drive-v2/change snippet/fetch_start_page_token.py new file mode 100644 index 00000000..9d8f16cb --- /dev/null +++ b/drive/snippets/drive-v2/change snippet/fetch_start_page_token.py @@ -0,0 +1,46 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_fetch_start_page_token] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def fetch_start_page_token(): + """Retrieve page token for the current state of the account. + Returns & prints : start page token + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + + # pylint: disable=maybe-no-member + response = service.changes().getStartPageToken().execute() + print(f'Start token: {response.get("startPageToken")}') + + except HttpError as error: + print(f"An error occurred: {error}") + response = None + + return response.get("startPageToken") + + +if __name__ == "__main__": + fetch_start_page_token() +# [END drive_fetch_start_page_token] diff --git a/drive/snippets/drive-v2/change snippet/test_fetch_changes.py b/drive/snippets/drive-v2/change snippet/test_fetch_changes.py new file mode 100644 index 00000000..adb16f2d --- /dev/null +++ b/drive/snippets/drive-v2/change snippet/test_fetch_changes.py @@ -0,0 +1,34 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import fetch_changes +import fetch_start_page_token + + +class TestFetchChanges(unittest.TestCase): + """Unit test class for Change snippet""" + + @classmethod + def test_fetch_changes(cls): + """Test fetch_changes""" + start_token = fetch_start_page_token.fetch_start_page_token() + token = fetch_changes.fetch_changes(start_token) + cls.assertIsNotNone(cls, token) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/change snippet/test_fetch_start_page_token.py b/drive/snippets/drive-v2/change snippet/test_fetch_start_page_token.py new file mode 100644 index 00000000..69b2cd15 --- /dev/null +++ b/drive/snippets/drive-v2/change snippet/test_fetch_start_page_token.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import fetch_start_page_token + + +class TestFetchChanges(unittest.TestCase): + """Unit test class for Change snippet""" + + @classmethod + def test_fetch_start_page_token(cls): + """Test fetch_start_page_token""" + token = fetch_start_page_token.fetch_start_page_token() + cls.assertIsNotNone(cls, token) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/drive_snippet/create_drive.py b/drive/snippets/drive-v2/drive_snippet/create_drive.py new file mode 100644 index 00000000..a5ef4de8 --- /dev/null +++ b/drive/snippets/drive-v2/drive_snippet/create_drive.py @@ -0,0 +1,59 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_create_drive] +import uuid + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_drive(): + """Create a drive. + Returns: + Id of the created drive + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + + drive_metadata = {"name": "Project Resources"} + request_id = str(uuid.uuid4()) + # pylint: disable=maybe-no-member + drive = ( + service.drives() + .insert(body=drive_metadata, requestId=request_id, fields="id") + .execute() + ) + print(f'Drive ID: {drive.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + drive = None + + return drive.get("id") + + +if __name__ == "__main__": + create_drive() +# [END drive_create_drive] diff --git a/drive/snippets/drive-v2/drive_snippet/recover_drives.py b/drive/snippets/drive-v2/drive_snippet/recover_drives.py new file mode 100644 index 00000000..4584df3d --- /dev/null +++ b/drive/snippets/drive-v2/drive_snippet/recover_drives.py @@ -0,0 +1,93 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_recover_drives] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def recover_drives(real_user): + """Find all shared drives without an organizer and add one. + Args: + real_user:User ID for the new organizer. + Returns: + drives object + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + + drives = [] + + page_token = None + new_organizer_permission = { + "type": "user", + "role": "organizer", + "value": "user@example.com", + } + + new_organizer_permission["value"] = real_user + # pylint: disable=maybe-no-member + + while True: + response = ( + service.drives() + .list( + q="organizerCount = 0", + useDomainAdminAccess=True, + fields="nextPageToken, items(id, name)", + pageToken=page_token, + ) + .execute() + ) + for drive in response.get("items", []): + print( + "Found shared drive without organizer: " + f"{drive.get('title')}, {drive.get('id')}" + ) + permission = ( + service.permissions() + .insert( + fileId=drive.get("id"), + body=new_organizer_permission, + useDomainAdminAccess=True, + supportsAllDrives=True, + fields="id", + ) + .execute() + ) + print(f'Added organizer permission: {permission.get("id")}') + + drives.extend(response.get("items", [])) + page_token = response.get("nextPageToken", None) + if page_token is None: + break + + except HttpError as error: + print(f"An error occurred: {error}") + + return drives + + +if __name__ == "__main__": + recover_drives(real_user="gduser1@workspacesamples.dev") +# [END drive_recover_drives] diff --git a/drive/snippets/drive-v2/file snippet/create_folder.py b/drive/snippets/drive-v2/file snippet/create_folder.py new file mode 100644 index 00000000..a32b24bf --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/create_folder.py @@ -0,0 +1,53 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_create_folder] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_folder(): + """Create a folder and prints the folder ID + Returns : Folder Id + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + file_metadata = { + "title": "Invoices", + "mimeType": "application/vnd.google-apps.folder", + } + + # pylint: disable=maybe-no-member + file = service.files().insert(body=file_metadata, fields="id").execute() + print(f'Folder ID: "{file.get("id")}".') + return file.get("id") + + except HttpError as error: + print(f"An error occurred: {error}") + return None + + +if __name__ == "__main__": + create_folder() +# [END drive_create_folder] diff --git a/drive/snippets/drive-v2/file snippet/create_shortcut.py b/drive/snippets/drive-v2/file snippet/create_shortcut.py new file mode 100644 index 00000000..70665c09 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/create_shortcut.py @@ -0,0 +1,50 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_create_shortcut] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_shortcut(): + """Create a third party shortcut + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + file_metadata = { + "title": "Project plan", + "mimeType": "application/vnd.google-apps.drive-sdk", + } + # pylint: disable=maybe-no-member + file = service.files().insert(body=file_metadata, fields="id").execute() + print(f'File ID: {file.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + return file.get("id") + + +if __name__ == "__main__": + create_shortcut() +# [END drive_create_shortcut] diff --git a/drive/snippets/drive-v2/file snippet/download_file.py b/drive/snippets/drive-v2/file snippet/download_file.py new file mode 100644 index 00000000..68fe2faa --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/download_file.py @@ -0,0 +1,62 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_download_file] +import io + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaIoBaseDownload + + +def download_file(real_file_id): + """Downloads a file + Args: + real_file_id: ID of the file to download + Returns : IO object with location. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + + file_id = real_file_id + + # pylint: disable=maybe-no-member + request = service.files().get_media(fileId=file_id) + file = io.BytesIO() + downloader = MediaIoBaseDownload(file, request) + done = False + while done is False: + status, done = downloader.next_chunk() + print(f"Download {int(status.progress() * 100)}.") + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.getvalue() + + +if __name__ == "__main__": + download_file(real_file_id="1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9") +# [END drive_download_file] diff --git a/drive/snippets/drive-v2/file snippet/export_pdf.py b/drive/snippets/drive-v2/file snippet/export_pdf.py new file mode 100644 index 00000000..cde24bf0 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/export_pdf.py @@ -0,0 +1,64 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_export_pdf] +import io + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaIoBaseDownload + + +def export_pdf(real_file_id): + """Download a Document file in PDF format. + Args: + real_file_id : file ID of any workspace document format file + Returns : IO object with location + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + + file_id = real_file_id + + # pylint: disable=maybe-no-member + request = service.files().export_media( + fileId=file_id, mimeType="application/pdf" + ) + file = io.BytesIO() + downloader = MediaIoBaseDownload(file, request) + done = False + while done is False: + status, done = downloader.next_chunk() + print(f"Download {int(status.progress() * 100)}.") + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.getvalue() + + +if __name__ == "__main__": + export_pdf(real_file_id="1zbp8wAyuImX91Jt9mI-CAX_1TqkBLDEDcr2WeXBbKUY") +# [END drive_export_pdf] diff --git a/drive/snippets/drive-v2/file snippet/move_file_to_folder.py b/drive/snippets/drive-v2/file snippet/move_file_to_folder.py new file mode 100644 index 00000000..a4093ce4 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/move_file_to_folder.py @@ -0,0 +1,74 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_move_file_to_folder] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def move_file_to_folder(file_id, folder_id): + """Move specified file to the specified folder. + Args: + file_id: Id of the file to move. + folder_id: Id of the folder + Print: An object containing the new parent folder and other meta data + Returns : Parent Ids for the file + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # call drive api client + service = build("drive", "v2", credentials=creds) + + # Retrieve the existing parents to remove + # pylint: disable=maybe-no-member + file = service.files().get(fileId=file_id, fields="parents").execute() + previous_parents = ",".join( + [parent["id"] for parent in file.get("parents")] + ) + # Move the file to the new folder + file = ( + service.files() + .update( + fileId=file_id, + addParents=folder_id, + removeParents=previous_parents, + fields="id, parents", + ) + .execute() + ) + new_parent_folder_id = [parent["id"] for parent in file.get("parents")] + print( + f'file with ID : {file.get("id")} moved to folder : ' + f"{new_parent_folder_id}" + ) + return [parent["id"] for parent in file.get("parents")] + + except HttpError as error: + print(f"An error occurred: {error}") + return None + + +if __name__ == "__main__": + move_file_to_folder( + file_id="14fesChjgzDA7lUu9ZeGqXOuXMPgaVkxS", + folder_id="1KzT9gjq-AHfciwNzKjh7nUd6prrQOA4", + ) +# [END drive_move_file_to_folder] diff --git a/drive/snippets/drive-v2/file snippet/search_file.py b/drive/snippets/drive-v2/file snippet/search_file.py new file mode 100644 index 00000000..efc990d9 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/search_file.py @@ -0,0 +1,66 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_search_file] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def search_file(): + """Search file in drive location + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + files = [] + page_token = None + while True: + # pylint: disable=maybe-no-member + response = ( + service.files() + .list( + q="mimeType='image/jpeg'", + spaces="drive", + fields="nextPageToken, items(id, title)", + pageToken=page_token, + ) + .execute() + ) + for file in response.get("items", []): + # Process change + print(f'Found file: {file.get("title")}, {file.get("id")}') + files.extend(response.get("items", [])) + page_token = response.get("nextPageToken", None) + if page_token is None: + break + + except HttpError as error: + print(f"An error occurred: {error}") + files = None + + return files + + +if __name__ == "__main__": + search_file() +# [END drive_search_file] diff --git a/drive/snippets/drive-v2/file snippet/share_file.py b/drive/snippets/drive-v2/file snippet/share_file.py new file mode 100644 index 00000000..c45a11fd --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/share_file.py @@ -0,0 +1,94 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_share_file] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def share_file(real_file_id, real_user, real_domain): + """Batch permission modification. + Args: + real_file_id: file Id + real_user: User ID + real_domain: Domain of the user ID + Prints modified permissions + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + ids = [] + file_id = real_file_id + + def callback(request_id, response, exception): + if exception: + print(exception) + else: + print(f"Request_Id: {request_id}") + print(f'Permission Id: {response.get("id")}') + ids.append(response.get("id")) + + # pylint: disable=maybe-no-member + batch = service.new_batch_http_request(callback=callback) + user_permission = { + "type": "user", + "role": "writer", + "value": "user@example.com", + } + user_permission["value"] = real_user + batch.add( + service.permissions().insert( + fileId=file_id, + body=user_permission, + fields="id", + ) + ) + domain_permission = { + "type": "domain", + "role": "reader", + "value": "example.com", + } + domain_permission["value"] = real_domain + batch.add( + service.permissions().insert( + fileId=file_id, + body=domain_permission, + fields="id", + ) + ) + batch.execute() + + except HttpError as error: + print(f"An error occurred: {error}") + ids = None + + return ids + + +if __name__ == "__main__": + share_file( + real_file_id="1dUiRSoAQKkM3a4nTPeNQWgiuau1KdQ_l", + real_user="anurag@workspacesamples.dev", + real_domain="workspacesamples.dev", + ) +# [END drive_share_file] diff --git a/drive/snippets/drive-v2/file snippet/test_create_folder.py b/drive/snippets/drive-v2/file snippet/test_create_folder.py new file mode 100644 index 00000000..24eb83d0 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/test_create_folder.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import create_folder + + +class TestCreateFolder(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_create_folder(cls): + """Test create_folder""" + file_id = create_folder.create_folder() + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/file snippet/test_create_shortcut.py b/drive/snippets/drive-v2/file snippet/test_create_shortcut.py new file mode 100644 index 00000000..82789471 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/test_create_shortcut.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import create_shortcut + + +class TestCreateShortcut(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_create_shortcut(cls): + """Test create_folder""" + file_id = create_shortcut.create_shortcut() + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/file snippet/test_download_file.py b/drive/snippets/drive-v2/file snippet/test_download_file.py new file mode 100644 index 00000000..bb5455bd --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/test_download_file.py @@ -0,0 +1,34 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import download_file + + +class TestDownloadFile(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_download_file(cls): + """Test Download_file""" + # valid file id + real_file_id = "1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9" + file = download_file.download_file(real_file_id=real_file_id) + cls.assertNotEqual(cls, 0, len(file)) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/file snippet/test_export_pdf.py b/drive/snippets/drive-v2/file snippet/test_export_pdf.py new file mode 100644 index 00000000..e9dfb88f --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/test_export_pdf.py @@ -0,0 +1,34 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import export_pdf + + +class TestExportPdf(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_export_pdf(cls): + """Test export_pdf""" + # valid file ID + real_file_id = "1zbp8wAyuImX91Jt9mI-CAX_1TqkBLDEDcr2WeXBbKUY" + file = export_pdf.export_pdf(real_file_id=real_file_id) + cls.assertNotEqual(cls, 0, len(file)) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/file snippet/test_move_file_to_folder.py b/drive/snippets/drive-v2/file snippet/test_move_file_to_folder.py new file mode 100644 index 00000000..b031b13c --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/test_move_file_to_folder.py @@ -0,0 +1,37 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import move_file_to_folder + + +class TestMoveFileToFolder(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_move_file_to_folder(cls): + """Test move_file_to_folder""" + file_id = "1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9" + folder_id = "1v5eyIbXCr9TZX3eX_44HEExfe7yRj24V" + + update = move_file_to_folder.move_file_to_folder( + file_id=file_id, folder_id=folder_id + ) + cls.assertIsNotNone(cls, 0, len(update)) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/file snippet/test_search_file.py b/drive/snippets/drive-v2/file snippet/test_search_file.py new file mode 100644 index 00000000..a86d2158 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/test_search_file.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import search_file + + +class TestSearchFile(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_search_file(cls): + """Test search_file""" + file = search_file.search_file() + cls.assertNotEqual(cls, 0, len(file)) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/file snippet/test_share_file.py b/drive/snippets/drive-v2/file snippet/test_share_file.py new file mode 100644 index 00000000..4c281998 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/test_share_file.py @@ -0,0 +1,39 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import share_file + + +class TestShareFile(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_share_file(cls): + """Test share_file""" + real_file_id = "1dUiRSoAQKkM3a4nTPeNQWgiuau1KdQ_l" + real_user = "gduser1@workspacesamples.dev" + real_domain = "workspacesamples.dev" + file = share_file.share_file( + real_file_id=real_file_id, + real_user=real_user, + real_domain=real_domain, + ) + cls.assertNotEqual(cls, 0, len(file)) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/file snippet/test_touch_file.py b/drive/snippets/drive-v2/file snippet/test_touch_file.py new file mode 100644 index 00000000..f35dc08d --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/test_touch_file.py @@ -0,0 +1,39 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import re +import unittest +from datetime import datetime + +import touch_file + + +class TestTouchFile(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_touch_file(cls): + """Test touch_file""" + real_file_id = "1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9" + now = datetime.utcnow().isoformat() + "Z" + now = re.sub(r"\d{3}Z", "Z", now) # Truncate microseconds + modified_time = touch_file.touch_file( + real_file_id=real_file_id, real_timestamp=now + ) + cls.assertIsNotNone(cls, modified_time) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/file snippet/test_upload_basic.py b/drive/snippets/drive-v2/file snippet/test_upload_basic.py new file mode 100644 index 00000000..c0bb7c30 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/test_upload_basic.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import upload_basic + + +class TestUploadBasic(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_upload_basic(cls): + """Test upload_basic""" + file_id = upload_basic.upload_basic() + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/file snippet/test_upload_revision.py b/drive/snippets/drive-v2/file snippet/test_upload_revision.py new file mode 100644 index 00000000..c1ab5a6d --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/test_upload_revision.py @@ -0,0 +1,34 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import upload_revision + + +class TestUploadRevision(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_upload_revision(cls): + """Test upload_revision""" + + real_file_id = "1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9" + file_id = upload_revision.upload_revision(real_file_id=real_file_id) + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/file snippet/test_upload_to_folder.py b/drive/snippets/drive-v2/file snippet/test_upload_to_folder.py new file mode 100644 index 00000000..82a314cf --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/test_upload_to_folder.py @@ -0,0 +1,33 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import upload_to_folder + + +class TestUploadToFolder(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_upload_to_folder(cls): + """Test upload_to_folder""" + folder_id = "1s0oKEZZXjImNngxHGnY0xed6Mw-tvspu" + file_id = upload_to_folder.upload_to_folder(folder_id=folder_id) + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/file snippet/test_upload_with_conversion.py b/drive/snippets/drive-v2/file snippet/test_upload_with_conversion.py new file mode 100644 index 00000000..ce3bd168 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/test_upload_with_conversion.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import upload_with_conversion + + +class TestUploadWithConversion(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_upload_to_folder(cls): + """Test upload_with_conversion""" + file_id = upload_with_conversion.upload_with_conversion() + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v2/file snippet/touch_file.py b/drive/snippets/drive-v2/file snippet/touch_file.py new file mode 100644 index 00000000..8369b274 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/touch_file.py @@ -0,0 +1,71 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_touch_file] +from datetime import datetime + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def touch_file(real_file_id, real_timestamp): + """Change the file's modification timestamp. + Args: + real_file_id: ID of the file to change modified time + real_timestamp: Timestamp to override Modified date time of the file + Returns : Modified Date and time. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + + file_metadata = {"modifiedDate": datetime.utcnow().isoformat() + "Z"} + + file_id = real_file_id + file_metadata["modifiedDate"] = real_timestamp + # pylint: disable=maybe-no-member + file = ( + service.files() + .update( + fileId=file_id, + body=file_metadata, + setModifiedDate=True, + fields="id, modifiedDate", + ) + .execute() + ) + print(f'Modified time: {file.get("modifiedDate")}') + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.get("modifiedDate") + + +if __name__ == "__main__": + touch_file( + real_file_id="1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9", + real_timestamp="2022-03-02T05:43:27.504Z", + ) +# [END drive_touch_file] diff --git a/drive/snippets/drive-v2/file snippet/upload_basic.py b/drive/snippets/drive-v2/file snippet/upload_basic.py new file mode 100644 index 00000000..2821ed9d --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/upload_basic.py @@ -0,0 +1,57 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_upload_basic] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaFileUpload + + +def upload_basic(): + """Insert new file. + Returns : Id's of the file uploaded + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + + file_metadata = {"title": "photo.jpg"} + media = MediaFileUpload("photo.jpg", mimetype="image/jpeg") + # pylint: disable=maybe-no-member + file = ( + service.files() + .insert(body=file_metadata, media_body=media, fields="id") + .execute() + ) + print(f'File ID: {file.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.get("id") + + +if __name__ == "__main__": + upload_basic() +# [END drive_upload_basic] diff --git a/drive/snippets/drive-v2/file snippet/upload_revision.py b/drive/snippets/drive-v2/file snippet/upload_revision.py new file mode 100644 index 00000000..7cce8f5a --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/upload_revision.py @@ -0,0 +1,56 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_upload_revision] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaFileUpload + + +def upload_revision(real_file_id): + """Replace the old file with new one on same file ID + Args: ID of the file to be replaced + Returns: file ID + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + file_id = real_file_id + media = MediaFileUpload("photo.jpg", mimetype="image/jpeg", resumable=True) + # pylint: disable=maybe-no-member + file = ( + service.files() + .update(fileId=file_id, body={}, media_body=media, fields="id") + .execute() + ) + print(f'File ID: {file.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + + return file.get("id") + + +if __name__ == "__main__": + upload_revision(real_file_id="1M4xjYwPynOk5TsIWN7hcGYkFdBkPTd5F") +# [END drive_upload_revision] diff --git a/drive/snippets/drive-v2/file snippet/upload_to_folder.py b/drive/snippets/drive-v2/file snippet/upload_to_folder.py new file mode 100644 index 00000000..4b9a5179 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/upload_to_folder.py @@ -0,0 +1,57 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_upload_to_folder] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaFileUpload + + +def upload_to_folder(folder_id): + """Upload a file to the specified folder and prints file ID, folder ID + Args: Id of the folder + Returns: ID of the file uploaded + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + + file_metadata = {"title": "photo.jpg", "parents": [{"id": folder_id}]} + media = MediaFileUpload("photo.jpg", mimetype="image/jpeg", resumable=True) + # pylint: disable=maybe-no-member + file = ( + service.files() + .insert(body=file_metadata, media_body=media, fields="id") + .execute() + ) + print(f'File ID: "{file.get("id")}".') + return file.get("id") + + except HttpError as error: + print(f"An error occurred: {error}") + return None + + +if __name__ == "__main__": + upload_to_folder(folder_id="1s0oKEZZXjImNngxHGnY0xed6Mw-tvspu") +# [END drive_upload_to_folder] diff --git a/drive/snippets/drive-v2/file snippet/upload_with_conversion.py b/drive/snippets/drive-v2/file snippet/upload_with_conversion.py new file mode 100644 index 00000000..1e010f67 --- /dev/null +++ b/drive/snippets/drive-v2/file snippet/upload_with_conversion.py @@ -0,0 +1,60 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_upload_with_conversion] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaFileUpload + + +def upload_with_conversion(): + """Upload file with conversion + Returns: ID of the file uploaded + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v2", credentials=creds) + + file_metadata = { + "title": "My Report", + "mimeType": "application/vnd.google-apps.spreadsheet", + } + media = MediaFileUpload("report.csv", mimetype="text/csv", resumable=True) + # pylint: disable=maybe-no-member + file = ( + service.files() + .insert(body=file_metadata, media_body=media, fields="id") + .execute() + ) + print(f'File with ID: "{file.get("id")}" has been uploaded.') + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.get("id") + + +if __name__ == "__main__": + upload_with_conversion() +# [END drive_upload_with_conversion] diff --git a/drive/snippets/drive-v2/team_drive_snippets/create_team_drive.py b/drive/snippets/drive-v2/team_drive_snippets/create_team_drive.py new file mode 100644 index 00000000..1c1b1a79 --- /dev/null +++ b/drive/snippets/drive-v2/team_drive_snippets/create_team_drive.py @@ -0,0 +1,57 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_create_team_drive] +import uuid + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_team_drive(): + """Create a drive for team. + Returns: ID of the created drive + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # call drive api client + service = build("drive", "v2", credentials=creds) + + # pylint: disable=maybe-no-member + team_drive_metadata = {"name": "Project Resources"} + request_id = str(uuid.uuid4()) + team_drive = ( + service.teamdrives() + .insert(body=team_drive_metadata, requestId=request_id, fields="id") + .execute() + ) + print(f'Team Drive ID: {team_drive.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + team_drive = None + + return team_drive.get("id") + + +if __name__ == "__main__": + create_team_drive() +# [END drive_create_team_drive] diff --git a/drive/snippets/drive-v2/team_drive_snippets/recover_team_drives.py b/drive/snippets/drive-v2/team_drive_snippets/recover_team_drives.py new file mode 100644 index 00000000..150f9710 --- /dev/null +++ b/drive/snippets/drive-v2/team_drive_snippets/recover_team_drives.py @@ -0,0 +1,93 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_recover_team_drives] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def recover_team_drives(real_user): + """Finds all Team Drives without an organizer and add one + Args: + real_user:User ID for the new organizer. + Returns: + team drives_object. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # call drive api client + service = build("drive", "v2", credentials=creds) + + # pylint: disable=maybe-no-member + team_drives = [] + + page_token = None + new_organizer_permission = { + "type": "user", + "role": "organizer", + "value": "user@example.com", + } + new_organizer_permission["value"] = real_user + + while True: + response = ( + service.teamdrives() + .list( + q="organizerCount = 0", + useDomainAdminAccess=True, + fields="nextPageToken, items(id, name)", + pageToken=page_token, + ) + .execute() + ) + for team_drive in response.get("items", []): + print( + "Found Team Drive without organizer: " + f"{team_drive.get('title')}, {team_drive.get('id')}" + ) + permission = ( + service.permissions() + .insert( + fileId=team_drive.get("id"), + body=new_organizer_permission, + useDomainAdminAccess=True, + supportsTeamDrives=True, + fields="id", + ) + .execute() + ) + print(f'Added organizer permission: {permission.get("id")}') + + team_drives.extend(response.get("items", [])) + page_token = response.get("nextPageToken", None) + if page_token is None: + break + + except HttpError as error: + print(f"An error occurred: {error}") + team_drives = None + + print(team_drives) + + +if __name__ == "__main__": + recover_team_drives(real_user="rajesh@workspacesamples.dev") +# [END drive_recover_team_drives] diff --git a/drive/snippets/drive-v3/app_data_snippet/fetch_appdata_folder.py b/drive/snippets/drive-v3/app_data_snippet/fetch_appdata_folder.py new file mode 100644 index 00000000..2cfa9117 --- /dev/null +++ b/drive/snippets/drive-v3/app_data_snippet/fetch_appdata_folder.py @@ -0,0 +1,49 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_fetch_appdata_folder] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def fetch_appdata_folder(): + """List out application data folder and prints folder ID. + Returns : Folder ID + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # call drive api client + service = build("drive", "v3", credentials=creds) + + # pylint: disable=maybe-no-member + file = service.files().get(fileId="appDataFolder", fields="id").execute() + print(f'Folder ID: {file.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.get("id") + + +if __name__ == "__main__": + fetch_appdata_folder() +# [END drive_fetch_appdata_folder] diff --git a/drive/snippets/drive-v3/app_data_snippet/list_appdata.py b/drive/snippets/drive-v3/app_data_snippet/list_appdata.py new file mode 100644 index 00000000..e69fe22a --- /dev/null +++ b/drive/snippets/drive-v3/app_data_snippet/list_appdata.py @@ -0,0 +1,60 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_list_appdata] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def list_appdata(): + """List all files inserted in the application data folder + prints file titles with Ids. + Returns : List of items + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # call drive api client + service = build("drive", "v3", credentials=creds) + + # pylint: disable=maybe-no-member + response = ( + service.files() + .list( + spaces="appDataFolder", + fields="nextPageToken, files(id, name)", + pageSize=10, + ) + .execute() + ) + for file in response.get("files", []): + # Process change + print(f'Found file: {file.get("name")}, {file.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + response = None + + return response.get("files") + + +if __name__ == "__main__": + list_appdata() +# [END drive_list_appdata] diff --git a/drive/snippets/drive-v3/app_data_snippet/test_fetch_appdata_folder.py b/drive/snippets/drive-v3/app_data_snippet/test_fetch_appdata_folder.py new file mode 100644 index 00000000..5eddf5f8 --- /dev/null +++ b/drive/snippets/drive-v3/app_data_snippet/test_fetch_appdata_folder.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import fetch_appdata_folder + + +class TestFetchAppdataFolder(unittest.TestCase): + """Unit test class for Appdata snippet""" + + @classmethod + def test_list_appdata(cls): + """Test list_appdata""" + file_id = fetch_appdata_folder.fetch_appdata_folder() + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/app_data_snippet/test_list_appdata.py b/drive/snippets/drive-v3/app_data_snippet/test_list_appdata.py new file mode 100644 index 00000000..f4a959a2 --- /dev/null +++ b/drive/snippets/drive-v3/app_data_snippet/test_list_appdata.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import list_appdata + + +class TestListAppdata(unittest.TestCase): + """Unit test class for Appdata snippet""" + + @classmethod + def test_list_appdata(cls): + """Test list_appdata""" + files = list_appdata.list_appdata() + cls.assertNotEqual(cls, 0, len(files)) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/app_data_snippet/test_upload_appdata.py b/drive/snippets/drive-v3/app_data_snippet/test_upload_appdata.py new file mode 100644 index 00000000..e4835c35 --- /dev/null +++ b/drive/snippets/drive-v3/app_data_snippet/test_upload_appdata.py @@ -0,0 +1,36 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import upload_app_data + + +class TestUploadAppdata(unittest.TestCase): + """ + Unit test class for Appdata snippet + """ + + @classmethod + def test_upload_adddata(cls): + """Test upload_appdata + create a text file titled "abc.txt" in order to pass this test + """ + file_id = upload_app_data.upload_appdata() + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/app_data_snippet/upload_appdata.py b/drive/snippets/drive-v3/app_data_snippet/upload_appdata.py new file mode 100644 index 00000000..c35f3e66 --- /dev/null +++ b/drive/snippets/drive-v3/app_data_snippet/upload_appdata.py @@ -0,0 +1,56 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_upload_appdata] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaFileUpload + + +def upload_appdata(): + """Insert a file in the application data folder and prints file Id. + Returns : ID's of the inserted files + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # call drive api client + service = build("drive", "v3", credentials=creds) + + # pylint: disable=maybe-no-member + file_metadata = {"name": "abc.txt", "parents": ["appDataFolder"]} + media = MediaFileUpload("abc.txt", mimetype="text/txt", resumable=True) + file = ( + service.files() + .create(body=file_metadata, media_body=media, fields="id") + .execute() + ) + print(f'File ID: {file.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.get("id") + + +if __name__ == "__main__": + upload_appdata() +# [END drive_upload_appdata] diff --git a/drive/snippets/drive-v3/change_snippet/fetch_changes.py b/drive/snippets/drive-v3/change_snippet/fetch_changes.py new file mode 100644 index 00000000..d6809e51 --- /dev/null +++ b/drive/snippets/drive-v3/change_snippet/fetch_changes.py @@ -0,0 +1,64 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_fetch_changes] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def fetch_changes(saved_start_page_token): + """Retrieve the list of changes for the currently authenticated user. + prints changed file's ID + Args: + saved_start_page_token : StartPageToken for the current state of the + account. + Returns: saved start page token. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + + # Begin with our last saved start token for this user or the + # current token from getStartPageToken() + page_token = saved_start_page_token + # pylint: disable=maybe-no-member + + while page_token is not None: + response = ( + service.changes().list(pageToken=page_token, spaces="drive").execute() + ) + for change in response.get("changes"): + # Process change + print(f'Change found for file: {change.get("fileId")}') + if "newStartPageToken" in response: + # Last page, save this token for the next polling interval + saved_start_page_token = response.get("newStartPageToken") + page_token = response.get("nextPageToken") + + except HttpError as error: + print(f"An error occurred: {error}") + saved_start_page_token = None + + return saved_start_page_token + + +if __name__ == "__main__": + # saved_start_page_token is the token number + fetch_changes(saved_start_page_token=209) +# [END drive_fetch_changes] diff --git a/drive/snippets/drive-v3/change_snippet/fetch_start_page_token.py b/drive/snippets/drive-v3/change_snippet/fetch_start_page_token.py new file mode 100644 index 00000000..df36b35a --- /dev/null +++ b/drive/snippets/drive-v3/change_snippet/fetch_start_page_token.py @@ -0,0 +1,47 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_fetch_start_page_token] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def fetch_start_page_token(): + """Retrieve page token for the current state of the account. + Returns & prints : start page token + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + + # pylint: disable=maybe-no-member + response = service.changes().getStartPageToken().execute() + print(f'Start token: {response.get("startPageToken")}') + + except HttpError as error: + print(f"An error occurred: {error}") + response = None + + return response.get("startPageToken") + + +if __name__ == "__main__": + fetch_start_page_token() +# [END drive_fetch_start_page_token] diff --git a/drive/snippets/drive-v3/change_snippet/test_fetch_changes.py b/drive/snippets/drive-v3/change_snippet/test_fetch_changes.py new file mode 100644 index 00000000..adb16f2d --- /dev/null +++ b/drive/snippets/drive-v3/change_snippet/test_fetch_changes.py @@ -0,0 +1,34 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import fetch_changes +import fetch_start_page_token + + +class TestFetchChanges(unittest.TestCase): + """Unit test class for Change snippet""" + + @classmethod + def test_fetch_changes(cls): + """Test fetch_changes""" + start_token = fetch_start_page_token.fetch_start_page_token() + token = fetch_changes.fetch_changes(start_token) + cls.assertIsNotNone(cls, token) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/change_snippet/test_fetch_start_page_token.py b/drive/snippets/drive-v3/change_snippet/test_fetch_start_page_token.py new file mode 100644 index 00000000..69b2cd15 --- /dev/null +++ b/drive/snippets/drive-v3/change_snippet/test_fetch_start_page_token.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import fetch_start_page_token + + +class TestFetchChanges(unittest.TestCase): + """Unit test class for Change snippet""" + + @classmethod + def test_fetch_start_page_token(cls): + """Test fetch_start_page_token""" + token = fetch_start_page_token.fetch_start_page_token() + cls.assertIsNotNone(cls, token) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/drive_snippet/create_drive.py b/drive/snippets/drive-v3/drive_snippet/create_drive.py new file mode 100644 index 00000000..871a495f --- /dev/null +++ b/drive/snippets/drive-v3/drive_snippet/create_drive.py @@ -0,0 +1,59 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_create_drive] +import uuid + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_drive(): + """Create a drive. + Returns: + Id of the created drive + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + + drive_metadata = {"name": "Project Resources"} + request_id = str(uuid.uuid4()) + # pylint: disable=maybe-no-member + drive = ( + service.drives() + .create(body=drive_metadata, requestId=request_id, fields="id") + .execute() + ) + print(f'Drive ID: {drive.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + drive = None + + return drive.get("id") + + +if __name__ == "__main__": + create_drive() +# [END drive_create_drive] diff --git a/drive/snippets/drive-v3/drive_snippet/recover_drives.py b/drive/snippets/drive-v3/drive_snippet/recover_drives.py new file mode 100644 index 00000000..2f1159c7 --- /dev/null +++ b/drive/snippets/drive-v3/drive_snippet/recover_drives.py @@ -0,0 +1,92 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_recover_drives] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def recover_drives(real_user): + """Find all shared drives without an organizer and add one. + Args: + real_user:User ID for the new organizer. + Returns: + drives object + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + + drives = [] + + # pylint: disable=maybe-no-member + page_token = None + new_organizer_permission = { + "type": "user", + "role": "organizer", + "emailAddress": "user@example.com", + } + new_organizer_permission["emailAddress"] = real_user + + while True: + response = ( + service.drives() + .list( + q="organizerCount = 0", + fields="nextPageToken, drives(id, name)", + useDomainAdminAccess=True, + pageToken=page_token, + ) + .execute() + ) + for drive in response.get("drives", []): + print( + "Found shared drive without organizer: " + f"{drive.get('title')}, {drive.get('id')}" + ) + permission = ( + service.permissions() + .create( + fileId=drive.get("id"), + body=new_organizer_permission, + useDomainAdminAccess=True, + supportsAllDrives=True, + fields="id", + ) + .execute() + ) + print(f'Added organizer permission: {permission.get("id")}') + + drives.extend(response.get("drives", [])) + page_token = response.get("nextPageToken", None) + if page_token is None: + break + + except HttpError as error: + print(f"An error occurred: {error}") + + return drives + + +if __name__ == "__main__": + recover_drives(real_user="gduser1@workspacesamples.dev") +# [END drive_recover_drives] diff --git a/drive/snippets/drive-v3/file_snippet/create_folder.py b/drive/snippets/drive-v3/file_snippet/create_folder.py new file mode 100644 index 00000000..b0fc814e --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/create_folder.py @@ -0,0 +1,53 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_create_folder] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_folder(): + """Create a folder and prints the folder ID + Returns : Folder Id + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + file_metadata = { + "name": "Invoices", + "mimeType": "application/vnd.google-apps.folder", + } + + # pylint: disable=maybe-no-member + file = service.files().create(body=file_metadata, fields="id").execute() + print(f'Folder ID: "{file.get("id")}".') + return file.get("id") + + except HttpError as error: + print(f"An error occurred: {error}") + return None + + +if __name__ == "__main__": + create_folder() +# [END drive_create_folder] diff --git a/drive/snippets/drive-v3/file_snippet/create_shortcut.py b/drive/snippets/drive-v3/file_snippet/create_shortcut.py new file mode 100644 index 00000000..435b7b4d --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/create_shortcut.py @@ -0,0 +1,51 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_create_shortcut] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_shortcut(): + """Create a third party shortcut + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + file_metadata = { + "name": "Project plan", + "mimeType": "application/vnd.google-apps.drive-sdk", + } + + # pylint: disable=maybe-no-member + file = service.files().create(body=file_metadata, fields="id").execute() + print(f'File ID: {file.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + return file.get("id") + + +if __name__ == "__main__": + create_shortcut() +# [END drive_create_shortcut] diff --git a/drive/snippets/drive-v3/file_snippet/download_file.py b/drive/snippets/drive-v3/file_snippet/download_file.py new file mode 100644 index 00000000..c0345524 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/download_file.py @@ -0,0 +1,62 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_download_file] +import io + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaIoBaseDownload + + +def download_file(real_file_id): + """Downloads a file + Args: + real_file_id: ID of the file to download + Returns : IO object with location. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + + file_id = real_file_id + + # pylint: disable=maybe-no-member + request = service.files().get_media(fileId=file_id) + file = io.BytesIO() + downloader = MediaIoBaseDownload(file, request) + done = False + while done is False: + status, done = downloader.next_chunk() + print(f"Download {int(status.progress() * 100)}.") + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.getvalue() + + +if __name__ == "__main__": + download_file(real_file_id="1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9") +# [END drive_download_file] diff --git a/drive/snippets/drive-v3/file_snippet/export_pdf.py b/drive/snippets/drive-v3/file_snippet/export_pdf.py new file mode 100644 index 00000000..4588cbbf --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/export_pdf.py @@ -0,0 +1,64 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_export_pdf] +import io + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaIoBaseDownload + + +def export_pdf(real_file_id): + """Download a Document file in PDF format. + Args: + real_file_id : file ID of any workspace document format file + Returns : IO object with location + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + + file_id = real_file_id + + # pylint: disable=maybe-no-member + request = service.files().export_media( + fileId=file_id, mimeType="application/pdf" + ) + file = io.BytesIO() + downloader = MediaIoBaseDownload(file, request) + done = False + while done is False: + status, done = downloader.next_chunk() + print(f"Download {int(status.progress() * 100)}.") + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.getvalue() + + +if __name__ == "__main__": + export_pdf(real_file_id="1zbp8wAyuImX91Jt9mI-CAX_1TqkBLDEDcr2WeXBbKUY") +# [END drive_export_pdf] diff --git a/drive/snippets/drive-v3/file_snippet/move_file_to_folder.py b/drive/snippets/drive-v3/file_snippet/move_file_to_folder.py new file mode 100644 index 00000000..5d3a0a28 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/move_file_to_folder.py @@ -0,0 +1,67 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_move_file_to_folder] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def move_file_to_folder(file_id, folder_id): + """Move specified file to the specified folder. + Args: + file_id: Id of the file to move. + folder_id: Id of the folder + Print: An object containing the new parent folder and other meta data + Returns : Parent Ids for the file + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # call drive api client + service = build("drive", "v3", credentials=creds) + + # pylint: disable=maybe-no-member + # Retrieve the existing parents to remove + file = service.files().get(fileId=file_id, fields="parents").execute() + previous_parents = ",".join(file.get("parents")) + # Move the file to the new folder + file = ( + service.files() + .update( + fileId=file_id, + addParents=folder_id, + removeParents=previous_parents, + fields="id, parents", + ) + .execute() + ) + return file.get("parents") + + except HttpError as error: + print(f"An error occurred: {error}") + return None + + +if __name__ == "__main__": + move_file_to_folder( + file_id="1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9", + folder_id="1jvTFoyBhUspwDncOTB25kb9k0Fl0EqeN", + ) +# [END drive_move_file_to_folder] diff --git a/drive/snippets/drive-v3/file_snippet/search_file.py b/drive/snippets/drive-v3/file_snippet/search_file.py new file mode 100644 index 00000000..fc17b006 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/search_file.py @@ -0,0 +1,66 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_search_file] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def search_file(): + """Search file in drive location + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + files = [] + page_token = None + while True: + # pylint: disable=maybe-no-member + response = ( + service.files() + .list( + q="mimeType='image/jpeg'", + spaces="drive", + fields="nextPageToken, files(id, name)", + pageToken=page_token, + ) + .execute() + ) + for file in response.get("files", []): + # Process change + print(f'Found file: {file.get("name")}, {file.get("id")}') + files.extend(response.get("files", [])) + page_token = response.get("nextPageToken", None) + if page_token is None: + break + + except HttpError as error: + print(f"An error occurred: {error}") + files = None + + return files + + +if __name__ == "__main__": + search_file() +# [END drive_search_file] diff --git a/drive/snippets/drive-v3/file_snippet/share_file.py b/drive/snippets/drive-v3/file_snippet/share_file.py new file mode 100644 index 00000000..8850aa5e --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/share_file.py @@ -0,0 +1,97 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_share_file] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def share_file(real_file_id, real_user, real_domain): + """Batch permission modification. + Args: + real_file_id: file Id + real_user: User ID + real_domain: Domain of the user ID + Prints modified permissions + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + ids = [] + file_id = real_file_id + + def callback(request_id, response, exception): + if exception: + # Handle error + print(exception) + else: + print(f"Request_Id: {request_id}") + print(f'Permission Id: {response.get("id")}') + ids.append(response.get("id")) + + # pylint: disable=maybe-no-member + batch = service.new_batch_http_request(callback=callback) + user_permission = { + "type": "user", + "role": "writer", + "emailAddress": "user@example.com", + } + # [START_EXCLUDE silent] + user_permission["emailAddress"] = real_user + # [END_EXCLUDE] + batch.add( + service.permissions().create( + fileId=file_id, + body=user_permission, + fields="id", + ) + ) + domain_permission = { + "type": "domain", + "role": "reader", + "domain": "example.com", + } + domain_permission["domain"] = real_domain + batch.add( + service.permissions().create( + fileId=file_id, + body=domain_permission, + fields="id", + ) + ) + batch.execute() + + except HttpError as error: + print(f"An error occurred: {error}") + ids = None + + return ids + + +if __name__ == "__main__": + share_file( + real_file_id="1dUiRSoAQKkM3a4nTPeNQWgiuau1KdQ_l", + real_user="gduser1@workspacesamples.dev", + real_domain="workspacesamples.dev", + ) +# [END drive_share_file] diff --git a/drive/snippets/drive-v3/file_snippet/test_create_folder.py b/drive/snippets/drive-v3/file_snippet/test_create_folder.py new file mode 100644 index 00000000..24eb83d0 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/test_create_folder.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import create_folder + + +class TestCreateFolder(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_create_folder(cls): + """Test create_folder""" + file_id = create_folder.create_folder() + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/file_snippet/test_create_shortcut.py b/drive/snippets/drive-v3/file_snippet/test_create_shortcut.py new file mode 100644 index 00000000..82789471 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/test_create_shortcut.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import create_shortcut + + +class TestCreateShortcut(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_create_shortcut(cls): + """Test create_folder""" + file_id = create_shortcut.create_shortcut() + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/file_snippet/test_download_file.py b/drive/snippets/drive-v3/file_snippet/test_download_file.py new file mode 100644 index 00000000..bb5455bd --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/test_download_file.py @@ -0,0 +1,34 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import download_file + + +class TestDownloadFile(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_download_file(cls): + """Test Download_file""" + # valid file id + real_file_id = "1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9" + file = download_file.download_file(real_file_id=real_file_id) + cls.assertNotEqual(cls, 0, len(file)) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/file_snippet/test_export_pdf.py b/drive/snippets/drive-v3/file_snippet/test_export_pdf.py new file mode 100644 index 00000000..e9dfb88f --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/test_export_pdf.py @@ -0,0 +1,34 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import export_pdf + + +class TestExportPdf(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_export_pdf(cls): + """Test export_pdf""" + # valid file ID + real_file_id = "1zbp8wAyuImX91Jt9mI-CAX_1TqkBLDEDcr2WeXBbKUY" + file = export_pdf.export_pdf(real_file_id=real_file_id) + cls.assertNotEqual(cls, 0, len(file)) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/file_snippet/test_move_file_to_folder.py b/drive/snippets/drive-v3/file_snippet/test_move_file_to_folder.py new file mode 100644 index 00000000..b031b13c --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/test_move_file_to_folder.py @@ -0,0 +1,37 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import move_file_to_folder + + +class TestMoveFileToFolder(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_move_file_to_folder(cls): + """Test move_file_to_folder""" + file_id = "1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9" + folder_id = "1v5eyIbXCr9TZX3eX_44HEExfe7yRj24V" + + update = move_file_to_folder.move_file_to_folder( + file_id=file_id, folder_id=folder_id + ) + cls.assertIsNotNone(cls, 0, len(update)) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/file_snippet/test_search_file.py b/drive/snippets/drive-v3/file_snippet/test_search_file.py new file mode 100644 index 00000000..a86d2158 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/test_search_file.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import search_file + + +class TestSearchFile(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_search_file(cls): + """Test search_file""" + file = search_file.search_file() + cls.assertNotEqual(cls, 0, len(file)) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/file_snippet/test_share_file.py b/drive/snippets/drive-v3/file_snippet/test_share_file.py new file mode 100644 index 00000000..4c281998 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/test_share_file.py @@ -0,0 +1,39 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import share_file + + +class TestShareFile(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_share_file(cls): + """Test share_file""" + real_file_id = "1dUiRSoAQKkM3a4nTPeNQWgiuau1KdQ_l" + real_user = "gduser1@workspacesamples.dev" + real_domain = "workspacesamples.dev" + file = share_file.share_file( + real_file_id=real_file_id, + real_user=real_user, + real_domain=real_domain, + ) + cls.assertNotEqual(cls, 0, len(file)) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/file_snippet/test_touch_file.py b/drive/snippets/drive-v3/file_snippet/test_touch_file.py new file mode 100644 index 00000000..f35dc08d --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/test_touch_file.py @@ -0,0 +1,39 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import re +import unittest +from datetime import datetime + +import touch_file + + +class TestTouchFile(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_touch_file(cls): + """Test touch_file""" + real_file_id = "1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9" + now = datetime.utcnow().isoformat() + "Z" + now = re.sub(r"\d{3}Z", "Z", now) # Truncate microseconds + modified_time = touch_file.touch_file( + real_file_id=real_file_id, real_timestamp=now + ) + cls.assertIsNotNone(cls, modified_time) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/file_snippet/test_upload_basic.py b/drive/snippets/drive-v3/file_snippet/test_upload_basic.py new file mode 100644 index 00000000..c0bb7c30 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/test_upload_basic.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import upload_basic + + +class TestUploadBasic(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_upload_basic(cls): + """Test upload_basic""" + file_id = upload_basic.upload_basic() + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/file_snippet/test_upload_revision.py b/drive/snippets/drive-v3/file_snippet/test_upload_revision.py new file mode 100644 index 00000000..c1ab5a6d --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/test_upload_revision.py @@ -0,0 +1,34 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import upload_revision + + +class TestUploadRevision(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_upload_revision(cls): + """Test upload_revision""" + + real_file_id = "1KuPmvGq8yoYgbfW74OENMCB5H0n_2Jm9" + file_id = upload_revision.upload_revision(real_file_id=real_file_id) + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/file_snippet/test_upload_to_folder.py b/drive/snippets/drive-v3/file_snippet/test_upload_to_folder.py new file mode 100644 index 00000000..82a314cf --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/test_upload_to_folder.py @@ -0,0 +1,33 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import upload_to_folder + + +class TestUploadToFolder(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_upload_to_folder(cls): + """Test upload_to_folder""" + folder_id = "1s0oKEZZXjImNngxHGnY0xed6Mw-tvspu" + file_id = upload_to_folder.upload_to_folder(folder_id=folder_id) + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/file_snippet/test_upload_with_conversion.py b/drive/snippets/drive-v3/file_snippet/test_upload_with_conversion.py new file mode 100644 index 00000000..ce3bd168 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/test_upload_with_conversion.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import upload_with_conversion + + +class TestUploadWithConversion(unittest.TestCase): + """Unit test class for file snippet""" + + @classmethod + def test_upload_to_folder(cls): + """Test upload_with_conversion""" + file_id = upload_with_conversion.upload_with_conversion() + cls.assertIsNotNone(cls, file_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/drive/snippets/drive-v3/file_snippet/touch_file.py b/drive/snippets/drive-v3/file_snippet/touch_file.py new file mode 100644 index 00000000..c27ec382 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/touch_file.py @@ -0,0 +1,65 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_touch_file] +from datetime import datetime + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def touch_file(real_file_id, real_timestamp): + """Change the file's modification timestamp. + Args: + real_file_id: ID of the file to change modified time + real_timestamp: Timestamp to override Modified date time of the file + Returns : Modified Date and time. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + + file_metadata = {"modifiedTime": datetime.utcnow().isoformat() + "Z"} + # pylint: disable=maybe-no-member + file_id = real_file_id + file_metadata["modifiedTime"] = real_timestamp + file = ( + service.files() + .update(fileId=file_id, body=file_metadata, fields="id, modifiedTime") + .execute() + ) + print(f'Modified time: {file.get("modifiedTime")}') + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.get("modifiedDate") + + +if __name__ == "__main__": + touch_file( + real_file_id="17EqlSf7FpPU95SS00sICyVzQHpeET1cz", + real_timestamp="2022-03-02T05:43:27.504Z", + ) +# [END drive_touch_file] diff --git a/drive/snippets/drive-v3/file_snippet/upload_basic.py b/drive/snippets/drive-v3/file_snippet/upload_basic.py new file mode 100644 index 00000000..1795c4d8 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/upload_basic.py @@ -0,0 +1,57 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_upload_basic] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaFileUpload + + +def upload_basic(): + """Insert new file. + Returns : Id's of the file uploaded + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + + file_metadata = {"name": "download.jpeg"} + media = MediaFileUpload("download.jpeg", mimetype="image/jpeg") + # pylint: disable=maybe-no-member + file = ( + service.files() + .create(body=file_metadata, media_body=media, fields="id") + .execute() + ) + print(f'File ID: {file.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.get("id") + + +if __name__ == "__main__": + upload_basic() +# [END drive_upload_basic] diff --git a/drive/snippets/drive-v3/file_snippet/upload_revision.py b/drive/snippets/drive-v3/file_snippet/upload_revision.py new file mode 100644 index 00000000..2fd6f3ff --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/upload_revision.py @@ -0,0 +1,58 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_upload_revision] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaFileUpload + + +def upload_revision(real_file_id): + """Replace the old file with new one on same file ID + Args: ID of the file to be replaced + Returns: file ID + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + file_id = real_file_id + media = MediaFileUpload( + "download.jpeg", mimetype="image/jpeg", resumable=True + ) + # pylint: disable=maybe-no-member + file = ( + service.files() + .update(fileId=file_id, body={}, media_body=media, fields="id") + .execute() + ) + print(f'File ID: {file.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + + return file.get("id") + + +if __name__ == "__main__": + upload_revision(real_file_id="1jJTiihczk_xSNPVLwMySQBJACXYdpGTi") +# [END drive_upload_revision] diff --git a/drive/snippets/drive-v3/file_snippet/upload_to_folder.py b/drive/snippets/drive-v3/file_snippet/upload_to_folder.py new file mode 100644 index 00000000..13441920 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/upload_to_folder.py @@ -0,0 +1,59 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_upload_to_folder] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaFileUpload + + +def upload_to_folder(folder_id): + """Upload a file to the specified folder and prints file ID, folder ID + Args: Id of the folder + Returns: ID of the file uploaded + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + + file_metadata = {"name": "photo.jpg", "parents": [folder_id]} + media = MediaFileUpload( + "download.jpeg", mimetype="image/jpeg", resumable=True + ) + # pylint: disable=maybe-no-member + file = ( + service.files() + .create(body=file_metadata, media_body=media, fields="id") + .execute() + ) + print(f'File ID: "{file.get("id")}".') + return file.get("id") + + except HttpError as error: + print(f"An error occurred: {error}") + return None + + +if __name__ == "__main__": + upload_to_folder(folder_id="1s0oKEZZXjImNngxHGnY0xed6Mw-tvspu") +# [END drive_upload_to_folder] diff --git a/drive/snippets/drive-v3/file_snippet/upload_with_conversion.py b/drive/snippets/drive-v3/file_snippet/upload_with_conversion.py new file mode 100644 index 00000000..cff3c070 --- /dev/null +++ b/drive/snippets/drive-v3/file_snippet/upload_with_conversion.py @@ -0,0 +1,60 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_upload_with_conversion] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError +from googleapiclient.http import MediaFileUpload + + +def upload_with_conversion(): + """Upload file with conversion + Returns: ID of the file uploaded + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create drive api client + service = build("drive", "v3", credentials=creds) + + file_metadata = { + "name": "My Report", + "mimeType": "application/vnd.google-apps.spreadsheet", + } + media = MediaFileUpload("report.csv", mimetype="text/csv", resumable=True) + # pylint: disable=maybe-no-member + file = ( + service.files() + .create(body=file_metadata, media_body=media, fields="id") + .execute() + ) + print(f'File with ID: "{file.get("id")}" has been uploaded.') + + except HttpError as error: + print(f"An error occurred: {error}") + file = None + + return file.get("id") + + +if __name__ == "__main__": + upload_with_conversion() +# [END drive_upload_with_conversion] diff --git a/drive/snippets/drive-v3/team_drive_snippets/create_team_drive.py b/drive/snippets/drive-v3/team_drive_snippets/create_team_drive.py new file mode 100644 index 00000000..cca27ce2 --- /dev/null +++ b/drive/snippets/drive-v3/team_drive_snippets/create_team_drive.py @@ -0,0 +1,57 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_create_team_drive] +import uuid + +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_team_drive(): + """Create a drive for team. + Returns: ID of the created drive + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # call drive api client + service = build("drive", "v3", credentials=creds) + + # pylint: disable=maybe-no-member + team_drive_metadata = {"name": "Project Resources"} + request_id = str(uuid.uuid4()) + team_drive = ( + service.teamdrives() + .create(body=team_drive_metadata, requestId=request_id, fields="id") + .execute() + ) + print(f'Team Drive ID: {team_drive.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + team_drive = None + + return team_drive.get("id") + + +if __name__ == "__main__": + create_team_drive() +# [END drive_create_team_drive] diff --git a/drive/snippets/drive-v3/team_drive_snippets/recover_team_drives.py b/drive/snippets/drive-v3/team_drive_snippets/recover_team_drives.py new file mode 100644 index 00000000..3a082cbf --- /dev/null +++ b/drive/snippets/drive-v3/team_drive_snippets/recover_team_drives.py @@ -0,0 +1,95 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START drive_recover_team_drives] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def recover_team_drives(real_user): + """Finds all Team Drives without an organizer and add one + Args: + real_user:User ID for the new organizer. + Returns: + team drives_object. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # call drive api client + service = build("drive", "v3", credentials=creds) + + # pylint: disable=maybe-no-member + team_drives = [] + + page_token = None + new_organizer_permission = { + "type": "user", + "role": "organizer", + "value": "user@example.com", + } + + new_organizer_permission["emailAddress"] = real_user + + while True: + response = ( + service.teamdrives() + .list( + q="organizerCount = 0", + fields="nextPageToken, teamDrives(id, name)", + useDomainAdminAccess=True, + pageToken=page_token, + ) + .execute() + ) + + for team_drive in response.get("teamDrives", []): + print( + "Found Team Drive without organizer: {team_drive.get(" + '"title")},{team_drive.get("id")}' + ) + permission = ( + service.permissions() + .create( + fileId=team_drive.get("id"), + body=new_organizer_permission, + useDomainAdminAccess=True, + supportsTeamDrives=True, + fields="id", + ) + .execute() + ) + print(f'Added organizer permission:{permission.get("id")}') + + team_drives.extend(response.get("teamDrives", [])) + page_token = response.get("nextPageToken", None) + if page_token is None: + break + + except HttpError as error: + print(f"An error occurred: {error}") + team_drives = None + + print(team_drives) + + +if __name__ == "__main__": + recover_team_drives(real_user="gduser1@workspacesamples.dev") +# [END drive_recover_team_drives] diff --git a/events/next18/customer_data_service.py b/events/next18/customer_data_service.py index d7e2e143..84766454 100644 --- a/events/next18/customer_data_service.py +++ b/events/next18/customer_data_service.py @@ -22,106 +22,110 @@ class CustomerDataService(object): - _CUSTOMER_DATA = { - 'mars': { - 'customer_name': 'Mars Inc.', - 'customer_logo': - 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/' + - 'OSIRIS_Mars_true_color.jpg/550px-OSIRIS_Mars_true_color.jpg', - 'curr_q': 'Q2', - 'curr_q_total_sales': '$2,532,124', - 'curr_q_qoq': '0.054', - 'prev_q': 'Q1', - 'prev_q_total_sales': '$2,413,584', - 'next_q': 'Q3', - 'next_q_total_sales_proj': '$2,634,765', - 'next_q_qoq_proj': '0.041', - 'top1_sku': 'Phobos', - 'top1_sales': '$334,384', - 'top2_sku': 'Deimos', - 'top2_sales': '$315,718', - 'top3_sku': 'Charon', - 'top3_sales': '$285,727', - 'top4_sku': 'Nix', - 'top4_sales': '$264,023', - 'top5_sku': 'Hydra', - 'top5_sales': '$212,361', - }, - 'jupiter': { - 'customer_name': 'Jupiter LLC', - 'customer_logo': - 'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/' + - 'Jupiter_and_its_shrunken_Great_Red_Spot.jpg/660px-Jupiter_' + - 'and_its_shrunken_Great_Red_Spot.jpg', - 'curr_q': 'Q2', - 'curr_q_total_sales': '$1,532,124', - 'curr_q_qoq': '0.031', - 'prev_q': 'Q1', - 'prev_q_total_sales': '$1,413,584', - 'next_q': 'Q3', - 'next_q_total_sales_proj': '$1,634,765', - 'next_q_qoq_proj': '0.021', - 'top1_sku': 'Io', - 'top1_sales': '$234,384', - 'top2_sku': 'Europa', - 'top2_sales': '$215,718', - 'top3_sku': 'Ganymede', - 'top3_sales': '$185,727', - 'top4_sku': 'Callisto', - 'top4_sales': '$164,023', - 'top5_sku': 'Amalthea', - 'top5_sales': '$112,361', - }, - 'saturn': { - 'customer_name': 'Saturn', - 'customer_logo': - 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/' + - 'Saturn_during_Equinox.jpg/800px-Saturn_during_Equinox.jpg', - 'curr_q': 'Q2', - 'curr_q_total_sales': '$2,532,124', - 'curr_q_qoq': '0.032', - 'prev_q': 'Q1', - 'prev_q_total_sales': '$2,413,584', - 'next_q': 'Q3', - 'next_q_total_sales_proj': '$2,634,765', - 'next_q_qoq_proj': '0.029', - 'top1_sku': 'Mimas', - 'top1_sales': '$334,384', - 'top2_sku': 'Enceladus', - 'top2_sales': '$315,718', - 'top3_sku': 'Tethys', - 'top3_sales': '$285,727', - 'top4_sku': 'Dione', - 'top4_sales': '$264,023', - 'top5_sku': 'Rhea', - 'top5_sales': '$212,361', - }, - 'neptune': { - 'customer_name': 'Neptune', - 'customer_logo': - 'https://upload.wikimedia.org/wikipedia/commons/thumb/5/56/' + - 'Neptune_Full.jpg/600px-Neptune_Full.jpg', - 'curr_q': 'Q2', - 'curr_q_total_sales': '$2,532,124', - 'curr_q_qoq': '0.027', - 'prev_q': 'Q1', - 'prev_q_total_sales': '$2,413,584', - 'next_q': 'Q3', - 'next_q_total_sales_proj': '$2,634,765', - 'next_q_qoq_proj': '0.039', - 'top1_sku': 'Triton', - 'top1_sales': '$334,384', - 'top2_sku': 'Nereid', - 'top2_sales': '$315,718', - 'top3_sku': 'Naiad', - 'top3_sales': '$285,727', - 'top4_sku': 'Thalassa', - 'top4_sales': '$264,023', - 'top5_sku': 'Despina', - 'top5_sales': '$212,361', - }, - } + _CUSTOMER_DATA = { + "mars": { + "customer_name": "Mars Inc.", + "customer_logo": ( + "https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/" + + "OSIRIS_Mars_true_color.jpg/550px-OSIRIS_Mars_true_color.jpg" + ), + "curr_q": "Q2", + "curr_q_total_sales": "$2,532,124", + "curr_q_qoq": "0.054", + "prev_q": "Q1", + "prev_q_total_sales": "$2,413,584", + "next_q": "Q3", + "next_q_total_sales_proj": "$2,634,765", + "next_q_qoq_proj": "0.041", + "top1_sku": "Phobos", + "top1_sales": "$334,384", + "top2_sku": "Deimos", + "top2_sales": "$315,718", + "top3_sku": "Charon", + "top3_sales": "$285,727", + "top4_sku": "Nix", + "top4_sales": "$264,023", + "top5_sku": "Hydra", + "top5_sales": "$212,361", + }, + "jupiter": { + "customer_name": "Jupiter LLC", + "customer_logo": ( + "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2b/" + + "Jupiter_and_its_shrunken_Great_Red_Spot.jpg/660px-Jupiter_" + + "and_its_shrunken_Great_Red_Spot.jpg" + ), + "curr_q": "Q2", + "curr_q_total_sales": "$1,532,124", + "curr_q_qoq": "0.031", + "prev_q": "Q1", + "prev_q_total_sales": "$1,413,584", + "next_q": "Q3", + "next_q_total_sales_proj": "$1,634,765", + "next_q_qoq_proj": "0.021", + "top1_sku": "Io", + "top1_sales": "$234,384", + "top2_sku": "Europa", + "top2_sales": "$215,718", + "top3_sku": "Ganymede", + "top3_sales": "$185,727", + "top4_sku": "Callisto", + "top4_sales": "$164,023", + "top5_sku": "Amalthea", + "top5_sales": "$112,361", + }, + "saturn": { + "customer_name": "Saturn", + "customer_logo": ( + "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c7/" + + "Saturn_during_Equinox.jpg/800px-Saturn_during_Equinox.jpg" + ), + "curr_q": "Q2", + "curr_q_total_sales": "$2,532,124", + "curr_q_qoq": "0.032", + "prev_q": "Q1", + "prev_q_total_sales": "$2,413,584", + "next_q": "Q3", + "next_q_total_sales_proj": "$2,634,765", + "next_q_qoq_proj": "0.029", + "top1_sku": "Mimas", + "top1_sales": "$334,384", + "top2_sku": "Enceladus", + "top2_sales": "$315,718", + "top3_sku": "Tethys", + "top3_sales": "$285,727", + "top4_sku": "Dione", + "top4_sales": "$264,023", + "top5_sku": "Rhea", + "top5_sales": "$212,361", + }, + "neptune": { + "customer_name": "Neptune", + "customer_logo": ( + "https://upload.wikimedia.org/wikipedia/commons/thumb/5/56/" + + "Neptune_Full.jpg/600px-Neptune_Full.jpg" + ), + "curr_q": "Q2", + "curr_q_total_sales": "$2,532,124", + "curr_q_qoq": "0.027", + "prev_q": "Q1", + "prev_q_total_sales": "$2,413,584", + "next_q": "Q3", + "next_q_total_sales_proj": "$2,634,765", + "next_q_qoq_proj": "0.039", + "top1_sku": "Triton", + "top1_sales": "$334,384", + "top2_sku": "Nereid", + "top2_sales": "$315,718", + "top3_sku": "Naiad", + "top3_sales": "$285,727", + "top4_sku": "Thalassa", + "top4_sales": "$264,023", + "top5_sku": "Despina", + "top5_sales": "$212,361", + }, + } - def GetCustomerData(self, customer_id, properties): - customer_data = self._CUSTOMER_DATA[customer_id] - return [customer_data[p.lower()] for p in properties] + def GetCustomerData(self, customer_id, properties): + customer_data = self._CUSTOMER_DATA[customer_id] + return [customer_data[p.lower()] for p in properties] diff --git a/events/next18/customer_spreadsheet_reader.py b/events/next18/customer_spreadsheet_reader.py index e0c6167d..28008c89 100644 --- a/events/next18/customer_spreadsheet_reader.py +++ b/events/next18/customer_spreadsheet_reader.py @@ -21,54 +21,62 @@ class CustomerSpreadsheetReader(object): - def __init__(self, sheets_service, spreadsheet_id): - self._sheets_service = sheets_service - self._spreadsheet_id = spreadsheet_id - self._data_filters = collections.OrderedDict() + def __init__(self, sheets_service, spreadsheet_id): + self._sheets_service = sheets_service + self._spreadsheet_id = spreadsheet_id + self._data_filters = collections.OrderedDict() - def ReadColumnData(self, column_id): - data_filter = { - 'developerMetadataLookup': { - 'metadataKey': 'column_id', - 'metadataValue': column_id, - } + def ReadColumnData(self, column_id): + data_filter = { + "developerMetadataLookup": { + "metadataKey": "column_id", + "metadataValue": column_id, } - self._data_filters[column_id] = data_filter + } + self._data_filters[column_id] = data_filter - def ExecuteRead(self): - filters = list(self._data_filters.values()) - get_body = {'dataFilters': filters} - read_fields = ','.join([ - 'sheets.properties.sheetId', - 'sheets.data.rowData.values.formattedValue', - 'developerMetadata.metadataValue']) - spreadsheet = self._sheets_service.spreadsheets().getByDataFilter( - spreadsheetId=self._spreadsheet_id, body=get_body, - fields=read_fields).execute() - customer_spreadsheet = CustomerSpreadsheet( - spreadsheet, self._data_filters) - self._data_filters = collections.OrderedDict() - return customer_spreadsheet + def ExecuteRead(self): + filters = list(self._data_filters.values()) + get_body = {"dataFilters": filters} + read_fields = ",".join([ + "sheets.properties.sheetId", + "sheets.data.rowData.values.formattedValue", + "developerMetadata.metadataValue", + ]) + spreadsheet = ( + self._sheets_service.spreadsheets() + .getByDataFilter( + spreadsheetId=self._spreadsheet_id, + body=get_body, + fields=read_fields, + ) + .execute() + ) + customer_spreadsheet = CustomerSpreadsheet(spreadsheet, self._data_filters) + self._data_filters = collections.OrderedDict() + return customer_spreadsheet class CustomerSpreadsheet(object): - def __init__(self, spreadsheet, data_filters): - self._spreadsheet = spreadsheet - self._data_filters = data_filters + def __init__(self, spreadsheet, data_filters): + self._spreadsheet = spreadsheet + self._data_filters = data_filters - def GetSheetId(self): - sheet = self._spreadsheet.get('sheets')[0] - return sheet.get('properties').get('sheetId') + def GetSheetId(self): + sheet = self._spreadsheet.get("sheets")[0] + return sheet.get("properties").get("sheetId") - def GetTemplateId(self): - metadata = self._spreadsheet.get('developerMetadata')[0] - return metadata.get('metadataValue') + def GetTemplateId(self): + metadata = self._spreadsheet.get("developerMetadata")[0] + return metadata.get("metadataValue") - def GetColumnData(self, column_id): - index = list(self._data_filters.keys()).index(column_id) - data = self._spreadsheet.get('sheets')[0].get('data')[index] - values = [row.get('values')[0].get('formattedValue') - for row in data.get('rowData')] - # Remove the first value which is just the label - return values[1:] + def GetColumnData(self, column_id): + index = list(self._data_filters.keys()).index(column_id) + data = self._spreadsheet.get("sheets")[0].get("data")[index] + values = [ + row.get("values")[0].get("formattedValue") + for row in data.get("rowData") + ] + # Remove the first value which is just the label + return values[1:] diff --git a/events/next18/presentation_reader.py b/events/next18/presentation_reader.py index edc82861..4b023b50 100644 --- a/events/next18/presentation_reader.py +++ b/events/next18/presentation_reader.py @@ -24,54 +24,55 @@ class PresentationReader(object): - def __init__(self, slides_service, presentation_id): - self._slides_service = slides_service - self._presentation_id = presentation_id - self._presentation = None + def __init__(self, slides_service, presentation_id): + self._slides_service = slides_service + self._presentation_id = presentation_id + self._presentation = None - def _InitPresentation(self): - if not self._presentation: - self._presentation = self._slides_service.presentations().get( - presentationId=self._presentation_id).execute() + def _InitPresentation(self): + if not self._presentation: + self._presentation = ( + self._slides_service.presentations() + .get(presentationId=self._presentation_id) + .execute() + ) - def GetTitle(self): - self._InitPresentation() - return self._presentation.get('title') + def GetTitle(self): + self._InitPresentation() + return self._presentation.get("title") - def GetAllPlaceholders(self): - self._InitPresentation() - slides = self._presentation.get('slides') - placeholders = [] - for slide in slides: - elements = slide.get('pageElements') - for element in elements: - shape = element.get('shape') - table = element.get('table') - # Skip page elements that aren't shapes or tables since they're - # the only types that support text. - if not shape and not table: - continue - if shape: - placeholders += self._GetPlaceholdersFromText( - shape.get('text')) - elif table: - rows = table.get('tableRows') - for row in rows: - cells = row.get('tableCells') - for cell in cells: - placeholders += self._GetPlaceholdersFromText( - cell.get('text')) - # Return the unique placeholders - seen = set() - return [p for p in placeholders if not (p in seen or seen.add(p))] + def GetAllPlaceholders(self): + self._InitPresentation() + slides = self._presentation.get("slides") + placeholders = [] + for slide in slides: + elements = slide.get("pageElements") + for element in elements: + shape = element.get("shape") + table = element.get("table") + # Skip page elements that aren't shapes or tables since they're + # the only types that support text. + if not shape and not table: + continue + if shape: + placeholders += self._GetPlaceholdersFromText(shape.get("text")) + elif table: + rows = table.get("tableRows") + for row in rows: + cells = row.get("tableCells") + for cell in cells: + placeholders += self._GetPlaceholdersFromText(cell.get("text")) + # Return the unique placeholders + seen = set() + return [p for p in placeholders if not (p in seen or seen.add(p))] - def _GetPlaceholdersFromText(self, text): - if not text: - return [] - placeholders = [] - elements = text.get('textElements') - for element in elements: - if element.get('textRun'): - content = element.get('textRun').get('content') - placeholders += re.findall('{.*?}', content) - return placeholders + def _GetPlaceholdersFromText(self, text): + if not text: + return [] + placeholders = [] + elements = text.get("textElements") + for element in elements: + if element.get("textRun"): + content = element.get("textRun").get("content") + placeholders += re.findall("{.*?}", content) + return placeholders diff --git a/events/next18/presentation_writer.py b/events/next18/presentation_writer.py index 16cfb568..7436ec89 100644 --- a/events/next18/presentation_writer.py +++ b/events/next18/presentation_writer.py @@ -14,47 +14,41 @@ # pylint: disable=E1102 # python3 -"""Functionality for writing to a presentation. -""" +"""Functionality for writing to a presentation.""" class PresentationWriter(object): - """Queues writes for modifying a presentation. - - Call ExecuteBatchUpdate to flush pending writes. - """ - - def __init__(self, slides_service, presentation_id): - self._slides_service = slides_service - self._presentation_id = presentation_id - self._requests = [] - - def ReplaceAllText(self, find_text, replace_text): - request = { - 'replaceAllText': { - 'replaceText': replace_text, - 'containsText': { - 'text': find_text, - 'matchCase': True - } - } + """Queues writes for modifying a presentation. + + Call ExecuteBatchUpdate to flush pending writes. + """ + + def __init__(self, slides_service, presentation_id): + self._slides_service = slides_service + self._presentation_id = presentation_id + self._requests = [] + + def ReplaceAllText(self, find_text, replace_text): + request = { + "replaceAllText": { + "replaceText": replace_text, + "containsText": {"text": find_text, "matchCase": True}, } - self._requests.append(request) - - def ReplaceAllShapesWithImage(self, find_text, image_url): - request = { - 'replaceAllShapesWithImage': { - 'imageUrl': image_url, - 'containsText': { - 'text': find_text, - 'matchCase': True - } - } + } + self._requests.append(request) + + def ReplaceAllShapesWithImage(self, find_text, image_url): + request = { + "replaceAllShapesWithImage": { + "imageUrl": image_url, + "containsText": {"text": find_text, "matchCase": True}, } - self._requests.append(request) - - def ExecuteBatchUpdate(self): - body = {'requests': self._requests} - self._requests = [] - self._slides_service.presentations().batchUpdate( - presentationId=self._presentation_id, body=body).execute() + } + self._requests.append(request) + + def ExecuteBatchUpdate(self): + body = {"requests": self._requests} + self._requests = [] + self._slides_service.presentations().batchUpdate( + presentationId=self._presentation_id, body=body + ).execute() diff --git a/events/next18/qbr_tool.py b/events/next18/qbr_tool.py index cf1e49e8..15a06fa3 100644 --- a/events/next18/qbr_tool.py +++ b/events/next18/qbr_tool.py @@ -20,8 +20,6 @@ pushes the data to Google Slides """ -from __future__ import print_function - import argparse import re @@ -36,149 +34,163 @@ from oauth2client import file as oauth_file from oauth2client import tools -SCOPES = ['https://www.googleapis.com/auth/drive'] -store = oauth_file.Storage('token.json') +SCOPES = ["https://www.googleapis.com/auth/drive"] +store = oauth_file.Storage("token.json") creds = store.get() if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('credentials.json', SCOPES) - creds = tools.run_flow(flow, store) + flow = client.flow_from_clientsecrets("credentials.json", SCOPES) + creds = tools.run_flow(flow, store) -slides_service = build('slides', 'v1', http=creds.authorize(Http())) -sheets_service = build('sheets', 'v4', http=creds.authorize(Http())) -drive_service = build('drive', 'v3', http=creds.authorize(Http())) +slides_service = build("slides", "v1", http=creds.authorize(Http())) +sheets_service = build("sheets", "v4", http=creds.authorize(Http())) +drive_service = build("drive", "v3", http=creds.authorize(Http())) def main(): - parser = argparse.ArgumentParser() - parser.add_argument( - 'command', - help='The command to run', - choices=['create_sheet', 'create_presentations', 'add_customers']) - parser.add_argument('--spreadsheet_id', help='The spreadsheet to use') - parser.add_argument( - '--template_id', help='The presentation to use as a template') - parser.add_argument( - '--customer_ids', nargs='+', help='The customers to use') - args = parser.parse_args() - - if args.command == 'create_sheet': - create_sheet(args.template_id) - elif args.command == 'create_presentations': - create_presentations(args.spreadsheet_id, args.customer_ids) - elif args.command == 'add_customers': - add_customers(args.spreadsheet_id, args.customer_ids) + parser = argparse.ArgumentParser() + parser.add_argument( + "command", + help="The command to run", + choices=["create_sheet", "create_presentations", "add_customers"], + ) + parser.add_argument("--spreadsheet_id", help="The spreadsheet to use") + parser.add_argument( + "--template_id", help="The presentation to use as a template" + ) + parser.add_argument("--customer_ids", nargs="+", help="The customers to use") + args = parser.parse_args() + + if args.command == "create_sheet": + create_sheet(args.template_id) + elif args.command == "create_presentations": + create_presentations(args.spreadsheet_id, args.customer_ids) + elif args.command == "add_customers": + add_customers(args.spreadsheet_id, args.customer_ids) def create_sheet(template_id): - pres_reader = presentation_reader.PresentationReader( - slides_service, template_id) - placeholders = pres_reader.GetAllPlaceholders() - presentation_title = pres_reader.GetTitle() - - # Create the data manager spreadsheet - spreadsheet_title = 'Data Sheet - ' + presentation_title - spreadsheet = spreadsheet_writer.CreateSpreadsheet( - sheets_service=sheets_service, - title=spreadsheet_title, - sheet_titles=['Customer Data']) - - # Get the spreadsheet ID and sheet IDs from the created spreadsheet. - spreadsheet_id = spreadsheet.get('spreadsheetId') - sheet_id = spreadsheet.get('sheets')[0].get('properties').get('sheetId') - - # Write the placeholders and metadata to the spreadsheet. - writer = spreadsheet_writer.SpreadsheetWriter( - sheets_service, spreadsheet_id) - writer.PopulateColumn( - sheet_id=sheet_id, - column_index=0, - column_id='placeholders', - values=placeholders) - writer.AddTemplateIdToSpreadsheetMetadata(template_id) - writer.ExecuteBatchUpdate() - - print('Spreadsheet URL: https://docs.google.com/spreadsheets/d/' + - spreadsheet_id) + pres_reader = presentation_reader.PresentationReader( + slides_service, template_id + ) + placeholders = pres_reader.GetAllPlaceholders() + presentation_title = pres_reader.GetTitle() + + # Create the data manager spreadsheet + spreadsheet_title = "Data Sheet - " + presentation_title + spreadsheet = spreadsheet_writer.CreateSpreadsheet( + sheets_service=sheets_service, + title=spreadsheet_title, + sheet_titles=["Customer Data"], + ) + + # Get the spreadsheet ID and sheet IDs from the created spreadsheet. + spreadsheet_id = spreadsheet.get("spreadsheetId") + sheet_id = spreadsheet.get("sheets")[0].get("properties").get("sheetId") + + # Write the placeholders and metadata to the spreadsheet. + writer = spreadsheet_writer.SpreadsheetWriter(sheets_service, spreadsheet_id) + writer.PopulateColumn( + sheet_id=sheet_id, + column_index=0, + column_id="placeholders", + values=placeholders, + ) + writer.AddTemplateIdToSpreadsheetMetadata(template_id) + writer.ExecuteBatchUpdate() + + print( + "Spreadsheet URL: https://docs.google.com/spreadsheets/d/" + + spreadsheet_id + ) def add_customers(spreadsheet_id, customer_ids): - # Read the placeholders by querying for the developer metadata we added - # while creating the spreadsheet - spreadsheet_reader = customer_spreadsheet_reader.CustomerSpreadsheetReader( - sheets_service, spreadsheet_id) - spreadsheet_reader.ReadColumnData('placeholders') - customer_spreadsheet = spreadsheet_reader.ExecuteRead() - - sheet_id = customer_spreadsheet.GetSheetId() - placeholders = customer_spreadsheet.GetColumnData('placeholders') - - # Process the placeholders into our query properties - properties = [] - for p in placeholders: - # Remove any suffix from the property name - m = re.search(r'{(\w+)(\.\w+)*}', p) - properties.append(m.group(1)) - - data_service = customer_data_service.CustomerDataService() - writer = spreadsheet_writer.SpreadsheetWriter( - sheets_service, spreadsheet_id) - - for customer_id in customer_ids: - # Get the customer data from the internal customer data service - customer_data = data_service.GetCustomerData(customer_id, properties) - - # Write the customer data to the spreadsheet - writer.InsertColumn(sheet_id=sheet_id, column_index=1) - writer.PopulateColumn( - sheet_id=sheet_id, - column_index=1, - column_id=customer_id, - values=customer_data) - - writer.ExecuteBatchUpdate() + # Read the placeholders by querying for the developer metadata we added + # while creating the spreadsheet + spreadsheet_reader = customer_spreadsheet_reader.CustomerSpreadsheetReader( + sheets_service, spreadsheet_id + ) + spreadsheet_reader.ReadColumnData("placeholders") + customer_spreadsheet = spreadsheet_reader.ExecuteRead() + + sheet_id = customer_spreadsheet.GetSheetId() + placeholders = customer_spreadsheet.GetColumnData("placeholders") + + # Process the placeholders into our query properties + properties = [] + for p in placeholders: + # Remove any suffix from the property name + m = re.search(r"{(\w+)(\.\w+)*}", p) + properties.append(m.group(1)) + + data_service = customer_data_service.CustomerDataService() + writer = spreadsheet_writer.SpreadsheetWriter(sheets_service, spreadsheet_id) + + for customer_id in customer_ids: + # Get the customer data from the internal customer data service + customer_data = data_service.GetCustomerData(customer_id, properties) + + # Write the customer data to the spreadsheet + writer.InsertColumn(sheet_id=sheet_id, column_index=1) + writer.PopulateColumn( + sheet_id=sheet_id, + column_index=1, + column_id=customer_id, + values=customer_data, + ) + + writer.ExecuteBatchUpdate() def create_presentations(spreadsheet_id, customer_ids): - spreadsheet_reader = customer_spreadsheet_reader.CustomerSpreadsheetReader( - sheets_service, spreadsheet_id) - - spreadsheet_reader.ReadColumnData('placeholders') - for customer_id in customer_ids: - spreadsheet_reader.ReadColumnData(customer_id) - - customer_spreadsheet = spreadsheet_reader.ExecuteRead() - placeholders = customer_spreadsheet.GetColumnData('placeholders') - - # Get the template presentation ID and its title - template_id = customer_spreadsheet.GetTemplateId() - pres_reader = presentation_reader.PresentationReader( - slides_service, template_id) - title = pres_reader.GetTitle() - - # Generate a presentation for each customer - for customer_id in customer_ids: - # Create a copy of the presentation - new_title = customer_id + ' - ' + title - presentation_id = drive_service.files().copy( - fileId=template_id, body={ - 'name': new_title - }).execute().get('id') - - # Replace the placeholders with the customer data in the copy - data = customer_spreadsheet.GetColumnData(customer_id) - data_dict = dict(zip(placeholders, data)) - writer = presentation_writer.PresentationWriter(slides_service, - presentation_id) - for placeholder, value in data_dict.items(): - if re.findall(r'{(\w+).image}', placeholder): - writer.ReplaceAllShapesWithImage(placeholder, value) - else: - writer.ReplaceAllText(placeholder, value) - writer.ExecuteBatchUpdate() - - print(customer_id + - ': https://docs.google.com/presentation/d/' + presentation_id) - - -if __name__ == '__main__': - main() + spreadsheet_reader = customer_spreadsheet_reader.CustomerSpreadsheetReader( + sheets_service, spreadsheet_id + ) + + spreadsheet_reader.ReadColumnData("placeholders") + for customer_id in customer_ids: + spreadsheet_reader.ReadColumnData(customer_id) + + customer_spreadsheet = spreadsheet_reader.ExecuteRead() + placeholders = customer_spreadsheet.GetColumnData("placeholders") + + # Get the template presentation ID and its title + template_id = customer_spreadsheet.GetTemplateId() + pres_reader = presentation_reader.PresentationReader( + slides_service, template_id + ) + title = pres_reader.GetTitle() + + # Generate a presentation for each customer + for customer_id in customer_ids: + # Create a copy of the presentation + new_title = customer_id + " - " + title + presentation_id = ( + drive_service.files() + .copy(fileId=template_id, body={"name": new_title}) + .execute() + .get("id") + ) + + # Replace the placeholders with the customer data in the copy + data = customer_spreadsheet.GetColumnData(customer_id) + data_dict = dict(zip(placeholders, data)) + writer = presentation_writer.PresentationWriter( + slides_service, presentation_id + ) + for placeholder, value in data_dict.items(): + if re.findall(r"{(\w+).image}", placeholder): + writer.ReplaceAllShapesWithImage(placeholder, value) + else: + writer.ReplaceAllText(placeholder, value) + writer.ExecuteBatchUpdate() + + print( + customer_id + + ": https://docs.google.com/presentation/d/" + + presentation_id + ) + + +if __name__ == "__main__": + main() diff --git a/events/next18/spreadsheet_writer.py b/events/next18/spreadsheet_writer.py index 4e7e880b..7f285ad3 100644 --- a/events/next18/spreadsheet_writer.py +++ b/events/next18/spreadsheet_writer.py @@ -18,122 +18,115 @@ def CreateSpreadsheet(sheets_service, title, sheet_titles): - """Creates an empty spreadsheet. - - It creates a spreadsheet with the provided title, and creates a sheet for - each entry in the sheet_titles list with the corresponding sheet title. - """ - sheets = [] - for sheet_title in sheet_titles: - sheet = { - 'properties': { - 'title': sheet_title, - }, - } - sheets.append(sheet) - - spreadsheet = { - 'properties': { - 'title': title, + """Creates an empty spreadsheet. + + It creates a spreadsheet with the provided title, and creates a sheet for + each entry in the sheet_titles list with the corresponding sheet title. + """ + sheets = [] + for sheet_title in sheet_titles: + sheet = { + "properties": { + "title": sheet_title, }, - 'sheets': sheets, } - return sheets_service.spreadsheets().create(body=spreadsheet).execute() + sheets.append(sheet) + + spreadsheet = { + "properties": { + "title": title, + }, + "sheets": sheets, + } + return sheets_service.spreadsheets().create(body=spreadsheet).execute() class SpreadsheetWriter(object): - """Queues writes for modifying a spreadsheet. - - Call ExecuteBatchUpdate to flush pending writes. - """ - - def __init__(self, sheets_service, spreadsheet_id): - self._sheets_service = sheets_service - self._spreadsheet_id = spreadsheet_id - self._requests = [] - - def InsertColumn(self, sheet_id, column_index): - request = { - 'insertDimension': { - 'range': { - 'sheetId': sheet_id, - 'dimension': 'COLUMNS', - 'startIndex': column_index, - 'endIndex': column_index + 1, - }, - } + """Queues writes for modifying a spreadsheet. + + Call ExecuteBatchUpdate to flush pending writes. + """ + + def __init__(self, sheets_service, spreadsheet_id): + self._sheets_service = sheets_service + self._spreadsheet_id = spreadsheet_id + self._requests = [] + + def InsertColumn(self, sheet_id, column_index): + request = { + "insertDimension": { + "range": { + "sheetId": sheet_id, + "dimension": "COLUMNS", + "startIndex": column_index, + "endIndex": column_index + 1, + }, } - self._requests.append(request) - - def PopulateColumn(self, sheet_id, column_index, column_id, values): - # Include the column ID in the column values - values = [column_id] + values - - # Populate the column with the values - rows = [] - for value in values: - row_data = { - 'values': [ - { - 'userEnteredValue': { - 'stringValue': value - } - } - ] - } - rows.append(row_data) - - update_request = { - 'updateCells': { - 'rows': rows, - 'fields': 'userEnteredValue', - 'start': { - 'sheetId': sheet_id, - 'rowIndex': 0, - 'columnIndex': column_index - } - } + } + self._requests.append(request) + + def PopulateColumn(self, sheet_id, column_index, column_id, values): + # Include the column ID in the column values + values = [column_id] + values + + # Populate the column with the values + rows = [] + for value in values: + row_data = {"values": [{"userEnteredValue": {"stringValue": value}}]} + rows.append(row_data) + + update_request = { + "updateCells": { + "rows": rows, + "fields": "userEnteredValue", + "start": { + "sheetId": sheet_id, + "rowIndex": 0, + "columnIndex": column_index, + }, } - self._requests.append(update_request) - - # Add developer metadata to the column to make it easier to read later - # by being able to just query it by the column ID - metadata_request = { - 'createDeveloperMetadata': { - 'developerMetadata': { - 'metadataKey': 'column_id', - 'metadataValue': column_id, - 'location': { - 'dimensionRange': { - 'sheetId': sheet_id, - 'dimension': 'COLUMNS', - 'startIndex': column_index, - 'endIndex': column_index + 1, - } - }, - 'visibility': 'DOCUMENT', - } + } + self._requests.append(update_request) + + # Add developer metadata to the column to make it easier to read later + # by being able to just query it by the column ID + metadata_request = { + "createDeveloperMetadata": { + "developerMetadata": { + "metadataKey": "column_id", + "metadataValue": column_id, + "location": { + "dimensionRange": { + "sheetId": sheet_id, + "dimension": "COLUMNS", + "startIndex": column_index, + "endIndex": column_index + 1, + } + }, + "visibility": "DOCUMENT", } } - self._requests.append(metadata_request) - - def AddTemplateIdToSpreadsheetMetadata(self, template_id): - request = { - 'createDeveloperMetadata': { - 'developerMetadata': { - 'metadataKey': 'template_id', - 'metadataValue': template_id, - 'location': { - 'spreadsheet': True - }, - 'visibility': 'DOCUMENT', - } + } + self._requests.append(metadata_request) + + def AddTemplateIdToSpreadsheetMetadata(self, template_id): + request = { + "createDeveloperMetadata": { + "developerMetadata": { + "metadataKey": "template_id", + "metadataValue": template_id, + "location": {"spreadsheet": True}, + "visibility": "DOCUMENT", } } - self._requests.append(request) - - def ExecuteBatchUpdate(self): - body = {'requests': self._requests} - self._requests = [] - return self._sheets_service.spreadsheets().batchUpdate( - spreadsheetId=self._spreadsheet_id, body=body).execute() + } + self._requests.append(request) + + def ExecuteBatchUpdate(self): + body = {"requests": self._requests} + self._requests = [] + return ( + self._sheets_service.spreadsheets() + .batchUpdate(spreadsheetId=self._spreadsheet_id, body=body) + .execute() + ) diff --git a/forms/quickstart/README.md b/forms/quickstart/README.md new file mode 100644 index 00000000..bbcb3bdd --- /dev/null +++ b/forms/quickstart/README.md @@ -0,0 +1,18 @@ +# Google Forms Python Quickstart + +Complete the steps described in the [Google Forms Python Quickstart]( +https://developers.google.com/forms/quickstart/python), and in +about five minutes you'll have a simple Python command-line application that +makes requests to the Google Forms API. + +## Install + +```shell +pip install -r requirements.txt +``` + +## Run + +```shell +python quickstart.py +``` diff --git a/forms/quickstart/quickstart.py b/forms/quickstart/quickstart.py new file mode 100644 index 00000000..7291e14a --- /dev/null +++ b/forms/quickstart/quickstart.py @@ -0,0 +1,90 @@ +# Copyright 2021 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START forms_quickstart] +from apiclient import discovery +from httplib2 import Http +from oauth2client import client, file, tools + +SCOPES = "https://www.googleapis.com/auth/forms.body" +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" + +store = file.Storage("token.json") +creds = None +if not creds or creds.invalid: + flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES) + creds = tools.run_flow(flow, store) + +form_service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, +) + +# Request body for creating a form +NEW_FORM = { + "info": { + "title": "Quickstart form", + } +} + +# Request body to add a multiple-choice question +NEW_QUESTION = { + "requests": [ + { + "createItem": { + "item": { + "title": ( + "In what year did the United States land a mission on" + " the moon?" + ), + "questionItem": { + "question": { + "required": True, + "choiceQuestion": { + "type": "RADIO", + "options": [ + {"value": "1965"}, + {"value": "1967"}, + {"value": "1969"}, + {"value": "1971"}, + ], + "shuffle": True, + }, + } + }, + }, + "location": {"index": 0}, + } + } + ] +} + +# Creates the initial form +result = form_service.forms().create(body=NEW_FORM).execute() + +# Adds the question to the form +question_setting = ( + form_service.forms() + .batchUpdate(formId=result["formId"], body=NEW_QUESTION) + .execute() +) + +# Prints the result to show the question has been added +get_result = form_service.forms().get(formId=result["formId"]).execute() +print(get_result) + +# [END forms_quickstart] diff --git a/drive/activity/requirements.txt b/forms/quickstart/requirements.txt similarity index 100% rename from drive/activity/requirements.txt rename to forms/quickstart/requirements.txt diff --git a/forms/snippets/README.md b/forms/snippets/README.md deleted file mode 100644 index bf5f8862..00000000 --- a/forms/snippets/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Google Forms API - -The Google Forms API is currently in Restricted Beta. To use the API and these samples prior to General Availability, -your Google Cloud project must be allowlisted. To request that your project be allowlisted, complete the -[Early Adopter Program application](https://developers.google.com/forms/api/eap). diff --git a/forms/snippets/add_item.py b/forms/snippets/add_item.py index b2a9f242..b7e24671 100644 --- a/forms/snippets/add_item.py +++ b/forms/snippets/add_item.py @@ -13,24 +13,26 @@ # limitations under the License. # [START forms_add_item] -from __future__ import print_function - from apiclient import discovery from httplib2 import Http from oauth2client import client, file, tools SCOPES = "https://www.googleapis.com/auth/forms.body" -API_KEY = "" -DISCOVERY_DOC = f"https://forms.googleapis.com/$discovery/rest?version=v1beta&key={API_KEY}&labels=FORMS_BETA_TESTERS" +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" -store = file.Storage('credentials.json') +store = file.Storage("token.json") creds = None if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('client_secrets.json', SCOPES) - creds = tools.run_flow(flow, store) + flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES) + creds = tools.run_flow(flow, store) -form_service = discovery.build('forms', 'v1beta', http=creds.authorize( - Http()), discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False) +form_service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, +) form = { "info": { @@ -43,28 +45,32 @@ # Request body to add a video item to a Form update = { - "requests": [{ - "createItem": { - "item": { - "title": "Homework video", - "description": "Quizzes in Google Forms", - "videoItem": { - "video": { - "youtubeUri": "https://www.youtube.com/watch?v=Lt5HqPvM-eI" - } - } - }, - "location": { - "index": 0 + "requests": [ + { + "createItem": { + "item": { + "title": "Homework video", + "description": "Quizzes in Google Forms", + "videoItem": { + "video": { + "youtubeUri": ( + "https://www.youtube.com/watch?v=Lt5HqPvM-eI" + ) + } + }, + }, + "location": {"index": 0}, } } - } ] } # Add the video to the form -question_setting = form_service.forms().batchUpdate( - formId=createResult["formId"], body=update).execute() +question_setting = ( + form_service.forms() + .batchUpdate(formId=createResult["formId"], body=update) + .execute() +) # Print the result to see it now has a video result = form_service.forms().get(formId=createResult["formId"]).execute() diff --git a/forms/snippets/add_responder.py b/forms/snippets/add_responder.py new file mode 100644 index 00000000..7a6d367d --- /dev/null +++ b/forms/snippets/add_responder.py @@ -0,0 +1,86 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START forms_add_responder] + +import os.path + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build + + +# If modifying these SCOPES, delete the file token.json. +SCOPES = ["https://www.googleapis.com/auth/drive.file"] +CLIENT_SECRET_FILE = "client_secrets.json" +TOKEN_FILE = "token.json" + +# TODO: Replace with your Form ID and responder's email +YOUR_FORM_ID = "YOUR_FORM_ID" +YOUR_RESPONDER_EMAIL = "user@example.com" + + +def get_credentials(): + """Gets the credentials for the user.""" + creds = None + if os.path.exists(TOKEN_FILE): + creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES) + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + CLIENT_SECRET_FILE, SCOPES + ) + creds = flow.run_local_server(port=8080) + with open(TOKEN_FILE, "w") as token: + token.write(creds.to_json()) + return creds + + +def add_responder(): + """Adds the responder to the form.""" + creds = get_credentials() + drive_service = build("drive", "v3", credentials=creds) + + permission_body = { + "view": "published", + "role": "reader", + "type": "user", + "emailAddress": YOUR_RESPONDER_EMAIL, + } + + try: + response = ( + drive_service.permissions() + .create( + fileId=YOUR_FORM_ID, + body=permission_body, + sendNotificationEmail=False, # Optional: to avoid sending an email + ) + .execute() + ) + print( + f"Added responder {YOUR_RESPONDER_EMAIL}. Permission ID:" + f" {response.get('id')}" + ) + print(response) + except Exception as e: + print(f"An error occurred: {e}") + + +if __name__ == "__main__": + add_responder() +# [END forms_add_responder] diff --git a/forms/snippets/anyone_with_link_responder.py b/forms/snippets/anyone_with_link_responder.py new file mode 100644 index 00000000..16257690 --- /dev/null +++ b/forms/snippets/anyone_with_link_responder.py @@ -0,0 +1,187 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +import os.path + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build + + +# If modifying these SCOPES, delete the file token.json. +SCOPES = ["https://www.googleapis.com/auth/drive.file"] +CLIENT_SECRET_FILE = "client_secrets.json" +TOKEN_FILE = "token.json" + +# TODO: Replace with your Form ID and responder's email +YOUR_FORM_ID = "YOUR_FORM_ID" + + +def get_credentials(): + """Gets the credentials for the user.""" + creds = None + if os.path.exists(TOKEN_FILE): + creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES) + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + CLIENT_SECRET_FILE, SCOPES + ) + creds = flow.run_local_server(port=8080) + with open(TOKEN_FILE, "w") as token: + token.write(creds.to_json()) + return creds + + +# [START forms_is_anyone_with_link_responder] +def is_anyone_with_link_responder(): + """Checks if anyone with the link is a responder for the form.""" + creds = get_credentials() + drive_service = build("drive", "v3", credentials=creds) + anyone_with_link_responder = False + + try: + permissions_result = ( + drive_service.permissions() + .list( + fileId=YOUR_FORM_ID, + fields="permissions(id,type,role,view)", + includePermissionsForView="published", + ) + .execute() + ) + + permissions = permissions_result.get("permissions", []) + if not permissions: + print(f"No permissions found for form ID: {YOUR_FORM_ID}") + else: + for permission in permissions: + if ( + permission.get("type") == "anyone" + and permission.get("view") == "published" + and permission.get("role") == "reader" + ): + anyone_with_link_responder = True + break + + if anyone_with_link_responder: + print( + f"Form '{YOUR_FORM_ID}' IS configured for 'Anyone with the link' to" + " respond." + ) + else: + print( + f"Form '{YOUR_FORM_ID}' is NOT configured for 'Anyone with the link'" + " to respond." + ) + + except Exception as e: + print(f"An error occurred: {e}") + return anyone_with_link_responder + + +# [END forms_is_anyone_with_link_responder] + + +# [START forms_set_anyone_with_link_responder] +def set_anyone_with_link_responder(): + """Sets anyone with the link to be a responder for the form.""" + creds = get_credentials() + drive_service = build("drive", "v3", credentials=creds) + + permission_body = { + "type": "anyone", + "view": "published", # Key for making it a responder setting + "role": "reader", + } + + try: + response = ( + drive_service.permissions() + .create( + fileId=YOUR_FORM_ID, + body=permission_body, + ) + .execute() + ) + print( + "'Anyone with the link can respond' permission set for form" + f" {YOUR_FORM_ID}: {response}" + ) + return True + except Exception as e: + print(f"An error occurred: {e}") + return False + + +# [END forms_set_anyone_with_link_responder] + + +# [START forms_remove_anyone_with_link_responder] +def remove_anyone_with_link_responder(): + """Removes anyone with the link as a responder for the form.""" + creds = get_credentials() + drive_service = build("drive", "v3", credentials=creds) + + permission_id_to_delete = None + + try: + permissions_result = ( + drive_service.permissions() + .list( + fileId=YOUR_FORM_ID, + fields="permissions(id,type,role,view)", + includePermissionsForView="published", + ) + .execute() + ) + + permissions = permissions_result.get("permissions", []) + for permission in permissions: + if ( + permission.get("type") == "anyone" + and permission.get("role") == "reader" + and permission.get("view") == "published" + ): + permission_id_to_delete = permission.get("id") + break + + if permission_id_to_delete: + drive_service.permissions().delete( + fileId=YOUR_FORM_ID, permissionId=permission_id_to_delete + ).execute() + print( + "Successfully removed 'Anyone with the link' permission (ID:" + f" {permission_id_to_delete}) from form {YOUR_FORM_ID}." + ) + return True + else: + print( + "'Anyone with the link can respond' permission not found for form" + f" {YOUR_FORM_ID}." + ) + + except Exception as e: + print(f"An error occurred: {e}") + return False + + +# [END forms_remove_anyone_with_link_responder] + +if __name__ == "__main__": + is_anyone_with_link_responder() diff --git a/forms/snippets/convert_form.py b/forms/snippets/convert_form.py index 2c68d97a..f248b613 100644 --- a/forms/snippets/convert_form.py +++ b/forms/snippets/convert_form.py @@ -13,28 +13,30 @@ # limitations under the License. # [START forms_convert_form] -from __future__ import print_function - from apiclient import discovery from httplib2 import Http from oauth2client import client, file, tools SCOPES = "https://www.googleapis.com/auth/forms.body" -API_KEY = "" -DISCOVERY_DOC = f"https://forms.googleapis.com/$discovery/rest?version=v1beta&key={API_KEY}&labels=FORMS_BETA_TESTERS" +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" -store = file.Storage('credentials.json') +store = file.Storage("token.json") creds = None if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('client_secrets.json', SCOPES) - creds = tools.run_flow(flow, store) + flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES) + creds = tools.run_flow(flow, store) -form_service = discovery.build('forms', 'v1beta', http=creds.authorize( - Http()), discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False) +form_service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, +) form = { "info": { - "title": "My new form", + "title": "My new quiz", } } @@ -46,20 +48,19 @@ "requests": [ { "updateSettings": { - "settings": { - "quizSettings": { - "isQuiz": True - } - }, - "updateMask": "quizSettings.isQuiz" + "settings": {"quizSettings": {"isQuiz": True}}, + "updateMask": "quizSettings.isQuiz", } } ] } # Converts the form into a quiz -question_setting = form_service.forms().batchUpdate(formId=result["formId"], - body=update).execute() +question_setting = ( + form_service.forms() + .batchUpdate(formId=result["formId"], body=update) + .execute() +) # Print the result to see it's now a quiz getresult = form_service.forms().get(formId=result["formId"]).execute() diff --git a/forms/snippets/create_form.py b/forms/snippets/create_form.py index ec61385a..9a86e9ae 100644 --- a/forms/snippets/create_form.py +++ b/forms/snippets/create_form.py @@ -13,24 +13,26 @@ # limitations under the License. # [START forms_create_form] -from __future__ import print_function - from apiclient import discovery from httplib2 import Http from oauth2client import client, file, tools SCOPES = "https://www.googleapis.com/auth/drive" -API_KEY = "" -DISCOVERY_DOC = f"https://forms.googleapis.com/$discovery/rest?version=v1beta&key={API_KEY}&labels=FORMS_BETA_TESTERS" +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" -store = file.Storage('credentials.json') +store = file.Storage("token.json") creds = None if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('client_secrets.json', SCOPES) - creds = tools.run_flow(flow, store) + flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES) + creds = tools.run_flow(flow, store) -form_service = discovery.build('forms', 'v1beta', http=creds.authorize( - Http()), discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False) +form_service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, +) form = { "info": { diff --git a/forms/snippets/create_watch.py b/forms/snippets/create_watch.py index 8dce58a2..4211e836 100644 --- a/forms/snippets/create_watch.py +++ b/forms/snippets/create_watch.py @@ -13,37 +13,35 @@ # limitations under the License. # [START forms_create_watch] -from __future__ import print_function - from apiclient import discovery from httplib2 import Http from oauth2client import client, file, tools SCOPES = "https://www.googleapis.com/auth/drive" -API_KEY = "" -DISCOVERY_DOC = f"https://forms.googleapis.com/$discovery/rest?version=v1beta&key={API_KEY}&labels=FORMS_BETA_TESTERS" +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" -store = file.Storage('credentials.json') +store = file.Storage("token.json") creds = None if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('client_secret.json', SCOPES) - creds = tools.run_flow(flow, store) + flow = client.flow_from_clientsecrets("client_secret.json", SCOPES) + creds = tools.run_flow(flow, store) -service = discovery.build('forms', 'v1beta', http=creds.authorize( - Http()), discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False) +service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, +) watch = { "watch": { - "target": { - "topic": { - "topicName": "" - } - }, - "eventType": "RESPONSES" + "target": {"topic": {"topicName": ""}}, + "eventType": "RESPONSES", } } -form_id = '' +form_id = "" # Print JSON response after form watch creation result = service.forms().watches().create(formId=form_id, body=watch).execute() diff --git a/forms/snippets/delete_watch.py b/forms/snippets/delete_watch.py index d493cf85..06b40ac0 100644 --- a/forms/snippets/delete_watch.py +++ b/forms/snippets/delete_watch.py @@ -13,28 +13,32 @@ # limitations under the License. # [START forms_delete_watch] -from __future__ import print_function - from apiclient import discovery from httplib2 import Http from oauth2client import client, file, tools SCOPES = "https://www.googleapis.com/auth/drive" -API_KEY = "" -DISCOVERY_DOC = f"https://forms.googleapis.com/$discovery/rest?version=v1beta&key={API_KEY}&labels=FORMS_BETA_TESTERS" +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" -store = file.Storage('credentials.json') +store = file.Storage("token.json") creds = None if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('client_secret.json', SCOPES) - creds = tools.run_flow(flow, store) -service = discovery.build('forms', 'v1beta', http=creds.authorize( - Http()), discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False) + flow = client.flow_from_clientsecrets("client_secret.json", SCOPES) + creds = tools.run_flow(flow, store) +service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, +) -form_id = '' -watch_id = '' +form_id = "" +watch_id = "" # Print JSON response after deleting a form watch -result = service.forms().watches().delete(formId=form_id, watchId=watch_id).execute() +result = ( + service.forms().watches().delete(formId=form_id, watchId=watch_id).execute() +) print(result) # [END forms_delete_watch] diff --git a/forms/snippets/duplicate_form.py b/forms/snippets/duplicate_form.py index df4ca506..eb49e08a 100644 --- a/forms/snippets/duplicate_form.py +++ b/forms/snippets/duplicate_form.py @@ -13,8 +13,6 @@ # limitations under the License. # [START forms_duplicate_form] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -23,38 +21,40 @@ from googleapiclient.discovery import build # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/drive'] +SCOPES = ["https://www.googleapis.com/auth/drive"] def main(): - """Shows copy file example in Drive v3 API. - Prints the name, id and other data of the copied file. - """ - creds = None - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'client_secrets.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) - - service = build('drive', 'v3', credentials=creds) - - # Call the Drive v3 API - origin_file_id = '1ox-6vHFeKpC6mon-tL5ygBC8zpbTnTp76JCZdIg80hA' # example ID - copied_file = {'title': 'my_copy'} - results = service.files().copy( - fileId=origin_file_id, body=copied_file).execute() - print(results) - - -if __name__ == '__main__': - main() + """Shows copy file example in Drive v3 API. + Prints the name, id and other data of the copied file. + """ + creds = None + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "client_secrets.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) + + service = build("drive", "v3", credentials=creds) + + # Call the Drive v3 API + origin_file_id = "1ox-6vHFeKpC6mon-tL5ygBC8zpbTnTp76JCZdIg80hA" # example ID + copied_file = {"title": "my_copy"} + results = ( + service.files().copy(fileId=origin_file_id, body=copied_file).execute() + ) + print(results) + + +if __name__ == "__main__": + main() # [END forms_duplicate_form] diff --git a/forms/snippets/get_responders.py b/forms/snippets/get_responders.py new file mode 100644 index 00000000..10eca545 --- /dev/null +++ b/forms/snippets/get_responders.py @@ -0,0 +1,91 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START forms_get_responders] + +import os.path + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build + + +# If modifying these SCOPES, delete the file token.json. +SCOPES = ["https://www.googleapis.com/auth/drive.metadata.readonly"] +CLIENT_SECRET_FILE = "client_secrets.json" +TOKEN_FILE = "token.json" + +# TODO: Replace with your Form ID +YOUR_FORM_ID = "YOUR_FORM_ID" + + +def get_credentials(): + """Gets the credentials for the user.""" + creds = None + if os.path.exists(TOKEN_FILE): + creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES) + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + CLIENT_SECRET_FILE, SCOPES + ) + creds = flow.run_local_server(port=8080) + with open(TOKEN_FILE, "w") as token: + token.write(creds.to_json()) + return creds + + +def get_responders(): + """Gets the responders for the form.""" + creds = get_credentials() + drive_service = build("drive", "v3", credentials=creds) + + try: + response = ( + drive_service.permissions() + .list( + fileId=YOUR_FORM_ID, + fields="permissions(id,emailAddress,type,role,view)", + includePermissionsForView="published", + ) + .execute() + ) + + published_readers = [] + for permission in response.get("permissions", []): + # 'view': 'published' indicates it's related to the published link. + # 'role': 'reader' is standard for responders. + # 'type': 'user' for specific users, 'anyone' for public. + if ( + permission.get("view") == "published" + and permission.get("role") == "reader" + ): + published_readers.append(permission) + + if published_readers: + print("Responders for this form:") + print(published_readers) + else: + print("No specific published readers found for this form.") + + except Exception as e: + print(f"An error occurred: {e}") + + +if __name__ == "__main__": + get_responders() +# [END forms_get_responders] diff --git a/forms/snippets/list_watches.py b/forms/snippets/list_watches.py index 3a48832b..eeb24c11 100644 --- a/forms/snippets/list_watches.py +++ b/forms/snippets/list_watches.py @@ -13,25 +13,27 @@ # limitations under the License. # [START forms_list_form_watches] -from __future__ import print_function - from apiclient import discovery from httplib2 import Http from oauth2client import client, file, tools SCOPES = "https://www.googleapis.com/auth/drive" -API_KEY = "" -DISCOVERY_DOC = f"https://forms.googleapis.com/$discovery/rest?version=v1beta&key={API_KEY}&labels=FORMS_BETA_TESTERS" +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" -store = file.Storage('credentials.json') +store = file.Storage("token.json") creds = None if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('client_secret.json', SCOPES) - creds = tools.run_flow(flow, store) -service = discovery.build('forms', 'v1beta', http=creds.authorize( - Http()), discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False) + flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES) + creds = tools.run_flow(flow, store) +service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, +) -form_id = '' +form_id = "" # Print JSON list of form watches result = service.forms().watches().list(formId=form_id).execute() diff --git a/forms/snippets/publish_form.py b/forms/snippets/publish_form.py new file mode 100644 index 00000000..fa6a4fb3 --- /dev/null +++ b/forms/snippets/publish_form.py @@ -0,0 +1,86 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START forms_publish_form] + +import os.path + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build + + +# If modifying these SCOPES, delete the file token.json. +SCOPES = ["https://www.googleapis.com/auth/forms.body"] +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" +CLIENT_SECRET_FILE = "client_secrets.json" +TOKEN_FILE = "token.json" + +# TODO: Replace with your Form ID +YOUR_FORM_ID = "YOUR_FORM_ID" + + +def get_credentials(): + """Gets the credentials for the user.""" + creds = None + if os.path.exists(TOKEN_FILE): + creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES) + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + CLIENT_SECRET_FILE, SCOPES + ) + creds = flow.run_local_server(port=8080) + with open(TOKEN_FILE, "w") as token: + token.write(creds.to_json()) + return creds + + +def publish_form(): + """Publishes the form.""" + creds = get_credentials() + form_service = build( + "forms", + "v1", + credentials=creds, + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, + ) + + # Request body for updating publish settings + set_publish_settings_request = { + "publishSettings": { + "publishState": {"isPublished": True, "isAcceptingResponses": True} + }, + } + + try: + response = ( + form_service.forms() + .setPublishSettings( + formId=YOUR_FORM_ID, body=set_publish_settings_request + ) + .execute() + ) + print(f"Form {YOUR_FORM_ID} publish settings updated: {response}") + except Exception as e: + print(f"An error occurred: {e}") + + +if __name__ == "__main__": + publish_form() +# [END forms_publish_form] diff --git a/forms/snippets/remove_responder.py b/forms/snippets/remove_responder.py new file mode 100644 index 00000000..ce5e55b6 --- /dev/null +++ b/forms/snippets/remove_responder.py @@ -0,0 +1,101 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START forms_remove_responder] + +import os.path + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build + + +# If modifying these SCOPES, delete the file token.json. +SCOPES = ["https://www.googleapis.com/auth/drive.file"] +CLIENT_SECRET_FILE = "client_secrets.json" +TOKEN_FILE = "token.json" + +# TODO: Replace with your Form ID and responder's email +YOUR_FORM_ID = "YOUR_FORM_ID" +YOUR_RESPONDER_EMAIL = "user@example.com" + + +def get_credentials(): + """Gets the credentials for the user.""" + creds = None + if os.path.exists(TOKEN_FILE): + creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES) + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + CLIENT_SECRET_FILE, SCOPES + ) + creds = flow.run_local_server(port=8080) + with open(TOKEN_FILE, "w") as token: + token.write(creds.to_json()) + return creds + + +def remove_responder(): + """Removes the responder for the form.""" + creds = get_credentials() + drive_service = build("drive", "v3", credentials=creds) + + try: + # First, find the permission ID for the user + permissions_result = ( + drive_service.permissions() + .list( + fileId=YOUR_FORM_ID, + fields="permissions(id,emailAddress,role,view,type)", + includePermissionsForView="published", + ) + .execute() + ) + + permission_id_to_delete = None + for p in permissions_result.get("permissions", []): + if ( + p.get("emailAddress") == YOUR_RESPONDER_EMAIL + and p.get("role") == "reader" + and p.get("view") == "published" + and p.get("type") == "user" + ): + permission_id_to_delete = p.get("id") + break + + if permission_id_to_delete: + drive_service.permissions().delete( + fileId=YOUR_FORM_ID, permissionId=permission_id_to_delete + ).execute() + print( + f"Successfully removed responder {YOUR_RESPONDER_EMAIL} (Permission" + f" ID: {permission_id_to_delete}) from form {YOUR_FORM_ID}." + ) + else: + print( + f"Responder {YOUR_RESPONDER_EMAIL} not found or not a published" + f" reader for form {YOUR_FORM_ID}." + ) + + except Exception as e: + print(f"An error occurred: {e}") + + +if __name__ == "__main__": + remove_responder() +# [END forms_remove_responder] diff --git a/forms/snippets/renew_watch.py b/forms/snippets/renew_watch.py index 552ed63a..1faeefda 100644 --- a/forms/snippets/renew_watch.py +++ b/forms/snippets/renew_watch.py @@ -13,28 +13,32 @@ # limitations under the License. # [START forms_renew_watch] -from __future__ import print_function - from apiclient import discovery from httplib2 import Http from oauth2client import client, file, tools SCOPES = "https://www.googleapis.com/auth/drive" -API_KEY = "" -DISCOVERY_DOC = f"https://forms.googleapis.com/$discovery/rest?version=v1beta&key={API_KEY}&labels=FORMS_BETA_TESTERS" +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" -store = file.Storage('credentials.json') +store = file.Storage("token.json") creds = None if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('client_secret.json', SCOPES) - creds = tools.run_flow(flow, store) -service = discovery.build('forms', 'v1beta', http=creds.authorize( - Http()), discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False) + flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES) + creds = tools.run_flow(flow, store) +service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, +) -form_id = '' -watch_id = '' +form_id = "" +watch_id = "" # Print JSON response after renewing a form watch -result = service.forms().watches().renew(formId=form_id, watchId=watch_id).execute() +result = ( + service.forms().watches().renew(formId=form_id, watchId=watch_id).execute() +) print(result) # [END forms_renew_watch] diff --git a/forms/snippets/retrieve_all_responses.py b/forms/snippets/retrieve_all_responses.py index 234c7192..0bf9d98c 100644 --- a/forms/snippets/retrieve_all_responses.py +++ b/forms/snippets/retrieve_all_responses.py @@ -13,26 +13,28 @@ # limitations under the License. # [START forms_retrieve_all_responses] -from __future__ import print_function - from apiclient import discovery from httplib2 import Http from oauth2client import client, file, tools SCOPES = "https://www.googleapis.com/auth/forms.responses.readonly" -API_KEY = "" -DISCOVERY_DOC = f"https://forms.googleapis.com/$discovery/rest?version=v1beta&key={API_KEY}&labels=FORMS_BETA_TESTERS" +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" -store = file.Storage('credentials.json') +store = file.Storage("token.json") creds = None if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('client_secrets.json', SCOPES) - creds = tools.run_flow(flow, store) -service = discovery.build('forms', 'v1beta', http=creds.authorize( - Http()), discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False) + flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES) + creds = tools.run_flow(flow, store) +service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, +) # Prints the responses of your specified form: -form_id = '' +form_id = "" result = service.forms().responses().list(formId=form_id).execute() print(result) # [END forms_retrieve_all_responses] diff --git a/forms/snippets/retrieve_contents.py b/forms/snippets/retrieve_contents.py index 83b5e197..1144713e 100644 --- a/forms/snippets/retrieve_contents.py +++ b/forms/snippets/retrieve_contents.py @@ -13,26 +13,28 @@ # limitations under the License. # [START forms_retrieve_contents] -from __future__ import print_function - from apiclient import discovery from httplib2 import Http from oauth2client import client, file, tools SCOPES = "https://www.googleapis.com/auth/forms.body.readonly" -API_KEY = "" -DISCOVERY_DOC = f"https://forms.googleapis.com/$discovery/rest?version=v1beta&key={API_KEY}&labels=FORMS_BETA_TESTERS" +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" -store = file.Storage('credentials.json') +store = file.Storage("token.json") creds = None if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('client_secrets.json', SCOPES) - creds = tools.run_flow(flow, store) -service = discovery.build('forms', 'v1beta', http=creds.authorize( - Http()), discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False) + flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES) + creds = tools.run_flow(flow, store) +service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, +) # Prints the title of the sample form: -form_id = '' +form_id = "" result = service.forms().get(formId=form_id).execute() print(result) # [END forms_retrieve_contents] diff --git a/forms/snippets/retrieve_single_response.py b/forms/snippets/retrieve_single_response.py index c7bd7eb3..757f65f0 100644 --- a/forms/snippets/retrieve_single_response.py +++ b/forms/snippets/retrieve_single_response.py @@ -13,28 +13,34 @@ # limitations under the License. # [START forms_retrieve_single_response] -from __future__ import print_function - from apiclient import discovery from httplib2 import Http from oauth2client import client, file, tools SCOPES = "https://www.googleapis.com/auth/forms.responses.readonly" -API_KEY = "" -DISCOVERY_DOC = f"https://forms.googleapis.com/$discovery/rest?version=v1beta&key={API_KEY}&labels=FORMS_BETA_TESTERS" +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" -store = file.Storage('credentials.json') +store = file.Storage("token.json") creds = None if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('client_secrets.json', SCOPES) - creds = tools.run_flow(flow, store) -service = discovery.build('forms', 'v1beta', http=creds.authorize( - Http()), discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False) + flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES) + creds = tools.run_flow(flow, store) +service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, +) # Prints the specified response from your form: -form_id = '' -response_id = '' -result = service.forms().responses().get( - formId=form_id, responseId=response_id).execute() +form_id = "" +response_id = "" +result = ( + service.forms() + .responses() + .get(formId=form_id, responseId=response_id) + .execute() +) print(result) # [END forms_retrieve_single_response] diff --git a/forms/snippets/stop_accepting_responses.py b/forms/snippets/stop_accepting_responses.py new file mode 100644 index 00000000..b7ed29e3 --- /dev/null +++ b/forms/snippets/stop_accepting_responses.py @@ -0,0 +1,90 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START forms_stop_accepting_responses] + +import os.path + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build + + +# If modifying these SCOPES, delete the file token.json. +SCOPES = ["https://www.googleapis.com/auth/forms.body"] +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" +CLIENT_SECRET_FILE = "client_secrets.json" +TOKEN_FILE = "token.json" + +# TODO: Replace with your Form ID +YOUR_FORM_ID = "YOUR_FORM_ID" + + +def get_credentials(): + """Gets the credentials for the user.""" + creds = None + if os.path.exists(TOKEN_FILE): + creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES) + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + CLIENT_SECRET_FILE, SCOPES + ) + creds = flow.run_local_server(port=8080) + with open(TOKEN_FILE, "w") as token: + token.write(creds.to_json()) + return creds + + +def close_form(): + """Closes the form for responses.""" + creds = get_credentials() + form_service = build( + "forms", + "v1", + credentials=creds, + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, + ) + + stop_accepting_request_body = { + "publishSettings": { + "publishState": { + "isPublished": True, # Keep it published + "isAcceptingResponses": False, # But stop accepting responses + } + } + } + + try: + response = ( + form_service.forms() + .setPublishSettings( # Corrected: form_service + formId=YOUR_FORM_ID, body=stop_accepting_request_body + ) + .execute() + ) + print(f"Form {YOUR_FORM_ID} stopped accepting responses: {response}") + return True + except Exception as e: + print(f"An error occurred: {e}") + return False + + +if __name__ == "__main__": + close_form() +# [END forms_stop_accepting_responses] diff --git a/forms/snippets/supports_publishing.py b/forms/snippets/supports_publishing.py new file mode 100644 index 00000000..37433ab6 --- /dev/null +++ b/forms/snippets/supports_publishing.py @@ -0,0 +1,92 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START forms_supports_publishing] + +import os.path + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build + + +# If modifying these SCOPES, delete the file token.json. +SCOPES = ["https://www.googleapis.com/auth/forms.body"] +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" +CLIENT_SECRET_FILE = "client_secrets.json" +TOKEN_FILE = "token.json" + +# TODO: Replace with your Form ID +YOUR_FORM_ID = "YOUR_FORM_ID" + + +def get_credentials(): + """Gets the credentials for the user.""" + creds = None + if os.path.exists(TOKEN_FILE): + creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES) + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + CLIENT_SECRET_FILE, SCOPES + ) + creds = flow.run_local_server(port=8080) + with open(TOKEN_FILE, "w") as token: + token.write(creds.to_json()) + return creds + + +def supports_publishing(): + """Checks if the form supports publishing.""" + creds = get_credentials() + form_service = build( + "forms", + "v1", + credentials=creds, + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, + ) + + is_legacy = True # Assume legacy until proven otherwise + try: + form_metadata = form_service.forms().get(formId=YOUR_FORM_ID).execute() + # If 'publishSettings' field exists, it's not a legacy form regarding this feature. + if "publishSettings" in form_metadata: + print( + f"Form '{YOUR_FORM_ID}' (Title:" + f" {form_metadata.get('info', {}).get('title')}) is NOT a legacy form" + " (supports publishSettings)." + ) + is_legacy = False + else: + print( + f"Form '{YOUR_FORM_ID}' (Title:" + f" {form_metadata.get('info', {}).get('title')}) IS a legacy form" + " (does not have publishSettings field)." + ) + return not is_legacy # Returns true if it supports publishing + + except Exception as e: + print(f"An error occurred while checking form {YOUR_FORM_ID}: {e}") + # Depending on the error, it might indicate non-existence or access issues, + # not necessarily legacy status. + return None + + +if __name__ == "__main__": + supports_publishing() +# [END forms_supports_publishing] diff --git a/forms/snippets/unpublish_form.py b/forms/snippets/unpublish_form.py new file mode 100644 index 00000000..3011fbe0 --- /dev/null +++ b/forms/snippets/unpublish_form.py @@ -0,0 +1,89 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START forms_unpublish_form] + +import os.path + +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build + + +# If modifying these SCOPES, delete the file token.json. +SCOPES = ["https://www.googleapis.com/auth/forms.body"] +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" +CLIENT_SECRET_FILE = "client_secrets.json" +TOKEN_FILE = "token.json" + +# TODO: Replace with your Form ID +YOUR_FORM_ID = "YOUR_FORM_ID" + + +def get_credentials(): + """Gets the credentials for the user.""" + creds = None + if os.path.exists(TOKEN_FILE): + creds = Credentials.from_authorized_user_file(TOKEN_FILE, SCOPES) + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + CLIENT_SECRET_FILE, SCOPES + ) + creds = flow.run_local_server(port=8080) + with open(TOKEN_FILE, "w") as token: + token.write(creds.to_json()) + return creds + + +def unpublish_form(): + """Unpublishes the form.""" + creds = get_credentials() + form_service = build( + "forms", + "v1", + credentials=creds, + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, + ) + + # Request body for updating publish settings + set_publish_settings_request = { + "publishSettings": {"publishState": {"isPublished": False}}, + } + + try: + response = ( + form_service.forms() + .setPublishSettings( + formId=YOUR_FORM_ID, body=set_publish_settings_request + ) + .execute() + ) + print( + f"Form {YOUR_FORM_ID} publish settings updated to unpublished:" + f" {response}" + ) + return True + except Exception as e: + print(f"An error occurred: {e}") + return False + + +if __name__ == "__main__": + unpublish_form() +# [END forms_unpublish_form] diff --git a/forms/snippets/update_form.py b/forms/snippets/update_form.py index b11df710..030cacd7 100644 --- a/forms/snippets/update_form.py +++ b/forms/snippets/update_form.py @@ -13,24 +13,26 @@ # limitations under the License. # [START forms_update_form] -from __future__ import print_function - from apiclient import discovery from httplib2 import Http from oauth2client import client, file, tools SCOPES = "https://www.googleapis.com/auth/forms.body" -API_KEY = "" -DISCOVERY_DOC = f"https://forms.googleapis.com/$discovery/rest?version=v1beta&key={API_KEY}&labels=FORMS_BETA_TESTERS" +DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1" -store = file.Storage('credentials.json') +store = file.Storage("token.json") creds = None if not creds or creds.invalid: - flow = client.flow_from_clientsecrets('client_secrets.json', SCOPES) - creds = tools.run_flow(flow, store) + flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES) + creds = tools.run_flow(flow, store) -form_service = discovery.build('forms', 'v1beta', http=creds.authorize( - Http()), discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False) +form_service = discovery.build( + "forms", + "v1", + http=creds.authorize(Http()), + discoveryServiceUrl=DISCOVERY_DOC, + static_discovery=False, +) form = { "info": { @@ -43,19 +45,27 @@ # Request body to add description to a Form update = { - "requests": [{ - "updateFormInfo": { - "info": { - "description": "Please complete this quiz based on this week's readings for class." - }, - "updateMask": "description" + "requests": [ + { + "updateFormInfo": { + "info": { + "description": ( + "Please complete this quiz based on this week's" + " readings for class." + ) + }, + "updateMask": "description", + } } - }] + ] } # Update the form with a description -question_setting = form_service.forms().batchUpdate( - formId=createResult["formId"], body=update).execute() +question_setting = ( + form_service.forms() + .batchUpdate(formId=createResult["formId"], body=update) + .execute() +) # Print the result to see it now has a description getresult = form_service.forms().get(formId=createResult["formId"]).execute() diff --git a/gmail/quickstart/quickstart.py b/gmail/quickstart/quickstart.py index 4509faa7..b56e73ad 100644 --- a/gmail/quickstart/quickstart.py +++ b/gmail/quickstart/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START gmail_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -24,49 +22,50 @@ from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'] +SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"] def main(): - """Shows basic usage of the Gmail API. - Lists the user's Gmail labels. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Shows basic usage of the Gmail API. + Lists the user's Gmail labels. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - try: - # Call the Gmail API - service = build('gmail', 'v1', credentials=creds) - results = service.users().labels().list(userId='me').execute() - labels = results.get('labels', []) + try: + # Call the Gmail API + service = build("gmail", "v1", credentials=creds) + results = service.users().labels().list(userId="me").execute() + labels = results.get("labels", []) - if not labels: - print('No labels found.') - return - print('Labels:') - for label in labels: - print(label['name']) + if not labels: + print("No labels found.") + return + print("Labels:") + for label in labels: + print(label["name"]) - except HttpError as error: - # TODO(developer) - Handle errors from gmail API. - print(f'An error occurred: {error}') + except HttpError as error: + # TODO(developer) - Handle errors from gmail API. + print(f"An error occurred: {error}") -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END gmail_quickstart] diff --git a/gmail/snippet/base_test.py b/gmail/snippet/base_test.py index f60d12c6..ec0790c4 100644 --- a/gmail/snippet/base_test.py +++ b/gmail/snippet/base_test.py @@ -1,3 +1,17 @@ +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + import os import unittest @@ -6,32 +20,34 @@ class BaseTest(unittest.TestCase): - - RECIPIENT = 'gduser01@workspacesamples.dev' - TEST_USER = 'ci-test01@workspacesamples.dev' - FORWARDING_ADDRESS = 'gduser01@workspacesamples.dev' - - @classmethod - def setUpClass(cls): - cls.service = cls.create_service() - - @classmethod - def create_credentials(cls): - scope = ['https://www.googleapis.com/auth/gmail.compose', - 'https://www.googleapis.com/auth/gmail.send', - 'https://www.googleapis.com/auth/gmail.labels', - 'https://www.googleapis.com/auth/gmail.settings.basic', - 'https://www.googleapis.com/auth/gmail.settings.sharing', - 'https://mail.google.com/'] - credentials = ServiceAccountCredentials.from_json_keyfile_name(os.environ['SERVICE_ACCOUNT_CREDENTIALS'], - scopes=scope) - return credentials.create_delegated(BaseTest.TEST_USER) - - @classmethod - def create_service(cls): - credentials = cls.create_credentials() - return discovery.build('gmail', 'v1', credentials=credentials) - - -if __name__ == '__main__': - unittest.main() + RECIPIENT = "gduser01@workspacesamples.dev" + TEST_USER = "ci-test01@workspacesamples.dev" + FORWARDING_ADDRESS = "gduser01@workspacesamples.dev" + + @classmethod + def setUpClass(cls): + cls.service = cls.create_service() + + @classmethod + def create_credentials(cls): + scope = [ + "https://www.googleapis.com/auth/gmail.compose", + "https://www.googleapis.com/auth/gmail.send", + "https://www.googleapis.com/auth/gmail.labels", + "https://www.googleapis.com/auth/gmail.settings.basic", + "https://www.googleapis.com/auth/gmail.settings.sharing", + "https://mail.google.com/", + ] + credentials = ServiceAccountCredentials.from_json_keyfile_name( + os.environ["SERVICE_ACCOUNT_CREDENTIALS"], scopes=scope + ) + return credentials.create_delegated(BaseTest.TEST_USER) + + @classmethod + def create_service(cls): + credentials = cls.create_credentials() + return discovery.build("gmail", "v1", credentials=credentials) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/list_messages.py b/gmail/snippet/list_messages.py new file mode 100644 index 00000000..d57bd444 --- /dev/null +++ b/gmail/snippet/list_messages.py @@ -0,0 +1,76 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START gmail_list_messages] +import os.path +from google.auth.transport.requests import Request +from google.oauth2.credentials import Credentials +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + +# If modifying these scopes, delete the file token.json. +SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"] + + +def main(): + """Shows basic usage of the Gmail API. + Lists the user's Gmail messages. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file("credentials.json", SCOPES) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) + + try: + # Call the Gmail API + service = build("gmail", "v1", credentials=creds) + results = ( + service.users().messages().list(userId="me", labelIds=["INBOX"]).execute() + ) + messages = results.get("messages", []) + + if not messages: + print("No messages found.") + return + + print("Messages:") + for message in messages: + print(f'Message ID: {message["id"]}') + msg = ( + service.users().messages().get(userId="me", id=message["id"]).execute() + ) + print(f' Subject: {msg["snippet"]}') + + except HttpError as error: + # TODO(developer) - Handle errors from gmail API. + print(f"An error occurred: {error}") + + +if __name__ == "__main__": + main() + +# [END gmail_list_messages] diff --git a/gmail/snippet/send mail/create_draft.py b/gmail/snippet/send mail/create_draft.py index 918e9496..5b13f0a3 100644 --- a/gmail/snippet/send mail/create_draft.py +++ b/gmail/snippet/send mail/create_draft.py @@ -12,14 +12,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - """ -# [START gmail_create_draft] - -from __future__ import print_function +# [START gmail_create_draft] import base64 -from email.mime.text import MIMEText +from email.message import EmailMessage import google.auth from googleapiclient.discovery import build @@ -27,44 +24,49 @@ def gmail_create_draft(): - """Create and insert a draft email. - Print the returned draft's message and id. - Returns: Draft object, including draft id and message meta data. - - Load pre-authorized user credentials from the environment. - TODO(developer) - See https://developers.google.com/identity - for guides on implementing OAuth2 for the application. - """ - creds, _ = google.auth.default() - - try: - # create gmail api client - service = build('gmail', 'v1', credentials=creds) - - message = MIMEText('This is automated draft mail') - message['to'] = 'gduser1@workspacesamples.dev' - message['from'] = 'gduser2@workspacesamples.dev' - message['subject'] = 'Automated draft' - encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode() - - create_message = { - 'message': { - 'raw': encoded_message - } - } - # pylint: disable=E1101 - draft = service.users().drafts().create(userId="me", - body=create_message).execute() - - print(F'Draft id: {draft["id"]}\nDraft message: {draft["message"]}') - - except HttpError as error: - print(F'An error occurred: {error}') - draft = None - - return draft - - -if __name__ == '__main__': - gmail_create_draft() + """Create and insert a draft email. + Print the returned draft's message and id. + Returns: Draft object, including draft id and message meta data. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create gmail api client + service = build("gmail", "v1", credentials=creds) + + message = EmailMessage() + + message.set_content("This is automated draft mail") + + message["To"] = "gduser1@workspacesamples.dev" + message["From"] = "gduser2@workspacesamples.dev" + message["Subject"] = "Automated draft" + + # encoded message + encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode() + + create_message = {"message": {"raw": encoded_message}} + # pylint: disable=E1101 + draft = ( + service.users() + .drafts() + .create(userId="me", body=create_message) + .execute() + ) + + print(f'Draft id: {draft["id"]}\nDraft message: {draft["message"]}') + + except HttpError as error: + print(f"An error occurred: {error}") + draft = None + + return draft + + +if __name__ == "__main__": + gmail_create_draft() # [END gmail_create_draft] diff --git a/gmail/snippet/send mail/create_draft_with_attachment.py b/gmail/snippet/send mail/create_draft_with_attachment.py index 88d75ded..ba871a23 100644 --- a/gmail/snippet/send mail/create_draft_with_attachment.py +++ b/gmail/snippet/send mail/create_draft_with_attachment.py @@ -12,17 +12,15 @@ See the License for the specific language governing permissions and limitations under the License. """ -# [START gmail_create_draft_with_attachment] - -from __future__ import print_function +# [START gmail_create_draft_with_attachment] import base64 import mimetypes import os +from email.message import EmailMessage from email.mime.audio import MIMEAudio from email.mime.base import MIMEBase from email.mime.image import MIMEImage -from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText import google.auth @@ -31,78 +29,90 @@ def gmail_create_draft_with_attachment(): - """Create and insert a draft email with attachment. - Print the returned draft's message and id. - Returns: Draft object, including draft id and message meta data. - - Load pre-authorized user credentials from the environment. - TODO(developer) - See https://developers.google.com/identity - for guides on implementing OAuth2 for the application. - """ - creds, _ = google.auth.default() - - try: - # create gmail api client - service = build('gmail', 'v1', credentials=creds) - mime_message = MIMEMultipart() - mime_message['to'] = 'gduser1@workspacesamples.dev' - mime_message['from'] = 'gduser2@workspacesamples.dev' - mime_message['subject'] = 'sample with attachment' - text_part = MIMEText('Hi, this is automated mail with attachment.' - 'Please do not reply.') - mime_message.attach(text_part) - image_attachment = build_file_part(file='photo.jpg') - mime_message.attach(image_attachment) - encoded_message = base64.urlsafe_b64encode(mime_message.as_bytes()).decode() - - create_draft_request_body = { - 'message': { - 'raw': encoded_message - } - } - # pylint: disable=E1101 - draft = service.users().drafts().create(userId="me", - body=create_draft_request_body)\ - .execute() - print(F'Draft id: {draft["id"]}\nDraft message: {draft["message"]}') - except HttpError as error: - print(F'An error occurred: {error}') - draft = None - return draft + """Create and insert a draft email with attachment. + Print the returned draft's message and id. + Returns: Draft object, including draft id and message meta data. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create gmail api client + service = build("gmail", "v1", credentials=creds) + mime_message = EmailMessage() + + # headers + mime_message["To"] = "gduser1@workspacesamples.dev" + mime_message["From"] = "gduser2@workspacesamples.dev" + mime_message["Subject"] = "sample with attachment" + + # text + mime_message.set_content( + "Hi, this is automated mail with attachment.Please do not reply." + ) + + # attachment + attachment_filename = "photo.jpg" + # guessing the MIME type + type_subtype, _ = mimetypes.guess_type(attachment_filename) + maintype, subtype = type_subtype.split("/") + + with open(attachment_filename, "rb") as fp: + attachment_data = fp.read() + mime_message.add_attachment(attachment_data, maintype, subtype) + + encoded_message = base64.urlsafe_b64encode(mime_message.as_bytes()).decode() + + create_draft_request_body = {"message": {"raw": encoded_message}} + # pylint: disable=E1101 + draft = ( + service.users() + .drafts() + .create(userId="me", body=create_draft_request_body) + .execute() + ) + print(f'Draft id: {draft["id"]}\nDraft message: {draft["message"]}') + except HttpError as error: + print(f"An error occurred: {error}") + draft = None + return draft def build_file_part(file): - """Creates a MIME part for a file. - - Args: - file: The path to the file to be attached. - - Returns: - A MIME part that can be attached to a message. - """ - content_type, encoding = mimetypes.guess_type(file) - - if content_type is None or encoding is not None: - content_type = 'application/octet-stream' - main_type, sub_type = content_type.split('/', 1) - if main_type == 'text': - with open(file, 'rb'): - msg = MIMEText('r', _subtype=sub_type) - elif main_type == 'image': - with open(file, 'rb'): - msg = MIMEImage('r', _subtype=sub_type) - elif main_type == 'audio': - with open(file, 'rb'): - msg = MIMEAudio('r', _subtype=sub_type) - else: - with open(file, 'rb'): - msg = MIMEBase(main_type, sub_type) - msg.set_payload(file.read()) - filename = os.path.basename(file) - msg.add_header('Content-Disposition', 'attachment', filename=filename) - return msg - - -if __name__ == '__main__': - gmail_create_draft_with_attachment() - # [END gmail_create_draft_with_attachment] + """Creates a MIME part for a file. + + Args: + file: The path to the file to be attached. + + Returns: + A MIME part that can be attached to a message. + """ + content_type, encoding = mimetypes.guess_type(file) + + if content_type is None or encoding is not None: + content_type = "application/octet-stream" + main_type, sub_type = content_type.split("/", 1) + if main_type == "text": + with open(file, "rb"): + msg = MIMEText("r", _subtype=sub_type) + elif main_type == "image": + with open(file, "rb"): + msg = MIMEImage("r", _subtype=sub_type) + elif main_type == "audio": + with open(file, "rb"): + msg = MIMEAudio("r", _subtype=sub_type) + else: + with open(file, "rb"): + msg = MIMEBase(main_type, sub_type) + msg.set_payload(file.read()) + filename = os.path.basename(file) + msg.add_header("Content-Disposition", "attachment", filename=filename) + return msg + + +if __name__ == "__main__": + gmail_create_draft_with_attachment() + # [END gmail_create_draft_with_attachment] diff --git a/gmail/snippet/send mail/send_message.py b/gmail/snippet/send mail/send_message.py index dd19c41b..697f922e 100644 --- a/gmail/snippet/send mail/send_message.py +++ b/gmail/snippet/send mail/send_message.py @@ -10,12 +10,10 @@ See the License for the specific language governing permissions and limitations under the License. """ -# [START gmail_send_message] - -from __future__ import print_function +# [START gmail_send_message] import base64 -from email.mime.text import MIMEText +from email.message import EmailMessage import google.auth from googleapiclient.discovery import build @@ -23,42 +21,44 @@ def gmail_send_message(): - """Create and send an email message - Print the returned message id - Returns: Message object, including message id + """Create and send an email message + Print the returned message id + Returns: Message object, including message id + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + service = build("gmail", "v1", credentials=creds) + message = EmailMessage() - Load pre-authorized user credentials from the environment. - TODO(developer) - See https://developers.google.com/identity - for guides on implementing OAuth2 for the application. - """ - creds, _ = google.auth.default() + message.set_content("This is automated draft mail") - try: - service = build('gmail', 'v1', credentials=creds) - message = MIMEText('This is automated draft mail') - message['to'] = 'gduser1@workspacesamples.dev' - message['from'] = 'gduser2@workspacesamples.dev' - message['subject'] = 'Automated draft' - # encoded message - encoded_message = base64.urlsafe_b64encode(message.as_bytes()) \ - .decode() + message["To"] = "gduser1@workspacesamples.dev" + message["From"] = "gduser2@workspacesamples.dev" + message["Subject"] = "Automated draft" - create_message = { - 'message': { + # encoded message + encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode() - 'raw': encoded_message - } - } - # pylint: disable=E1101 - send_message = (service.users().messages().send - (userId="me", body=create_message).execute()) - print(F'Message Id: {send_message["id"]}') - except HttpError as error: - print(F'An error occurred: {error}') - send_message = None - return send_message + create_message = {"raw": encoded_message} + # pylint: disable=E1101 + send_message = ( + service.users() + .messages() + .send(userId="me", body=create_message) + .execute() + ) + print(f'Message Id: {send_message["id"]}') + except HttpError as error: + print(f"An error occurred: {error}") + send_message = None + return send_message -if __name__ == '__main__': - gmail_send_message() +if __name__ == "__main__": + gmail_send_message() # [END gmail_send_message] diff --git a/gmail/snippet/send mail/send_message_with_attachment.py b/gmail/snippet/send mail/send_message_with_attachment.py deleted file mode 100644 index a8832250..00000000 --- a/gmail/snippet/send mail/send_message_with_attachment.py +++ /dev/null @@ -1,103 +0,0 @@ -""" -Copyright 2019 Google LLC -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" -# [START gmail_send_message_with_attachment] -from __future__ import print_function - -import base64 -import mimetypes -import os -from email.mime.audio import MIMEAudio -from email.mime.base import MIMEBase -from email.mime.image import MIMEImage -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText - -import google.auth -from googleapiclient.discovery import build -from googleapiclient.errors import HttpError - - -def gmail_send_message_with_attachment(): - """Create and send an email message with attachment - Print the returned message id - Returns: Message object, including message id - - Load pre-authorized user credentials from the environment. - TODO(developer) - See https://developers.google.com/identity - for guides on implementing OAuth2 for the application. - """ - creds, _ = google.auth.default() - - try: - service = build('gmail', 'v1', credentials=creds) - mime_message = MIMEMultipart() - mime_message['to'] = 'gduser1@workspacesamples.dev' - mime_message['from'] = 'gduser2@workspacesamples.dev' - mime_message['subject'] = 'sample with attachment' - text_part = MIMEText('Hi, this is automated mail with attachment.' - 'Please do not reply.') - mime_message.attach(text_part) - image_attachment = build_file_part(file='photo.jpg') - mime_message.attach(image_attachment) - # encoded message - encoded_message = base64.urlsafe_b64encode(mime_message.as_bytes()) \ - .decode() - - send_message_request_body = { - 'message': { - - 'raw': encoded_message - } - } - # pylint: disable=E1101 - send_message = (service.users().messages().send - (userId='me', body=send_message_request_body).execute()) - print(F'Message Id: {send_message["id"]}') - except HttpError as error: - print(F'An error occurred: {error}') - send_message = None - return send_message - - -def build_file_part(file): - """Creates a MIME part for a file. - Args: - file: The path to the file to be attached. - Returns: - A MIME part that can be attached to a message. - """ - content_type, encoding = mimetypes.guess_type(file) - if content_type is None or encoding is not None: - content_type = 'application/octet-stream' - main_type, sub_type = content_type.split('/', 1) - if main_type == 'text': - with open(file, 'rb'): - msg = MIMEText('r', _subtype=sub_type) - elif main_type == 'image': - with open(file, 'rb'): - msg = MIMEImage('r', _subtype=sub_type) - elif main_type == 'audio': - with open(file, 'rb'): - msg = MIMEAudio('r', _subtype=sub_type) - else: - with open(file, 'rb'): - msg = MIMEBase(main_type, sub_type) - msg.set_payload(file.read()) - filename = os.path.basename(file) - msg.add_header('Content-Disposition', 'attachment', filename=filename) - return msg - - -if __name__ == '__main__': - gmail_send_message_with_attachment() -# [END gmail_send_message_with_attachment] diff --git a/gmail/snippet/send mail/test_create_draft.py b/gmail/snippet/send mail/test_create_draft.py new file mode 100644 index 00000000..f8636c1f --- /dev/null +++ b/gmail/snippet/send mail/test_create_draft.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from create_draft import gmail_create_draft + + +class TestCreateDraft(unittest.TestCase): + """Unit test class for snippet""" + + @classmethod + def test_create_draft(cls): + """Unit test for create draft""" + draft = gmail_create_draft() + cls.assertIsNotNone(cls, draft) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/send mail/test_create_draft_with_attachment.py b/gmail/snippet/send mail/test_create_draft_with_attachment.py new file mode 100644 index 00000000..502c813e --- /dev/null +++ b/gmail/snippet/send mail/test_create_draft_with_attachment.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from create_draft_with_attachment import gmail_create_draft_with_attachment + + +class TestCreateDraftWithAttachment(unittest.TestCase): + """Unit test class for Change snippet""" + + @classmethod + def test_create_draft_with_attachment(cls): + """Test create draft with attachment""" + draft = gmail_create_draft_with_attachment() + cls.assertIsNotNone(cls, draft) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/send mail/test_send_message.py b/gmail/snippet/send mail/test_send_message.py new file mode 100644 index 00000000..3aecfb65 --- /dev/null +++ b/gmail/snippet/send mail/test_send_message.py @@ -0,0 +1,31 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from send_message import gmail_send_message + + +class TestSendMessage(unittest.TestCase): + """Unit test class for snippet""" + + def test_send_message(self): + """test send message""" + send_message = gmail_send_message() + self.assertIsNotNone(self, send_message) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/send mail/test_send_message_with_attachment.py b/gmail/snippet/send mail/test_send_message_with_attachment.py new file mode 100644 index 00000000..79cfe0f5 --- /dev/null +++ b/gmail/snippet/send mail/test_send_message_with_attachment.py @@ -0,0 +1,31 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from send_message_with_attachment import gmail_send_message_with_attachment + + +class TestSendMessageWithAttachment(unittest.TestCase): + """Unit test class for gmail snippet""" + + def test_send_message_with_attachment(self): + """test send message with attachment""" + send_message = gmail_send_message_with_attachment() + self.assertIsNotNone(self, send_message) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/send_mail.py b/gmail/snippet/send_mail.py deleted file mode 100644 index 2724a0ae..00000000 --- a/gmail/snippet/send_mail.py +++ /dev/null @@ -1,146 +0,0 @@ -"""Send an email message from the user's account. -""" - -import base64 -import mimetypes -import os -from email.mime.audio import MIMEAudio -from email.mime.base import MIMEBase -from email.mime.image import MIMEImage -from email.mime.multipart import MIMEMultipart -from email.mime.text import MIMEText - -from apiclient import errors - - -# [START create_draft] -def create_draft(service, user_id, message_body): - """Create and insert a draft email. Print the returned draft's message and id. - - Args: - service: Authorized Gmail API service instance. - user_id: User's email address. The special value "me" - can be used to indicate the authenticated user. - message_body: The body of the email message, including headers. - - Returns: - Draft object, including draft id and message meta data. - """ - try: - message = {'message': message_body} - draft = service.users().drafts().create(userId=user_id, body=message).execute() - - print('Draft id: %s\nDraft message: %s' % (draft['id'], draft['message'])) - - return draft - except errors.HttpError as error: - print('An error occurred: %s' % error) - return None - - -# [END create_draft] - - -# [START send_email] -def send_message(service, user_id, message): - """Send an email message. - - Args: - service: Authorized Gmail API service instance. - user_id: User's email address. The special value "me" - can be used to indicate the authenticated user. - message: Message to be sent. - - Returns: - Sent Message. - """ - try: - message = (service.users().messages().send(userId=user_id, body=message) - .execute()) - print('Message Id: %s' % message['id']) - return message - except errors.HttpError as error: - print('An error occurred: %s' % error) - - -# [END send_email] - - -# [START create_message] -def create_message(sender, to, subject, message_text): - """Create a message for an email. - - Args: - sender: Email address of the sender. - to: Email address of the receiver. - subject: The subject of the email message. - message_text: The text of the email message. - - Returns: - An object containing a base64url encoded email object. - """ - message = MIMEText(message_text) - message['to'] = to - message['from'] = sender - message['subject'] = subject - - encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode() - - return {'raw': encoded_message} - - -# [END create_message] - - -# [START create_message_attachment] -def create_message_with_attachment( - sender, to, subject, message_text, file): - """Create a message for an email. - - Args: - sender: Email address of the sender. - to: Email address of the receiver. - subject: The subject of the email message. - message_text: The text of the email message. - file: The path to the file to be attached. - - Returns: - An object containing a base64url encoded email object. - """ - message = MIMEMultipart() - message['to'] = to - message['from'] = sender - message['subject'] = subject - - msg = MIMEText(message_text) - message.attach(msg) - - content_type, encoding = mimetypes.guess_type(file) - - if content_type is None or encoding is not None: - content_type = 'application/octet-stream' - main_type, sub_type = content_type.split('/', 1) - if main_type == 'text': - fp = open(file, 'rb') - msg = MIMEText(fp.read(), _subtype=sub_type) - fp.close() - elif main_type == 'image': - fp = open(file, 'rb') - msg = MIMEImage(fp.read(), _subtype=sub_type) - fp.close() - elif main_type == 'audio': - fp = open(file, 'rb') - msg = MIMEAudio(fp.read(), _subtype=sub_type) - fp.close() - else: - fp = open(file, 'rb') - msg = MIMEBase(main_type, sub_type) - msg.set_payload(fp.read()) - fp.close() - filename = os.path.basename(file) - msg.add_header('Content-Disposition', 'attachment', filename=filename) - message.attach(msg) - - encoded_message = base64.urlsafe_b64encode(message.as_bytes()).decode() - return {'raw': encoded_message} -# [END create_message_attachment] diff --git a/gmail/snippet/settings snippets/create_filter.py b/gmail/snippet/settings snippets/create_filter.py index 17508392..3531e2c5 100644 --- a/gmail/snippet/settings snippets/create_filter.py +++ b/gmail/snippet/settings snippets/create_filter.py @@ -12,52 +12,53 @@ See the License for the specific language governing permissions and limitations under the License. """ -# [START gmail_create_filter] - -from __future__ import print_function +# [START gmail_create_filter] import google.auth from googleapiclient.discovery import build from googleapiclient.errors import HttpError def create_filter(): - """Create a filter. - Returns: Draft object, including filter id. - - Load pre-authorized user credentials from the environment. - TODO(developer) - See https://developers.google.com/identity - for guides on implementing OAuth2 for the application. - """ - creds, _ = google.auth.default() - - try: - # create gmail api client - service = build('gmail', 'v1', credentials=creds) - - label_name = 'IMPORTANT' - filter_content = { - 'criteria': { - 'from': 'gsuder1@workspacesamples.dev' - }, - 'action': { - 'addLabelIds': [label_name], - 'removeLabelIds': ['INBOX'] - } - } - - # pylint: disable=E1101 - result = service.users().settings().filters().create( - userId='me', body=filter_content).execute() - print(F'Created filter with id: {result.get("id")}') - - except HttpError as error: - print(F'An error occurred: {error}') - result = None - - return result.get('id') - - -if __name__ == '__main__': - create_filter() + """Create a filter. + Returns: Draft object, including filter id. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create gmail api client + service = build("gmail", "v1", credentials=creds) + + label_name = "IMPORTANT" + filter_content = { + "criteria": {"from": "gsuder1@workspacesamples.dev"}, + "action": { + "addLabelIds": [label_name], + "removeLabelIds": ["INBOX"], + }, + } + + # pylint: disable=E1101 + result = ( + service.users() + .settings() + .filters() + .create(userId="me", body=filter_content) + .execute() + ) + print(f'Created filter with id: {result.get("id")}') + + except HttpError as error: + print(f"An error occurred: {error}") + result = None + + return result.get("id") + + +if __name__ == "__main__": + create_filter() # [END gmail_create_filter] diff --git a/gmail/snippet/settings snippets/enable_auto_reply.py b/gmail/snippet/settings snippets/enable_auto_reply.py index 69bd727c..fa9f3425 100644 --- a/gmail/snippet/settings snippets/enable_auto_reply.py +++ b/gmail/snippet/settings snippets/enable_auto_reply.py @@ -12,10 +12,8 @@ See the License for the specific language governing permissions and limitations under the License. """ -# [START gmail_enable_auto_reply] - -from __future__ import print_function +# [START gmail_enable_auto_reply] from datetime import datetime, timedelta import google.auth @@ -25,45 +23,50 @@ def enable_auto_reply(): - """Enable auto reply. - Returns:Draft object, including reply message and response meta data. - - Load pre-authorized user credentials from the environment. - TODO(developer) - See https://developers.google.com/identity - for guides on implementing OAuth2 for the application. - """ - creds, _ = google.auth.default() - - try: - # create gmail api client - service = build('gmail', 'v1', credentials=creds) - - epoch = datetime.utcfromtimestamp(0) - now = datetime.now() - start_time = (now - epoch).total_seconds() * 1000 - end_time = (now + timedelta(days=7) - epoch).total_seconds() * 1000 - vacation_settings = { - 'enableAutoReply': True, - 'responseBodyHtml': "I am on vacation and will reply when I am " - "back in the office. Thanks!", - 'restrictToDomain': True, - 'startTime': long(start_time), - 'endTime': long(end_time) - } - - # pylint: disable=E1101 - response = service.users().settings().updateVacation( - userId='me', body=vacation_settings).execute() - print(F'Enabled AutoReply with message: ' - F'{response.get("responseBodyHtml")}') - - except HttpError as error: - print(F'An error occurred: {error}') - response = None - - return response - - -if __name__ == '__main__': - enable_auto_reply() + """Enable auto reply. + Returns:Draft object, including reply message and response meta data. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create gmail api client + service = build("gmail", "v1", credentials=creds) + + epoch = datetime.utcfromtimestamp(0) + now = datetime.now() + start_time = (now - epoch).total_seconds() * 1000 + end_time = (now + timedelta(days=7) - epoch).total_seconds() * 1000 + vacation_settings = { + "enableAutoReply": True, + "responseBodyHtml": ( + "I am on vacation and will reply when I am " + "back in the office. Thanks!" + ), + "restrictToDomain": True, + "startTime": long(start_time), + "endTime": long(end_time), + } + + # pylint: disable=E1101 + response = ( + service.users() + .settings() + .updateVacation(userId="me", body=vacation_settings) + .execute() + ) + print(f"Enabled AutoReply with message: {response.get('responseBodyHtml')}") + + except HttpError as error: + print(f"An error occurred: {error}") + response = None + + return response + + +if __name__ == "__main__": + enable_auto_reply() # [END gmail_enable_auto_reply] diff --git a/gmail/snippet/settings snippets/enable_forwarding.py b/gmail/snippet/settings snippets/enable_forwarding.py index 97f63157..17e4b80f 100644 --- a/gmail/snippet/settings snippets/enable_forwarding.py +++ b/gmail/snippet/settings snippets/enable_forwarding.py @@ -12,52 +12,59 @@ See the License for the specific language governing permissions and limitations under the License. """ -# [START gmail_enable_forwarding] - -from __future__ import print_function +# [START gmail_enable_forwarding] import google.auth from googleapiclient.discovery import build from googleapiclient.errors import HttpError def enable_forwarding(): - """Enable email forwarding. - Returns:Draft object, including forwarding id and result meta data. - - Load pre-authorized user credentials from the environment. - TODO(developer) - See https://developers.google.com/identity - for guides on implementing OAuth2 for the application. - """ - creds, _ = google.auth.default() - - try: - # create gmail api client - service = build('gmail', 'v1', credentials=creds) - - address = {'forwardingEmail': 'gduser1@workspacesamples.dev'} - - # pylint: disable=E1101 - result = service.users().settings().forwardingAddresses(). \ - create(userId='me', body=address).execute() - if result.get('verificationStatus') == 'accepted': - body = { - 'emailAddress': result.get('forwardingEmail'), - 'enabled': True, - 'disposition': 'trash' - } - # pylint: disable=E1101 - result = service.users().settings().updateAutoForwarding( - userId='me', body=body).execute() - print(F'Forwarding is enabled : {result}') - - except HttpError as error: - print(F'An error occurred: {error}') - result = None - - return result - - -if __name__ == '__main__': - enable_forwarding() + """Enable email forwarding. + Returns:Draft object, including forwarding id and result meta data. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create gmail api client + service = build("gmail", "v1", credentials=creds) + + address = {"forwardingEmail": "gduser1@workspacesamples.dev"} + + # pylint: disable=E1101 + result = ( + service.users() + .settings() + .forwardingAddresses() + .create(userId="me", body=address) + .execute() + ) + if result.get("verificationStatus") == "accepted": + body = { + "emailAddress": result.get("forwardingEmail"), + "enabled": True, + "disposition": "trash", + } + # pylint: disable=E1101 + result = ( + service.users() + .settings() + .updateAutoForwarding(userId="me", body=body) + .execute() + ) + print(f"Forwarding is enabled : {result}") + + except HttpError as error: + print(f"An error occurred: {error}") + result = None + + return result + + +if __name__ == "__main__": + enable_forwarding() # [END gmail_enable_forwarding] diff --git a/gmail/snippet/settings snippets/test_create_filter.py b/gmail/snippet/settings snippets/test_create_filter.py new file mode 100644 index 00000000..27a4abe8 --- /dev/null +++ b/gmail/snippet/settings snippets/test_create_filter.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from create_filter import create_filter + + +class TestCreateFilter(unittest.TestCase): + """Unit test class to implement test case for Snippets""" + + @classmethod + def test_create_file(cls): + """test to create file""" + result = create_filter() + cls.assertIsNotNone(cls, result) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/settings snippets/test_enable_auto_reply.py b/gmail/snippet/settings snippets/test_enable_auto_reply.py new file mode 100644 index 00000000..92873a8a --- /dev/null +++ b/gmail/snippet/settings snippets/test_enable_auto_reply.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from enable_auto_reply import enable_auto_reply + + +class TestEnableAutoReply(unittest.TestCase): + """Unit test class for the snippet""" + + @classmethod + def test_enable_auto_reply(cls): + """test to enable auto reply""" + result = enable_auto_reply() + cls.assertIsNotNone(cls, result) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/settings snippets/test_enable_forwarding.py b/gmail/snippet/settings snippets/test_enable_forwarding.py new file mode 100644 index 00000000..22411e23 --- /dev/null +++ b/gmail/snippet/settings snippets/test_enable_forwarding.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from enable_forwarding import enable_forwarding + + +class TestEnableForwarding(unittest.TestCase): + """Unit test class to test enable forwarding snippet""" + + @classmethod + def test_enable_forwarding(cls): + """test to enable forwarding""" + result = enable_forwarding() + cls.assertIsNotNone(cls, result) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/settings snippets/test_update_signature.py b/gmail/snippet/settings snippets/test_update_signature.py new file mode 100644 index 00000000..71a60c7a --- /dev/null +++ b/gmail/snippet/settings snippets/test_update_signature.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from update_signature import update_signature + + +class TestUpdateSignature(unittest.TestCase): + """Unit test class to test Update signature snippet""" + + @classmethod + def test_update_signature(cls): + """test to update signature""" + result = update_signature() + cls.assertIsNotNone(cls, result) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/settings snippets/update_signature.py b/gmail/snippet/settings snippets/update_signature.py index bfa93c37..b0711616 100644 --- a/gmail/snippet/settings snippets/update_signature.py +++ b/gmail/snippet/settings snippets/update_signature.py @@ -12,57 +12,62 @@ See the License for the specific language governing permissions and limitations under the License. """ -# [START gmail_update_signature] - -from __future__ import print_function +# [START gmail_update_signature] import google.auth from googleapiclient.discovery import build from googleapiclient.errors import HttpError def update_signature(): - """Create and update signature in gmail. - Returns:Draft object, including updated signature. - - Load pre-authorized user credentials from the environment. - TODO(developer) - See https://developers.google.com/identity - for guides on implementing OAuth2 for the application. - """ - creds, _ = google.auth.default() - - try: - # create gmail api client - service = build('gmail', 'v1', credentials=creds) - - primary_alias = None - - # pylint: disable=E1101 - aliases = service.users().settings().sendAs().list(userId='me')\ - .execute() - for alias in aliases.get('sendAs'): - if alias.get('isPrimary'): - primary_alias = alias - break - - send_as_configuration = { - 'displayName': primary_alias.get('sendAsEmail'), - 'signature': 'Automated Signature' - } - - # pylint: disable=E1101 - result = service.users().settings().sendAs() \ - .patch(userId='me', sendAsEmail=primary_alias.get('sendAsEmail'), - body=send_as_configuration).execute() - print(F'Updated signature for: {result.get("displayName")}') - - except HttpError as error: - print(F'An error occurred: {error}') - result = None - - return result.get('signature') - - -if __name__ == '__main__': - update_signature() + """Create and update signature in gmail. + Returns:Draft object, including updated signature. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create gmail api client + service = build("gmail", "v1", credentials=creds) + + primary_alias = None + + # pylint: disable=E1101 + aliases = service.users().settings().sendAs().list(userId="me").execute() + for alias in aliases.get("sendAs"): + if alias.get("isPrimary"): + primary_alias = alias + break + + send_as_configuration = { + "displayName": primary_alias.get("sendAsEmail"), + "signature": "Automated Signature", + } + + # pylint: disable=E1101 + result = ( + service.users() + .settings() + .sendAs() + .patch( + userId="me", + sendAsEmail=primary_alias.get("sendAsEmail"), + body=send_as_configuration, + ) + .execute() + ) + print(f'Updated signature for: {result.get("displayName")}') + + except HttpError as error: + print(f"An error occurred: {error}") + result = None + + return result.get("signature") + + +if __name__ == "__main__": + update_signature() # [END gmail_update_signature] diff --git a/gmail/snippet/settings_snippets.py b/gmail/snippet/settings_snippets.py deleted file mode 100644 index 30addfa6..00000000 --- a/gmail/snippet/settings_snippets.py +++ /dev/null @@ -1,101 +0,0 @@ -from datetime import datetime, timedelta - -from numpy import long - - -class SettingsSnippets: - - def __init__(self, service): - self.service = service - - def update_signature(self): - gmail_service = self.service - # [START updateSignature] - primary_alias = None - aliases = gmail_service.users().settings().sendAs(). \ - list(userId='me').execute() - for alias in aliases.get('sendAs'): - if alias.get('isPrimary'): - primary_alias = alias - break - - sendAsConfiguration = { - 'signature': 'I heart cats' - } - result = gmail_service.users().settings().sendAs(). \ - patch(userId='me', - sendAsEmail=primary_alias.get('sendAsEmail'), - body=sendAsConfiguration).execute() - print('Updated signature for: %s' % result.get('displayName')) - # [END updateSignature] - return result.get('signature') - - def create_filter(self, real_label_id): - gmail_service = self.service - # [START createFilter] - label_id = 'Label_14' # ID of user label to add - # [START_EXCLUDE silent] - label_id = real_label_id - # [END_EXCLUDE] - filter = { - 'criteria': { - 'from': 'cat-enthusiasts@example.com' - }, - 'action': { - 'addLabelIds': [label_id], - 'removeLabelIds': ['INBOX'] - } - } - result = gmail_service.users().settings().filters(). \ - create(userId='me', body=filter).execute() - print('Created filter: %s' % result.get('id')) - # [END createFilter] - return result.get('id') - - def enable_forwarding(self, real_forwarding_address): - gmail_service = self.service - # [START enableForwarding] - address = { - 'forwardingEmail': 'user2@example.com' - } - # [START_EXCLUDE silent] - address = { - 'forwardingEmail': real_forwarding_address - } - # [END_EXCLUDE] - result = gmail_service.users().settings().forwardingAddresses(). \ - create(userId='me', body=address).execute() - if result.get('verificationStatus') == 'accepted': - body = { - 'emailAddress': result.get('forwardingEmail'), - 'enabled': True, - 'disposition': 'trash' - } - result = gmail_service.users().settings(). \ - updateAutoForwarding(userId='me', body=body).execute() - # [START_EXCLUDE silent] - return result - # [END_EXCLUDE] - - # [END enableForwarding] - return None - - def enable_auto_reply(self): - gmail_service = self.service - # [START enableAutoReply] - epoch = datetime.utcfromtimestamp(0) - now = datetime.now() - start_time = (now - epoch).total_seconds() * 1000 - end_time = (now + timedelta(days=7) - epoch).total_seconds() * 1000 - vacation_settings = { - 'enableAutoReply': True, - 'responseBodyHtml': "I'm on vacation and will reply when I'm " - "back in the office. Thanks!", - 'restrictToDomain': True, - 'startTime': long(start_time), - 'endTime': long(end_time) - } - response = gmail_service.users().settings(). \ - updateVacation(userId='me', body=vacation_settings).execute() - # [END enableAutoReply] - return response diff --git a/gmail/snippet/smime snippets/create_smime_info.py b/gmail/snippet/smime snippets/create_smime_info.py index ba66e5ae..90c4325a 100644 --- a/gmail/snippet/smime snippets/create_smime_info.py +++ b/gmail/snippet/smime snippets/create_smime_info.py @@ -9,38 +9,36 @@ See the License for the specific language governing permissions and limitations under the License. """ -# [START gmail_create_smime_info] - -from __future__ import print_function +# [START gmail_create_smime_info] import base64 def create_smime_info(cert_filename, cert_password): - """Create an smimeInfo resource for a certificate from file. - Args: - cert_filename: Name of the file containing the S/MIME certificate. - cert_password: Password for the certificate file, or None if the file is not - password-protected. - Returns : Smime object, including smime information - """ - + """Create an smimeInfo resource for a certificate from file. + Args: + cert_filename: Name of the file containing the S/MIME certificate. + cert_password: Password for the certificate file, or None if the file is not + password-protected. + Returns : Smime object, including smime information + """ + + smime_info = None + try: + with open(cert_filename, "rb") as cert: + smime_info = {} + data = cert.read().encode("UTF-8") + smime_info["pkcs12"] = base64.urlsafe_b64encode(data).decode() + if cert_password and len(cert_password) > 0: + smime_info["encryptedKeyPassword"] = cert_password + + except (OSError, IOError) as error: + print(f"An error occurred while reading the certificate file: {error}") smime_info = None - try: - with open(cert_filename, 'rb') as cert: - smime_info = {} - data = cert.read().encode('UTF-8') - smime_info['pkcs12'] = base64.urlsafe_b64encode(data).decode() - if cert_password and len(cert_password) > 0: - smime_info['encryptedKeyPassword'] = cert_password - - except (OSError, IOError) as error: - print(F'An error occurred while reading the certificate file: {error}') - smime_info = None - return smime_info + return smime_info -if __name__ == '__main__': - print(create_smime_info(cert_filename='xyz', cert_password='xyz')) +if __name__ == "__main__": + print(create_smime_info(cert_filename="xyz", cert_password="xyz")) # [END gmail_create_smime_info] diff --git a/gmail/snippet/smime snippets/insert_cert_from_csv.py b/gmail/snippet/smime snippets/insert_cert_from_csv.py new file mode 100644 index 00000000..d07fc2fa --- /dev/null +++ b/gmail/snippet/smime snippets/insert_cert_from_csv.py @@ -0,0 +1,52 @@ +"""Copyright 2018 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START gmail_insert_cert_from_csv] +import csv + +import create_smime_info +import insert_smime_info + + +def insert_cert_from_csv(csv_filename): + """Upload S/MIME certificates based on the contents of a CSV file. + Each row of the CSV file should contain a user ID, path to the certificate, + and the certificate password. + + Args: + csv_filename: Name of the CSV file. + """ + + try: + with open(csv_filename, "rb") as cert: + csv_reader = csv.reader(cert, delimiter=",") + next(csv_reader, None) # skip CSV file header + for row in csv_reader: + user_id = row[0] + cert_filename = row[1] + cert_password = row[2] + smime_info = create_smime_info.create_smime_info( + cert_filename=cert_filename, cert_password=cert_password + ) + if smime_info: + insert_smime_info.insert_smime_info() + else: + print(f"Unable to read certificate file for user_id: {user_id}") + return smime_info + + except (OSError, IOError) as error: + print(f"An error occured while reading the CSV file: {error}") + + +if __name__ == "__main__": + insert_cert_from_csv(csv_filename="xyz") +# [END gmail_insert_cert_from_csv] diff --git a/gmail/snippet/smime snippets/insert_smime_info.py b/gmail/snippet/smime snippets/insert_smime_info.py index 46a8fe8e..19540ad7 100644 --- a/gmail/snippet/smime snippets/insert_smime_info.py +++ b/gmail/snippet/smime snippets/insert_smime_info.py @@ -9,10 +9,8 @@ See the License for the specific language governing permissions and limitations under the License. """ -# [START gmail_insert_smime_info] - -from __future__ import print_function +# [START gmail_insert_smime_info] import create_smime_info import google.auth from googleapiclient.discovery import build @@ -20,40 +18,47 @@ def insert_smime_info(): - """Upload an S/MIME certificate for the user. - Print the inserted certificate's id - Returns : Result object with inserted certificate id and other meta-data + """Upload an S/MIME certificate for the user. + Print the inserted certificate's id + Returns : Result object with inserted certificate id and other meta-data - Load pre-authorized user credentials from the environment. - TODO(developer) - See https://developers.google.com/identity - for guides on implementing OAuth2 for the application. - """ - creds, _ = google.auth.default() + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() - try: - # create gmail api client - service = build('gmail', 'v1', credentials=creds) + try: + # create gmail api client + service = build("gmail", "v1", credentials=creds) - user_id = 'gduser1@workspacesamples.dev' - smime_info = create_smime_info.create_smime_info(cert_filename='xyz', cert_password='xyz') - send_as_email = None + user_id = "gduser1@workspacesamples.dev" + smime_info = create_smime_info.create_smime_info( + cert_filename="xyz", cert_password="xyz" + ) + send_as_email = None - if not send_as_email: - send_as_email = user_id + if not send_as_email: + send_as_email = user_id - # pylint: disable=maybe-no-member - results = service.users().settings().sendAs().smimeInfo().\ - insert(userId=user_id, sendAsEmail=send_as_email, body=smime_info)\ - .execute() - print(F'Inserted certificate; id: {results["id"]}') + # pylint: disable=maybe-no-member + results = ( + service.users() + .settings() + .sendAs() + .smimeInfo() + .insert(userId=user_id, sendAsEmail=send_as_email, body=smime_info) + .execute() + ) + print(f'Inserted certificate; id: {results["id"]}') - except HttpError as error: - print(F'An error occurred: {error}') - results = None + except HttpError as error: + print(f"An error occurred: {error}") + results = None - return results + return results -if __name__ == '__main__': - insert_smime_info() +if __name__ == "__main__": + insert_smime_info() # [END gmail_insert_smime_info] diff --git a/gmail/snippet/smime snippets/test_create_smime_info.py b/gmail/snippet/smime snippets/test_create_smime_info.py new file mode 100644 index 00000000..d5a1f278 --- /dev/null +++ b/gmail/snippet/smime snippets/test_create_smime_info.py @@ -0,0 +1,33 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from create_smime_info import create_smime_info + + +class TestCreateSmimeInfo(unittest.TestCase): + """Unit test class to test Snippet""" + + @classmethod + def test_create_smime_info(cls): + """test to create smime info""" + # enter the file and password accordingly + smime_info = create_smime_info(cert_filename="abc", cert_password="abc") + cls.assertIsNotNone(cls, smime_info) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/smime snippets/test_insert_cert_from_csv.py b/gmail/snippet/smime snippets/test_insert_cert_from_csv.py new file mode 100644 index 00000000..4d728d2b --- /dev/null +++ b/gmail/snippet/smime snippets/test_insert_cert_from_csv.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from insert_cert_from_csv import insert_cert_from_csv + + +class TestInsertCertFromCsv(unittest.TestCase): + """unittest class for testing the snippetts""" + + @classmethod + def test_insert_cert_from_csv(cls): + """test to insert cert from csv""" + result = insert_cert_from_csv("test.csv") + cls.assertIsNotNone(cls, result) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/smime snippets/test_insert_smime_info.py b/gmail/snippet/smime snippets/test_insert_smime_info.py new file mode 100644 index 00000000..3478fcf7 --- /dev/null +++ b/gmail/snippet/smime snippets/test_insert_smime_info.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from insert_smime_info import insert_smime_info + + +class TestInsertSmimeInfo(unittest.TestCase): + """Unit test class for snippet""" + + @classmethod + def test_insert_smime_info(cls): + """test to insert smime info""" + result = insert_smime_info() + cls.assertIsNotNone(cls, result) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/smime snippets/test_update_smime_cert.py b/gmail/snippet/smime snippets/test_update_smime_cert.py new file mode 100644 index 00000000..54343b28 --- /dev/null +++ b/gmail/snippet/smime snippets/test_update_smime_cert.py @@ -0,0 +1,38 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from update_smime_cert import update_smime_cert + + +class TestUpdateSmimeCert(unittest.TestCase): + """Unit test class for snippets""" + + @classmethod + def test_update_smime_cert(cls): + """test update smime cert""" + result = update_smime_cert( + user_id="xyz", + send_as_email="yzx", + cert_filename="abc", + cert_password="abc", + expire_dt="cde", + ) + cls.assertIsNotNone(cls, result) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/smime snippets/test_update_smime_from_csv.py b/gmail/snippet/smime snippets/test_update_smime_from_csv.py new file mode 100644 index 00000000..f03d082f --- /dev/null +++ b/gmail/snippet/smime snippets/test_update_smime_from_csv.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from update_smime_from_csv import update_smime_from_csv + + +class TestUpdateSmimeFromCsv(unittest.TestCase): + """unit test class for snippets""" + + @classmethod + def test_update_smime_from_csv(cls): + """test to update smime from csv""" + result = update_smime_from_csv(csv_filename="abc") + cls.assertIsNotNone(cls, result) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/smime snippets/update_smime_cert.py b/gmail/snippet/smime snippets/update_smime_cert.py new file mode 100644 index 00000000..34d72ded --- /dev/null +++ b/gmail/snippet/smime snippets/update_smime_cert.py @@ -0,0 +1,126 @@ +"""Copyright 2018 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START gmail_update_smime_certs] +from datetime import datetime + +import create_smime_info +import google.auth +import insert_smime_info +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def update_smime_cert( + user_id, send_as_email, cert_filename, cert_password, expire_dt +): + """Update S/MIME certificates for the user. + + First performs a lookup of all certificates for a user. If there are no + certificates, or they all expire before the specified date/time, uploads the + certificate in the specified file. If the default certificate is expired or + there was no default set, chooses the certificate with the expiration furthest + into the future and sets it as default. + + Args: + user_id: User's email address. + send_as_email: The "send as" email address, or None if it should be the same + as user_id. + cert_filename: Name of the file containing the S/MIME certificate. + cert_password: Password for the certificate file, or None if the file is not + password-protected. + expire_dt: DateTime object against which the certificate expiration is + compared. If None, uses the current time. + + Returns: + The ID of the default certificate. + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + if not send_as_email: + send_as_email = user_id + + creds, _ = google.auth.default() + + try: + # create gmail api client + service = build("gmail", "v1", credentials=creds) + + # pylint: disable=maybe-no-member + results = ( + service.users() + .settings() + .sendAs() + .smimeInfo() + .list(userId=user_id, sendAsEmail=send_as_email) + .execute() + ) + + except HttpError as error: + print(f"An error occurred during list: {error}") + return None + + default_cert_id = None + best_cert_id = (None, datetime.datetime.fromtimestamp(0)) + + if not expire_dt: + expire_dt = datetime.datetime.now() + if results and "smimeInfo" in results: + for smime_info in results["smimeInfo"]: + cert_id = smime_info["id"] + is_default_cert = smime_info["isDefault"] + if is_default_cert: + default_cert_id = cert_id + exp = datetime.datetime.fromtimestamp(smime_info["expiration"] / 1000) + if exp > expire_dt: + if exp > best_cert_id[1]: + best_cert_id = (cert_id, exp) + else: + if is_default_cert: + default_cert_id = None + + if not default_cert_id: + default_id = best_cert_id[0] + if not default_id and cert_filename: + create_smime_info.create_smime_info( + cert_filename=cert_filename, cert_password=cert_password + ) + results = insert_smime_info.insert_smime_info() + if results: + default_id = results["id"] + + if default_id: + try: + # pylint: disable=maybe-no-member + service.users().settings().sendAs().smimeInfo().setDefault( + userId=user_id, sendAsEmail=send_as_email, id=default_id + ).execute() + return default_id + except HttpError as error: + print(f"An error occurred during setDefault: {error}") + else: + return default_cert_id + + return None + + +if __name__ == "__main__": + update_smime_cert( + user_id="xyz", + send_as_email=None, + cert_filename="xyz", + cert_password="xyz", + expire_dt=None, + ) +# [END gmail_update_smime_certs] diff --git a/gmail/snippet/smime snippets/update_smime_from_csv.py b/gmail/snippet/smime snippets/update_smime_from_csv.py new file mode 100644 index 00000000..6e38e07f --- /dev/null +++ b/gmail/snippet/smime snippets/update_smime_from_csv.py @@ -0,0 +1,52 @@ +"""Copyright 2018 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START gmail_update_smime_from_csv] +import csv + +import update_smime_cert + + +# pylint: disable-this-line-in-some-way +def update_smime_from_csv(csv_filename, expire_dt=None): + """Update S/MIME certificates based on the contents of a CSV file. + + Each row of the CSV file should contain a user ID, path to the certificate, + and the certificate password. + + Args: + csv_filename: Name of the CSV file. + expire_dt: DateTime object against which the certificate expiration is + compared. If None, uses the current time. + """ + ret0 = "" + try: + with open(csv_filename, "rb") as cert: + csv_reader = csv.reader(cert, delimiter=",") + next(csv_reader, None) # skip CSV file header + for row in csv_reader: + user_id = row[0] + ret0 = update_smime_cert.update_smime_cert( + user_id, + send_as_email=user_id, + cert_filename=row[1], + cert_password=row[2], + expire_dt=expire_dt, + ) + return ret0 + except (OSError, IOError) as error: + print(f"An error occured while reading the CSV file: {error}") + + +if __name__ == "__main__": + update_smime_from_csv(csv_filename="xyz") +# [END gmail_update_smime_from_csv] diff --git a/gmail/snippet/smime_snippets.py b/gmail/snippet/smime_snippets.py deleted file mode 100644 index 6cc30818..00000000 --- a/gmail/snippet/smime_snippets.py +++ /dev/null @@ -1,203 +0,0 @@ -"""Snippets for managing S/MIME certificate for a user's account. - -These snippets appear at: -https://developers.google.com/gmail/api/guides/smime_certs -""" -import base64 -import csv -import datetime - -from apiclient import errors - - -# [START create_smime_info] -def create_smime_info(cert_filename, cert_password=None): - """Create an smimeInfo resource for a certificate from file. - - Args: - cert_filename: Name of the file containing the S/MIME certificate. - cert_password: Password for the certificate file, or None if the file is not - password-protected. - """ - smime_info = None - try: - with open(cert_filename, 'r') as f: - smime_info = {} - data = f.read().encode('UTF-8') - smime_info['pkcs12'] = base64.urlsafe_b64encode(data) - if cert_password and len(cert_password) > 0: - smime_info['encryptedKeyPassword'] = cert_password - except (OSError, IOError) as error: - print('An error occurred while reading the certificate file: %s' % error) - - return smime_info - - -# [END create_smime_info] - - -# [START insert_smime_info] -def insert_smime_info(service, user_id, smime_info, send_as_email=None): - """Upload an S/MIME certificate for the user. - - Args: - service: Authorized GMail API service instance. - user_id: User's email address. - smime_info: The smimeInfo resource containing the user's S/MIME certificate. - send_as_email: The "send as" email address, or None if it should be the same - as user_id. - """ - if not send_as_email: - send_as_email = user_id - try: - results = service.users().settings().sendAs().smimeInfo().insert( - userId=user_id, sendAsEmail=send_as_email, body=smime_info).execute() - print('Inserted certificate; id: %s' % results['id']) - return results - except errors.HttpError as error: - print('An error occurred: %s' % error) - return None - - -# [END insert_smime_info] - - -# [START insert_cert_from_csv] -def insert_cert_from_csv(service_builder, csv_filename): - """Upload S/MIME certificates based on the contents of a CSV file. - - Each row of the CSV file should contain a user ID, path to the certificate, - and the certificate password. - - Args: - service_builder: A function that returns an authorized GMail API service - instance for a given user. - csv_filename: Name of the CSV file. - """ - try: - with open(csv_filename, 'r') as f: - csv_reader = csv.reader(f, delimiter=',') - next(csv_reader, None) # skip CSV file header - for row in csv_reader: - user_id = row[0] - cert_filename = row[1] - cert_password = row[2] - smime_info = create_smime_info(cert_filename, cert_password) - if smime_info: - insert_smime_info(service_builder(user_id), user_id, smime_info) - else: - print('Unable to read certificate file for user_id: %s' % user_id) - except (OSError, IOError) as error: - print('An error occured while reading the CSV file: %s' % error) - - -# [END insert_cert_from_csv] - - -# [START update_smime_certs] -def update_smime_certs(service, - user_id, - send_as_email=None, - cert_filename=None, - cert_password=None, - expire_dt=None): - """Update S/MIME certificates for the user. - - First performs a lookup of all certificates for a user. If there are no - certificates, or they all expire before the specified date/time, uploads the - certificate in the specified file. If the default certificate is expired or - there was no default set, chooses the certificate with the expiration furthest - into the future and sets it as default. - - Args: - service: Authorized GMail API service instance. - user_id: User's email address. - send_as_email: The "send as" email address, or None if it should be the same - as user_id. - cert_filename: Name of the file containing the S/MIME certificate. - cert_password: Password for the certificate file, or None if the file is not - password-protected. - expire_dt: DateTime object against which the certificate expiration is - compared. If None, uses the current time. - - Returns: - The ID of the default certificate. - """ - if not send_as_email: - send_as_email = user_id - - try: - results = service.users().settings().sendAs().smimeInfo().list( - userId=user_id, sendAsEmail=send_as_email).execute() - except errors.HttpError as error: - print('An error occurred during list: %s' % error) - return None - - default_cert_id = None - best_cert_id = (None, datetime.datetime.fromtimestamp(0)) - - if not expire_dt: - expire_dt = datetime.datetime.now() - if results and 'smimeInfo' in results: - for smime_info in results['smimeInfo']: - cert_id = smime_info['id'] - is_default_cert = smime_info['isDefault'] - if is_default_cert: - default_cert_id = cert_id - exp = datetime.datetime.fromtimestamp(smime_info['expiration'] / 1000) - if exp > expire_dt: - if exp > best_cert_id[1]: - best_cert_id = (cert_id, exp) - else: - if is_default_cert: - default_cert_id = None - - if not default_cert_id: - default_id = best_cert_id[0] - if not default_id and cert_filename: - smime_info = create_smime_info(cert_filename, cert_password) - results = insert_smime_info(service, user_id, smime_info) - if results: - default_id = results['id'] - - if default_id: - try: - service.users().settings().sendAs().smimeInfo().setDefault( - userId=user_id, sendAsEmail=send_as_email, id=default_id).execute() - return default_id - except errors.HttpError as error: - print('An error occurred during setDefault: %s' % error) - else: - return default_cert_id - - return None - - -def update_smime_from_csv(service_builder, csv_filename, expire_dt=None): - """Update S/MIME certificates based on the contents of a CSV file. - - Each row of the CSV file should contain a user ID, path to the certificate, - and the certificate password. - - Args: - service_builder: A function that returns an authorized GMail API service - instance for a given user. - csv_filename: Name of the CSV file. - expire_dt: DateTime object against which the certificate expiration is - compared. If None, uses the current time. - """ - try: - with open(csv_filename, 'r') as f: - csv_reader = csv.reader(f, delimiter=',') - next(csv_reader, None) # skip CSV file header - for row in csv_reader: - user_id = row[0] - update_smime_certs( - service_builder(user_id), - user_id, - cert_filename=row[1], - cert_password=row[2], - expire_dt=expire_dt) - except (OSError, IOError) as error: - print('An error occured while reading the CSV file: %s' % error) -# [END update_smime_certs] diff --git a/gmail/snippet/test_send_mail.py b/gmail/snippet/test_send_mail.py deleted file mode 100644 index e29c7771..00000000 --- a/gmail/snippet/test_send_mail.py +++ /dev/null @@ -1,53 +0,0 @@ -import unittest - -import send_mail -from base_test import BaseTest - - -class SendMailTest(BaseTest): - @classmethod - def setUpClass(cls): - super(SendMailTest, cls).setUpClass() - - def setUp(self): - super(SendMailTest, self).setUp() - - def tearDown(self): - super(SendMailTest, self).tearDown() - - def test_create_message(self): - message = send_mail.create_message(SendMailTest.TEST_USER, - SendMailTest.RECIPIENT, - 'Test', - 'Hello!') - self.assertIsNotNone(message) # Weak assertion - - def test_create_message_with_attachment(self): - message = send_mail.create_message_with_attachment(SendMailTest.TEST_USER, - SendMailTest.RECIPIENT, - 'Test', - 'Hello!', - 'files/photo.jpg') - self.assertIsNotNone(message) # Weak assertion - - def test_create_draft(self): - message = send_mail.create_message(SendMailTest.TEST_USER, - SendMailTest.RECIPIENT, - 'Test', - 'Hello!') - draft = send_mail.create_draft(self.service, 'me', message) - self.assertIsNotNone(draft) - self.service.users().drafts().delete(userId='me', id=draft.get('id')) - - def test_send_mail(self): - message = send_mail.create_message_with_attachment(SendMailTest.TEST_USER, - SendMailTest.RECIPIENT, - 'Test', - 'Hello!', - 'files/photo.jpg') - sent_message = send_mail.send_message(self.service, 'me', message) - self.assertIsNotNone(sent_message) - - -if __name__ == '__main__': - unittest.main() diff --git a/gmail/snippet/test_settings_snippets.py b/gmail/snippet/test_settings_snippets.py deleted file mode 100644 index 95bd72d0..00000000 --- a/gmail/snippet/test_settings_snippets.py +++ /dev/null @@ -1,60 +0,0 @@ -import unittest - -from base_test import BaseTest -from settings_snippets import SettingsSnippets - - -class SettingsSnippetsTest(BaseTest): - - @classmethod - def setUpClass(cls): - super(SettingsSnippetsTest, cls).setUpClass() - cls.snippets = SettingsSnippets(cls.service) - - def setUp(self): - super(SettingsSnippetsTest, self).setUp() - - def tearDown(self): - super(SettingsSnippetsTest, self).tearDown() - - def create_test_label(self): - labels = self.service.users().labels().list(userId='me').execute() - for label in labels.get('labels'): - if label.get('name') == 'testLabel': - return label - - body = { - 'name': 'testLabel', - 'labelListVisibility': 'labelShow', - 'messageListVisibility': 'show' - } - return self.service.users().labels().create(userId='me', body=body).execute() - - def test_update_signature(self): - signature = self.snippets.update_signature() - self.assertIsNotNone(signature) - - def test_create_filter(self): - test_label = self.create_test_label() - id = self.snippets.create_filter(test_label.get('id')) - self.assertIsNotNone(id) - self.service.users().settings().filters().delete(userId='me', id=id).execute() - self.service.users().labels().delete(userId='me', id=test_label.get('id')) - - def test_enable_auto_forwarding(self): - forwarding = self.snippets.enable_forwarding(BaseTest.FORWARDING_ADDRESS) - self.assertIsNotNone(forwarding) - body = { - 'enabled': False, - } - self.service.users().settings().updateAutoForwarding(userId='me', body=body).execute() - self.service.users().settings().forwardingAddresses().delete( - userId='me', forwardingEmail=BaseTest.FORWARDING_ADDRESS).execute() - - def test_enable_auto_reply(self): - settings = self.snippets.enable_auto_reply() - self.assertIsNotNone(settings) - - -if __name__ == '__main__': - unittest.main() diff --git a/gmail/snippet/test_smime_snippets.py b/gmail/snippet/test_smime_snippets.py deleted file mode 100644 index 65d790d0..00000000 --- a/gmail/snippet/test_smime_snippets.py +++ /dev/null @@ -1,368 +0,0 @@ -import datetime -import unittest -from unittest import mock - -import httplib2 -import smime_snippets -from apiclient import errors - - -class SmimeSnippetsTest(unittest.TestCase): - CURRENT_TIME = 1234567890 - TEST_USER = 'user1@example.com' - - @classmethod - def setUpClass(cls): - pass - - def setUp(self): - self.mock_delete = mock.Mock() - self.mock_get = mock.Mock() - self.mock_insert = mock.Mock() - self.mock_list = mock.Mock() - self.mock_set_default = mock.Mock() - - self.mock_service = mock.Mock() - self.mock_service.users.return_value = self.mock_service - self.mock_service.settings.return_value = self.mock_service - self.mock_service.sendAs.return_value = self.mock_service - self.mock_service.smimeInfo.return_value = self.mock_service - - self.mock_service.delete = self.mock_delete - self.mock_service.get = self.mock_get - self.mock_service.insert = self.mock_insert - self.mock_service.list = self.mock_list - self.mock_service.setDefault = self.mock_set_default - - def tearDown(self): - # The delete() and get() API methods are not used and should not be called. - self.mock_delete.assert_not_called() - self.mock_get.assert_not_called() - - @staticmethod - def make_fake_insert_result(id='new_certificate_id', - is_default=False, - expiration=CURRENT_TIME + 1): - """Construct a fake result of calling insert() on the S/MIME API. - - By default, the certificate expires after CURRENT_TIME. - """ - return { - 'id': id, - 'isDefault': is_default, - 'expiration': expiration * 1000, - 'issuerCn': 'fake_authority', - 'pem': 'fake_certificate_contents', - } - - def make_fake_list_result(self, - is_default=[False], - expiration=[CURRENT_TIME + 1]): - """Construct a fake result of calling list() on the S/MIME API. - - No more than one of the values in is_default may be True. - By default, each certificate expires after CURRENT_TIME. - """ - self.assertEqual(len(is_default), len(expiration)) - self.assertLessEqual(is_default.count(True), 1) - smime_info = [] - id_base = 'existing_certificate_id_%d' - for i in range(len(is_default)): - smime_info.append( - self.make_fake_insert_result( - id=(id_base % i), - is_default=is_default[i], - expiration=expiration[i])) - return {'smimeInfo': smime_info} - - @staticmethod - def make_http_error(status, reason): - """Construct a fake HttpError thrown by the API.""" - response = httplib2.Response({'status': status}) - response.reason = reason - return errors.HttpError(resp=response, content=b'') - - def test_create_smime_info(self): - smime_info = smime_snippets.create_smime_info('files/cert.p12') - - self.assertIsNotNone(smime_info) - self.assertListEqual(list(smime_info.keys()), ['pkcs12']) - self.assertGreater(len(smime_info['pkcs12']), 0) - - def test_create_smime_info_with_password(self): - smime_info = smime_snippets.create_smime_info('files/cert.p12', 'password') - - self.assertIsNotNone(smime_info) - self.assertSetEqual( - set(smime_info.keys()), set(['pkcs12', 'encryptedKeyPassword'])) - self.assertGreater(len(smime_info['pkcs12']), 0) - self.assertEqual(smime_info['encryptedKeyPassword'], 'password') - - def test_create_smime_info_file_not_found(self): - smime_info = smime_snippets.create_smime_info('files/notfound.p12') - - self.mock_insert.assert_not_called() - self.mock_list.assert_not_called() - self.mock_set_default.assert_not_called() - - self.assertIsNone(smime_info) - - def test_insert_smime_info(self): - insert_result = self.make_fake_insert_result() - self.mock_insert.return_value = mock.Mock( - **{'execute.return_value': insert_result}) - - smime_info = smime_snippets.create_smime_info('files/cert.p12') - result = smime_snippets.insert_smime_info(self.mock_service, self.TEST_USER, - smime_info) - - self.mock_insert.assert_called_with( - userId=self.TEST_USER, sendAsEmail=self.TEST_USER, body=smime_info) - self.mock_list.assert_not_called() - self.mock_set_default.assert_not_called() - - self.assertEqual(result, insert_result) - - def test_insert_smime_info_error(self): - fake_error = self.make_http_error(500, 'Fake Error') - self.mock_insert.side_effect = fake_error - - smime_info = smime_snippets.create_smime_info('files/cert.p12') - result = smime_snippets.insert_smime_info( - self.mock_service, self.TEST_USER, smime_info, 'user1alias@example.com') - - self.mock_insert.assert_called_with( - userId=self.TEST_USER, - sendAsEmail='user1alias@example.com', - body=smime_info) - self.mock_list.assert_not_called() - self.mock_set_default.assert_not_called() - - self.assertIsNone(result) - - def test_insert_cert_from_csv(self): - self.mock_insert.return_value = mock.Mock( - **{'execute.return_value': self.make_fake_insert_result()}) - - smime_snippets.insert_cert_from_csv(lambda x: self.mock_service, - 'files/certs.csv') - - self.assertListEqual(self.mock_insert.call_args_list, [ - mock.call( - userId='user1@example.com', - sendAsEmail='user1@example.com', - body=mock.ANY), - mock.call( - userId='user2@example.com', - sendAsEmail='user2@example.com', - body=mock.ANY) - ]) - self.mock_list.assert_not_called() - self.mock_set_default.assert_not_called() - - def test_insert_cert_from_csv_fails(self): - smime_snippets.insert_cert_from_csv(lambda x: self.mock_service, - 'files/notfound.csv') - - self.mock_insert.assert_not_called() - self.mock_list.assert_not_called() - self.mock_set_default.assert_not_called() - - def test_update_smime_certs_no_certs(self): - self.mock_list.return_value = mock.Mock(**{'execute.return_value': None}) - - default_cert_id = smime_snippets.update_smime_certs(self.mock_service, - self.TEST_USER) - - self.mock_list.assert_called_with( - userId=self.TEST_USER, sendAsEmail=self.TEST_USER) - self.mock_insert.assert_not_called() - self.mock_set_default.assert_not_called() - - self.assertIsNone(default_cert_id) - - def test_update_smime_certs_no_certs_upload_new_cert(self): - self.mock_list.return_value = mock.Mock(**{'execute.return_value': None}) - self.mock_insert.return_value = mock.Mock( - **{'execute.return_value': self.make_fake_insert_result()}) - - default_cert_id = smime_snippets.update_smime_certs( - self.mock_service, self.TEST_USER, cert_filename='files/cert.p12') - - self.mock_list.assert_called_with( - userId=self.TEST_USER, sendAsEmail=self.TEST_USER) - self.mock_insert.assert_called_with( - userId=self.TEST_USER, sendAsEmail=self.TEST_USER, body=mock.ANY) - self.mock_set_default.assert_called_with( - userId=self.TEST_USER, - sendAsEmail=self.TEST_USER, - id='new_certificate_id') - - self.assertEqual(default_cert_id, 'new_certificate_id') - - def test_update_smime_certs_valid_default_cert_no_upload(self): - expire_dt = datetime.datetime.fromtimestamp(self.CURRENT_TIME) - fake_list_result = self.make_fake_list_result(is_default=[True]) - self.mock_list.return_value = mock.Mock( - **{'execute.return_value': fake_list_result}) - - default_cert_id = smime_snippets.update_smime_certs( - self.mock_service, - self.TEST_USER, - cert_filename='files/cert.p12', - expire_dt=expire_dt) - - self.mock_list.assert_called_with( - userId=self.TEST_USER, sendAsEmail=self.TEST_USER) - self.mock_insert.assert_not_called() - self.mock_set_default.assert_not_called() - - self.assertEqual(default_cert_id, 'existing_certificate_id_0') - - def test_update_smime_certs_expired_default_cert_upload_new_cert(self): - expire_dt = datetime.datetime.fromtimestamp(self.CURRENT_TIME + 2) - fake_list_result = self.make_fake_list_result(is_default=[True]) - self.mock_list.return_value = mock.Mock( - **{'execute.return_value': fake_list_result}) - self.mock_insert.return_value = mock.Mock( - **{'execute.return_value': self.make_fake_insert_result()}) - - default_cert_id = smime_snippets.update_smime_certs( - self.mock_service, - self.TEST_USER, - cert_filename='files/cert.p12', - expire_dt=expire_dt) - - self.mock_list.assert_called_with( - userId=self.TEST_USER, sendAsEmail=self.TEST_USER) - self.mock_insert.assert_called_with( - userId=self.TEST_USER, sendAsEmail=self.TEST_USER, body=mock.ANY) - self.mock_set_default.assert_called_with( - userId=self.TEST_USER, - sendAsEmail=self.TEST_USER, - id='new_certificate_id') - - self.assertEqual(default_cert_id, 'new_certificate_id') - - def test_update_smime_certs_default_cert_expired_other_cert_new_default(self): - expire_dt = datetime.datetime.fromtimestamp(self.CURRENT_TIME) - fake_list_result = self.make_fake_list_result( - is_default=[True, False], - expiration=[self.CURRENT_TIME - 1, self.CURRENT_TIME + 1]) - self.mock_list.return_value = mock.Mock( - **{'execute.return_value': fake_list_result}) - - default_cert_id = smime_snippets.update_smime_certs( - self.mock_service, - self.TEST_USER, - cert_filename='files/cert.p12', - expire_dt=expire_dt) - - self.mock_list.assert_called_with( - userId=self.TEST_USER, sendAsEmail=self.TEST_USER) - self.mock_set_default.assert_called_with( - userId=self.TEST_USER, - sendAsEmail=self.TEST_USER, - id='existing_certificate_id_1') - self.mock_insert.assert_not_called() - - self.assertEqual(default_cert_id, 'existing_certificate_id_1') - - def test_update_smime_certs_no_defaults_choose_best_cert_as_new_default(self): - expire_dt = datetime.datetime.fromtimestamp(self.CURRENT_TIME) - fake_list_result = self.make_fake_list_result( - is_default=[False, False, False, False], - expiration=[ - self.CURRENT_TIME + 2, self.CURRENT_TIME + 1, self.CURRENT_TIME + 4, - self.CURRENT_TIME + 3 - ]) - self.mock_list.return_value = mock.Mock( - **{'execute.return_value': fake_list_result}) - - default_cert_id = smime_snippets.update_smime_certs( - self.mock_service, - self.TEST_USER, - cert_filename='files/cert.p12', - expire_dt=expire_dt) - - self.mock_list.assert_called_with( - userId=self.TEST_USER, sendAsEmail=self.TEST_USER) - self.mock_set_default.assert_called_with( - userId=self.TEST_USER, - sendAsEmail=self.TEST_USER, - id='existing_certificate_id_2') - self.mock_insert.assert_not_called() - - self.assertEqual(default_cert_id, 'existing_certificate_id_2') - - def test_update_smime_certs_error(self): - expire_dt = datetime.datetime.fromtimestamp(self.CURRENT_TIME) - fake_error = self.make_http_error(500, 'Fake Error') - self.mock_list.side_effect = fake_error - - default_cert_id = smime_snippets.update_smime_certs( - self.mock_service, - self.TEST_USER, - cert_filename='files/cert.p12', - expire_dt=expire_dt) - - self.mock_list.assert_called_with( - userId=self.TEST_USER, sendAsEmail=self.TEST_USER) - self.mock_insert.assert_not_called() - self.mock_set_default.assert_not_called() - - self.assertIsNone(default_cert_id) - - def test_update_smime_from_csv(self): - self.mock_list.return_value = mock.Mock(**{'execute.return_value': None}) - self.mock_insert.return_value = mock.Mock( - **{'execute.return_value': self.make_fake_insert_result()}) - - smime_snippets.update_smime_from_csv(lambda x: self.mock_service, - 'files/certs.csv') - - self.assertListEqual(self.mock_list.call_args_list, [ - mock.call(userId='user1@example.com', sendAsEmail='user1@example.com'), - mock.call(userId='user2@example.com', sendAsEmail='user2@example.com'), - mock.call(userId='user3@example.com', sendAsEmail='user3@example.com'), - ]) - self.assertListEqual(self.mock_insert.call_args_list, [ - mock.call( - userId='user1@example.com', - sendAsEmail='user1@example.com', - body=mock.ANY), - mock.call( - userId='user2@example.com', - sendAsEmail='user2@example.com', - body=mock.ANY), - mock.call( - userId='user3@example.com', - sendAsEmail='user3@example.com', - body=mock.ANY) - ]) - self.assertListEqual(self.mock_set_default.call_args_list, [ - mock.call( - userId='user1@example.com', - sendAsEmail='user1@example.com', - id='new_certificate_id'), - mock.call( - userId='user2@example.com', - sendAsEmail='user2@example.com', - id='new_certificate_id'), - mock.call( - userId='user3@example.com', - sendAsEmail='user3@example.com', - id='new_certificate_id'), - ]) - - def test_update_smime_from_csv_fails(self): - smime_snippets.update_smime_from_csv(lambda x: self.mock_service, - 'files/notfound.csv') - - self.mock_insert.assert_not_called() - self.mock_list.assert_not_called() - self.mock_set_default.assert_not_called() - - -if __name__ == '__main__': - unittest.main() diff --git a/gmail/snippet/test_threads.py b/gmail/snippet/test_threads.py deleted file mode 100644 index db39ace2..00000000 --- a/gmail/snippet/test_threads.py +++ /dev/null @@ -1,24 +0,0 @@ -import unittest - -import threads -from base_test import BaseTest - - -class ThreadsTest(BaseTest): - @classmethod - def setUpClass(cls): - super(ThreadsTest, cls).setUpClass() - - def setUp(self): - super(ThreadsTest, self).setUp() - - def tearDown(self): - super(ThreadsTest, self).tearDown() - - def test_show_chatty_threads(self): - # TODO - Capture output and assert - threads.show_chatty_threads(self.service) - - -if __name__ == '__main__': - unittest.main() diff --git a/gmail/snippet/test_update_signature.py b/gmail/snippet/test_update_signature.py deleted file mode 100644 index ac85a078..00000000 --- a/gmail/snippet/test_update_signature.py +++ /dev/null @@ -1,26 +0,0 @@ -import unittest - -from base_test import BaseTest -from settings_snippets import SettingsSnippets - - -class SettingsSnippetsTest(BaseTest): - - @classmethod - def setUpClass(cls): - super(SettingsSnippetsTest, cls).setUpClass() - cls.snippets = SettingsSnippets(cls.service) - - def setUp(self): - super(SettingsSnippetsTest, self).setUp() - - def tearDown(self): - super(SettingsSnippetsTest, self).tearDown() - - def test_update_signature(self): - signature = self.snippets.update_signature() - self.assertIsNotNone(signature) - - -if __name__ == '__main__': - unittest.main() diff --git a/gmail/snippet/thread/test_thread.py b/gmail/snippet/thread/test_thread.py new file mode 100644 index 00000000..a365fa28 --- /dev/null +++ b/gmail/snippet/thread/test_thread.py @@ -0,0 +1,32 @@ +"""Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +from threads import show_chatty_threads + + +class TestThreads(unittest.TestCase): + """unit test class for snippets""" + + @classmethod + def test_threads(cls): + """to test threads""" + result = show_chatty_threads() + cls.assertIsNotNone(cls, result) + + +if __name__ == "__main__": + unittest.main() diff --git a/gmail/snippet/thread/threads.py b/gmail/snippet/thread/threads.py index 9df8bcd5..973316a3 100644 --- a/gmail/snippet/thread/threads.py +++ b/gmail/snippet/thread/threads.py @@ -9,50 +9,54 @@ See the License for the specific language governing permissions and limitations under the License. """ -# [START gmail_show_chatty_threads] - -from __future__ import print_function +# [START gmail_show_chatty_threads] import google.auth from googleapiclient.discovery import build from googleapiclient.errors import HttpError def show_chatty_threads(): - """Display threads with long conversations(>= 3 messages) - Return: None - - Load pre-authorized user credentials from the environment. - TODO(developer) - See https://developers.google.com/identity - for guides on implementing OAuth2 for the application. - """ - creds, _ = google.auth.default() - - try: - # create gmail api client - service = build('gmail', 'v1', credentials=creds) - - # pylint: disable=maybe-no-member - threads = service.users().threads().list(userId='me').execute().get('threads', []) - for thread in threads: - tdata = service.users().threads().get(userId='me', id=thread['id']).execute() - nmsgs = len(tdata['messages']) - - # skip if <3 msgs in thread - if nmsgs > 2: - msg = tdata['messages'][0]['payload'] - subject = '' - for header in msg['headers']: - if header['name'] == 'Subject': - subject = header['value'] - break - if subject: # skip if no Subject line - print(F'- {subject}, {nmsgs}') - - except HttpError as error: - print(F'An error occurred: {error}') - - -if __name__ == '__main__': - show_chatty_threads() + """Display threads with long conversations(>= 3 messages) + Return: None + + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + + try: + # create gmail api client + service = build("gmail", "v1", credentials=creds) + + # pylint: disable=maybe-no-member + # pylint: disable:R1710 + threads = ( + service.users().threads().list(userId="me").execute().get("threads", []) + ) + for thread in threads: + tdata = ( + service.users().threads().get(userId="me", id=thread["id"]).execute() + ) + nmsgs = len(tdata["messages"]) + + # skip if <3 msgs in thread + if nmsgs > 2: + msg = tdata["messages"][0]["payload"] + subject = "" + for header in msg["headers"]: + if header["name"] == "Subject": + subject = header["value"] + break + if subject: # skip if no Subject line + print(f"- {subject}, {nmsgs}") + return threads + + except HttpError as error: + print(f"An error occurred: {error}") + + +if __name__ == "__main__": + show_chatty_threads() # [END gmail_show_chatty_threads] diff --git a/gmail/snippet/threads.py b/gmail/snippet/threads.py deleted file mode 100644 index fb20da43..00000000 --- a/gmail/snippet/threads.py +++ /dev/null @@ -1,23 +0,0 @@ -"""Functions for using threads with the Gmail API -""" - -from __future__ import print_function - - -# [START show_chatty_threads] -def show_chatty_threads(service, user_id='me'): - threads = service.users().threads().list(userId=user_id).execute().get('threads', []) - for thread in threads: - tdata = service.users().threads().get(userId=user_id, id=thread['id']).execute() - nmsgs = len(tdata['messages']) - - if nmsgs > 2: # skip if <3 msgs in thread - msg = tdata['messages'][0]['payload'] - subject = '' - for header in msg['headers']: - if header['name'] == 'Subject': - subject = header['value'] - break - if subject: # skip if no Subject line - print('- %s (%d msgs)' % (subject, nmsgs)) -# [END show_chatty_threads] diff --git a/meet/README.md b/meet/README.md new file mode 100644 index 00000000..588c40a2 --- /dev/null +++ b/meet/README.md @@ -0,0 +1 @@ +Additional samples can be found at https://github.com/googleapis/google-cloud-python/tree/main/packages/google-apps-meet \ No newline at end of file diff --git a/drive/activity/quickstart.py b/meet/quickstart/quickstart.py similarity index 57% rename from drive/activity/quickstart.py rename to meet/quickstart/quickstart.py index 8f31c5de..077f91d4 100644 --- a/drive/activity/quickstart.py +++ b/meet/quickstart/quickstart.py @@ -12,24 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -# [START drive_activity_quickstart] +# [START meet_quickstart] from __future__ import print_function -import datetime import os.path from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow -from googleapiclient.discovery import build +from google.apps import meet_v2 + # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/activity'] +SCOPES = ['https://www.googleapis.com/auth/meetings.space.created'] def main(): - """Shows basic usage of the Drive Activity API. - Prints information about the last 10 events that occured the user's Drive. + """Shows basic usage of the Google Meet API. """ creds = None # The file token.json stores the user's access and refresh tokens, and is @@ -49,29 +48,16 @@ def main(): with open('token.json', 'w') as token: token.write(creds.to_json()) - service = build('appsactivity', 'v1', credentials=creds) - - # Call the Drive Activity API - results = service.activities().list(source='drive.google.com', - drive_ancestorId='root', pageSize=10).execute() - activities = results.get('activities', []) - - if not activities: - print('No activity.') - else: - print('Recent activity:') - for activity in activities: - event = activity['combinedEvent'] - user = event.get('user', None) - target = event.get('target', None) - if user is None or target is None: - continue - time = datetime.datetime.fromtimestamp( - int(event['eventTimeMillis']) / 1000) - print(u'{0}: {1}, {2}, {3} ({4})'.format(time, user['name'], - event['primaryEventType'], target['name'], target['mimeType'])) + try: + client = meet_v2.SpacesServiceClient(credentials=creds) + request = meet_v2.CreateSpaceRequest() + response = client.create_space(request=request) + print(f'Space created: {response.meeting_uri}') + except Exception as error: + # TODO(developer) - Handle errors from Meet API. + print(f'An error occurred: {error}') if __name__ == '__main__': main() -# [END drive_activity_quickstart] +# [END meet_quickstart] diff --git a/meet/quickstart/requirements.txt b/meet/quickstart/requirements.txt new file mode 100644 index 00000000..7eee1e6d --- /dev/null +++ b/meet/quickstart/requirements.txt @@ -0,0 +1,3 @@ +google-apps-meet==0.1.6 +google-auth-httplib2==0.1.0 +google-auth-oauthlib==0.4.0 \ No newline at end of file diff --git a/people/quickstart/quickstart.py b/people/quickstart/quickstart.py index 65a07282..3129e453 100644 --- a/people/quickstart/quickstart.py +++ b/people/quickstart/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START people_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -24,51 +22,58 @@ from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/contacts.readonly'] +SCOPES = ["https://www.googleapis.com/auth/contacts.readonly"] def main(): - """Shows basic usage of the People API. - Prints the name of the first 10 connections. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Shows basic usage of the People API. + Prints the name of the first 10 connections. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - try: - service = build('people', 'v1', credentials=creds) + try: + service = build("people", "v1", credentials=creds) - # Call the People API - print('List 10 connection names') - results = service.people().connections().list( - resourceName='people/me', + # Call the People API + print("List 10 connection names") + results = ( + service.people() + .connections() + .list( + resourceName="people/me", pageSize=10, - personFields='names,emailAddresses').execute() - connections = results.get('connections', []) + personFields="names,emailAddresses", + ) + .execute() + ) + connections = results.get("connections", []) - for person in connections: - names = person.get('names', []) - if names: - name = names[0].get('displayName') - print(name) - except HttpError as err: - print(err) + for person in connections: + names = person.get("names", []) + if names: + name = names[0].get("displayName") + print(name) + except HttpError as err: + print(err) -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END people_quickstart] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..eaaa7420 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,5 @@ +[tool.pyink] +line-length = 80 +preview = true +pyink-indentation = 2 +pyink-use-majority-quotes = true \ No newline at end of file diff --git a/renovate.json b/renovate.json deleted file mode 100644 index f45d8f11..00000000 --- a/renovate.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": [ - "config:base" - ] -} diff --git a/sheets/quickstart/quickstart.py b/sheets/quickstart/quickstart.py index 236ab878..6cb90a21 100644 --- a/sheets/quickstart/quickstart.py +++ b/sheets/quickstart/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START sheets_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -24,56 +22,60 @@ from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly'] +SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"] # The ID and range of a sample spreadsheet. -SAMPLE_SPREADSHEET_ID = '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms' -SAMPLE_RANGE_NAME = 'Class Data!A2:E' +SAMPLE_SPREADSHEET_ID = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms" +SAMPLE_RANGE_NAME = "Class Data!A2:E" def main(): - """Shows basic usage of the Sheets API. - Prints values from a sample spreadsheet. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Shows basic usage of the Sheets API. + Prints values from a sample spreadsheet. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - try: - service = build('sheets', 'v4', credentials=creds) + try: + service = build("sheets", "v4", credentials=creds) - # Call the Sheets API - sheet = service.spreadsheets() - result = sheet.values().get(spreadsheetId=SAMPLE_SPREADSHEET_ID, - range=SAMPLE_RANGE_NAME).execute() - values = result.get('values', []) + # Call the Sheets API + sheet = service.spreadsheets() + result = ( + sheet.values() + .get(spreadsheetId=SAMPLE_SPREADSHEET_ID, range=SAMPLE_RANGE_NAME) + .execute() + ) + values = result.get("values", []) - if not values: - print('No data found.') - return + if not values: + print("No data found.") + return - print('Name, Major:') - for row in values: - # Print columns A and E, which correspond to indices 0 and 4. - print('%s, %s' % (row[0], row[4])) - except HttpError as err: - print(err) + print("Name, Major:") + for row in values: + # Print columns A and E, which correspond to indices 0 and 4. + print(f"{row[0]}, {row[4]}") + except HttpError as err: + print(err) -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END sheets_quickstart] diff --git a/sheets/snippets/base_test.py b/sheets/snippets/base_test.py index 4f189163..56ea3116 100644 --- a/sheets/snippets/base_test.py +++ b/sheets/snippets/base_test.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import print_function - import sys import unittest @@ -24,75 +22,74 @@ class BaseTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.credentials = cls.create_credentials() - http = cls.credentials.authorize(httplib2.Http()) - cls.credentials.refresh(http) - cls.service = build('sheets', 'v4', http=http) - cls.drive_service = build('drive', 'v3', http=http) - # Hide STDOUT output generated by snippets. - cls.stdout = sys.stdout - sys.stdout = None - @classmethod - def tearDownClass(cls): - # Restore STDOUT. - sys.stdout = cls.stdout + @classmethod + def setUpClass(cls): + cls.credentials = cls.create_credentials() + http = cls.credentials.authorize(httplib2.Http()) + cls.credentials.refresh(http) + cls.service = build("sheets", "v4", http=http) + cls.drive_service = build("drive", "v3", http=http) + # Hide STDOUT output generated by snippets. + cls.stdout = sys.stdout + sys.stdout = None - @classmethod - def create_credentials(cls): - cls.credentials = GoogleCredentials.get_application_default() - scope = ['https://www.googleapis.com/auth/drive'] - return cls.credentials.create_scoped(scope) + @classmethod + def tearDownClass(cls): + # Restore STDOUT. + sys.stdout = cls.stdout - def setUp(self): - self.files_to_delete = [] + @classmethod + def create_credentials(cls): + cls.credentials = GoogleCredentials.get_application_default() + scope = ["https://www.googleapis.com/auth/drive"] + return cls.credentials.create_scoped(scope) - def tearDown(self): - for file_id in self.files_to_delete: - try: - self.drive_service.files().delete(fileId=file_id).execute() - except errors.HttpError: - print('Unable to delete file %s' % file_id, file=sys.stderr) + def setUp(self): + self.files_to_delete = [] - def delete_file_on_cleanup(self, file_id): - self.files_to_delete.append(file_id) + def tearDown(self): + for file_id in self.files_to_delete: + try: + self.drive_service.files().delete(fileId=file_id).execute() + except errors.HttpError: + print(f"Unable to delete file {file_id}", file=sys.stderr) - def create_test_spreadsheet(self): - spreadsheet = { - 'properties': { - 'title': 'Sales Report' - } - } - spreadsheet = self.service.spreadsheets().create(body=spreadsheet, - fields='spreadsheetId').execute() - self.delete_file_on_cleanup(spreadsheet.get('spreadsheetId')) - return spreadsheet.get('spreadsheetId') + def delete_file_on_cleanup(self, file_id): + self.files_to_delete.append(file_id) - def populate_values(self, spreadsheet_id): - body = { - 'requests': [{ - 'repeatCell': { - 'range': { - 'sheetId': 0, - 'startRowIndex': 0, - 'endRowIndex': 10, - 'startColumnIndex': 0, - 'endColumnIndex': 10 - }, - 'cell': { - 'userEnteredValue': { - 'stringValue': 'Hello' - } + def create_test_spreadsheet(self): + spreadsheet = {"properties": {"title": "Sales Report"}} + spreadsheet = ( + self.service.spreadsheets() + .create(body=spreadsheet, fields="spreadsheetId") + .execute() + ) + self.delete_file_on_cleanup(spreadsheet.get("spreadsheetId")) + return spreadsheet.get("spreadsheetId") + + def populate_values(self, spreadsheet_id): + body = { + "requests": [ + { + "repeatCell": { + "range": { + "sheetId": 0, + "startRowIndex": 0, + "endRowIndex": 10, + "startColumnIndex": 0, + "endColumnIndex": 10, }, - 'fields': 'userEnteredValue' + "cell": {"userEnteredValue": {"stringValue": "Hello"}}, + "fields": "userEnteredValue", } - }] - } - self.service.spreadsheets().batchUpdate( - spreadsheetId=spreadsheet_id, body=body).execute() + } + ] + } + self.service.spreadsheets().batchUpdate( + spreadsheetId=spreadsheet_id, body=body + ).execute() -if __name__ == '__main__': - unittest.main() +if __name__ == "__main__": + unittest.main() diff --git a/sheets/snippets/sheets_append_values.py b/sheets/snippets/sheets_append_values.py new file mode 100644 index 00000000..3fba74f5 --- /dev/null +++ b/sheets/snippets/sheets_append_values.py @@ -0,0 +1,72 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START sheets_append_values] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def append_values(spreadsheet_id, range_name, value_input_option, _values): + """ + Creates the batch_update the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("sheets", "v4", credentials=creds) + + values = [ + [ + # Cell values ... + ], + # Additional rows ... + ] + # [START_EXCLUDE silent] + values = _values + # [END_EXCLUDE] + body = {"values": values} + result = ( + service.spreadsheets() + .values() + .append( + spreadsheetId=spreadsheet_id, + range=range_name, + valueInputOption=value_input_option, + body=body, + ) + .execute() + ) + print(f"{(result.get('updates').get('updatedCells'))} cells appended.") + return result + + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Pass: spreadsheet_id, range_name value_input_option and _values) + append_values( + "1CM29gwKIzeXsAppeNwrc8lbYaVMmUclprLuLYuHog4k", + "A1:C2", + "USER_ENTERED", + [["F", "B"], ["C", "D"]], + ) + # [END sheets_append_values] diff --git a/sheets/snippets/sheets_batch_get_values.py b/sheets/snippets/sheets_batch_get_values.py new file mode 100644 index 00000000..65666839 --- /dev/null +++ b/sheets/snippets/sheets_batch_get_values.py @@ -0,0 +1,58 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START sheets_batch_get_values] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def batch_get_values(spreadsheet_id, _range_names): + """ + Creates the batch_update the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("sheets", "v4", credentials=creds) + range_names = [ + # Range names ... + ] + # [START_EXCLUDE silent] + range_names = _range_names + # [END_EXCLUDE] + result = ( + service.spreadsheets() + .values() + .batchGet(spreadsheetId=spreadsheet_id, ranges=range_names) + .execute() + ) + ranges = result.get("valueRanges", []) + print(f"{len(ranges)} ranges retrieved") + return result + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Pass: spreadsheet_id, and range_name + + batch_get_values("1CM29gwKIzeXsAppeNwrc8lbYaVMmUclprLuLYuHog4k", "A1:C2") + # [END sheets_batch_get_values] diff --git a/sheets/snippets/sheets_batch_update.py b/sheets/snippets/sheets_batch_update.py new file mode 100644 index 00000000..037db84c --- /dev/null +++ b/sheets/snippets/sheets_batch_update.py @@ -0,0 +1,79 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START sheets_batch_update] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def sheets_batch_update(spreadsheet_id, title, find, replacement): + """ + Update the sheet details in batch, the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + + try: + service = build("sheets", "v4", credentials=creds) + + requests = [] + # Change the spreadsheet's title. + requests.append( + { + "updateSpreadsheetProperties": { + "properties": {"title": title}, + "fields": "title", + } + } + ) + # Find and replace text + requests.append( + { + "findReplace": { + "find": find, + "replacement": replacement, + "allSheets": True, + } + } + ) + # Add additional requests (operations) ... + + body = {"requests": requests} + response = ( + service.spreadsheets() + .batchUpdate(spreadsheetId=spreadsheet_id, body=body) + .execute() + ) + find_replace_response = response.get("replies")[1].get("findReplace") + print( + f"{find_replace_response.get('occurrencesChanged')} replacements made." + ) + return response + + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + sheets_batch_update("spreadsheet_id", "title", "find", "replacement") + +# [END sheets_batch_update] diff --git a/sheets/snippets/sheets_batch_update_values.py b/sheets/snippets/sheets_batch_update_values.py new file mode 100644 index 00000000..c125ccbd --- /dev/null +++ b/sheets/snippets/sheets_batch_update_values.py @@ -0,0 +1,72 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START sheets_batch_update_values] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def batch_update_values( + spreadsheet_id, range_name, value_input_option, _values +): + """ + Creates the batch_update the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("sheets", "v4", credentials=creds) + + values = [ + [ + # Cell values ... + ], + # Additional rows + ] + # [START_EXCLUDE silent] + values = _values + # [END_EXCLUDE] + data = [ + {"range": range_name, "values": values}, + # Additional ranges to update ... + ] + body = {"valueInputOption": value_input_option, "data": data} + result = ( + service.spreadsheets() + .values() + .batchUpdate(spreadsheetId=spreadsheet_id, body=body) + .execute() + ) + print(f"{(result.get('totalUpdatedCells'))} cells updated.") + return result + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Pass: spreadsheet_id, range_name value_input_option and _values) + batch_update_values( + "1CM29gwKIzeXsAppeNwrc8lbYaVMmUclprLuLYuHog4k", + "A1:C2", + "USER_ENTERED", + [["F", "B"], ["C", "D"]], + ) + # [END sheets_batch_update_values] diff --git a/sheets/snippets/sheets_conditional_formatting.py b/sheets/snippets/sheets_conditional_formatting.py new file mode 100644 index 00000000..d8951f1d --- /dev/null +++ b/sheets/snippets/sheets_conditional_formatting.py @@ -0,0 +1,111 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START sheets_conditional_formatting] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def conditional_formatting(spreadsheet_id): + """ + Creates the batch_update the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("sheets", "v4", credentials=creds) + + my_range = { + "sheetId": 0, + "startRowIndex": 1, + "endRowIndex": 11, + "startColumnIndex": 0, + "endColumnIndex": 4, + } + requests = [ + { + "addConditionalFormatRule": { + "rule": { + "ranges": [my_range], + "booleanRule": { + "condition": { + "type": "CUSTOM_FORMULA", + "values": [ + { + "userEnteredValue": ( + "=GT($D2,median($D$2:$D$11))" + ) + } + ], + }, + "format": { + "textFormat": {"foregroundColor": {"red": 0.8}} + }, + }, + }, + "index": 0, + } + }, + { + "addConditionalFormatRule": { + "rule": { + "ranges": [my_range], + "booleanRule": { + "condition": { + "type": "CUSTOM_FORMULA", + "values": [ + { + "userEnteredValue": ( + "=LT($D2,median($D$2:$D$11))" + ) + } + ], + }, + "format": { + "backgroundColor": { + "red": 1, + "green": 0.4, + "blue": 0.4, + } + }, + }, + }, + "index": 0, + } + }, + ] + body = {"requests": requests} + response = ( + service.spreadsheets() + .batchUpdate(spreadsheetId=spreadsheet_id, body=body) + .execute() + ) + print(f"{(len(response.get('replies')))} cells updated.") + return response + + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Pass: spreadsheet_id + conditional_formatting("1CM29gwKIzeXsAppeNwrc8lbYaVMmUclprLuLYuHog4k") + # [END sheets_conditional_formatting] diff --git a/sheets/snippets/sheets_create.py b/sheets/snippets/sheets_create.py new file mode 100644 index 00000000..20585f12 --- /dev/null +++ b/sheets/snippets/sheets_create.py @@ -0,0 +1,50 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START sheets_create] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create(title): + """ + Creates the Sheet the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("sheets", "v4", credentials=creds) + spreadsheet = {"properties": {"title": title}} + spreadsheet = ( + service.spreadsheets() + .create(body=spreadsheet, fields="spreadsheetId") + .execute() + ) + print(f"Spreadsheet ID: {(spreadsheet.get('spreadsheetId'))}") + return spreadsheet.get("spreadsheetId") + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Pass: title + create("mysheet1") + # [END sheets_create] diff --git a/sheets/snippets/sheets_filter_views.py b/sheets/snippets/sheets_filter_views.py new file mode 100644 index 00000000..d7514aee --- /dev/null +++ b/sheets/snippets/sheets_filter_views.py @@ -0,0 +1,119 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START sheets_filter_views] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def filter_views(spreadsheet_id): + """ + Creates the batch_update the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("sheets", "v4", credentials=creds) + + my_range = { + "sheetId": 0, + "startRowIndex": 0, + "startColumnIndex": 0, + } + addfilterviewrequest = { + "addFilterView": { + "filter": { + "title": "Sample Filter", + "range": my_range, + "sortSpecs": [{ + "dimensionIndex": 3, + "sortOrder": "DESCENDING", + }], + "criteria": { + 0: {"hiddenValues": ["Panel"]}, + 6: { + "condition": { + "type": "DATE_BEFORE", + "values": {"userEnteredValue": "4/30/2016"}, + } + }, + }, + } + } + } + + body = {"requests": [addfilterviewrequest]} + addfilterviewresponse = ( + service.spreadsheets() + .batchUpdate(spreadsheetId=spreadsheet_id, body=body) + .execute() + ) + + duplicatefilterviewrequest = { + "duplicateFilterView": { + "filterId": addfilterviewresponse["replies"][0]["addFilterView"][ + "filter" + ]["filterViewId"] + } + } + + body = {"requests": [duplicatefilterviewrequest]} + duplicatefilterviewresponse = ( + service.spreadsheets() + .batchUpdate(spreadsheetId=spreadsheet_id, body=body) + .execute() + ) + + updatefilterviewrequest = { + "updateFilterView": { + "filter": { + "filterViewId": duplicatefilterviewresponse["replies"][0][ + "duplicateFilterView" + ]["filter"]["filterViewId"], + "title": "Updated Filter", + "criteria": { + 0: {}, + 3: { + "condition": { + "type": "NUMBER_GREATER", + "values": {"userEnteredValue": "5"}, + } + }, + }, + }, + "fields": {"paths": ["criteria", "title"]}, + } + } + + body = {"requests": [updatefilterviewrequest]} + updatefilterviewresponse = ( + service.spreadsheets() + .batchUpdate(spreadsheetId=spreadsheet_id, body=body) + .execute() + ) + print(str(updatefilterviewresponse)) + except HttpError as error: + print(f"An error occurred: {error}") + + +if __name__ == "__main__": + # Pass: spreadsheet_id + filter_views("1CM29gwKIzeXsAppeNwrc8lbYaVMmUclprLuLYuHog4k") + # [END sheets_filter_views] diff --git a/sheets/snippets/sheets_get_values.py b/sheets/snippets/sheets_get_values.py new file mode 100644 index 00000000..d0d46d6a --- /dev/null +++ b/sheets/snippets/sheets_get_values.py @@ -0,0 +1,52 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START sheets_get_values] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def get_values(spreadsheet_id, range_name): + """ + Creates the batch_update the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("sheets", "v4", credentials=creds) + + result = ( + service.spreadsheets() + .values() + .get(spreadsheetId=spreadsheet_id, range=range_name) + .execute() + ) + rows = result.get("values", []) + print(f"{len(rows)} rows retrieved") + return result + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Pass: spreadsheet_id, and range_name + get_values("1CM29gwKIzeXsAppeNwrc8lbYaVMmUclprLuLYuHog4k", "A1:C2") + # [END sheets_get_values] diff --git a/sheets/snippets/sheets_pivot_tables.py b/sheets/snippets/sheets_pivot_tables.py new file mode 100644 index 00000000..86524c32 --- /dev/null +++ b/sheets/snippets/sheets_pivot_tables.py @@ -0,0 +1,114 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START sheets_pivot_tables] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def pivot_tables(spreadsheet_id): + """ + Creates the batch_update the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("sheets", "v4", credentials=creds) + # Create two sheets for our pivot table. + body = {"requests": [{"addSheet": {}}, {"addSheet": {}}]} + batch_update_response = ( + service.spreadsheets() + .batchUpdate(spreadsheetId=spreadsheet_id, body=body) + .execute() + ) + source_sheet_id = ( + batch_update_response.get("replies")[0] + .get("addSheet") + .get("properties") + .get("sheetId") + ) + target_sheet_id = ( + batch_update_response.get("replies")[1] + .get("addSheet") + .get("properties") + .get("sheetId") + ) + requests = [] + requests.append( + { + "updateCells": { + "rows": { + "values": [ + { + "pivotTable": { + "source": { + "sheetId": source_sheet_id, + "startRowIndex": 0, + "startColumnIndex": 0, + "endRowIndex": 20, + "endColumnIndex": 7, + }, + "rows": [ + { + "sourceColumnOffset": 1, + "showTotals": True, + "sortOrder": "ASCENDING", + }, + ], + "columns": [{ + "sourceColumnOffset": 4, + "sortOrder": "ASCENDING", + "showTotals": True, + }], + "values": [{ + "summarizeFunction": "COUNTA", + "sourceColumnOffset": 4, + }], + "valueLayout": "HORIZONTAL", + } + } + ] + }, + "start": { + "sheetId": target_sheet_id, + "rowIndex": 0, + "columnIndex": 0, + }, + "fields": "pivotTable", + } + } + ) + body = {"requests": requests} + response = ( + service.spreadsheets() + .batchUpdate(spreadsheetId=spreadsheet_id, body=body) + .execute() + ) + return response + + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Pass: spreadsheet_id + pivot_tables("1CM29gwKIzeXsAppeNwrc8lbYaVMmUclprLuLYuHog4k") + # [END sheets_pivot_tables] diff --git a/sheets/snippets/sheets_update_values.py b/sheets/snippets/sheets_update_values.py new file mode 100644 index 00000000..962b8cc3 --- /dev/null +++ b/sheets/snippets/sheets_update_values.py @@ -0,0 +1,70 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START sheets_update_values] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def update_values(spreadsheet_id, range_name, value_input_option, _values): + """ + Creates the batch_update the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("sheets", "v4", credentials=creds) + values = [ + [ + # Cell values ... + ], + # Additional rows ... + ] + # [START_EXCLUDE silent] + values = _values + # [END_EXCLUDE] + body = {"values": values} + result = ( + service.spreadsheets() + .values() + .update( + spreadsheetId=spreadsheet_id, + range=range_name, + valueInputOption=value_input_option, + body=body, + ) + .execute() + ) + print(f"{result.get('updatedCells')} cells updated.") + return result + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Pass: spreadsheet_id, range_name, value_input_option and _values + update_values( + "1CM29gwKIzeXsAppeNwrc8lbYaVMmUclprLuLYuHog4k", + "A1:C2", + "USER_ENTERED", + [["A", "B"], ["C", "D"]], + ) + # [END sheets_update_values] diff --git a/sheets/snippets/spreadsheet_snippets.py b/sheets/snippets/spreadsheet_snippets.py deleted file mode 100644 index bef2d5f4..00000000 --- a/sheets/snippets/spreadsheet_snippets.py +++ /dev/null @@ -1,394 +0,0 @@ -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import print_function - - -class SpreadsheetSnippets(object): - def __init__(self, service): - self.service = service - - def create(self, title): - service = self.service - # [START sheets_create] - spreadsheet = { - 'properties': { - 'title': title - } - } - spreadsheet = service.spreadsheets().create(body=spreadsheet, - fields='spreadsheetId').execute() - print('Spreadsheet ID: {0}'.format(spreadsheet.get('spreadsheetId'))) - # [END sheets_create] - return spreadsheet.get('spreadsheetId') - - def batch_update(self, spreadsheet_id, title, find, replacement): - service = self.service - # [START sheets_batch_update] - requests = [] - # Change the spreadsheet's title. - requests.append({ - 'updateSpreadsheetProperties': { - 'properties': { - 'title': title - }, - 'fields': 'title' - } - }) - # Find and replace text - requests.append({ - 'findReplace': { - 'find': find, - 'replacement': replacement, - 'allSheets': True - } - }) - # Add additional requests (operations) ... - - body = { - 'requests': requests - } - response = service.spreadsheets().batchUpdate( - spreadsheetId=spreadsheet_id, - body=body).execute() - find_replace_response = response.get('replies')[1].get('findReplace') - print('{0} replacements made.'.format( - find_replace_response.get('occurrencesChanged'))) - # [END sheets_batch_update] - return response - - def get_values(self, spreadsheet_id, range_name): - service = self.service - # [START sheets_get_values] - result = service.spreadsheets().values().get( - spreadsheetId=spreadsheet_id, range=range_name).execute() - rows = result.get('values', []) - print('{0} rows retrieved.'.format(len(rows))) - # [END sheets_get_values] - return result - - def batch_get_values(self, spreadsheet_id, _range_names): - service = self.service - # [START sheets_batch_get_values] - range_names = [ - # Range names ... - ] - # [START_EXCLUDE silent] - range_names = _range_names - # [END_EXCLUDE] - result = service.spreadsheets().values().batchGet( - spreadsheetId=spreadsheet_id, ranges=range_names).execute() - ranges = result.get('valueRanges', []) - print('{0} ranges retrieved.'.format(len(ranges))) - # [END sheets_batch_get_values] - return result - - def update_values(self, spreadsheet_id, range_name, value_input_option, - _values): - service = self.service - # [START sheets_update_values] - values = [ - [ - # Cell values ... - ], - # Additional rows ... - ] - # [START_EXCLUDE silent] - values = _values - # [END_EXCLUDE] - body = { - 'values': values - } - result = service.spreadsheets().values().update( - spreadsheetId=spreadsheet_id, range=range_name, - valueInputOption=value_input_option, body=body).execute() - print('{0} cells updated.'.format(result.get('updatedCells'))) - # [END sheets_update_values] - return result - - def batch_update_values(self, spreadsheet_id, range_name, - value_input_option, _values): - service = self.service - # [START sheets_batch_update_values] - values = [ - [ - # Cell values ... - ], - # Additional rows - ] - # [START_EXCLUDE silent] - values = _values - # [END_EXCLUDE] - data = [ - { - 'range': range_name, - 'values': values - }, - # Additional ranges to update ... - ] - body = { - 'valueInputOption': value_input_option, - 'data': data - } - result = service.spreadsheets().values().batchUpdate( - spreadsheetId=spreadsheet_id, body=body).execute() - print('{0} cells updated.'.format(result.get('totalUpdatedCells'))) - # [END sheets_batch_update_values] - return result - - def append_values(self, spreadsheet_id, range_name, value_input_option, - _values): - service = self.service - # [START sheets_append_values] - values = [ - [ - # Cell values ... - ], - # Additional rows ... - ] - # [START_EXCLUDE silent] - values = _values - # [END_EXCLUDE] - body = { - 'values': values - } - result = service.spreadsheets().values().append( - spreadsheetId=spreadsheet_id, range=range_name, - valueInputOption=value_input_option, body=body).execute() - print('{0} cells appended.'.format(result - .get('updates') - .get('updatedCells'))) - # [END sheets_append_values] - return result - - def pivot_tables(self, spreadsheet_id): - service = self.service - # Create two sheets for our pivot table. - body = { - 'requests': [{ - 'addSheet': {} - }, { - 'addSheet': {} - }] - } - batch_update_response = service.spreadsheets() \ - .batchUpdate(spreadsheetId=spreadsheet_id, body=body).execute() - source_sheet_id = batch_update_response.get('replies')[0] \ - .get('addSheet').get('properties').get('sheetId') - target_sheet_id = batch_update_response.get('replies')[1] \ - .get('addSheet').get('properties').get('sheetId') - requests = [] - # [START sheets_pivot_tables] - requests.append({ - 'updateCells': { - 'rows': { - 'values': [ - { - 'pivotTable': { - 'source': { - 'sheetId': source_sheet_id, - 'startRowIndex': 0, - 'startColumnIndex': 0, - 'endRowIndex': 20, - 'endColumnIndex': 7 - }, - 'rows': [ - { - 'sourceColumnOffset': 1, - 'showTotals': True, - 'sortOrder': 'ASCENDING', - - }, - - ], - 'columns': [ - { - 'sourceColumnOffset': 4, - 'sortOrder': 'ASCENDING', - 'showTotals': True, - - } - ], - 'values': [ - { - 'summarizeFunction': 'COUNTA', - 'sourceColumnOffset': 4 - } - ], - 'valueLayout': 'HORIZONTAL' - } - } - ] - }, - 'start': { - 'sheetId': target_sheet_id, - 'rowIndex': 0, - 'columnIndex': 0 - }, - 'fields': 'pivotTable' - } - }) - body = { - 'requests': requests - } - response = service.spreadsheets() \ - .batchUpdate(spreadsheetId=spreadsheet_id, body=body).execute() - # [END sheets_pivot_tables] - return response - - def conditional_formatting(self, spreadsheet_id): - service = self.service - - # [START sheets_conditional_formatting] - my_range = { - 'sheetId': 0, - 'startRowIndex': 1, - 'endRowIndex': 11, - 'startColumnIndex': 0, - 'endColumnIndex': 4, - } - requests = [{ - 'addConditionalFormatRule': { - 'rule': { - 'ranges': [my_range], - 'booleanRule': { - 'condition': { - 'type': 'CUSTOM_FORMULA', - 'values': [{ - 'userEnteredValue': - '=GT($D2,median($D$2:$D$11))' - }] - }, - 'format': { - 'textFormat': { - 'foregroundColor': {'red': 0.8} - } - } - } - }, - 'index': 0 - } - }, { - 'addConditionalFormatRule': { - 'rule': { - 'ranges': [my_range], - 'booleanRule': { - 'condition': { - 'type': 'CUSTOM_FORMULA', - 'values': [{ - 'userEnteredValue': - '=LT($D2,median($D$2:$D$11))' - }] - }, - 'format': { - 'backgroundColor': { - 'red': 1, - 'green': 0.4, - 'blue': 0.4 - } - } - } - }, - 'index': 0 - } - }] - body = { - 'requests': requests - } - response = service.spreadsheets() \ - .batchUpdate(spreadsheetId=spreadsheet_id, body=body).execute() - print('{0} cells updated.'.format(len(response.get('replies')))) - # [END sheets_conditional_formatting] - return response - - def filter_views(self, spreadsheet_id): - service = self.service - - # [START sheets_filter_views] - my_range = { - 'sheetId': 0, - 'startRowIndex': 0, - 'startColumnIndex': 0, - } - addFilterViewRequest = { - 'addFilterView': { - 'filter': { - 'title': 'Sample Filter', - 'range': my_range, - 'sortSpecs': [{ - 'dimensionIndex': 3, - 'sortOrder': 'DESCENDING' - }], - 'criteria': { - 0: { - 'hiddenValues': ['Panel'] - }, - 6: { - 'condition': { - 'type': 'DATE_BEFORE', - 'values': { - 'userEnteredValue': '4/30/2016' - } - } - } - } - } - } - } - - body = {'requests': [addFilterViewRequest]} - addFilterViewResponse = service.spreadsheets() \ - .batchUpdate(spreadsheetId=spreadsheet_id, body=body).execute() - - duplicateFilterViewRequest = { - 'duplicateFilterView': { - 'filterId': - addFilterViewResponse['replies'][0]['addFilterView']['filter'] - ['filterViewId'] - } - } - - body = {'requests': [duplicateFilterViewRequest]} - duplicateFilterViewResponse = service.spreadsheets() \ - .batchUpdate(spreadsheetId=spreadsheet_id, body=body).execute() - - updateFilterViewRequest = { - 'updateFilterView': { - 'filter': { - 'filterViewId': duplicateFilterViewResponse['replies'][0] - ['duplicateFilterView']['filter']['filterViewId'], - 'title': 'Updated Filter', - 'criteria': { - 0: {}, - 3: { - 'condition': { - 'type': 'NUMBER_GREATER', - 'values': { - 'userEnteredValue': '5' - } - } - } - } - }, - 'fields': { - 'paths': ['criteria', 'title'] - } - } - } - - body = {'requests': [updateFilterViewRequest]} - updateFilterViewResponse = service.spreadsheets() \ - .batchUpdate(spreadsheetId=spreadsheet_id, body=body).execute() - print(str(updateFilterViewResponse)) - # [END sheets_filter_views] diff --git a/sheets/snippets/test_sheets_append_values.py b/sheets/snippets/test_sheets_append_values.py new file mode 100644 index 00000000..1a2257dd --- /dev/null +++ b/sheets/snippets/test_sheets_append_values.py @@ -0,0 +1,40 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import sheets_append_values +from base_test import BaseTest + + +class Testappendvalues(BaseTest): + """Unit test for append value Sheet snippet""" + + def test_append_values(self): + """test append values function""" + spreadsheet_id = self.create_test_spreadsheet() + self.populate_values(spreadsheet_id) + result = sheets_append_values.append_values( + spreadsheet_id, "Sheet1", "USER_ENTERED", [["A", "B"], ["C", "D"]] + ) + self.assertIsNotNone(result) + self.assertEqual("Sheet1!A1:J10", result.get("tableRange")) + updates = result.get("updates") + self.assertEqual("Sheet1!A11:B12", updates.get("updatedRange")) + self.assertEqual(2, updates.get("updatedRows")) + self.assertEqual(2, updates.get("updatedColumns")) + self.assertEqual(4, updates.get("updatedCells")) + + +if __name__ == "__main__": + unittest.main() diff --git a/sheets/snippets/test_sheets_batch_get_values.py b/sheets/snippets/test_sheets_batch_get_values.py new file mode 100644 index 00000000..cd87729f --- /dev/null +++ b/sheets/snippets/test_sheets_batch_get_values.py @@ -0,0 +1,39 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import sheets_batch_get_values +from base_test import BaseTest + + +class Testgetvalues(BaseTest): + """Unit test class for get value Sheet snippet""" + + def test_batch_get_values(self): + """test batch get values function""" + spreadsheet_id = self.create_test_spreadsheet() + self.populate_values(spreadsheet_id) + result = sheets_batch_get_values.batch_get_values( + spreadsheet_id, ["A1:A3", "B1:C1"] + ) + self.assertIsNotNone(result) + valueranges = result.get("valueRanges") + self.assertIsNotNone(valueranges) + self.assertEqual(2, len(valueranges)) + values = valueranges[0].get("values") + self.assertEqual(3, len(values)) + + +if __name__ == "__main__": + unittest.main() diff --git a/sheets/snippets/test_sheets_batch_update.py b/sheets/snippets/test_sheets_batch_update.py new file mode 100644 index 00000000..e371e5da --- /dev/null +++ b/sheets/snippets/test_sheets_batch_update.py @@ -0,0 +1,41 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import sheets_batch_update +import sheets_create +from base_test import BaseTest + + +class Testbatchupdate(BaseTest): + """Unit test class for Batch update Sheet snippet""" + + def test_batch_update(self): + """test_batch_update function""" + spreadsheet_id = sheets_create.create("Title") + self.populate_values(spreadsheet_id) + response = sheets_batch_update.sheets_batch_update( + spreadsheet_id, "New Title", "Hello", "Goodbye" + ) + self.assertIsNotNone(response) + replies = response.get("replies") + self.assertIsNotNone(replies) + self.assertEqual(2, len(replies)) + find_replace_response = replies[1].get("findReplace") + self.assertIsNotNone(find_replace_response) + self.assertEqual(100, find_replace_response.get("occurrencesChanged")) + + +if __name__ == "__main__": + unittest.main() diff --git a/sheets/snippets/test_sheets_batch_update_values.py b/sheets/snippets/test_sheets_batch_update_values.py new file mode 100644 index 00000000..c594a0a8 --- /dev/null +++ b/sheets/snippets/test_sheets_batch_update_values.py @@ -0,0 +1,37 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import sheets_batch_update_values +from base_test import BaseTest + + +class Testbatchupdatevalues(BaseTest): + """Unit test for Batch update value Sheet snippet""" + + def test_batch_update_values(self): + """batch updates values""" + spreadsheet_id = self.create_test_spreadsheet() + result = sheets_batch_update_values.batch_update_values( + spreadsheet_id, "A1:B2", "USER_ENTERED", [["A", "B"], ["C", "D"]] + ) + self.assertIsNotNone(result) + self.assertEqual(1, len(result.get("responses"))) + self.assertEqual(2, result.get("totalUpdatedRows")) + self.assertEqual(2, result.get("totalUpdatedColumns")) + self.assertEqual(4, result.get("totalUpdatedCells")) + + +if __name__ == "__main__": + unittest.main() diff --git a/sheets/snippets/test_sheets_conditional_formatting.py b/sheets/snippets/test_sheets_conditional_formatting.py new file mode 100644 index 00000000..2bf78657 --- /dev/null +++ b/sheets/snippets/test_sheets_conditional_formatting.py @@ -0,0 +1,34 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import sheets_conditional_formatting +from base_test import BaseTest + + +class Testconditionalformatting(BaseTest): + """Unit test for sheets conditional_formatting value Sheet snippet""" + + def test_conditional_formatting(self): + """sheets_conditional_formatting function""" + spreadsheet_id = self.create_test_spreadsheet() + self.populate_values(spreadsheet_id) + response = sheets_conditional_formatting.conditional_formatting( + spreadsheet_id + ) + self.assertEqual(2, len(response.get("replies"))) + + +if __name__ == "__main__": + unittest.main() diff --git a/sheets/snippets/test_sheets_create.py b/sheets/snippets/test_sheets_create.py new file mode 100644 index 00000000..3ac529e5 --- /dev/null +++ b/sheets/snippets/test_sheets_create.py @@ -0,0 +1,31 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import sheets_create +from base_test import BaseTest + + +class Testsheetscreate(BaseTest): + """Unit test class for Create Sheet snippet""" + + def test_create(self): + """sheet function for Create sheet""" + spreadsheet_id = sheets_create.create("Title") + self.assertIsNotNone(spreadsheet_id) + self.delete_file_on_cleanup(spreadsheet_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/sheets/snippets/test_sheets_filter_views.py b/sheets/snippets/test_sheets_filter_views.py new file mode 100644 index 00000000..6ccc1d71 --- /dev/null +++ b/sheets/snippets/test_sheets_filter_views.py @@ -0,0 +1,31 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import sheets_filter_views +from base_test import BaseTest + + +class Testfilterviews(BaseTest): + """Unit test for sheets conditional_formatting value Sheet snippet""" + + def test_filter_views(self): + """test filter view function""" + spreadsheet_id = self.create_test_spreadsheet() + self.populate_values(spreadsheet_id) + sheets_filter_views.filter_views(spreadsheet_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/sheets/snippets/test_sheets_get_values.py b/sheets/snippets/test_sheets_get_values.py new file mode 100644 index 00000000..912914e6 --- /dev/null +++ b/sheets/snippets/test_sheets_get_values.py @@ -0,0 +1,36 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import sheets_get_values +from base_test import BaseTest + + +class Testgetvalues(BaseTest): + """Unit test class for get value Sheet snippet""" + + def test_get_values(self): + """test_get_values""" + spreadsheet_id = self.create_test_spreadsheet() + self.populate_values(spreadsheet_id) + result = sheets_get_values.get_values(spreadsheet_id, "A1:C2") + self.assertIsNotNone(result) + values = result.get("values") + self.assertIsNotNone(values) + self.assertEqual(2, len(values)) + self.assertEqual(3, len(values[0])) + + +if __name__ == "__main__": + unittest.main() diff --git a/sheets/snippets/test_sheets_pivot_tables.py b/sheets/snippets/test_sheets_pivot_tables.py new file mode 100644 index 00000000..42acc343 --- /dev/null +++ b/sheets/snippets/test_sheets_pivot_tables.py @@ -0,0 +1,32 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import sheets_pivot_tables +from base_test import BaseTest + + +class Testpivottables(BaseTest): + """Unit test for Pivot tables value Sheet snippet""" + + def test_pivot_tables(self): + """pivot table function""" + spreadsheet_id = self.create_test_spreadsheet() + self.populate_values(spreadsheet_id) + response = sheets_pivot_tables.pivot_tables(spreadsheet_id) + self.assertIsNotNone(response) + + +if __name__ == "__main__": + unittest.main() diff --git a/sheets/snippets/test_sheets_update_values.py b/sheets/snippets/test_sheets_update_values.py new file mode 100644 index 00000000..2367ae3a --- /dev/null +++ b/sheets/snippets/test_sheets_update_values.py @@ -0,0 +1,36 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import sheets_update_values +from base_test import BaseTest + + +class Testupdatesvalues(BaseTest): + """Unit test for update value Sheet snippet""" + + def test_update_values(self): + """test updates_values""" + spreadsheet_id = self.create_test_spreadsheet() + result = sheets_update_values.update_values( + spreadsheet_id, "A1:B2", "USER_ENTERED", [["A", "B"], ["C", "D"]] + ) + self.assertIsNotNone(result) + self.assertEqual(2, result.get("updatedRows")) + self.assertEqual(2, result.get("updatedColumns")) + self.assertEqual(4, result.get("updatedCells")) + + +if __name__ == "__main__": + unittest.main() diff --git a/sheets/snippets/test_spreadsheet_snippets.py b/sheets/snippets/test_spreadsheet_snippets.py deleted file mode 100644 index fe8da01a..00000000 --- a/sheets/snippets/test_spreadsheet_snippets.py +++ /dev/null @@ -1,127 +0,0 @@ -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest - -from base_test import BaseTest -from spreadsheet_snippets import SpreadsheetSnippets - - -class SpreadsheetSnippetsTest(BaseTest): - @classmethod - def setUpClass(cls): - super(SpreadsheetSnippetsTest, cls).setUpClass() - cls.snippets = SpreadsheetSnippets(cls.service) - - def test_create(self): - spreadsheet_id = self.snippets.create('Title') - self.assertIsNotNone(spreadsheet_id) - self.delete_file_on_cleanup(spreadsheet_id) - - def test_batch_update(self): - spreadsheet_id = self.create_test_spreadsheet() - self.populate_values(spreadsheet_id) - response = self.snippets.batch_update(spreadsheet_id, - 'New Title', 'Hello', 'Goodbye') - self.assertIsNotNone(response) - replies = response.get('replies') - self.assertIsNotNone(replies) - self.assertEqual(2, len(replies)) - find_replace_response = replies[1].get('findReplace') - self.assertIsNotNone(find_replace_response) - self.assertEqual(100, find_replace_response.get('occurrencesChanged')) - - def test_get_values(self): - spreadsheet_id = self.create_test_spreadsheet() - self.populate_values(spreadsheet_id) - result = self.snippets.get_values(spreadsheet_id, 'A1:C2') - self.assertIsNotNone(result) - values = result.get('values') - self.assertIsNotNone(values) - self.assertEqual(2, len(values)) - self.assertEqual(3, len(values[0])) - - def test_batch_get_values(self): - spreadsheet_id = self.create_test_spreadsheet() - self.populate_values(spreadsheet_id) - result = self.snippets.batch_get_values(spreadsheet_id, - ['A1:A3', 'B1:C1']) - self.assertIsNotNone(result) - valueRanges = result.get('valueRanges') - self.assertIsNotNone(valueRanges) - self.assertEqual(2, len(valueRanges)) - values = valueRanges[0].get('values') - self.assertEqual(3, len(values)) - - def test_update_values(self): - spreadsheet_id = self.create_test_spreadsheet() - result = self.snippets.update_values(spreadsheet_id, - 'A1:B2', 'USER_ENTERED', [ - ['A', 'B'], - ['C', 'D'] - ]) - self.assertIsNotNone(result) - self.assertEqual(2, result.get('updatedRows')) - self.assertEqual(2, result.get('updatedColumns')) - self.assertEqual(4, result.get('updatedCells')) - - def test_batch_update_values(self): - spreadsheet_id = self.create_test_spreadsheet() - result = self.snippets.batch_update_values(spreadsheet_id, - 'A1:B2', 'USER_ENTERED', [ - ['A', 'B'], - ['C', 'D'] - ]) - self.assertIsNotNone(result) - self.assertEqual(1, len(result.get('responses'))) - self.assertEqual(2, result.get('totalUpdatedRows')) - self.assertEqual(2, result.get('totalUpdatedColumns')) - self.assertEqual(4, result.get('totalUpdatedCells')) - - def test_append_values(self): - spreadsheet_id = self.create_test_spreadsheet() - self.populate_values(spreadsheet_id) - result = self.snippets.append_values(spreadsheet_id, - 'Sheet1', 'USER_ENTERED', [ - ['A', 'B'], - ['C', 'D'] - ]) - self.assertIsNotNone(result) - self.assertEqual('Sheet1!A1:J10', result.get('tableRange')) - updates = result.get('updates') - self.assertEqual('Sheet1!A11:B12', updates.get('updatedRange')) - self.assertEqual(2, updates.get('updatedRows')) - self.assertEqual(2, updates.get('updatedColumns')) - self.assertEqual(4, updates.get('updatedCells')) - - def test_pivot_tables(self): - spreadsheet_id = self.create_test_spreadsheet() - self.populate_values(spreadsheet_id) - response = self.snippets.pivot_tables(spreadsheet_id) - self.assertIsNotNone(response) - - def test_conditional_formatting(self): - spreadsheet_id = self.create_test_spreadsheet() - self.populate_values(spreadsheet_id) - response = self.snippets.conditional_formatting(spreadsheet_id) - self.assertEqual(2, len(response.get('replies'))) - - def test_filter_views(self): - spreadsheet_id = self.create_test_spreadsheet() - self.populate_values(spreadsheet_id) - self.snippets.filter_views(spreadsheet_id) - - -if __name__ == '__main__': - unittest.main() diff --git a/slides/quickstart/quickstart.py b/slides/quickstart/quickstart.py index 6a0ff8e7..f7c17aa3 100644 --- a/slides/quickstart/quickstart.py +++ b/slides/quickstart/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START slides_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -24,50 +22,54 @@ from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/presentations.readonly'] +SCOPES = ["https://www.googleapis.com/auth/presentations.readonly"] # The ID of a sample presentation. -PRESENTATION_ID = '1EAYk18WDjIG-zp_0vLm3CsfQh_i8eXc67Jo2O9C6Vuc' +PRESENTATION_ID = "1EAYk18WDjIG-zp_0vLm3CsfQh_i8eXc67Jo2O9C6Vuc" def main(): - """Shows basic usage of the Slides API. - Prints the number of slides and elments in a sample presentation. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Shows basic usage of the Slides API. + Prints the number of slides and elements in a sample presentation. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - try: - service = build('slides', 'v1', credentials=creds) + try: + service = build("slides", "v1", credentials=creds) - # Call the Slides API - presentation = service.presentations().get( - presentationId=PRESENTATION_ID).execute() - slides = presentation.get('slides') + # Call the Slides API + presentation = ( + service.presentations().get(presentationId=PRESENTATION_ID).execute() + ) + slides = presentation.get("slides") - print('The presentation contains {} slides:'.format(len(slides))) - for i, slide in enumerate(slides): - print('- Slide #{} contains {} elements.'.format( - i + 1, len(slide.get('pageElements')))) - except HttpError as err: - print(err) + print(f"The presentation contains {len(slides)} slides:") + for i, slide in enumerate(slides): + print( + f"- Slide #{i + 1} contains" + f" {len(slide.get('pageElements'))} elements." + ) + except HttpError as err: + print(err) -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END slides_quickstart] diff --git a/slides/snippets/base_test.py b/slides/snippets/base_test.py index 7bcd1ac7..60952df2 100644 --- a/slides/snippets/base_test.py +++ b/slides/snippets/base_test.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import print_function - import sys import unittest @@ -24,155 +22,150 @@ class BaseTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.credentials = cls.create_credentials() - http = cls.credentials.authorize(httplib2.Http()) - cls.credentials.refresh(http) - cls.service = build('slides', 'v1', http=http) - cls.drive_service = build('drive', 'v3', http=http) - cls.sheets_service = build('sheets', 'v4', http=http) - # Hide STDOUT output generated by snippets. - cls.stdout = sys.stdout - sys.stdout = None - - @classmethod - def tearDownClass(cls): - # Restore STDOUT. - sys.stdout = cls.stdout - - @classmethod - def create_credentials(cls): - credentials = GoogleCredentials.get_application_default() - scope = [ - 'https://www.googleapis.com/auth/drive', - ] - return credentials.create_scoped(scope) - - def setUp(self): - self.files_to_delete = [] - - def tearDown(self): - for file_id in self.files_to_delete: - try: - self.drive_service.files().delete(fileId=file_id).execute() - except errors.HttpError: - print('Unable to delete file %s' % file_id, file=sys.stderr) - - def delete_file_on_cleanup(self, file_id): - self.files_to_delete.append(file_id) - - def create_test_presentation(self): - presentation = { - 'title': 'Test Preso' - } - presentation = self.service.presentations().create( - body=presentation).execute() - self.delete_file_on_cleanup(presentation.get('presentationId')) - return presentation.get('presentationId') - - def add_slides(self, presentation_id, num, layout='TITLE_AND_TWO_COLUMNS'): - requests = [] - slide_ids = [] - for i in range(num): - slide_id = 'slide_{0}'.format(i) - slide_ids.append(slide_id) - requests.append({ - 'createSlide': { - 'objectId': slide_ids[i], - 'slideLayoutReference': { - 'predefinedLayout': layout - } - } - }) - body = { - 'requests': requests - } - self.service.presentations().batchUpdate( - presentationId=presentation_id, body=body).execute() - return slide_ids - - def create_test_textbox(self, presentation_id, page_id): - box_id = 'MyTextBox_01' - pt350 = { - 'magnitude': 350, - 'unit': 'PT' - } - requests = [] - requests.append({ - 'createShape': { - 'objectId': box_id, - 'shapeType': 'TEXT_BOX', - 'elementProperties': { - 'pageObjectId': page_id, - 'size': { - 'height': pt350, - 'width': pt350 + + @classmethod + def setUpClass(cls): + cls.credentials = cls.create_credentials() + http = cls.credentials.authorize(httplib2.Http()) + cls.credentials.refresh(http) + cls.service = build("slides", "v1", http=http) + cls.drive_service = build("drive", "v3", http=http) + cls.sheets_service = build("sheets", "v4", http=http) + # Hide STDOUT output generated by snippets. + cls.stdout = sys.stdout + sys.stdout = None + + @classmethod + def tearDownClass(cls): + # Restore STDOUT. + sys.stdout = cls.stdout + + @classmethod + def create_credentials(cls): + credentials = GoogleCredentials.get_application_default() + scope = [ + "https://www.googleapis.com/auth/drive", + ] + return credentials.create_scoped(scope) + + def setUp(self): + self.files_to_delete = [] + + def tearDown(self): + for file_id in self.files_to_delete: + try: + self.drive_service.files().delete(fileId=file_id).execute() + except errors.HttpError: + print(f"Unable to delete file {file_id}", file=sys.stderr) + + def delete_file_on_cleanup(self, file_id): + self.files_to_delete.append(file_id) + + def create_test_presentation(self): + presentation = {"title": "Test Preso"} + presentation = ( + self.service.presentations().create(body=presentation).execute() + ) + self.delete_file_on_cleanup(presentation.get("presentationId")) + return presentation.get("presentationId") + + def add_slides(self, presentation_id, num, layout="TITLE_AND_TWO_COLUMNS"): + requests = [] + slide_ids = [] + for i in range(num): + slide_id = f"slide_{i}" + slide_ids.append(slide_id) + requests.append( + { + "createSlide": { + "objectId": slide_ids[i], + "slideLayoutReference": {"predefinedLayout": layout}, + } + } + ) + body = {"requests": requests} + self.service.presentations().batchUpdate( + presentationId=presentation_id, body=body + ).execute() + return slide_ids + + def create_test_textbox(self, presentation_id, page_id): + box_id = "MyTextBox_01" + pt350 = {"magnitude": 350, "unit": "PT"} + requests = [] + requests.append( + { + "createShape": { + "objectId": box_id, + "shapeType": "TEXT_BOX", + "elementProperties": { + "pageObjectId": page_id, + "size": {"height": pt350, "width": pt350}, + "transform": { + "scaleX": 1, + "scaleY": 1, + "translateX": 350, + "translateY": 100, + "unit": "PT", }, - 'transform': { - 'scaleX': 1, - 'scaleY': 1, - 'translateX': 350, - 'translateY': 100, - 'unit': 'PT' - } - } + }, } - }) - requests.append({ - 'insertText': { - 'objectId': box_id, - 'insertionIndex': 0, - 'text': 'New Box Text Inserted' - } - }) - - body = { - 'requests': requests } - response = self.service.presentations().batchUpdate( - presentationId=presentation_id, body=body).execute() - return response.get('replies')[0].get('createShape').get('objectId') - - def create_test_sheets_chart( - self, presentation_id, page_id, spreadsheet_id, sheet_chart_id): - chart_id = 'MyChart_01' - emu4M = { - 'magnitude': 4000000, - 'unit': 'EMU' + ) + requests.append( + { + "insertText": { + "objectId": box_id, + "insertionIndex": 0, + "text": "New Box Text Inserted", + } } - requests = [] - requests.append({ - 'createSheetsChart': { - 'objectId': chart_id, - 'spreadsheetId': spreadsheet_id, - 'chartId': sheet_chart_id, - 'linkingMode': 'LINKED', - 'elementProperties': { - 'pageObjectId': page_id, - 'size': { - 'height': emu4M, - 'width': emu4M + ) + + body = {"requests": requests} + response = ( + self.service.presentations() + .batchUpdate(presentationId=presentation_id, body=body) + .execute() + ) + return response.get("replies")[0].get("createShape").get("objectId") + + def create_test_sheets_chart( + self, presentation_id, page_id, spreadsheet_id, sheet_chart_id + ): + chart_id = "MyChart_01" + emu4M = {"magnitude": 4000000, "unit": "EMU"} + requests = [] + requests.append( + { + "createSheetsChart": { + "objectId": chart_id, + "spreadsheetId": spreadsheet_id, + "chartId": sheet_chart_id, + "linkingMode": "LINKED", + "elementProperties": { + "pageObjectId": page_id, + "size": {"height": emu4M, "width": emu4M}, + "transform": { + "scaleX": 1, + "scaleY": 1, + "translateX": 100000, + "translateY": 100000, + "unit": "EMU", }, - 'transform': { - 'scaleX': 1, - 'scaleY': 1, - 'translateX': 100000, - 'translateY': 100000, - 'unit': 'EMU' - } - } + }, } - }) - - body = { - 'requests': requests } - response = self.service.presentations().batchUpdate( - presentationId=presentation_id, body=body).execute() - return response.get('replies')[0] \ - .get('createSheetsChart').get('objectId') + ) + + body = {"requests": requests} + response = ( + self.service.presentations() + .batchUpdate(presentationId=presentation_id, body=body) + .execute() + ) + return response.get("replies")[0].get("createSheetsChart").get("objectId") -if __name__ == '__main__': - unittest.main() +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/slides_copy_presentation.py b/slides/snippets/slides_copy_presentation.py new file mode 100644 index 00000000..fc4c0343 --- /dev/null +++ b/slides/snippets/slides_copy_presentation.py @@ -0,0 +1,53 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START slides_copy_presentation] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def copy_presentation(presentation_id, copy_title): + """ + Creates the copy Presentation the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + drive_service = build("drive", "v3", credentials=creds) + body = {"name": copy_title} + drive_response = ( + drive_service.files().copy(fileId=presentation_id, body=body).execute() + ) + presentation_copy_id = drive_response.get("id") + + except HttpError as error: + print(f"An error occurred: {error}") + print("Presentations not copied") + return error + + return presentation_copy_id + # [END slides_copy_presentation] + + +if __name__ == "__main__": + # Put the presentation_id, Page_id of slides whose list needs + # to be submitted. + copy_presentation("16eRvJHRrM8Sej5YA0yCHVzQCPLz31-JhbOa4XpP8Yko", "wspace") diff --git a/slides/snippets/slides_create_bulleted_text.py b/slides/snippets/slides_create_bulleted_text.py new file mode 100644 index 00000000..e7051222 --- /dev/null +++ b/slides/snippets/slides_create_bulleted_text.py @@ -0,0 +1,67 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START slides_create_bulleted_text] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_bulleted_text(presentation_id, shape_id): + """ + Run create_bulleted_text the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + slides_service = build("slides", "v1", credentials=creds) + # Add arrow-diamond-disc bullets to all text in the shape. + requests = [ + { + "createParagraphBullets": { + "objectId": shape_id, + "textRange": {"type": "ALL"}, + "bulletPreset": "BULLET_ARROW_DIAMOND_DISC", + } + } + ] + + # Execute the requests. + body = {"requests": requests} + response = ( + slides_service.presentations() + .batchUpdate(presentationId=presentation_id, body=body) + .execute() + ) + print(f"Added bullets to text in shape with ID: {shape_id}") + + return response + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Put the presentation_id and shape_id + # to be submitted. + create_bulleted_text( + "1VD1xmi1-9DonI4zmCKENTzlVxIL5SdGGTmbHmnBjQ1E", "MyTextBox_9" + ) + +# [END slides_create_bulleted_text] diff --git a/slides/snippets/slides_create_image.py b/slides/snippets/slides_create_image.py new file mode 100644 index 00000000..e0152156 --- /dev/null +++ b/slides/snippets/slides_create_image.py @@ -0,0 +1,85 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START slides_create_image] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_image(presentation_id, page_id): + """ + Creates images the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("slides", "v1", credentials=creds) + # pylint: disable = invalid-name + IMAGE_URL = ( + "https://www.google.com/images/branding/" + "googlelogo/2x/googlelogo_color_272x92dp.png" + ) + # pylint: disable=invalid-name + requests = [] + image_id = "MyImage_11" + emu4M = {"magnitude": 4000000, "unit": "EMU"} + requests.append( + { + "createImage": { + "objectId": image_id, + "url": IMAGE_URL, + "elementProperties": { + "pageObjectId": page_id, + "size": {"height": emu4M, "width": emu4M}, + "transform": { + "scaleX": 1, + "scaleY": 1, + "translateX": 100000, + "translateY": 100000, + "unit": "EMU", + }, + }, + } + } + ) + + # Execute the request. + body = {"requests": requests} + response = ( + service.presentations() + .batchUpdate(presentationId=presentation_id, body=body) + .execute() + ) + create_image_response = response.get("replies")[0].get("createImage") + print(f"Created image with ID: {(create_image_response.get('objectId'))}") + + return response + except HttpError as error: + print(f"An error occurred: {error}") + print("Images not created") + return error + + +if __name__ == "__main__": + # Put the presentation_id, Page_id of slides whose list needs + # to be submitted. + create_image("12SQU9Ik-ShXecJoMtT-LlNwEPiFR7AadnxV2KiBXCnE", "My2ndpage") + # [END slides_create_image] diff --git a/slides/snippets/slides_create_presentation.py b/slides/snippets/slides_create_presentation.py new file mode 100644 index 00000000..2f683e6c --- /dev/null +++ b/slides/snippets/slides_create_presentation.py @@ -0,0 +1,53 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START slides_create_presentation] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_presentation(title): + """ + Creates the Presentation the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("slides", "v1", credentials=creds) + + body = {"title": title} + presentation = service.presentations().create(body=body).execute() + print( + f"Created presentation with ID:{(presentation.get('presentationId'))}" + ) + return presentation + + except HttpError as error: + print(f"An error occurred: {error}") + print("presentation not created") + return error + + +if __name__ == "__main__": + # Put the title of the presentation + + create_presentation("finalp") + +# [END slides_create_presentation] diff --git a/slides/snippets/slides_create_sheets_chart.py b/slides/snippets/slides_create_sheets_chart.py new file mode 100644 index 00000000..9efa3d64 --- /dev/null +++ b/slides/snippets/slides_create_sheets_chart.py @@ -0,0 +1,90 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START slides_create_sheets_chart] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_sheets_chart( + presentation_id, page_id, spreadsheet_id, sheet_chart_id +): + """ + create_sheets_chart the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + slides_service = build("slides", "v1", credentials=creds) + # Embed a Sheets chart (indicated by the spreadsheet_id and + # sheet_chart_id) onto a page in the presentation. + # Setting the linking mode as "LINKED" allows the + # chart to be refreshed if the Sheets version is updated. + + emu4m = {"magnitude": 4000000, "unit": "EMU"} + + presentation_chart_id = "MyEmbeddedChart" + requests = [ + { + "createSheetsChart": { + "objectId": presentation_chart_id, + "spreadsheetId": spreadsheet_id, + "chartId": sheet_chart_id, + "linkingMode": "LINKED", + "elementProperties": { + "pageObjectId": page_id, + "size": {"height": emu4m, "width": emu4m}, + "transform": { + "scaleX": 1, + "scaleY": 1, + "translateX": 100000, + "translateY": 100000, + "unit": "EMU", + }, + }, + } + } + ] + + # Execute the request. + body = {"requests": requests} + response = ( + slides_service.presentations() + .batchUpdate(presentationId=presentation_id, body=body) + .execute() + ) + print(f"Added a linked Sheets chart with ID: {presentation_chart_id}") + return response + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Put the presentation_id, Page_id of slides + # spreadsheet_id and sheet_chart_id to be submitted. + create_sheets_chart( + "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4", + "FIRSTSLIDE", + "17eqFZl_WK4WVixX8PjvjfLD77DraoFwMDXeiHB3dvuM", + "1107320627", + ) + +# [END slides_create_sheets_chart] diff --git a/slides/snippets/slides_create_slide.py b/slides/snippets/slides_create_slide.py new file mode 100644 index 00000000..fafbfa78 --- /dev/null +++ b/slides/snippets/slides_create_slide.py @@ -0,0 +1,72 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START slides_create_slide] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_slide(presentation_id, page_id): + """ + Creates the Presentation the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application.\n + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("slides", "v1", credentials=creds) + # Add a slide at index 1 using the predefined + # 'TITLE_AND_TWO_COLUMNS' layout and the ID page_id. + requests = [ + { + "createSlide": { + "objectId": page_id, + "insertionIndex": "1", + "slideLayoutReference": { + "predefinedLayout": "TITLE_AND_TWO_COLUMNS" + }, + } + } + ] + + # If you wish to populate the slide with elements, + # add element create requests here, using the page_id. + + # Execute the request. + body = {"requests": requests} + response = ( + service.presentations() + .batchUpdate(presentationId=presentation_id, body=body) + .execute() + ) + create_slide_response = response.get("replies")[0].get("createSlide") + print(f"Created slide with ID:{(create_slide_response.get('objectId'))}") + except HttpError as error: + print(f"An error occurred: {error}") + print("Slides not created") + return error + + return response + + +if __name__ == "__main__": + # Put the presentation_id, Page_id of slides whose list needs + # to be submitted. + create_slide("12SQU9Ik-ShXecJoMtT-LlNwEPiFR7AadnxV2KiBXCnE", "My4ndpage") + # [END slides_create_slide] diff --git a/slides/snippets/slides_create_textbox_with_text.py b/slides/snippets/slides_create_textbox_with_text.py new file mode 100644 index 00000000..d3879d19 --- /dev/null +++ b/slides/snippets/slides_create_textbox_with_text.py @@ -0,0 +1,89 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START slides_create_textbox_with_text] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def create_textbox_with_text(presentation_id, page_id): + """ + Creates the textbox with text, the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("slides", "v1", credentials=creds) + # Create a new square textbox, using the supplied element ID. + element_id = "MyTextBox_10" + pt350 = {"magnitude": 350, "unit": "PT"} + requests = [ + { + "createShape": { + "objectId": element_id, + "shapeType": "TEXT_BOX", + "elementProperties": { + "pageObjectId": page_id, + "size": {"height": pt350, "width": pt350}, + "transform": { + "scaleX": 1, + "scaleY": 1, + "translateX": 350, + "translateY": 100, + "unit": "PT", + }, + }, + } + }, + # Insert text into the box, using the supplied element ID. + { + "insertText": { + "objectId": element_id, + "insertionIndex": 0, + "text": "New Box Text Inserted!", + } + }, + ] + + # Execute the request. + body = {"requests": requests} + response = ( + service.presentations() + .batchUpdate(presentationId=presentation_id, body=body) + .execute() + ) + create_shape_response = response.get("replies")[0].get("createShape") + print(f"Created textbox with ID:{(create_shape_response.get('objectId'))}") + except HttpError as error: + print(f"An error occurred: {error}") + + return error + + return response + + +if __name__ == "__main__": + # Put the presentation_id, Page_id of slides whose list needs + # to be submitted. + create_textbox_with_text( + "12SQU9Ik-ShXecJoMtT-LlNwEPiFR7AadnxV2KiBXCnE", "Myfirstpage" + ) + +# [END slides_create_textbox_with_text] diff --git a/slides/snippets/slides_image_merging.py b/slides/snippets/slides_image_merging.py new file mode 100644 index 00000000..f27161c8 --- /dev/null +++ b/slides/snippets/slides_image_merging.py @@ -0,0 +1,115 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START slides_image_merging] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def image_merging(template_presentation_id, image_url, customer_name): + """image_merging require template_presentation_id, + image_url and customer_name + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + slides_service = build("slides", "v1", credentials=creds) + drive_service = build("drive", "v3", credentials=creds) + logo_url = image_url + + customer_graphic_url = image_url + + # Duplicate the template presentation using the Drive API. + copy_title = customer_name + " presentation" + drive_response = ( + drive_service.files() + .copy(fileId=template_presentation_id, body={"name": copy_title}) + .execute() + ) + presentation_copy_id = drive_response.get("id") + + # Create the image merge (replaceAllShapesWithImage) requests. + requests = [] + requests.append( + { + "replaceAllShapesWithImage": { + "imageUrl": logo_url, + "replaceMethod": "CENTER_INSIDE", + "containsText": { + "text": "{{company-logo}}", + "matchCase": True, + }, + } + } + ) + requests.append( + { + "replaceAllShapesWithImage": { + "imageUrl": customer_graphic_url, + "replaceMethod": "CENTER_INSIDE", + "containsText": { + "text": "{{customer-graphic}}", + "matchCase": True, + }, + } + } + ) + + # Execute the requests. + body = {"requests": requests} + response = ( + slides_service.presentations() + .batchUpdate(presentationId=presentation_copy_id, body=body) + .execute() + ) + + # Count the number of replacements made. + num_replacements = 0 + + for reply in response.get("replies"): + # add below line + + if reply.get("occurrencesChanged") is not None: + # end tag + num_replacements += reply.get("replaceAllShapesWithImage").get( + "occurrencesChanged" + ) + + print(f"Created merged presentation with ID:{presentation_copy_id}") + print(f"Replaced {num_replacements} shapes with images") + except HttpError as error: + print(f"An error occurred: {error}") + print("Images is not merged") + return error + + return response + + +if __name__ == "__main__": + # Put the template_presentation_id, image_url and customer_name + + image_merging( + "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4", + "https://www.google.com/images/branding/" + "googlelogo/2x/googlelogo_color_272x92dp.png", + "Fake Customer", + ) + + # [END slides_image_merging] diff --git a/slides/snippets/slides_refresh_sheets_chart.py b/slides/snippets/slides_refresh_sheets_chart.py new file mode 100644 index 00000000..a0179a4f --- /dev/null +++ b/slides/snippets/slides_refresh_sheets_chart.py @@ -0,0 +1,57 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START slides_refresh_sheets_chart] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def refresh_sheets_chart(presentation_id, presentation_chart_id): + """ + refresh_sheets_chart the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + slides_service = build("slides", "v1", credentials=creds) + # Refresh an existing linked Sheets chart embedded in a presentation. + requests = [{"refreshSheetsChart": {"objectId": presentation_chart_id}}] + + # Execute the request. + body = {"requests": requests} + response = ( + slides_service.presentations() + .batchUpdate(presentationId=presentation_id, body=body) + .execute() + ) + print(f"Refreshed a linked Sheets chart with ID:{presentation_chart_id}") + return response + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Put the presentation_id, presentation_chart_id + # to be submitted. + refresh_sheets_chart( + "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4", "1107320627" + ) + # [END slides_refresh_sheets_chart] diff --git a/slides/snippets/slides_simple_text_replace.py b/slides/snippets/slides_simple_text_replace.py new file mode 100644 index 00000000..bd4adaaa --- /dev/null +++ b/slides/snippets/slides_simple_text_replace.py @@ -0,0 +1,72 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START slides_simple_text_replace] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def simple_text_replace(presentation_id, shape_id, replacement_text): + """ + Run simple_text_replace the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + slides_service = build("slides", "v1", credentials=creds) + # Remove existing text in the shape, then insert new text. + requests = [] + requests.append( + {"deleteText": {"objectId": shape_id, "textRange": {"type": "ALL"}}} + ) + requests.append( + { + "insertText": { + "objectId": shape_id, + "insertionIndex": 0, + "text": replacement_text, + } + } + ) + + # Execute the requests. + body = {"requests": requests} + response = ( + slides_service.presentations() + .batchUpdate(presentationId=presentation_id, body=body) + .execute() + ) + print(f"Replaced text in shape with ID: {shape_id}") + return response + except HttpError as error: + print(f"An error occurred: {error}") + print("Text is not merged") + return error + + +if __name__ == "__main__": + # Put the presentation_id, shape_id and replacement_text + simple_text_replace( + "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4", + "MyTextBox_6", + "GWSpace_now", + ) + +# [END slides_simple_text_replace] diff --git a/slides/snippets/slides_snippets.py b/slides/snippets/slides_snippets.py deleted file mode 100644 index 1d57a56c..00000000 --- a/slides/snippets/slides_snippets.py +++ /dev/null @@ -1,523 +0,0 @@ -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import print_function - - -class SlidesSnippets(object): - def __init__(self, service, drive_service, sheets_service, credentials): - self.service = service - self.drive_service = drive_service - self.sheets_service = sheets_service - self.credentials = credentials - - def create_presentation(self, title): - slides_service = self.service - # [START slides_create_presentation] - body = { - 'title': title - } - presentation = slides_service.presentations() \ - .create(body=body).execute() - print('Created presentation with ID: {0}'.format( - presentation.get('presentationId'))) - # [END slides_create_presentation] - return presentation - - def copy_presentation(self, presentation_id, copy_title): - drive_service = self.drive_service - # [START slides_copy_presentation] - body = { - 'name': copy_title - } - drive_response = drive_service.files().copy( - fileId=presentation_id, body=body).execute() - presentation_copy_id = drive_response.get('id') - # [END slides_copy_presentation] - return presentation_copy_id - - def create_slide(self, presentation_id, page_id): - slides_service = self.service - # [START slides_create_slide] - # Add a slide at index 1 using the predefined - # 'TITLE_AND_TWO_COLUMNS' layout and the ID page_id. - requests = [ - { - 'createSlide': { - 'objectId': page_id, - 'insertionIndex': '1', - 'slideLayoutReference': { - 'predefinedLayout': 'TITLE_AND_TWO_COLUMNS' - } - } - } - ] - - # If you wish to populate the slide with elements, - # add element create requests here, using the page_id. - - # Execute the request. - body = { - 'requests': requests - } - response = slides_service.presentations() \ - .batchUpdate(presentationId=presentation_id, body=body).execute() - create_slide_response = response.get('replies')[0].get('createSlide') - print('Created slide with ID: {0}'.format( - create_slide_response.get('objectId'))) - # [END slides_create_slide] - return response - - def create_textbox_with_text(self, presentation_id, page_id): - slides_service = self.service - # [START slides_create_textbox_with_text] - # Create a new square textbox, using the supplied element ID. - element_id = 'MyTextBox_01' - pt350 = { - 'magnitude': 350, - 'unit': 'PT' - } - requests = [ - { - 'createShape': { - 'objectId': element_id, - 'shapeType': 'TEXT_BOX', - 'elementProperties': { - 'pageObjectId': page_id, - 'size': { - 'height': pt350, - 'width': pt350 - }, - 'transform': { - 'scaleX': 1, - 'scaleY': 1, - 'translateX': 350, - 'translateY': 100, - 'unit': 'PT' - } - } - } - }, - - # Insert text into the box, using the supplied element ID. - { - 'insertText': { - 'objectId': element_id, - 'insertionIndex': 0, - 'text': 'New Box Text Inserted!' - } - } - ] - - # Execute the request. - body = { - 'requests': requests - } - response = slides_service.presentations() \ - .batchUpdate(presentationId=presentation_id, body=body).execute() - create_shape_response = response.get('replies')[0].get('createShape') - print('Created textbox with ID: {0}'.format( - create_shape_response.get('objectId'))) - # [END slides_create_textbox_with_text] - return response - - def create_image(self, presentation_id, page_id): - slides_service = self.service - # [START slides_create_image] - # Create a new image, using the supplied object ID, - # with content downloaded from IMAGE_URL. - IMAGE_URL = ('https://www.google.com/images/branding/' - 'googlelogo/2x/googlelogo_color_272x92dp.png') - requests = [] - image_id = 'MyImage_01' - emu4M = { - 'magnitude': 4000000, - 'unit': 'EMU' - } - requests.append({ - 'createImage': { - 'objectId': image_id, - 'url': IMAGE_URL, - 'elementProperties': { - 'pageObjectId': page_id, - 'size': { - 'height': emu4M, - 'width': emu4M - }, - 'transform': { - 'scaleX': 1, - 'scaleY': 1, - 'translateX': 100000, - 'translateY': 100000, - 'unit': 'EMU' - } - } - } - }) - - # Execute the request. - body = { - 'requests': requests - } - response = slides_service.presentations() \ - .batchUpdate(presentationId=presentation_id, body=body).execute() - create_image_response = response.get('replies')[0].get('createImage') - print('Created image with ID: {0}'.format( - create_image_response.get('objectId'))) - - # [END slides_create_image] - return response - - def text_merging(self, template_presentation_id, data_spreadsheet_id): - slides_service = self.service - sheets_service = self.sheets_service - drive_service = self.drive_service - responses = [] - # [START slides_text_merging] - # Use the Sheets API to load data, one record per row. - data_range_notation = 'Customers!A2:M6' - sheets_response = sheets_service.spreadsheets().values().get( - spreadsheetId=data_spreadsheet_id, - range=data_range_notation).execute() - values = sheets_response.get('values') - - # For each record, create a new merged presentation. - for row in values: - customer_name = row[2] # name in column 3 - case_description = row[5] # case description in column 6 - total_portfolio = row[11] # total portfolio in column 12 - - # Duplicate the template presentation using the Drive API. - copy_title = customer_name + ' presentation' - body = { - 'name': copy_title - } - drive_response = drive_service.files().copy( - fileId=template_presentation_id, body=body).execute() - presentation_copy_id = drive_response.get('id') - - # Create the text merge (replaceAllText) requests - # for this presentation. - requests = [ - { - 'replaceAllText': { - 'containsText': { - 'text': '{{customer-name}}', - 'matchCase': True - }, - 'replaceText': customer_name - } - }, - { - 'replaceAllText': { - 'containsText': { - 'text': '{{case-description}}', - 'matchCase': True - }, - 'replaceText': case_description - } - }, - { - 'replaceAllText': { - 'containsText': { - 'text': '{{total-portfolio}}', - 'matchCase': True - }, - 'replaceText': total_portfolio - } - } - ] - - # Execute the requests for this presentation. - body = { - 'requests': requests - } - response = slides_service.presentations().batchUpdate( - presentationId=presentation_copy_id, body=body).execute() - # [START_EXCLUDE silent] - responses.append(response) - # [END_EXCLUDE] - # Count the total number of replacements made. - num_replacements = 0 - for reply in response.get('replies'): - num_replacements += reply.get('replaceAllText') \ - .get('occurrencesChanged') - print('Created presentation for %s with ID: %s' % - (customer_name, presentation_copy_id)) - print('Replaced %d text instances' % num_replacements) - - # [END slides_text_merging] - return responses - - def image_merging(self, template_presentation_id, - image_url, customer_name): - slides_service = self.service - drive_service = self.drive_service - logo_url = image_url - customer_graphic_url = image_url - - # [START slides_image_merging] - # Duplicate the template presentation using the Drive API. - copy_title = customer_name + ' presentation' - drive_response = drive_service.files().copy( - fileId=template_presentation_id, - body={'name': copy_title}).execute() - presentation_copy_id = drive_response.get('id') - - # Create the image merge (replaceAllShapesWithImage) requests. - requests = [] - requests.append({ - 'replaceAllShapesWithImage': { - 'imageUrl': logo_url, - 'replaceMethod': 'CENTER_INSIDE', - 'containsText': { - 'text': '{{company-logo}}', - 'matchCase': True - } - } - }) - requests.append({ - 'replaceAllShapesWithImage': { - 'imageUrl': customer_graphic_url, - 'replaceMethod': 'CENTER_INSIDE', - 'containsText': { - 'text': '{{customer-graphic}}', - 'matchCase': True - } - } - }) - - # Execute the requests. - body = { - 'requests': requests - } - response = slides_service.presentations().batchUpdate( - presentationId=presentation_copy_id, body=body).execute() - - # Count the number of replacements made. - num_replacements = 0 - for reply in response.get('replies'): - num_replacements += reply.get('replaceAllShapesWithImage') \ - .get('occurrencesChanged') - print('Created merged presentation with ID: {0}' - .format(presentation_copy_id)) - print('Replaced %d shapes with images.' % num_replacements) - # [END slides_image_merging] - return response - - def simple_text_replace(self, presentation_id, shape_id, replacement_text): - slides_service = self.service - # [START slides_simple_text_replace] - # Remove existing text in the shape, then insert new text. - requests = [] - requests.append({ - 'deleteText': { - 'objectId': shape_id, - 'textRange': { - 'type': 'ALL' - } - } - }) - requests.append({ - 'insertText': { - 'objectId': shape_id, - 'insertionIndex': 0, - 'text': replacement_text - } - }) - - # Execute the requests. - body = { - 'requests': requests - } - response = slides_service.presentations().batchUpdate( - presentationId=presentation_id, body=body).execute() - print('Replaced text in shape with ID: {0}'.format(shape_id)) - # [END slides_simple_text_replace] - return response - - def text_style_update(self, presentation_id, shape_id): - slides_service = self.service - # [START slides_text_style_update] - # Update the text style so that the first 5 characters are bolded - # and italicized, the next 5 are displayed in blue 14 pt Times - # New Roman font, and the next 5 are hyperlinked. - requests = [ - { - 'updateTextStyle': { - 'objectId': shape_id, - 'textRange': { - 'type': 'FIXED_RANGE', - 'startIndex': 0, - 'endIndex': 5 - }, - 'style': { - 'bold': True, - 'italic': True - }, - 'fields': 'bold,italic' - } - }, - { - 'updateTextStyle': { - 'objectId': shape_id, - 'textRange': { - 'type': 'FIXED_RANGE', - 'startIndex': 5, - 'endIndex': 10 - }, - 'style': { - 'fontFamily': 'Times New Roman', - 'fontSize': { - 'magnitude': 14, - 'unit': 'PT' - }, - 'foregroundColor': { - 'opaqueColor': { - 'rgbColor': { - 'blue': 1.0, - 'green': 0.0, - 'red': 0.0 - } - } - } - }, - 'fields': 'foregroundColor,fontFamily,fontSize' - } - }, - { - 'updateTextStyle': { - 'objectId': shape_id, - 'textRange': { - 'type': 'FIXED_RANGE', - 'startIndex': 10, - 'endIndex': 15 - }, - 'style': { - 'link': { - 'url': 'www.example.com' - } - }, - 'fields': 'link' - } - } - ] - - # Execute the requests. - body = { - 'requests': requests - } - response = slides_service.presentations().batchUpdate( - presentationId=presentation_id, body=body).execute() - print('Updated the text style for shape with ID: {0}'.format(shape_id)) - # [END slides_text_style_update] - return response - - def create_bulleted_text(self, presentation_id, shape_id): - slides_service = self.service - # [START slides_create_bulleted_text] - # Add arrow-diamond-disc bullets to all text in the shape. - requests = [ - { - 'createParagraphBullets': { - 'objectId': shape_id, - 'textRange': { - 'type': 'ALL' - }, - 'bulletPreset': 'BULLET_ARROW_DIAMOND_DISC' - } - } - ] - - # Execute the requests. - body = { - 'requests': requests - } - response = slides_service.presentations().batchUpdate( - presentationId=presentation_id, body=body).execute() - print('Added bullets to text in shape with ID: {0}'.format(shape_id)) - # [END slides_create_bulleted_text] - return response - - def create_sheets_chart(self, presentation_id, page_id, spreadsheet_id, - sheet_chart_id): - slides_service = self.service - # [START slides_create_sheets_chart] - # Embed a Sheets chart (indicated by the spreadsheet_id and - # sheet_chart_id) onto a page in the presentation. - # Setting the linking mode as "LINKED" allows the - # chart to be refreshed if the Sheets version is updated. - emu4M = { - 'magnitude': 4000000, - 'unit': 'EMU' - } - presentation_chart_id = 'MyEmbeddedChart' - requests = [ - { - 'createSheetsChart': { - 'objectId': presentation_chart_id, - 'spreadsheetId': spreadsheet_id, - 'chartId': sheet_chart_id, - 'linkingMode': 'LINKED', - 'elementProperties': { - 'pageObjectId': page_id, - 'size': { - 'height': emu4M, - 'width': emu4M - }, - 'transform': { - 'scaleX': 1, - 'scaleY': 1, - 'translateX': 100000, - 'translateY': 100000, - 'unit': 'EMU' - } - } - } - } - ] - - # Execute the request. - body = { - 'requests': requests - } - response = slides_service.presentations().batchUpdate( - presentationId=presentation_id, body=body).execute() - print('Added a linked Sheets chart with ID: {0}'.format( - presentation_chart_id)) - # [END slides_create_sheets_chart] - return response - - def refresh_sheets_chart(self, presentation_id, presentation_chart_id): - slides_service = self.service - # [START slides_refresh_sheets_chart] - # Refresh an existing linked Sheets chart embedded in a presentation. - requests = [ - { - 'refreshSheetsChart': { - 'objectId': presentation_chart_id - } - } - ] - - # Execute the request. - body = { - 'requests': requests - } - response = slides_service.presentations().batchUpdate( - presentationId=presentation_id, body=body).execute() - print('Refreshed a linked Sheets chart with ID: {0}' - .format(presentation_chart_id)) - # [END slides_refresh_sheets_chart] - return response diff --git a/slides/snippets/slides_text_merging.py b/slides/snippets/slides_text_merging.py new file mode 100644 index 00000000..690ac1ab --- /dev/null +++ b/slides/snippets/slides_text_merging.py @@ -0,0 +1,129 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START slides_text_merging] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def text_merging(template_presentation_id, data_spreadsheet_id): + """ + Run Text merging the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + + try: + service = build("slides", "v1", credentials=creds) + sheets_service = build("sheets", "v4", credentials=creds) + drive_service = build("drive", "v3", credentials=creds) + # Use the Sheets API to load data, one record per row. + data_range_notation = "Customers!A2:M6" + sheets_response = ( + sheets_service.spreadsheets() + .values() + .get(spreadsheetId=data_spreadsheet_id, range=data_range_notation) + .execute() + ) + values = sheets_response.get("values") + + # For each record, create a new merged presentation. + for row in values: + customer_name = row[2] # name in column 3 + case_description = row[5] # case description in column 6 + total_portfolio = row[11] # total portfolio in column 12 + + # Duplicate the template presentation using the Drive API. + copy_title = customer_name + " presentation" + body = {"name": copy_title} + drive_response = ( + drive_service.files() + .copy(fileId=template_presentation_id, body=body) + .execute() + ) + presentation_copy_id = drive_response.get("id") + + # Create the text merge (replaceAllText) requests + # for this presentation. + requests = [ + { + "replaceAllText": { + "containsText": { + "text": "{{customer-name}}", + "matchCase": True, + }, + "replaceText": customer_name, + } + }, + { + "replaceAllText": { + "containsText": { + "text": "{{case-description}}", + "matchCase": True, + }, + "replaceText": case_description, + } + }, + { + "replaceAllText": { + "containsText": { + "text": "{{total-portfolio}}", + "matchCase": True, + }, + "replaceText": total_portfolio, + } + }, + ] + + # Execute the requests for this presentation. + body = {"requests": requests} + response = ( + service.presentations() + .batchUpdate(presentationId=presentation_copy_id, body=body) + .execute() + ) + + # Count the total number of replacements made. + num_replacements = 0 + for reply in response.get("replies"): + if reply.get("occurrencesChanged") is not None: + num_replacements += reply.get("replaceAllText").get( + "occurrencesChanged" + ) + print( + "Created presentation for " + f"{customer_name} with ID: {presentation_copy_id}" + ) + print(f"Replaced {num_replacements} text instances") + + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Put the template_presentation_id, data_spreadsheet_id + # of slides + + text_merging( + "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4", + "17eqFZl_WK4WVixX8PjvjfLD77DraoFwMDXeiHB3dvuM", + ) + # [END slides_text_merging] diff --git a/slides/snippets/slides_text_style_update.py b/slides/snippets/slides_text_style_update.py new file mode 100644 index 00000000..5615fe6e --- /dev/null +++ b/slides/snippets/slides_text_style_update.py @@ -0,0 +1,109 @@ +""" +Copyright 2022 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +# [START slides_text_style_update] +import google.auth +from googleapiclient.discovery import build +from googleapiclient.errors import HttpError + + +def text_style_update(presentation_id, shape_id): + """ + create_sheets_chart the user has access to. + Load pre-authorized user credentials from the environment. + TODO(developer) - See https://developers.google.com/identity + for guides on implementing OAuth2 for the application. + """ + creds, _ = google.auth.default() + # pylint: disable=maybe-no-member + try: + service = build("slides", "v1", credentials=creds) + # Update the text style so that the first 5 characters are bolded + # and italicized, the next 5 are displayed in blue 14 pt Times + # New Roman font, and the next 5 are hyperlinked. + requests = [ + { + "updateTextStyle": { + "objectId": shape_id, + "textRange": { + "type": "FIXED_RANGE", + "startIndex": 0, + "endIndex": 5, + }, + "style": {"bold": True, "italic": True}, + "fields": "bold,italic", + } + }, + { + "updateTextStyle": { + "objectId": shape_id, + "textRange": { + "type": "FIXED_RANGE", + "startIndex": 5, + "endIndex": 10, + }, + "style": { + "fontFamily": "Times New Roman", + "fontSize": {"magnitude": 14, "unit": "PT"}, + "foregroundColor": { + "opaqueColor": { + "rgbColor": { + "blue": 1.0, + "green": 0.0, + "red": 0.0, + } + } + }, + }, + "fields": "foregroundColor,fontFamily,fontSize", + } + }, + { + "updateTextStyle": { + "objectId": shape_id, + "textRange": { + "type": "FIXED_RANGE", + "startIndex": 10, + "endIndex": 15, + }, + "style": {"link": {"url": "www.example.com"}}, + "fields": "link", + } + }, + ] + + # Execute the requests. + body = {"requests": requests} + response = ( + service.presentations() + .batchUpdate(presentationId=presentation_id, body=body) + .execute() + ) + print(f"Updated the text style for shape with ID:{shape_id}") + + return response + except HttpError as error: + print(f"An error occurred: {error}") + return error + + +if __name__ == "__main__": + # Put the presentation_id, shape_id of slides + # to be submitted. + text_style_update( + "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4", "MyTextBox_9" + ) +# [END slides_text_style_update] diff --git a/slides/snippets/test_slides_copy_presentation.py b/slides/snippets/test_slides_copy_presentation.py new file mode 100644 index 00000000..c72c829c --- /dev/null +++ b/slides/snippets/test_slides_copy_presentation.py @@ -0,0 +1,34 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import slides_copy_presentation +from base_test import BaseTest + + +class TestCopyPresentation(BaseTest): + """Unit test for Copy presentation snippet""" + + def test_copy_presentation(self): + """set title for copy presentation""" + presentation_id = self.create_test_presentation() + copy_id = slides_copy_presentation.copy_presentation( + presentation_id, "My Duplicate Presentation" + ) + self.assertIsNotNone(copy_id) + self.delete_file_on_cleanup(copy_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/test_slides_create_bulleted_text.py b/slides/snippets/test_slides_create_bulleted_text.py new file mode 100644 index 00000000..885862e6 --- /dev/null +++ b/slides/snippets/test_slides_create_bulleted_text.py @@ -0,0 +1,36 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest +from pprint import pformat + +import slides_create_bulleted_text +from base_test import BaseTest + + +class TestCreateBulletedText(BaseTest): + """Unit test for create_bulleted_text snippet""" + + def test_create_bulleted_text(self): + """create_bulleted_text function""" + presentation_id = self.create_test_presentation() + page_id = self.add_slides(presentation_id, 1, "BLANK")[0] + box_id = self.create_test_textbox(presentation_id, page_id) + response = slides_create_bulleted_text.create_bulleted_text( + presentation_id, box_id + ) + self.assertEqual(1, len(response.get("replies")), msg=pformat(response)) + + +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/test_slides_create_image.py b/slides/snippets/test_slides_create_image.py new file mode 100644 index 00000000..e8bb4237 --- /dev/null +++ b/slides/snippets/test_slides_create_image.py @@ -0,0 +1,35 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest +from pprint import pformat + +import slides_create_image +from base_test import BaseTest + + +class TestCreateTextboxWithText(BaseTest): + """Unit test case for create_image snippet""" + + def test_create_image(self): + """presentation id and page id for create image""" + presentation_id = self.create_test_presentation() + page_id = self.add_slides(presentation_id, 1, "BLANK")[0] + response = slides_create_image.create_image(presentation_id, page_id) + self.assertEqual(1, len(response.get("replies")), msg=pformat(response)) + image_id = response.get("replies")[0].get("createImage").get("objectId") + self.assertIsNotNone(image_id, msg=pformat(response)) + + +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/test_slides_create_presentation.py b/slides/snippets/test_slides_create_presentation.py new file mode 100644 index 00000000..06fd5f01 --- /dev/null +++ b/slides/snippets/test_slides_create_presentation.py @@ -0,0 +1,31 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import slides_create_presentation +from base_test import BaseTest + + +class TestCreatePresentation(BaseTest): + """Unit test for create presentation snippet""" + + def test_create_presentation(self): + """Set title for create presentation""" + presentation = slides_create_presentation.create_presentation("Title") + self.assertIsNotNone(presentation) + self.delete_file_on_cleanup(presentation.get("presentationId")) + + +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/test_slides_create_sheets_chart.py b/slides/snippets/test_slides_create_sheets_chart.py new file mode 100644 index 00000000..e9c1b8f3 --- /dev/null +++ b/slides/snippets/test_slides_create_sheets_chart.py @@ -0,0 +1,42 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest +from pprint import pformat + +import slides_create_sheets_chart +from base_test import BaseTest + + +class TestCreateSheetsChart(BaseTest): + """Unit test for create_sheets_chart snippet""" + + DATA_SPREADSHEET_ID = "17eqFZl_WK4WVixX8PjvjfLD77DraoFwMDXeiHB3dvuM" + CHART_ID = 1107320627 + + def test_create_sheets_chart(self): + """create_sheet chart method""" + presentation_id = self.create_test_presentation() + page_id = self.add_slides(presentation_id, 1, "BLANK")[0] + response = slides_create_sheets_chart.create_sheets_chart( + presentation_id, page_id, self.DATA_SPREADSHEET_ID, self.CHART_ID + ) + self.assertEqual(1, len(response.get("replies")), msg=pformat(response)) + chart_id = ( + response.get("replies")[0].get("createSheetsChart").get("objectId") + ) + self.assertIsNotNone(chart_id, msg=pformat(response)) + + +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/test_slides_create_slide.py b/slides/snippets/test_slides_create_slide.py new file mode 100644 index 00000000..22c30eb1 --- /dev/null +++ b/slides/snippets/test_slides_create_slide.py @@ -0,0 +1,36 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import slides_create_slide +from base_test import BaseTest + + +class TestCreateSlide(BaseTest): + """Unit test for create Slide snippet""" + + def test_create_slide(self): + """pass presentation_id and page_id for creating the slides""" + presentation_id = self.create_test_presentation() + self.add_slides(presentation_id, 3) + page_id = "my_page_id" + response = slides_create_slide.create_slide(presentation_id, page_id) + self.assertEqual( + page_id, + response.get("replies")[0].get("createSlide").get("objectId"), + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/test_slides_create_textbox_with_text.py b/slides/snippets/test_slides_create_textbox_with_text.py new file mode 100644 index 00000000..36b0b70a --- /dev/null +++ b/slides/snippets/test_slides_create_textbox_with_text.py @@ -0,0 +1,37 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest +from pprint import pformat + +import slides_create_textbox_with_text +from base_test import BaseTest + + +class TestCreateTextboxWithText(BaseTest): + """Unit test for TestCreateTextboxWithText snippet""" + + def test_create_textbox_with_text(self): + """Pass Presentation id and page id""" + presentation_id = self.create_test_presentation() + page_id = self.add_slides(presentation_id, 1, "BLANK")[0] + response = slides_create_textbox_with_text.create_textbox_with_text( + presentation_id, page_id + ) + self.assertEqual(2, len(response.get("replies")), msg=pformat(response)) + box_id = response.get("replies")[0].get("createShape").get("objectId") + self.assertIsNotNone(box_id, msg=pformat(response)) + + +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/test_slides_image_merging.py b/slides/snippets/test_slides_image_merging.py new file mode 100644 index 00000000..844480ea --- /dev/null +++ b/slides/snippets/test_slides_image_merging.py @@ -0,0 +1,48 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest +from pprint import pformat + +import slides_image_merging +from base_test import BaseTest + + +class TestTextMerging(BaseTest): + """Unit test for text merging snippet""" + + TEMPLATE_PRESENTATION_ID = "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4" + DATA_SPREADSHEET_ID = "17eqFZl_WK4WVixX8PjvjfLD77DraoFwMDXeiHB3dvuM" + IMAGE_URL = "https://picsum.photos/200" + CHART_ID = 1107320627 + CUSTOMER_NAME = "Fake Customer" + + def test_image_merging(self): + """image merging function""" + response = slides_image_merging.image_merging( + self.TEMPLATE_PRESENTATION_ID, self.IMAGE_URL, self.CUSTOMER_NAME + ) + presentation_id = response.get("presentationId") + self.delete_file_on_cleanup(presentation_id) + self.assertIsNotNone(presentation_id, msg=pformat(response)) + self.assertEqual(2, len(response.get("replies")), msg=pformat(response)) + num_replacements = 0 + for reply in response.get("replies"): + if isinstance(reply, int): + num_replacements += reply.get("replaceAllShapesWithImage").get( + "occurrencesChanged" + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/test_slides_refresh_sheets_chart.py b/slides/snippets/test_slides_refresh_sheets_chart.py new file mode 100644 index 00000000..9ecb7723 --- /dev/null +++ b/slides/snippets/test_slides_refresh_sheets_chart.py @@ -0,0 +1,41 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest +from pprint import pformat + +import slides_refresh_sheets_chart +from base_test import BaseTest + + +class TestCreateSheetsChart(BaseTest): + """Unit test for refresh_sheets_chart snippet""" + + DATA_SPREADSHEET_ID = "17eqFZl_WK4WVixX8PjvjfLD77DraoFwMDXeiHB3dvuM" + CHART_ID = 1107320627 + + def test_refresh_sheets_chart(self): + """refresh_sheets_chart method""" + presentation_id = self.create_test_presentation() + page_id = self.add_slides(presentation_id, 1, "BLANK")[0] + chart_id = self.create_test_sheets_chart( + presentation_id, page_id, self.DATA_SPREADSHEET_ID, self.CHART_ID + ) + response = slides_refresh_sheets_chart.refresh_sheets_chart( + presentation_id, chart_id + ) + self.assertEqual(1, len(response.get("replies")), msg=pformat(response)) + + +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/test_slides_simple_text_replace.py b/slides/snippets/test_slides_simple_text_replace.py new file mode 100644 index 00000000..bbf854a2 --- /dev/null +++ b/slides/snippets/test_slides_simple_text_replace.py @@ -0,0 +1,36 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest +from pprint import pformat + +import slides_simple_text_replace +from base_test import BaseTest + + +class TestSimpleTextReplace(BaseTest): + """Unit test for SimpleTextReplace snippet""" + + def test_simple_text_replace(self): + """test_simple_text_replace function""" + presentation_id = self.create_test_presentation() + page_id = self.add_slides(presentation_id, 1, "BLANK")[0] + box_id = self.create_test_textbox(presentation_id, page_id) + response = slides_simple_text_replace.simple_text_replace( + presentation_id, box_id, "MY NEW TEXT" + ) + self.assertEqual(2, len(response.get("replies")), msg=pformat(response)) + + +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/test_slides_text_merging.py b/slides/snippets/test_slides_text_merging.py new file mode 100644 index 00000000..f3f0ac54 --- /dev/null +++ b/slides/snippets/test_slides_text_merging.py @@ -0,0 +1,34 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest + +import slides_text_merging +from base_test import BaseTest + + +class TestTextMerging(BaseTest): + """Unit test for SimpleTextReplace snippet""" + + TEMPLATE_PRESENTATION_ID = "10QnVUx1X2qHsL17WUidGpPh_SQhXYx40CgIxaKk8jU4" + DATA_SPREADSHEET_ID = "17eqFZl_WK4WVixX8PjvjfLD77DraoFwMDXeiHB3dvuM" + + def test_text_merging(self): + """text_merging method""" + slides_text_merging.text_merging( + self.TEMPLATE_PRESENTATION_ID, self.DATA_SPREADSHEET_ID + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/test_slides_text_style_update.py b/slides/snippets/test_slides_text_style_update.py new file mode 100644 index 00000000..1e7e8df0 --- /dev/null +++ b/slides/snippets/test_slides_text_style_update.py @@ -0,0 +1,36 @@ +""" +Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import unittest +from pprint import pformat + +import slides_text_style_update +from base_test import BaseTest + + +class TestTextStyleUpdate(BaseTest): + """Unit test for SimpleTextReplace snippet""" + + def test_text_style_update(self): + """test_text_style_update function""" + presentation_id = self.create_test_presentation() + page_id = self.add_slides(presentation_id, 1, "BLANK")[0] + box_id = self.create_test_textbox(presentation_id, page_id) + response = slides_text_style_update.text_style_update( + presentation_id, box_id + ) + self.assertEqual(3, len(response.get("replies")), msg=pformat(response)) + + +if __name__ == "__main__": + unittest.main() diff --git a/slides/snippets/test_snippets.py b/slides/snippets/test_snippets.py deleted file mode 100644 index 9e9677fb..00000000 --- a/slides/snippets/test_snippets.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest -from pprint import pformat - -from base_test import BaseTest -from slides_snippets import SlidesSnippets - - -class SnippetsTest(BaseTest): - IMAGE_URL = 'https://www.google.com/images/' \ - 'branding/googlelogo/2x/googlelogo_color_272x92dp.png' - TEMPLATE_PRESENTATION_ID = '1ElmXUX6de-b_OkH2iOK8PKS9FfQeln_Rx0aloIg6Rdc' - DATA_SPREADSHEET_ID = '17eqFZl_WK4WVixX8PjvjfLD77DraoFwMDXeiHB3dvuM' - CHART_ID = 1107320627 - CUSTOMER_NAME = 'Fake Customer' - - @classmethod - def setUpClass(cls): - super(SnippetsTest, cls).setUpClass() - cls.snippets = SlidesSnippets( - cls.service, - cls.drive_service, - cls.sheets_service, - cls.credentials) - - def test_create_presentation(self): - presentation = self.snippets.create_presentation('Title') - self.assertIsNotNone(presentation) - self.delete_file_on_cleanup(presentation.get('presentationId')) - - def test_copy_presentation(self): - presentation_id = self.create_test_presentation() - copy_id = self.snippets.copy_presentation( - presentation_id, 'My Duplicate Presentation') - self.assertIsNotNone(copy_id) - self.delete_file_on_cleanup(copy_id) - - def test_create_slide(self): - presentation_id = self.create_test_presentation() - self.add_slides(presentation_id, 3) - page_id = 'my_page_id' - response = self.snippets.create_slide(presentation_id, page_id) - self.assertEqual(page_id, - response.get('replies')[0].get('createSlide').get('objectId')) - - def test_create_textbox_with_text(self): - presentation_id = self.create_test_presentation() - page_id = self.add_slides(presentation_id, 1, 'BLANK')[0] - response = self.snippets.create_textbox_with_text( - presentation_id, page_id) - self.assertEqual(2, len(response.get('replies')), - msg=pformat(response)) - box_id = response.get('replies')[0].get('createShape').get('objectId') - self.assertIsNotNone(box_id, msg=pformat(response)) - - def test_create_image(self): - presentation_id = self.create_test_presentation() - page_id = self.add_slides(presentation_id, 1, 'BLANK')[0] - response = self.snippets.create_image(presentation_id, page_id) - self.assertEqual(1, len(response.get('replies')), - msg=pformat(response)) - image_id = response.get('replies')[0].get( - 'createImage').get('objectId') - self.assertIsNotNone(image_id, msg=pformat(response)) - - def test_text_merging(self): - responses = self.snippets.text_merging( - SnippetsTest.TEMPLATE_PRESENTATION_ID, - SnippetsTest.DATA_SPREADSHEET_ID) - for response in responses: - presentation_id = response.get('presentationId') - self.delete_file_on_cleanup(presentation_id) - self.assertIsNotNone(presentation_id, msg=pformat(response)) - self.assertEqual(3, len(response.get('replies')), - msg=pformat(response)) - num_replacements = 0 - for reply in response.get('replies'): - num_replacements += reply.get('replaceAllText') \ - .get('occurrencesChanged') - self.assertEqual(4, num_replacements, msg=pformat(reply)) - - def test_image_merging(self): - response = self.snippets.image_merging( - SnippetsTest.TEMPLATE_PRESENTATION_ID, - SnippetsTest.IMAGE_URL, - SnippetsTest.CUSTOMER_NAME) - presentation_id = response.get('presentationId') - self.delete_file_on_cleanup(presentation_id) - self.assertIsNotNone(presentation_id, msg=pformat(response)) - self.assertEqual(2, len(response.get('replies')), - msg=pformat(response)) - num_replacements = 0 - for reply in response.get('replies'): - num_replacements += reply.get('replaceAllShapesWithImage') \ - .get('occurrencesChanged') - self.assertEqual(2, num_replacements) - - def test_simple_text_replace(self): - presentation_id = self.create_test_presentation() - page_id = self.add_slides(presentation_id, 1, 'BLANK')[0] - box_id = self.create_test_textbox(presentation_id, page_id) - response = self.snippets.simple_text_replace( - presentation_id, box_id, 'MY NEW TEXT') - self.assertEqual(2, len(response.get('replies')), - msg=pformat(response)) - - def test_text_style_update(self): - presentation_id = self.create_test_presentation() - page_id = self.add_slides(presentation_id, 1, 'BLANK')[0] - box_id = self.create_test_textbox(presentation_id, page_id) - response = self.snippets.text_style_update(presentation_id, box_id) - self.assertEqual(3, len(response.get('replies')), - msg=pformat(response)) - - def test_create_bulleted_text(self): - presentation_id = self.create_test_presentation() - page_id = self.add_slides(presentation_id, 1, 'BLANK')[0] - box_id = self.create_test_textbox(presentation_id, page_id) - response = self.snippets.create_bulleted_text(presentation_id, box_id) - self.assertEqual(1, len(response.get('replies')), - msg=pformat(response)) - - def test_create_sheets_chart(self): - presentation_id = self.create_test_presentation() - page_id = self.add_slides(presentation_id, 1, 'BLANK')[0] - response = self.snippets.create_sheets_chart(presentation_id, - page_id, SnippetsTest.DATA_SPREADSHEET_ID, SnippetsTest.CHART_ID) - self.assertEqual(1, len(response.get('replies')), - msg=pformat(response)) - chart_id = response.get('replies')[0].get('createSheetsChart') \ - .get('objectId') - self.assertIsNotNone(chart_id, msg=pformat(response)) - - def test_refresh_sheets_chart(self): - presentation_id = self.create_test_presentation() - page_id = self.add_slides(presentation_id, 1, 'BLANK')[0] - chart_id = self.create_test_sheets_chart(presentation_id, - page_id, SnippetsTest.DATA_SPREADSHEET_ID, SnippetsTest.CHART_ID) - response = self.snippets.refresh_sheets_chart( - presentation_id, chart_id) - self.assertEqual(1, len(response.get('replies')), - msg=pformat(response)) - - -if __name__ == '__main__': - unittest.main() diff --git a/solutions/webhook-chat-app/README.md b/solutions/webhook-chat-app/README.md new file mode 100644 index 00000000..59643860 --- /dev/null +++ b/solutions/webhook-chat-app/README.md @@ -0,0 +1,4 @@ +# Google Chat App Webhook + +Please see related guide on how to +[send messages to Google Chat with incoming webhooks](https://developers.google.com/workspace/chat/quickstart/webhooks). diff --git a/solutions/webhook-chat-app/quickstart.py b/solutions/webhook-chat-app/quickstart.py new file mode 100644 index 00000000..c7aba443 --- /dev/null +++ b/solutions/webhook-chat-app/quickstart.py @@ -0,0 +1,44 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# A sample script for using an incoming webhook for Google Chat rooms. + + +# [START chat_webhook] +from json import dumps +from httplib2 import Http + +# Copy the webhook URL from the Chat space where the webhook is registered. +# The values for SPACE_ID, KEY, and TOKEN are set by Chat, and are included +# when you copy the webhook URL. + +def main(): + """Google Chat incoming webhook quickstart.""" + url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN" + app_message = { + "text": "Hello from a Python script!" + } + message_headers = {"Content-Type": "application/json; charset=UTF-8"} + http_obj = Http() + response = http_obj.request( + uri=url, + method="POST", + headers=message_headers, + body=dumps(app_message), + ) + print(response) + + +if __name__ == "__main__": + main() +# [END chat_webhook] diff --git a/solutions/webhook-chat-app/requirements.txt b/solutions/webhook-chat-app/requirements.txt new file mode 100644 index 00000000..e75eb880 --- /dev/null +++ b/solutions/webhook-chat-app/requirements.txt @@ -0,0 +1 @@ +httplib2>=0.17.0 diff --git a/solutions/webhook-chat-app/thread-reply.py b/solutions/webhook-chat-app/thread-reply.py new file mode 100644 index 00000000..ba8b9aee --- /dev/null +++ b/solutions/webhook-chat-app/thread-reply.py @@ -0,0 +1,52 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# [START chat_webhook_thread] +from json import dumps +from httplib2 import Http + +# Copy the webhook URL from the Chat space where the webhook is registered. +# The values for SPACE_ID, KEY, and TOKEN are set by Chat, and are included +# when you copy the webhook URL. +# +# Then, append messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD to the +# webhook URL. + + +def main(): + """Google Chat incoming webhook that starts or replies to a message thread.""" + url = "https://chat.googleapis.com/v1/spaces/SPACE_ID/messages?key=KEY&token=TOKEN&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD" + app_message = { + "text": "Hello from a Python script!", + # To start a thread, set threadKey to an arbitratry string. + # To reply to a thread, specify that thread's threadKey value. + "thread": { + "threadKey": "THREAD_KEY_VALUE" + }, + } + message_headers = {"Content-Type": "application/json; charset=UTF-8"} + http_obj = Http() + response = http_obj.request( + uri=url, + method="POST", + headers=message_headers, + body=dumps(app_message), + ) + print(response) + + +if __name__ == "__main__": + main() +# [END chat_webhook_thread] diff --git a/tasks/quickstart/quickstart.py b/tasks/quickstart/quickstart.py index ab0882b5..6f380c22 100644 --- a/tasks/quickstart/quickstart.py +++ b/tasks/quickstart/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START tasks_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -24,49 +22,50 @@ from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/tasks.readonly'] +SCOPES = ["https://www.googleapis.com/auth/tasks.readonly"] def main(): - """Shows basic usage of the Tasks API. - Prints the title and ID of the first 10 task lists. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) + """Shows basic usage of the Tasks API. + Prints the title and ID of the first 10 task lists. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - try: - service = build('tasks', 'v1', credentials=creds) + try: + service = build("tasks", "v1", credentials=creds) - # Call the Tasks API - results = service.tasklists().list(maxResults=10).execute() - items = results.get('items', []) + # Call the Tasks API + results = service.tasklists().list(maxResults=10).execute() + items = results.get("items", []) - if not items: - print('No task lists found.') - return + if not items: + print("No task lists found.") + return - print('Task lists:') - for item in items: - print(u'{0} ({1})'.format(item['title'], item['id'])) - except HttpError as err: - print(err) + print("Task lists:") + for item in items: + print(f"{item['title']} ({item['id']})") + except HttpError as err: + print(err) -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END tasks_quickstart] diff --git a/vault/quickstart/quickstart.py b/vault/quickstart/quickstart.py index cfd5f6ff..57dadd95 100644 --- a/vault/quickstart/quickstart.py +++ b/vault/quickstart/quickstart.py @@ -13,8 +13,6 @@ # limitations under the License. # [START vault_quickstart] -from __future__ import print_function - import os.path from google.auth.transport.requests import Request @@ -24,50 +22,50 @@ from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. -SCOPES = ['https://www.googleapis.com/auth/ediscovery'] +SCOPES = ["https://www.googleapis.com/auth/ediscovery"] def main(): - """Shows basic usage of the Vault API. - Prints the names and IDs of the first 10 matters in Vault. - """ - creds = None - # The file token.json stores the user's access and refresh tokens, and is - # created automatically when the authorization flow completes for the first - # time. - if os.path.exists('token.json'): - creds = Credentials.from_authorized_user_file('token.json', SCOPES) - # If there are no (valid) credentials available, let the user log in. - if not creds or not creds.valid: - if creds and creds.expired and creds.refresh_token: - creds.refresh(Request()) - else: - flow = InstalledAppFlow.from_client_secrets_file( - 'credentials.json', SCOPES) - creds = flow.run_local_server(port=0) - # Save the credentials for the next run - with open('token.json', 'w') as token: - token.write(creds.to_json()) - - try: + """Shows basic usage of the Vault API. + Prints the names and IDs of the first 10 matters in Vault. + """ + creds = None + # The file token.json stores the user's access and refresh tokens, and is + # created automatically when the authorization flow completes for the first + # time. + if os.path.exists("token.json"): + creds = Credentials.from_authorized_user_file("token.json", SCOPES) + # If there are no (valid) credentials available, let the user log in. + if not creds or not creds.valid: + if creds and creds.expired and creds.refresh_token: + creds.refresh(Request()) + else: + flow = InstalledAppFlow.from_client_secrets_file( + "credentials.json", SCOPES + ) + creds = flow.run_local_server(port=0) + # Save the credentials for the next run + with open("token.json", "w") as token: + token.write(creds.to_json()) - service = build('vault', 'v1', credentials=creds) + try: + service = build("vault", "v1", credentials=creds) - # Call the Vault API - results = service.matters().list(pageSize=10).execute() - matters = results.get('matters', []) + # Call the Vault API + results = service.matters().list(pageSize=10).execute() + matters = results.get("matters", []) - if not matters: - print('No matters found.') - return + if not matters: + print("No matters found.") + return - print('Matters:') - for matter in matters: - print(u'{} ({})'.format(matter.get('name'), matter.get('id'))) - except HttpError as err: - print(err) + print("Matters:") + for matter in matters: + print(f"{matter.get('name')} ({matter.get('id')})") + except HttpError as err: + print(err) -if __name__ == '__main__': - main() +if __name__ == "__main__": + main() # [END vault_quickstart]