Coverage for src / competitive_verifier / documents / render_data.py: 100%
56 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-04-26 12:38 +0900
« prev ^ index » next coverage.py v7.13.1, created at 2026-04-26 12:38 +0900
1import datetime
2import enum
3from collections.abc import Sequence
4from typing import Annotated, Any
6from pydantic import BaseModel, ConfigDict, PlainSerializer, model_validator
7from pydantic.alias_generators import to_camel
9from competitive_verifier.models import ForcePosixPath, SortedPathList, TestcaseResult
12class FileType(enum.Enum):
13 LIBRARY = enum.auto()
14 TEST = enum.auto()
17class StatusIcon(str, enum.Enum):
18 def __new__(cls, file_type: FileType, result: str, _: object) -> "StatusIcon":
19 value = f"{file_type.name}_{result}"
20 member = str.__new__(cls, value)
21 member._value_ = value
22 return member
24 def __init__(self, file_type: FileType, result: str, is_success: bool) -> None: # noqa: FBT001
25 super().__init__()
26 self.file_type = file_type
27 self.result = result
28 self.is_success = is_success
30 LIBRARY_ALL_AC = (FileType.LIBRARY, "ALL_AC", True)
31 LIBRARY_PARTIAL_AC = (FileType.LIBRARY, "PARTIAL_AC", True)
32 LIBRARY_SOME_WA = (FileType.LIBRARY, "SOME_WA", False)
33 LIBRARY_ALL_WA = (FileType.LIBRARY, "ALL_WA", False)
34 LIBRARY_NO_TESTS = (FileType.LIBRARY, "NO_TESTS", True)
35 TEST_ACCEPTED = (FileType.TEST, "ACCEPTED", True)
36 TEST_WRONG_ANSWER = (FileType.TEST, "WRONG_ANSWER", False)
37 TEST_WAITING_JUDGE = (FileType.TEST, "WAITING_JUDGE", True)
39 @property
40 def is_failed(self) -> bool:
41 return not self.is_success
43 @property
44 def is_test(self) -> bool:
45 return self.file_type == FileType.TEST
48class RenderBaseModel(BaseModel):
49 model_config = ConfigDict(
50 alias_generator=to_camel,
51 populate_by_name=True,
52 extra="allow",
53 )
56class RenderLink(RenderBaseModel):
57 path: ForcePosixPath
58 filename: str
59 icon: StatusIcon
60 title: str | None = None
62 @model_validator(mode="after")
63 def validate_title(self: "RenderLink") -> "RenderLink":
64 if self.path.as_posix() == self.title:
65 self.title = None
66 return self
69class Dependency(RenderBaseModel):
70 type: str
71 files: list[RenderLink]
74class EmbeddedCode(RenderBaseModel):
75 name: str
76 code: str
79class EnvTestcaseResult(RenderBaseModel, TestcaseResult):
80 environment: str | None
83class CategorizedIndex(RenderBaseModel):
84 name: str
85 pages: list[RenderLink]
88class IndexFiles(RenderBaseModel):
89 type: str
90 categories: list[CategorizedIndex]
93class PageRenderData(RenderBaseModel):
94 path: ForcePosixPath
95 path_extension: str
96 title: str | None
97 embedded: list[EmbeddedCode]
99 timestamp: Annotated[
100 datetime.datetime,
101 PlainSerializer(lambda x: str(x), return_type=str, when_used="json"),
102 ]
103 attributes: dict[str, Any]
104 testcases: list[EnvTestcaseResult] | None = None
106 is_failed: bool
107 is_verification_file: bool
108 verification_status: StatusIcon
110 depends_on: SortedPathList
111 required_by: SortedPathList
112 verified_with: SortedPathList
114 document_path: ForcePosixPath | None = None
115 dependencies: list[Dependency]
118class CodePageData(PageRenderData):
119 document_content: str | None
122class MultiCodePageData(RenderBaseModel):
123 path: ForcePosixPath
124 verification_status: StatusIcon
125 is_failed: bool
126 codes: Sequence[CodePageData]
127 dependencies: list[Dependency]
130class IndexRenderData(RenderBaseModel):
131 top: list[IndexFiles]