diff --git a/CHANGELOG.md b/CHANGELOG.md index 18f8fff4..d0e46b95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## v2.4.0 - 2024-02-20 + +### Added + +- [#260](https://github.com/networktocode/circuit-maintenance-parser/pull/260) - Add Google parser +- [#259](https://github.com/networktocode/circuit-maintenance-parser/pull/259) - Add Crown Castle fiber parser +- [#258](https://github.com/networktocode/circuit-maintenance-parser/pull/258) - Add Netflix parser + +### Changed + +- [#264](https://github.com/networktocode/circuit-maintenance-parser/pull/264) - Adopt Pydantic 2.0 +- [#256](https://github.com/networktocode/circuit-maintenance-parser/pull/256) - Improved Equinix parser + +### Fixed + +- [#257](https://github.com/networktocode/circuit-maintenance-parser/pull/257) - Update incorrect file comment +- [#255](https://github.com/networktocode/circuit-maintenance-parser/pull/255) - + Properly process Amazon emergency maintenance notifications + ## v2.3.0 - 2023-12-15 ### Added diff --git a/README.md b/README.md index e11debf1..e9cb7249 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ You can leverage this library in your automation framework to process circuit ma - **provider**: identifies the provider of the service that is the subject of the maintenance notification. - **account**: identifies an account associated with the service that is the subject of the maintenance notification. - **maintenance_id**: contains text that uniquely identifies (at least within the context of a specific provider) the maintenance that is the subject of the notification. -- **circuits**: list of circuits affected by the maintenance notification and their specific impact. Note that in a maintenance canceled notification, some providers omit the circuit list, so this may be blank for maintenance notifications with a status of CANCELLED. +- **circuits**: list of circuits affected by the maintenance notification and their specific impact. Note that in a maintenance canceled or completed notification, some providers omit the circuit list, so this may be blank for maintenance notifications with a status of CANCELLED or COMPLETED. - **start**: timestamp that defines the starting date/time of the maintenance in GMT. - **end**: timestamp that defines the ending date/time of the maintenance in GMT. - **stamp**: timestamp that defines the update date/time of the maintenance in GMT. @@ -71,12 +71,15 @@ By default, there is a `GenericProvider` that supports a `SimpleProcessor` using - BSO - Cogent - Colt +- Crown Castle Fiber - Equinix - EXA (formerly GTT) - HGC +- Google - Lumen - Megaport - Momentum +- Netflix (AS2906 only) - Seaborn - Sparkle - Telstra diff --git a/circuit_maintenance_parser/__init__.py b/circuit_maintenance_parser/__init__.py index 40c8eefd..f0a73af7 100644 --- a/circuit_maintenance_parser/__init__.py +++ b/circuit_maintenance_parser/__init__.py @@ -12,13 +12,16 @@ BSO, Cogent, Colt, + CrownCastle, Equinix, EUNetworks, GTT, + Google, HGC, Lumen, Megaport, Momentum, + Netflix, NTT, PacketFabric, Seaborn, @@ -38,13 +41,16 @@ BSO, Cogent, Colt, + CrownCastle, Equinix, EUNetworks, + Google, GTT, HGC, Lumen, Megaport, Momentum, + Netflix, NTT, PacketFabric, Seaborn, @@ -80,7 +86,6 @@ def get_provider_class(provider_name: str) -> Type[GenericProvider]: if provider_parser.get_provider_type() == provider_name: break else: - raise NonexistentProviderError( f"{provider_name} is not a currently supported provider. Only {', '.join(SUPPORTED_PROVIDER_NAMES)}" ) @@ -90,7 +95,6 @@ def get_provider_class(provider_name: str) -> Type[GenericProvider]: def get_provider_class_from_sender(email_sender: str) -> Type[GenericProvider]: """Returns the notification parser class for an email sender address.""" - for provider_parser in SUPPORTED_PROVIDERS: if provider_parser.get_default_organizer() == email_sender: break diff --git a/circuit_maintenance_parser/data.py b/circuit_maintenance_parser/data.py index 485248f1..250b9d0b 100644 --- a/circuit_maintenance_parser/data.py +++ b/circuit_maintenance_parser/data.py @@ -3,7 +3,7 @@ from typing import List, NamedTuple, Optional, Type, Set import email -from pydantic import BaseModel, Extra +from pydantic import BaseModel from circuit_maintenance_parser.constants import EMAIL_HEADER_SUBJECT, EMAIL_HEADER_DATE @@ -18,7 +18,7 @@ class DataPart(NamedTuple): content: bytes -class NotificationData(BaseModel, extra=Extra.forbid): +class NotificationData(BaseModel, extra="forbid"): """Base class for Notification Data types.""" data_parts: List[DataPart] = [] diff --git a/circuit_maintenance_parser/output.py b/circuit_maintenance_parser/output.py index 98892f2a..0be025ac 100644 --- a/circuit_maintenance_parser/output.py +++ b/circuit_maintenance_parser/output.py @@ -8,7 +8,7 @@ from typing import List -from pydantic import BaseModel, validator, StrictStr, StrictInt, Extra, PrivateAttr +from pydantic import field_validator, BaseModel, StrictStr, StrictInt, PrivateAttr class Impact(str, Enum): @@ -52,7 +52,7 @@ class Status(str, Enum): NO_CHANGE = "NO-CHANGE" -class CircuitImpact(BaseModel, extra=Extra.forbid): +class CircuitImpact(BaseModel, extra="forbid"): """CircuitImpact class. Each Circuit Maintenance can contain multiple affected circuits, and each one can have a different level of impact. @@ -73,9 +73,9 @@ class CircuitImpact(BaseModel, extra=Extra.forbid): ... ) Traceback (most recent call last): ... - pydantic.error_wrappers.ValidationError: 1 validation error for CircuitImpact + pydantic_core._pydantic_core.ValidationError: 1 validation error for CircuitImpact impact - value is not a valid enumeration member; permitted: 'NO-IMPACT', 'REDUCED-REDUNDANCY', 'DEGRADED', 'OUTAGE' (type=type_error.enum; enum_values=[, , , ]) + Input should be 'NO-IMPACT', 'REDUCED-REDUNDANCY', 'DEGRADED' or 'OUTAGE' [type=enum, input_value='wrong impact', input_type=str] """ circuit_id: StrictStr @@ -83,13 +83,21 @@ class CircuitImpact(BaseModel, extra=Extra.forbid): impact: Impact = Impact.OUTAGE # pylint: disable=no-self-argument - @validator("impact") + @field_validator("impact") + @classmethod def validate_impact_type(cls, value): """Validate Impact type.""" if value not in Impact: raise ValueError("Not a valid impact type") return value + def to_json(self): + """Return a JSON serializable dict.""" + return { + "circuit_id": self.circuit_id, + "impact": self.impact.value, + } + class Metadata(BaseModel): """Metadata class to provide context about the Maintenance object.""" @@ -100,7 +108,7 @@ class Metadata(BaseModel): generated_by_llm: bool = False -class Maintenance(BaseModel, extra=Extra.forbid): +class Maintenance(BaseModel, extra="forbid"): """Maintenance class. Mandatory attributes: @@ -108,7 +116,8 @@ class Maintenance(BaseModel, extra=Extra.forbid): account: identifies an account associated with the service that is the subject of the maintenance notification maintenance_id: contains text that uniquely identifies the maintenance that is the subject of the notification circuits: list of circuits affected by the maintenance notification and their specific impact. Note this can be - an empty list for notifications with a CANCELLED status if the provider does not populate the circuit list. + an empty list for notifications with a CANCELLED or COMPLETED status if the provider does not populate the + circuit list. status: defines the overall status or confirmation for the maintenance start: timestamp that defines the start date of the maintenance in GMT end: timestamp that defines the end date of the maintenance in GMT @@ -163,34 +172,40 @@ class Maintenance(BaseModel, extra=Extra.forbid): def __init__(self, **data): """Initialize the Maintenance object.""" - self._metadata = data.pop("_metadata") + metadata = data.pop("_metadata") super().__init__(**data) + self._metadata = metadata - # pylint: disable=no-self-argument - @validator("status") + @field_validator("status") + @classmethod def validate_status_type(cls, value): """Validate Status type.""" if value not in Status: raise ValueError("Not a valid status type") return value - @validator("provider", "account", "maintenance_id", "organizer") + @field_validator("provider", "account", "maintenance_id", "organizer") + @classmethod def validate_empty_strings(cls, value): """Validate emptry strings.""" if value in ["", "None"]: raise ValueError("String is empty or 'None'") return value - @validator("circuits") + @field_validator("circuits") + @classmethod def validate_empty_circuits(cls, value, values): """Validate non-cancel notifications have a populated circuit list.""" - if len(value) < 1 and values["status"] != "CANCELLED": + values = values.data + if len(value) < 1 and str(values["status"]) in ("CANCELLED", "COMPLETED"): raise ValueError("At least one circuit has to be included in the maintenance") return value - @validator("end") + @field_validator("end") + @classmethod def validate_end_time(cls, end, values): """Validate that End time happens after Start time.""" + values = values.data if "start" not in values: raise ValueError("Start time is a mandatory attribute.") start = values["start"] @@ -208,6 +223,6 @@ def to_json(self) -> str: return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True, indent=2) @property - def metadata(self): + def metadata(self) -> Metadata: """Get Maintenance Metadata.""" return self._metadata diff --git a/circuit_maintenance_parser/parser.py b/circuit_maintenance_parser/parser.py index e220e363..2e6aa13e 100644 --- a/circuit_maintenance_parser/parser.py +++ b/circuit_maintenance_parser/parser.py @@ -12,7 +12,7 @@ import bs4 # type: ignore from bs4.element import ResultSet # type: ignore -from pydantic import BaseModel +from pydantic import BaseModel, PrivateAttr from icalendar import Calendar # type: ignore from circuit_maintenance_parser.errors import ParserError @@ -34,7 +34,7 @@ class Parser(BaseModel): """ # _data_types are used to match the Parser to to each type of DataPart - _data_types = ["text/plain", "plain"] + _data_types = PrivateAttr(["text/plain", "plain"]) # TODO: move it to where it is used, Cogent parser _geolocator = Geolocator() @@ -42,7 +42,7 @@ class Parser(BaseModel): @classmethod def get_data_types(cls) -> List[str]: """Return the expected data type.""" - return cls._data_types + return cls._data_types.get_default() @classmethod def get_name(cls) -> str: @@ -92,7 +92,7 @@ class ICal(Parser): Reference: https://tools.ietf.org/html/draft-gunter-calext-maintenance-notifications-00 """ - _data_types = ["text/calendar", "ical", "icalendar"] + _data_types = PrivateAttr(["text/calendar", "ical", "icalendar"]) def parser_hook(self, raw: bytes, content_type: str): """Execute parsing.""" @@ -164,7 +164,7 @@ def parse_ical(gcal: Calendar) -> List[Dict]: class Html(Parser): """Html parser.""" - _data_types = ["text/html", "html"] + _data_types = PrivateAttr(["text/html", "html"]) @staticmethod def remove_hex_characters(string): @@ -201,7 +201,11 @@ def clean_line(line): class EmailDateParser(Parser): """Parser for Email Date.""" - _data_types = [EMAIL_HEADER_DATE] + _data_types = PrivateAttr( + [ + EMAIL_HEADER_DATE, + ] + ) def parser_hook(self, raw: bytes, content_type: str): """Execute parsing.""" @@ -214,7 +218,11 @@ def parser_hook(self, raw: bytes, content_type: str): class EmailSubjectParser(Parser): """Parse data from subject or email.""" - _data_types = [EMAIL_HEADER_SUBJECT] + _data_types = PrivateAttr( + [ + EMAIL_HEADER_SUBJECT, + ] + ) def parser_hook(self, raw: bytes, content_type: str): """Execute parsing.""" @@ -236,7 +244,7 @@ def bytes_to_string(string): class Csv(Parser): """Csv parser.""" - _data_types = ["application/csv", "text/csv", "application/octet-stream"] + _data_types = PrivateAttr(["application/csv", "text/csv", "application/octet-stream"]) def parser_hook(self, raw: bytes, content_type: str): """Execute parsing.""" @@ -255,7 +263,11 @@ def parse_csv(raw: bytes) -> List[Dict]: class Text(Parser): """Text parser.""" - _data_types = ["text/plain"] + _data_types = PrivateAttr( + [ + "text/plain", + ] + ) def parser_hook(self, raw: bytes, content_type: str): """Execute parsing.""" @@ -278,7 +290,7 @@ def parse_text(self, text) -> List[Dict]: class LLM(Parser): """LLM parser.""" - _data_types = ["text/html", "html", "text/plain"] + _data_types = PrivateAttr(["text/html", "html", "text/plain"]) _llm_question = """Please, could you extract a JSON form without any other comment, with the following JSON schema (timestamps in EPOCH and taking into account the GMT offset): diff --git a/circuit_maintenance_parser/parsers/aws.py b/circuit_maintenance_parser/parsers/aws.py index 00be0b8c..e2c27450 100644 --- a/circuit_maintenance_parser/parsers/aws.py +++ b/circuit_maintenance_parser/parsers/aws.py @@ -1,4 +1,4 @@ -"""AquaComms parser.""" +"""AWS parser.""" import hashlib import logging import quopri @@ -65,7 +65,7 @@ def parse_text(self, text): maintenace_id = "" status = Status.CONFIRMED for line in text.splitlines(): - if "planned maintenance" in line.lower(): + if "planned maintenance" in line.lower() or "maintenance has been scheduled" in line.lower(): data["summary"] = line search = re.search( r"([A-Z][a-z]{2}, [0-9]{1,2} [A-Z][a-z]{2,9} [0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2} [A-Z]{2,3}) to ([A-Z][a-z]{2}, [0-9]{1,2} [A-Z][a-z]{2,9} [0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2} [A-Z]{2,3})", diff --git a/circuit_maintenance_parser/parsers/crowncastle.py b/circuit_maintenance_parser/parsers/crowncastle.py new file mode 100644 index 00000000..855814d5 --- /dev/null +++ b/circuit_maintenance_parser/parsers/crowncastle.py @@ -0,0 +1,90 @@ +"""Crown Castle Fiber parser.""" +import logging +import re +from datetime import datetime + +from circuit_maintenance_parser.parser import Html, Impact, CircuitImpact, Status + +# pylint: disable=too-many-nested-blocks, too-many-branches + +logger = logging.getLogger(__name__) + + +class HtmlParserCrownCastle1(Html): + """Notifications Parser for Crown Castle Fiber notifications.""" + + def parse_html(self, soup): + """Execute parsing.""" + data = {} + data["circuits"] = [] + + data["status"] = self.get_status(soup) + + for paragraph in soup.find_all("p"): + for pstring in paragraph.strings: + search = re.match(r"^Dear (.*),", pstring) + if search: + data["account"] = search.group(1) + + self.parse_strong(soup, data) + + table = soup.find("table", "timezonegrid") + for row in table.find_all("tr"): + cols = row.find_all("td") + if len(cols) == 5: + if cols[4].string.strip() == "GMT": + st_dt = cols[0].string.strip() + " " + cols[1].string.strip() + " GMT" + en_dt = cols[2].string.strip() + " " + cols[3].string.strip() + " GMT" + data["start"] = self.dt2ts(datetime.strptime(st_dt, "%m/%d/%Y %I:%M %p %Z")) + data["end"] = self.dt2ts(datetime.strptime(en_dt, "%m/%d/%Y %I:%M %p %Z")) + + table = soup.find("table", id="circuitgrid") + if table is not None: + for row in table.find_all("tr"): + cols = row.find_all("td") + if len(cols) == 6: + if cols[4].string.strip() == "None": + impact = Impact("NO-IMPACT") + else: + impact = Impact("OUTAGE") + data["circuits"].append(CircuitImpact(impact=impact, circuit_id=cols[0].string.strip())) + + return [data] + + def parse_strong(self, soup, data): + """Parse the strong tags, to find summary and maintenance ID info.""" + for strong in soup.find_all("strong"): + if strong.string.strip() == "Ticket Number:": + data["maintenance_id"] = strong.next_sibling.strip() + if strong.string.strip() == "Description:": + summary = strong.parent.next_sibling.next_sibling.contents[0].string.strip() + summary = re.sub(r"[\n\r]", "", summary) + data["summary"] = summary + if strong.string.strip().startswith("Work Description:"): + for sibling in strong.parent.next_siblings: + summary = "".join(sibling.strings) + summary = re.sub(r"[\n\r]", "", summary) + if summary != "": + data["summary"] = summary + break + + def get_status(self, soup): + """Get the status of the maintenance.""" + for paragraph in soup.find_all("p"): + for pstring in paragraph.strings: + if "has been completed." in pstring: + return Status("COMPLETED") + + for underline in soup.find_all("u"): + if underline.string is None: + continue + if underline.string.strip() == "Maintenance Notification": + return Status("CONFIRMED") + if underline.string.strip() == "Emergency Notification": + return Status("CONFIRMED") + if underline.string.strip() == "Maintenance Notification - Rescheduled Event": + return Status("RE-SCHEDULED") + if underline.string.strip() == "Maintenance Cancellation Notification": + return Status("CANCELLED") + + return Status("NO-CHANGE") diff --git a/circuit_maintenance_parser/parsers/equinix.py b/circuit_maintenance_parser/parsers/equinix.py index 7b95e90b..fa0097c0 100644 --- a/circuit_maintenance_parser/parsers/equinix.py +++ b/circuit_maintenance_parser/parsers/equinix.py @@ -23,7 +23,8 @@ def parse_html(self, soup: ResultSet) -> List[Dict]: """ data: Dict[str, Any] = {"circuits": []} - impact = self._parse_b(soup.find_all("b"), data) + bolded_elems = soup.find_all(["b", "strong"]) + impact = self._parse_bolded(bolded_elems, data) self._parse_table(soup.find_all("th"), data, impact) return [data] @@ -43,8 +44,8 @@ def _isascii(string): except UnicodeEncodeError: return False - def _parse_b(self, b_elements, data): - """Parse the elements from the notification to capture start and end times, description, and impact. + def _parse_bolded(self, b_elements, data): + """Parse the / elements from the notification to capture start and end times, description, and impact. Args: b_elements (): resulting soup object with all elements @@ -63,7 +64,7 @@ def _parse_b(self, b_elements, data): raw_year_span = b_elem.text.strip().split() start_year = raw_year_span[1].split("-")[-1] end_year = raw_year_span[-1].split("-")[-1] - if "UTC:" in b_elem: + if start_year != 0 and "UTC:" in b_elem.text: raw_time = b_elem.next_sibling # for non english equinix notifications # english section is usually at the bottom @@ -80,9 +81,13 @@ def _parse_b(self, b_elements, data): # all circuits in the notification share the same impact if "IMPACT:" in b_elem: impact_line = b_elem.next_sibling + impact_sibling_line = (impact_line.next_sibling and impact_line.next_sibling.text) or "" + if "No impact to your service" in impact_line: impact = Impact.NO_IMPACT - elif "There will be service interruptions" in impact_line.next_sibling.text: + elif "There will be service interruptions" in impact_line: + impact = Impact.OUTAGE + elif "There will be service interruptions" in impact_sibling_line: impact = Impact.OUTAGE elif "Loss of redundancy" in impact_line: impact = Impact.REDUCED_REDUNDANCY @@ -158,6 +163,8 @@ def parse_subject(self, subject: str) -> List[Dict]: data["status"] = Status.RE_SCHEDULED elif "scheduled" in subject.lower() or "reminder" in subject.lower(): data["status"] = Status.CONFIRMED + elif "cancelled" in subject.lower(): + data["status"] = Status.CANCELLED else: # Some Equinix notifications don't clearly state a status in their subject. # From inspection of examples, it looks like "Confirmed" would be the most appropriate in this case. diff --git a/circuit_maintenance_parser/parsers/google.py b/circuit_maintenance_parser/parsers/google.py new file mode 100644 index 00000000..ef7ef2f6 --- /dev/null +++ b/circuit_maintenance_parser/parsers/google.py @@ -0,0 +1,44 @@ +"""Google parser.""" +import logging +import re +from datetime import datetime + +from circuit_maintenance_parser.parser import Html, Impact, CircuitImpact, Status + +# pylint: disable=too-many-nested-blocks, too-many-branches + +logger = logging.getLogger(__name__) + + +class HtmlParserGoogle1(Html): + """Notifications Parser for Google notifications.""" + + def parse_html(self, soup): + """Execute parsing.""" + data = {} + data["circuits"] = [] + data["status"] = Status.CONFIRMED + + for span in soup.find_all("span"): + if span.string is None: + continue + if span.string.strip() == "Start Time:": + dt_str = span.next_sibling.string.strip() + data["start"] = self.dt2ts(datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S %z UTC")) + elif span.string.strip() == "End Time:": + dt_str = span.next_sibling.string.strip() + data["end"] = self.dt2ts(datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S %z UTC")) + elif span.string.strip() == "Peer ASN:": + data["account"] = span.parent.next_sibling.string.strip() + elif span.string.strip() == "Google Neighbor Address(es):": + googleaddr = span.parent.next_sibling.string.strip() + elif span.string.strip() == "Peer Neighbor Address(es):": + cid = googleaddr + "-" + span.parent.next_sibling.string.strip() + data["circuits"].append(CircuitImpact(circuit_id=cid, impact=Impact.OUTAGE)) + + summary = list(soup.find("div").find("div").strings)[-1].strip() + match = re.search(r" - Reference (.*)$", summary) + data["summary"] = summary + data["maintenance_id"] = match[1] + + return [data] diff --git a/circuit_maintenance_parser/parsers/netflix.py b/circuit_maintenance_parser/parsers/netflix.py new file mode 100644 index 00000000..bbe6bee0 --- /dev/null +++ b/circuit_maintenance_parser/parsers/netflix.py @@ -0,0 +1,78 @@ +"""Netflix parser.""" +import hashlib +import logging +import re + +from dateutil import parser + +from circuit_maintenance_parser.parser import CircuitImpact, Impact, Status, Text + +# pylint: disable=too-many-nested-blocks, too-many-branches + +logger = logging.getLogger(__name__) + + +class TextParserNetflix1(Text): + """Parse text body of Netflix AS2906 (not 40027) email.""" + + def parse_text(self, text): + """Parse text. + + Example: + Example.com (AS65001), + + Netflix (AS2906) will be performing scheduled maintenance in Edgeconnex LAS01, + at 2024-01-31 18:00:00+00:00 UTC. + + Traffic will drain away onto redundant equipment prior to the start of the + maintenance. + + After approximately 1 hour of draining, you will see BGP sessions and associated + interfaces flap. + + Expected downtime will be approximately 60 minutes. Traffic will be restored + shortly thereafter. + + Please do not shut down any links or BGP sessions during this downtime. + + Your IPs: + 192.0.2.1 + 2001:db8::1 + """ + data = {"circuits": []} + impact = Impact.OUTAGE + status = Status.CONFIRMED + minutes = 0 + hours = 0 + maintenance_id = "" + + for line in text.splitlines(): + search = re.search(r" \((AS[0-9]+)\),$", line) + if search: + data["account"] = search.group(1) + if " maintenance in " in line: + data["summary"] = line.lstrip() + search = re.search(r" ([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})\+00:00 UTC", line) + if search: + data["start"] = self.dt2ts(parser.parse(search.group(1))) + search = re.search(r" ([0-9]+) minutes", line) + if search: + minutes = int(search.group(1)) + search = re.search(r" ([0-9]+) hours", line) + if search: + hours = int(search.group(1)) + if re.search(r"^[.0-9]+$", line.lstrip()): + data["circuits"].append(CircuitImpact(circuit_id=line.lstrip(), impact=impact)) + maintenance_id += line + "/" + if re.search(r"^[0-9a-f:]+$", line.lstrip()): + data["circuits"].append(CircuitImpact(circuit_id=line.lstrip(), impact=impact)) + maintenance_id += line + "/" + + data["end"] = data["start"] + hours * 3600 + minutes * 60 + + # Netflix does not send a maintenance ID, so a hash value is being generated using the start, + # end and IDs of all circuits in the notification. + maintenance_id += str(data["start"]) + "/" + str(data["end"]) + data["maintenance_id"] = hashlib.md5(maintenance_id.encode("utf-8")).hexdigest() # nosec + data["status"] = status + return [data] diff --git a/circuit_maintenance_parser/processor.py b/circuit_maintenance_parser/processor.py index e05b7b85..b01d0e4a 100644 --- a/circuit_maintenance_parser/processor.py +++ b/circuit_maintenance_parser/processor.py @@ -5,8 +5,7 @@ from typing import Iterable, Type, Dict, List -from pydantic import BaseModel, Extra -from pydantic.error_wrappers import ValidationError +from pydantic import BaseModel, ValidationError from circuit_maintenance_parser.output import Maintenance, Metadata from circuit_maintenance_parser.data import NotificationData @@ -17,7 +16,7 @@ logger = logging.getLogger(__name__) -class GenericProcessor(BaseModel, extra=Extra.forbid): +class GenericProcessor(BaseModel, extra="forbid"): """Base class for the Processors. Attributes: diff --git a/circuit_maintenance_parser/provider.py b/circuit_maintenance_parser/provider.py index 507bacb3..ed1877d4 100644 --- a/circuit_maintenance_parser/provider.py +++ b/circuit_maintenance_parser/provider.py @@ -7,7 +7,7 @@ from typing import Iterable, List, Dict import chardet -from pydantic import BaseModel +from pydantic import BaseModel, PrivateAttr from circuit_maintenance_parser.utils import rgetattr @@ -23,12 +23,15 @@ from circuit_maintenance_parser.parsers.bso import HtmlParserBSO1 from circuit_maintenance_parser.parsers.cogent import HtmlParserCogent1, TextParserCogent1, SubjectParserCogent1 from circuit_maintenance_parser.parsers.colt import CsvParserColt1, SubjectParserColt1, SubjectParserColt2 +from circuit_maintenance_parser.parsers.crowncastle import HtmlParserCrownCastle1 from circuit_maintenance_parser.parsers.equinix import HtmlParserEquinix, SubjectParserEquinix from circuit_maintenance_parser.parsers.gtt import HtmlParserGTT1 +from circuit_maintenance_parser.parsers.google import HtmlParserGoogle1 from circuit_maintenance_parser.parsers.hgc import HtmlParserHGC1, HtmlParserHGC2, SubjectParserHGC1 from circuit_maintenance_parser.parsers.lumen import HtmlParserLumen1 from circuit_maintenance_parser.parsers.megaport import HtmlParserMegaport1 from circuit_maintenance_parser.parsers.momentum import HtmlParserMomentum1, SubjectParserMomentum1 +from circuit_maintenance_parser.parsers.netflix import TextParserNetflix1 from circuit_maintenance_parser.parsers.seaborn import ( HtmlParserSeaborn1, HtmlParserSeaborn2, @@ -70,22 +73,22 @@ class GenericProvider(BaseModel): GenericProvider() """ - _processors: List[GenericProcessor] = [SimpleProcessor(data_parsers=[ICal])] - _default_organizer: str = "unknown" + _processors: List[GenericProcessor] = PrivateAttr([SimpleProcessor(data_parsers=[ICal])]) + _default_organizer: str = PrivateAttr("unknown") - _include_filter: Dict[str, List[str]] = {} - _exclude_filter: Dict[str, List[str]] = {} + _include_filter: Dict[str, List[str]] = PrivateAttr({}) + _exclude_filter: Dict[str, List[str]] = PrivateAttr({}) def include_filter_check(self, data: NotificationData) -> bool: """If `_include_filter` is defined, it verifies that the matching criteria is met.""" - if self._include_filter: - return self.filter_check(self._include_filter, data, "include") + if self.get_default_include_filters(): + return self.filter_check(self.get_default_include_filters(), data, "include") return True def exclude_filter_check(self, data: NotificationData) -> bool: """If `_exclude_filter` is defined, it verifies that the matching criteria is met.""" - if self._exclude_filter: - return self.filter_check(self._exclude_filter, data, "exclude") + if self.get_default_exclude_filters(): + return self.filter_check(self.get_default_exclude_filters(), data, "exclude") return False @staticmethod @@ -146,9 +149,24 @@ def get_maintenances(self, data: NotificationData) -> Iterable[Maintenance]: ) @classmethod - def get_default_organizer(cls): + def get_default_organizer(cls) -> str: """Expose default_organizer as class attribute.""" - return cls._default_organizer + return cls._default_organizer.get_default() # type: ignore + + @classmethod + def get_default_processors(cls) -> List[GenericProcessor]: + """Expose default_processors as class attribute.""" + return cls._processors.get_default() # type: ignore + + @classmethod + def get_default_include_filters(cls) -> Dict[str, List[str]]: + """Expose include_filter as class attribute.""" + return cls._include_filter.get_default() # type: ignore + + @classmethod + def get_default_exclude_filters(cls) -> Dict[str, List[str]]: + """Expose exclude_filter as class attribute.""" + return cls._exclude_filter.get_default() # type: ignore @classmethod def get_extended_data(cls): @@ -156,7 +174,7 @@ def get_extended_data(cls): It's used when the data is not available in the notification itself """ - return {"organizer": cls._default_organizer, "provider": cls.get_provider_type()} + return {"organizer": cls.get_default_organizer(), "provider": cls.get_provider_type()} @classmethod def get_provider_type(cls) -> str: @@ -172,67 +190,90 @@ def get_provider_type(cls) -> str: class AquaComms(GenericProvider): """AquaComms provider custom class.""" - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserAquaComms1, SubjectParserAquaComms1]), - ] - _default_organizer = "tickets@aquacomms.com" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserAquaComms1, SubjectParserAquaComms1]), + ] + ) + _default_organizer = PrivateAttr("tickets@aquacomms.com") class Arelion(GenericProvider): """Arelion (formerly Telia Carrier) provider custom class.""" - _exclude_filter = {EMAIL_HEADER_SUBJECT: ["Disturbance Information"]} + _exclude_filter = PrivateAttr({EMAIL_HEADER_SUBJECT: ["Disturbance Information"]}) - _default_organizer = "support@arelion.com" + _default_organizer = PrivateAttr("support@arelion.com") class AWS(GenericProvider): """AWS provider custom class.""" - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, TextParserAWS1, SubjectParserAWS1]), - ] - _default_organizer = "aws-account-notifications@amazon.com" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, TextParserAWS1, SubjectParserAWS1]), + ] + ) + _default_organizer = PrivateAttr("aws-account-notifications@amazon.com") class BSO(GenericProvider): """BSO provider custom class.""" - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserBSO1]), - ] - _default_organizer = "network-servicedesk@bso.co" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserBSO1]), + ] + ) + _default_organizer = PrivateAttr("network-servicedesk@bso.co") class Cogent(GenericProvider): """Cogent provider custom class.""" - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserCogent1]), - CombinedProcessor(data_parsers=[EmailDateParser, TextParserCogent1, SubjectParserCogent1]), - ] - _default_organizer = "support@cogentco.com" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserCogent1]), + CombinedProcessor(data_parsers=[EmailDateParser, TextParserCogent1, SubjectParserCogent1]), + ] + ) + _default_organizer = PrivateAttr("support@cogentco.com") class Colt(GenericProvider): """Cogent provider custom class.""" - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, CsvParserColt1, SubjectParserColt1]), - CombinedProcessor(data_parsers=[EmailDateParser, CsvParserColt1, SubjectParserColt2]), - ] - _default_organizer = "PlannedWorks@colt.net" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, CsvParserColt1, SubjectParserColt1]), + CombinedProcessor(data_parsers=[EmailDateParser, CsvParserColt1, SubjectParserColt2]), + ] + ) + _default_organizer = PrivateAttr("PlannedWorks@colt.net") + + +class CrownCastle(GenericProvider): + """Crown Castle Fiber provider custom class.""" + + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserCrownCastle1]), + ] + ) + _default_organizer = PrivateAttr("fiberchangemgmt@crowncastle.com") class Equinix(GenericProvider): """Equinix provider custom class.""" - _include_filter = {EMAIL_HEADER_SUBJECT: ["Network Maintenance"]} + _include_filter = PrivateAttr({EMAIL_HEADER_SUBJECT: ["Network Maintenance"]}) - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[HtmlParserEquinix, SubjectParserEquinix, EmailDateParser]), - ] - _default_organizer = "servicedesk@equinix.com" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[HtmlParserEquinix, SubjectParserEquinix, EmailDateParser]), + ] + ) + _default_organizer = PrivateAttr("servicedesk@equinix.com") class EUNetworks(GenericProvider): @@ -241,86 +282,120 @@ class EUNetworks(GenericProvider): _default_organizer = "noc@eunetworks.com" +class Google(GenericProvider): + """Google provider custom class.""" + + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserGoogle1]), + ] + ) + _default_organizer = PrivateAttr("noc-noreply@google.com") + + class GTT(GenericProvider): """EXA (formerly GTT) provider custom class.""" # "Planned Work Notification", "Emergency Work Notification" - _include_filter = {EMAIL_HEADER_SUBJECT: ["Work Notification"]} + _include_filter = PrivateAttr({EMAIL_HEADER_SUBJECT: ["Work Notification"]}) - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserGTT1]), - ] - _default_organizer = "InfraCo.CM@exainfra.net" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserGTT1]), + ] + ) + _default_organizer = PrivateAttr("InfraCo.CM@exainfra.net") class HGC(GenericProvider): """HGC provider custom class.""" - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserHGC1, SubjectParserHGC1]), - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserHGC2, SubjectParserHGC1]), - ] - _default_organizer = "HGCINOCPW@hgc.com.hk" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserHGC1, SubjectParserHGC1]), + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserHGC2, SubjectParserHGC1]), + ] + ) + _default_organizer = PrivateAttr("HGCINOCPW@hgc.com.hk") class Lumen(GenericProvider): """Lumen provider custom class.""" - _include_filter = {EMAIL_HEADER_SUBJECT: ["Scheduled Maintenance"]} + _include_filter = PrivateAttr({EMAIL_HEADER_SUBJECT: ["Scheduled Maintenance"]}) - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserLumen1]), - ] - _default_organizer = "smc@lumen.com" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserLumen1]), + ] + ) + _default_organizer = PrivateAttr("smc@lumen.com") class Megaport(GenericProvider): """Megaport provider custom class.""" - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserMegaport1]), - ] - _default_organizer = "support@megaport.com" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserMegaport1]), + ] + ) + _default_organizer = PrivateAttr("support@megaport.com") class Momentum(GenericProvider): """Momentum provider custom class.""" - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserMomentum1, SubjectParserMomentum1]), - ] - _default_organizer = "maintenance@momentumtelecom.com" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserMomentum1, SubjectParserMomentum1]), + ] + ) + _default_organizer = PrivateAttr("maintenance@momentumtelecom.com") + + +class Netflix(GenericProvider): + """Netflix provider custom class.""" + + _processors: List[GenericProcessor] = PrivateAttr( + [CombinedProcessor(data_parsers=[EmailDateParser, TextParserNetflix1])] + ) + _default_organizer = PrivateAttr("cdnetops@netflix.com") class NTT(GenericProvider): """NTT provider custom class.""" - _default_organizer = "noc@us.ntt.net" + _default_organizer = PrivateAttr("noc@us.ntt.net") class PacketFabric(GenericProvider): """PacketFabric provider custom class.""" - _default_organizer = "support@packetfabric.com" + _default_organizer = PrivateAttr("support@packetfabric.com") class Seaborn(GenericProvider): """Seaborn provider custom class.""" - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserSeaborn1, SubjectParserSeaborn1]), - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserSeaborn2, SubjectParserSeaborn2]), - ] - _default_organizer = "inoc@superonline.net" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserSeaborn1, SubjectParserSeaborn1]), + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserSeaborn2, SubjectParserSeaborn2]), + ] + ) + _default_organizer = PrivateAttr("inoc@superonline.net") class Sparkle(GenericProvider): """Sparkle provider custom class.""" - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[HtmlParserSparkle1, EmailDateParser]), - ] - _default_organizer = "TISAmericaNOC@tisparkle.com" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[HtmlParserSparkle1, EmailDateParser]), + ] + ) + _default_organizer = PrivateAttr("TISAmericaNOC@tisparkle.com") class Telia(Arelion): @@ -332,30 +407,36 @@ class Telia(Arelion): class Telstra(GenericProvider): """Telstra provider custom class.""" - _processors: List[GenericProcessor] = [ - SimpleProcessor(data_parsers=[ICal]), - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserTelstra2]), - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserTelstra1]), - ] - _default_organizer = "gpen@team.telstra.com" + _processors: List[GenericProcessor] = PrivateAttr( + [ + SimpleProcessor(data_parsers=[ICal]), + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserTelstra2]), + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserTelstra1]), + ] + ) + _default_organizer = PrivateAttr("gpen@team.telstra.com") class Turkcell(GenericProvider): """Turkcell provider custom class.""" - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserTurkcell1]), - ] - _default_organizer = "inoc@superonline.net" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserTurkcell1]), + ] + ) + _default_organizer = PrivateAttr("inoc@superonline.net") class Verizon(GenericProvider): """Verizon provider custom class.""" - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserVerizon1]), - ] - _default_organizer = "NO-REPLY-sched-maint@EMEA.verizonbusiness.com" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, HtmlParserVerizon1]), + ] + ) + _default_organizer = PrivateAttr("NO-REPLY-sched-maint@EMEA.verizonbusiness.com") class Zayo(GenericProvider): @@ -366,7 +447,9 @@ class Zayo(GenericProvider): "html": ["Maintenance Ticket #"], } - _processors: List[GenericProcessor] = [ - CombinedProcessor(data_parsers=[EmailDateParser, SubjectParserZayo1, HtmlParserZayo1]), - ] - _default_organizer = "mr@zayo.com" + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[EmailDateParser, SubjectParserZayo1, HtmlParserZayo1]), + ] + ) + _default_organizer = PrivateAttr("mr@zayo.com") diff --git a/poetry.lock b/poetry.lock index 5066a315..5a2aa703 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,25 +1,40 @@ # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} + [[package]] name = "anyio" -version = "3.7.1" +version = "4.2.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = true -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, - {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, + {file = "anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee"}, + {file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"}, ] [package.dependencies] -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] -doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] -test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (<0.22)"] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] [[package]] name = "astroid" @@ -81,42 +96,45 @@ tzdata = ["tzdata"] [[package]] name = "bandit" -version = "1.7.5" +version = "1.7.7" description = "Security oriented static analyser for python code." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "bandit-1.7.5-py3-none-any.whl", hash = "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549"}, - {file = "bandit-1.7.5.tar.gz", hash = "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"}, + {file = "bandit-1.7.7-py3-none-any.whl", hash = "sha256:17e60786a7ea3c9ec84569fd5aee09936d116cb0cb43151023258340dbffb7ed"}, + {file = "bandit-1.7.7.tar.gz", hash = "sha256:527906bec6088cb499aae31bc962864b4e77569e9d529ee51df3a93b4b8ab28a"}, ] [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -GitPython = ">=1.0.1" PyYAML = ">=5.3.1" rich = "*" stevedore = ">=1.20.0" [package.extras] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"] +baseline = ["GitPython (>=3.1.30)"] +test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] toml = ["tomli (>=1.1.0)"] yaml = ["PyYAML"] [[package]] name = "beautifulsoup4" -version = "4.12.2" +version = "4.12.3" description = "Screen-scraping library" optional = false python-versions = ">=3.6.0" files = [ - {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"}, - {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"}, + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, ] [package.dependencies] soupsieve = ">1.2" [package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] @@ -173,12 +191,13 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "bs4" -version = "0.0.1" -description = "Dummy package for Beautiful Soup" +version = "0.0.2" +description = "Dummy package for Beautiful Soup (beautifulsoup4)" optional = false python-versions = "*" files = [ - {file = "bs4-0.0.1.tar.gz", hash = "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"}, + {file = "bs4-0.0.2-py2.py3-none-any.whl", hash = "sha256:abf8742c0805ef7f662dce4b51cca104cffe52b835238afc169142ab9b3fbccc"}, + {file = "bs4-0.0.2.tar.gz", hash = "sha256:a48685c58f50fe127722417bae83fe6badf500d54b55f7e39ffe43b798653925"}, ] [package.dependencies] @@ -186,13 +205,13 @@ beautifulsoup4 = "*" [[package]] name = "certifi" -version = "2023.7.22" +version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, - {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, + {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, + {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] @@ -409,38 +428,39 @@ yaml = ["PyYAML"] [[package]] name = "dill" -version = "0.3.7" +version = "0.3.8" description = "serialize all of Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "dill-0.3.7-py3-none-any.whl", hash = "sha256:76b122c08ef4ce2eedcd4d1abd8e641114bfc6c2867f49f3c41facf65bf19f5e"}, - {file = "dill-0.3.7.tar.gz", hash = "sha256:cc1c8b182eb3013e24bd475ff2e9295af86c1a38eb1aff128dac8962a9ce3c03"}, + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, ] [package.extras] graph = ["objgraph (>=1.7.2)"] +profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "distro" -version = "1.8.0" +version = "1.9.0" description = "Distro - an OS platform information API" optional = true python-versions = ">=3.6" files = [ - {file = "distro-1.8.0-py3-none-any.whl", hash = "sha256:99522ca3e365cac527b44bde033f64c6945d90eb9f769703caaec52b09bbd3ff"}, - {file = "distro-1.8.0.tar.gz", hash = "sha256:02e111d1dc6a50abb8eed6bf31c3e48ed8b0830d1ea2a1b78c61765c2513fdd8"}, + {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, + {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, ] [[package]] name = "exceptiongroup" -version = "1.1.3" +version = "1.2.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.1.3-py3-none-any.whl", hash = "sha256:343280667a4585d195ca1cf9cef84a4e178c4b6cf2274caef9859782b567d5e3"}, - {file = "exceptiongroup-1.1.3.tar.gz", hash = "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9"}, + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] [package.extras] @@ -475,13 +495,13 @@ files = [ [[package]] name = "geopy" -version = "2.4.0" +version = "2.4.1" description = "Python Geocoding Toolbox" optional = false python-versions = ">=3.7" files = [ - {file = "geopy-2.4.0-py3-none-any.whl", hash = "sha256:d2639a46d0ce4c091e9688b750ba94348a14b898a1e55c68f4b4a07e7d1afa20"}, - {file = "geopy-2.4.0.tar.gz", hash = "sha256:a59392bf17adb486b25dbdd71fbed27733bdf24a2dac588047a619de56695e36"}, + {file = "geopy-2.4.1-py3-none-any.whl", hash = "sha256:ae8b4bc5c1131820f4d75fce9d4aaaca0c85189b3aa5d64c3dcaf5e3b7b882a7"}, + {file = "geopy-2.4.1.tar.gz", hash = "sha256:50283d8e7ad07d89be5cb027338c6365a32044df3ae2556ad3f52f4840b3d0d1"}, ] [package.dependencies] @@ -496,37 +516,6 @@ dev-test = ["coverage", "pytest (>=3.10)", "pytest-asyncio (>=0.17)", "sphinx (< requests = ["requests (>=2.16.2)", "urllib3 (>=1.24.2)"] timezone = ["pytz"] -[[package]] -name = "gitdb" -version = "4.0.11" -description = "Git Object Database" -optional = false -python-versions = ">=3.7" -files = [ - {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, - {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, -] - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.40" -description = "GitPython is a Python library used to interact with Git repositories" -optional = false -python-versions = ">=3.7" -files = [ - {file = "GitPython-3.1.40-py3-none-any.whl", hash = "sha256:cf14627d5a8049ffbf49915732e5eddbe8134c3bdb9d476e6182b676fc573f8a"}, - {file = "GitPython-3.1.40.tar.gz", hash = "sha256:22b126e9ffb671fdd0c129796343a02bf67bf2994b35449ffc9321aa755e18a4"}, -] - -[package.dependencies] -gitdb = ">=4.0.1,<5" - -[package.extras] -test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest", "pytest-cov", "pytest-instafail", "pytest-subtests", "pytest-sugar"] - [[package]] name = "h11" version = "0.14.0" @@ -613,19 +602,19 @@ trio = ["trio (>=0.22.0,<0.23.0)"] [[package]] name = "httpx" -version = "0.25.1" +version = "0.26.0" description = "The next generation HTTP client." optional = true python-versions = ">=3.8" files = [ - {file = "httpx-0.25.1-py3-none-any.whl", hash = "sha256:fec7d6cc5c27c578a391f7e87b9aa7d3d8fbcd034f6399f9f79b45bcc12a866a"}, - {file = "httpx-0.25.1.tar.gz", hash = "sha256:ffd96d5cf901e63863d9f1b4b6807861dbea4d301613415d9e6e57ead15fc5d0"}, + {file = "httpx-0.26.0-py3-none-any.whl", hash = "sha256:8915f5a3627c4d47b73e8202457cb28f1266982d1159bd5779d86a80c0eab1cd"}, + {file = "httpx-0.26.0.tar.gz", hash = "sha256:451b55c30d5185ea6b23c2c793abf9bb237d2a7dfb901ced6ff69ad37ec1dfaf"}, ] [package.dependencies] anyio = "*" certifi = "*" -httpcore = "*" +httpcore = "==1.*" idna = "*" sniffio = "*" @@ -653,13 +642,13 @@ pytz = "*" [[package]] name = "idna" -version = "3.4" +version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" files = [ - {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, - {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, ] [[package]] @@ -686,172 +675,171 @@ files = [ [[package]] name = "isort" -version = "5.12.0" +version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" files = [ - {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, - {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, ] [package.extras] -colors = ["colorama (>=0.4.3)"] -pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] -plugins = ["setuptools"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] +colors = ["colorama (>=0.4.6)"] [[package]] name = "lazy-object-proxy" -version = "1.9.0" +version = "1.10.0" description = "A fast and thorough lazy object proxy." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, - {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, - {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, - {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, - {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, - {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, + {file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab7004cf2e59f7c2e4345604a3e6ea0d92ac44e1c2375527d56492014e690c3"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc0d2fc424e54c70c4bc06787e4072c4f3b1aa2f897dfdc34ce1013cf3ceef05"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e2adb09778797da09d2b5ebdbceebf7dd32e2c96f79da9052b2e87b6ea495895"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1f711e2c6dcd4edd372cf5dec5c5a30d23bba06ee012093267b3376c079ec83"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win32.whl", hash = "sha256:76a095cfe6045c7d0ca77db9934e8f7b71b14645f0094ffcd842349ada5c5fb9"}, + {file = "lazy_object_proxy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:b4f87d4ed9064b2628da63830986c3d2dca7501e6018347798313fcf028e2fd4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fec03caabbc6b59ea4a638bee5fce7117be8e99a4103d9d5ad77f15d6f81020c"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02c83f957782cbbe8136bee26416686a6ae998c7b6191711a04da776dc9e47d4"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009e6bb1f1935a62889ddc8541514b6a9e1fcf302667dcb049a0be5c8f613e56"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:75fc59fc450050b1b3c203c35020bc41bd2695ed692a392924c6ce180c6f1dc9"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:782e2c9b2aab1708ffb07d4bf377d12901d7a1d99e5e410d648d892f8967ab1f"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win32.whl", hash = "sha256:edb45bb8278574710e68a6b021599a10ce730d156e5b254941754a9cc0b17d03"}, + {file = "lazy_object_proxy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:e271058822765ad5e3bca7f05f2ace0de58a3f4e62045a8c90a0dfd2f8ad8cc6"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e98c8af98d5707dcdecc9ab0863c0ea6e88545d42ca7c3feffb6b4d1e370c7ba"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:952c81d415b9b80ea261d2372d2a4a2332a3890c2b83e0535f263ddfe43f0d43"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b39d3a151309efc8cc48675918891b865bdf742a8616a337cb0090791a0de9"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e221060b701e2aa2ea991542900dd13907a5c90fa80e199dbf5a03359019e7a3"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:92f09ff65ecff3108e56526f9e2481b8116c0b9e1425325e13245abfd79bdb1b"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win32.whl", hash = "sha256:3ad54b9ddbe20ae9f7c1b29e52f123120772b06dbb18ec6be9101369d63a4074"}, + {file = "lazy_object_proxy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:127a789c75151db6af398b8972178afe6bda7d6f68730c057fbbc2e96b08d282"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4ed0518a14dd26092614412936920ad081a424bdcb54cc13349a8e2c6d106a"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ad9e6ed739285919aa9661a5bbed0aaf410aa60231373c5579c6b4801bd883c"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fc0a92c02fa1ca1e84fc60fa258458e5bf89d90a1ddaeb8ed9cc3147f417255"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0aefc7591920bbd360d57ea03c995cebc204b424524a5bd78406f6e1b8b2a5d8"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5faf03a7d8942bb4476e3b62fd0f4cf94eaf4618e304a19865abf89a35c0bbee"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win32.whl", hash = "sha256:e333e2324307a7b5d86adfa835bb500ee70bfcd1447384a822e96495796b0ca4"}, + {file = "lazy_object_proxy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:cb73507defd385b7705c599a94474b1d5222a508e502553ef94114a143ec6696"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:366c32fe5355ef5fc8a232c5436f4cc66e9d3e8967c01fb2e6302fd6627e3d94"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2297f08f08a2bb0d32a4265e98a006643cd7233fb7983032bd61ac7a02956b3b"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18dd842b49456aaa9a7cf535b04ca4571a302ff72ed8740d06b5adcd41fe0757"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:217138197c170a2a74ca0e05bddcd5f1796c735c37d0eee33e43259b192aa424"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9a3a87cf1e133e5b1994144c12ca4aa3d9698517fe1e2ca82977781b16955658"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win32.whl", hash = "sha256:30b339b2a743c5288405aa79a69e706a06e02958eab31859f7f3c04980853b70"}, + {file = "lazy_object_proxy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:a899b10e17743683b293a729d3a11f2f399e8a90c73b089e29f5d0fe3509f0dd"}, + {file = "lazy_object_proxy-1.10.0-pp310.pp311.pp312.pp38.pp39-none-any.whl", hash = "sha256:80fa48bd89c8f2f456fc0765c11c23bf5af827febacd2f523ca5bc1893fcc09d"}, ] [[package]] name = "lxml" -version = "4.9.3" +version = "4.9.4" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" files = [ - {file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"}, - {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"}, - {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"}, - {file = "lxml-4.9.3-cp27-cp27m-win32.whl", hash = "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7"}, - {file = "lxml-4.9.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1"}, - {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"}, - {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"}, - {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, - {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, - {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f"}, - {file = "lxml-4.9.3-cp310-cp310-win32.whl", hash = "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85"}, - {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, - {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, - {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, - {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6"}, - {file = "lxml-4.9.3-cp311-cp311-win32.whl", hash = "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305"}, - {file = "lxml-4.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc"}, - {file = "lxml-4.9.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4"}, - {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be"}, - {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13"}, - {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9"}, - {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5"}, - {file = "lxml-4.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8"}, - {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56dc1f1ebccc656d1b3ed288f11e27172a01503fc016bcabdcbc0978b19352b7"}, - {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:578695735c5a3f51569810dfebd05dd6f888147a34f0f98d4bb27e92b76e05c2"}, - {file = "lxml-4.9.3-cp35-cp35m-win32.whl", hash = "sha256:704f61ba8c1283c71b16135caf697557f5ecf3e74d9e453233e4771d68a1f42d"}, - {file = "lxml-4.9.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c41bfca0bd3532d53d16fd34d20806d5c2b1ace22a2f2e4c0008570bf2c58833"}, - {file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"}, - {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"}, - {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0c0850c8b02c298d3c7006b23e98249515ac57430e16a166873fc47a5d549287"}, - {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:aca086dc5f9ef98c512bac8efea4483eb84abbf926eaeedf7b91479feb092458"}, - {file = "lxml-4.9.3-cp36-cp36m-win32.whl", hash = "sha256:50baa9c1c47efcaef189f31e3d00d697c6d4afda5c3cde0302d063492ff9b477"}, - {file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, - {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, - {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a"}, - {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02"}, - {file = "lxml-4.9.3-cp37-cp37m-win32.whl", hash = "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f"}, - {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, - {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, - {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40"}, - {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7"}, - {file = "lxml-4.9.3-cp38-cp38-win32.whl", hash = "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574"}, - {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, - {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, - {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432"}, - {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69"}, - {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50"}, - {file = "lxml-4.9.3-cp39-cp39-win32.whl", hash = "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2"}, - {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, - {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, - {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, - {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, - {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, - {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, + {file = "lxml-4.9.4-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e214025e23db238805a600f1f37bf9f9a15413c7bf5f9d6ae194f84980c78722"}, + {file = "lxml-4.9.4-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec53a09aee61d45e7dbe7e91252ff0491b6b5fee3d85b2d45b173d8ab453efc1"}, + {file = "lxml-4.9.4-cp27-cp27m-win32.whl", hash = "sha256:7d1d6c9e74c70ddf524e3c09d9dc0522aba9370708c2cb58680ea40174800013"}, + {file = "lxml-4.9.4-cp27-cp27m-win_amd64.whl", hash = "sha256:cb53669442895763e61df5c995f0e8361b61662f26c1b04ee82899c2789c8f69"}, + {file = "lxml-4.9.4-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:647bfe88b1997d7ae8d45dabc7c868d8cb0c8412a6e730a7651050b8c7289cf2"}, + {file = "lxml-4.9.4-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4d973729ce04784906a19108054e1fd476bc85279a403ea1a72fdb051c76fa48"}, + {file = "lxml-4.9.4-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:056a17eaaf3da87a05523472ae84246f87ac2f29a53306466c22e60282e54ff8"}, + {file = "lxml-4.9.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:aaa5c173a26960fe67daa69aa93d6d6a1cd714a6eb13802d4e4bd1d24a530644"}, + {file = "lxml-4.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:647459b23594f370c1c01768edaa0ba0959afc39caeeb793b43158bb9bb6a663"}, + {file = "lxml-4.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:bdd9abccd0927673cffe601d2c6cdad1c9321bf3437a2f507d6b037ef91ea307"}, + {file = "lxml-4.9.4-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:00e91573183ad273e242db5585b52670eddf92bacad095ce25c1e682da14ed91"}, + {file = "lxml-4.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a602ed9bd2c7d85bd58592c28e101bd9ff9c718fbde06545a70945ffd5d11868"}, + {file = "lxml-4.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:de362ac8bc962408ad8fae28f3967ce1a262b5d63ab8cefb42662566737f1dc7"}, + {file = "lxml-4.9.4-cp310-cp310-win32.whl", hash = "sha256:33714fcf5af4ff7e70a49731a7cc8fd9ce910b9ac194f66eaa18c3cc0a4c02be"}, + {file = "lxml-4.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:d3caa09e613ece43ac292fbed513a4bce170681a447d25ffcbc1b647d45a39c5"}, + {file = "lxml-4.9.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:359a8b09d712df27849e0bcb62c6a3404e780b274b0b7e4c39a88826d1926c28"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:43498ea734ccdfb92e1886dfedaebeb81178a241d39a79d5351ba2b671bff2b2"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4855161013dfb2b762e02b3f4d4a21cc7c6aec13c69e3bffbf5022b3e708dd97"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c71b5b860c5215fdbaa56f715bc218e45a98477f816b46cfde4a84d25b13274e"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9a2b5915c333e4364367140443b59f09feae42184459b913f0f41b9fed55794a"}, + {file = "lxml-4.9.4-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d82411dbf4d3127b6cde7da0f9373e37ad3a43e89ef374965465928f01c2b979"}, + {file = "lxml-4.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:273473d34462ae6e97c0f4e517bd1bf9588aa67a1d47d93f760a1282640e24ac"}, + {file = "lxml-4.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:389d2b2e543b27962990ab529ac6720c3dded588cc6d0f6557eec153305a3622"}, + {file = "lxml-4.9.4-cp311-cp311-win32.whl", hash = "sha256:8aecb5a7f6f7f8fe9cac0bcadd39efaca8bbf8d1bf242e9f175cbe4c925116c3"}, + {file = "lxml-4.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:c7721a3ef41591341388bb2265395ce522aba52f969d33dacd822da8f018aff8"}, + {file = "lxml-4.9.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:dbcb2dc07308453db428a95a4d03259bd8caea97d7f0776842299f2d00c72fc8"}, + {file = "lxml-4.9.4-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:01bf1df1db327e748dcb152d17389cf6d0a8c5d533ef9bab781e9d5037619229"}, + {file = "lxml-4.9.4-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e8f9f93a23634cfafbad6e46ad7d09e0f4a25a2400e4a64b1b7b7c0fbaa06d9d"}, + {file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3f3f00a9061605725df1816f5713d10cd94636347ed651abdbc75828df302b20"}, + {file = "lxml-4.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:953dd5481bd6252bd480d6ec431f61d7d87fdcbbb71b0d2bdcfc6ae00bb6fb10"}, + {file = "lxml-4.9.4-cp312-cp312-win32.whl", hash = "sha256:266f655d1baff9c47b52f529b5f6bec33f66042f65f7c56adde3fcf2ed62ae8b"}, + {file = "lxml-4.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:f1faee2a831fe249e1bae9cbc68d3cd8a30f7e37851deee4d7962b17c410dd56"}, + {file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:23d891e5bdc12e2e506e7d225d6aa929e0a0368c9916c1fddefab88166e98b20"}, + {file = "lxml-4.9.4-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e96a1788f24d03e8d61679f9881a883ecdf9c445a38f9ae3f3f193ab6c591c66"}, + {file = "lxml-4.9.4-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:5557461f83bb7cc718bc9ee1f7156d50e31747e5b38d79cf40f79ab1447afd2d"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:fdb325b7fba1e2c40b9b1db407f85642e32404131c08480dd652110fc908561b"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d74d4a3c4b8f7a1f676cedf8e84bcc57705a6d7925e6daef7a1e54ae543a197"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ac7674d1638df129d9cb4503d20ffc3922bd463c865ef3cb412f2c926108e9a4"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:ddd92e18b783aeb86ad2132d84a4b795fc5ec612e3545c1b687e7747e66e2b53"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bd9ac6e44f2db368ef8986f3989a4cad3de4cd55dbdda536e253000c801bcc7"}, + {file = "lxml-4.9.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:bc354b1393dce46026ab13075f77b30e40b61b1a53e852e99d3cc5dd1af4bc85"}, + {file = "lxml-4.9.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:f836f39678cb47c9541f04d8ed4545719dc31ad850bf1832d6b4171e30d65d23"}, + {file = "lxml-4.9.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:9c131447768ed7bc05a02553d939e7f0e807e533441901dd504e217b76307745"}, + {file = "lxml-4.9.4-cp36-cp36m-win32.whl", hash = "sha256:bafa65e3acae612a7799ada439bd202403414ebe23f52e5b17f6ffc2eb98c2be"}, + {file = "lxml-4.9.4-cp36-cp36m-win_amd64.whl", hash = "sha256:6197c3f3c0b960ad033b9b7d611db11285bb461fc6b802c1dd50d04ad715c225"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:7b378847a09d6bd46047f5f3599cdc64fcb4cc5a5a2dd0a2af610361fbe77b16"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:1343df4e2e6e51182aad12162b23b0a4b3fd77f17527a78c53f0f23573663545"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6dbdacf5752fbd78ccdb434698230c4f0f95df7dd956d5f205b5ed6911a1367c"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:506becdf2ecaebaf7f7995f776394fcc8bd8a78022772de66677c84fb02dd33d"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca8e44b5ba3edb682ea4e6185b49661fc22b230cf811b9c13963c9f982d1d964"}, + {file = "lxml-4.9.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9d9d5726474cbbef279fd709008f91a49c4f758bec9c062dfbba88eab00e3ff9"}, + {file = "lxml-4.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:bbdd69e20fe2943b51e2841fc1e6a3c1de460d630f65bde12452d8c97209464d"}, + {file = "lxml-4.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8671622256a0859f5089cbe0ce4693c2af407bc053dcc99aadff7f5310b4aa02"}, + {file = "lxml-4.9.4-cp37-cp37m-win32.whl", hash = "sha256:dd4fda67f5faaef4f9ee5383435048ee3e11ad996901225ad7615bc92245bc8e"}, + {file = "lxml-4.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6bee9c2e501d835f91460b2c904bc359f8433e96799f5c2ff20feebd9bb1e590"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:1f10f250430a4caf84115b1e0f23f3615566ca2369d1962f82bef40dd99cd81a"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3b505f2bbff50d261176e67be24e8909e54b5d9d08b12d4946344066d66b3e43"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:1449f9451cd53e0fd0a7ec2ff5ede4686add13ac7a7bfa6988ff6d75cff3ebe2"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:4ece9cca4cd1c8ba889bfa67eae7f21d0d1a2e715b4d5045395113361e8c533d"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59bb5979f9941c61e907ee571732219fa4774d5a18f3fa5ff2df963f5dfaa6bc"}, + {file = "lxml-4.9.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b1980dbcaad634fe78e710c8587383e6e3f61dbe146bcbfd13a9c8ab2d7b1192"}, + {file = "lxml-4.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9ae6c3363261021144121427b1552b29e7b59de9d6a75bf51e03bc072efb3c37"}, + {file = "lxml-4.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bcee502c649fa6351b44bb014b98c09cb00982a475a1912a9881ca28ab4f9cd9"}, + {file = "lxml-4.9.4-cp38-cp38-win32.whl", hash = "sha256:a8edae5253efa75c2fc79a90068fe540b197d1c7ab5803b800fccfe240eed33c"}, + {file = "lxml-4.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:701847a7aaefef121c5c0d855b2affa5f9bd45196ef00266724a80e439220e46"}, + {file = "lxml-4.9.4-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:f610d980e3fccf4394ab3806de6065682982f3d27c12d4ce3ee46a8183d64a6a"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:aa9b5abd07f71b081a33115d9758ef6077924082055005808f68feccb27616bd"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:365005e8b0718ea6d64b374423e870648ab47c3a905356ab6e5a5ff03962b9a9"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:16b9ec51cc2feab009e800f2c6327338d6ee4e752c76e95a35c4465e80390ccd"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a905affe76f1802edcac554e3ccf68188bea16546071d7583fb1b693f9cf756b"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fd814847901df6e8de13ce69b84c31fc9b3fb591224d6762d0b256d510cbf382"}, + {file = "lxml-4.9.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:91bbf398ac8bb7d65a5a52127407c05f75a18d7015a270fdd94bbcb04e65d573"}, + {file = "lxml-4.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f99768232f036b4776ce419d3244a04fe83784bce871b16d2c2e984c7fcea847"}, + {file = "lxml-4.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bb5bd6212eb0edfd1e8f254585290ea1dadc3687dd8fd5e2fd9a87c31915cdab"}, + {file = "lxml-4.9.4-cp39-cp39-win32.whl", hash = "sha256:88f7c383071981c74ec1998ba9b437659e4fd02a3c4a4d3efc16774eb108d0ec"}, + {file = "lxml-4.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:936e8880cc00f839aa4173f94466a8406a96ddce814651075f95837316369899"}, + {file = "lxml-4.9.4-pp310-pypy310_pp73-macosx_11_0_x86_64.whl", hash = "sha256:f6c35b2f87c004270fa2e703b872fcc984d714d430b305145c39d53074e1ffe0"}, + {file = "lxml-4.9.4-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:606d445feeb0856c2b424405236a01c71af7c97e5fe42fbc778634faef2b47e4"}, + {file = "lxml-4.9.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a1bdcbebd4e13446a14de4dd1825f1e778e099f17f79718b4aeaf2403624b0f7"}, + {file = "lxml-4.9.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0a08c89b23117049ba171bf51d2f9c5f3abf507d65d016d6e0fa2f37e18c0fc5"}, + {file = "lxml-4.9.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:232fd30903d3123be4c435fb5159938c6225ee8607b635a4d3fca847003134ba"}, + {file = "lxml-4.9.4-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:231142459d32779b209aa4b4d460b175cadd604fed856f25c1571a9d78114771"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:520486f27f1d4ce9654154b4494cf9307b495527f3a2908ad4cb48e4f7ed7ef7"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:562778586949be7e0d7435fcb24aca4810913771f845d99145a6cee64d5b67ca"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a9e7c6d89c77bb2770c9491d988f26a4b161d05c8ca58f63fb1f1b6b9a74be45"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:786d6b57026e7e04d184313c1359ac3d68002c33e4b1042ca58c362f1d09ff58"}, + {file = "lxml-4.9.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:95ae6c5a196e2f239150aa4a479967351df7f44800c93e5a975ec726fef005e2"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:9b556596c49fa1232b0fff4b0e69b9d4083a502e60e404b44341e2f8fb7187f5"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:cc02c06e9e320869d7d1bd323df6dd4281e78ac2e7f8526835d3d48c69060683"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:857d6565f9aa3464764c2cb6a2e3c2e75e1970e877c188f4aeae45954a314e0c"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c42ae7e010d7d6bc51875d768110c10e8a59494855c3d4c348b068f5fb81fdcd"}, + {file = "lxml-4.9.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f10250bb190fb0742e3e1958dd5c100524c2cc5096c67c8da51233f7448dc137"}, + {file = "lxml-4.9.4.tar.gz", hash = "sha256:b1541e50b78e15fa06a2670157a1962ef06591d4c998b998047fff5e3236880e"}, ] [package.extras] cssselect = ["cssselect (>=0.7)"] html5 = ["html5lib"] htmlsoup = ["BeautifulSoup4"] -source = ["Cython (>=0.29.35)"] +source = ["Cython (==0.29.37)"] [[package]] name = "markdown-it-py" @@ -901,45 +889,49 @@ files = [ [[package]] name = "mypy" -version = "0.982" +version = "1.8.0" description = "Optional static typing for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "mypy-0.982-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5085e6f442003fa915aeb0a46d4da58128da69325d8213b4b35cc7054090aed5"}, - {file = "mypy-0.982-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:41fd1cf9bc0e1c19b9af13a6580ccb66c381a5ee2cf63ee5ebab747a4badeba3"}, - {file = "mypy-0.982-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f793e3dd95e166b66d50e7b63e69e58e88643d80a3dcc3bcd81368e0478b089c"}, - {file = "mypy-0.982-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86ebe67adf4d021b28c3f547da6aa2cce660b57f0432617af2cca932d4d378a6"}, - {file = "mypy-0.982-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:175f292f649a3af7082fe36620369ffc4661a71005aa9f8297ea473df5772046"}, - {file = "mypy-0.982-cp310-cp310-win_amd64.whl", hash = "sha256:8ee8c2472e96beb1045e9081de8e92f295b89ac10c4109afdf3a23ad6e644f3e"}, - {file = "mypy-0.982-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58f27ebafe726a8e5ccb58d896451dd9a662a511a3188ff6a8a6a919142ecc20"}, - {file = "mypy-0.982-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6af646bd46f10d53834a8e8983e130e47d8ab2d4b7a97363e35b24e1d588947"}, - {file = "mypy-0.982-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7aeaa763c7ab86d5b66ff27f68493d672e44c8099af636d433a7f3fa5596d40"}, - {file = "mypy-0.982-cp37-cp37m-win_amd64.whl", hash = "sha256:724d36be56444f569c20a629d1d4ee0cb0ad666078d59bb84f8f887952511ca1"}, - {file = "mypy-0.982-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14d53cdd4cf93765aa747a7399f0961a365bcddf7855d9cef6306fa41de01c24"}, - {file = "mypy-0.982-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:26ae64555d480ad4b32a267d10cab7aec92ff44de35a7cd95b2b7cb8e64ebe3e"}, - {file = "mypy-0.982-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6389af3e204975d6658de4fb8ac16f58c14e1bacc6142fee86d1b5b26aa52bda"}, - {file = "mypy-0.982-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b35ce03a289480d6544aac85fa3674f493f323d80ea7226410ed065cd46f206"}, - {file = "mypy-0.982-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c6e564f035d25c99fd2b863e13049744d96bd1947e3d3d2f16f5828864506763"}, - {file = "mypy-0.982-cp38-cp38-win_amd64.whl", hash = "sha256:cebca7fd333f90b61b3ef7f217ff75ce2e287482206ef4a8b18f32b49927b1a2"}, - {file = "mypy-0.982-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a705a93670c8b74769496280d2fe6cd59961506c64f329bb179970ff1d24f9f8"}, - {file = "mypy-0.982-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:75838c649290d83a2b83a88288c1eb60fe7a05b36d46cbea9d22efc790002146"}, - {file = "mypy-0.982-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:91781eff1f3f2607519c8b0e8518aad8498af1419e8442d5d0afb108059881fc"}, - {file = "mypy-0.982-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa97b9ddd1dd9901a22a879491dbb951b5dec75c3b90032e2baa7336777363b"}, - {file = "mypy-0.982-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a692a8e7d07abe5f4b2dd32d731812a0175626a90a223d4b58f10f458747dd8a"}, - {file = "mypy-0.982-cp39-cp39-win_amd64.whl", hash = "sha256:eb7a068e503be3543c4bd329c994103874fa543c1727ba5288393c21d912d795"}, - {file = "mypy-0.982-py3-none-any.whl", hash = "sha256:1021c241e8b6e1ca5a47e4d52601274ac078a89845cfde66c6d5f769819ffa1d"}, - {file = "mypy-0.982.tar.gz", hash = "sha256:85f7a343542dc8b1ed0a888cdd34dca56462654ef23aa673907305b260b3d746"}, + {file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"}, + {file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"}, + {file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"}, + {file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"}, + {file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"}, + {file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"}, + {file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"}, + {file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"}, + {file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:52825b01f5c4c1c4eb0db253ec09c7aa17e1a7304d247c48b6f3599ef40db8bd"}, + {file = "mypy-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f5ac9a4eeb1ec0f1ccdc6f326bcdb464de5f80eb07fb38b5ddd7b0de6bc61e55"}, + {file = "mypy-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afe3fe972c645b4632c563d3f3eff1cdca2fa058f730df2b93a35e3b0c538218"}, + {file = "mypy-1.8.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:42c6680d256ab35637ef88891c6bd02514ccb7e1122133ac96055ff458f93fc3"}, + {file = "mypy-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:720a5ca70e136b675af3af63db533c1c8c9181314d207568bbe79051f122669e"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:028cf9f2cae89e202d7b6593cd98db6759379f17a319b5faf4f9978d7084cdc6"}, + {file = "mypy-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4e6d97288757e1ddba10dd9549ac27982e3e74a49d8d0179fc14d4365c7add66"}, + {file = "mypy-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f1478736fcebb90f97e40aff11a5f253af890c845ee0c850fe80aa060a267c6"}, + {file = "mypy-1.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42419861b43e6962a649068a61f4a4839205a3ef525b858377a960b9e2de6e0d"}, + {file = "mypy-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:2b5b6c721bd4aabaadead3a5e6fa85c11c6c795e0c81a7215776ef8afc66de02"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c1538c38584029352878a0466f03a8ee7547d7bd9f641f57a0f3017a7c905b8"}, + {file = "mypy-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ef4be7baf08a203170f29e89d79064463b7fc7a0908b9d0d5114e8009c3a259"}, + {file = "mypy-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178def594014aa6c35a8ff411cf37d682f428b3b5617ca79029d8ae72f5402b"}, + {file = "mypy-1.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ab3c84fa13c04aeeeabb2a7f67a25ef5d77ac9d6486ff33ded762ef353aa5592"}, + {file = "mypy-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:99b00bc72855812a60d253420d8a2eae839b0afa4938f09f4d2aa9bb4654263a"}, + {file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"}, + {file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"}, ] [package.dependencies] -mypy-extensions = ">=0.4.3" +mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=3.10" +typing-extensions = ">=4.1.0" [package.extras] dmypy = ["psutil (>=4.0)"] -python2 = ["typed-ast (>=1.4.0,<2)"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] reports = ["lxml"] [[package]] @@ -1011,24 +1003,70 @@ files = [ {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, ] +[[package]] +name = "numpy" +version = "1.26.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + [[package]] name = "openai" -version = "1.2.4" +version = "1.12.0" description = "The official Python library for the openai API" optional = true python-versions = ">=3.7.1" files = [ - {file = "openai-1.2.4-py3-none-any.whl", hash = "sha256:53927a2ca276eec0a0efdc1ae829f74a51f49b7d3e14cc6f820aeafb0abfd802"}, - {file = "openai-1.2.4.tar.gz", hash = "sha256:d99a474049376be431d9b4dec3a5c895dd76e19165748c5944e80b7905d1b1ff"}, + {file = "openai-1.12.0-py3-none-any.whl", hash = "sha256:a54002c814e05222e413664f651b5916714e4700d041d5cf5724d3ae1a3e3481"}, + {file = "openai-1.12.0.tar.gz", hash = "sha256:99c5d257d09ea6533d689d1cc77caa0ac679fa21efef8893d8b0832a86877f1b"}, ] [package.dependencies] -anyio = ">=3.5.0,<4" +anyio = ">=3.5.0,<5" distro = ">=1.7.0,<2" httpx = ">=0.23.0,<1" pydantic = ">=1.9.0,<3" +sniffio = "*" tqdm = ">4" -typing-extensions = ">=4.5,<5" +typing-extensions = ">=4.7,<5" [package.extras] datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] @@ -1063,13 +1101,13 @@ totp = ["cryptography"] [[package]] name = "pathspec" -version = "0.11.2" +version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"}, - {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"}, + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, ] [[package]] @@ -1085,28 +1123,28 @@ files = [ [[package]] name = "platformdirs" -version = "4.0.0" +version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "platformdirs-4.0.0-py3-none-any.whl", hash = "sha256:118c954d7e949b35437270383a3f2531e99dd93cf7ce4dc8340d3356d30f173b"}, - {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] [[package]] name = "pluggy" -version = "1.3.0" +version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ - {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, - {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, + {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, + {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] [package.extras] @@ -1137,56 +1175,113 @@ files = [ [[package]] name = "pydantic" -version = "1.10.13" -description = "Data validation and settings management using python type hints" +version = "2.6.1" +description = "Data validation using Python type hints" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic-1.10.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:efff03cc7a4f29d9009d1c96ceb1e7a70a65cfe86e89d34e4a5f2ab1e5693737"}, - {file = "pydantic-1.10.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ecea2b9d80e5333303eeb77e180b90e95eea8f765d08c3d278cd56b00345d01"}, - {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1740068fd8e2ef6eb27a20e5651df000978edce6da6803c2bef0bc74540f9548"}, - {file = "pydantic-1.10.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84bafe2e60b5e78bc64a2941b4c071a4b7404c5c907f5f5a99b0139781e69ed8"}, - {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc0898c12f8e9c97f6cd44c0ed70d55749eaf783716896960b4ecce2edfd2d69"}, - {file = "pydantic-1.10.13-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:654db58ae399fe6434e55325a2c3e959836bd17a6f6a0b6ca8107ea0571d2e17"}, - {file = "pydantic-1.10.13-cp310-cp310-win_amd64.whl", hash = "sha256:75ac15385a3534d887a99c713aa3da88a30fbd6204a5cd0dc4dab3d770b9bd2f"}, - {file = "pydantic-1.10.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c553f6a156deb868ba38a23cf0df886c63492e9257f60a79c0fd8e7173537653"}, - {file = "pydantic-1.10.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e08865bc6464df8c7d61439ef4439829e3ab62ab1669cddea8dd00cd74b9ffe"}, - {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e31647d85a2013d926ce60b84f9dd5300d44535a9941fe825dc349ae1f760df9"}, - {file = "pydantic-1.10.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:210ce042e8f6f7c01168b2d84d4c9eb2b009fe7bf572c2266e235edf14bacd80"}, - {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8ae5dd6b721459bfa30805f4c25880e0dd78fc5b5879f9f7a692196ddcb5a580"}, - {file = "pydantic-1.10.13-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f8e81fc5fb17dae698f52bdd1c4f18b6ca674d7068242b2aff075f588301bbb0"}, - {file = "pydantic-1.10.13-cp311-cp311-win_amd64.whl", hash = "sha256:61d9dce220447fb74f45e73d7ff3b530e25db30192ad8d425166d43c5deb6df0"}, - {file = "pydantic-1.10.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4b03e42ec20286f052490423682016fd80fda830d8e4119f8ab13ec7464c0132"}, - {file = "pydantic-1.10.13-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f59ef915cac80275245824e9d771ee939133be38215555e9dc90c6cb148aaeb5"}, - {file = "pydantic-1.10.13-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a1f9f747851338933942db7af7b6ee8268568ef2ed86c4185c6ef4402e80ba8"}, - {file = "pydantic-1.10.13-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:97cce3ae7341f7620a0ba5ef6cf043975cd9d2b81f3aa5f4ea37928269bc1b87"}, - {file = "pydantic-1.10.13-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:854223752ba81e3abf663d685f105c64150873cc6f5d0c01d3e3220bcff7d36f"}, - {file = "pydantic-1.10.13-cp37-cp37m-win_amd64.whl", hash = "sha256:b97c1fac8c49be29486df85968682b0afa77e1b809aff74b83081cc115e52f33"}, - {file = "pydantic-1.10.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c958d053453a1c4b1c2062b05cd42d9d5c8eb67537b8d5a7e3c3032943ecd261"}, - {file = "pydantic-1.10.13-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c5370a7edaac06daee3af1c8b1192e305bc102abcbf2a92374b5bc793818599"}, - {file = "pydantic-1.10.13-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d6f6e7305244bddb4414ba7094ce910560c907bdfa3501e9db1a7fd7eaea127"}, - {file = "pydantic-1.10.13-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d3a3c792a58e1622667a2837512099eac62490cdfd63bd407993aaf200a4cf1f"}, - {file = "pydantic-1.10.13-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c636925f38b8db208e09d344c7aa4f29a86bb9947495dd6b6d376ad10334fb78"}, - {file = "pydantic-1.10.13-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:678bcf5591b63cc917100dc50ab6caebe597ac67e8c9ccb75e698f66038ea953"}, - {file = "pydantic-1.10.13-cp38-cp38-win_amd64.whl", hash = "sha256:6cf25c1a65c27923a17b3da28a0bdb99f62ee04230c931d83e888012851f4e7f"}, - {file = "pydantic-1.10.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8ef467901d7a41fa0ca6db9ae3ec0021e3f657ce2c208e98cd511f3161c762c6"}, - {file = "pydantic-1.10.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:968ac42970f57b8344ee08837b62f6ee6f53c33f603547a55571c954a4225691"}, - {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9849f031cf8a2f0a928fe885e5a04b08006d6d41876b8bbd2fc68a18f9f2e3fd"}, - {file = "pydantic-1.10.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:56e3ff861c3b9c6857579de282ce8baabf443f42ffba355bf070770ed63e11e1"}, - {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f00790179497767aae6bcdc36355792c79e7bbb20b145ff449700eb076c5f96"}, - {file = "pydantic-1.10.13-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:75b297827b59bc229cac1a23a2f7a4ac0031068e5be0ce385be1462e7e17a35d"}, - {file = "pydantic-1.10.13-cp39-cp39-win_amd64.whl", hash = "sha256:e70ca129d2053fb8b728ee7d1af8e553a928d7e301a311094b8a0501adc8763d"}, - {file = "pydantic-1.10.13-py3-none-any.whl", hash = "sha256:b87326822e71bd5f313e7d3bfdc77ac3247035ac10b0c0618bd99dcf95b1e687"}, - {file = "pydantic-1.10.13.tar.gz", hash = "sha256:32c8b48dcd3b2ac4e78b0ba4af3a2c2eb6048cb75202f0ea7b34feb740efc340"}, + {file = "pydantic-2.6.1-py3-none-any.whl", hash = "sha256:0b6a909df3192245cb736509a92ff69e4fef76116feffec68e93a567347bae6f"}, + {file = "pydantic-2.6.1.tar.gz", hash = "sha256:4fd5c182a2488dc63e6d32737ff19937888001e2a6d86e94b3f233104a5d1fa9"}, ] [package.dependencies] -python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""} -typing-extensions = ">=4.2.0" +annotated-types = ">=0.4.0" +pydantic-core = "2.16.2" +typing-extensions = ">=4.6.1" [package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.16.2" +description = "" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.16.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3fab4e75b8c525a4776e7630b9ee48aea50107fea6ca9f593c98da3f4d11bf7c"}, + {file = "pydantic_core-2.16.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8bde5b48c65b8e807409e6f20baee5d2cd880e0fad00b1a811ebc43e39a00ab2"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2924b89b16420712e9bb8192396026a8fbd6d8726224f918353ac19c4c043d2a"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:16aa02e7a0f539098e215fc193c8926c897175d64c7926d00a36188917717a05"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:936a787f83db1f2115ee829dd615c4f684ee48ac4de5779ab4300994d8af325b"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:459d6be6134ce3b38e0ef76f8a672924460c455d45f1ad8fdade36796df1ddc8"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9ee4febb249c591d07b2d4dd36ebcad0ccd128962aaa1801508320896575ef"}, + {file = "pydantic_core-2.16.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40a0bd0bed96dae5712dab2aba7d334a6c67cbcac2ddfca7dbcc4a8176445990"}, + {file = "pydantic_core-2.16.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:870dbfa94de9b8866b37b867a2cb37a60c401d9deb4a9ea392abf11a1f98037b"}, + {file = "pydantic_core-2.16.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:308974fdf98046db28440eb3377abba274808bf66262e042c412eb2adf852731"}, + {file = "pydantic_core-2.16.2-cp310-none-win32.whl", hash = "sha256:a477932664d9611d7a0816cc3c0eb1f8856f8a42435488280dfbf4395e141485"}, + {file = "pydantic_core-2.16.2-cp310-none-win_amd64.whl", hash = "sha256:8f9142a6ed83d90c94a3efd7af8873bf7cefed2d3d44387bf848888482e2d25f"}, + {file = "pydantic_core-2.16.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:406fac1d09edc613020ce9cf3f2ccf1a1b2f57ab00552b4c18e3d5276c67eb11"}, + {file = "pydantic_core-2.16.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce232a6170dd6532096cadbf6185271e4e8c70fc9217ebe105923ac105da9978"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a90fec23b4b05a09ad988e7a4f4e081711a90eb2a55b9c984d8b74597599180f"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8aafeedb6597a163a9c9727d8a8bd363a93277701b7bfd2749fbefee2396469e"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9957433c3a1b67bdd4c63717eaf174ebb749510d5ea612cd4e83f2d9142f3fc8"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0d7a9165167269758145756db43a133608a531b1e5bb6a626b9ee24bc38a8f7"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dffaf740fe2e147fedcb6b561353a16243e654f7fe8e701b1b9db148242e1272"}, + {file = "pydantic_core-2.16.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8ed79883b4328b7f0bd142733d99c8e6b22703e908ec63d930b06be3a0e7113"}, + {file = "pydantic_core-2.16.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cf903310a34e14651c9de056fcc12ce090560864d5a2bb0174b971685684e1d8"}, + {file = "pydantic_core-2.16.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:46b0d5520dbcafea9a8645a8164658777686c5c524d381d983317d29687cce97"}, + {file = "pydantic_core-2.16.2-cp311-none-win32.whl", hash = "sha256:70651ff6e663428cea902dac297066d5c6e5423fda345a4ca62430575364d62b"}, + {file = "pydantic_core-2.16.2-cp311-none-win_amd64.whl", hash = "sha256:98dc6f4f2095fc7ad277782a7c2c88296badcad92316b5a6e530930b1d475ebc"}, + {file = "pydantic_core-2.16.2-cp311-none-win_arm64.whl", hash = "sha256:ef6113cd31411eaf9b39fc5a8848e71c72656fd418882488598758b2c8c6dfa0"}, + {file = "pydantic_core-2.16.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:88646cae28eb1dd5cd1e09605680c2b043b64d7481cdad7f5003ebef401a3039"}, + {file = "pydantic_core-2.16.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7b883af50eaa6bb3299780651e5be921e88050ccf00e3e583b1e92020333304b"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bf26c2e2ea59d32807081ad51968133af3025c4ba5753e6a794683d2c91bf6e"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99af961d72ac731aae2a1b55ccbdae0733d816f8bfb97b41909e143de735f522"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02906e7306cb8c5901a1feb61f9ab5e5c690dbbeaa04d84c1b9ae2a01ebe9379"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5362d099c244a2d2f9659fb3c9db7c735f0004765bbe06b99be69fbd87c3f15"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ac426704840877a285d03a445e162eb258924f014e2f074e209d9b4ff7bf380"}, + {file = "pydantic_core-2.16.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b94cbda27267423411c928208e89adddf2ea5dd5f74b9528513f0358bba019cb"}, + {file = "pydantic_core-2.16.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6db58c22ac6c81aeac33912fb1af0e930bc9774166cdd56eade913d5f2fff35e"}, + {file = "pydantic_core-2.16.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:396fdf88b1b503c9c59c84a08b6833ec0c3b5ad1a83230252a9e17b7dfb4cffc"}, + {file = "pydantic_core-2.16.2-cp312-none-win32.whl", hash = "sha256:7c31669e0c8cc68400ef0c730c3a1e11317ba76b892deeefaf52dcb41d56ed5d"}, + {file = "pydantic_core-2.16.2-cp312-none-win_amd64.whl", hash = "sha256:a3b7352b48fbc8b446b75f3069124e87f599d25afb8baa96a550256c031bb890"}, + {file = "pydantic_core-2.16.2-cp312-none-win_arm64.whl", hash = "sha256:a9e523474998fb33f7c1a4d55f5504c908d57add624599e095c20fa575b8d943"}, + {file = "pydantic_core-2.16.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:ae34418b6b389d601b31153b84dce480351a352e0bb763684a1b993d6be30f17"}, + {file = "pydantic_core-2.16.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:732bd062c9e5d9582a30e8751461c1917dd1ccbdd6cafb032f02c86b20d2e7ec"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b52776a2e3230f4854907a1e0946eec04d41b1fc64069ee774876bbe0eab55"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef551c053692b1e39e3f7950ce2296536728871110e7d75c4e7753fb30ca87f4"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ebb892ed8599b23fa8f1799e13a12c87a97a6c9d0f497525ce9858564c4575a4"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa6c8c582036275997a733427b88031a32ffa5dfc3124dc25a730658c47a572f"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ba0884a91f1aecce75202473ab138724aa4fb26d7707f2e1fa6c3e68c84fbf"}, + {file = "pydantic_core-2.16.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7924e54f7ce5d253d6160090ddc6df25ed2feea25bfb3339b424a9dd591688bc"}, + {file = "pydantic_core-2.16.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69a7b96b59322a81c2203be537957313b07dd333105b73db0b69212c7d867b4b"}, + {file = "pydantic_core-2.16.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7e6231aa5bdacda78e96ad7b07d0c312f34ba35d717115f4b4bff6cb87224f0f"}, + {file = "pydantic_core-2.16.2-cp38-none-win32.whl", hash = "sha256:41dac3b9fce187a25c6253ec79a3f9e2a7e761eb08690e90415069ea4a68ff7a"}, + {file = "pydantic_core-2.16.2-cp38-none-win_amd64.whl", hash = "sha256:f685dbc1fdadb1dcd5b5e51e0a378d4685a891b2ddaf8e2bba89bd3a7144e44a"}, + {file = "pydantic_core-2.16.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:55749f745ebf154c0d63d46c8c58594d8894b161928aa41adbb0709c1fe78b77"}, + {file = "pydantic_core-2.16.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b30b0dd58a4509c3bd7eefddf6338565c4905406aee0c6e4a5293841411a1286"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18de31781cdc7e7b28678df7c2d7882f9692ad060bc6ee3c94eb15a5d733f8f7"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5864b0242f74b9dd0b78fd39db1768bc3f00d1ffc14e596fd3e3f2ce43436a33"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8f9186ca45aee030dc8234118b9c0784ad91a0bb27fc4e7d9d6608a5e3d386c"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc6f6c9be0ab6da37bc77c2dda5f14b1d532d5dbef00311ee6e13357a418e646"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa057095f621dad24a1e906747179a69780ef45cc8f69e97463692adbcdae878"}, + {file = "pydantic_core-2.16.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ad84731a26bcfb299f9eab56c7932d46f9cad51c52768cace09e92a19e4cf55"}, + {file = "pydantic_core-2.16.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3b052c753c4babf2d1edc034c97851f867c87d6f3ea63a12e2700f159f5c41c3"}, + {file = "pydantic_core-2.16.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e0f686549e32ccdb02ae6f25eee40cc33900910085de6aa3790effd391ae10c2"}, + {file = "pydantic_core-2.16.2-cp39-none-win32.whl", hash = "sha256:7afb844041e707ac9ad9acad2188a90bffce2c770e6dc2318be0c9916aef1469"}, + {file = "pydantic_core-2.16.2-cp39-none-win_amd64.whl", hash = "sha256:9da90d393a8227d717c19f5397688a38635afec89f2e2d7af0df037f3249c39a"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f60f920691a620b03082692c378661947d09415743e437a7478c309eb0e4f82"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:47924039e785a04d4a4fa49455e51b4eb3422d6eaacfde9fc9abf8fdef164e8a"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6294e76b0380bb7a61eb8a39273c40b20beb35e8c87ee101062834ced19c545"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe56851c3f1d6f5384b3051c536cc81b3a93a73faf931f404fef95217cf1e10d"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9d776d30cde7e541b8180103c3f294ef7c1862fd45d81738d156d00551005784"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:72f7919af5de5ecfaf1eba47bf9a5d8aa089a3340277276e5636d16ee97614d7"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:4bfcbde6e06c56b30668a0c872d75a7ef3025dc3c1823a13cf29a0e9b33f67e8"}, + {file = "pydantic_core-2.16.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ff7c97eb7a29aba230389a2661edf2e9e06ce616c7e35aa764879b6894a44b25"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9b5f13857da99325dcabe1cc4e9e6a3d7b2e2c726248ba5dd4be3e8e4a0b6d0e"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a7e41e3ada4cca5f22b478c08e973c930e5e6c7ba3588fb8e35f2398cdcc1545"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60eb8ceaa40a41540b9acae6ae7c1f0a67d233c40dc4359c256ad2ad85bdf5e5"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7beec26729d496a12fd23cf8da9944ee338c8b8a17035a560b585c36fe81af20"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22c5f022799f3cd6741e24f0443ead92ef42be93ffda0d29b2597208c94c3753"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:eca58e319f4fd6df004762419612122b2c7e7d95ffafc37e890252f869f3fb2a"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ed957db4c33bc99895f3a1672eca7e80e8cda8bd1e29a80536b4ec2153fa9804"}, + {file = "pydantic_core-2.16.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:459c0d338cc55d099798618f714b21b7ece17eb1a87879f2da20a3ff4c7628e2"}, + {file = "pydantic_core-2.16.2.tar.gz", hash = "sha256:0ba503850d8b8dcc18391f10de896ae51d37fe5fe43dbfb6a35c5c5cad271a06"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydocstyle" @@ -1218,17 +1313,18 @@ files = [ [[package]] name = "pygments" -version = "2.16.1" +version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ - {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, - {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [package.extras] plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pylint" @@ -1261,13 +1357,13 @@ testutils = ["gitpython (>3)"] [[package]] name = "pytest" -version = "7.4.3" +version = "7.4.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.3-py3-none-any.whl", hash = "sha256:0d009c083ea859a71b76adf7c1d502e4bc170b80a8ef002da5806527b9591fac"}, - {file = "pytest-7.4.3.tar.gz", hash = "sha256:d989d136982de4e3b29dabcc838ad581c64e8ed52c11fbe86ddebd9da0818cd5"}, + {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, + {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, ] [package.dependencies] @@ -1295,29 +1391,15 @@ files = [ [package.dependencies] six = ">=1.5" -[[package]] -name = "python-dotenv" -version = "1.0.0" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, - {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - [[package]] name = "pytz" -version = "2023.3.post1" +version = "2024.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"}, - {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"}, + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, ] [[package]] @@ -1345,6 +1427,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -1421,13 +1504,13 @@ test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "tes [[package]] name = "rich" -version = "13.6.0" +version = "13.7.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.6.0-py3-none-any.whl", hash = "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245"}, - {file = "rich-13.6.0.tar.gz", hash = "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"}, + {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, + {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, ] [package.dependencies] @@ -1440,17 +1523,17 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "setuptools" -version = "68.2.2" +version = "69.0.3" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, - {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, + {file = "setuptools-69.0.3-py3-none-any.whl", hash = "sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05"}, + {file = "setuptools-69.0.3.tar.gz", hash = "sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] @@ -1465,17 +1548,6 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -[[package]] -name = "smmap" -version = "5.0.1" -description = "A pure Python implementation of a sliding window memory map manager" -optional = false -python-versions = ">=3.7" -files = [ - {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, - {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, -] - [[package]] name = "sniffio" version = "1.3.0" @@ -1525,23 +1597,26 @@ pbr = ">=2.0.0,<2.1.0 || >2.1.0" [[package]] name = "timezonefinder" -version = "6.2.0" -description = "fast python package for finding the timezone of any point on earth (coordinates) offline" +version = "6.4.1" +description = "python package for finding the timezone of any point on earth (coordinates) offline" optional = false python-versions = ">=3.8,<4" files = [ - {file = "timezonefinder-6.2.0-cp38-cp38-manylinux_2_35_x86_64.whl", hash = "sha256:06aa5926ed31687ea9eb00ab53203631f09a78f307285b4929da4ac4e2889240"}, - {file = "timezonefinder-6.2.0.tar.gz", hash = "sha256:d41fd2650bb4221fae5a61f9c2767158f9727c4aaca95e24da86394feb704220"}, + {file = "timezonefinder-6.4.1-cp38-cp38-manylinux_2_35_x86_64.whl", hash = "sha256:5b55b0a156b84e3271aafe8fa45fa5af6f90e5feec6b64be346749a378d63b69"}, + {file = "timezonefinder-6.4.1.tar.gz", hash = "sha256:80ee0e277e496cec891849d99e7bf88e5c55cd03471fd720d505ac477b5347de"}, ] [package.dependencies] cffi = ">=1.15.1,<2" h3 = ">=3.7.6,<4" -numpy = ">=1.18,<2" +numpy = [ + {version = "<1.25", markers = "python_version < \"3.9\""}, + {version = ">=1.25,<2", markers = "python_version >= \"3.9\""}, +] setuptools = ">=65.5" [package.extras] -numba = ["numba (>=0.56,<1)"] +numba = ["numba (<0.59)", "numba (>=0.59,<1)"] pytz = ["pytz (>=2022.7.1)"] [[package]] @@ -1568,13 +1643,13 @@ files = [ [[package]] name = "tomlkit" -version = "0.12.2" +version = "0.12.3" description = "Style preserving TOML library" optional = false python-versions = ">=3.7" files = [ - {file = "tomlkit-0.12.2-py3-none-any.whl", hash = "sha256:eeea7ac7563faeab0a1ed8fe12c2e5a51c61f933f2502f7e9db0241a65163ad0"}, - {file = "tomlkit-0.12.2.tar.gz", hash = "sha256:df32fab589a81f0d7dc525a4267b6d7a64ee99619cbd1eeb0fae32c1dd426977"}, + {file = "tomlkit-0.12.3-py3-none-any.whl", hash = "sha256:b0a645a9156dc7cb5d3a1f0d4bab66db287fcb8e0430bdd4664a095ea16414ba"}, + {file = "tomlkit-0.12.3.tar.gz", hash = "sha256:75baf5012d06501f07bee5bf8e801b9f343e7aac5a92581f20f80ce632e6b5a4"}, ] [[package]] @@ -1610,13 +1685,13 @@ files = [ [[package]] name = "types-python-dateutil" -version = "2.8.19.14" +version = "2.8.19.20240106" description = "Typing stubs for python-dateutil" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.8.19.14.tar.gz", hash = "sha256:1f4f10ac98bb8b16ade9dbee3518d9ace017821d94b057a425b069f834737f4b"}, - {file = "types_python_dateutil-2.8.19.14-py3-none-any.whl", hash = "sha256:f977b8de27787639986b4e28963263fd0e5158942b3ecef91b9335c130cb1ce9"}, + {file = "types-python-dateutil-2.8.19.20240106.tar.gz", hash = "sha256:1f8db221c3b98e6ca02ea83a58371b22c374f42ae5bbdf186db9c9a76581459f"}, + {file = "types_python_dateutil-2.8.19.20240106-py3-none-any.whl", hash = "sha256:efbbdc54590d0f16152fa103c9879c7d4a00e82078f6e2cf01769042165acaa2"}, ] [[package]] @@ -1643,28 +1718,29 @@ files = [ [[package]] name = "typing-extensions" -version = "4.8.0" +version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, - {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] [[package]] name = "urllib3" -version = "2.1.0" +version = "2.2.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, + {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"}, + {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -1749,13 +1825,13 @@ files = [ [[package]] name = "yamllint" -version = "1.33.0" +version = "1.34.0" description = "A linter for YAML files." optional = false python-versions = ">=3.8" files = [ - {file = "yamllint-1.33.0-py3-none-any.whl", hash = "sha256:28a19f5d68d28d8fec538a1db21bb2d84c7dc2e2ea36266da8d4d1c5a683814d"}, - {file = "yamllint-1.33.0.tar.gz", hash = "sha256:2dceab9ef2d99518a2fcf4ffc964d44250ac4459be1ba3ca315118e4a1a81f7d"}, + {file = "yamllint-1.34.0-py3-none-any.whl", hash = "sha256:33b813f6ff2ffad2e57a288281098392b85f7463ce1f3d5cd45aa848b916a806"}, + {file = "yamllint-1.34.0.tar.gz", hash = "sha256:7f0a6a41e8aab3904878da4ae34b6248b6bc74634e0d3a90f0fb2d7e723a3d4f"}, ] [package.dependencies] @@ -1771,4 +1847,4 @@ openai = ["openai"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "3c1ae685a6e3e7bf00968676a2565d4a547af1f4f67ff96d1e61e975f2e5e898" +content-hash = "00aa1c1b8cdcd2d76d7eab0f895007f5b5f95bb7a338343d3aff8d9603acb5ed" diff --git a/pyproject.toml b/pyproject.toml index 86938cef..30109152 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "circuit-maintenance-parser" -version = "2.3.0" +version = "2.4.0" description = "Python library to parse Circuit Maintenance notifications and return a structured data back" authors = ["Network to Code "] license = "Apache-2.0" @@ -24,9 +24,9 @@ include = [ [tool.poetry.dependencies] python = "^3.8" click = ">=7.1, <9.0" -pydantic = {version = ">= 1.8.0, != 1.9.*, >= 1.10.4, < 2", extras = ["dotenv"]} +pydantic = {version = ">= 2.0.0", extras = ["dotenv"]} icalendar = "^5.0.0" -bs4 = "^0.0.1" +bs4 = "^0.0.2" lxml = "^4.6.2" geopy = "^2.1.0" timezonefinder = "^6.0.1" @@ -50,7 +50,7 @@ yamllint = "^1.20.0" bandit = "^1.6.2" invoke = "^2.2.0" flake8 = "^5.0.0" -mypy = "^0.982" +mypy = "^1.8.0" # Dependencies for mypy to correctly analyze code using these libraries types-python-dateutil = "^2.8.3" types-pytz = "^2022.0.0" @@ -114,3 +114,8 @@ build-backend = "poetry.masonry.api" [tool.pytest.ini_options] python_paths = "./" addopts = "-vv --doctest-modules" + +[tool.mypy] +plugins = [ + "pydantic.mypy" +] diff --git a/tests/unit/data/aws/aws3.eml b/tests/unit/data/aws/aws3.eml new file mode 100644 index 00000000..71321c1a --- /dev/null +++ b/tests/unit/data/aws/aws3.eml @@ -0,0 +1,41 @@ +Subject: [rCluster Request] [rCloud AWS Notification] AWS Direct Connect + Planned Maintenance Notification [AWS Account: 0000000000001] +MIME-Version: 1.0 +Content-Type: text/plain; charset="UTF-8" +Content-Transfer-Encoding: quoted-printable +X-SM-COMMUNICATION: true +X-SM-COMMUNICATION-TYPE: AWS_DIRECTCONNECT_MAINTENANCE_SCHEDULED +X-SM-DEDUPING-ID: 7cc8bab7-00bb-44e0-a3ec-bdd1a5560b80-EMAIL--1012261942-036424c1a19ca69ca7ea459ebd6823e1 +Date: Thu, 6 May 2021 21:52:56 +0000 +Feedback-ID: 1.us-east-1.xvKJ2gIiw98/SnInpbS9SQT1XBoAzwrySbDsqgMkBQI=:AmazonSES +X-SES-Outgoing: 2021.05.06-62.46.238.146 +X-Original-Sender: no-reply-aws@amazon.com +X-Original-Authentication-Results: mx.google.com; dkim=pass + header.i=@amazon.com header.s=szqgv33erturdv5cvz4vtb5qcy53gdkn + header.b=IQc0x0aC; dkim=pass header.i=@amazonses.com + header.s=ug7nbtf4gccmlpwj322ax3p6ow6yfsug header.b=X4gZtDlT; spf=pass + (google.com: domain of 0100017943ab6519-f09ba161-049c-45e4-8ff3-698af4d94f86-000000@amazonses.com + designates 62.46.238.146 as permitted sender) smtp.mailfrom=0100017943ab6519-f09ba161-049c-45e4-8ff3-698af4d94f86-000000@amazonses.com; + dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=amazon.com +X-Original-From: "Amazon Web Services, Inc." +Reply-To: "Amazon Web Services, Inc." +Precedence: list + +Hello, + +Emergency maintenance has been scheduled on an AWS Direct Connect endpoint in Datacenter Foo3, Anywhere, USA from Wed, 20 Dec 2023 03:00:00 GMT to Wed, 20 Dec 2023 07:00:00 GMT for 4 hours. This maintenance will cause a disruption to the following Direct Connect connections you own: + +dxvif-00000001 +dxcon-00000002 +dxlag-00000003 + +If you have configured your service to use redundant Direct Connect connections, then alternate connections will be available for the duration of the maintenance. + +If you have any questions, please contact AWS Support[1]. + +[1] https://aws.amazon.com/support + +Sincerely, +Amazon Web Services + +Amazon Web Services, Inc. is a subsidiary of Amazon.com, Inc. Amazon.com is a registered trademark of Amazon.com, Inc. This message was produced and distributed by Amazon Web Services Inc., 410 Terry Ave. North, Seattle, WA 98109-5210. diff --git a/tests/unit/data/aws/aws3_result.json b/tests/unit/data/aws/aws3_result.json new file mode 100644 index 00000000..c3523c6a --- /dev/null +++ b/tests/unit/data/aws/aws3_result.json @@ -0,0 +1,28 @@ +[ + { + "account": "0000000000001", + "circuits": [ + { + "circuit_id": "dxvif-00000001", + "impact": "OUTAGE" + }, + { + "circuit_id": "dxcon-00000002", + "impact": "OUTAGE" + }, + { + "circuit_id": "dxlag-00000003", + "impact": "OUTAGE" + } + ], + "end": 1703055600, + "maintenance_id": "677c40657cf43e821df44c75d500785b", + "organizer": "aws-account-notifications@amazon.com", + "provider": "aws", + "sequence": 1, + "stamp": 1620337976, + "start": 1703041200, + "status": "CONFIRMED", + "summary": "Emergency maintenance has been scheduled on an AWS Direct Connect endpoint in Datacenter Foo3, Anywhere, USA from Wed, 20 Dec 2023 03:00:00 GMT to Wed, 20 Dec 2023 07:00:00 GMT for 4 hours. This maintenance will cause a disruption to the following Direct Connect connections you own:" + } +] diff --git a/tests/unit/data/aws/aws3_text_parser_result.json b/tests/unit/data/aws/aws3_text_parser_result.json new file mode 100644 index 00000000..56234bcd --- /dev/null +++ b/tests/unit/data/aws/aws3_text_parser_result.json @@ -0,0 +1,23 @@ +[ + { + "circuits": [ + { + "circuit_id": "dxvif-00000001", + "impact": "OUTAGE" + }, + { + "circuit_id": "dxcon-00000002", + "impact": "OUTAGE" + }, + { + "circuit_id": "dxlag-00000003", + "impact": "OUTAGE" + } + ], + "end": 1703055600, + "maintenance_id": "677c40657cf43e821df44c75d500785b", + "start": 1703041200, + "status": "CONFIRMED", + "summary": "Emergency maintenance has been scheduled on an AWS Direct Connect endpoint in Datacenter Foo3, Anywhere, USA from Wed, 20 Dec 2023 03:00:00 GMT to Wed, 20 Dec 2023 07:00:00 GMT for 4 hours. This maintenance will cause a disruption to the following Direct Connect connections you own:" + } +] diff --git a/tests/unit/data/crowncastle/crowncastle1.eml b/tests/unit/data/crowncastle/crowncastle1.eml new file mode 100644 index 00000000..db1cf659 --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle1.eml @@ -0,0 +1,116 @@ +Delivered-To: noc@example.com +Message-ID: <1@FIBERCHANGEMGMT.CROWNCASTLE.COM> +MIME-Version: 1.0 +From: Change Management +To: NOC +Date: Tue, 10 Dec 2023 01:44:00 -0500 +Subject: Crown Castle Fiber Scheduled Maintenance Notification: CM20231201000 on 1/3/2024 3:00 AM EST +Content-Type: text/html; charset="utf-8" +Content-Transfer-Encoding: base64 + +PCFET0NUWVBFIGh0bWw+DQo8aHRtbCBsYW5nPSJlbiI+DQo8aGVhZD4NCjxtZXRhIGh0dHAtZXF1 +aXY9IkNvbnRlbnQtVHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PXV0Zi04Ij4NCjx0 +aXRsZT48L3RpdGxlPg0KPHN0eWxlPgogICAgICAgIC50aW1lem9uZWdyaWQsICNjaXJjdWl0Z3Jp +ZCB7CiAgICAgICAgICAgIGZvbnQtZmFtaWx5OiBBcmlhbDsKICAgICAgICAgICAgYm9yZGVyLWNv +bGxhcHNlOiBjb2xsYXBzZTsKICAgICAgICAgICAgZm9udC1zaXplOiAxMnB4OwogICAgICAgIH0K +CiAgICAgICAgICAgIC50aW1lem9uZWdyaWQgdGQsIC50aW1lem9uZWdyaWQgdGgsICNjaXJjdWl0 +Z3JpZCB0ZCwgI2NpcmN1aXRncmlkIHRoIHsKICAgICAgICAgICAgICAgIGJvcmRlcjogMXB4IHNv +bGlkICNkZGQ7CiAgICAgICAgICAgICAgICBwYWRkaW5nOiA4cHg7CiAgICAgICAgICAgIH0KCiAg +ICAgICAgICAgIC50aW1lem9uZWdyaWQgdGhlYWQsICNjaXJjdWl0Z3JpZCB0aGVhZCB7CiAgICAg +ICAgICAgICAgICBwYWRkaW5nLXRvcDogMnB4OwogICAgICAgICAgICAgICAgcGFkZGluZy1ib3R0 +b206IDJweDsKICAgICAgICAgICAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNkZGQ7CiAgICAgICAg +ICAgICAgICB0ZXh0LWFsaWduOiBsZWZ0OwogICAgICAgICAgICB9CgogICAgICAgIGJvZHkgewog +ICAgICAgICAgICBmb250LWZhbWlseTogQXJpYWw7CiAgICAgICAgICAgIGJvcmRlci1jb2xsYXBz +ZTogY29sbGFwc2U7CiAgICAgICAgICAgIGZvbnQtc2l6ZTogMTJweDsKICAgICAgICB9CiAgICA8 +L3N0eWxlPg0KPC9oZWFkPg0KPGJvZHk+DQo8ZGl2Pg0KPHRhYmxlIHN0eWxlPSJ3aWR0aDogOTAl +OyBib3JkZXI6bm9uZTsgYm9yZGVyLXNwYWNpbmc6MDsgcGFkZGluZzowOyI+DQo8dGJvZHk+DQo8 +dHI+DQo8dGQgdmFsaWduPSJ0b3AiPg0KPHAgYWxpZ249ImNlbnRlciI+PGI+PHU+PHNwYW4+PGlt +ZyBzcmM9Imh0dHBzOi8vdGVtcGdvLmNyb3duY2FzdGxlLmNvbS9ycy8zNDMtTFFSLTY1MC9pbWFn +ZXMvaW1hZ2UwMV9DQ0xvZ28yLnBuZyIgaGVpZ2h0PSI2NSIgd2lkdGg9IjI0NiI+PC9zcGFuPjwv +dT48L2I+PC9wPg0KPC90ZD4NCjwvdHI+DQo8dHI+DQo8dGQgdmFsaWduPSJ0b3AiPg0KPHAgYWxp +Z249ImNlbnRlciI+Jm5ic3A7PC9wPg0KPC90ZD4NCjwvdHI+DQo8dHI+DQo8dGQgdmFsaWduPSJ0 +b3AiPg0KPHAgYWxpZ249ImNlbnRlciI+PGI+PHNwYW4gc3R5bGU9ImNvbG9yOiM1QTY3NzE7Zm9u +dC1zaXplOjI0cHgiPjx1Pk1haW50ZW5hbmNlIE5vdGlmaWNhdGlvbjwvdT48L3NwYW4+PC9iPjwv +cD4NCjwvdGQ+DQo8L3RyPg0KPHRyPg0KPHRkPg0KPHA+PC9wPg0KPC90ZD4NCjwvdHI+DQo8dHI+ +DQo8dGQ+DQo8cD48L3A+DQo8L3RkPg0KPC90cj4NCjx0cj4NCjx0ZCB2YWxpZ249InRvcCI+DQo8 +cD4mbmJzcDs8L3A+DQo8cD4mbmJzcDs8L3A+DQo8cD48L3A+DQo8cD48L3A+DQo8cD5EZWFyIEV4 +YW1wbGUgQ3VzdG9tZXIsIDxicj4NCjxicj4NClRoaXMgbm90aWNlIGlzIGJlaW5nIHNlbnQgdG8g +bm90aWZ5IHlvdSBvZiB0aGUgZm9sbG93aW5nIHBsYW5uZWQgbWFpbnRlbmFuY2UgZXZlbnQgb24g +dGhlIENyb3duIENhc3RsZSBGaWJlciBuZXR3b3JrLg0KPGJyPg0KPGJyPg0KPC9wPg0KPHA+PHN0 +cm9uZz5UaWNrZXQgTnVtYmVyOiA8L3N0cm9uZz5DTTIwMjMxMjAxMDAwPC9wPg0KPHA+PHN0cm9u +Zz5Mb2NhdGlvbiBvZiBXb3JrOiA8L3N0cm9uZz5Bbnl3aGVyZSwgRkw8L3A+DQo8cD48L3A+DQo8 +cD4NCjx0YWJsZSBjbGFzcz0idGltZXpvbmVncmlkIj4NCjx0Ym9keT4NCjx0cj4NCjx0aCBjb2xz +cGFuPSIyIj5TY2hlZHVsZWQgU3RhcnQgRGF0ZSAmYW1wOyBUaW1lIDwvdGg+DQo8dGggY29sc3Bh +bj0iMiI+U2NoZWR1bGVkIEVuZCBEYXRlICZhbXA7IFRpbWUgPC90aD4NCjx0aD5UaW1lIFpvbmUg +PC90aD4NCjwvdHI+DQo8dHI+DQo8dGQ+MS8zLzIwMjQgPC90ZD4NCjx0ZD4zOjAwIEFNIDwvdGQ+ +DQo8dGQ+MS8zLzIwMjQgPC90ZD4NCjx0ZD45OjAwIEFNIDwvdGQ+DQo8dGQ+RWFzdGVybiA8L3Rk +Pg0KPC90cj4NCjx0cj4NCjx0ZD4xLzMvMjAyNCA8L3RkPg0KPHRkPjI6MDAgQU0gPC90ZD4NCjx0 +ZD4xLzMvMjAyNCA8L3RkPg0KPHRkPjg6MDAgQU0gPC90ZD4NCjx0ZD5DZW50cmFsIDwvdGQ+DQo8 +L3RyPg0KPHRyPg0KPHRkPjEvMy8yMDI0IDwvdGQ+DQo8dGQ+MTowMCBBTSA8L3RkPg0KPHRkPjEv +My8yMDI0IDwvdGQ+DQo8dGQ+NzowMCBBTSA8L3RkPg0KPHRkPk1vdW50YWluIDwvdGQ+DQo8L3Ry +Pg0KPHRyPg0KPHRkPjEvMy8yMDI0IDwvdGQ+DQo8dGQ+MTI6MDAgQU0gPC90ZD4NCjx0ZD4xLzMv +MjAyNCA8L3RkPg0KPHRkPjY6MDAgQU0gPC90ZD4NCjx0ZD5QYWNpZmljIDwvdGQ+DQo8L3RyPg0K +PHRyPg0KPHRkPjEvMy8yMDI0IDwvdGQ+DQo8dGQ+ODowMCBBTSA8L3RkPg0KPHRkPjEvMy8yMDI0 +IDwvdGQ+DQo8dGQ+MjowMCBQTSA8L3RkPg0KPHRkPkdNVCA8L3RkPg0KPC90cj4NCjwvdGJvZHk+ +DQo8L3RhYmxlPg0KPC9wPg0KPHA+PHN0cm9uZz5FeHBlY3RlZCBDdXN0b21lciBJbXBhY3Q6PC9z +dHJvbmc+IFBvdGVudGlhbCBTZXJ2aWNlIEFmZmVjdGluZzwvcD4NCjxwPjxzdHJvbmc+RXhwZWN0 +ZWQgSW1wYWN0IER1cmF0aW9uOjwvc3Ryb25nPiBOb25lIEV4cGVjdGVkPC9wPg0KPHA+PC9wPg0K +PHA+PHN0cm9uZz5Xb3JrPC9zdHJvbmc+IDxzdHJvbmc+RGVzY3JpcHRpb246PC9zdHJvbmc+PC9w +Pg0KPHA+Q3Jvd24gQ2FzdGxlIEZpYmVyIHdpbGwgYmUgY29uZHVjdGluZyBhIHJlcXVpcmVkIG1h +aW50ZW5hbmNlIGF0IHRoZSBhYm92ZS1saXN0ZWQgbG9jYXRpb24gZm9yIHJvdXRpbmUgc3BsaWNp +bmcuIEFsdGhvdWdoIG5vIGltcGFjdCB0byB5b3VyIGNpcmN1aXQocykgbGlzdGVkIGJlbG93IGlz +IGV4cGVjdGVkLCB0aGlzIG1haW50ZW5hbmNlIGlzIGRlZW1lZCBwb3RlbnRpYWxseSBzZXJ2aWNl +IGFmZmVjdGluZy4gV2UgYXBvbG9naXplIGZvciBhbnkNCiByZXN1bHRpbmcgaW5jb252ZW5pZW5j +ZSBhbmQgYXNzdXJlIHlvdSBvZiBvdXIgZWZmb3J0cyB0byBtaW5pbWl6ZSBhbnkgc2VydmljZSBk +aXNydXB0aW9uLjwvcD4NCjxwPjxicj4NCkN1c3RvbWVyIENpcmN1aXRzOiA8L3A+DQo8cD4NCjx0 +YWJsZSBpZD0iY2lyY3VpdGdyaWQiPg0KPHRoZWFkPg0KPHRyPg0KPHRoPkNpcmN1aXQgSUQ8L3Ro +Pg0KPHRoPkFjdGl2ZSBQcm9kdWN0PC90aD4NCjx0aD5BIExvY2F0aW9uPC90aD4NCjx0aD5aIExv +Y2F0aW9uPC90aD4NCjx0aD5JbXBhY3Q8L3RoPg0KPHRoPk5vdGVzPC90aD4NCjwvdHI+DQo8L3Ro +ZWFkPg0KPHRib2R5Pg0KPHRyPg0KPHRkPjAwMDAwMC1ERi1BQkNERkwwMS1EQ0JBRkwwMjwvdGQ+ +DQo8dGQ+RGFyayBGaWJlciAvIFBvaW50IHRvIFBvaW50IC8gTi9BPC90ZD4NCjx0ZD4xMjMgTWFp +biwgQmFzZW1lbnQsIEFueXdoZXJlLCBGTCAxMjM0NTwvdGQ+DQo8dGQ+NTY3IE1haW4sIDFzdCBG +bG9vciwgQW55d2hlcmUsIEZMIDU0MzIxPC90ZD4NCjx0ZD5Ob25lPC90ZD4NCjx0ZD48L3RkPg0K +PC90cj4NCjx0cj4NCjx0ZD4xMTExMTEtREZBQS1DQ0Y8L3RkPg0KPHRkPkRhcmsgRmliZXIgLyBQ +b2ludCB0byBQb2ludCAvIE4vQTwvdGQ+DQo8dGQ+MjM0IE1haW4sIEJhc2VtZW50LCBBbnl3aGVy +ZSwgRkwgMTIzNDU8L3RkPg0KPHRkPjY3OCBNYWluLCAxc3QgRmxvb3IsIEFueXdoZXJlLCBGTCA1 +NDMyMTwvdGQ+DQo8dGQ+Tm9uZTwvdGQ+DQo8dGQ+PC90ZD4NCjwvdHI+DQo8L3Rib2R5Pg0KPC90 +YWJsZT4NCjwvcD4NCjxwPjwvcD4NCjxwPjwvcD4NCjxwPjwvcD4NCjxwPjxzdHJvbmc+RGFyay1G +aWJlciBDdXN0b21lcnM6PC9zdHJvbmc+IDxicj4NCjxicj4NCkRhcmsgRmliZXIgc2VydmljZXMg +Y2Fubm90IGJlIG1vbml0b3JlZCBieSBDcm93biBDYXN0bGUsIHdlIGFyZSByZWxpYW50IG9uIGN1 +c3RvbWVyIGZlZWQgYmFjayBmb3IgY29uZmlybWF0aW9uIHRoYXQgc2VydmljZXMgaGF2ZSByZXN0 +b3JlZC4gVG8gZW5zdXJlIHlvdXIgc2VydmljZXMgaGF2ZSByZXN0b3JlZCwgd2UgYXJlIHJlcXVl +c3RpbmcgdGhhdCB5b3UgcHJvdmlkZSBhIGNvbnRhY3Qgd2hpY2ggaXMgYm90aCBmYW1pbGlhciB3 +aXRoIHRoZSBzZXJ2aWNlDQogYW5kIHdvdWxkIGJlIGFibGUgdG8gcHJvbXB0bHkgY29uZmlybSBz +ZXJ2aWNlIHJlc3RvcmF0aW9uIG9uY2UgdGhlIENNIGlzIGNvbXBsZXRlLiBUaGUgcmVxdWVzdCBm +b3IgY29uZmlybWF0aW9uIG1heSBiZSBuZWVkZWQgYWZ0ZXIgaG91cnMsIHBsZWFzZSBwcm92aWRl +IGJvdGggYSBuYW1lIGFuZCBjb250YWN0IHBob25lIG51bWJlciBpbiByZXNwb25zZSB0byB0aGlz +IGVtYWlsLg0KPGJyPg0KPGJyPg0KSWYgeW91IGhhdmUgYW55IHF1ZXN0aW9ucyBvciBjb25jZXJu +cyBwcmlvciB0byB0aGlzIGV2ZW50LCBwbGVhc2UgcmVwbHkgdG8gdGhpcyBub3RpZmljYXRpb24g +YXMgc29vbiBhcyBwb3NzaWJsZS4NCjxicj4NCjxicj4NCkJ5IHJlc3BvbmRpbmcgdG8gdGhpcyBu +b3RpZmljYXRpb24gaW4gYSB0aW1lbHkgbWFubmVyLCBDcm93biBDYXN0bGUgRmliZXIgQ2hhbmdl +IE1hbmFnZW1lbnQgY2FuIGF0dGVtcHQgdG8gcmVzb2x2ZSBhbnkgcG90ZW50aWFsIGNvbmZsaWN0 +cyB0aGF0IG1heSBhcmlzZS4NCjxicj4NCjxicj4NCklmIHlvdSBoYXZlIGFueSBxdWVzdGlvbnMs +IGNvbmNlcm5zIG9yIGlzc3VlcyBiZWZvcmUsIGR1cmluZyBvciBhZnRlciB0aGlzIG1haW50ZW5h +bmNlIHdpbmRvdywgcGxlYXNlIGNvbnRhY3Qgb3VyIENoYW5nZSBNYW5hZ2VtZW50IERlcGFydG1l +bnQgYXQgMS01MDgtNjIxLTE4ODggYW5kIHJlZmVyZW5jZSB0aGlzIHRpY2tldCBudW1iZXIuDQo8 +YnI+DQo8YnI+DQpJZiB5b3UgaGF2ZSBhbnkgc2VydmljZS9wZXJmb3JtYW5jZSByZWxhdGVkIGlz +c3VlcyBhZnRlciB0aGlzIG1haW50ZW5hbmNlIHdpbmRvdywgcGxlYXNlIGNvbnRhY3Qgb3VyIE5l +dHdvcmsgT3BlcmF0aW9ucyBDZW50ZXIgYXQgMS04NTUtOTMtRklCRVIgKDEtODU1LTkzMy00MjM3 +KSBhbmQgcmVmZXJlbmNlIHRoaXMgdGlja2V0IG51bWJlci4NCjwvcD4NCjxwPjwvcD4NCjxicj4N +ClRoYW5rIFlvdSw8YnI+DQo8YnI+DQo8aT5DaGFuZ2UgQ29udHJvbDxicj4NCkVtYWlsOiA8YSBj +bGFzcz0iYXV0by1zdHlsZTEiIGhyZWY9Im1haWx0bzpmaWJlcmNoYW5nZW1nbXRAY3Jvd25jYXN0 +bGUuY29tIj48c3BhbiBzdHlsZT0iY29sb3I6IzAwNjZjYzsiPmZpYmVyY2hhbmdlbWdtdEBjcm93 +bmNhc3RsZS5jb208L3NwYW4+PC9hPjxicj4NCjUwOC02MjEtMTg4ODwvaT4NCjxwPjwvcD4NCjxw +PjxpbWcgd2lkdGg9IjIwOCIgaGVpZ2h0PSI1NSIgc3JjPSJodHRwczovL3RlbXBnby5jcm93bmNh +c3RsZS5jb20vcnMvMzQzLUxRUi02NTAvaW1hZ2VzL2ltYWdlMDFfQ0NMb2dvMi5wbmciPjwvcD4N +CjwvdGQ+DQo8L3RyPg0KPC90Ym9keT4NCjwvdGFibGU+DQo8L2Rpdj4NCjxwPjwvcD4NCjxwIGFs +aWduPSJjZW50ZXIiPjxpPjxzcGFuIHN0eWxlPSJjb2xvcjpibGFjaztmb250LXNpemU6OS41cHgi +PlBsZWFzZSBub3RlOiBldmVyeSBtYWludGVuYW5jZSBlbnRhaWxzIGEgY2VydGFpbiBsZXZlbCBv +ZiByaXNrIGFuZCBhbHRob3VnaCBDcm93biBDYXN0bGUgRmliZXIgbWFrZXMgZXZlcnkgZWZmb3J0 +IHRvIHByb3ZpZGUgYWNjdXJhdGUgZXhwZWN0ZWQgY3VzdG9tZXIgaW1wYWN0LCBjb25kaXRpb25z +IG91dHNpZGUgb2YgQ3Jvd24gQ2FzdGxlDQogRmliZXIncyBjb250cm9sIG1heSBjYXVzZSBpbXBh +Y3QgdG8gYmUgZ3JlYXRlciB0aGFuIGFudGljaXBhdGVkLjwvc3Bhbj48L2k+PC9wPg0KVGhpcyBl +bWFpbCBtYXkgY29udGFpbiBjb25maWRlbnRpYWwgb3IgcHJpdmlsZWdlZCBtYXRlcmlhbC4gVXNl +IG9yIGRpc2Nsb3N1cmUgb2YgaXQgYnkgYW55b25lIG90aGVyIHRoYW4gdGhlIHJlY2lwaWVudCBp +cyB1bmF1dGhvcml6ZWQuIElmIHlvdSBhcmUgbm90IGFuIGludGVuZGVkIHJlY2lwaWVudCwgcGxl +YXNlIGRlbGV0ZSB0aGlzIGVtYWlsLg0KPC9ib2R5Pg0KPC9odG1sPg0K diff --git a/tests/unit/data/crowncastle/crowncastle1_result.json b/tests/unit/data/crowncastle/crowncastle1_result.json new file mode 100644 index 00000000..85678966 --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle1_result.json @@ -0,0 +1,25 @@ +[ + { + "account": "Example Customer", + "circuits": [ + { + "circuit_id": "000000-DF-ABCDFL01-DCBAFL02", + "impact": "NO-IMPACT" + }, + { + "circuit_id": "111111-DFAA-CCF", + "impact": "NO-IMPACT" + } + ], + "end": 1704290400, + "maintenance_id": "CM20231201000", + "organizer": "fiberchangemgmt@crowncastle.com", + "provider": "crowncastle", + "sequence": 1, + "stamp": 1702190640, + "start": 1704268800, + "status": "CONFIRMED", + "summary": "Crown Castle Fiber will be conducting a required maintenance at the above-listed location for routine splicing. Although no impact to your circuit(s) listed below is expected, this maintenance is deemed potentially service affecting. We apologize for any resulting inconvenience and assure you of our efforts to minimize any service disruption.", + "uid": "0" + } +] diff --git a/tests/unit/data/crowncastle/crowncastle2.html b/tests/unit/data/crowncastle/crowncastle2.html new file mode 100644 index 00000000..870b69be --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle2.html @@ -0,0 +1,201 @@ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+

