11import zipfile
22import json
33import csv
4- from collections import OrderedDict
4+ from collections import Counter , OrderedDict
55from io import StringIO
66from django .http import HttpResponse
77from tempfile import SpooledTemporaryFile
@@ -424,26 +424,43 @@ def collect_leaderboard_data(self, competition, phase_pk=None):
424424 phase_id = phases [0 ].id
425425
426426 leaderboard = Leaderboard .objects .prefetch_related ('columns' ).get (phases = phase_id )
427- leaderboard_titles = {phase ['id' ]: f'{ leaderboard .title } - { phase ["name" ]} ({ phase ["id" ]} )' for phase in submission_query }
427+ leaderboard_titles = {
428+ phase ['id' ]: f'{ leaderboard .title } - { phase ["name" ]} ({ phase ["id" ]} )'
429+ for phase in submission_query
430+ }
428431 leaderboard_data = {title : {} for title in leaderboard_titles .values ()}
429432
430433 for phase in submission_query :
431434 generated_columns = OrderedDict ()
432435 for task in phase ['tasks' ]:
433436 for col in leaderboard .columns .all ():
434- generated_columns .update ({f'{ col .key } -{ task ["id" ]} ' : f'{ task ["name" ]} ({ task ["id" ]} )-{ col .title } ' })
437+ generated_columns .update ({
438+ f'{ col .key } -{ task ["id" ]} ' : f'{ task ["name" ]} ({ task ["id" ]} )-{ col .title } '
439+ })
440+
435441 for submission in phase ['submissions' ]:
436- submission_key = f'{ submission ["owner" ]} -{ submission ["parent" ] or submission ["id" ]} '
437- if submission_key not in leaderboard_data [leaderboard_titles [phase ['id' ]]].keys ():
438- leaderboard_data [leaderboard_titles [phase ['id' ]]].update ({submission_key : OrderedDict ()})
439- if 'fact_sheet_answers' in submission .keys () and submission ['fact_sheet_answers' ]:
440- leaderboard_data [leaderboard_titles [phase ['id' ]]][submission_key ]\
441- .update ({'fact_sheet_answers' : submission ['fact_sheet_answers' ]})
442+ queue_name = submission .get ('queue_name' ) or ''
443+ submission_key = f'{ submission ["owner" ]} -{ submission ["id" ]} '
444+ if queue_name :
445+ submission_key = f'{ submission_key } -{ queue_name } '
446+
447+ if submission_key not in leaderboard_data [leaderboard_titles [phase ['id' ]]]:
448+ leaderboard_data [leaderboard_titles [phase ['id' ]]][submission_key ] = OrderedDict ()
449+
450+ if submission .get ('fact_sheet_answers' ):
451+ leaderboard_data [leaderboard_titles [phase ['id' ]]][submission_key ].update ({
452+ 'fact_sheet_answers' : submission ['fact_sheet_answers' ]
453+ })
454+
442455 for col_title in generated_columns .values ():
443456 leaderboard_data [leaderboard_titles [phase ['id' ]]][submission_key ].update ({col_title : "" })
457+
444458 for score in submission ['scores' ]:
445459 score_column = generated_columns [f'{ score ["column_key" ]} -{ submission ["task" ]} ' ]
446- leaderboard_data [leaderboard_titles [phase ['id' ]]][submission_key ].update ({score_column : score ['score' ]})
460+ leaderboard_data [leaderboard_titles [phase ['id' ]]][submission_key ].update ({
461+ score_column : score ['score' ]
462+ })
463+
447464 return leaderboard_data
448465
449466 @action (detail = True , methods = ['GET' ], renderer_classes = [JSONRenderer , CSVRenderer , ZipRenderer ])
@@ -773,6 +790,23 @@ def rerun_submissions(self, request, pk):
773790 @action (detail = True , methods = ['GET' ], permission_classes = [AllowAny ])
774791 def get_leaderboard (self , request , pk ):
775792 phase = self .get_object ()
793+
794+ def _clean_group_label (raw_name , submission_parent_id = None ):
795+ if not raw_name :
796+ return None
797+
798+ label = str (raw_name )
799+
800+ if submission_parent_id is not None :
801+ prefix = f"{ submission_parent_id } _"
802+ if label .startswith (prefix ):
803+ label = label [len (prefix ):]
804+
805+ if "__" in label :
806+ label = label .rsplit ("__" , 1 )[1 ]
807+
808+ return label or None
809+
776810 if phase .competition .fact_sheet :
777811 fact_sheet_keys = [
778812 (
@@ -792,21 +826,73 @@ def get_leaderboard(self, request, pk):
792826 'submissions' : [],
793827 'tasks' : [],
794828 'fact_sheet_keys' : fact_sheet_keys or None ,
795- 'primary_index' : query ['leaderboard' ]['primary_index' ]
829+ 'primary_index' : query ['leaderboard' ]['primary_index' ],
830+ 'has_group_queues' : False ,
796831 }
797832
798- columns = [ col for col in query ['columns' ]]
833+ columns = list ( query ['columns' ])
799834 submissions_keys = {}
800835 submission_detailed_results = {}
801836
837+ group_name_by_user_queue = {}
838+ for group in phase .competition .participant_groups .filter (
839+ queue__isnull = False
840+ ).select_related ('queue' ).prefetch_related ('user_set' ):
841+ cleaned_group_name = _clean_group_label (group .name )
842+ for user in group .user_set .all ():
843+ group_name_by_user_queue [(user .username , group .queue_id )] = cleaned_group_name
844+
845+ parent_ids = {
846+ s ['parent' ]
847+ for s in query ['submissions' ]
848+ if s ['parent' ] is not None
849+ }
850+ parent_task_counts = Counter (
851+ (s ['parent' ], s ['task' ])
852+ for s in query ['submissions' ]
853+ if s ['parent' ] is not None
854+ )
855+
802856 for submission in query ['submissions' ]:
803- submission_key = f"{ submission ['owner' ]} { submission ['parent' ] or submission ['id' ]} "
857+ if submission ['id' ] in parent_ids :
858+ continue
859+
860+ submission_parent_id = submission .get ('parent' ) or submission .get ('id' )
861+ raw_queue_name = submission .get ('queue_name' ) or ''
862+ queue_id = submission .get ('queue_id' )
863+
864+ group_name = group_name_by_user_queue .get (
865+ (submission ['owner' ], queue_id )
866+ ) if queue_id else None
867+
868+ group_label = _clean_group_label (
869+ group_name or raw_queue_name ,
870+ submission_parent_id = submission_parent_id
871+ )
872+
873+ display_group = (
874+ f"{ submission_parent_id } _{ group_label } "
875+ if group_label
876+ else None
877+ )
878+
879+ parent_id = submission ['parent' ]
880+ task_id = submission .get ('task' )
881+
882+ # Cas particulier: plusieurs submissions d'un même parent sans queue explicite
883+ is_multi_group_null_queue = (
884+ parent_id is not None
885+ and not queue_id
886+ and parent_task_counts .get ((parent_id , task_id ), 0 ) > 1
887+ )
888+
889+ if is_multi_group_null_queue :
890+ submission_key = f"{ submission ['owner' ]} { parent_id } _{ submission ['id' ]} "
891+ else :
892+ submission_key = f"{ submission ['owner' ]} { submission_parent_id } _{ group_label or '' } "
893+
804894 # gather detailed result from submissions for each task
805- # detailed_results are gathered based on submission key
806- # `id` is used to fetch the right detailed result in detailed results page
807- # `detailed_result` url is not needed
808895 submission_detailed_results .setdefault (submission_key , []).append ({
809- # 'detailed_result': submission['detailed_result'],
810896 'task' : submission ['task' ],
811897 'id' : submission ['id' ]
812898 })
@@ -821,23 +907,18 @@ def get_leaderboard(self, request, pk):
821907 'fact_sheet_answers' : submission ['fact_sheet_answers' ],
822908 'slug_url' : submission ['slug_url' ],
823909 'organization' : submission ['organization' ],
824- 'created_when' : submission ['created_when' ]
910+ 'created_when' : submission ['created_when' ],
911+ 'queue_name' : display_group ,
825912 })
826913
827- for score in submission ['scores' ]:
914+ if queue_id or is_multi_group_null_queue :
915+ response ['has_group_queues' ] = True
828916
829- # to check if a column is found
830- # this is useful because of `hidden` field
831- # if a column is hidden it will not be shown here so
832- # we will not return that score to the front-end
917+ for score in submission ['scores' ]:
833918 column_found = False
834- # default precision is set to 2
835919 precision = 2
836- # default hidden is set to false
837920 hidden = False
838921
839- # loop over columns to find a column with the same index
840- # replace default precision with column precision
841922 for col in columns :
842923 if col ["index" ] == score ["index" ]:
843924 precision = col ["precision" ]
@@ -847,13 +928,8 @@ def get_leaderboard(self, request, pk):
847928
848929 tempScore = score
849930 tempScore ['task_id' ] = submission ['task' ]
850- # round the score to 'precision' decimal points
851931 tempScore ['score' ] = str (round (float (tempScore ["score" ]), precision ))
852932
853- # only add scores to the scores list
854- # if this column is found
855- # and
856- # column is not hidden
857933 if column_found and not hidden :
858934 response ['submissions' ][submissions_keys [submission_key ]]['scores' ].append (tempScore )
859935
@@ -877,7 +953,6 @@ def get_leaderboard(self, request, pk):
877953 # --- end pagination addition ---
878954
879955 for task in query ['tasks' ]:
880- # This can be used to rendered variable columns on each task
881956 tempTask = {
882957 'name' : task ['name' ],
883958 'id' : task ['id' ],
0 commit comments