Skip to content

Commit e2a84eb

Browse files
IdirLISNObada Haddad
authored andcommitted
added logic to create group in competition with yaml
1 parent 3ee0dcb commit e2a84eb

1 file changed

Lines changed: 137 additions & 1 deletion

File tree

src/apps/competitions/unpackers/base_unpacker.py

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from django.core.files import File
77
from django.test import RequestFactory
88
from django.utils import timezone
9+
from django.db import transaction
910

1011
from api.serializers.competitions import CompetitionSerializer
1112
from api.serializers.leaderboards import LeaderboardSerializer
@@ -14,6 +15,7 @@
1415
from datasets.models import Data
1516
from queues.models import Queue
1617
from tasks.models import Task, Solution
18+
from profiles.models import CustomGroup
1719
from utils.storage import md5
1820
from .utils import CompetitionUnpackingException, zip_if_directory
1921

@@ -27,6 +29,7 @@ def __init__(self, competition_yaml, temp_directory, creator):
2729
self.created_tasks = []
2830
self.created_solutions = []
2931
self.created_datasets = []
32+
self.created_groups = []
3033

3134
# We'll make a fake request to pass to DRF serializers for request.user context
3235
self.fake_request = RequestFactory()
@@ -198,13 +201,133 @@ def _unpack_terms(self):
198201
raise NotImplementedError
199202

200203
def _unpack_image(self):
204+
201205
try:
202206
image_name = self.competition_yaml['image']
203207
except KeyError:
204208
raise CompetitionUnpackingException('An image for this competition could not be found in the yaml')
205209

206210
self.competition['logo'] = self._read_image(image_name)
207211

212+
def _unpack_groups(self):
213+
"""
214+
Parse groups from YAML.
215+
Expected format:
216+
groups:
217+
- name: "Group A"
218+
queue: "Queue Name" # optional
219+
"""
220+
raw = self.competition_yaml.get('groups')
221+
222+
if not raw:
223+
self.competition['participant_groups_raw'] = []
224+
return
225+
226+
parsed = []
227+
228+
for g in raw:
229+
name = (g.get('name') or '').strip()
230+
231+
if not name:
232+
raise CompetitionUnpackingException(
233+
'Each group must have a non-empty "name" field.'
234+
)
235+
236+
parsed.append({
237+
'name': name,
238+
'queue_field': g.get('queue')
239+
})
240+
241+
self.competition['participant_groups_raw'] = parsed
242+
243+
244+
245+
def _save_groups(self, competition):
246+
"""
247+
Create CustomGroup objects and attach them to competition.
248+
If group already exists, it is reused.
249+
"""
250+
251+
groups_raw = self.competition.get('participant_groups_raw') or []
252+
253+
if not groups_raw:
254+
return
255+
256+
import uuid
257+
258+
with transaction.atomic():
259+
for grp in groups_raw:
260+
name = grp['name'].strip()
261+
queue_field = grp.get('queue_field')
262+
263+
if not name:
264+
raise CompetitionUnpackingException(
265+
"Participant group name cannot be empty."
266+
)
267+
queue_obj = None
268+
269+
if queue_field:
270+
if isinstance(queue_field, int) or (
271+
isinstance(queue_field, str) and queue_field.isdigit()
272+
):
273+
queue_obj = Queue.objects.filter(pk=int(queue_field)).first()
274+
275+
else:
276+
try:
277+
uuid_value = uuid.UUID(str(queue_field))
278+
queue_obj = Queue.objects.filter(vhost=uuid_value).first()
279+
except ValueError:
280+
queue_obj = None
281+
282+
if not queue_obj:
283+
queues = Queue.objects.filter(name=queue_field)
284+
285+
if queues.count() > 1:
286+
raise CompetitionUnpackingException(
287+
f"Multiple queues found with name '{queue_field}'. "
288+
f"Use id or UUID instead for group '{name}'."
289+
)
290+
291+
queue_obj = queues.first()
292+
293+
if not queue_obj:
294+
raise CompetitionUnpackingException(
295+
f"Queue '{queue_field}' does not exist "
296+
f"for group '{name}'."
297+
)
298+
299+
if not queue_obj.is_public:
300+
organizers = queue_obj.organizers.values_list(
301+
'username', flat=True
302+
)
303+
304+
if (
305+
queue_obj.owner != self.creator
306+
and self.creator.username not in organizers
307+
):
308+
raise CompetitionUnpackingException(
309+
f"You do not have access to queue '{queue_field}' "
310+
f"for group '{name}'."
311+
)
312+
313+
group, created = CustomGroup.objects.get_or_create(
314+
name=name,
315+
defaults={'queue': queue_obj}
316+
)
317+
318+
if not created and queue_field:
319+
if group.queue != queue_obj:
320+
group.queue = queue_obj
321+
group.save()
322+
323+
if created:
324+
self.created_groups.append(group)
325+
326+
competition.participant_groups.add(group)
327+
328+
329+
330+
208331
def _unpack_queue(self):
209332
# Get Queue by vhost/uuid. If instance not returned, or we don't have access don't set it!
210333
vhost = self.competition_yaml.get('queue')
@@ -343,6 +466,11 @@ def _save_competition(self):
343466
def _clean(self):
344467
for dataset in self.created_datasets:
345468
dataset.delete()
469+
for group in getattr(self, 'created_groups', []):
470+
try:
471+
group.delete()
472+
except Exception:
473+
pass
346474
for task in self.created_tasks:
347475
task.delete()
348476
for solution in self.created_solutions:
@@ -353,7 +481,15 @@ def save(self):
353481
self._save_tasks()
354482
self._save_solutions()
355483
self._save_leaderboards()
356-
return self._save_competition()
484+
self._unpack_groups()
485+
486+
# Create competition
487+
competition_instance = self._save_competition()
488+
489+
# Create and attach groups
490+
self._save_groups(competition_instance)
491+
492+
return competition_instance
357493
except Exception as e:
358494
self._clean()
359495
raise e

0 commit comments

Comments
 (0)