+
+

 

+
+

Maintenance Notification

+
+

+
+

+
+

 

+

 

+

+

+

Dear Example Customer,
+
+This notice is being sent to notify you of the following planned maintenance event on the Crown Castle Fiber network. +
+
+

+

Ticket Number: CM20231201000

+

Location of Work: Anywhere, FL

+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Scheduled Start Date & Time Scheduled End Date & Time Time Zone
1/3/2024 3:00 AM 1/3/2024 9:00 AM Eastern
1/3/2024 2:00 AM 1/3/2024 8:00 AM Central
1/3/2024 1:00 AM 1/3/2024 7:00 AM Mountain
1/3/2024 12:00 AM 1/3/2024 6:00 AM Pacific
1/3/2024 8:00 AM 1/3/2024 2:00 PM GMT
+

+

Expected Customer Impact: Potential Service Affecting

+

Expected Impact Duration: None Expected

+

+

Work Description:

+

Crown Castle Fiber will be conducting a required maintenance at the above-listed location for routine splicing. Although no impact to your circuit(s) listed below is expected, this maintenance is deemed potentially service affecting. We apologize for any + resulting inconvenience and assure you of our efforts to minimize any service disruption.

+


+Customer Circuits:

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Circuit IDActive ProductA LocationZ LocationImpactNotes
000000-DF-ABCDFL01-DCBAFL02Dark Fiber / Point to Point / N/A123 Main, Basement, Anywhere, FL 12345567 Main, 1st Floor, Anywhere, FL 54321None
111111-DFAA-CCFDark Fiber / Point to Point / N/A234 Main, Basement, Anywhere, FL 12345678 Main, 1st Floor, Anywhere, FL 54321None
+

