Merge branch 'develop.cvat.ai' into develop
commit
e1c90477e7
@ -0,0 +1,82 @@
|
||||
name: Black
|
||||
on: pull_request
|
||||
jobs:
|
||||
Linter:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- id: files
|
||||
uses: jitterbit/get-changed-files@v1
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run checks
|
||||
env:
|
||||
PR_FILES_AM: ${{ steps.files.outputs.added_modified }}
|
||||
PR_FILES_RENAMED: ${{ steps.files.outputs.renamed }}
|
||||
run: |
|
||||
# If different modules use different Black configs,
|
||||
# we need to run Black for each python component group separately.
|
||||
# Otherwise, they all will use the same config.
|
||||
ENABLED_DIRS=("cvat-sdk" "cvat-cli" "tests/python/sdk" "tests/python/cli")
|
||||
|
||||
isValueIn () {
|
||||
# Checks if a value is in an array
|
||||
# https://stackoverflow.com/a/8574392
|
||||
# args: value, array
|
||||
local e match="$1"
|
||||
shift
|
||||
for e; do
|
||||
[[ "$e" == "$match" ]] && return 0;
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
startswith () {
|
||||
# Inspired by https://stackoverflow.com/a/2172367
|
||||
# Checks if the first arg starts with the second one
|
||||
local value="$1"
|
||||
local beginning="$2"
|
||||
return $([[ $value == ${beginning}* ]])
|
||||
}
|
||||
|
||||
PR_FILES="$PR_FILES_AM $PR_FILES_RENAMED"
|
||||
UPDATED_DIRS=""
|
||||
for FILE in $PR_FILES; do
|
||||
EXTENSION="${FILE##*.}"
|
||||
DIRECTORY="$(dirname $FILE)"
|
||||
if [[ "$EXTENSION" == "py" ]]; then
|
||||
for EDIR in ${ENABLED_DIRS[@]}; do
|
||||
if startswith "${DIRECTORY}/" "${EDIR}/" && ! isValueIn "${EDIR}" ${UPDATED_DIRS[@]};
|
||||
then
|
||||
UPDATED_DIRS+=" ${EDIR}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ! -z $UPDATED_DIRS ]]; then
|
||||
sudo apt-get --no-install-recommends install -y build-essential curl python3-dev python3-pip python3-venv
|
||||
python3 -m venv .env
|
||||
. .env/bin/activate
|
||||
pip install -U pip wheel setuptools
|
||||
pip install $(egrep "black.*" ./cvat-cli/requirements/development.txt)
|
||||
mkdir -p black_report
|
||||
|
||||
echo "Black version: "$(black --version)
|
||||
echo "The dirs will be checked: $UPDATED_DIRS"
|
||||
EXIT_CODE=0
|
||||
for DIR in $UPDATED_DIRS; do
|
||||
black --check $DIR >> ./black_report/black_checks.txt || EXIT_CODE=$(($? | $EXIT_CODE)) || true
|
||||
done
|
||||
deactivate
|
||||
exit $EXIT_CODE
|
||||
else
|
||||
echo "No files with the \"py\" extension found"
|
||||
fi
|
||||
|
||||
- name: Upload artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: black_report
|
||||
path: black_report
|
||||
@ -0,0 +1,21 @@
|
||||
# Workflow deletes image artifacts that created by CI workflow
|
||||
name: Delete image artifacts
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [CI, Comment]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- name: Clean up
|
||||
run: |
|
||||
wri=${{ github.event.workflow_run.id }}
|
||||
for ai in $(gh api /repos/${{ github.repository }}/actions/runs/$wri/artifacts | jq '.artifacts[] | select( .name | startswith("cvat")) | .id');
|
||||
do
|
||||
gh api --method DELETE /repos/${{ github.repository }}/actions/artifacts/$ai
|
||||
done
|
||||
@ -0,0 +1,89 @@
|
||||
name: Comment
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
env:
|
||||
WORKFLOW_RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
|
||||
jobs:
|
||||
verify_author:
|
||||
if: contains(github.event.issue.html_url, '/pull') &&
|
||||
contains(github.event.comment.body, '/check')
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
ref: ${{ steps.get-ref.outputs.ref }}
|
||||
cid: ${{ steps.send-status.outputs.cid }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- name: Check author of comment
|
||||
id: check-author
|
||||
run: |
|
||||
PERM=$(gh api repos/${{ github.repository }}/collaborators/${{ github.event.comment.user.login }}/permission | jq -r '.permission')
|
||||
if [[ $PERM == "write" || $PERM == "maintain" || $PERM == "admin" ]];
|
||||
then
|
||||
ALLOW="true"
|
||||
fi
|
||||
echo ::set-output name=allow::${ALLOW}
|
||||
|
||||
- name: Verify that author of comment is collaborator
|
||||
if: steps.check-author.outputs.allow == ''
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('User that send comment with /check command is not collaborator')
|
||||
|
||||
- name: Get branch name
|
||||
id: get-ref
|
||||
run: |
|
||||
BRANCH=$(gh api /repos/${{ github.repository }}/pulls/${{ github.event.issue.number }} | jq -r '.head.ref')
|
||||
echo ::set-output name=ref::${BRANCH}
|
||||
|
||||
- name: Send comment. Test are executing
|
||||
id: send-status
|
||||
run: |
|
||||
BODY=":hourglass: Tests are executing, see more information [here](${{ env.WORKFLOW_RUN_URL }})"
|
||||
BODY=$BODY"\n :warning: Cancel [this](${{ env.WORKFLOW_RUN_URL }}) workflow manually first, if you want to restart full check"
|
||||
BODY=$(echo -e $BODY)
|
||||
|
||||
COMMENT_ID=$(gh api --method POST \
|
||||
/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments \
|
||||
-f body="${BODY}" | jq '.id')
|
||||
|
||||
echo ::set-output name=cid::${COMMENT_ID}
|
||||
|
||||
run-full:
|
||||
needs: verify_author
|
||||
uses: ./.github/workflows/full.yml
|
||||
with:
|
||||
ref: ${{ needs.verify_author.outputs.ref }}
|
||||
|
||||
send_status:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [run-full, verify_author]
|
||||
if: needs.run-full.result != 'skipped' && always()
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- name: Send status in comments
|
||||
run: |
|
||||
BODY=""
|
||||
|
||||
if [[ "${{ needs.run-full.result }}" == "failure" ]]
|
||||
then
|
||||
BODY=":x: Some checks failed"
|
||||
elif [[ "${{ needs.run-full.result }}" == "success" ]]
|
||||
then
|
||||
BODY=":heavy_check_mark: All checks completed successfully"
|
||||
elif [[ "${{ needs.run-full.result }}" == "cancelled" ]]
|
||||
then
|
||||
BODY=":no_entry_sign: Workflows has been canceled"
|
||||
fi
|
||||
|
||||
BODY=$BODY"\n :page_facing_up: See logs [here](${WORKFLOW_RUN_URL})"
|
||||
BODY=$(echo -e $BODY)
|
||||
|
||||
gh api --method PATCH \
|
||||
/repos/${{ github.repository }}/issues/comments/${{ needs.verify_author.outputs.cid }} \
|
||||
-f body="${BODY}"
|
||||
@ -0,0 +1,364 @@
|
||||
name: Full
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
ref:
|
||||
type: string
|
||||
required: true
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
type: string
|
||||
required: true
|
||||
|
||||
env:
|
||||
WORKFLOW_RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
|
||||
jobs:
|
||||
search_cache:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
sha: ${{ steps.get-sha.outputs.sha}}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
steps:
|
||||
- name: Getting SHA with cache from the default branch
|
||||
id: get-sha
|
||||
run: |
|
||||
DEFAULT_BRANCH=$(gh api /repos/$REPO | jq -r '.default_branch')
|
||||
for sha in $(gh api "/repos/$REPO/commits?per_page=100&sha=$DEFAULT_BRANCH" | jq -r '.[].sha');
|
||||
do
|
||||
RUN_status=$(gh api /repos/${REPO}/actions/workflows/cache.yml/runs | \
|
||||
jq -r ".workflow_runs[]? | select((.head_sha == \"${sha}\") and (.conclusion == \"success\")) | .status")
|
||||
if [[ ${RUN_status} == "completed" ]]; then
|
||||
SHA=$sha
|
||||
break
|
||||
fi
|
||||
done
|
||||
echo Default branch is ${DEFAULT_BRANCH}
|
||||
echo Workflow will try to get cache from commit: ${SHA}
|
||||
echo ::set-output name=default_branch::${DEFAULT_BRANCH}
|
||||
echo ::set-output name=sha::${SHA}
|
||||
|
||||
build:
|
||||
needs: search_cache
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
|
||||
- name: CVAT server. Getting cache from the default branch
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /tmp/cvat_cache_server
|
||||
key: ${{ runner.os }}-build-server-${{ needs.search_cache.outputs.sha }}
|
||||
|
||||
- name: CVAT UI. Getting cache from the default branch
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /tmp/cvat_cache_ui
|
||||
key: ${{ runner.os }}-build-ui-${{ needs.search_cache.outputs.sha }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Create image directory
|
||||
run: |
|
||||
mkdir /tmp/cvat_server
|
||||
mkdir /tmp/cvat_ui
|
||||
|
||||
- name: CVAT server. Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
cache-from: type=local,src=/tmp/cvat_cache_server
|
||||
context: .
|
||||
file: Dockerfile
|
||||
tags: cvat/server
|
||||
outputs: type=docker,dest=/tmp/cvat_server/image.tar
|
||||
|
||||
- name: CVAT UI. Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
cache-from: type=local,src=/tmp/cvat_cache_ui
|
||||
context: .
|
||||
file: Dockerfile.ui
|
||||
tags: cvat/ui
|
||||
outputs: type=docker,dest=/tmp/cvat_ui/image.tar
|
||||
|
||||
- name: Upload CVAT server artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: cvat_server
|
||||
path: /tmp/cvat_server/image.tar
|
||||
|
||||
- name: Upload CVAT UI artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: cvat_ui
|
||||
path: /tmp/cvat_ui/image.tar
|
||||
|
||||
rest_api:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.8'
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@master
|
||||
|
||||
- name: Getting CVAT Elasticsearch cache from the default branch
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /tmp/cvat_cache_elasticsearch
|
||||
key: ${{ runner.os }}-build-elasticsearch-${{ needs.search_cache.outputs.sha }}
|
||||
|
||||
- name: Getting CVAT Logstash cache from the default branch
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /tmp/cvat_cache_logstash
|
||||
key: ${{ runner.os }}-build-logstash-${{ needs.search_cache.outputs.sha }}
|
||||
|
||||
- name: Building CVAT Elasticsearch
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./components/analytics/elasticsearch/
|
||||
file: ./components/analytics/elasticsearch/Dockerfile
|
||||
cache-from: type=local,src=/tmp/cvat_cache_elasticsearch
|
||||
tags: cvat_elasticsearch:latest
|
||||
load: true
|
||||
build-args: ELK_VERSION=6.8.23
|
||||
|
||||
- name: Building CVAT Logstash
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./components/analytics/logstash/
|
||||
file: ./components/analytics/logstash/Dockerfile
|
||||
cache-from: type=local,src=/tmp/cvat_cache_logstash
|
||||
tags: cvat_logstash:latest
|
||||
load: true
|
||||
build-args: ELK_VERSION=6.8.23
|
||||
|
||||
- name: Download CVAT server image
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: cvat_server
|
||||
path: /tmp/cvat_server/
|
||||
|
||||
- name: Download CVAT UI images
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: cvat_ui
|
||||
path: /tmp/cvat_ui/
|
||||
|
||||
- name: Load Docker images
|
||||
run: |
|
||||
docker load --input /tmp/cvat_server/image.tar
|
||||
docker load --input /tmp/cvat_ui/image.tar
|
||||
docker image ls -a
|
||||
|
||||
- name: Running REST API and SDK tests
|
||||
run: |
|
||||
docker run --rm -v ${PWD}/cvat-sdk/schema/:/transfer \
|
||||
--entrypoint /bin/bash -u root cvat/server \
|
||||
-c 'python manage.py spectacular --file /transfer/schema.yml'
|
||||
pip3 install --user -r cvat-sdk/gen/requirements.txt
|
||||
cd cvat-sdk/
|
||||
gen/generate.sh
|
||||
cd ..
|
||||
|
||||
pip3 install --user cvat-sdk/
|
||||
pip3 install --user cvat-cli/
|
||||
pip3 install --user -r tests/python/requirements.txt
|
||||
pytest tests/python -s -v
|
||||
|
||||
- name: Creating a log file from cvat containers
|
||||
if: failure()
|
||||
env:
|
||||
LOGS_DIR: "${{ github.workspace }}/rest_api"
|
||||
run: |
|
||||
mkdir $LOGS_DIR
|
||||
docker logs test_cvat_server_1 > $LOGS_DIR/cvat.log
|
||||
docker logs test_cvat_opa_1 2> $LOGS_DIR/cvat_opa.log
|
||||
|
||||
- name: Uploading "cvat" container logs as an artifact
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
env:
|
||||
LOGS_DIR: "${{ github.workspace }}/rest_api"
|
||||
with:
|
||||
name: container_logs
|
||||
path: $LOGS_DIR
|
||||
|
||||
unit_testing:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@master
|
||||
|
||||
- name: Download CVAT server image
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: cvat_server
|
||||
path: /tmp/cvat_server/
|
||||
|
||||
- name: Load Docker images
|
||||
run: |
|
||||
docker load --input /tmp/cvat_server/image.tar
|
||||
docker image ls -a
|
||||
|
||||
- name: Running OPA tests
|
||||
run: |
|
||||
curl -L -o opa https://openpolicyagent.org/downloads/v0.34.2/opa_linux_amd64_static
|
||||
chmod +x ./opa
|
||||
./opa test cvat/apps/iam/rules
|
||||
|
||||
- name: Running unit tests
|
||||
env:
|
||||
HOST_COVERAGE_DATA_DIR: ${{ github.workspace }}
|
||||
CONTAINER_COVERAGE_DATA_DIR: "/coverage_data"
|
||||
run: |
|
||||
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d cvat_opa
|
||||
max_tries=12
|
||||
while [[ $(curl -s -o /dev/null -w "%{http_code}" localhost:8181/health) != "200" && max_tries -gt 0 ]]; do (( max_tries-- )); sleep 5; done
|
||||
|
||||
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.ci.yml run cvat_ci /bin/bash \
|
||||
-c 'python manage.py test cvat/apps -v 2'
|
||||
|
||||
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f docker-compose.ci.yml run cvat_ci /bin/bash \
|
||||
-c 'yarn --frozen-lockfile --ignore-scripts && yarn workspace cvat-core run test'
|
||||
|
||||
- name: Creating a log file from cvat containers
|
||||
if: failure()
|
||||
env:
|
||||
LOGS_DIR: "${{ github.workspace }}/unit_testing"
|
||||
run: |
|
||||
mkdir $LOGS_DIR
|
||||
docker logs cvat > $LOGS_DIR/cvat.log
|
||||
docker logs cvat_opa 2> $LOGS_DIR/cvat_opa.log
|
||||
|
||||
- name: Uploading "cvat" container logs as an artifact
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
env:
|
||||
LOGS_DIR: "${{ github.workspace }}/unit_testing"
|
||||
with:
|
||||
name: container_logs
|
||||
path: $LOGS_DIR
|
||||
|
||||
e2e_testing:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
specs: ['actions_tasks', 'actions_tasks2', 'actions_tasks3',
|
||||
'actions_objects', 'actions_objects2', 'actions_users',
|
||||
'actions_projects_models', 'actions_organizations', 'canvas3d_functionality',
|
||||
'canvas3d_functionality_2', 'issues_prs', 'issues_prs2']
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '16.x'
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@master
|
||||
|
||||
- name: Download CVAT server image
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: cvat_server
|
||||
path: /tmp/cvat_server/
|
||||
|
||||
- name: Download CVAT UI image
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: cvat_ui
|
||||
path: /tmp/cvat_ui/
|
||||
|
||||
- name: Load Docker images
|
||||
run: |
|
||||
docker load --input /tmp/cvat_server/image.tar
|
||||
docker load --input /tmp/cvat_ui/image.tar
|
||||
docker image ls -a
|
||||
|
||||
- name: Run CVAT instance
|
||||
run: |
|
||||
docker-compose \
|
||||
-f docker-compose.yml \
|
||||
-f docker-compose.dev.yml \
|
||||
-f components/serverless/docker-compose.serverless.yml \
|
||||
-f tests/docker-compose.file_share.yml up -d
|
||||
|
||||
- name: Waiting for server
|
||||
env:
|
||||
API_ABOUT_PAGE: "localhost:8080/api/server/about"
|
||||
run: |
|
||||
max_tries=60
|
||||
status_code=$(curl -s -o /tmp/server_response -w "%{http_code}" ${API_ABOUT_PAGE})
|
||||
while [[ $status_code != "401" && max_tries -gt 0 ]]
|
||||
do
|
||||
echo Number of attempts left: $max_tries
|
||||
echo Status code of response: $status_code
|
||||
sleep 5
|
||||
status_code=$(curl -s -o /tmp/server_response -w "%{http_code}" ${API_ABOUT_PAGE})
|
||||
(( max_tries-- ))
|
||||
done
|
||||
|
||||
- name: Run E2E tests
|
||||
env:
|
||||
DJANGO_SU_NAME: 'admin'
|
||||
DJANGO_SU_EMAIL: 'admin@localhost.company'
|
||||
DJANGO_SU_PASSWORD: '12qwaszx'
|
||||
run: |
|
||||
docker exec -i cvat /bin/bash -c "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell"
|
||||
cd ./tests
|
||||
yarn --frozen-lockfile
|
||||
|
||||
shopt -s extglob
|
||||
if [[ ${{ matrix.specs }} == canvas3d_* ]]; then
|
||||
npx cypress run \
|
||||
--headed \
|
||||
--browser chrome \
|
||||
--config-file cypress_canvas3d.json \
|
||||
--spec 'cypress/integration/${{ matrix.specs }}/**/*.js,cypress/integration/remove_users_tasks_projects_organizations.js'
|
||||
else
|
||||
npx cypress run \
|
||||
--browser chrome \
|
||||
--spec 'cypress/integration/${{ matrix.specs }}/**/*.js,cypress/integration/remove_users_tasks_projects_organizations.js'
|
||||
fi
|
||||
|
||||
- name: Creating a log file from "cvat" container logs
|
||||
if: failure()
|
||||
run: |
|
||||
docker logs cvat > ${{ github.workspace }}/tests/cvat_${{ matrix.specs }}.log
|
||||
|
||||
- name: Uploading "cvat" container logs as an artifact
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: container_logs
|
||||
path: ${{ github.workspace }}/tests/cvat_${{ matrix.specs }}.log
|
||||
|
||||
- name: Uploading cypress screenshots as an artifact
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: cypress_screenshots_${{ matrix.specs }}
|
||||
path: ${{ github.workspace }}/tests/cypress/screenshots
|
||||
@ -0,0 +1,82 @@
|
||||
name: isort
|
||||
on: pull_request
|
||||
jobs:
|
||||
Linter:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- id: files
|
||||
uses: jitterbit/get-changed-files@v1
|
||||
continue-on-error: true
|
||||
|
||||
- name: Run checks
|
||||
env:
|
||||
PR_FILES_AM: ${{ steps.files.outputs.added_modified }}
|
||||
PR_FILES_RENAMED: ${{ steps.files.outputs.renamed }}
|
||||
run: |
|
||||
# If different modules use different isort configs,
|
||||
# we need to run isort for each python component group separately.
|
||||
# Otherwise, they all will use the same config.
|
||||
ENABLED_DIRS=("cvat-sdk" "cvat-cli" "tests/python/sdk" "tests/python/cli")
|
||||
|
||||
isValueIn () {
|
||||
# Checks if a value is in an array
|
||||
# https://stackoverflow.com/a/8574392
|
||||
# args: value, array
|
||||
local e match="$1"
|
||||
shift
|
||||
for e; do
|
||||
[[ "$e" == "$match" ]] && return 0;
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
startswith () {
|
||||
# Inspired by https://stackoverflow.com/a/2172367
|
||||
# Checks if the first arg starts with the second one
|
||||
local value="$1"
|
||||
local beginning="$2"
|
||||
return $([[ $value == ${beginning}* ]])
|
||||
}
|
||||
|
||||
PR_FILES="$PR_FILES_AM $PR_FILES_RENAMED"
|
||||
UPDATED_DIRS=""
|
||||
for FILE in $PR_FILES; do
|
||||
EXTENSION="${FILE##*.}"
|
||||
DIRECTORY="$(dirname $FILE)"
|
||||
if [[ "$EXTENSION" == "py" ]]; then
|
||||
for EDIR in ${ENABLED_DIRS[@]}; do
|
||||
if startswith "${DIRECTORY}/" "${EDIR}/" && ! isValueIn "${EDIR}" ${UPDATED_DIRS[@]};
|
||||
then
|
||||
UPDATED_DIRS+=" ${EDIR}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ! -z $UPDATED_DIRS ]]; then
|
||||
sudo apt-get --no-install-recommends install -y build-essential curl python3-dev python3-pip python3-venv
|
||||
python3 -m venv .env
|
||||
. .env/bin/activate
|
||||
pip install -U pip wheel setuptools
|
||||
pip install $(egrep "isort.*" ./cvat-cli/requirements/development.txt)
|
||||
mkdir -p isort_report
|
||||
|
||||
echo "isort version: "$(isort --version)
|
||||
echo "The dirs will be checked: $UPDATED_DIRS"
|
||||
EXIT_CODE=0
|
||||
for DIR in $UPDATED_DIRS; do
|
||||
isort --check $DIR >> ./isort_report/isort_checks.txt || EXIT_CODE=$(($? | $EXIT_CODE)) || true
|
||||
done
|
||||
deactivate
|
||||
exit $EXIT_CODE
|
||||
else
|
||||
echo "No files with the \"py\" extension found"
|
||||
fi
|
||||
|
||||
- name: Upload artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: isort_report
|
||||
path: isort_report
|
||||
@ -0,0 +1,2 @@
|
||||
cvat-sdk/docs/
|
||||
cvat-sdk/README.md
|
||||
@ -1,5 +1,4 @@
|
||||
server.host: 0.0.0.0
|
||||
elasticsearch.url: http://elasticsearch:9200
|
||||
elasticsearch.requestHeadersWhitelist: ['cookie', 'authorization', 'x-forwarded-user']
|
||||
kibana.defaultAppId: 'discover'
|
||||
server.basePath: /analytics
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
queue.type: persisted
|
||||
queue.max_bytes: 1gb
|
||||
queue.checkpoint.writes: 20
|
||||
http.host: 0.0.0.0
|
||||
|
||||
@ -1,169 +0,0 @@
|
||||
{
|
||||
"name": "cvat-canvas",
|
||||
"version": "2.13.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cvat-canvas",
|
||||
"version": "2.13.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/polylabel": "^1.0.5",
|
||||
"polylabel": "^1.1.0",
|
||||
"svg.draggable.js": "2.2.2",
|
||||
"svg.draw.js": "^2.0.4",
|
||||
"svg.js": "2.7.1",
|
||||
"svg.resize.js": "1.4.3",
|
||||
"svg.select.js": "3.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/polylabel": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/polylabel/-/polylabel-1.0.5.tgz",
|
||||
"integrity": "sha512-gnaNmo1OJiYNBFAZMZdqLZ3hKx2ee4ksAzqhKWBxuQ61PmhINHMcvIqsGmyCD1WFKCkwRt9NFhMSmKE6AgYY+w=="
|
||||
},
|
||||
"node_modules/polylabel": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/polylabel/-/polylabel-1.1.0.tgz",
|
||||
"integrity": "sha512-bxaGcA40sL3d6M4hH72Z4NdLqxpXRsCFk8AITYg6x1rn1Ei3izf00UMLklerBZTO49aPA3CYrIwVulx2Bce2pA==",
|
||||
"dependencies": {
|
||||
"tinyqueue": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.draggable.js": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
|
||||
"integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.draw.js": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/svg.draw.js/-/svg.draw.js-2.0.4.tgz",
|
||||
"integrity": "sha512-NMbecB0vg11AP76B0aLfI2cX7g9WurPM8x3yKxuJ9feM1vkI1GVjWZZjWpo3mkEzB1UJ8pKngaPaUCIOGi8uUA==",
|
||||
"dependencies": {
|
||||
"svg.js": "2.x.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.js": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
|
||||
"integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA=="
|
||||
},
|
||||
"node_modules/svg.resize.js": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
|
||||
"integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.6.5",
|
||||
"svg.select.js": "^2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.resize.js/node_modules/svg.select.js": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
|
||||
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.2.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svg.select.js": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
|
||||
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
|
||||
"dependencies": {
|
||||
"svg.js": "^2.6.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyqueue": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz",
|
||||
"integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/polylabel": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/polylabel/-/polylabel-1.0.5.tgz",
|
||||
"integrity": "sha512-gnaNmo1OJiYNBFAZMZdqLZ3hKx2ee4ksAzqhKWBxuQ61PmhINHMcvIqsGmyCD1WFKCkwRt9NFhMSmKE6AgYY+w=="
|
||||
},
|
||||
"polylabel": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/polylabel/-/polylabel-1.1.0.tgz",
|
||||
"integrity": "sha512-bxaGcA40sL3d6M4hH72Z4NdLqxpXRsCFk8AITYg6x1rn1Ei3izf00UMLklerBZTO49aPA3CYrIwVulx2Bce2pA==",
|
||||
"requires": {
|
||||
"tinyqueue": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"svg.draggable.js": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz",
|
||||
"integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==",
|
||||
"requires": {
|
||||
"svg.js": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"svg.draw.js": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/svg.draw.js/-/svg.draw.js-2.0.4.tgz",
|
||||
"integrity": "sha512-NMbecB0vg11AP76B0aLfI2cX7g9WurPM8x3yKxuJ9feM1vkI1GVjWZZjWpo3mkEzB1UJ8pKngaPaUCIOGi8uUA==",
|
||||
"requires": {
|
||||
"svg.js": "2.x.x"
|
||||
}
|
||||
},
|
||||
"svg.js": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz",
|
||||
"integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA=="
|
||||
},
|
||||
"svg.resize.js": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz",
|
||||
"integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==",
|
||||
"requires": {
|
||||
"svg.js": "^2.6.5",
|
||||
"svg.select.js": "^2.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"svg.select.js": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz",
|
||||
"integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==",
|
||||
"requires": {
|
||||
"svg.js": "^2.2.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"svg.select.js": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz",
|
||||
"integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==",
|
||||
"requires": {
|
||||
"svg.js": "^2.6.5"
|
||||
}
|
||||
},
|
||||
"tinyqueue": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz",
|
||||
"integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,55 +0,0 @@
|
||||
{
|
||||
"name": "cvat-canvas3d",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cvat-canvas3d",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/three": "^0.125.3",
|
||||
"camera-controls": "^1.25.3",
|
||||
"three": "^0.126.1"
|
||||
},
|
||||
"devDependencies": {}
|
||||
},
|
||||
"node_modules/@types/three": {
|
||||
"version": "0.125.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.125.3.tgz",
|
||||
"integrity": "sha512-tUPMzKooKDvMOhqcNVUPwkt+JNnF8ASgWSsrLgleVd0SjLj4boJhteSsF9f6YDjye0mmUjO+BDMWW83F97ehXA=="
|
||||
},
|
||||
"node_modules/camera-controls": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-1.33.0.tgz",
|
||||
"integrity": "sha512-QTXwz/XbLCPGf7l6u9cWKfR3WwKulnNAahfg+RE+dFOAQ40KKvwTIvBs3Q29kqntJlKvY79ZVsmPUEUA6LoF2A==",
|
||||
"peerDependencies": {
|
||||
"three": ">=0.126.1"
|
||||
}
|
||||
},
|
||||
"node_modules/three": {
|
||||
"version": "0.126.1",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.126.1.tgz",
|
||||
"integrity": "sha512-eOEXnZeE1FDV0XgL1u08auIP13jxdN9LQBAEmlErYzMxtIIfuGIAZbijOyookALUhqVzVOx0Tywj6n192VM+nQ=="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/three": {
|
||||
"version": "0.125.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.125.3.tgz",
|
||||
"integrity": "sha512-tUPMzKooKDvMOhqcNVUPwkt+JNnF8ASgWSsrLgleVd0SjLj4boJhteSsF9f6YDjye0mmUjO+BDMWW83F97ehXA=="
|
||||
},
|
||||
"camera-controls": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-1.33.0.tgz",
|
||||
"integrity": "sha512-QTXwz/XbLCPGf7l6u9cWKfR3WwKulnNAahfg+RE+dFOAQ40KKvwTIvBs3Q29kqntJlKvY79ZVsmPUEUA6LoF2A==",
|
||||
"requires": {}
|
||||
},
|
||||
"three": {
|
||||
"version": "0.126.1",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.126.1.tgz",
|
||||
"integrity": "sha512-eOEXnZeE1FDV0XgL1u08auIP13jxdN9LQBAEmlErYzMxtIIfuGIAZbijOyookALUhqVzVOx0Tywj6n192VM+nQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*,cover
|
||||
.hypothesis/
|
||||
venv/
|
||||
.venv/
|
||||
.python-version
|
||||
.pytest_cache
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
#Ipython Notebook
|
||||
.ipynb_checkpoints
|
||||
@ -0,0 +1,2 @@
|
||||
include README.md
|
||||
include requirements/base.txt
|
||||
@ -0,0 +1,11 @@
|
||||
# Command-line client for CVAT
|
||||
|
||||
## Installation
|
||||
|
||||
`pip install cvat-cli/`
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
$ cvat-cli --help
|
||||
```
|
||||
@ -0,0 +1,19 @@
|
||||
# Developer guide
|
||||
|
||||
Install testing requirements:
|
||||
|
||||
```bash
|
||||
pip install -r requirements/testing.txt
|
||||
```
|
||||
|
||||
Run unit tests:
|
||||
```
|
||||
cd cvat/
|
||||
python manage.py test --settings cvat.settings.testing cvat-cli/
|
||||
```
|
||||
|
||||
Install package in the editable mode:
|
||||
|
||||
```bash
|
||||
pip install -e .
|
||||
```
|
||||
@ -0,0 +1,15 @@
|
||||
[build-system]
|
||||
requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.isort]
|
||||
profile = "black"
|
||||
forced_separate = ["tests"]
|
||||
line_length = 100
|
||||
skip_gitignore = true # align tool behavior with Black
|
||||
|
||||
# Can't just use a pyproject in the root dir, so duplicate
|
||||
# https://github.com/psf/black/issues/2863
|
||||
[tool.black]
|
||||
line-length = 100
|
||||
target-version = ['py38']
|
||||
@ -0,0 +1,2 @@
|
||||
cvat-sdk==2.0
|
||||
Pillow>=6.2.0
|
||||
@ -0,0 +1,5 @@
|
||||
-r base.txt
|
||||
|
||||
black>=22.1.0
|
||||
isort>=5.10.1
|
||||
pylint>=2.7.0
|
||||
@ -0,0 +1,67 @@
|
||||
# Copyright (C) 2022 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import os.path as osp
|
||||
import re
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
|
||||
def find_version(project_dir=None):
|
||||
if not project_dir:
|
||||
project_dir = osp.dirname(osp.abspath(__file__))
|
||||
|
||||
file_path = osp.join(project_dir, "version.py")
|
||||
|
||||
with open(file_path, "r") as version_file:
|
||||
version_text = version_file.read()
|
||||
|
||||
# PEP440:
|
||||
# https://www.python.org/dev/peps/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
|
||||
pep_regex = r"([1-9]\d*!)?(0|[1-9]\d*)(\.(0|[1-9]\d*))*((a|b|rc)(0|[1-9]\d*))?(\.post(0|[1-9]\d*))?(\.dev(0|[1-9]\d*))?"
|
||||
version_regex = r"VERSION\s*=\s*.(" + pep_regex + ")."
|
||||
match = re.match(version_regex, version_text)
|
||||
if not match:
|
||||
raise RuntimeError("Failed to find version string in '%s'" % file_path)
|
||||
|
||||
version = version_text[match.start(1) : match.end(1)]
|
||||
return version
|
||||
|
||||
|
||||
BASE_REQUIREMENTS_FILE = "requirements/base.txt"
|
||||
|
||||
|
||||
def parse_requirements(filename=BASE_REQUIREMENTS_FILE):
|
||||
with open(filename) as fh:
|
||||
return fh.readlines()
|
||||
|
||||
|
||||
BASE_REQUIREMENTS = parse_requirements(BASE_REQUIREMENTS_FILE)
|
||||
|
||||
with open("README.md", "r") as fh:
|
||||
long_description = fh.read()
|
||||
|
||||
setup(
|
||||
name="cvat-cli",
|
||||
version=find_version(project_dir="src/cvat_cli"),
|
||||
description="Command-line client for CVAT",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
url="https://github.com/cvat-ai/cvat/",
|
||||
package_dir={"": "src"},
|
||||
packages=find_packages(where="src"),
|
||||
classifiers=[
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
],
|
||||
python_requires=">=3.7",
|
||||
install_requires=BASE_REQUIREMENTS,
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"cvat-cli=cvat_cli.__main__:main",
|
||||
],
|
||||
},
|
||||
include_package_data=True,
|
||||
)
|
||||
@ -0,0 +1,61 @@
|
||||
# Copyright (C) 2020-2022 Intel Corporation
|
||||
# Copyright (C) 2022 CVAT.ai Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import logging
|
||||
import sys
|
||||
from http.client import HTTPConnection
|
||||
from typing import List
|
||||
|
||||
from cvat_sdk import exceptions, make_client
|
||||
|
||||
from cvat_cli.cli import CLI
|
||||
from cvat_cli.parser import get_action_args, make_cmdline_parser
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def configure_logger(level):
|
||||
formatter = logging.Formatter(
|
||||
"[%(asctime)s] %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", style="%"
|
||||
)
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(level)
|
||||
if level <= logging.DEBUG:
|
||||
HTTPConnection.debuglevel = 1
|
||||
|
||||
|
||||
def main(args: List[str] = None):
|
||||
actions = {
|
||||
"create": CLI.tasks_create,
|
||||
"delete": CLI.tasks_delete,
|
||||
"ls": CLI.tasks_list,
|
||||
"frames": CLI.tasks_frames,
|
||||
"dump": CLI.tasks_dump,
|
||||
"upload": CLI.tasks_upload,
|
||||
"export": CLI.tasks_export,
|
||||
"import": CLI.tasks_import,
|
||||
}
|
||||
parser = make_cmdline_parser()
|
||||
parsed_args = parser.parse_args(args)
|
||||
configure_logger(parsed_args.loglevel)
|
||||
|
||||
with make_client(parsed_args.server_host, port=parsed_args.server_port) as client:
|
||||
client.logger = logger
|
||||
|
||||
action_args = get_action_args(parser, parsed_args)
|
||||
try:
|
||||
cli = CLI(client=client, credentials=parsed_args.auth)
|
||||
actions[parsed_args.action](cli, **vars(action_args))
|
||||
except exceptions.ApiException as e:
|
||||
logger.critical(e)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@ -0,0 +1,137 @@
|
||||
# Copyright (C) 2020-2022 Intel Corporation
|
||||
# Copyright (C) 2022 CVAT.ai Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Dict, List, Sequence, Tuple
|
||||
|
||||
import tqdm
|
||||
from cvat_sdk import Client, models
|
||||
from cvat_sdk.core.helpers import TqdmProgressReporter
|
||||
from cvat_sdk.core.types import ResourceType
|
||||
|
||||
|
||||
class CLI:
|
||||
def __init__(self, client: Client, credentials: Tuple[str, str]):
|
||||
self.client = client
|
||||
|
||||
# allow arbitrary kwargs in models
|
||||
# TODO: will silently ignore invalid args, so remove this ASAP
|
||||
self.client.api.configuration.discard_unknown_keys = True
|
||||
|
||||
self.client.login(credentials)
|
||||
|
||||
def tasks_list(self, *, use_json_output: bool = False, **kwargs):
|
||||
"""List all tasks in either basic or JSON format."""
|
||||
results = self.client.list_tasks(return_json=use_json_output, **kwargs)
|
||||
if use_json_output:
|
||||
print(json.dumps(json.loads(results), indent=2))
|
||||
else:
|
||||
for r in results:
|
||||
print(r.id)
|
||||
|
||||
def tasks_create(
|
||||
self,
|
||||
name: str,
|
||||
labels: List[Dict[str, str]],
|
||||
resource_type: ResourceType,
|
||||
resources: Sequence[str],
|
||||
*,
|
||||
annotation_path: str = "",
|
||||
annotation_format: str = "CVAT XML 1.1",
|
||||
status_check_period: int = 2,
|
||||
dataset_repository_url: str = "",
|
||||
lfs: bool = False,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
"""
|
||||
Create a new task with the given name and labels JSON and add the files to it.
|
||||
"""
|
||||
task = self.client.create_task(
|
||||
spec=models.TaskWriteRequest(name=name, labels=labels, **kwargs),
|
||||
resource_type=resource_type,
|
||||
resources=resources,
|
||||
data_params=kwargs,
|
||||
annotation_path=annotation_path,
|
||||
annotation_format=annotation_format,
|
||||
status_check_period=status_check_period,
|
||||
dataset_repository_url=dataset_repository_url,
|
||||
use_lfs=lfs,
|
||||
pbar=self._make_pbar(),
|
||||
)
|
||||
print("Created task id", task.id)
|
||||
|
||||
def tasks_delete(self, task_ids: Sequence[int]) -> None:
|
||||
"""Delete a list of tasks, ignoring those which don't exist."""
|
||||
self.client.delete_tasks(task_ids=task_ids)
|
||||
|
||||
def tasks_frames(
|
||||
self,
|
||||
task_id: int,
|
||||
frame_ids: Sequence[int],
|
||||
*,
|
||||
outdir: str = "",
|
||||
quality: str = "original",
|
||||
) -> None:
|
||||
"""
|
||||
Download the requested frame numbers for a task and save images as
|
||||
task_<ID>_frame_<FRAME>.jpg.
|
||||
"""
|
||||
self.client.retrieve_task(task_id=task_id).download_frames(
|
||||
frame_ids=frame_ids,
|
||||
outdir=outdir,
|
||||
quality=quality,
|
||||
filename_pattern="task_{task_id}_frame_{frame_id:06d}{frame_ext}",
|
||||
)
|
||||
|
||||
def tasks_dump(
|
||||
self,
|
||||
task_id: int,
|
||||
fileformat: str,
|
||||
filename: str,
|
||||
*,
|
||||
status_check_period: int = 2,
|
||||
include_images: bool = False,
|
||||
) -> None:
|
||||
"""
|
||||
Download annotations for a task in the specified format (e.g. 'YOLO ZIP 1.0').
|
||||
"""
|
||||
self.client.retrieve_task(task_id=task_id).export_dataset(
|
||||
format_name=fileformat,
|
||||
filename=filename,
|
||||
pbar=self._make_pbar(),
|
||||
status_check_period=status_check_period,
|
||||
include_images=include_images,
|
||||
)
|
||||
|
||||
def tasks_upload(
|
||||
self, task_id: str, fileformat: str, filename: str, *, status_check_period: int = 2
|
||||
) -> None:
|
||||
"""Upload annotations for a task in the specified format
|
||||
(e.g. 'YOLO ZIP 1.0')."""
|
||||
self.client.retrieve_task(task_id=task_id).import_annotations(
|
||||
format_name=fileformat,
|
||||
filename=filename,
|
||||
status_check_period=status_check_period,
|
||||
pbar=self._make_pbar(),
|
||||
)
|
||||
|
||||
def tasks_export(self, task_id: str, filename: str, *, status_check_period: int = 2) -> None:
|
||||
"""Download a task backup"""
|
||||
self.client.retrieve_task(task_id=task_id).download_backup(
|
||||
filename=filename, status_check_period=status_check_period, pbar=self._make_pbar()
|
||||
)
|
||||
|
||||
def tasks_import(self, filename: str, *, status_check_period: int = 2) -> None:
|
||||
"""Import a task from a backup file"""
|
||||
self.client.create_task_from_backup(
|
||||
filename=filename, status_check_period=status_check_period, pbar=self._make_pbar()
|
||||
)
|
||||
|
||||
def _make_pbar(self, title: str = None) -> TqdmProgressReporter:
|
||||
return TqdmProgressReporter(
|
||||
tqdm.tqdm(unit_scale=True, unit="B", unit_divisor=1024, desc=title)
|
||||
)
|
||||
@ -0,0 +1,334 @@
|
||||
# Copyright (C) 2021-2022 Intel Corporation
|
||||
# Copyright (C) 2022 CVAT.ai Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import argparse
|
||||
import getpass
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from distutils.util import strtobool
|
||||
|
||||
from cvat_sdk.core.types import ResourceType
|
||||
|
||||
from .version import VERSION
|
||||
|
||||
|
||||
def get_auth(s):
|
||||
"""Parse USER[:PASS] strings and prompt for password if none was
|
||||
supplied."""
|
||||
user, _, password = s.partition(":")
|
||||
password = password or os.environ.get("PASS") or getpass.getpass()
|
||||
return user, password
|
||||
|
||||
|
||||
def parse_label_arg(s):
|
||||
"""If s is a file load it as JSON, otherwise parse s as JSON."""
|
||||
if os.path.exists(s):
|
||||
with open(s, "r") as fp:
|
||||
return json.load(fp)
|
||||
else:
|
||||
return json.loads(s)
|
||||
|
||||
|
||||
def parse_resource_type(s: str) -> ResourceType:
|
||||
try:
|
||||
return ResourceType[s.upper()]
|
||||
except KeyError:
|
||||
return s
|
||||
|
||||
|
||||
def make_cmdline_parser() -> argparse.ArgumentParser:
|
||||
#######################################################################
|
||||
# Command line interface definition
|
||||
#######################################################################
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Perform common operations related to CVAT tasks.\n\n"
|
||||
)
|
||||
parser.add_argument("--version", action="version", version=VERSION)
|
||||
|
||||
task_subparser = parser.add_subparsers(dest="action")
|
||||
|
||||
#######################################################################
|
||||
# Positional arguments
|
||||
#######################################################################
|
||||
parser.add_argument(
|
||||
"--auth",
|
||||
type=get_auth,
|
||||
metavar="USER:[PASS]",
|
||||
default=getpass.getuser(),
|
||||
help="""defaults to the current user and supports the PASS
|
||||
environment variable or password prompt
|
||||
(default user: %(default)s).""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--server-host", type=str, default="localhost", help="host (default: %(default)s)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--server-port", type=int, default="8080", help="port (default: %(default)s)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--https",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="using https connection (default: %(default)s)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--debug",
|
||||
action="store_const",
|
||||
dest="loglevel",
|
||||
const=logging.DEBUG,
|
||||
default=logging.INFO,
|
||||
help="show debug output",
|
||||
)
|
||||
|
||||
#######################################################################
|
||||
# Create
|
||||
#######################################################################
|
||||
task_create_parser = task_subparser.add_parser(
|
||||
"create",
|
||||
description="""Create a new CVAT task. To create a task, you need
|
||||
to specify labels using the --labels argument or
|
||||
attach the task to an existing project using the
|
||||
--project_id argument.""",
|
||||
)
|
||||
task_create_parser.add_argument("name", type=str, help="name of the task")
|
||||
task_create_parser.add_argument(
|
||||
"resource_type",
|
||||
default="local",
|
||||
choices=list(ResourceType),
|
||||
type=parse_resource_type,
|
||||
help="type of files specified",
|
||||
)
|
||||
task_create_parser.add_argument("resources", type=str, help="list of paths or URLs", nargs="+")
|
||||
task_create_parser.add_argument(
|
||||
"--annotation_path", default="", type=str, help="path to annotation file"
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--annotation_format",
|
||||
default="CVAT 1.1",
|
||||
type=str,
|
||||
help="format of the annotation file being uploaded, e.g. CVAT 1.1",
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--bug_tracker", "--bug", default=None, type=str, help="bug tracker URL"
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--chunk_size", default=None, type=int, help="the number of frames per chunk"
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--completion_verification_period",
|
||||
dest="status_check_period",
|
||||
default=20,
|
||||
type=float,
|
||||
help="""number of seconds to wait until checking
|
||||
if data compression finished (necessary before uploading annotations)""",
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--copy_data",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="""set the option to copy the data, only used when resource type is
|
||||
share (default: %(default)s)""",
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--dataset_repository_url",
|
||||
default="",
|
||||
type=str,
|
||||
help=(
|
||||
"git repository to store annotations e.g."
|
||||
" https://github.com/user/repos [annotation/<anno_file_name.zip>]"
|
||||
),
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--frame_step",
|
||||
default=None,
|
||||
type=int,
|
||||
help="""set the frame step option in the advanced configuration
|
||||
when uploading image series or videos (default: %(default)s)""",
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--image_quality",
|
||||
default=70,
|
||||
type=int,
|
||||
help="""set the image quality option in the advanced configuration
|
||||
when creating tasks.(default: %(default)s)""",
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--labels",
|
||||
default="[]",
|
||||
type=parse_label_arg,
|
||||
help="string or file containing JSON labels specification",
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--lfs",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="using lfs for dataset repository (default: %(default)s)",
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--project_id", default=None, type=int, help="project ID if project exists"
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--overlap",
|
||||
default=None,
|
||||
type=int,
|
||||
help="the number of intersected frames between different segments",
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--segment_size", default=None, type=int, help="the number of frames in a segment"
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--sorting-method",
|
||||
default="lexicographical",
|
||||
choices=["lexicographical", "natural", "predefined", "random"],
|
||||
help="""data soring method (default: %(default)s)""",
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--start_frame", default=None, type=int, help="the start frame of the video"
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--stop_frame", default=None, type=int, help="the stop frame of the video"
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--use_cache", action="store_true", help="""use cache""" # automatically sets default=False
|
||||
)
|
||||
task_create_parser.add_argument(
|
||||
"--use_zip_chunks",
|
||||
action="store_true", # automatically sets default=False
|
||||
help="""zip chunks before sending them to the server""",
|
||||
)
|
||||
|
||||
#######################################################################
|
||||
# Delete
|
||||
#######################################################################
|
||||
delete_parser = task_subparser.add_parser("delete", description="Delete a CVAT task.")
|
||||
delete_parser.add_argument("task_ids", type=int, help="list of task IDs", nargs="+")
|
||||
|
||||
#######################################################################
|
||||
# List
|
||||
#######################################################################
|
||||
ls_parser = task_subparser.add_parser(
|
||||
"ls", description="List all CVAT tasks in simple or JSON format."
|
||||
)
|
||||
ls_parser.add_argument(
|
||||
"--json",
|
||||
dest="use_json_output",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="output JSON data",
|
||||
)
|
||||
|
||||
#######################################################################
|
||||
# Frames
|
||||
#######################################################################
|
||||
frames_parser = task_subparser.add_parser(
|
||||
"frames", description="Download all frame images for a CVAT task."
|
||||
)
|
||||
frames_parser.add_argument("task_id", type=int, help="task ID")
|
||||
frames_parser.add_argument(
|
||||
"frame_ids", type=int, help="list of frame IDs to download", nargs="+"
|
||||
)
|
||||
frames_parser.add_argument(
|
||||
"--outdir", type=str, default="", help="directory to save images (default: CWD)"
|
||||
)
|
||||
frames_parser.add_argument(
|
||||
"--quality",
|
||||
type=str,
|
||||
choices=("original", "compressed"),
|
||||
default="original",
|
||||
help="choose quality of images (default: %(default)s)",
|
||||
)
|
||||
|
||||
#######################################################################
|
||||
# Dump
|
||||
#######################################################################
|
||||
dump_parser = task_subparser.add_parser(
|
||||
"dump", description="Download annotations for a CVAT task."
|
||||
)
|
||||
dump_parser.add_argument("task_id", type=int, help="task ID")
|
||||
dump_parser.add_argument("filename", type=str, help="output file")
|
||||
dump_parser.add_argument(
|
||||
"--format",
|
||||
dest="fileformat",
|
||||
type=str,
|
||||
default="CVAT for images 1.1",
|
||||
help="annotation format (default: %(default)s)",
|
||||
)
|
||||
dump_parser.add_argument(
|
||||
"--completion_verification_period",
|
||||
dest="status_check_period",
|
||||
default=3,
|
||||
type=float,
|
||||
help="number of seconds to wait until checking if dataset building finished",
|
||||
)
|
||||
dump_parser.add_argument(
|
||||
"--with-images",
|
||||
type=strtobool,
|
||||
default=False,
|
||||
dest="include_images",
|
||||
help="Whether to include images or not (default: %(default)s)",
|
||||
)
|
||||
|
||||
#######################################################################
|
||||
# Upload Annotations
|
||||
#######################################################################
|
||||
upload_parser = task_subparser.add_parser(
|
||||
"upload", description="Upload annotations for a CVAT task."
|
||||
)
|
||||
upload_parser.add_argument("task_id", type=int, help="task ID")
|
||||
upload_parser.add_argument("filename", type=str, help="upload file")
|
||||
upload_parser.add_argument(
|
||||
"--format",
|
||||
dest="fileformat",
|
||||
type=str,
|
||||
default="CVAT 1.1",
|
||||
help="annotation format (default: %(default)s)",
|
||||
)
|
||||
|
||||
#######################################################################
|
||||
# Export task
|
||||
#######################################################################
|
||||
export_task_parser = task_subparser.add_parser("export", description="Export a CVAT task.")
|
||||
export_task_parser.add_argument("task_id", type=int, help="task ID")
|
||||
export_task_parser.add_argument("filename", type=str, help="output file")
|
||||
export_task_parser.add_argument(
|
||||
"--completion_verification_period",
|
||||
dest="status_check_period",
|
||||
default=3,
|
||||
type=float,
|
||||
help="time interval between checks if archive building has been finished, in seconds",
|
||||
)
|
||||
|
||||
#######################################################################
|
||||
# Import task
|
||||
#######################################################################
|
||||
import_task_parser = task_subparser.add_parser("import", description="Import a CVAT task.")
|
||||
import_task_parser.add_argument("filename", type=str, help="upload file")
|
||||
import_task_parser.add_argument(
|
||||
"--completion_verification_period",
|
||||
dest="status_check_period",
|
||||
default=3,
|
||||
type=float,
|
||||
help="time interval between checks if archive proessing was finished, in seconds",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def get_action_args(
|
||||
parser: argparse.ArgumentParser, parsed_args: argparse.Namespace
|
||||
) -> argparse.Namespace:
|
||||
# FIXME: a hacky way to remove unnecessary args
|
||||
action_args = dict(vars(parsed_args))
|
||||
|
||||
for action in parser._actions:
|
||||
action_args.pop(action.dest, None)
|
||||
|
||||
# remove default args
|
||||
for k, v in dict(action_args).items():
|
||||
if v is None:
|
||||
action_args.pop(k, None)
|
||||
|
||||
return argparse.Namespace(**action_args)
|
||||
@ -0,0 +1 @@
|
||||
VERSION = "0.2-alpha"
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,189 +0,0 @@
|
||||
// Copyright (C) 2019-2020 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
(() => {
|
||||
/**
|
||||
* Class representing an annotation loader
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
*/
|
||||
class Loader {
|
||||
constructor(initialData) {
|
||||
const data = {
|
||||
name: initialData.name,
|
||||
format: initialData.ext,
|
||||
version: initialData.version,
|
||||
enabled: initialData.enabled,
|
||||
dimension: initialData.dimension,
|
||||
};
|
||||
|
||||
Object.defineProperties(this, {
|
||||
name: {
|
||||
/**
|
||||
* @name name
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Loader
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.name,
|
||||
},
|
||||
format: {
|
||||
/**
|
||||
* @name format
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Loader
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.format,
|
||||
},
|
||||
version: {
|
||||
/**
|
||||
* @name version
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Loader
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.version,
|
||||
},
|
||||
enabled: {
|
||||
/**
|
||||
* @name enabled
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Loader
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.enabled,
|
||||
},
|
||||
dimension: {
|
||||
/**
|
||||
* @name dimension
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.enums.DimensionType
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.dimension,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing an annotation dumper
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
*/
|
||||
class Dumper {
|
||||
constructor(initialData) {
|
||||
const data = {
|
||||
name: initialData.name,
|
||||
format: initialData.ext,
|
||||
version: initialData.version,
|
||||
enabled: initialData.enabled,
|
||||
dimension: initialData.dimension,
|
||||
};
|
||||
|
||||
Object.defineProperties(this, {
|
||||
name: {
|
||||
/**
|
||||
* @name name
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Dumper
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.name,
|
||||
},
|
||||
format: {
|
||||
/**
|
||||
* @name format
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Dumper
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.format,
|
||||
},
|
||||
version: {
|
||||
/**
|
||||
* @name version
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Dumper
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.version,
|
||||
},
|
||||
enabled: {
|
||||
/**
|
||||
* @name enabled
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Loader
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.enabled,
|
||||
},
|
||||
dimension: {
|
||||
/**
|
||||
* @name dimension
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.enums.DimensionType
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.dimension,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing an annotation format
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
*/
|
||||
class AnnotationFormats {
|
||||
constructor(initialData) {
|
||||
const data = {
|
||||
exporters: initialData.exporters.map((el) => new Dumper(el)),
|
||||
importers: initialData.importers.map((el) => new Loader(el)),
|
||||
};
|
||||
|
||||
// Now all fields are readonly
|
||||
Object.defineProperties(this, {
|
||||
loaders: {
|
||||
/**
|
||||
* @name loaders
|
||||
* @type {module:API.cvat.classes.Loader[]}
|
||||
* @memberof module:API.cvat.classes.AnnotationFormats
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => [...data.importers],
|
||||
},
|
||||
dumpers: {
|
||||
/**
|
||||
* @name dumpers
|
||||
* @type {module:API.cvat.classes.Dumper[]}
|
||||
* @memberof module:API.cvat.classes.AnnotationFormats
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => [...data.exporters],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AnnotationFormats,
|
||||
Loader,
|
||||
Dumper,
|
||||
};
|
||||
})();
|
||||
@ -0,0 +1,210 @@
|
||||
// Copyright (C) 2019-2022 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
interface RawLoaderData {
|
||||
name: string;
|
||||
ext: string;
|
||||
version: string;
|
||||
enabled: boolean;
|
||||
dimension: '2d' | '3d';
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing an annotation loader
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
*/
|
||||
export class Loader {
|
||||
public name: string;
|
||||
public format: string;
|
||||
public version: string;
|
||||
public enabled: boolean;
|
||||
public dimension: '2d' | '3d';
|
||||
|
||||
constructor(initialData: RawLoaderData) {
|
||||
const data = {
|
||||
name: initialData.name,
|
||||
format: initialData.ext,
|
||||
version: initialData.version,
|
||||
enabled: initialData.enabled,
|
||||
dimension: initialData.dimension,
|
||||
};
|
||||
|
||||
Object.defineProperties(this, {
|
||||
name: {
|
||||
/**
|
||||
* @name name
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Loader
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.name,
|
||||
},
|
||||
format: {
|
||||
/**
|
||||
* @name format
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Loader
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.format,
|
||||
},
|
||||
version: {
|
||||
/**
|
||||
* @name version
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Loader
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.version,
|
||||
},
|
||||
enabled: {
|
||||
/**
|
||||
* @name enabled
|
||||
* @type {boolean}
|
||||
* @memberof module:API.cvat.classes.Loader
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.enabled,
|
||||
},
|
||||
dimension: {
|
||||
/**
|
||||
* @name dimension
|
||||
* @type {module:API.cvat.enums.DimensionType}
|
||||
* @memberof module:API.cvat.classes.Loader
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.dimension,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type RawDumperData = RawLoaderData;
|
||||
|
||||
/**
|
||||
* Class representing an annotation dumper
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
*/
|
||||
export class Dumper {
|
||||
public name: string;
|
||||
public format: string;
|
||||
public version: string;
|
||||
public enabled: boolean;
|
||||
public dimension: '2d' | '3d';
|
||||
|
||||
constructor(initialData: RawDumperData) {
|
||||
const data = {
|
||||
name: initialData.name,
|
||||
format: initialData.ext,
|
||||
version: initialData.version,
|
||||
enabled: initialData.enabled,
|
||||
dimension: initialData.dimension,
|
||||
};
|
||||
|
||||
Object.defineProperties(this, {
|
||||
name: {
|
||||
/**
|
||||
* @name name
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Dumper
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.name,
|
||||
},
|
||||
format: {
|
||||
/**
|
||||
* @name format
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Dumper
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.format,
|
||||
},
|
||||
version: {
|
||||
/**
|
||||
* @name version
|
||||
* @type {string}
|
||||
* @memberof module:API.cvat.classes.Dumper
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.version,
|
||||
},
|
||||
enabled: {
|
||||
/**
|
||||
* @name enabled
|
||||
* @type {boolean}
|
||||
* @memberof module:API.cvat.classes.Dumper
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.enabled,
|
||||
},
|
||||
dimension: {
|
||||
/**
|
||||
* @name dimension
|
||||
* @type {module:API.cvat.enums.DimensionType}
|
||||
* @memberof module:API.cvat.classes.Dumper
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => data.dimension,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
interface AnnotationFormatRawData {
|
||||
importers: RawLoaderData[];
|
||||
exporters: RawDumperData[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing an annotation format
|
||||
* @memberof module:API.cvat.classes
|
||||
* @hideconstructor
|
||||
*/
|
||||
export class AnnotationFormats {
|
||||
public loaders: Loader[];
|
||||
public dumpers: Dumper[];
|
||||
|
||||
constructor(initialData: AnnotationFormatRawData) {
|
||||
const data = {
|
||||
exporters: initialData.exporters.map((el) => new Dumper(el)),
|
||||
importers: initialData.importers.map((el) => new Loader(el)),
|
||||
};
|
||||
|
||||
Object.defineProperties(this, {
|
||||
loaders: {
|
||||
/**
|
||||
* @name loaders
|
||||
* @type {module:API.cvat.classes.Loader[]}
|
||||
* @memberof module:API.cvat.classes.AnnotationFormats
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => [...data.importers],
|
||||
},
|
||||
dumpers: {
|
||||
/**
|
||||
* @name dumpers
|
||||
* @type {module:API.cvat.classes.Dumper[]}
|
||||
* @memberof module:API.cvat.classes.AnnotationFormats
|
||||
* @readonly
|
||||
* @instance
|
||||
*/
|
||||
get: () => [...data.exporters],
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue