Skip to content

Feature/gamesreleasedtoday #334

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 21 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/config.py.example
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ WEBSITE_LANGUAGE = "en"
# Get a free API KEY for Astronomy feature @ www.weatherapi.com/signup.aspx
ASTRONOMY_API_KEY = os.getenv('ASTRONOMY_API_KEY')
WEATHER_API_KEY = os.getenv('WEATHER_API_KEY')
RAWG_API_KEY = os.getenv("RAWG_API_KEY")

# https://developers.google.com/calendar/quickstart/python -
# follow instracions and make an env variable with the path to the file.
Expand Down
165 changes: 165 additions & 0 deletions app/internal/game_releases_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
from __future__ import annotations

from collections import defaultdict
from datetime import datetime
from functools import lru_cache
from typing import TYPE_CHECKING, Any, DefaultDict, Dict, List

import httpx
from loguru import logger
from sqlalchemy.orm import Session

from app import config
from app.database.models import UserSettings

if TYPE_CHECKING:
from app.routers.calendar_grid import Day, Week


def is_user_signed_up_for_game_releases(
session: Session,
current_user_id: int,
) -> bool:
is_signed_up = bool(
session.query(UserSettings)
.filter(UserSettings.user_id == current_user_id)
.filter(UserSettings.video_game_releases.is_(True))
.first(),
)

return is_signed_up


def add_game_events_to_weeks(
weeks: List["Week"],
is_active: bool = True,
) -> List["Week"]:
if not is_active:
return weeks
first_week: Week = weeks[0]
last_week: Week = weeks[-1]
first_day: Day = first_week.days[0]
last_day: Day = last_week.days[-1]
first_day_str = datetime.strptime(first_day.set_id(), "%d-%B-%Y")
last_day_str = datetime.strptime(last_day.set_id(), "%d-%B-%Y")

output = get_games_data_by_dates_from_api(
start_date=first_day_str.strftime("%Y-%m-%d"),
end_date=last_day_str.strftime("%Y-%m-%d"),
)
if not output["success"]:
logger.exception("Unsuccessful RAWG API call")
return weeks
games_by_dates = output["results"]

unformatted_games_by_dates = get_games_data_separated_by_dates(
games_by_dates,
)
formatted_games = get_formatted_games_in_days(unformatted_games_by_dates)

return insert_formatted_games_to_weeks(weeks, formatted_games)


def insert_formatted_games_to_weeks(
weeks: List["Week"],
formatted_games: DefaultDict[List[str]],
) -> List["Week"]:
for week in weeks:
for day in week.days:
if day.set_id() in formatted_games.keys():
for game in formatted_games[day.set_id()]:
day.dailyevents.append(
(
f"GR!- {(game)[:10]}",
(game),
),
)
return weeks


@lru_cache(maxsize=128)
def get_games_data_by_dates_from_api(
start_date: str,
end_date: str,
) -> Dict[str, Any]:
API = "https://api.rawg.io/api/games"
NO_API_RESPONSE = "The RAWG server did not response"
input_query_string = {
"dates": f"{start_date},{end_date}",
"key": config.RAWG_API_KEY,
}

output: Dict[str, Any] = {}
try:
response = httpx.get(
API,
params=input_query_string,
)
except httpx.HTTPError:
output["success"] = False
output["error"] = NO_API_RESPONSE
return output

if response.status_code != httpx.codes.OK:
output["success"] = False
output["error"] = NO_API_RESPONSE
return output

output["success"] = True
try:
output.update(response.json())
return output
except KeyError:
output["success"] = False
output["error"] = response.json()["error"]["message"]
return output


def get_games_data_separated_by_dates(
api_data: Dict[str, Any],
) -> DefaultDict[List]:
games_data = defaultdict(list)
for result in api_data:
current = {
"name": result["name"],
"platforms": [],
}
if result["platforms"]:
for platform in result["platforms"]:
current["platforms"].append(platform["platform"]["name"])
ybd_release_date = translate_ymd_date_to_dby(result["released"])
games_data[ybd_release_date].append(current)
return games_data


