Add project proxy tests in SDK (#5431)

Ported from https://github.com/opencv/cvat/pull/4819

- Fixed project APIs in SDK core
- Added tests for projects
main
Maxim Zhiltsov 3 years ago committed by GitHub
parent 15eecf81af
commit 6cf67dba9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,7 +6,7 @@ from __future__ import annotations
import json
from pathlib import Path
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, List, Optional
from cvat_sdk.api_client import apis, models
from cvat_sdk.core.downloading import Downloader
@ -19,6 +19,7 @@ from cvat_sdk.core.proxies.model_proxy import (
ModelUpdateMixin,
build_model_bases,
)
from cvat_sdk.core.proxies.tasks import Task
from cvat_sdk.core.uploading import DatasetUploader, Uploader
if TYPE_CHECKING:
@ -30,7 +31,10 @@ _ProjectEntityBase, _ProjectRepoBase = build_model_bases(
class Project(
_ProjectEntityBase, models.IProjectRead, ModelUpdateMixin[models.IPatchedProjectWriteRequest]
_ProjectEntityBase,
models.IProjectRead,
ModelUpdateMixin[models.IPatchedProjectWriteRequest],
ModelDeleteMixin,
):
_model_partial_update_arg = "patched_project_write_request"
@ -117,13 +121,15 @@ class Project(
(annotations, _) = self.api.retrieve_annotations(self.id)
return annotations
def get_tasks(self) -> List[Task]:
return [Task(self._client, m) for m in self.api.list_tasks(id=self.id)[0].results]
class ProjectsRepo(
_ProjectRepoBase,
ModelCreateMixin[Project, models.IProjectWriteRequest],
ModelListMixin[Project],
ModelRetrieveMixin[Project],
ModelDeleteMixin,
):
_entity_type = Project
@ -170,12 +176,12 @@ class ProjectsRepo(
filename = Path(filename)
if status_check_period is None:
status_check_period = self.config.status_check_period
status_check_period = self._client.config.status_check_period
params = {"filename": filename.name}
url = self.api_map.make_endpoint_url(self.api.create_backup_endpoint.path)
url = self._client.api_map.make_endpoint_url(self.api.create_backup_endpoint.path)
uploader = Uploader(self)
uploader = Uploader(self._client)
response = uploader.upload_file(
url,
filename,
@ -195,6 +201,8 @@ class ProjectsRepo(
)
project_id = json.loads(response.data)["id"]
self._client.logger.info(f"Project has been imported sucessfully. Project ID: {project_id}")
self._client.logger.info(
f"Project has been imported successfully. Project ID: {project_id}"
)
return self.retrieve(project_id)

@ -3,6 +3,7 @@
# SPDX-License-Identifier: MIT
from pathlib import Path
from zipfile import ZipFile
import pytest
from cvat_sdk import Client
@ -56,3 +57,13 @@ def fxt_login(admin_user: str, restore_db_per_class):
with client:
client.login((user, USER_PASS))
yield (client, user)
@pytest.fixture
def fxt_coco_dataset(tmp_path: Path, fxt_image_file: Path, fxt_coco_file: Path):
dataset_path = tmp_path / "coco_dataset.zip"
with ZipFile(dataset_path, "x") as f:
f.write(fxt_image_file, arcname="images/" + fxt_image_file.name)
f.write(fxt_coco_file, arcname="annotations/instances_default.json")
yield dataset_path

@ -0,0 +1,189 @@
# Copyright (C) 2022 CVAT.ai Corporation
#
# SPDX-License-Identifier: MIT
import io
from logging import Logger
from pathlib import Path
from typing import Tuple
import pytest
from cvat_sdk import Client, models
from cvat_sdk.api_client import exceptions
from cvat_sdk.core.proxies.projects import Project
from cvat_sdk.core.proxies.tasks import ResourceType, Task
from cvat_sdk.core.utils import filter_dict
from .util import make_pbar
class TestProjectUsecases:
@pytest.fixture(autouse=True)
def setup(
self,
tmp_path: Path,
fxt_login: Tuple[Client, str],
fxt_logger: Tuple[Logger, io.StringIO],
fxt_stdout: io.StringIO,
):
self.tmp_path = tmp_path
logger, self.logger_stream = fxt_logger
self.stdout = fxt_stdout
self.client, self.user = fxt_login
self.client.logger = logger
api_client = self.client.api_client
for k in api_client.configuration.logger:
api_client.configuration.logger[k] = logger
@pytest.fixture
def fxt_new_task(self, fxt_image_file: Path):
task = self.client.tasks.create_from_data(
spec={
"name": "test_task",
"labels": [{"name": "car"}, {"name": "person"}],
},
resource_type=ResourceType.LOCAL,
resources=[str(fxt_image_file)],
data_params={"image_quality": 80},
)
return task
@pytest.fixture
def fxt_task_with_shapes(self, fxt_new_task: Task):
fxt_new_task.set_annotations(
models.LabeledDataRequest(
shapes=[
models.LabeledShapeRequest(
frame=0,
label_id=fxt_new_task.labels[0].id,
type="rectangle",
points=[1, 1, 2, 2],
),
],
)
)
return fxt_new_task
@pytest.fixture
def fxt_new_project(self):
project = self.client.projects.create(
spec={
"name": "test_project",
"labels": [{"name": "car"}, {"name": "person"}],
},
)
return project
@pytest.fixture
def fxt_empty_project(self):
return self.client.projects.create(spec={"name": "test_project"})
@pytest.fixture
def fxt_project_with_shapes(self, fxt_task_with_shapes: Task):
project = self.client.projects.create(
spec=models.ProjectWriteRequest(
name="test_project",
labels=[
models.PatchedLabelRequest(
**filter_dict(label.to_dict(), drop=["id", "has_parent"])
)
for label in fxt_task_with_shapes.labels
],
)
)
fxt_task_with_shapes.update(models.PatchedTaskWriteRequest(project_id=project.id))
project.fetch()
return project
@pytest.fixture
def fxt_backup_file(self, fxt_project_with_shapes: Project):
backup_path = self.tmp_path / "backup.zip"
fxt_project_with_shapes.download_backup(str(backup_path))
yield backup_path
def test_can_create_empty_project(self):
project = self.client.projects.create(spec=models.ProjectWriteRequest(name="test project"))
assert project.id != 0
assert project.name == "test project"
def test_can_create_project_from_dataset(self, fxt_coco_dataset: Path):
pbar_out = io.StringIO()
pbar = make_pbar(file=pbar_out)
project = self.client.projects.create_from_dataset(
spec=models.ProjectWriteRequest(name="project with data"),
dataset_path=fxt_coco_dataset,
dataset_format="COCO 1.0",
pbar=pbar,
)
assert project.get_tasks()[0].size == 1
assert "100%" in pbar_out.getvalue().strip("\r").split("\r")[-1]
assert self.stdout.getvalue() == ""
def test_can_retrieve_project(self, fxt_new_project: Project):
project_id = fxt_new_project.id
project = self.client.projects.retrieve(project_id)
assert project.id == project_id
assert self.stdout.getvalue() == ""
def test_can_list_projects(self, fxt_new_project: Project):
project_id = fxt_new_project.id
projects = self.client.projects.list()
assert any(p.id == project_id for p in projects)
assert self.stdout.getvalue() == ""
def test_can_update_project(self, fxt_new_project: Project):
fxt_new_project.update(models.PatchedProjectWriteRequest(name="foo"))
retrieved_project = self.client.projects.retrieve(fxt_new_project.id)
assert retrieved_project.name == "foo"
assert fxt_new_project.name == retrieved_project.name
assert self.stdout.getvalue() == ""
def test_can_delete_project(self, fxt_new_project: Project):
fxt_new_project.remove()
with pytest.raises(exceptions.NotFoundException):
fxt_new_project.fetch()
assert self.stdout.getvalue() == ""
def test_can_get_tasks(self, fxt_project_with_shapes: Project):
task_ids = set(fxt_project_with_shapes.tasks)
tasks = fxt_project_with_shapes.get_tasks()
assert len(tasks) == 1
assert {tasks[0].id} == task_ids
def test_can_download_backup(self, fxt_project_with_shapes: Project):
pbar_out = io.StringIO()
pbar = make_pbar(file=pbar_out)
backup_path = self.tmp_path / "backup.zip"
fxt_project_with_shapes.download_backup(str(backup_path), pbar=pbar)
assert backup_path.stat().st_size > 0
assert "100%" in pbar_out.getvalue().strip("\r").split("\r")[-1]
assert self.stdout.getvalue() == ""
def test_can_create_from_backup(self, fxt_backup_file: Path):
pbar_out = io.StringIO()
pbar = make_pbar(file=pbar_out)
restored_project = self.client.projects.create_from_backup(fxt_backup_file, pbar=pbar)
assert restored_project.get_tasks()[0].size == 1
assert "100%" in pbar_out.getvalue().strip("\r").split("\r")[-1]
assert self.stdout.getvalue() == ""
Loading…
Cancel
Save