Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store outages in Outages.yaml #6

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
5 changes: 5 additions & 0 deletions outages.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- name: MGHPCC Shutdown 2024
information: https://nerc.mghpcc.org/event/mghpcc-annual-power-shutdown-2024/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is always a URL, I would just call it "url" (and we would validate as such).

timeframes:
- start: "2024-05-22T08:00:00-0400"
end: "2024-05-28T23:00:00-0400"
33 changes: 33 additions & 0 deletions src/nerc_rates/models_outages.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would either merge these models into models.py, or I would rename models.py to models/rates.py and make this models/outages.py, and then have models/__init__.py import all the models.

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from typing import Annotated
from datetime import datetime, timezone

import pydantic

from .models import Base


class OutageTimeFrames(Base):
start: datetime
end: datetime
Comment on lines +10 to +11
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the Rate model, we use attributes date_from and date_until (and map them to YAML attributes from and until). Any reason not to use the same names here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For rate I chose from and until as a shorthand for "effective from" and "effective until". Here start and end felt more aligned with how I would refer to an outage start and end. Ultimately from and until would work fine too and I don't have a strong opinion either way.



class OutageItemDict(Base):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not actually a dicitonary (unlike RateItemDict, which is).

Suggested change
class OutageItemDict(Base):
class OutageItem(Base):

name: str
information: str
timeframes: list[OutageTimeFrames]


class Outages(pydantic.RootModel):
root: list[OutageItemDict]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
root: list[OutageItemDict]
root: list[OutageItem]


def get_outages_during(self, start, end):
start = datetime.strptime(start, "%Y-%m-%d").astimezone(timezone.utc)
end = datetime.strptime(end, "%Y-%m-%d").astimezone(timezone.utc)

outages = []
for outage in self.root:
for o in outage.timeframes:
if start < o.start < end or start < o.end < end:
outages.append((max(start, o.start), min(end, o.end)))

return outages
28 changes: 28 additions & 0 deletions src/nerc_rates/outages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import requests
import yaml

from .models_outages import Outages

DEFAULT_OUTAGES_FILE = "outages.yaml"
DEFAULT_OUTAGES_URL = (
"https://raw.githubusercontent.com/CCI-MOC/nerc-outages/main/outages.yaml"
)


def load_from_url(url: str | None = None) -> Outages:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want the outages file to be loaded separately from the rates file, or should a single call to load_from_... load both rates and outages data? That would require restructuring some things.

if url is None:
url = DEFAULT_OUTAGES_URL

r = requests.get(url, allow_redirects=True)
r.raise_for_status()
config = yaml.safe_load(r.content.decode("utf-8"))
return Outages.model_validate(config)


def load_from_file(path: str | None = None) -> Outages:
if path is None:
path = DEFAULT_OUTAGES_FILE

with open(path, "r") as f:
config = yaml.safe_load(f)
return Outages.model_validate(config)