Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/429.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added Hawaiki Provider, Parser and Tests
2 changes: 2 additions & 0 deletions circuit_maintenance_parser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
GenericProvider,
GlobalCloudXchange,
Google,
Hawaiki,
Lumen,
Megaport,
Momentum,
Expand Down Expand Up @@ -65,6 +66,7 @@
GlobalCloudXchange,
Google,
GTT,
Hawaiki,
HGC,
Lumen,
Megaport,
Expand Down
93 changes: 93 additions & 0 deletions circuit_maintenance_parser/parsers/hawaiki.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""Hawaiki parser."""

import logging
import re
from typing import Dict, List

from dateutil import parser

from circuit_maintenance_parser.output import CircuitImpact, Impact, Status
from circuit_maintenance_parser.parser import EmailSubjectParser, Text

logger = logging.getLogger(__name__)
IMPACT_LINE_RE = re.compile(r"^Service impact:\s*(.+)$", re.IGNORECASE | re.MULTILINE)


class SubjectParserHawaiki1(EmailSubjectParser):
"""Extract maintenance_id from Hawaiki email subject."""

def parse_subject(self, subject: str) -> List[Dict]:
"""Extract ticket number from subject like '[Ticket#2026072290001004] ...'."""
match = re.search(r"\[Ticket#(\S+)\]", subject)
if match:
return [{"maintenance_id": match.group(1)}]
return [{}]


class TextParserHawaiki1(Text):
"""Parse plain-text body of Hawaiki initial maintenance notifications."""

def parse_text(self, text: str) -> List[Dict]:
"""Extract attributes from Hawaiki notification.

Example:
Service ID: HW-CID-12345

Maintenance Window
Start: 2026-August-05 20:00 UTC
End: 2026-August-05 21:00 UTC
"""
data: Dict = {
"circuits": [],
"status": Status.CONFIRMED,
"account": "Customer info unavailable",
"summary": "Hawaiki maintenance notification",
}

lines = text.splitlines()
in_maintenance_window = False

# All circuits in a Hawaiki notification share one "Service impact" line, and it appears
# after the "Service ID" lines, so resolve it up front.
impact = self._parse_impact(text)

for line in lines:
line = line.strip()

match = re.match(r"Service ID:\s*(.+)", line)
if match:
circuit_id = match.group(1).strip()
data["circuits"].append(CircuitImpact(circuit_id=circuit_id, impact=impact))
continue

if line == "Maintenance Window":
in_maintenance_window = True
continue

if in_maintenance_window:
match = re.match(r"Start:\s*(.+)", line)
if match:
data["start"] = self.dt2ts(parser.parse(match.group(1).strip()))
continue
match = re.match(r"End:\s*(.+)", line)
if match:
data["end"] = self.dt2ts(parser.parse(match.group(1).strip()))
in_maintenance_window = False
continue

return [data]

@staticmethod
def _parse_impact(text: str) -> Impact:
"""Map the notification's single 'Service impact' line to an Impact."""
match = IMPACT_LINE_RE.search(text)
if not match:
logger.warning("No Hawaiki 'Service impact' line found, defaulting to OUTAGE.")
return Impact.OUTAGE
impact_text = match.group(1).strip().lower()
if "no service impact" in impact_text:
return Impact.NO_IMPACT
if "redundancy" in impact_text or "re-routed" in impact_text:
return Impact.REDUCED_REDUNDANCY
logger.warning("Unrecognized Hawaiki service impact %r, defaulting to OUTAGE.", impact_text)
return Impact.OUTAGE
11 changes: 11 additions & 0 deletions circuit_maintenance_parser/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from circuit_maintenance_parser.parsers.globalcloudxchange import HtmlParserGcx1, SubjectParserGcx1
from circuit_maintenance_parser.parsers.google import HtmlParserGoogle1, SubjectParserGoogle1
from circuit_maintenance_parser.parsers.gtt import HtmlParserGTT1
from circuit_maintenance_parser.parsers.hawaiki import SubjectParserHawaiki1, TextParserHawaiki1
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
Expand Down Expand Up @@ -648,6 +649,16 @@ class Windstream(GenericProvider):
_default_organizer = PrivateAttr("wci.maintenance.notifications@windstream.com")


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

_include_filter = PrivateAttr({"text/plain": ["Maintenance Window"], "text/html": ["Maintenance Window"]})
_processors: List[GenericProcessor] = PrivateAttr(
[CombinedProcessor(data_parsers=[EmailDateParser, TextParserHawaiki1, SubjectParserHawaiki1])]
)
_default_organizer = PrivateAttr("support@bw-digital.com")


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

