Skip to content

Commit

Permalink
Merge pull request #275 from networktocode/release/2.5.0
Browse files Browse the repository at this point in the history
Release v2.5.0
  • Loading branch information
chadell authored Mar 14, 2024
2 parents baba76e + 37c396c commit e4c3a0d
Show file tree
Hide file tree
Showing 13 changed files with 266 additions and 33 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## v.2.5.0 - 2024-03-13

### Added

- [#274](https://github.com/networktocode/circuit-maintenance-parser/pull/274) - Add Global Cloud XChange Parser

## v2.4.0 - 2024-02-20

### Added
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ By default, there is a `GenericProvider` that supports a `SimpleProcessor` using
- Equinix
- EXA (formerly GTT)
- HGC
- Global Cloud Xchange
- Google
- Lumen
- Megaport
Expand Down
19 changes: 11 additions & 8 deletions circuit_maintenance_parser/__init__.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
"""Circuit-maintenance-parser init."""
from typing import Type, Optional

from typing import Optional, Type

from .data import NotificationData
from .output import Maintenance
from .errors import NonexistentProviderError, ProviderError
from .output import Maintenance
from .provider import (
GenericProvider,
AquaComms,
Arelion,
AWS,
BSO,
GTT,
HGC,
NTT,
AquaComms,
Arelion,
Cogent,
Colt,
CrownCastle,
Equinix,
EUNetworks,
GTT,
GenericProvider,
GlobalCloudXchange,
Google,
HGC,
Lumen,
Megaport,
Momentum,
Netflix,
NTT,
PacketFabric,
Seaborn,
Sparkle,
Expand All @@ -44,6 +46,7 @@
CrownCastle,
Equinix,
EUNetworks,
GlobalCloudXchange,
Google,
GTT,
HGC,
Expand Down
86 changes: 86 additions & 0 deletions circuit_maintenance_parser/parsers/globalcloudxchange.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""Circuit Maintenance Parser for Equinix Email Notifications."""

import re
from datetime import datetime
from typing import Any, Dict, List

from bs4.element import ResultSet # type: ignore

from circuit_maintenance_parser.output import Impact
from circuit_maintenance_parser.parser import EmailSubjectParser, Html, Status


class HtmlParserGcx1(Html):
"""Custom Parser for HTML portion of Global Cloud Xchange circuit maintenance notifications."""

def parse_html(self, soup: ResultSet) -> List[Dict]:
"""Parse an Global Cloud Xchange circuit maintenance email.
Args:
soup (ResultSet): beautiful soup object containing the html portion of an email.
Returns:
Dict: The data dict containing circuit maintenance data.
"""
data: Dict[str, Any] = {"circuits": []}

for div in soup.find_all("div"):
for pstring in div.strings:
search = re.search("Dear (.*),", pstring)
if search:
data["account"] = search.group(1)

# Find Circuits
for table in soup.find_all("table"):
for row in table.find_all("tr"):
cols = row.find_all("td")
if len(cols) == 2 and "Service ID" not in cols[0].text:
impact = Impact.OUTAGE
if "at risk" in cols[1].text.lower():
impact = Impact.REDUCED_REDUNDANCY

data["circuits"].append({"circuit_id": cols[0].text, "impact": impact})

return [data]


class SubjectParserGcx1(EmailSubjectParser):
"""Parse the subject of a Global Cloud Xchange circuit maintenance email. The subject contains the maintenance ID and status."""

def parse_subject(self, subject: str) -> List[Dict]:
"""Parse the Global Cloud Xchange Email subject for summary and status.
Args:
subject (str): subject of email
e.g. 'PE2024020844407 | Emergency | Service Advisory Notice | Span Loss Rectification | 12-Feb-2024 09:00 (GMT) - 12-Feb-2024 17:00 (GMT)'.
Returns:
List[Dict]: Returns the data object with summary and status fields.
"""
data = {}
search = re.search(
r"^([A-Z0-9]+) \| (\w+) \| ([\w\s]+) \| ([\w\s]+) \| (\d+-[A-Za-z]{3}-\d{4} \d{2}:\d{2}) \(GMT\) - (\d+-[A-Za-z]{3}-\d{4} \d{2}:\d{2}) \(GMT\)$",
subject,
)
if search:
data["maintenance_id"] = search.group(1)
date_format = date_format = "%d-%b-%Y %H:%M"
data["start"] = self.dt2ts(datetime.strptime(search.group(5), date_format))
data["end"] = self.dt2ts(datetime.strptime(search.group(6), date_format))
data["summary"] = search.group(4)

if "completed" in subject.lower():
data["status"] = Status.COMPLETED
elif "rescheduled" in subject.lower():
data["status"] = Status.RE_SCHEDULED
elif "scheduled" in subject.lower() or "reminder" in subject.lower() or "notice" in subject.lower():
data["status"] = Status.CONFIRMED
elif "cancelled" in subject.lower():
data["status"] = Status.CANCELLED
else:
# Some Global Cloud Xchange 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.
data["status"] = Status.CONFIRMED

return [data]
34 changes: 22 additions & 12 deletions circuit_maintenance_parser/provider.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
"""Definition of Provider class as the entry point to the library."""

import logging
import os
import re
import traceback
from typing import Dict, Iterable, List

from typing import Iterable, List, Dict
import chardet

from pydantic import BaseModel, PrivateAttr

from circuit_maintenance_parser.utils import rgetattr

from circuit_maintenance_parser.output import Maintenance
from circuit_maintenance_parser.constants import EMAIL_HEADER_SUBJECT
from circuit_maintenance_parser.data import NotificationData
from circuit_maintenance_parser.parser import ICal, EmailDateParser
from circuit_maintenance_parser.errors import ProcessorError, ProviderError
from circuit_maintenance_parser.processor import CombinedProcessor, SimpleProcessor, GenericProcessor
from circuit_maintenance_parser.constants import EMAIL_HEADER_SUBJECT

from circuit_maintenance_parser.output import Maintenance
from circuit_maintenance_parser.parser import EmailDateParser, ICal
from circuit_maintenance_parser.parsers.aquacomms import HtmlParserAquaComms1, SubjectParserAquaComms1
from circuit_maintenance_parser.parsers.aws import SubjectParserAWS1, TextParserAWS1
from circuit_maintenance_parser.parsers.bso import HtmlParserBSO1
from circuit_maintenance_parser.parsers.cogent import HtmlParserCogent1, TextParserCogent1, SubjectParserCogent1
from circuit_maintenance_parser.parsers.cogent import HtmlParserCogent1, SubjectParserCogent1, TextParserCogent1
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.globalcloudxchange import HtmlParserGcx1, SubjectParserGcx1
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, 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.openai import OpenAIParser
from circuit_maintenance_parser.parsers.seaborn import (
HtmlParserSeaborn1,
HtmlParserSeaborn2,
Expand All @@ -43,7 +41,8 @@
from circuit_maintenance_parser.parsers.turkcell import HtmlParserTurkcell1
from circuit_maintenance_parser.parsers.verizon import HtmlParserVerizon1
from circuit_maintenance_parser.parsers.zayo import HtmlParserZayo1, SubjectParserZayo1
from circuit_maintenance_parser.parsers.openai import OpenAIParser
from circuit_maintenance_parser.processor import CombinedProcessor, GenericProcessor, SimpleProcessor
from circuit_maintenance_parser.utils import rgetattr

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -282,6 +281,17 @@ class EUNetworks(GenericProvider):
_default_organizer = "[email protected]"


class GlobalCloudXchange(GenericProvider):
"""Global Cloud Xchange provider custom class."""

_processors: List[GenericProcessor] = PrivateAttr(
[
CombinedProcessor(data_parsers=[EmailDateParser, SubjectParserGcx1, HtmlParserGcx1]),
]
)
_default_organizer = PrivateAttr("[email protected]")


class Google(GenericProvider):
"""Google provider custom class."""

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "circuit-maintenance-parser"
version = "2.4.0"
version = "2.5.0"
description = "Python library to parse Circuit Maintenance notifications and return a structured data back"
authors = ["Network to Code <[email protected]>"]
license = "Apache-2.0"
Expand Down
65 changes: 65 additions & 0 deletions tests/unit/data/globalcloudxchange/globalcloudxchange1.eml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
MIME-Version: 1.0
From: [email protected]
To: [email protected]
Reply-To: [email protected]
Date: 8 Feb 2024 17:09:33 +0000
Subject: PE2024020844407 | Emergency | Service Advisory Notice |
Span Loss Rectification | 12-Feb-2024 09:00 (GMT) - 12-Feb-2024 17:00 (GMT)
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable



<div style=3D"color:Black; font-size:11pt; font-family:Calibri; width:100px=
;"><html><head></head><body><div style=3D"margin-top:20px; margin-left:5px;=
margin-bottom:15px; font-family:calibri;width: 900px;"> =20
Dear Example Company INC, <br/> <br/>
Our provider will be carrying an emergency activity for span loss im=
provement work on old and new fibers between Cairo --Suez on HAWK Egypt bac=
khaul north route.
<br/>
<br/>Impact: At risk
<br/>
<br/>The following schedule is provided as a guide, which is subject to cha=
nge depending on conditions encountered during the activity: <br/><br/></d=
iv><div style=3D"margin-left:50px; font-family:Calibri;width: 900px;"><tabl=
e cellpadding=3D2 cellspacing=3D2 border=3D solid black 2px><tr bgcolor=3D#=
4b6c9e><td align=3Dcenter><font face=3D"calibri" color=3DWhite><b>Window</b=
></font></td><td align=3Dcenter><font face=3D"calibri" color=3DWhite><b>Pri=
mary Start Date (GMT) </b></font></td><td align=3Dcenter><font face=3D"cali=
bri" color=3DWhite><b>Primary End Date (GMT) </b></font></td><td align=3Dce=
nter><font face=3D"calibri" color=3DWhite><b>Backup Start Date (GMT) </b></=
font></td><td align=3Dcenter><font face=3D"calibri" color=3DWhite><b>Backup=
End Date (GMT) </b></font></td></tr><tr bgcolor=3D#EEEEF4><td align=3Dcent=
er><font face=3D"calibri">1</font></td><td align=3Dcenter><font face=3D"cal=
ibri">12-Feb-2024 09:00</font></td><td align=3Dcenter><font face=3D"calibri=
">12-Feb-2024 17:00</font></td><td align=3Dcenter><font face=3D"calibri">NA=
</font></td><td align=3Dcenter><font face=3D"calibri">NA</font></td></tr></=
table></div><br/><br/><div style=3D"margin-left:50px; font-family:Calibri;w=
idth: 700px;"><table cellpadding=3D2 cellspacing=3D2 border=3D solid black =
2px><tr bgcolor=3D#4b6c9e><td align=3Dcenter><font face=3D"calibri" color=
=3DWhite><b>Service ID</b></font></td><td align=3Dcenter><font face=3D"cali=
bri" color=3DWhite><b>Impact</b></font></td></tr><tr bgcolor=3D#EEEEF4><td =
align=3Dcenter><font face=3D"calibri">RGWLS31171</font></td><td align=3Dcen=
ter><font face=3D"calibri">At Risk</font></td></tr></table></div><div style=
=3D"margin-top:20px; margin-left:5px; margin-bottom:15px;font-family:calibr=
i;width: 700px;"> We hope that you will accept our apology for any inconven=
ience this work may cause you and your customers.<br/><br/>
Please contact the Global Cloud Xchange GNOC on +44(0) 208 282 1599/=
[email protected] should you require any additional information.<br/><br=
/>
=09
Regards<br/>Pradnya V Kokane<br/>Change Management, Global Network O=
perations Center<br/>D: +91 22 303 86148 <br/>M: +91 7977316326<br/>PVKoka=
[email protected] <br/>
<img src=3D"https://nims.gcxworld.com/nims/Presentation/images/NewLo=
go_BlackText.png" width=3D"117" height=3D"35" border=3D"0" alt=3D""><br/>
<a href=3D"https://www.linkedin.com/company/gcxworld">LinkedIn</a> |=
<a href=3D"https://www.facebook.com/globalcloudxchange">Facebook</a> | <a =
href=3D"http://twitter.com/globalcloudx">Twitter</a> | <a href=3D"https://p=
lus.google.com/+Globalcloudxchange">Google+ </a><br/>
</div> </body></html></div>

<p></p>

-- <br />
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"account": "Example Company INC",
"circuits": [
{
"circuit_id": "RGWLS31171",
"impact": "REDUCED-REDUNDANCY"
}
]
}
]
17 changes: 17 additions & 0 deletions tests/unit/data/globalcloudxchange/globalcloudxchange1_result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[
{
"account": "Example Company INC",
"circuits": [
{
"circuit_id": "RGWLS31171",
"impact": "REDUCED-REDUNDANCY"
}
],
"end": 1707757200,
"maintenance_id": "PE2024020844407",
"stamp": 1707412173,
"start": 1707728400,
"status": "CONFIRMED",
"summary": "Span Loss Rectification"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PE2024020844407 | Emergency | Service Advisory Notice | Span Loss Rectification | 12-Feb-2024 09:00 (GMT) - 12-Feb-2024 17:00 (GMT)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[
{
"maintenance_id": "PE2024020844407",
"start": 1707728400,
"end": 1707757200,
"status": "CONFIRMED",
"summary": "Span Loss Rectification"
}
]
Loading

0 comments on commit e4c3a0d

Please sign in to comment.