Add admin functionality to edit a signup response
authorMagnus Hagander <magnus@hagander.net>
Mon, 9 Jul 2018 11:00:55 +0000 (13:00 +0200)
committerMagnus Hagander <magnus@hagander.net>
Mon, 9 Jul 2018 11:00:55 +0000 (13:00 +0200)
This allows the admin to override a response given by an attendee, or to
change it based on information received via other means such as email.

docs/confreg/signups.md
postgresqleu/confwiki/forms.py
postgresqleu/confwiki/views.py
postgresqleu/urls.py
template/confwiki/signup_admin_edit_form.html

index 71787c3069f652cb18b81286202043145480e904..1fbee96c41c32104a023bc624429a6077a49729d 100644 (file)
@@ -83,3 +83,22 @@ The results will have a summary with how many attendees have picked
 each specific option, as well as the total value calculated from
 weighted values. Below it there will also be a complete list of every
 individual response.
+
+## Editing attendee signups
+
+Normally, the attendees should handle their own signups, and they
+should never be edited.
+
+However, it is sometimes required that the administrator edit them,
+for example if the deadline has passed, and an attendee needs to
+change their response.
+
+All responses that have been recorded can be edited. In a signup that
+only has yes/no as the answer, the no responses are not recorded, so
+changing a record to no will remove it. For both types of signups it
+is always possible to remove an entry.
+
+If the signup is *not public*, it is also possible to add a new
+response (for example if an attendee never signed up, but reported
+their attendance via email or phone or other means). Duplicate
+responses are of course not allowed.
index 918ff0c93c593c17fb066bcc094dacba3298b5b3..550e44538d249550f096de1bfc639cf2ee16d154 100644 (file)
@@ -1,8 +1,9 @@
 from django import forms
 from django.core.exceptions import ValidationError
+from django.db.models import Q
 
 from postgresqleu.confreg.models import RegistrationType, ConferenceRegistration
-from models import Wikipage, Signup
+from models import Wikipage, Signup, AttendeeSignup
 
 class WikipageEditForm(forms.ModelForm):
        class Meta:
@@ -83,6 +84,31 @@ class SignupAdminEditForm(forms.ModelForm):
                model = Signup
                exclude = ['conference', ]
 
+class SignupAdminEditSignupForm(forms.ModelForm):
+       choice = forms.ChoiceField(required=True)
+       class Meta:
+               model = AttendeeSignup
+               fields = ['attendee', 'choice', ]
+
+       def __init__(self, signup, *args, **kwargs):
+               self.signup = signup
+               self.isnew = kwargs.pop('isnew')
+               super(SignupAdminEditSignupForm, self).__init__(*args, **kwargs)
+
+               if self.isnew:
+                       self.fields['attendee'].queryset = ConferenceRegistration.objects.filter(conference=signup.conference).filter(
+                               Q(user_attendees=signup) | Q(regtype__user_regtypes=signup)).exclude(attendeesignup__signup=signup).distinct()
+               else:
+                       del self.fields['attendee']
+
+               if signup.options:
+                       choices = signup.options.split(',')
+                       self.fields['choice'].choices = [(k,k) for k in choices]
+                       self.fields['choice'].choices.insert(0, ('', ''))
+               else:
+                       # This one is boolean only
+                       self.fields['choice'].choices = (('', ''), ('yes','Yes'), ('', 'No'), )
+                       self.fields['choice'].required = False
 
 class SignupSendmailForm(forms.Form):
        _recipient_choices = [
index 1a17cc0dfffccf27e498677f11a62be0dddc649a..f373127fb04f498a9cdbe2c5635a93e53c4efcda 100644 (file)
@@ -23,6 +23,7 @@ from forms import WikipageEditForm, WikipageAdminEditForm
 
 from models import Signup, AttendeeSignup
 from forms import SignupSubmitForm, SignupAdminEditForm, SignupSendmailForm
+from forms import SignupAdminEditSignupForm
 
 @login_required
 def wikipage(request, confurl, wikiurl):
@@ -345,10 +346,10 @@ def signup_admin_edit(request, urlname, signupid):
                })
                sumresults = cursor.fetchall()
                results['summary'] = [dict(zip(['choice', 'num', 'percentwidth'], r)) for r in sumresults]
