825 lines
28 KiB
Python
825 lines
28 KiB
Python
|
|
# Copyright (C) 2018-2022 Intel Corporation
|
|||
|
|
# Copyright (C) CVAT.ai Corporation
|
|||
|
|
#
|
|||
|
|
# SPDX-License-Identifier: MIT
|
|||
|
|
|
|||
|
|
"""
|
|||
|
|
Django settings for CVAT project.
|
|||
|
|
|
|||
|
|
Generated by 'django-admin startproject' using Django 2.0.1.
|
|||
|
|
|
|||
|
|
For more information on this file, see
|
|||
|
|
https://docs.djangoproject.com/en/2.0/topics/settings/
|
|||
|
|
|
|||
|
|
For the full list of settings and their values, see
|
|||
|
|
https://docs.djangoproject.com/en/2.0/ref/settings/
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import os
|
|||
|
|
import sys
|
|||
|
|
import tempfile
|
|||
|
|
import urllib
|
|||
|
|
from datetime import timedelta
|
|||
|
|
from enum import Enum, IntEnum
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
from attr.converters import to_bool
|
|||
|
|
from corsheaders.defaults import default_headers
|
|||
|
|
from django.core.exceptions import ImproperlyConfigured
|
|||
|
|
from logstash_async.constants import constants as logstash_async_constants
|
|||
|
|
|
|||
|
|
from cvat import __version__
|
|||
|
|
from cvat.apps.iam.password_validation import DEFAULT_MIN_PASSWORD_LENGTH
|
|||
|
|
|
|||
|
|
# Build paths inside the project like this: BASE_DIR / ...
|
|||
|
|
BASE_DIR = Path(__file__).parents[2]
|
|||
|
|
|
|||
|
|
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "localhost,127.0.0.1").split(",")
|
|||
|
|
INTERNAL_IPS = ["127.0.0.1"]
|
|||
|
|
SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY", "")
|
|||
|
|
|
|||
|
|
|
|||
|
|
def generate_secret_key():
|
|||
|
|
"""
|
|||
|
|
Creates secret_key.py in such a way that multiple processes calling
|
|||
|
|
this will all end up with the same key (assuming that they share the
|
|||
|
|
same "keys" directory).
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from django.utils.crypto import get_random_string
|
|||
|
|
|
|||
|
|
keys_dir = BASE_DIR / "keys"
|
|||
|
|
keys_dir.mkdir(exist_ok=True)
|
|||
|
|
|
|||
|
|
secret_key_fname = "secret_key.py" # nosec
|
|||
|
|
|
|||
|
|
with tempfile.NamedTemporaryFile(mode="wt", dir=keys_dir, prefix=secret_key_fname + ".") as f:
|
|||
|
|
chars = "abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)"
|
|||
|
|
f.write("SECRET_KEY = '{}'\n".format(get_random_string(50, chars)))
|
|||
|
|
|
|||
|
|
# Make sure the file contents are written before we link to it
|
|||
|
|
# from the final location.
|
|||
|
|
f.flush()
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
os.link(f.name, keys_dir / secret_key_fname)
|
|||
|
|
except FileExistsError:
|
|||
|
|
# Somebody else created the secret key first.
|
|||
|
|
# Discard ours and use theirs.
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
|
|||
|
|
if not SECRET_KEY:
|
|||
|
|
sys.path.append(os.fspath(BASE_DIR))
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
from keys.secret_key import SECRET_KEY # pylint: disable=unused-import
|
|||
|
|
except ModuleNotFoundError:
|
|||
|
|
generate_secret_key()
|
|||
|
|
from keys.secret_key import SECRET_KEY
|
|||
|
|
|
|||
|
|
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
|||
|
|
INSTALLED_APPS = [
|
|||
|
|
"django.contrib.admin",
|
|||
|
|
"django.contrib.auth",
|
|||
|
|
"django.contrib.contenttypes",
|
|||
|
|
"django.contrib.sessions",
|
|||
|
|
"django.contrib.messages",
|
|||
|
|
"django.contrib.staticfiles",
|
|||
|
|
"django_rq",
|
|||
|
|
"django_sendfile",
|
|||
|
|
"dj_rest_auth",
|
|||
|
|
"dj_rest_auth.registration",
|
|||
|
|
"dj_pagination",
|
|||
|
|
"django_filters",
|
|||
|
|
"rest_framework",
|
|||
|
|
"rest_framework.authtoken",
|
|||
|
|
"rest_framework_api_key",
|
|||
|
|
"drf_spectacular",
|
|||
|
|
"django.contrib.sites",
|
|||
|
|
"allauth",
|
|||
|
|
"allauth.account",
|
|||
|
|
"corsheaders",
|
|||
|
|
"allauth.socialaccount",
|
|||
|
|
"health_check",
|
|||
|
|
"health_check.cache",
|
|||
|
|
"health_check.db",
|
|||
|
|
"health_check.contrib.psutil",
|
|||
|
|
"cvat.apps.iam",
|
|||
|
|
"cvat.apps.dataset_manager",
|
|||
|
|
"cvat.apps.organizations",
|
|||
|
|
"cvat.apps.engine",
|
|||
|
|
"cvat.apps.dataset_repo",
|
|||
|
|
"cvat.apps.lambda_manager",
|
|||
|
|
"cvat.apps.webhooks",
|
|||
|
|
"cvat.apps.health",
|
|||
|
|
"cvat.apps.events",
|
|||
|
|
"cvat.apps.quality_control",
|
|||
|
|
"cvat.apps.redis_handler",
|
|||
|
|
"cvat.apps.consensus",
|
|||
|
|
"cvat.apps.access_tokens",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
SITE_ID = 1
|
|||
|
|
|
|||
|
|
|
|||
|
|
def parse_num_proxies(value: str | None) -> int | None:
|
|||
|
|
if value in (None, ""):
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
num_proxies = int(value)
|
|||
|
|
except (TypeError, ValueError):
|
|||
|
|
raise ImproperlyConfigured("CVAT_NUM_PROXIES must be an integer")
|
|||
|
|
|
|||
|
|
if num_proxies < 0:
|
|||
|
|
raise ImproperlyConfigured("CVAT_NUM_PROXIES must be a non-negative integer")
|
|||
|
|
|
|||
|
|
return num_proxies
|
|||
|
|
|
|||
|
|
|
|||
|
|
REST_FRAMEWORK = {
|
|||
|
|
"DEFAULT_PARSER_CLASSES": [
|
|||
|
|
"rest_framework.parsers.JSONParser",
|
|||
|
|
],
|
|||
|
|
"DEFAULT_RENDERER_CLASSES": [
|
|||
|
|
"cvat.apps.engine.renderers.CVATAPIRenderer",
|
|||
|
|
"rest_framework.renderers.BrowsableAPIRenderer",
|
|||
|
|
],
|
|||
|
|
"DEFAULT_PERMISSION_CLASSES": [
|
|||
|
|
"rest_framework.permissions.AllowAny",
|
|||
|
|
],
|
|||
|
|
"DEFAULT_AUTHENTICATION_CLASSES": [
|
|||
|
|
"cvat.apps.iam.no_auth.NoAuthAuthentication",
|
|||
|
|
],
|
|||
|
|
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.AcceptHeaderVersioning",
|
|||
|
|
"ALLOWED_VERSIONS": ("2.0"),
|
|||
|
|
"DEFAULT_VERSION": "2.0",
|
|||
|
|
"VERSION_PARAM": "version",
|
|||
|
|
"DEFAULT_PAGINATION_CLASS": "cvat.apps.engine.pagination.CustomPagination",
|
|||
|
|
"PAGE_SIZE": 10,
|
|||
|
|
"DEFAULT_FILTER_BACKENDS": (
|
|||
|
|
"cvat.apps.engine.filters.SimpleFilter",
|
|||
|
|
"cvat.apps.engine.filters.SearchFilter",
|
|||
|
|
"cvat.apps.engine.filters.OrderingFilter",
|
|||
|
|
"cvat.apps.engine.filters.JsonLogicFilter",
|
|||
|
|
"cvat.apps.iam.filters.OrganizationFilterBackend",
|
|||
|
|
),
|
|||
|
|
"SEARCH_PARAM": "search",
|
|||
|
|
# Disable default handling of the 'format' query parameter by REST framework
|
|||
|
|
"URL_FORMAT_OVERRIDE": "scheme",
|
|||
|
|
"DEFAULT_THROTTLE_CLASSES": [
|
|||
|
|
"rest_framework.throttling.AnonRateThrottle",
|
|||
|
|
"rest_framework.throttling.ScopedRateThrottle",
|
|||
|
|
],
|
|||
|
|
"DEFAULT_THROTTLE_RATES": {
|
|||
|
|
"anon": "100/minute",
|
|||
|
|
# dj-rest-auth views define this scope. Keep them unthrottled by default.
|
|||
|
|
"dj_rest_auth": None,
|
|||
|
|
},
|
|||
|
|
"NUM_PROXIES": parse_num_proxies(os.getenv("CVAT_NUM_PROXIES", "0")),
|
|||
|
|
"DEFAULT_METADATA_CLASS": "rest_framework.metadata.SimpleMetadata",
|
|||
|
|
"DEFAULT_SCHEMA_CLASS": "cvat.apps.iam.schema.CustomAutoSchema",
|
|||
|
|
"EXCEPTION_HANDLER": "cvat.apps.events.handlers.handle_viewset_exception",
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
REST_AUTH = {
|
|||
|
|
"REGISTER_SERIALIZER": "cvat.apps.iam.serializers.RegisterSerializerEx",
|
|||
|
|
"LOGIN_SERIALIZER": "cvat.apps.iam.serializers.LoginSerializerEx",
|
|||
|
|
"PASSWORD_RESET_SERIALIZER": "cvat.apps.iam.serializers.PasswordResetSerializerEx",
|
|||
|
|
# Define password-setting serializers explicitly so CVAT controls length limits
|
|||
|
|
# instead of inheriting hardcoded third-party defaults.
|
|||
|
|
"PASSWORD_RESET_CONFIRM_SERIALIZER": "cvat.apps.iam.serializers.PasswordResetConfirmSerializerEx",
|
|||
|
|
"PASSWORD_CHANGE_SERIALIZER": "cvat.apps.iam.serializers.PasswordChangeSerializerEx",
|
|||
|
|
"OLD_PASSWORD_FIELD_ENABLED": True,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ANALYTICS_ENABLED = to_bool(os.getenv("CVAT_ANALYTICS", False))
|
|||
|
|
|
|||
|
|
if ANALYTICS_ENABLED:
|
|||
|
|
INSTALLED_APPS += ["cvat.apps.log_viewer"]
|
|||
|
|
|
|||
|
|
MIDDLEWARE = [
|
|||
|
|
"django.middleware.security.SecurityMiddleware",
|
|||
|
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
|||
|
|
"cvat.apps.iam.middleware.SessionRefreshMiddleware",
|
|||
|
|
"corsheaders.middleware.CorsMiddleware",
|
|||
|
|
"django.middleware.common.CommonMiddleware",
|
|||
|
|
"django.middleware.csrf.CsrfViewMiddleware",
|
|||
|
|
# FIXME
|
|||
|
|
# 'corsheaders.middleware.CorsPostCsrfMiddleware',
|
|||
|
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
|||
|
|
"cvat.apps.iam.no_auth_middleware.NoAuthMiddleware",
|
|||
|
|
"django.middleware.gzip.GZipMiddleware",
|
|||
|
|
"cvat.apps.engine.middleware.RequestTrackingMiddleware",
|
|||
|
|
"cvat.apps.engine.middleware.LastActivityMiddleware",
|
|||
|
|
"crum.CurrentRequestUserMiddleware",
|
|||
|
|
"django.contrib.messages.middleware.MessageMiddleware",
|
|||
|
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|||
|
|
"dj_pagination.middleware.PaginationMiddleware",
|
|||
|
|
"cvat.apps.iam.middleware.ContextMiddleware",
|
|||
|
|
"allauth.account.middleware.AccountMiddleware",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# HSAP iframe 嵌入标注画布(跨端口 8787 → 8080)
|
|||
|
|
X_FRAME_OPTIONS = "ALLOWALL"
|
|||
|
|
|
|||
|
|
UI_URL = ""
|
|||
|
|
|
|||
|
|
ROOT_URLCONF = "cvat.urls"
|
|||
|
|
|
|||
|
|
TEMPLATES = [
|
|||
|
|
{
|
|||
|
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
|||
|
|
"DIRS": [],
|
|||
|
|
"APP_DIRS": True,
|
|||
|
|
"OPTIONS": {
|
|||
|
|
"context_processors": [
|
|||
|
|
"django.template.context_processors.debug",
|
|||
|
|
"django.template.context_processors.request",
|
|||
|
|
"django.contrib.auth.context_processors.auth",
|
|||
|
|
"django.contrib.messages.context_processors.messages",
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# IAM settings
|
|||
|
|
IAM_TYPE = "BASIC"
|
|||
|
|
IAM_BASE_EXCEPTION = None # a class which will be used by IAM to report errors
|
|||
|
|
IAM_DEFAULT_ROLE = "user"
|
|||
|
|
|
|||
|
|
IAM_ADMIN_ROLE = "admin"
|
|||
|
|
# Index in the list below corresponds to the priority (0 has highest priority)
|
|||
|
|
IAM_ROLES = [IAM_ADMIN_ROLE, "user", "worker"]
|
|||
|
|
IAM_OPA_URL = os.getenv("CVAT_OPA_URL", "http://opa:8181")
|
|||
|
|
IAM_OPA_DATA_URL = f"{IAM_OPA_URL}/v1/data"
|
|||
|
|
LOGIN_URL = "rest_login"
|
|||
|
|
LOGIN_REDIRECT_URL = "/"
|
|||
|
|
|
|||
|
|
OBJECTS_NOT_RELATED_WITH_ORG = [
|
|||
|
|
"user",
|
|||
|
|
"lambda_function",
|
|||
|
|
"lambda_request",
|
|||
|
|
"server",
|
|||
|
|
"request",
|
|||
|
|
"access_token",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# ORG settings
|
|||
|
|
ORG_INVITATION_CONFIRM = "No"
|
|||
|
|
ORG_INVITATION_EXPIRY_DAYS = 7
|
|||
|
|
|
|||
|
|
|
|||
|
|
AUTHENTICATION_BACKENDS = [
|
|||
|
|
"django.contrib.auth.backends.ModelBackend",
|
|||
|
|
"allauth.account.auth_backends.AuthenticationBackend",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# https://github.com/pennersr/django-allauth
|
|||
|
|
ACCOUNT_EMAIL_VERIFICATION = "none"
|
|||
|
|
ACCOUNT_AUTHENTICATION_METHOD = "username_email"
|
|||
|
|
|
|||
|
|
# set UI url to redirect after a successful e-mail confirmation
|
|||
|
|
# changed from '/auth/login' to '/auth/email-confirmation' for email confirmation message
|
|||
|
|
ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = "/auth/email-confirmation"
|
|||
|
|
ACCOUNT_EMAIL_VERIFICATION_SENT_REDIRECT_URL = "/auth/email-verification-sent"
|
|||
|
|
INCORRECT_EMAIL_CONFIRMATION_URL = "/auth/incorrect-email-confirmation"
|
|||
|
|
|
|||
|
|
# Django-RQ
|
|||
|
|
# https://github.com/rq/django-rq
|
|||
|
|
|
|||
|
|
|
|||
|
|
class CVAT_QUEUES(Enum):
|
|||
|
|
IMPORT_DATA = "import"
|
|||
|
|
EXPORT_DATA = "export"
|
|||
|
|
AUTO_ANNOTATION = "annotation"
|
|||
|
|
WEBHOOKS = "webhooks"
|
|||
|
|
NOTIFICATIONS = "notifications"
|
|||
|
|
QUALITY_REPORTS = "quality_reports"
|
|||
|
|
CLEANING = "cleaning"
|
|||
|
|
CHUNKS = "chunks"
|
|||
|
|
CONSENSUS = "consensus"
|
|||
|
|
|
|||
|
|
|
|||
|
|
redis_inmem_host = os.getenv("CVAT_REDIS_INMEM_HOST", "localhost")
|
|||
|
|
redis_inmem_port = os.getenv("CVAT_REDIS_INMEM_PORT", 6379)
|
|||
|
|
redis_inmem_password = os.getenv("CVAT_REDIS_INMEM_PASSWORD", "")
|
|||
|
|
|
|||
|
|
|
|||
|
|
class REDIS_INMEM_DATABASES(IntEnum):
|
|||
|
|
RQ = 0
|
|||
|
|
CACHE = 1
|
|||
|
|
|
|||
|
|
|
|||
|
|
REDIS_INMEM_SETTINGS = {
|
|||
|
|
"HOST": redis_inmem_host,
|
|||
|
|
"PORT": redis_inmem_port,
|
|||
|
|
"DB": REDIS_INMEM_DATABASES.RQ,
|
|||
|
|
"PASSWORD": redis_inmem_password,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
RQ_QUEUES = {
|
|||
|
|
CVAT_QUEUES.IMPORT_DATA.value: {
|
|||
|
|
**REDIS_INMEM_SETTINGS,
|
|||
|
|
"DEFAULT_TIMEOUT": "4h",
|
|||
|
|
# custom fields
|
|||
|
|
"PARSED_JOB_ID_CLASS": "cvat.apps.engine.rq.ImportRequestId",
|
|||
|
|
},
|
|||
|
|
CVAT_QUEUES.EXPORT_DATA.value: {
|
|||
|
|
**REDIS_INMEM_SETTINGS,
|
|||
|
|
"DEFAULT_TIMEOUT": "4h",
|
|||
|
|
# custom fields
|
|||
|
|
"PARSED_JOB_ID_CLASS": "cvat.apps.engine.rq.ExportRequestId",
|
|||
|
|
},
|
|||
|
|
CVAT_QUEUES.AUTO_ANNOTATION.value: {
|
|||
|
|
**REDIS_INMEM_SETTINGS,
|
|||
|
|
"DEFAULT_TIMEOUT": "24h",
|
|||
|
|
},
|
|||
|
|
CVAT_QUEUES.WEBHOOKS.value: {
|
|||
|
|
**REDIS_INMEM_SETTINGS,
|
|||
|
|
"DEFAULT_TIMEOUT": "1h",
|
|||
|
|
},
|
|||
|
|
CVAT_QUEUES.NOTIFICATIONS.value: {
|
|||
|
|
**REDIS_INMEM_SETTINGS,
|
|||
|
|
"DEFAULT_TIMEOUT": "1h",
|
|||
|
|
},
|
|||
|
|
CVAT_QUEUES.QUALITY_REPORTS.value: {
|
|||
|
|
**REDIS_INMEM_SETTINGS,
|
|||
|
|
"DEFAULT_TIMEOUT": "1h",
|
|||
|
|
# custom fields
|
|||
|
|
"PARSED_JOB_ID_CLASS": "cvat.apps.quality_control.rq.QualityRequestId",
|
|||
|
|
},
|
|||
|
|
CVAT_QUEUES.CLEANING.value: {
|
|||
|
|
**REDIS_INMEM_SETTINGS,
|
|||
|
|
"DEFAULT_TIMEOUT": "2h",
|
|||
|
|
},
|
|||
|
|
CVAT_QUEUES.CHUNKS.value: {
|
|||
|
|
**REDIS_INMEM_SETTINGS,
|
|||
|
|
"DEFAULT_TIMEOUT": "5m",
|
|||
|
|
},
|
|||
|
|
CVAT_QUEUES.CONSENSUS.value: {
|
|||
|
|
**REDIS_INMEM_SETTINGS,
|
|||
|
|
"DEFAULT_TIMEOUT": "1h",
|
|||
|
|
# custom fields
|
|||
|
|
"PARSED_JOB_ID_CLASS": "cvat.apps.consensus.rq.ConsensusRequestId",
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
NUCLIO = {
|
|||
|
|
"SCHEME": os.getenv("CVAT_NUCLIO_SCHEME", "http"),
|
|||
|
|
"HOST": os.getenv("CVAT_NUCLIO_HOST", "localhost"),
|
|||
|
|
"PORT": int(os.getenv("CVAT_NUCLIO_PORT", 8070)),
|
|||
|
|
"DEFAULT_TIMEOUT": int(os.getenv("CVAT_NUCLIO_DEFAULT_TIMEOUT", 120)),
|
|||
|
|
"FUNCTION_NAMESPACE": os.getenv("CVAT_NUCLIO_FUNCTION_NAMESPACE", "nuclio"),
|
|||
|
|
"INVOKE_METHOD": os.getenv(
|
|||
|
|
"CVAT_NUCLIO_INVOKE_METHOD",
|
|||
|
|
default="dashboard" if "KUBERNETES_SERVICE_HOST" in os.environ else "direct",
|
|||
|
|
),
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
assert NUCLIO["INVOKE_METHOD"] in {"dashboard", "direct"}
|
|||
|
|
|
|||
|
|
RQ_SHOW_ADMIN_LINK = True
|
|||
|
|
RQ_EXCEPTION_HANDLERS = [
|
|||
|
|
"cvat.apps.engine.views.rq_exception_handler",
|
|||
|
|
"cvat.apps.events.handlers.handle_rq_exception",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
PERIODIC_RQ_JOBS = [
|
|||
|
|
{
|
|||
|
|
"queue": CVAT_QUEUES.CLEANING.value,
|
|||
|
|
"id": "clean_up_sessions",
|
|||
|
|
"func": "cvat.apps.iam.utils.clean_up_sessions",
|
|||
|
|
"cron_string": "0 0 * * *",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"queue": CVAT_QUEUES.CLEANING.value,
|
|||
|
|
"id": "cron_export_cache_directory_cleanup",
|
|||
|
|
"func": "cvat.apps.dataset_manager.cron.cleanup_export_cache_directory",
|
|||
|
|
# Run twice a day (at midnight and at noon)
|
|||
|
|
"cron_string": "0 0,12 * * *",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"queue": CVAT_QUEUES.CLEANING.value,
|
|||
|
|
"id": "cron_tmp_directory_cleanup",
|
|||
|
|
"func": "cvat.apps.dataset_manager.cron.cleanup_tmp_directory",
|
|||
|
|
# Run once a day
|
|||
|
|
"cron_string": "0 18 * * *",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"queue": CVAT_QUEUES.CLEANING.value,
|
|||
|
|
"id": "clear_unusable_access_tokens",
|
|||
|
|
"func": "cvat.apps.access_tokens.cron.clear_unusable_access_tokens",
|
|||
|
|
"cron_string": "0 0 * * 0",
|
|||
|
|
},
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# Password validation
|
|||
|
|
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
|
|||
|
|
|
|||
|
|
AUTH_PASSWORD_VALIDATORS = [
|
|||
|
|
{
|
|||
|
|
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
|||
|
|
"OPTIONS": {"min_length": DEFAULT_MIN_PASSWORD_LENGTH},
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"NAME": "cvat.apps.iam.password_validation.MaximumLengthPasswordValidator",
|
|||
|
|
},
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
# Internationalization
|
|||
|
|
# https://docs.djangoproject.com/en/2.0/topics/i18n/
|
|||
|
|
|
|||
|
|
LANGUAGE_CODE = "en-us"
|
|||
|
|
|
|||
|
|
TIME_ZONE = os.getenv("TZ", "Etc/UTC")
|
|||
|
|
|
|||
|
|
USE_I18N = True
|
|||
|
|
|
|||
|
|
USE_TZ = True
|
|||
|
|
|
|||
|
|
CSRF_COOKIE_NAME = "csrftoken"
|
|||
|
|
|
|||
|
|
# Static files (CSS, JavaScript, Images)
|
|||
|
|
# https://docs.djangoproject.com/en/2.0/howto/static-files/
|
|||
|
|
|
|||
|
|
STATIC_URL = "/static/"
|
|||
|
|
STATIC_ROOT = BASE_DIR / "static"
|
|||
|
|
STATIC_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
# Make sure to update other config files when updating these directories
|
|||
|
|
DATA_ROOT = BASE_DIR / "data"
|
|||
|
|
|
|||
|
|
MEDIA_DATA_ROOT = DATA_ROOT / "data"
|
|||
|
|
MEDIA_DATA_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
CACHE_ROOT = DATA_ROOT / "cache"
|
|||
|
|
CACHE_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
EXPORT_CACHE_ROOT = CACHE_ROOT / "export"
|
|||
|
|
EXPORT_CACHE_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
EVENTS_LOCAL_DB_ROOT = BASE_DIR / "events"
|
|||
|
|
EVENTS_LOCAL_DB_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
EVENTS_LOCAL_DB_FILE = Path(
|
|||
|
|
EVENTS_LOCAL_DB_ROOT,
|
|||
|
|
os.getenv("CVAT_EVENTS_LOCAL_DB_FILENAME", "events.db"),
|
|||
|
|
)
|
|||
|
|
EVENTS_LOCAL_DB_FILE.touch(exist_ok=True)
|
|||
|
|
|
|||
|
|
JOBS_ROOT = DATA_ROOT / "jobs"
|
|||
|
|
JOBS_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
TASKS_ROOT = DATA_ROOT / "tasks"
|
|||
|
|
TASKS_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
PROJECTS_ROOT = DATA_ROOT / "projects"
|
|||
|
|
PROJECTS_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
ASSETS_ROOT = DATA_ROOT / "assets"
|
|||
|
|
ASSETS_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
SHARE_ROOT = BASE_DIR / "share"
|
|||
|
|
SHARE_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
LOGS_ROOT = BASE_DIR / "logs"
|
|||
|
|
LOGS_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
MIGRATIONS_LOGS_ROOT = LOGS_ROOT / "migrations"
|
|||
|
|
MIGRATIONS_LOGS_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
CLOUD_STORAGE_ROOT = DATA_ROOT / "storages"
|
|||
|
|
CLOUD_STORAGE_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
|
|||
|
|
TMP_FILES_ROOT = DATA_ROOT / "tmp"
|
|||
|
|
TMP_FILES_ROOT.mkdir(parents=True, exist_ok=True)
|
|||
|
|
IGNORE_TMP_FOLDER_CLEANUP_ERRORS = True
|
|||
|
|
|
|||
|
|
# logging is known to be unreliable with RQ when using async transports
|
|||
|
|
vector_log_handler = os.getenv("VECTOR_EVENT_HANDLER", "AsynchronousLogstashHandler")
|
|||
|
|
|
|||
|
|
logstash_async_constants.QUEUED_EVENTS_FLUSH_INTERVAL = 2.0
|
|||
|
|
LOGGING = {
|
|||
|
|
"version": 1,
|
|||
|
|
"disable_existing_loggers": False,
|
|||
|
|
"formatters": {
|
|||
|
|
"vector": {"format": "%(message)s"},
|
|||
|
|
"standard": {"format": "[%(asctime)s] %(levelname)s %(name)s: %(message)s"},
|
|||
|
|
},
|
|||
|
|
"handlers": {
|
|||
|
|
"console": {
|
|||
|
|
"class": "logging.StreamHandler",
|
|||
|
|
"filters": [],
|
|||
|
|
"formatter": "standard",
|
|||
|
|
},
|
|||
|
|
"server_file": {
|
|||
|
|
"class": "logging.handlers.RotatingFileHandler",
|
|||
|
|
"level": "DEBUG",
|
|||
|
|
"filename": LOGS_ROOT / "cvat_server.log",
|
|||
|
|
"formatter": "standard",
|
|||
|
|
"maxBytes": 1024 * 1024 * 50, # 50 MB
|
|||
|
|
"backupCount": 5,
|
|||
|
|
},
|
|||
|
|
"dataset_handler": {
|
|||
|
|
"class": "logging.handlers.RotatingFileHandler",
|
|||
|
|
"level": "DEBUG",
|
|||
|
|
"filename": LOGS_ROOT / "cvat_server_dataset.log",
|
|||
|
|
"formatter": "standard",
|
|||
|
|
"maxBytes": 1024 * 1024 * 50, # 50 MB
|
|||
|
|
"backupCount": 3,
|
|||
|
|
},
|
|||
|
|
"vector": {
|
|||
|
|
"level": "INFO",
|
|||
|
|
"class": f"logstash_async.handler.{vector_log_handler}",
|
|||
|
|
"formatter": "vector",
|
|||
|
|
"transport": "logstash_async.transport.HttpTransport",
|
|||
|
|
"ssl_enable": False,
|
|||
|
|
"ssl_verify": False,
|
|||
|
|
"host": os.getenv("DJANGO_LOG_SERVER_HOST", "localhost"),
|
|||
|
|
"port": os.getenv("DJANGO_LOG_SERVER_PORT", 8282),
|
|||
|
|
"version": 1,
|
|||
|
|
"message_type": "django",
|
|||
|
|
"database_path": EVENTS_LOCAL_DB_FILE,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
"root": {
|
|||
|
|
"handlers": ["console", "server_file"],
|
|||
|
|
},
|
|||
|
|
"loggers": {
|
|||
|
|
"cvat": {
|
|||
|
|
"level": os.getenv("DJANGO_LOG_LEVEL", "DEBUG"),
|
|||
|
|
},
|
|||
|
|
"dataset_logger": {
|
|||
|
|
"handlers": ["dataset_handler"],
|
|||
|
|
},
|
|||
|
|
"django": {
|
|||
|
|
"level": "INFO",
|
|||
|
|
},
|
|||
|
|
"vector": {
|
|||
|
|
"handlers": [],
|
|||
|
|
"level": "INFO",
|
|||
|
|
# set True for debug
|
|||
|
|
"propagate": False,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
CVAT_LOG_IMPORT_ERRORS = to_bool(os.getenv("CVAT_LOG_IMPORT_ERRORS", False))
|
|||
|
|
|
|||
|
|
if os.getenv("DJANGO_LOG_SERVER_HOST"):
|
|||
|
|
LOGGING["loggers"]["vector"]["handlers"] += ["vector"]
|
|||
|
|
|
|||
|
|
DATA_UPLOAD_MAX_MEMORY_SIZE = 100 * 1024 * 1024 # 100 MB
|
|||
|
|
DATA_UPLOAD_MAX_NUMBER_FIELDS = None # this django check disabled
|
|||
|
|
DATA_UPLOAD_MAX_NUMBER_FILES = None
|
|||
|
|
|
|||
|
|
redis_ondisk_host = os.getenv("CVAT_REDIS_ONDISK_HOST", "localhost")
|
|||
|
|
# The default port is not Redis's default port (6379).
|
|||
|
|
# This is so that a developer can run both in-mem Redis and on-disk Kvrocks on their machine
|
|||
|
|
# without running into a port conflict.
|
|||
|
|
redis_ondisk_port = os.getenv("CVAT_REDIS_ONDISK_PORT", 6666)
|
|||
|
|
redis_ondisk_password = os.getenv("CVAT_REDIS_ONDISK_PASSWORD", "")
|
|||
|
|
|
|||
|
|
# Sets the timeout for the expiration of data chunk in redis_ondisk
|
|||
|
|
CVAT_CHUNK_CACHE_TTL = 3600 * 24 # 1 day
|
|||
|
|
|
|||
|
|
# Sets the timeout for the expiration of preview image in redis_ondisk
|
|||
|
|
CVAT_PREVIEW_CACHE_TTL = 3600 * 24 * 7 # 7 days
|
|||
|
|
|
|||
|
|
CACHES = {
|
|||
|
|
"default": {
|
|||
|
|
"BACKEND": "django.core.cache.backends.redis.RedisCache",
|
|||
|
|
"LOCATION": f"redis://:{urllib.parse.quote(redis_inmem_password)}@{redis_inmem_host}:{redis_inmem_port}/{REDIS_INMEM_DATABASES.CACHE}",
|
|||
|
|
},
|
|||
|
|
"media": {
|
|||
|
|
"BACKEND": "django.core.cache.backends.redis.RedisCache",
|
|||
|
|
"LOCATION": f"redis://:{urllib.parse.quote(redis_ondisk_password)}@{redis_ondisk_host}:{redis_ondisk_port}",
|
|||
|
|
"TIMEOUT": CVAT_CHUNK_CACHE_TTL,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
USE_CACHE = True
|
|||
|
|
|
|||
|
|
CORS_ALLOW_HEADERS = list(default_headers) + [
|
|||
|
|
# tus upload protocol headers
|
|||
|
|
"upload-offset",
|
|||
|
|
"upload-length",
|
|||
|
|
"tus-version",
|
|||
|
|
"tus-resumable",
|
|||
|
|
# extended upload protocol headers
|
|||
|
|
"upload-start",
|
|||
|
|
"upload-finish",
|
|||
|
|
"upload-multiple",
|
|||
|
|
"x-organization",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
CORS_EXPOSE_HEADERS = [
|
|||
|
|
"Content-Range",
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
TUS_MAX_FILE_SIZE = 26843545600 # 25gb
|
|||
|
|
|
|||
|
|
# This setting makes request secure if X-Forwarded-Proto: 'https' header is specified by our proxy
|
|||
|
|
# More about forwarded headers - https://doc.traefik.io/traefik/getting-started/faq/#what-are-the-forwarded-headers-when-proxying-http-requests
|
|||
|
|
# How django uses X-Forwarded-Proto - https://docs.djangoproject.com/en/2.2/ref/settings/#secure-proxy-ssl-header
|
|||
|
|
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
|||
|
|
|
|||
|
|
SECURE_REFERRER_POLICY = "strict-origin-when-cross-origin"
|
|||
|
|
|
|||
|
|
# Forwarded host - https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-USE_X_FORWARDED_HOST
|
|||
|
|
# Is used in TUS uploads to provide correct upload endpoint
|
|||
|
|
USE_X_FORWARDED_HOST = True
|
|||
|
|
|
|||
|
|
# Django-sendfile requires to set SENDFILE_ROOT
|
|||
|
|
# https://github.com/moggers87/django-sendfile2
|
|||
|
|
SENDFILE_ROOT = BASE_DIR
|
|||
|
|
|
|||
|
|
CVAT_DOCS_URL = "https://docs.cvat.ai/docs/"
|
|||
|
|
|
|||
|
|
SPECTACULAR_SETTINGS = {
|
|||
|
|
"TITLE": "CVAT REST API",
|
|||
|
|
"DESCRIPTION": "REST API for Computer Vision Annotation Tool (CVAT)",
|
|||
|
|
# Statically set schema version. May also be an empty string. When used together with
|
|||
|
|
# view versioning, will become '0.0.0 (v2)' for 'v2' versioned requests.
|
|||
|
|
# Set VERSION to None if only the request version should be rendered.
|
|||
|
|
"VERSION": __version__,
|
|||
|
|
"CONTACT": {
|
|||
|
|
"name": "CVAT.ai team",
|
|||
|
|
"url": "https://github.com/cvat-ai/cvat",
|
|||
|
|
"email": "support@cvat.ai",
|
|||
|
|
},
|
|||
|
|
"LICENSE": {
|
|||
|
|
"name": "MIT License",
|
|||
|
|
"url": "https://en.wikipedia.org/wiki/MIT_License",
|
|||
|
|
},
|
|||
|
|
"SERVE_PUBLIC": True,
|
|||
|
|
"SERVE_PERMISSIONS": ["rest_framework.permissions.AllowAny"],
|
|||
|
|
# https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/
|
|||
|
|
"SWAGGER_UI_SETTINGS": {
|
|||
|
|
"deepLinking": True,
|
|||
|
|
"displayOperationId": True,
|
|||
|
|
"displayRequestDuration": True,
|
|||
|
|
"filter": True,
|
|||
|
|
"showExtensions": True,
|
|||
|
|
},
|
|||
|
|
"TOS": "https://www.google.com/policies/terms/",
|
|||
|
|
"EXTERNAL_DOCS": {
|
|||
|
|
"description": "CVAT documentation",
|
|||
|
|
"url": CVAT_DOCS_URL,
|
|||
|
|
},
|
|||
|
|
# OTHER SETTINGS
|
|||
|
|
# https://drf-spectacular.readthedocs.io/en/latest/settings.html
|
|||
|
|
#
|
|||
|
|
# TODO: Our current implementation does not suppose this.
|
|||
|
|
# Need to reconsider this later. It happens, for example,
|
|||
|
|
# in TaskSerializer for data-originated fields - they can be empty.
|
|||
|
|
# https://github.com/tfranzel/drf-spectacular/issues/54
|
|||
|
|
"COMPONENT_NO_READ_ONLY_REQUIRED": True,
|
|||
|
|
# Required for correct file upload type (bytes)
|
|||
|
|
"COMPONENT_SPLIT_REQUEST": True,
|
|||
|
|
"ENUM_NAME_OVERRIDES": {
|
|||
|
|
"LabelType": "cvat.apps.engine.models.LabelType",
|
|||
|
|
"ShapeType": "cvat.apps.engine.models.ShapeType",
|
|||
|
|
"OperationStatus": "cvat.apps.engine.models.StateChoice",
|
|||
|
|
"ChunkType": "cvat.apps.engine.models.DataChoice",
|
|||
|
|
"MediaType": "cvat.apps.engine.models.MediaType",
|
|||
|
|
"Dimension": "cvat.apps.engine.models.DimensionType",
|
|||
|
|
"StorageMethod": "cvat.apps.engine.models.StorageMethodChoice",
|
|||
|
|
"JobStatus": "cvat.apps.engine.models.StatusChoice",
|
|||
|
|
"JobStage": "cvat.apps.engine.models.StageChoice",
|
|||
|
|
"JobType": "cvat.apps.engine.models.JobType",
|
|||
|
|
"TaskMode": "cvat.apps.engine.models.TaskMode",
|
|||
|
|
"StorageType": "cvat.apps.engine.models.StorageChoice",
|
|||
|
|
"SortingMethod": "cvat.apps.engine.models.SortingMethod",
|
|||
|
|
"WebhookType": "cvat.apps.webhooks.models.WebhookTypeChoice",
|
|||
|
|
"WebhookContentType": "cvat.apps.webhooks.models.WebhookContentTypeChoice",
|
|||
|
|
"RequestStatus": "cvat.apps.redis_handler.serializers.RequestStatus",
|
|||
|
|
"ValidationMode": "cvat.apps.engine.models.ValidationMode",
|
|||
|
|
"FrameSelectionMethod": "cvat.apps.engine.models.JobFrameSelectionMethod",
|
|||
|
|
"AnnotationConflictType": "cvat.apps.quality_control.models.AnnotationConflictType",
|
|||
|
|
"AnnotationConflictSeverity": "cvat.apps.quality_control.models.AnnotationConflictSeverity",
|
|||
|
|
"AnnotationConflictAnnotationType": "cvat.apps.quality_control.models.AnnotationType",
|
|||
|
|
"MismatchingAnnotationKind": "cvat.apps.quality_control.models.MismatchingAnnotationKind",
|
|||
|
|
"QualityTargetMetric": "cvat.apps.quality_control.models.QualityTargetMetricType",
|
|||
|
|
"QualityPointSizeBase": "cvat.apps.quality_control.models.PointSizeBase",
|
|||
|
|
"QualityReportTarget": "cvat.apps.quality_control.models.QualityReportTarget",
|
|||
|
|
},
|
|||
|
|
# Coercion of {pk} to {id} is controlled by SCHEMA_COERCE_PATH_PK. Additionally,
|
|||
|
|
# some libraries (e.g. drf-nested-routers) use "_pk" suffixed path variables.
|
|||
|
|
# This setting globally coerces path variables like "{user_pk}" to "{user_id}".
|
|||
|
|
"SCHEMA_COERCE_PATH_PK_SUFFIX": True,
|
|||
|
|
"SCHEMA_PATH_PREFIX": "/api",
|
|||
|
|
"SCHEMA_PATH_PREFIX_TRIM": False,
|
|||
|
|
"GENERIC_ADDITIONAL_PROPERTIES": None,
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# set similar UI restrictions
|
|||
|
|
# https://github.com/cvat-ai/cvat/blob/bad1dc2799afbb22222faaecc7336d999f4cc3fe/cvat-ui/src/utils/validation-patterns.ts#L26
|
|||
|
|
ACCOUNT_USERNAME_MIN_LENGTH = 5
|
|||
|
|
ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True
|
|||
|
|
|
|||
|
|
ACCOUNT_ADAPTER = "cvat.apps.iam.adapters.DefaultAccountAdapterEx"
|
|||
|
|
|
|||
|
|
CVAT_HOST = os.getenv("CVAT_HOST", "localhost")
|
|||
|
|
CVAT_BASE_URL = os.getenv("CVAT_BASE_URL", f"http://{CVAT_HOST}:8080").rstrip("/")
|
|||
|
|
|
|||
|
|
CLICKHOUSE = {
|
|||
|
|
"events": {
|
|||
|
|
"NAME": os.getenv("CLICKHOUSE_DB", "cvat"),
|
|||
|
|
"HOST": os.getenv("CLICKHOUSE_HOST", "localhost"),
|
|||
|
|
"PORT": os.getenv("CLICKHOUSE_PORT", 8123),
|
|||
|
|
"USER": os.getenv("CLICKHOUSE_USER", "user"),
|
|||
|
|
"PASSWORD": os.getenv("CLICKHOUSE_PASSWORD", "user"),
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (postgres_password_file := os.getenv("CVAT_POSTGRES_PASSWORD_FILE")) is not None:
|
|||
|
|
if "CVAT_POSTGRES_PASSWORD" in os.environ:
|
|||
|
|
raise ImproperlyConfigured(
|
|||
|
|
"The CVAT_POSTGRES_PASSWORD and CVAT_POSTGRES_PASSWORD_FILE"
|
|||
|
|
" environment variables must not be set at the same time"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
postgres_password = Path(postgres_password_file).read_text(encoding="UTF-8").rstrip("\n")
|
|||
|
|
else:
|
|||
|
|
postgres_password = os.getenv("CVAT_POSTGRES_PASSWORD", "")
|
|||
|
|
|
|||
|
|
# Database
|
|||
|
|
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
|
|||
|
|
DATABASES = {
|
|||
|
|
"default": {
|
|||
|
|
"ENGINE": "django.db.backends.postgresql",
|
|||
|
|
"HOST": os.getenv("CVAT_POSTGRES_HOST", "cvat_db"),
|
|||
|
|
"NAME": os.getenv("CVAT_POSTGRES_DBNAME", "cvat"),
|
|||
|
|
"USER": os.getenv("CVAT_POSTGRES_USER", "root"),
|
|||
|
|
"PASSWORD": postgres_password,
|
|||
|
|
"PORT": os.getenv("CVAT_POSTGRES_PORT", 5432),
|
|||
|
|
"OPTIONS": {
|
|||
|
|
"application_name": os.getenv("CVAT_POSTGRES_APPLICATION_NAME", "cvat"),
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
BUCKET_CONTENT_MAX_PAGE_SIZE = 500
|
|||
|
|
|
|||
|
|
IMPORT_CACHE_FAILED_TTL = timedelta(days=30)
|
|||
|
|
IMPORT_CACHE_SUCCESS_TTL = timedelta(hours=1)
|
|||
|
|
IMPORT_CACHE_CLEAN_DELAY = timedelta(hours=12)
|
|||
|
|
|
|||
|
|
ASSET_MAX_SIZE_MB = 10
|
|||
|
|
ASSET_SUPPORTED_TYPES = ("image/jpeg", "image/png", "image/webp", "image/gif", "application/pdf")
|
|||
|
|
ASSET_MAX_IMAGE_SIZE = 1920
|
|||
|
|
ASSET_MAX_COUNT_PER_GUIDE = 150
|
|||
|
|
|
|||
|
|
SMOKESCREEN_ENABLED = to_bool(os.getenv("SMOKESCREEN_ENABLED", True))
|
|||
|
|
|
|||
|
|
# By default, email backend is django.core.mail.backends.smtp.EmailBackend
|
|||
|
|
# But it won't work without additional configuration, so we set it to None
|
|||
|
|
# to check configuration and throw ImproperlyConfigured if thats a case
|
|||
|
|
EMAIL_BACKEND = None
|
|||
|
|
|
|||
|
|
ONE_RUNNING_JOB_IN_QUEUE_PER_USER = to_bool(os.getenv("ONE_RUNNING_JOB_IN_QUEUE_PER_USER", False))
|
|||
|
|
|
|||
|
|
# How many chunks can be prepared simultaneously during task creation in case the cache is not used
|
|||
|
|
CVAT_CONCURRENT_CHUNK_PROCESSING = int(os.getenv("CVAT_CONCURRENT_CHUNK_PROCESSING", 1))
|
|||
|
|
|
|||
|
|
from cvat.rq_patching import patch_rq
|
|||
|
|
|
|||
|
|
patch_rq()
|
|||
|
|
|
|||
|
|
CLOUD_DATA_DOWNLOADING_MAX_THREADS_NUMBER_PER_CPU = 4
|
|||
|
|
|
|||
|
|
# Indicates the maximum number of days a file or directory is retained in the temporary directory
|
|||
|
|
TMP_FILE_OR_DIR_RETENTION_DAYS = 3
|
|||
|
|
|
|||
|
|
LOGO_FILENAME = "logo.svg"
|
|||
|
|
ABOUT_INFO = {
|
|||
|
|
"subtitle": "Open Data Annotation Platform",
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if ONE_RUNNING_JOB_IN_QUEUE_PER_USER:
|
|||
|
|
PERIODIC_RQ_JOBS.append(
|
|||
|
|
{
|
|||
|
|
"queue": CVAT_QUEUES.CLEANING.value,
|
|||
|
|
"id": "cleanup_deferred_job_registry",
|
|||
|
|
"func": "cvat.apps.redis_handler.cron.cleanup_deferred_job_registry",
|
|||
|
|
"cron_string": "0 8 * * *",
|
|||
|
|
}
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
USER_LAST_ACTIVITY_UPDATE_MIN_INTERVAL = timedelta(days=1)
|
|||
|
|
|
|||
|
|
# Health check settings
|
|||
|
|
HEALTH_CHECK = {"DISK_USAGE_MAX": int(os.getenv("CVAT_HEALTH_DISK_USAGE_MAX", 90))}
|