def get_formatted_games_in_days(
separated_games_dict: DefaultDict[List],
with_platforms: bool = False,
) -> DefaultDict[List[str]]:
formatted_games = defaultdict(list)

for date, game_data in separated_games_dict.items():
for game in game_data:
formatted_game_str = format_single_game(game, with_platforms)
formatted_games[date].append(formatted_game_str)
return formatted_games


def format_single_game(raw_game: Dict, with_platforms: bool = False) -> str:
formatted_game_str = ""
formatted_game_str += raw_game["name"]
if with_platforms:
formatted_game_str += "-Platforms-<br>"
for platform in raw_game["platforms"]:
formatted_game_str += f"{platform},"
return formatted_game_str


def translate_ymd_date_to_dby(ymd_str: str) -> str:
ymd_time = datetime.strptime(ymd_str, "%Y-%m-%d")
return ymd_time.strftime("%d-%B-%Y")


def translate_dby_date_to_ymd(dby_str: str) -> str:
dby_time = datetime.strptime(dby_str, "%d-%B-%Y")
return dby_time.strftime("%Y-%m-%d")
2 changes: 2 additions & 0 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def create_tables(engine, psql_environment):
features,
four_o_four,
friendview,
game_release_dates_service,
google_connect,
joke,
login,
Expand Down Expand Up @@ -145,6 +146,7 @@ async def swagger_ui_redirect():
weekview.router,
weight.router,
whatsapp.router,
game_release_dates_service.router,
]

for router in routers_to_include:
Expand Down
16 changes: 9 additions & 7 deletions app/routers/calendar.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from http import HTTPStatus

from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse
from starlette.responses import Response
from fastapi.responses import HTMLResponse, Response

from app.dependencies import templates
from app.routers import calendar_grid as cg
Expand All @@ -11,7 +10,7 @@
prefix="/calendar/month",
tags=["calendar"],
responses={404: {"description": "Not found"}},
include_in_schema=False
include_in_schema=False,
)


Expand All @@ -25,18 +24,21 @@ async def calendar(request: Request) -> Response:
"request": request,
"day": day,
"week_days": cg.Week.DAYS_OF_THE_WEEK,
"weeks_block": cg.get_month_block(day)
}
"weeks_block": cg.get_month_block(day),
},
)


@router.get("/add/{date}")
async def update_calendar(
request: Request, date: str, days: int
request: Request,
date: str,
days: int,
) -> HTMLResponse:
last_day = cg.Day.convert_str_to_date(date)
next_weeks = cg.create_weeks(cg.get_n_days(last_day, days))
template = templates.get_template(
'partials/calendar/monthly_view/add_week.html')
"partials/calendar/monthly_view/add_week.html",
)
content = template.render(weeks_block=next_weeks)
return HTMLResponse(content=content, status_code=HTTPStatus.OK)
6 changes: 5 additions & 1 deletion app/routers/calendar_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

import pytz

from app.internal.game_releases_utils import add_game_events_to_weeks

MONTH_BLOCK: int = 6

locale.setlocale(locale.LC_ALL, "en_US.UTF-8")
Expand Down Expand Up @@ -190,7 +192,9 @@ def create_weeks(
"""Return lists of Weeks objects."""
ndays: List[Day] = list(days)
num_days: int = len(ndays)
return [Week(ndays[i : i + length]) for i in range(0, num_days, length)]
_weeks = [Week(ndays[i : i + length]) for i in range(0, num_days, length)]

return add_game_events_to_weeks(_weeks, is_active=True)


def get_month_block(day: Day, n: int = MONTH_BLOCK) -> List[Week]:
Expand Down
144 changes: 144 additions & 0 deletions app/routers/game_release_dates_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import datetime
from typing import Dict, List

import requests
from fastapi import APIRouter, Depends, Request
from fastapi.responses import RedirectResponse, Response
from sqlalchemy.orm import Session
from starlette.status import HTTP_302_FOUND

from app.database.models import UserSettings
from app.dependencies import get_db, templates
from app.internal.game_releases_utils import (
is_user_signed_up_for_game_releases,
)
from app.internal.security.dependencies import current_user
from app.internal.security.schema import CurrentUser
from app.internal.utils import create_model
from app.routers.profile import router as profile_router

router = APIRouter(
prefix="/game-releases",
tags=["game-releases"],
responses={404: {"description": "Not found"}},
)


@router.post("/get_releases_by_dates")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a post request. /{date}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need it to be awaited

async def fetch_released_games(
request: Request,
session=Depends(get_db),
) -> Response:
data = await request.form()

from_date = data["from-date"]
to_date = data["to-date"]

games = get_games_data(from_date, to_date)

return templates.TemplateResponse(
"partials/calendar/feature_settings/games_list.html",
{"request": request, "games": games},
)


@router.get("/next-month")
def get_game_releases_month(request: Request) -> List:
today = datetime.datetime.today()
delta = datetime.timedelta(days=30)
today_str = today.strftime("%Y-%m-%d")
in_month_str = (today + delta).strftime("%Y-%m-%d")

return get_games_data(today_str, in_month_str)


def get_games_data(start_date: datetime, end_date: datetime) -> List[Dict]:
API = "https://api.rawg.io/api/games"

current_day_games = requests.get(
f"{API}?dates={start_date},{end_date}",
)
Comment on lines +56 to +60
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will cause great load on our server, especially with high amount of users. Use JS instead.

current_day_games = current_day_games.json()["results"]
games_data = []
for result in current_day_games:
current = {
"name": result["name"],
"slug": result["slug"],
"platforms": [],
}

for platform in result["platforms"]:
current["platforms"].append(platform["platform"]["name"])
current["release_date"] = result["released"]
games_data.append(current)

return games_data


@router.post("/subscribe")
async def subscribe_game_release_service(
request: Request,
session: Session = Depends(get_db),
user: CurrentUser = Depends(current_user),
) -> Response:
if is_user_signed_up_for_game_releases(session, user.user_id):
return RedirectResponse(
profile_router.url_path_for("profile"),
status_code=HTTP_302_FOUND,
)
games_setting_true_for_model = {
"user_id": user.user_id,
"video_game_releases": True,
}
current_user_settings = session.query(UserSettings).filter(
UserSettings.user_id == user.user_id,
)
if current_user_settings:
# TODO:
# If all users are created with a UserSettings entry -
# unnecessary check
current_user_settings.update(games_setting_true_for_model)
session.commit()
else:
create_model(session, UserSettings, **games_setting_true_for_model)
return RedirectResponse(
profile_router.url_path_for("profile"),
status_code=HTTP_302_FOUND,
)


@router.post("/unsubscribe")
async def unsubscribe_game_release_service(
request: Request,
session: Session = Depends(get_db),
user: CurrentUser = Depends(current_user),
) -> RedirectResponse:
current_user_id = user.user_id

if not is_user_signed_up_for_game_releases(session, current_user_id):
return RedirectResponse(
profile_router.url_path_for("profile"),
status_code=HTTP_302_FOUND,
)
else:
games_setting_false_for_model = {
"user_id": str(current_user_id),
"video_game_releases": False,
}
current_user_settings = session.query(UserSettings).filter(
UserSettings.user_id == current_user_id,
)
if current_user_settings:
# TODO:
# If all users are created with a UserSettings entry -
# unnecessary check
current_user_settings.update(games_setting_false_for_model)
session.commit()
else:
create_model(
session, UserSettings, **games_setting_false_for_model
)
return RedirectResponse(
profile_router.url_path_for("profile"),
status_code=HTTP_302_FOUND,
)
Loading