-               cursor.execute("SELECT firstname || ' ' || lastname,choice,saved FROM confreg_conferenceregistration r INNER JOIN confwiki_attendeesignup s ON r.id=s.attendee_id WHERE s.signup_id=%(signup)s ORDER BY saved", {
+               cursor.execute("SELECT s.id, firstname || ' ' || lastname,choice,saved FROM confreg_conferenceregistration r INNER JOIN confwiki_attendeesignup s ON r.id=s.attendee_id WHERE s.signup_id=%(signup)s ORDER BY saved", {
                        'signup': signup.id,
                })
-               results['details'] = [dict(zip(['name', 'choice', 'when'], r)) for r in cursor.fetchall()]
+               results['details'] = [dict(zip(['id', 'name', 'choice', 'when'], r)) for r in cursor.fetchall()]
                if signup.optionvalues:
                        optionstrings = signup.options.split(',')
                        optionvalues = [int(x) for x in signup.optionvalues.split(',')]
@@ -388,6 +389,44 @@ def signup_admin_edit(request, urlname, signupid):
                'helplink': 'signups',
        })
 
+@transaction.atomic
+def signup_admin_editsignup(request, urlname, signupid, id):
+       conference = get_authenticated_conference(request, urlname)
+       signup = get_object_or_404(Signup, conference=conference, pk=signupid)
+
+       if id == 'new':
+               attendeesignup = AttendeeSignup(signup=signup)
+       else:
+               attendeesignup = get_object_or_404(AttendeeSignup, signup=signup, pk=id)
+
+       if request.method == 'POST' and request.POST['submit'] == 'Delete':
+               attendeesignup.delete()
+               return HttpResponseRedirect('../../')
+       elif request.method == 'POST':
+               form = SignupAdminEditSignupForm(signup, isnew=(id == 'new'), instance=attendeesignup, data=request.POST)
+               if form.is_valid():
+                       if (not signup.options) and (not form.cleaned_data['choice']):
+                               # Yes/no signup changed to no means we actually delete the
+                               # record completeliy.
+                               attendeesignup.delete()
+                       else:
+                               form.save()
+                       return HttpResponseRedirect('../../')
+       else:
+               form = SignupAdminEditSignupForm(signup, isnew=(id == 'new'), instance=attendeesignup)
+
+       return render(request, 'confreg/admin_backend_form.html', {
+               'conference': conference,
+               'form': form,
+               'what': 'attendee signup',
+               'cancelurl': '../../',
+               'allow_delete': (id != 'new'),
+               'breadcrumbs': (
+                       ('/events/admin/{0}/signups/'.format(conference.urlname), 'Signups'),
+                       ('/events/admin/{0}/signups/{1}/'.format(conference.urlname, signup.id), signup.title),
+               ),
+               'helplink': 'signups',
+       })
 
 @transaction.atomic
 def signup_admin_sendmail(request, urlname, signupid):
index 4b05d372a787aa320be10de07e260f6f6518cea3..555ec09cd4066f829508feafc4aec36606dbaaed 100644 (file)
@@ -146,6 +146,7 @@ urlpatterns = [
        url(r'^events/admin/(\w+)/signups/$', postgresqleu.confwiki.views.signup_admin),
        url(r'^events/admin/(\w+)/signups/(new|\d+)/$', postgresqleu.confwiki.views.signup_admin_edit),
        url(r'^events/admin/(\w+)/signups/(\d+)/sendmail/$', postgresqleu.confwiki.views.signup_admin_sendmail),
+       url(r'^events/admin/(\w+)/signups/(\d+)/edit/(new|\d+)/$', postgresqleu.confwiki.views.signup_admin_editsignup),
        url(r'^events/admin/(\w+)/transfer/$', postgresqleu.confreg.views.transfer_reg),
        url(r'^events/admin/(?P<urlname>[^/]+)/volunteer/', include(postgresqleu.confreg.volsched), {'adm': True}),
        url(r'^events/admin/(\w+)/regdays/(.*/)?$', postgresqleu.confreg.backendviews.edit_regdays),
index fb1f859cd8eaac1684ead86ad88f39992ce591d5..89573410edac7791cd49f78fcdfef41abc4860a7 100644 (file)
@@ -95,9 +95,13 @@ $(function() {
   <td>{{r.name}}</td>
   <td>{{r.choice}}</td>
   <td>{{r.when}}</td>
+  <td><a class="btn btn-xs btn-default" href="edit/{{r.id}}/">edit</a></td>
  </tr>
 {%endfor%}
 </table>
+{%if not signup.public%}
+<a class="btn btn-xs btn-default" href="edit/new/">Add new</a>
+{%endif%}
 
 {%if results.awaiting%}
 <h3>Awaiting response</h3>