Skip to content

Commit

Permalink
Merge pull request #28 from networktocode/release-v1.2.0
Browse files Browse the repository at this point in the history
Release v1.2.0
chadell authored Jun 21, 2021
2 parents 81f2600 + 5df94a4 commit e412b28
Showing 71 changed files with 1,086 additions and 451 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Default owner(s) of all files in this repository
* @chadell
* @chadell @glennmatthews
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Changelog

## v1.1.0
## v1.2.0 - 2021-06-21

### Added

- #25 - added **Cogent** parser
- #26 - Multiple Parsers per Provider, even combining ICal with custom HTML.

## v1.1.0 - 2021-06-09

### Added

30 changes: 18 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -21,16 +21,24 @@ enables supporting other providers that are not using this proposed practice, ge

You can leverage on this library in your automation framework to process circuit maintenance notifications, and use the standarised output to handle your received circuit maintenance notifications in a simple way.

## Supported Providers
## How does it work?

### Supported providers using the BCOP standard
Starting from a Provider parsing class, **multiple** parsers can be attached (in a specific order) and specific provider information (such as the default email used from the provider).

Each provider could use the standard ICal format commented above or define its custom HTML parsers, supporting multiple notification types for the same provider that could be transitioning from one type to another.

### Supported Providers

#### Supported providers using the BCOP standard

- EuNetworks
- NTT
- PacketFabric
- Telstra

### Supported providers based on other parsers
#### Supported providers based on other parsers

