From 167c38c246bfbfd93b0212992a69f9e3b5927f09 Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Sat, 18 Nov 2023 19:21:03 -0500 Subject: [PATCH 01/14] Update dev.py --- backend/src/subscriber/routers/dev.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/backend/src/subscriber/routers/dev.py b/backend/src/subscriber/routers/dev.py index e55d9897..0f0e3e35 100644 --- a/backend/src/subscriber/routers/dev.py +++ b/backend/src/subscriber/routers/dev.py @@ -1,7 +1,8 @@ from datetime import date, timedelta from typing import Any, Dict, Optional -from fastapi import APIRouter, Response, status +import asyncio +from fastapi import APIRouter, BackgroundTasks, Response, status from src.data.mongo.secret import update_keys from src.models import UserPackage, WrappedPackage @@ -47,3 +48,16 @@ async def get_wrapped_user_raw( user_id, date(year, 1, 1), date(year, 12, 31), "US/Eastern", access_token ) return get_wrapped_data(user_data, year) + + +async def print_task(start_str: str, end_str: str): + print(start_str) + await asyncio.sleep(10) + print(end_str) + + +@router.get("/test", status_code=status.HTTP_200_OK, response_model=Dict[str, Any]) +@async_fail_gracefully +async def test(response: Response, background_tasks: BackgroundTasks) -> str: + background_tasks.add_task(print_task, "start", "end") + return "Test" From 67bd78c979bbf06eee4dba75eba03d95eff4f58f Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Mon, 20 Nov 2023 21:41:39 -0500 Subject: [PATCH 02/14] Fix alru cache --- backend/delete_old_data.py | 2 +- backend/src/constants.py | 6 -- backend/src/data/github/auth/main.py | 2 +- backend/src/data/github/graphql/template.py | 6 +- backend/src/data/github/rest/template.py | 2 +- backend/src/data/mongo/main.py | 6 +- backend/src/data/mongo/secret/functions.py | 12 ++-- backend/src/data/mongo/user/functions.py | 8 +-- backend/src/data/mongo/user/get.py | 24 +++---- backend/src/data/mongo/user/models.py | 2 +- .../src/data/mongo/user_months/functions.py | 4 +- backend/src/data/mongo/user_months/get.py | 2 +- backend/src/main.py | 52 ++------------ backend/src/publisher/processing/__init__.py | 2 - backend/src/publisher/processing/pubsub.py | 23 ------ .../src/publisher/processing/user/get_data.py | 19 +++-- backend/src/publisher/routers/__init__.py | 8 +-- .../src/publisher/routers/auth/standalone.py | 2 +- backend/src/publisher/routers/pubsub.py | 21 ------ .../src/subscriber/aggregation/auth/auth.py | 6 +- .../subscriber/aggregation/user/languages.py | 2 +- .../aggregation/wrapped/calendar.py | 8 +-- backend/src/subscriber/processing/auth.py | 11 +-- backend/src/subscriber/processing/user.py | 12 ++-- backend/src/subscriber/processing/wrapped.py | 6 +- backend/src/subscriber/routers/__init__.py | 3 +- backend/src/subscriber/routers/dev.py | 9 +-- backend/src/subscriber/routers/pubsub.py | 23 ------ backend/src/subscriber/routers/wrapped.py | 13 ++-- backend/src/utils/alru_cache.py | 33 ++++++--- backend/src/utils/pubsub.py | 70 ------------------- backend/tests/__init__.py | 2 +- backend/tests/utils/test_alru_cache.py | 21 +++--- 33 files changed, 117 insertions(+), 305 deletions(-) delete mode 100644 backend/src/publisher/processing/pubsub.py delete mode 100644 backend/src/publisher/routers/pubsub.py delete mode 100644 backend/src/subscriber/routers/pubsub.py delete mode 100644 backend/src/utils/pubsub.py diff --git a/backend/delete_old_data.py b/backend/delete_old_data.py index 15ed6ba2..5e7db095 100755 --- a/backend/delete_old_data.py +++ b/backend/delete_old_data.py @@ -29,7 +29,7 @@ async def count_old_rows(cutoff_date: datetime) -> int: async def delete_old_rows(cutoff_date: datetime): filters = get_filters(cutoff_date) - result = await USER_MONTHS.delete_many(filters) # type: ignore + result = await USER_MONTHS.delete_many(filters) print(f"Deleted {result.deleted_count} rows") diff --git a/backend/src/constants.py b/backend/src/constants.py index c325367e..02c1c49c 100644 --- a/backend/src/constants.py +++ b/backend/src/constants.py @@ -33,12 +33,6 @@ OAUTH_CLIENT_SECRET = os.getenv("OAUTH_CLIENT_SECRET", "") # client secret for App OAUTH_REDIRECT_URI = os.getenv("OAUTH_REDIRECT_URI", "") # redirect uri for App -# PUBSUB -PUBSUB_TOKEN = os.getenv("PUBSUB_TOKEN", "") -# LOCAL_SUBSCRIBER = "http://" + ("subscriber" if DOCKER else "localhost") + ":8001" -LOCAL_SUBSCRIBER = "http://backend:8000" if DOCKER else BACKEND_URL -LOCAL_PUBLISHER = "http://backend:8000" if DOCKER else BACKEND_URL - # MONGODB MONGODB_PASSWORD = os.getenv("MONGODB_PASSWORD", "") diff --git a/backend/src/data/github/auth/main.py b/backend/src/data/github/auth/main.py index f199bc62..175a476e 100644 --- a/backend/src/data/github/auth/main.py +++ b/backend/src/data/github/auth/main.py @@ -20,7 +20,7 @@ def get_unknown_user(access_token: str) -> Optional[str]: } r = s.get("https://api.github.com/user", params={}, headers=headers) - return r.json().get("login", None) # type: ignore + return r.json().get("login", None) class OAuthError(Exception): diff --git a/backend/src/data/github/graphql/template.py b/backend/src/data/github/graphql/template.py index 027fc65d..45b924a7 100644 --- a/backend/src/data/github/graphql/template.py +++ b/backend/src/data/github/graphql/template.py @@ -44,7 +44,7 @@ def get_template( headers: Dict[str, str] = {"Authorization": f"bearer {new_access_token}"} try: - r = s.post( # type: ignore + r = s.post( "https://api.github.com/graphql", json=query, headers=headers, @@ -55,7 +55,7 @@ def get_template( print("GraphQL", new_access_token, datetime.now() - start) if r.status_code == 200: - data = r.json() # type: ignore + data = r.json() if "errors" in data: if ( "type" in data["errors"][0] @@ -64,7 +64,7 @@ def get_template( and isinstance(data["errors"][0]["path"], list) and data["errors"][0]["path"][0] == "nodes" ): - raise GraphQLErrorMissingNode(node=int(data["errors"][0]["path"][1])) # type: ignore + raise GraphQLErrorMissingNode(node=int(data["errors"][0]["path"][1])) if retries < 2: print("GraphQL Error, Retrying:", new_access_token) diff --git a/backend/src/data/github/rest/template.py b/backend/src/data/github/rest/template.py index 6fd1aaf8..b82185cd 100644 --- a/backend/src/data/github/rest/template.py +++ b/backend/src/data/github/rest/template.py @@ -60,7 +60,7 @@ def _get_template( if r.status_code == 200: print("REST API", new_access_token, datetime.now() - start) - return r.json() # type: ignore + return r.json() if r.status_code == 401: raise RESTErrorUnauthorized("REST Error: Unauthorized") diff --git a/backend/src/data/mongo/main.py b/backend/src/data/mongo/main.py index 8b779873..f950fc99 100644 --- a/backend/src/data/mongo/main.py +++ b/backend/src/data/mongo/main.py @@ -1,5 +1,3 @@ -# type: ignore - from motor.motor_asyncio import AsyncIOMotorClient from src.constants import MONGODB_PASSWORD, PROD @@ -17,10 +15,10 @@ def get_conn_str(password: str, database: str) -> str: DB = CLIENT.prod_backend else: conn_str = get_conn_str(MONGODB_PASSWORD, "dev_backend") - CLIENT = AsyncIOMotorClient( + CLIENT = AsyncIOMotorClient( # type: ignore conn_str, serverSelectionTimeoutMS=5000, tlsInsecure=True ) - DB = CLIENT.dev_backend + DB = CLIENT.dev_backend # type: ignore SECRETS = DB.secrets diff --git a/backend/src/data/mongo/secret/functions.py b/backend/src/data/mongo/secret/functions.py index f0602e40..4fe078e6 100644 --- a/backend/src/data/mongo/secret/functions.py +++ b/backend/src/data/mongo/secret/functions.py @@ -1,21 +1,21 @@ from datetime import timedelta from random import randint -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Tuple from src.constants import TEST_TOKEN -from src.data.mongo.main import SECRETS # type: ignore +from src.data.mongo.main import SECRETS from src.data.mongo.secret.models import SecretModel from src.utils import alru_cache @alru_cache(ttl=timedelta(minutes=15)) -async def get_keys(no_cache: bool = False) -> List[str]: - secrets: Optional[Dict[str, Any]] = await SECRETS.find_one({"project": "main"}) # type: ignore +async def get_keys(no_cache: bool = False) -> Tuple[bool, List[str]]: + secrets: Optional[Dict[str, Any]] = await SECRETS.find_one({"project": "main"}) if secrets is None: - return (False, []) # type: ignore + return (False, []) tokens = SecretModel.model_validate(secrets).access_tokens - return (True, tokens) # type: ignore + return (True, tokens) secret_keys: List[str] = [] diff --git a/backend/src/data/mongo/user/functions.py b/backend/src/data/mongo/user/functions.py index e7be05e0..74b7f7fa 100644 --- a/backend/src/data/mongo/user/functions.py +++ b/backend/src/data/mongo/user/functions.py @@ -1,17 +1,17 @@ from typing import Any, Dict, Optional -from src.data.mongo.main import USERS # type: ignore +from src.data.mongo.main import USERS async def is_user_key(user_id: str, user_key: str) -> bool: - user: Optional[dict[str, str]] = await USERS.find_one( # type: ignore + user: Optional[dict[str, str]] = await USERS.find_one( {"user_id": user_id}, {"user_key": 1} ) return user is not None and user.get("user_key", "") == user_key async def update_user(user_id: str, raw_user: Dict[str, Any]) -> None: - await USERS.update_one( # type: ignore + await USERS.update_one( {"user_id": user_id}, {"$set": raw_user}, upsert=True, @@ -24,5 +24,5 @@ async def delete_user(user_id: str, user_key: str, use_user_key: bool = True) -> if not is_key: return False - await USERS.delete_one({"user_id": user_id}) # type: ignore + await USERS.delete_one({"user_id": user_id}) return True diff --git a/backend/src/data/mongo/user/get.py b/backend/src/data/mongo/user/get.py index d00b2f06..8a59f7c0 100644 --- a/backend/src/data/mongo/user/get.py +++ b/backend/src/data/mongo/user/get.py @@ -1,8 +1,8 @@ -from typing import Any, Dict, Optional +from typing import Any, Dict, Optional, Tuple from pydantic import ValidationError -from src.data.mongo.main import USERS # type: ignore +from src.data.mongo.main import USERS from src.data.mongo.user.models import FullUserModel, PublicUserModel from src.utils import alru_cache @@ -10,30 +10,30 @@ @alru_cache() async def get_public_user( user_id: str, no_cache: bool = False -) -> Optional[PublicUserModel]: - user: Optional[Dict[str, Any]] = await USERS.find_one({"user_id": user_id}) # type: ignore +) -> Tuple[bool, Optional[PublicUserModel]]: + user: Optional[Dict[str, Any]] = await USERS.find_one({"user_id": user_id}) if user is None: # flag is false, don't cache - return (False, None) # type: ignore + return (False, None) try: - return (True, PublicUserModel.parse_obj(user)) # type: ignore + return (True, PublicUserModel.model_validate(user)) except (TypeError, KeyError, ValidationError): - return (False, None) # type: ignore + return (False, None) @alru_cache() async def get_full_user( user_id: str, no_cache: bool = False -) -> Optional[FullUserModel]: - user: Optional[Dict[str, Any]] = await USERS.find_one({"user_id": user_id}) # type: ignore +) -> Tuple[bool, Optional[FullUserModel]]: + user: Optional[Dict[str, Any]] = await USERS.find_one({"user_id": user_id}) if user is None: # flag is false, don't cache - return (False, None) # type: ignore + return (False, None) try: - return (True, FullUserModel.parse_obj(user)) # type: ignore + return (True, FullUserModel.model_validate(user)) except (TypeError, KeyError, ValidationError): - return (False, None) # type: ignore + return (False, None) diff --git a/backend/src/data/mongo/user/models.py b/backend/src/data/mongo/user/models.py index f10602c2..cf9746a3 100644 --- a/backend/src/data/mongo/user/models.py +++ b/backend/src/data/mongo/user/models.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel, validator # type: ignore +from pydantic import BaseModel, validator class PublicUserModel(BaseModel): diff --git a/backend/src/data/mongo/user_months/functions.py b/backend/src/data/mongo/user_months/functions.py index 103bd360..78f97667 100644 --- a/backend/src/data/mongo/user_months/functions.py +++ b/backend/src/data/mongo/user_months/functions.py @@ -1,4 +1,4 @@ -from src.data.mongo.main import USER_MONTHS # type: ignore +from src.data.mongo.main import USER_MONTHS from src.data.mongo.user_months.models import UserMonth @@ -6,7 +6,7 @@ async def set_user_month(user_month: UserMonth): compressed_user_month = user_month.model_dump() compressed_user_month["data"] = user_month.data.compress() - await USER_MONTHS.update_one( # type: ignore + await USER_MONTHS.update_one( {"user_id": user_month.user_id, "month": user_month.month}, {"$set": compressed_user_month}, upsert=True, diff --git a/backend/src/data/mongo/user_months/get.py b/backend/src/data/mongo/user_months/get.py index 05647449..d1f4fe96 100644 --- a/backend/src/data/mongo/user_months/get.py +++ b/backend/src/data/mongo/user_months/get.py @@ -2,7 +2,7 @@ from typing import Any, Dict, List from src.constants import API_VERSION -from src.data.mongo.main import USER_MONTHS # type: ignore +from src.data.mongo.main import USER_MONTHS from src.data.mongo.user_months.models import UserMonth from src.models import UserPackage diff --git a/backend/src/main.py b/backend/src/main.py index e1ed0cfd..f42d61d6 100644 --- a/backend/src/main.py +++ b/backend/src/main.py @@ -1,10 +1,9 @@ from typing import Dict import sentry_sdk -from dotenv import find_dotenv, load_dotenv # type: ignore +from dotenv import find_dotenv, load_dotenv from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from google.api_core.exceptions import AlreadyExists from sentry_sdk.integrations.asgi import SentryAsgiMiddleware load_dotenv(find_dotenv()) @@ -12,49 +11,10 @@ # flake8: noqa E402 # add endpoints here (after load dotenv) -from src.constants import ( - DOCKER, - LOCAL_SUBSCRIBER, - PROD, - PROJECT_ID, - PUBSUB_TOKEN, - SENTRY_DSN, -) -from src.publisher.routers import ( - asset_router, - auth_router, - pubsub_router as pub_router, - user_router, -) -from src.subscriber.routers import ( - dev_router, - pubsub_router as sub_router, - wrapped_router, -) -from src.utils.pubsub import create_push_subscription, create_topic - -""" -EMULATOR SETUP -""" - - -if not PROD and DOCKER: - topics = ["user"] - subscriptions = ["user_sub"] - endpoints = [f"{LOCAL_SUBSCRIBER}/pubsub/sub/user/{PUBSUB_TOKEN}"] - - for topic, subscription, endpoint in zip(topics, subscriptions, endpoints): - try: - print("Creating Topic", PROJECT_ID, topic) - create_topic(PROJECT_ID, topic) - except AlreadyExists: - print("Topic Already Exists") +from src.constants import PROD, SENTRY_DSN +from src.publisher.routers import asset_router, auth_router, user_router +from src.subscriber.routers import dev_router, wrapped_router - try: - print("Creating Subscription", PROJECT_ID, topic, subscription, endpoint) - create_push_subscription(PROJECT_ID, topic, subscription, endpoint) - except AlreadyExists: - print("Subscription already exists") """ SETUP @@ -96,13 +56,9 @@ def get_info() -> Dict[str, bool]: return {"PROD": PROD} -# (Originally Publisher) app.include_router(user_router, prefix="/user", tags=["Users"]) -app.include_router(pub_router, prefix="/pubsub", tags=["PubSub"]) app.include_router(auth_router, prefix="/auth", tags=["Auth"]) app.include_router(asset_router, prefix="/assets", tags=["Assets"]) -# (Originally Subscriber) app.include_router(dev_router, prefix="/dev", tags=["Dev"]) -app.include_router(sub_router, prefix="/pubsub", tags=["PubSub"]) app.include_router(wrapped_router, prefix="/wrapped", tags=["Wrapped"]) diff --git a/backend/src/publisher/processing/__init__.py b/backend/src/publisher/processing/__init__.py index 4efbb45e..c952c06f 100644 --- a/backend/src/publisher/processing/__init__.py +++ b/backend/src/publisher/processing/__init__.py @@ -1,4 +1,3 @@ -from src.publisher.processing.pubsub import publish_user from src.publisher.processing.user.auth import authenticate, delete_user, set_user_key from src.publisher.processing.user.get_data import get_user, get_user_demo from src.publisher.processing.user.svg import svg_base @@ -10,5 +9,4 @@ "get_user", "get_user_demo", "svg_base", - "publish_user", ] diff --git a/backend/src/publisher/processing/pubsub.py b/backend/src/publisher/processing/pubsub.py deleted file mode 100644 index 0de74d5c..00000000 --- a/backend/src/publisher/processing/pubsub.py +++ /dev/null @@ -1,23 +0,0 @@ -from typing import Optional - -from src.constants import DOCKER, PROD -from src.subscriber.processing import query_user -from src.utils.pubsub import publish_to_topic - - -async def publish_user( - user_id: str, - access_token: Optional[str] = None, - private_access: bool = False, -): - if PROD or DOCKER: - publish_to_topic( - topic="user", - message={ - "user_id": user_id, - "access_token": access_token, - "private_access": private_access, - }, - ) - else: - return await query_user(user_id, access_token, private_access) diff --git a/backend/src/publisher/processing/user/get_data.py b/backend/src/publisher/processing/user/get_data.py index 0152b06b..eeed6530 100644 --- a/backend/src/publisher/processing/user/get_data.py +++ b/backend/src/publisher/processing/user/get_data.py @@ -1,11 +1,10 @@ from datetime import date, timedelta -from typing import Optional +from typing import Optional, Tuple from src.data.mongo.secret.functions import update_keys from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user from src.data.mongo.user_months import get_user_months from src.models import UserPackage -from src.publisher.processing.pubsub import publish_user # TODO: replace with call to subscriber so compute not on publisher from src.subscriber.aggregation import get_user_data @@ -15,10 +14,10 @@ @alru_cache() async def update_user( user_id: str, access_token: Optional[str], private_access: bool -) -> bool: +) -> Tuple[bool, bool]: """Sends a message to pubsub to request a user update (auto cache updates)""" - await publish_user(user_id, access_token, private_access) - return (True, True) # type: ignore + # await publish_user(user_id, access_token, private_access) + return (True, True) async def _get_user( @@ -42,21 +41,21 @@ async def get_user( start_date: date, end_date: date, no_cache: bool = False, -) -> Optional[UserPackage]: +) -> Tuple[bool, Optional[UserPackage]]: user: Optional[PublicUserModel] = await db_get_public_user(user_id) if user is None: - return (False, None) # type: ignore + return (False, None) private_access = user.private_access or False await update_user(user_id, user.access_token, private_access) user_data = await _get_user(user_id, private_access, start_date, end_date) - return (user_data is not None, user_data) # type: ignore + return (user_data is not None, user_data) @alru_cache(ttl=timedelta(minutes=15)) async def get_user_demo( user_id: str, start_date: date, end_date: date, no_cache: bool = False -) -> UserPackage: +) -> Tuple[bool, UserPackage]: await update_keys() timezone_str = "US/Eastern" data = await get_user_data( @@ -67,4 +66,4 @@ async def get_user_demo( access_token=None, catch_errors=True, ) - return (True, data) # type: ignore + return (True, data) diff --git a/backend/src/publisher/routers/__init__.py b/backend/src/publisher/routers/__init__.py index cd4e1824..fbf4be5f 100644 --- a/backend/src/publisher/routers/__init__.py +++ b/backend/src/publisher/routers/__init__.py @@ -1,11 +1,5 @@ from src.publisher.routers.assets.assets import router as asset_router from src.publisher.routers.auth.main import router as auth_router -from src.publisher.routers.pubsub import router as pubsub_router from src.publisher.routers.users.main import router as user_router -__all__ = [ - "asset_router", - "user_router", - "auth_router", - "pubsub_router", -] +__all__ = ["asset_router", "user_router", "auth_router"] diff --git a/backend/src/publisher/routers/auth/standalone.py b/backend/src/publisher/routers/auth/standalone.py index 215b07f3..ef875e93 100644 --- a/backend/src/publisher/routers/auth/standalone.py +++ b/backend/src/publisher/routers/auth/standalone.py @@ -24,7 +24,7 @@ def redirect_private(user_id: Optional[str] = None) -> RedirectResponse: @router.get("/redirect", include_in_schema=False) async def redirect_return(code: str = "", private_access: bool = False) -> str: try: - user_id = await authenticate(code=code, private_access=private_access) # type: ignore + user_id = await authenticate(code=code, private_access=private_access) return f"You ({user_id}) are now authenticated!" except Exception as e: logging.exception(e) diff --git a/backend/src/publisher/routers/pubsub.py b/backend/src/publisher/routers/pubsub.py deleted file mode 100644 index 2348ae1c..00000000 --- a/backend/src/publisher/routers/pubsub.py +++ /dev/null @@ -1,21 +0,0 @@ -from typing import Optional - -from fastapi import APIRouter, Response, status - -from src.publisher.processing import publish_user -from src.utils import fail_gracefully - -router = APIRouter() - - -@router.get( - "/pub/user/{user_id}/{access_token}", - status_code=status.HTTP_200_OK, - include_in_schema=False, -) -@fail_gracefully -async def pub_user( - response: Response, user_id: str, access_token: Optional[str] -) -> str: - await publish_user(user_id, access_token) - return user_id diff --git a/backend/src/subscriber/aggregation/auth/auth.py b/backend/src/subscriber/aggregation/auth/auth.py index fddc1ff6..3274f308 100644 --- a/backend/src/subscriber/aggregation/auth/auth.py +++ b/backend/src/subscriber/aggregation/auth/auth.py @@ -1,5 +1,5 @@ from datetime import timedelta -from typing import List +from typing import List, Tuple from src.constants import OWNER, REPO from src.data.github.rest import ( @@ -35,7 +35,7 @@ async def get_valid_db_user(user_id: str) -> bool: @alru_cache(ttl=timedelta(minutes=15)) async def get_repo_stargazers( owner: str = OWNER, repo: str = REPO, no_cache: bool = False -) -> List[str]: +) -> Tuple[bool, List[str]]: access_token = get_access_token() data: List[str] = [] page = 0 @@ -45,7 +45,7 @@ async def get_repo_stargazers( data.extend(temp_data) page += 1 - return (True, data) # type: ignore + return (True, data) async def get_user_stars(user_id: str) -> List[str]: diff --git a/backend/src/subscriber/aggregation/user/languages.py b/backend/src/subscriber/aggregation/user/languages.py index bb3a37c4..eeb5ec51 100644 --- a/backend/src/subscriber/aggregation/user/languages.py +++ b/backend/src/subscriber/aggregation/user/languages.py @@ -27,7 +27,7 @@ def add_lines( if name not in self.langs: self.langs[name] = {"color": color, "additions": 0, "deletions": 0} self.langs[name]["additions"] += additions # type: ignore - self.langs[name]["deletions"] += deletions # type: ignore + self.langs[name]["deletions"] += deletions def normalize(self, add_ratio: float, del_ratio: float): for lang in self.langs: diff --git a/backend/src/subscriber/aggregation/wrapped/calendar.py b/backend/src/subscriber/aggregation/wrapped/calendar.py index b7c3776c..a0905a7a 100644 --- a/backend/src/subscriber/aggregation/wrapped/calendar.py +++ b/backend/src/subscriber/aggregation/wrapped/calendar.py @@ -40,10 +40,10 @@ def get_calendar_data(data: UserPackage, year: int) -> CalendarData: for k, v in item.stats.languages.items(): if k in top_langs: - out["top_langs"][k]["loc_added"] = v.additions - v.deletions # type: ignore - out["top_langs"][k]["loc_changed"] = v.additions + v.deletions # type: ignore - out["loc_added"] += v.additions - v.deletions # type: ignore - out["loc_changed"] += v.additions + v.deletions # type: ignore + out["top_langs"][k]["loc_added"] = v.additions - v.deletions + out["top_langs"][k]["loc_changed"] = v.additions + v.deletions + out["loc_added"] += v.additions - v.deletions + out["loc_changed"] += v.additions + v.deletions out_obj = CalendarDayDatum.model_validate(out) total_out.append(out_obj) diff --git a/backend/src/subscriber/processing/auth.py b/backend/src/subscriber/processing/auth.py index e24c54c6..33bcefff 100644 --- a/backend/src/subscriber/processing/auth.py +++ b/backend/src/subscriber/processing/auth.py @@ -1,4 +1,5 @@ from datetime import timedelta +from typing import Tuple from src.constants import OWNER, REPO from src.data.github.rest import RESTError @@ -43,17 +44,17 @@ async def check_user_starred_repo( @alru_cache(ttl=timedelta(hours=1)) -async def get_is_valid_user(user_id: str) -> str: +async def get_is_valid_user(user_id: str) -> Tuple[bool, str]: if user_id in USER_WHITELIST: - return (True, "Valid user") # type: ignore + return (True, "Valid user") valid_github_user = await check_github_user_exists(user_id) if not valid_github_user: - return (False, "GitHub user not found") # type: ignore + return (False, "GitHub user not found") valid_db_user = await check_db_user_exists(user_id) user_starred = await check_user_starred_repo(user_id) if not (user_starred or valid_db_user): - return (False, "Repo not starred") # type: ignore + return (False, "Repo not starred") - return (True, "Valid user") # type: ignore + return (True, "Valid user") diff --git a/backend/src/subscriber/processing/user.py b/backend/src/subscriber/processing/user.py index 222db58e..435765ce 100644 --- a/backend/src/subscriber/processing/user.py +++ b/backend/src/subscriber/processing/user.py @@ -1,10 +1,10 @@ from calendar import monthrange from datetime import date, datetime, timedelta -from typing import List, Optional +from typing import List, Optional, Tuple import requests -from src.constants import API_VERSION, BACKEND_URL, DOCKER, LOCAL_PUBLISHER, PROD +from src.constants import API_VERSION, BACKEND_URL, PROD from src.data.github.graphql import GraphQLErrorRateLimit from src.data.mongo.secret import update_keys from src.data.mongo.user_months import UserMonth, get_user_months, set_user_month @@ -72,7 +72,7 @@ async def query_user( start_date: date = date.today() - timedelta(365), end_date: date = date.today(), no_cache: bool = False, -) -> UserPackage: +) -> Tuple[bool, UserPackage]: # Return (possibly incomplete) within 45 seconds start_time = datetime.now() incomplete = False @@ -116,10 +116,8 @@ async def query_user( # cache buster for publisher if PROD: s.get(f"{BACKEND_URL}/user/{user_id}?no_cache=True") - elif DOCKER: - s.get(f"{LOCAL_PUBLISHER}/user/{user_id}?no_cache=True") - return (False, out) # type: ignore + return (False, out) # only cache if just the current month updated - return (True, out) # type: ignore + return (True, out) diff --git a/backend/src/subscriber/processing/wrapped.py b/backend/src/subscriber/processing/wrapped.py index 09199f61..a3110989 100644 --- a/backend/src/subscriber/processing/wrapped.py +++ b/backend/src/subscriber/processing/wrapped.py @@ -1,5 +1,5 @@ from datetime import date, timedelta -from typing import Optional +from typing import Optional, Tuple from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user from src.models import UserPackage, WrappedPackage @@ -11,7 +11,7 @@ @alru_cache(ttl=timedelta(hours=12)) async def query_wrapped_user( user_id: str, year: int, no_cache: bool = False -) -> Optional[WrappedPackage]: +) -> Tuple[bool, Optional[WrappedPackage]]: start_date, end_date = date(year, 1, 1), date(year, 12, 31) user: Optional[PublicUserModel] = await db_get_public_user(user_id) access_token = None if user is None else user.access_token @@ -22,4 +22,4 @@ async def query_wrapped_user( wrapped_package = get_wrapped_data(user_package, year) # Don't cache if incomplete - return (not wrapped_package.incomplete, wrapped_package) # type: ignore + return (not wrapped_package.incomplete, wrapped_package) diff --git a/backend/src/subscriber/routers/__init__.py b/backend/src/subscriber/routers/__init__.py index fc60224b..c97f178e 100644 --- a/backend/src/subscriber/routers/__init__.py +++ b/backend/src/subscriber/routers/__init__.py @@ -1,5 +1,4 @@ from src.subscriber.routers.dev import router as dev_router -from src.subscriber.routers.pubsub import router as pubsub_router from src.subscriber.routers.wrapped import router as wrapped_router -__all__ = ["dev_router", "pubsub_router", "wrapped_router"] +__all__ = ["dev_router", "wrapped_router"] diff --git a/backend/src/subscriber/routers/dev.py b/backend/src/subscriber/routers/dev.py index 0f0e3e35..ed2a4c02 100644 --- a/backend/src/subscriber/routers/dev.py +++ b/backend/src/subscriber/routers/dev.py @@ -2,7 +2,7 @@ from typing import Any, Dict, Optional import asyncio -from fastapi import APIRouter, BackgroundTasks, Response, status +from fastapi import APIRouter, Response, status from src.data.mongo.secret import update_keys from src.models import UserPackage, WrappedPackage @@ -54,10 +54,3 @@ async def print_task(start_str: str, end_str: str): print(start_str) await asyncio.sleep(10) print(end_str) - - -@router.get("/test", status_code=status.HTTP_200_OK, response_model=Dict[str, Any]) -@async_fail_gracefully -async def test(response: Response, background_tasks: BackgroundTasks) -> str: - background_tasks.add_task(print_task, "start", "end") - return "Test" diff --git a/backend/src/subscriber/routers/pubsub.py b/backend/src/subscriber/routers/pubsub.py deleted file mode 100644 index b092ea07..00000000 --- a/backend/src/subscriber/routers/pubsub.py +++ /dev/null @@ -1,23 +0,0 @@ -from typing import Any, Dict - -from fastapi import APIRouter, Request, Response, status - -from src.subscriber.processing import query_user -from src.subscriber.routers.decorators import pubsub_fail_gracefully -from src.utils.pubsub import parse_request - -router = APIRouter() - -""" -USER PUBSUB -""" - - -@router.post( - "/sub/user/{token}", status_code=status.HTTP_200_OK, response_model=Dict[str, Any] -) -@pubsub_fail_gracefully -async def sub_user(response: Response, token: str, request: Request) -> Any: - data: Dict[str, Any] = await parse_request(token, request) - await query_user(data["user_id"], data["access_token"], data["private_access"]) - return data diff --git a/backend/src/subscriber/routers/wrapped.py b/backend/src/subscriber/routers/wrapped.py index 3a4145d7..9fd9c769 100644 --- a/backend/src/subscriber/routers/wrapped.py +++ b/backend/src/subscriber/routers/wrapped.py @@ -1,4 +1,4 @@ -from typing import Any, Dict +from typing import Any, Dict, Tuple, Optional from fastapi import APIRouter, Response, status @@ -13,19 +13,20 @@ "/valid/{user_id}", status_code=status.HTTP_200_OK, response_model=Dict[str, Any] ) @async_fail_gracefully -async def check_valid_user(response: Response, user_id: str) -> str: +async def check_valid_user(response: Response, user_id: str) -> Tuple[bool, str]: print("Checking valid user") out = await get_is_valid_user(user_id) print(out) - return out + return (True, out) @router.get("/{user_id}", status_code=status.HTTP_200_OK, response_model=Dict[str, Any]) @async_fail_gracefully async def get_wrapped_user( response: Response, user_id: str, year: int = 2022, no_cache: bool = False -) -> WrappedPackage: +) -> Tuple[bool, Optional[WrappedPackage]]: valid_user = await get_is_valid_user(user_id) if valid_user != "Valid user": - return WrappedPackage.empty() - return await query_wrapped_user(user_id, year, no_cache=no_cache) + return (False, WrappedPackage.empty()) + wrapped_object = await query_wrapped_user(user_id, year, no_cache=no_cache) + return (True, wrapped_object) diff --git a/backend/src/utils/alru_cache.py b/backend/src/utils/alru_cache.py index 84585dfd..c6b0305d 100644 --- a/backend/src/utils/alru_cache.py +++ b/backend/src/utils/alru_cache.py @@ -1,14 +1,31 @@ from datetime import datetime, timedelta from functools import wraps -from typing import Any, Callable, Dict, List, Tuple +from typing import ( + Any, + Awaitable, + Callable, + Dict, + FrozenSet, + List, + ParamSpec, + Tuple, + TypeVar, +) + +Param = ParamSpec("Param") +TOutput = TypeVar("TOutput") + +TKey = Tuple[Tuple[Any, ...], FrozenSet[Tuple[str, Any]]] def alru_cache(max_size: int = 128, ttl: timedelta = timedelta(minutes=1)): - def decorator(func: Callable[..., Any]) -> Callable[..., Any]: - cache: Dict[Any, Tuple[datetime, Any]] = {} - keys: List[Any] = [] + def decorator( + func: Callable[Param, Awaitable[Tuple[bool, TOutput]]] + ) -> Callable[Param, Awaitable[TOutput]]: + cache: Dict[TKey, Tuple[datetime, TOutput]] = {} + keys: List[TKey] = [] - def in_cache(key: Any) -> bool: + def in_cache(key: TKey) -> bool: # key not in cache if key not in cache: return False @@ -20,7 +37,7 @@ def in_cache(key: Any) -> bool: # key in cache and not expired return True - def update_cache_and_return(key: Any, flag: bool, value: Any) -> Any: + def update_cache_and_return(key: TKey, flag: bool, value: TOutput) -> TOutput: # if flag = False, do not update cache and return value if not flag: return value @@ -43,8 +60,8 @@ def update_cache_and_return(key: Any, flag: bool, value: Any) -> Any: return value # equal to cache[key][1] @wraps(func) - async def wrapper(*args: List[Any], **kwargs: Dict[str, Any]) -> Any: - key = tuple(args), frozenset( + async def wrapper(*args: Param.args, **kwargs: Param.kwargs) -> TOutput: + key: TKey = tuple(args), frozenset( [(k, v) for k, v in kwargs.items() if k not in ["no_cache"]] ) if "no_cache" in kwargs and kwargs["no_cache"]: diff --git a/backend/src/utils/pubsub.py b/backend/src/utils/pubsub.py deleted file mode 100644 index e288f97b..00000000 --- a/backend/src/utils/pubsub.py +++ /dev/null @@ -1,70 +0,0 @@ -import base64 -import json -from typing import Any, Dict - -from fastapi import HTTPException, Request -from google.cloud import pubsub_v1 # type: ignore - -from src.constants import DOCKER, PROD, PROJECT_ID, PUBSUB_TOKEN - -""" -EMULATOR FUNTIONS -""" - - -def create_topic(project_id: str, topic_id: str) -> None: - """Create a new Pub/Sub topic.""" - publisher = pubsub_v1.PublisherClient() - topic_path = publisher.topic_path(project_id, topic_id) # type: ignore - topic = publisher.create_topic(request={"name": topic_path}) # type: ignore - print(f"Created topic: {topic.name}") # type: ignore - - -def create_push_subscription( - project_id: str, topic_id: str, subscription_id: str, endpoint: str -) -> None: - """Create a new push subscription on the given topic.""" - publisher = pubsub_v1.PublisherClient() - subscriber = pubsub_v1.SubscriberClient() - topic_path = publisher.topic_path(project_id, topic_id) # type: ignore - sub_path = subscriber.subscription_path(project_id, subscription_id) # type: ignore - push_config = pubsub_v1.types.PushConfig(push_endpoint=endpoint) # type: ignore - - # Wrap the subscriber in a 'with' block to automatically call close() to - # close the underlying gRPC channel when done. - with subscriber: - request = {"name": sub_path, "topic": topic_path, "push_config": push_config} # type: ignore - subscription = subscriber.create_subscription(request=request) # type: ignore - - print(f"Push subscription created: {subscription}.") - print(f"Endpoint for subscription is: {endpoint}") - - -""" -APPLICATION FUNTIONS -""" - -publisher = pubsub_v1.PublisherClient() - - -def publish_to_topic(topic: str, message: Dict[str, Any]) -> None: - # Encode data - data = json.dumps(message).encode("utf-8") - - if PROD or DOCKER: - # Write to PubSub or emulator - topic_path: str = publisher.topic_path(PROJECT_ID, topic) # type: ignore - publisher.publish(topic_path, data=data) # type: ignore - else: - raise HTTPException(400, "Must be in production or docker") - - -async def parse_request(token: str, request: Request) -> Dict[str, Any]: - if token != PUBSUB_TOKEN: - raise HTTPException(400, "Invalid token") - - data = await request.json() - if PROD or DOCKER: - data = json.loads(base64.b64decode(data["message"]["data"])) - - return data diff --git a/backend/tests/__init__.py b/backend/tests/__init__.py index 4b93c20a..134bb6a6 100644 --- a/backend/tests/__init__.py +++ b/backend/tests/__init__.py @@ -1,3 +1,3 @@ -from dotenv import find_dotenv, load_dotenv # type: ignore +from dotenv import find_dotenv, load_dotenv load_dotenv(find_dotenv(), verbose=True) diff --git a/backend/tests/utils/test_alru_cache.py b/backend/tests/utils/test_alru_cache.py index 011ea4ec..b9dd4235 100644 --- a/backend/tests/utils/test_alru_cache.py +++ b/backend/tests/utils/test_alru_cache.py @@ -1,5 +1,6 @@ from asyncio import sleep from datetime import timedelta +from typing import Tuple from aiounittest.case import AsyncTestCase @@ -11,10 +12,10 @@ async def test_basic_alru_cache(self): count = 0 @alru_cache() - async def f(x: int) -> int: + async def f(x: int) -> Tuple[bool, int]: nonlocal count count += 1 - return (True, x) # type: ignore + return (True, x) assert count == 0 assert await f(1) == 1 @@ -32,10 +33,10 @@ async def test_alru_cache_with_flag(self): count = 0 @alru_cache() - async def f(x: int) -> int: + async def f(x: int) -> Tuple[bool, int]: nonlocal count count += 1 - return (count % 2 == 0, x) # type: ignore + return (count % 2 == 0, x) assert count == 0 assert await f(1) == 1 @@ -53,10 +54,10 @@ async def test_alru_cache_with_maxsize(self): count = 0 @alru_cache(max_size=2) - async def f(x: int) -> int: + async def f(x: int) -> Tuple[bool, int]: nonlocal count count += 1 - return (True, x) # type: ignore + return (True, x) assert count == 0 assert await f(1) == 1 @@ -72,10 +73,10 @@ async def test_alru_cache_with_ttl(self): count = 0 @alru_cache(ttl=timedelta(milliseconds=1)) - async def f(x: int) -> int: + async def f(x: int) -> Tuple[bool, int]: nonlocal count count += 1 - return (True, x) # type: ignore + return (True, x) assert count == 0 assert await f(1) == 1 @@ -90,10 +91,10 @@ async def test_alru_cache_with_no_cache(self): count = 0 @alru_cache() - async def f(x: int, no_cache: bool = False) -> int: + async def f(x: int, no_cache: bool = False) -> Tuple[bool, int]: nonlocal count count += 1 - return (True, x) # type: ignore + return (True, x) assert count == 0 assert await f(1) == 1 From 427902c48bc7a457542daea8eba9166e4f0d21a4 Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Mon, 20 Nov 2023 21:42:34 -0500 Subject: [PATCH 03/14] Remove umami --- frontend/public/index.html | 7 ------- 1 file changed, 7 deletions(-) diff --git a/frontend/public/index.html b/frontend/public/index.html index c91b46b0..726981bf 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -68,13 +68,6 @@ gtag('config', 'G-FT096DX9CK'); - - GitHub Trends From f3698fa73dbc11fe52c0d65fa568b5912780b6fd Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Mon, 20 Nov 2023 21:47:17 -0500 Subject: [PATCH 04/14] Cleanup --- backend/delete_old_data.py | 4 ++-- backend/src/main.py | 1 - backend/src/subscriber/routers/dev.py | 2 +- backend/src/subscriber/routers/wrapped.py | 16 +++++++--------- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/backend/delete_old_data.py b/backend/delete_old_data.py index 5e7db095..2a4e30a3 100755 --- a/backend/delete_old_data.py +++ b/backend/delete_old_data.py @@ -1,8 +1,8 @@ +import asyncio +from datetime import datetime from typing import Any -import asyncio from dotenv import find_dotenv, load_dotenv -from datetime import datetime load_dotenv(find_dotenv()) diff --git a/backend/src/main.py b/backend/src/main.py index f42d61d6..6a06a7d1 100644 --- a/backend/src/main.py +++ b/backend/src/main.py @@ -15,7 +15,6 @@ from src.publisher.routers import asset_router, auth_router, user_router from src.subscriber.routers import dev_router, wrapped_router - """ SETUP """ diff --git a/backend/src/subscriber/routers/dev.py b/backend/src/subscriber/routers/dev.py index ed2a4c02..82adf2e9 100644 --- a/backend/src/subscriber/routers/dev.py +++ b/backend/src/subscriber/routers/dev.py @@ -1,7 +1,7 @@ +import asyncio from datetime import date, timedelta from typing import Any, Dict, Optional -import asyncio from fastapi import APIRouter, Response, status from src.data.mongo.secret import update_keys diff --git a/backend/src/subscriber/routers/wrapped.py b/backend/src/subscriber/routers/wrapped.py index 9fd9c769..8038dfef 100644 --- a/backend/src/subscriber/routers/wrapped.py +++ b/backend/src/subscriber/routers/wrapped.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Tuple, Optional +from typing import Any, Dict, Optional from fastapi import APIRouter, Response, status @@ -13,20 +13,18 @@ "/valid/{user_id}", status_code=status.HTTP_200_OK, response_model=Dict[str, Any] ) @async_fail_gracefully -async def check_valid_user(response: Response, user_id: str) -> Tuple[bool, str]: - print("Checking valid user") +async def check_valid_user(response: Response, user_id: str) -> str: out = await get_is_valid_user(user_id) - print(out) - return (True, out) + return out @router.get("/{user_id}", status_code=status.HTTP_200_OK, response_model=Dict[str, Any]) @async_fail_gracefully async def get_wrapped_user( response: Response, user_id: str, year: int = 2022, no_cache: bool = False -) -> Tuple[bool, Optional[WrappedPackage]]: +) -> Optional[WrappedPackage]: valid_user = await get_is_valid_user(user_id) if valid_user != "Valid user": - return (False, WrappedPackage.empty()) - wrapped_object = await query_wrapped_user(user_id, year, no_cache=no_cache) - return (True, wrapped_object) + return WrappedPackage.empty() + + return await query_wrapped_user(user_id, year, no_cache=no_cache) From 490165d596359843cba7e6911a56cbb47b670106 Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Mon, 20 Nov 2023 23:37:03 -0500 Subject: [PATCH 05/14] Start reorganization --- backend/.env-template | 2 -- backend/deploy/cloudbuild.yaml | 1 - backend/deploy/pubsub.Dockerfile | 11 ------- backend/pyproject.toml | 1 - backend/requirements.txt | 1 - backend/src/main.py | 9 ++++-- .../layer0}/__init__.py | 6 ++-- .../layer0}/auth/__init__.py | 2 +- .../layer0}/auth/auth.py | 0 .../src/processing/layer0/user/__init__.py | 9 ++++++ .../layer0}/user/contributions.py | 5 +-- .../layer0}/user/follows.py | 0 .../layer0}/user/languages.py | 0 .../layer0}/user/package.py | 2 +- .../src/processing/layer0/wrapped/__init__.py | 3 ++ .../layer0}/wrapped/calendar.py | 0 .../layer0}/wrapped/langs.py | 0 .../layer0}/wrapped/numeric.py | 0 .../layer0}/wrapped/package.py | 12 ++++---- .../layer0}/wrapped/repos.py | 0 .../layer0}/wrapped/time.py | 0 .../layer0}/wrapped/timestamps.py | 0 backend/src/processing/layer1/__init__.py | 9 ++++++ .../processing => processing/layer1}/auth.py | 0 .../processing => processing/layer1}/user.py | 2 -- .../layer1}/wrapped.py | 5 +-- backend/src/processing/layer2/__init__.py | 9 ++++++ .../layer2/user}/__init__.py | 0 .../layer2}/user/commits.py | 2 +- .../layer2}/user/models.py | 0 backend/src/processing/layer3/__init__.py | 12 ++++++++ .../layer3}/user/__init__.py | 0 .../layer3}/user/auth.py | 1 - .../layer3}/user/get_data.py | 13 +------- .../layer3}/user/svg.py | 2 +- backend/src/publisher/aggregation/__init__.py | 9 ------ backend/src/publisher/processing/__init__.py | 12 -------- backend/src/publisher/routers/__init__.py | 5 --- .../src/publisher/routers/users/__init__.py | 0 .../src/{publisher => }/render/__init__.py | 6 ++-- backend/src/{publisher => }/render/error.py | 4 +-- backend/src/{publisher => }/render/style.py | 0 .../src/{publisher => }/render/template.py | 2 +- .../src/{publisher => }/render/top_langs.py | 6 ++-- .../src/{publisher => }/render/top_repos.py | 6 ++-- backend/src/routers/__init__.py | 7 +++++ .../user => routers/assets}/__init__.py | 0 .../{publisher => }/routers/assets/assets.py | 0 .../routers/assets/assets/error.png | Bin .../routers/assets/assets/stopwatch.png | Bin .../assets => routers/auth}/__init__.py | 0 .../src/{publisher => }/routers/auth/main.py | 4 +-- .../routers/auth/standalone.py | 4 +-- .../{publisher => }/routers/auth/website.py | 0 .../src/{publisher => }/routers/decorators.py | 2 +- backend/src/{subscriber => }/routers/dev.py | 0 .../auth => routers/users}/__init__.py | 0 .../src/{publisher => }/routers/users/db.py | 0 .../src/{publisher => }/routers/users/main.py | 6 ++-- .../src/{publisher => }/routers/users/svg.py | 8 ++--- .../src/{subscriber => }/routers/wrapped.py | 2 +- backend/src/subscriber/__init__.py | 0 .../subscriber/aggregation/user/__init__.py | 9 ------ .../aggregation/wrapped/__init__.py | 3 -- backend/src/subscriber/processing/__init__.py | 9 ------ backend/src/subscriber/routers/__init__.py | 4 --- backend/src/subscriber/routers/decorators.py | 29 ------------------ backend/src/utils/__init__.py | 2 -- docker-compose.yaml | 17 +--------- 69 files changed, 100 insertions(+), 175 deletions(-) delete mode 100644 backend/deploy/pubsub.Dockerfile rename backend/src/{subscriber/aggregation => processing/layer0}/__init__.py (70%) rename backend/src/{subscriber/aggregation => processing/layer0}/auth/__init__.py (80%) rename backend/src/{subscriber/aggregation => processing/layer0}/auth/auth.py (100%) create mode 100644 backend/src/processing/layer0/user/__init__.py rename backend/src/{subscriber/aggregation => processing/layer0}/user/contributions.py (99%) rename backend/src/{subscriber/aggregation => processing/layer0}/user/follows.py (100%) rename backend/src/{subscriber/aggregation => processing/layer0}/user/languages.py (100%) rename backend/src/{subscriber/aggregation => processing/layer0}/user/package.py (90%) create mode 100644 backend/src/processing/layer0/wrapped/__init__.py rename backend/src/{subscriber/aggregation => processing/layer0}/wrapped/calendar.py (100%) rename backend/src/{subscriber/aggregation => processing/layer0}/wrapped/langs.py (100%) rename backend/src/{subscriber/aggregation => processing/layer0}/wrapped/numeric.py (100%) rename backend/src/{subscriber/aggregation => processing/layer0}/wrapped/package.py (67%) rename backend/src/{subscriber/aggregation => processing/layer0}/wrapped/repos.py (100%) rename backend/src/{subscriber/aggregation => processing/layer0}/wrapped/time.py (100%) rename backend/src/{subscriber/aggregation => processing/layer0}/wrapped/timestamps.py (100%) create mode 100644 backend/src/processing/layer1/__init__.py rename backend/src/{subscriber/processing => processing/layer1}/auth.py (100%) rename backend/src/{subscriber/processing => processing/layer1}/user.py (98%) rename backend/src/{subscriber/processing => processing/layer1}/wrapped.py (89%) create mode 100644 backend/src/processing/layer2/__init__.py rename backend/src/{publisher => processing/layer2/user}/__init__.py (100%) rename backend/src/{publisher/aggregation => processing/layer2}/user/commits.py (98%) rename backend/src/{publisher/aggregation => processing/layer2}/user/models.py (100%) create mode 100644 backend/src/processing/layer3/__init__.py rename backend/src/{publisher/aggregation => processing/layer3}/user/__init__.py (100%) rename backend/src/{publisher/processing => processing/layer3}/user/auth.py (96%) rename backend/src/{publisher/processing => processing/layer3}/user/get_data.py (80%) rename backend/src/{publisher/processing => processing/layer3}/user/svg.py (91%) delete mode 100644 backend/src/publisher/aggregation/__init__.py delete mode 100644 backend/src/publisher/processing/__init__.py delete mode 100644 backend/src/publisher/routers/__init__.py delete mode 100644 backend/src/publisher/routers/users/__init__.py rename backend/src/{publisher => }/render/__init__.py (60%) rename backend/src/{publisher => }/render/error.py (95%) rename backend/src/{publisher => }/render/style.py (100%) rename backend/src/{publisher => }/render/template.py (98%) rename backend/src/{publisher => }/render/top_langs.py (92%) rename backend/src/{publisher => }/render/top_repos.py (92%) create mode 100644 backend/src/routers/__init__.py rename backend/src/{publisher/processing/user => routers/assets}/__init__.py (100%) rename backend/src/{publisher => }/routers/assets/assets.py (100%) rename backend/src/{publisher => }/routers/assets/assets/error.png (100%) rename backend/src/{publisher => }/routers/assets/assets/stopwatch.png (100%) rename backend/src/{publisher/routers/assets => routers/auth}/__init__.py (100%) rename backend/src/{publisher => }/routers/auth/main.py (51%) rename backend/src/{publisher => }/routers/auth/standalone.py (92%) rename backend/src/{publisher => }/routers/auth/website.py (100%) rename backend/src/{publisher => }/routers/decorators.py (98%) rename backend/src/{subscriber => }/routers/dev.py (100%) rename backend/src/{publisher/routers/auth => routers/users}/__init__.py (100%) rename backend/src/{publisher => }/routers/users/db.py (100%) rename backend/src/{publisher => }/routers/users/main.py (81%) rename backend/src/{publisher => }/routers/users/svg.py (93%) rename backend/src/{subscriber => }/routers/wrapped.py (92%) delete mode 100644 backend/src/subscriber/__init__.py delete mode 100644 backend/src/subscriber/aggregation/user/__init__.py delete mode 100644 backend/src/subscriber/aggregation/wrapped/__init__.py delete mode 100644 backend/src/subscriber/processing/__init__.py delete mode 100644 backend/src/subscriber/routers/__init__.py delete mode 100644 backend/src/subscriber/routers/decorators.py diff --git a/backend/.env-template b/backend/.env-template index fb75a2a6..2e1cfa32 100644 --- a/backend/.env-template +++ b/backend/.env-template @@ -7,6 +7,4 @@ OAUTH_REDIRECT_URI=abc123 GOOGLE_APPLICATION_CREDENTIALS=abc123 -PUBSUB_TOKEN=abc123 - MONGODB_PASSWORD=abc123 \ No newline at end of file diff --git a/backend/deploy/cloudbuild.yaml b/backend/deploy/cloudbuild.yaml index 1dc7adef..6a20545d 100644 --- a/backend/deploy/cloudbuild.yaml +++ b/backend/deploy/cloudbuild.yaml @@ -8,7 +8,6 @@ steps: args: ["run", "create-env"] dir: "backend" env: - - "PUBSUB_TOKEN=${_PUBSUB_TOKEN}" - "OAUTH_CLIENT_ID=${_OAUTH_CLIENT_ID}" - "OAUTH_CLIENT_SECRET=${_OAUTH_CLIENT_SECRET}" - "OAUTH_REDIRECT_URI=${_OAUTH_REDIRECT_URI}" diff --git a/backend/deploy/pubsub.Dockerfile b/backend/deploy/pubsub.Dockerfile deleted file mode 100644 index 68570c6f..00000000 --- a/backend/deploy/pubsub.Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM google/cloud-sdk:alpine - -RUN apk --update add openjdk8-jre netcat-openbsd - -RUN gcloud components install beta pubsub-emulator - -RUN gcloud components update - -EXPOSE 8085 - -CMD [ "gcloud", "beta", "emulators", "pubsub", "start", "--host-port", "0.0.0.0:8085" ] \ No newline at end of file diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 1e2b6c9b..37ee2130 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -15,7 +15,6 @@ motor = "^3.3.1" aiofiles = "^23.2.1" aiounittest = "^1.4.2" coveralls = "^3.3.1" -google-cloud-pubsub = "^2.18.4" grpcio = "^1.59.2" gunicorn = "^21.2.0" pymongo = {extras = ["srv"], version = "^4.6.0"} diff --git a/backend/requirements.txt b/backend/requirements.txt index ab0724c8..ce529488 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -14,7 +14,6 @@ docopt==0.6.2 fastapi==0.104.1 google-api-core[grpc]==2.14.0 google-auth==2.23.4 -google-cloud-pubsub==2.18.4 googleapis-common-protos==1.61.0 googleapis-common-protos[grpc]==1.61.0 grpc-google-iam-v1==0.12.7 diff --git a/backend/src/main.py b/backend/src/main.py index 6a06a7d1..a58a20d9 100644 --- a/backend/src/main.py +++ b/backend/src/main.py @@ -12,8 +12,13 @@ # add endpoints here (after load dotenv) from src.constants import PROD, SENTRY_DSN -from src.publisher.routers import asset_router, auth_router, user_router -from src.subscriber.routers import dev_router, wrapped_router +from src.routers import ( + asset_router, + auth_router, + dev_router, + user_router, + wrapped_router, +) """ SETUP diff --git a/backend/src/subscriber/aggregation/__init__.py b/backend/src/processing/layer0/__init__.py similarity index 70% rename from backend/src/subscriber/aggregation/__init__.py rename to backend/src/processing/layer0/__init__.py index 8d3c6e3a..99a607e4 100644 --- a/backend/src/subscriber/aggregation/__init__.py +++ b/backend/src/processing/layer0/__init__.py @@ -1,15 +1,15 @@ -from src.subscriber.aggregation.auth import ( +from src.processing.layer0.auth import ( get_repo_stargazers, get_user_stars, get_valid_db_user, get_valid_github_user, ) -from src.subscriber.aggregation.user import ( +from src.processing.layer0.user import ( get_contributions, get_user_data, get_user_follows, ) -from src.subscriber.aggregation.wrapped import get_wrapped_data +from src.processing.layer0.wrapped import get_wrapped_data __all__ = [ "get_contributions", diff --git a/backend/src/subscriber/aggregation/auth/__init__.py b/backend/src/processing/layer0/auth/__init__.py similarity index 80% rename from backend/src/subscriber/aggregation/auth/__init__.py rename to backend/src/processing/layer0/auth/__init__.py index 8b2db088..30052fc0 100644 --- a/backend/src/subscriber/aggregation/auth/__init__.py +++ b/backend/src/processing/layer0/auth/__init__.py @@ -1,4 +1,4 @@ -from src.subscriber.aggregation.auth.auth import ( +from src.processing.layer0.auth.auth import ( get_repo_stargazers, get_user_stars, get_valid_db_user, diff --git a/backend/src/subscriber/aggregation/auth/auth.py b/backend/src/processing/layer0/auth/auth.py similarity index 100% rename from backend/src/subscriber/aggregation/auth/auth.py rename to backend/src/processing/layer0/auth/auth.py diff --git a/backend/src/processing/layer0/user/__init__.py b/backend/src/processing/layer0/user/__init__.py new file mode 100644 index 00000000..36b754b5 --- /dev/null +++ b/backend/src/processing/layer0/user/__init__.py @@ -0,0 +1,9 @@ +from src.processing.layer0.user.contributions import get_contributions +from src.processing.layer0.user.follows import get_user_follows +from src.processing.layer0.user.package import get_user_data + +__all__ = [ + "get_contributions", + "get_user_follows", + "get_user_data", +] diff --git a/backend/src/subscriber/aggregation/user/contributions.py b/backend/src/processing/layer0/user/contributions.py similarity index 99% rename from backend/src/subscriber/aggregation/user/contributions.py rename to backend/src/processing/layer0/user/contributions.py index 2bf05432..c28e7102 100644 --- a/backend/src/subscriber/aggregation/user/contributions.py +++ b/backend/src/processing/layer0/user/contributions.py @@ -29,10 +29,7 @@ get_repo_commits, ) from src.models import UserContributions -from src.subscriber.aggregation.user.languages import ( - CommitLanguages, - get_commit_languages, -) +from src.processing.layer0.user.languages import CommitLanguages, get_commit_languages from src.utils import date_to_datetime, gather diff --git a/backend/src/subscriber/aggregation/user/follows.py b/backend/src/processing/layer0/user/follows.py similarity index 100% rename from backend/src/subscriber/aggregation/user/follows.py rename to backend/src/processing/layer0/user/follows.py diff --git a/backend/src/subscriber/aggregation/user/languages.py b/backend/src/processing/layer0/user/languages.py similarity index 100% rename from backend/src/subscriber/aggregation/user/languages.py rename to backend/src/processing/layer0/user/languages.py diff --git a/backend/src/subscriber/aggregation/user/package.py b/backend/src/processing/layer0/user/package.py similarity index 90% rename from backend/src/subscriber/aggregation/user/package.py rename to backend/src/processing/layer0/user/package.py index 81790a5c..e4d2716d 100644 --- a/backend/src/subscriber/aggregation/user/package.py +++ b/backend/src/processing/layer0/user/package.py @@ -2,7 +2,7 @@ from typing import Optional from src.models import UserPackage -from src.subscriber.aggregation.user.contributions import get_contributions +from src.processing.layer0.user.contributions import get_contributions # from src.subscriber.aggregation.user.follows import get_user_follows diff --git a/backend/src/processing/layer0/wrapped/__init__.py b/backend/src/processing/layer0/wrapped/__init__.py new file mode 100644 index 00000000..07424616 --- /dev/null +++ b/backend/src/processing/layer0/wrapped/__init__.py @@ -0,0 +1,3 @@ +from src.processing.layer0.wrapped.package import main as get_wrapped_data + +__all__ = ["get_wrapped_data"] diff --git a/backend/src/subscriber/aggregation/wrapped/calendar.py b/backend/src/processing/layer0/wrapped/calendar.py similarity index 100% rename from backend/src/subscriber/aggregation/wrapped/calendar.py rename to backend/src/processing/layer0/wrapped/calendar.py diff --git a/backend/src/subscriber/aggregation/wrapped/langs.py b/backend/src/processing/layer0/wrapped/langs.py similarity index 100% rename from backend/src/subscriber/aggregation/wrapped/langs.py rename to backend/src/processing/layer0/wrapped/langs.py diff --git a/backend/src/subscriber/aggregation/wrapped/numeric.py b/backend/src/processing/layer0/wrapped/numeric.py similarity index 100% rename from backend/src/subscriber/aggregation/wrapped/numeric.py rename to backend/src/processing/layer0/wrapped/numeric.py diff --git a/backend/src/subscriber/aggregation/wrapped/package.py b/backend/src/processing/layer0/wrapped/package.py similarity index 67% rename from backend/src/subscriber/aggregation/wrapped/package.py rename to backend/src/processing/layer0/wrapped/package.py index 5f99c643..bc747a7c 100644 --- a/backend/src/subscriber/aggregation/wrapped/package.py +++ b/backend/src/processing/layer0/wrapped/package.py @@ -1,10 +1,10 @@ from src.models import UserPackage, WrappedPackage -from src.subscriber.aggregation.wrapped.calendar import get_calendar_data -from src.subscriber.aggregation.wrapped.langs import get_lang_data -from src.subscriber.aggregation.wrapped.numeric import get_numeric_data -from src.subscriber.aggregation.wrapped.repos import get_repo_data -from src.subscriber.aggregation.wrapped.time import get_day_data, get_month_data -from src.subscriber.aggregation.wrapped.timestamps import get_timestamp_data +from src.processing.layer0.wrapped.calendar import get_calendar_data +from src.processing.layer0.wrapped.langs import get_lang_data +from src.processing.layer0.wrapped.numeric import get_numeric_data +from src.processing.layer0.wrapped.repos import get_repo_data +from src.processing.layer0.wrapped.time import get_day_data, get_month_data +from src.processing.layer0.wrapped.timestamps import get_timestamp_data # from src.processing.user.follows import get_user_follows diff --git a/backend/src/subscriber/aggregation/wrapped/repos.py b/backend/src/processing/layer0/wrapped/repos.py similarity index 100% rename from backend/src/subscriber/aggregation/wrapped/repos.py rename to backend/src/processing/layer0/wrapped/repos.py diff --git a/backend/src/subscriber/aggregation/wrapped/time.py b/backend/src/processing/layer0/wrapped/time.py similarity index 100% rename from backend/src/subscriber/aggregation/wrapped/time.py rename to backend/src/processing/layer0/wrapped/time.py diff --git a/backend/src/subscriber/aggregation/wrapped/timestamps.py b/backend/src/processing/layer0/wrapped/timestamps.py similarity index 100% rename from backend/src/subscriber/aggregation/wrapped/timestamps.py rename to backend/src/processing/layer0/wrapped/timestamps.py diff --git a/backend/src/processing/layer1/__init__.py b/backend/src/processing/layer1/__init__.py new file mode 100644 index 00000000..f79d1139 --- /dev/null +++ b/backend/src/processing/layer1/__init__.py @@ -0,0 +1,9 @@ +from src.processing.layer1.auth import get_is_valid_user +from src.processing.layer1.user import query_user +from src.processing.layer1.wrapped import query_wrapped_user + +__all__ = [ + "query_user", + "query_wrapped_user", + "get_is_valid_user", +] diff --git a/backend/src/subscriber/processing/auth.py b/backend/src/processing/layer1/auth.py similarity index 100% rename from backend/src/subscriber/processing/auth.py rename to backend/src/processing/layer1/auth.py diff --git a/backend/src/subscriber/processing/user.py b/backend/src/processing/layer1/user.py similarity index 98% rename from backend/src/subscriber/processing/user.py rename to backend/src/processing/layer1/user.py index 435765ce..b7e6006a 100644 --- a/backend/src/subscriber/processing/user.py +++ b/backend/src/processing/layer1/user.py @@ -14,8 +14,6 @@ s = requests.Session() -# NOTE: query user from PubSub, not from subscriber user router - async def query_user_month( user_id: str, diff --git a/backend/src/subscriber/processing/wrapped.py b/backend/src/processing/layer1/wrapped.py similarity index 89% rename from backend/src/subscriber/processing/wrapped.py rename to backend/src/processing/layer1/wrapped.py index a3110989..0238dc42 100644 --- a/backend/src/subscriber/processing/wrapped.py +++ b/backend/src/processing/layer1/wrapped.py @@ -1,10 +1,11 @@ from datetime import date, timedelta from typing import Optional, Tuple +from backend.src.processing.layer0 import get_wrapped_data + from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user from src.models import UserPackage, WrappedPackage -from src.subscriber.aggregation import get_wrapped_data -from src.subscriber.processing.user import query_user +from src.processing.layer1.user import query_user from src.utils import alru_cache diff --git a/backend/src/processing/layer2/__init__.py b/backend/src/processing/layer2/__init__.py new file mode 100644 index 00000000..32cd9733 --- /dev/null +++ b/backend/src/processing/layer2/__init__.py @@ -0,0 +1,9 @@ +from src.processing.layer2.user.commits import get_top_languages, get_top_repos +from src.processing.layer2.user.models import LanguageStats, RepoStats + +__all__ = [ + "get_top_languages", + "get_top_repos", + "LanguageStats", + "RepoStats", +] diff --git a/backend/src/publisher/__init__.py b/backend/src/processing/layer2/user/__init__.py similarity index 100% rename from backend/src/publisher/__init__.py rename to backend/src/processing/layer2/user/__init__.py diff --git a/backend/src/publisher/aggregation/user/commits.py b/backend/src/processing/layer2/user/commits.py similarity index 98% rename from backend/src/publisher/aggregation/user/commits.py rename to backend/src/processing/layer2/user/commits.py index d6545199..5ef38609 100644 --- a/backend/src/publisher/aggregation/user/commits.py +++ b/backend/src/processing/layer2/user/commits.py @@ -2,7 +2,7 @@ from src.constants import DEFAULT_COLOR from src.models import UserPackage -from src.publisher.aggregation.user.models import LanguageStats, RepoStats +from src.processing.layer2.user.models import LanguageStats, RepoStats dict_type = Dict[str, Union[str, int, float]] diff --git a/backend/src/publisher/aggregation/user/models.py b/backend/src/processing/layer2/user/models.py similarity index 100% rename from backend/src/publisher/aggregation/user/models.py rename to backend/src/processing/layer2/user/models.py diff --git a/backend/src/processing/layer3/__init__.py b/backend/src/processing/layer3/__init__.py new file mode 100644 index 00000000..2429f943 --- /dev/null +++ b/backend/src/processing/layer3/__init__.py @@ -0,0 +1,12 @@ +from src.processing.layer3.user.auth import authenticate, delete_user, set_user_key +from src.processing.layer3.user.get_data import get_user, get_user_demo +from src.processing.layer3.user.svg import svg_base + +__all__ = [ + "set_user_key", + "authenticate", + "delete_user", + "get_user", + "get_user_demo", + "svg_base", +] diff --git a/backend/src/publisher/aggregation/user/__init__.py b/backend/src/processing/layer3/user/__init__.py similarity index 100% rename from backend/src/publisher/aggregation/user/__init__.py rename to backend/src/processing/layer3/user/__init__.py diff --git a/backend/src/publisher/processing/user/auth.py b/backend/src/processing/layer3/user/auth.py similarity index 96% rename from backend/src/publisher/processing/user/auth.py rename to backend/src/processing/layer3/user/auth.py index 38e312da..2d0817b5 100644 --- a/backend/src/publisher/processing/user/auth.py +++ b/backend/src/processing/layer3/user/auth.py @@ -7,7 +7,6 @@ get_public_user as db_get_public_user, update_user as db_update_user, ) -from src.publisher.processing.user.get_data import update_user # frontend first calls set_user_key with code and user_key # next they call authenticate which determines the user_id to associate with the code/user_key diff --git a/backend/src/publisher/processing/user/get_data.py b/backend/src/processing/layer3/user/get_data.py similarity index 80% rename from backend/src/publisher/processing/user/get_data.py rename to backend/src/processing/layer3/user/get_data.py index eeed6530..d70b6130 100644 --- a/backend/src/publisher/processing/user/get_data.py +++ b/backend/src/processing/layer3/user/get_data.py @@ -5,21 +5,10 @@ from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user from src.data.mongo.user_months import get_user_months from src.models import UserPackage - -# TODO: replace with call to subscriber so compute not on publisher -from src.subscriber.aggregation import get_user_data +from src.processing.layer0 import get_user_data from src.utils import alru_cache -@alru_cache() -async def update_user( - user_id: str, access_token: Optional[str], private_access: bool -) -> Tuple[bool, bool]: - """Sends a message to pubsub to request a user update (auto cache updates)""" - # await publish_user(user_id, access_token, private_access) - return (True, True) - - async def _get_user( user_id: str, private_access: bool, start_date: date, end_date: date ) -> Optional[UserPackage]: diff --git a/backend/src/publisher/processing/user/svg.py b/backend/src/processing/layer3/user/svg.py similarity index 91% rename from backend/src/publisher/processing/user/svg.py rename to backend/src/processing/layer3/user/svg.py index 9932f346..799df3d7 100644 --- a/backend/src/publisher/processing/user/svg.py +++ b/backend/src/processing/layer3/user/svg.py @@ -2,7 +2,7 @@ from typing import Optional, Tuple from src.models import UserPackage -from src.publisher.processing.user.get_data import get_user, get_user_demo +from src.processing.layer3.user.get_data import get_user, get_user_demo from src.utils import use_time_range diff --git a/backend/src/publisher/aggregation/__init__.py b/backend/src/publisher/aggregation/__init__.py deleted file mode 100644 index 205f3248..00000000 --- a/backend/src/publisher/aggregation/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from src.publisher.aggregation.user.commits import get_top_languages, get_top_repos -from src.publisher.aggregation.user.models import LanguageStats, RepoStats - -__all__ = [ - "get_top_languages", - "get_top_repos", - "LanguageStats", - "RepoStats", -] diff --git a/backend/src/publisher/processing/__init__.py b/backend/src/publisher/processing/__init__.py deleted file mode 100644 index c952c06f..00000000 --- a/backend/src/publisher/processing/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from src.publisher.processing.user.auth import authenticate, delete_user, set_user_key -from src.publisher.processing.user.get_data import get_user, get_user_demo -from src.publisher.processing.user.svg import svg_base - -__all__ = [ - "set_user_key", - "authenticate", - "delete_user", - "get_user", - "get_user_demo", - "svg_base", -] diff --git a/backend/src/publisher/routers/__init__.py b/backend/src/publisher/routers/__init__.py deleted file mode 100644 index fbf4be5f..00000000 --- a/backend/src/publisher/routers/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from src.publisher.routers.assets.assets import router as asset_router -from src.publisher.routers.auth.main import router as auth_router -from src.publisher.routers.users.main import router as user_router - -__all__ = ["asset_router", "user_router", "auth_router"] diff --git a/backend/src/publisher/routers/users/__init__.py b/backend/src/publisher/routers/users/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/src/publisher/render/__init__.py b/backend/src/render/__init__.py similarity index 60% rename from backend/src/publisher/render/__init__.py rename to backend/src/render/__init__.py index c9a4bd15..0503646f 100644 --- a/backend/src/publisher/render/__init__.py +++ b/backend/src/render/__init__.py @@ -1,11 +1,11 @@ -from src.publisher.render.error import ( +from src.render.error import ( get_empty_demo_svg, get_error_svg, get_loading_svg, get_no_data_svg, ) -from src.publisher.render.top_langs import get_top_langs_svg -from src.publisher.render.top_repos import get_top_repos_svg +from src.render.top_langs import get_top_langs_svg +from src.render.top_repos import get_top_repos_svg __all__ = [ "get_empty_demo_svg", diff --git a/backend/src/publisher/render/error.py b/backend/src/render/error.py similarity index 95% rename from backend/src/publisher/render/error.py rename to backend/src/render/error.py index 5d0b78ee..03b0b86d 100644 --- a/backend/src/publisher/render/error.py +++ b/backend/src/render/error.py @@ -3,8 +3,8 @@ from svgwrite import Drawing from src.constants import BACKEND_URL -from src.publisher.render.style import styles_no_animation, themes -from src.publisher.render.template import get_template +from src.render.style import styles_no_animation, themes +from src.render.template import get_template THEME = "classic" diff --git a/backend/src/publisher/render/style.py b/backend/src/render/style.py similarity index 100% rename from backend/src/publisher/render/style.py rename to backend/src/render/style.py diff --git a/backend/src/publisher/render/template.py b/backend/src/render/template.py similarity index 98% rename from backend/src/publisher/render/template.py rename to backend/src/render/template.py index 99fdae98..8eb76769 100644 --- a/backend/src/publisher/render/template.py +++ b/backend/src/render/template.py @@ -7,7 +7,7 @@ from svgwrite.shapes import Circle from src.constants import DEFAULT_COLOR -from src.publisher.render.style import styles, styles_no_animation, themes +from src.render.style import styles, styles_no_animation, themes def get_template( diff --git a/backend/src/publisher/render/top_langs.py b/backend/src/render/top_langs.py similarity index 92% rename from backend/src/publisher/render/top_langs.py rename to backend/src/render/top_langs.py index 4e996852..66760431 100644 --- a/backend/src/publisher/render/top_langs.py +++ b/backend/src/render/top_langs.py @@ -4,9 +4,9 @@ from svgwrite import Drawing -from src.publisher.aggregation import LanguageStats -from src.publisher.render.error import get_no_data_svg -from src.publisher.render.template import ( +from backend.src.processing.layer2 import LanguageStats +from src.render.error import get_no_data_svg +from src.render.template import ( get_bar_section, get_lang_name_section, get_template, diff --git a/backend/src/publisher/render/top_repos.py b/backend/src/render/top_repos.py similarity index 92% rename from backend/src/publisher/render/top_repos.py rename to backend/src/render/top_repos.py index 15c52aab..38b5d9a4 100644 --- a/backend/src/publisher/render/top_repos.py +++ b/backend/src/render/top_repos.py @@ -4,9 +4,9 @@ from svgwrite import Drawing -from src.publisher.aggregation.user.models import RepoStats -from src.publisher.render.error import get_no_data_svg -from src.publisher.render.template import ( +from src.processing.layer2.user.models import RepoStats +from src.render.error import get_no_data_svg +from src.render.template import ( get_bar_section, get_lang_name_section, get_template, diff --git a/backend/src/routers/__init__.py b/backend/src/routers/__init__.py new file mode 100644 index 00000000..9003fcad --- /dev/null +++ b/backend/src/routers/__init__.py @@ -0,0 +1,7 @@ +from src.routers.assets.assets import router as asset_router +from src.routers.auth.main import router as auth_router +from src.routers.dev import router as dev_router +from src.routers.users.main import router as user_router +from src.routers.wrapped import router as wrapped_router + +__all__ = ["asset_router", "auth_router", "dev_router", "user_router", "wrapped_router"] diff --git a/backend/src/publisher/processing/user/__init__.py b/backend/src/routers/assets/__init__.py similarity index 100% rename from backend/src/publisher/processing/user/__init__.py rename to backend/src/routers/assets/__init__.py diff --git a/backend/src/publisher/routers/assets/assets.py b/backend/src/routers/assets/assets.py similarity index 100% rename from backend/src/publisher/routers/assets/assets.py rename to backend/src/routers/assets/assets.py diff --git a/backend/src/publisher/routers/assets/assets/error.png b/backend/src/routers/assets/assets/error.png similarity index 100% rename from backend/src/publisher/routers/assets/assets/error.png rename to backend/src/routers/assets/assets/error.png diff --git a/backend/src/publisher/routers/assets/assets/stopwatch.png b/backend/src/routers/assets/assets/stopwatch.png similarity index 100% rename from backend/src/publisher/routers/assets/assets/stopwatch.png rename to backend/src/routers/assets/assets/stopwatch.png diff --git a/backend/src/publisher/routers/assets/__init__.py b/backend/src/routers/auth/__init__.py similarity index 100% rename from backend/src/publisher/routers/assets/__init__.py rename to backend/src/routers/auth/__init__.py diff --git a/backend/src/publisher/routers/auth/main.py b/backend/src/routers/auth/main.py similarity index 51% rename from backend/src/publisher/routers/auth/main.py rename to backend/src/routers/auth/main.py index 4fce5355..b3606853 100644 --- a/backend/src/publisher/routers/auth/main.py +++ b/backend/src/routers/auth/main.py @@ -1,7 +1,7 @@ from fastapi import APIRouter -from src.publisher.routers.auth.standalone import router as standalone_router -from src.publisher.routers.auth.website import router as website_router +from src.routers.auth.standalone import router as standalone_router +from src.routers.auth.website import router as website_router router = APIRouter() router.include_router(standalone_router, prefix="") diff --git a/backend/src/publisher/routers/auth/standalone.py b/backend/src/routers/auth/standalone.py similarity index 92% rename from backend/src/publisher/routers/auth/standalone.py rename to backend/src/routers/auth/standalone.py index ef875e93..0844c038 100644 --- a/backend/src/publisher/routers/auth/standalone.py +++ b/backend/src/routers/auth/standalone.py @@ -1,12 +1,12 @@ import logging from typing import Optional +from backend.src.processing.layer3 import authenticate, delete_user from fastapi import APIRouter from fastapi.responses import RedirectResponse from src.constants import OAUTH_CLIENT_ID -from src.publisher.processing import authenticate, delete_user -from src.publisher.routers.decorators import get_redirect_url +from src.routers.decorators import get_redirect_url router = APIRouter() diff --git a/backend/src/publisher/routers/auth/website.py b/backend/src/routers/auth/website.py similarity index 100% rename from backend/src/publisher/routers/auth/website.py rename to backend/src/routers/auth/website.py diff --git a/backend/src/publisher/routers/decorators.py b/backend/src/routers/decorators.py similarity index 98% rename from backend/src/publisher/routers/decorators.py rename to backend/src/routers/decorators.py index 719aa7de..e6536644 100644 --- a/backend/src/publisher/routers/decorators.py +++ b/backend/src/routers/decorators.py @@ -9,7 +9,7 @@ from svgwrite.drawing import Drawing # type: ignore from src.constants import OAUTH_CLIENT_ID, OAUTH_REDIRECT_URI -from src.publisher.render import get_error_svg +from src.render import get_error_svg # for standalone auth routes diff --git a/backend/src/subscriber/routers/dev.py b/backend/src/routers/dev.py similarity index 100% rename from backend/src/subscriber/routers/dev.py rename to backend/src/routers/dev.py diff --git a/backend/src/publisher/routers/auth/__init__.py b/backend/src/routers/users/__init__.py similarity index 100% rename from backend/src/publisher/routers/auth/__init__.py rename to backend/src/routers/users/__init__.py diff --git a/backend/src/publisher/routers/users/db.py b/backend/src/routers/users/db.py similarity index 100% rename from backend/src/publisher/routers/users/db.py rename to backend/src/routers/users/db.py diff --git a/backend/src/publisher/routers/users/main.py b/backend/src/routers/users/main.py similarity index 81% rename from backend/src/publisher/routers/users/main.py rename to backend/src/routers/users/main.py index f7ca66f4..c5974fa3 100644 --- a/backend/src/publisher/routers/users/main.py +++ b/backend/src/routers/users/main.py @@ -1,12 +1,12 @@ from datetime import date, timedelta from typing import Optional +from backend.src.processing.layer3 import get_user from fastapi import APIRouter, Response, status from src.models import UserPackage -from src.publisher.processing import get_user -from src.publisher.routers.users.db import router as db_router -from src.publisher.routers.users.svg import router as svg_router +from src.routers.users.db import router as db_router +from src.routers.users.svg import router as svg_router from src.utils import async_fail_gracefully router = APIRouter() diff --git a/backend/src/publisher/routers/users/svg.py b/backend/src/routers/users/svg.py similarity index 93% rename from backend/src/publisher/routers/users/svg.py rename to backend/src/routers/users/svg.py index 44b01ed4..9c8e6e37 100644 --- a/backend/src/publisher/routers/users/svg.py +++ b/backend/src/routers/users/svg.py @@ -1,19 +1,19 @@ from datetime import date, timedelta from typing import Any +from backend.src.processing.layer2 import get_top_languages, get_top_repos +from backend.src.processing.layer3 import svg_base from fastapi import Response, status from fastapi.responses import HTMLResponse from fastapi.routing import APIRouter -from src.publisher.aggregation import get_top_languages, get_top_repos -from src.publisher.processing import svg_base -from src.publisher.render import ( +from src.render import ( get_empty_demo_svg, get_loading_svg, get_top_langs_svg, get_top_repos_svg, ) -from src.publisher.routers.decorators import svg_fail_gracefully +from src.routers.decorators import svg_fail_gracefully router = APIRouter() diff --git a/backend/src/subscriber/routers/wrapped.py b/backend/src/routers/wrapped.py similarity index 92% rename from backend/src/subscriber/routers/wrapped.py rename to backend/src/routers/wrapped.py index 8038dfef..45cc882b 100644 --- a/backend/src/subscriber/routers/wrapped.py +++ b/backend/src/routers/wrapped.py @@ -3,7 +3,7 @@ from fastapi import APIRouter, Response, status from src.models import WrappedPackage -from src.subscriber.processing import get_is_valid_user, query_wrapped_user +from src.processing.layer1 import get_is_valid_user, query_wrapped_user from src.utils import async_fail_gracefully router = APIRouter() diff --git a/backend/src/subscriber/__init__.py b/backend/src/subscriber/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/src/subscriber/aggregation/user/__init__.py b/backend/src/subscriber/aggregation/user/__init__.py deleted file mode 100644 index c707d8ee..00000000 --- a/backend/src/subscriber/aggregation/user/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from src.subscriber.aggregation.user.contributions import get_contributions -from src.subscriber.aggregation.user.follows import get_user_follows -from src.subscriber.aggregation.user.package import get_user_data - -__all__ = [ - "get_contributions", - "get_user_follows", - "get_user_data", -] diff --git a/backend/src/subscriber/aggregation/wrapped/__init__.py b/backend/src/subscriber/aggregation/wrapped/__init__.py deleted file mode 100644 index 39face5f..00000000 --- a/backend/src/subscriber/aggregation/wrapped/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from src.subscriber.aggregation.wrapped.package import main as get_wrapped_data - -__all__ = ["get_wrapped_data"] diff --git a/backend/src/subscriber/processing/__init__.py b/backend/src/subscriber/processing/__init__.py deleted file mode 100644 index b85ba591..00000000 --- a/backend/src/subscriber/processing/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from src.subscriber.processing.auth import get_is_valid_user -from src.subscriber.processing.user import query_user -from src.subscriber.processing.wrapped import query_wrapped_user - -__all__ = [ - "query_user", - "query_wrapped_user", - "get_is_valid_user", -] diff --git a/backend/src/subscriber/routers/__init__.py b/backend/src/subscriber/routers/__init__.py deleted file mode 100644 index c97f178e..00000000 --- a/backend/src/subscriber/routers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from src.subscriber.routers.dev import router as dev_router -from src.subscriber.routers.wrapped import router as wrapped_router - -__all__ = ["dev_router", "wrapped_router"] diff --git a/backend/src/subscriber/routers/decorators.py b/backend/src/subscriber/routers/decorators.py deleted file mode 100644 index b7523407..00000000 --- a/backend/src/subscriber/routers/decorators.py +++ /dev/null @@ -1,29 +0,0 @@ -import logging -from datetime import datetime -from functools import wraps -from typing import Any, Callable, Dict, List - -from fastapi import status -from fastapi.responses import Response - - -# NOTE: returns HTTP_200_OK regardless to avoid retrying PubSub API -def pubsub_fail_gracefully(func: Callable[..., Any]): - @wraps(func) # needed to play nice with FastAPI decorator - async def wrapper( - response: Response, *args: List[Any], **kwargs: Dict[str, Any] - ) -> Any: - start = datetime.now() - try: - data = await func(response, *args, **kwargs) - return {"data": data, "message": "200 OK", "time": datetime.now() - start} - except Exception as e: - logging.exception(e) - response.status_code = status.HTTP_200_OK - return { - "data": [], - "message": f"Error {str(e)}", - "time": datetime.now() - start, - } - - return wrapper diff --git a/backend/src/utils/__init__.py b/backend/src/utils/__init__.py index 62443abb..23ff5fd6 100644 --- a/backend/src/utils/__init__.py +++ b/backend/src/utils/__init__.py @@ -3,8 +3,6 @@ from src.utils.gather import gather from src.utils.utils import date_to_datetime, format_number, use_time_range -# PubSub removed from __all__ to prevent tests requiring GCP - __all__ = [ "alru_cache", "fail_gracefully", diff --git a/docker-compose.yaml b/docker-compose.yaml index 1a88c166..8d06466a 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,19 +1,6 @@ version: "3" services: - pubsub: - container_name: pubsub - build: - context: "./backend" - dockerfile: deploy/pubsub.Dockerfile - ports: - - 8085:8085 - volumes: - - ./backend:/backend - environment: - - GOOGLE_APPLICATION_CREDENTIALS=/backend/gcloud_key.json - - PUBSUB_EMULATOR_HOST=pubsub:8085 - backend: container_name: backend build: @@ -25,11 +12,9 @@ services: - ./backend:/backend environment: - GOOGLE_APPLICATION_CREDENTIALS=/backend/gcloud_key.json - - PUBSUB_EMULATOR_HOST=pubsub:8085 - PROJECT_ID=github-334619 - DOCKER=True - depends_on: - - pubsub + frontend: container_name: frontend build: From b6b87aa685778a30fcc411894686cfc1bac6a620 Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Mon, 20 Nov 2023 23:41:21 -0500 Subject: [PATCH 06/14] More reorg --- backend/src/processing/layer0/{auth => }/auth.py | 0 backend/src/processing/layer0/auth/__init__.py | 13 ------------- backend/src/processing/layer1/auth.py | 2 +- backend/src/processing/layer1/user.py | 2 +- backend/src/processing/layer1/wrapped.py | 3 +-- backend/src/processing/layer2/__init__.py | 13 +++++++++++-- .../src/processing/{layer3/user => layer2}/auth.py | 0 backend/src/processing/layer2/{user => }/commits.py | 2 +- .../processing/{layer3/user => layer2}/get_data.py | 0 backend/src/processing/layer2/{user => }/models.py | 0 .../src/processing/{layer3/user => layer2}/svg.py | 2 +- backend/src/processing/layer2/user/__init__.py | 0 backend/src/processing/layer3/__init__.py | 12 ------------ backend/src/processing/layer3/user/__init__.py | 0 backend/src/render/top_langs.py | 8 ++------ backend/src/render/top_repos.py | 8 ++------ backend/src/routers/auth/standalone.py | 2 +- backend/src/routers/auth/website.py | 2 +- backend/src/routers/dev.py | 2 +- backend/src/routers/users/main.py | 2 +- backend/src/routers/users/svg.py | 3 +-- .../subscriber/aggregation/test_contributions.py | 2 +- .../tests/subscriber/aggregation/test_follows.py | 2 +- 23 files changed, 27 insertions(+), 53 deletions(-) rename backend/src/processing/layer0/{auth => }/auth.py (100%) delete mode 100644 backend/src/processing/layer0/auth/__init__.py rename backend/src/processing/{layer3/user => layer2}/auth.py (100%) rename backend/src/processing/layer2/{user => }/commits.py (98%) rename backend/src/processing/{layer3/user => layer2}/get_data.py (100%) rename backend/src/processing/layer2/{user => }/models.py (100%) rename backend/src/processing/{layer3/user => layer2}/svg.py (91%) delete mode 100644 backend/src/processing/layer2/user/__init__.py delete mode 100644 backend/src/processing/layer3/__init__.py delete mode 100644 backend/src/processing/layer3/user/__init__.py diff --git a/backend/src/processing/layer0/auth/auth.py b/backend/src/processing/layer0/auth.py similarity index 100% rename from backend/src/processing/layer0/auth/auth.py rename to backend/src/processing/layer0/auth.py diff --git a/backend/src/processing/layer0/auth/__init__.py b/backend/src/processing/layer0/auth/__init__.py deleted file mode 100644 index 30052fc0..00000000 --- a/backend/src/processing/layer0/auth/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from src.processing.layer0.auth.auth import ( - get_repo_stargazers, - get_user_stars, - get_valid_db_user, - get_valid_github_user, -) - -__all__ = [ - "get_valid_github_user", - "get_valid_db_user", - "get_repo_stargazers", - "get_user_stars", -] diff --git a/backend/src/processing/layer1/auth.py b/backend/src/processing/layer1/auth.py index 33bcefff..12fbe781 100644 --- a/backend/src/processing/layer1/auth.py +++ b/backend/src/processing/layer1/auth.py @@ -3,7 +3,7 @@ from src.constants import OWNER, REPO from src.data.github.rest import RESTError -from src.subscriber.aggregation import ( +from src.processing.layer0 import ( get_repo_stargazers, get_user_stars, get_valid_db_user, diff --git a/backend/src/processing/layer1/user.py b/backend/src/processing/layer1/user.py index b7e6006a..33588c04 100644 --- a/backend/src/processing/layer1/user.py +++ b/backend/src/processing/layer1/user.py @@ -9,7 +9,7 @@ from src.data.mongo.secret import update_keys from src.data.mongo.user_months import UserMonth, get_user_months, set_user_month from src.models.user.main import UserPackage -from src.subscriber.aggregation import get_user_data +from src.processing.layer0 import get_user_data from src.utils import alru_cache, date_to_datetime s = requests.Session() diff --git a/backend/src/processing/layer1/wrapped.py b/backend/src/processing/layer1/wrapped.py index 0238dc42..e78252b5 100644 --- a/backend/src/processing/layer1/wrapped.py +++ b/backend/src/processing/layer1/wrapped.py @@ -1,10 +1,9 @@ from datetime import date, timedelta from typing import Optional, Tuple -from backend.src.processing.layer0 import get_wrapped_data - from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user from src.models import UserPackage, WrappedPackage +from src.processing.layer0 import get_wrapped_data from src.processing.layer1.user import query_user from src.utils import alru_cache diff --git a/backend/src/processing/layer2/__init__.py b/backend/src/processing/layer2/__init__.py index 32cd9733..edc02085 100644 --- a/backend/src/processing/layer2/__init__.py +++ b/backend/src/processing/layer2/__init__.py @@ -1,9 +1,18 @@ -from src.processing.layer2.user.commits import get_top_languages, get_top_repos -from src.processing.layer2.user.models import LanguageStats, RepoStats +from src.processing.layer2.auth import authenticate, delete_user, set_user_key +from src.processing.layer2.commits import get_top_languages, get_top_repos +from src.processing.layer2.get_data import get_user, get_user_demo +from src.processing.layer2.models import LanguageStats, RepoStats +from src.processing.layer2.svg import svg_base __all__ = [ "get_top_languages", "get_top_repos", "LanguageStats", "RepoStats", + "set_user_key", + "authenticate", + "delete_user", + "get_user", + "get_user_demo", + "svg_base", ] diff --git a/backend/src/processing/layer3/user/auth.py b/backend/src/processing/layer2/auth.py similarity index 100% rename from backend/src/processing/layer3/user/auth.py rename to backend/src/processing/layer2/auth.py diff --git a/backend/src/processing/layer2/user/commits.py b/backend/src/processing/layer2/commits.py similarity index 98% rename from backend/src/processing/layer2/user/commits.py rename to backend/src/processing/layer2/commits.py index 5ef38609..dc33c9b6 100644 --- a/backend/src/processing/layer2/user/commits.py +++ b/backend/src/processing/layer2/commits.py @@ -2,7 +2,7 @@ from src.constants import DEFAULT_COLOR from src.models import UserPackage -from src.processing.layer2.user.models import LanguageStats, RepoStats +from src.processing.layer2.models import LanguageStats, RepoStats dict_type = Dict[str, Union[str, int, float]] diff --git a/backend/src/processing/layer3/user/get_data.py b/backend/src/processing/layer2/get_data.py similarity index 100% rename from backend/src/processing/layer3/user/get_data.py rename to backend/src/processing/layer2/get_data.py diff --git a/backend/src/processing/layer2/user/models.py b/backend/src/processing/layer2/models.py similarity index 100% rename from backend/src/processing/layer2/user/models.py rename to backend/src/processing/layer2/models.py diff --git a/backend/src/processing/layer3/user/svg.py b/backend/src/processing/layer2/svg.py similarity index 91% rename from backend/src/processing/layer3/user/svg.py rename to backend/src/processing/layer2/svg.py index 799df3d7..e22c9830 100644 --- a/backend/src/processing/layer3/user/svg.py +++ b/backend/src/processing/layer2/svg.py @@ -2,7 +2,7 @@ from typing import Optional, Tuple from src.models import UserPackage -from src.processing.layer3.user.get_data import get_user, get_user_demo +from src.processing.layer2.get_data import get_user, get_user_demo from src.utils import use_time_range diff --git a/backend/src/processing/layer2/user/__init__.py b/backend/src/processing/layer2/user/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/src/processing/layer3/__init__.py b/backend/src/processing/layer3/__init__.py deleted file mode 100644 index 2429f943..00000000 --- a/backend/src/processing/layer3/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -from src.processing.layer3.user.auth import authenticate, delete_user, set_user_key -from src.processing.layer3.user.get_data import get_user, get_user_demo -from src.processing.layer3.user.svg import svg_base - -__all__ = [ - "set_user_key", - "authenticate", - "delete_user", - "get_user", - "get_user_demo", - "svg_base", -] diff --git a/backend/src/processing/layer3/user/__init__.py b/backend/src/processing/layer3/user/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/src/render/top_langs.py b/backend/src/render/top_langs.py index 66760431..16fc97f2 100644 --- a/backend/src/render/top_langs.py +++ b/backend/src/render/top_langs.py @@ -4,13 +4,9 @@ from svgwrite import Drawing -from backend.src.processing.layer2 import LanguageStats +from src.processing.layer2 import LanguageStats from src.render.error import get_no_data_svg -from src.render.template import ( - get_bar_section, - get_lang_name_section, - get_template, -) +from src.render.template import get_bar_section, get_lang_name_section, get_template from src.utils import format_number diff --git a/backend/src/render/top_repos.py b/backend/src/render/top_repos.py index 38b5d9a4..8932a31a 100644 --- a/backend/src/render/top_repos.py +++ b/backend/src/render/top_repos.py @@ -4,13 +4,9 @@ from svgwrite import Drawing -from src.processing.layer2.user.models import RepoStats +from src.processing.layer2.models import RepoStats from src.render.error import get_no_data_svg -from src.render.template import ( - get_bar_section, - get_lang_name_section, - get_template, -) +from src.render.template import get_bar_section, get_lang_name_section, get_template from src.utils import format_number diff --git a/backend/src/routers/auth/standalone.py b/backend/src/routers/auth/standalone.py index 0844c038..8be0b77a 100644 --- a/backend/src/routers/auth/standalone.py +++ b/backend/src/routers/auth/standalone.py @@ -1,11 +1,11 @@ import logging from typing import Optional -from backend.src.processing.layer3 import authenticate, delete_user from fastapi import APIRouter from fastapi.responses import RedirectResponse from src.constants import OAUTH_CLIENT_ID +from src.processing.layer2 import authenticate, delete_user from src.routers.decorators import get_redirect_url router = APIRouter() diff --git a/backend/src/routers/auth/website.py b/backend/src/routers/auth/website.py index f6e2422d..c19c8337 100644 --- a/backend/src/routers/auth/website.py +++ b/backend/src/routers/auth/website.py @@ -4,7 +4,7 @@ from fastapi.responses import Response from fastapi.routing import APIRouter -from src.publisher.processing import authenticate, delete_user, set_user_key +from src.processing.layer2 import authenticate, delete_user, set_user_key from src.utils import async_fail_gracefully router = APIRouter() diff --git a/backend/src/routers/dev.py b/backend/src/routers/dev.py index 82adf2e9..51b2f9b8 100644 --- a/backend/src/routers/dev.py +++ b/backend/src/routers/dev.py @@ -6,7 +6,7 @@ from src.data.mongo.secret import update_keys from src.models import UserPackage, WrappedPackage -from src.subscriber.aggregation import get_user_data, get_wrapped_data +from src.processing.layer0 import get_user_data, get_wrapped_data from src.utils import async_fail_gracefully, use_time_range router = APIRouter() diff --git a/backend/src/routers/users/main.py b/backend/src/routers/users/main.py index c5974fa3..5853bacb 100644 --- a/backend/src/routers/users/main.py +++ b/backend/src/routers/users/main.py @@ -1,10 +1,10 @@ from datetime import date, timedelta from typing import Optional -from backend.src.processing.layer3 import get_user from fastapi import APIRouter, Response, status from src.models import UserPackage +from src.processing.layer2 import get_user from src.routers.users.db import router as db_router from src.routers.users.svg import router as svg_router from src.utils import async_fail_gracefully diff --git a/backend/src/routers/users/svg.py b/backend/src/routers/users/svg.py index 9c8e6e37..d1dc72d9 100644 --- a/backend/src/routers/users/svg.py +++ b/backend/src/routers/users/svg.py @@ -1,12 +1,11 @@ from datetime import date, timedelta from typing import Any -from backend.src.processing.layer2 import get_top_languages, get_top_repos -from backend.src.processing.layer3 import svg_base from fastapi import Response, status from fastapi.responses import HTMLResponse from fastapi.routing import APIRouter +from src.processing.layer2 import get_top_languages, get_top_repos, svg_base from src.render import ( get_empty_demo_svg, get_loading_svg, diff --git a/backend/tests/subscriber/aggregation/test_contributions.py b/backend/tests/subscriber/aggregation/test_contributions.py index 54bd26c4..5cb5eaa3 100644 --- a/backend/tests/subscriber/aggregation/test_contributions.py +++ b/backend/tests/subscriber/aggregation/test_contributions.py @@ -4,7 +4,7 @@ from src.constants import TEST_TOKEN as TOKEN, TEST_USER_ID as USER_ID from src.models import UserContributions -from src.subscriber.aggregation import get_contributions +from src.processing.layer0 import get_contributions class TestTemplate(AsyncTestCase): diff --git a/backend/tests/subscriber/aggregation/test_follows.py b/backend/tests/subscriber/aggregation/test_follows.py index ed7b02ac..8dc77593 100644 --- a/backend/tests/subscriber/aggregation/test_follows.py +++ b/backend/tests/subscriber/aggregation/test_follows.py @@ -2,7 +2,7 @@ from src.constants import TEST_TOKEN as TOKEN, TEST_USER_ID as USER_ID from src.models import UserFollows -from src.subscriber.aggregation import get_user_follows +from src.processing.layer0 import get_user_follows class TestTemplate(unittest.TestCase): From 310429725a60452a7a3cc273020520e940b385f7 Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Mon, 20 Nov 2023 23:53:01 -0500 Subject: [PATCH 07/14] Order inits --- backend/src/data/github/graphql/__init__.py | 18 ++++++------- backend/src/data/github/rest/__init__.py | 8 +++--- backend/src/data/mongo/secret/__init__.py | 2 +- backend/src/data/mongo/user/__init__.py | 8 +++--- .../src/data/mongo/user_months/__init__.py | 2 +- backend/src/main.py | 5 ++-- backend/src/models/__init__.py | 26 +++++++++---------- backend/src/processing/layer0/__init__.py | 10 +++---- .../src/processing/layer0/user/languages.py | 2 +- backend/src/processing/layer1/__init__.py | 2 +- backend/src/processing/layer2/__init__.py | 10 +++---- backend/src/render/__init__.py | 2 +- backend/src/utils/__init__.py | 4 +-- 13 files changed, 48 insertions(+), 51 deletions(-) diff --git a/backend/src/data/github/graphql/__init__.py b/backend/src/data/github/graphql/__init__.py index 9d71df77..002a18ec 100644 --- a/backend/src/data/github/graphql/__init__.py +++ b/backend/src/data/github/graphql/__init__.py @@ -24,21 +24,21 @@ from src.data.github.graphql.user.follows.models import RawFollows __all__ = [ - "get_user_contribution_calendar", - "get_user_contribution_events", - "get_user_followers", - "get_user_following", "get_commits", - "get_repo", "RawCommit", "RawRepo", + "get_repo", + "GraphQLErrorMissingNode", + "GraphQLErrorRateLimit", + "GraphQLErrorTimeout", + "get_query_limit", + "get_user_contribution_calendar", + "get_user_contribution_events", "RawCalendar", "RawEvents", "RawEventsCommit", "RawEventsEvent", + "get_user_followers", + "get_user_following", "RawFollows", - "get_query_limit", - "GraphQLErrorRateLimit", - "GraphQLErrorTimeout", - "GraphQLErrorMissingNode", ] diff --git a/backend/src/data/github/rest/__init__.py b/backend/src/data/github/rest/__init__.py index 6fc0717c..008de45e 100644 --- a/backend/src/data/github/rest/__init__.py +++ b/backend/src/data/github/rest/__init__.py @@ -5,13 +5,13 @@ from src.data.github.rest.user import get_user, get_user_starred_repos __all__ = [ - "get_repo_commits", - "get_repo_stargazers", - "get_user", - "get_user_starred_repos", "get_commit_files", "RawCommit", "RawCommitFile", + "get_repo_commits", + "get_repo_stargazers", "RESTError", "RESTErrorNotFound", + "get_user", + "get_user_starred_repos", ] diff --git a/backend/src/data/mongo/secret/__init__.py b/backend/src/data/mongo/secret/__init__.py index 4a786790..2ed1ba10 100644 --- a/backend/src/data/mongo/secret/__init__.py +++ b/backend/src/data/mongo/secret/__init__.py @@ -1,3 +1,3 @@ from src.data.mongo.secret.functions import get_random_key, update_keys -__all__ = ["update_keys", "get_random_key"] +__all__ = ["get_random_key", "update_keys"] diff --git a/backend/src/data/mongo/user/__init__.py b/backend/src/data/mongo/user/__init__.py index ce37339d..a33c74fa 100644 --- a/backend/src/data/mongo/user/__init__.py +++ b/backend/src/data/mongo/user/__init__.py @@ -3,11 +3,11 @@ from src.data.mongo.user.models import FullUserModel, PublicUserModel __all__ = [ - "get_full_user", - "get_public_user", + "delete_user", "is_user_key", "update_user", - "delete_user", - "PublicUserModel", + "get_full_user", + "get_public_user", "FullUserModel", + "PublicUserModel", ] diff --git a/backend/src/data/mongo/user_months/__init__.py b/backend/src/data/mongo/user_months/__init__.py index a9c0b980..1619d06e 100644 --- a/backend/src/data/mongo/user_months/__init__.py +++ b/backend/src/data/mongo/user_months/__init__.py @@ -2,4 +2,4 @@ from src.data.mongo.user_months.get import get_user_months from src.data.mongo.user_months.models import UserMonth -__all__ = ["get_user_months", "set_user_month", "UserMonth"] +__all__ = ["set_user_month", "get_user_months", "UserMonth"] diff --git a/backend/src/main.py b/backend/src/main.py index a58a20d9..c5942ace 100644 --- a/backend/src/main.py +++ b/backend/src/main.py @@ -60,9 +60,8 @@ def get_info() -> Dict[str, bool]: return {"PROD": PROD} -app.include_router(user_router, prefix="/user", tags=["Users"]) -app.include_router(auth_router, prefix="/auth", tags=["Auth"]) app.include_router(asset_router, prefix="/assets", tags=["Assets"]) - +app.include_router(auth_router, prefix="/auth", tags=["Auth"]) app.include_router(dev_router, prefix="/dev", tags=["Dev"]) +app.include_router(user_router, prefix="/user", tags=["Users"]) app.include_router(wrapped_router, prefix="/wrapped", tags=["Wrapped"]) diff --git a/backend/src/models/__init__.py b/backend/src/models/__init__.py index a1e14fe6..a9f4ca8c 100644 --- a/backend/src/models/__init__.py +++ b/backend/src/models/__init__.py @@ -19,30 +19,28 @@ from src.models.wrapped.timestamps import TimestampData, TimestampDatum __all__ = [ - # User - "UserPackage", - "UserContributions", "ContributionDay", - "RepoContributionStats", "Language", + "RepoContributionStats", + "UserContributions", "User", "UserFollows", - # Wrapped - "WrappedPackage", + "UserPackage", + "CalendarData", "CalendarDayDatum", "CalendarLanguageDayDatum", - "CalendarData", "LangData", "LangDatum", - "MonthData", - "DayData", - "TimeDatum", + "WrappedPackage", + "ContribStats", + "LOCStats", + "MiscStats", + "NumericData", "RepoData", "RepoDatum", + "DayData", + "MonthData", + "TimeDatum", "TimestampData", "TimestampDatum", - "NumericData", - "ContribStats", - "LOCStats", - "MiscStats", ] diff --git a/backend/src/processing/layer0/__init__.py b/backend/src/processing/layer0/__init__.py index 99a607e4..b3cf0728 100644 --- a/backend/src/processing/layer0/__init__.py +++ b/backend/src/processing/layer0/__init__.py @@ -12,12 +12,12 @@ from src.processing.layer0.wrapped import get_wrapped_data __all__ = [ - "get_contributions", - "get_user_follows", - "get_user_data", - "get_valid_github_user", - "get_valid_db_user", "get_repo_stargazers", "get_user_stars", + "get_valid_db_user", + "get_valid_github_user", + "get_contributions", + "get_user_data", + "get_user_follows", "get_wrapped_data", ] diff --git a/backend/src/processing/layer0/user/languages.py b/backend/src/processing/layer0/user/languages.py index eeb5ec51..bb3a37c4 100644 --- a/backend/src/processing/layer0/user/languages.py +++ b/backend/src/processing/layer0/user/languages.py @@ -27,7 +27,7 @@ def add_lines( if name not in self.langs: self.langs[name] = {"color": color, "additions": 0, "deletions": 0} self.langs[name]["additions"] += additions # type: ignore - self.langs[name]["deletions"] += deletions + self.langs[name]["deletions"] += deletions # type: ignore def normalize(self, add_ratio: float, del_ratio: float): for lang in self.langs: diff --git a/backend/src/processing/layer1/__init__.py b/backend/src/processing/layer1/__init__.py index f79d1139..f89f772a 100644 --- a/backend/src/processing/layer1/__init__.py +++ b/backend/src/processing/layer1/__init__.py @@ -3,7 +3,7 @@ from src.processing.layer1.wrapped import query_wrapped_user __all__ = [ + "get_is_valid_user", "query_user", "query_wrapped_user", - "get_is_valid_user", ] diff --git a/backend/src/processing/layer2/__init__.py b/backend/src/processing/layer2/__init__.py index edc02085..9c138c3c 100644 --- a/backend/src/processing/layer2/__init__.py +++ b/backend/src/processing/layer2/__init__.py @@ -5,14 +5,14 @@ from src.processing.layer2.svg import svg_base __all__ = [ - "get_top_languages", - "get_top_repos", - "LanguageStats", - "RepoStats", - "set_user_key", "authenticate", "delete_user", + "set_user_key", + "get_top_languages", + "get_top_repos", "get_user", "get_user_demo", + "LanguageStats", + "RepoStats", "svg_base", ] diff --git a/backend/src/render/__init__.py b/backend/src/render/__init__.py index 0503646f..ad179f3a 100644 --- a/backend/src/render/__init__.py +++ b/backend/src/render/__init__.py @@ -9,9 +9,9 @@ __all__ = [ "get_empty_demo_svg", - "get_no_data_svg", "get_error_svg", "get_loading_svg", + "get_no_data_svg", "get_top_langs_svg", "get_top_repos_svg", ] diff --git a/backend/src/utils/__init__.py b/backend/src/utils/__init__.py index 23ff5fd6..52532cbd 100644 --- a/backend/src/utils/__init__.py +++ b/backend/src/utils/__init__.py @@ -5,10 +5,10 @@ __all__ = [ "alru_cache", - "fail_gracefully", "async_fail_gracefully", + "fail_gracefully", "gather", "date_to_datetime", - "use_time_range", "format_number", + "use_time_range", ] From f5190fd87a9d864fc2ab03d4354daa595e808a1b Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Mon, 20 Nov 2023 23:56:19 -0500 Subject: [PATCH 08/14] Start update_user --- backend/src/processing/layer2/auth.py | 1 + backend/src/processing/layer2/get_data.py | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/backend/src/processing/layer2/auth.py b/backend/src/processing/layer2/auth.py index 2d0817b5..16c6544e 100644 --- a/backend/src/processing/layer2/auth.py +++ b/backend/src/processing/layer2/auth.py @@ -7,6 +7,7 @@ get_public_user as db_get_public_user, update_user as db_update_user, ) +from src.processing.layer2.get_data import update_user # frontend first calls set_user_key with code and user_key # next they call authenticate which determines the user_id to associate with the code/user_key diff --git a/backend/src/processing/layer2/get_data.py b/backend/src/processing/layer2/get_data.py index d70b6130..d1e019b8 100644 --- a/backend/src/processing/layer2/get_data.py +++ b/backend/src/processing/layer2/get_data.py @@ -24,6 +24,14 @@ async def _get_user( return user_data.trim(start_date, end_date) +@alru_cache() +async def update_user( + user_id: str, access_token: str, private_access: bool +) -> Tuple[bool, bool]: + # TODO: implement this + return (True, True) + + @alru_cache() async def get_user( user_id: str, From 572f7d787ae07324a37e88c02b981ccc1cb75af4 Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Thu, 23 Nov 2023 14:01:02 -0500 Subject: [PATCH 09/14] More cleanup --- backend/src/aggregation/layer0/__init__.py | 3 +++ .../layer0}/contributions.py | 2 +- .../user => aggregation/layer0}/follows.py | 0 .../user => aggregation/layer0}/languages.py | 0 .../user => aggregation/layer0}/package.py | 2 +- backend/src/aggregation/layer1/__init__.py | 3 +++ .../layer0 => aggregation/layer1}/auth.py | 0 .../layer1/user.py | 13 +++++++---- backend/src/aggregation/layer2/__init__.py | 4 ++++ .../layer1 => aggregation/layer2}/auth.py | 7 +++--- .../layer2/user.py} | 6 ++++- backend/src/data/mongo/user/models.py | 1 + .../layer2/models.py => models/svg.py} | 0 backend/src/processing/{layer2 => }/auth.py | 2 +- backend/src/processing/layer0/__init__.py | 23 ------------------- .../src/processing/layer0/user/__init__.py | 9 -------- .../src/processing/layer0/wrapped/__init__.py | 3 --- backend/src/processing/layer1/__init__.py | 9 -------- backend/src/processing/layer2/__init__.py | 18 --------------- backend/src/processing/user/__init__.py | 4 ++++ .../processing/{layer2 => user}/commits.py | 2 +- .../src/processing/{layer2 => user}/svg.py | 2 +- backend/src/processing/wrapped/__init__.py | 3 +++ .../{layer0 => }/wrapped/calendar.py | 0 .../processing/{layer0 => }/wrapped/langs.py | 0 .../{layer1/wrapped.py => wrapped/main.py} | 4 ++-- .../{layer0 => }/wrapped/numeric.py | 0 .../{layer0 => }/wrapped/package.py | 14 +++++------ .../processing/{layer0 => }/wrapped/repos.py | 0 .../processing/{layer0 => }/wrapped/time.py | 0 .../{layer0 => }/wrapped/timestamps.py | 0 backend/src/render/top_langs.py | 2 +- backend/src/render/top_repos.py | 2 +- backend/src/routers/auth/standalone.py | 2 +- backend/src/routers/auth/website.py | 2 +- backend/src/routers/dev.py | 10 ++------ backend/src/routers/users/db.py | 7 ++++-- backend/src/routers/users/main.py | 6 ++--- backend/src/routers/users/svg.py | 2 +- backend/src/routers/wrapped.py | 6 ++--- .../{subscriber => aggregation}/__init__.py | 0 .../layer0}/__init__.py | 0 .../layer0}/test_contributions.py | 2 +- .../layer0}/test_follows.py | 2 +- frontend/src/constants.js | 6 ++++- 45 files changed, 74 insertions(+), 109 deletions(-) create mode 100644 backend/src/aggregation/layer0/__init__.py rename backend/src/{processing/layer0/user => aggregation/layer0}/contributions.py (99%) rename backend/src/{processing/layer0/user => aggregation/layer0}/follows.py (100%) rename backend/src/{processing/layer0/user => aggregation/layer0}/languages.py (100%) rename backend/src/{processing/layer0/user => aggregation/layer0}/package.py (90%) create mode 100644 backend/src/aggregation/layer1/__init__.py rename backend/src/{processing/layer0 => aggregation/layer1}/auth.py (100%) rename backend/src/{processing => aggregation}/layer1/user.py (93%) create mode 100644 backend/src/aggregation/layer2/__init__.py rename backend/src/{processing/layer1 => aggregation/layer2}/auth.py (97%) rename backend/src/{processing/layer2/get_data.py => aggregation/layer2/user.py} (93%) rename backend/src/{processing/layer2/models.py => models/svg.py} (100%) rename backend/src/processing/{layer2 => }/auth.py (96%) delete mode 100644 backend/src/processing/layer0/__init__.py delete mode 100644 backend/src/processing/layer0/user/__init__.py delete mode 100644 backend/src/processing/layer0/wrapped/__init__.py delete mode 100644 backend/src/processing/layer1/__init__.py delete mode 100644 backend/src/processing/layer2/__init__.py create mode 100644 backend/src/processing/user/__init__.py rename backend/src/processing/{layer2 => user}/commits.py (98%) rename backend/src/processing/{layer2 => user}/svg.py (91%) create mode 100644 backend/src/processing/wrapped/__init__.py rename backend/src/processing/{layer0 => }/wrapped/calendar.py (100%) rename backend/src/processing/{layer0 => }/wrapped/langs.py (100%) rename backend/src/processing/{layer1/wrapped.py => wrapped/main.py} (90%) rename backend/src/processing/{layer0 => }/wrapped/numeric.py (100%) rename backend/src/processing/{layer0 => }/wrapped/package.py (63%) rename backend/src/processing/{layer0 => }/wrapped/repos.py (100%) rename backend/src/processing/{layer0 => }/wrapped/time.py (100%) rename backend/src/processing/{layer0 => }/wrapped/timestamps.py (100%) rename backend/tests/{subscriber => aggregation}/__init__.py (100%) rename backend/tests/{subscriber/aggregation => aggregation/layer0}/__init__.py (100%) rename backend/tests/{subscriber/aggregation => aggregation/layer0}/test_contributions.py (89%) rename backend/tests/{subscriber/aggregation => aggregation/layer0}/test_follows.py (85%) diff --git a/backend/src/aggregation/layer0/__init__.py b/backend/src/aggregation/layer0/__init__.py new file mode 100644 index 00000000..9a4ac758 --- /dev/null +++ b/backend/src/aggregation/layer0/__init__.py @@ -0,0 +1,3 @@ +from src.aggregation.layer0.package import get_user_data + +__all__ = ["get_user_data"] diff --git a/backend/src/processing/layer0/user/contributions.py b/backend/src/aggregation/layer0/contributions.py similarity index 99% rename from backend/src/processing/layer0/user/contributions.py rename to backend/src/aggregation/layer0/contributions.py index c28e7102..5e9caa02 100644 --- a/backend/src/processing/layer0/user/contributions.py +++ b/backend/src/aggregation/layer0/contributions.py @@ -29,7 +29,7 @@ get_repo_commits, ) from src.models import UserContributions -from src.processing.layer0.user.languages import CommitLanguages, get_commit_languages +from src.aggregation.layer0.languages import CommitLanguages, get_commit_languages from src.utils import date_to_datetime, gather diff --git a/backend/src/processing/layer0/user/follows.py b/backend/src/aggregation/layer0/follows.py similarity index 100% rename from backend/src/processing/layer0/user/follows.py rename to backend/src/aggregation/layer0/follows.py diff --git a/backend/src/processing/layer0/user/languages.py b/backend/src/aggregation/layer0/languages.py similarity index 100% rename from backend/src/processing/layer0/user/languages.py rename to backend/src/aggregation/layer0/languages.py diff --git a/backend/src/processing/layer0/user/package.py b/backend/src/aggregation/layer0/package.py similarity index 90% rename from backend/src/processing/layer0/user/package.py rename to backend/src/aggregation/layer0/package.py index e4d2716d..2c6b273e 100644 --- a/backend/src/processing/layer0/user/package.py +++ b/backend/src/aggregation/layer0/package.py @@ -2,7 +2,7 @@ from typing import Optional from src.models import UserPackage -from src.processing.layer0.user.contributions import get_contributions +from src.aggregation.layer0.contributions import get_contributions # from src.subscriber.aggregation.user.follows import get_user_follows diff --git a/backend/src/aggregation/layer1/__init__.py b/backend/src/aggregation/layer1/__init__.py new file mode 100644 index 00000000..204d5445 --- /dev/null +++ b/backend/src/aggregation/layer1/__init__.py @@ -0,0 +1,3 @@ +from src.aggregation.layer1.user import query_user + +__all__ = ["query_user"] diff --git a/backend/src/processing/layer0/auth.py b/backend/src/aggregation/layer1/auth.py similarity index 100% rename from backend/src/processing/layer0/auth.py rename to backend/src/aggregation/layer1/auth.py diff --git a/backend/src/processing/layer1/user.py b/backend/src/aggregation/layer1/user.py similarity index 93% rename from backend/src/processing/layer1/user.py rename to backend/src/aggregation/layer1/user.py index 33588c04..08098a9d 100644 --- a/backend/src/processing/layer1/user.py +++ b/backend/src/aggregation/layer1/user.py @@ -9,12 +9,15 @@ from src.data.mongo.secret import update_keys from src.data.mongo.user_months import UserMonth, get_user_months, set_user_month from src.models.user.main import UserPackage -from src.processing.layer0 import get_user_data +from src.aggregation.layer0.package import get_user_data from src.utils import alru_cache, date_to_datetime s = requests.Session() +# Formerly the subscriber, compute and save new data here + + async def query_user_month( user_id: str, access_token: Optional[str], @@ -83,17 +86,17 @@ async def query_user( curr_months = [x.month for x in curr_data if x.complete] month, year = start_date.month, start_date.year - months: List[date] = [] + new_months: List[date] = [] while date(year, month, 1) <= end_date: start = date(year, month, 1) if date_to_datetime(start) not in curr_months: - months.append(start) + new_months.append(start) month = month % 12 + 1 year = year + (month == 1) # Start with complete months and add any incomplete months all_user_packages: List[UserPackage] = [x.data for x in curr_data if x.complete] - for month in months: + for month in new_months: if datetime.now() - start_time < timedelta(seconds=40): temp = await query_user_month(user_id, access_token, private_access, month) if temp is not None: @@ -110,7 +113,7 @@ async def query_user( out += user_package out.incomplete = incomplete - if incomplete or len(months) > 1: + if incomplete or len(new_months) > 1: # cache buster for publisher if PROD: s.get(f"{BACKEND_URL}/user/{user_id}?no_cache=True") diff --git a/backend/src/aggregation/layer2/__init__.py b/backend/src/aggregation/layer2/__init__.py new file mode 100644 index 00000000..204baa8e --- /dev/null +++ b/backend/src/aggregation/layer2/__init__.py @@ -0,0 +1,4 @@ +from src.aggregation.layer2.user import get_user, get_user_demo +from src.aggregation.layer2.auth import get_is_valid_user + +__all__ = ["get_user", "get_user_demo", "get_is_valid_user"] diff --git a/backend/src/processing/layer1/auth.py b/backend/src/aggregation/layer2/auth.py similarity index 97% rename from backend/src/processing/layer1/auth.py rename to backend/src/aggregation/layer2/auth.py index 12fbe781..fcd2dfa3 100644 --- a/backend/src/processing/layer1/auth.py +++ b/backend/src/aggregation/layer2/auth.py @@ -3,14 +3,15 @@ from src.constants import OWNER, REPO from src.data.github.rest import RESTError -from src.processing.layer0 import ( - get_repo_stargazers, - get_user_stars, +from src.aggregation.layer1.auth import ( get_valid_db_user, get_valid_github_user, + get_repo_stargazers, + get_user_stars, ) from src.utils import alru_cache + USER_WHITELIST = [ "torvalds", "fchollet", diff --git a/backend/src/processing/layer2/get_data.py b/backend/src/aggregation/layer2/user.py similarity index 93% rename from backend/src/processing/layer2/get_data.py rename to backend/src/aggregation/layer2/user.py index d1e019b8..538fb364 100644 --- a/backend/src/processing/layer2/get_data.py +++ b/backend/src/aggregation/layer2/user.py @@ -5,10 +5,13 @@ from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user from src.data.mongo.user_months import get_user_months from src.models import UserPackage -from src.processing.layer0 import get_user_data +from src.aggregation.layer0 import get_user_data from src.utils import alru_cache +# Formerly the publisher, loads existing data here + + async def _get_user( user_id: str, private_access: bool, start_date: date, end_date: date ) -> Optional[UserPackage]: @@ -55,6 +58,7 @@ async def get_user_demo( ) -> Tuple[bool, UserPackage]: await update_keys() timezone_str = "US/Eastern" + # recompute/cache but don't save to db data = await get_user_data( user_id=user_id, start_date=start_date, diff --git a/backend/src/data/mongo/user/models.py b/backend/src/data/mongo/user/models.py index cf9746a3..26ef9320 100644 --- a/backend/src/data/mongo/user/models.py +++ b/backend/src/data/mongo/user/models.py @@ -9,6 +9,7 @@ class PublicUserModel(BaseModel): private_access: Optional[bool] class Config: + from_attributes = True validate_assignment = True @validator("private_access", pre=True, always=True) diff --git a/backend/src/processing/layer2/models.py b/backend/src/models/svg.py similarity index 100% rename from backend/src/processing/layer2/models.py rename to backend/src/models/svg.py diff --git a/backend/src/processing/layer2/auth.py b/backend/src/processing/auth.py similarity index 96% rename from backend/src/processing/layer2/auth.py rename to backend/src/processing/auth.py index 16c6544e..b783fddd 100644 --- a/backend/src/processing/layer2/auth.py +++ b/backend/src/processing/auth.py @@ -7,7 +7,7 @@ get_public_user as db_get_public_user, update_user as db_update_user, ) -from src.processing.layer2.get_data import update_user +from backend.src.aggregation.layer2.user import update_user # frontend first calls set_user_key with code and user_key # next they call authenticate which determines the user_id to associate with the code/user_key diff --git a/backend/src/processing/layer0/__init__.py b/backend/src/processing/layer0/__init__.py deleted file mode 100644 index b3cf0728..00000000 --- a/backend/src/processing/layer0/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -from src.processing.layer0.auth import ( - get_repo_stargazers, - get_user_stars, - get_valid_db_user, - get_valid_github_user, -) -from src.processing.layer0.user import ( - get_contributions, - get_user_data, - get_user_follows, -) -from src.processing.layer0.wrapped import get_wrapped_data - -__all__ = [ - "get_repo_stargazers", - "get_user_stars", - "get_valid_db_user", - "get_valid_github_user", - "get_contributions", - "get_user_data", - "get_user_follows", - "get_wrapped_data", -] diff --git a/backend/src/processing/layer0/user/__init__.py b/backend/src/processing/layer0/user/__init__.py deleted file mode 100644 index 36b754b5..00000000 --- a/backend/src/processing/layer0/user/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from src.processing.layer0.user.contributions import get_contributions -from src.processing.layer0.user.follows import get_user_follows -from src.processing.layer0.user.package import get_user_data - -__all__ = [ - "get_contributions", - "get_user_follows", - "get_user_data", -] diff --git a/backend/src/processing/layer0/wrapped/__init__.py b/backend/src/processing/layer0/wrapped/__init__.py deleted file mode 100644 index 07424616..00000000 --- a/backend/src/processing/layer0/wrapped/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from src.processing.layer0.wrapped.package import main as get_wrapped_data - -__all__ = ["get_wrapped_data"] diff --git a/backend/src/processing/layer1/__init__.py b/backend/src/processing/layer1/__init__.py deleted file mode 100644 index f89f772a..00000000 --- a/backend/src/processing/layer1/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from src.processing.layer1.auth import get_is_valid_user -from src.processing.layer1.user import query_user -from src.processing.layer1.wrapped import query_wrapped_user - -__all__ = [ - "get_is_valid_user", - "query_user", - "query_wrapped_user", -] diff --git a/backend/src/processing/layer2/__init__.py b/backend/src/processing/layer2/__init__.py deleted file mode 100644 index 9c138c3c..00000000 --- a/backend/src/processing/layer2/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -from src.processing.layer2.auth import authenticate, delete_user, set_user_key -from src.processing.layer2.commits import get_top_languages, get_top_repos -from src.processing.layer2.get_data import get_user, get_user_demo -from src.processing.layer2.models import LanguageStats, RepoStats -from src.processing.layer2.svg import svg_base - -__all__ = [ - "authenticate", - "delete_user", - "set_user_key", - "get_top_languages", - "get_top_repos", - "get_user", - "get_user_demo", - "LanguageStats", - "RepoStats", - "svg_base", -] diff --git a/backend/src/processing/user/__init__.py b/backend/src/processing/user/__init__.py new file mode 100644 index 00000000..c34d1792 --- /dev/null +++ b/backend/src/processing/user/__init__.py @@ -0,0 +1,4 @@ +from src.processing.user.commits import get_top_languages, get_top_repos +from src.processing.user.svg import svg_base + +__all__ = ["get_top_languages", "get_top_repos", "svg_base"] diff --git a/backend/src/processing/layer2/commits.py b/backend/src/processing/user/commits.py similarity index 98% rename from backend/src/processing/layer2/commits.py rename to backend/src/processing/user/commits.py index dc33c9b6..cd47ce48 100644 --- a/backend/src/processing/layer2/commits.py +++ b/backend/src/processing/user/commits.py @@ -2,7 +2,7 @@ from src.constants import DEFAULT_COLOR from src.models import UserPackage -from src.processing.layer2.models import LanguageStats, RepoStats +from backend.src.models.svg import LanguageStats, RepoStats dict_type = Dict[str, Union[str, int, float]] diff --git a/backend/src/processing/layer2/svg.py b/backend/src/processing/user/svg.py similarity index 91% rename from backend/src/processing/layer2/svg.py rename to backend/src/processing/user/svg.py index e22c9830..921f9fff 100644 --- a/backend/src/processing/layer2/svg.py +++ b/backend/src/processing/user/svg.py @@ -2,7 +2,7 @@ from typing import Optional, Tuple from src.models import UserPackage -from src.processing.layer2.get_data import get_user, get_user_demo +from backend.src.aggregation.layer2.user import get_user, get_user_demo from src.utils import use_time_range diff --git a/backend/src/processing/wrapped/__init__.py b/backend/src/processing/wrapped/__init__.py new file mode 100644 index 00000000..a04782ee --- /dev/null +++ b/backend/src/processing/wrapped/__init__.py @@ -0,0 +1,3 @@ +from src.processing.wrapped.main import query_wrapped_user + +__all__ = ["query_wrapped_user"] diff --git a/backend/src/processing/layer0/wrapped/calendar.py b/backend/src/processing/wrapped/calendar.py similarity index 100% rename from backend/src/processing/layer0/wrapped/calendar.py rename to backend/src/processing/wrapped/calendar.py diff --git a/backend/src/processing/layer0/wrapped/langs.py b/backend/src/processing/wrapped/langs.py similarity index 100% rename from backend/src/processing/layer0/wrapped/langs.py rename to backend/src/processing/wrapped/langs.py diff --git a/backend/src/processing/layer1/wrapped.py b/backend/src/processing/wrapped/main.py similarity index 90% rename from backend/src/processing/layer1/wrapped.py rename to backend/src/processing/wrapped/main.py index e78252b5..3ffad75c 100644 --- a/backend/src/processing/layer1/wrapped.py +++ b/backend/src/processing/wrapped/main.py @@ -3,8 +3,8 @@ from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user from src.models import UserPackage, WrappedPackage -from src.processing.layer0 import get_wrapped_data -from src.processing.layer1.user import query_user +from src.processing.wrapped.package import get_wrapped_data +from src.aggregation.layer1 import query_user from src.utils import alru_cache diff --git a/backend/src/processing/layer0/wrapped/numeric.py b/backend/src/processing/wrapped/numeric.py similarity index 100% rename from backend/src/processing/layer0/wrapped/numeric.py rename to backend/src/processing/wrapped/numeric.py diff --git a/backend/src/processing/layer0/wrapped/package.py b/backend/src/processing/wrapped/package.py similarity index 63% rename from backend/src/processing/layer0/wrapped/package.py rename to backend/src/processing/wrapped/package.py index bc747a7c..8d451b27 100644 --- a/backend/src/processing/layer0/wrapped/package.py +++ b/backend/src/processing/wrapped/package.py @@ -1,15 +1,15 @@ from src.models import UserPackage, WrappedPackage -from src.processing.layer0.wrapped.calendar import get_calendar_data -from src.processing.layer0.wrapped.langs import get_lang_data -from src.processing.layer0.wrapped.numeric import get_numeric_data -from src.processing.layer0.wrapped.repos import get_repo_data -from src.processing.layer0.wrapped.time import get_day_data, get_month_data -from src.processing.layer0.wrapped.timestamps import get_timestamp_data +from src.processing.wrapped.calendar import get_calendar_data +from src.processing.wrapped.langs import get_lang_data +from src.processing.wrapped.numeric import get_numeric_data +from src.processing.wrapped.repos import get_repo_data +from src.processing.wrapped.time import get_day_data, get_month_data +from src.processing.wrapped.timestamps import get_timestamp_data # from src.processing.user.follows import get_user_follows -def main(user_package: UserPackage, year: int) -> WrappedPackage: +def get_wrapped_data(user_package: UserPackage, year: int) -> WrappedPackage: """packages all processing steps for the user query""" month_data = get_month_data(user_package) diff --git a/backend/src/processing/layer0/wrapped/repos.py b/backend/src/processing/wrapped/repos.py similarity index 100% rename from backend/src/processing/layer0/wrapped/repos.py rename to backend/src/processing/wrapped/repos.py diff --git a/backend/src/processing/layer0/wrapped/time.py b/backend/src/processing/wrapped/time.py similarity index 100% rename from backend/src/processing/layer0/wrapped/time.py rename to backend/src/processing/wrapped/time.py diff --git a/backend/src/processing/layer0/wrapped/timestamps.py b/backend/src/processing/wrapped/timestamps.py similarity index 100% rename from backend/src/processing/layer0/wrapped/timestamps.py rename to backend/src/processing/wrapped/timestamps.py diff --git a/backend/src/render/top_langs.py b/backend/src/render/top_langs.py index 16fc97f2..c9afc1e6 100644 --- a/backend/src/render/top_langs.py +++ b/backend/src/render/top_langs.py @@ -4,7 +4,7 @@ from svgwrite import Drawing -from src.processing.layer2 import LanguageStats +from src.models.svg import LanguageStats from src.render.error import get_no_data_svg from src.render.template import get_bar_section, get_lang_name_section, get_template from src.utils import format_number diff --git a/backend/src/render/top_repos.py b/backend/src/render/top_repos.py index 8932a31a..14eddcdc 100644 --- a/backend/src/render/top_repos.py +++ b/backend/src/render/top_repos.py @@ -4,7 +4,7 @@ from svgwrite import Drawing -from src.processing.layer2.models import RepoStats +from src.models.svg import RepoStats from src.render.error import get_no_data_svg from src.render.template import get_bar_section, get_lang_name_section, get_template from src.utils import format_number diff --git a/backend/src/routers/auth/standalone.py b/backend/src/routers/auth/standalone.py index 8be0b77a..8104c8ff 100644 --- a/backend/src/routers/auth/standalone.py +++ b/backend/src/routers/auth/standalone.py @@ -5,7 +5,7 @@ from fastapi.responses import RedirectResponse from src.constants import OAUTH_CLIENT_ID -from src.processing.layer2 import authenticate, delete_user +from src.processing.auth import authenticate, delete_user from src.routers.decorators import get_redirect_url router = APIRouter() diff --git a/backend/src/routers/auth/website.py b/backend/src/routers/auth/website.py index c19c8337..0be08baf 100644 --- a/backend/src/routers/auth/website.py +++ b/backend/src/routers/auth/website.py @@ -4,7 +4,7 @@ from fastapi.responses import Response from fastapi.routing import APIRouter -from src.processing.layer2 import authenticate, delete_user, set_user_key +from src.processing.auth import authenticate, delete_user, set_user_key from src.utils import async_fail_gracefully router = APIRouter() diff --git a/backend/src/routers/dev.py b/backend/src/routers/dev.py index 51b2f9b8..faf46c53 100644 --- a/backend/src/routers/dev.py +++ b/backend/src/routers/dev.py @@ -1,4 +1,3 @@ -import asyncio from datetime import date, timedelta from typing import Any, Dict, Optional @@ -6,7 +5,8 @@ from src.data.mongo.secret import update_keys from src.models import UserPackage, WrappedPackage -from src.processing.layer0 import get_user_data, get_wrapped_data +from src.aggregation.layer0 import get_user_data +from src.processing.wrapped.package import get_wrapped_data from src.utils import async_fail_gracefully, use_time_range router = APIRouter() @@ -48,9 +48,3 @@ async def get_wrapped_user_raw( user_id, date(year, 1, 1), date(year, 12, 31), "US/Eastern", access_token ) return get_wrapped_data(user_data, year) - - -async def print_task(start_str: str, end_str: str): - print(start_str) - await asyncio.sleep(10) - print(end_str) diff --git a/backend/src/routers/users/db.py b/backend/src/routers/users/db.py index af393509..de38265e 100644 --- a/backend/src/routers/users/db.py +++ b/backend/src/routers/users/db.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, Dict, Any from fastapi import APIRouter, Response, status @@ -9,7 +9,10 @@ @router.get( - "/get/metadata/{user_id}", status_code=status.HTTP_200_OK, include_in_schema=False + "/get/metadata/{user_id}", + status_code=status.HTTP_200_OK, + include_in_schema=False, + response_model=Dict[str, Any], ) @async_fail_gracefully async def get_db_public_user( diff --git a/backend/src/routers/users/main.py b/backend/src/routers/users/main.py index 5853bacb..5285ab71 100644 --- a/backend/src/routers/users/main.py +++ b/backend/src/routers/users/main.py @@ -1,10 +1,10 @@ from datetime import date, timedelta -from typing import Optional +from typing import Optional, Dict, Any from fastapi import APIRouter, Response, status from src.models import UserPackage -from src.processing.layer2 import get_user +from src.aggregation.layer2 import get_user from src.routers.users.db import router as db_router from src.routers.users.svg import router as svg_router from src.utils import async_fail_gracefully @@ -19,7 +19,7 @@ """ -@router.get("/{user_id}", status_code=status.HTTP_200_OK) +@router.get("/{user_id}", status_code=status.HTTP_200_OK, response_model=Dict[str, Any]) @async_fail_gracefully async def get_user_endpoint( response: Response, diff --git a/backend/src/routers/users/svg.py b/backend/src/routers/users/svg.py index d1dc72d9..d9d130a8 100644 --- a/backend/src/routers/users/svg.py +++ b/backend/src/routers/users/svg.py @@ -5,7 +5,7 @@ from fastapi.responses import HTMLResponse from fastapi.routing import APIRouter -from src.processing.layer2 import get_top_languages, get_top_repos, svg_base +from src.processing.user import get_top_languages, get_top_repos, svg_base from src.render import ( get_empty_demo_svg, get_loading_svg, diff --git a/backend/src/routers/wrapped.py b/backend/src/routers/wrapped.py index 45cc882b..6d8735af 100644 --- a/backend/src/routers/wrapped.py +++ b/backend/src/routers/wrapped.py @@ -3,7 +3,8 @@ from fastapi import APIRouter, Response, status from src.models import WrappedPackage -from src.processing.layer1 import get_is_valid_user, query_wrapped_user +from src.aggregation.layer2 import get_is_valid_user +from src.processing.wrapped import query_wrapped_user from src.utils import async_fail_gracefully router = APIRouter() @@ -14,8 +15,7 @@ ) @async_fail_gracefully async def check_valid_user(response: Response, user_id: str) -> str: - out = await get_is_valid_user(user_id) - return out + return await get_is_valid_user(user_id) @router.get("/{user_id}", status_code=status.HTTP_200_OK, response_model=Dict[str, Any]) diff --git a/backend/tests/subscriber/__init__.py b/backend/tests/aggregation/__init__.py similarity index 100% rename from backend/tests/subscriber/__init__.py rename to backend/tests/aggregation/__init__.py diff --git a/backend/tests/subscriber/aggregation/__init__.py b/backend/tests/aggregation/layer0/__init__.py similarity index 100% rename from backend/tests/subscriber/aggregation/__init__.py rename to backend/tests/aggregation/layer0/__init__.py diff --git a/backend/tests/subscriber/aggregation/test_contributions.py b/backend/tests/aggregation/layer0/test_contributions.py similarity index 89% rename from backend/tests/subscriber/aggregation/test_contributions.py rename to backend/tests/aggregation/layer0/test_contributions.py index 5cb5eaa3..14fe6eaf 100644 --- a/backend/tests/subscriber/aggregation/test_contributions.py +++ b/backend/tests/aggregation/layer0/test_contributions.py @@ -4,7 +4,7 @@ from src.constants import TEST_TOKEN as TOKEN, TEST_USER_ID as USER_ID from src.models import UserContributions -from src.processing.layer0 import get_contributions +from src.aggregation.layer0.contributions import get_contributions class TestTemplate(AsyncTestCase): diff --git a/backend/tests/subscriber/aggregation/test_follows.py b/backend/tests/aggregation/layer0/test_follows.py similarity index 85% rename from backend/tests/subscriber/aggregation/test_follows.py rename to backend/tests/aggregation/layer0/test_follows.py index 8dc77593..1f72c836 100644 --- a/backend/tests/subscriber/aggregation/test_follows.py +++ b/backend/tests/aggregation/layer0/test_follows.py @@ -2,7 +2,7 @@ from src.constants import TEST_TOKEN as TOKEN, TEST_USER_ID as USER_ID from src.models import UserFollows -from src.processing.layer0 import get_user_follows +from src.aggregation.layer0.follows import get_user_follows class TestTemplate(unittest.TestCase): diff --git a/frontend/src/constants.js b/frontend/src/constants.js index 16b3bbf2..4db49281 100644 --- a/frontend/src/constants.js +++ b/frontend/src/constants.js @@ -2,10 +2,14 @@ export const PROD = process.env.REACT_APP_PROD === 'true'; export const USE_LOGGER = true; -export const CLIENT_ID = process.env.REACT_APP_CLIENT_ID; +export const CLIENT_ID = PROD + ? process.env.REACT_APP_PROD_CLIENT_ID + : process.env.REACT_APP_DEV_CLIENT_ID; + export const REDIRECT_URI = PROD ? 'https://www.githubtrends.io/user' : 'http://localhost:3000/user'; + export const GITHUB_PRIVATE_AUTH_URL = `https://github.com/login/oauth/authorize?scope=user,repo&client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}/private`; export const GITHUB_PUBLIC_AUTH_URL = `https://github.com/login/oauth/authorize?client_id=${CLIENT_ID}&redirect_uri=${REDIRECT_URI}/public`; From dac316fa630252d5ba5e8c42fe2af5194f916c06 Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Thu, 23 Nov 2023 14:01:07 -0500 Subject: [PATCH 10/14] More --- backend/src/constants.py | 10 +++++++--- backend/src/data/mongo/user/get.py | 2 -- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/backend/src/constants.py b/backend/src/constants.py index 02c1c49c..534a0123 100644 --- a/backend/src/constants.py +++ b/backend/src/constants.py @@ -29,9 +29,13 @@ BLACKLIST = ["Jupyter Notebook", "HTML"] # languages to ignore # OAUTH -OAUTH_CLIENT_ID = os.getenv("OAUTH_CLIENT_ID", "") # client ID for GitHub OAuth App -OAUTH_CLIENT_SECRET = os.getenv("OAUTH_CLIENT_SECRET", "") # client secret for App -OAUTH_REDIRECT_URI = os.getenv("OAUTH_REDIRECT_URI", "") # redirect uri for App +prefix = "PROD" if PROD else "DEV" +# client ID for GitHub OAuth App +OAUTH_CLIENT_ID = os.getenv(f"{prefix}_OAUTH_CLIENT_ID", "") +# client secret for App +OAUTH_CLIENT_SECRET = os.getenv(f"{prefix}_OAUTH_CLIENT_SECRET", "") +# redirect uri for App +OAUTH_REDIRECT_URI = os.getenv(f"{prefix}_OAUTH_REDIRECT_URI", "") # MONGODB diff --git a/backend/src/data/mongo/user/get.py b/backend/src/data/mongo/user/get.py index 8a59f7c0..bf51ab94 100644 --- a/backend/src/data/mongo/user/get.py +++ b/backend/src/data/mongo/user/get.py @@ -12,11 +12,9 @@ async def get_public_user( user_id: str, no_cache: bool = False ) -> Tuple[bool, Optional[PublicUserModel]]: user: Optional[Dict[str, Any]] = await USERS.find_one({"user_id": user_id}) - if user is None: # flag is false, don't cache return (False, None) - try: return (True, PublicUserModel.model_validate(user)) except (TypeError, KeyError, ValidationError): From a064038230ec187a4205a77f1e7266aaaf9d71f1 Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Thu, 23 Nov 2023 14:01:27 -0500 Subject: [PATCH 11/14] isort --- backend/src/aggregation/layer0/contributions.py | 2 +- backend/src/aggregation/layer0/package.py | 2 +- backend/src/aggregation/layer1/user.py | 2 +- backend/src/aggregation/layer2/__init__.py | 2 +- backend/src/aggregation/layer2/auth.py | 9 ++++----- backend/src/aggregation/layer2/user.py | 3 +-- backend/src/processing/auth.py | 3 ++- backend/src/processing/user/commits.py | 3 ++- backend/src/processing/user/svg.py | 3 ++- backend/src/processing/wrapped/main.py | 2 +- backend/src/routers/dev.py | 2 +- backend/src/routers/users/db.py | 2 +- backend/src/routers/users/main.py | 4 ++-- backend/src/routers/wrapped.py | 2 +- backend/tests/aggregation/layer0/test_contributions.py | 2 +- backend/tests/aggregation/layer0/test_follows.py | 2 +- 16 files changed, 23 insertions(+), 22 deletions(-) diff --git a/backend/src/aggregation/layer0/contributions.py b/backend/src/aggregation/layer0/contributions.py index 5e9caa02..9514a9aa 100644 --- a/backend/src/aggregation/layer0/contributions.py +++ b/backend/src/aggregation/layer0/contributions.py @@ -4,6 +4,7 @@ import pytz +from src.aggregation.layer0.languages import CommitLanguages, get_commit_languages from src.constants import ( GRAPHQL_NODE_CHUNK_SIZE, GRAPHQL_NODE_THREADS, @@ -29,7 +30,6 @@ get_repo_commits, ) from src.models import UserContributions -from src.aggregation.layer0.languages import CommitLanguages, get_commit_languages from src.utils import date_to_datetime, gather diff --git a/backend/src/aggregation/layer0/package.py b/backend/src/aggregation/layer0/package.py index 2c6b273e..469996ff 100644 --- a/backend/src/aggregation/layer0/package.py +++ b/backend/src/aggregation/layer0/package.py @@ -1,8 +1,8 @@ from datetime import date from typing import Optional -from src.models import UserPackage from src.aggregation.layer0.contributions import get_contributions +from src.models import UserPackage # from src.subscriber.aggregation.user.follows import get_user_follows diff --git a/backend/src/aggregation/layer1/user.py b/backend/src/aggregation/layer1/user.py index 08098a9d..f525cdc6 100644 --- a/backend/src/aggregation/layer1/user.py +++ b/backend/src/aggregation/layer1/user.py @@ -4,12 +4,12 @@ import requests +from src.aggregation.layer0.package import get_user_data from src.constants import API_VERSION, BACKEND_URL, PROD from src.data.github.graphql import GraphQLErrorRateLimit from src.data.mongo.secret import update_keys from src.data.mongo.user_months import UserMonth, get_user_months, set_user_month from src.models.user.main import UserPackage -from src.aggregation.layer0.package import get_user_data from src.utils import alru_cache, date_to_datetime s = requests.Session() diff --git a/backend/src/aggregation/layer2/__init__.py b/backend/src/aggregation/layer2/__init__.py index 204baa8e..1ed2309f 100644 --- a/backend/src/aggregation/layer2/__init__.py +++ b/backend/src/aggregation/layer2/__init__.py @@ -1,4 +1,4 @@ -from src.aggregation.layer2.user import get_user, get_user_demo from src.aggregation.layer2.auth import get_is_valid_user +from src.aggregation.layer2.user import get_user, get_user_demo __all__ = ["get_user", "get_user_demo", "get_is_valid_user"] diff --git a/backend/src/aggregation/layer2/auth.py b/backend/src/aggregation/layer2/auth.py index fcd2dfa3..c971adf9 100644 --- a/backend/src/aggregation/layer2/auth.py +++ b/backend/src/aggregation/layer2/auth.py @@ -1,17 +1,16 @@ from datetime import timedelta from typing import Tuple -from src.constants import OWNER, REPO -from src.data.github.rest import RESTError from src.aggregation.layer1.auth import ( - get_valid_db_user, - get_valid_github_user, get_repo_stargazers, get_user_stars, + get_valid_db_user, + get_valid_github_user, ) +from src.constants import OWNER, REPO +from src.data.github.rest import RESTError from src.utils import alru_cache - USER_WHITELIST = [ "torvalds", "fchollet", diff --git a/backend/src/aggregation/layer2/user.py b/backend/src/aggregation/layer2/user.py index 538fb364..3b57fd19 100644 --- a/backend/src/aggregation/layer2/user.py +++ b/backend/src/aggregation/layer2/user.py @@ -1,14 +1,13 @@ from datetime import date, timedelta from typing import Optional, Tuple +from src.aggregation.layer0 import get_user_data from src.data.mongo.secret.functions import update_keys from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user from src.data.mongo.user_months import get_user_months from src.models import UserPackage -from src.aggregation.layer0 import get_user_data from src.utils import alru_cache - # Formerly the publisher, loads existing data here diff --git a/backend/src/processing/auth.py b/backend/src/processing/auth.py index b783fddd..08f9608a 100644 --- a/backend/src/processing/auth.py +++ b/backend/src/processing/auth.py @@ -1,5 +1,7 @@ from typing import Any, Dict, Optional +from backend.src.aggregation.layer2.user import update_user + from src.data.github.auth import authenticate as github_authenticate from src.data.mongo.user import ( PublicUserModel, @@ -7,7 +9,6 @@ get_public_user as db_get_public_user, update_user as db_update_user, ) -from backend.src.aggregation.layer2.user import update_user # frontend first calls set_user_key with code and user_key # next they call authenticate which determines the user_id to associate with the code/user_key diff --git a/backend/src/processing/user/commits.py b/backend/src/processing/user/commits.py index cd47ce48..9da615dc 100644 --- a/backend/src/processing/user/commits.py +++ b/backend/src/processing/user/commits.py @@ -1,8 +1,9 @@ from typing import Any, Dict, List, Optional, Tuple, Union +from backend.src.models.svg import LanguageStats, RepoStats + from src.constants import DEFAULT_COLOR from src.models import UserPackage -from backend.src.models.svg import LanguageStats, RepoStats dict_type = Dict[str, Union[str, int, float]] diff --git a/backend/src/processing/user/svg.py b/backend/src/processing/user/svg.py index 921f9fff..66158d82 100644 --- a/backend/src/processing/user/svg.py +++ b/backend/src/processing/user/svg.py @@ -1,8 +1,9 @@ from datetime import date from typing import Optional, Tuple -from src.models import UserPackage from backend.src.aggregation.layer2.user import get_user, get_user_demo + +from src.models import UserPackage from src.utils import use_time_range diff --git a/backend/src/processing/wrapped/main.py b/backend/src/processing/wrapped/main.py index 3ffad75c..20122693 100644 --- a/backend/src/processing/wrapped/main.py +++ b/backend/src/processing/wrapped/main.py @@ -1,10 +1,10 @@ from datetime import date, timedelta from typing import Optional, Tuple +from src.aggregation.layer1 import query_user from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user from src.models import UserPackage, WrappedPackage from src.processing.wrapped.package import get_wrapped_data -from src.aggregation.layer1 import query_user from src.utils import alru_cache diff --git a/backend/src/routers/dev.py b/backend/src/routers/dev.py index faf46c53..c4ed92ec 100644 --- a/backend/src/routers/dev.py +++ b/backend/src/routers/dev.py @@ -3,9 +3,9 @@ from fastapi import APIRouter, Response, status +from src.aggregation.layer0 import get_user_data from src.data.mongo.secret import update_keys from src.models import UserPackage, WrappedPackage -from src.aggregation.layer0 import get_user_data from src.processing.wrapped.package import get_wrapped_data from src.utils import async_fail_gracefully, use_time_range diff --git a/backend/src/routers/users/db.py b/backend/src/routers/users/db.py index de38265e..ae8066b8 100644 --- a/backend/src/routers/users/db.py +++ b/backend/src/routers/users/db.py @@ -1,4 +1,4 @@ -from typing import Optional, Dict, Any +from typing import Any, Dict, Optional from fastapi import APIRouter, Response, status diff --git a/backend/src/routers/users/main.py b/backend/src/routers/users/main.py index 5285ab71..0ea70cc6 100644 --- a/backend/src/routers/users/main.py +++ b/backend/src/routers/users/main.py @@ -1,10 +1,10 @@ from datetime import date, timedelta -from typing import Optional, Dict, Any +from typing import Any, Dict, Optional from fastapi import APIRouter, Response, status -from src.models import UserPackage from src.aggregation.layer2 import get_user +from src.models import UserPackage from src.routers.users.db import router as db_router from src.routers.users.svg import router as svg_router from src.utils import async_fail_gracefully diff --git a/backend/src/routers/wrapped.py b/backend/src/routers/wrapped.py index 6d8735af..101920fa 100644 --- a/backend/src/routers/wrapped.py +++ b/backend/src/routers/wrapped.py @@ -2,8 +2,8 @@ from fastapi import APIRouter, Response, status -from src.models import WrappedPackage from src.aggregation.layer2 import get_is_valid_user +from src.models import WrappedPackage from src.processing.wrapped import query_wrapped_user from src.utils import async_fail_gracefully diff --git a/backend/tests/aggregation/layer0/test_contributions.py b/backend/tests/aggregation/layer0/test_contributions.py index 14fe6eaf..47afd4d8 100644 --- a/backend/tests/aggregation/layer0/test_contributions.py +++ b/backend/tests/aggregation/layer0/test_contributions.py @@ -2,9 +2,9 @@ from aiounittest.case import AsyncTestCase +from src.aggregation.layer0.contributions import get_contributions from src.constants import TEST_TOKEN as TOKEN, TEST_USER_ID as USER_ID from src.models import UserContributions -from src.aggregation.layer0.contributions import get_contributions class TestTemplate(AsyncTestCase): diff --git a/backend/tests/aggregation/layer0/test_follows.py b/backend/tests/aggregation/layer0/test_follows.py index 1f72c836..7664c806 100644 --- a/backend/tests/aggregation/layer0/test_follows.py +++ b/backend/tests/aggregation/layer0/test_follows.py @@ -1,8 +1,8 @@ import unittest +from src.aggregation.layer0.follows import get_user_follows from src.constants import TEST_TOKEN as TOKEN, TEST_USER_ID as USER_ID from src.models import UserFollows -from src.aggregation.layer0.follows import get_user_follows class TestTemplate(unittest.TestCase): From f9630f707fe59a567cab7ce8d1774449ba590d2b Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Thu, 23 Nov 2023 14:05:27 -0500 Subject: [PATCH 12/14] More isort --- backend/src/processing/auth.py | 3 +-- backend/src/processing/user/commits.py | 3 +-- backend/src/processing/user/svg.py | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/backend/src/processing/auth.py b/backend/src/processing/auth.py index 08f9608a..a99f649d 100644 --- a/backend/src/processing/auth.py +++ b/backend/src/processing/auth.py @@ -1,7 +1,6 @@ from typing import Any, Dict, Optional -from backend.src.aggregation.layer2.user import update_user - +from src.aggregation.layer2.user import update_user from src.data.github.auth import authenticate as github_authenticate from src.data.mongo.user import ( PublicUserModel, diff --git a/backend/src/processing/user/commits.py b/backend/src/processing/user/commits.py index 9da615dc..38f90470 100644 --- a/backend/src/processing/user/commits.py +++ b/backend/src/processing/user/commits.py @@ -1,9 +1,8 @@ from typing import Any, Dict, List, Optional, Tuple, Union -from backend.src.models.svg import LanguageStats, RepoStats - from src.constants import DEFAULT_COLOR from src.models import UserPackage +from src.models.svg import LanguageStats, RepoStats dict_type = Dict[str, Union[str, int, float]] diff --git a/backend/src/processing/user/svg.py b/backend/src/processing/user/svg.py index 66158d82..06c5395d 100644 --- a/backend/src/processing/user/svg.py +++ b/backend/src/processing/user/svg.py @@ -1,8 +1,7 @@ from datetime import date from typing import Optional, Tuple -from backend.src.aggregation.layer2.user import get_user, get_user_demo - +from src.aggregation.layer2.user import get_user, get_user_demo from src.models import UserPackage from src.utils import use_time_range From 688bf3e05b2502f557deb248ba69d56da02584bf Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Thu, 23 Nov 2023 14:40:12 -0500 Subject: [PATCH 13/14] Background tasks --- backend/src/aggregation/layer2/__init__.py | 2 +- backend/src/aggregation/layer2/user.py | 19 +++++++---------- backend/src/models/background.py | 9 ++++++++ backend/src/processing/auth.py | 24 ++++++++++++++++------ backend/src/processing/user/svg.py | 10 ++++++--- backend/src/routers/auth/website.py | 14 ++++++++++--- backend/src/routers/background.py | 7 +++++++ backend/src/routers/users/main.py | 13 ++++++++++-- backend/src/routers/users/svg.py | 15 +++++++++++--- 9 files changed, 83 insertions(+), 30 deletions(-) create mode 100644 backend/src/models/background.py create mode 100644 backend/src/routers/background.py diff --git a/backend/src/aggregation/layer2/__init__.py b/backend/src/aggregation/layer2/__init__.py index 1ed2309f..13c2a315 100644 --- a/backend/src/aggregation/layer2/__init__.py +++ b/backend/src/aggregation/layer2/__init__.py @@ -1,4 +1,4 @@ from src.aggregation.layer2.auth import get_is_valid_user from src.aggregation.layer2.user import get_user, get_user_demo -__all__ = ["get_user", "get_user_demo", "get_is_valid_user"] +__all__ = ["get_is_valid_user", "get_user", "get_user_demo"] diff --git a/backend/src/aggregation/layer2/user.py b/backend/src/aggregation/layer2/user.py index 3b57fd19..969c3fe0 100644 --- a/backend/src/aggregation/layer2/user.py +++ b/backend/src/aggregation/layer2/user.py @@ -6,6 +6,7 @@ from src.data.mongo.user import PublicUserModel, get_public_user as db_get_public_user from src.data.mongo.user_months import get_user_months from src.models import UserPackage +from src.models.background import UpdateUserBackgroundTask from src.utils import alru_cache # Formerly the publisher, loads existing data here @@ -26,29 +27,23 @@ async def _get_user( return user_data.trim(start_date, end_date) -@alru_cache() -async def update_user( - user_id: str, access_token: str, private_access: bool -) -> Tuple[bool, bool]: - # TODO: implement this - return (True, True) - - @alru_cache() async def get_user( user_id: str, start_date: date, end_date: date, no_cache: bool = False, -) -> Tuple[bool, Optional[UserPackage]]: +) -> Tuple[bool, Tuple[Optional[UserPackage], Optional[UpdateUserBackgroundTask]]]: user: Optional[PublicUserModel] = await db_get_public_user(user_id) if user is None: - return (False, None) + return (False, (None, None)) private_access = user.private_access or False - await update_user(user_id, user.access_token, private_access) user_data = await _get_user(user_id, private_access, start_date, end_date) - return (user_data is not None, user_data) + background_task = UpdateUserBackgroundTask( + user_id=user_id, access_token=user.access_token, private_access=private_access + ) + return (user_data is not None, (user_data, background_task)) @alru_cache(ttl=timedelta(minutes=15)) diff --git a/backend/src/models/background.py b/backend/src/models/background.py new file mode 100644 index 00000000..e6a3dc38 --- /dev/null +++ b/backend/src/models/background.py @@ -0,0 +1,9 @@ +from typing import Optional + +from pydantic import BaseModel + + +class UpdateUserBackgroundTask(BaseModel): + user_id: str + access_token: Optional[str] + private_access: bool diff --git a/backend/src/processing/auth.py b/backend/src/processing/auth.py index a99f649d..69847d04 100644 --- a/backend/src/processing/auth.py +++ b/backend/src/processing/auth.py @@ -1,6 +1,5 @@ -from typing import Any, Dict, Optional +from typing import Any, Dict, Optional, Tuple -from src.aggregation.layer2.user import update_user from src.data.github.auth import authenticate as github_authenticate from src.data.mongo.user import ( PublicUserModel, @@ -8,6 +7,7 @@ get_public_user as db_get_public_user, update_user as db_update_user, ) +from src.models.background import UpdateUserBackgroundTask # frontend first calls set_user_key with code and user_key # next they call authenticate which determines the user_id to associate with the code/user_key @@ -20,7 +20,9 @@ async def set_user_key(code: str, user_key: str) -> str: return user_key -async def authenticate(code: str, private_access: bool) -> str: +async def authenticate( + code: str, private_access: bool +) -> Tuple[str, Optional[UpdateUserBackgroundTask]]: user_id, access_token = await github_authenticate(code) curr_user: Optional[PublicUserModel] = await db_get_public_user(user_id) @@ -32,19 +34,29 @@ async def authenticate(code: str, private_access: bool) -> str: "private_access": private_access, } + background_task = None if curr_user is not None: curr_private_access = curr_user.private_access new_private_access = curr_private_access or private_access raw_user["private_access"] = new_private_access if new_private_access != curr_private_access: - await update_user(user_id, access_token, new_private_access) + # new private access status + background_task = UpdateUserBackgroundTask( + user_id=user_id, + access_token=access_token, + private_access=new_private_access, + ) else: # first time sign up - await update_user(user_id, access_token, private_access) + background_task = UpdateUserBackgroundTask( + user_id=user_id, + access_token=access_token, + private_access=private_access, + ) await db_update_user(user_id, raw_user) - return user_id + return user_id, background_task async def delete_user(user_id: str, user_key: str, use_user_key: bool = True) -> bool: diff --git a/backend/src/processing/user/svg.py b/backend/src/processing/user/svg.py index 06c5395d..232de21d 100644 --- a/backend/src/processing/user/svg.py +++ b/backend/src/processing/user/svg.py @@ -3,6 +3,7 @@ from src.aggregation.layer2.user import get_user, get_user_demo from src.models import UserPackage +from src.models.background import UpdateUserBackgroundTask from src.utils import use_time_range @@ -13,15 +14,18 @@ async def svg_base( time_range: str, demo: bool, no_cache: bool = False, -) -> Tuple[Optional[UserPackage], str]: +) -> Tuple[Optional[UserPackage], Optional[UpdateUserBackgroundTask], str]: # process time_range, start_date, end_date time_range = "one_month" if demo else time_range start_date, end_date, time_str = use_time_range(time_range, start_date, end_date) # fetch data, either using demo or user method + background_task = None if demo: output = await get_user_demo(user_id, start_date, end_date, no_cache=no_cache) else: - output = await get_user(user_id, start_date, end_date, no_cache=no_cache) + output, background_task = await get_user( + user_id, start_date, end_date, no_cache=no_cache + ) - return output, time_str + return output, background_task, time_str diff --git a/backend/src/routers/auth/website.py b/backend/src/routers/auth/website.py index 0be08baf..37b53211 100644 --- a/backend/src/routers/auth/website.py +++ b/backend/src/routers/auth/website.py @@ -1,10 +1,11 @@ from typing import Any, Dict -from fastapi import status +from fastapi import BackgroundTasks, status from fastapi.responses import Response from fastapi.routing import APIRouter from src.processing.auth import authenticate, delete_user, set_user_key +from src.routers.background import run_in_background from src.utils import async_fail_gracefully router = APIRouter() @@ -29,9 +30,16 @@ async def set_user_key_endpoint(response: Response, code: str, user_key: str) -> ) @async_fail_gracefully async def authenticate_endpoint( - response: Response, code: str, private_access: bool = False + response: Response, + background_tasks: BackgroundTasks, + code: str, + private_access: bool = False, ) -> str: - return await authenticate(code, private_access) + output, background_task = await authenticate(code, private_access) + if background_task is not None: + # set a background task to update the user + background_tasks.add_task(run_in_background, task=background_task) + return output @router.get( diff --git a/backend/src/routers/background.py b/backend/src/routers/background.py new file mode 100644 index 00000000..34d4f940 --- /dev/null +++ b/backend/src/routers/background.py @@ -0,0 +1,7 @@ +from src.aggregation.layer1 import query_user +from src.models.background import UpdateUserBackgroundTask + + +async def run_in_background(task: UpdateUserBackgroundTask): + if isinstance(task, UpdateUserBackgroundTask): # type: ignore + await query_user(task.user_id, task.access_token, task.private_access) diff --git a/backend/src/routers/users/main.py b/backend/src/routers/users/main.py index 0ea70cc6..d52542ea 100644 --- a/backend/src/routers/users/main.py +++ b/backend/src/routers/users/main.py @@ -1,10 +1,11 @@ from datetime import date, timedelta from typing import Any, Dict, Optional -from fastapi import APIRouter, Response, status +from fastapi import APIRouter, BackgroundTasks, Response, status from src.aggregation.layer2 import get_user from src.models import UserPackage +from src.routers.background import run_in_background from src.routers.users.db import router as db_router from src.routers.users.svg import router as svg_router from src.utils import async_fail_gracefully @@ -23,10 +24,18 @@ @async_fail_gracefully async def get_user_endpoint( response: Response, + background_tasks: BackgroundTasks, user_id: str, start_date: date = date.today() - timedelta(365), end_date: date = date.today(), timezone_str: str = "US/Eastern", no_cache: bool = False, ) -> Optional[UserPackage]: - return await get_user(user_id, start_date, end_date, no_cache=no_cache) + output, background_task = await get_user( + user_id, start_date, end_date, no_cache=no_cache + ) + if background_task is not None: + # set a background task to update the user + background_tasks.add_task(run_in_background, task=background_task) + + return output diff --git a/backend/src/routers/users/svg.py b/backend/src/routers/users/svg.py index d9d130a8..c97f6a2b 100644 --- a/backend/src/routers/users/svg.py +++ b/backend/src/routers/users/svg.py @@ -1,7 +1,7 @@ from datetime import date, timedelta from typing import Any -from fastapi import Response, status +from fastapi import BackgroundTasks, Response, status from fastapi.responses import HTMLResponse from fastapi.routing import APIRouter @@ -12,6 +12,7 @@ get_top_langs_svg, get_top_repos_svg, ) +from src.routers.background import run_in_background from src.routers.decorators import svg_fail_gracefully router = APIRouter() @@ -23,6 +24,7 @@ @svg_fail_gracefully async def get_user_lang_svg( response: Response, + background_tasks: BackgroundTasks, user_id: str, start_date: date = date.today() - timedelta(30), end_date: date = date.today(), @@ -37,9 +39,12 @@ async def get_user_lang_svg( use_animation: bool = True, theme: str = "classic", ) -> Any: - output, time_str = await svg_base( + output, background_task, time_str = await svg_base( user_id, start_date, end_date, time_range, demo, no_cache ) + if background_task is not None: + # set a background task to update the user + background_tasks.add_task(run_in_background, task=background_task) # if no data, return loading svg if output is None: @@ -65,6 +70,7 @@ async def get_user_lang_svg( @svg_fail_gracefully async def get_user_repo_svg( response: Response, + background_tasks: BackgroundTasks, user_id: str, start_date: date = date.today() - timedelta(30), end_date: date = date.today(), @@ -78,9 +84,12 @@ async def get_user_repo_svg( use_animation: bool = True, theme: str = "classic", ) -> Any: - output, time_str = await svg_base( + output, background_task, time_str = await svg_base( user_id, start_date, end_date, time_range, demo, no_cache ) + if background_task is not None: + # set a background task to update the user + background_tasks.add_task(run_in_background, task=background_task) # if no data, return loading svg if output is None: From 89b524f12321b81a507b0aca50cfa83fa91fe8b4 Mon Sep 17 00:00:00 2001 From: Abhijit Gupta Date: Thu, 23 Nov 2023 14:42:08 -0500 Subject: [PATCH 14/14] update reqs --- backend/poetry.lock | 616 +++++++++++++-------------------------- backend/requirements.txt | 26 +- 2 files changed, 202 insertions(+), 440 deletions(-) diff --git a/backend/poetry.lock b/backend/poetry.lock index 2c0c1216..46c60268 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -101,28 +101,16 @@ d = ["aiohttp (>=3.7.4)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] -[[package]] -name = "cachetools" -version = "5.3.2" -description = "Extensible memoizing collections and decorators" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "cachetools-5.3.2-py3-none-any.whl", hash = "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1"}, - {file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"}, -] - [[package]] name = "certifi" -version = "2023.7.22" +version = "2023.11.17" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, + {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, + {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, ] [[package]] @@ -445,203 +433,72 @@ mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.11.0,<2.12.0" pyflakes = ">=3.1.0,<3.2.0" -[[package]] -name = "google-api-core" -version = "2.14.0" -description = "Google API client core library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-api-core-2.14.0.tar.gz", hash = "sha256:5368a4502b793d9bbf812a5912e13e4e69f9bd87f6efb508460c43f5bbd1ce41"}, - {file = "google_api_core-2.14.0-py3-none-any.whl", hash = "sha256:de2fb50ed34d47ddbb2bd2dcf680ee8fead46279f4ed6b16de362aca23a18952"}, -] - -[package.dependencies] -google-auth = ">=2.14.1,<3.0.dev0" -googleapis-common-protos = ">=1.56.2,<2.0.dev0" -grpcio = [ - {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}, - {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, -] -grpcio-status = [ - {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "extra == \"grpc\""}, - {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, -] -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-auth" -version = "2.23.4" -description = "Google Authentication Library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-auth-2.23.4.tar.gz", hash = "sha256:79905d6b1652187def79d491d6e23d0cbb3a21d3c7ba0dbaa9c8a01906b13ff3"}, - {file = "google_auth-2.23.4-py2.py3-none-any.whl", hash = "sha256:d4bbc92fe4b8bfd2f3e8d88e5ba7085935da208ee38a134fc280e7ce682a05f2"}, -] - -[package.dependencies] -cachetools = ">=2.0.0,<6.0" -pyasn1-modules = ">=0.2.1" -rsa = ">=3.1.4,<5" - -[package.extras] -aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] -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.0.dev0)"] - -[[package]] -name = "google-cloud-pubsub" -version = "2.18.4" -description = "Google Cloud Pub/Sub API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-pubsub-2.18.4.tar.gz", hash = "sha256:32eb61fd4c1dc6c842f594d69d9afa80544e3b327aa640a164eb6fb0201eaf2d"}, - {file = "google_cloud_pubsub-2.18.4-py2.py3-none-any.whl", hash = "sha256:f32144ad9ed32331a80a2f8379a3ca7526bbc01e7bd76de2e8ab52e492d21f50"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.0 || >=2.11.0,<3.0.0dev", extras = ["grpc"]} -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -grpcio = ">=1.51.3,<2.0dev" -grpcio-status = ">=1.33.2" -proto-plus = {version = ">=1.22.2,<2.0.0dev", markers = "python_version >= \"3.11\""} -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.0dev" - -[package.extras] -libcst = ["libcst (>=0.3.10)"] - -[[package]] -name = "googleapis-common-protos" -version = "1.61.0" -description = "Common protobufs used in Google APIs" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "googleapis-common-protos-1.61.0.tar.gz", hash = "sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b"}, - {file = "googleapis_common_protos-1.61.0-py2.py3-none-any.whl", hash = "sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0"}, -] - -[package.dependencies] -grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} -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 = "grpc-google-iam-v1" -version = "0.12.7" -description = "IAM API client library" -category = "main" -optional = false -python-versions = ">=3.7" -files = [ - {file = "grpc-google-iam-v1-0.12.7.tar.gz", hash = "sha256:009197a7f1eaaa22149c96e5e054ac5934ba7241974e92663d8d3528a21203d1"}, - {file = "grpc_google_iam_v1-0.12.7-py2.py3-none-any.whl", hash = "sha256:834da89f4c4a2abbe842a793ed20fc6d9a77011ef2626755b1b89116fb9596d7"}, -] - -[package.dependencies] -googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} -grpcio = ">=1.44.0,<2.0.0dev" -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.0dev" - [[package]] name = "grpcio" -version = "1.59.2" +version = "1.59.3" description = "HTTP/2-based RPC framework" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "grpcio-1.59.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:d2fa68a96a30dd240be80bbad838a0ac81a61770611ff7952b889485970c4c71"}, - {file = "grpcio-1.59.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:cf0dead5a2c5a3347af2cfec7131d4f2a2e03c934af28989c9078f8241a491fa"}, - {file = "grpcio-1.59.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:e420ced29b5904cdf9ee5545e23f9406189d8acb6750916c2db4793dada065c6"}, - {file = "grpcio-1.59.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b230028a008ae1d0f430acb227d323ff8a619017415cf334c38b457f814119f"}, - {file = "grpcio-1.59.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a4a3833c0e067f3558538727235cd8a49709bff1003200bbdefa2f09334e4b1"}, - {file = "grpcio-1.59.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6b25ed37c27e652db01be341af93fbcea03d296c024d8a0e680017a268eb85dd"}, - {file = "grpcio-1.59.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73abb8584b0cf74d37f5ef61c10722adc7275502ab71789a8fe3cb7ef04cf6e2"}, - {file = "grpcio-1.59.2-cp310-cp310-win32.whl", hash = "sha256:d6f70406695e3220f09cd7a2f879333279d91aa4a8a1d34303b56d61a8180137"}, - {file = "grpcio-1.59.2-cp310-cp310-win_amd64.whl", hash = "sha256:3c61d641d4f409c5ae46bfdd89ea42ce5ea233dcf69e74ce9ba32b503c727e29"}, - {file = "grpcio-1.59.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:3059668df17627f0e0fa680e9ef8c995c946c792612e9518f5cc1503be14e90b"}, - {file = "grpcio-1.59.2-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:72ca2399097c0b758198f2ff30f7178d680de8a5cfcf3d9b73a63cf87455532e"}, - {file = "grpcio-1.59.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:c978f864b35f2261e0819f5cd88b9830b04dc51bcf055aac3c601e525a10d2ba"}, - {file = "grpcio-1.59.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9411e24328a2302e279e70cae6e479f1fddde79629fcb14e03e6d94b3956eabf"}, - {file = "grpcio-1.59.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb7e0fe6ad73b7f06d7e2b689c19a71cf5cc48f0c2bf8608469e51ffe0bd2867"}, - {file = "grpcio-1.59.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c2504eed520958a5b77cc99458297cb7906308cb92327f35fb7fbbad4e9b2188"}, - {file = "grpcio-1.59.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2171c39f355ba5b551c5d5928d65aa6c69807fae195b86ef4a7d125bcdb860a9"}, - {file = "grpcio-1.59.2-cp311-cp311-win32.whl", hash = "sha256:d2794f0e68b3085d99b4f6ff9c089f6fdd02b32b9d3efdfbb55beac1bf22d516"}, - {file = "grpcio-1.59.2-cp311-cp311-win_amd64.whl", hash = "sha256:2067274c88bc6de89c278a672a652b4247d088811ece781a4858b09bdf8448e3"}, - {file = "grpcio-1.59.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:535561990e075fa6bd4b16c4c3c1096b9581b7bb35d96fac4650f1181e428268"}, - {file = "grpcio-1.59.2-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:a213acfbf186b9f35803b52e4ca9addb153fc0b67f82a48f961be7000ecf6721"}, - {file = "grpcio-1.59.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:6959fb07e8351e20501ffb8cc4074c39a0b7ef123e1c850a7f8f3afdc3a3da01"}, - {file = "grpcio-1.59.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e82c5cf1495244adf5252f925ac5932e5fd288b3e5ab6b70bec5593074b7236c"}, - {file = "grpcio-1.59.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:023088764012411affe7db183d1ada3ad9daf2e23ddc719ff46d7061de661340"}, - {file = "grpcio-1.59.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:da2d94c15f88cd40d7e67f7919d4f60110d2b9d5b1e08cf354c2be773ab13479"}, - {file = "grpcio-1.59.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6009386a2df66159f64ac9f20425ae25229b29b9dd0e1d3dd60043f037e2ad7e"}, - {file = "grpcio-1.59.2-cp312-cp312-win32.whl", hash = "sha256:75c6ecb70e809cf1504465174343113f51f24bc61e22a80ae1c859f3f7034c6d"}, - {file = "grpcio-1.59.2-cp312-cp312-win_amd64.whl", hash = "sha256:cbe946b3e6e60a7b4618f091e62a029cb082b109a9d6b53962dd305087c6e4fd"}, - {file = "grpcio-1.59.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:f8753a6c88d1d0ba64302309eecf20f70d2770f65ca02d83c2452279085bfcd3"}, - {file = "grpcio-1.59.2-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:f1ef0d39bc1feb420caf549b3c657c871cad4ebbcf0580c4d03816b0590de0cf"}, - {file = "grpcio-1.59.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:4c93f4abbb54321ee6471e04a00139c80c754eda51064187963ddf98f5cf36a4"}, - {file = "grpcio-1.59.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:08d77e682f2bf730a4961eea330e56d2f423c6a9b91ca222e5b1eb24a357b19f"}, - {file = "grpcio-1.59.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff16d68bf453275466a9a46739061a63584d92f18a0f5b33d19fc97eb69867c"}, - {file = "grpcio-1.59.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4abb717e320e74959517dc8e84a9f48fbe90e9abe19c248541e9418b1ce60acd"}, - {file = "grpcio-1.59.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:36f53c2b3449c015880e7d55a89c992c357f176327b0d2873cdaaf9628a37c69"}, - {file = "grpcio-1.59.2-cp37-cp37m-win_amd64.whl", hash = "sha256:cc3e4cd087f07758b16bef8f31d88dbb1b5da5671d2f03685ab52dece3d7a16e"}, - {file = "grpcio-1.59.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:27f879ae604a7fcf371e59fba6f3ff4635a4c2a64768bd83ff0cac503142fef4"}, - {file = "grpcio-1.59.2-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:7cf05053242f61ba94014dd3a986e11a083400a32664058f80bf4cf817c0b3a1"}, - {file = "grpcio-1.59.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:e1727c1c0e394096bb9af185c6923e8ea55a5095b8af44f06903bcc0e06800a2"}, - {file = "grpcio-1.59.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d573e70a6fe77555fb6143c12d3a7d3fa306632a3034b4e7c59ca09721546f8"}, - {file = "grpcio-1.59.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31176aa88f36020055ace9adff2405a33c8bdbfa72a9c4980e25d91b2f196873"}, - {file = "grpcio-1.59.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:11168ef43e4a43ff1b1a65859f3e0ef1a173e277349e7fb16923ff108160a8cd"}, - {file = "grpcio-1.59.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:53c9aa5ddd6857c0a1cd0287225a2a25873a8e09727c2e95c4aebb1be83a766a"}, - {file = "grpcio-1.59.2-cp38-cp38-win32.whl", hash = "sha256:3b4368b33908f683a363f376dfb747d40af3463a6e5044afee07cf9436addf96"}, - {file = "grpcio-1.59.2-cp38-cp38-win_amd64.whl", hash = "sha256:0a754aff9e3af63bdc4c75c234b86b9d14e14a28a30c4e324aed1a9b873d755f"}, - {file = "grpcio-1.59.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:1f9524d1d701e399462d2c90ba7c193e49d1711cf429c0d3d97c966856e03d00"}, - {file = "grpcio-1.59.2-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:f93dbf58f03146164048be5426ffde298b237a5e059144847e4940f5b80172c3"}, - {file = "grpcio-1.59.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:6da6dea3a1bacf99b3c2187e296db9a83029ed9c38fd4c52b7c9b7326d13c828"}, - {file = "grpcio-1.59.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5f09cffa619adfb44799fa4a81c2a1ad77c887187613fb0a8f201ab38d89ba1"}, - {file = "grpcio-1.59.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c35aa9657f5d5116d23b934568e0956bd50c615127810fffe3ac356a914c176a"}, - {file = "grpcio-1.59.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:74100fecaec8a535e380cf5f2fb556ff84957d481c13e54051c52e5baac70541"}, - {file = "grpcio-1.59.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:128e20f57c5f27cb0157e73756d1586b83c1b513ebecc83ea0ac37e4b0e4e758"}, - {file = "grpcio-1.59.2-cp39-cp39-win32.whl", hash = "sha256:686e975a5d16602dc0982c7c703948d17184bd1397e16c8ee03511ecb8c4cdda"}, - {file = "grpcio-1.59.2-cp39-cp39-win_amd64.whl", hash = "sha256:242adc47725b9a499ee77c6a2e36688fa6c96484611f33b1be4c57ab075a92dd"}, - {file = "grpcio-1.59.2.tar.gz", hash = "sha256:d8f9cd4ad1be90b0cf350a2f04a38a36e44a026cac1e036ac593dc48efe91d52"}, + {file = "grpcio-1.59.3-cp310-cp310-linux_armv7l.whl", hash = "sha256:aca028a6c7806e5b61e5f9f4232432c52856f7fcb98e330b20b6bc95d657bdcc"}, + {file = "grpcio-1.59.3-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:19ad26a7967f7999c8960d2b9fe382dae74c55b0c508c613a6c2ba21cddf2354"}, + {file = "grpcio-1.59.3-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:72b71dad2a3d1650e69ad42a5c4edbc59ee017f08c32c95694172bc501def23c"}, + {file = "grpcio-1.59.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c0f0a11d82d0253656cc42e04b6a149521e02e755fe2e4edd21123de610fd1d4"}, + {file = "grpcio-1.59.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60cddafb70f9a2c81ba251b53b4007e07cca7389e704f86266e22c4bffd8bf1d"}, + {file = "grpcio-1.59.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6c75a1fa0e677c1d2b6d4196ad395a5c381dfb8385f07ed034ef667cdcdbcc25"}, + {file = "grpcio-1.59.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e1d8e01438d5964a11167eec1edb5f85ed8e475648f36c834ed5db4ffba24ac8"}, + {file = "grpcio-1.59.3-cp310-cp310-win32.whl", hash = "sha256:c4b0076f0bf29ee62335b055a9599f52000b7941f577daa001c7ef961a1fbeab"}, + {file = "grpcio-1.59.3-cp310-cp310-win_amd64.whl", hash = "sha256:b1f00a3e6e0c3dccccffb5579fc76ebfe4eb40405ba308505b41ef92f747746a"}, + {file = "grpcio-1.59.3-cp311-cp311-linux_armv7l.whl", hash = "sha256:3996aaa21231451161dc29df6a43fcaa8b332042b6150482c119a678d007dd86"}, + {file = "grpcio-1.59.3-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:cb4e9cbd9b7388fcb06412da9f188c7803742d06d6f626304eb838d1707ec7e3"}, + {file = "grpcio-1.59.3-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:8022ca303d6c694a0d7acfb2b472add920217618d3a99eb4b14edc7c6a7e8fcf"}, + {file = "grpcio-1.59.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b36683fad5664283755a7f4e2e804e243633634e93cd798a46247b8e54e3cb0d"}, + {file = "grpcio-1.59.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8239b853226e4824e769517e1b5232e7c4dda3815b200534500338960fcc6118"}, + {file = "grpcio-1.59.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0511af8653fbda489ff11d542a08505d56023e63cafbda60e6e00d4e0bae86ea"}, + {file = "grpcio-1.59.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e78dc982bda74cef2ddfce1c91d29b96864c4c680c634e279ed204d51e227473"}, + {file = "grpcio-1.59.3-cp311-cp311-win32.whl", hash = "sha256:6a5c3a96405966c023e139c3bcccb2c7c776a6f256ac6d70f8558c9041bdccc3"}, + {file = "grpcio-1.59.3-cp311-cp311-win_amd64.whl", hash = "sha256:ed26826ee423b11477297b187371cdf4fa1eca874eb1156422ef3c9a60590dd9"}, + {file = "grpcio-1.59.3-cp312-cp312-linux_armv7l.whl", hash = "sha256:45dddc5cb5227d30fa43652d8872dc87f086d81ab4b500be99413bad0ae198d7"}, + {file = "grpcio-1.59.3-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:1736496d74682e53dd0907fd515f2694d8e6a96c9a359b4080b2504bf2b2d91b"}, + {file = "grpcio-1.59.3-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:ddbd1a16138e52e66229047624de364f88a948a4d92ba20e4e25ad7d22eef025"}, + {file = "grpcio-1.59.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcfa56f8d031ffda902c258c84c4b88707f3a4be4827b4e3ab8ec7c24676320d"}, + {file = "grpcio-1.59.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2eb8f0c7c0c62f7a547ad7a91ba627a5aa32a5ae8d930783f7ee61680d7eb8d"}, + {file = "grpcio-1.59.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8d993399cc65e3a34f8fd48dd9ad7a376734564b822e0160dd18b3d00c1a33f9"}, + {file = "grpcio-1.59.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0bd141f4f41907eb90bda74d969c3cb21c1c62779419782a5b3f5e4b5835718"}, + {file = "grpcio-1.59.3-cp312-cp312-win32.whl", hash = "sha256:33b8fd65d4e97efa62baec6171ce51f9cf68f3a8ba9f866f4abc9d62b5c97b79"}, + {file = "grpcio-1.59.3-cp312-cp312-win_amd64.whl", hash = "sha256:0e735ed002f50d4f3cb9ecfe8ac82403f5d842d274c92d99db64cfc998515e07"}, + {file = "grpcio-1.59.3-cp37-cp37m-linux_armv7l.whl", hash = "sha256:ea40ce4404e7cca0724c91a7404da410f0144148fdd58402a5942971e3469b94"}, + {file = "grpcio-1.59.3-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:83113bcc393477b6f7342b9f48e8a054330c895205517edc66789ceea0796b53"}, + {file = "grpcio-1.59.3-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:73afbac602b8f1212a50088193601f869b5073efa9855b3e51aaaec97848fc8a"}, + {file = "grpcio-1.59.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:575d61de1950b0b0699917b686b1ca108690702fcc2df127b8c9c9320f93e069"}, + {file = "grpcio-1.59.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd76057b5c9a4d68814610ef9226925f94c1231bbe533fdf96f6181f7d2ff9e"}, + {file = "grpcio-1.59.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:95d6fd804c81efe4879e38bfd84d2b26e339a0a9b797e7615e884ef4686eb47b"}, + {file = "grpcio-1.59.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0d42048b8a3286ea4134faddf1f9a59cf98192b94aaa10d910a25613c5eb5bfb"}, + {file = "grpcio-1.59.3-cp37-cp37m-win_amd64.whl", hash = "sha256:4619fea15c64bcdd9d447cdbdde40e3d5f1da3a2e8ae84103d94a9c1df210d7e"}, + {file = "grpcio-1.59.3-cp38-cp38-linux_armv7l.whl", hash = "sha256:95b5506e70284ac03b2005dd9ffcb6708c9ae660669376f0192a710687a22556"}, + {file = "grpcio-1.59.3-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:9e17660947660ccfce56c7869032910c179a5328a77b73b37305cd1ee9301c2e"}, + {file = "grpcio-1.59.3-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:00912ce19914d038851be5cd380d94a03f9d195643c28e3ad03d355cc02ce7e8"}, + {file = "grpcio-1.59.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e58b3cadaa3c90f1efca26ba33e0d408b35b497307027d3d707e4bcd8de862a6"}, + {file = "grpcio-1.59.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d787ecadea865bdf78f6679f6f5bf4b984f18f659257ba612979df97a298b3c3"}, + {file = "grpcio-1.59.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0814942ba1bba269db4e760a34388640c601dece525c6a01f3b4ff030cc0db69"}, + {file = "grpcio-1.59.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fb111aa99d3180c361a35b5ae1e2c63750220c584a1344229abc139d5c891881"}, + {file = "grpcio-1.59.3-cp38-cp38-win32.whl", hash = "sha256:eb8ba504c726befe40a356ecbe63c6c3c64c9a439b3164f5a718ec53c9874da0"}, + {file = "grpcio-1.59.3-cp38-cp38-win_amd64.whl", hash = "sha256:cdbc6b32fadab9bebc6f49d3e7ec4c70983c71e965497adab7f87de218e84391"}, + {file = "grpcio-1.59.3-cp39-cp39-linux_armv7l.whl", hash = "sha256:c82ca1e4be24a98a253d6dbaa216542e4163f33f38163fc77964b0f0d255b552"}, + {file = "grpcio-1.59.3-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:36636babfda14f9e9687f28d5b66d349cf88c1301154dc71c6513de2b6c88c59"}, + {file = "grpcio-1.59.3-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5f9b2e591da751ac7fdd316cc25afafb7a626dededa9b414f90faad7f3ccebdb"}, + {file = "grpcio-1.59.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a93a82876a4926bf451db82ceb725bd87f42292bacc94586045261f501a86994"}, + {file = "grpcio-1.59.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce31fa0bfdd1f2bb15b657c16105c8652186eab304eb512e6ae3b99b2fdd7d13"}, + {file = "grpcio-1.59.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:16da0e40573962dab6cba16bec31f25a4f468e6d05b658e589090fe103b03e3d"}, + {file = "grpcio-1.59.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d1a17372fd425addd5812049fa7374008ffe689585f27f802d0935522cf4b7"}, + {file = "grpcio-1.59.3-cp39-cp39-win32.whl", hash = "sha256:52cc38a7241b5f7b4a91aaf9000fdd38e26bb00d5e8a71665ce40cfcee716281"}, + {file = "grpcio-1.59.3-cp39-cp39-win_amd64.whl", hash = "sha256:b491e5bbcad3020a96842040421e508780cade35baba30f402df9d321d1c423e"}, + {file = "grpcio-1.59.3.tar.gz", hash = "sha256:7800f99568a74a06ebdccd419dd1b6e639b477dcaf6da77ea702f8fb14ce5f80"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.59.2)"] - -[[package]] -name = "grpcio-status" -version = "1.59.2" -description = "Status proto mapping for gRPC" -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "grpcio-status-1.59.2.tar.gz", hash = "sha256:a2c2b146e66b73ba80d021ab34fce5db4dd9be67ca4566cda40d36b185ce54f4"}, - {file = "grpcio_status-1.59.2-py3-none-any.whl", hash = "sha256:24bdf3b3b83b9112f43bd0626f82510d12cc1d919a45028ac20eb6919218e508"}, -] - -[package.dependencies] -googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.59.2" -protobuf = ">=4.21.6" +protobuf = ["grpcio-tools (>=1.59.3)"] [[package]] name = "gunicorn" @@ -727,14 +584,14 @@ test = ["Cython (>=0.29.24,<0.30.0)"] [[package]] name = "identify" -version = "2.5.31" +version = "2.5.32" description = "File identification library for Python" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.5.31-py2.py3-none-any.whl", hash = "sha256:90199cb9e7bd3c5407a9b7e81b4abec4bb9d249991c79439ec8af740afc6293d"}, - {file = "identify-2.5.31.tar.gz", hash = "sha256:7736b3c7a28233637e3c36550646fc6389bedd74ae84cb788200cc8e2dd60b75"}, + {file = "identify-2.5.32-py2.py3-none-any.whl", hash = "sha256:0b7656ef6cba81664b783352c73f8c24b39cf82f926f78f4550eda928e5e0545"}, + {file = "identify-2.5.32.tar.gz", hash = "sha256:5d9979348ec1a21c768ae07e0a652924538e8bce67313a73cb0f681cf08ba407"}, ] [package.extras] @@ -784,14 +641,14 @@ files = [ [[package]] name = "motor" -version = "3.3.1" +version = "3.3.2" description = "Non-blocking MongoDB driver for Tornado or asyncio" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "motor-3.3.1-py3-none-any.whl", hash = "sha256:a0dee83ad0d47b353932ac37467ba397b1e649ce7e3eea7f5a90554883d7cdbe"}, - {file = "motor-3.3.1.tar.gz", hash = "sha256:c5eb400e27d722a3db03a9826656b6d13acf9b6c70c2fb4604f474eac9da5be4"}, + {file = "motor-3.3.2-py3-none-any.whl", hash = "sha256:6fe7e6f0c4f430b9e030b9d22549b732f7c2226af3ab71ecc309e4a1b7d19953"}, + {file = "motor-3.3.2.tar.gz", hash = "sha256:d2fc38de15f1c8058f389c1a44a4d4105c0405c48c061cd492a654496f7bc26a"}, ] [package.dependencies] @@ -804,7 +661,7 @@ gssapi = ["pymongo[gssapi] (>=4.5,<5)"] ocsp = ["pymongo[ocsp] (>=4.5,<5)"] snappy = ["pymongo[snappy] (>=4.5,<5)"] srv = ["pymongo[srv] (>=4.5,<5)"] -test = ["aiohttp", "mockupdb", "motor[encryption]", "pytest (>=7)", "tornado (>=5)"] +test = ["aiohttp (<3.8.6)", "mockupdb", "motor[encryption]", "pytest (>=7)", "tornado (>=5)"] zstd = ["pymongo[zstd] (>=4.5,<5)"] [[package]] @@ -860,14 +717,14 @@ files = [ [[package]] name = "platformdirs" -version = "3.11.0" +version = "4.0.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, - {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, + {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, + {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, ] [package.extras] @@ -893,72 +750,6 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" -[[package]] -name = "proto-plus" -version = "1.22.3" -description = "Beautiful, Pythonic protocol buffers." -category = "main" -optional = false -python-versions = ">=3.6" -files = [ - {file = "proto-plus-1.22.3.tar.gz", hash = "sha256:fdcd09713cbd42480740d2fe29c990f7fbd885a67efc328aa8be6ee3e9f76a6b"}, - {file = "proto_plus-1.22.3-py3-none-any.whl", hash = "sha256:a49cd903bc0b6ab41f76bf65510439d56ca76f868adf0274e738bfdd096894df"}, -] - -[package.dependencies] -protobuf = ">=3.19.0,<5.0.0dev" - -[package.extras] -testing = ["google-api-core[grpc] (>=1.31.5)"] - -[[package]] -name = "protobuf" -version = "4.25.0" -description = "" -category = "main" -optional = false -python-versions = ">=3.8" -files = [ - {file = "protobuf-4.25.0-cp310-abi3-win32.whl", hash = "sha256:5c1203ac9f50e4853b0a0bfffd32c67118ef552a33942982eeab543f5c634395"}, - {file = "protobuf-4.25.0-cp310-abi3-win_amd64.whl", hash = "sha256:c40ff8f00aa737938c5378d461637d15c442a12275a81019cc2fef06d81c9419"}, - {file = "protobuf-4.25.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:cf21faba64cd2c9a3ed92b7a67f226296b10159dbb8fbc5e854fc90657d908e4"}, - {file = "protobuf-4.25.0-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:32ac2100b0e23412413d948c03060184d34a7c50b3e5d7524ee96ac2b10acf51"}, - {file = "protobuf-4.25.0-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:683dc44c61f2620b32ce4927de2108f3ebe8ccf2fd716e1e684e5a50da154054"}, - {file = "protobuf-4.25.0-cp38-cp38-win32.whl", hash = "sha256:1a3ba712877e6d37013cdc3476040ea1e313a6c2e1580836a94f76b3c176d575"}, - {file = "protobuf-4.25.0-cp38-cp38-win_amd64.whl", hash = "sha256:b2cf8b5d381f9378afe84618288b239e75665fe58d0f3fd5db400959274296e9"}, - {file = "protobuf-4.25.0-cp39-cp39-win32.whl", hash = "sha256:63714e79b761a37048c9701a37438aa29945cd2417a97076048232c1df07b701"}, - {file = "protobuf-4.25.0-cp39-cp39-win_amd64.whl", hash = "sha256:d94a33db8b7ddbd0af7c467475fb9fde0c705fb315a8433c0e2020942b863a1f"}, - {file = "protobuf-4.25.0-py3-none-any.whl", hash = "sha256:1a53d6f64b00eecf53b65ff4a8c23dc95df1fa1e97bb06b8122e5a64f49fc90a"}, - {file = "protobuf-4.25.0.tar.gz", hash = "sha256:68f7caf0d4f012fd194a301420cf6aa258366144d814f358c5b32558228afa7c"}, -] - -[[package]] -name = "pyasn1" -version = "0.5.0" -description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -category = "main" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "pyasn1-0.5.0-py2.py3-none-any.whl", hash = "sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57"}, - {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 = "pycodestyle" version = "2.11.1" @@ -973,19 +764,19 @@ files = [ [[package]] name = "pydantic" -version = "2.4.2" +version = "2.5.2" description = "Data validation using Python type hints" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic-2.4.2-py3-none-any.whl", hash = "sha256:bc3ddf669d234f4220e6e1c4d96b061abe0998185a8d7855c0126782b7abc8c1"}, - {file = "pydantic-2.4.2.tar.gz", hash = "sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7"}, + {file = "pydantic-2.5.2-py3-none-any.whl", hash = "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0"}, + {file = "pydantic-2.5.2.tar.gz", hash = "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.10.1" +pydantic-core = "2.14.5" typing-extensions = ">=4.6.1" [package.extras] @@ -993,118 +784,117 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.10.1" +version = "2.14.5" description = "" category = "main" optional = false python-versions = ">=3.7" files = [ - {file = "pydantic_core-2.10.1-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:d64728ee14e667ba27c66314b7d880b8eeb050e58ffc5fec3b7a109f8cddbd63"}, - {file = "pydantic_core-2.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:48525933fea744a3e7464c19bfede85df4aba79ce90c60b94d8b6e1eddd67096"}, - {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef337945bbd76cce390d1b2496ccf9f90b1c1242a3a7bc242ca4a9fc5993427a"}, - {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1392e0638af203cee360495fd2cfdd6054711f2db5175b6e9c3c461b76f5175"}, - {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0675ba5d22de54d07bccde38997e780044dcfa9a71aac9fd7d4d7a1d2e3e65f7"}, - {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:128552af70a64660f21cb0eb4876cbdadf1a1f9d5de820fed6421fa8de07c893"}, - {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f6e6aed5818c264412ac0598b581a002a9f050cb2637a84979859e70197aa9e"}, - {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ecaac27da855b8d73f92123e5f03612b04c5632fd0a476e469dfc47cd37d6b2e"}, - {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3c01c2fb081fced3bbb3da78510693dc7121bb893a1f0f5f4b48013201f362e"}, - {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:92f675fefa977625105708492850bcbc1182bfc3e997f8eecb866d1927c98ae6"}, - {file = "pydantic_core-2.10.1-cp310-none-win32.whl", hash = "sha256:420a692b547736a8d8703c39ea935ab5d8f0d2573f8f123b0a294e49a73f214b"}, - {file = "pydantic_core-2.10.1-cp310-none-win_amd64.whl", hash = "sha256:0880e239827b4b5b3e2ce05e6b766a7414e5f5aedc4523be6b68cfbc7f61c5d0"}, - {file = "pydantic_core-2.10.1-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:073d4a470b195d2b2245d0343569aac7e979d3a0dcce6c7d2af6d8a920ad0bea"}, - {file = "pydantic_core-2.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:600d04a7b342363058b9190d4e929a8e2e715c5682a70cc37d5ded1e0dd370b4"}, - {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39215d809470f4c8d1881758575b2abfb80174a9e8daf8f33b1d4379357e417c"}, - {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eeb3d3d6b399ffe55f9a04e09e635554012f1980696d6b0aca3e6cf42a17a03b"}, - {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a7902bf75779bc12ccfc508bfb7a4c47063f748ea3de87135d433a4cca7a2f"}, - {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3625578b6010c65964d177626fde80cf60d7f2e297d56b925cb5cdeda6e9925a"}, - {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa48fc31fc7243e50188197b5f0c4228956f97b954f76da157aae7f67269ae8"}, - {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:07ec6d7d929ae9c68f716195ce15e745b3e8fa122fc67698ac6498d802ed0fa4"}, - {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6f31a17acede6a8cd1ae2d123ce04d8cca74056c9d456075f4f6f85de055607"}, - {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d8f1ebca515a03e5654f88411420fea6380fc841d1bea08effb28184e3d4899f"}, - {file = "pydantic_core-2.10.1-cp311-none-win32.whl", hash = "sha256:6db2eb9654a85ada248afa5a6db5ff1cf0f7b16043a6b070adc4a5be68c716d6"}, - {file = "pydantic_core-2.10.1-cp311-none-win_amd64.whl", hash = "sha256:4a5be350f922430997f240d25f8219f93b0c81e15f7b30b868b2fddfc2d05f27"}, - {file = "pydantic_core-2.10.1-cp311-none-win_arm64.whl", hash = "sha256:5fdb39f67c779b183b0c853cd6b45f7db84b84e0571b3ef1c89cdb1dfc367325"}, - {file = "pydantic_core-2.10.1-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:b1f22a9ab44de5f082216270552aa54259db20189e68fc12484873d926426921"}, - {file = "pydantic_core-2.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8572cadbf4cfa95fb4187775b5ade2eaa93511f07947b38f4cd67cf10783b118"}, - {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db9a28c063c7c00844ae42a80203eb6d2d6bbb97070cfa00194dff40e6f545ab"}, - {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e2a35baa428181cb2270a15864ec6286822d3576f2ed0f4cd7f0c1708472aff"}, - {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05560ab976012bf40f25d5225a58bfa649bb897b87192a36c6fef1ab132540d7"}, - {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6495008733c7521a89422d7a68efa0a0122c99a5861f06020ef5b1f51f9ba7c"}, - {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ac492c686defc8e6133e3a2d9eaf5261b3df26b8ae97450c1647286750b901"}, - {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8282bab177a9a3081fd3d0a0175a07a1e2bfb7fcbbd949519ea0980f8a07144d"}, - {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:aafdb89fdeb5fe165043896817eccd6434aee124d5ee9b354f92cd574ba5e78f"}, - {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f6defd966ca3b187ec6c366604e9296f585021d922e666b99c47e78738b5666c"}, - {file = "pydantic_core-2.10.1-cp312-none-win32.whl", hash = "sha256:7c4d1894fe112b0864c1fa75dffa045720a194b227bed12f4be7f6045b25209f"}, - {file = "pydantic_core-2.10.1-cp312-none-win_amd64.whl", hash = "sha256:5994985da903d0b8a08e4935c46ed8daf5be1cf217489e673910951dc533d430"}, - {file = "pydantic_core-2.10.1-cp312-none-win_arm64.whl", hash = "sha256:0d8a8adef23d86d8eceed3e32e9cca8879c7481c183f84ed1a8edc7df073af94"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9badf8d45171d92387410b04639d73811b785b5161ecadabf056ea14d62d4ede"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:ebedb45b9feb7258fac0a268a3f6bec0a2ea4d9558f3d6f813f02ff3a6dc6698"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfe1090245c078720d250d19cb05d67e21a9cd7c257698ef139bc41cf6c27b4f"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e357571bb0efd65fd55f18db0a2fb0ed89d0bb1d41d906b138f088933ae618bb"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3dcd587b69bbf54fc04ca157c2323b8911033e827fffaecf0cafa5a892a0904"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c120c9ce3b163b985a3b966bb701114beb1da4b0468b9b236fc754783d85aa3"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15d6bca84ffc966cc9976b09a18cf9543ed4d4ecbd97e7086f9ce9327ea48891"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cabb9710f09d5d2e9e2748c3e3e20d991a4c5f96ed8f1132518f54ab2967221"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:82f55187a5bebae7d81d35b1e9aaea5e169d44819789837cdd4720d768c55d15"}, - {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1d40f55222b233e98e3921df7811c27567f0e1a4411b93d4c5c0f4ce131bc42f"}, - {file = "pydantic_core-2.10.1-cp37-none-win32.whl", hash = "sha256:14e09ff0b8fe6e46b93d36a878f6e4a3a98ba5303c76bb8e716f4878a3bee92c"}, - {file = "pydantic_core-2.10.1-cp37-none-win_amd64.whl", hash = "sha256:1396e81b83516b9d5c9e26a924fa69164156c148c717131f54f586485ac3c15e"}, - {file = "pydantic_core-2.10.1-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:6835451b57c1b467b95ffb03a38bb75b52fb4dc2762bb1d9dbed8de31ea7d0fc"}, - {file = "pydantic_core-2.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b00bc4619f60c853556b35f83731bd817f989cba3e97dc792bb8c97941b8053a"}, - {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa467fd300a6f046bdb248d40cd015b21b7576c168a6bb20aa22e595c8ffcdd"}, - {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d99277877daf2efe074eae6338453a4ed54a2d93fb4678ddfe1209a0c93a2468"}, - {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa7db7558607afeccb33c0e4bf1c9a9a835e26599e76af6fe2fcea45904083a6"}, - {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aad7bd686363d1ce4ee930ad39f14e1673248373f4a9d74d2b9554f06199fb58"}, - {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:443fed67d33aa85357464f297e3d26e570267d1af6fef1c21ca50921d2976302"}, - {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:042462d8d6ba707fd3ce9649e7bf268633a41018d6a998fb5fbacb7e928a183e"}, - {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ecdbde46235f3d560b18be0cb706c8e8ad1b965e5c13bbba7450c86064e96561"}, - {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ed550ed05540c03f0e69e6d74ad58d026de61b9eaebebbaaf8873e585cbb18de"}, - {file = "pydantic_core-2.10.1-cp38-none-win32.whl", hash = "sha256:8cdbbd92154db2fec4ec973d45c565e767ddc20aa6dbaf50142676484cbff8ee"}, - {file = "pydantic_core-2.10.1-cp38-none-win_amd64.whl", hash = "sha256:9f6f3e2598604956480f6c8aa24a3384dbf6509fe995d97f6ca6103bb8c2534e"}, - {file = "pydantic_core-2.10.1-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:655f8f4c8d6a5963c9a0687793da37b9b681d9ad06f29438a3b2326d4e6b7970"}, - {file = "pydantic_core-2.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e570ffeb2170e116a5b17e83f19911020ac79d19c96f320cbfa1fa96b470185b"}, - {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64322bfa13e44c6c30c518729ef08fda6026b96d5c0be724b3c4ae4da939f875"}, - {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:485a91abe3a07c3a8d1e082ba29254eea3e2bb13cbbd4351ea4e5a21912cc9b0"}, - {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7c2b8eb9fc872e68b46eeaf835e86bccc3a58ba57d0eedc109cbb14177be531"}, - {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5cb87bdc2e5f620693148b5f8f842d293cae46c5f15a1b1bf7ceeed324a740c"}, - {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25bd966103890ccfa028841a8f30cebcf5875eeac8c4bde4fe221364c92f0c9a"}, - {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f323306d0556351735b54acbf82904fe30a27b6a7147153cbe6e19aaaa2aa429"}, - {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0c27f38dc4fbf07b358b2bc90edf35e82d1703e22ff2efa4af4ad5de1b3833e7"}, - {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f1365e032a477c1430cfe0cf2856679529a2331426f8081172c4a74186f1d595"}, - {file = "pydantic_core-2.10.1-cp39-none-win32.whl", hash = "sha256:a1c311fd06ab3b10805abb72109f01a134019739bd3286b8ae1bc2fc4e50c07a"}, - {file = "pydantic_core-2.10.1-cp39-none-win_amd64.whl", hash = "sha256:ae8a8843b11dc0b03b57b52793e391f0122e740de3df1474814c700d2622950a"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d43002441932f9a9ea5d6f9efaa2e21458221a3a4b417a14027a1d530201ef1b"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fcb83175cc4936a5425dde3356f079ae03c0802bbdf8ff82c035f8a54b333521"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:962ed72424bf1f72334e2f1e61b68f16c0e596f024ca7ac5daf229f7c26e4208"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cf5bb4dd67f20f3bbc1209ef572a259027c49e5ff694fa56bed62959b41e1f9"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e544246b859f17373bed915182ab841b80849ed9cf23f1f07b73b7c58baee5fb"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c0877239307b7e69d025b73774e88e86ce82f6ba6adf98f41069d5b0b78bd1bf"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:53df009d1e1ba40f696f8995683e067e3967101d4bb4ea6f667931b7d4a01357"}, - {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a1254357f7e4c82e77c348dabf2d55f1d14d19d91ff025004775e70a6ef40ada"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:524ff0ca3baea164d6d93a32c58ac79eca9f6cf713586fdc0adb66a8cdeab96a"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f0ac9fb8608dbc6eaf17956bf623c9119b4db7dbb511650910a82e261e6600f"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:320f14bd4542a04ab23747ff2c8a778bde727158b606e2661349557f0770711e"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63974d168b6233b4ed6a0046296803cb13c56637a7b8106564ab575926572a55"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:417243bf599ba1f1fef2bb8c543ceb918676954734e2dcb82bf162ae9d7bd514"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dda81e5ec82485155a19d9624cfcca9be88a405e2857354e5b089c2a982144b2"}, - {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:14cfbb00959259e15d684505263d5a21732b31248a5dd4941f73a3be233865b9"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:631cb7415225954fdcc2a024119101946793e5923f6c4d73a5914d27eb3d3a05"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:bec7dd208a4182e99c5b6c501ce0b1f49de2802448d4056091f8e630b28e9a52"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:149b8a07712f45b332faee1a2258d8ef1fb4a36f88c0c17cb687f205c5dc6e7d"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d966c47f9dd73c2d32a809d2be529112d509321c5310ebf54076812e6ecd884"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7eb037106f5c6b3b0b864ad226b0b7ab58157124161d48e4b30c4a43fef8bc4b"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:154ea7c52e32dce13065dbb20a4a6f0cc012b4f667ac90d648d36b12007fa9f7"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e562617a45b5a9da5be4abe72b971d4f00bf8555eb29bb91ec2ef2be348cd132"}, - {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f23b55eb5464468f9e0e9a9935ce3ed2a870608d5f534025cd5536bca25b1402"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:e9121b4009339b0f751955baf4543a0bfd6bc3f8188f8056b1a25a2d45099934"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0523aeb76e03f753b58be33b26540880bac5aa54422e4462404c432230543f33"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0e2959ef5d5b8dc9ef21e1a305a21a36e254e6a34432d00c72a92fdc5ecda5"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da01bec0a26befab4898ed83b362993c844b9a607a86add78604186297eb047e"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2e9072d71c1f6cfc79a36d4484c82823c560e6f5599c43c1ca6b5cdbd54f881"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f36a3489d9e28fe4b67be9992a23029c3cec0babc3bd9afb39f49844a8c721c5"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f64f82cc3443149292b32387086d02a6c7fb39b8781563e0ca7b8d7d9cf72bd7"}, - {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b4a6db486ac8e99ae696e09efc8b2b9fea67b63c8f88ba7a1a16c24a057a0776"}, - {file = "pydantic_core-2.10.1.tar.gz", hash = "sha256:0f8682dbdd2f67f8e1edddcbffcc29f60a6182b4901c367fc8c1c40d30bb0a82"}, + {file = "pydantic_core-2.14.5-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd"}, + {file = "pydantic_core-2.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66"}, + {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997"}, + {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093"}, + {file = "pydantic_core-2.14.5-cp310-none-win32.whl", hash = "sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720"}, + {file = "pydantic_core-2.14.5-cp310-none-win_amd64.whl", hash = "sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b"}, + {file = "pydantic_core-2.14.5-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459"}, + {file = "pydantic_core-2.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4"}, + {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada"}, + {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda"}, + {file = "pydantic_core-2.14.5-cp311-none-win32.whl", hash = "sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651"}, + {file = "pydantic_core-2.14.5-cp311-none-win_amd64.whl", hash = "sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077"}, + {file = "pydantic_core-2.14.5-cp311-none-win_arm64.whl", hash = "sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf"}, + {file = "pydantic_core-2.14.5-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093"}, + {file = "pydantic_core-2.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e"}, + {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69"}, + {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d"}, + {file = "pydantic_core-2.14.5-cp312-none-win32.whl", hash = "sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260"}, + {file = "pydantic_core-2.14.5-cp312-none-win_amd64.whl", hash = "sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36"}, + {file = "pydantic_core-2.14.5-cp312-none-win_arm64.whl", hash = "sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:af36f36538418f3806048f3b242a1777e2540ff9efaa667c27da63d2749dbce0"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:45e95333b8418ded64745f14574aa9bfc212cb4fbeed7a687b0c6e53b5e188cd"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e47a76848f92529879ecfc417ff88a2806438f57be4a6a8bf2961e8f9ca9ec7"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d81e6987b27bc7d101c8597e1cd2bcaa2fee5e8e0f356735c7ed34368c471550"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34708cc82c330e303f4ce87758828ef6e457681b58ce0e921b6e97937dd1e2a3"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c1988019752138b974c28f43751528116bcceadad85f33a258869e641d753"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e4d090e73e0725b2904fdbdd8d73b8802ddd691ef9254577b708d413bf3006e"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c7d5b5005f177764e96bd584d7bf28d6e26e96f2a541fdddb934c486e36fd59"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a71891847f0a73b1b9eb86d089baee301477abef45f7eaf303495cd1473613e4"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a717aef6971208f0851a2420b075338e33083111d92041157bbe0e2713b37325"}, + {file = "pydantic_core-2.14.5-cp37-none-win32.whl", hash = "sha256:de790a3b5aa2124b8b78ae5faa033937a72da8efe74b9231698b5a1dd9be3405"}, + {file = "pydantic_core-2.14.5-cp37-none-win_amd64.whl", hash = "sha256:6c327e9cd849b564b234da821236e6bcbe4f359a42ee05050dc79d8ed2a91588"}, + {file = "pydantic_core-2.14.5-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf"}, + {file = "pydantic_core-2.14.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b"}, + {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec"}, + {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124"}, + {file = "pydantic_core-2.14.5-cp38-none-win32.whl", hash = "sha256:fe0a5a1025eb797752136ac8b4fa21aa891e3d74fd340f864ff982d649691867"}, + {file = "pydantic_core-2.14.5-cp38-none-win_amd64.whl", hash = "sha256:079206491c435b60778cf2b0ee5fd645e61ffd6e70c47806c9ed51fc75af078d"}, + {file = "pydantic_core-2.14.5-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7"}, + {file = "pydantic_core-2.14.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955"}, + {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5"}, + {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209"}, + {file = "pydantic_core-2.14.5-cp39-none-win32.whl", hash = "sha256:ec1e72d6412f7126eb7b2e3bfca42b15e6e389e1bc88ea0069d0cc1742f477c6"}, + {file = "pydantic_core-2.14.5-cp39-none-win_amd64.whl", hash = "sha256:c0b97ec434041827935044bbbe52b03d6018c2897349670ff8fe11ed24d1d4ab"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3"}, + {file = "pydantic_core-2.14.5.tar.gz", hash = "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71"}, ] [package.dependencies] @@ -1401,31 +1191,16 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "rsa" -version = "4.9" -description = "Pure-Python RSA implementation" -category = "main" -optional = false -python-versions = ">=3.6,<4" -files = [ - {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, - {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, -] - -[package.dependencies] -pyasn1 = ">=0.1.3" - [[package]] name = "sentry-sdk" -version = "1.34.0" +version = "1.36.0" description = "Python client for Sentry (https://sentry.io)" category = "main" optional = false python-versions = "*" files = [ - {file = "sentry-sdk-1.34.0.tar.gz", hash = "sha256:e5d0d2b25931d88fa10986da59d941ac6037f742ab6ff2fce4143a27981d60c3"}, - {file = "sentry_sdk-1.34.0-py2.py3-none-any.whl", hash = "sha256:76dd087f38062ac6c1e30ed6feb533ee0037ff9e709974802db7b5dbf2e5db21"}, + {file = "sentry-sdk-1.36.0.tar.gz", hash = "sha256:f32dd16547f2f45e1c71a96fd4a48925e629541f7ddfe3d5d25ef7d5e94eb3c8"}, + {file = "sentry_sdk-1.36.0-py2.py3-none-any.whl", hash = "sha256:25d574f94fdf72199e331c2401fdac60d01b5be8f32822174c51c3ff0fc2f8cb"}, ] [package.dependencies] @@ -1464,18 +1239,18 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "68.2.2" +version = "69.0.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "dev" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, - {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, + {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, + {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] @@ -1535,19 +1310,18 @@ files = [ [[package]] name = "urllib3" -version = "2.0.7" +version = "2.1.0" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, - {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, + {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, + {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -1624,20 +1398,20 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)" [[package]] name = "virtualenv" -version = "20.24.6" +version = "20.24.7" description = "Virtual Python Environment builder" category = "dev" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.24.6-py3-none-any.whl", hash = "sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381"}, - {file = "virtualenv-20.24.6.tar.gz", hash = "sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af"}, + {file = "virtualenv-20.24.7-py3-none-any.whl", hash = "sha256:a18b3fd0314ca59a2e9f4b556819ed07183b3e9a3702ecfe213f593d44f7b3fd"}, + {file = "virtualenv-20.24.7.tar.gz", hash = "sha256:69050ffb42419c91f6c1284a7b24e0475d793447e35929b488bf6a0aade39353"}, ] [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<4" +platformdirs = ">=3.9.1,<5" [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] @@ -1896,4 +1670,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "dcaae2ec5ca3e050b55251e74f46a91c9191d1df0b29772ac11286bffa0a5a49" +content-hash = "f195a0e14ec30b2323f58379c62a3c727862443ddf4b0f741d93f94ef621498b" diff --git a/backend/requirements.txt b/backend/requirements.txt index ce529488..3154fc20 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -2,8 +2,7 @@ aiofiles==23.2.1 aiounittest==1.4.2 annotated-types==0.6.0 anyio==3.7.1 -cachetools==5.3.2 -certifi==2023.7.22 +certifi==2023.11.17 charset-normalizer==3.3.2 click==8.1.7 colorama==0.4.6 @@ -12,38 +11,27 @@ coveralls==3.3.1 dnspython==2.4.2 docopt==0.6.2 fastapi==0.104.1 -google-api-core[grpc]==2.14.0 -google-auth==2.23.4 -googleapis-common-protos==1.61.0 -googleapis-common-protos[grpc]==1.61.0 -grpc-google-iam-v1==0.12.7 -grpcio-status==1.59.2 -grpcio==1.59.2 +grpcio==1.59.3 gunicorn==21.2.0 h11==0.14.0 httptools==0.6.1 idna==3.4 -motor==3.3.1 +motor==3.3.2 packaging==23.2 -proto-plus==1.22.3 -protobuf==4.25.0 -pyasn1-modules==0.3.0 -pyasn1==0.5.0 -pydantic-core==2.10.1 -pydantic==2.4.2 +pydantic-core==2.14.5 +pydantic==2.5.2 pymongo==4.6.0 pymongo[srv]==4.6.0 python-dotenv==1.0.0 pytz==2023.3.post1 pyyaml==6.0.1 requests==2.31.0 -rsa==4.9 -sentry-sdk==1.34.0 +sentry-sdk==1.36.0 sniffio==1.3.0 starlette==0.27.0 svgwrite==1.4.3 typing-extensions==4.8.0 -urllib3==2.0.7 +urllib3==2.1.0 uvicorn[standard]==0.24.0.post1 uvloop==0.19.0 watchfiles==0.21.0