mangroveorg/datawinners

View on GitHub
datawinners/entity/helper.py

Summary

Maintainability
D
2 days
Test Coverage
# vim: ai ts=4 sts=4 et sw=4 encoding=utf-8
from collections import OrderedDict
import re
import logging
from django.db import IntegrityError
from datawinners import utils

from django.contrib.auth.models import User
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _, ugettext
from mangrove.transport import Request, TransportInfo
from mangrove.form_model.field import TextField, IntegerField, DateField, GeoCodeField, SelectField, \
    HierarchyField, TelephoneNumberField, UniqueIdField, ShortCodeField
from datawinners.utils import get_organization_from_manager
from mangrove.contrib.deletion import ENTITY_DELETION_FORM_CODE
from mangrove.datastore.entity import get_by_short_code_include_voided
from mangrove.errors.MangroveException import MangroveException
from mangrove.form_model.field import TextField, HierarchyField, GeoCodeField, TelephoneNumberField
from mangrove.form_model.form_model import NAME_FIELD,\
    NAME_FIELD_CODE, LOCATION_TYPE_FIELD_NAME, LOCATION_TYPE_FIELD_CODE,\
    GEO_CODE, MOBILE_NUMBER_FIELD, MOBILE_NUMBER_FIELD_CODE,\
    SHORT_CODE_FIELD, REGISTRATION_FORM_CODE,\
    ENTITY_TYPE_FIELD_CODE, GEO_CODE_FIELD_NAME, SHORT_CODE, REPORTER, EMAIL_FIELD, get_form_model_by_code, EntityFormModel, get_form_model_by_entity_type
from mangrove.form_model.validation import TextLengthConstraint,\
    RegexConstraint
from mangrove.transport.player.player import WebPlayer
from mangrove.utils.types import  is_sequence, is_not_empty
from mangrove.errors.MangroveException import NumberNotRegisteredException, \
    DataObjectAlreadyExists
from mangrove.transport.repository.reporters import find_reporter_entity
from datawinners.accountmanagement.models import Organization,\
    DataSenderOnTrialAccount, NGOUserProfile
from datawinners.location.LocationTree import get_location_tree
from datawinners.messageprovider.message_handler import\
    get_success_msg_for_ds_registration_using
from datawinners.location.LocationTree import get_location_hierarchy
from datawinners.submission.location import LocationBridge
from mangrove.form_model.field import ShortCodeField


websubmission_logger = logging.getLogger("websubmission")
FIRSTNAME_FIELD = "firstname"
FIRSTNAME_FIELD_CODE = "f"

def remove_hyphens(telephone_number):
    return re.sub('[- \(\)+]', '', smart_unicode(telephone_number))


def unique(dbm, telephone_number):
    telephone_number = remove_hyphens(telephone_number)
    try:
        find_reporter_entity(dbm, telephone_number)
    except NumberNotRegisteredException:
        return True
    return False


def _create_constraints_for_mobile_number():
    mobile_number_length = TextLengthConstraint(max=15)
    mobile_number_pattern = RegexConstraint(reg='^[0-9]+$')
    mobile_constraints = [mobile_number_length, mobile_number_pattern]
    return mobile_constraints


def _generate_form_code(manager, prefix, rank=''):
    form_code = "%s%s" % (prefix, rank)
    rows = manager.load_all_rows_in_view("questionnaire", key=form_code)
    if len(rows) > 0:
        rank = 1 if rank is '' else rank + 1
        form_code = _generate_form_code(manager, prefix, rank)
    return form_code


