Skip to content

Commit 92e75f0

Browse files
authored
Merge pull request #260 from stanfordnmbl/dev
[DO NOT MERGE UNTIL AFTER DIGITAL HEALTH DAY] mono queuing and queue index improvements
2 parents f952ec0 + 55915d3 commit 92e75f0

File tree

9 files changed

+141
-15
lines changed

9 files changed

+141
-15
lines changed

mcserver/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class SessionAdmin(admin.ModelAdmin):
5858
'public',
5959
'created_at', 'updated_at', 'server',
6060
'status', 'status_changed',
61-
'trashed', 'trashed_at',
61+
'trashed', 'trashed_at', 'isMono',
6262
)
6363
raw_id_fields = ('user', 'subject')
6464
search_fields = ['id', 'user__username', "subject__name"]
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 4.2.22 on 2025-06-05 21:23
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('mcserver', '0039_merge_20241008_1623'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='session',
15+
name='is_mono',
16+
field=models.BooleanField(default=False),
17+
),
18+
migrations.AlterField(
19+
model_name='subject',
20+
name='sex_at_birth',
21+
field=models.CharField(blank=True, choices=[('woman', 'Female'), ('man', 'Male'), ('intersect', 'Intersex'), ('not-listed', 'Not Listed'), ('prefer-not-respond', 'Prefer Not to Respond')], max_length=20, null=True),
22+
),
23+
]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 4.2.22 on 2025-06-05 23:28
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('mcserver', '0040_session_is_mono_alter_subject_sex_at_birth'),
10+
]
11+
12+
operations = [
13+
migrations.RemoveField(
14+
model_name='session',
15+
name='is_mono',
16+
),
17+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.2.22 on 2025-06-06 16:03
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('mcserver', '0041_remove_session_is_mono'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='session',
15+
name='isMono',
16+
field=models.BooleanField(default=False),
17+
),
18+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.2.22 on 2025-06-06 16:50
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('mcserver', '0042_session_ismono'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='session',
15+
name='isMono',
16+
field=models.BooleanField(db_index=True, default=False),
17+
),
18+
]
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Generated by Django 3.1.14 on 2025-06-06 22:29
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('mcserver', '0043_alter_session_ismono'),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name='analysisresult',
15+
name='status',
16+
field=models.IntegerField(choices=[(100, 'Continue'), (101, 'Switching Protocols'), (102, 'Processing'), (200, 'OK'), (201, 'Created'), (202, 'Accepted'), (203, 'Non-Authoritative Information'), (204, 'No Content'), (205, 'Reset Content'), (206, 'Partial Content'), (207, 'Multi-Status'), (208, 'Already Reported'), (226, 'IM Used'), (300, 'Multiple Choices'), (301, 'Moved Permanently'), (302, 'Found'), (303, 'See Other'), (304, 'Not Modified'), (305, 'Use Proxy'), (307, 'Temporary Redirect'), (308, 'Permanent Redirect'), (400, 'Bad Request'), (401, 'Unauthorized'), (402, 'Payment Required'), (403, 'Forbidden'), (404, 'Not Found'), (405, 'Method Not Allowed'), (406, 'Not Acceptable'), (407, 'Proxy Authentication Required'), (408, 'Request Timeout'), (409, 'Conflict'), (410, 'Gone'), (411, 'Length Required'), (412, 'Precondition Failed'), (413, 'Request Entity Too Large'), (414, 'Request-URI Too Long'), (415, 'Unsupported Media Type'), (416, 'Requested Range Not Satisfiable'), (417, 'Expectation Failed'), (421, 'Misdirected Request'), (422, 'Unprocessable Entity'), (423, 'Locked'), (424, 'Failed Dependency'), (426, 'Upgrade Required'), (428, 'Precondition Required'), (429, 'Too Many Requests'), (431, 'Request Header Fields Too Large'), (500, 'Internal Server Error'), (501, 'Not Implemented'), (502, 'Bad Gateway'), (503, 'Service Unavailable'), (504, 'Gateway Timeout'), (505, 'HTTP Version Not Supported'), (506, 'Variant Also Negotiates'), (507, 'Insufficient Storage'), (508, 'Loop Detected'), (510, 'Not Extended'), (511, 'Network Authentication Required')], default=200, help_text='Status code function responsed with.', verbose_name='Status'),
17+
),
18+
migrations.AlterField(
19+
model_name='trial',
20+
name='updated_at',
21+
field=models.DateTimeField(auto_now=True, db_index=True),
22+
),
23+
]

