Skip to content

Commit

Permalink
Background tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
avgupta456 committed Nov 23, 2023
1 parent f9630f7 commit 688bf3e
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 30 deletions.
2 changes: 1 addition & 1 deletion backend/src/aggregation/layer2/__init__.py
Original file line number Diff line number Diff line change
@@ -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"]
19 changes: 7 additions & 12 deletions backend/src/aggregation/layer2/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))
Expand Down
9 changes: 9 additions & 0 deletions backend/src/models/background.py
Original file line number Diff line number Diff line change
@@ -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
24 changes: 18 additions & 6 deletions backend/src/processing/auth.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
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,
delete_user as db_delete_user,
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
Expand All @@ -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)
Expand All @@ -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:
Expand Down
10 changes: 7 additions & 3 deletions backend/src/processing/user/svg.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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
14 changes: 11 additions & 3 deletions backend/src/routers/auth/website.py
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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(
Expand Down
7 changes: 7 additions & 0 deletions backend/src/routers/background.py
Original file line number Diff line number Diff line change
@@ -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)
13 changes: 11 additions & 2 deletions backend/src/routers/users/main.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
15 changes: 12 additions & 3 deletions backend/src/routers/users/svg.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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()
Expand All @@ -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(),
Expand All @@ -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:
Expand All @@ -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(),
Expand All @@ -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:
Expand Down

0 comments on commit 688bf3e

Please sign in to comment.