def _create_registration_form(manager, entity_name=None, form_code=None, entity_type=None):
    code_generator = question_code_generator()

    question1 = TextField(name=FIRSTNAME_FIELD, code=code_generator.next(),
        label=_("What is the %(entity_type)s's first name?") % {'entity_type': entity_name},
        defaultValue="some default value",
        instruction=_("Enter a %(entity_type)s first name") % {'entity_type': entity_name})

    question2 = TextField(name=NAME_FIELD, code=code_generator.next(),
        label=_("What is the %(entity_type)s's last name?") % {'entity_type': entity_name},
        defaultValue="some default value",
        instruction=_("Enter a %(entity_type)s last name") % {'entity_type': entity_name})
    question3 = HierarchyField(name=LOCATION_TYPE_FIELD_NAME, code=code_generator.next(),
        label=_("What is the %(entity_type)s's location?") % {'entity_type': entity_name}, instruction=unicode(_("Enter a region, district, or commune")))
    question4 = GeoCodeField(name=GEO_CODE_FIELD_NAME, code=code_generator.next(),
        label=_("What is the %(entity_type)s's GPS co-ordinates?") % {'entity_type': entity_name},
        instruction=unicode(_("Answer must be GPS coordinates in the following format (latitude,longitude). Example: -18.1324,27.6547")))
    question5 = TelephoneNumberField(name=MOBILE_NUMBER_FIELD, code=code_generator.next(),
        label=_("What is the %(entity_type)s's mobile telephone number?") % {'entity_type': entity_name},
        defaultValue="some default value",
        instruction=_(
            "Enter the (%(entity_type)s)'s number with the country code and telephone number. Example: 261333745269") % {
            'entity_type': entity_name}, constraints=(
            _create_constraints_for_mobile_number()))
    question6 = ShortCodeField(name=SHORT_CODE_FIELD, code=code_generator.next(),
        label=_("What is the %(entity_type)s's Unique ID Number?") % {'entity_type': entity_name},
        defaultValue="some default value",
        instruction=unicode(_("Enter an id, or allow us to generate it")), required=False)
    questions = [question1, question2, question3, question4, question5, question6]

    form_model = EntityFormModel(manager, name=entity_name, form_code=form_code, fields=questions, is_registration_model=True,
        entity_type=entity_type)
    return form_model



def get_subject_field_instruction(field, entity_type):
    if field.name == FIRSTNAME_FIELD:
        instruction = _("Enter a %(entity_type)s first name") % {'entity_type': entity_type}
    elif field.name == NAME_FIELD:
        instruction = _("Enter a %(entity_type)s last name") % {'entity_type': entity_type}
    elif field.name == LOCATION_TYPE_FIELD_NAME:
        instruction = _("Enter a region, district, or commune") % {'entity_type': entity_type}
    elif field.name == GEO_CODE_FIELD_NAME:
        instruction = _("Answer must be GPS coordinates in the following format (latitude,longitude). Example: -18.1324,27.6547")
    elif field.name == MOBILE_NUMBER_FIELD:
        instruction = _("Enter the (%(entity_type)s)'s number with the country code and telephone number. Example: 261333745269") % {'entity_type': entity_type}
    elif field.name == SHORT_CODE_FIELD:
        instruction = _("Enter an id, or allow us to generate it")
    else:
        instruction = get_field_instruction(field)
    return instruction

def get_field_instruction(field):
    if type(field) == ShortCodeField and field.constraint[0].max == 20:
        instruction = ugettext("Answer must be 20 characters maximum")

    if type(field) == TextField:
        constraint = field.constraints
        constraint = constraint[0]

        if constraint.max:
            instruction = ugettext("Answer must be a word %d characters maximum") % int(constraint.max)
        else:
            instruction = ugettext("Answer must be a word")

    if type(field) == TelephoneNumberField:
        instruction = ugettext("Answer must be country code plus telephone number. Example: 261333745269")

    if type(field) == HierarchyField:
        instruction = ugettext("Answer must be a word")

    if type(field) == IntegerField:
        instruction = ugettext("Answer must be a number.")
        constraint = field.constraints
        if len(constraint):
            constraint = constraint[0]
            if constraint.max and constraint.min:
                instruction = ugettext("Answer must be a number between %d-%d.") % (int(constraint.min), int(constraint.max))
            elif constraint.min:
                instruction = ugettext("Answer must be a number. The minimum is %d.") % int(constraint.min)
            elif constraint.max:
                instruction = ugettext("Answer must be a number. The maximum is %d.") % int(constraint.max)

    if type(field) == DateField:
        example = {
            "mm.yyyy": ('12.2011'),
            "dd.mm.yyyy": ('25.12.2011'),
            "mm.dd.yyyy": ('12.25.2011')
        }
        date = {
            "mm.yyyy": ugettext("month.year"),
            "dd.mm.yyyy": ugettext("day.month.year"),
            "mm.dd.yyyy": ugettext("month.day.year")
        }
        example = example.get(field.date_format)
        date_format = date.get(field.date_format)
        instruction = ugettext("Answer must be a date in the following format: %s. Example: %s") % (date_format, example)
    if type(field) == GeoCodeField:
        instruction = ugettext("Answer must be GPS coordinates in the following format (latitude,longitude). Example: -18.1324,27.6547")
    if type(field) == SelectField:
        if field.is_single_select:
            instruction = ugettext("Choose 1 answer from the list. Example: a")
        else:
            instruction = ugettext("Choose 1 or more answers from the list. Example: a or ab ")
    if type(field) == UniqueIdField:
        instruction = ugettext("Answer must be the Identification Number of the %s you are reporting on.") \
        % field.unique_id_type
    return instruction

