From 9e379403340ace95995ecf09906af71fa409fefe427825574e546b6b5544e24d Mon Sep 17 00:00:00 2001 From: Hadrian Burkhardt Date: Thu, 30 Nov 2023 04:26:43 +0100 Subject: [PATCH] linting --- stw_potsdam/builder.py | 108 ++++++++++++++++++++++-- stw_potsdam/swp_webspeiseplan_api.py | 38 +++++++-- stw_potsdam/swp_webspeiseplan_parser.py | 30 ++++++- stw_potsdam/views.py | 4 +- tests/test_consistency.py | 5 ++ tests/test_views.py | 4 +- 6 files changed, 174 insertions(+), 15 deletions(-) diff --git a/stw_potsdam/builder.py b/stw_potsdam/builder.py index a731a41..742d11f 100644 --- a/stw_potsdam/builder.py +++ b/stw_potsdam/builder.py @@ -3,15 +3,21 @@ from datetime import date class Builder: + """A class method for creating a new class.""" + def __init__(self): + """Initialize the object for the OpenMensa Feed Doc XML.""" self._doc = minidom.Document() self._om = self._doc.createElement("openmensa") self._om.setAttribute("version", "2.1") self._om.setAttribute("xmlns", "http://openmensa.org/open-mensa-v2") - self._om.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance") + self._om.setAttribute( + "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" + ) self._om.setAttribute( "xsi:schemaLocation", - "http://openmensa.org/open-mensa-v2 http://openmensa.org/open-mensa-v2.xsd", + "http://openmensa.org/open-mensa-v2 " + + "http://openmensa.org/open-mensa-v2.xsd", ) self._version = None self._name = None @@ -28,6 +34,11 @@ class Builder: @property def version(self): + """The version of the device . + + Returns: + [type]: [description] + """ return self._version @version.setter @@ -40,6 +51,11 @@ class Builder: @property def name(self): + """Name of the canteen . + + Returns: + [type]: [description] + """ return self._name @name.setter @@ -52,12 +68,17 @@ class Builder: @property def address(self): + """The address of the canteen . + + Returns: + [type]: [description] + """ return self._address @address.setter def address(self, value: tuple[str, str, str]): - street_nr, zip, city = value - self._address = f"{street_nr}, {zip} {city}" + street_nr, zip_code, city = value + self._address = f"{street_nr}, {zip_code} {city}" @address.deleter def address(self): @@ -65,6 +86,11 @@ class Builder: @property def city(self): + """Get the city of the canteen . + + Returns: + [type]: [description] + """ return self._city @city.setter @@ -77,6 +103,11 @@ class Builder: @property def phone(self): + """The phone number . + + Returns: + [type]: [description] + """ return self._phone @phone.setter @@ -89,6 +120,11 @@ class Builder: @property def email(self): + """The email address of the canteen . + + Returns: + [type]: [description] + """ return self._email @email.setter @@ -101,6 +137,11 @@ class Builder: @property def location(self): + """Get a tuple containing the location as latitude and longitude . + + Returns: + [type]: [description] + """ return (self._longitude, self._latitude) @location.setter @@ -115,6 +156,11 @@ class Builder: @property def availability(self): + """Whether the canteen is public or restriced. + + Returns: + [type]: [description] + """ return self._availability @availability.setter @@ -130,11 +176,22 @@ class Builder: @property def times(self): + """Get the opening times the canteen. + + Returns: + [type]: [description] + """ return self._times @times.setter def times(self, value: dict[str, str]): def attach_weekday(tag: str, value: str): + """Attach a week tag to the week . + + Args: + tag (str): [description] + value (str): [description] + """ if value == "": return d = self._doc.createElement(tag) @@ -167,17 +224,31 @@ class Builder: @property def feed(self): + """Get a feed object . + + Returns: + [type]: [description] + """ return self._feed @feed.setter def feed(self, value: dict): + """Set the feed element . + + Args: + value (dict): [description] + """ name: str = value.get("name") priority: int = value.get("priority") url: str = value.get("url") source: str = value.get("source") hour: int = value.get("hour") - dayOfMonth: int | str = value.get("dayOfMonth") if value.get("dayOfMonth") else "*" - dayOfWeek: int | str = value.get("dayOfWeek") if value.get("dayOfWeek") else "*" + dayOfMonth: int | str = ( + value.get("dayOfMonth") if value.get("dayOfMonth") else "*" + ) + dayOfWeek: int | str = ( + value.get("dayOfWeek") if value.get("dayOfWeek") else "*" + ) month: int | str = value.get("month") if value.get("month") else "*" minute: int = value.get("minute") if value.get("minute") else 0 retry: str = value.get("retry") @@ -211,6 +282,11 @@ class Builder: @property def day(self): + """Returns the number of day of the week . + + Returns: + [type]: [description] + """ return self._day @day.setter @@ -229,6 +305,15 @@ class Builder: prices: dict[str, float], note: str = None, ): + """Add a meal element to the document . + + Args: + date (date): [description] + category (str): [description] + name (str): [description] + prices (dict[str, float]): [description] + note (str, optional): [description]. Defaults to None. + """ meal = self._doc.createElement("meal") node = self._doc.createElement("name") meal.appendChild(node) @@ -270,12 +355,23 @@ class Builder: meal_list.append(meal) def __append_node(self, tag: str, value: str): + """Create a node with a tag and text . + + Args: + tag (str): [description] + value (str): [description] + """ elem = self._doc.createElement(tag) self._canteen.appendChild(elem) node = self._doc.createTextNode(value) elem.appendChild(node) def toXML(self): + """Return a XML string representing the canteen. + + Returns: + [type]: [description] + """ self._doc.appendChild(self._om) if self.version: self.__append_node("version", self.version) diff --git a/stw_potsdam/swp_webspeiseplan_api.py b/stw_potsdam/swp_webspeiseplan_api.py index be1ba05..d2bbae5 100644 --- a/stw_potsdam/swp_webspeiseplan_api.py +++ b/stw_potsdam/swp_webspeiseplan_api.py @@ -6,9 +6,16 @@ import json class SWP_Webspeiseplan_API: + """This class is used download content from SWP_Webspeiseplan. + + Returns: + [type]: [description] + """ + URL_BASE = "https://swp.webspeiseplan.de" def __init__(self): + """Initialize the configuration for the web service .""" logging.basicConfig() self.logger = logging.getLogger(__name__) self.__parse_token() @@ -20,7 +27,9 @@ class SWP_Webspeiseplan_API: "_": int(time.time() * 1000), } - self.outlets = {outlet["name"]: outlet for outlet in self.__parse_model(params)} + self.outlets = { + outlet["name"]: outlet for outlet in self.__parse_model(params) + } self.menus = {} self.meal_categories = {} for outlet in self.outlets.values(): @@ -38,6 +47,7 @@ class SWP_Webspeiseplan_API: self.meal_categories[outlet["name"]] = id2cat def __parse_token(self): + """Get the token from the proxy server.""" req = urllib.request.Request(self.URL_BASE) with urllib.request.urlopen(req) as resp: txt = resp.read().decode("utf-8") @@ -50,14 +60,23 @@ class SWP_Webspeiseplan_API: self.logger.debug(f"__parse_token: PROXY_TOKEN {self.proxy_token}") def __spoof_req_headers(req: urllib.request.Request): - req.add_header("Accept", "application/json, text/javascript, */*; q=0.01") + """Add headers to a request . + + Args: + req (urllib.request.Request): [description] + """ + req.add_header( + "Accept", "application/json, text/javascript, */*; q=0.01" + ) req.add_header("Accept-Language", "en-US,en;q=0.9") req.add_header("Connection", "keep-alive") req.add_header("Host", "swp.webspeiseplan.de") req.add_header("Referer", "https://swp.webspeiseplan.de/InitialConfig") req.add_header( "Sec-Ch-Ua", - '"Not/A)Brand";v="99", "Google Chrome";v="115", "Chromium";v="115"', + '"Not/A)Brand";v="99", ' + + '"Google Chrome";v="115", ' + + '"Chromium";v="115"', ) req.add_header("Sec-Ch-Ua-Mobile", "?0") req.add_header("Sec-Ch-Ua-Platform", "Linux") @@ -66,11 +85,21 @@ class SWP_Webspeiseplan_API: req.add_header("Sec-Fetch-Site", "same-origin") req.add_header( "User-Agent", - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "Mozilla/5.0 (X11; Linux x86_64) " + + "AppleWebKit/537.36 (KHTML, like Gecko) " + + "Chrome/115.0.0.0 Safari/537.36", ) req.add_header("X-Requested-With", "XMLHttpRequest") def __parse_model(self, params: dict): + """Retrieve data from host. + + Args: + params (dict): [description] + + Returns: + [type]: [description] + """ url = f"{self.URL_BASE}/index.php?" + "&".join( [f"{k}={v}" for k, v in params.items()] ) @@ -80,4 +109,3 @@ class SWP_Webspeiseplan_API: with urllib.request.urlopen(req) as resp: data = resp.read() return json.loads(data)["content"] - diff --git a/stw_potsdam/swp_webspeiseplan_parser.py b/stw_potsdam/swp_webspeiseplan_parser.py index 0017ebd..5b876c9 100644 --- a/stw_potsdam/swp_webspeiseplan_parser.py +++ b/stw_potsdam/swp_webspeiseplan_parser.py @@ -5,6 +5,8 @@ from stw_potsdam.swp_webspeiseplan_api import SWP_Webspeiseplan_API class SWP_Webspeiseplan_Parser: + """Class method to parse SWP_Webspeiseplan.""" + def __init__( self, menu_data: list[dict], @@ -12,6 +14,14 @@ class SWP_Webspeiseplan_Parser: outlet_data: dict, url: str, ): + """Initialize the parser . + + Args: + menu_data (list[dict]): [description] + meal_categories (list[dict]): [description] + outlet_data (dict): [description] + url (str): [description] + """ logging.basicConfig() self.logger = logging.getLogger(__name__) self.menu_data = menu_data @@ -24,6 +34,11 @@ class SWP_Webspeiseplan_Parser: self.__parse_meals() def __parse_canteen(self, outlet: dict): + """Parse the outlet data from outlet. + + Args: + outlet (dict): [description] + """ canteen = self._builder canteen.name = outlet["name"] canteen.address = ( @@ -53,13 +68,16 @@ class SWP_Webspeiseplan_Parser: } times = { - k: v.replace("None, None", "").replace("None,", "").replace(", None", "") + k: v.replace("None, None", "") + .replace("None,", "") + .replace(", None", "") for k, v in times.items() } canteen.times = times def __parse_feed(self): + """Parse feed and set feed.""" feed = { "name": "full", "priority": 0, @@ -71,6 +89,7 @@ class SWP_Webspeiseplan_Parser: self._builder.feed = feed def __parse_meals(self): + """Parse the menu and adds it to the builder.""" for menu in self.menu_data: for meal in menu["speiseplanGerichtData"]: info = meal["speiseplanAdvancedGericht"] @@ -78,7 +97,9 @@ class SWP_Webspeiseplan_Parser: additional_info = meal["zusatzinformationen"] self._builder.add_meal( date=date, - category=self.meal_categories[info["gerichtkategorieID"]]["name"], + category=self.meal_categories[info["gerichtkategorieID"]][ + "name" + ], name=info["gerichtname"], prices={ "student": additional_info["mitarbeiterpreisDecimal2"], @@ -89,4 +110,9 @@ class SWP_Webspeiseplan_Parser: @property def xml_feed(self): + """Return the XML string of the builder. + + Returns: + [type]: [description] + """ return self._builder.toXML() diff --git a/stw_potsdam/views.py b/stw_potsdam/views.py index ca64cb6..d04cb14 100644 --- a/stw_potsdam/views.py +++ b/stw_potsdam/views.py @@ -45,6 +45,7 @@ def get_menu(): log.debug("Downloading menu for SWP") return SWP_Webspeiseplan_API() + @app.route("/canteens/") @app.route("/canteens//xml") def canteen_xml_feed(canteen_name): @@ -63,9 +64,10 @@ def canteen_xml_feed(canteen_name): ) xml = swp_parser.xml_feed.decode() response = make_response(xml) - response.mimetype = 'text/xml' + response.mimetype = "text/xml" return response + @app.route("/") @app.route("/canteens") def canteen_index(): diff --git a/tests/test_consistency.py b/tests/test_consistency.py index 95003fd..1de97a5 100644 --- a/tests/test_consistency.py +++ b/tests/test_consistency.py @@ -25,6 +25,7 @@ def _read_feed(resource_name): with io.open(_resource_path(resource_name), encoding='utf-8') as xml: return xml.read() + @pytest.mark.xfail(strict=True) def test_meta_consistency(): raise NotImplementedError() @@ -34,6 +35,7 @@ def test_meta_consistency(): expected = _read_feed('meta_output.xml') assert expected == actual + @pytest.mark.xfail(strict=True) def test_menu_consistency(): raise NotImplementedError() @@ -42,6 +44,7 @@ def test_menu_consistency(): expected = _read_feed('menu_output.xml') assert expected == actual + @pytest.mark.xfail(strict=True) def test_empty_menu(): raise NotImplementedError() @@ -50,6 +53,7 @@ def test_empty_menu(): expected = _read_feed('empty_menu_output.xml') assert expected == actual + @pytest.mark.xfail(strict=True) def test_offers_dictionary(): raise NotImplementedError() @@ -58,6 +62,7 @@ def test_offers_dictionary(): expected = _read_feed('offers-dict-output.xml') assert expected == actual + @pytest.mark.xfail(strict=True) def test_missing_category(): raise NotImplementedError() diff --git a/tests/test_views.py b/tests/test_views.py index f171af5..3a3f258 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -48,11 +48,13 @@ def test_canteen_not_found(client, url): def test_canteen_menu_api_unavailable(client, api_offline): _request_check_meals(client) + @pytest.mark.xfail(strict=True) def test_canteen_menu_request(client, api_online_one_shot): raise NotImplementedError() _request_check_meals(client) + @pytest.mark.xfail(strict=True) def test_canteen_menu_cached(client, api_online_one_shot): raise NotImplementedError() @@ -66,8 +68,8 @@ def test_canteen_menu_second_request_indeed_fails(client, api_online_one_shot): views.cache.clear() _request_check_meals(client) -@pytest.mark.xfail(strict=True) +@pytest.mark.xfail(strict=True) def _request_check_meals(client): raise NotImplementedError() response = client.get("/canteens/griebnitzsee/xml")