Files
Roman Donchenko 56a4d22914 Remove imports that were deprecated in Python 3.10 (or earlier) (#10053)
Now that we've dropped Python 3.9 support, we can get rid of these. Some
of these were actually deprecated in Python 3.9 too, but they must've
slipped through after the last cleanup round.

Done with:

ruff check --select=UP006,UP035 --target-version=py310 --fix
--unsafe-fixes

plus a bit of manual cleanup.
2025-11-27 17:51:25 +02:00

120 lines
4.0 KiB
Python

# Copyright (C) CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
"""
This script lists resources on the CVAT server
and saves them to JSON files in the `assets` directory.
Before running it, start the test instance by running:
pytest tests/python --start-services
The script determines which endpoints to query by looking at the set of existing JSON files.
For example, if `tasks.json` exists, the script will overwrite it with output of `GET /api/tasks`.
Underscores in the file name are replaced with slashes in the URL path. If the default path for an
asset file is different, it can be overridden via the --asset-path argument.
In addition, `annotations.json` is always saved.
"""
import argparse
import json
from datetime import timezone
from http import HTTPStatus
from pathlib import Path
from typing import Any
from config import ASSETS_DIR, get_method
from dateutil.parser import ParserError, parse
def clean_list_response(data: dict[str, Any]) -> dict[str, Any]:
# truncate milliseconds to 3 digit precision to align with data.json
# "2023-03-30T09:37:31.615123Z" ->
# "2023-03-30T09:37:31.615000Z"
for result in data["results"]:
for k, v in result.items():
if not isinstance(v, str):
continue
try:
parsed_date = parse(v)
except ParserError:
continue
parsed_date = parsed_date.replace(
microsecond=parsed_date.microsecond - (parsed_date.microsecond % 1000)
)
result[k] = parsed_date.astimezone(timezone.utc).isoformat().replace("+00:00", "Z")
return data
def _parse_asset_url_path(s: str) -> tuple[str, str]:
asset_filename, url_path = s.lower().rsplit(":", maxsplit=1)
return asset_filename, url_path
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--assets-dir", type=Path, default=ASSETS_DIR)
parser.add_argument(
"--asset-path",
dest="asset_url_paths",
default=[],
action="append",
type=_parse_asset_url_path,
help="Repeatable, an override for the default inferred URL path for an asset. "
"Format: '<asset filename without extension>:<url path after api/>'",
)
args = parser.parse_args()
assets_dir: Path = args.assets_dir
asset_url_paths: dict[str, str] = dict(args.asset_url_paths)
annotations = {}
access_tokens = {"user": {}}
for dump_path in assets_dir.glob("*.json"):
asset_name = dump_path.stem
endpoint = asset_url_paths.get(asset_name, asset_name.replace("_", "/"))
if asset_name in ("annotations", "access_tokens"):
continue # this will be handled at the end
response = get_method("admin1", endpoint, page_size="all")
with open(dump_path, "w") as f:
json.dump(clean_list_response(response.json()), f, indent=2, sort_keys=True)
if endpoint in ["jobs", "tasks"]:
obj_type = endpoint.removesuffix("s")
annotations[obj_type] = {}
for obj in response.json()["results"]:
oid = obj["id"]
response = get_method("admin1", f"{endpoint}/{oid}/annotations")
if response.status_code == HTTPStatus.OK:
annotations[obj_type][oid] = response.json()
if endpoint == "users":
obj_type = endpoint.removesuffix("s")
for user in response.json()["results"]:
response = get_method(
user["username"], "auth/access_tokens", page_size=100, sort="id"
)
if response.status_code == HTTPStatus.OK:
access_tokens[obj_type][user["username"]] = response.json()["results"]
for filename, data in [
("annotations.json", annotations),
("access_tokens.json", access_tokens),
]:
with open(assets_dir / filename, "w") as f:
json.dump(data, f, indent=2, sort_keys=True)
if __name__ == "__main__":
main()