+

+

+

+

Dark-Fiber Customers:
+
+Dark Fiber services cannot be monitored by Crown Castle, we are reliant on customer feed back for confirmation that services have restored. To ensure your services have restored, we are requesting that you provide a contact which is both familiar with the service + and would be able to promptly confirm service restoration once the CM is complete. The request for confirmation may be needed after hours, please provide both a name and contact phone number in response to this email. +
+
+If you have any questions or concerns prior to this event, please reply to this notification as soon as possible. +
+
+By responding to this notification in a timely manner, Crown Castle Fiber Change Management can attempt to resolve any potential conflicts that may arise. +
+
+If you have any questions, concerns or issues before, during or after this maintenance window, please contact our Change Management Department at 1-508-621-1888 and reference this ticket number. +
+
+If you have any service/performance related issues after this maintenance window, please contact our Network Operations Center at 1-855-93-FIBER (1-855-933-4237) and reference this ticket number. +

+

+
+Thank You,
+
+Change Control
+Email: fiberchangemgmt@crowncastle.com
+508-621-1888
+

+

+
+
+

+

Please note: every maintenance entails a certain level of risk and although Crown Castle Fiber makes every effort to provide accurate expected customer impact, conditions outside of Crown Castle + Fiber's control may cause impact to be greater than anticipated.

