def _recursive_group_problems(problems, result_info, categories, div_id): if not categories: problems.sort(key=attrgetter('short_name')) results = [result_info[problem] for problem in problems] percentages = [ 100.0 * result['score'] / result['max_score'] if result['exists'] else 0.0 for result in results ] if len(problems) > 0: total_percent_solved = sum(percentages) / len(problems) else: total_percent_solved = 0.0 return { 'div_id': 'problems-' + div_id, 'subnodes': {}, 'problem_info': zip(problems, results), 'progress_percentage': total_percent_solved, 'progress_percentage_rounded': round(total_percent_solved, 1), 'attempted': any(result['exists'] for result in results) } node = { 'div_id': div_id, 'subnodes': {}, 'problem_info': [], 'progress_percentage': 0.0, 'progress_percentage_rounded': 0.0, 'attempted': False } category = categories[0] iter = groupby(problems, key=lambda problem: \ get_prefetched_value(problem, category)) total_percentage = 0 for value, group in iter: child_id = div_id + '-' + str(value.value) child_problems = list(group) child = _recursive_group_problems(child_problems, result_info, categories[1:], child_id) node['subnodes'][value] = child if child['attempted'] == True: node['attempted'] = True total_percentage += child['progress_percentage'] * len(child_problems) if len(problems) > 0: total_percentage /= len(problems) else: total_percentage = 0.0 node['progress_percentage'] = total_percentage node['progress_percentage_rounded'] = round(total_percentage, 1) return node
def task_archive_tag_view(request, origin_tag): origin_tag = OriginTag.objects.filter(name=origin_tag).prefetch_related( 'localizations', 'info_categories__localizations', 'info_categories__parent_tag__localizations', 'info_categories__values__localizations', 'info_categories__values__parent_tag__localizations', ) origin_tag = get_object_or_404(origin_tag) categories = origin_tag.info_categories.all() # We use getparams for filtering by OriginInfo - make sure they are valid for getparam in _filter_neutral_get_params(request.GET.keys()): if not categories.filter(name=getparam).exists(): raise Http404 problems = (origin_tag.problems.all().select_related( 'problemsite', 'main_problem_instance').prefetch_related( 'origininfovalue_set__localizations', 'origininfovalue_set__category')) problems = _filter_problems_prefetched(problems, request.GET) # We want to achieve something like Django's regroup, but with dynamic keys: # 1. Don't use categories with Null order for grouping categories = sorted([cat for cat in categories if cat.order], key=lambda cat: cat.order) # 2. Stable sort the problem list by each category in reverse order. # This gives the correct order for the final grouping. for cat in categories[::-1]: problems.sort( key=lambda problem: get_prefetched_value(problem, cat).order) # 3. Now we can recursively group the problem list by each category. user_results = _get_results_info(request, problems) problems_root_node = _recursive_group_problems(problems, user_results, categories, 'problemgroups') navbar_links = navbar_links_registry.template_context(request) return TemplateResponse( request, 'problems/task-archive-tag.html', { 'origin_tag': origin_tag, 'problems': problems_root_node, 'navbar_links': navbar_links, }, )
def _recursive_group_problems(problems, categories, div_id): if not categories: return { 'div_id': 'problems-' + div_id, 'subnodes': {}, 'problem_list': list(problems) } node = {'div_id': div_id, 'subnodes': {}, 'problem_list': []} category = categories[0] iter = groupby(problems, key=lambda problem: \ get_prefetched_value(problem, category)) for value, group in iter: child_id = div_id + '-' + str(value.value) node['subnodes'][value] = \ _recursive_group_problems(list(group), categories[1:], child_id) return node