Source code for mentoring.forms
"""
Forms relating to mentoring and mentoring meetings.
"""
from datetime import timedelta
from django import forms
from django.db import transaction
from django.core.exceptions import ValidationError
from mentoring.models import Relationship, Meeting
def long_label_from_user(user):
"""Utility function to format a log <name> (<crsid>) stype label from an
instance of a User.
"""
return '{} ({})'.format(user.get_full_name(), user.username)
class UserChoiceField(forms.ModelChoiceField):
"""
A sub-class of ModelChoiceField which expects User-model instances an
formats its label as "<name> (<crsid>)".
"""
def label_from_instance(self, obj):
return long_label_from_user(obj)
class HTML5DateInput(forms.DateInput):
"""A version of DateInput which uses the HTML5 date input type."""
input_type = 'date'
[docs]class ReportMentorMeetingForm(forms.Form):
"""
A form which allows a user to record a meeting where they were a mentor.
Much like a :py:class:`ModelForm` this object provides a single
:py:meth:`.save` method which can be used to save the cleaned data to the
database.
:param django.contrib.auth.models.User mentor: A **required** argument which
specifies the user who is the mentor.
"""
class Meta(object):
submit_text = 'Record meeting'
# The mentor field is ignored but is included here to reassure the user that
# they are indeed doing the right thing.
mentor = forms.CharField(disabled=True, required=False)
mentee = UserChoiceField(queryset=None)
held_on = forms.DateField(widget=HTML5DateInput)
duration = forms.IntegerField(
label='Approximate duration in minutes',
min_value=1, max_value=3600, initial=30)
def __init__(self, *args, **kwargs):
self._mentor_user = kwargs.pop('mentor', None)
super(ReportMentorMeetingForm, self).__init__(*args, **kwargs)
self.fields['mentee'].queryset = \
Relationship.objects.mentees_for_user(self._mentor_user)
if self._mentor_user is not None:
self.fields['mentor'].initial = long_label_from_user(
self._mentor_user)
def clean(self):
cleaned_data = super(ReportMentorMeetingForm, self).clean()
if self._mentor_user is None:
raise ValidationError('A mentor must be specified',
code='missing_mentor')
# Ensure that there's not an existing meeting on this date. This is
# UI-level validation rather than a uniqueness constraint in the DB.
# (The check is intended to guard against double-reporting of a meeting
# rather than a loss of DB consistency.)
qs = Meeting.objects.filter(
held_on=cleaned_data.get('held_on'),
relationship__mentor=self._mentor_user,
relationship__mentee=cleaned_data.get('mentee'),
)
if qs.exists():
raise ValidationError(
('A meeting between %(mentor)s and %(mentee)s on %(date)s '
'has already been recorded.'),
code='duplicate_meeting',
params={
'mentor': long_label_from_user(self._mentor_user),
'mentee': long_label_from_user(cleaned_data.get('mentee')),
'date': cleaned_data.get('held_on').isoformat(),
})
return cleaned_data
@transaction.atomic
[docs] def save(self):
"""Save this meeting to the DB."""
relationship = Relationship.objects.get(
mentor=self._mentor_user, mentee=self.cleaned_data['mentee'])
Meeting.objects.update_or_create(
relationship=relationship, held_on=self.cleaned_data['held_on'],
approximate_duration=timedelta(minutes=self.cleaned_data['duration']),
)