Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/apis/common.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

::: t4_devkit.common.io

::: t4_devkit.common.sanity

::: t4_devkit.common.serialize

::: t4_devkit.common.timestamp
Expand Down
53 changes: 53 additions & 0 deletions docs/tutorials/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Following command line tools are supported:

- `t4viz`: Visualize T4 dataset features.
- `t4sanity`: Sanity checker of T4 datasets.

### `t4viz`

Expand Down Expand Up @@ -91,3 +92,55 @@ t4viz <COMMAND> ... -o <OUTPUT_DIR>
```

Note that if you specify `--output` option, viewer will not be spawned.

### `t4sanity`

`t4sanity` performs sanity checks on T4 datasets, reporting any issues in a structured format.
It checks the dataset directories and versions, tries to load them using the `Tier4` library, and reports any exceptions or warnings.

```shell
$ t4sanity -h

Usage: t4sanity [OPTIONS] DB_PARENT

╭─ Arguments ──────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ * db_parent TEXT Path to parent directory of the databases [default: None] [required] │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ --version -v Show the application version and exit. │
│ --include-warning -iw Indicates whether to report any warnings. │
│ --install-completion Install completion for the current shell. │
│ --show-completion Show completion for the current shell, to copy it or customize the installation. │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
```

#### Examples

As an example, we have the following the dataset structure:

```shell
<DATA_ROOT>
├── dataset1
│ └── <VERSION>
│ ├── annotation
│ ├── data
| ...
├── dataset2
│ ├── annotation
│ ├── data
| ...
...
```

To run sanity check ignoring warnings, providing the path to the parent directory of the datasets:

```shell
t4sanity <DATA_ROOT>
```

To run sanity check and report any warnings, use the `-iw; --include-warning` option:

```shell
t4sanity <DATA_ROOT> -iw
```
4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ dependencies = [
"shapely<2.0.0; python_version=='3.10'",
"shapely>=2.0.0; python_version>'3.10'",
"pycocotools>=2.0.8",
"click>=8.1.8",
"pyyaml>=6.0.2",
"typer>=0.15.3",
"tabulate>=0.9.0",
"tqdm>=4.67.1",
]

[dependency-groups]
Expand All @@ -36,6 +37,7 @@ dev = [

[project.scripts]
t4viz = "t4_devkit.cli.visualize:cli"
t4sanity = "t4_devkit.cli.sanity:cli"

[tool.ruff]
line-length = 100
Expand Down
53 changes: 53 additions & 0 deletions t4_devkit/cli/sanity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from __future__ import annotations

from pathlib import Path

import typer
from tabulate import tabulate
from tqdm import tqdm

from t4_devkit.common.sanity import DBException, sanity_check

from .version import version_callback

cli = typer.Typer(
name="t4sanity",
no_args_is_help=True,
context_settings={"help_option_names": ["-h", "--help"]},
pretty_exceptions_enable=False,
)


def _run_sanity_check(db_parent: str, *, include_warning: bool = False) -> list[DBException]:
exceptions: list[DBException] = []

db_dirs: list[Path] = Path(db_parent).glob("*")

for db_root in tqdm(db_dirs, desc=">>>Sanity checking..."):
result = sanity_check(db_root, include_warning=include_warning)
if result:
exceptions.append(result)
return exceptions


@cli.command()
def main(
version: bool = typer.Option(
False,
"--version",
"-v",
help="Show the application version and exit.",
callback=version_callback,
is_eager=True,
),
db_parent: str = typer.Argument(..., help="Path to parent directory of the databases."),
include_warning: bool = typer.Option(
False, "-iw", "--include-warning", help="Indicates whether to report any warnings."
),
) -> None:
exceptions = _run_sanity_check(db_parent, include_warning=include_warning)

headers = ["DatasetID", "Version", "Message"]
table = [[e.dataset_id, e.version, e.message] for e in exceptions]

print(tabulate(table, headers=headers, tablefmt="pretty"))
16 changes: 16 additions & 0 deletions t4_devkit/cli/version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from __future__ import annotations

import importlib
import importlib.metadata

import typer
from rich.console import Console

console = Console()


def version_callback(value: bool) -> None:
if value:
version = importlib.metadata.version("t4-devkit")
console.print(f"[bold green]t4viz[/bold green]: [cyan]{version}[/cyan]")
raise typer.Exit()
18 changes: 2 additions & 16 deletions t4_devkit/cli/visualize.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
from __future__ import annotations

import importlib
import importlib.metadata
import os
from typing import Annotated

import typer
from rich.console import Console

from t4_devkit import Tier4

console = Console()
from .version import version_callback

cli = typer.Typer(
name="t4viz",
Expand Down Expand Up @@ -123,26 +120,15 @@ def _create_dir(dir_path: str | None) -> None:
os.makedirs(dir_path, exist_ok=True)


def _version_callback(value: bool):
if value:
version = importlib.metadata.version("t4-devkit")
console.print(f"[bold green]t4viz[/bold green]: [cyan]{version}[/cyan]")
raise typer.Exit()


@cli.callback()
def main(
version: bool = typer.Option(
False,
"--version",
"-v",
help="Show the application version and exit.",
callback=_version_callback,
callback=version_callback,
is_eager=True,
),
) -> None:
pass


if __name__ == "__main__":
cli()
60 changes: 60 additions & 0 deletions t4_devkit/common/sanity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from __future__ import annotations

import re
import warnings
from pathlib import Path

from attrs import define

from t4_devkit import Tier4

__all__ = ["DBException", "sanity_check"]


@define
class DBException:
"""A dataclass to store error message of the corresponding dataset."""

dataset_id: str
version: str | None
message: str


def sanity_check(db_root: str | Path, *, include_warning: bool = False) -> DBException | None:
"""Perform sanity check and report exception or warning encountered while loading the dataset.

Args:
db_root (str | Path): Path to root directory of the dataset.
include_warning (bool, optional): Indicates whether to report warnings.

Returns:
Exception or warning if exits, otherwise returns None.
"""
db_root_path = Path(db_root)

version_pattern = re.compile(r".*/\d+$")
versions = [d.name for d in db_root_path.iterdir() if version_pattern.match(str(d))]

if versions:
version = sorted(versions)[-1]
data_root = db_root_path.joinpath(version).as_posix()
else:
version = None
data_root = db_root_path.as_posix()

with warnings.catch_warnings():
if include_warning:
warnings.filterwarnings("error")
else:
warnings.filterwarnings("ignore")

try:
_ = Tier4("annotation", data_root=data_root, verbose=False)
exception = None
except Exception as e:
exception = DBException(
dataset_id=db_root_path.name,
version=version,
message=str(e),
)
return exception
Loading