diff --git a/circuit_maintenance_parser/__init__.py b/circuit_maintenance_parser/__init__.py index 0d7dc878..2b5e97f9 100644 --- a/circuit_maintenance_parser/__init__.py +++ b/circuit_maintenance_parser/__init__.py @@ -31,6 +31,7 @@ PacketFabric, Seaborn, Sparkle, + SummitIG, Tata, Telia, Telstra, @@ -66,6 +67,7 @@ PCCW, Seaborn, Sparkle, + SummitIG, Tata, Telia, Telstra, diff --git a/circuit_maintenance_parser/parsers/summitig.py b/circuit_maintenance_parser/parsers/summitig.py new file mode 100644 index 00000000..3e2d4f26 --- /dev/null +++ b/circuit_maintenance_parser/parsers/summitig.py @@ -0,0 +1,140 @@ +"""SummitIG Parser.""" + +import re +from datetime import datetime +from typing import Any, ClassVar, Dict, List +from zoneinfo import ZoneInfo + +from bs4.element import ResultSet # type: ignore + +from circuit_maintenance_parser.parser import CircuitImpact, Html, Impact, Status + + +class HtmlParserSummitIG(Html): + """Custom parser for SummitIG HTML circuit maintenance notifications.""" + + STATUS_MAP: ClassVar[Dict[str, Status]] = { + "Scheduled": Status.CONFIRMED, + "Restored": Status.COMPLETED, + "Splicing": Status.IN_PROCESS, + } + + def parse_html(self, soup: ResultSet) -> List[Dict]: + """Parse a summit circuit maintenance email. + + Args: + soup (ResultSet): beautiful soup object containing the html portion of an email. + + Returns: + List[Dict]: Data dictionaries containing the circuit maintenance data. + """ + data: Dict[str, Any] = {} + + table = soup.find("table") + if table is None: + return [data] + + for row in table.find_all("tr"): + th = row.find("th") + td = row.find("td") + if not th or not td: + continue + + key = th.get_text(strip=True) + value = td.get_text(strip=True) + self._apply_field(data, key, value) + + return [data] + + def _apply_field(self, data: Dict[str, Any], key: str, value: str) -> None: + """Map a table field into the parsed data dictionary. + + Args: + data (dict[str, Any]): a dictionary containing tags and their values. + key (str): the key that we want to map to the appropriate field. + value (str): the value that we want to associate with the appropriate field. + """ + simple_fields = { + "Company": ("account", value), + "Event": ("maintenance_id", value), + "Description": ("summary", value), + } + + if key in simple_fields: + field_name, field_value = simple_fields[key] + data[field_name] = field_value + return + + if key == "Status": + data["status"] = self.STATUS_MAP.get(value, Status.TENTATIVE) + return + + if key == "Circuits": + circuits = [] + for circuit in value.split(","): + c = CircuitImpact(circuit_id=circuit, impact=Impact.OUTAGE) + circuits.append(c) + data["circuits"] = circuits + return + + if "Start" in key: + timezone = _extract_timezone(key) + full_time = _parse_summit_datetime(value, timezone) + data["start"] = full_time + return + + if "End" in key: + timezone = _extract_timezone(key) + full_time = _parse_summit_datetime(value, timezone) + data["end"] = full_time + + +def _extract_timezone(key: str) -> str: + """Extract a timezone from a SummitIG table start or end time. + + Args: + key (str): A string in the format of 'Start Time (EST)' or 'End Time (EST)'. + + Returns: + str: The timezone (EST, CDT, ...) extracted from the input. + """ + # We need to pull the timezone out of the key + # Start Time (EST) + match = re.search(r"\((.*?)\)", key) + if not match: + raise ValueError(f"No timezone found in Key: '{key}'") + return match.group(1) + + +def _parse_summit_datetime(date_time: str, timezone: str) -> int: + """Create a unix timestamp based on a date and a timezone provided by Summit. + + Args: + date_time: A datetime string without the timezone '2/21/2025'. + timezone: The abbreviated timezone 'EST', 'PST', etc. + + Returns: + int: The unix timestamp based on the datetime and timezone inputs. + """ + # Parse datetime string + dt = datetime.strptime(date_time, "%m/%d/%Y %I:%M %p") + + # Map timezone abbreviation → IANA timezone + tz_map = { + "EST": "America/New_York", + "EDT": "America/New_York", + "CST": "America/Chicago", + "CDT": "America/Chicago", + "MST": "America/Denver", + "MDT": "America/Denver", + "PST": "America/Los_Angeles", + "PDT": "America/Los_Angeles", + } + + if timezone not in tz_map: + raise ValueError(f"Unknown timezone: {timezone}") + + # Attach timezone + dt = dt.replace(tzinfo=ZoneInfo(tz_map[timezone])) + + return int(dt.timestamp()) diff --git a/circuit_maintenance_parser/provider.py b/circuit_maintenance_parser/provider.py index 5b02c2b0..515ed52c 100644 --- a/circuit_maintenance_parser/provider.py +++ b/circuit_maintenance_parser/provider.py @@ -40,6 +40,7 @@ SubjectParserSeaborn2, ) from circuit_maintenance_parser.parsers.sparkle import HtmlParserSparkle1 +from circuit_maintenance_parser.parsers.summitig import HtmlParserSummitIG from circuit_maintenance_parser.parsers.tata import HtmlParserTata, SubjectParserTata from circuit_maintenance_parser.parsers.telstra import HtmlParserTelstra1, HtmlParserTelstra2 from circuit_maintenance_parser.parsers.turkcell import HtmlParserTurkcell1 @@ -511,6 +512,17 @@ class Sparkle(GenericProvider): _default_organizer = PrivateAttr("TISAmericaNOC@tisparkle.com") +class SummitIG(GenericProvider): + """SummitIG provider custom class.""" + + _processors: List[GenericProcessor] = PrivateAttr( + [ + CombinedProcessor(data_parsers=[HtmlParserSummitIG, EmailDateParser]), + ] + ) + _default_organizer = PrivateAttr("outages@summitig.net") + + class Tata(GenericProvider): """Tata provider custom class.""" diff --git a/tests/unit/data/summitig/summitig.eml b/tests/unit/data/summitig/summitig.eml new file mode 100644 index 00000000..4374c765 --- /dev/null +++ b/tests/unit/data/summitig/summitig.eml @@ -0,0 +1,357 @@ +Delivered-To: testuser@example.com +Received: by 2002:a05:6022:7414:b0:64:350:7ed3 with SMTP id FAKESMTPid12345lab; + Tue, 21 Jan 2025 10:35:26 -0800 (PST) +X-Forwarded-Encrypted: i=4; AJvYcCUoB00tyfj8iSjiwjQR7Vp8/L7ryN16tm3sC4L2L97AB+nCtq4wI3gRcKstzkadHmv6nThxBL7e1qnDYA==@example.com +X-Google-Smtp-Source: AGHT+IFIzKRaQ/falRau+1TvEYqtBFdDHp844nxkCGgsogssStBuX4YRJ5elxUJHLV2vahs5SszH +X-Received: by 2002:a05:6830:3914:b0:71d:5a8a:1a29 with SMTP id FAKEID1234567890abcdefghijklmnop; + Tue, 21 Jan 2025 10:35:25 -0800 (PST) +ARC-Seal: i=3; a=rsa-sha256; t=1737484525; cv=pass; + d=google.com; s=arc-20240605; + b=GrQCUGBJhOps2BoT+YWQ8c0T8osaLL1tsYyKl3CU+zk2/gm8vzpYrH1vT/l/mSTyvY + 5tz4pZ5b8i4NfNL3IUSIpHKpmItV4e1Hw5tbzh09eWCvm72GRlga4TBPhRhZLxHRTA4P + kZFENpXSqHtR9e3WA6Nq9TgB8ROAP4WV5dCEzwy/FB+pvAK2MqXK9XzRHYasRUfb/6Mf + 3o/bcUnn+2nsbxasAYEB8U683KfQzJeroBMwEaFJBlvKFotrFO7HLK5YGFZ9/8s5I75Z + 0toSeP1i8qMVZJpW2o5z1MIwxJtpVw0/5ZV+Q2QHYe7xoB6nuXuuA5yNbX+SWPNVtqV7 + dmXg== +ARC-Message-Signature: i=3; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; + h=list-unsubscribe:list-archive:list-help:list-post:list-id + :mailing-list:precedence:message-id:sender:reply-to:to:from:subject + :mime-version:date; + bh=tjhIpKrWjWJmrV2yAc56AcL0Vhob2JLznwJUrnheLTM=; + fh=9ziL+ecbPFndbrySCBG3djh4L+ypelTN47vqWq7LuHc=; + b=Zak8NCoprArn1wr8P/4SKgx6I1/KeCXkQgCyq8EkmkKaz8nl2OQb2cuZBqcgJL3MPd + HQgVxfOCqv64g9eKAy2XTISYT+xeUxm+swdGRXJQytHvAO1011vDZmiJLEbbh0rm6dR5 + 2Y30gH8T055lcBmqt6cZ3CvGpXG3mLS2OmstyEwxsR3hxdaINDa+1QS49rdbtabjmNC4 + sPOVh6/eudpUX3MLjaVAbrsDJyQn3ICU4jot5g9a8W9TZ5YMYagQ2LABDr2Uqt/oKYbs + K5wX+ZxIIHeQNQPrqdTG2DZAyY36ZgHa12HKdQCRSRjfG8yQfShqEsch6hqmMmpuYQTB + YEwg==; + dara=google.com +ARC-Authentication-Results: i=3; mx.google.com; + arc=pass (i=2 spf=pass spfdomain=summitig.net dkim=pass dkdomain=summitig.net); + spf=pass (google.com: domain of noc+bncFAKETOKEN123456789abcdefghij@example.com designates 209.85.214.198 as permitted sender) smtp.mailfrom=noc+bncFAKETOKEN123456789ABCDEFGHIJ@example.com; + dmarc=fail (p=NONE sp=NONE dis=NONE arc=pass) header.from=summitig.net; + dara=pass header.i=@example.com +Return-Path: +Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com. [209.85.214.198]) + by mx.google.com with ESMTPS id 46e09a7af769-724d1d83a80si891830a34.163.2025.01.21.10.35.25 + for + (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); + Tue, 21 Jan 2025 10:35:25 -0800 (PST) +Received-SPF: pass (google.com: domain of noc+bncFAKETOKEN123456789abcdefghij@example.com designates 209.85.214.198 as permitted sender) client-ip=209.85.214.198; +Authentication-Results: mx.google.com; + arc=pass (i=2 spf=pass spfdomain=summitig.net dkim=pass dkdomain=summitig.net); + spf=pass (google.com: domain of noc+bncFAKETOKEN123456789abcdefghij@example.com designates 209.85.214.198 as permitted sender) smtp.mailfrom=noc+bncFAKETOKEN123456789ABCDEFGHIJ@example.com; + dmarc=fail (p=NONE sp=NONE dis=NONE arc=pass) header.from=summitig.net; + dara=pass header.i=@example.com +Received: by mail-pl1-f198.google.com with SMTP id FAKESMTPSESSION12345fake.session.1 + for ; Tue, 21 Jan 2025 10:35:25 -0800 (PST) +ARC-Seal: i=2; a=rsa-sha256; t=1737484525; cv=pass; + d=google.com; s=arc-20240605; + b=Vvt4r9lHotO+bnNJZhMlbUUVkm+YHhNwC6t78LWG2KA9U2JKRXd8chAAsxpz4vTq7i + DJ/TuWak2cQtpvq1vJjqu0UR7n8tf02d1K9DMYSx6N8ek3f3TEgSZSG64EbofQPP85/z + wlwLdT5TdM8XN/JirBQsEPmvBIgBe1H12v5d05l9eiEEWLUR0gsi3LtGPXXMrbW7aD0S + cbMworO0gbI2RtIMDW1e/CRQf/L619gafd6nN5sDQGYJl2J8/P9OPUgcMEHNowst0tsU + pIG6Xfitwjibg22gN9kKAfd7xSem2WAvBVR3ckVi5FbApRuYrxief3fBIIW4DND4x9No + SEZg== +ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; + h=list-unsubscribe:list-archive:list-help:list-post:list-id + :mailing-list:precedence:message-id:sender:reply-to:to:from:subject + :mime-version:date; + bh=tjhIpKrWjWJmrV2yAc56AcL0Vhob2JLznwJUrnheLTM=; + fh=9ziL+ecbPFndbrySCBG3djh4L+ypelTN47vqWq7LuHc=; + b=jqdR3b1Rm+VWCpQwBRTn/6FUQFz+7xXZpNrY7UAZsqtONvo6Jf9YVc5VZaZhhjM1/I + eWp47UfwsJkynbhQOT9DSC2qyiD8JX4KLxFWlqRgqOjvvPXzlM4/X2d5ojpCKqpudy19 + oUxgBZnCNAYU6MY5IjpAHg1tHU6yBDnK/vuSq0lQ9UfMmCKfTkrnIdHS9p23qruKgGTZ + Uuln1ntmzKgX6YXyOnYPih4q/GmYG4h+pmMsCn1XoSwqdfh8v7TSa5gOh6GDO3QqXCWl + VKXGA4CrWdQaVyASuhOeE+McId/dxQh+cf/I1ndU/Dv/rezdbj7jc47Z+UfAczrbLFxU + roBQ==; + darn=example.com +ARC-Authentication-Results: i=2; mx.google.com; + dkim=pass header.i=@summitig.net header.s=mx header.b=YuiqjTJW; + spf=pass (google.com: domain of bounce+12345.67890-noc=example.com@summitig.net designates 209.61.151.224 as permitted sender) smtp.mailfrom="bounce+12345.67890-noc=example.com@summitig.net" +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20230601; t=1737484525; x=1738089325; + h=list-unsubscribe:list-archive:list-help:list-post + :x-spam-checked-in-group:list-id:mailing-list:precedence + :x-original-authentication-results:x-original-sender:message-id + :sender:reply-to:to:from:subject:mime-version:date:x-beenthere + :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; + bh=tjhIpKrWjWJmrV2yAc56AcL0Vhob2JLznwJUrnheLTM=; + b=HogaGY75sIEEXD4DIoi6/UwGjjsvpPCugnFqH020anbmFzbQXc0+TvxHd8kCFO58kb + MaOB4gitmOIpBfdsCVOauzo4lQaFAUYSbjEutXy/SFXSJ8DWJO+NDCIeaBZvxZgkapAi + bCJBUfszG5nEV5ztr6O1Cc3OJvzCQFLxA91O4fA0SC+Kq3BGRJ8rAE9xVuhCbH3ZOBhK + khx7O70Y97qn++ub+bQaQYinSDEhd7FeNfyF5bNgUDxmaL18TNR5e3VkwMeyECGNjx5K + kSM5gn8fhf4gqICFYnZ8l7SCPWpNTM+HzEv7jdK55lIQpobzPV8f6NY7fo/l/QfUHdXT + tSXQ== +X-Forwarded-Encrypted: i=2; AJvYcCVxUmvBBLU4RgQvIVqxddlfHSPUd9nAxAVSwjc1bwV/slzkgL3a89ZkNbzgOaAsVCP7r5XxLsgHWI3oeA==@example.com +X-Gm-Message-State: AOJu0YyKFsVq0Fh+3oYJwVzNr2rTuNJmlm0EqFPa0YTdmFpUpGaNU959 + dY78ooYguIUUtwHtIke6cELoghul6UYhtiqcP9XHTpcg7oo2uGOjPYytGzw4AW70JrF8+CC8vDj + UE060gpJyW5Ylao62FsbCXVKvf3prbWR2aRm7QSPbmqeytB3RJeuBwVOM +X-Gm-Gg: ASbGncvd3AXF6DXm8Gj8wWirn7/9WFhKqZv9qSG94JODrq6+dCOoNj9L31S4T/o/api + nOm/becAbzb9qPgHawT/xDi7zpHQ+PCUNIA== +X-Received: by 2002:a05:6a20:394a:b0:1db:de38:294b with SMTP id FAKESMTPID12345fake.session.2; + Tue, 21 Jan 2025 10:35:25 -0800 (PST) +X-Received: by 2002:a05:6a20:394a:b0:1db:de38:294b with SMTP id 35fa12345678.fake.session.3; + Tue, 21 Jan 2025 10:35:24 -0800 (PST) +X-BeenThere: noc@example.com +Received: by 2002:a05:6a00:3a1e:b0:728:e1d1:39e3 with SMTP id + FAKESMTPID12345.fake.pod.test.us; Tue, 21 Jan 2025 + 10:35:24 -0800 (PST) +X-Received: by 2002:a05:6a00:c92:b0:726:41e:b310 with SMTP id FAKESMTPID12345.fake.session.4; + Tue, 21 Jan 2025 10:35:24 -0800 (PST) +ARC-Seal: i=1; a=rsa-sha256; t=1737484524; cv=none; + d=google.com; s=arc-20240605; + b=T1UJCXWgs9braezhbgKeSL0fgB5l7gaZGAT21eZCC8h/XxW37DihINrMXfaHPPOcdq + GyHfeKNykgGNCqTK8/K2PmNmQWD3VsxwvGjgZrmfKdF/0cqT69YhEcARIFoNvgPDNPuN + v5NBjGWEQ6BN+uLvufviOgcGdYBJG5+0t4Gj2/IdAC67keQpNiSaKlb9xvE7Ut2svW0o + JPa67Sl+NHiBeN2PNRMgYnM9Dp8YGiF0d9bzz2z1LGsiEcvEgk3L9fWoHL5ffwj8x53D + 6MyUXZt+d9nzA2FHO0DchCTY9eBDUQUvCfzSDyxT9aIUfzBl2VmpdT/EKD5uL47MrQlM + DORg== +ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; + h=message-id:sender:reply-to:to:from:subject:mime-version:date + :dkim-signature; + bh=tjhIpKrWjWJmrV2yAc56AcL0Vhob2JLznwJUrnheLTM=; + fh=riydSgYT/DRez6EtLL0r8OwFur1iVw8rmA4s0L7xKy0=; + b=DGOioEOQc452XDshEA6HYztivcSRy+ucgZt0UOu/K0jFINSY0pokOs7j/hyzsObJxD + Vb+2eTYuyYyRFlWGhb68KiOKHm2lG4t5jjgM8LRQJePtpus5hadNKX9OFB6UEeJH1u1q + xx7xz8P6LpTumT7YOR+jPsrp4QPzwqS1XmwuDk5spIDzxdWypFVicqMqFwDZKBbwEuaN + jlWQF5K1eVLPzDFumxawVLd9AZkDguIExy26zhuH1PjpIWTfRsM1gbTAcbBiBC1Re9n3 + Th90d3v7hixnMmSBotDZGlsyDD9nrNITTrnn5kx3O3Fpq86Z59J1rZjqcXV8H1kfYA5E + JVqA==; + dara=google.com +ARC-Authentication-Results: i=1; mx.google.com; + dkim=pass header.i=@summitig.net header.s=mx header.b=YuiqjTJW; + spf=pass (google.com: domain of bounce+12345.67890-noc=example.com@summitig.net designates 209.61.151.224 as permitted sender) smtp.mailfrom="bounce+12345.67890-noc=example.com@summitig.net" +Received: from mx0a-001ed902.pphosted.com (mx0a-001ed902.pphosted.com. [205.220.162.87]) + by mx.google.com with ESMTPS id FAKESMTPID12345.fake.session.5.2025.01.21.10.35.23 + for + (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); + Tue, 21 Jan 2025 10:35:23 -0800 (PST) +Received-SPF: pass (google.com: domain of bounce+12345.67890-noc=example.com@summitig.net designates 209.61.151.224 as permitted sender) client-ip=209.61.151.224; +Received: from pps.filterd (m0123456.ppops.net [127.0.0.1]) + by mx0a-001ed902.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id FAKEPROOFPOINT123456 + for ; Tue, 21 Jan 2025 18:35:23 GMT +Received: from rs224.mailgun.us (rs224.mailgun.us [209.61.151.224]) + by mx0a-001ed902.pphosted.com (PPS) with ESMTPS id FAKEMAILGUN12345 + (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NO) + for ; Tue, 21 Jan 2025 18:35:22 +0000 (GMT) +X-Mailgun-Sending-Ip: 209.61.151.224 +X-Mailgun-Sending-Ip-Pool-Name: +X-Mailgun-Sending-Ip-Pool: +X-Mailgun-Sid: WyJiZTA0MiIsIm5vY0BleGFtcGxlLmNvbSIsImMwOTE5MSJd +Received: by 233f92db713f with HTTP id FAKEHTTPID1234567890; Tue, 21 Jan 2025 + 18:35:21 GMT +Date: Tue, 21 Jan 2025 18:35:21 +0000 +Mime-Version: 1.0 +Content-Type: multipart/mixed; + boundary="63c0c0de9b5871df2f262e298098653621bfda5574b81834cd61ac268c02" +Subject: SummitIG Maintenance Notification +From: outages@summitig.net +To: noc@example.com +Reply-To: SummitigNOC@summitig.com +Sender: outages@summitig.net +Message-Id: <20250121183521.FAKE12345678FAKE@summitig.net> +X-CLX-Response: RAKEDATA123456789FAKEDATA987654321FAKE + + + + +X-Proofpoint-GUID: FAKEGUID123456789ABCDEF12345678 +X-Proofpoint-ORIG-GUID: FAKEGUID123456789ABCDEF12345678 +X-CLX-Shades: MLX +X-Proofpoint-Virus-Version: vendor=baseguard + engine=ICAP:2.0.293,Aquarius:18.0.1057,Hydra:6.0.680,FMLib:17.12.68.34 + definitions=2025-01-21_07,2025-01-21_03,2024-11-22_01 +X-Proofpoint-Spam-Details: rule=inbound_notspam policy=inbound score=0 mlxscore=0 bulkscore=0 + unknownsenderscore=20 priorityscore=0 clxscore=180 suspectscore=0 + adultscore=0 mlxlogscore=592 impostorscore=0 spamscore=0 + lowpriorityscore=0 malwarescore=0 phishscore=0 classifier=spam adjust=0 + reason=mlx scancount=1 engine=8.19.0-2411120000 + definitions=main-2501210149 domainage_hfrom=4590 domainage_replyto=4590 +X-Original-Sender: outages@summitig.net +X-Original-Authentication-Results: mx.google.com; dkim=pass + header.i=@summitig.net header.s=mx header.b=YuiqjTJW; spf=pass + (google.com: domain of bounce+12345.67890-noc=example.com@summitig.net + designates 209.61.151.224 as permitted sender) smtp.mailfrom="bounce+12345.67890-noc=example.com@summitig.net" +Precedence: list +Mailing-list: list noc@example.com; contact noc+owners@example.com +List-ID: +X-Spam-Checked-In-Group: noc@example.com +X-Google-Group-Id: 999888777 +List-Post: , +List-Help: , + +List-Archive: +List-Unsubscribe: , + +X-Gm-Internal: True + +--63c0c0de9b5871df2f262e298098653621bfda5574b81834cd61ac268c02 +Mime-Version: 1.0 +Content-Type: multipart/alternative; + boundary="e9b497c4e0ef455a70787e9cad9758c7e519d1e729151f879f0bc22f458b" + +--e9b497c4e0ef455a70787e9cad9758c7e519d1e729151f879f0bc22f458b +Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; charset="utf-8" + + +

