Skip to content

Commit

Permalink
Add organization support to the CLI (#6317)
Browse files Browse the repository at this point in the history
This is the CLI equivalent of #5718.
  • Loading branch information
SpecLad authored Jun 15, 2023
1 parent fb87d7d commit 871756d
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 16 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- \[Server API\] An option to supply custom file ordering for task data uploads (<https://github.com/opencv/cvat/pull/5083>)
- New option ``semi-auto`` is available as annotations source (<https://github.com/opencv/cvat/pull/6263>)
- \[CLI\] An option to select the organization
(<https://github.com/opencv/cvat/pull/6317>)

### Changed
- Allowed to use dataset manifest for the `predefined` sorting method for task data (<https://github.com/opencv/cvat/pull/5083>)
Expand Down
6 changes: 5 additions & 1 deletion cvat-cli/src/cvat_cli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,17 @@ def build_client(parsed_args: SimpleNamespace, logger: logging.Logger) -> Client
if parsed_args.server_port:
url += f":{parsed_args.server_port}"

return Client(
client = Client(
url=url,
logger=logger,
config=config,
check_server_version=False, # version is checked after auth to support versions < 2.3
)

client.organization_slug = parsed_args.organization

return client


def main(args: List[str] = None):
actions = {
Expand Down
9 changes: 9 additions & 0 deletions cvat-cli/src/cvat_cli/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ def make_cmdline_parser() -> argparse.ArgumentParser:
default=None,
help="port (default: 80 for http and 443 for https connections)",
)
parser.add_argument(
"--organization",
"--org",
metavar="SLUG",
help="""short name (slug) of the organization
to use when listing or creating resources;
set to blank string to use the personal workspace
(default: list all accessible objects, create in personal workspace)""",
)
parser.add_argument(
"--debug",
action="store_const",
Expand Down
29 changes: 22 additions & 7 deletions site/content/en/docs/api_sdk/cli/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,29 @@ We support Python versions 3.7 - 3.9.
You can get help with `cvat-cli --help`.

```
usage: cvat-cli [-h] [--auth USER:[PASS]]
[--server-host SERVER_HOST] [--server-port SERVER_PORT] [--debug]
{create,delete,ls,frames,dump,upload,export,import} ...
usage: cvat-cli [-h] [--version] [--insecure] [--auth USER:[PASS]] [--server-host SERVER_HOST]
[--server-port SERVER_PORT] [--organization SLUG] [--debug]
{create,delete,ls,frames,dump,upload,export,import} ...
Perform common operations related to CVAT tasks.
positional arguments:
{create,delete,ls,frames,dump,upload,export,import}
optional arguments:
options:
-h, --help show this help message and exit
--auth USER:[PASS] defaults to the current user and supports the PASS
environment variable or password prompt.
--version show program's version number and exit
--insecure Allows to disable SSL certificate check
--auth USER:[PASS] defaults to the current user and supports the PASS environment variable or password
prompt (default user: ...).
--server-host SERVER_HOST
host (default: localhost)
--server-port SERVER_PORT
port (default: 8080)
port (default: 80 for http and 443 for https connections)
--organization SLUG, --org SLUG
short name (slug) of the organization to use when listing or creating resources; set
to blank string to use the personal workspace (default: list all accessible objects,
create in personal workspace)
--debug show debug output
```

Expand Down Expand Up @@ -110,6 +116,11 @@ by using the [label constructor](/docs/manual/basics/creating_an_annotation_task
cvat-cli --server-host example.com --auth user-1 create "task 1" \
--labels labels.json local image1.jpg
```
- Create a task named "task 1" on the default server, with labels from "labels.json"
and local image "file1.jpg", as the current user, in organization "myorg":
```bash
cvat-cli --org myorg create "task 1" --labels labels.json local file1.jpg
```
- Create a task named "task 1", labels from the project with id 1 and with a remote video file,
the task will be created as user "user-1":
```bash
Expand Down Expand Up @@ -172,6 +183,10 @@ by using the [label constructor](/docs/manual/basics/creating_an_annotation_task
```bash
cvat-cli ls
```
- List all tasks in organization "myorg":
```bash
cvat-cli --org myorg ls
```
- Save list of all tasks into file "list_of_tasks.json":
```bash
cvat-cli ls --json > list_of_tasks.json
Expand Down
65 changes: 57 additions & 8 deletions tests/python/cli/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
import json
import os
from pathlib import Path
from typing import Optional

import packaging.version as pv
import pytest
from cvat_cli.cli import CLI
from cvat_sdk import Client, make_client
from cvat_sdk.api_client import exceptions
from cvat_sdk.api_client import exceptions, models
from cvat_sdk.core.proxies.tasks import ResourceType, Task
from PIL import Image

Expand Down Expand Up @@ -84,15 +85,21 @@ def fxt_new_task(self):

return task

def run_cli(self, cmd: str, *args: str, expected_code: int = 0) -> str:
def run_cli(
self, cmd: str, *args: str, expected_code: int = 0, organization: Optional[str] = None
) -> str:
common_args = [
f"--auth={self.user}:{self.password}",
f"--server-host={self.host}",
f"--server-port={self.port}",
]

if organization is not None:
common_args.append(f"--organization={organization}")

run_cli(
self,
"--auth",
f"{self.user}:{self.password}",
"--server-host",
self.host,
"--server-port",
self.port,
*common_args,
cmd,
*args,
expected_code=expected_code,
Expand Down Expand Up @@ -253,3 +260,45 @@ def my_init(self, *args, **kwargs):
self.run_cli(*(["--insecure"] if not verify else []), "ls")

assert capture.value.args[0] == verify

def test_can_control_organization_context(self):
org = "cli-test-org"
self.client.organizations.create(models.OrganizationWriteRequest(org))

files = generate_images(self.tmp_path, 1)

stdout = self.run_cli(
"create",
"personal_task",
ResourceType.LOCAL.name,
*map(os.fspath, files),
"--labels=" + json.dumps([{"name": "person"}]),
"--completion_verification_period=0.01",
organization="",
)

personal_task_id = int(stdout.split()[-1])

stdout = self.run_cli(
"create",
"org_task",
ResourceType.LOCAL.name,
*map(os.fspath, files),
"--labels=" + json.dumps([{"name": "person"}]),
"--completion_verification_period=0.01",
organization=org,
)

org_task_id = int(stdout.split()[-1])

personal_task_ids = list(map(int, self.run_cli("ls", organization="").split()))
assert personal_task_id in personal_task_ids
assert org_task_id not in personal_task_ids

org_task_ids = list(map(int, self.run_cli("ls", organization=org).split()))
assert personal_task_id not in org_task_ids
assert org_task_id in org_task_ids

all_task_ids = list(map(int, self.run_cli("ls").split()))
assert personal_task_id in all_task_ids
assert org_task_id in all_task_ids

0 comments on commit 871756d

Please sign in to comment.