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)
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
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
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']
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']
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)
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)
def test_basic_get(self): citizen = CitizenFactory() citizen2 = get_citizen_by_national_id(citizen.national_id) self.assertEqual(citizen.pk, citizen2.pk)
def test_missing_citizen(self): citizen = CitizenFactory(missing=now()) citizen2 = get_citizen_by_national_id(citizen.national_id) self.assertIsNone(citizen2)
def test_no_such_citizen(self): citizen = get_citizen_by_national_id(99) self.assertIsNone(citizen)
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)))
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)))