mcserver/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ class Session(models.Model):
7878

7979
trashed = models.BooleanField(default=False)
8080
trashed_at = models.DateTimeField(blank=True, null=True)
81+
82+
isMono = models.BooleanField(default=False, db_index=True)
8183

8284
class Meta:
8385
ordering = ['-created_at']
@@ -107,7 +109,7 @@ class Trial(models.Model):
107109
name = models.CharField(max_length=64, null=True)
108110
meta = models.JSONField(blank=True, null=True)
109111
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
110-
updated_at = models.DateTimeField(auto_now=True)
112+
updated_at = models.DateTimeField(auto_now=True, db_index=True)
111113
server = models.GenericIPAddressField(null=True, blank=True)
112114
is_docker = models.BooleanField(null=True, blank=True)
113115
hostname = models.CharField(max_length=64, null=True, blank=True)

mcserver/serializers.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,16 @@ def session_name(self, session):
201201
return str(session.id).split("-")[0]
202202

203203
def get_sessionName(self, session):
204-
return session.meta.get("sessionName", "") if session.meta else ""
204+
if hasattr(session, 'meta') and session.meta:
205+
return session.meta.get("sessionName", "")
206+
return ""
205207

206208
class Meta:
207209
model = Session
208210
fields = [
209211
'id', 'user', 'public', 'name', 'sessionName',
210212
'qrcode', 'meta', 'trials', 'server',
211-
'subject',
213+
'subject', 'isMono',
212214
'created_at', 'updated_at',
213215
'trashed', 'trashed_at', 'trials_count', 'trashed_trials_count',
214216
]
@@ -226,7 +228,7 @@ class Meta:
226228
fields = [
227229
'id', 'user', 'public', 'name', 'sessionName',
228230
'qrcode', 'meta', 'trials', 'server',
229-
'subject',
231+
'subject', 'isMono',
230232
'created_at', 'updated_at',
231233
'trashed', 'trashed_at', 'trials_count', 'trashed_trials_count',
232234
]
@@ -254,7 +256,9 @@ def session_name(self, session):
254256
return str(session.id).split("-")[0]
255257

256258
def get_sessionName(self, session):
257-
return session.meta.get("sessionName", "")
259+
if hasattr(session, 'meta') and session.meta:
260+
return session.meta.get("sessionName", "")
261+
return ""
258262

259263

260264
class SessionStatusSerializer(serializers.ModelSerializer):
@@ -431,3 +435,4 @@ class AnalysisDashboardSerializer(serializers.ModelSerializer):
431435
class Meta:
432436
model = AnalysisDashboard
433437
fields = ('id', 'title', 'function', 'template', 'layout')
438+

mcserver/views.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,12 @@ def get_n_calibrated_cameras(self, request, pk):
231231
calibration_trials = session.trial_set.filter(name="calibration")
232232
last_calibration_trial_num_videos = 0
233233

234+
if session.isMono:
235+
return Response({
236+
'error_message': error_message,
237+
'data': 1
238+
})
239+
234240
# Check if there is a calibration trial. If not, it must be in a parent session.
235241
loop_counter = 0
236242
while not calibration_trials and session.meta.get('sessionWithCalibration') and loop_counter < 100:
@@ -389,9 +395,10 @@ def valid(self, request):
389395
sessions = sessions.filter(subject=subject)
390396

