Skip to content
Merged

Dev #292

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 33 additions & 8 deletions mcserver/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin, GroupAdmin
from django.contrib.admin.models import LogEntry
from django.db.models import Exists, OuterRef
from datetime import timedelta


Expand Down Expand Up @@ -54,17 +55,24 @@ class TrialInline(admin.TabularInline):
@admin.register(Session)
class SessionAdmin(admin.ModelAdmin):
list_display = (
'id', 'user', 'subject',
'public',
'id', 'user', 'subject', 'session_name',
'public', 'isMono',
'created_at', 'updated_at', 'server',
'status', 'status_changed',
'trashed', 'trashed_at', 'isMono',
'trashed', 'trashed_at',
)
raw_id_fields = ('user', 'subject')
search_fields = ['id', 'user__username', "subject__name"]
search_fields = ['id', 'user__username', 'subject__name', 'meta__sessionName', 'trial__name']
inlines = [TrialInline]
actions = ['set_subject']

def session_name(self, obj):
if obj.meta:
return obj.meta.get('sessionName', '')
return ''

session_name.short_description = 'Session name'

def set_subject(self, request, queryset):
from .forms import SubjectSelectForm

Expand Down Expand Up @@ -96,21 +104,37 @@ class ResultInline(admin.TabularInline):

@admin.register(Trial)
class TrialAdmin(admin.ModelAdmin):
search_fields = ['id', 'name', 'session__id']
search_fields = ['id', 'name', 'session__id', 'session__meta__sessionName']
list_display = (
'id',
'name',
'session',
'status',
'has_lidar_data',
'created_at', 'updated_at',
'server', 'git_commit',
'formatted_duration', 'formatted_count',
'is_meta_null',
'trashed', 'trashed_at',
)
raw_id_fields = ('session',)
readonly_fields = ('has_lidar_data',)
inlines = [ResultInline]

def get_queryset(self, request):
qs = super().get_queryset(request)
lidar_videos = Video.objects.filter(
trial=OuterRef('pk'),
isLidar=True,
)
return qs.annotate(_has_lidar_data=Exists(lidar_videos))

def has_lidar_data(self, obj):
return obj._has_lidar_data

has_lidar_data.short_description = 'Lidar'
has_lidar_data.boolean = True

def is_meta_null(self, obj):
return obj.meta is None

Expand All @@ -135,14 +159,15 @@ class ResultAdmin(admin.ModelAdmin):
'id', 'trial', 'tag', 'media',
'device_id',
'created_at', 'updated_at')
search_fields = ['id', 'trial__id']
search_fields = ['id', 'trial__id', 'trial__name']
raw_id_fields = ('trial',)


@admin.register(Video)
class VideoAdmin(admin.ModelAdmin):
search_fields = ['id', 'trial__id']
list_display = ('id', 'trial', 'video', 'created_at', 'updated_at')
search_fields = ['id', 'trial__id', 'trial__name']
list_display = ('id', 'trial', 'video', 'isLidar', 'created_at', 'updated_at')

raw_id_fields = ('trial',)


Expand Down
18 changes: 18 additions & 0 deletions mcserver/migrations/0045_video_islidar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.1.14 on 2026-06-13 06:21

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('mcserver', '0044_auto_20250606_1629'),
]

operations = [
migrations.AddField(
model_name='video',
name='isLidar',
field=models.BooleanField(default=False),
),
]
1 change: 1 addition & 0 deletions mcserver/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ class Video(models.Model):
video_thumb = models.FileField(blank=True, null=True, upload_to=random_filename)
keypoints = models.FileField(blank=True, null=True)
parameters = models.JSONField(blank=True, null=True)
isLidar = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
updated_at = models.DateTimeField(auto_now=True, db_index=True)

Expand Down
37 changes: 32 additions & 5 deletions mcserver/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,24 @@
TrialTags
)
from rest_framework.validators import UniqueValidator
from django.db.models import Prefetch, Q
from django.db.models import Prefetch, Q, Exists, OuterRef
from django.utils.translation import gettext as _


