TabbycatDebate/tabbycat

View on GitHub
tabbycat/adjallocation/views.py

Summary

Maintainability
F
3 days
Test Coverage
F
48%
import json
import logging

from django.contrib import messages
from django.db.models import Prefetch
from django.forms import ModelChoiceField
from django.utils.translation import gettext as _, gettext_lazy, ngettext
from django.views.generic.base import TemplateView

from actionlog.mixins import LogActionMixin
from actionlog.models import ActionLogEntry
from availability.utils import annotate_availability
from participants.models import Adjudicator, Region
from participants.prefetch import populate_feedback_scores
from tournaments.mixins import DebateDragAndDropMixin, TournamentMixin
from utils.misc import ranks_dictionary, redirect_tournament, reverse_tournament
from utils.mixins import AdministratorMixin
from utils.views import ModelFormSetView

from .conflicts import ConflictsInfo, HistoryInfo
from .models import (AdjudicatorAdjudicatorConflict, AdjudicatorInstitutionConflict,
                     AdjudicatorTeamConflict,
                     PreformedPanelAdjudicator, TeamInstitutionConflict)
from .serializers import EditDebateAdjsDebateSerializer, EditPanelAdjsPanelSerializer, EditPanelOrDebateAdjSerializer

logger = logging.getLogger(__name__)


class BaseEditDebateOrPanelAdjudicatorsView(DebateDragAndDropMixin, AdministratorMixin, TemplateView):

    def get_extra_info(self):
        info = super().get_extra_info()
        # TODO: construct adj score ranges from settings
        info['highlights']['gender'] = [
            {'pk': 'm', 'fields': {'name': _('Male')}},
            {'pk': 'f', 'fields': {'name': _('Female')}},
            {'pk': 'o', 'fields': {'name': _('Other')}},
            {'pk': 'u', 'fields': {'name': _('Unknown')}},
        ]
        info['adjMinScore'] = self.tournament.pref('adj_min_score')
        info['adjMaxScore'] = self.tournament.pref('adj_max_score')
        info['highlights']['rank'] = ranks_dictionary(
            self.tournament, info['adjMinScore'], info['adjMaxScore'])
        regions = [{'pk': r.id, 'fields': {'name': r.name}} for r in Region.objects.all()]
        info['highlights']['region'] = regions
        allocation_preferences = [
            'draw_rules__adj_min_voting_score',
            'draw_rules__adj_conflict_penalty',
            'draw_rules__adj_history_penalty',
            'draw_rules__preformed_panel_mismatch_penalty',
            'draw_rules__no_trainee_position',
            'draw_rules__no_panellist_position',
        ]
        info['allocationSettings'] = {}
        for key in allocation_preferences:
            info['allocationSettings'][key] = self.tournament.preferences[key]

        info['clashes'] = self.get_adjudicator_conflicts()
        info['histories'] = self.get_history_conflicts()
        info['hasPreformedPanels'] = self.round.preformedpanel_set.exists()
        return info

    def get_serialised_allocatable_items(self):
        adjs = Adjudicator.objects.filter(tournament=self.tournament)
        adjs = annotate_availability(adjs, self.round)
        populate_feedback_scores(adjs)
        weight = self.tournament.current_round.feedback_weight
        serialized_adjs = EditPanelOrDebateAdjSerializer(
            adjs, many=True, context={'feedback_weight': weight})
        return self.json_render(serialized_adjs.data)

    def get_adjudicator_conflicts(self):
        conflicts = ConflictsInfo(teams=self.tournament.team_set.all(),
                                  adjudicators=self.tournament.adjudicator_set.all())
        team_conflicts, adj_conflicts = conflicts.serialized_by_participant()
        return {'teams': team_conflicts, 'adjudicators': adj_conflicts}

    def get_history_conflicts(self):
        history = HistoryInfo(self.round)
        team_history, adj_history = history.serialized_by_participant()
        return {'teams': team_history,  'adjudicators': adj_history}

    def get_context_data(self, **kwargs):
        kwargs['vueDebatesOrPanelAdjudicators'] = json.dumps(None)
        return super().get_context_data(**kwargs)


class EditDebateAdjudicatorsView(BaseEditDebateOrPanelAdjudicatorsView):
    template_name = "edit_debate_adjudicators.html"
    page_title = gettext_lazy("Edit Allocation")
    prefetch_adjs = True # Fetched in full as get_serialised

    def get_extra_info(self):
        info = super().get_extra_info()
        return info

    def debates_or_panels_factory(self, debates):
        return EditDebateAdjsDebateSerializer(
            debates, many=True, context={'sides': self.tournament.sides,
                                         'round': self.round})


class EditPanelAdjudicatorsView(BaseEditDebateOrPanelAdjudicatorsView):
    template_name = "edit_panel_adjudicators.html"
    page_title = gettext_lazy("Edit Panels")

    def get_extra_info(self):
        info = super().get_extra_info()
        info['backUrl'] = reverse_tournament('panel-adjudicators-index',
                                             self.tournament)  # Override
        info['backLabel'] = _("Return to Panels Overview")
        return info

    def get_draw_or_panels_objects(self):
        panels = self.round.preformedpanel_set.all().prefetch_related(
            Prefetch('preformedpaneladjudicator_set',
                queryset=PreformedPanelAdjudicator.objects.select_related('adjudicator')),
        )
        return panels

    def debates_or_panels_factory(self, panels):
        return EditPanelAdjsPanelSerializer(panels, many=True,
                                            context={'round': self.round})


class PanelAdjudicatorsIndexView(TemplateView, AdministratorMixin):
    template_name = "preformed_index.html"
    page_title = gettext_lazy("Preformed Panels")


# ==============================================================================
# Conflict formset views
# ==============================================================================

class TeamChoiceField(ModelChoiceField):

    def label_from_instance(self, obj):
        return obj.short_name


class BaseAdjudicatorConflictsView(LogActionMixin, AdministratorMixin, TournamentMixin, ModelFormSetView):

    template_name = 'edit_conflicts.html'
    page_emoji = "🔶"

    formset_factory_kwargs = {
        'extra': 10,
        'can_delete': True,
    }

    def get_context_data(self, **kwargs):
        kwargs['save_text'] = self.save_text
        return super().get_context_data(**kwargs)

    def get_success_url(self, *args, **kwargs):
        return reverse_tournament('importer-simple-index', self.tournament)

    def formset_valid(self, formset):
        result = super().formset_valid(formset)
        nsaved = len(self.instances)
        ndeleted = len(formset.deleted_objects)
        self.add_message(nsaved, ndeleted)
        if "add_more" in self.request.POST:
            return redirect_tournament(self.same_view, self.tournament)
        return result


class AdjudicatorTeamConflictsView(BaseAdjudicatorConflictsView):

    action_log_type = ActionLogEntry.ActionType.CONFLICTS_ADJ_TEAM_EDIT
    formset_model = AdjudicatorTeamConflict
    page_title = gettext_lazy("Adjudicator-Team Conflicts")
    save_text = gettext_lazy("Save Adjudicator-Team Conflicts")
    same_view = 'adjallocation-conflicts-adj-team'
    formset_factory_kwargs = BaseAdjudicatorConflictsView.formset_factory_kwargs.copy()
    formset_factory_kwargs.update({
        'fields': ('adjudicator', 'team'),
        'field_classes': {'team': TeamChoiceField},
    })

    def get_formset(self):
        formset = super().get_formset()
        all_adjs = self.tournament.adjudicator_set.order_by('name').all()
        all_teams = self.tournament.team_set.order_by('short_name').all()
        for form in formset:
            form.fields['adjudicator'].queryset = all_adjs  # order alphabetically
            form.fields['team'].queryset = all_teams        # order alphabetically
        return formset

    def get_formset_queryset(self):
        return self.formset_model.objects.filter(
            adjudicator__tournament=self.tournament,
        ).order_by('adjudicator__name')

    def add_message(self, nsaved, ndeleted):
        if nsaved > 0:
            messages.success(self.request, ngettext(
                "Saved %(count)d adjudicator-team conflict.",
                "Saved %(count)d adjudicator-team conflicts.",
                nsaved,
            ) % {'count': nsaved})
        if ndeleted > 0:
            messages.success(self.request, ngettext(
                "Deleted %(count)d adjudicator-team conflict.",
                "Deleted %(count)d adjudicator-team conflicts.",
                ndeleted,
            ) % {'count': ndeleted})
        if nsaved == 0 and ndeleted == 0:
            messages.success(self.request, _("No changes were made to adjudicator-team conflicts."))