Expand Down
67 changes: 67 additions & 0 deletions tests/unit/data/hawaiki/hawaiki1.eml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
Date: Wed, 5 Aug 2026 20:00:00 +0000
From: HAWAIKI NOC <support@bw-digital.com>
To: noc@example.com
Subject: [Ticket#2026072290001004] Hawaiki Submarine Cable: Hazardous Condition Notification 2026-Aug-05 20:00 UTC
Content-Type: multipart/mixed; boundary="----------=_boundary-hawaiki1"
MIME-Version: 1.0

------------=_boundary-hawaiki1
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit

Dear valued Hawaiki customer,

Please be advised of maintenance works to be carried out on Hawaiki network.
This work is NOT EXPECTED to impact your service identified below.

Service ID: HW-CID-12345

Reason for Notification: Maintenance works to be carried out in Hawaiki network

Maintenance Window
Start: 2026-August-05 20:00 UTC
End: 2026-August-05 21:00 UTC

Contingency Window
Start: 2026-August-06 20:00 UTC
End: 2026-August-06 21:00 UTC

Location of Maintenance: Pacific City

Service impact: No service impact expected

Hawaiki NOC will monitor the progress of works throughout the maintenance
window, keep you informed of developments as they occur and advise when the
activity is complete.

Please contact the HAWAIKI SUBMARINE CABLE Network Operations Centre (NOC)
with any questions of concerns.

-----
Best Regards,
HAWAIKI NOC

------------=_boundary-hawaiki1
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: 7bit

<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/></head><body>
<p>Dear valued Hawaiki customer,</p>
<p>Please be advised of maintenance works to be carried out on Hawaiki network. This work is NOT EXPECTED to impact your service identified below.</p>
<p><strong>Service ID:</strong> HW-CID-12345</p>
<p><strong>Reason for Notification:</strong> Maintenance works to be carried out in Hawaiki network</p>
<p><strong>Maintenance Window</strong><br />
Start: 2026-August-05 20:00 UTC<br />
End: 2026-August-05 21:00 UTC<br />
<br />
<strong>Contingency Window</strong><br />
Start: 2026-August-06 20:00 UTC<br />
End: 2026-August-06 21:00 UTC<br />
<br />
<strong>Location of Maintenance</strong>: Pacific City<br />
<br />
<strong>Service impact</strong>: No service impact expected</p>
<p>Hawaiki NOC will monitor the progress of works throughout the maintenance window.</p>
</body></html>

------------=_boundary-hawaiki1--
21 changes: 21 additions & 0 deletions tests/unit/data/hawaiki/hawaiki1_result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[
{
"account": "Customer info unavailable",
"circuits": [
{
"circuit_id": "HW-CID-12345",
"impact": "NO-IMPACT"
}
],
"end": 1785963600,
"maintenance_id": "2026072290001004",
"organizer": "support@bw-digital.com",
"provider": "hawaiki",
"sequence": 1,
"stamp": 1785960000,
"start": 1785960000,
"status": "CONFIRMED",
"summary": "Hawaiki maintenance notification",
"uid": "0"
}
]
38 changes: 38 additions & 0 deletions tests/unit/data/hawaiki/hawaiki_completion.eml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
Date: Wed, 5 Aug 2026 21:34:23 +0000
From: HAWAIKI NOC <support@bw-digital.com>
To: noc@example.com
Subject: Re: [Ticket#2026072290001004] Hawaiki Submarine Cable: Hazardous Condition Notification 2026-Aug-05 20:00 UTC
Content-Type: multipart/alternative; boundary="----------=_boundary-hawaiki-completion"
MIME-Version: 1.0

------------=_boundary-hawaiki-completion
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: 7bit

Dear Valued Hawaiki Customer,

Please be informed that the maintenance has been completed. Contingency window
will not be required.

Ticket Reference:
2026072290001004

-----
Best Regards,
HAWAIKI NOC

------------=_boundary-hawaiki-completion
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: 7bit

<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head>
<body>
<p>Dear Valued Hawaiki Customer,</p>
<p>Please be informed that the maintenance has been completed. Contingency window will not be required.</p>
<p><strong>Ticket Reference:</strong><br />
2026072290001004</p>
<p>Best Regards,<br />
HAWAIKI NOC</p>
</body></html>

------------=_boundary-hawaiki-completion--
19 changes: 19 additions & 0 deletions tests/unit/test_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
GenericProvider,
GlobalCloudXchange,
Google,
Hawaiki,
Lumen,
Megaport,
Momentum,
Expand Down Expand Up @@ -599,6 +600,24 @@
Path(dir_path, "data", "google", "google9_completed_no_end_time_result.json"),
],
),
# Hawaiki - completion/update emails contain no "Maintenance Window" and are filtered to []
(
Hawaiki,
[
("email", Path(dir_path, "data", "hawaiki", "hawaiki_completion.eml")),
],
[],
),
# Hawaiki - initial maintenance notification with Maintenance Window block
(
Hawaiki,
[
("email", Path(dir_path, "data", "hawaiki", "hawaiki1.eml")),
],
[
Path(dir_path, "data", "hawaiki", "hawaiki1_result.json"),
],
),
# Lumen
(
Lumen,
Expand Down
Loading