Skip to content

Script to create entries from the CI. #19

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

Merged
merged 18 commits into from
Apr 15, 2025
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
4 changes: 2 additions & 2 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ jobs:
poetry install
- name: Check schema matches model
run: |
poetry run python ceda_status_validator/generate_schema.py
poetry run python -m ceda_status_validator.generate_schema
diff generated_schema.json statuspage.schema.json
- name: Run the validator
run: |
poetry run python ceda_status_validator/validate.py
poetry run python -m ceda_status_validator.validate
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ repos:
hooks:
- id: validate
name: validate
entry: poetry run python ceda_status_validator/validate.py
entry: poetry run python -m ceda_status_validator.validate
language: system
types: [ json ]
- id: black
Expand Down
18 changes: 16 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@ If you edit the repository directly on the GitHub website, please look out for a

## Editing

The JSON content is stored in `status.json` and is fed by the "raw" view at the URL
There are a few options for editing the status page: you can either edit the JSON manually, with the online Json-Editor, or use the CLI editor (currently just for adding entries).

### Manually

The JSON content is stored in `status.json` and is fed to the [CEDA status page](https://www.ceda.ac.uk/status) using the "raw" view at the URL
https://raw.githubusercontent.com/cedadev/ceda-status/main/status.json

### Json-Editor

The schema file `statuspage.schema.json` is designed to be used in conjunction with the online [Json-Editor](https://pmk65.github.io/jedemov2/dist/demo.html) tool. Hence it includes some configuration for that tool as well as schema rules to validate a `statuspage` JSON document.

The JSON content can be edited manually, but the Json-Editor tool provides useful validation to avoid syntax / structural errors.
Expand All @@ -46,9 +52,17 @@ Procedure:
- Navigate to the "Output" tab: the JSON content should be updated with (valid) changes
- Copy that JSON content back to your own editor as new content for `statuspage.schema.json`
- Committing and pushing your changes to `status.json` in the `main` branch on GitHub means that these changes are made live.
- The CEDA status page at `www.ceda.ac.uk/status` has JQuery code to read the JSON feed from the "raw" URL above.
- The [CEDA status page](https://www.ceda.ac.uk/status) has JQuery code to read the JSON feed from the "raw" URL above.
- A user viewing the status page should now see the updated status content in their browser.

### CLI editor

To add an entry, run the following and fill in the information at the prompts. Make sure you follow the content guidance at the end of this README!

```
poetry run python -m ceda_status_validator.editor add-entry
```

## Content guidance

**PLEASE BE AS TERSE AS POSSIBLE: THERE IS LIMITED SCREEN-SPACE WHEN DISPLAYED IN THE MOTD**
Expand Down
Empty file.
92 changes: 92 additions & 0 deletions ceda_status_validator/editor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import datetime as dt
import json

import click

from . import model


@click.group()
def cli() -> None:
"""Script to help manage entries on the status page."""


@cli.command()
@click.option(
"--status",
prompt=True,
type=click.Choice(list(model.Status.__members__.keys())),
default=model.Status.DOWN.name,
help="Current status of this service to be shown to users.",
)
@click.option(
"--affected-services",
prompt=True,
type=str,
help="Free-text list of services which are affected. E.g. 'sof storage'. Keep it short!",
)
@click.option(
"--summary",
prompt=True,
type=str,
help="One-list summary of the problem. Keep it short!",
)
@click.option(
"--details",
prompt=True,
type=str,
help="Longer summary of the problem. Still keep it short!",
)
@click.option(
"--start-date",
prompt=True,
type=click.DateTime(
formats=[
"%d/%m/%Y %H:%M",
"%Y-%m-%d",
"%Y%m%d",
"%Y-%m-%dT%H:%M:%S",
"%Y-%m-%d %H:%M:%S",
"%Y-%m-%d %H:%M",
]
),
# Round down to nearest hour
default=dt.datetime.now().replace(minute=0, second=0, microsecond=0),
help="Time this incident will be shown to have started, and time of the first update.",
)
def add_entry(
status: str,
affected_services: str,
start_date: dt.datetime,
details: str,
summary: str,
) -> None:
"""Update status.json with an extra incident."""
# Create a new incident from CLI data.
update = model.Update(
date=start_date,
details=details,
)
incident = model.Incident(
status=model.Status[status].value,
affectedServices=affected_services,
summary=summary,
date=start_date,
updates=[update],
)

# Read in existing file and parse it.
with open("status.json", "r", encoding="utf-8") as thefile:
status_file_contents = json.load(thefile)
status_file_model = model.StatusPage.model_validate(status_file_contents)

# Add in the new incident.
status_file_model.root.append(incident)

# Write the file back.
with open("status.json", "w", encoding="utf-8") as thefile:
thefile.write(status_file_model.model_dump_json(indent=2, exclude_none=True))


if __name__ == "__main__":
cli()
2 changes: 1 addition & 1 deletion ceda_status_validator/generate_schema.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json

from model import StatusPage
from .model import StatusPage

schema = StatusPage.model_json_schema()

Expand Down
28 changes: 23 additions & 5 deletions ceda_status_validator/model.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import datetime as dt
import typing
from enum import Enum
from typing import Annotated, Any, Optional

from pydantic import AfterValidator, BaseModel, Field, HttpUrl, RootModel
import pydantic
from pydantic import BaseModel, Field, HttpUrl, RootModel

INPUT_DATE_FORMAT = "%Y-%m-%dT%H:%M"

T = typing.TypeVar("T")

def check_date_format(value: str) -> dt.datetime:

def check_date_format(value: T) -> T:
"""Makes sure the datetime format matches what the MOTD and CEDA status page expect"""
return dt.datetime.strptime(value, INPUT_DATE_FORMAT)
if isinstance(value, str):
dt.datetime.strptime(value, INPUT_DATE_FORMAT)
return value


class Status(Enum):
Expand All @@ -24,20 +30,32 @@ class Status(Enum):
class Update(BaseModel):
"""An update contains further details and an optional URL for more info"""

date: Annotated[str, AfterValidator(check_date_format)]
date: Annotated[dt.datetime, pydantic.BeforeValidator(check_date_format)]
details: str
url: Optional[HttpUrl] = None

@pydantic.field_serializer("date")
def serialize_date(
self, date: dt.datetime, _info: pydantic.FieldSerializationInfo
) -> str:
return str(date.strftime(INPUT_DATE_FORMAT))


class Incident(BaseModel):
"""An incident contains details and a list of updates associated with it"""

status: Status
affectedServices: str
summary: str
date: Annotated[str, AfterValidator(check_date_format)]
date: Annotated[dt.datetime, pydantic.BeforeValidator(check_date_format)]
updates: list[Update] = Field(min_length=1)

@pydantic.field_serializer("date")
def serialize_date(
self, date: dt.datetime, _info: pydantic.FieldSerializationInfo
) -> str:
return str(date.strftime(INPUT_DATE_FORMAT))


class StatusPage(RootModel[Any]):
"""The root of the status page object, containing a list of incidents"""
Expand Down
3 changes: 2 additions & 1 deletion ceda_status_validator/validate.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import sys

from model import StatusPage
from pydantic import ValidationError

from .model import StatusPage

with open("status.json", "r") as status_file:
status_page = status_file.read()

Expand Down
Loading