Skip to content

Commit ab32553

Browse files
authored
feat(sanity): check if category indices are consistent (#255)
* feat(sanity): check if category indices are consistent Signed-off-by: Max SCHMELLER <max.schmeller@tier4.jp> * chore: remove backticks, improve wording Signed-off-by: Max SCHMELLER <max.schmeller@tier4.jp> * docs: add REC007 to `requirement` docs Signed-off-by: Max SCHMELLER <max.schmeller@tier4.jp> --------- Signed-off-by: Max SCHMELLER <max.schmeller@tier4.jp>
1 parent d9b44c8 commit ab32553

3 files changed

Lines changed: 61 additions & 0 deletions

File tree

docs/schema/requirement.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
| `REC004` | `ego-pose-not-empty` | `ERROR` | `EgoPose` record is not empty. |
2525
| `REC005` | `calibrated-sensor-non-empty` | `ERROR` | `CalibratedSensor` record is not empty. |
2626
| `REC006` | `instance-not-empty` | `ERROR` | `Instance` record is not empty if either `SampleAnnotation` or `ObjectAnnotation` is not empty. |
27+
| `REC007` | `category-indices-consistent` | `ERROR` | `Category` records must either all have a unique `index` or all have a `null` index. |
2728

2829
## Reference (`REF`)
2930

t4_devkit/sanity/record/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
from .rec004 import * # noqa
77
from .rec005 import * # noqa
88
from .rec006 import * # noqa
9+
from .rec007 import * # noqa

t4_devkit/sanity/record/rec007.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
from returns.maybe import Maybe, Nothing, Some
6+
7+
from t4_devkit.schema import SchemaName
8+
9+
from ..checker import Checker, RuleID, RuleName, Severity
10+
from ..registry import CHECKERS
11+
from ..result import Reason
12+
from ..safety import load_json_safe
13+
14+
if TYPE_CHECKING:
15+
from ..context import SanityContext
16+
17+
__all__ = ["REC007"]
18+
19+
20+
@CHECKERS.register()
21+
class REC007(Checker):
22+
"""A checker of REC007."""
23+
24+
id = RuleID("REC007")
25+
name = RuleName("category-indices-consistent")
26+
severity = Severity.ERROR
27+
description = "All categories must either have a unique 'index' or all have a 'null' index."
28+
schema = SchemaName.CATEGORY
29+
30+
def can_skip(self, context: SanityContext) -> Maybe[Reason]:
31+
match context.to_schema_file(self.schema):
32+
case Some(x):
33+
if not x.exists():
34+
return Maybe.from_value(Reason(f"Missing {self.schema.filename}"))
35+
else:
36+
return Nothing
37+
case _:
38+
return Maybe.from_value(Reason("Missing 'annotation' directory path"))
39+
40+
def check(self, context: SanityContext) -> list[Reason] | None:
41+
filepath = context.to_schema_file(self.schema).unwrap()
42+
records = load_json_safe(filepath).unwrap()
43+
44+
indices = [record.get("index") for record in records]
45+
all_none = all(index is None for index in indices)
46+
if all_none:
47+
return None
48+
49+
some_none = any(index is None for index in indices)
50+
if some_none:
51+
return [
52+
Reason("All categories either must have an 'index' set or all have a 'null' index.")
53+
]
54+
55+
has_duplicates = len(set(indices)) < len(indices)
56+
if has_duplicates:
57+
return [Reason("Categories must have unique 'index' values.")]
58+
59+
return None

0 commit comments

Comments
 (0)