- Cogent
- Lumen
- Megaport
- Telstra
@@ -76,10 +84,8 @@ END:VCALENDAR
"""

data = {
"subject": "this is a circuit maintenance from some NSP",
"sender": "[email protected]",
"source": "gmail",
"raw": raw_text,
"provider_type": "NTT"
}

parser = init_parser(**data)
@@ -179,13 +185,13 @@ The project is following Network to Code software development guidelines and is
- Install dependencies and library locally: `poetry install`
- Run CI tests locally: `invoke tests --local`

### How to add a new Circuit Maintenance parser?
### How to add a new Circuit Maintenance provider?

1. Within `circuit_maintenance_parser/parsers`, **add your new parser**, inheriting from generic
`MaintenanceNotification` class or custom ones such as `ICal` or `Html`.
2. Add a Circuit Maintenance **integration test for the new provider parser**, with at least one test case under
`tests/integration/data`.
3. **Expose the new parser class** updating the map `SUPPORTED_PROVIDER_PARSERS` in
1. If your Provider requires a custom parser, within `circuit_maintenance_parser/parsers`, **add your new parser**, inheriting from generic
`Parser` class or custom ones such as `ICal` or `Html` and add a **unit test for the new provider parser**, with at least one test case under
`tests/unit/data`.
2. Add new class in `providers.py` with the custom info, defining in `_parser_classes` the list of parsers that you will use, using the generic `ICal` and/or your custom parsers.
3. **Expose the new parser class** updating the map `SUPPORTED_PROVIDERS` in
`circuit_maintenance_parser/__init__.py` to officially expose the parser.

## Questions
97 changes: 53 additions & 44 deletions circuit_maintenance_parser/__init__.py
Original file line number Diff line number Diff line change
@@ -1,89 +1,98 @@
"""Notifications parser init."""

from typing import Type, Optional
from typing import Type, Optional, Iterable

from .errors import NonexistentParserError, ParsingError
from .parser import MaintenanceNotification, ICal
from .parsers.eunetworks import ParserEUNetworks
from .parsers.lumen import ParserLumen
from .parsers.megaport import ParserMegaport
from .parsers.ntt import ParserNTT
from .parsers.packetfabric import ParserPacketFabric
from .parsers.telstra import ParserTelstra
from .parsers.zayo import ParserZayo


SUPPORTED_PROVIDER_PARSERS = (
ParserEUNetworks,
ParserLumen,
ParserMegaport,
ParserNTT,
ParserPacketFabric,
ParserTelstra,
ParserZayo,
ICal,

from .providers import (
GenericProvider,
Cogent,
EUNetworks,
Lumen,
Megaport,
NTT,
PacketFabric,
Telstra,
Zayo,
)

SUPPORTED_PROVIDERS = (
GenericProvider,
Cogent,
EUNetworks,
Lumen,
Megaport,
NTT,
PacketFabric,
Telstra,
Zayo,
)

SUPPORTED_PROVIDER_NAMES = [parser.get_default_provider() for parser in SUPPORTED_PROVIDER_PARSERS]
SUPPORTED_ORGANIZER_EMAILS = [parser.get_default_organizer() for parser in SUPPORTED_PROVIDER_PARSERS]
SUPPORTED_PROVIDER_NAMES = [provider.get_provider_type() for provider in SUPPORTED_PROVIDERS]
SUPPORTED_ORGANIZER_EMAILS = [provider.get_default_organizer() for provider in SUPPORTED_PROVIDERS]


def init_parser(**kwargs) -> Optional[MaintenanceNotification]:
def init_parser(**kwargs) -> Optional[GenericProvider]:
"""Returns an instance of the corresponding Notification Parser."""
try:
provider_type = kwargs.get("provider_type")
if not provider_type:
provider_type = "ical"
parser_type = get_parser(provider_type)
return parser_type(**kwargs)
provider_type = GenericProvider.get_provider_type()
provider_parser_class = get_provider_class(provider_type)
return provider_parser_class(**kwargs)

except NonexistentParserError:
return None


def get_parser(provider_name: str) -> Type[MaintenanceNotification]:
"""Returns the notification parser class for a specific provider."""
def get_provider_class(provider_name: str) -> Type[GenericProvider]:
"""Returns the Provider parser class for a specific provider_type."""
provider_name = provider_name.lower()

for parser in SUPPORTED_PROVIDER_PARSERS:
if parser.get_default_provider() == provider_name:
for provider_parser in SUPPORTED_PROVIDERS:
if provider_parser.get_provider_type() == provider_name:
break
else:

raise NonexistentParserError(
f"{provider_name} is not a currently supported parser. Only {', '.join(SUPPORTED_PROVIDER_NAMES)}"
)

return parser
return provider_parser


def get_parser_from_sender(email_sender: str) -> Type[MaintenanceNotification]:
def get_provider_class_from_sender(email_sender: str) -> Type[GenericProvider]:
"""Returns the notification parser class for an email sender address."""

for parser in SUPPORTED_PROVIDER_PARSERS:
if parser.get_default_organizer() == email_sender:
for provider_parser in SUPPORTED_PROVIDERS:
if provider_parser.get_default_organizer() == email_sender:
break
else:
raise NonexistentParserError(
f"{email_sender} is not a currently supported parser. Only {', '.join(SUPPORTED_ORGANIZER_EMAILS)}"
f"{email_sender} is not a currently supported provider parser. Only {', '.join(SUPPORTED_ORGANIZER_EMAILS)}"
)

return parser
return provider_parser


def get_provider_data_type(provider_name: str) -> str:
"""Returns the expected data type for each provider."""
def get_provider_data_types(provider_name: str) -> Iterable[str]:
"""Returns the expected data types for each provider."""
provider_name = provider_name.lower()

for parser in SUPPORTED_PROVIDER_PARSERS:
if parser.get_default_provider() == provider_name:
for provider in SUPPORTED_PROVIDERS:
if provider.get_provider_type() == provider_name:
break
else:
raise NonexistentParserError(
f"{provider_name} is not a currently supported parser. Only {', '.join(SUPPORTED_PROVIDER_NAMES)}"
f"{provider_name} is not a currently supported provider. Only {', '.join(SUPPORTED_PROVIDER_NAMES)}"
)

return parser.get_data_type()
return provider.get_data_types()


__all__ = ["init_parser", "get_parser", "get_parser_from_sender", "get_provider_data_type", "ParsingError"]
__all__ = [
"init_parser",
"get_provider_class",
"get_provider_class_from_sender",
"get_provider_data_types",
"ParsingError",
]
4 changes: 2 additions & 2 deletions circuit_maintenance_parser/cli.py
Original file line number Diff line number Diff line change
@@ -4,14 +4,14 @@

import click

from . import SUPPORTED_PROVIDER_PARSERS, init_parser, ParsingError
from . import SUPPORTED_PROVIDERS, init_parser, ParsingError


@click.command()
@click.option("--raw-file", required=True, help="File containing raw data to parse.")
@click.option(
"--parser",
type=click.Choice([parser.get_default_provider() for parser in SUPPORTED_PROVIDER_PARSERS]),
type=click.Choice([parser.get_provider_type() for parser in SUPPORTED_PROVIDERS]),
default="ical",
help="Parser type.",
)
Loading

0 comments on commit e412b28

Please sign in to comment.