diff --git a/.env.example b/.env.example index 969477a9..ff24da67 100644 --- a/.env.example +++ b/.env.example @@ -17,7 +17,7 @@ USE_API_CACHE_IN_APP=false EXPIRED_CACHE_REFRESH_LIMIT=3600 HEROES_PATH_CACHE_TIMEOUT=86400 HERO_PATH_CACHE_TIMEOUT=86400 -HOME_PATH_CACHE_TIMEOUT=86400 +CSV_CACHE_TIMEOUT=86400 CAREER_PATH_CACHE_TIMEOUT=7200 SEARCH_ACCOUNT_PATH_CACHE_TIMEOUT=3600 NAMECARDS_TIMEOUT=7200 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a3ce9e32..d3532057 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,6 +5,7 @@ This guide aims to help you in contributing in OverFast API. The first step for As of now, only some specific stuff can easily be updated by anyone, even without any knowledge in Python or FastAPI framework. If I take too much time to update them, don't hesitate to make a PR if you need up-to-date data : - The CSV file containing basic heroes data : name, role, and some statistics like health, armor and shields +- The CSV file containing the list of gamemodes of the game - The CSV file containing the list of maps of the game ## 🦸 Heroes data @@ -16,6 +17,12 @@ The CSV file containing heroes statistics data is located in `app/data/heroes.cs - `armor` : Armor of the hero, mainly possessed by tanks - `shields` : Shields of the hero +## 🎲 Gamemodes list +The CSV file containing gamemodes list is located in `app/data/gamemodes.csv`. Data is divided into 3 columns : +- `key` : Key of the gamemode, used in URLs of the API, and for the name of the corresponding screenshot and icon files +- `name` : Name of the gamemode (in english) +- `description` : Description of the gamemode (in english) + ## 🗺️ Maps list The CSV file containing maps list is located in `app/data/maps.csv`. Data is divided into 5 columns : - `key` : Key of the map, used in URLs of the API, and for the name of the corresponding screenshot file diff --git a/app/commands/check_and_update_cache.py b/app/commands/check_and_update_cache.py index 5622c42a..eb178425 100644 --- a/app/commands/check_and_update_cache.py +++ b/app/commands/check_and_update_cache.py @@ -10,8 +10,8 @@ from app.common.helpers import overfast_internal_error from app.common.logging import logger from app.config import settings -from app.parsers.abstract_parser import AbstractParser from app.parsers.gamemodes_parser import GamemodesParser +from app.parsers.generics.abstract_parser import AbstractParser from app.parsers.hero_parser import HeroParser from app.parsers.heroes_parser import HeroesParser from app.parsers.heroes_stats_parser import HeroesStatsParser diff --git a/app/common/enums.py b/app/common/enums.py index 67abcb33..72c9041a 100644 --- a/app/common/enums.py +++ b/app/common/enums.py @@ -132,16 +132,13 @@ class Locale(StrEnum): CHINESE_TAIWAN = "zh-tw" -class MapGamemode(StrEnum): - """Maps gamemodes keys""" - - ASSAULT = "assault" - CAPTURE_THE_FLAG = "capture-the-flag" - CONTROL = "control" - DEATHMATCH = "deathmatch" - ELIMINATION = "elimination" - ESCORT = "escort" - FLASHPOINT = "flashpoint" - HYBRID = "hybrid" - PUSH = "push" - TEAM_DEATHMATCH = "team-deathmatch" +# Dynamically create the MapGamemode enum by using the CSV File +gamemodes_data = read_csv_data_file("gamemodes.csv") +MapGamemode = StrEnum( + "MapGamemode", + { + gamemode["key"].upper().replace("-", "_"): gamemode["key"] + for gamemode in gamemodes_data + }, +) +MapGamemode.__doc__ = "Maps gamemodes keys" diff --git a/app/config.py b/app/config.py index 3b88f07d..a9dd53d9 100644 --- a/app/config.py +++ b/app/config.py @@ -88,8 +88,8 @@ class Settings(BaseSettings): # Cache TTL for specific hero data (seconds) hero_path_cache_timeout: int = 86400 - # Cache TTL for Blizzard homepage data : gamemodes and roles (seconds) - home_path_cache_timeout: int = 86400 + # Cache TTL for local CSV-based data : heroes stats, gamemodes and maps + csv_cache_timeout: int = 86400 # Cache TTL for career pages data (seconds) career_path_cache_timeout: int = 7200 diff --git a/app/data/gamemodes.csv b/app/data/gamemodes.csv new file mode 100644 index 00000000..41d05439 --- /dev/null +++ b/app/data/gamemodes.csv @@ -0,0 +1,11 @@ +key,name,description +assault,Assault,"Teams fight to capture or defend two successive points against the enemy team. It's an inactive Overwatch 1 gamemode, also called 2CP." +capture-the-flag,Capture the Flag,Teams compete to capture the enemy team’s flag while defending their own. +control,Control,Teams fight to hold a single objective. The first team to win two rounds wins the map. +deathmatch,Deathmatch,Race to reach 20 points first by racking up kills in a free-for-all format. +elimination,Elimination,"Dispatch all enemies to win the round. Win three rounds to claim victory. Available with teams of one, three, or six." +escort,Escort,"One team escorts a payload to its delivery point, while the other races to stop them." +flashpoint,Flashpoint,"Teams fight for control of key positions across the map, aiming to capture three of them before their opponents do." +hybrid,Hybrid,"Attackers capture a payload, then escort it to its destination; defenders try to hold them back." +push,Push,Teams battle to take control of a robot and push it toward the enemy base. +team-deathmatch,Team Deathmatch,Team up and triumph over your enemies by scoring the most kills. diff --git a/app/handlers/list_gamemodes_request_handler.py b/app/handlers/list_gamemodes_request_handler.py index 4da2a417..7c5c7532 100644 --- a/app/handlers/list_gamemodes_request_handler.py +++ b/app/handlers/list_gamemodes_request_handler.py @@ -13,4 +13,4 @@ class ListGamemodesRequestHandler(APIRequestHandler): """ parser_classes: ClassVar[list] = [GamemodesParser] - timeout = settings.home_path_cache_timeout + timeout = settings.csv_cache_timeout diff --git a/app/handlers/list_maps_request_handler.py b/app/handlers/list_maps_request_handler.py index 9f54a0cb..3ba6493a 100644 --- a/app/handlers/list_maps_request_handler.py +++ b/app/handlers/list_maps_request_handler.py @@ -13,4 +13,4 @@ class ListMapsRequestHandler(APIRequestHandler): """ parser_classes: ClassVar[list] = [MapsParser] - timeout = settings.home_path_cache_timeout + timeout = settings.csv_cache_timeout diff --git a/app/parsers/gamemodes_parser.py b/app/parsers/gamemodes_parser.py index d2f224aa..2d4ef6f7 100644 --- a/app/parsers/gamemodes_parser.py +++ b/app/parsers/gamemodes_parser.py @@ -1,47 +1,27 @@ """Gamemodes Parser module""" -from app.config import settings -from .api_parser import APIParser +from .generics.csv_parser import CSVParser -class GamemodesParser(APIParser): +class GamemodesParser(CSVParser): """Overwatch map gamemodes list page Parser class""" - root_path = settings.home_path - timeout = settings.home_path_cache_timeout + filename = "gamemodes" def parse_data(self) -> list[dict]: - gamemodes_container = ( - self.root_tag.find("div", class_="maps", recursive=False) - .find("blz-carousel-section", recursive=False) - .find("blz-carousel", recursive=False) - ) - - gamemodes_extras = [ - { - "key": feature_div["label"], - "description": ( - feature_div.find("blz-header") - .find("div", slot="description") - .get_text() - .strip() - ), - "screenshot": feature_div.find("blz-image")["src:min-plus"], - } - for feature_div in gamemodes_container.find_all("blz-feature") - ] - return [ { - "key": gamemodes_extras[gamemode_index]["key"], - "name": gamemode_div.get_text(), - "icon": gamemode_div.find("blz-image")["src:min-plus"], - "description": gamemodes_extras[gamemode_index]["description"], - "screenshot": gamemodes_extras[gamemode_index]["screenshot"], - } - for gamemode_index, gamemode_div in enumerate( - gamemodes_container.find("blz-tab-controls").find_all( - "blz-tab-control", + "key": gamemode["key"], + "name": gamemode["name"], + "icon": self.get_static_url( + f"{gamemode['key']}-icon", + extension="svg", ), - ) + "description": gamemode["description"], + "screenshot": self.get_static_url( + gamemode["key"], + extension="avif", + ), + } + for gamemode in self.csv_data ] diff --git a/app/parsers/generics/__init__.py b/app/parsers/generics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/parsers/abstract_parser.py b/app/parsers/generics/abstract_parser.py similarity index 100% rename from app/parsers/abstract_parser.py rename to app/parsers/generics/abstract_parser.py diff --git a/app/parsers/api_parser.py b/app/parsers/generics/api_parser.py similarity index 100% rename from app/parsers/api_parser.py rename to app/parsers/generics/api_parser.py diff --git a/app/parsers/generics/csv_parser.py b/app/parsers/generics/csv_parser.py new file mode 100644 index 00000000..7419df04 --- /dev/null +++ b/app/parsers/generics/csv_parser.py @@ -0,0 +1,45 @@ +"""Abstract API Parser module""" +from abc import abstractmethod + +from app.common.helpers import read_csv_data_file +from app.config import settings + +from .abstract_parser import AbstractParser + + +class CSVParser(AbstractParser): + """CSV Parser class used to define generic behavior for parsers used + to extract data from local CSV files. + """ + + # Timeout to use for every CSV-based data + timeout = settings.csv_cache_timeout + + # Name of CSV file to retrieve (without extension), also + # used as a sub-folder name for storing related static files + filename: str + + async def retrieve_and_parse_data(self) -> None: + """Method used to retrieve data from CSV file and storing + it into self.data attribute + """ + + # Read the CSV file + self.csv_data = read_csv_data_file(f"{self.filename}.csv") + + # Parse the data + self.data = self.parse_data() + + # Update the Parser Cache + self.cache_manager.update_parser_cache(self.cache_key, self.data, self.timeout) + + @abstractmethod + def parse_data(self) -> dict | list[dict]: + """Main submethod of the parser, mainly doing the parsing of CSV data and + returning a dict, which will be cached and used by the API. Can + raise an error if there is an issue when parsing the data. + """ + + def get_static_url(self, key: str, extension: str = "jpg") -> str: + """Method used to retrieve the URL of a local static file""" + return f"{settings.app_base_url}/static/{self.filename}/{key}.{extension}" diff --git a/app/parsers/hero_parser.py b/app/parsers/hero_parser.py index da7d95ea..3d1b689f 100644 --- a/app/parsers/hero_parser.py +++ b/app/parsers/hero_parser.py @@ -8,7 +8,7 @@ from app.common.exceptions import ParserBlizzardError from app.config import settings -from .api_parser import APIParser +from .generics.api_parser import APIParser from .helpers import get_full_url, get_role_from_icon_url diff --git a/app/parsers/heroes_parser.py b/app/parsers/heroes_parser.py index 5a6d7e54..db6b5678 100644 --- a/app/parsers/heroes_parser.py +++ b/app/parsers/heroes_parser.py @@ -1,7 +1,7 @@ """Heroes page Parser module""" from app.config import settings -from .api_parser import APIParser +from .generics.api_parser import APIParser class HeroesParser(APIParser): diff --git a/app/parsers/heroes_stats_parser.py b/app/parsers/heroes_stats_parser.py index 6c062320..82f9c5f5 100644 --- a/app/parsers/heroes_stats_parser.py +++ b/app/parsers/heroes_stats_parser.py @@ -1,29 +1,21 @@ """Heroes Stats Parser module""" from typing import ClassVar -from app.common.helpers import read_csv_data_file -from app.config import settings +from .generics.csv_parser import CSVParser -from .abstract_parser import AbstractParser - -class HeroesStatsParser(AbstractParser): +class HeroesStatsParser(CSVParser): """Heroes stats (health, armor, shields) Parser class""" - timeout = settings.hero_path_cache_timeout + filename = "heroes" hitpoints_keys: ClassVar[set] = {"health", "armor", "shields"} - async def retrieve_and_parse_data(self) -> None: - heroes_stats_data = read_csv_data_file("heroes.csv") - - self.data = { + def parse_data(self) -> dict: + return { hero_stats["key"]: {"hitpoints": self.__get_hitpoints(hero_stats)} - for hero_stats in heroes_stats_data + for hero_stats in self.csv_data } - # Update the Parser Cache - self.cache_manager.update_parser_cache(self.cache_key, self.data, self.timeout) - def __get_hitpoints(self, hero_stats: dict) -> dict: hitpoints = {hp_key: int(hero_stats[hp_key]) for hp_key in self.hitpoints_keys} hitpoints["total"] = sum(hitpoints.values()) diff --git a/app/parsers/maps_parser.py b/app/parsers/maps_parser.py index 8a1b3283..92da0dab 100644 --- a/app/parsers/maps_parser.py +++ b/app/parsers/maps_parser.py @@ -1,32 +1,25 @@ """Maps Parser module""" -from app.common.helpers import read_csv_data_file -from app.config import settings -from .abstract_parser import AbstractParser +from .generics.csv_parser import CSVParser -class MapsParser(AbstractParser): +class MapsParser(CSVParser): """Overwatch maps list page Parser class""" - timeout = settings.home_path_cache_timeout + filename = "maps" - async def retrieve_and_parse_data(self) -> None: - maps_data = read_csv_data_file("maps.csv") - - self.data = [ + def parse_data(self) -> list[dict]: + return [ { "name": map_dict["name"], - "screenshot": self.get_screenshot_url(map_dict["key"]), + "screenshot": self.get_static_url(map_dict["key"]), "gamemodes": map_dict["gamemodes"].split(","), "location": map_dict["location"], - "country_code": map_dict["country_code"] or None, + "country_code": map_dict.get("country_code") or None, } - for map_dict in maps_data + for map_dict in self.csv_data ] - # Update the Parser Cache - self.cache_manager.update_parser_cache(self.cache_key, self.data, self.timeout) - def filter_request_using_query(self, **kwargs) -> list: gamemode = kwargs.get("gamemode") return ( @@ -36,6 +29,3 @@ def filter_request_using_query(self, **kwargs) -> list: map_dict for map_dict in self.data if gamemode in map_dict["gamemodes"] ] ) - - def get_screenshot_url(self, map_key: str) -> str: - return f"{settings.app_base_url}/static/maps/{map_key}.jpg" diff --git a/app/parsers/namecard_parser.py b/app/parsers/namecard_parser.py index cd016c59..8eab655a 100644 --- a/app/parsers/namecard_parser.py +++ b/app/parsers/namecard_parser.py @@ -5,7 +5,7 @@ from app.common.logging import logger from app.config import settings -from .api_parser import APIParser +from .generics.api_parser import APIParser class NamecardParser(APIParser): diff --git a/app/parsers/player_parser.py b/app/parsers/player_parser.py index d01a798f..91181040 100644 --- a/app/parsers/player_parser.py +++ b/app/parsers/player_parser.py @@ -14,7 +14,7 @@ from app.common.exceptions import ParserBlizzardError from app.config import settings -from .api_parser import APIParser +from .generics.api_parser import APIParser from .helpers import ( get_computed_stat_value, get_division_from_rank_icon, diff --git a/app/parsers/roles_parser.py b/app/parsers/roles_parser.py index d47bbd2c..e2438aad 100644 --- a/app/parsers/roles_parser.py +++ b/app/parsers/roles_parser.py @@ -1,7 +1,7 @@ """Roles Parser module""" from app.config import settings -from .api_parser import APIParser +from .generics.api_parser import APIParser from .helpers import get_role_from_icon_url diff --git a/app/routers/gamemodes.py b/app/routers/gamemodes.py index c151ce96..337bfa69 100644 --- a/app/routers/gamemodes.py +++ b/app/routers/gamemodes.py @@ -1,8 +1,8 @@ """Gamemodes endpoints router : gamemodes list, etc.""" -from fastapi import APIRouter, Query, Request +from fastapi import APIRouter, Request from app.common.decorators import validation_error_handler -from app.common.enums import Locale, RouteTag +from app.common.enums import RouteTag from app.common.helpers import routes_responses from app.handlers.list_gamemodes_request_handler import ListGamemodesRequestHandler from app.models.gamemodes import GamemodeDetails @@ -16,14 +16,11 @@ tags=[RouteTag.GAMEMODES], summary="Get a list of gamemodes", description=( - "Get a list of Overwatch gamemodes : Assault, Escort, Hybrid, etc." + "Get a list of Overwatch gamemodes : Assault, Escort, Flashpoint, Hybrid, etc." "
**Cache TTL : 1 day.**" ), operation_id="list_map_gamemodes", ) @validation_error_handler(response_model=GamemodeDetails) -async def list_map_gamemodes( - request: Request, - locale: Locale = Query(Locale.ENGLISH_US, title="Locale to be displayed"), -) -> list[GamemodeDetails]: - return await ListGamemodesRequestHandler(request).process_request(locale=locale) +async def list_map_gamemodes(request: Request) -> list[GamemodeDetails]: + return await ListGamemodesRequestHandler(request).process_request() diff --git a/pyproject.toml b/pyproject.toml index 80a4ecc0..a9dd403a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "overfast-api" -version = "2.19.3" +version = "2.19.4" description = "Overwatch API giving data about heroes, maps, and players statistics." license = "MIT" authors = ["TeKrop "] @@ -12,14 +12,14 @@ documentation = "https://overfast-api.tekrop.fr/" [tool.poetry.dependencies] python = "^3.11" beautifulsoup4 = "^4.12.2" -fastapi = "^0.101.0" +fastapi = "^0.103.0" httpx = {extras = ["http2"], version = "^0.24.1"} loguru = "^0.7.0" lxml = "^4.9.3" -redis = "^4.6.0" +redis = "^5.0.0" uvicorn = {extras = ["standard"], version = "^0.23.2"} -pydantic = "^2.1.1" -pydantic-settings = "^2.0.2" +pydantic = "^2.3.0" +pydantic-settings = "^2.0.3" [tool.poetry.group.dev.dependencies] black = "^23.7.0" diff --git a/static/gamemodes/assault-icon.svg b/static/gamemodes/assault-icon.svg new file mode 100644 index 00000000..adef9256 --- /dev/null +++ b/static/gamemodes/assault-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/static/gamemodes/assault.avif b/static/gamemodes/assault.avif new file mode 100644 index 00000000..f1371d7f Binary files /dev/null and b/static/gamemodes/assault.avif differ diff --git a/static/gamemodes/capture-the-flag-icon.svg b/static/gamemodes/capture-the-flag-icon.svg new file mode 100644 index 00000000..e48b47f7 --- /dev/null +++ b/static/gamemodes/capture-the-flag-icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/static/gamemodes/capture-the-flag.avif b/static/gamemodes/capture-the-flag.avif new file mode 100644 index 00000000..e4bef676 Binary files /dev/null and b/static/gamemodes/capture-the-flag.avif differ diff --git a/static/gamemodes/control-icon.svg b/static/gamemodes/control-icon.svg new file mode 100644 index 00000000..8d766328 --- /dev/null +++ b/static/gamemodes/control-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/gamemodes/control.avif b/static/gamemodes/control.avif new file mode 100644 index 00000000..81fe12d5 Binary files /dev/null and b/static/gamemodes/control.avif differ diff --git a/static/gamemodes/deathmatch-icon.svg b/static/gamemodes/deathmatch-icon.svg new file mode 100644 index 00000000..6caafb28 --- /dev/null +++ b/static/gamemodes/deathmatch-icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/static/gamemodes/deathmatch.avif b/static/gamemodes/deathmatch.avif new file mode 100644 index 00000000..64460347 Binary files /dev/null and b/static/gamemodes/deathmatch.avif differ diff --git a/static/gamemodes/elimination-icon.svg b/static/gamemodes/elimination-icon.svg new file mode 100644 index 00000000..d54ff094 --- /dev/null +++ b/static/gamemodes/elimination-icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/gamemodes/elimination.avif b/static/gamemodes/elimination.avif new file mode 100644 index 00000000..84ffa855 Binary files /dev/null and b/static/gamemodes/elimination.avif differ diff --git a/static/gamemodes/escort-icon.svg b/static/gamemodes/escort-icon.svg new file mode 100644 index 00000000..4fa16869 --- /dev/null +++ b/static/gamemodes/escort-icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/gamemodes/escort.avif b/static/gamemodes/escort.avif new file mode 100644 index 00000000..e12c785a Binary files /dev/null and b/static/gamemodes/escort.avif differ diff --git a/static/gamemodes/flashpoint-icon.svg b/static/gamemodes/flashpoint-icon.svg new file mode 100644 index 00000000..38842187 --- /dev/null +++ b/static/gamemodes/flashpoint-icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static/gamemodes/flashpoint.avif b/static/gamemodes/flashpoint.avif new file mode 100644 index 00000000..f2509125 Binary files /dev/null and b/static/gamemodes/flashpoint.avif differ diff --git a/static/gamemodes/hybrid-icon.svg b/static/gamemodes/hybrid-icon.svg new file mode 100644 index 00000000..fa713606 --- /dev/null +++ b/static/gamemodes/hybrid-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/gamemodes/hybrid.avif b/static/gamemodes/hybrid.avif new file mode 100644 index 00000000..9a130bbd Binary files /dev/null and b/static/gamemodes/hybrid.avif differ diff --git a/static/gamemodes/push-icon.svg b/static/gamemodes/push-icon.svg new file mode 100644 index 00000000..730c2890 --- /dev/null +++ b/static/gamemodes/push-icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/gamemodes/push.avif b/static/gamemodes/push.avif new file mode 100644 index 00000000..b022f00c Binary files /dev/null and b/static/gamemodes/push.avif differ diff --git a/static/gamemodes/team-deathmatch-icon.svg b/static/gamemodes/team-deathmatch-icon.svg new file mode 100644 index 00000000..81c12c1b --- /dev/null +++ b/static/gamemodes/team-deathmatch-icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/static/gamemodes/team-deathmatch.avif b/static/gamemodes/team-deathmatch.avif new file mode 100644 index 00000000..ef023f18 Binary files /dev/null and b/static/gamemodes/team-deathmatch.avif differ diff --git a/tests/commands/test_check_and_update_cache.py b/tests/commands/test_check_and_update_cache.py index 93dea234..8a5110a0 100644 --- a/tests/commands/test_check_and_update_cache.py +++ b/tests/commands/test_check_and_update_cache.py @@ -39,9 +39,7 @@ def test_check_and_update_gamemodes_cache_to_update( home_html_data: list, gamemodes_json_data: dict, ): - gamemodes_cache_key = ( - f"GamemodesParser-{settings.blizzard_host}/{locale}{settings.home_path}" - ) + gamemodes_cache_key = "GamemodesParser" # Add some data (to update and not to update) cache_manager.update_parser_cache( @@ -167,7 +165,7 @@ def test_check_and_update_cache_no_update(cache_manager: CacheManager, locale: s settings.expired_cache_refresh_limit + 5, ) cache_manager.update_parser_cache( - f"GamemodesParser-{settings.blizzard_host}/{locale}{settings.home_path}", + "GamemodesParser", [], settings.expired_cache_refresh_limit + 10, ) @@ -206,7 +204,7 @@ def test_check_and_update_specific_player_to_update( settings.expired_cache_refresh_limit + 5, ) cache_manager.update_parser_cache( - f"GamemodesParser-{settings.blizzard_host}/{locale}{settings.home_path}", + "GamemodesParser", [], settings.expired_cache_refresh_limit + 10, ) @@ -256,7 +254,7 @@ def test_check_and_update_player_stats_summary_to_update( settings.expired_cache_refresh_limit + 5, ) cache_manager.update_parser_cache( - f"GamemodesParser-{settings.blizzard_host}/{locale}{settings.home_path}", + "GamemodesParser", [], settings.expired_cache_refresh_limit + 10, ) @@ -403,14 +401,11 @@ def test_check_parser_init_error( def test_check_and_update_several_to_update( cache_manager: CacheManager, - locale: str, home_html_data: list, gamemodes_json_data: dict, maps_json_data: dict, ): - gamemodes_cache_key = ( - f"GamemodesParser-{settings.blizzard_host}/{locale}{settings.home_path}" - ) + gamemodes_cache_key = "GamemodesParser" maps_cache_key = "MapsParser" # Add some data to update @@ -468,7 +463,7 @@ def test_check_and_update_namecard_to_update( settings.expired_cache_refresh_limit + 5, ) cache_manager.update_parser_cache( - f"GamemodesParser-{settings.blizzard_host}/{locale}{settings.home_path}", + "GamemodesParser", [], settings.expired_cache_refresh_limit + 10, ) diff --git a/tests/fixtures/json/gamemodes.json b/tests/fixtures/json/gamemodes.json index b807ed53..8e70a553 100644 --- a/tests/fixtures/json/gamemodes.json +++ b/tests/fixtures/json/gamemodes.json @@ -1 +1,72 @@ -[{"key":"push","name":"Push","icon":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt054b513cd6e95acf/62fd5b4a8972f93d1e325243/Push.svg","description":"Teams battle to take control of a robot and push it toward the enemy base.","screenshot":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt93eefb6e91347639/62fc2d9eda42240856c1459c/Toronto_Push.jpg"},{"key":"control","name":"Control","icon":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt5ea0d3baf0e2d03f/62fc2d8bda42240856c14598/Control.svg","description":"Teams fight to hold a single objective. The first team to win two rounds wins the map.","screenshot":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blta3f8e647a52bb9e9/62fc312456388515882767ed/Oasis_Control.jpg"},{"key":"escort","name":"Escort","icon":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/bltba08041a1eb32c43/62fc2d8bc317e303559ab5b1/Escort.svg","description":"One team escorts a payload to its delivery point, while the other races to stop them.","screenshot":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt7d04bb6ad434cafa/62fc332a7ea1d970140fa139/Monte_Carlo_Escort.jpg"},{"key":"hybrid","name":"Hybrid","icon":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/bltd55839866311dd1e/62fc2d8bd62b1d3a8d1e5318/Hybrid.svg","description":"Attackers capture a payload, then escort it to its destination; defenders try to hold them back.","screenshot":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt253b3f05c32db72b/62fc2d9e947fcf14cd224b18/Rio_de_Janeiro_Hybrid.jpg"},{"key":"capture-the-flag","name":"Capture the Flag","icon":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt81ca1f07a1cf5a6e/62fc2d8bc8d34f7f53197dc6/Capture_the_Flag.svg","description":"Teams compete to capture the enemy team’s flag while defending their own.","screenshot":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt42d587e346415db3/62fc2d9e8282cd7f515e855b/Lijang_Tower_Capture_the_Flag.jpg"},{"key":"elimination","name":"Elimination","icon":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt4a8d26b9e2ae0739/62fc2d8b7198180857a9e75d/Elimination.svg","description":"Dispatch all enemies to win the round. Win three rounds to claim victory. Available with teams of one, three, or six.","screenshot":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt4ccc6acd0dbed78a/62fc2d9eb60eb7158600e04c/Black_Forest_Elimination.jpg"},{"key":"deathmatch","name":"Deathmatch","icon":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt9b57125d24705a16/62fc2d8b8972f93d1e32520e/Deathmatch.svg","description":"Race to reach 20 points first by racking up kills in a free-for-all format.","screenshot":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt88ad6ddb9de11b3e/62fc2d9e26d7fa3ee92d03e2/Eichenwalde_Deathmatch.png"},{"key":"team-deathmatch","name":"Team Deathmatch","icon":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blta97d85f157469bd5/62fc2d8b3a2b8b6d5939b85a/Team_Deathmatch.svg","description":"Team up and triumph over your enemies by scoring the most kills.","screenshot":"https://blz-contentstack-images.akamaized.net/v3/assets/blt9c12f249ac15c7ec/blt63df614431a3f554/62fc2d9ead1dcd16f35de8e9/Kanezaka_Team_Deathmatch.jpg"}] \ No newline at end of file +[ + { + "key": "assault", + "name": "Assault", + "icon": "https://overfast-api.tekrop.fr/static/gamemodes/assault-icon.svg", + "description": "Teams fight to capture or defend two successive points against the enemy team. It's an inactive Overwatch 1 gamemode, also called 2CP.", + "screenshot": "https://overfast-api.tekrop.fr/static/gamemodes/assault.avif" + }, + { + "key": "capture-the-flag", + "name": "Capture the Flag", + "icon": "https://overfast-api.tekrop.fr/static/gamemodes/capture-the-flag-icon.svg", + "description": "Teams compete to capture the enemy team’s flag while defending their own.", + "screenshot": "https://overfast-api.tekrop.fr/static/gamemodes/capture-the-flag.avif" + }, + { + "key": "control", + "name": "Control", + "icon": "https://overfast-api.tekrop.fr/static/gamemodes/control-icon.svg", + "description": "Teams fight to hold a single objective. The first team to win two rounds wins the map.", + "screenshot": "https://overfast-api.tekrop.fr/static/gamemodes/control.avif" + }, + { + "key": "deathmatch", + "name": "Deathmatch", + "icon": "https://overfast-api.tekrop.fr/static/gamemodes/deathmatch-icon.svg", + "description": "Race to reach 20 points first by racking up kills in a free-for-all format.", + "screenshot": "https://overfast-api.tekrop.fr/static/gamemodes/deathmatch.avif" + }, + { + "key": "elimination", + "name": "Elimination", + "icon": "https://overfast-api.tekrop.fr/static/gamemodes/elimination-icon.svg", + "description": "Dispatch all enemies to win the round. Win three rounds to claim victory. Available with teams of one, three, or six.", + "screenshot": "https://overfast-api.tekrop.fr/static/gamemodes/elimination.avif" + }, + { + "key": "escort", + "name": "Escort", + "icon": "https://overfast-api.tekrop.fr/static/gamemodes/escort-icon.svg", + "description": "One team escorts a payload to its delivery point, while the other races to stop them.", + "screenshot": "https://overfast-api.tekrop.fr/static/gamemodes/escort.avif" + }, + { + "key": "flashpoint", + "name": "Flashpoint", + "icon": "https://overfast-api.tekrop.fr/static/gamemodes/flashpoint-icon.svg", + "description": "Teams fight for control of key positions across the map, aiming to capture three of them before their opponents do.", + "screenshot": "https://overfast-api.tekrop.fr/static/gamemodes/flashpoint.avif" + }, + { + "key": "hybrid", + "name": "Hybrid", + "icon": "https://overfast-api.tekrop.fr/static/gamemodes/hybrid-icon.svg", + "description": "Attackers capture a payload, then escort it to its destination; defenders try to hold them back.", + "screenshot": "https://overfast-api.tekrop.fr/static/gamemodes/hybrid.avif" + }, + { + "key": "push", + "name": "Push", + "icon": "https://overfast-api.tekrop.fr/static/gamemodes/push-icon.svg", + "description": "Teams battle to take control of a robot and push it toward the enemy base.", + "screenshot": "https://overfast-api.tekrop.fr/static/gamemodes/push.avif" + }, + { + "key": "team-deathmatch", + "name": "Team Deathmatch", + "icon": "https://overfast-api.tekrop.fr/static/gamemodes/team-deathmatch-icon.svg", + "description": "Team up and triumph over your enemies by scoring the most kills.", + "screenshot": "https://overfast-api.tekrop.fr/static/gamemodes/team-deathmatch.avif" + } +] \ No newline at end of file diff --git a/tests/views/test_gamemodes_route.py b/tests/views/test_gamemodes_route.py index fd1623ec..e1daf8f4 100644 --- a/tests/views/test_gamemodes_route.py +++ b/tests/views/test_gamemodes_route.py @@ -26,32 +26,6 @@ def test_get_gamemodes(gamemodes_json_data: list): assert response.json() == gamemodes_json_data -def test_get_gamemodes_after_get_roles(gamemodes_json_data: list): - # Used to check we don't have any conflict between parsers - # using the same Blizzard URL and associated Parser caches - client.get("/heroes/roles") - response = client.get("/gamemodes") - assert response.status_code == status.HTTP_200_OK - assert response.json() == gamemodes_json_data - - -def test_get_gamemodes_blizzard_error(): - with patch.object( - overfast_client, - "get", - return_value=Mock( - status_code=status.HTTP_503_SERVICE_UNAVAILABLE, - text="Service Unavailable", - ), - ): - response = client.get("/gamemodes") - - assert response.status_code == status.HTTP_504_GATEWAY_TIMEOUT - assert response.json() == { - "error": "Couldn't get Blizzard page (HTTP 503 error) : Service Unavailable", - } - - def test_get_gamemodes_internal_error(): with patch( "app.handlers.list_gamemodes_request_handler." diff --git a/tests/views/test_hero_routes.py b/tests/views/test_hero_routes.py index 1d19deb3..f0a7c0ac 100644 --- a/tests/views/test_hero_routes.py +++ b/tests/views/test_hero_routes.py @@ -145,7 +145,7 @@ def test_get_hero_no_hitpoints( Mock(status_code=status.HTTP_200_OK, text=heroes_html_data), ], ), patch( - "app.parsers.heroes_stats_parser.read_csv_data_file", + "app.parsers.generics.csv_parser.read_csv_data_file", return_value=heroes_stats, ): response = client.get(f"/heroes/{hero_name}")