Skip to content

Commit

Permalink
fix: Allow the switching of data sources
Browse files Browse the repository at this point in the history
  • Loading branch information
PurpleBooth committed Jul 4, 2023
1 parent de7cb74 commit 5bf68de
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 66 deletions.
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,29 @@ poetry run carbon_guard --help
│ * --max-carbon-intensi… INTEGER Set the max carbon │
│ intensity in │
│ gCO2eq/kWh. │
│ [env var: │
│ MAX_CARBON_INTENSITY] │
│ [default: None] │
│ [required] │
│ --repository-mode [file|uk-carbon-inte Where to read carbon │
│ --data-source [file|uk-carbon-inte Where to read carbon │
│ nsity] intensity data from │
│ [env var: │
│ REPOSITORY_MODE] │
│ [default: │
│ uk-carbon-intensity] │
│ --from-file-carbon-i… PATH File to read carbon │
│ intensity from in file │
│ mode │
│ [env var: │
│ FROM_FILE_CARBON_INTE… │
│ [default: │
│ .carbon_intensity] │
│ --uk-carbon-intensit… PARSE_URL URL for the carbon │
│ intensity API │
│ [env var: │
│ UK_CARBON_INTENSITY_A… │
│ [default: │
│ https://api.carbonint… │
│ --help Show this message and │
│ exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
Expand All @@ -50,6 +65,17 @@ carbon_intensity_is 999
poetry run carbon_guard --max-carbon-intensity=999
```

``` ,verify(script_name="carbon_check", stream=stdout)
Carbon levels under threshold, proceeding.
```


You may change the data source by specifying the `--data-source` flag.

```shell,script(name="carbon_check", expected_exit_code=0)
poetry run carbon_guard --data-source uk-carbon-intensity --max-carbon-intensity=100000
```

``` ,verify(script_name="carbon_check", stream=stdout)
Carbon levels under threshold, proceeding.
```
59 changes: 51 additions & 8 deletions src/main.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,75 @@
from enum import StrEnum
from pathlib import Path

from httpx import URL

UK_CARBON_INTENSITY_API_BASE_URL: URL = URL("https://api.carbonintensity.org.uk")
FILE_FOR_INTENSITY_READING: str = ".carbon_intensity"

import typer
from typing_extensions import Annotated

from src.repos.carbon_intensity import FromFileCarbonIntensityRepo
from src.repos.carbon_intensity import (
CarbonIntensityRepo,
FromFileCarbonIntensityRepo,
UkCarbonIntensityApiRepo,
)


class RepositoryMode(StrEnum):
class DataSource(StrEnum):
FILE = "file"
UK_CARBON_INTENSITY = "uk-carbon-intensity"


def parse_url(url: str) -> URL:
return URL(url)


def main(
max_carbon_intensity: Annotated[
int,
typer.Option(help="Set the max carbon intensity in gCO2eq/kWh."),
typer.Option(
help="Set the max carbon intensity in gCO2eq/kWh.",
envvar="MAX_CARBON_INTENSITY",
),
],
repository_mode: Annotated[
RepositoryMode,
data_source: Annotated[
DataSource,
typer.Option(
case_sensitive=False,
help="Where to read carbon intensity data from",
envvar="REPOSITORY_MODE",
),
] = RepositoryMode.UK_CARBON_INTENSITY,
] = DataSource.UK_CARBON_INTENSITY,
from_file_carbon_intensity_file_path: Annotated[
Path,
typer.Option(
help="File to read carbon intensity from in file mode",
envvar="FROM_FILE_CARBON_INTENSITY_FILE_PATH",
),
] = Path(FILE_FOR_INTENSITY_READING),
uk_carbon_intensity_api_base_url: Annotated[
URL,
typer.Option(
help="URL for the carbon intensity API",
envvar="UK_CARBON_INTENSITY_API_BASE_URL",
parser=parse_url,
),
] = UK_CARBON_INTENSITY_API_BASE_URL,
) -> None:
file_intensity_repo = FromFileCarbonIntensityRepo(Path(".carbon_intensity"))
if file_intensity_repo.get_carbon_intensity() > max_carbon_intensity:
intensity_repo: CarbonIntensityRepo = FromFileCarbonIntensityRepo(
from_file_carbon_intensity_file_path
)

match data_source:
case DataSource.FILE:
intensity_repo = intensity_repo
case DataSource.UK_CARBON_INTENSITY:
intensity_repo = UkCarbonIntensityApiRepo(
base_url=uk_carbon_intensity_api_base_url
)

if intensity_repo.get_carbon_intensity() > max_carbon_intensity:
typer.echo("Carbon levels exceed threshold, skipping.")
raise typer.Exit(1)

Expand Down
7 changes: 3 additions & 4 deletions src/repos/carbon_intensity.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from pathlib import Path
from typing import Protocol
from urllib.parse import ParseResult

from httpx import Client
from httpx import URL, Client
from pydantic import BaseModel, field_validator


Expand Down Expand Up @@ -49,8 +48,8 @@ def ensure_data_is_not_empty(


class UkCarbonIntensityApiRepo:
def __init__(self, base_url: ParseResult):
self._client = Client(base_url=base_url.geturl(), http2=True)
def __init__(self, base_url: URL):
self._client = Client(base_url=base_url, http2=True)

def get_carbon_intensity(self) -> int:
response = self._client.get("/intensity")
Expand Down
107 changes: 54 additions & 53 deletions tests/repos/test_carbon_intensity.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
import tempfile
from pathlib import Path
from typing import Generator
from urllib.parse import urlparse

import pytest
from httpx import URL
from pytest import FixtureRequest
from wiremock.constants import Config
from wiremock.resources.mappings import (
Expand All @@ -25,66 +25,67 @@
)


@pytest.fixture(params=["inmemory", "file", "uk-carbon-intensity"])
def carbon_intensity_repo(
request: FixtureRequest, expected_carbon_intensity: int
) -> Generator[CarbonIntensityRepo, None, None]:
if request.param == "inmemory":
yield InMemoryCarbonIntensityRepo(carbon_intensity=expected_carbon_intensity)
return
class TestCarbonIntensityRepository:
@pytest.fixture(params=["inmemory", "file", "uk-carbon-intensity"])
def carbon_intensity_repo(
self, request: FixtureRequest, expected_carbon_intensity: int
) -> Generator[CarbonIntensityRepo, None, None]:
if request.param == "inmemory":
yield InMemoryCarbonIntensityRepo(
carbon_intensity=expected_carbon_intensity
)
return

if request.param == "file":
with tempfile.NamedTemporaryFile() as file:
file.write(str(expected_carbon_intensity).encode("utf8"))
file.seek(0)
yield FromFileCarbonIntensityRepo(file_path=Path(file.name))
if request.param == "file":
with tempfile.NamedTemporaryFile() as file:
file.write(str(expected_carbon_intensity).encode("utf8"))
file.seek(0)
yield FromFileCarbonIntensityRepo(file_path=Path(file.name))

if request.param == "uk-carbon-intensity":
with WireMockServer() as wm:
base_url = f"http://localhost:{wm.port}"
Config.base_url = f"{base_url}/__admin"
if request.param == "uk-carbon-intensity":
with WireMockServer() as wm:
base_url = f"http://localhost:{wm.port}"
Config.base_url = f"{base_url}/__admin"

current_intensity = Mapping(
priority=100,
request=MappingRequest(
method=HttpMethods.GET,
url_path="/intensity",
),
response=MappingResponse(
status=200,
body=json.dumps(
{
"data": [
{
"from": "2023-07-04T10:00Z",
"to": "2023-07-04T10:30Z",
"intensity": {
"forecast": 116,
"actual": expected_carbon_intensity,
"index": "moderate",
},
}
]
}
current_intensity = Mapping(
priority=100,
request=MappingRequest(
method=HttpMethods.GET,
url_path="/intensity",
),
),
)

Mappings.create_mapping(mapping=current_intensity)
yield UkCarbonIntensityApiRepo(base_url=urlparse(base_url))

return None
response=MappingResponse(
status=200,
body=json.dumps(
{
"data": [
{
"from": "2023-07-04T10:00Z",
"to": "2023-07-04T10:30Z",
"intensity": {
"forecast": 116,
"actual": expected_carbon_intensity,
"index": "moderate",
},
}
]
}
),
),
)

Mappings.create_mapping(mapping=current_intensity)
yield UkCarbonIntensityApiRepo(base_url=URL(base_url))

@pytest.fixture()
def expected_carbon_intensity() -> int:
return 7
return None

@pytest.fixture()
def expected_carbon_intensity(self) -> int:
return 7

def test_gives_me_a_global_carbon_intensity_repo(
carbon_intensity_repo: CarbonIntensityRepo, expected_carbon_intensity: int
) -> None:
assert carbon_intensity_repo.get_carbon_intensity() == expected_carbon_intensity
def test_gives_me_a_global_carbon_intensity_repo(
self, carbon_intensity_repo: CarbonIntensityRepo, expected_carbon_intensity: int
) -> None:
assert carbon_intensity_repo.get_carbon_intensity() == expected_carbon_intensity


def test_no_uk_carbon_intensity_data_raises() -> None:
Expand Down

0 comments on commit 5bf68de

Please sign in to comment.