mangroveorg/datawinners

View on GitHub
datawinners/entity/forms.py

Summary

Maintainability
A
35 mins
Test Coverage
import re

from django import forms
from django.contrib.auth.models import User
from datawinners.accountmanagement.helper import is_mobile_number_unique_for_trial_account
from django.forms import HiddenInput, BooleanField
from django.forms.fields import RegexField, CharField, FileField, MultipleChoiceField, EmailField
from django.forms.widgets import CheckboxSelectMultiple, TextInput
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from django.forms.forms import Form

from datawinners.accountmanagement.models import Organization
from datawinners.entity.datasender_search import datasender_count_with
from mangrove.form_model.form_model import MOBILE_NUMBER_FIELD_CODE, GEO_CODE, GEO_CODE_FIELD_NAME
from mangrove.utils.types import is_empty
from datawinners.entity.fields import PhoneNumberField


class EntityTypeForm(Form):
    error_css_class = 'error'
    required_css_class = 'required'

    entity_type_regex = CharField(max_length=20, required=False, label=_("New Subject(eg clinic, waterpoint etc)"))

    def clean_entity_type_regex(self):
        value = self.cleaned_data['entity_type_regex']
        if value == '':
            self._errors['entity_type_regex'] = _("You can't leave this empty.")
        elif not re.match("^\s*([A-Za-z\d]+(\s[A-Za-z\d]+)*)\s*$", value):
            self._errors['entity_type_regex'] = _("Please use only letters (a-z), numbers, and spaces.")
        return value


class SubjectForm(Form):
    required_css_class = 'required'
    error_css_class = 'error'

    type = CharField(max_length=30, required=True, label=_("Type"))
    name = CharField(max_length=30, required=True, label=_("Name"))
    uniqueID = CharField(max_length=100, required=True, label=_("Unique Identification Number (ID)"))
    location = CharField(max_length=30, required=True, label=_("Location"))
    description = CharField(max_length=30, required=False, label=_("Description"))
    mobileNumber = CharField(max_length=30, required=False, label=_("Mobile Number"))


def smartphone_icon():
    return ' + <img src="/media/images/smart_phone.png" /><span>Smartphone</span>'