+This email may contain confidential or privileged material. Use or disclosure of it by anyone other than the recipient is unauthorized. If you are not an intended recipient, please delete this email. + + diff --git a/tests/unit/data/crowncastle/crowncastle2_parser_result.json b/tests/unit/data/crowncastle/crowncastle2_parser_result.json new file mode 100644 index 00000000..a7b562b7 --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle2_parser_result.json @@ -0,0 +1,20 @@ +[ + { + "account": "Example Customer", + "circuits": [ + { + "circuit_id": "000000-DF-ABCDFL01-DCBAFL02", + "impact": "NO-IMPACT" + }, + { + "circuit_id": "111111-DFAA-CCF", + "impact": "NO-IMPACT" + } + ], + "end": 1704290400, + "maintenance_id": "CM20231201000", + "start": 1704268800, + "status": "CONFIRMED", + "summary": "Crown Castle Fiber will be conducting a required maintenance at the above-listed location for routine splicing. Although no impact to your circuit(s) listed below is expected, this maintenance is deemed potentially service affecting. We apologize for any resulting inconvenience and assure you of our efforts to minimize any service disruption." + } +] diff --git a/tests/unit/data/crowncastle/crowncastle3.html b/tests/unit/data/crowncastle/crowncastle3.html new file mode 100644 index 00000000..44c4ad54 --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle3.html @@ -0,0 +1,173 @@ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+