def annotate_has_lidar_data(queryset):
lidar_videos = Video.objects.filter(
trial=OuterRef('pk'),
parameters__has_lidar_data=True,
)
return queryset.annotate(_has_lidar_data=Exists(lidar_videos))


def trial_has_lidar_data(trial):
if hasattr(trial, '_has_lidar_data'):
return trial._has_lidar_data
return trial.video_set.filter(parameters__has_lidar_data=True).exists()


class UserSerializer(serializers.ModelSerializer):
email = serializers.EmailField(
required=True,
Expand Down Expand Up @@ -131,7 +145,7 @@ class VideoSerializer(serializers.ModelSerializer):
required=False)
class Meta:
model = Video
fields = ['id', 'trial', 'device_id', 'video', 'video_url', 'video_thumb', 'parameters', 'created_at', 'updated_at']
fields = ['id', 'trial', 'device_id', 'video', 'video_url', 'video_thumb', 'parameters', 'isLidar', 'created_at', 'updated_at']


# Serializers define the API representation.
Expand All @@ -147,7 +161,15 @@ class Meta:
class TrialSerializer(serializers.ModelSerializer):
videos = VideoSerializer(source='video_set', many=True)
results = ResultSerializer(source='result_set', many=True)

hasLidarData = serializers.SerializerMethodField()

@staticmethod
def setup_eager_loading(queryset):
return annotate_has_lidar_data(queryset)

def get_hasLidarData(self, trial):
return trial_has_lidar_data(trial)

class Meta:
model = Trial
fields = [
Expand All @@ -156,7 +178,7 @@ class Meta:
'server', 'is_docker', 'hostname',
'processed_duration', 'processed_count',
'git_commit',
'trashed', 'trashed_at',
'trashed', 'trashed_at', 'hasLidarData',
]


Expand All @@ -173,7 +195,12 @@ class SessionSerializer(serializers.ModelSerializer):
@staticmethod
def setup_eager_loading(queryset):
# queryset = queryset.prefetch_related("trial_set").all()
queryset = queryset.prefetch_related(Prefetch('trial_set', queryset=Trial.objects.order_by('created_at'))).all()
queryset = queryset.prefetch_related(
Prefetch(
'trial_set',
queryset=annotate_has_lidar_data(Trial.objects.order_by('created_at')),
)
).all()

return queryset

Expand Down
11 changes: 8 additions & 3 deletions mcserver/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1563,9 +1563,11 @@ def set_session_status(self, request, pk):
# - if no it asks again in 5 sec
# - if yes it runs processing and sends back the results
class TrialViewSet(viewsets.ModelViewSet):
queryset = Trial.objects.all().order_by("created_at")
serializer_class = TrialSerializer
permission_classes = [IsPublic | (IsOwner | IsAdmin | IsBackend)]

@setup_eager_loading
def get_queryset(self):
user = self.request.user
# Admins and backend users see all trials
Expand All @@ -1579,17 +1581,17 @@ def get_queryset(self):
# Unauthenticated users see only public trials
else:
return Trial.objects.filter(session__public=True).order_by("created_at")

def get_permissions(self):
custom_permission_classes = None
if self.action in ['dequeue', 'get_trials_with_status']:
custom_permission_classes = [IsAdmin | IsBackend]
elif self.request.method in ['POST', 'PUT', 'PATCH', 'DELETE']:
custom_permission_classes = [IsOwner | IsAdmin | IsBackend]

if custom_permission_classes is not None:
return [permission() for permission in custom_permission_classes]

return super().get_permissions()

@action(detail=False)
Expand Down Expand Up @@ -1879,6 +1881,9 @@ def perform_update(self, serializer):
serializer.validated_data["video"] = serializer.validated_data["video_url"]
del serializer.validated_data["video_url"]

if "isLidar" in self.request.data:
serializer.validated_data["isLidar"] = str(self.request.data.get("isLidar", "")).lower() == "true"

super().perform_update(serializer)

class ResultViewSet(viewsets.ModelViewSet):
Expand Down
Loading