diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index e718ea4b..1afeac3f 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -29,6 +29,7 @@ jobs: python -m pip install --upgrade pip pip install mypy ruff pytest if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + sudo apt-get update sudo apt-get install pandoc tidy ghostscript python3 texlive-fonts-recommended texlive-lang-cyrillic texlive-latex-extra texlive-plain-generic - name: Lint with ruff run: ruff check --output-format=github @@ -45,7 +46,9 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install apt packages (for debbuild) - run: sudo apt-get install debhelper dh-virtualenv dpkg-dev python3-venv automake g++ make libboost-regex-dev libgmp-dev python3 git build-essential + run: | + sudo apt-get update + sudo apt-get install debhelper dh-virtualenv dpkg-dev python3-venv automake g++ make libboost-regex-dev libgmp-dev python3 git build-essential shell: bash - name: Build debian packages run: make builddeb diff --git a/problemtools/metadata.py b/problemtools/metadata.py index a02b341c..ec91d8c8 100644 --- a/problemtools/metadata.py +++ b/problemtools/metadata.py @@ -3,13 +3,16 @@ import re from dataclasses import dataclass, field from enum import StrEnum +from pathlib import Path from typing import Any, Literal, Self, Type, Union from uuid import UUID from pydantic import BaseModel, ConfigDict, Field +import yaml from . import config from . import formatversion +from . import statement_util class ProblemType(StrEnum): @@ -189,7 +192,7 @@ class Metadata(BaseModel): """ problem_format_version: str - type: list[str] + type: list[ProblemType] name: dict[str, str] uuid: UUID | None version: str | None @@ -224,7 +227,12 @@ def from_legacy(cls: Type[Self], legacy: MetadataLegacy, names_from_statements: metadata = legacy.model_dump() metadata['type'] = [metadata['type']] # Support for *ancient* problems where names_from_statements is empty - metadata['name'] = names_from_statements if names_from_statements else {'': metadata['name']} + if names_from_statements: + metadata['name'] = names_from_statements + elif metadata['name']: + metadata['name'] = {'en': metadata['name']} + else: + metadata['name'] = {} metadata['version'] = None def parse_author_field(author: str) -> list[Person]: @@ -301,7 +309,9 @@ def parse_person(person: str | Person) -> Person: def parse_metadata( - version: formatversion.FormatData, problem_yaml_data: dict[str, Any], names_from_statements: dict[str, str] + version: formatversion.FormatData, + problem_yaml_data: dict[str, Any], + names_from_statements: dict[str, str] | None = None, ) -> Metadata: """ Parses a data structure from problem.yaml into a Metadata model @@ -318,8 +328,28 @@ def parse_metadata( if version.name == formatversion.VERSION_LEGACY: legacy_model = MetadataLegacy.model_validate(data) - return Metadata.from_legacy(legacy_model, names_from_statements) + return Metadata.from_legacy(legacy_model, names_from_statements or {}) else: assert version.name == formatversion.VERSION_2023_07 model_2023_07 = Metadata2023_07.model_validate(data) return Metadata.from_2023_07(model_2023_07) + + +def load_metadata(problem_root: Path) -> tuple[Metadata, dict]: + """ + Loads metadata from a problem directory. + + Returns Metadata as well as the raw parsed yaml. The latter is likely only of use to verifyproblem. + Leaks exceptions, which is a bit of a mess. Unclear how to best deal with error handling. + """ + with (problem_root / 'problem.yaml').open() as f: + data = yaml.safe_load(f) + if data is None: # Loading empty yaml returns None + data = {} + + version = formatversion.get_format_data_by_name(data.get('problem_format_version', formatversion.VERSION_LEGACY)) + if version.name == formatversion.VERSION_LEGACY: + names_from_statements = statement_util.load_names_from_statements(problem_root, version) + else: + names_from_statements = None + return parse_metadata(version, data, names_from_statements), data diff --git a/problemtools/statement_util.py b/problemtools/statement_util.py index e7f8513b..bf3da6ea 100644 --- a/problemtools/statement_util.py +++ b/problemtools/statement_util.py @@ -1,19 +1,54 @@ -import os -from typing import Optional, List, Tuple +import collections import html import json +import os import re import subprocess import tempfile from pathlib import Path +from typing import Optional, List, Tuple from . import formatversion -from . import verifyproblem +from . import metadata ALLOWED_IMAGE_EXTENSIONS = ('.png', '.jpg', '.jpeg') # ".svg" FOOTNOTES_STRINGS = ['
', '