diff --git a/jarvis/__init__.py b/jarvis/__init__.py index acb5cd0..7eba22d 100644 --- a/jarvis/__init__.py +++ b/jarvis/__init__.py @@ -1,5 +1,7 @@ """Main JARVIS package.""" import logging +from functools import partial +from typing import Any import aioredis import jurigged @@ -17,11 +19,47 @@ from jarvis.utils import get_extensions __version__ = const.__version__ +def jlogger(logger: logging.Logger, event: Any) -> None: + """ + Logging for jurigged + + Args: + logger: Logger to use + event: Event to parse + """ + jlog = partial(logger.log, 11) + if isinstance(event, jurigged.live.WatchOperation): + jlog(f"[bold]Watch[/] {event.filename}", extra={"markup": True}) + elif isinstance(event, jurigged.codetools.AddOperation): + event_str = f"{event.defn.parent.dotpath()}:{event.defn.stashed.lineno}" + if isinstance(event.defn, jurigged.codetools.LineDefinition): + event_str += f" | {event.defn.text}" + jlog( + f"[bold green]Run[/] {event_str}", + extra={"markup": True}, + ) + else: + jlog(f"[bold green]Add[/] {event_str}", extra={"markup": True}) + elif isinstance(event, jurigged.codetools.UpdateOperation): + if isinstance(event.defn, jurigged.codetools.FunctionDefinition): + event_str = f"{event.defn.parent.dotpath()}:{event.defn.stashed.lineno}" + jlog(f"[bold yellow]Update[/] {event_str}", extra={"markup": True}) + elif isinstance(event, jurigged.codetools.DeleteOperation): + event_str = f"{event.defn.parent.dotpath()}:{event.defn.stashed.lineno}" + if isinstance(event.defn, jurigged.codetools.LineDefinition): + event_str += f" | {event.defn.text}" + jlog(f"[bold red]Delete[/] {event_str}", extra={"markup": True}) + elif isinstance(event, (Exception, SyntaxError)): + logger.exception("Jurigged encountered error", exc_info=True) + else: + jlog(event) + + async def run() -> None: """Run JARVIS""" # Configure logger jconfig = JarvisConfig.from_yaml() - logger = get_logger("jarvis", show_locals=jconfig.log_level == "DEBUG") + logger = get_logger("jarvis", show_locals=False) # jconfig.log_level == "DEBUG") logger.setLevel(jconfig.log_level) file_handler = logging.FileHandler(filename="jarvis.log", encoding="UTF-8", mode="w") file_handler.setFormatter( @@ -49,7 +87,8 @@ async def run() -> None: # External modules if jconfig.log_level == "DEBUG": - jurigged.watch(pattern="jarvis/*.py") + logging.addLevelName(11, "\033[35mJURIG\033[0m ") + jurigged.watch(pattern="jarvis/*.py", logger=partial(jlogger, logger)) if jconfig.rook_token: rook.start(token=jconfig.rook_token, labels={"env": "dev"}) diff --git a/jarvis/branding.py b/jarvis/branding.py index 33b1352..e7d2a02 100644 --- a/jarvis/branding.py +++ b/jarvis/branding.py @@ -11,8 +11,13 @@ COMMAND_TYPES = { "SOFT": ["warning"], "GOOD": ["unban", "unmute"], } - CUSTOM_COMMANDS = {} +CUSTOM_EMOJIS = { + "ico_clock_green": "<:ico_clock_green:1019710693206933605>", + "ico_clock_yellow": "<:ico_clock_yellow:1019710734340472834>", + "ico_clock_red": "<:ico_clock_red:1019710735896551534>", + "ico_check_green": "<:ico_check_green:1019725504120639549>", +} def get_command_color(command: str) -> str: diff --git a/jarvis/cogs/dbrand.py b/jarvis/cogs/dbrand.py index 4900e0f..859220f 100644 --- a/jarvis/cogs/dbrand.py +++ b/jarvis/cogs/dbrand.py @@ -1,11 +1,12 @@ """JARVIS dbrand cog.""" import logging import re -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone import aiohttp from bs4 import BeautifulSoup from naff import Client, Extension, InteractionContext +from naff.client.utils import find from naff.models.discord.embed import EmbedField from naff.models.naff.application_commands import ( OptionTypes, @@ -16,6 +17,7 @@ from naff.models.naff.command import cooldown from naff.models.naff.cooldowns import Buckets from thefuzz import process +from jarvis.branding import CUSTOM_EMOJIS from jarvis.config import JarvisConfig from jarvis.data.dbrand import shipping_lookup from jarvis.utils import build_embed @@ -51,11 +53,11 @@ async def parse_db_status() -> dict: elif "column--status" in cell["class"]: info = cell.find("span")["class"] if any("green" in x for x in info): - cell = "🟢" + cell = CUSTOM_EMOJIS.get("ico_clock_green", "🟢") elif any("yellow" in x for x in info): - cell = "🟡" + cell = CUSTOM_EMOJIS.get("ico_clock_yellow", "🟡") elif any("red" in x for x in info): - cell = "🔴" + cell = CUSTOM_EMOJIS.get("ico_clock_red", "🔴") elif any("black" in x for x in info): cell = "⚫" else: @@ -91,20 +93,26 @@ class DbrandCog(Extension): @db.subcommand(sub_cmd_name="status", sub_cmd_description="Get dbrand operational status") async def _status(self, ctx: InteractionContext) -> None: status = self.cache.get("status") - if not status or status["cache_expiry"] <= datetime.utcnow(): + if not status or status["cache_expiry"] <= datetime.now(tz=timezone.utc): status = await parse_db_status() - status["cache_expiry"] = datetime.utcnow() + timedelta(hours=2) + status["cache_expiry"] = datetime.now(tz=timezone.utc) + timedelta(hours=2) self.cache["status"] = status status = status.get("operations") + emojies = [x["Status"] for x in status] fields = [ - EmbedField(name=f'{x["Status"]} {x["Service"]}', value=x["Detail"]) for x in status + EmbedField(name=f'{x["Status"]} {x["Service"]}', value=x["Detail"]) for x in status ] + color = "#FBBD1E" + if all("green" in x for x in emojies): + color = "#38F657" + elif all("red" in x for x in emojies): + color = "#F12D20" embed = build_embed( title="Operational Status", description="Current dbrand operational status.\n[View online](https://dbrand.com/status)", fields=fields, url="https://dbrand.com/status", - color="#FFBB00", + color=color, ) embed.set_thumbnail(url="https://dev.zevaryx.com/db_logo.png") @@ -112,6 +120,7 @@ class DbrandCog(Extension): text="dbrand.com", icon_url="https://dev.zevaryx.com/db_logo.png", ) + await ctx.send(embeds=embed) @db.subcommand(sub_cmd_name="gripcheck", sub_cmd_description="Watch a dbrand grip get thrown") @@ -206,12 +215,12 @@ class DbrandCog(Extension): await ctx.defer() dest = search.lower() data = self.cache.get(dest, None) - if not data or data["cache_expiry"] < datetime.utcnow(): + if not data or data["cache_expiry"] < datetime.now(tz=timezone.utc): api_link = self.api_url + dest data = await self._session.get(api_link) if 200 <= data.status < 400: data = await data.json() - data["cache_expiry"] = datetime.utcnow() + timedelta(hours=24) + data["cache_expiry"] = datetime.now(tz=timezone.utc) + timedelta(hours=24) self.cache[dest] = data else: data = None @@ -220,32 +229,56 @@ class DbrandCog(Extension): fields = [] for service in data["shipping_services_available"]: service_data = self.cache.get(f"{dest}-{service}") - if not service_data or service_data["cache_expiry"] < datetime.utcnow(): + if not service_data or service_data["cache_expiry"] < datetime.now(tz=timezone.utc): service_data = await self._session.get( self.api_url + dest + "/" + service["url"] ) if service_data.status > 400: continue service_data = await service_data.json() - service_data["cache_expiry"] = datetime.utcnow() + timedelta(hours=24) + service_data["cache_expiry"] = datetime.now(tz=timezone.utc) + timedelta( + hours=24 + ) self.cache[f"{dest}-{service}"] = service_data title = f'{service_data["carrier"]} {service_data["tier-title"]} | {service_data["costs-min"]}' message = service_data["time-title"] if service_data["free_threshold_available"]: title += " | Free over " + service_data["free-threshold"] fields.append(EmbedField(title, message)) + + status = self.cache.get("status") + if not status or status["cache_expiry"] <= datetime.now(tz=timezone.utc): + status = await parse_db_status() + status["cache_expiry"] = datetime.now(tz=timezone.utc) + timedelta(hours=2) + self.cache["status"] = status + status = status["countries"] + + country = data["country"] + if country.startswith("the"): + country = country.replace("the", "").strip() + shipping_info = find(lambda x: x["Country"] == country, status) + country = "-".join(x for x in data["country"].split(" ") if x != "the") - country_urlsafe = country.replace("-", "%20") - description = ( - f"Click the link above to see shipping time to {data['country']}." - "\n[View all shipping destinations](https://dbrand.com/shipping)" - " | [Check shipping status]" - f"(https://dbrand.com/status#main-content:~:text={country_urlsafe})" - ) + description = "" + color = "#FFBB00" + if shipping_info: + description = f'{shipping_info["Status"]}\u200b \u200b {shipping_info["Est. Delivery Time"].split(":")[0]}' + created = self.cache.get("status").get("cache_expiry") - timedelta(hours=2) + ts = int(created.timestamp()) + description += f" \u200b | \u200b Last updated: \n\u200b" + if "green" in shipping_info["Status"]: + color = "#38F657" + elif "yellow" in shipping_info["Status"]: + color = "#FBBD1E" + elif "red" in shipping_info["Status"]: + color = "#F12D20" + else: + color = "#FFFFFF" + embed = build_embed( title="Shipping to {}".format(data["country"]), description=description, - color="#FFBB00", + color=color, fields=fields, url=self.base_url + "shipping/" + country, ) diff --git a/poetry.lock b/poetry.lock index c1ce6e3..df79f1b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -172,10 +172,10 @@ aiohttp = "*" yarl = "*" [package.extras] +test = ["vcrpy (==4.0.2)", "testfixtures (>4.13.2,<7)", "pytest-vcr", "pytest", "mock (>=0.8)", "asynctest (>=0.13.0)"] +lint = ["pydocstyle", "pre-commit", "flynt", "flake8", "black"] +dev = ["vcrpy (==4.0.2)", "testfixtures (>4.13.2,<7)", "pytest-vcr", "pytest", "mock (>=0.8)", "asynctest (>=0.13.0)", "pydocstyle", "pre-commit", "flynt", "flake8", "black"] ci = ["coveralls"] -dev = ["black", "flake8", "flynt", "pre-commit", "pydocstyle", "asynctest (>=0.13.0)", "mock (>=0.8)", "pytest", "pytest-vcr", "testfixtures (>4.13.2,<7)", "vcrpy (==4.0.2)"] -lint = ["black", "flake8", "flynt", "pre-commit", "pydocstyle"] -test = ["asynctest (>=0.13.0)", "mock (>=0.8)", "pytest", "pytest-vcr", "testfixtures (>4.13.2,<7)", "vcrpy (==4.0.2)"] [[package]] name = "attrs" @@ -306,7 +306,7 @@ optional = false python-versions = "*" [package.extras] -test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] +test = ["hypothesis (==3.55.3)", "flake8 (==3.7.8)"] [[package]] name = "dateparser" @@ -323,9 +323,9 @@ regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27,<2022.3.15" tzlocal = "*" [package.extras] -calendars = ["convertdate", "hijri-converter", "convertdate"] -fasttext = ["fasttext"] langdetect = ["langdetect"] +fasttext = ["fasttext"] +calendars = ["convertdate", "hijri-converter", "convertdate"] [[package]] name = "discord-typings" @@ -454,7 +454,7 @@ ansicon = {version = "*", markers = "platform_system == \"Windows\""} [[package]] name = "jurigged" -version = "0.5.2" +version = "0.5.3" description = "Live update of Python functions" category = "main" optional = false @@ -467,7 +467,7 @@ ovld = ">=0.3.1,<0.4.0" watchdog = ">=1.0.2" [package.extras] -develoop = ["giving (>=0.3.6,<0.4.0)", "rich (>=10.13.0,<11.0.0)", "hrepr (>=0.4.0,<0.5.0)"] +develoop = ["hrepr (>=0.4.0,<0.5.0)", "rich (>=10.13.0,<11.0.0)", "giving (>=0.3.6,<0.4.0)"] [[package]] name = "marshmallow" @@ -529,7 +529,7 @@ python-versions = "*" [[package]] name = "naff" -version = "1.9.0" +version = "1.10.0" description = "Not another freaking fork" category = "main" optional = false @@ -784,7 +784,7 @@ optional = false python-versions = ">=3.6.8" [package.extras] -diagrams = ["railroad-diagrams", "jinja2"] +diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyppeteer" @@ -1163,9 +1163,9 @@ optional = false python-versions = ">=3.7" [package.extras] -docs = ["Sphinx (>=3.4)", "sphinx-rtd-theme (>=0.5)"] -optional = ["python-socks", "wsaccel"] test = ["websockets"] +optional = ["wsaccel", "python-socks"] +docs = ["sphinx-rtd-theme (>=0.5)", "Sphinx (>=3.4)"] [[package]] name = "websockets" @@ -1413,7 +1413,10 @@ dateparser = [ {file = "dateparser-1.1.1-py2.py3-none-any.whl", hash = "sha256:9600874312ff28a41f96ec7ccdc73be1d1c44435719da47fea3339d55ff5a628"}, {file = "dateparser-1.1.1.tar.gz", hash = "sha256:038196b1f12c7397e38aad3d61588833257f6f552baa63a1499e6987fa8d42d9"}, ] -discord-typings = [] +discord-typings = [ + {file = "discord-typings-0.5.1.tar.gz", hash = "sha256:1a4fb1e00201416ae94ca64ca5935d447c005e0475b1ec274c1a6e09072db70e"}, + {file = "discord_typings-0.5.1-py3-none-any.whl", hash = "sha256:55ebdb6d6f0f47df774a0c31193ba6a45de14625fab9c6fbd43bfe87bb8c0128"}, +] distro = [ {file = "distro-1.7.0-py3-none-any.whl", hash = "sha256:d596311d707e692c2160c37807f83e3820c5d539d5a83e87cfb6babd8ba3a06b"}, {file = "distro-1.7.0.tar.gz", hash = "sha256:151aeccf60c216402932b52e40ee477a939f8d58898927378a02abbe852c1c39"}, @@ -1509,8 +1512,8 @@ jinxed = [ {file = "jinxed-1.2.0.tar.gz", hash = "sha256:032acda92d5c57cd216033cbbd53de731e6ed50deb63eb4781336ca55f72cda5"}, ] jurigged = [ - {file = "jurigged-0.5.2-py3-none-any.whl", hash = "sha256:410ff6199c659108dace9179507342883fe2fffec1966fd19709f9d59fd69e24"}, - {file = "jurigged-0.5.2.tar.gz", hash = "sha256:de1d4daeb99c0299eaa86f691d35cb1eab3bfa836cfe9a3551a56f3829479e3b"}, + {file = "jurigged-0.5.3-py3-none-any.whl", hash = "sha256:355a9bddf42cae541e862796fb125827fc35573a982c6f35d3dc5621e59c91e3"}, + {file = "jurigged-0.5.3.tar.gz", hash = "sha256:47cf4e9f10455a39602caa447888c06adda962699c65f19d8c37509817341b5e"}, ] marshmallow = [ {file = "marshmallow-3.16.0-py3-none-any.whl", hash = "sha256:53a1e0ee69f79e1f3e80d17393b25cfc917eda52f859e8183b4af72c3390c1f1"}, @@ -1590,8 +1593,8 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] naff = [ - {file = "naff-1.9.0-py3-none-any.whl", hash = "sha256:20144495aed9452d9d2e713eb6ade9636601457ca3de255684b2186068505bcd"}, - {file = "naff-1.9.0.tar.gz", hash = "sha256:f4870ea304747368d6d750f3d52fcbc96017bd7afaa7ec06a3e9a68ff301997d"}, + {file = "naff-1.10.0-py3-none-any.whl", hash = "sha256:bb28ef19efb3f8e04f3569a3aac6b3e2738cf5747dea0bed483c458588933682"}, + {file = "naff-1.10.0.tar.gz", hash = "sha256:d0ab71c39ea5bf352228f0bc3d3dfe3610122cb01733bca4565497078de95650"}, ] nafftrack = [] nanoid = [ @@ -1850,6 +1853,7 @@ pymongo = [ {file = "pymongo-3.12.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:71c0db2c313ea8a80825fb61b7826b8015874aec29ee6364ade5cb774fe4511b"}, {file = "pymongo-3.12.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5b779e87300635b8075e8d5cfd4fdf7f46078cd7610c381d956bca5556bb8f97"}, {file = "pymongo-3.12.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:351a2efe1c9566c348ad0076f4bf541f4905a0ebe2d271f112f60852575f3c16"}, + {file = "pymongo-3.12.3-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:858af7c2ab98f21ed06b642578b769ecfcabe4754648b033168a91536f7beef9"}, {file = "pymongo-3.12.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0a02313e71b7c370c43056f6b16c45effbb2d29a44d24403a3d5ba6ed322fa3f"}, {file = "pymongo-3.12.3-cp310-cp310-manylinux1_i686.whl", hash = "sha256:d3082e5c4d7b388792124f5e805b469109e58f1ab1eb1fbd8b998e8ab766ffb7"}, {file = "pymongo-3.12.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:514e78d20d8382d5b97f32b20c83d1d0452c302c9a135f0a9022236eb9940fda"}, @@ -1963,7 +1967,9 @@ python-gitlab = [ {file = "python-gitlab-3.5.0.tar.gz", hash = "sha256:29ae7fb9b8c9aeb2e6e19bd2fd04867e93ecd7af719978ce68fac0cf116ab30d"}, {file = "python_gitlab-3.5.0-py3-none-any.whl", hash = "sha256:73b5aa6502efa557ee1a51227cceb0243fac5529627da34f08c5f265bf50417c"}, ] -python-levenshtein = [] +python-levenshtein = [ + {file = "python-Levenshtein-0.12.2.tar.gz", hash = "sha256:dc2395fbd148a1ab31090dd113c366695934b9e85fe5a4b2a032745efd0346f6"}, +] pytz = [ {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, @@ -1980,6 +1986,13 @@ pyyaml = [ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, + {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, + {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, + {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, + {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, @@ -2133,7 +2146,10 @@ soupsieve = [ {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, ] -thefuzz = [] +thefuzz = [ + {file = "thefuzz-0.19.0-py2.py3-none-any.whl", hash = "sha256:4fcdde8e40f5ca5e8106bc7665181f9598a9c8b18b0a4d38c41a095ba6788972"}, + {file = "thefuzz-0.19.0.tar.gz", hash = "sha256:6f7126db2f2c8a54212b05e3a740e45f4291c497d75d20751728f635bb74aa3d"}, +] tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -2146,7 +2162,10 @@ tweepy = [ {file = "tweepy-4.10.0-py3-none-any.whl", hash = "sha256:f0abbd234a588e572f880f99a094ac321217ff3eade6c0eca118ed6db8e2cf0a"}, {file = "tweepy-4.10.0.tar.gz", hash = "sha256:7f92574920c2f233663fff154745fc2bb0d10aedc23617379a912d8e4fefa399"}, ] -typing-extensions = [] +typing-extensions = [ + {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, + {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, +] tzdata = [ {file = "tzdata-2022.1-py2.py3-none-any.whl", hash = "sha256:238e70234214138ed7b4e8a0fab0e5e13872edab3be586ab8198c407620e2ab9"}, {file = "tzdata-2022.1.tar.gz", hash = "sha256:8b536a8ec63dc0751342b3984193a3118f8fca2afe25752bb9b7fffd398552d3"},