import logging import re from collections import OrderedDict from django import forms from django.forms import ModelForm from django_common.auth_backends import User from web.models import Worker, WorkerStudyRole from web.models.constants import GLOBAL_STUDY_ID from web.models.worker import role_choices_by_worker_type, worker_type_by_worker from web.models.worker_study_role import WORKER_STAFF, WORKER_VOUCHER_PARTNER, WORKER_HEALTH_PARTNER from web.decorators import PermissionDecorator logger = logging.getLogger(__name__) class WorkerForm(ModelForm): class Meta: model = Worker exclude = ['appointments'] def __init__(self, *args, **kwargs): worker_type = kwargs.pop('worker_type', WORKER_STAFF) super(WorkerForm, self).__init__(*args, **kwargs) instance = getattr(self, 'instance', None) initial_role = None initial_permissions = None if instance is not None and instance.pk: worker_type = worker_type_by_worker(instance) roles = WorkerStudyRole.objects.filter(worker=instance, study_id=GLOBAL_STUDY_ID) if roles.count() > 0: initial_role = roles[0].role initial_permissions = roles[0].permissions.all() choices = role_choices_by_worker_type(worker_type) if worker_type in [WORKER_VOUCHER_PARTNER, WORKER_HEALTH_PARTNER]: ''' Since ChoiceField expects tuples (id, label), for a *custom* HiddenInput field we need to create a charfield with TextInput widget providing the value of the *id*. hidden_form_field CSS class is defined in templates/doctors/add.html and has display:none property. ''' if initial_role is None: initial_role = choices[0][0] # get 1st element of 1st tuple (('VOUCHER_PARTNER', 'Voucher Partner'),) self.fields['role'] = forms.CharField(label='Role', widget=forms.TextInput(attrs={'class': 'hidden_form_field'}), disabled=True) else: self.fields['role'] = forms.ChoiceField(label='Role', choices=choices) self.fields['role'].initial = initial_role #use only the codename used in the code decorators self.fields['permissions'] = forms.ModelMultipleChoiceField(required=False, queryset=PermissionDecorator.get_permissions()) if initial_permissions is not None: self.fields['permissions'].initial = [c.pk for c in initial_permissions] del self.fields['user'] if worker_type == WORKER_STAFF: if instance.user is not None: self.fields['Superuser'] = forms.BooleanField(label='Is Superuser?', disabled=True, required=False, help_text='Super users have all permissions. This is a read only field.') self.fields['Superuser'].initial = instance.user.is_superuser del self.fields['voucher_types'] del self.fields['name'] else: del self.fields['locations'] del self.fields['first_name'] del self.fields['last_name'] if worker_type != WORKER_VOUCHER_PARTNER: del self.fields['voucher_partner_code'] fields = OrderedDict() if worker_type == WORKER_STAFF: if instance is None or instance.pk is None: fields['login'] = forms.CharField(label='Login') fields['password'] = forms.CharField(label='Password', widget=forms.PasswordInput) fields['password2'] = forms.CharField(label='Repeat password', widget=forms.PasswordInput) for key, value in self.fields.items(): fields[key] = value self.fields = fields def save(self, commit=True): create_user = self.cleaned_data.get("login", None) is not None user = None if create_user: user = User.objects.create_user(username=self.cleaned_data['login'], email=self.cleaned_data['email'], password=self.cleaned_data['password']) instance = super(WorkerForm, self).save(commit) if create_user: instance.user = user instance.save() roles = WorkerStudyRole.objects.filter(worker=instance, study_id=GLOBAL_STUDY_ID) if roles.count() > 0: for role in roles: #should be only one role.role=self.cleaned_data['role'] role.permissions.set(self.cleaned_data['permissions']) role.save() else: role = WorkerStudyRole.objects.create(worker=instance, study_id=GLOBAL_STUDY_ID, role=self.cleaned_data['role']) role.permissions.set(self.cleaned_data['permissions']) role.save() def clean(self): cleaned_data = super(WorkerForm, self).clean() if cleaned_data.get('password', None) is not None: password = cleaned_data['password'] if cleaned_data['password'] != cleaned_data['password2']: self.add_error('password', "Password don't match") self.add_error('password2', "Password don't match") min_length = 10 if len(password) < min_length: self.add_error('password', 'Password must be at least ' + str(min_length) + ' characters long.') # check for digit if not any(char.isdigit() for char in password): self.add_error('password', 'Password must contain at least 1 digit.') # check for letter if not any(char.isalpha() for char in password): self.add_error('password', 'Password must contain at least 1 letter.') if cleaned_data.get('login', None) is not None: if not re.match('^[.a-zA-Z0-9]+$', cleaned_data['login']): self.add_error('login', 'Login can contain only alphanumeric characters or dot.') return cleaned_data