diff --git a/docker-compose.yml b/docker-compose.yml index 2bda16a..0db8ec9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,18 +12,18 @@ services: - "8000:8000" volumes: - .:/backend_labs - depends_on: - - postgres - - postgres: - image: postgres:15.3 - container_name: postgres - volumes: - - postgres_data:/var/lib/postgresql/data/ - env_file: - - .env - ports: - - "5432:5432" - -volumes: - postgres_data: +# depends_on: +# - postgres +# +# postgres: +# image: postgres:15.3 +# container_name: postgres +# volumes: +# - postgres_data:/var/lib/postgresql/data/ +# env_file: +# - .env +# ports: +# - "5432:5432" +# +#volumes: +# postgres_data: diff --git a/poetry.lock b/poetry.lock index c9be213..b627b58 100644 --- a/poetry.lock +++ b/poetry.lock @@ -77,6 +77,18 @@ files = [ tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] +[[package]] +name = "cachetools" +version = "5.3.1" +description = "Extensible memoizing collections and decorators" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, + {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, +] + [[package]] name = "certifi" version = "2023.5.7" @@ -257,6 +269,127 @@ dev = ["pre-commit (>=2.17.0,<3.0.0)", "ruff (==0.0.138)", "uvicorn[standard] (> doc = ["mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "typer-cli (>=0.0.13,<0.0.14)", "typer[all] (>=0.6.1,<0.8.0)"] test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6.5.0,<8.0)", "databases[sqlite] (>=0.3.2,<0.7.0)", "email-validator (>=1.1.1,<2.0.0)", "flask (>=1.1.2,<3.0.0)", "httpx (>=0.23.0,<0.24.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.982)", "orjson (>=3.2.1,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "peewee (>=3.13.3,<4.0.0)", "pytest (>=7.1.3,<8.0.0)", "python-jose[cryptography] (>=3.3.0,<4.0.0)", "python-multipart (>=0.0.5,<0.0.7)", "pyyaml (>=5.3.1,<7.0.0)", "ruff (==0.0.138)", "sqlalchemy (>=1.3.18,<1.4.43)", "types-orjson (==3.6.2)", "types-ujson (==5.7.0.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)"] +[[package]] +name = "google-api-core" +version = "2.11.1" +description = "Google API client core library" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-core-2.11.1.tar.gz", hash = "sha256:25d29e05a0058ed5f19c61c0a78b1b53adea4d9364b464d014fbda941f6d1c9a"}, + {file = "google_api_core-2.11.1-py3-none-any.whl", hash = "sha256:d92a5a92dc36dd4f4b9ee4e55528a90e432b059f93aee6ad857f9de8cc7ae94a"}, +] + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-api-python-client" +version = "2.90.0" +description = "Google API Client Library for Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-python-client-2.90.0.tar.gz", hash = "sha256:cbcb3ba8be37c6806676a49df16ac412077e5e5dc7fa967941eff977b31fba03"}, + {file = "google_api_python_client-2.90.0-py2.py3-none-any.whl", hash = "sha256:4a41ffb7797d4f28e44635fb1e7076240b741c6493e7c3233c0e4421cec7c913"}, +] + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0.dev0" +google-auth = ">=1.19.0,<3.0.0.dev0" +google-auth-httplib2 = ">=0.1.0" +httplib2 = ">=0.15.0,<1.dev0" +uritemplate = ">=3.0.1,<5" + +[[package]] +name = "google-auth" +version = "2.17.3" +description = "Google Authentication Library" +category = "main" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" +files = [ + {file = "google-auth-2.17.3.tar.gz", hash = "sha256:ce311e2bc58b130fddf316df57c9b3943c2a7b4f6ec31de9663a9333e4064efc"}, + {file = "google_auth-2.17.3-py2.py3-none-any.whl", hash = "sha256:f586b274d3eb7bd932ea424b1c702a30e0393a2e2bc4ca3eae8263ffd8be229f"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} +six = ">=1.9.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "requests (>=2.20.0,<3.0.0dev)"] +enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0dev)"] + +[[package]] +name = "google-auth-httplib2" +version = "0.1.0" +description = "Google Authentication Library: httplib2 transport" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, + {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"}, +] + +[package.dependencies] +google-auth = "*" +httplib2 = ">=0.15.0" +six = "*" + +[[package]] +name = "google-auth-oauthlib" +version = "1.0.0" +description = "Google Authentication Library" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "google-auth-oauthlib-1.0.0.tar.gz", hash = "sha256:e375064964820b47221a7e1b7ee1fd77051b6323c3f9e3e19785f78ab67ecfc5"}, + {file = "google_auth_oauthlib-1.0.0-py2.py3-none-any.whl", hash = "sha256:95880ca704928c300f48194d1770cf5b1462835b6e49db61445a520f793fd5fb"}, +] + +[package.dependencies] +google-auth = ">=2.15.0" +requests-oauthlib = ">=0.7.0" + +[package.extras] +tool = ["click (>=6.0.0)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.59.1" +description = "Common protobufs used in Google APIs" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "googleapis-common-protos-1.59.1.tar.gz", hash = "sha256:b35d530fe825fb4227857bc47ad84c33c809ac96f312e13182bdeaa2abe1178a"}, + {file = "googleapis_common_protos-1.59.1-py2.py3-none-any.whl", hash = "sha256:0cbedb6fb68f1c07e18eb4c48256320777707e7d0c55063ae56c15db3224a61e"}, +] + +[package.dependencies] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + [[package]] name = "greenlet" version = "2.0.2" @@ -364,6 +497,21 @@ files = [ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] +[[package]] +name = "httplib2" +version = "0.22.0" +description = "A comprehensive HTTP client library." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, + {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, +] + +[package.dependencies] +pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} + [[package]] name = "idna" version = "3.4" @@ -474,6 +622,23 @@ files = [ {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + [[package]] name = "passlib" version = "1.7.4" @@ -495,6 +660,29 @@ bcrypt = ["bcrypt (>=3.1.0)"] build-docs = ["cloud-sptheme (>=1.10.1)", "sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)"] totp = ["cryptography"] +[[package]] +name = "protobuf" +version = "4.23.3" +description = "" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "protobuf-4.23.3-cp310-abi3-win32.whl", hash = "sha256:514b6bbd54a41ca50c86dd5ad6488afe9505901b3557c5e0f7823a0cf67106fb"}, + {file = "protobuf-4.23.3-cp310-abi3-win_amd64.whl", hash = "sha256:cc14358a8742c4e06b1bfe4be1afbdf5c9f6bd094dff3e14edb78a1513893ff5"}, + {file = "protobuf-4.23.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:2991f5e7690dab569f8f81702e6700e7364cc3b5e572725098215d3da5ccc6ac"}, + {file = "protobuf-4.23.3-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:08fe19d267608d438aa37019236db02b306e33f6b9902c3163838b8e75970223"}, + {file = "protobuf-4.23.3-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3b01a5274ac920feb75d0b372d901524f7e3ad39c63b1a2d55043f3887afe0c1"}, + {file = "protobuf-4.23.3-cp37-cp37m-win32.whl", hash = "sha256:aca6e86a08c5c5962f55eac9b5bd6fce6ed98645d77e8bfc2b952ecd4a8e4f6a"}, + {file = "protobuf-4.23.3-cp37-cp37m-win_amd64.whl", hash = "sha256:0149053336a466e3e0b040e54d0b615fc71de86da66791c592cc3c8d18150bf8"}, + {file = "protobuf-4.23.3-cp38-cp38-win32.whl", hash = "sha256:84ea0bd90c2fdd70ddd9f3d3fc0197cc24ecec1345856c2b5ba70e4d99815359"}, + {file = "protobuf-4.23.3-cp38-cp38-win_amd64.whl", hash = "sha256:3bcbeb2bf4bb61fe960dd6e005801a23a43578200ea8ceb726d1f6bd0e562ba1"}, + {file = "protobuf-4.23.3-cp39-cp39-win32.whl", hash = "sha256:5cb9e41188737f321f4fce9a4337bf40a5414b8d03227e1d9fbc59bc3a216e35"}, + {file = "protobuf-4.23.3-cp39-cp39-win_amd64.whl", hash = "sha256:29660574cd769f2324a57fb78127cda59327eb6664381ecfe1c69731b83e8288"}, + {file = "protobuf-4.23.3-py3-none-any.whl", hash = "sha256:447b9786ac8e50ae72cae7a2eec5c5df6a9dbf9aa6f908f1b8bda6032644ea62"}, + {file = "protobuf-4.23.3.tar.gz", hash = "sha256:7a92beb30600332a52cdadbedb40d33fd7c8a0d7f549c440347bc606fb3fe34b"}, +] + [[package]] name = "psycopg2" version = "2.9.6" @@ -530,6 +718,21 @@ files = [ {file = "pyasn1-0.5.0.tar.gz", hash = "sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde"}, ] +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, + {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + [[package]] name = "pydantic" version = "1.10.9" @@ -583,6 +786,21 @@ typing-extensions = ">=4.2.0" dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=1.0.3)"] +[[package]] +name = "pyparsing" +version = "3.1.0" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.0-py3-none-any.whl", hash = "sha256:d554a96d1a7d3ddaf7183104485bc19fd80543ad6ac5bdb6426719d766fb06c1"}, + {file = "pyparsing-3.1.0.tar.gz", hash = "sha256:edb662d6fe322d6e990b1594b5feaeadf806803359e3d4d42f11e295e588f0ea"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + [[package]] name = "python-jose" version = "3.3.0" @@ -642,6 +860,25 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +description = "OAuthlib authentication support for Requests." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, + {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, +] + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + [[package]] name = "rsa" version = "4.9" @@ -835,6 +1072,18 @@ files = [ {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, ] +[[package]] +name = "uritemplate" +version = "4.1.1" +description = "Implementation of RFC 6570 URI Templates" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, + {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, +] + [[package]] name = "urllib3" version = "2.0.3" @@ -875,4 +1124,4 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "c9c66d7cf4eb31fdb3b4bb60c157739e9cd4c6f71511a9398705943e5a63fe3c" +content-hash = "f97783e2d795f0b96d6b8d2085f4b8871bc30363f47d22cc659a9108181b4910" diff --git a/pyproject.toml b/pyproject.toml index 509e5d2..9a1decd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,9 @@ psycopg2 = "^2.9.6" python-multipart = "^0.0.6" python-jose = "^3.3.0" passlib = {extras = ["bcrypt"], version = "^1.7.4"} +google-api-python-client = "^2.90.0" +google-auth-httplib2 = "^0.1.0" +google-auth-oauthlib = "^1.0.0" [build-system] diff --git a/src/database.py b/src/database.py index 2b4f174..287cf39 100644 --- a/src/database.py +++ b/src/database.py @@ -2,13 +2,13 @@ import os from sqlmodel import SQLModel, create_engine, Session -# db_string = "sqlite:///database.sqlite" +db_string = "sqlite:///database.sqlite" -db_string = os.environ.get("DB_OVERRIDES_STRING") -if not db_string: - db_string = f'{os.environ.get("DB_CONNECTION")}://' \ - f'{os.environ.get("DB_USERNAME")}:{os.environ.get("DB_PASSWORD", default="")}@' \ - f'{os.environ.get("DB_HOST")}:{os.environ.get("DB_PORT")}/{os.environ.get("DB_DATABASE")}' +# db_string = os.environ.get("DB_OVERRIDES_STRING") +# if not db_string: +# db_string = f'{os.environ.get("DB_CONNECTION")}://' \ +# f'{os.environ.get("DB_USERNAME")}:{os.environ.get("DB_PASSWORD", default="")}@' \ +# f'{os.environ.get("DB_HOST")}:{os.environ.get("DB_PORT")}/{os.environ.get("DB_DATABASE")}' connect_args = {"check_same_thread": False} if "sqlite" in db_string else {} diff --git a/src/main.py b/src/main.py index 3994b69..4c791bb 100644 --- a/src/main.py +++ b/src/main.py @@ -1,9 +1,21 @@ -from fastapi import FastAPI +from datetime import datetime, timedelta +from typing import List, Optional + +from fastapi import FastAPI, Query, Depends, HTTPException, status from fastapi.staticfiles import StaticFiles from fastapi.middleware.cors import CORSMiddleware +from sqlmodel import SQLModel +from pydantic import BaseModel, Field from routes import main_router + +from google.auth.transport.requests import Request +from google.oauth2 import service_account +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build, Resource + + app = FastAPI() origins = ["*"] @@ -19,3 +31,194 @@ app.add_middleware( app.mount("/static", StaticFiles(directory="static"), name="static") app.include_router(router=main_router) + +SCOPES = ['https://www.googleapis.com/auth/calendar'] +FILE_PATH = 'token.json' +CALENDAR_ID = '926affbbac4e8e6701060f1fec6189162b0b6d246db13d84682749d647af9a1f@group.calendar.google.com' +creds = service_account.Credentials.from_service_account_file(filename=FILE_PATH, scopes=SCOPES) + +TITLE_FREE = 'Запись свободна' +COLOR_FREE = '10' +TITLE_BUSY = 'Занято' +COLOR_BUSY = '6' + + +def get_calendar_service(): + with build('calendar', 'v3', credentials=creds) as service: + print("Service created") + yield service + + +class CalendarDate(SQLModel): + dateTime: str = Field(alias="dateTime") + timeZone: str = Field(alias="timeZone") + date: str | None + + +class CalendarAttendees(SQLModel): + email: str + response_status: str | None = Field(default=None, alias="responseStatus") + + +class CalendarEvent(SQLModel): + id: str | None = None + start: CalendarDate + end: CalendarDate + colorId: str | None = None + summary: str | None = None + description: str | None = None + status: str | None = None + attendees: List[CalendarAttendees] | None = None + + +@app.get('/calendar_events', response_model=List[CalendarEvent]) +def get_calendar_events( + lower_bound: datetime = None, + upper_bound: datetime = None, + service: Resource = Depends(get_calendar_service) +): + if lower_bound and upper_bound: + events_result = service.events().list( + calendarId=CALENDAR_ID, + orderBy="startTime", + singleEvents=True, + timeMin=lower_bound.isoformat(), + timeMax=upper_bound.isoformat() + ).execute() + elif lower_bound: + events_result = service.events().list( + calendarId=CALENDAR_ID, + orderBy="startTime", + singleEvents=True, + timeMin=lower_bound.isoformat() + ).execute() + elif upper_bound: + events_result = service.events().list( + calendarId=CALENDAR_ID, + orderBy="startTime", + singleEvents=True, + timeMax=upper_bound.isoformat() + ).execute() + else: + events_result = service.events().list( + calendarId=CALENDAR_ID, + orderBy="startTime", + singleEvents=True + ).execute() + + events = events_result.get('items', []) + + return events + + +@app.post('/calendar_events') +def create_calendar_event(calendar_event: CalendarEvent, service: Resource = Depends(get_calendar_service)): + # Call the Calendar API + now = datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time + print(calendar_event.dict(exclude_unset=True)) + events_result = service.events().insert(calendarId=CALENDAR_ID, body=calendar_event.dict(exclude_unset=True)).execute() + + return calendar_event + + +@app.post('/calendar_events/create_slots', response_model=List[CalendarEvent]) +def create_calendar_slots(datetime_start: datetime, timezone: str, slot_length_minutes: int, count: int, service: Resource = Depends(get_calendar_service)): + # Call the Calendar API + events = [] + batch = service.new_batch_http_request() + for i in range(count): + event = (CalendarEvent( + start=CalendarDate( + dateTime=(datetime_start + timedelta(minutes=slot_length_minutes*i)).isoformat(), + timeZone=timezone + ), + end=CalendarDate( + dateTime=(datetime_start + timedelta(minutes=slot_length_minutes * i) + timedelta(minutes=slot_length_minutes)).isoformat(), + timeZone=timezone + ), + colorId='10', + summary='Запись свободна' + )) + batch.add(service.events().insert(calendarId=CALENDAR_ID, body=event.dict(exclude_unset=True))) + events.append(event) + + # events_result = service.events().insert(calendarId=CALENDAR_ID, body=event.dict(exclude_unset=True)).execute() + + batch.execute() + return events + + +@app.post('/calendar_events/mark_busy', response_model=CalendarEvent) +def mark_busy_calendar_slot( + description: str | None = None, + lower_bound: datetime = None, + upper_bound: datetime = None, + service: Resource = Depends(get_calendar_service) +): + events_dict = get_calendar_events(lower_bound=lower_bound, upper_bound=upper_bound, service=service) + events = list(map(lambda x: CalendarEvent(**x), events_dict)) + + free_slots = list(filter(lambda x: x.summary == TITLE_FREE if x.summary else False, events)) + + if not free_slots: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Free slot not found") + + event = free_slots[0] + event.summary = TITLE_BUSY + event.colorId = COLOR_BUSY + if description: + event.description = description + + service.events().update(calendarId=CALENDAR_ID, eventId=event.id, body=event.dict(exclude_unset=True)).execute() + + return event + + +@app.post('/calendar_events/mark_free', response_model=CalendarEvent) +def mark_free_calendar_slot( + lower_bound: datetime = None, + upper_bound: datetime = None, + service: Resource = Depends(get_calendar_service) +): + events_dict = get_calendar_events(lower_bound=lower_bound, upper_bound=upper_bound, service=service) + events = list(map(lambda x: CalendarEvent(**x), events_dict)) + + busy_slots = list(filter(lambda x: x.summary == TITLE_BUSY if x.summary else False, events)) + + if not busy_slots: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Busy slot not found") + + event = busy_slots[0] + event.summary = TITLE_FREE + event.colorId = COLOR_FREE + + service.events().update(calendarId=CALENDAR_ID, eventId=event.id, body=event.dict(exclude_unset=True)).execute() + + return event + + +@app.post('/calendar_events/mark_free/batch', response_model=List[CalendarEvent]) +def mark_free_calendar_slots( + lower_bound: datetime = None, + upper_bound: datetime = None, + service: Resource = Depends(get_calendar_service) +): + events_dict = get_calendar_events(lower_bound=lower_bound, upper_bound=upper_bound, service=service) + events = list(map(lambda x: CalendarEvent(**x), events_dict)) + + busy_slots = list(filter(lambda x: x.summary == TITLE_BUSY if x.summary else False, events)) + + if not busy_slots: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Busy slot not found") + + batch = service.new_batch_http_request() + + for event in busy_slots: + event.summary = TITLE_FREE + event.colorId = COLOR_FREE + event.description = '' + batch.add(service.events().update(calendarId=CALENDAR_ID, eventId=event.id, body=event.dict(exclude_unset=True))) + + batch.execute() + + return busy_slots diff --git a/src/routes.py b/src/routes.py index b18e09c..0858518 100644 --- a/src/routes.py +++ b/src/routes.py @@ -7,4 +7,4 @@ from blog.router import router as blog_router main_router = APIRouter() # main_router.include_router(router=users_router) # main_router.include_router(router=products_router) -main_router.include_router(router=blog_router) +# main_router.include_router(router=blog_router) diff --git a/src/token.json b/src/token.json new file mode 100644 index 0000000..527cb43 --- /dev/null +++ b/src/token.json @@ -0,0 +1,13 @@ +{ + "type": "service_account", + "project_id": "calendar-test-390701", + "private_key_id": "0c0c4ccf5e4c772638d12d13e60b58538f3341ba", + "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCuhvclYmgqFCtV\n7ePXy/UrYeej4ny8ty7+iSXF29NRfNOzizdRZkpJuwr1E6PLM+u0SNGrWiHDtNGO\nryh+BaV4Go/+KnqjuH4Tf7ZV9OoySxEvm6dDXw2EXuQCSPTbdA8/WcN9y80mc7JW\nvQKRjcbN/JANq1EtgZGRQY4E4fUDvxezRBw8j7w1y6FmpAJQUl9RE/45L9w7cP1X\nhF2K5Vibgl7fn/oScaOJjxttoz7noLxC7HPxNfQharU9B3e7LBJ86rnDt7g+X2SJ\nrdW7DXLE54Oj4sgNPlCwAivxRkdQQbn8Q/STXd5LzSeVaAYj6Ykv87un60xfp+nn\n/87PXl7ZAgMBAAECggEAHGtDxqWjOpQ1iPTcxgtN1+DwZuz+zNKzZ0/j2Q0g+ELw\nTMCITcvvStmXQNhlh+A41bmJ0seGz1HGpzT5zf4+DImewDogkD+piqP1h+0CpGqO\n2NwVZ3th6G0fNF626HPvS6yNOxMQS95JFaYKILN1F55fGAAOju9rSbuNVFmK8S9M\nDcDEha/NAChT7xFkmv4BcCl79CFvlrImHd/HMIkyCPUyjh8OTaehcnJK/A5DuLn+\n+mcXKLlhkBST4Bq/2fLfWxSqO++O3L0Jc+3ErswQEjc5kqnDyZPPFwjkP8BvIsM4\nbOgIqpXL5EraOZ1DomMKBDKF9tE4JaqVGzDWQ+sKNQKBgQDjLbJJfxqQBGDxfmu+\nBhggryEfe0n9qnvvbZtxwb+IXKkmDDppRZHfK8qjct5FmO1QCpKyLOrGtC7YzxK7\nWOswtPEK70QBZCE5hER/lymq7aiqOoG/tO94WiB0O5xtCWICFv9LoSUc4hBJyBHl\ntneHtg6hHGaeE216ziPk2QLbSwKBgQDEq0KfKU2rr6+IXTiaOXPlMtGUM7SSPSWW\njZh8Nuk4XGvMBWgZJrzQIH2MLX/qYvpnkJJVONupr3SWZ+2GOXSHyt6VhflJ20LC\n5Q+zJ6+j5iKq5GKhCH24FRSdbS9gcwExsw6FAx1bXuiyjBNFpHemQdotS8ikUC1J\nHcM38pmT6wKBgQDgOL37ZIQnGOtF720QBJvTxoZ+uATzZU0lp50AziXoxOv+LZuJ\nQ60SA/rhEALp1mKD2auoSEUBEd4bB2aJmt2nGS+/hyAr71QLnMyu0dfCaQdL3Ksr\nN74Tn4Dgt+VQiS4+ucwILWif9U/2Qo3HmCypubr+sTVnV+16g/NRcA8tuQKBgQCE\nTZHr8KVHslCrs8q4NZ45ug4zXPVEy83VkjvalDYgrEv4XErDYMP0X2cWPiWnUB6U\nPTW0DyP26wnLhO7NNSmnkFpczFv/y3kGE4KUS0ZlRBReUnXnTwcEJ2pO3XzZrYRI\nIuTJpmbBaP+x3Xm5iALV3ccgYbMAls9gOJ1zrciTMQKBgQCHVOOFmhHSMTyqXdeM\nnjHGafUfJFHCDSt4+QPq2a2G59q1bEANTtvVuq+v0xgfvqImVXqtHKRUC5oP7wXk\nQcecRaLhjxNdXhaZwycNi5wggIy64PVF835isrdOQ3N6oJ3JqDRaBSa37xbyEj+H\ntlTKoos6HgXYuRRLoLdxtpyntw==\n-----END PRIVATE KEY-----\n", + "client_email": "calendar-app@calendar-test-390701.iam.gserviceaccount.com", + "client_id": "105991143927033285509", + "auth_uri": "https://accounts.google.com/o/oauth2/auth", + "token_uri": "https://oauth2.googleapis.com/token", + "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", + "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/calendar-app%40calendar-test-390701.iam.gserviceaccount.com", + "universe_domain": "googleapis.com" +}