+

+
+

 

+
+

Maintenance Cancellation Notification +

+
+

 

+

 

+

+

+

+
+

+
+

+

Dear Example Customer,
+
+This notice is being sent to notify you that the aforementioned planned maintenance event on the Crown Castle Fiber network has been cancelled. +
+
+

+

Ticket Number: CM20231201000

+

Location of Work: Anywhere, FL

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Scheduled Start Date & Time Scheduled End Date & Time Time Zone
12/14/2023 10:00 PM 12/15/2023 6:00 AM Eastern
12/14/2023 9:00 PM 12/15/2023 5:00 AM Central
12/14/2023 8:00 PM 12/15/2023 4:00 AM Mountain
12/14/2023 7:00 PM 12/15/2023 3:00 AM Pacific
12/15/2023 3:00 AM 12/15/2023 11:00 AM GMT
+

+

+

Expected Impact Duration: Diverse Impact - Please See Impact per Circuit

+

+

Work Description: **THIS EVENT HAS BEEN CANCELLED**

+

Please note this maintenance has been cancelled. Crown Castle Fiber apologizes for any resulting inconvenience and thank you for your patience.

+


+Customer Circuits:

+

+ + + + + + + + + + + + + + + + + + + + + +
Circuit IDActive ProductA LocationZ LocationImpactNotes
666666-WAVE-CCFWavelength / DC to DC Connectivity - Inter Market / 10GigE123 Main St, Basement, Anywhere, FL 12345321 Main St, 1st Floor, Anywhere, FL 54321Up to 7 hours
+

+


+
+If you have any questions or concerns, please contact Crown Castle Fiber Change Control and reference this ticket number.

+

+

Change Control
+Email: fiberchangemgmt@crowncastle.com
+1-508-621-1888

+

+
+

+

Please note: every maintenance entails a certain level of risk and although Crown Castle Fiber makes every effort to provide accurate expected customer impact, conditions + outside of Crown Castle Fiber's control may cause impact to be greater than anticipated.

+
+This email may contain confidential or privileged material. Use or disclosure of it by anyone other than the recipient is unauthorized. If you are not an intended recipient, please delete this email. + + diff --git a/tests/unit/data/crowncastle/crowncastle3_parser_result.json b/tests/unit/data/crowncastle/crowncastle3_parser_result.json new file mode 100644 index 00000000..770e88c0 --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle3_parser_result.json @@ -0,0 +1,16 @@ +[ + { + "account": "Example Customer", + "circuits": [ + { + "circuit_id": "666666-WAVE-CCF", + "impact": "OUTAGE" + } + ], + "end": 1702638000, + "maintenance_id": "CM20231201000", + "start": 1702609200, + "status": "CANCELLED", + "summary": "Please note this maintenance has been cancelled. Crown Castle Fiber apologizes for any resulting inconvenience and thank you for your patience." + } +] diff --git a/tests/unit/data/crowncastle/crowncastle4.html b/tests/unit/data/crowncastle/crowncastle4.html new file mode 100644 index 00000000..09da0193 --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle4.html @@ -0,0 +1,188 @@ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+

+
+

 

+
+

Maintenance Notification - Rescheduled Event

+
+

+
+

+
+

 

+

 

+

+

+

Dear Example Customer,
+
+This notice is being sent to notify you of the rescheduled date/time of the previously notified planned maintenance event on the Crown Castle Fiber Network. +

+

+

Ticket Number: CM20231201000

+

Location of Work: Anywhere, FL

+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Scheduled Start Date & Time Scheduled End Date & Time Time Zone
12/12/2023 12:00 AM 12/12/2023 6:00 AM Eastern
12/11/2023 11:00 PM 12/12/2023 5:00 AM Central
12/11/2023 10:00 PM 12/12/2023 4:00 AM Mountain
12/11/2023 9:00 PM 12/12/2023 3:00 AM Pacific
12/12/2023 5:00 AM 12/12/2023 11:00 AM GMT
+

+

Expected Customer Impact: Potential Service Affecting

+

Expected Impact Duration: None Expected

+

+

Work Description: *** Rescheduled - Please note new date ***

+

Please note this maintenance has been rescheduled to the dates/times listed above. The previous date was 12/12/23 and the new rescheduled date is 13/12/23. Crown Castle Fiber apologizes for any inconvenience this may cause.

+


+Customer Circuits:

+


+ + + + + + + + + + + + + + + + + + + + + +
Circuit IDActive ProductA LocationZ LocationImpactNotes
666666-DBAA-CCFDark Fiber / Point to Point / N/A123 Main St, Basement, Anywhere, FL 12345321 Main St, 1st Floor, Anywhere, FL 54321None Expected
+

+

+

+

+

Dark-Fiber Customers:
+
+Dark Fiber services cannot be monitored by Crown Castle, we are reliant on customer feed back for confirmation that services have restored. To ensure your services have restored, we are requesting that you provide a contact which is both familiar with the service + and would be able to promptly confirm service restoration once the CM is complete. The request for confirmation may be needed after hours, please provide both a name and contact phone number in response to this email. +
+
+If you have any questions or concerns prior to this event, please reply to this notification as soon as possible. +
+
+By responding to this notification in a timely manner, Crown Castle Fiber Change Management can attempt to resolve any potential conflicts that may arise. +
+
+If you have any questions, concerns or issues before, during or after this maintenance window, please contact our Change Management Department at 1-508-621-1888 and reference this ticket number. +
+
+If you have any service/performance related issues after this maintenance window, please contact our Network Operations Center at 1-855-93-FIBER (1-855-933-4237) and reference this ticket number. +

+

+

Thank You,

+

Change Control
+Email: fiberchangemgmt@crowncastle.com
+508-621-1888

+

+
+

+

Please note: every maintenance entails a certain level of risk and although Crown Castle Fiber makes every effort to provide accurate expected customer impact, conditions outside Crown Castle Fiber's + control may cause impact to be greater than anticipated.

+
+This email may contain confidential or privileged material. Use or disclosure of it by anyone other than the recipient is unauthorized. If you are not an intended recipient, please delete this email. + + diff --git a/tests/unit/data/crowncastle/crowncastle4_parser_result.json b/tests/unit/data/crowncastle/crowncastle4_parser_result.json new file mode 100644 index 00000000..37f48099 --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle4_parser_result.json @@ -0,0 +1,16 @@ +[ + { + "account": "Example Customer", + "circuits": [ + { + "circuit_id": "666666-DBAA-CCF", + "impact": "OUTAGE" + } + ], + "end": 1702378800, + "maintenance_id": "CM20231201000", + "start": 1702357200, + "status": "RE-SCHEDULED", + "summary": "Please note this maintenance has been rescheduled to the dates/times listed above. The previous date was 12/12/23 and the new rescheduled date is 13/12/23. Crown Castle Fiber apologizes for any inconvenience this may cause." + } +] diff --git a/tests/unit/data/crowncastle/crowncastle5.html b/tests/unit/data/crowncastle/crowncastle5.html new file mode 100644 index 00000000..8cf57d68 --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle5.html @@ -0,0 +1,336 @@ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+

+
+

 

+
+

Maintenance Event Status

+
+

+
+

+
+

 

+

 

+

+

+

+

Dear Example Customer,
+
+This notice is being sent to inform you that the work associated with today’s maintenance window has been completed. We thank you for your patience. +

+

*DARK FIBER CUSTOMERS PLEASE NOTE: All dark fiber services must be reviewed and checked for restoration at this time to ensure connectivity after this maintenance work since we have now completed the event in its entirety. If your services have not restored + please contact our Change Control support line at 1-508-621-1888 immediately to troubleshoot and resolve the matter.

+

If this was a one day maintenance event, please consider this event completed. If maintenance was scheduled for several days, please note the event will be officially complete on the last day of the event. If you have received this notice and your services + have not restored please contact our Change Control support line at 1-508-621-1888.

+

Ticket Number: CM20231201000

+

Location of Work: Anywhere, FL

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Scheduled Start Date & Time Scheduled End Date & Time Time Zone
12/5/2023 4:30 AM 12/5/2023 9:00 AM Eastern
12/5/2023 3:30 AM 12/5/2023 8:00 AM Central
12/5/2023 2:30 AM 12/5/2023 7:00 AM Mountain
12/5/2023 1:30 AM 12/5/2023 6:00 AM Pacific
12/5/2023 9:30 AM 12/5/2023 2:00 PM GMT
+

+

Expected Customer Impact: Please Reference Your Original Notice Below For Impact

+

Expected Impact Duration: None

+

+

+


+

+

Change Control
+Email: fiberchangemgmt@crowncastle.com
+1-508-621-1888

+

+
+

+

Please note: every maintenance entails a certain level of risk and although Crown Castle Fiber makes every effort to provide accurate expected customer impact, conditions outside Crown Castle Fiber's + control may cause impact to be greater than anticipated.

+
+
+Forwarded: Original Event Notice
+
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+

+
+

 

+
+

Maintenance Notification - Rescheduled Event

+
+

+
+

+
+

 

+

 

+

+

+

Dear Example Customer,
+
+This notice is being sent to notify you of the rescheduled date/time of the previously notified planned maintenance event on the Crown Castle Fiber Network. +

+

+

Ticket Number: CM20231201000

+

Location of Work: Anywhere, FL

+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Scheduled Start Date & Time Scheduled End Date & Time Time Zone
12/5/2023 4:30 AM 12/5/2023 9:00 AM Eastern
12/5/2023 3:30 AM 12/5/2023 8:00 AM Central
12/5/2023 2:30 AM 12/5/2023 7:00 AM Mountain
12/5/2023 1:30 AM 12/5/2023 6:00 AM Pacific
12/5/2023 9:30 AM 12/5/2023 2:00 PM GMT
+

+

Expected Customer Impact: Potential Service Affecting

+

Expected Impact Duration: None

+

+

Work Description: *** Rescheduled - Please note new date ***

+

Please note this maintenance has been rescheduled to the dates/times listed above. The previous date was 12/1/23 and the new rescheduled date is 12/5/23. Crown Castle Fiber apologizes for any inconvenience this may cause.

+


+Customer Circuits:

+


+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Circuit IDActive ProductA LocationZ LocationImpactNotes
666666-DF-ANYWFL00-ANYWFL01Dark Fiber / Point to Point / N/A123 Main St, Basement, Anywhere, FL 12345321 Main St, 1st Floor, Anywhere, FL 54321None
666666-DBAA-CCFDark Fiber / Point to Point / N/A234 Main St, Basement, Anywhere, FL 12345432 Main St, 1st Floor, Anywhere, FL 54321None
+

+

+

+

+

Dark-Fiber Customers:
+
+Dark Fiber services cannot be monitored by Crown Castle, we are reliant on customer feed back for confirmation that services have restored. To ensure your services have restored, we are requesting that you provide a contact which is both familiar with the service + and would be able to promptly confirm service restoration once the CM is complete. The request for confirmation may be needed after hours, please provide both a name and contact phone number in response to this email. +
+
+If you have any questions or concerns prior to this event, please reply to this notification as soon as possible. +
+
+By responding to this notification in a timely manner, Crown Castle Fiber Change Management can attempt to resolve any potential conflicts that may arise. +
+
+If you have any questions, concerns or issues before, during or after this maintenance window, please contact our Change Management Department at 1-508-621-1888 and reference this ticket number. +
+
+If you have any service/performance related issues after this maintenance window, please contact our Network Operations Center at 1-855-93-FIBER (1-855-933-4237) and reference this ticket number. +

+

+

Thank You,

+

Change Control
+Email: fiberchangemgmt@crowncastle.com
+508-621-1888

+

+
+

+

Please note: every maintenance entails a certain level of risk and although Crown Castle Fiber makes every effort to provide accurate expected customer impact, conditions outside Crown Castle Fiber's + control may cause impact to be greater than anticipated.

+
+This email may contain confidential or privileged material. Use or disclosure of it by anyone other than the recipient is unauthorized. If you are not an intended recipient, please delete this email. + + diff --git a/tests/unit/data/crowncastle/crowncastle5_parser_result.json b/tests/unit/data/crowncastle/crowncastle5_parser_result.json new file mode 100644 index 00000000..76a72e61 --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle5_parser_result.json @@ -0,0 +1,20 @@ +[ + { + "account": "Example Customer", + "circuits": [ + { + "circuit_id": "666666-DF-ANYWFL00-ANYWFL01", + "impact": "NO-IMPACT" + }, + { + "circuit_id": "666666-DBAA-CCF", + "impact": "NO-IMPACT" + } + ], + "end": 1701784800, + "maintenance_id": "CM20231201000", + "start": 1701768600, + "status": "COMPLETED", + "summary": "Please note this maintenance has been rescheduled to the dates/times listed above. The previous date was 12/1/23 and the new rescheduled date is 12/5/23. Crown Castle Fiber apologizes for any inconvenience this may cause." + } +] diff --git a/tests/unit/data/crowncastle/crowncastle6.html b/tests/unit/data/crowncastle/crowncastle6.html new file mode 100644 index 00000000..4a57e414 --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle6.html @@ -0,0 +1,144 @@ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+

+
+

 

+
+

Maintenance Event Status

+
+

+
+

+
+

 

+

 

+

+

+

+

Dear Example Customer,
+
+This notice is being sent to inform you that the work associated with today’s maintenance window has been completed. We thank you for your patience. +

+

*DARK FIBER CUSTOMERS PLEASE NOTE: All dark fiber services must be reviewed and checked for restoration at this time to ensure connectivity after this maintenance work since we have now completed the event in its entirety. If your services have not restored + please contact our Change Control support line at 1-508-621-1888 immediately to troubleshoot and resolve the matter.

+

If this was a one day maintenance event, please consider this event completed. If maintenance was scheduled for several days, please note the event will be officially complete on the last day of the event. If you have received this notice and your services + have not restored please contact our Change Control support line at 1-508-621-1888.

+

Ticket Number: CM20231201000

+

Location of Work: Anywhere, FL

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Scheduled Start Date & Time Scheduled End Date & Time Time Zone
12/2/2023 10:00 PM 12/3/2023 6:00 AM Eastern
12/2/2023 9:00 PM 12/3/2023 5:00 AM Central
12/2/2023 8:00 PM 12/3/2023 4:00 AM Mountain
12/2/2023 7:00 PM 12/3/2023 3:00 AM Pacific
12/3/2023 3:00 AM 12/3/2023 11:00 AM GMT
+

