diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000000..e0ae80ab1858 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,97 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "CVAT Server", + "type": "python", + "request": "launch", + "stopOnEntry": false, + "pythonPath": "${config:python.pythonPath}", + "program": "${workspaceRoot}/manage.py", + "args": [ + "runserver", + "--noreload", + "--nothreading", + "--insecure", + "127.0.0.1:7000" + ], + "debugOptions": [ + "RedirectOutput", + "DjangoDebugging" + ], + "cwd": "${workspaceFolder}", + "env": {}, + "envFile": "${workspaceFolder}/.env", + }, + { + "name": "CVAT Client", + "type": "chrome", + "request": "launch", + "url": "http://localhost:7000/", + "disableNetworkCache":true, + "trace": true, + "showAsyncStacks": true, + "pathMapping":{ + "/static/engine/": "${workspaceFolder}/cvat/apps/engine/static/engine/", + "/static/dashboard/": "${workspaceFolder}/cvat/apps/dashboard/static/dashboard/", + } + }, + { + "name": "CVAT RQ - default", + "type": "python", + "request": "launch", + "stopOnEntry": false, + "pythonPath": "${config:python.pythonPath}", + "program": "${workspaceRoot}/manage.py", + "args": [ + "rqworker", + "default", + "--worker-class", + "cvat.simpleworker.SimpleWorker", + ], + "debugOptions": [ + "RedirectOutput", + "DjangoDebugging" + ], + "cwd": "${workspaceFolder}", + "env": {}, + "envFile": "${workspaceFolder}/.env", + + }, + { + "name": "CVAT RQ - low", + "type": "python", + "request": "launch", + "stopOnEntry": false, + "pythonPath": "${config:python.pythonPath}", + "program": "${workspaceRoot}/manage.py", + "args": [ + "rqworker", + "low", + "--worker-class", + "cvat.simpleworker.SimpleWorker", + ], + "debugOptions": [ + "RedirectOutput", + "DjangoDebugging" + ], + "cwd": "${workspaceFolder}", + "env": {}, + "envFile": "${workspaceFolder}/.env", + }, + ], + "compounds": [ + { + "name": "CVAT Debugging", + "configurations": [ + "CVAT Client", + "CVAT Server", + "CVAT RQ - default", + "CVAT RQ - low", + ] + } + ] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 356157809027..d3776747e461 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,3 +2,52 @@ When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. + +## Development environment + +Next steps should work on clear Ubuntu 18.04. + +- Install necessary dependencies: + +```sh +$ sudo apt-get install -y curl redis-server python3-dev python3-pip python3-venv libldap2-dev libsasl2-dev +``` + +- Install [Visual Studio Code](https://code.visualstudio.com/docs/setup/linux#_debian-and-ubuntu-based-distributions) for development + +- Install CVAT on your local host: + +```sh +$ git clone https://github.com/opencv/cvat +$ cd cvat && mkdir logs keys +$ python3 -m venv .env +$ . .env/bin/activate +$ pip install -U pip wheel +$ pip install -r cvat/requirements/development.txt +$ python manage.py migrate +$ python manage.py collectstatic +``` + +- Create a super user for CVAT: + +```sh +$ python manage.py createsuperuser +Username (leave blank to use 'django'): *** +Email address: *** +Password: *** +Password (again): *** +``` + +- Run Visual Studio Code from the virtual environment + +``` +$ code . +``` + +- Inside Visual Studio Code install [Debugger for Chrome](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) and [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) extensions + +- Reload Visual Studio Code + +- Select `CVAT Debugging` configuration and start debugging (F5) + +You have done! Now it is possible to insert breakpoints and debug server and client of the tool. \ No newline at end of file diff --git a/README.md b/README.md index 57234fff115c..9ed6f778b89c 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,7 @@ Type your login/password for the superuser [on the login page](http://localhost: ### Stop all containers -The command below will stop and remove containers, networks, volumes, and images -created by `up`. +The command below will stop and remove containers and networks created by `up`. See documentation for [docker-compose down](https://docs.docker.com/compose/reference/down/) for more details. ```bash docker-compose down diff --git a/cvat/apps/engine/media.mimetypes b/cvat/apps/engine/media.mimetypes index 289bffc4b6e1..79ee9c539e6e 100644 --- a/cvat/apps/engine/media.mimetypes +++ b/cvat/apps/engine/media.mimetypes @@ -196,3 +196,27 @@ image/x-kde-raw mos image/x-dds dds image/x-quicktime qif +# possible archive mimetypes (limited set) +application/gzip gz +application/rar rar +application/x-7z-compressed 7z +application/x-bzip bz bz2 +application/x-bzip-compressed-tar tar.bz tar.bz2 tb2 tbz tbz2 +application/x-compress z +application/x-compressed-tar tar.gz tgz +application/x-cpio cpio +application/x-gtar-compressed tgz taz +application/x-lha lzh +application/x-lhz lhz +application/x-lrzip-compressed-tar tar.lrz tlrz +application/x-lz4 lz4 +application/x-lzip lz +application/x-lzip-compressed-tar tar.lz +application/x-lzma lzma +application/x-lzma-compressed-tar tar.lzma tlz +application/x-lzop lzo +application/x-tar gtar tar +application/x-tarz tar.z +application/x-tzo tar.lzo +application/x-xz-compressed-tar txz +application/zip zip diff --git a/cvat/apps/engine/task.py b/cvat/apps/engine/task.py index eb364814ad69..ce3a7b6e63de 100644 --- a/cvat/apps/engine/task.py +++ b/cvat/apps/engine/task.py @@ -298,6 +298,10 @@ def _parse_labels(labels): last_label = "" for token in shlex.split(labels): if token[0] != "~" and token[0] != "@": + if token in parsed_labels: + raise ValueError("labels string is not corect. " + + "`{}` label is specified at least twice.".format(token)) + parsed_labels[token] = {} last_label = token else: @@ -306,7 +310,29 @@ def _parse_labels(labels): atype = match.group(2) aname = match.group(3) values = list(csv.reader(StringIO(match.group(4)), quotechar="'"))[0] - parsed_labels[last_label][aname] = {'prefix':prefix, 'type':atype, 'values':values} + attr = {'prefix':prefix, 'name':aname, 'type':atype, 'values':values, 'text':token} + + if not attr['type'] in ['checkbox', 'radio', 'number', 'text', 'select']: + raise ValueError("labels string is not corect. " + + "`{}` attribute has incorrect type {}.".format( + attr['name'], attr['type'])) + + if attr['name'] in parsed_labels[last_label]: + raise ValueError("labels string is not corect. " + + "`{}` attribute is specified at least twice.".format(attr['name'])) + + if attr['type'] == 'checkbox': # checkbox=name:true/false + if not (len(values) == 1 and values[0] in ['true', 'false']): + raise ValueError("labels string is not corect. " + + "`{}` attribute has incorrect value.".format(attr['name'])) + elif attr['type'] == 'number': # number=name:min,max,step + if not (len(values) == 3 and values[0].isdigit() and \ + values[1].isdigit() and values[2].isdigit() and \ + int(values[0]) < int(values[1])): + raise ValueError("labels string is not corect. " + + "`{}` attribute has incorrect format.".format(attr['name'])) + + parsed_labels[last_label][attr['name']] = attr return parsed_labels @@ -437,22 +463,20 @@ def _create_thread(tid, params): db_job.segment = db_segment db_job.save() - labels = params['labels'] - global_logger.info("labels with attributes for task #{} is {}".format(db_task.id, labels)) - db_label = None - for token in shlex.split(labels): - if token[0] != "~" and token[0] != "@": - db_label = models.Label() - db_label.task = db_task - db_label.name = token - db_label.save() - elif db_label != None: + global_logger.info("labels with attributes for task #{} is {}".format( + db_task.id, params['labels'])) + parsed_labels = _parse_labels(params['labels']) + for label in parsed_labels: + db_label = models.Label() + db_label.task = db_task + db_label.name = label + db_label.save() + + for attr in parsed_labels[label]: db_attrspec = models.AttributeSpec() db_attrspec.label = db_label - db_attrspec.text = token + db_attrspec.text = parsed_labels[label][attr]['text'] db_attrspec.save() - else: - raise ValueError("Invalid labels format {}".format(labels)) db_task.mode = mode db_task.save()