def _get_form_code_prefix(entity_name):
    return entity_name.lower().replace(" ", "")[:3]


def _get_generated_form_code(entity_name, manager):
    prefix = _get_form_code_prefix(entity_name)
    form_code = _generate_form_code(manager, prefix)
    return form_code


def create_registration_form(manager, entity_name):
    if is_sequence(entity_name):
        entity_name = entity_name[0]
    form_code = _get_generated_form_code(entity_name, manager)
    form_model = _create_registration_form(manager, entity_name, form_code, [entity_name])
    form_model.save()
    return form_model

def get_country_appended_location(location_hierarchy, country):
    location_hierarchy_split = location_hierarchy.split(',')
    country_already_appended = (location_hierarchy_split[len(location_hierarchy_split)-1].strip() == country)
    if not location_hierarchy or country_already_appended:
        return ','.join([x.strip() for x in location_hierarchy_split])
    else:
        return ','.join(location_hierarchy_split) + ',' + country

def _get_data(form_data, country,reporter_id=None):
    #TODO need to refactor this code. The master dictionary should be maintained by the registration form model
    mapper = {'telephone_number': MOBILE_NUMBER_FIELD_CODE, 'geo_code': GEO_CODE, 'Name': NAME_FIELD_CODE,
              'location': LOCATION_TYPE_FIELD_CODE, 'short_code':SHORT_CODE_FIELD, "email": EMAIL_FIELD}
    data = dict()
    data[mapper['telephone_number']] = form_data.get('telephone_number')
    data[mapper['location']] = get_country_appended_location(form_data.get('location'), country)
    data[mapper['geo_code']] = form_data.get('geo_code')
    data[mapper['Name']] = form_data.get('name')
    data[mapper['email']] = form_data.get('email')
    data['is_data_sender'] = form_data.get('is_data_sender')
    data['form_code'] = REGISTRATION_FORM_CODE
    data[ENTITY_TYPE_FIELD_CODE] = REPORTER
    data['s'] = reporter_id
    return data


def _add_data_sender_to_trial_organization(telephone_number, org_id):
    data_sender = DataSenderOnTrialAccount.objects.model(mobile_number=telephone_number,
        organization=Organization.objects.get(org_id=org_id))
    data_sender.save(force_insert=True)

def update_data_sender_from_trial_organization(old_telephone_number,new_telephone_number, org_id):
    data_sender = DataSenderOnTrialAccount.objects.model(mobile_number=old_telephone_number,
        organization=Organization.objects.get(org_id=org_id))
    data_sender.delete()
    _add_data_sender_to_trial_organization(new_telephone_number,org_id=org_id)