+

Expected Customer Impact: Please Reference Your Original Notice Below For Impact

+

Expected Impact Duration: Diverse Impact - Please See Impact per Circuit

+

+

+


+

+

Change Control
+Email: fiberchangemgmt@crowncastle.com
+1-508-621-1888

+

+
+

+

Please note: every maintenance entails a certain level of risk and although Crown Castle Fiber makes every effort to provide accurate expected customer impact, conditions outside Crown Castle Fiber's + control may cause impact to be greater than anticipated.

+
+This email may contain confidential or privileged material. Use or disclosure of it by anyone other than the recipient is unauthorized. If you are not an intended recipient, please delete this email. + + diff --git a/tests/unit/data/crowncastle/crowncastle6_parser_result.json b/tests/unit/data/crowncastle/crowncastle6_parser_result.json new file mode 100644 index 00000000..03f5a0da --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle6_parser_result.json @@ -0,0 +1,10 @@ +[ + { + "account": "Example Customer", + "circuits": [], + "end": 1701601200, + "maintenance_id": "CM20231201000", + "start": 1701572400, + "status": "COMPLETED" + } +] diff --git a/tests/unit/data/crowncastle/crowncastle7.eml b/tests/unit/data/crowncastle/crowncastle7.eml new file mode 100644 index 00000000..7006dc84 --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle7.eml @@ -0,0 +1,88 @@ +Delivered-To: noc@example.com +Message-ID: <1@FIBERCHANGEMGMT.CROWNCASTLE.COM> +MIME-Version: 1.0 +From: Change Management +To: NOC +Date: Tue, 10 Dec 2023 01:44:00 -0500 +Subject: Crown Castle Fiber Scheduled Maintenance Notification: CM20231201000 on 12/2/2023 3:00 AM EST Event Completed +Content-Type: text/html; charset="utf-8" +Content-Transfer-Encoding: base64 + +PCFET0NUWVBFIGh0bWw+DQo8aHRtbD4NCjxoZWFkPg0KPG1ldGEgaHR0cC1lcXVpdj0iQ29udGVu +dC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7IGNoYXJzZXQ9dXRmLTgiPg0KPHRpdGxlPjwvdGl0 +bGU+DQo8c3R5bGU+CiAgICAgICAgLnRpbWV6b25lZ3JpZCwgI2NpcmN1aXRncmlkIHsKICAgICAg +ICAgICAgZm9udC1mYW1pbHk6IEFyaWFsOwogICAgICAgICAgICBib3JkZXItY29sbGFwc2U6IGNv +bGxhcHNlOwogICAgICAgICAgICBmb250LXNpemU6IDEycHg7CiAgICAgICAgfQoKICAgICAgICAg +ICAgLnRpbWV6b25lZ3JpZCB0ZCwgLnRpbWV6b25lZ3JpZCB0aCwgI2NpcmN1aXRncmlkIHRkLCAj +Y2lyY3VpdGdyaWQgdGggewogICAgICAgICAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2RkZDsK +ICAgICAgICAgICAgICAgIHBhZGRpbmc6IDhweDsKICAgICAgICAgICAgfQoKICAgICAgICAgICAg +LnRpbWV6b25lZ3JpZCB0aGVhZCwgI2NpcmN1aXRncmlkIHRoZWFkIHsKICAgICAgICAgICAgICAg +IHBhZGRpbmctdG9wOiAycHg7CiAgICAgICAgICAgICAgICBwYWRkaW5nLWJvdHRvbTogMnB4Owog +ICAgICAgICAgICAgICAgYm9yZGVyOiAxcHggc29saWQgI2RkZDsKICAgICAgICAgICAgICAgIHRl +eHQtYWxpZ246IGxlZnQ7CiAgICAgICAgICAgIH0KCiAgICAgICAgYm9keSB7CiAgICAgICAgICAg +IGZvbnQtZmFtaWx5OiBBcmlhbDsKICAgICAgICAgICAgYm9yZGVyLWNvbGxhcHNlOiBjb2xsYXBz +ZTsKICAgICAgICAgICAgZm9udC1zaXplOiAxMnB4OwogICAgICAgIH0KICAgIDwvc3R5bGU+DQo8 +L2hlYWQ+DQo8Ym9keT4NCjxkaXY+DQo8dGFibGUgc3R5bGU9IndpZHRoOiA5MCU7IGJvcmRlcjpu +b25lOyBib3JkZXItc3BhY2luZzowOyBwYWRkaW5nOjA7Ij4NCjx0Ym9keT4NCjx0cj4NCjx0ZCB2 +YWxpZ249InRvcCI+DQo8cCBzdHlsZT0ibWFyZ2luOjBpbiAwaW4gMHB0O3RleHQtYWxpZ246Y2Vu +dGVyIj48aW1nIHNyYz0iaHR0cHM6Ly90ZW1wZ28uY3Jvd25jYXN0bGUuY29tL3JzLzM0My1MUVIt +NjUwL2ltYWdlcy9pbWFnZTAxX0NDTG9nbzIucG5nIiBoZWlnaHQ9IjY1IiB3aWR0aD0iMjQ2Ij48 +L3A+DQo8L3RkPg0KPC90cj4NCjx0cj4NCjx0ZCB2YWxpZ249InRvcCI+DQo8cD4mbmJzcDs8L3A+ +DQo8L3RkPg0KPC90cj4NCjx0cj4NCjx0ZCB2YWxpZ249InRvcCI+DQo8cCBzdHlsZT0idGV4dC1h +bGlnbjpjZW50ZXIiPjxiPjxzcGFuIHN0eWxlPSJjb2xvcjojNUE2NzcxO2ZvbnQtc2l6ZToyNHB4 +Ij48dT5NYWludGVuYW5jZSBFdmVudCBTdGF0dXM8L3U+PC9zcGFuPjwvYj48L3A+DQo8L3RkPg0K +PC90cj4NCjx0cj4NCjx0ZD4NCjxwPjwvcD4NCjwvdGQ+DQo8L3RyPg0KPHRyPg0KPHRkPg0KPHA+ +PC9wPg0KPC90ZD4NCjwvdHI+DQo8dHI+DQo8dGQgdmFsaWduPSJ0b3AiPg0KPHA+Jm5ic3A7PC9w +Pg0KPHA+Jm5ic3A7PC9wPg0KPHA+PC9wPg0KPHA+PC9wPg0KPHA+PC9wPg0KPHA+RGVhciBFeGFt +cGxlIEN1c3RvbWVyLCA8YnI+DQo8YnI+DQpUaGlzIG5vdGljZSBpcyBiZWluZyBzZW50IHRvIGlu +Zm9ybSB5b3UgdGhhdCB0aGUgd29yayBhc3NvY2lhdGVkIHdpdGggdG9kYXnigJlzIG1haW50ZW5h +bmNlIHdpbmRvdyBoYXMgYmVlbiBjb21wbGV0ZWQuIFdlIHRoYW5rIHlvdSBmb3IgeW91ciBwYXRp +ZW5jZS4NCjwvcD4NCjxwPipEQVJLIEZJQkVSIENVU1RPTUVSUyBQTEVBU0UgTk9URTogQWxsIGRh +cmsgZmliZXIgc2VydmljZXMgbXVzdCBiZSByZXZpZXdlZCBhbmQgY2hlY2tlZCBmb3IgcmVzdG9y +YXRpb24gYXQgdGhpcyB0aW1lIHRvIGVuc3VyZSBjb25uZWN0aXZpdHkgYWZ0ZXIgdGhpcyBtYWlu +dGVuYW5jZSB3b3JrIHNpbmNlIHdlIGhhdmUgbm93IGNvbXBsZXRlZCB0aGUgZXZlbnQgaW4gaXRz +IGVudGlyZXR5LiBJZiB5b3VyIHNlcnZpY2VzIGhhdmUgbm90IHJlc3RvcmVkDQogcGxlYXNlIGNv +bnRhY3Qgb3VyIENoYW5nZSBDb250cm9sIHN1cHBvcnQgbGluZSBhdCAxLTUwOC02MjEtMTg4OCBp +bW1lZGlhdGVseSB0byB0cm91Ymxlc2hvb3QgYW5kIHJlc29sdmUgdGhlIG1hdHRlci48L3A+DQo8 +cD5JZiB0aGlzIHdhcyBhIG9uZSBkYXkgbWFpbnRlbmFuY2UgZXZlbnQsIHBsZWFzZSBjb25zaWRl +ciB0aGlzIGV2ZW50IGNvbXBsZXRlZC4gSWYgbWFpbnRlbmFuY2Ugd2FzIHNjaGVkdWxlZCBmb3Ig +c2V2ZXJhbCBkYXlzLCBwbGVhc2Ugbm90ZSB0aGUgZXZlbnQgd2lsbCBiZSBvZmZpY2lhbGx5IGNv +bXBsZXRlIG9uIHRoZSBsYXN0IGRheSBvZiB0aGUgZXZlbnQuIElmIHlvdSBoYXZlIHJlY2VpdmVk +IHRoaXMgbm90aWNlIGFuZCB5b3VyIHNlcnZpY2VzDQogaGF2ZSBub3QgcmVzdG9yZWQgcGxlYXNl +IGNvbnRhY3Qgb3VyIENoYW5nZSBDb250cm9sIHN1cHBvcnQgbGluZSBhdCAxLTUwOC02MjEtMTg4 +OC48L3A+DQo8cD48c3Ryb25nPlRpY2tldCBOdW1iZXI6IDwvc3Ryb25nPkNNMjAyMzEyMDEwMDA8 +L3A+DQo8cD48c3Ryb25nPkxvY2F0aW9uIG9mIFdvcms6IDwvc3Ryb25nPkFueXdoZXJlLCBGTDwv +cD4NCjxwPg0KPHRhYmxlIGNsYXNzPSJ0aW1lem9uZWdyaWQiPg0KPHRib2R5Pg0KPHRyPg0KPHRo +IGNvbHNwYW49IjIiPlNjaGVkdWxlZCBTdGFydCBEYXRlICZhbXA7IFRpbWUgPC90aD4NCjx0aCBj +b2xzcGFuPSIyIj5TY2hlZHVsZWQgRW5kIERhdGUgJmFtcDsgVGltZSA8L3RoPg0KPHRoPlRpbWUg +Wm9uZSA8L3RoPg0KPC90cj4NCjx0cj4NCjx0ZD4xMi8yLzIwMjMgPC90ZD4NCjx0ZD4xMDowMCBQ +TSA8L3RkPg0KPHRkPjEyLzMvMjAyMyA8L3RkPg0KPHRkPjY6MDAgQU0gPC90ZD4NCjx0ZD5FYXN0 +ZXJuIDwvdGQ+DQo8L3RyPg0KPHRyPg0KPHRkPjEyLzIvMjAyMyA8L3RkPg0KPHRkPjk6MDAgUE0g +PC90ZD4NCjx0ZD4xMi8zLzIwMjMgPC90ZD4NCjx0ZD41OjAwIEFNIDwvdGQ+DQo8dGQ+Q2VudHJh +bCA8L3RkPg0KPC90cj4NCjx0cj4NCjx0ZD4xMi8yLzIwMjMgPC90ZD4NCjx0ZD44OjAwIFBNIDwv +dGQ+DQo8dGQ+MTIvMy8yMDIzIDwvdGQ+DQo8dGQ+NDowMCBBTSA8L3RkPg0KPHRkPk1vdW50YWlu +IDwvdGQ+DQo8L3RyPg0KPHRyPg0KPHRkPjEyLzIvMjAyMyA8L3RkPg0KPHRkPjc6MDAgUE0gPC90 +ZD4NCjx0ZD4xMi8zLzIwMjMgPC90ZD4NCjx0ZD4zOjAwIEFNIDwvdGQ+DQo8dGQ+UGFjaWZpYyA8 +L3RkPg0KPC90cj4NCjx0cj4NCjx0ZD4xMi8zLzIwMjMgPC90ZD4NCjx0ZD4zOjAwIEFNIDwvdGQ+ +DQo8dGQ+MTIvMy8yMDIzIDwvdGQ+DQo8dGQ+MTE6MDAgQU0gPC90ZD4NCjx0ZD5HTVQgPC90ZD4N +CjwvdHI+DQo8L3Rib2R5Pg0KPC90YWJsZT4NCjwvcD4NCjxwPjxzdHJvbmc+RXhwZWN0ZWQgQ3Vz +dG9tZXIgSW1wYWN0Ojwvc3Ryb25nPiBQbGVhc2UgUmVmZXJlbmNlIFlvdXIgT3JpZ2luYWwgTm90 +aWNlIEJlbG93IEZvciBJbXBhY3Q8L3A+DQo8cD48c3Ryb25nPkV4cGVjdGVkIEltcGFjdCBEdXJh +dGlvbjo8L3N0cm9uZz4gRGl2ZXJzZSBJbXBhY3QgLSBQbGVhc2UgU2VlIEltcGFjdCBwZXIgQ2ly +Y3VpdDwvcD4NCjxwPjwvcD4NCjxwPjwvcD4NCjxwPjxicj4NCjwvcD4NCjxwPjxpPkNoYW5nZSBD +b250cm9sPGJyPg0KRW1haWw6IDxhIGhyZWY9Im1haWx0bzpmaWJlcmNoYW5nZW1nbXRAY3Jvd25j +YXN0bGUuY29tIj48c3BhbiBzdHlsZT0iY29sb3I6IzAwNjZjYzsiPmZpYmVyY2hhbmdlbWdtdEBj +cm93bmNhc3RsZS5jb208L3NwYW4+PC9hPjxicj4NCjEtNTA4LTYyMS0xODg4PC9pPjwvcD4NCjxw +PjxpbWcgd2lkdGg9IjIwOCIgaGVpZ2h0PSI1NSIgc3JjPSJodHRwczovL3RlbXBnby5jcm93bmNh +c3RsZS5jb20vcnMvMzQzLUxRUi02NTAvaW1hZ2VzL2ltYWdlMDFfQ0NMb2dvMi5wbmciPjwvcD4N +CjwvdGQ+DQo8L3RyPg0KPC90Ym9keT4NCjwvdGFibGU+DQo8cD48L3A+DQo8cCBhbGlnbj0iY2Vu +dGVyIj48aT48c3BhbiBzdHlsZT0iY29sb3I6YmxhY2s7Zm9udC1zaXplOjkuNXB4Ij5QbGVhc2Ug +bm90ZTogZXZlcnkgbWFpbnRlbmFuY2UgZW50YWlscyBhIGNlcnRhaW4gbGV2ZWwgb2YgcmlzayBh +bmQgYWx0aG91Z2ggQ3Jvd24gQ2FzdGxlIEZpYmVyIG1ha2VzIGV2ZXJ5IGVmZm9ydCB0byBwcm92 +aWRlIGFjY3VyYXRlIGV4cGVjdGVkIGN1c3RvbWVyIGltcGFjdCwgY29uZGl0aW9ucyBvdXRzaWRl +IENyb3duIENhc3RsZSBGaWJlcidzDQogY29udHJvbCBtYXkgY2F1c2UgaW1wYWN0IHRvIGJlIGdy +ZWF0ZXIgdGhhbiBhbnRpY2lwYXRlZC48L3NwYW4+PC9pPjwvcD4NCjwvZGl2Pg0KVGhpcyBlbWFp +bCBtYXkgY29udGFpbiBjb25maWRlbnRpYWwgb3IgcHJpdmlsZWdlZCBtYXRlcmlhbC4gVXNlIG9y +IGRpc2Nsb3N1cmUgb2YgaXQgYnkgYW55b25lIG90aGVyIHRoYW4gdGhlIHJlY2lwaWVudCBpcyB1 +bmF1dGhvcml6ZWQuIElmIHlvdSBhcmUgbm90IGFuIGludGVuZGVkIHJlY2lwaWVudCwgcGxlYXNl +IGRlbGV0ZSB0aGlzIGVtYWlsLg0KPC9ib2R5Pg0KPC9odG1sPg0K diff --git a/tests/unit/data/crowncastle/crowncastle7_result.json b/tests/unit/data/crowncastle/crowncastle7_result.json new file mode 100644 index 00000000..d9762bb2 --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle7_result.json @@ -0,0 +1,15 @@ +[ + { + "account": "Example Customer", + "circuits": [], + "end": 1701601200, + "maintenance_id": "CM20231201000", + "organizer": "fiberchangemgmt@crowncastle.com", + "provider": "crowncastle", + "sequence": 1, + "stamp": 1702190640, + "start": 1701572400, + "status": "COMPLETED", + "uid": "0" + } +] diff --git a/tests/unit/data/crowncastle/crowncastle8.html b/tests/unit/data/crowncastle/crowncastle8.html new file mode 100644 index 00000000..eb0301bf --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle8.html @@ -0,0 +1,202 @@ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+

+
+

 

+
+

Emergency Notification

+
+

+
+

+
+

 

+

 

+

+

+

+

Dear Example Customer,

+


+This notice is being sent to notify you of the following +Emergency Demand Maintenance Event on the Crown Castle Fiber network. +
+
+

+

+

Ticket Number: CM20231201000

+

Location of Work: Anywhere, FL

+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Scheduled Start Date & Time Scheduled End Date & Time Time Zone
12/13/2023 10:00 PM 12/14/2023 6:00 AM Eastern
12/13/2023 9:00 PM 12/14/2023 5:00 AM Central
12/13/2023 8:00 PM 12/14/2023 4:00 AM Mountain
12/13/2023 7:00 PM 12/14/2023 3:00 AM Pacific
12/14/2023 3:00 AM 12/14/2023 11:00 AM GMT
+

+

Expected Customer Impact: Potential Service Affecting

+

Expected Impact Duration: Please See Below

+

+

Work Description:

+


+

+

A Crown Castle Fiber partner + carrier will be conducting an emergency enclosure repair on their network at the above-listed location. Although no impact is expected, your services listed below might experience short-duration impact due to the need to drain the enclosure, and move the cables + within the enclosure. We apologize for any resulting inconvenience and assure you of our efforts to minimize any service disruption. +

+

+


+Customer Circuits:

+

+ + + + + + + + + + + + + + + + + + + + + +
Circuit IDActive ProductA LocationZ LocationImpactNotes
666666-WAVE-CCFWavelength / DC to DC Connectivity - Inter Market123 Main St, Basement, Anywhere, FL 12345321 Main St, 1st Floor, Anywhere, FL 54321Please See Below
+

+

+

+

+

Dark-Fiber Customers:
+
+Dark Fiber services cannot be monitored by Crown Castle, we are reliant on customer feed back for confirmation that services have restored. To ensure your services have restored, we are requesting that you provide a contact which is both familiar with the service + and would be able to promptly confirm service restoration once the CM is complete. The request for confirmation may be needed after hours, please provide both a name and contact phone number in response to this email. +
+
+If you have any questions or concerns prior to this event, please reply to this notification as soon as possible. +
+
+By responding to this notification in a timely manner, Crown Castle Fiber Change Management can attempt to resolve any potential conflicts that may arise. +
+
+If you have any questions, concerns or issues before, during or after this maintenance window, please contact our Change Management Department at 1-508-621-1888 and reference this ticket number. +
+
+If you have any service/performance related issues after this maintenance window, please contact our Network Operations Center at 1-855-93-FIBER (1-855-933-4237) and reference this ticket number. +

+

+

Thank You,

+

Change Control
+Email: fiberchangemgmt@crowncastle.com
+508-621-1888

+

+

+
+

+

Please note: every maintenance entails a certain level of risk and although Crown Castle Fiber makes every effort to provide accurate expected customer impact, conditions outside Crown + Castle Fiber's control may cause impact to be greater than anticipated.