Eve= +nt Update

+ + + + + + + + + + + + + + + + + + =20 + + + + + + + + + + + + + + + + + + + + + + + + +
EventTEST-EVENT-001
CompanyTest Company Inc
TypeMaintenance
Start Time (EST)2/21/2025 12:00 AM
End Time (EST)2/21/2025 6:00 AM
DescriptionNetwork Maintenance Improvements
Location38.9072,-77.0369
ImpactOutage
CircuitsTEST-001 Test Company,TEST-002 Test Company
StatusScheduled
CommentThe event is estimated to start at the scheduled time.
+ +--e9b497c4e0ef455a70787e9cad9758c7e519d1e729151f879f0bc22f458b +Content-Transfer-Encoding: quoted-printable +Content-Type: text/html; charset="utf-8" + + +

Eve= +nt Update

+ + + + + + + + + + + + + + + + + + =20 + + + + + + + + + + + + + + + + + + + + + + + + +
EventTEST-EVENT-001
CompanyTest Company Inc
TypeMaintenance
Start Time (EST)2/21/2025 12:00 AM
End Time (EST)2/21/2025 6:00 AM
DescriptionNetwork Maintenance Improvements
Location38.9072,-77.0369
ImpactOutage
CircuitsTEST-001 Test Company,TEST-002 Test Company
StatusScheduled
CommentThe event is estimated to start at the scheduled time.
+ +--e9b497c4e0ef455a70787e9cad9758c7e519d1e729151f879f0bc22f458b-- + +--63c0c0de9b5871df2f262e298098653621bfda5574b81834cd61ac268c02 +Content-Type: text/calendar; name="outageWindow.ics" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="outageWindow.ics" + +QkVHSU46VkNBTEVOREFSClZFUlNJT046Mi4wClBST0RJRDpzdW1taXRpZy5jb20KQ0FMU0NB +TEU6R1JFR09SSUFOCk1FVEhPRDpQVUJMSVNICkJFR0lOOlZUSU1FWk9ORQpUWklEOlVUQwpCRUdJ +TjpTVEFOREFSRApEVFNUQVJUOjE5NzAwMTAxVDAwMDAwMFoKVFpPRkZTRVRGUk9NOiswMDAwClRa +T0ZGU0VUVE86KzAwMDAKRU5EOlNUQU5EQVJECkVORDpWVElNRVpPTkUKQkVHSU46VkVWRU5UCkRU +U1RBUlQ7VFpJRD1VVEM6MjAyNTAyMjFUMDUwMDAwWgpEVEVORDtUWklEPVVUQzoyMDI1MDIyMVQx +MTAwMDBaClNVTU1BUlk6U3VtbWl0IElHIFBsYW5uZWQgT3V0YWdlCkRFU0NSSVBUSU9OOlBsYW5u +ZWQgb3V0YWdlIGZvciBvdXRhZ2UgSUQ6IFRFU1QtRVZFTlQtMDAxClBSSU9SSVRZOjMKRU5EOlZF +VkVOVApFTkQ6VkNBTEVOREFSCg== +--63c0c0de9b5871df2f262e298098653621bfda5574b81834cd61ac268c02-- diff --git a/tests/unit/data/summitig/summitig_html_parser_result.json b/tests/unit/data/summitig/summitig_html_parser_result.json new file mode 100644 index 00000000..1e86d370 --- /dev/null +++ b/tests/unit/data/summitig/summitig_html_parser_result.json @@ -0,0 +1,20 @@ +[ + { + "account": "Test Company Inc", + "circuits": [ + { + "circuit_id": "TEST-001 Test Company", + "impact": "OUTAGE" + }, + { + "circuit_id": "TEST-002 Test Company", + "impact": "OUTAGE" + } + ], + "end": 1740135600, + "maintenance_id": "TEST-EVENT-001", + "start": 1740114000, + "status": "CONFIRMED", + "summary": "Network Maintenance Improvements" + } +] diff --git a/tests/unit/data/summitig/summitig_result.json b/tests/unit/data/summitig/summitig_result.json new file mode 100644 index 00000000..424a1aaf --- /dev/null +++ b/tests/unit/data/summitig/summitig_result.json @@ -0,0 +1,21 @@ +[ + { + "account": "Test Company Inc", + "circuits": [ + { + "circuit_id": "TEST-001 Test Company", + "impact": "OUTAGE" + }, + { + "circuit_id": "TEST-002 Test Company", + "impact": "OUTAGE" + } + ], + "end": 1740135600, + "maintenance_id": "TEST-EVENT-001", + "stamp": 1737484521, + "start": 1740114000, + "status": "CONFIRMED", + "summary": "Network Maintenance Improvements" + } +] diff --git a/tests/unit/test_e2e.py b/tests/unit/test_e2e.py index e8930493..c1d8feed 100644 --- a/tests/unit/test_e2e.py +++ b/tests/unit/test_e2e.py @@ -37,6 +37,7 @@ PacketFabric, Seaborn, Sparkle, + SummitIG, Tata, Telstra, Turkcell, @@ -783,6 +784,16 @@ Path(dir_path, "data", "sparkle", "sparkle1_result.json"), ], ), + # SummitIG + ( + SummitIG, + [ + ("email", Path(dir_path, "data", "summitig", "summitig.eml")), + ], + [ + Path(dir_path, "data", "summitig", "summitig_result.json"), + ], + ), # Tata ( Tata, diff --git a/tests/unit/test_parsers.py b/tests/unit/test_parsers.py index fc7d839b..8f26d0d0 100644 --- a/tests/unit/test_parsers.py +++ b/tests/unit/test_parsers.py @@ -33,6 +33,7 @@ SubjectParserSeaborn2, ) from circuit_maintenance_parser.parsers.sparkle import HtmlParserSparkle1 +from circuit_maintenance_parser.parsers.summitig import HtmlParserSummitIG from circuit_maintenance_parser.parsers.tata import HtmlParserTata, SubjectParserTata from circuit_maintenance_parser.parsers.telstra import HtmlParserTelstra1, HtmlParserTelstra2 from circuit_maintenance_parser.parsers.turkcell import HtmlParserTurkcell1 @@ -602,6 +603,12 @@ def default(self, o): Path(dir_path, "data", "sparkle", "sparkle1.eml"), Path(dir_path, "data", "sparkle", "sparkle1_html_parser_result.json"), ), + # SummitIG + ( + HtmlParserSummitIG, + Path(dir_path, "data", "summitig", "summitig.eml"), + Path(dir_path, "data", "summitig", "summitig_html_parser_result.json"), + ), # Tata ( HtmlParserTata,