def get_object(self, queryset=None): task = super(TaskScoresMixin, self).get_object(queryset) self.task = annotate_exmo_perms(task, self.request.user) if not self.request.user.has_perm('exmo2010.view_task', self.task): raise PermissionDenied return self.task
def tasks_index(request): if not request.user.is_expertB: raise PermissionDenied queryform = TasksIndexQueryForm(request.user, request.GET) Monitoring.objects.filter() filter = Q(user=request.user, organization__monitoring__status__in=[RATE, RES, INT, FIN]) tasks = Task.objects.filter(filter).prefetch_related('organization__monitoring', 'qanswer_set') if queryform.is_valid(): tasks = queryform.apply(tasks) monitorings = defaultdict(list) tasks = annotate_exmo_perms(tasks, request.user) for task in tasks: monitorings[task.organization.monitoring].append(task) task_order_key = lambda task: task.status + task.completeness / 100 for monitoring in monitorings: monitorings[monitoring].sort(key=task_order_key) context = { 'tasks_exist': Task.objects.filter(filter).exists(), 'monitorings': sorted(monitorings.items(), key=lambda x: x[0].publish_date), 'queryform': queryform } return TemplateResponse(request, 'home/tasks_index.html', context)
def post(self, request, task_pk): task = get_object_or_404(Task, pk=task_pk) annotate_exmo_perms(task, request.user) if request.user.has_perm(self.permission_required, task): try: self.action(task) except ValidationError as e: # Action was not successful, task was not saved in the db # We should return actual task status from db, not the one that was modified in action task = get_object_or_404(Task, pk=task_pk) annotate_exmo_perms(task, request.user) # Add error messages to the status (concatenate all errors). status_display = '%s [%s]' % (task.get_status_display(), ' '.join(e.messages)) return JSONResponse(status_display=status_display, perms=str(task.perms)) elif not request.user.has_perm('exmo2010.view_task', task): raise PermissionDenied return JSONResponse(status_display=task.get_status_display(), perms=str(task.perms))
def get_context_data(self, **kwargs): context = super(RecommendationsView, self).get_context_data(**kwargs) monitoring = self.task.organization.monitoring scores = list(self.task.score_set.all()) comments_by_score = defaultdict(list) for comment in CommentExmo.objects.filter(object_pk__in=[s.pk for s in scores]): comments_by_score[int(comment.object_pk)].append(comment) final_scores = [s for s in scores if s.revision == Score.FINAL] interim_scores_by_param = dict((s.parameter.pk, s) for s in scores if s.revision == Score.INTERIM) param_nonrelevant = set(self.task.organization.parameter_set.values_list('pk', flat=True)) param_relevant = monitoring.parameter_set.exclude(pk__in=param_nonrelevant) param_weight_sum = sum(p.weight for p in param_relevant) scores = [] for score in final_scores: if score.pk in comments_by_score: score.comments = comments_by_score[score.pk] else: if score.parameter.pk in param_nonrelevant: continue if not score.recommendations: continue if param_weight_sum: interim_score = interim_scores_by_param.get(score.parameter.pk) or score score.interim_cost = score.parameter.weight * (100.0 - interim_score.openness) / param_weight_sum score.cost = round_ex(score.parameter.weight * (100.0 - score.openness) / param_weight_sum) else: score.interim_cost = score.cost = 0.0 score.is_finished = bool(score.cost == 0 and not score.recommendations) score.is_relevant = bool(score.parameter.pk not in param_nonrelevant) scores.append(score) scores.sort(key=lambda s: (-s.interim_cost, s.parameter.code)) context.update({ 'scores': annotate_exmo_perms(scores, self.request.user), 'mon': monitoring, 'orgs_count': monitoring.organization_set.count(), 'registered_count': monitoring.organization_set.filter(inv_status__in=('RGS', 'ACT')).count(), 'openness': self.task.openness, 'masked_expert_name': _(config_value('GlobalParameters', 'EXPERT')), 'is_representative': self.request.user.represents(self.task.organization), }) if context['openness'] is None: context.update(total_cost=None, delta=None) else: context.update({ 'total_cost': round_ex(100.0 - float(context['openness'])), 'delta': context['openness'] - self.task.openness_initial }) return context
def monitoring_by_experts(request, monitoring_pk): """ Статистика мониторинга по экспертам. """ monitoring = get_object_or_404(Monitoring, pk=monitoring_pk) if not request.user.has_perm('exmo2010.admin_monitoring', monitoring): raise PermissionDenied experts = Task.objects.filter(organization__monitoring=monitoring).values('user').annotate(cuser=Count('user')) epk = [e['user'] for e in experts] org_list = "( %s )" % " ,".join([str(o.pk) for o in Organization.objects.filter(monitoring=monitoring)]) queryset = User.objects.filter(pk__in=epk).extra(select={ 'open_tasks': 'select count(*) from %(task_table)s where %(task_table)s.user_id = %(user_table)s.id and status = %(status)s and %(task_table)s.organization_id in %(org_list)s' % { 'task_table': Task._meta.db_table, 'user_table': User._meta.db_table, 'org_list': org_list, 'status': Task.TASK_OPEN }, 'ready_tasks': 'select count(*) from %(task_table)s where %(task_table)s.user_id = %(user_table)s.id and status = %(status)s and %(task_table)s.organization_id in %(org_list)s' % { 'task_table': Task._meta.db_table, 'user_table': User._meta.db_table, 'org_list': org_list, 'status': Task.TASK_READY }, 'approved_tasks': 'select count(*) from %(task_table)s where %(task_table)s.user_id = %(user_table)s.id and status = %(status)s and %(task_table)s.organization_id in %(org_list)s' % { 'task_table': Task._meta.db_table, 'user_table': User._meta.db_table, 'org_list': org_list, 'status': Task.TASK_APPROVED }, 'all_tasks': 'select count(*) from %(task_table)s where %(task_table)s.user_id = %(user_table)s.id and %(task_table)s.organization_id in %(org_list)s' % { 'task_table': Task._meta.db_table, 'user_table': User._meta.db_table, 'org_list': org_list, }, }) headers = ( (_('Expert'), 'username', 'username', None, None), (_('Open tasks'), 'open_tasks', None, None, None), (_('Ready tasks'), 'ready_tasks', None, None, None), (_('Approved tasks'), 'approved_tasks', None, None, None), (_('All tasks'), 'all_tasks', None, None, None), ) return table( request, headers, queryset=queryset, paginate_by=15, extra_context={'monitoring': annotate_exmo_perms(monitoring, request.user)}, template_name="manage_monitoring/expert_list.html", )
def tasks_by_monitoring(request, monitoring_pk): user = request.user monitoring = get_object_or_404(Monitoring, pk=monitoring_pk) if not user.is_expert or not user.has_perm('exmo2010.view_monitoring', monitoring): raise PermissionDenied title = _('Task list for %(monitoring)s') % {'monitoring': monitoring} headers = [ (_('organization'), 'organization__name', 'organization__name', None, None), (_('status'), 'status', 'status', int, Task.TASK_STATUS), (_('complete, %'), None, None, None, None), ] if user.is_expertA: users = User.objects.filter( task__organization__monitoring=monitoring).distinct() user_choice = [(u.username, u.profile.legal_name) for u in users] headers.insert(1, (_('expert'), 'user__username', 'user__username', None, user_choice)) else: filter1 = request.GET.get('filter1') if filter1: try: int(filter1) except ValueError: request.GET = QueryDict('') tasks = Task.objects.filter( organization__monitoring=monitoring).select_related( 'user__userprofile', 'organization') # TODO: use queryform instead of table(). return table( request, headers, queryset=perm_filter(request.user, 'view_task', tasks).prefetch_related('qanswer_set'), paginate_by=50, extra_context={ 'monitoring': annotate_exmo_perms(monitoring, request.user), 'title': title, 'invcodeform': SettingsInvCodeForm(), }, template_name="manage_monitoring/tasks.html", )
def tasks_by_monitoring(request, monitoring_pk): user = request.user monitoring = get_object_or_404(Monitoring, pk=monitoring_pk) if not user.is_expert or not user.has_perm('exmo2010.view_monitoring', monitoring): raise PermissionDenied title = _('Task list for %(monitoring)s') % {'monitoring': monitoring} headers = [ (_('organization'), 'organization__name', 'organization__name', None, None), (_('status'), 'status', 'status', int, Task.TASK_STATUS), (_('complete, %'), None, None, None, None), ] if user.is_expertA: users = User.objects.filter(task__organization__monitoring=monitoring).distinct() user_choice = [(u.username, u.profile.legal_name) for u in users] headers.insert(1, (_('expert'), 'user__username', 'user__username', None, user_choice)) else: filter1 = request.GET.get('filter1') if filter1: try: int(filter1) except ValueError: request.GET = QueryDict('') tasks = Task.objects.filter(organization__monitoring=monitoring).select_related('user__userprofile', 'organization') # TODO: use queryform instead of table(). return table( request, headers, queryset=perm_filter(request.user, 'view_task', tasks).prefetch_related('qanswer_set'), paginate_by=50, extra_context={ 'monitoring': annotate_exmo_perms(monitoring, request.user), 'title': title, 'invcodeform': SettingsInvCodeForm(), }, template_name="manage_monitoring/tasks.html", )
def score_view(request, **kwargs): if request.method not in ['POST', 'GET']: return HttpResponseNotAllowed(permitted_methods=['POST', 'GET']) if 'score_pk' in kwargs: # Score edit or view score = get_object_or_404(Score, pk=kwargs['score_pk']) task = score.task param = score.parameter if request.method == 'POST' and not request.user.has_perm('exmo2010.edit_score', score): raise PermissionDenied if request.method == 'GET' and not request.user.has_perm('exmo2010.view_score', score): raise PermissionDenied else: # Score creation by task and param task = get_object_or_404(Task, pk=kwargs['task_pk']) param = get_object_or_404(Parameter, pk=kwargs['parameter_pk']) if not request.user.has_perm('exmo2010.fill_task', task): raise PermissionDenied # Check if score already exist. try: score = Score.objects.get(parameter=param, task=task, revision=Score.FINAL) except Score.DoesNotExist: # OK, score does not exist and can be created. score = Score(parameter=param, task=task, revision=Score.FINAL) else: # User requested score creation by task and param, but score already exist. if request.user.has_perm('exmo2010.view_score', score): # Redirect user to GET score page, even if current request method is POST. # This way duplicate POST to create score will be ignored. return HttpResponseRedirect(reverse('exmo2010:score', args=[score.pk])) else: raise PermissionDenied org = task.organization param.relevant = bool(task.organization not in param.exclude.all()) if not param.relevant and not request.user.is_expert: raise PermissionDenied # Relevant criteria names criteria = ['found'] + filter(param.__getattribute__, Parameter.OPTIONAL_CRITERIA) ScoreForm = modelform_factory( Score, fields=criteria + ['recommendations', 'links', 'editor'], widgets=dict((crit, RadioSelect) for crit in criteria)) ScoreForm.base_fields['comment'] = RichTextFormField(config_name='advanced', required=False) # Replace '-----' with '-' in empty radio choices. for f in criteria: ScoreForm.base_fields[f].widget.choices[0] = ('', '–') form = ScoreForm(request.POST if request.method == 'POST' else None, instance=score) if request.method == 'POST' and form.is_valid(): if score.pk and not request.user.has_perm('exmo2010.edit_score', score): raise PermissionDenied with transaction.commit_on_success(): if org.monitoring.status in Monitoring.after_interaction_status and 'score_pk' in kwargs: if not Score.objects.filter(parameter=param, task=task, revision=Score.INTERIM).exists(): # Interim revision does not exist and should be created. It will have new pk. interim_score = Score.objects.get(pk=kwargs['score_pk']) last_modified = interim_score.last_modified interim_score.pk = None interim_score.revision = Score.INTERIM interim_score.save() # Restore last_modified field, that was overwritten by save() Score.objects.filter(pk=interim_score.pk).update(last_modified=last_modified) _add_comment(request, score) score = form.save(commit=False) score.editor = request.user score.save() if org.monitoring.is_interact or org.monitoring.is_finishing: return HttpResponseRedirect(reverse('exmo2010:score', args=[score.pk])) else: url = reverse('exmo2010:task_scores', args=[task.pk]) return HttpResponseRedirect('%s#parameter_%s' % (url, param.code)) score_interim = Score.objects.filter(parameter=param, task=task, revision=Score.INTERIM) score_table = [{ 'label': score._meta.get_field_by_name(criterion)[0].verbose_name, 'score': getattr(score, criterion), 'score_interim': getattr(score_interim[0], criterion) if score_interim else '', 'criterion': criterion, 'max_score': getattr(score, criterion) == score._meta.get_field(criterion).choices[-1][-1] } for criterion in criteria] score_delta = score.openness - score_interim[0].openness if score_interim else 0 if request.user.is_expert: show_interim_score = True elif request.user.is_anonymous(): show_interim_score = False else: show_interim_score = request.user.profile.show_interim_score all_max = True # Flag if all criteria set to max for crit in criteria: if getattr(score, crit) != score._meta.get_field(crit).choices[-1][-1]: all_max = False criteria = [form[crit] for crit in criteria] for boundfield in criteria: # Add attribute with initial value to each criterion boundfield boundfield.initial = form.initial.get(boundfield.name, boundfield.field.initial) if request.user.is_expertA: claim_list = score.claim_set.all() elif request.user.is_expertB: claim_list = score.claim_set.filter(addressee=request.user) else: claim_list = [] context = { 'form': form, 'score': annotate_exmo_perms(score, request.user), 'score_interim': annotate_exmo_perms(score_interim[0] if score_interim else None, request.user), 'param': annotate_exmo_perms(param, request.user), 'task': annotate_exmo_perms(task, request.user), 'org': org, 'score_table': score_table, 'score_delta': score_delta, 'show_interim_score': show_interim_score, 'masked_expert_name': _(config_value('GlobalParameters', 'EXPERT')), 'criteria': criteria, 'interaction': org.monitoring.is_interact or org.monitoring.is_finishing, 'claim_list': claim_list, 'recommendations_required': param.monitoring.no_interact is False and not all_max, 'comment_form': score.comment_form(request.POST if request.method == 'POST' else None), 'invcodeform': SettingsInvCodeForm(), } return TemplateResponse(request, 'scores/score.html', context)
def score_view(request, **kwargs): if request.method not in ['POST', 'GET']: return HttpResponseNotAllowed(permitted_methods=['POST', 'GET']) if 'score_pk' in kwargs: # Score edit or view score = get_object_or_404(Score, pk=kwargs['score_pk']) task = score.task param = score.parameter if request.method == 'POST' and not request.user.has_perm( 'exmo2010.edit_score', score): raise PermissionDenied if request.method == 'GET' and not request.user.has_perm( 'exmo2010.view_score', score): raise PermissionDenied else: # Score creation by task and param task = get_object_or_404(Task, pk=kwargs['task_pk']) param = get_object_or_404(Parameter, pk=kwargs['parameter_pk']) if not request.user.has_perm('exmo2010.fill_task', task): raise PermissionDenied # Check if score already exist. try: score = Score.objects.get(parameter=param, task=task, revision=Score.FINAL) except Score.DoesNotExist: # OK, score does not exist and can be created. score = Score(parameter=param, task=task, revision=Score.FINAL) else: # User requested score creation by task and param, but score already exist. if request.user.has_perm('exmo2010.view_score', score): # Redirect user to GET score page, even if current request method is POST. # This way duplicate POST to create score will be ignored. return HttpResponseRedirect( reverse('exmo2010:score', args=[score.pk])) else: raise PermissionDenied org = task.organization param.relevant = bool(task.organization not in param.exclude.all()) if not param.relevant and not request.user.is_expert: raise PermissionDenied # Relevant criteria names criteria = ['found'] + filter(param.__getattribute__, Parameter.OPTIONAL_CRITERIA) ScoreForm = modelform_factory( Score, fields=criteria + ['recommendations', 'links', 'editor'], widgets=dict((crit, RadioSelect) for crit in criteria)) ScoreForm.base_fields['comment'] = RichTextFormField( config_name='advanced', required=False) # Replace '-----' with '-' in empty radio choices. for f in criteria: ScoreForm.base_fields[f].widget.choices[0] = ('', '–') form = ScoreForm(request.POST if request.method == 'POST' else None, instance=score) if request.method == 'POST' and form.is_valid(): if score.pk and not request.user.has_perm('exmo2010.edit_score', score): raise PermissionDenied with transaction.commit_on_success(): if org.monitoring.status in Monitoring.after_interaction_status and 'score_pk' in kwargs: if not Score.objects.filter(parameter=param, task=task, revision=Score.INTERIM).exists(): # Interim revision does not exist and should be created. It will have new pk. interim_score = Score.objects.get(pk=kwargs['score_pk']) last_modified = interim_score.last_modified interim_score.pk = None interim_score.revision = Score.INTERIM interim_score.save() # Restore last_modified field, that was overwritten by save() Score.objects.filter(pk=interim_score.pk).update( last_modified=last_modified) _add_comment(request, score) score = form.save(commit=False) score.editor = request.user score.save() if org.monitoring.is_interact or org.monitoring.is_finishing: return HttpResponseRedirect( reverse('exmo2010:score', args=[score.pk])) else: url = reverse('exmo2010:task_scores', args=[task.pk]) return HttpResponseRedirect('%s#parameter_%s' % (url, param.code)) score_interim = Score.objects.filter(parameter=param, task=task, revision=Score.INTERIM) score_table = [{ 'label': score._meta.get_field_by_name(criterion)[0].verbose_name, 'score': getattr(score, criterion), 'score_interim': getattr(score_interim[0], criterion) if score_interim else '', 'criterion': criterion, 'max_score': getattr(score, criterion) == score._meta.get_field(criterion).choices[-1][-1] } for criterion in criteria] score_delta = score.openness - score_interim[ 0].openness if score_interim else 0 if request.user.is_expert: show_interim_score = True elif request.user.is_anonymous(): show_interim_score = False else: show_interim_score = request.user.profile.show_interim_score all_max = True # Flag if all criteria set to max for crit in criteria: if getattr(score, crit) != score._meta.get_field(crit).choices[-1][-1]: all_max = False criteria = [form[crit] for crit in criteria] for boundfield in criteria: # Add attribute with initial value to each criterion boundfield boundfield.initial = form.initial.get(boundfield.name, boundfield.field.initial) if request.user.is_expertA: claim_list = score.claim_set.all() elif request.user.is_expertB: claim_list = score.claim_set.filter(addressee=request.user) else: claim_list = [] context = { 'form': form, 'score': annotate_exmo_perms(score, request.user), 'score_interim': annotate_exmo_perms(score_interim[0] if score_interim else None, request.user), 'param': annotate_exmo_perms(param, request.user), 'task': annotate_exmo_perms(task, request.user), 'org': org, 'score_table': score_table, 'score_delta': score_delta, 'show_interim_score': show_interim_score, 'masked_expert_name': _(config_value('GlobalParameters', 'EXPERT')), 'criteria': criteria, 'interaction': org.monitoring.is_interact or org.monitoring.is_finishing, 'claim_list': claim_list, 'recommendations_required': param.monitoring.no_interact is False and not all_max, 'comment_form': score.comment_form(request.POST if request.method == 'POST' else None), 'invcodeform': SettingsInvCodeForm(), } return TemplateResponse(request, 'scores/score.html', context)
def get_context_data(self, **kwargs): context = super(RecommendationsView, self).get_context_data(**kwargs) monitoring = self.task.organization.monitoring scores = list(self.task.score_set.all()) comments_by_score = defaultdict(list) for comment in CommentExmo.objects.filter( object_pk__in=[s.pk for s in scores]): comments_by_score[int(comment.object_pk)].append(comment) final_scores = [s for s in scores if s.revision == Score.FINAL] interim_scores_by_param = dict( (s.parameter.pk, s) for s in scores if s.revision == Score.INTERIM) param_nonrelevant = set( self.task.organization.parameter_set.values_list('pk', flat=True)) param_relevant = monitoring.parameter_set.exclude( pk__in=param_nonrelevant) param_weight_sum = sum(p.weight for p in param_relevant) scores = [] for score in final_scores: if score.pk in comments_by_score: score.comments = comments_by_score[score.pk] else: if score.parameter.pk in param_nonrelevant: continue if not score.recommendations: continue if param_weight_sum: interim_score = interim_scores_by_param.get( score.parameter.pk) or score score.interim_cost = score.parameter.weight * ( 100.0 - interim_score.openness) / param_weight_sum score.cost = round_ex(score.parameter.weight * (100.0 - score.openness) / param_weight_sum) else: score.interim_cost = score.cost = 0.0 score.is_finished = bool(score.cost == 0 and not score.recommendations) score.is_relevant = bool( score.parameter.pk not in param_nonrelevant) scores.append(score) scores.sort(key=lambda s: (-s.interim_cost, s.parameter.code)) context.update({ 'scores': annotate_exmo_perms(scores, self.request.user), 'mon': monitoring, 'orgs_count': monitoring.organization_set.count(), 'registered_count': monitoring.organization_set.filter(inv_status__in=('RGS', 'ACT')).count(), 'openness': self.task.openness, 'masked_expert_name': _(config_value('GlobalParameters', 'EXPERT')), 'is_representative': self.request.user.represents(self.task.organization), }) if context['openness'] is None: context.update(total_cost=None, delta=None) else: context.update({ 'total_cost': round_ex(100.0 - float(context['openness'])), 'delta': context['openness'] - self.task.openness_initial }) return context
def object_list(request, queryset, paginate_by=None, page=None, allow_empty=True, template_name=None, template_loader=loader, extra_context=None, context_processors=None, template_object_name='object', mimetype=None): """ Generic list of objects. Templates: ``<app_label>/<model_name>_list.html`` Context: object_list list of objects is_paginated are the results paginated? results_per_page number of objects per page (if paginated) has_next is there a next page? has_previous is there a prev page? page the current page next the next page previous the previous page pages number of pages, total hits number of objects, total last_on_page the result number of the last of object in the object_list (1-indexed) first_on_page the result number of the first object in the object_list (1-indexed) page_range: A list of the page numbers (1-indexed). """ if extra_context is None: extra_context = {} queryset = queryset._clone() if paginate_by: paginator = Paginator(queryset, paginate_by) page = request.GET.get('page') try: queryset = paginator.page(page) except PageNotAnInteger: # If page is not an integer, deliver first page. queryset = paginator.page(1) except EmptyPage: # If page is out of range, deliver last page of results. queryset = paginator.page(paginator.num_pages) context = {'%s_list' % template_object_name: annotate_exmo_perms(queryset, request.user)} if not allow_empty and len(queryset) == 0: raise Http404 for key, value in extra_context.items(): if callable(value): context[key] = value() else: context[key] = value if not template_name: model = queryset.model template_name = "%s/%s_list.html" % (model._meta.app_label, model._meta.object_name.lower()) return TemplateResponse(request, template_name, context)
def monitoring_by_experts(request, monitoring_pk): """ Статистика мониторинга по экспертам. """ monitoring = get_object_or_404(Monitoring, pk=monitoring_pk) if not request.user.has_perm('exmo2010.admin_monitoring', monitoring): raise PermissionDenied experts = Task.objects.filter( organization__monitoring=monitoring).values('user').annotate( cuser=Count('user')) epk = [e['user'] for e in experts] org_list = "( %s )" % " ,".join([ str(o.pk) for o in Organization.objects.filter(monitoring=monitoring) ]) queryset = User.objects.filter(pk__in=epk).extra( select={ 'open_tasks': 'select count(*) from %(task_table)s where %(task_table)s.user_id = %(user_table)s.id and status = %(status)s and %(task_table)s.organization_id in %(org_list)s' % { 'task_table': Task._meta.db_table, 'user_table': User._meta.db_table, 'org_list': org_list, 'status': Task.TASK_OPEN }, 'ready_tasks': 'select count(*) from %(task_table)s where %(task_table)s.user_id = %(user_table)s.id and status = %(status)s and %(task_table)s.organization_id in %(org_list)s' % { 'task_table': Task._meta.db_table, 'user_table': User._meta.db_table, 'org_list': org_list, 'status': Task.TASK_READY }, 'approved_tasks': 'select count(*) from %(task_table)s where %(task_table)s.user_id = %(user_table)s.id and status = %(status)s and %(task_table)s.organization_id in %(org_list)s' % { 'task_table': Task._meta.db_table, 'user_table': User._meta.db_table, 'org_list': org_list, 'status': Task.TASK_APPROVED }, 'all_tasks': 'select count(*) from %(task_table)s where %(task_table)s.user_id = %(user_table)s.id and %(task_table)s.organization_id in %(org_list)s' % { 'task_table': Task._meta.db_table, 'user_table': User._meta.db_table, 'org_list': org_list, }, }) headers = ( (_('Expert'), 'username', 'username', None, None), (_('Open tasks'), 'open_tasks', None, None, None), (_('Ready tasks'), 'ready_tasks', None, None, None), (_('Approved tasks'), 'approved_tasks', None, None, None), (_('All tasks'), 'all_tasks', None, None, None), ) return table( request, headers, queryset=queryset, paginate_by=15, extra_context={ 'monitoring': annotate_exmo_perms(monitoring, request.user) }, template_name="manage_monitoring/expert_list.html", )