+
+` This email may contain confidential or privileged material. Use or disclosure of it by anyone other than the recipient is unauthorized. If you are not an intended recipient, please delete this email. + + diff --git a/tests/unit/data/crowncastle/crowncastle8_parser_result.json b/tests/unit/data/crowncastle/crowncastle8_parser_result.json new file mode 100644 index 00000000..50b49a4b --- /dev/null +++ b/tests/unit/data/crowncastle/crowncastle8_parser_result.json @@ -0,0 +1,16 @@ +[ + { + "account": "Example Customer", + "circuits": [ + { + "circuit_id": "666666-WAVE-CCF", + "impact": "OUTAGE" + } + ], + "end": 1702551600, + "maintenance_id": "CM20231201000", + "start": 1702522800, + "status": "CONFIRMED", + "summary": "A Crown Castle Fiber partner carrier will be conducting an emergency enclosure repair on their network at the above-listed location. Although no impact is expected, your services listed below might experience short-duration impact due to the need to drain the enclosure, and move the cables within the enclosure. We apologize for any resulting inconvenience and assure you of our efforts to minimize any service disruption." + } +] diff --git a/tests/unit/data/equinix/equinix6.eml b/tests/unit/data/equinix/equinix6.eml new file mode 100644 index 00000000..a7ae0e7f --- /dev/null +++ b/tests/unit/data/equinix/equinix6.eml @@ -0,0 +1,333 @@ +Delivered-To: foobar@example.com +Date: Tue, 19 Dec 2023 13:54:45 -0800 (PST) +From: Equinix Network Maintenance +Reply-To: +Message-ID: +Subject: CANCELLED - Remedial 3rd Party Dark Fiber OTDR testing between SV2 and SV8 - SV Metro Area Network Maintenance - 19-DEC-2023 [CHG0031000] +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="----=_Part_123455_12375292.1234567" +Precedence: bulk +Auto-Submitted: auto-generated +X-ServiceNow-Generated: true + +------=_Part_123455_12375292.1234567 +Content-Type: multipart/alternative; boundary="----=_Part_123456_54321.1234567" + +------=_Part_123456_54321.1234567 +Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; charset=UTF-8 + + + + + + + + +=C2=A0=C2=A0=C2=A0 +=C2=A0Dear Equinix Customers,=C2=A0 +=C2=A0 +The notification indicating maintenance on the systems below has been cance= +lled. Any follow up or new system maintenance requests will follow our stan= +dard notification protocol. +=C2=A0=C2=A0=C2=A0 + **************************************************************************= +*** +From: Equinix Network Maintenance +Sent: 2023-12-18 23:23:55 +Subject: Remedial 3rd Party Dark Fiber OTDR testing between SV2 and SV8 - S= +V Metro Area Network Maintenance - 19-DEC-2023 [CHG0031000] +=C2=A0=C2=A0=C2=A0 +=C2=A0Dear Equinix Customers,=C2=A0 +=C2=A0 +DATE: 19-DEC-2023 - 19-DEC-2023 + +SPAN: 19-DEC-2023 - 19-DEC-2023 + +LOCAL: TUESDAY, 19 DEC 14:00 - TUESDAY, 19 DEC 18:00 +UTC: TUESDAY, 19 DEC 22:00 - WEDNESDAY, 20 DEC 02:00 + +IBX(s): SV2, SV8 +=C2=A0 +DESCRIPTION: Please be advised that one of our dark fiber providers will be= + performing a planned maintenance activity. + +EXPECTED SERVICE IMPACT:=20 +1. Unprotected Metro Connect (MC) circuits or Metro Remote ports will exper= +ience service interruption. +2. Protected Metro Connect circuits will experience multiple switching hits= + as traffic switches to protection path. +3. One of your Dual Diverse Metro Connect circuits will experience port dow= +ntime of up to 60 Minutes while Redundant circuits will not be affected. +4. Equinix Fabric Metro Remote Port will experience service interruption. +5. Network Edge circuits will experience service interruption. + +EXPECTED DURATION:=20 +1.Up to 60 minutes for Unprotected MC +2.Up to 15 minutes for one of your Dual Diverse MC +3.Up to 15 minutes for Equinix Fabric Metro Remote ports +4.Up to 15 minutes for Network Edge circuits=C2=A0 +=C2=A0 +PRODUCTS: Metro Connect=C2=A0 +=C2=A0 +IMPACT: Loss of redundancy to your service +=C2=A0 +RECOMMENDED ACTION: Please be advised that one of our dark fiber providers = +will be performing a planned maintenance activity. + +EXPECTED SERVICE IMPACT:=20 +1. Unprotected Metro Connect (MC) circuits or Metro Remote ports will exper= +ience service interruption. +2. Protected Metro Connect circuits will experience multiple switching hits= + as traffic switches to protection path. +3. One of your Dual Diverse Metro Connect circuits will experience port dow= +ntime of up to 60 Minutes while Redundant circuits will not be affected. +4. Equinix Fabric Metro Remote Port will experience service interruption. +5. Network Edge circuits will experience service interruption. + +EXPECTED DURATION:=20 +1.Up to 60 minutes for Unprotected MC +2.Up to 15 minutes for one of your Dual Diverse MC +3.Up to 15 minutes for Equinix Fabric Metro Remote ports +4.Up to 15 minutes for Network Edge circuits +=C2=A0 + +Impacted Assets + +Equinix Connect + +Account #ProductIBXService Serial # +555555Equinix ConnectSV82345678 + +=C2=A0 +We apologize for any inconvenience you may experience during this activity.= + Your cooperation and understanding are=C2=A0greatly appreciated. +=C2=A0 +Please do not reply to this email address. If you have any questions or con= +cerns regarding this notification, please log a network ticket via the Equi= +nix Customer Portal or contact Global Service Desk and quote the ticket ref= +erence. +=C2=A0 +Equinix is available to answer questions, provide up-to-date status informa= +tion or additional details regarding the maintenance. Please reference CHG0= +031000.=C2=A0 +=C2=A0=C2=A0=C2=A0 + + + + + + + + + + + + + + +Service Insight | Notification Preferences | Equinix Status Page | Support = +Case=20 + + Glossary of terms | Equinix Customer Portal=20 + + + + + + + + + + + + +Equinix | Legal | Privacy =20 + =E2=80=AF =E2=80=AF =E2=80=AF =20 + + + + + + +------=_Part_123456_54321.1234567 +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset=UTF-8 + +
+ + + + + +
3D"Meet
   
 Dear Equinix Customers, 
&= +nbsp;
The notification indicating maintenance on the systems bel= +ow has been cancelled. Any follow up or new system maintenance requests wil= +l follow our standard notification protocol.
   
 ******************= +***********************************************************
From: Equini= +x Network Maintenance <no-reply@equinix.com>
Sent: 2023-12-18 23:2= +3:55
Subject: Remedial 3rd Party Dark Fiber OTDR testing between SV2 and= + SV8 - SV Metro Area Network Maintenance - 19-DEC-2023 [CHG0031000]
  &n= +bsp;
 Dear Equinix Customers, 
&n= +bsp;
DATE: 19-DEC-2023 - 19-DEC-2023
= +
SPAN: 19-DEC-2023 - 19-DEC-2023

LOCAL: TUESDAY, 1= +9 DEC 14:00 - TUESDAY, 19 DEC 18:00
UTC: TUESDAY, 19 DEC 22:00 - = +WEDNESDAY, 20 DEC 02:00

IBX(s): SV2, SV8
&= +nbsp;
DESCRIPTION: Please be advised that one o= +f our dark fiber providers will be performing a planned maintenance activit= +y. +
+
EXPECTED SERVICE IMPACT:=20 +
1. Unprotected Metro Connect (MC) circuits or Metro Remote ports will e= +xperience service interruption. +
2. Protected Metro Connect circuits will experience multiple switching = +hits as traffic switches to protection path. +
3. One of your Dual Diverse Metro Connect circuits will experience port= + downtime of up to 60 Minutes while Redundant circuits will not be affected= +. +
4. Equinix Fabric Metro Remote Port will experience service interruptio= +n. +
5. Network Edge circuits will experience service interruption. +
+
EXPECTED DURATION:=20 +
1.Up to 60 minutes for Unprotected MC +
2.Up to 15 minutes for one of your Dual Diverse MC +
3.Up to 15 minutes for Equinix Fabric Metro Remote ports +
4.Up to 15 minutes for Network Edge circuits 

 
PRODUCTS: Metro Connect 
 <= +/span>
IMPACT: Loss of redundancy to your service
 
RECOMMENDED ACTION: Please be adv= +ised that one of our dark fiber providers will be performing a planned main= +tenance activity. +
+
EXPECTED SERVICE IMPACT:=20 +
1. Unprotected Metro Connect (MC) circuits or Metro Remote ports will e= +xperience service interruption. +
2. Protected Metro Connect circuits will experience multiple switching = +hits as traffic switches to protection path. +
3. One of your Dual Diverse Metro Connect circuits will experience port= + downtime of up to 60 Minutes while Redundant circuits will not be affected= +. +
4. Equinix Fabric Metro Remote Port will experience service interruptio= +n. +
5. Network Edge circuits will experience service interruption. +
+
EXPECTED DURATION:=20 +
1.Up to 60 minutes for Unprotected MC +
2.Up to 15 minutes for one of your Dual Diverse MC +
3.Up to 15 minutes for Equinix Fabric Metro Remote ports +
4.Up to 15 minutes for Network Edge circuits

 <= +br>
Impacted Assets

Equinix Connec= +t

Account #ProductIBXService Serial #
555= +555Equinix ConnectSV82345678

 
We apologize for any in= +convenience you may experience during this activity. Your cooperation and u= +nderstanding are greatly appreciated.
 
Ple= +ase do not reply to this email address. If you have any questions or concer= +ns regarding this notification, please log a network ticket via the Equinix= + Customer Portal or contact Global Service Desk and quote the ticket refere= +nce.

 
Equinix is available to answer questions,= + provide up-to-date status information or additional details regarding the = +maintenance. Please reference CHG0031000.
 
   
+ + + + + +
+ + + + + + +
+

+

Service Insight | Notification Preferences | Equinix Status Page | Support Case 

+

 Glossary of terms | Equinix Customer Portal 

+

+
+ + + + + + + +
+------=_Part_123456_54321.1234567-- +------=_Part_123455_12375292.1234567-- diff --git a/tests/unit/data/equinix/equinix6_result.json b/tests/unit/data/equinix/equinix6_result.json new file mode 100644 index 00000000..cd5803f8 --- /dev/null +++ b/tests/unit/data/equinix/equinix6_result.json @@ -0,0 +1,13 @@ +[ + { + "account": "555555", + "circuits": [ + { + "circuit_id": "2345678", + "impact": "REDUCED-REDUNDANCY" + } + ], + "end": 1703037600, + "start": 1703023200 + } +] diff --git a/tests/unit/data/equinix/equinix6_result_combined.json b/tests/unit/data/equinix/equinix6_result_combined.json new file mode 100644 index 00000000..b3f68f8a --- /dev/null +++ b/tests/unit/data/equinix/equinix6_result_combined.json @@ -0,0 +1,21 @@ +[ + { + "account": "555555", + "circuits": [ + { + "circuit_id": "2345678", + "impact": "REDUCED-REDUNDANCY" + } + ], + "end": 1703037600, + "maintenance_id": "CHG0031000", + "organizer": "servicedesk@equinix.com", + "provider": "equinix", + "sequence": 1, + "stamp": 1703022885, + "start": 1703023200, + "status": "CANCELLED", + "summary": "CANCELLED - Remedial 3rd Party Dark Fiber OTDR testing between SV2 and SV8 - SV Metro Area Network Maintenance - 19-DEC-2023 [CHG0031000]", + "uid": "0" + } +] diff --git a/tests/unit/data/equinix/equinix7.eml b/tests/unit/data/equinix/equinix7.eml new file mode 100644 index 00000000..1885c937 --- /dev/null +++ b/tests/unit/data/equinix/equinix7.eml @@ -0,0 +1,266 @@ +Date: Thu, 21 Dec 2023 04:16:05 -0800 (PST) +From: Equinix Network Maintenance +Reply-To: +Subject: Scheduled Software Upgrade - AM6, DB2, HE6, ZW1 Network Maintenance - (SERVICE IMPACTING) - 10-JAN-2024 [CHG0031000] +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="----=_Part_111111_2222222.1234567898" +X-ServiceNow-Generated: true + +------=_Part_111111_2222222.1234567898 +Content-Type: multipart/alternative; boundary="----=_Part_111112_17421832.1234567898" + +------=_Part_111112_17421832.1234567898 +Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; charset=UTF-8 + + + + + + + + +=C2=A0=C2=A0=C2=A0 +=C2=A0Dear Equinix Customers,=C2=A0 +=C2=A0 +DATE: 10-JAN-2024 - 11-JAN-2024 + +SPAN: 10-JAN-2024 - 11-JAN-2024 + +UTC: WEDNESDAY, 10 JAN 22:00 - THURSDAY, 11 JAN 06:00 + +(Time Conversion Guide) +=C2=A0 +LOCATION: AM6, DB2, HE6, ZW1=C2=A0 +=C2=A0 +DESCRIPTION: Please be advised that Equinix engineers will be performing a = +planned software upgrade.=20 + +For customers without redundancy configured ports and services a downtime i= +s expected. +Customers with redundant configured services will only experience a loss of= + redundancy. + +This work will impact network ports and virtual services listed in this not= +ification. + +The expected duration would be:=20 +up to 120 minutes in the maintenance window (between 20 to 120 minutes).=C2= +=A0 +=C2=A0 +OUTAGE DURATION: 2 Hours +=C2=A0 +PRODUCTS: Equinix Connect, Equinix Internet Access, Equinix Fabric, Metro C= +onnect=C2=A0 +=C2=A0 +IMPACT: There will be service interruptions +=C2=A0 +RECOMMENDED ACTION: The Equinix platform provides redundancy options. To mi= +tigate the impact of a maintenance event, we recommend customers to setup r= +edundant connections according to best practices. + +No action is required from customers with redundancy. +=C2=A0 + +Impacted Assets + +Equinix Connect + +Account #ProductIBXService Serial # +555555Equinix ConnectDB2EC1234.1 + +=C2=A0 +We apologize for any inconvenience you may experience during this activity.= + Your cooperation and understanding are=C2=A0greatly appreciated. +=C2=A0 +Please do not reply to this email address. If you have any questions or con= +cerns regarding this notification, please log a network ticket via the Equi= +nix Customer Portal or contact Global Service Desk and quote the ticket ref= +erence. +=C2=A0 +Equinix is available to answer questions, provide up-to-date status informa= +tion or additional details regarding the maintenance. Please reference CHG0= +031000.=C2=A0 +=C2=A0=C2=A0=C2=A0 + + + + + + + + + + + + + + +Service Insight | Notification Preferences | Equinix Status Page | Support = +Case=20 + + Glossary of terms | Equinix Customer Portal=20 + + + + + + + + + + + + +Equinix | Legal | Privacy =20 + =E2=80=AF =E2=80=AF =E2=80=AF =20 + + + + + + +------=_Part_111112_17421832.1234567898 +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset=UTF-8 + + + + + + +
+ + + + + +
3D"Meet
   
 Dear Equinix Customers, 
 = +;
DATE: 10-JAN-2024 - 11-JAN-2024
SPAN: 10-JAN-2024 - 11-JAN-2024

UTC: WEDNESDAY, 10 J= +AN 22:00 - THURSDAY, 11 JAN 06:00

(Time Conv= +ersion Guide)
 
LOCATION: A= +M6, DB2, HE6, ZW1 
 
DESCRIPTION: Please be advised that Equinix engineers will be performing a plann= +ed software upgrade.=20 +
+
For customers without redundancy configured ports and services a downti= +me is expected. +
Customers with redundant configured services will only experience a los= +s of redundancy. +
+
This work will impact network ports and virtual services listed in this= + notification. +
+
The expected duration would be:=20 +
up to 120 minutes in the maintenance window (between 20 to 120 minutes)= +. 

 
OUTAGE DURATION: 2 Hours
&= +nbsp;

PRODUCTS: Equinix Connect, Equinix Intern= +et Access, Equinix Fabric, Metro Connect 
 
= +IMPACT: There will be service interruptions
&n= +bsp;
RECOMMENDED ACTION: The Equinix platform p= +rovides redundancy options. To mitigate the impact of a maintenance event, = +we recommend customers to setup redundant connections according to best pra= +ctices. +
+
No action is required from customers with redundancy.

 = +

<= +/tr>
Impacted Assets

Equini= +x Connect

Account #ProductIBXService Serial #
555555Equinix ConnectDB2EC1= +234.1

 
We apologize f= +or any inconvenience you may experience during this activity. Your cooperat= +ion and understanding are greatly appreciated.
 

Please do not reply to this email address. If you have any question= +s or concerns regarding this notification, please log a network ticket via = +the Equinix Customer Portal or contact Global Service Desk and quote the ti= +cket reference.
 
Equinix is available to answer= + questions, provide up-to-date status information or additional details reg= +arding the maintenance. Please reference CHG0031000.
 
&n= +bsp;  
+
+ + + + + + +
+

+

Service Insight | Notification Preferences | Equinix Status Page | Support Case 

+

 Glossary of terms | Equinix Customer Portal 

+

+
+ + + + + + + +
+------=_Part_111112_17421832.1234567898-- +------=_Part_111111_2222222.1234567898-- diff --git a/tests/unit/data/equinix/equinix7_result.json b/tests/unit/data/equinix/equinix7_result.json new file mode 100644 index 00000000..47f688fb --- /dev/null +++ b/tests/unit/data/equinix/equinix7_result.json @@ -0,0 +1,13 @@ +[ + { + "account": "555555", + "circuits": [ + { + "circuit_id": "EC1234.1", + "impact": "OUTAGE" + } + ], + "end": 1704952800, + "start": 1704924000 + } +] diff --git a/tests/unit/data/equinix/equinix7_result_combined.json b/tests/unit/data/equinix/equinix7_result_combined.json new file mode 100644 index 00000000..04889a94 --- /dev/null +++ b/tests/unit/data/equinix/equinix7_result_combined.json @@ -0,0 +1,21 @@ +[ + { + "account": "555555", + "circuits": [ + { + "circuit_id": "EC1234.1", + "impact": "OUTAGE" + } + ], + "end": 1704952800, + "maintenance_id": "CHG0031000", + "organizer": "servicedesk@equinix.com", + "provider": "equinix", + "sequence": 1, + "stamp": 1703160965, + "start": 1704924000, + "status": "CONFIRMED", + "summary": "Scheduled Software Upgrade - AM6, DB2, HE6, ZW1 Network Maintenance - (SERVICE IMPACTING) - 10-JAN-2024 [CHG0031000]", + "uid": "0" + } +] diff --git a/tests/unit/data/equinix/equinix8.eml b/tests/unit/data/equinix/equinix8.eml new file mode 100644 index 00000000..2ff95f96 --- /dev/null +++ b/tests/unit/data/equinix/equinix8.eml @@ -0,0 +1,266 @@ +Date: Thu, 21 Dec 2023 04:16:05 -0800 (PST) +From: Equinix Network Maintenance +Reply-To: +Subject: Scheduled Software Upgrade - AM6, DB2, HE6, ZW1 Network Maintenance - (SERVICE IMPACTING) - 10-JAN-2024 [CHG0031000] +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="----=_Part_111111_2222222.1234567898" +X-ServiceNow-Generated: true + +------=_Part_111111_2222222.1234567898 +Content-Type: multipart/alternative; boundary="----=_Part_111112_17421832.1234567898" + +------=_Part_111112_17421832.1234567898 +Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; charset=UTF-8 + + + + + + + + +=C2=A0=C2=A0=C2=A0 +=C2=A0Dear Equinix Customers,=C2=A0 +=C2=A0 +DATE: 10-JAN-2024 - 11-JAN-2024 + +SPAN: 10-JAN-2024 - 11-JAN-2024 + +UTC: WEDNESDAY, 10 JAN 22:00 - THURSDAY, 11 JAN 06:00 + +(Time Conversion Guide) +=C2=A0 +LOCATION: AM6, DB2, HE6, ZW1=C2=A0 +=C2=A0 +DESCRIPTION: Please be advised that Equinix engineers will be performing a = +planned software upgrade.=20 + +For customers without redundancy configured ports and services a downtime i= +s expected. +Customers with redundant configured services will only experience a loss of= + redundancy. + +This work will impact network ports and virtual services listed in this not= +ification. + +The expected duration would be:=20 +up to 120 minutes in the maintenance window (between 20 to 120 minutes).=C2= +=A0 +=C2=A0 +OUTAGE DURATION: 2 Hours +=C2=A0 +PRODUCTS: Equinix Connect, Equinix Internet Access, Equinix Fabric, Metro C= +onnect=C2=A0 +=C2=A0 +IMPACT: There will be service interruptions +=C2=A0 +RECOMMENDED ACTION: The Equinix platform provides redundancy options. To mi= +tigate the impact of a maintenance event, we recommend customers to setup r= +edundant connections according to best practices. + +No action is required from customers with redundancy. +=C2=A0 + +Impacted Assets + +Equinix Connect + +Account #ProductIBXService Serial # +555555Equinix ConnectDB2EC1234.1 + +=C2=A0 +We apologize for any inconvenience you may experience during this activity.= + Your cooperation and understanding are=C2=A0greatly appreciated. +=C2=A0 +Please do not reply to this email address. If you have any questions or con= +cerns regarding this notification, please log a network ticket via the Equi= +nix Customer Portal or contact Global Service Desk and quote the ticket ref= +erence. +=C2=A0 +Equinix is available to answer questions, provide up-to-date status informa= +tion or additional details regarding the maintenance. Please reference CHG0= +031000.=C2=A0 +=C2=A0=C2=A0=C2=A0 + + + + + + + + + + + + + + +Service Insight | Notification Preferences | Equinix Status Page | Support = +Case=20 + + Glossary of terms | Equinix Customer Portal=20 + + + + + + + + + + + + +Equinix | Legal | Privacy =20 + =E2=80=AF =E2=80=AF =E2=80=AF =20 + + + + + + +------=_Part_111112_17421832.1234567898 +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset=UTF-8 + + + + + + +
+ + + + + +
3D"Meet
   
 Dear Equinix Customers, 
 = +;
DATE: 10-JAN-2024 - 11-JAN-2024
SPAN: 10-JAN-2024 - 11-JAN-2024

UTC: WEDNESDAY, 10 J= +AN 22:00 - THURSDAY, 11 JAN 06:00

(Time Conv= +ersion Guide)
 
LOCATION: A= +M6, DB2, HE6, ZW1 
 
DESCRIPTION: Please be advised that Equinix engineers will be performing a plann= +ed software upgrade.=20 +
+
For customers without redundancy configured ports and services a downti= +me is expected. +
Customers with redundant configured services will only experience a los= +s of redundancy. +
+
This work will impact network ports and virtual services listed in this= + notification. +
+
The expected duration would be:=20 +
up to 120 minutes in the maintenance window (between 20 to 120 minutes)= +. 

 
OUTAGE DURATION: 2 Hours
&= +nbsp;

PRODUCTS: Equinix Connect, Equinix Intern= +et Access, Equinix Fabric, Metro Connect 
 
= +IMPACT:=09There will be se= +rvice interruptions
&n= +bsp;
RECOMMENDED ACTION: The Equinix platform p= +rovides redundancy options. To mitigate the impact of a maintenance event, = +we recommend customers to setup redundant connections according to best pra= +ctices. +
+
No action is required from customers with redundancy.

 = +

<= +/tr>
Impacted Assets

Equini= +x Connect

Account #ProductIBXService Serial #
555555Equinix ConnectDB2EC1= +234.1

 
We apologize f= +or any inconvenience you may experience during this activity. Your cooperat= +ion and understanding are greatly appreciated.
 

Please do not reply to this email address. If you have any question= +s or concerns regarding this notification, please log a network ticket via = +the Equinix Customer Portal or contact Global Service Desk and quote the ti= +cket reference.
 
Equinix is available to answer= + questions, provide up-to-date status information or additional details reg= +arding the maintenance. Please reference CHG0031000.
 
&n= +bsp;  
+
+ + + + + + +
+

+

Service Insight | Notification Preferences | Equinix Status Page | Support Case 

+

 Glossary of terms | Equinix Customer Portal 

+

+
+ + + + + + + +
+------=_Part_111112_17421832.1234567898-- +------=_Part_111111_2222222.1234567898-- diff --git a/tests/unit/data/equinix/equinix8.json b/tests/unit/data/equinix/equinix8.json new file mode 100644 index 00000000..04889a94 --- /dev/null +++ b/tests/unit/data/equinix/equinix8.json @@ -0,0 +1,21 @@ +[ + { + "account": "555555", + "circuits": [ + { + "circuit_id": "EC1234.1", + "impact": "OUTAGE" + } + ], + "end": 1704952800, + "maintenance_id": "CHG0031000", + "organizer": "servicedesk@equinix.com", + "provider": "equinix", + "sequence": 1, + "stamp": 1703160965, + "start": 1704924000, + "status": "CONFIRMED", + "summary": "Scheduled Software Upgrade - AM6, DB2, HE6, ZW1 Network Maintenance - (SERVICE IMPACTING) - 10-JAN-2024 [CHG0031000]", + "uid": "0" + } +] diff --git a/tests/unit/data/equinix/equinix8_result.json b/tests/unit/data/equinix/equinix8_result.json new file mode 100644 index 00000000..47f688fb --- /dev/null +++ b/tests/unit/data/equinix/equinix8_result.json @@ -0,0 +1,13 @@ +[ + { + "account": "555555", + "circuits": [ + { + "circuit_id": "EC1234.1", + "impact": "OUTAGE" + } + ], + "end": 1704952800, + "start": 1704924000 + } +] diff --git a/tests/unit/data/equinix/equinix8_result_combined.json b/tests/unit/data/equinix/equinix8_result_combined.json new file mode 100644 index 00000000..04889a94 --- /dev/null +++ b/tests/unit/data/equinix/equinix8_result_combined.json @@ -0,0 +1,21 @@ +[ + { + "account": "555555", + "circuits": [ + { + "circuit_id": "EC1234.1", + "impact": "OUTAGE" + } + ], + "end": 1704952800, + "maintenance_id": "CHG0031000", + "organizer": "servicedesk@equinix.com", + "provider": "equinix", + "sequence": 1, + "stamp": 1703160965, + "start": 1704924000, + "status": "CONFIRMED", + "summary": "Scheduled Software Upgrade - AM6, DB2, HE6, ZW1 Network Maintenance - (SERVICE IMPACTING) - 10-JAN-2024 [CHG0031000]", + "uid": "0" + } +] diff --git a/tests/unit/data/equinix/equinix9.eml b/tests/unit/data/equinix/equinix9.eml new file mode 100644 index 00000000..477f5a2c --- /dev/null +++ b/tests/unit/data/equinix/equinix9.eml @@ -0,0 +1,326 @@ +Return-Path: +Date: Tue, 7 Nov 2023 05:04:09 +0000 (UTC) +From: Equinix Network Maintenance NO-REPLY +Reply-To: Equinix Network Maintenance NO-REPLY +To: "Equinix Network Maintenance.NL" +Subject: COMPLETED - Scheduled Software Upgrade-AM Metro Area Network Maintenance (SERVICE IMPACTING)-06-NOV-2023 [5-230281950000] +MIME-Version: 1.0 +Content-Type: multipart/alternative; boundary="----=_Part_333333_222222222.111111111111111111" +X-Priority: 1 +Organization: Equinix, Inc + +------=_Part_333333_222222222.111111111111111111 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: quoted-printable + + Geachte Equinix Klant/Dear Equinix Customer, + +De hieronder vermelde onderhoudswerkzaamheden zijn inmiddels afgerond. + +The maintenance listed below has now been completed. + + =20 + +Geachte Equinix klant, + +DATUM:=09=0906-NOV-2023 - 07-NOV-2023 + +TIJDSDUUR:=09=0906-NOV-2023 - 07-NOV-2023 + +LOKALE TIJD:=09=09MAANDAG, 06 NOV 22:00 - DINSDAG, 07 NOV 06:00 +UTC:=09=09MAANDAG, 06 NOV 21:00 - DINSDAG, 07 NOV 05:00 + +IBX(s): AM1,AM2 + +BESCHRIJVING:=09 +Please be advised that Equinix engineers will perform a software upgrade on= + our Equinix Fabric routers.=20 +The Equinix platform provides redundancy options. To mitigate the impact of= + a maintenance event, we recommend customers to setup redundant connections= + according to best practices. + +EXPECTED SERVICE IMPACT:=20 +For customers without redundancy configured ports and services a downtime i= +s expected. Customers with redundant configured services will only experien= +ce a loss of redundancy. +This work will impact network ports and virtual services listed in the Tabl= +e below. + +EXPECTED DURATION:=20 +Up to 60 minutes in the maintenance window (between 20 to 60 minutes) + +PRODUCTEN:EQUINIX CONNECT, EQUINIX FABRIC, EQUINIX INTERNET ACCESS, INTER M= +ETRO CONNECT, METRO CONNECT + +IMPACT:=09Er zullen service onderbrekingen zijn. + + +Equinix Connect =09 =09=09=09 =09=09=09 =09=09=09=09 Account # =09=09=09= +=09 Product =09=09=09=09 IBX =09=09=09=09 Service Serial # =09=09= +=09 =09=09=09=09 =09=09=09=09=09=09 333333 =09=09=09=09=09=09 Equinix Conne= +ct =09=09=09=09=09=09 AM5 =09=09=09=09=09=09 44444.AM1 =09=09=09=09 =09=09= +=09=09 =09=09=09=09=09=09 333333 =09=09=09=09=09=09 Equinix Connect =09=09= +=09=09=09=09 AM5 =09=09=09=09=09=09 66666.AM1 =09=09=09=09 =09=09=09 =09 + + + +Onze excuses voor het eventuele ongemak dat u kunt ervaren tijdens deze act= +iviteit. Uw medewerking en begrip worden zeer gewaardeerd. + +De Equinix SMC is beschikbaar om up-to-date informatie of extra gegevens te= + verschaffen. Mocht u vragen hebben over deze activiteit wilt u dan referer= +en aan 5-230281950685.=20 + +***************************************************************************= +*********** + +Dear Equinix Customer, + +DATE:=09=0906-NOV-2023 - 07-NOV-2023 + +SPAN:=09=0906-NOV-2023 - 07-NOV-2023 + +LOCAL:=09=09MONDAY, 06 NOV 22:00 - TUESDAY, 07 NOV 06:00 +UTC:=09=09MONDAY, 06 NOV 21:00 - TUESDAY, 07 NOV 05:00 + +IBX(s): AM1,AM11,AM3,AM5,EN1 + + +DESCRIPTION: +Please be advised that Equinix engineers will perform a software upgrade on= + our Equinix Fabric routers.=20 +The Equinix platform provides redundancy options. To mitigate the impact of= + a maintenance event, we recommend customers to setup redundant connections= + according to best practices. + +EXPECTED SERVICE IMPACT:=20 +For customers without redundancy configured ports and services a downtime i= +s expected. Customers with redundant configured services will only experien= +ce a loss of redundancy. +This work will impact network ports and virtual services listed in the Tabl= +e below. + +EXPECTED DURATION:=20 +Up to 60 minutes in the maintenance window (between 20 to 60 minutes) + +PRODUCTS:=09EQUINIX CONNECT, EQUINIX FABRIC, EQUINIX INTERNET ACCESS, INTER= + METRO CONNECT, METRO CONNECT + +IMPACT:=09There will be service interruptions. + +=09 +We apologize for any inconvenience you may experience during this activity.= + Your cooperation and understanding are greatly appreciated. + +The Equinix SMC is available to provide up-to-date status information or ad= +ditional details, should you have any questions regarding the maintenance. = + Please reference 5-230281950000.=20 + + +Sincerely,=20 +Equinix SMC=20 +Contacts: +Please do not reply to this email address. If you have any questions or con= +cerns regarding this notification, please log a network ticket via the Equi= +nix Customer Portal, or contact Global Service Desk and quote the the ticke= +t reference [5-230281950000].=20 +To unsubscribe from notifications, please log in to the Equinix Customer Po= +rtal and change your preferences. + +------=_Part_333333_222222222.111111111111111111 +Content-Type: text/html; charset=UTF-8 +Content-Transfer-Encoding: 7bit + + + + + + + + + + + + + + +
+ + + + + +
Meet Equinix
+ +
+
+
+ + + + + + +
  + +
+

+Geachte Equinix Klant/Dear Equinix Customer,
+
+De hieronder vermelde onderhoudswerkzaamheden zijn inmiddels afgerond.
+
+The maintenance listed below has now been completed.
+
+

+
+


+Geachte Equinix klant,
+
+DATUM: 06-NOV-2023 - 07-NOV-2023
+
+TIJDSDUUR: 06-NOV-2023 - 07-NOV-2023
+
+LOKALE TIJD: MAANDAG, 06 NOV 22:00 - DINSDAG, 07 NOV 06:00
+UTC: MAANDAG, 06 NOV 21:00 - DINSDAG, 07 NOV 05:00
+
+IBX(s): AM1,AM11,AM3,AM5,EN1
+
+BESCHRIJVING:
Please be advised that Equinix engineers will perform a software upgrade on our Equinix Fabric routers.
+The Equinix platform provides redundancy options. To mitigate the impact of a maintenance event, we recommend customers to setup redundant connections according to best practices.
+
+EXPECTED SERVICE IMPACT:
+For customers without redundancy configured ports and services a downtime is expected. Customers with redundant configured services will only experience a loss of redundancy.
+This work will impact network ports and virtual services listed in the Table below.
+
+EXPECTED DURATION:
+Up to 60 minutes in the maintenance window (between 20 to 60 minutes)
+
+PRODUCTEN:EQUINIX CONNECT, EQUINIX FABRIC, EQUINIX INTERNET ACCESS, INTER METRO CONNECT, METRO CONNECT
+
+IMPACT: Er zullen service onderbrekingen zijn.
+


+
+ Equinix Connect + + +
+ + + + + + + + + + + + + + + + + + +
Account #ProductIBXService Serial #
333333Equinix ConnectAM544444.AM1
333333Equinix ConnectAM566666.AM1
+
+
+


+Onze excuses voor het eventuele ongemak dat u kunt ervaren tijdens deze activiteit. Uw medewerking en begrip worden zeer gewaardeerd.
+
+De Equinix SMC is beschikbaar om up-to-date informatie of extra gegevens te verschaffen. Mocht u vragen hebben over deze activiteit wilt u dan refereren aan 5-230281950000.
+
+**************************************************************************************
+
+Dear Equinix Customer,
+
+DATE: 06-NOV-2023 - 07-NOV-2023
+
+SPAN: 06-NOV-2023 - 07-NOV-2023
+
+LOCAL: MONDAY, 06 NOV 22:00 - TUESDAY, 07 NOV 06:00
+UTC: MONDAY, 06 NOV 21:00 - TUESDAY, 07 NOV 05:00
+
+IBX(s): AM1,AM11,AM3,AM5,EN1
+
+
+DESCRIPTION:
Please be advised that Equinix engineers will perform a software upgrade on our Equinix Fabric routers.
+The Equinix platform provides redundancy options. To mitigate the impact of a maintenance event, we recommend customers to setup redundant connections according to best practices.
+
+EXPECTED SERVICE IMPACT:
+For customers without redundancy configured ports and services a downtime is expected. Customers with redundant configured services will only experience a loss of redundancy.
+This work will impact network ports and virtual services listed in the Table below.
+
+EXPECTED DURATION:
+Up to 60 minutes in the maintenance window (between 20 to 60 minutes)
+
+PRODUCTS: EQUINIX CONNECT, EQUINIX FABRIC, EQUINIX INTERNET ACCESS, INTER METRO CONNECT, METRO CONNECT
+
+IMPACT: There will be service interruptions.
+

We apologize for any inconvenience you may experience during this activity. Your cooperation and understanding are greatly appreciated.

The Equinix SMC is available to provide up-to-date status information or additional details, should you have any questions regarding the maintenance. Please reference 5-230281950000.
+
+

Sincerely,
Equinix SMC

Contacts:

Please do not reply to this email address. If you have any questions or concerns regarding this notification, please log a network ticket via the Equinix Customer Portal, or contact Global Service Desk and quote the the ticket reference [5-230281950000].


+
To unsubscribe from notifications, please log in to the Equinix Customer Portal and change your preferences.

+
+
+
+
+

 

+
+
+ + + +
Equinix + + + +
How are we doing? Tell Equinix - We're Listening. + +  +
+
 
+
+ + + + + +
+ + + + + + + + + +------=_Part_333333_222222222.111111111111111111-- diff --git a/tests/unit/data/equinix/equinix9_result.json b/tests/unit/data/equinix/equinix9_result.json new file mode 100644 index 00000000..2824641e --- /dev/null +++ b/tests/unit/data/equinix/equinix9_result.json @@ -0,0 +1,17 @@ +[ + { + "account": "333333", + "circuits": [ + { + "circuit_id": "44444.AM1", + "impact": "OUTAGE" + }, + { + "circuit_id": "66666.AM1", + "impact": "OUTAGE" + } + ], + "end": 1699333200, + "start": 1699304400 + } +] diff --git a/tests/unit/data/equinix/equinix9_result_combined.json b/tests/unit/data/equinix/equinix9_result_combined.json new file mode 100644 index 00000000..70a06f9a --- /dev/null +++ b/tests/unit/data/equinix/equinix9_result_combined.json @@ -0,0 +1,25 @@ +[ + { + "account": "333333", + "circuits": [ + { + "circuit_id": "44444.AM1", + "impact": "OUTAGE" + }, + { + "circuit_id": "66666.AM1", + "impact": "OUTAGE" + } + ], + "end": 1699333200, + "maintenance_id": "5-230281950000", + "organizer": "servicedesk@equinix.com", + "provider": "equinix", + "sequence": 1, + "stamp": 1699333449, + "start": 1699304400, + "status": "COMPLETED", + "summary": "COMPLETED - Scheduled Software Upgrade-AM Metro Area Network Maintenance (SERVICE IMPACTING)-06-NOV-2023 [5-230281950000]", + "uid": "0" + } +] diff --git a/tests/unit/data/google/google1.html b/tests/unit/data/google/google1.html new file mode 100644 index 00000000..9990a28e --- /dev/null +++ b/tests/unit/data/google/google1.html @@ -0,0 +1,64 @@ +
+
Google Network Maintenance Notification - Reference PCR/123456
+
+ +
+ This message was sent to: [sample@example.net]
+ This address was selected from the Google ISP Portal. You can update your preferences by navigating to "Configuration > Contacts" and adjusting Email Subscriptions for each user. You can find additional details about Contacts here. +
+ +
+
+ + Details:
+ Start Time: 2023-12-05 20:55:00 +0000 UTC
+ End Time: 2023-12-06 05:05:00 +0000 UTC
+ Duration: 8h10m0s
+ + + + + + =20 + + + + + =20 + + + + + =20 +
Google ASN:15169Peer ASN:65001
Google Neighbor Address(es):192.0.2.1Peer Neighbor Address(es):192.0.2.0
Google Neighbor Address(es):2001:db8::1Peer Neighbor Address(es):2001:db8::
+ +

+ Google systems will automatically re-route the traffic away from the peering link(s) under maintenance. +

+

+ Depending on the nature of the work, your BGP session with Google may stay up through the maintenance window. Traffic may shift to alternate sessions with Google and/or your upstream provider(s). +

+

+ Questions about this event can be sent to noc@google.com. +

+

+ Thank you for peering with Google!
+

+
+
+
+ PRIVILEGE AND CONFIDENTIALITY NOTICE: The information in this + e-mail communication and any attached documents may be privileged, + confidential or otherwise protected from disclosure and is intended only + for the use of the designated recipient(s). If the reader is neither the + intended recipient nor an employee or agent thereof who is responsible + for delivering it to the intended recipient, you are hereby notified + that any review, dissemination, distribution, copying or other use of + this communication is strictly prohibited. If you have received this + communication in error, please immediately notify us by return e-mail + and promptly delete the original electronic e-mail communication and + any attached documentation. Receipt by anyone other than the intended + recipient is not a waiver of any attorney-client or work-product + privilege. +
+
diff --git a/tests/unit/data/google/google1_parser_result.json b/tests/unit/data/google/google1_parser_result.json new file mode 100644 index 00000000..a7d84b51 --- /dev/null +++ b/tests/unit/data/google/google1_parser_result.json @@ -0,0 +1,20 @@ +[ + { + "account": "65001", + "circuits": [ + { + "circuit_id": "192.0.2.1-192.0.2.0", + "impact": "OUTAGE" + }, + { + "circuit_id": "2001:db8::1-2001:db8::", + "impact": "OUTAGE" + } + ], + "end": 1701839100, + "maintenance_id": "PCR/123456", + "start": 1701809700, + "status": "CONFIRMED", + "summary": "Network Maintenance Notification - Reference PCR/123456" + } +] diff --git a/tests/unit/data/google/google2.eml b/tests/unit/data/google/google2.eml new file mode 100644 index 00000000..5e8c58cb --- /dev/null +++ b/tests/unit/data/google/google2.eml @@ -0,0 +1,157 @@ +Date: Wed, 06 Dec 2023 00:40:05 +0000 +Subject: [Scheduled] Google Planned Network Maintenance Notification - Reference PCR/123456 +From: Google NOC +Content-Type: multipart/alternative; boundary="000000000000a556e4060bffffff" + +--000000000000a556e4060bffffff +Content-Type: text/plain; charset="UTF-8"; format=flowed; delsp=yes + +Google Network Maintenance Notification - Reference PCR/123456 + + +This message was sent to: [sample@example.net] +This address was selected from the Google ISP Portal. You can update your +preferences by navigating to "Configuration > Contacts" and adjusting Email +Subscriptions for each user. You can find additional details about Contacts +here. + + +Details: +Start Time: 2023-12-05 20:55:00 +0000 UTC +End Time: 2023-12-06 05:05:00 +0000 UTC +Duration: 8h10m0s + + +Google ASN: 15169 Peer ASN: 65001 + +Google Neighbor Address(es): 192.0.2.1 Peer Neighbor Address(es): +192.0.2.0 + +Google Neighbor Address(es): 2001:db8::1 Peer Neighbor +Address(es): 2001:db8:: + + +Google systems will automatically re-route the traffic away from the +peering link(s) under maintenance. + +Depending on the nature of the work, your BGP session with Google may stay +up through the maintenance window. Traffic may shift to alternate sessions +with Google and/or your upstream provider(s). + +Questions about this event can be sent to noc@google.com. + +Thank you for peering with Google! + + + +PRIVILEGE AND CONFIDENTIALITY NOTICE: The information in this e-mail +communication and any attached documents may be privileged, confidential or +otherwise protected from disclosure and is intended only for the use of the +designated recipient(s). If the reader is neither the intended recipient +nor an employee or agent thereof who is responsible for delivering it to +the intended recipient, you are hereby notified that any review, +dissemination, distribution, copying or other use of this communication is +strictly prohibited. If you have received this communication in error, +please immediately notify us by return e-mail and promptly delete the +original electronic e-mail communication and any attached documentation. +Receipt by anyone other than the intended recipient is not a waiver of any +attorney-client or work-product privilege. + + +--000000000000a556e4060bffffff +Content-Type: text/html; charset="UTF-8" +Content-Transfer-Encoding: quoted-printable + + +
+
Google Network Maintenance No= +tification - Reference PCR/123456
+
+ =20 +
+ This message was sent to: [sample@example.net]
+ This address was selected from the Google ISP Portal. You can update your preferences by navigating to "C= +onfiguration > Contacts" and adjusting Email Subscriptions for each user. Y= +ou can find additional details about Contacts here. +
+ +
+
+ =20 + Details:
+ Start Time: 2023-12-05 20:55:00= + +0000 UTC
+ End Time: 2023-12-06 05:05:00 &= +#43;0000 UTC
+ Duration: 8h10m0s
+ + + + + =20 + + + + + =20 + + + + + =20 +
Google ASN:151= +69Peer ASN:65001<= +/td> +
Google Neighbor Address(es):192.0.2.1Peer Neighbor Address(es):192.0.2.0
Google Neighbor Address(es):2001:db8::1Peer Neighbor Address(es):2001:db8::
+ +

+ Google systems will automatically re-route the traffic away from the peer= +ing link(s) under maintenance. +

+

+ Depending on the nature of the work, your BGP session with Google may sta= +y up through the maintenance window. Traffic may shift to alternate session= +s with Google and/or your upstream provider(s). +

+

+ Questions about this event can be sent to noc@google.com. +

+

+ Thank you for peering with Google!
+

+
+
+
+ PRIVILEGE AND CONFIDENTIALITY NOTICE: The information in this + e-mail communication and any attached documents may be privileged, + confidential or otherwise protected from disclosure and is intended onl= +y + for the use of the designated recipient(s). If the reader is neither th= +e + intended recipient nor an employee or agent thereof who is responsible + for delivering it to the intended recipient, you are hereby notified + that any review, dissemination, distribution, copying or other use of + this communication is strictly prohibited. If you have received this + communication in error, please immediately notify us by return e-mail + and promptly delete the original electronic e-mail communication and + any attached documentation. Receipt by anyone other than the intended + recipient is not a waiver of any attorney-client or work-product + privilege. +
+
+ +--000000000000a556e4060bffffff-- diff --git a/tests/unit/data/google/google2_result.json b/tests/unit/data/google/google2_result.json new file mode 100644 index 00000000..ce194ea5 --- /dev/null +++ b/tests/unit/data/google/google2_result.json @@ -0,0 +1,25 @@ +[ + { + "account": "65001", + "circuits": [ + { + "circuit_id": "192.0.2.1-192.0.2.0", + "impact": "OUTAGE" + }, + { + "circuit_id": "2001:db8::1-2001:db8::", + "impact": "OUTAGE" + } + ], + "end": 1701839100, + "maintenance_id": "PCR/123456", + "organizer": "noc-noreply@google.com", + "provider": "google", + "sequence": 1, + "stamp": 1701823205, + "start": 1701809700, + "status": "CONFIRMED", + "summary": "Network Maintenance Notification - Reference PCR/123456", + "uid": "0" + } +] diff --git a/tests/unit/data/netflix/netflix1.eml b/tests/unit/data/netflix/netflix1.eml new file mode 100644 index 00000000..03a19543 --- /dev/null +++ b/tests/unit/data/netflix/netflix1.eml @@ -0,0 +1,35 @@ +From: cdnetops@saasmail.netflix.com +Reply-To: cdnetops@netflix.com +To: noc@example.com +Subject: Dry Run Notification- Netflix Open Connect - Scheduled Maintenance - Edgeconnex LAS01 - 2024-01-31 18:00:00+00:00 UTC +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 7bit +Date: Fri, 22 Dec 2023 18:14:04 +0000 + +Example.com (AS65000), + + Netflix (AS2906) will be performing scheduled maintenance in Edgeconnex LAS01, at 2024-01-31 18:00:00+00:00 UTC. + + Traffic will drain away onto redundant equipment prior to the start of the maintenance. + + After approximately 1 hour of draining, you will see BGP sessions and associated interfaces flap. + + Expected downtime will be approximately 60 minutes. Traffic will be restored shortly thereafter. + + Please do not shut down any links or BGP sessions during this downtime. + + Your IPs: + 192.0.2.1 + 2001:db8::1 + + Please note, if you do IRR filtering, that we often update our prefixes. + + Our prefix set is RS-NETFLIX and our as-set is AS-NFLX. + + Please be sure to accept advertisements that originate from ASNs: 2906, 40027, and 55095. + + Thank you, + + Netflix Open Connect + diff --git a/tests/unit/data/netflix/netflix1_result.json b/tests/unit/data/netflix/netflix1_result.json new file mode 100644 index 00000000..6786cf82 --- /dev/null +++ b/tests/unit/data/netflix/netflix1_result.json @@ -0,0 +1,24 @@ +[ + { + "account": "AS65000", + "circuits": [ + { + "circuit_id": "192.0.2.1", + "impact": "OUTAGE" + }, + { + "circuit_id": "2001:db8::1", + "impact": "OUTAGE" + } + ], + "end": 1706727600, + "maintenance_id": "65160ee397421aab53c9b3486fc425ea", + "organizer": "cdnetops@netflix.com", + "sequence": 1, + "stamp": 1703268844, + "start": 1706724000, + "status": "CONFIRMED", + "summary": "Netflix (AS2906) will be performing scheduled maintenance in Edgeconnex LAS01, at 2024-01-31 18:00:00+00:00 UTC.", + "uid": "0" + } +] diff --git a/tests/unit/data/netflix/netflix1_text_parser_result.json b/tests/unit/data/netflix/netflix1_text_parser_result.json new file mode 100644 index 00000000..aa433753 --- /dev/null +++ b/tests/unit/data/netflix/netflix1_text_parser_result.json @@ -0,0 +1,20 @@ +[ + { + "account": "AS65000", + "circuits": [ + { + "circuit_id": "192.0.2.1", + "impact": "OUTAGE" + }, + { + "circuit_id": "2001:db8::1", + "impact": "OUTAGE" + } + ], + "end": 1706727600, + "maintenance_id": "65160ee397421aab53c9b3486fc425ea", + "start": 1706724000, + "status": "CONFIRMED", + "summary": "Netflix (AS2906) will be performing scheduled maintenance in Edgeconnex LAS01, at 2024-01-31 18:00:00+00:00 UTC." + } +] diff --git a/tests/unit/data/netflix/netflix2.eml b/tests/unit/data/netflix/netflix2.eml new file mode 100644 index 00000000..3c99a722 --- /dev/null +++ b/tests/unit/data/netflix/netflix2.eml @@ -0,0 +1,35 @@ +From: cdnetops@saasmail.netflix.com +Reply-To: cdnetops@netflix.com +To: noc@example.com +Subject: Dry Run Notification- Netflix Open Connect - Scheduled Maintenance - Edgeconnex LAS01 - 2024-01-31 18:00:00+00:00 UTC +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 7bit +Date: Fri, 22 Dec 2023 18:14:04 +0000 + +Example.com (AS65000), + + Netflix (AS2906) will be performing scheduled maintenance in Edgeconnex LAS01, at 2024-01-31 18:00:00+00:00 UTC. + + Traffic will drain away onto redundant equipment prior to the start of the maintenance. + + After approximately 1 hour of draining, you will see BGP sessions and associated interfaces flap. + + Expected downtime will be approximately 2 hours. Traffic will be restored shortly thereafter. + + Please do not shut down any links or BGP sessions during this downtime. + + Your IPs: + 192.0.2.1 + 2001:db8::1 + + Please note, if you do IRR filtering, that we often update our prefixes. + + Our prefix set is RS-NETFLIX and our as-set is AS-NFLX. + + Please be sure to accept advertisements that originate from ASNs: 2906, 40027, and 55095. + + Thank you, + + Netflix Open Connect + diff --git a/tests/unit/data/netflix/netflix2_result.json b/tests/unit/data/netflix/netflix2_result.json new file mode 100644 index 00000000..2a3d5f44 --- /dev/null +++ b/tests/unit/data/netflix/netflix2_result.json @@ -0,0 +1,24 @@ +[ + { + "account": "AS65000", + "circuits": [ + { + "circuit_id": "192.0.2.1", + "impact": "OUTAGE" + }, + { + "circuit_id": "2001:db8::1", + "impact": "OUTAGE" + } + ], + "end": 1706731200, + "maintenance_id": "4a171c65729e191ab8fc3cdd49bc5ea2", + "organizer": "cdnetops@netflix.com", + "sequence": 1, + "stamp": 1703268844, + "start": 1706724000, + "status": "CONFIRMED", + "summary": "Netflix (AS2906) will be performing scheduled maintenance in Edgeconnex LAS01, at 2024-01-31 18:00:00+00:00 UTC.", + "uid": "0" + } +] diff --git a/tests/unit/data/netflix/netflix2_text_parser_result.json b/tests/unit/data/netflix/netflix2_text_parser_result.json new file mode 100644 index 00000000..4c81a197 --- /dev/null +++ b/tests/unit/data/netflix/netflix2_text_parser_result.json @@ -0,0 +1,20 @@ +[ + { + "account": "AS65000", + "circuits": [ + { + "circuit_id": "192.0.2.1", + "impact": "OUTAGE" + }, + { + "circuit_id": "2001:db8::1", + "impact": "OUTAGE" + } + ], + "end": 1706731200, + "maintenance_id": "4a171c65729e191ab8fc3cdd49bc5ea2", + "start": 1706724000, + "status": "CONFIRMED", + "summary": "Netflix (AS2906) will be performing scheduled maintenance in Edgeconnex LAS01, at 2024-01-31 18:00:00+00:00 UTC." + } +] diff --git a/tests/unit/test_e2e.py b/tests/unit/test_e2e.py index 0a19a759..2498de3b 100644 --- a/tests/unit/test_e2e.py +++ b/tests/unit/test_e2e.py @@ -19,11 +19,14 @@ BSO, Cogent, Colt, + CrownCastle, EUNetworks, + Google, GTT, HGC, Lumen, Megaport, + Netflix, NTT, Momentum, PacketFabric, @@ -102,6 +105,15 @@ Path(dir_path, "data", "aws", "aws2_result.json"), ], ), + ( + AWS, + [ + ("email", Path(dir_path, "data", "aws", "aws3.eml")), + ], + [ + Path(dir_path, "data", "aws", "aws3_result.json"), + ], + ), # BSO ( BSO, @@ -249,6 +261,21 @@ Path(dir_path, "data", "colt", "colt7_result.json"), ], ), + # Crown Castle + ( + CrownCastle, + [ + ("email", Path(dir_path, "data", "crowncastle", "crowncastle1.eml")), + ], + [Path(dir_path, "data", "crowncastle", "crowncastle1_result.json")], + ), + ( + CrownCastle, + [ + ("email", Path(dir_path, "data", "crowncastle", "crowncastle7.eml")), + ], + [Path(dir_path, "data", "crowncastle", "crowncastle7_result.json")], + ), # Equinix ( Equinix, @@ -270,6 +297,26 @@ [("email", Path(dir_path, "data", "equinix", "equinix5.eml"))], [Path(dir_path, "data", "equinix", "equinix5_result_combined.json")], ), + ( + Equinix, + [("email", Path(dir_path, "data", "equinix", "equinix6.eml"))], + [Path(dir_path, "data", "equinix", "equinix6_result_combined.json")], + ), + ( + Equinix, + [("email", Path(dir_path, "data", "equinix", "equinix7.eml"))], + [Path(dir_path, "data", "equinix", "equinix7_result_combined.json")], + ), + ( + Equinix, + [("email", Path(dir_path, "data", "equinix", "equinix8.eml"))], + [Path(dir_path, "data", "equinix", "equinix8_result_combined.json")], + ), + ( + Equinix, + [("email", Path(dir_path, "data", "equinix", "equinix9.eml"))], + [Path(dir_path, "data", "equinix", "equinix9_result_combined.json")], + ), # EUNetworks ( EUNetworks, @@ -374,6 +421,16 @@ Path(dir_path, "data", "hgc", "hgc2_result.json"), ], ), + # Google + ( + Google, + [ + ("email", Path(dir_path, "data", "google", "google2.eml")), + ], + [ + Path(dir_path, "data", "google", "google2_result.json"), + ], + ), # Lumen ( Lumen, @@ -503,6 +560,25 @@ Path(dir_path, "data", "momentum", "momentum1_result.json"), ], ), + # Netflix + ( + Netflix, + [ + ("email", Path(dir_path, "data", "netflix", "netflix1.eml")), + ], + [ + Path(dir_path, "data", "netflix", "netflix1_result.json"), + ], + ), + ( + Netflix, + [ + ("email", Path(dir_path, "data", "netflix", "netflix2.eml")), + ], + [ + Path(dir_path, "data", "netflix", "netflix2_result.json"), + ], + ), # NTT ( NTT, @@ -838,7 +914,6 @@ def test_provider_get_maintenances( extended_data = provider_class.get_extended_data() default_maintenance_data = {"uid": "0", "sequence": 1, "summary": ""} extended_data.update(default_maintenance_data) - data = None for data_type, data_file in test_data_files: with open(data_file, "rb") as file_obj: @@ -891,8 +966,7 @@ def test_provider_get_maintenances( """\ Failed creating Maintenance notification for GenericProvider. Details: -- Processor SimpleProcessor from GenericProvider failed due to: 1 validation error for Maintenance\naccount\n field required (type=value_error.missing) -""", +- Processor SimpleProcessor from GenericProvider failed due to: 1 validation error for Maintenance\naccount\n Field required""", ), ( GenericProvider, @@ -904,8 +978,7 @@ def test_provider_get_maintenances( Details: - Processor SimpleProcessor from GenericProvider failed due to: 1 validation error for Maintenance maintenance_id - field required (type=value_error.missing) -""", + Field required""", ), ( GenericProvider, @@ -945,13 +1018,11 @@ def test_provider_get_maintenances( "ical", Path(dir_path, "data", "ical", "ical_no_account"), ProviderError, - """\ -Failed creating Maintenance notification for Telstra. -Details: -- Processor SimpleProcessor from Telstra failed due to: 1 validation error for Maintenance\naccount\n field required (type=value_error.missing) -- Processor CombinedProcessor from Telstra failed due to: None of the supported parsers for processor CombinedProcessor (EmailDateParser, HtmlParserTelstra2) was matching any of the provided data types (ical). -- Processor CombinedProcessor from Telstra failed due to: None of the supported parsers for processor CombinedProcessor (EmailDateParser, HtmlParserTelstra1) was matching any of the provided data types (ical). -""", + [ + "- Processor SimpleProcessor from Telstra failed due to: 1 validation error for Maintenance\naccount\n Field required", + "- Processor CombinedProcessor from Telstra failed due to: None of the supported parsers for processor CombinedProcessor (EmailDateParser, HtmlParserTelstra2) was matching any of the provided data types (ical).", + "- Processor CombinedProcessor from Telstra failed due to: None of the supported parsers for processor CombinedProcessor (EmailDateParser, HtmlParserTelstra1) was matching any of the provided data types (ical).", + ], ), # Zayo ( @@ -964,8 +1035,7 @@ def test_provider_get_maintenances( Details: - Processor CombinedProcessor from Zayo failed due to: 1 validation error for Maintenance maintenance_id - String is empty or 'None' (type=value_error) -""", + Value error, String is empty or 'None'""", ), ( Zayo, @@ -998,5 +1068,10 @@ def test_errored_provider_process(provider_class, data_type, data_file, exceptio with pytest.raises(exception) as exc: provider_class().get_maintenances(data) - assert len(exc.value.related_exceptions) == len(provider_class._processors) # pylint: disable=protected-access - assert str(exc.value) == error_message + assert len(exc.value.related_exceptions) == len( + provider_class.get_default_processors() + ) # pylint: disable=protected-access + if isinstance(error_message, str): + error_message = [error_message] + for item in error_message: + assert item in str(exc.value) diff --git a/tests/unit/test_parsers.py b/tests/unit/test_parsers.py index 0ee25efa..2ddd7a02 100644 --- a/tests/unit/test_parsers.py +++ b/tests/unit/test_parsers.py @@ -12,12 +12,15 @@ from circuit_maintenance_parser.parsers.bso import HtmlParserBSO1 from circuit_maintenance_parser.parsers.cogent import HtmlParserCogent1 from circuit_maintenance_parser.parsers.colt import CsvParserColt1, SubjectParserColt1, SubjectParserColt2 +from circuit_maintenance_parser.parsers.crowncastle import HtmlParserCrownCastle1 from circuit_maintenance_parser.parsers.equinix import HtmlParserEquinix, SubjectParserEquinix +from circuit_maintenance_parser.parsers.google import HtmlParserGoogle1 from circuit_maintenance_parser.parsers.gtt import HtmlParserGTT1 from circuit_maintenance_parser.parsers.hgc import HtmlParserHGC1, HtmlParserHGC2 from circuit_maintenance_parser.parsers.lumen import HtmlParserLumen1 from circuit_maintenance_parser.parsers.megaport import HtmlParserMegaport1 from circuit_maintenance_parser.parsers.momentum import HtmlParserMomentum1 +from circuit_maintenance_parser.parsers.netflix import TextParserNetflix1 from circuit_maintenance_parser.parsers.seaborn import ( HtmlParserSeaborn1, HtmlParserSeaborn2, @@ -34,6 +37,20 @@ dir_path = os.path.dirname(os.path.realpath(__file__)) +# TODO: As a future breaking change, the output could be fully serializable by default +# Currently, the object contains internal objects (e.g., CircuitImpact) that can't be +# converted into JSON without this encoder. +class NestedEncoder(json.JSONEncoder): + """Helper Class to encode to JSON recursively calling custom methods.""" + + def default(self, o): + """Expected method to serialize to JSON.""" + if hasattr(o, "to_json"): + return o.to_json() + + return json.JSONEncoder.default(self, o) + + @pytest.mark.parametrize( "parser_class, raw_file, results_file", [ @@ -105,6 +122,11 @@ Path(dir_path, "data", "aws", "aws2.eml"), Path(dir_path, "data", "aws", "aws2_subject_parser_result.json"), ), + ( + TextParserAWS1, + Path(dir_path, "data", "aws", "aws3.eml"), + Path(dir_path, "data", "aws", "aws3_text_parser_result.json"), + ), # BSO ( HtmlParserBSO1, @@ -213,6 +235,37 @@ Path(dir_path, "data", "colt", "colt7.eml"), Path(dir_path, "data", "colt", "colt7_subject_parser_1_result.json"), ), + # Crown Castle Fiber + ( + HtmlParserCrownCastle1, + Path(dir_path, "data", "crowncastle", "crowncastle2.html"), + Path(dir_path, "data", "crowncastle", "crowncastle2_parser_result.json"), + ), + ( + HtmlParserCrownCastle1, + Path(dir_path, "data", "crowncastle", "crowncastle3.html"), + Path(dir_path, "data", "crowncastle", "crowncastle3_parser_result.json"), + ), + ( + HtmlParserCrownCastle1, + Path(dir_path, "data", "crowncastle", "crowncastle4.html"), + Path(dir_path, "data", "crowncastle", "crowncastle4_parser_result.json"), + ), + ( + HtmlParserCrownCastle1, + Path(dir_path, "data", "crowncastle", "crowncastle5.html"), + Path(dir_path, "data", "crowncastle", "crowncastle5_parser_result.json"), + ), + ( + HtmlParserCrownCastle1, + Path(dir_path, "data", "crowncastle", "crowncastle6.html"), + Path(dir_path, "data", "crowncastle", "crowncastle6_parser_result.json"), + ), + ( + HtmlParserCrownCastle1, + Path(dir_path, "data", "crowncastle", "crowncastle8.html"), + Path(dir_path, "data", "crowncastle", "crowncastle8_parser_result.json"), + ), # Equinix ( HtmlParserEquinix, @@ -239,6 +292,32 @@ Path(dir_path, "data", "equinix", "equinix5.eml"), Path(dir_path, "data", "equinix", "equinix5_result.json"), ), + ( + HtmlParserEquinix, + Path(dir_path, "data", "equinix", "equinix6.eml"), + Path(dir_path, "data", "equinix", "equinix6_result.json"), + ), + ( + HtmlParserEquinix, + Path(dir_path, "data", "equinix", "equinix7.eml"), + Path(dir_path, "data", "equinix", "equinix7_result.json"), + ), + ( + HtmlParserEquinix, + Path(dir_path, "data", "equinix", "equinix8.eml"), + Path(dir_path, "data", "equinix", "equinix8_result.json"), + ), + ( + HtmlParserEquinix, + Path(dir_path, "data", "equinix", "equinix9.eml"), + Path(dir_path, "data", "equinix", "equinix9_result.json"), + ), + # Google + ( + HtmlParserGoogle1, + Path(dir_path, "data", "google", "google1.html"), + Path(dir_path, "data", "google", "google1_parser_result.json"), + ), # GTT ( HtmlParserGTT1, @@ -344,6 +423,17 @@ Path(dir_path, "data", "momentum", "momentum1.eml"), Path(dir_path, "data", "momentum", "momentum1_html_parser_result.json"), ), + # Netflix + ( + TextParserNetflix1, + Path(dir_path, "data", "netflix", "netflix1.eml"), + Path(dir_path, "data", "netflix", "netflix1_text_parser_result.json"), + ), + ( + TextParserNetflix1, + Path(dir_path, "data", "netflix", "netflix2.eml"), + Path(dir_path, "data", "netflix", "netflix2_text_parser_result.json"), + ), # NTT ( ICal, @@ -551,6 +641,8 @@ def test_parsers(parser_class, raw_file, results_file): parsed_notifications = parser_class().parse(raw_data, parser_class.get_data_types()[0]) + parsed_notifications = json.loads(json.dumps(parsed_notifications, cls=NestedEncoder)) + with open(results_file, encoding="utf-8") as res_file: expected_result = json.load(res_file) @@ -564,4 +656,4 @@ def test_parsers(parser_class, raw_file, results_file): def test_parser_no_data(parser_class): """Test parser with no data.""" with pytest.raises(ParserError): - parser_class().parse(b"", parser_class._data_types[0]) # pylint: disable=protected-access + parser_class().parse(b"", parser_class.get_data_types()[0]) # pylint: disable=protected-access diff --git a/tests/unit/test_processors.py b/tests/unit/test_processors.py index 7b8a75ab..a241e5e0 100644 --- a/tests/unit/test_processors.py +++ b/tests/unit/test_processors.py @@ -3,7 +3,7 @@ from unittest.mock import patch import pytest -from pydantic.error_wrappers import ValidationError +from pydantic import ValidationError from circuit_maintenance_parser.output import Maintenance, Metadata from circuit_maintenance_parser.processor import CombinedProcessor, SimpleProcessor @@ -139,9 +139,8 @@ def test_combinedprocessor(): def test_combinedprocessor_missing_data(): """Tests CombinedProcessor when there is not enough info to create a Maintenance.""" processor = CombinedProcessor(data_parsers=[FakeParser0, FakeParser1]) - - with patch("circuit_maintenance_parser.processor.Maintenance") as mock_maintenance: - mock_maintenance.side_effect = ValidationError(errors=["whatever"], model=Maintenance) + with patch("circuit_maintenance_parser.processor.Maintenance.validate_status_type") as mock_maintenance: + mock_maintenance.side_effect = lambda v: ValidationError(errors=["whatever"], model=Maintenance) with pytest.raises(ProcessorError) as e_info: # Using the fake_data that returns mutliple maintenances that are not expected in this processor type processor.process(fake_data_for_combined, EXTENDED_DATA)