def process_create_data_sender_form(dbm, form, org_id):
    message = None
    data_sender_id = None

    if form.is_valid():
        try:
            organization = Organization.objects.get(org_id=org_id)
            if organization.in_trial_mode:
                _add_data_sender_to_trial_organization(form.cleaned_data["telephone_number"], org_id)
            web_player = WebPlayer(dbm, LocationBridge(location_tree=get_location_tree(), get_loc_hierarchy=get_location_hierarchy))
            reporter_id = form.cleaned_data["short_code"].lower() if form.cleaned_data != "" else None
            request = Request(message=_get_data(form.cleaned_data, organization.country_name(), reporter_id),
                transportInfo=TransportInfo(transport='web', source='web', destination='mangrove'))

            response = web_player.accept(request, logger=websubmission_logger)
            if response.success:
                data_sender_id = response.short_code
                message = get_success_msg_for_ds_registration_using(response, "web")
            else:
                form.update_errors(response.errors)
        except IntegrityError as e:
            form.update_errors({MOBILE_NUMBER_FIELD_CODE: _("This phone number is already in use. Please supply a different phone number")})
        except DataObjectAlreadyExists as e:
            raise e
        except MangroveException as exception:
            message = exception.message

    return data_sender_id, message


def question_code_generator():
    i = 1
    while 1:
        code = 'q' + str(i)
        yield code
        i += 1


def delete_entity_instance(manager, all_ids, entity_type, transport_info):
    web_player = WebPlayer(manager)
    for entity_id in all_ids:
        message = {ENTITY_TYPE_FIELD_CODE: entity_type,
                   SHORT_CODE: entity_id,
                   'form_code': ENTITY_DELETION_FORM_CODE}
        mangrove_request = Request(message, transport_info)
        web_player.accept(mangrove_request)

def delete_datasender_users_if_any(all_ids, organization):
    for id in all_ids:
        profiles = NGOUserProfile.objects.filter(org_id=organization.org_id,reporter_id=id)
        if is_not_empty(profiles):
            profiles[0].user.delete()

def delete_datasender_for_trial_mode(manager, all_ids, entity_type):
    for entity_id in all_ids:
        entity_to_be_deleted = get_by_short_code_include_voided(manager, entity_id, [entity_type])
        DataSenderOnTrialAccount.objects.get(mobile_number=entity_to_be_deleted.value(MOBILE_NUMBER_FIELD)).delete()


def add_imported_data_sender_to_trial_organization(org_id, imported_datasenders, all_data_senders, index=0):
    organization = Organization.objects.get(org_id=org_id)
    if organization.in_trial_mode:
        mobile_number_index = index
        for ds in all_data_senders:
            if ds['short_code'] in imported_datasenders:
                _add_data_sender_to_trial_organization(ds['cols'][mobile_number_index], org_id)


def entity_type_as_sequence(entity_type):
    if not is_sequence(entity_type):
        entity_type = [entity_type.lower()]
    return entity_type


def set_email_for_contact(dbm, contact, email):
    email_field_code = "email"
    form_model = get_form_model_by_code(dbm, REGISTRATION_FORM_CODE)
    email_field_label = form_model.get_field_by_code(email_field_code).name
    data = (email_field_label, email)
    contact.update_latest_data([data])

def rep_id_name_dict_of_users(manager):
    org_id = get_organization_from_manager(manager).org_id
    orgUsers = NGOUserProfile.objects.filter(org_id=org_id).values_list("user_id", "reporter_id")

    rep_id_map = {}
    user_id_name_map = {}
    for u in orgUsers:
        rep_id_map.update({u[0]: u[1]})
    users = User.objects.filter(groups__name__in=['Project Managers', 'NGO Admins', "Extended Users", 'No Delete PM'], id__in=rep_id_map.keys()).values()

    for user in users:
        user_id_name_map[rep_id_map[user["id"]]] = user["first_name"]

    return user_id_name_map


def get_organization_telephone_number(request):
    organization_settings = utils.get_organization_settings_from_request(request)
    organisation_sms_numbers = organization_settings.get_organisation_sms_number()
    if organization_settings.organization.in_trial_mode:
        return organisation_sms_numbers[0]
    return organisation_sms_numbers[0] if not organisation_sms_numbers[0] or organisation_sms_numbers[0][0] \
                                          == "+" else "+%s" % organisation_sms_numbers[0]