Coverage for src / rendercv / schema / models / cv / entries / bases / entry_with_date.py: 100%
16 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-25 13:49 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-25 13:49 +0000
1import re
2from datetime import date as Date
3from typing import Annotated
5import pydantic
7from .entry import BaseEntry
10def validate_arbitrary_date(date: int | str) -> int | str:
11 """Validate date format while allowing flexible user input.
13 Why:
14 Users enter dates like "Fall 2023" or "2020-09" for events. Strict
15 dates (YYYY-MM-DD/YYYY-MM/YYYY) get validated via ISO parsing, while
16 custom text passes through for template rendering. ValueError from
17 fromisoformat() propagates to Pydantic, which converts it into a
18 specific user-friendly message (e.g., "The month must be between
19 1 and 12.").
21 Args:
22 date: Date value to validate.
24 Returns:
25 Original date if valid.
26 """
27 date_str = str(date)
29 if re.fullmatch(r"\d{4}-\d{2}-\d{2}", date_str):
30 Date.fromisoformat(date_str)
31 elif re.fullmatch(r"\d{4}-\d{2}", date_str):
32 Date.fromisoformat(f"{date_str}-01")
34 return date
37type ArbitraryDate = Annotated[
38 int | str, pydantic.AfterValidator(validate_arbitrary_date)
39]
42class BaseEntryWithDate(BaseEntry):
43 model_config = pydantic.ConfigDict(json_schema_extra={"description": None})
45 date: ArbitraryDate | None = pydantic.Field(
46 default=None,
47 description=(
48 "The date of this event in YYYY-MM-DD, YYYY-MM, or YYYY format, or any"
49 " custom text like 'Fall 2023'. Use this for single-day or imprecise dates."
50 " For date ranges, use `start_date` and `end_date` instead."
51 ),
52 examples=["2020-09-24", "2020-09", "2020", "Fall 2023", "Summer 2020"],
53 )