class ReporterRegistrationForm(Form):
    required_css_class = 'required'

    name = RegexField(regex="[^0-9.,\s@#$%&*~]*", max_length=80,
                      error_message=_("Please enter a valid value containing only letters a-z or A-Z or symbols '`- "),
                      label=_("Name"), required=False)
    telephone_number = PhoneNumberField(required=True, label=_("Mobile Number"),
                                        error_message=_("Please enter a valid phone number."))
    geo_code = CharField(max_length=30, required=False, label=_("GPS Coordinates"))

    location = CharField(max_length=500, required=False, label=_("Name"))
    project_id = CharField(required=False, widget=HiddenInput())

    DEVICE_CHOICES = (('sms', mark_safe('<img src="/media/images/mini_mobile.png" /> <span>SMS</span>')), (
        'web', mark_safe('<img src="/media/images/mini_computer.png" /> <span>Web</span>' + smartphone_icon())))
    devices = MultipleChoiceField(label=_('Device'), widget=CheckboxSelectMultiple(), choices=DEVICE_CHOICES,
                                  initial=['sms'], required=False, )
    email = EmailField(required=False, widget=TextInput(attrs=dict({'class': 'required'},
                                                                   maxlength=75)),
                       label=_("E-Mail"),
                       error_messages={
                           'invalid': _('Enter a valid email address. Example:name@organization.com')})

    short_code = CharField(required=False, max_length=12, label=_("ID"),
                           widget=TextInput(attrs=dict({'class': 'subject_field'})))
    generated_id = BooleanField(required=False, initial=True)

    # Needed for telephone number validation
    org_id = None

    def __init__(self, org_id=None, *args, **kwargs):
        self.org_id = org_id
        super(ReporterRegistrationForm, self).__init__(*args, **kwargs)

    def _is_int(self, s):
        try:
            int(s)
            return True
        except ValueError:
            return False


    def _geo_code_format_validations(self, lat_long, msg):
        if len(lat_long) != 2:
            self._errors['geo_code'] = self.error_class([msg])
        else:
            try:
                if not (-90 < float(lat_long[0]) < 90 and -180 < float(lat_long[1]) < 180):
                    self._errors['geo_code'] = self.error_class([msg])
            except Exception:
                self._errors['geo_code'] = self.error_class([msg])

    def _geo_code_validations(self):
        geo_code = self.cleaned_data.get("geo_code").strip()

        if not bool(geo_code):
            return

        msg = _(
            "Incorrect GPS format. The GPS coordinates must be in the following format: xx.xxxx,yy.yyyy. Example -18.8665,47.5315")

        geo_code_string = geo_code.strip()
        geo_code_string = geo_code_string.replace(",", " ")
        geo_code_string = re.sub(' +', ' ', geo_code_string)
        if not is_empty(geo_code_string):
            lat_long = geo_code_string.split(" ")
            self._geo_code_format_validations(lat_long, msg)
            self.cleaned_data['geo_code'] = geo_code_string

    def clean(self):
        self.convert_email_to_lowercase()
        if not self.cleaned_data.get('generated_id') and not self.cleaned_data.get('short_code'):
            msg = _('This field is required.')
            self.errors['short_code'] = self.error_class([msg])

        self._geo_code_validations()
        if not self.cleaned_data.get('project_id'):
            self.cleaned_data['is_data_sender'] = False
        else:
            self.cleaned_data['is_data_sender'] = 'True'

        return self.cleaned_data

    def clean_short_code(self):
        short_code = self.cleaned_data.get('short_code')

        if short_code:
            if len(short_code) > 12:
                msg = _("Unique ID should be less than 12 characters")
                self.errors['short_code'] = self.error_class([msg])

            if not re.match("^[a-zA-Z0-9]+$", short_code):
                msg = _("Only letters and numbers are valid")
                self.errors['short_code'] = self.error_class([msg])

        return short_code

    def clean_telephone_number(self):
        """
        Validate telephone number. This expects the dbm to be set on the form before trying to clean.
        """

        organization = Organization.objects.get(org_id=self.org_id)
        mobile_number = self.cleaned_data.get('telephone_number')
        if organization.in_trial_mode:
            if not is_mobile_number_unique_for_trial_account(organization, mobile_number):
                self._errors['telephone_number'] = self.error_class(
                    [_(u"Sorry, this number has already been used for a different DataWinners Basic account.")])
        return mobile_number


    def clean_email(self):
        """
        Validate that the supplied email address is unique for the
        site.

        """
        email = self.cleaned_data.get('email')
        if not email:
            return email
        mail_filter = User.objects.filter(email=email)
        if datasender_count_with(email) > 0 or mail_filter.exists():
            raise forms.ValidationError(
                _("This email address is already in use. Please supply a different email address."))
        return self.cleaned_data['email']

    def convert_email_to_lowercase(self):
        email = self.cleaned_data.get('email')
        if email is not None:
            self.cleaned_data['email'] = email.lower()

    def requires_web_access(self):
        return self.cleaned_data.get('email')

    def update_errors(self, validation_errors):
        mapper = {MOBILE_NUMBER_FIELD_CODE: 'telephone_number',
                  GEO_CODE: GEO_CODE_FIELD_NAME}
        for field_code, error in validation_errors.iteritems():
            self._errors[mapper.get(field_code)] = self.error_class([_(error)])


class EditReporterRegistrationForm(ReporterRegistrationForm):
    def __init__(self, org_id=None, existing_email=None, *args, **kwargs):
        super(EditReporterRegistrationForm, self).__init__(org_id, *args, **kwargs)
        if existing_email:
            existing_email = existing_email.strip()
        self.existing_email = existing_email

    def clean(self):
        self.convert_email_to_lowercase()
        self._geo_code_validations()
        return self.cleaned_data

    def clean_email(self):
        new_email = self.cleaned_data.get('email')
        if new_email != self.existing_email:
            return super(EditReporterRegistrationForm, self).clean_email()

        return self.existing_email


class SubjectUploadForm(Form):
    error_css_class = 'error'
    required_css_class = 'required'
    file = FileField(label='Import Subjects')