views.py 7.08 KB
Newer Older
1
from __future__ import annotations
2
from django.utils.translation import gettext as _
Enrico Zini's avatar
Enrico Zini committed
3
4
from django import forms
from django.shortcuts import redirect
5
from django.views.generic import TemplateView
6
from django.views.generic.edit import FormView
Enrico Zini's avatar
Enrico Zini committed
7
from django.utils.timezone import now, utc
8
9
from backend.mixins import VisitorMixin, VisitPersonMixin
from process.mixins import VisitProcessMixin
10
from backend.models import Person, Fingerprint
Enrico Zini's avatar
Enrico Zini committed
11
12
import backend.models as bmodels
import process.models as pmodels
13
from backend import const
14
import datetime
15
from nm2.lib import assets
16
from . import ops as mops
17
18
19
20
21
22


class Uploaders(VisitorMixin, TemplateView):
    """
    List inactive uploaders
    """
23
    assets = [assets.DataTablesBootstrap4]
24
25
26
27
28
29
30
31
32
33
34
    template_name = "mia/uploaders.html"

    def get_context_data(self, **kw):
        ctx = super().get_context_data(**kw)

        days = int(self.request.GET.get("days", 365 * 2))
        ctx["days"] = days

        # Map each Person to the Fingerprint with the most recent last_upload
        by_person = {}
        for fpr in Fingerprint.objects.filter(person__status=const.STATUS_DD_U).select_related("person"):
Enrico Zini's avatar
Enrico Zini committed
35
36
            if fpr.last_upload is None:
                continue
37
38
39
40
41
42
43
            old = by_person.get(fpr.person)
            if old is None or old.last_upload < fpr.last_upload:
                by_person[fpr.person] = fpr

        today = now().date()
        fprs = []
        for fpr in by_person.values():
Enrico Zini's avatar
Enrico Zini committed
44
45
            if (today - fpr.last_upload).days < days:
                continue
46
            fprs.append(fpr)
Enrico Zini's avatar
Enrico Zini committed
47
        fprs.sort(key=lambda f: f.last_upload)
48
        ctx["fprs"] = fprs
Enrico Zini's avatar
Enrico Zini committed
49
        ctx["no_fpr"] = Person.objects.filter(
Enrico Zini's avatar
Enrico Zini committed
50
                status__in=(const.STATUS_DD_NU, const.STATUS_DD_U), fprs__isnull=True).order_by("ldap_fields__uid")
51
        return ctx
52
53
54
55
56
57


class Voters(VisitorMixin, TemplateView):
    """
    List inactive voters
    """
58
    assets = [assets.DataTablesBootstrap4]
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    template_name = "mia/voters.html"

    def get_context_data(self, **kw):
        ctx = super().get_context_data(**kw)

        days = int(self.request.GET.get("days", 365 * 2))
        ctx["days"] = days

        # Map each Person to the Fingerprint with the most recent last_upload
        today = now().date()
        people = []
        for person in Person.objects.filter(status__in=(const.STATUS_DD_U, const.STATUS_DD_NU)):
            if person.last_vote is None:
                last = person.status_changed.date()
            else:
                last = person.last_vote
Enrico Zini's avatar
Enrico Zini committed
75
76
            if (today - last).days < days:
                continue
77
78
79
            people.append(person)

        fallback_sort_date = datetime.date(1970, 1, 1)
Enrico Zini's avatar
Enrico Zini committed
80
        people.sort(key=lambda p: (p.last_vote or fallback_sort_date))
81
82
        ctx["people"] = people
        return ctx
83
84
85
86
87
88
89
90
91
92
93
94


class MIAPingForm(forms.Form):
    email = forms.CharField(
        required=True,
        label=_("Email introduction"),
        widget=forms.Textarea(attrs=dict(rows=10, cols=80))
    )


class MIAPing(VisitPersonMixin, FormView):
    require_visitor = "admin"
95
    template_name = "mia/miaping.html"
96
97
98
99
100
101
102
103
104
105
106
    form_class = MIAPingForm

    def get_initial(self):
        initial = super().get_initial()
        initial["email"] = """
We are currently in the process of checking the activity of accounts
in the Debian LDAP, after the MIA team has contacted you already.
""".strip()
        return initial

    def form_valid(self, form):
107
        op = mops.WATPing(audit_author=self.request.user, person=self.person, text=form.cleaned_data["email"])
108
109
110
111
112
113
114
115
116
117
118
119
120
121
        op.execute(self.request)
        return redirect(op._process.get_absolute_url())


class MIARemoveForm(forms.Form):
    email = forms.CharField(
        required=True,
        label=_("Email introduction"),
        widget=forms.Textarea(attrs=dict(rows=10, cols=80))
    )


class MIARemove(VisitProcessMixin, FormView):
    require_visitor = "admin"
122
    template_name = "mia/miaremove.html"
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
    form_class = MIARemoveForm

    def get_context_data(self, **kw):
        ctx = super().get_context_data(**kw)
        ctx["status"] = self.compute_process_status()
        return ctx

    def get_initial(self):
        initial = super().get_initial()
        initial["email"] = """
We've sent the last warning email on {:%Y-%m-%d}, with no response.
""".format(self.process.started).strip()
        return initial

    def form_valid(self, form):
138
        op = mops.WATRemove(audit_author=self.request.user, process=self.process, text=form.cleaned_data["email"])
139
140
        op.execute(self.request)
        return redirect(self.process.get_absolute_url())
Enrico Zini's avatar
Enrico Zini committed
141
142
143
144
145
146
147
148
149
150
151
152


class FrontDesk(VisitorMixin, TemplateView):
    """
    List inactive uploaders
    """
    assets = [assets.DataTablesBootstrap4]
    template_name = "mia/fd.html"

    def get_context_data(self, **kw):
        ctx = super().get_context_data(**kw)
        from django.db.models import Max
Enrico Zini's avatar
Enrico Zini committed
153
        epoch = datetime.datetime(1970, 1, 1, tzinfo=utc)
Enrico Zini's avatar
Enrico Zini committed
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170

        fd = []
        for am in bmodels.AM.objects.filter(is_fd=True).select_related("person"):
            vals = [
                bmodels.PersonAuditLog.objects.filter(author=am.person).aggregate(val=Max("logdate")),
                pmodels.Process.objects.filter(frozen_by=am.person).aggregate(val=Max("frozen_time")),
                pmodels.Process.objects.filter(approved_by=am.person).aggregate(val=Max("approved_time")),
                pmodels.Requirement.objects.filter(approved_by=am.person).aggregate(val=Max("approved_time")),
                pmodels.AMAssignment.objects.filter(assigned_by=am.person).aggregate(val=Max("assigned_time")),
                pmodels.AMAssignment.objects.filter(unassigned_by=am.person).aggregate(val=Max("unassigned_time")),
                pmodels.Log.objects.filter(changed_by=am.person).aggregate(val=Max("logdate")),
            ]
            dates = [x["val"] for x in vals if x["val"] is not None]
            if not dates:
                fd.append((am.person, None))
            else:
                fd.append((am.person, max(dates)))
Enrico Zini's avatar
Enrico Zini committed
171
        fd.sort(key=lambda x: x[1] if x[1] is not None else epoch)
Enrico Zini's avatar
Enrico Zini committed
172
173
        ctx["fd"] = fd

Enrico Zini's avatar
Enrico Zini committed
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
        ams = []
        for am in bmodels.AM.objects.filter(is_am=True).select_related("person"):
            vals = [
                bmodels.PersonAuditLog.objects.filter(author=am.person).aggregate(val=Max("logdate")),
                pmodels.Requirement.objects.filter(approved_by=am.person).aggregate(val=Max("approved_time")),
                pmodels.AMAssignment.objects.filter(assigned_by=am.person).aggregate(val=Max("assigned_time")),
                pmodels.AMAssignment.objects.filter(unassigned_by=am.person).aggregate(val=Max("unassigned_time")),
                pmodels.Log.objects.filter(changed_by=am.person).aggregate(val=Max("logdate")),
                pmodels.Statement.objects.filter(uploaded_by=am.person).aggregate(val=Max("uploaded_time")),
            ]
            dates = [x["val"] for x in vals if x["val"] is not None]
            if not dates:
                ams.append((am.person, None))
            else:
                ams.append((am.person, max(dates)))
        ams.sort(key=lambda x: x[1] if x[1] is not None else epoch)
        ctx["am"] = ams

Enrico Zini's avatar
Enrico Zini committed
192
        return ctx