views.py 14.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# nm.debian.org website reports
#
# Copyright (C) 2012  Enrico Zini <enrico@debian.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from django import http, template, forms
from django.shortcuts import render_to_response, redirect
Enrico Zini's avatar
Enrico Zini committed
20
from django.contrib.auth.decorators import login_required
Enrico Zini's avatar
Enrico Zini committed
21
from django.utils.translation import ugettext as _
22
import backend.models as bmodels
Enrico Zini's avatar
Enrico Zini committed
23
from backend import const
Enrico Zini's avatar
Enrico Zini committed
24
import backend.auth
25

Enrico Zini's avatar
Enrico Zini committed
26
@backend.auth.is_am
Enrico Zini's avatar
Enrico Zini committed
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def amlist(request):
    from django.db import connection
    # Here is a list of active Application Managers:
    #     Login   Name    cur max hold    done    Role

    # Compute statistics indexed by AM id
    cursor = connection.cursor()
    cursor.execute("""
    SELECT am.id,
           count(*) as total,
           count(process.is_active) as active,
           count(process.progress=%s) as held
      FROM am
      JOIN process ON process.manager_id=am.id
     GROUP BY am.id
    """, (const.PROGRESS_AM_HOLD,))
    stats = dict()
    for amid, total, active, held in cursor:
        stats[amid] = (total, active, held)

    ams_active = []
    ams_inactive = []
    for a in bmodels.AM.objects.all().order_by("person__uid"):
        total, active, held = stats.get(a.id, (0, 0, 0))
        a.stats_total = total
        a.stats_active = active
        a.stats_done = total-active
        a.stats_held = held
        if a.is_am:
            ams_active.append(a)
        else:
            ams_inactive.append(a)

60
    return render_to_response("restricted/amlist.html",
Enrico Zini's avatar
Enrico Zini committed
61
62
63
64
65
                              dict(
                                  ams_active=ams_active,
                                  ams_inactive=ams_inactive,
                              ),
                              context_instance=template.RequestContext(request))
Enrico Zini's avatar
Enrico Zini committed
66
67
68

@backend.auth.is_am
def ammain(request):
Enrico Zini's avatar
Enrico Zini committed
69
    from django.db.models import Min
Enrico Zini's avatar
Enrico Zini committed
70
71
    person = request.user.get_profile()

Enrico Zini's avatar
Enrico Zini committed
72
73
    am_available = bmodels.AM.list_free()

74
75
76
77
78
79
    prog_app_new = bmodels.Process.objects.filter(progress__in=(
        const.PROGRESS_APP_NEW,
        const.PROGRESS_APP_RCVD,
        const.PROGRESS_ADV_RCVD)) \
                    .annotate(started=Min("log__logdate")).order_by("started")

Enrico Zini's avatar
Enrico Zini committed
80
81
82
    prog_app_ok = bmodels.Process.objects.filter(progress=const.PROGRESS_APP_OK) \
                  .annotate(started=Min("log__logdate")).order_by("started")

Enrico Zini's avatar
Enrico Zini committed
83
    prog_app_hold = bmodels.Process.objects.filter(manager=None, progress__in=(
Enrico Zini's avatar
Enrico Zini committed
84
85
86
87
88
89
90
91
92
93
94
        const.PROGRESS_APP_HOLD,
        const.PROGRESS_FD_HOLD,
        const.PROGRESS_DAM_HOLD)) \
                    .annotate(started=Min("log__logdate")).order_by("started")

    prog_am_rcvd = bmodels.Process.objects.filter(progress=const.PROGRESS_AM_RCVD) \
                   .annotate(started=Min("log__logdate")).order_by("started")

    prog_am_ok = bmodels.Process.objects.filter(progress=const.PROGRESS_AM_OK) \
                 .annotate(started=Min("log__logdate")).order_by("started")

Enrico Zini's avatar
Enrico Zini committed
95
96
97
98
    prog_fd_hold = bmodels.Process.objects.filter(progress=const.PROGRESS_FD_HOLD) \
                   .exclude(manager=None) \
                   .annotate(started=Min("log__logdate")).order_by("started")

