33from django .views .generic import TemplateView , DetailView
44
55from .models import Competition , CompetitionParticipant
6+ from django .core .serializers .json import DjangoJSONEncoder
7+
8+ from django .db .models import Q
9+ import json
10+ from django .contrib .auth .decorators import login_required
11+ from django .views .decorators .http import require_POST
12+ from django .shortcuts import get_object_or_404
13+ from django .http import JsonResponse , HttpResponseForbidden , HttpResponseBadRequest , HttpResponseRedirect
14+ from django .urls import reverse
15+ from django .db import transaction
16+ from django .contrib import messages
17+
18+
19+ from profiles .models import CustomGroup , User
20+ from queues .models import Queue
21+
622
723
824class CompetitionManagement (LoginRequiredMixin , TemplateView ):
@@ -21,6 +37,47 @@ class CompetitionUpdateForm(LoginRequiredMixin, DetailView):
2137 template_name = 'competitions/form.html'
2238 queryset = Competition .objects .all ()
2339
40+
41+ def get_context_data (self , ** kwargs ):
42+ ctx = super ().get_context_data (** kwargs )
43+ comp = self .object
44+
45+ groups_qs = CustomGroup .objects .filter (
46+ Q (id__in = comp .participant_groups .values_list ('id' , flat = True ))
47+ ).select_related ('queue' ).prefetch_related ('user_set' )
48+
49+ ctx ['available_groups_json' ] = json .dumps ([
50+ {
51+ 'id' : g .id ,
52+ 'name' : g .name ,
53+ 'queue' : g .queue .name if g .queue else None ,
54+ 'members' : [u .username for u in g .user_set .all ()],
55+ }
56+ for g in groups_qs
57+ ], cls = DjangoJSONEncoder )
58+
59+ ctx ['selected_group_ids_json' ] = json .dumps (
60+ list (comp .participant_groups .values_list ('id' , flat = True )),
61+ cls = DjangoJSONEncoder
62+ )
63+
64+ ctx ['available_queues_json' ] = json .dumps (
65+ list (Queue .objects .all ().values ('id' , 'name' )),
66+ cls = DjangoJSONEncoder
67+ )
68+
69+ ctx ['available_users_json' ] = json .dumps (
70+ list (
71+ User .objects
72+ .filter (is_active = True )
73+ .values ('id' , 'username' , 'email' )
74+ ),
75+ cls = DjangoJSONEncoder
76+ )
77+
78+ return ctx
79+
80+
2481 def get_object (self , * args , ** kwargs ):
2582 competition = super ().get_object (* args , ** kwargs )
2683
@@ -76,7 +133,7 @@ def get_object(self, *args, **kwargs):
76133 # get participants from CompetitionParticipant where user=user and competition=competition
77134 is_participant = CompetitionParticipant .objects .filter (user = self .request .user , competition = competition ).count () > 0
78135
79- # check if secret key provided is valid
136+ # check if secret key provided is valid,
80137 valid_secret_key = self .request .GET .get ('secret_key' ) == str (competition .secret_key )
81138
82139 if (
@@ -104,3 +161,157 @@ def get_context_data(self, **kwargs):
104161
105162class CompetitionDetailedResults (LoginRequiredMixin , TemplateView ):
106163 template_name = 'competitions/detailed_results.html'
164+
165+
166+ @login_required
167+ @require_POST
168+ def competition_create_group (request , pk ):
169+ competition = get_object_or_404 (Competition , pk = pk )
170+
171+ user = request .user
172+ if not (user .is_superuser or user == competition .created_by or user in competition .collaborators .all ()):
173+ return HttpResponseForbidden ("Not allowed" )
174+
175+ if request .content_type == 'application/json' :
176+ try :
177+ payload = json .loads (request .body .decode ())
178+ except Exception :
179+ return HttpResponseBadRequest ("Invalid JSON" )
180+ name = (payload .get ('name' ) or '' ).strip ()
181+ queue_id = payload .get ('queue_id' )
182+ user_ids = payload .get ('user_ids' ) or []
183+ else :
184+ name = (request .POST .get ('name' ) or '' ).strip ()
185+ queue_id = request .POST .get ('queue_id' ) or None
186+ user_ids = request .POST .getlist ('user_ids' ) or []
187+ if not user_ids and request .POST .get ('user_ids' ):
188+ user_ids = [u .strip () for u in request .POST .get ('user_ids' ).split (',' ) if u .strip ()]
189+
190+ if not name :
191+ return HttpResponseBadRequest ("Missing name" )
192+
193+ try :
194+ with transaction .atomic ():
195+ group = CustomGroup (name = name )
196+ if queue_id :
197+ try :
198+ queue = Queue .objects .get (pk = queue_id )
199+ group .queue = queue
200+ except Queue .DoesNotExist :
201+ group .queue = None
202+ group .save ()
203+
204+ if user_ids :
205+ # normalize to ints
206+ try :
207+ user_ids_int = [int (u ) for u in user_ids ]
208+ except Exception :
209+ user_ids_int = []
210+ if user_ids_int :
211+ users_qs = User .objects .filter (pk__in = user_ids_int )
212+ group .user_set .set (users_qs )
213+
214+ competition .participant_groups .add (group )
215+
216+ members = list (group .user_set .values_list ('username' , flat = True ))
217+ group_data = {
218+ 'id' : group .id ,
219+ 'name' : group .name ,
220+ 'queue' : group .queue .name if group .queue else None ,
221+ 'members' : members ,
222+ }
223+ except Exception as e :
224+ return HttpResponseBadRequest ("Error creating group: %s" % str (e ))
225+
226+ if request .is_ajax () or request .content_type == 'application/json' or request .headers .get ('X-Requested-With' ) == 'XMLHttpRequest' :
227+ return JsonResponse ({'status' : 'ok' , 'group' : group_data })
228+
229+ messages .success (request , "Groupe créé" )
230+ return HttpResponseRedirect (reverse ('competitions:edit' , kwargs = {'pk' : competition .pk }))
231+
232+
233+ @login_required
234+ @require_POST
235+ def competition_update_group (request , pk , group_id ):
236+ competition = get_object_or_404 (Competition , pk = pk )
237+ group = get_object_or_404 (CustomGroup , pk = group_id )
238+
239+ user = request .user
240+ if not (user .is_superuser or user == competition .created_by or user in competition .collaborators .all ()):
241+ return HttpResponseForbidden ("Not allowed" )
242+
243+ if request .content_type == 'application/json' :
244+ try :
245+ payload = json .loads (request .body .decode ())
246+ except Exception :
247+ return HttpResponseBadRequest ("Invalid JSON" )
248+ name = (payload .get ('name' ) or '' ).strip ()
249+ queue_id = payload .get ('queue_id' )
250+ user_ids = payload .get ('user_ids' , []) or []
251+ else :
252+ name = (request .POST .get ('name' ) or '' ).strip ()
253+ queue_id = request .POST .get ('queue_id' ) or None
254+ user_ids = request .POST .getlist ('user_ids[]' ) or []
255+ if not user_ids and request .POST .get ('user_ids' ):
256+ user_ids = [u .strip () for u in request .POST .get ('user_ids' ).split (',' ) if u .strip ()]
257+
258+ if not name :
259+ return HttpResponseBadRequest ("Missing name" )
260+
261+ try :
262+ with transaction .atomic ():
263+ group .name = name
264+ if queue_id :
265+ group .queue = Queue .objects .filter (pk = queue_id ).first ()
266+ else :
267+ group .queue = None
268+ group .save ()
269+
270+ # normalize user ids and set membership
271+ try :
272+ user_ids_int = [int (u ) for u in user_ids ]
273+ except Exception :
274+ user_ids_int = []
275+ group .user_set .set (User .objects .filter (pk__in = user_ids_int ))
276+ except Exception as e :
277+ return HttpResponseBadRequest ("Error updating group: %s" % str (e ))
278+
279+ resp = {
280+ 'status' : 'ok' ,
281+ 'group' : {
282+ 'id' : group .id ,
283+ 'name' : group .name ,
284+ 'queue' : group .queue .name if group .queue else None ,
285+ 'members' : list (group .user_set .values_list ('username' , flat = True )),
286+ }
287+ }
288+
289+ if request .is_ajax () or request .content_type == 'application/json' or request .headers .get ('X-Requested-With' ) == 'XMLHttpRequest' :
290+ return JsonResponse (resp )
291+
292+ messages .success (request , "Groupe modifié" )
293+ return HttpResponseRedirect (reverse ('competitions:edit' , kwargs = {'pk' : competition .pk }))
294+
295+
296+ @login_required
297+ @require_POST
298+ def competition_delete_group (request , pk , group_id ):
299+ competition = get_object_or_404 (Competition , pk = pk )
300+ group = get_object_or_404 (CustomGroup , pk = group_id )
301+
302+ user = request .user
303+ if not (user .is_superuser or user == competition .created_by or user in competition .collaborators .all ()):
304+ return HttpResponseForbidden ("Not allowed" )
305+
306+ try :
307+ with transaction .atomic ():
308+ competition .participant_groups .remove (group )
309+ group .delete ()
310+ except Exception as e :
311+ return HttpResponseBadRequest ("Error deleting group: %s" % str (e ))
312+
313+ if request .is_ajax () or request .content_type == 'application/json' or request .headers .get ('X-Requested-With' ) == 'XMLHttpRequest' :
314+ return JsonResponse ({'status' : 'ok' , 'group_id' : group_id })
315+
316+ messages .success (request , "Groupe supprimé" )
317+ return HttpResponseRedirect (reverse ('competitions:edit' , kwargs = {'pk' : competition .pk }))
0 commit comments