class AdjudicatorAdjudicatorConflictsView(BaseAdjudicatorConflictsView):

    action_log_type = ActionLogEntry.ActionType.CONFLICTS_ADJ_ADJ_EDIT
    formset_model = AdjudicatorAdjudicatorConflict
    page_title = gettext_lazy("Adjudicator-Adjudicator Conflicts")
    save_text = gettext_lazy("Save Adjudicator-Adjudicator Conflicts")
    same_view = 'adjallocation-conflicts-adj-adj'
    formset_factory_kwargs = BaseAdjudicatorConflictsView.formset_factory_kwargs.copy()
    formset_factory_kwargs.update({'fields': ('adjudicator1', 'adjudicator2')})

    def get_formset(self):
        formset = super().get_formset()
        all_adjs = self.tournament.adjudicator_set.order_by('name').all()
        for form in formset:
            form.fields['adjudicator1'].queryset = all_adjs  # order alphabetically
            form.fields['adjudicator2'].queryset = all_adjs  # order alphabetically
        return formset

    def get_formset_queryset(self):
        return self.formset_model.objects.filter(
            adjudicator1__tournament=self.tournament,
        ).order_by('adjudicator1__name')

    def add_message(self, nsaved, ndeleted):
        if nsaved > 0:
            messages.success(self.request, ngettext(
                "Saved %(count)d adjudicator-adjudicator conflict.",
                "Saved %(count)d adjudicator-adjudicator conflicts.",
                nsaved,
            ) % {'count': nsaved})
        if ndeleted > 0:
            messages.success(self.request, ngettext(
                "Deleted %(count)d adjudicator-adjudicator conflict.",
                "Deleted %(count)d adjudicator-adjudicator conflicts.",
                ndeleted,
            ) % {'count': ndeleted})
        if nsaved == 0 and ndeleted == 0:
            messages.success(self.request, _("No changes were made to adjudicator-adjudicator conflicts."))


class AdjudicatorInstitutionConflictsView(BaseAdjudicatorConflictsView):

    action_log_type = ActionLogEntry.ActionType.CONFLICTS_ADJ_INST_EDIT
    formset_model = AdjudicatorInstitutionConflict
    page_title = gettext_lazy("Adjudicator-Institution Conflicts")
    save_text = gettext_lazy("Save Adjudicator-Institution Conflicts")
    same_view = 'adjallocation-conflicts-adj-inst'
    formset_factory_kwargs = BaseAdjudicatorConflictsView.formset_factory_kwargs.copy()
    formset_factory_kwargs.update({'fields': ('adjudicator', 'institution')})

    def get_formset(self):
        formset = super().get_formset()
        all_adjs = self.tournament.adjudicator_set.order_by('name').all()
        for form in formset:
            form.fields['adjudicator'].queryset = all_adjs  # order alphabetically
        return formset

    def get_formset_queryset(self):
        return self.formset_model.objects.filter(
            adjudicator__tournament=self.tournament,
        ).order_by('adjudicator__name')

    def add_message(self, nsaved, ndeleted):
        if nsaved > 0:
            messages.success(self.request, ngettext(
                "Saved %(count)d adjudicator-institution conflict.",
                "Saved %(count)d adjudicator-institution conflicts.",
                nsaved,
            ) % {'count': nsaved})
        if ndeleted > 0:
            messages.success(self.request, ngettext(
                "Deleted %(count)d adjudicator-institution conflict.",
                "Deleted %(count)d adjudicator-institution conflicts.",
                ndeleted,
            ) % {'count': ndeleted})
        if nsaved == 0 and ndeleted == 0:
            messages.success(self.request, _("No changes were made to adjudicator-institution conflicts."))


class TeamInstitutionConflictsView(BaseAdjudicatorConflictsView):

    action_log_type = ActionLogEntry.ActionType.CONFLICTS_TEAM_INST_EDIT
    formset_model = TeamInstitutionConflict
    page_title = gettext_lazy("Team-Institution Conflicts")
    save_text = gettext_lazy("Save Team-Institution Conflicts")
    same_view = 'adjallocation-conflicts-team-inst'
    formset_factory_kwargs = BaseAdjudicatorConflictsView.formset_factory_kwargs.copy()
    formset_factory_kwargs.update({
        'fields': ('team', 'institution'),
        'field_classes': {'team': TeamChoiceField},
    })

    def get_formset(self):
        formset = super().get_formset()
        all_teams = self.tournament.team_set.order_by('short_name').all()
        for form in formset:
            form.fields['team'].queryset = all_teams  # order alphabetically
        return formset

    def get_formset_queryset(self):
        return self.formset_model.objects.filter(
            team__tournament=self.tournament,
        ).order_by('team__short_name')

    def add_message(self, nsaved, ndeleted):
        if nsaved > 0:
            messages.success(self.request, ngettext(
                "Saved %(count)d team-institution conflict.",
                "Saved %(count)d team-institution conflicts.",
                nsaved,
            ) % {'count': nsaved})
        if ndeleted > 0:
            messages.success(self.request, ngettext(
                "Deleted %(count)d team-institution conflict.",
                "Deleted %(count)d team-institution conflicts.",
                ndeleted,
            ) % {'count': ndeleted})
        if nsaved == 0 and ndeleted == 0:
            messages.success(self.request, _("No changes were made to team-institution conflicts."))