Validate generated conference fields
This commit is contained in:
parent
9958632242
commit
82023e16b3
2 changed files with 80 additions and 1 deletions
|
|
@ -80,6 +80,7 @@ Common optional fields:
|
|||
or loosely translate an address. If no reliable rendering is available, omit
|
||||
`address`; coordinates are preferable to an unreadable or inaccurate address.
|
||||
- `free`, `price`, `currency`, `hackathon`, `online`, `attendees`.
|
||||
- When `free: true`, omit `price` and `currency` because they are redundant.
|
||||
- Do not include `going`, `registered`, `accommodation_booked`,
|
||||
`transport_booked`, or `trip` unless the source explicitly says they apply to
|
||||
my attendance.
|
||||
|
|
@ -528,6 +529,27 @@ def validate_country(conf: dict[str, typing.Any]) -> None:
|
|||
conf["country"] = match.alpha_2.lower()
|
||||
|
||||
|
||||
def validate_series(
|
||||
conf: dict[str, typing.Any], series: dict[str, ConferenceSeries]
|
||||
) -> None:
|
||||
"""Ensure a generated series ID exists in conference_series.yaml."""
|
||||
series_id = conf.get("series")
|
||||
if series_id is None:
|
||||
return
|
||||
if not isinstance(series_id, str) or series_id not in series:
|
||||
raise ValueError(
|
||||
f"Generated conference uses unknown series {series_id!r}. "
|
||||
"Add it to conference_series.yaml first or remove the series field."
|
||||
)
|
||||
|
||||
|
||||
def normalize_free_event_fields(conf: dict[str, typing.Any]) -> None:
|
||||
"""Remove redundant pricing fields from free conferences."""
|
||||
if conf.get("free") is True:
|
||||
conf.pop("price", None)
|
||||
conf.pop("currency", None)
|
||||
|
||||
|
||||
def parse_yaml_datetime(value: typing.Any) -> datetime | None:
|
||||
"""Convert YAML date/datetime values to a datetime."""
|
||||
if isinstance(value, datetime):
|
||||
|
|
@ -749,6 +771,8 @@ def add_new_conference(url: str, yaml_path: str) -> bool:
|
|||
assert isinstance(new_conf, dict)
|
||||
|
||||
validate_country(new_conf)
|
||||
validate_series(new_conf, series)
|
||||
normalize_free_event_fields(new_conf)
|
||||
apply_event_metadata_dates(new_conf, soup)
|
||||
normalize_dates_field(new_conf)
|
||||
normalise_end_field(new_conf, source_text)
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@
|
|||
from datetime import date, datetime
|
||||
import typing
|
||||
|
||||
import lxml.html # type: ignore[import-untyped]
|
||||
import lxml.html
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from agenda import add_new_conference
|
||||
from agenda.conference import ConferenceSeries
|
||||
|
||||
|
||||
def test_parse_osm_url_mlat_mlon() -> None:
|
||||
|
|
@ -231,6 +232,7 @@ def test_build_prompt_includes_nested_dates_and_series() -> None:
|
|||
assert "March 2027" in prompt
|
||||
assert "For an address written in a non-Latin script" in prompt
|
||||
assert "If no reliable rendering is available, omit" in prompt
|
||||
assert "When `free: true`, omit `price` and `currency`" in prompt
|
||||
|
||||
|
||||
def test_validate_country_normalises_name() -> None:
|
||||
|
|
@ -242,6 +244,53 @@ def test_validate_country_normalises_name() -> None:
|
|||
assert conf["country"] == "gb"
|
||||
|
||||
|
||||
def test_validate_series_rejects_unknown_generated_id() -> None:
|
||||
"""Generated series IDs must exist in conference_series.yaml."""
|
||||
conf: dict[str, typing.Any] = {"series": "state-of-the-map-asia"}
|
||||
|
||||
with pytest.raises(ValueError, match="unknown series 'state-of-the-map-asia'"):
|
||||
add_new_conference.validate_series(
|
||||
conf,
|
||||
{"state-of-the-map": {"name": "State of the Map"}},
|
||||
)
|
||||
|
||||
|
||||
def test_validate_series_accepts_known_id_or_missing_field() -> None:
|
||||
"""Known series IDs and conferences without a series should pass."""
|
||||
series: dict[str, ConferenceSeries] = {
|
||||
"state-of-the-map": {"name": "State of the Map"}
|
||||
}
|
||||
|
||||
add_new_conference.validate_series({"series": "state-of-the-map"}, series)
|
||||
add_new_conference.validate_series({}, series)
|
||||
|
||||
|
||||
def test_normalize_free_event_fields_removes_price_and_currency() -> None:
|
||||
"""Free events should not retain redundant pricing fields."""
|
||||
conf: dict[str, typing.Any] = {
|
||||
"free": True,
|
||||
"price": 0,
|
||||
"currency": "JPY",
|
||||
}
|
||||
|
||||
add_new_conference.normalize_free_event_fields(conf)
|
||||
|
||||
assert conf == {"free": True}
|
||||
|
||||
|
||||
def test_normalize_free_event_fields_keeps_paid_event_pricing() -> None:
|
||||
"""Pricing fields should remain for events that are not marked free."""
|
||||
conf: dict[str, typing.Any] = {
|
||||
"free": False,
|
||||
"price": 25,
|
||||
"currency": "GBP",
|
||||
}
|
||||
|
||||
add_new_conference.normalize_free_event_fields(conf)
|
||||
|
||||
assert conf == {"free": False, "price": 25, "currency": "GBP"}
|
||||
|
||||
|
||||
def test_normalise_end_field_defaults_single_day_date() -> None:
|
||||
"""Non-Geomob conferences should default end to the start date."""
|
||||
conf: dict[str, typing.Any] = {
|
||||
|
|
@ -370,6 +419,12 @@ def test_add_new_conference_reuses_generic_url_for_new_year(
|
|||
) -> None:
|
||||
"""Generic URLs with digits in the domain should not be skipped early."""
|
||||
yaml_path = tmp_path / "conferences.yaml"
|
||||
(tmp_path / "conference_series.yaml").write_text(
|
||||
yaml.dump(
|
||||
{"foss4g-north-america": {"name": "FOSS4G North America"}},
|
||||
sort_keys=False,
|
||||
)
|
||||
)
|
||||
yaml_path.write_text(
|
||||
yaml.dump(
|
||||
[
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue