Esempio n. 1
0
def process_registration_lookup(national_id, sms):
    """
    Given the national ID string and the sms object from a syntactically
    valid registration lookup message, check the registration of the
    citizen with the given NID and respond with a message containing the
    current registration status.
    """
    citizen = get_citizen_by_national_id(national_id)
    if not citizen:
        return Result(sms.from_number, constants.VOTER_QUERY_NOT_FOUND)

    sms.citizen = citizen
    sms.save()

    registrations = citizen.registrations.all()
    context = {'person': citizen}  # Used for formatting messages, so key='person' is okay
    if registrations:
        if registrations.count() == 1:
            # Citizen has one confirmed registration
            registration = registrations[0]
            center = registration.registration_center
            context.update({'centre': center.name, 'code': center.center_id})
            return Result(sms.from_number, constants.VOTER_QUERY_REGISTERED_AT, context)
        else:  # pragma: no cover
            # Multiple confirmed registrations - this should never happen, due to
            # database constraints
            logger.error("Voter %s has multiple confirmed registrations", national_id)
            return Result(sms.from_number, constants.VOTER_QUERY_PROBLEM_ENCOUNTERED, context)
    # Citizen has not registered.
    return Result(sms.from_number, constants.VOTER_QUERY_NOT_REGISTERED, context)
Esempio n. 2
0
 def clean_national_id(self):
     national_id = long(self.cleaned_data['national_id'])
     citizen = get_citizen_by_national_id(national_id)
     if not citizen:
         raise ValidationError(get_message(constants.NID_INVALID).msg)
     self.citizen = citizen
     return national_id
Esempio n. 3
0
 def clean_national_id(self):
     national_id = int(self.cleaned_data['national_id'])
     citizen = get_citizen_by_national_id(national_id)
     if not citizen:
         raise ValidationError(_("That is not a valid National ID number."))
     self.citizen = citizen
     return national_id
Esempio n. 4
0
 def clean_national_id(self):
     national_id = long(self.cleaned_data['national_id'])
     citizen = get_citizen_by_national_id(national_id)
     if not citizen:
         raise ValidationError(get_message(constants.NID_INVALID).msg)
     self.citizen = citizen
     return national_id
Esempio n. 5
0
 def clean_national_id(self):
     national_id = int(self.cleaned_data['national_id'])
     citizen = get_citizen_by_national_id(national_id)
     if not citizen:
         raise ValidationError(_("That is not a valid National ID number."))
     self.citizen = citizen
     return national_id
Esempio n. 6
0
 def clean_upload_file(self):
     """Handle uploading a file of NIDs. Will validate the input, look up the citizens, and
     set self.citizens to a list of the Citizen objects they chose."""
     data = self.cleaned_data
     if data['how_to_select'] == Changeset.SELECT_UPLOADED_NIDS:
         uploaded_file = data['upload_file']
         if not uploaded_file:
             if self.instance.pk and self.instance.uploaded_citizens.exists(
             ):
                 # It's okay not to re-upload citizens, we already have some
                 return
             raise ValidationError(
                 _("An uploaded file is required if selection method is uploaded NIDs"
                   ))
         errors = []
         for i, line in enumerate(uploaded_file):
             line_number = i + 1  # Humans start counting at 1, not 0
             if len(errors) >= MAX_ERRORS:
                 errors.append(
                     _("Stopping after {number} errors.").format(
                         number=len(errors)))
                 break
             line = line.strip()
             if not line:
                 # Allow and ignore blank lines
                 continue
             if len(line) != NID_LENGTH:
                 errors.append(
                     _("Line {number}: Wrong length").format(
                         number=line_number))
                 continue
             try:
                 num = int(line)
             except (ValueError, TypeError):
                 errors.append(
                     _("Line {number}: Not a valid number").format(
                         number=line_number))
                 continue
             if line[0] not in ['1', '2']:
                 errors.append(
                     _("Line {number}: Not a valid national ID number").
                     format(number=line_number))
                 continue
             citizen = get_citizen_by_national_id(num)
             if not citizen:
                 errors.append(
                     _("Line {number}: No person registered with that NID").
                     format(number=line_number))
                 continue
             self.citizens.append(citizen)
         if errors:
             raise ValidationError(". ".join(errors))
     return data['upload_file']
Esempio n. 7
0
 def clean_upload_file(self):
     """Handle uploading a file of NIDs. Will validate the input, look up the citizens, and
     set self.citizens to a list of the Citizen objects they chose."""
     data = self.cleaned_data
     if data['how_to_select'] == Changeset.SELECT_UPLOADED_NIDS:
         uploaded_file = data['upload_file']
         if not uploaded_file:
             if self.instance.pk and self.instance.uploaded_citizens.exists():
                 # It's okay not to re-upload citizens, we already have some
                 return
             raise ValidationError(
                 _("An uploaded file is required if selection method is uploaded NIDs"))
         errors = []
         for i, line in enumerate(uploaded_file):
             line_number = i + 1  # Humans start counting at 1, not 0
             if len(errors) >= MAX_ERRORS:
                 errors.append(_("Stopping after {number} errors.").format(number=len(errors)))
                 break
             line = line.strip()
             if not line:
                 # Allow and ignore blank lines
                 continue
             if len(line) != NID_LENGTH:
                 errors.append(_("Line {number}: Wrong length").format(number=line_number))
                 continue
             try:
                 num = int(line)
             except (ValueError, TypeError):
                 errors.append(_("Line {number}: Not a valid number").format(number=line_number))
                 continue
             if line[0] not in ['1', '2']:
                 errors.append(
                     _("Line {number}: Not a valid national ID number")
                     .format(number=line_number))
                 continue
             citizen = get_citizen_by_national_id(num)
             if not citizen:
                 errors.append(
                     _("Line {number}: No person registered with that NID")
                     .format(number=line_number))
                 continue
             self.citizens.append(citizen)
         if errors:
             raise ValidationError(". ".join(errors))
     return data['upload_file']
Esempio n. 8
0
    def get(self, request, *args, **kwargs):
        obj = get_citizen_by_national_id(national_id=long(kwargs['voter_id']))
        if not obj:
            raise Http404

        result = dict(
            person_id=obj.civil_registry_id,
            national_id=obj.national_id,
            first_name=obj.first_name,
            father_name=obj.father_name,
            grandfather_name=obj.grandfather_name,
            mother_name=obj.mother_name,
            family_name=obj.family_name,
            gender='F' if obj.gender == FEMALE else 'M',
            registry_number=obj.fbr_number,
            birth_date=format_date(obj.birth_date),
        )
        return self.render_json_response(result)
Esempio n. 9
0
def process_registration_lookup(national_id, sms):
    """
    Given the national ID string and the sms object from a syntactically
    valid registration lookup message, check the registration of the
    citizen with the given NID and respond with a message containing the
    current registration status.
    """
    citizen = get_citizen_by_national_id(national_id)
    if not citizen:
        return Result(sms.from_number, constants.VOTER_QUERY_NOT_FOUND)

    sms.citizen = citizen
    sms.save()

    registrations = citizen.registrations.all()
    context = {
        'person': citizen
    }  # Used for formatting messages, so key='person' is okay
    if registrations:
        if registrations.count() == 1:
            # Citizen has one confirmed registration
            registration = registrations[0]
            center = registration.registration_center
            context.update({'centre': center.name, 'code': center.center_id})
            return Result(sms.from_number, constants.VOTER_QUERY_REGISTERED_AT,
                          context)
        else:  # pragma: no cover
            # Multiple confirmed registrations - this should never happen, due to
            # database constraints
            logger.error("Voter %s has multiple confirmed registrations",
                         national_id)
            return Result(sms.from_number,
                          constants.VOTER_QUERY_PROBLEM_ENCOUNTERED, context)
    # Citizen has not registered.
    return Result(sms.from_number, constants.VOTER_QUERY_NOT_REGISTERED,
                  context)
Esempio n. 10
0
 def test_basic_get(self):
     citizen = CitizenFactory()
     citizen2 = get_citizen_by_national_id(citizen.national_id)
     self.assertEqual(citizen.pk, citizen2.pk)
Esempio n. 11
0
 def test_missing_citizen(self):
     citizen = CitizenFactory(missing=now())
     citizen2 = get_citizen_by_national_id(citizen.national_id)
     self.assertIsNone(citizen2)
Esempio n. 12
0
 def test_no_such_citizen(self):
     citizen = get_citizen_by_national_id(99)
     self.assertIsNone(citizen)
Esempio n. 13
0
def process_registration_request(center_id, national_id, sms):
    """
        Process a well formatted registration request message and returns a
        translated response message.

        Attempts to retrieve a Citizen instance
            + searches our database for match.

        Updates SMS instance
            + all incoming messages get logged.

        Registers citizen
            + Verifies that citizen has only registered once.
            + Updates registration if allowed
            + Does nothing if registering to same center twice.

        Returns a Result object.
    """
    try:
        logger.debug("Getting registration centre")
        # Note that registration to a copy center is not allowed
        center = RegistrationCenter.objects.get(center_id=center_id, reg_open=True, copy_of=None)
    except RegistrationCenter.DoesNotExist:
        return Result(sms.from_number, constants.RESPONSE_CENTER_ID_INVALID)

    citizen = get_citizen_by_national_id(national_id)
    if not citizen:
        return Result(sms.from_number, constants.RESPONSE_NID_INVALID)

    sms.citizen = citizen

    if not citizen.is_eligible():
        logger.debug("Citizen is not eligible.")
        return Result(sms.from_number, constants.RESPONSE_NID_INVALID)

    try:
        registration = Registration.objects.get(citizen=citizen)
    except Registration.DoesNotExist:
        logger.debug("Citizen not already registered")
        registration = None

    remaining = remaining_registrations(sms.from_number)

    if registration:
        logger.debug("Citizen already registered")
        same_phone = registration.sms.from_number == sms.from_number
        unlocked = False
        if registration.unlocked:
            unlocked = True

        if same_phone or unlocked:
            # Same phone, or they've been granted an exception

            if not same_phone and remaining == 0:
                # They're trying to change to a phone that is already
                # at its maximum # of registrations.
                return Result(sms.from_number, constants.TOO_MANY_REGISTRATIONS_ON_PHONE,
                              dict(maximum=settings.MAX_REGISTRATIONS_PER_PHONE))

            # If they're out of changes, sending from same phone, they always get message 6
            if registration.change_count >= registration.max_changes:
                logger.debug("Out of changes, send message 6")
                return Result(sms.from_number, constants.MESSAGE_6,
                              dict(centre=registration.registration_center.name,
                                   code=registration.registration_center.center_id,
                                   person=unicode(citizen)))

            if registration.registration_center == center:
                # same location - but they could be changing their registered phone
                if registration.sms.from_number != sms.from_number:
                    registration.sms = sms
                    logger.debug("Updating phone number")
                    registration.repeat_count = 0
                    registration.save_with_archive_version()
                else:
                    logger.debug("registration is exact repeat")
                    registration.repeat_count += 1
                    # We're just counting their calls, not changing their
                    # registration; no need to make an archive copy.
                    registration.save()
                return Result(sms.from_number, constants.MESSAGE_1,
                              dict(centre=center.name,
                                   code=center.center_id,
                                   person=unicode(citizen)))
            # different voting center
            logger.debug("registration changing center, count=%d" % registration.change_count)

            # We know they still have changes left because we checked above
            registration.change_count += 1
            registration.repeat_count = 1
            registration.registration_center = center
            sms.save()
            registration.sms = sms
            if unlocked:
                # they've used their exception
                registration.unlocked_until = None
            registration.save_with_archive_version()
            if not registration.remaining_changes:
                # Last time
                return Result(sms.from_number, constants.MESSAGE_5,
                              dict(centre=center.name,
                                   code=center.center_id,
                                   person=unicode(citizen)))
            elif registration.remaining_changes == 1:
                # one more allowed
                return Result(sms.from_number, constants.MESSAGE_4,
                              dict(centre=center.name,
                                   code=center.center_id,
                                   person=unicode(citizen)))
            else:
                return Result(sms.from_number, constants.RESPONSE_VALID_REGISTRATION,
                              dict(centre=center.name,
                                   code=center.center_id,
                                   person=unicode(citizen)))

        # Different phone
        if registration.registration_center == center:
            # same registration - don't do anything
            return Result(sms.from_number, constants.MESSAGE_7,
                          dict(centre=registration.registration_center.name,
                               number=registration.sms.from_number[-4:]))
        # Cannot change anything from a different phone
        return Result(sms.from_number, constants.MESSAGE_2,
                      dict(centre=registration.registration_center.name,
                           number=registration.sms.from_number[-4:]))

    logger.debug("new registration")
    kwargs = {"citizen": citizen, "registration_center": center, "sms": sms}

    if remaining == 0:
        # They're trying to create a new registration, but
        # there are the maximum (or more) registrations from
        # this phone already.
        return Result(sms.from_number, constants.TOO_MANY_REGISTRATIONS_ON_PHONE,
                      dict(maximum=settings.MAX_REGISTRATIONS_PER_PHONE))

    # We have to save the SMS before we can save the Registration that refers to it
    sms.save()
    Registration.objects.create(**kwargs)

    remaining -= 1
    if remaining == 0:
        # Send a warning message - no more regs on this phone
        msg_code = constants.AT_MAXIMUM_REGISTRATIONS_ON_PHONE
    elif remaining == 1:
        # Send a warning message - only one more reg on this phone
        msg_code = constants.ONE_MORE_REGISTRATION_ON_PHONE
    else:
        # Normal message
        msg_code = constants.RESPONSE_VALID_REGISTRATION

    return Result(sms.from_number, msg_code,
                  dict(centre=center.name,
                       code=center.center_id,
                       person=unicode(citizen)))
Esempio n. 14
0
def process_registration_request(center_id, national_id, sms):
    """
        Process a well formatted registration request message and returns a
        translated response message.

        Attempts to retrieve a Citizen instance
            + searches our database for match.

        Updates SMS instance
            + all incoming messages get logged.

        Registers citizen
            + Verifies that citizen has only registered once.
            + Updates registration if allowed
            + Does nothing if registering to same center twice.

        Returns a Result object.
    """
    try:
        logger.debug("Getting registration centre")
        # Note that registration to a copy center is not allowed
        center = RegistrationCenter.objects.get(center_id=center_id,
                                                reg_open=True,
                                                copy_of=None)
    except RegistrationCenter.DoesNotExist:
        return Result(sms.from_number, constants.RESPONSE_CENTER_ID_INVALID)

    citizen = get_citizen_by_national_id(national_id)
    if not citizen:
        return Result(sms.from_number, constants.RESPONSE_NID_INVALID)

    sms.citizen = citizen

    if not citizen.is_eligible():
        logger.debug("Citizen is not eligible.")
        return Result(sms.from_number, constants.RESPONSE_NID_INVALID)

    try:
        registration = Registration.objects.get(citizen=citizen)
    except Registration.DoesNotExist:
        logger.debug("Citizen not already registered")
        registration = None

    remaining = remaining_registrations(sms.from_number)

    if registration:
        logger.debug("Citizen already registered")
        same_phone = registration.sms.from_number == sms.from_number
        unlocked = False
        if registration.unlocked:
            unlocked = True

        if same_phone or unlocked:
            # Same phone, or they've been granted an exception

            if not same_phone and remaining == 0:
                # They're trying to change to a phone that is already
                # at its maximum # of registrations.
                return Result(
                    sms.from_number, constants.TOO_MANY_REGISTRATIONS_ON_PHONE,
                    dict(maximum=settings.MAX_REGISTRATIONS_PER_PHONE))

            # If they're out of changes, sending from same phone, they always get message 6
            if registration.change_count >= registration.max_changes:
                logger.debug("Out of changes, send message 6")
                return Result(
                    sms.from_number, constants.MESSAGE_6,
                    dict(centre=registration.registration_center.name,
                         code=registration.registration_center.center_id,
                         person=unicode(citizen)))

            if registration.registration_center == center:
                # same location - but they could be changing their registered phone
                if registration.sms.from_number != sms.from_number:
                    registration.sms = sms
                    logger.debug("Updating phone number")
                    registration.repeat_count = 0
                    registration.save_with_archive_version()
                else:
                    logger.debug("registration is exact repeat")
                    registration.repeat_count += 1
                    # We're just counting their calls, not changing their
                    # registration; no need to make an archive copy.
                    registration.save()
                return Result(
                    sms.from_number, constants.MESSAGE_1,
                    dict(centre=center.name,
                         code=center.center_id,
                         person=unicode(citizen)))
            # different voting center
            logger.debug("registration changing center, count=%d" %
                         registration.change_count)

            # We know they still have changes left because we checked above
            registration.change_count += 1
            registration.repeat_count = 1
            registration.registration_center = center
            sms.save()
            registration.sms = sms
            if unlocked:
                # they've used their exception
                registration.unlocked_until = None
            registration.save_with_archive_version()
            if not registration.remaining_changes:
                # Last time
                return Result(
                    sms.from_number, constants.MESSAGE_5,
                    dict(centre=center.name,
                         code=center.center_id,
                         person=unicode(citizen)))
            elif registration.remaining_changes == 1:
                # one more allowed
                return Result(
                    sms.from_number, constants.MESSAGE_4,
                    dict(centre=center.name,
                         code=center.center_id,
                         person=unicode(citizen)))
            else:
                return Result(
                    sms.from_number, constants.RESPONSE_VALID_REGISTRATION,
                    dict(centre=center.name,
                         code=center.center_id,
                         person=unicode(citizen)))

        # Different phone
        if registration.registration_center == center:
            # same registration - don't do anything
            return Result(
                sms.from_number, constants.MESSAGE_7,
                dict(centre=registration.registration_center.name,
                     number=registration.sms.from_number[-4:]))
        # Cannot change anything from a different phone
        return Result(
            sms.from_number, constants.MESSAGE_2,
            dict(centre=registration.registration_center.name,
                 number=registration.sms.from_number[-4:]))

    logger.debug("new registration")
    kwargs = {"citizen": citizen, "registration_center": center, "sms": sms}

    if remaining == 0:
        # They're trying to create a new registration, but
        # there are the maximum (or more) registrations from
        # this phone already.
        return Result(sms.from_number,
                      constants.TOO_MANY_REGISTRATIONS_ON_PHONE,
                      dict(maximum=settings.MAX_REGISTRATIONS_PER_PHONE))

    # We have to save the SMS before we can save the Registration that refers to it
    sms.save()
    Registration.objects.create(**kwargs)

    remaining -= 1
    if remaining == 0:
        # Send a warning message - no more regs on this phone
        msg_code = constants.AT_MAXIMUM_REGISTRATIONS_ON_PHONE
    elif remaining == 1:
        # Send a warning message - only one more reg on this phone
        msg_code = constants.ONE_MORE_REGISTRATION_ON_PHONE
    else:
        # Normal message
        msg_code = constants.RESPONSE_VALID_REGISTRATION

    return Result(
        sms.from_number, msg_code,
        dict(centre=center.name,
             code=center.center_id,
             person=unicode(citizen)))