Skip to content

Commit 9586d47

Browse files
aliex-13mkekez-SIE
andauthored
Allow Lumen maintenance multiple windows to be parsed (#216)
* Add multiple window checks for Lumen * NRE-605 fix comments * Add unit tests with multiple windows Lumen * Fix linter issues from PR * Fix another linter typo * Fix linter pydocstyle * readme changes for more instructions on local testing * Added some more instructions and examples for readme * Update test_e2e tests for Lumen * remove date tests in e2e file for Lumen8 --------- Co-authored-by: mkekez <[email protected]>
1 parent 7fc8181 commit 9586d47

File tree

6 files changed

+300
-4
lines changed

6 files changed

+300
-4
lines changed

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,28 @@ The project is following Network to Code software development guidelines and is
312312
- The `Provider` also supports the definition of a `_include_filter` and a `_exclude_filter` to limit the notifications that are actually processed, avoiding false positive errors for notification that are not relevant.
313313
4. Update the `unit/test_e2e.py` with the new provider, providing some data to test and validate the final `Maintenances` created.
314314
5. **Expose the new `Provider` class** updating the map `SUPPORTED_PROVIDERS` in `circuit_maintenance_parser/__init__.py` to officially expose the `Provider`.
315+
6. You can run some tests here to verify that your new unit tests do not cause issues with existing tests, and in general they work as expected. You can do this by running `pytest --log-cli-level=DEBUG --capture=tee-sys`. You can narrow down the tests that you want to execute with the `-k` flag. If successful, your results should look similar to the following:
316+
317+
```
318+
-> % pytest --log-cli-level=DEBUG --capture=tee-sys -k test_parsers
319+
...omitted debug logs...
320+
====================================================== 99 passed, 174 deselected, 17 warnings in 10.35s ======================================================
321+
```
322+
7. Run some final CI tests locally to ensure that there is no linting/formatting issues with your changes. You should look to get a code score of 10/10. See the example below: `invoke tests --local`
323+
324+
```
325+
-> % invoke tests --local
326+
LOCAL - Running command black --check --diff .
327+
All done! ✨ 🍰 ✨
328+
41 files would be left unchanged.
329+
LOCAL - Running command flake8 .
330+
LOCAL - Running command find . -name "*.py" | xargs pylint
331+
************* Module tasks
332+
tasks.py:4:0: W0402: Uses of a deprecated module 'distutils.util' (deprecated-module)
333+
334+
--------------------------------------------------------------------
335+
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)
336+
```
315337

316338
### How to debug circuit-maintenance-parser library locally
317339

circuit_maintenance_parser/parsers/lumen.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import logging
33
from typing import Dict
44

5+
from copy import deepcopy
56
from dateutil import parser
67
import bs4 # type: ignore
78
from bs4.element import ResultSet # type: ignore
@@ -19,10 +20,22 @@ class HtmlParserLumen1(Html):
1920

2021
def parse_html(self, soup):
2122
"""Execute parsing."""
23+
maintenances = []
2224
data = {}
2325
self.parse_spans(soup.find_all("span"), data)
2426
self.parse_tables(soup.find_all("table"), data)
25-
return [data]
27+
28+
# Iterates over multiple windows and duplicates other maintenance info to a new dictionary while also updating start and end times for the specific window.
29+
for window in data["windows"]:
30+
maintenance = deepcopy(data)
31+
maintenance["start"], maintenance["end"] = window
32+
del maintenance["windows"]
33+
maintenances.append(maintenance)
34+
35+
# Deleting the key after we are finished checking for multiple windows and duplicating data.
36+
del data["windows"]
37+
38+
return maintenances
2639

2740
def parse_spans(self, spans: ResultSet, data: Dict):
2841
"""Parse Span tag."""
@@ -56,8 +69,11 @@ def parse_spans(self, spans: ResultSet, data: Dict):
5669
data["stamp"] = self.dt2ts(stamp)
5770
break
5871

59-
def parse_tables(self, tables: ResultSet, data: Dict):
72+
def parse_tables(self, tables: ResultSet, data: Dict): # pylint: disable=too-many-locals
6073
"""Parse Table tag."""
74+
# Initialise multiple windows list that will be used in parse_html
75+
data["windows"] = []
76+
6177
circuits = []
6278
for table in tables:
6379
cells = table.find_all("td")
@@ -68,9 +84,10 @@ def parse_tables(self, tables: ResultSet, data: Dict):
6884
for idx in range(num_columns, len(cells), num_columns):
6985
if "GMT" in cells[idx].string and "GMT" in cells[idx + 1].string:
7086
start = parser.parse(cells[idx].string.split(" GMT")[0])
71-
data["start"] = self.dt2ts(start)
87+
start_ts = self.dt2ts(start)
7288
end = parser.parse(cells[idx + 1].string.split(" GMT")[0])
73-
data["end"] = self.dt2ts(end)
89+
end_ts = self.dt2ts(end)
90+
data["windows"].append((start_ts, end_ts))
7491
break
7592

7693
elif cells[0].string == "Customer Name":

tests/unit/data/lumen/lumen8.html

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
<html><head>
2+
<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf-8"><s=
3+
tyle type=3D"text/css"> BODY { font-family: Arial; font-size:10pt;} </styl=
4+
e>
5+
<style type=3D"text/css"> TD { font-family: Arial; font-size: 8pt;} </styl=
6+
e>
7+
<style type=3D"text/css"> .headerRow { text-align: left; font-size:8pt; fon=
8+
t-weight:bold; white-space:nowrap; background-color: #cccccc; } </style>
9+
<style type=3D"text/css"> .busOrg { text-align: left; font-size:10pt; font-=
10+
weight:bold; } </style>
11+
<style type=3D"text/css"> span.changeControl { font-size:11pt; font-weight:=
12+
bold; } </style>
13+
<style type=3D"text/css"> span.maintenanceAdvisement{ font-size:10.5pt; col=
14+
or:red; font-weight:bold; text-transform:uppercase; } </style>
15+
<style type=3D"text/css"> span.headerSummary{ font-size:10.5pt; text-decora=
16+
tion: underline; font-weight:bold; } </style>
17+
<style type=3D"text/css"> span.gcrHeading{ font-size:12pt; font-weight:bold=
18+
; } </style>
19+
<style type=3D"text/css"> span.bodyHeading{ font-size:12pt; font-weight:bol=
20+
d; } </style>
21+
<style type=3D"text/css"> span.windowHeader { font-weight:normal; font-size=
22+
:10pt; } </style>
23+
<style type=3D"text/css"> span.windowLabel { font-weight:bold; color:#00000=
24+
0 } </style>
25+
<style type=3D"text/css"> span.summaryRow { text-align: center; font-family=
26+
: Arial ;font-size:10pt; text-align: left; } </style>
27+
<style type=3D"text/css"> span.primaryDates { color:red } span.thankYou { f=
28+
ont-size:10.5pt; font-weight:bold; } </style>
29+
<style type=3D"text/css"> span.footerText { font-size:8.5pt; font-family:Ar=
30+
ial; } </style>
31+
<style type=3D"text/css"> span.redBoldUpper { color:red; font-weight:bold;=
32+
text-transform:uppercase; } </style>
33+
<style type=3D"text/css"> span.windowtimeframe { font-family: Arial; font-s=
34+
ize:10pt;} </style>
35+
<style type=3D"text/css"> div.impactMatrix { padding-left:10px } </style>
36+
<style type=3D"text/css"> .footerLabel { font-weight:bold; text-align: lef=
37+
t; white-space:nowrap; width:40%; vertical-align:text-top; } </style>
38+
<style type=3D"text/css"> table.footerContact { font-size:9pt; font-family:=
39+
Arial; } </style>
40+
<style type=3D"text/css"> tr.footerContact { text-align: left; white-space=
41+
:nowrap; } </style>
42+
<style type=3D"text/css"> tr.contentRow { text-align: left; font-size:9pt; =
43+
white-space:nowrap; } </style>
44+
<style type=3D"text/css"> td.contacts { padding:.75pt .75pt .75pt .75pt; fo=
45+
nt-family: Arial; font-size:8.5pt; } </style>
46+
<style type=3D"text/css"> td.questions { padding:.75pt .75pt .75pt .75pt; f=
47+
ont-family: Arial; font-size:9.5pt; } </style>
48+
</head>
49+
<body>
50+
<div style=3D"overflow: hidden; "><p>
51+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img alt=3D"Lumen" he=
52+
ight=3D"75" src=3D"http://www.centurylink.com/common/images/email/LumenLogo=
53+
RegisteredTM.png" style=3D"position: relative; ">
54+
</div>
55+
<br>
56+
<span class=3D"bodyHeading">
57+
Scheduled Maintenance #: 12345678
58+
</span><span class=3D"bodyHeading"><br><br>
59+
Summary:
60+
</span><br><br>
61+
Lumen intends to carry out internal maintenance within its network. This h=
62+
as been designated as ESSENTIAL. The nature of this work is to repair fiber=
63+
and is required in order to avoid unplanned outages from damages related t=
64+
o natural causes.
65+
<br>
66+
<br>The estimated GPS location of work is:=01
67+
<br>LAT: 2 LONG: -1
68+
<br>
69+
<br>An alternate/backup maintenance window has been scheduled in the event =
70+
work is unable to be completed on the primary night. Currently no services=
71+
are scheduled to be impacted on the alternate night. Should the alternate=
72+
night be deemed necessary, you will receive an updated notification advisi=
73+
ng which services will be impacted on that night. Please see the date and =
74+
times of the alternate night below.
75+
<br>
76+
<br><b>Lumen sincerely apologizes for any inconvenience caused by this main=
77+
tenance</b>.
78+
<br><br><span class=3D"bodyHeading">
79+
Updates:
80+
</span><br><br>
81+
82+
2023-03-09 16:23:23 GMT - This maintenance is scheduled.<br>
83+
<br>
84+
<span class=3D"bodyHeading">
85+
Customer Impact:
86+
</span><br><br>
87+
<p><span class=3D"\&quot;windowHeader\&quot;"><span class=3D"\&quot;windowL=
88+
abel\&quot;">12345678-1</span> <br><table border=3D"1" cellspacing=3D"0" ce=
89+
llpadding=3D"5"><tr><td class=3D"\&quot;headerRow\&quot;">Start</td><td cla=
90+
ss=3D"\&quot;headerRow\&quot;">End</td></tr><tr><td>2023-03-22 04:00 GMT (G=
91+
reenwich Mean Time)</td><td>2023-03-22 10:00 GMT (Greenwich Mean Time)</td>=
92+
</tr><tr><td>2023-03-22 00:00 EDT (Eastern Daylight Time)</td><td>2023-03-2=
93+
2 06:00 EDT (Eastern Daylight Time)</td></tr></table><br><br><span class=3D=
94+
"\&quot;windowLabel\&quot;">Maintenance Location(s): </span>RANDOM LOCATION=
95+
;ANOTHER RANDOM ADDRESS SOMEWHERE<br></span></p><div class=3D"\&quot;impact=
96+
Matrix\&quot;"><table border=3D"1" cellspacing=3D"0" cellpadding=3D"5"><tr>=
97+
<td class=3D"\&quot;headerRow\&quot;">Customer Name</td><td class=3D"\&quot=
98+
;headerRow\&quot;">Circuit ID</td><td class=3D"\&quot;headerRow\&quot;">Alt=
99+
Circuit ID</td><td class=3D"\&quot;headerRow\&quot;">Bandwidth</td><td cla=
100+
ss=3D"\&quot;headerRow\&quot;">A Location</td><td class=3D"\&quot;headerRow=
101+
\&quot;">Z Location</td><td class=3D"\&quot;headerRow\&quot;">Impact Type</=
102+
td><td class=3D"\&quot;headerRow\&quot;">Maximum Duration</td><td class=3D"=
103+
\&quot;headerRow\&quot;">Order Number</td></tr><tr class=3D"\&quot;contentR=
104+
ow\&quot;"><td>Customer</td><td>123456789</td><td =
105+
>N/A</td><td>100 GIG</td><td>122 LATELY ST PONARM MI USA</td><td>992 A PRAM=
106+
KA ST CHICAGO IL USA</td><td>Outage</td><td>4 hours </td><td>&nbsp;</td></t=
107+
r><tr class=3D"\&quot;contentRow\&quot;"><td>CUSTOMER =
108+
</td><td>123456789</td><td>10GLAN-12345678</td><td>10GIG-E LAN</td><td>=
109+
SOMETHING SOMETHING SOMETHING </td><td>RANDOM LOCATION IN THE USA </td><=
110+
td>Outage</td><td>4 hours </td><td>&nbsp;</td></tr></table></div><span clas=
111+
s=3D"\&quot;alternateNight\&quot;"><p><b>Alternate Night:</b><br><span clas=
112+
s=3D"\&quot;windowLabel\&quot;">12345678-2</span> <br><table border=3D"1" c=
113+
ellspacing=3D"0" cellpadding=3D"5"><tr><td class=3D"\&quot;headerRow\&quot;=
114+
">Start</td><td class=3D"\&quot;headerRow\&quot;">End</td></tr><tr><td>2023=
115+
-03-23 04:00 GMT (Greenwich Mean Time)</td><td>2023-03-23 10:00 GMT (Greenw=
116+
ich Mean Time)</td></tr><tr><td>2023-03-23 00:00 EDT (Eastern Daylight Time=
117+
)</td><td>2023-03-23 06:00 EDT (Eastern Daylight Time)</td></tr></table><br=
118+
>
119+
<br><br><a href=3D"mailto:[email protected]?subject=3DRE: Lumen Schedul=
120+
ed Maintenance %23: 12345678 , Notification ID %23: 1234567891 &amp;body=3D=
121+
Hello%2C%20%0D%0A%0D%0A
122+
Please provide as much information as possible so we can support you faster=
123+
.%0D%0A%0D%0A
124+
Your Name%3A%0D%0A
125+
Your Phone%3A%0D%0A
126+
Your Business Name%3A%0D%0A
127+
Service ID(s) related to your inquiry%3A%0D%0A
128+
Your Request%3A%0D%0A%0D%0A%0D%0A
129+
">
130+
131+
132+
Click here</a> to open a case for assistance on this scheduled maintenance =
133+
via Email.
134+
135+
<br><br><a href=3D"https://urldefense.com/v3/__https://my.level3.com/portal=
136+
Web/mylevel3?goto-page=3DX3Dtj__;!!JmoZiZGBv3RvKRSx!7l4kkS_sEmvykcWOTU0O3PN=
137+
Bi40o2pEgWQ5AlotX7gdBsGDjOSzrIuNuyM_nXG3S6Ia9S01RWEpJeTxmUd5HFo-Ewb7QkvEQ-B=
138+
bTyQ$">
139+
Click here</a> for immediate information on scheduled maintenances via the =
140+
Lumen Customer Portal.
141+
142+
<br><br><a href=3D"https://urldefense.com/v3/__https://my.level3.com/portal=
143+
Web/mylevel3?goto-page=3DOEmaq__;!!JmoZiZGBv3RvKRSx!7l4kkS_sEmvykcWOTU0O3PN=
144+
Bi40o2pEgWQ5AlotX7gdBsGDjOSzrIuNuyM_nXG3S6Ia9S01RWEpJeTxmUd5HFo-Ewb7QkvG95T=
145+
9akA$">
146+
Click here</a> to manage your notification subscriptions via the Lumen Port=
147+
al.
148+
149+
<br><br><a href=3D"https://urldefense.com/v3/__https://my.level3.com/portal=
150+
Web/mylevel3?goto-page=3DX3Dtj__;!!JmoZiZGBv3RvKRSx!7l4kkS_sEmvykcWOTU0O3PN=
151+
Bi40o2pEgWQ5AlotX7gdBsGDjOSzrIuNuyM_nXG3S6Ia9S01RWEpJeTxmUd5HFo-Ewb7QkvEQ-B=
152+
bTyQ$">
153+
Click here</a> to open a case for assistance on this scheduled maintenance =
154+
via the Lumen Customer Portal.
155+
<br><br><br>
156+
Network Change Management Team
157+
<br>
158+
Lumen
159+
<br>
160+
1-855-244-6468 option 1
161+
<br><br>
162+
The information in this communication is confidential and may not be disclo=
163+
sed to=20
164+
third parties or shared further without the express permission of Lumen.=
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
[
2+
{
3+
"account": "Customer",
4+
"circuits": [
5+
{
6+
"circuit_id": "123456789",
7+
"impact": "OUTAGE"
8+
},
9+
{
10+
"circuit_id": "123456789",
11+
"impact": "OUTAGE"
12+
}
13+
],
14+
"end": 1679479200,
15+
"maintenance_id": "12345678",
16+
"stamp": 1678379003,
17+
"start": 1679457600,
18+
"status": "IN-PROCESS",
19+
"summary": "Lumen intends to carry out internal maintenance within its network. This has been designated as ESSENTIAL. The nature of this work is to repair fiber and is required in order to avoid unplanned outages from damages related to natural causes."
20+
},
21+
{
22+
"account": "Customer",
23+
"circuits": [
24+
{
25+
"circuit_id": "123456789",
26+
"impact": "OUTAGE"
27+
},
28+
{
29+
"circuit_id": "123456789",
30+
"impact": "OUTAGE"
31+
}
32+
],
33+
"end": 1679565600,
34+
"maintenance_id": "12345678",
35+
"stamp": 1678379003,
36+
"start": 1679544000,
37+
"status": "IN-PROCESS",
38+
"summary": "Lumen intends to carry out internal maintenance within its network. This has been designated as ESSENTIAL. The nature of this work is to repair fiber and is required in order to avoid unplanned outages from damages related to natural causes."
39+
}
40+
]

tests/unit/test_e2e.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,54 @@
423423
Path(dir_path, "data", "date", "email_date_1_result.json"),
424424
],
425425
),
426+
(
427+
Lumen,
428+
[
429+
("html", Path(dir_path, "data", "lumen", "lumen5.html")),
430+
(EMAIL_HEADER_DATE, Path(dir_path, "data", "date", "email_date_1")),
431+
(EMAIL_HEADER_SUBJECT, Path(dir_path, "data", "lumen", "subject_work_planned")),
432+
],
433+
[
434+
Path(dir_path, "data", "lumen", "lumen5_result.json"),
435+
Path(dir_path, "data", "date", "email_date_1_result.json"),
436+
],
437+
),
438+
(
439+
Lumen,
440+
[
441+
("html", Path(dir_path, "data", "lumen", "lumen6.html")),
442+
(EMAIL_HEADER_DATE, Path(dir_path, "data", "date", "email_date_1")),
443+
(EMAIL_HEADER_SUBJECT, Path(dir_path, "data", "lumen", "subject_work_planned")),
444+
],
445+
[
446+
Path(dir_path, "data", "lumen", "lumen6_result.json"),
447+
Path(dir_path, "data", "date", "email_date_1_result.json"),
448+
],
449+
),
450+
(
451+
Lumen,
452+
[
453+
("html", Path(dir_path, "data", "lumen", "lumen7.html")),
454+
(EMAIL_HEADER_DATE, Path(dir_path, "data", "date", "email_date_1")),
455+
(EMAIL_HEADER_SUBJECT, Path(dir_path, "data", "lumen", "subject_work_planned")),
456+
],
457+
[
458+
Path(dir_path, "data", "lumen", "lumen7_result.json"),
459+
Path(dir_path, "data", "date", "email_date_1_result.json"),
460+
],
461+
),
462+
(
463+
Lumen,
464+
[
465+
("html", Path(dir_path, "data", "lumen", "lumen8.html")),
466+
# (EMAIL_HEADER_DATE, Path(dir_path, "data", "date", "email_date_1")),
467+
(EMAIL_HEADER_SUBJECT, Path(dir_path, "data", "lumen", "subject_work_planned")),
468+
],
469+
[
470+
Path(dir_path, "data", "lumen", "lumen8_result.json"),
471+
# Path(dir_path, "data", "date", "email_date_1_result.json"),
472+
],
473+
),
426474
# Megaport
427475
(
428476
Megaport,

tests/unit/test_parsers.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,11 @@
317317
Path(dir_path, "data", "lumen", "lumen7.html"),
318318
Path(dir_path, "data", "lumen", "lumen7_result.json"),
319319
),
320+
(
321+
HtmlParserLumen1,
322+
Path(dir_path, "data", "lumen", "lumen8.html"),
323+
Path(dir_path, "data", "lumen", "lumen8_result.json"),
324+
),
320325
# Megaport
321326
(
322327
HtmlParserMegaport1,

0 commit comments

Comments
 (0)