Skip to content

Commit

Permalink
Merge branch 'develop' into kl/fix-issue-tool
Browse files Browse the repository at this point in the history
  • Loading branch information
klakhov committed Jul 31, 2024
2 parents 5b5726e + 33ff6b3 commit 4753ddb
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 70 deletions.
4 changes: 2 additions & 2 deletions cvat/apps/dataset_manager/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,11 +477,11 @@ def _delete(self, data=None):

def delete(self, data=None):
deleted_data = self._delete(data)
handle_annotations_change(self.db_job, deleted_data, "delete")

if not self._data_is_empty(deleted_data):
self._set_updated_date()

handle_annotations_change(self.db_job, deleted_data, "delete")

@staticmethod
def _extend_attributes(attributeval_set, default_attribute_values):
shape_attribute_specs_set = set(attr.spec_id for attr in attributeval_set)
Expand Down
4 changes: 2 additions & 2 deletions cvat/apps/engine/backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ def serialize_task():
for field in ('url', 'owner', 'assignee'):
task_serializer.fields.pop(field)

task_labels = LabelSerializer(self._db_task.get_labels(), many=True)
task_labels = LabelSerializer(self._db_task.get_labels(prefetch=True), many=True)

task = self._prepare_task_meta(task_serializer.data)
task['labels'] = [self._prepare_label_meta(l) for l in task_labels.data if not l['has_parent']]
Expand Down Expand Up @@ -790,7 +790,7 @@ def serialize_project():
for field in ('assignee', 'owner', 'url'):
project_serializer.fields.pop(field)

project_labels = LabelSerializer(self._db_project.get_labels(), many=True).data
project_labels = LabelSerializer(self._db_project.get_labels(prefetch=True), many=True).data

project = self._prepare_project_meta(project_serializer.data)
project['labels'] = [self._prepare_label_meta(l) for l in project_labels if not l['has_parent']]
Expand Down
21 changes: 14 additions & 7 deletions cvat/apps/engine/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,11 @@ class Project(TimestampedModel):
target_storage = models.ForeignKey('Storage', null=True, default=None,
blank=True, on_delete=models.SET_NULL, related_name='+')

def get_labels(self):
return self.label_set.filter(parent__isnull=True)
def get_labels(self, prefetch=False):
queryset = self.label_set.filter(parent__isnull=True).select_related('skeleton')
return queryset.prefetch_related(
'attributespec_set', 'sublabels__attributespec_set',
) if prefetch else queryset

def get_dirname(self):
return os.path.join(settings.PROJECTS_ROOT, str(self.id))
Expand Down Expand Up @@ -426,11 +429,15 @@ class Task(TimestampedModel):
class Meta:
default_permissions = ()

def get_labels(self):
def get_labels(self, prefetch=False):
project = self.project
if project:
return project.get_labels()
return self.label_set.filter(parent__isnull=True)
return project.get_labels(prefetch)

queryset = self.label_set.filter(parent__isnull=True).select_related('skeleton')
return queryset.prefetch_related(
'attributespec_set', 'sublabels__attributespec_set',
) if prefetch else queryset

def get_dirname(self):
return os.path.join(settings.TASKS_ROOT, str(self.id))
Expand Down Expand Up @@ -725,10 +732,10 @@ def get_bug_tracker(self):
project = task.project
return task.bug_tracker or getattr(project, 'bug_tracker', None)

def get_labels(self):
def get_labels(self, prefetch=False):
task = self.segment.task
project = task.project
return project.get_labels() if project else task.get_labels()
return project.get_labels(prefetch) if project else task.get_labels(prefetch)

class Meta:
default_permissions = ()
Expand Down
4 changes: 2 additions & 2 deletions cvat/apps/engine/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1360,10 +1360,10 @@ class Meta:

def to_representation(self, instance):
response = super().to_representation(instance)
task_subsets = set(instance.tasks.values_list('subset', flat=True))
task_subsets = {task.subset for task in instance.tasks.all()}
task_subsets.discard('')
response['task_subsets'] = list(task_subsets)
response['dimension'] = instance.tasks.first().dimension if instance.tasks.count() else None
response['dimension'] = getattr(instance.tasks.first(), 'dimension', None)
return response

class ProjectWriteSerializer(serializers.ModelSerializer):
Expand Down
69 changes: 38 additions & 31 deletions cvat/apps/engine/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,13 @@ class ProjectViewSet(viewsets.GenericViewSet, mixins.ListModelMixin,
mixins.RetrieveModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin,
PartialUpdateModelMixin, UploadMixin, DatasetMixin, BackupMixin, CsrfWorkaroundMixin
):
queryset = models.Project.objects.select_related(
'assignee', 'owner', 'target_storage', 'source_storage', 'annotation_guide',
).prefetch_related('tasks').all()

# NOTE: The search_fields attribute should be a list of names of text
# type fields on the model,such as CharField or TextField
queryset = models.Project.objects.select_related(
'owner', 'assignee', 'organization',
'annotation_guide', 'source_storage', 'target_storage',
)

search_fields = ('name', 'owner', 'assignee', 'status')
filter_fields = list(search_fields) + ['id', 'updated_date']
simple_filters = list(search_fields)
Expand All @@ -290,10 +291,13 @@ def get_serializer_class(self):

def get_queryset(self):
queryset = super().get_queryset()
if self.action in ('list', 'retrieve', 'partial_update', 'update') :
queryset = queryset.prefetch_related('tasks')

if self.action == 'list':
perm = ProjectPermission.create_scope_list(self.request)
return perm.filter(queryset)

if self.action == 'list':
perm = ProjectPermission.create_scope_list(self.request)
queryset = perm.filter(queryset)
return queryset

@transaction.atomic
Expand Down Expand Up @@ -630,7 +634,7 @@ def append_backup_chunk(self, request, file_id):
def preview(self, request, pk):
self._object = self.get_object() # call check_object_permissions as well

first_task = self._object.tasks.order_by('-id').first()
first_task = self._object.tasks.select_related('data__video').order_by('-id').first()
if not first_task:
return HttpResponseNotFound('Project image preview not found')

Expand Down Expand Up @@ -869,6 +873,8 @@ def get_queryset(self):
if self.action == 'list':
perm = TaskPermission.create_scope_list(self.request)
queryset = perm.filter(queryset)
elif self.action == 'preview':
queryset = Task.objects.select_related('data')

return queryset

Expand Down Expand Up @@ -2362,41 +2368,42 @@ def get_queryset(self):
code=status.HTTP_400_BAD_REQUEST
)

if job_id:
if job_id or task_id or project_id:
if job_id:
instance = Job.objects.select_related(
'assignee', 'segment__task__organization',
'segment__task__owner', 'segment__task__assignee',
'segment__task__project__organization',
'segment__task__project__owner',
'segment__task__project__assignee',
).get(id=job_id)
elif task_id:
instance = Task.objects.select_related(
'owner', 'assignee', 'organization',
'project__owner', 'project__assignee', 'project__organization',
).get(id=task_id)
elif project_id:
instance = Project.objects.select_related(
'owner', 'assignee', 'organization',
).get(id=project_id)

# NOTE: This filter is too complex to be implemented by other means
# It requires the following filter query:
# (
# project__task__segment__job__id = job_id
# OR
# task__segment__job__id = job_id
# )
job = Job.objects.get(id=job_id)
self.check_object_permissions(self.request, job)
queryset = job.get_labels()
elif task_id:
# NOTE: This filter is too complex to be implemented by other means
# It requires the following filter query:
# (
# project__task__id = task_id
# OR
# task_id = task_id
# project__task__id = task_id
# )
task = Task.objects.get(id=task_id)
self.check_object_permissions(self.request, task)
queryset = task.get_labels()
elif project_id:
# NOTE: this check is to make behavior consistent with other source filters
project = Project.objects.get(id=project_id)
self.check_object_permissions(self.request, project)
queryset = project.get_labels()
self.check_object_permissions(self.request, instance)
queryset = instance.get_labels(prefetch=True)
else:
# In other cases permissions are checked already
queryset = super().get_queryset()
perm = LabelPermission.create_scope_list(self.request)
queryset = perm.filter(queryset)

# Include only 1st level labels in list responses
queryset = queryset.filter(parent__isnull=True)
# Include only 1st level labels in list responses
queryset = perm.filter(queryset).filter(parent__isnull=True)
else:
queryset = super().get_queryset()

Expand Down
39 changes: 18 additions & 21 deletions cvat/apps/events/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import datetime
import traceback
from copy import deepcopy
from typing import Optional, Union

import rq
Expand Down Expand Up @@ -374,19 +373,21 @@ def handle_delete(scope, instance, store_in_deletion_cache=False, **kwargs):
)

def handle_annotations_change(instance, annotations, action, **kwargs):
_annotations = deepcopy(annotations)
def filter_shape_data(shape):
data = {
"id": shape["id"],
"frame": shape["frame"],
"attributes": shape["attributes"],
def filter_data(data):
filtered_data = {
"id": data["id"],
"frame": data["frame"],
"attributes": data["attributes"],
}
if label_id := data.get("label_id"):
filtered_data["label_id"] = label_id

label_id = shape.get("label_id", None)
if label_id:
data["label_id"] = label_id
return filtered_data

return data
def filter_track(track):
filtered_data = filter_data(track)
filtered_data["shapes"] = [filter_data(s) for s in track["shapes"]]
return filtered_data

oid = organization_id(instance)
oslug = organization_slug(instance)
Expand All @@ -397,7 +398,7 @@ def filter_shape_data(shape):
uname = user_name(instance)
uemail = user_email(instance)

tags = [filter_shape_data(tag) for tag in _annotations.get("tags", [])]
tags = [filter_data(tag) for tag in annotations.get("tags", [])]
if tags:
record_server_event(
scope=event_scope(action, "tags"),
Expand All @@ -416,8 +417,8 @@ def filter_shape_data(shape):
)

shapes_by_type = {shape_type[0]: [] for shape_type in ShapeType.choices()}
for shape in _annotations.get("shapes", []):
shapes_by_type[shape["type"]].append(filter_shape_data(shape))
for shape in annotations.get("shapes", []):
shapes_by_type[shape["type"]].append(filter_data(shape))

scope = event_scope(action, "shapes")
for shape_type, shapes in shapes_by_type.items():
Expand All @@ -440,13 +441,9 @@ def filter_shape_data(shape):
)

tracks_by_type = {shape_type[0]: [] for shape_type in ShapeType.choices()}
for track in _annotations.get("tracks", []):
track_shapes = track.pop("shapes")
track = filter_shape_data(track)
track["shapes"] = []
for track_shape in track_shapes:
track["shapes"].append(filter_shape_data(track_shape))
tracks_by_type[track_shapes[0]["type"]].append(track)
for track in annotations.get("tracks", []):
filtered_track = filter_track(track)
tracks_by_type[track["shapes"][0]["type"]].append(filtered_track)

scope = event_scope(action, "tracks")
for track_type, tracks in tracks_by_type.items():
Expand Down
4 changes: 2 additions & 2 deletions cvat/apps/iam/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ def get_organization(request):
org_slug = org_slug if org_slug is not None else org_header

if org_slug:
organization = Organization.objects.get(slug=org_slug)
organization = Organization.objects.select_related('owner').get(slug=org_slug)
elif org_id:
organization = Organization.objects.get(id=int(org_id))
organization = Organization.objects.select_related('owner').get(id=int(org_id))
except Organization.DoesNotExist:
raise NotFound(f'{org_slug or org_id} organization does not exist.')

Expand Down
2 changes: 1 addition & 1 deletion cvat/apps/iam/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def get_organization(request, obj):
raise exc

try:
return Organization.objects.get(id=organization_id)
return Organization.objects.select_related('owner').get(id=organization_id)
except Organization.DoesNotExist:
return None

Expand Down
4 changes: 2 additions & 2 deletions cvat/apps/lambda_manager/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def mandatory_arg(name: str) -> Any:
mapping = data.get("mapping", {})

model_labels = self.labels
task_labels = db_task.get_labels().prefetch_related('attributespec_set')
task_labels = db_task.get_labels(prefetch=True)

def labels_compatible(model_label: Dict, task_label: Label) -> bool:
model_type = model_label['type']
Expand Down Expand Up @@ -948,7 +948,7 @@ def convert_labels(db_labels):
labels[label.name]['attributes'][attr['name']] = attr['id']
return labels

labels = convert_labels(db_task.get_labels().prefetch_related('attributespec_set'))
labels = convert_labels(db_task.get_labels(prefetch=True))

if function.kind == LambdaType.DETECTOR:
cls._call_detector(function, db_task, labels, quality,
Expand Down

0 comments on commit 4753ddb

Please sign in to comment.