Enrico Zini's avatar
Enrico Zini committed
99
100
101
102
103
104
    prog_fd_ok = bmodels.Process.objects.filter(progress=const.PROGRESS_FD_OK) \
                 .annotate(started=Min("log__logdate")).order_by("started")

    prog_dam_ok = bmodels.Process.objects.filter(progress=const.PROGRESS_DAM_OK) \
                 .annotate(started=Min("log__logdate")).order_by("started")

Enrico Zini's avatar
Enrico Zini committed
105
106
107
108
    prog_dam_hold = bmodels.Process.objects.filter(progress=const.PROGRESS_DAM_HOLD) \
                   .exclude(manager=None) \
                   .annotate(started=Min("log__logdate")).order_by("started")

Enrico Zini's avatar
Enrico Zini committed
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
    am_prog_rcvd = bmodels.Process.objects.filter(progress=const.PROGRESS_AM_RCVD) \
                   .filter(manager=person.am) \
                   .annotate(started=Min("log__logdate")).order_by("started")

    am_prog_am = bmodels.Process.objects.filter(progress=const.PROGRESS_AM) \
                   .filter(manager=person.am) \
                   .annotate(started=Min("log__logdate")).order_by("started")

    am_prog_hold = bmodels.Process.objects.filter(progress=const.PROGRESS_AM_HOLD) \
                   .filter(manager=person.am) \
                   .annotate(started=Min("log__logdate")).order_by("started")

    am_prog_done = bmodels.Process.objects.filter(manager=person.am, progress__in=(
        const.PROGRESS_AM_OK,
        const.PROGRESS_FD_HOLD,
        const.PROGRESS_FD_OK,
        const.PROGRESS_DAM_HOLD,
        const.PROGRESS_DAM_OK,
        const.PROGRESS_DONE,
        const.PROGRESS_CANCELLED)) \
                    .annotate(started=Min("log__logdate")).order_by("started")

Enrico Zini's avatar
Enrico Zini committed
131
132
133
134
    return render_to_response("restricted/ammain.html",
                              dict(
                                  person=person,
                                  am=person.am,
Enrico Zini's avatar
Enrico Zini committed
135
                                  am_available=am_available,
136
                                  prog_app_new=prog_app_new,
Enrico Zini's avatar
Enrico Zini committed
137
138
139
140
                                  prog_app_ok=prog_app_ok,
                                  prog_app_hold=prog_app_hold,
                                  prog_am_rcvd=prog_am_rcvd,
                                  prog_am_ok=prog_am_ok,
Enrico Zini's avatar
Enrico Zini committed
141
                                  prog_fd_hold=prog_fd_hold,
Enrico Zini's avatar
Enrico Zini committed
142
                                  prog_fd_ok=prog_fd_ok,
Enrico Zini's avatar
Enrico Zini committed
143
                                  prog_dam_ok=prog_dam_ok,
Enrico Zini's avatar
Enrico Zini committed
144
                                  prog_dam_hold=prog_dam_hold,
Enrico Zini's avatar
Enrico Zini committed
145
146
147
148
                                  am_prog_rcvd=am_prog_rcvd,
                                  am_prog_am=am_prog_am,
                                  am_prog_hold=am_prog_hold,
                                  am_prog_done=am_prog_done,
Enrico Zini's avatar
Enrico Zini committed
149
150
                              ),
                              context_instance=template.RequestContext(request))
Enrico Zini's avatar
Enrico Zini committed
151

Enrico Zini's avatar
Enrico Zini committed
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
def make_am_form(editor):
    excludes = ["person", "is_am_ctte"]

    if editor.is_dam:
        pass
    elif editor.is_fd:
        excludes.append("is_dam")
    else:
        excludes.append("is_fd")
        excludes.append("is_dam")

    class AMForm(forms.ModelForm):
        class Meta:
            model = bmodels.AM
            exclude = excludes
    return AMForm


Enrico Zini's avatar
Enrico Zini committed
170
171
172
173
174
175
176
@backend.auth.is_am
def amprofile(request, uid=None):
    from django.db.models import Min

    if uid is None:
        person = request.user.get_profile()
    else:
177
178
179
180
        try:
            person = bmodels.Person.objects.get(uid=uid)
        except bmodels.Person.DoesNotExist:
            return http.HttpResponseNotFound("Person with uid %s not found" % uid)
Enrico Zini's avatar
Enrico Zini committed
181
182
    am = person.am

Enrico Zini's avatar
Enrico Zini committed
183
184
185
186
187
188
189
190
191
192
    AMForm = make_am_form(am)

    form = None
    if request.method == 'POST':
        form = AMForm(request.POST, instance=am)
        if form.is_valid():
            cur_am = request.user.get_profile().am
            if cur_am == am or cur_am.is_fd or cur_am.is_dam:
                form.save()
            else:
Enrico Zini's avatar
Enrico Zini committed
193
                return http.HttpResponseForbidden("Editing is restricted to the am and front desk members")
Enrico Zini's avatar
Enrico Zini committed
194
195
196
197
            # TODO: message that it has been saved
    else:
        form = AMForm(instance=am)

Enrico Zini's avatar
Enrico Zini committed
198
    processes = bmodels.Process.objects.filter(manager=am).annotate(started=Min("log__logdate")).order_by("-started")
Enrico Zini's avatar
Enrico Zini committed
199
200
201
202
203
204
205
206

    am_available = bmodels.AM.list_free()

    return render_to_response("restricted/amprofile.html",
                              dict(
                                  person=person,
                                  am=am,
                                  processes=processes,
Enrico Zini's avatar
Enrico Zini committed
207
                                  form=form,
Enrico Zini's avatar
Enrico Zini committed
208
209
                              ),
                              context_instance=template.RequestContext(request))
Enrico Zini's avatar
Enrico Zini committed
210

Enrico Zini's avatar
Enrico Zini committed
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231

def make_statusupdateform(editor):
    if editor.is_fd:
        choices = [(x[1], "%s - %s" % (x[1], x[2])) for x in const.ALL_PROGRESS]
    else:
        choices = [x[1:3] for x in const.ALL_PROGRESS if x[0] in ("PROGRESS_AM", "PROGRESS_AM_HOLD", "PROGRESS_AM_OK")]

    class StatusUpdateForm(forms.Form):
        progress = forms.ChoiceField(
            required=True,
            label=_("Progress"),
            choices=choices
        )
        logtext = forms.CharField(
            required=True,
            label=_("Log text"),
            widget=forms.Textarea(attrs=dict(rows=5, cols=80))
        )
    return StatusUpdateForm


Enrico Zini's avatar
Enrico Zini committed
232
@backend.auth.is_am
Enrico Zini's avatar
Enrico Zini committed
233
def nmstatus(request, key):
234
    process = bmodels.Process.lookup(key)
235
236
    if process is None:
        return redirect(reverse('root_faq') + "#process-lookup")
Enrico Zini's avatar
Enrico Zini committed
237
238
239
240
241
242

    person = process.person

    cur_person = request.user.get_profile()
    am = cur_person.am

Enrico Zini's avatar
Enrico Zini committed
243
    can_edit = process.is_active and (am.is_fd or am.is_dam or am == process.manager)
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260

    if can_edit:
        StatusUpdateForm = make_statusupdateform(am)
        if request.method == 'POST':
            form = StatusUpdateForm(request.POST)
            if form.is_valid():
                process.progress = form.cleaned_data["progress"]
                process.save()
                log = bmodels.Log(
                    changed_by=cur_person,
                    process=process,
                    progress=process.progress,
                    logtext=form.cleaned_data["logtext"]
                )
                log.save()
                form = StatusUpdateForm(initial=dict(progress=process.progress))
        else:
Enrico Zini's avatar
Enrico Zini committed
261
262
            form = StatusUpdateForm(initial=dict(progress=process.progress))
    else:
263
        form = None
Enrico Zini's avatar
Enrico Zini committed
264

265
    log = process.log.order_by("logdate")
Enrico Zini's avatar
Enrico Zini committed
266

Enrico Zini's avatar
Enrico Zini committed
267
    return render_to_response("restricted/nmstatus.html",
Enrico Zini's avatar
Enrico Zini committed
268
269
270
271
272
273
                              dict(
                                  process=process,
                                  person=person,
                                  cur_person=cur_person,
                                  am=am,
                                  log=log,
Enrico Zini's avatar
Enrico Zini committed
274
                                  form=form,
275
                                  can_edit=can_edit,
Enrico Zini's avatar
Enrico Zini committed
276
277
                              ),
                              context_instance=template.RequestContext(request))
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296

def make_person_form(editor):
    excludes = ["user"]

    if editor.is_dam:
        pass
    elif editor.is_fd:
        excludes.append("status")
    else:
        excludes.append("status")
        excludes.append("fd_comment")

    class PersonForm(forms.ModelForm):
        class Meta:
            model = bmodels.Person
            exclude = excludes
    return PersonForm

@backend.auth.is_am
Enrico Zini's avatar
Enrico Zini committed
297
298
def person(request, key):
    person = bmodels.Person.lookup(key)
299
300
    if person is None:
        return http.HttpResponseNotFound("Person with uid or email %s not found" % key)
301
302
303
304
305

    cur_person = request.user.get_profile()
    am = cur_person.am

    if not person.can_be_edited(am):
Enrico Zini's avatar
Enrico Zini committed
306
        return redirect('public_person', key=key)
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323

    PersonForm = make_person_form(am)

    form = None
    if request.method == 'POST':
        form = PersonForm(request.POST, instance=person)
        if form.is_valid():
            form.save()
            # TODO: message that it has been saved
    else:
        form = PersonForm(instance=person)

    return render_to_response("restricted/person.html",
                              dict(
                                  person=person,
                                  am=am,
                                  cur_person=cur_person,
Enrico Zini's avatar
Enrico Zini committed
324
                                  active_process=person.active_process,
325
326
327
328
                                  form=form,
                              ),
                              context_instance=template.RequestContext(request))

329
330
331
332
333
334
335
336
337
338
339
340
341
@backend.auth.is_am
def nmelist(request):
    from django.db.models import Min

    procs = bmodels.Process.objects.filter(is_active=True) \
             .annotate(started=Min("log__logdate")).order_by("started")

    return render_to_response("restricted/nmelist.html",
                              dict(
                                  procs=procs,
                              ),
                              context_instance=template.RequestContext(request))

342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398


def make_newprocessform(person):
    choices = [x[1:3] for x in const.ALL_STATUS if x[1] != person.status]

    class NewProcessForm(forms.Form):
        applying_for = forms.ChoiceField(
            required=True,
            label=_("Applying for"),
            choices=choices
        )
        logtext = forms.CharField(
            required=True,
            label=_("Log text"),
            widget=forms.Textarea(attrs=dict(rows=5, cols=80))
        )
    return NewProcessForm

@backend.auth.is_admin
def newprocess(request, key):
    person = bmodels.Person.lookup(key)
    if person is None:
        return http.HttpResponseNotFound("Person %s not found" % key)

    if person.active_process:
        return http.HttpResponseForbidden("Person %s already has an active process" % key)

    NewProcessForm = make_newprocessform(person)
    if request.method == 'POST':
        form = NewProcessForm(request.POST)
        if form.is_valid():
            process = bmodels.Process(
                person=person,
                progress=const.PROGRESS_APP_NEW,
                is_active=True,
                applying_for=form.cleaned_data["applying_for"]
            )
            process.save()

            log = bmodels.Log(
                changed_by=request.person,
                process=process,
                progress=process.progress,
                logtext=form.cleaned_data["logtext"]
            )
            log.save()
            # TODO: message
            return redirect('public_person', key=key)
    else:
        form = NewProcessForm(initial=dict(logtext="New process created"))

    return render_to_response("restricted/newprocess.html",
                              dict(
                                  person=person,
                                  form=form,
                              ),
                              context_instance=template.RequestContext(request))