391397
# A session is valid only if at least one trial is the "neutral" trial and its status is "done".
398+
# OR if it is a monocular session, which doesn't require a neutral trial.
392399
for session in sessions:
393400
trials = Trial.objects.filter(session__exact=session, name__exact="neutral")
394-
if trials.count() < 1:
401+
if trials.count() < 1 and not session.isMono:
395402
sessions = sessions.exclude(id__exact=session.id)
396403

397404
# Sort by
@@ -1016,6 +1023,10 @@ def set_metadata(self, request, pk):
10161023
"posemodel": request.GET.get("subject_pose_model",""),
10171024
}
10181025

1026+
if "isMono" in request.GET:
1027+
session.isMono = request.GET.get("isMono", "").lower() == 'true'
1028+
1029+
10191030
if "settings_framerate" in request.GET:
10201031
session.meta["settings"] = {
10211032
"framerate": request.GET.get("settings_framerate",""),
@@ -1460,16 +1471,22 @@ def dequeue(self, request):
14601471
try:
14611472
ip = get_client_ip(request)
14621473

1463-
workerType = self.request.query_params.get('workerType')
1474+
workerType = self.request.query_params.get('workerType', 'all')
1475+
isMonoQuery = self.request.query_params.get('isMono', 'False')
1476+
1477+
14641478

14651479
# find trials with some videos not uploaded
14661480
not_uploaded = Video.objects.filter(video='',
14671481
updated_at__gte=datetime.now() + timedelta(minutes=-15)).values_list("trial__id", flat=True)
1482+
14681483

1469-
print(not_uploaded)
1470-
1471-
uploaded_trials = Trial.objects.exclude(id__in=not_uploaded)
1472-
# uploaded_trials = Trial.objects.all()
1484+
if isMonoQuery == 'False':
1485+
uploaded_trials = Trial.objects.filter(updated_at__gte=datetime.now() + timedelta(days=-7)).exclude(
1486+
id__in=not_uploaded).exclude(session__isMono=True)
1487+
else:
1488+
uploaded_trials = Trial.objects.filter(updated_at__gte=datetime.now() + timedelta(days=-7)).exclude(
1489+
id__in=not_uploaded).filter(session__isMono=True)
14731490

14741491
if workerType != 'dynamic':
14751492
# Priority for 'calibration' and 'neutral'
@@ -1501,7 +1518,10 @@ def dequeue(self, request):
15011518
raise Http404
15021519

15031520
# prioritize admin and priority group trials (priority group doesn't exist yet, but should have same priv. as user)
1504-
trialsPrioritized = trials.filter(session__user__groups__name__in=["admin","priority"])
1521+
trialsPrioritized = trials.filter(session__user__groups__name__in=["admin"])
1522+
# if no admin trials, go to priority group trials
1523+
if trialsPrioritized.count() == 0:
1524+
trialsPrioritized = trials.filter(session__user__groups__name__in=["priority"])
15051525
# if not priority trials, go to normal trials
15061526
if trialsPrioritized.count() == 0:
15071527
trialsPrioritized = trials
@@ -1532,7 +1552,7 @@ def dequeue(self, request):
15321552
raise APIException(_('trial_dequeue_error'))
15331553

15341554
return Response(serializer.data)
1535-
1555+
15361556
@action(detail=False, permission_classes=[((IsAdmin | IsBackend))])
15371557
def get_trials_with_status(self, request):
15381558
"""
@@ -2575,4 +2595,4 @@ def data(self, request, pk):
25752595
return Response(dashboard.get_available_data())
25762596

25772597
return Response(dashboard.get_available_data(
2578-
only_public=True, subject_id=request.GET.get('subject_id'), share_token=request.GET.get('share_token')))
2598+
only_public=True, subject_id=request.GET.get('subject_id'), share_token=request.GET.get('share_token')))

0 commit comments

Comments
 (0)