Skip to content

Commit 7fc8181

Browse files
csesshmkekez-SIE
andauthored
Issue #210: Ability to parse multiple maintenance windows from Zayo maintenance notification (#214)
* Add multiple windows to Zayo maintenances * Updated unit test cases for Zayo Parser * Fixed CI/black job: replace single quotes with double quotes * Fixed CI/black job: double spaces in between classes * Fixed pydocstyle job: No blank lines allowed after function docstring --------- Co-authored-by: mkekez <[email protected]>
1 parent 5a96986 commit 7fc8181

File tree

12 files changed

+392
-61
lines changed

12 files changed

+392
-61
lines changed

circuit_maintenance_parser/parsers/zayo.py

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Zayo parser."""
22
import logging
33
import re
4+
from copy import deepcopy
45
from typing import Dict
56

67
import bs4 # type: ignore
@@ -44,21 +45,30 @@ class HtmlParserZayo1(Html):
4445

4546
def parse_html(self, soup):
4647
"""Execute parsing."""
48+
maintenances = []
4749
data = {}
4850
self.parse_bs(soup.find_all("b"), data)
4951
self.parse_tables(soup.find_all("table"), data)
5052

51-
if data:
52-
if "status" not in data:
53-
text = soup.get_text()
54-
if "will be commencing momentarily" in text:
55-
data["status"] = Status("IN-PROCESS")
56-
elif "has been completed" in text or "has closed" in text:
57-
data["status"] = Status("COMPLETED")
58-
elif "has rescheduled" in text:
59-
data["status"] = Status("RE-SCHEDULED")
53+
if not data:
54+
return [{}]
6055

61-
return [data]
56+
if "status" not in data:
57+
text = soup.get_text()
58+
if "will be commencing momentarily" in text:
59+
data["status"] = Status("IN-PROCESS")
60+
elif "has been completed" in text or "has closed" in text:
61+
data["status"] = Status("COMPLETED")
62+
elif "has rescheduled" in text:
63+
data["status"] = Status("RE-SCHEDULED")
64+
65+
for maintenance_window in data.get("windows", []):
66+
maintenance = deepcopy(data)
67+
maintenance["start"], maintenance["end"] = maintenance_window
68+
del maintenance["windows"]
69+
maintenances.append(maintenance)
70+
71+
return maintenances
6272

6373
def parse_bs(self, btags: ResultSet, data: dict):
6474
"""Parse B tag."""
@@ -71,41 +81,23 @@ def parse_bs(self, btags: ResultSet, data: dict):
7181
data["status"] = Status("CONFIRMED")
7282
elif "has cancelled" in line.text.lower():
7383
data["status"] = Status("CANCELLED")
74-
# Some Zayo notifications may include multiple activity dates.
75-
# For lack of a better way to handle this, we consolidate these into a single extended activity range.
76-
#
77-
# For example, given:
78-
#
79-
# 1st Activity Date
80-
# 01-Nov-2021 00:01 to 01-Nov-2021 05:00 ( Mountain )
81-
# 01-Nov-2021 06:01 to 01-Nov-2021 11:00 ( GMT )
82-
#
83-
# 2nd Activity Date
84-
# 02-Nov-2021 00:01 to 02-Nov-2021 05:00 ( Mountain )
85-
# 02-Nov-2021 06:01 to 02-Nov-2021 11:00 ( GMT )
86-
#
87-
# 3rd Activity Date
88-
# 03-Nov-2021 00:01 to 03-Nov-2021 05:00 ( Mountain )
89-
# 03-Nov-2021 06:01 to 03-Nov-2021 11:00 ( GMT )
90-
#
91-
# our end result would be (start: "01-Nov-2021 06:01", end: "03-Nov-2021 11:00")
9284
elif "activity date" in line.text.lower():
9385
logger.info("Found 'activity date': %s", line.text)
86+
87+
if "windows" not in data:
88+
data["windows"] = []
89+
9490
for sibling in line.next_siblings:
9591
text = sibling.text if isinstance(sibling, bs4.element.Tag) else sibling
9692
logger.debug("Checking for GMT date/timestamp in sibling: %s", text)
93+
9794
if "( GMT )" in text:
9895
window = self.clean_line(sibling).strip("( GMT )").split(" to ")
9996
start = parser.parse(window.pop(0))
100-
start_ts = self.dt2ts(start)
101-
# Keep the earliest of any listed start times
102-
if "start" not in data or data["start"] > start_ts:
103-
data["start"] = start_ts
10497
end = parser.parse(window.pop(0))
98+
start_ts = self.dt2ts(start)
10599
end_ts = self.dt2ts(end)
106-
# Keep the latest of any listed end times
107-
if "end" not in data or data["end"] < end_ts:
108-
data["end"] = end_ts
100+
data["windows"].append((start_ts, end_ts))
109101
break
110102
elif line.text.lower().strip().startswith("reason for maintenance:"):
111103
data["summary"] = self.clean_line(line.next_sibling)
@@ -148,13 +140,15 @@ def parse_tables(self, tables: ResultSet, data: Dict):
148140
"Customer Circuit ID",
149141
],
150142
)
143+
151144
if all(table_headers != expected_headers for expected_headers in expected_headers_ref):
152145
logger.warning("Table headers are not as expected: %s", head_row)
153146
continue
154147

155148
data_rows = table.find_all("td")
156149
if len(data_rows) % 5 != 0:
157150
raise AssertionError("Table format is not correct")
151+
158152
number_of_circuits = int(len(data_rows) / 5)
159153
for idx in range(number_of_circuits):
160154
data_circuit = {}
@@ -165,5 +159,6 @@ def parse_tables(self, tables: ResultSet, data: Dict):
165159
elif "no expected impact" in impact.lower():
166160
data_circuit["impact"] = Impact("NO-IMPACT")
167161
circuits.append(CircuitImpact(**data_circuit))
162+
168163
if circuits:
169164
data["circuits"] = circuits
Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
[
22
{
3+
"maintenance_id": "TTN-0003456789",
4+
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic.",
35
"circuits": [
46
{
57
"circuit_id": "/OGYX/123456/ /ZYO /",
@@ -10,10 +12,42 @@
1012
"impact": "NO-IMPACT"
1113
}
1214
],
13-
"end": 1635937200,
14-
"maintenance_id": "TTN-0003456789",
15+
"status": "IN-PROCESS",
1516
"start": 1635746460,
17+
"end": 1635764400
18+
},
19+
{
20+
"maintenance_id": "TTN-0003456789",
21+
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic.",
22+
"circuits": [
23+
{
24+
"circuit_id": "/OGYX/123456/ /ZYO /",
25+
"impact": "NO-IMPACT"
26+
},
27+
{
28+
"circuit_id": "/OGYX/234567/ /ZYO /",
29+
"impact": "NO-IMPACT"
30+
}
31+
],
32+
"status": "IN-PROCESS",
33+
"start": 1635832860,
34+
"end": 1635850800
35+
},
36+
{
37+
"maintenance_id": "TTN-0003456789",
38+
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic.",
39+
"circuits": [
40+
{
41+
"circuit_id": "/OGYX/123456/ /ZYO /",
42+
"impact": "NO-IMPACT"
43+
},
44+
{
45+
"circuit_id": "/OGYX/234567/ /ZYO /",
46+
"impact": "NO-IMPACT"
47+
}
48+
],
1649
"status": "IN-PROCESS",
17-
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic."
50+
"start": 1635919260,
51+
"end": 1635937200
1852
}
1953
]

tests/unit/data/zayo/zayo5_result.json

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,49 @@
1111
"impact": "NO-IMPACT"
1212
}
1313
],
14-
"end": 1635937200,
14+
"end": 1635764400,
1515
"maintenance_id": "TTN-0003456789",
1616
"stamp": 1635918838,
17+
"status": "IN-PROCESS",
1718
"start": 1635746460,
19+
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic."
20+
},
21+
{
22+
"account": "Some Customer Inc",
23+
"circuits": [
24+
{
25+
"circuit_id": "/OGYX/123456/ /ZYO /",
26+
"impact": "NO-IMPACT"
27+
},
28+
{
29+
"circuit_id": "/OGYX/234567/ /ZYO /",
30+
"impact": "NO-IMPACT"
31+
}
32+
],
33+
"end": 1635850800,
34+
"maintenance_id": "TTN-0003456789",
35+
"stamp": 1635918838,
36+
"status": "IN-PROCESS",
37+
"start": 1635832860,
38+
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic."
39+
},
40+
{
41+
"account": "Some Customer Inc",
42+
"circuits": [
43+
{
44+
"circuit_id": "/OGYX/123456/ /ZYO /",
45+
"impact": "NO-IMPACT"
46+
},
47+
{
48+
"circuit_id": "/OGYX/234567/ /ZYO /",
49+
"impact": "NO-IMPACT"
50+
}
51+
],
52+
"end": 1635937200,
53+
"maintenance_id": "TTN-0003456789",
54+
"stamp": 1635918838,
1855
"status": "IN-PROCESS",
56+
"start": 1635919260,
1957
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic."
2058
}
2159
]
Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,53 @@
11
[
22
{
3+
"maintenance_id": "TTN-0004567890",
4+
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic.",
35
"circuits": [
46
{
57
"circuit_id": "/OGYX/123418/ /ZYO /",
68
"impact": "NO-IMPACT"
79
},
810
{
9-
"circuit_id": "/OGYX/123408/ /ZYO /",
11+
"circuit_id":"/OGYX/123408/ /ZYO /",
1012
"impact": "NO-IMPACT"
1113
}
1214
],
13-
"end": 1635937200,
14-
"maintenance_id": "TTN-0004567890",
15+
"status": "COMPLETED",
1516
"start": 1635746460,
17+
"end": 1635764400
18+
},
19+
{
20+
"maintenance_id": "TTN-0004567890",
21+
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic.",
22+
"circuits": [
23+
{
24+
"circuit_id": "/OGYX/123418/ /ZYO /",
25+
"impact": "NO-IMPACT"
26+
},
27+
{
28+
"circuit_id":"/OGYX/123408/ /ZYO /",
29+
"impact": "NO-IMPACT"
30+
}
31+
],
32+
"status": "COMPLETED",
33+
"start": 1635832860,
34+
"end": 1635850800
35+
},
36+
{
37+
"maintenance_id": "TTN-0004567890",
38+
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic.",
39+
"circuits": [
40+
{
41+
"circuit_id": "/OGYX/123418/ /ZYO /",
42+
"impact": "NO-IMPACT"
43+
},
44+
{
45+
"circuit_id":"/OGYX/123408/ /ZYO /",
46+
"impact": "NO-IMPACT"
47+
}
48+
],
1649
"status": "COMPLETED",
17-
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic."
50+
"start": 1635919260,
51+
"end": 1635937200
1852
}
19-
]
53+
]

tests/unit/data/zayo/zayo6_result.json

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,53 @@
77
"impact": "NO-IMPACT"
88
},
99
{
10-
"circuit_id": "/OGYX/123408/ /ZYO /",
10+
"circuit_id":"/OGYX/123408/ /ZYO /",
1111
"impact": "NO-IMPACT"
1212
}
1313
],
14-
"end": 1635937200,
14+
"end": 1635764400,
1515
"maintenance_id": "TTN-0004567890",
1616
"stamp": 1635936668,
17+
"status": "COMPLETED",
1718
"start": 1635746460,
19+
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic."
20+
},
21+
{
22+
"account": "Some Customer Inc",
23+
"circuits": [
24+
{
25+
"circuit_id": "/OGYX/123418/ /ZYO /",
26+
"impact": "NO-IMPACT"
27+
},
28+
{
29+
"circuit_id":"/OGYX/123408/ /ZYO /",
30+
"impact": "NO-IMPACT"
31+
}
32+
],
33+
"end": 1635850800,
34+
"maintenance_id": "TTN-0004567890",
35+
"stamp": 1635936668,
36+
"status": "COMPLETED",
37+
"start": 1635832860,
38+
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic."
39+
},
40+
{
41+
"account": "Some Customer Inc",
42+
"circuits": [
43+
{
44+
"circuit_id": "/OGYX/123418/ /ZYO /",
45+
"impact": "NO-IMPACT"
46+
},
47+
{
48+
"circuit_id":"/OGYX/123408/ /ZYO /",
49+
"impact": "NO-IMPACT"
50+
}
51+
],
52+
"end": 1635937200,
53+
"maintenance_id": "TTN-0004567890",
54+
"stamp": 1635936668,
1855
"status": "COMPLETED",
56+
"start": 1635919260,
1957
"summary": "Routine Fiber splice - NO Impact is Expected to your services. This notification is to advise you that we will be entering a splice case that houses live traffic."
2058
}
21-
]
59+
]

tests/unit/data/zayo/zayo7_html_parser_result.json

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,36 @@
66
"impact": "OUTAGE"
77
}
88
],
9-
"end": 1637067600,
9+
"end": 1636894800,
1010
"maintenance_id": "TTN-0005432100",
1111
"start": 1636876860,
1212
"status": "COMPLETED",
1313
"summary": "Zayo will implement maintenance to repair damaged fiber splice case, to prevent unplanned outages"
14+
},
15+
{
16+
"circuits": [
17+
{
18+
"circuit_id": "/IPYX/100722/ /ZYO /",
19+
"impact": "OUTAGE"
20+
}
21+
],
22+
"end": 1636981200,
23+
"maintenance_id": "TTN-0005432100",
24+
"start": 1636963260,
25+
"status": "COMPLETED",
26+
"summary": "Zayo will implement maintenance to repair damaged fiber splice case, to prevent unplanned outages"
27+
},
28+
{
29+
"circuits": [
30+
{
31+
"circuit_id": "/IPYX/100722/ /ZYO /",
32+
"impact": "OUTAGE"
33+
}
34+
],
35+
"end": 1637067600,
36+
"maintenance_id": "TTN-0005432100",
37+
"start": 1637049660,
38+
"status": "COMPLETED",
39+
"summary": "Zayo will implement maintenance to repair damaged fiber splice case, to prevent unplanned outages"
1440
}
15-
]
41+
]

0 commit comments

Comments
 (0)