def create(center_without_office=False, num_copy_centers=DEFAULT_NUM_COPY_CENTERS, num_registrations=DEFAULT_NUM_REGISTRATIONS, num_registration_dates=DEFAULT_NUM_REGISTRATION_DATES, num_daily_reports=DEFAULT_NUM_DAILY_REPORTS, num_registration_centers=DEFAULT_NUM_REGISTRATION_CENTERS, num_subconstituencies=DEFAULT_NUM_SUBCONSTITUENCIES, use_existing_infra=False, num_inactive_centers_per_election=DEFAULT_NUM_INACTIVE_PER_ELECTION, num_no_reg_centers=DEFAULT_NUM_NO_REG_CENTERS, election_dates=()): assert settings.ENVIRONMENT not in ('production', 'testing') delete(delete_infra=not use_existing_infra) empty_report_store() # Remove any old data from Redis # Figure out ~10% of "normal" centers... fraction_of_normal_centers = \ max(1, int(0.1 * num_registration_centers)) if num_registration_centers else 0 # If numbers of some weird center types weren't specified, use a small # fraction of normal centers. if num_copy_centers == DEFAULT_NUM_COPY_CENTERS: num_copy_centers = fraction_of_normal_centers if num_no_reg_centers == DEFAULT_NUM_NO_REG_CENTERS: num_no_reg_centers = fraction_of_normal_centers carrier = BackendFactory() if election_dates: elections = [ ElectionFactory(polling_start_time=election_date.replace(hour=8), polling_end_time=election_date.replace(hour=20)) for election_date in election_dates ] else: election_date = PAST_DAY.replace(hour=8, microsecond=123456) election = ElectionFactory( polling_start_time=election_date, polling_end_time=election_date.replace(hour=20)) elections = (election, ) if not use_existing_infra: OfficeFactory() ConstituencyFactory(name_english='first') SubConstituencyFactory(name_english='Benghazi') offices = Office.objects.all() copy_centers = [] no_reg_centers = [] staff_phones = [] if use_existing_infra: # Pick centers that support registrations at random. centers = RegistrationCenter.objects.filter(reg_open=True)\ .exclude(center_type=RegistrationCenter.Types.COPY)\ .order_by('?')[:num_registration_centers] if num_copy_centers: # user wants some, but there might not be any copy_centers = RegistrationCenter.objects.\ filter(reg_open=True, center_type=RegistrationCenter.Types.COPY)\ .order_by('?')[:num_copy_centers] if num_no_reg_centers: # user wants some, but there might not be any no_reg_centers = RegistrationCenter.objects.\ filter(reg_open=False).order_by('?')[:num_no_reg_centers] # why like this? sliced queries and/or list all_kinds_of_centers = \ list(centers) + list(copy_centers) + list(no_reg_centers) else: subconstituencies = SubConstituency.objects.exclude( pk=SPLIT_CENTER_SUBCONSTITUENCY_ID) subconstituencies = subconstituencies[:num_subconstituencies] centers = [] for i in range(num_registration_centers): constituency = Constituency.objects.filter(name_english='first')[0] subconstituency = random.choice(subconstituencies) rc = RegistrationCenter(name='polling-center-%d' % i, center_id=CENTER_ID_MIN_INT_VALUE + i, constituency=constituency, subconstituency=subconstituency, office=random.choice(offices)) rc.full_clean() rc.save() centers.append(rc) for i in range(num_copy_centers): original = random.choice(centers) # XXX This doesn't handle accidentally making too many copies of the same # center, so make sure --num-centers is "big enough" w.r.t. --num-copy-centers. new_center_id = CENTER_ID_MIN_INT_VALUE + num_registration_centers + i copy = RegistrationCenter( name='Copy of %s' % original.name, center_id=new_center_id, constituency=original.constituency, subconstituency=original.subconstituency, office=original.office, center_type=RegistrationCenter.Types.COPY, copy_of=original) copy.full_clean() copy.save() copy_centers.append(copy) for i in range(num_no_reg_centers): constituency = Constituency.objects.filter(name_english='first')[0] subconstituency = random.choice(subconstituencies) center_id = CENTER_ID_MIN_INT_VALUE + num_registration_centers + num_copy_centers + i rc = RegistrationCenter(name='no-reg-polling-center-%d' % i, center_id=center_id, constituency=constituency, subconstituency=subconstituency, office=random.choice(offices), reg_open=False) rc.full_clean() rc.save() all_kinds_of_centers = centers + copy_centers + no_reg_centers if center_without_office: try: # by not specifying office and other infra, it will be "standalone" rc = RegistrationCenter(name='dummy-registration-center', center_id=UNUSED_CENTER_ID) rc.full_clean() rc.save() except ValidationError: pass # assume that it already exists for election in elections: num_daily_reports_on_election_day = int(round(0.9 * num_daily_reports)) centers_reported = set() for i in range(num_daily_reports_on_election_day): staff_phone_number = STAFF_PHONE_NUMBER_PATTERN % i from_center = random.choice(all_kinds_of_centers) ensure_staff_phone_exists( staff_phone_number, from_center, staff_phones, election.work_start_time + datetime.timedelta(minutes=5)) # split votes between two options number_of_votes = (random.randint(1, 100), random.randint(1, 100)) random_period_number = random.randint(FIRST_PERIOD_NUMBER, LAST_PERIOD_NUMBER) pr = PollingReport(election=election, phone_number=staff_phone_number, registration_center=from_center, period_number=random_period_number, num_voters=sum(number_of_votes), creation_date=election.polling_start_time) pr.full_clean() pr.save() s = SMS(from_number=staff_phone_number, to_number=POLLING_REPORT_PHONE_NUMBER, direction=INCOMING, message='my message', msg_type=SMS.POLLING_REPORT, message_code=MESSAGE_1, carrier=carrier, creation_date=election.polling_start_time) s.full_clean() s.save() if from_center in centers_reported: continue # can't send but one PreliminaryVoteCount from a center # send a corresponding vote count for option, votes_for_option in enumerate(number_of_votes, start=1): pvc = PreliminaryVoteCount( election=election, phone_number=staff_phone_number, registration_center=from_center, option=option, num_votes=votes_for_option, creation_date=election.polling_start_time) pvc.full_clean() pvc.save() s = SMS( from_number=staff_phone_number, to_number=PRELIMINARY_VOTE_COUNT_PHONE_NUMBER, # XXX no specific message type for PreliminaryVoteCount direction=INCOMING, message='my message', msg_type=SMS.POLLING_REPORT, message_code=MESSAGE_1, carrier=carrier, creation_date=election.polling_start_time) s.full_clean() s.save() centers_reported.add(from_center) # some daily reports on the day after for i in range(num_daily_reports - num_daily_reports_on_election_day): staff_phone_number = STAFF_PHONE_NUMBER_PATTERN % i rc = random.choice(all_kinds_of_centers) ensure_staff_phone_exists( staff_phone_number, rc, staff_phones, election.work_start_time + datetime.timedelta(minutes=5)) report_creation_date = election.polling_start_time + datetime.timedelta( days=1) pr = PollingReport( election=election, phone_number=staff_phone_number, registration_center=rc, period_number= LAST_PERIOD_NUMBER, # day after counts as last period num_voters=random.randint(1, 50), creation_date=report_creation_date) pr.full_clean() pr.save() s = SMS(from_number=staff_phone_number, to_number=POLLING_REPORT_PHONE_NUMBER, direction=INCOMING, message='my message', msg_type=SMS.POLLING_REPORT, message_code=MESSAGE_1, carrier=carrier, creation_date=election.polling_start_time) s.full_clean() s.save() # Tag some centers as inactive for the election. We may or may not pick some that # sent messages as being inactive. num_inactive_centers_per_election = \ min(num_inactive_centers_per_election, len(all_kinds_of_centers)) if num_inactive_centers_per_election: reordered = all_kinds_of_centers random.shuffle(reordered) for i in range(num_inactive_centers_per_election): inactive_on_election = CenterClosedForElection( registration_center=reordered[i], election=election) inactive_on_election.full_clean() inactive_on_election.save() tz = timezone(settings.TIME_ZONE) # construct a datetime that will change based on timezone discrepancies # 0-2am in Libya has a different date than the same time in UDT or EDT today_fragile = now().astimezone(tz).replace(hour=0, minute=59) # tz.normalize fixes up the date arithmetic when crossing DST boundaries creation_dates = \ [tz.normalize((today_fragile - datetime.timedelta(days=DAYS_BETWEEN_REGISTRATIONS * i)).astimezone(tz)) for i in range(num_registration_dates)] citizens = [] for i in range(num_registrations): # about 60% of registrations are for males, just as with actual data gender = MALE if random.randint(1, 100) <= 60 else FEMALE nat_id = '%d%011d' % (gender, i) creation_date = random.choice(creation_dates) modification_date = creation_date # Select voter ages from 18 years on up. voter_age = random.randint(18, 99) # If they were a certain age at any time yesterday, they are certainly that age at any time # today. yesterday = datetime.datetime.now().replace( tzinfo=tz) - datetime.timedelta(days=1) birth_date = datetime.date(yesterday.year - voter_age, yesterday.month, yesterday.day) civil_registry_id = random.randint(1, 99999999) citizen = CitizenFactory(civil_registry_id=civil_registry_id, national_id=nat_id, gender=gender, birth_date=birth_date) citizens.append(citizen) s = SMS(from_number=VOTER_PHONE_NUMBER_PATTERN % i, to_number=REGISTRATION_PHONE_NUMBER, citizen=citizen, direction=INCOMING, message='my reg message', msg_type=SMS.REGISTRATION, message_code=MESSAGE_1, carrier=carrier, creation_date=creation_date) s.full_clean() s.save() rc = random.choice(centers) confirmed = random.randint(1, 100) <= 80 # most are confirmed if confirmed: archive_time = None else: archive_time = random.choice(creation_dates) r = Registration(citizen=citizen, registration_center=rc, sms=s, archive_time=archive_time, creation_date=creation_date, modification_date=modification_date) r.full_clean() r.save() if num_registrations: # if any data being generated # generate a variety of sms messages for i in range(NUM_RANDOM_SMS_MESSAGES): sms_type = random.choice(SMS.MESSAGE_TYPES)[0] staff_phone = random.choice(staff_phones) s = SMS(from_number=staff_phone.phone_number, to_number=RANDOM_MESSAGE_PHONE_NUMBER, citizen=random.choice(citizens), direction=INCOMING, message='my long random message', msg_type=sms_type, message_code=MESSAGE_1, carrier=carrier, creation_date=random.choice(creation_dates)) s.full_clean() s.save() for election in elections: for rc in centers: i = random.randint(8888, 9999) staff_phone_number = STAFF_PHONE_NUMBER_PATTERN % i ensure_staff_phone_exists( staff_phone_number, rc, staff_phones, election.work_start_time + datetime.timedelta(minutes=5)) center_open = CenterOpen( election=election, phone_number=staff_phone_number, registration_center=rc, creation_date=election.polling_start_time.replace( hour=random.randint(0, 10), minute=23)) center_open.full_clean() center_open.save() s = SMS(from_number=staff_phone_number, to_number=ACTIVATE_PHONE_NUMBER, direction=INCOMING, message='my message', msg_type=SMS.ACTIVATE, message_code=MESSAGE_1, carrier=carrier, creation_date=election.polling_start_time) s.full_clean() s.save()
def setUp(self): self.registrations_per_center = 4 self.oil_center_period_1_voters = 1 self.oil_center_period_2_voters = 2 self.offices = [OfficeFactory(region=Office.REGION_EAST), OfficeFactory(region=Office.REGION_WEST)] # Note: An oil center doesn't normally allow registrations, but it does so for # this testcase. self.oil_center = RegistrationCenterFactory(office=self.offices[0], center_type=RegistrationCenter.Types.OIL) # !reg_open won't affect election day counts but it will affect whether # or not any registrations are found self.inactive_for_reg_center = RegistrationCenterFactory(office=self.offices[1], reg_open=False) self.centers = [self.oil_center, RegistrationCenterFactory(office=self.offices[0]), RegistrationCenterFactory(office=self.offices[0]), RegistrationCenterFactory(office=self.offices[1]), self.inactive_for_reg_center, ] copy_center = RegistrationCenterFactory(office=self.offices[1], copy_of=self.centers[3]) self.centers.append(copy_center) self.election_decoy_before = ElectionFactory( name_english='decoy before', name_arabic='decoy before (ar)', polling_start_time=now() - timedelta(days=10), polling_end_time=now() - timedelta(days=9), ) self.election = ElectionFactory( name_english='%s election' % type(self).__name__, name_arabic='not Arabic', polling_start_time=now() - timedelta(hours=2), polling_end_time=now() + timedelta(hours=2), ) self.election_decoy_after = ElectionFactory( name_english='decoy after', name_arabic='decoy after (ar)', polling_start_time=now() + timedelta(days=9), polling_end_time=now() + timedelta(days=10), ) self.center_opens = [] for center in self.centers: if center != self.centers[1]: self.center_opens.append(CenterOpenFactory(election=self.election, registration_center=center)) # CenterOpen may refer to a deleted center. Make sure that we don't find those self.deleted_center = RegistrationCenterFactory(office=self.offices[0], deleted=True) self.center_open_referring_to_deleted_center = CenterOpenFactory( election=self.election, registration_center=self.deleted_center) # Performance enhancement: this dummy person and SMS allow me to avoid creation of two # spurious objects for each registration I create. self.citizen = CitizenFactory() self.sms = SMSFactory(citizen=self.citizen) # Create registrations, but be careful not to create any at the copy center # or at the center which doesn't support registrations. self.registrations = [] for center in self.centers: if center.reg_open and not center.copy_of: self.registrations += \ RegistrationFactory.create_batch(self.registrations_per_center, citizen=self.citizen, sms=self.sms, registration_center=center) # These reports include quirks such as multiple reports for a center (very common in real # life), a missing final period report, and multiple reports for the same center & period. self.reports = [ PollingReportFactory(election=self.election, registration_center=self.oil_center, period_number=FIRST_PERIOD_NUMBER, num_voters=self.oil_center_period_1_voters), PollingReportFactory(election=self.election, registration_center=self.oil_center, period_number=FIRST_PERIOD_NUMBER + 1, num_voters=self.oil_center_period_2_voters), PollingReportFactory(election=self.election, registration_center=self.centers[2], period_number=FIRST_PERIOD_NUMBER, num_voters=1), # The next two reports are for the same center & period with different num_voters # to exercise the code that sorts by modification_date. PollingReportFactory(election=self.election, registration_center=self.centers[2], period_number=FIRST_PERIOD_NUMBER + 1, num_voters=4), PollingReportFactory(election=self.election, registration_center=self.centers[2], period_number=FIRST_PERIOD_NUMBER + 1, num_voters=6), PollingReportFactory(election=self.election, registration_center=self.centers[3], period_number=FIRST_PERIOD_NUMBER, num_voters=1), PollingReportFactory(election=self.election, registration_center=self.centers[3], period_number=FIRST_PERIOD_NUMBER + 1, num_voters=4), # This report for a deleted center should be ignored PollingReportFactory(election=self.election, registration_center=self.deleted_center, period_number=FIRST_PERIOD_NUMBER + 1, num_voters=50), PollingReportFactory(election=self.election, registration_center=self.inactive_for_reg_center, period_number=FIRST_PERIOD_NUMBER + 1, num_voters=50), # This report for a copy center should count towards the original/parent center PollingReportFactory(election=self.election, registration_center=copy_center, period_number=LAST_PERIOD_NUMBER, num_voters=1), ] self.result = generate_election_day_hq_reports(self.election) # Create an alternate result which reflects that the "oil center" is # marked inactive for this election. self.inactive_on_election = CenterClosedForElection( registration_center=self.oil_center, election=self.election ) self.inactive_on_election.full_clean() self.inactive_on_election.save() self.result_with_inactive = generate_election_day_hq_reports(self.election)
def setUp(self): self.staff_user = UserFactory() self.staff_user.is_staff = True self.staff_user.save() assert self.client.login(username=self.staff_user.username, password=DEFAULT_USER_PASSWORD) self.reporting_user = test_reports.TEST_USERNAME self.reporting_password = test_reports.TEST_PASSWORD REPORT_USER_DB[self.reporting_user] = self.reporting_password # Pick a start time that represents different days in Libya vs UTC tz = timezone(settings.TIME_ZONE) polling_start_time = astz(FUTURE_DAY.replace(hour=22), tz) polling_end_time = tz.normalize(polling_start_time + timedelta(hours=16)) self.election = ElectionFactory( polling_start_time=polling_start_time, polling_end_time=polling_end_time, ) self.election_day_dt = self.election.polling_start_time # Create "decoy" election just to confirm that it doesn't break reports. decoy_start_time = tz.normalize(polling_start_time - timedelta(days=10)) decoy_end_time = tz.normalize(decoy_start_time + timedelta(hours=16)) ElectionFactory( polling_start_time=decoy_start_time, polling_end_time=decoy_end_time, ) self.all_centers = [] self.rc_1 = RegistrationCenterFactory() self.all_centers.append(self.rc_1) self.rc_2 = RegistrationCenterFactory() self.all_centers.append(self.rc_2) self.rc_3 = RegistrationCenterFactory() self.all_centers.append(self.rc_3) self.rc_4 = RegistrationCenterFactory() self.all_centers.append(self.rc_4) self.copy_of_rc_1 = RegistrationCenterFactory(copy_of=self.rc_1, office=self.rc_1.office) self.all_centers.append(self.copy_of_rc_1) # rc_5 is inactive for this election self.rc_5 = RegistrationCenterFactory(office=self.rc_1.office) self.all_centers.append(self.rc_5) inactive_on_election = CenterClosedForElection( registration_center=self.rc_5, election=self.election) inactive_on_election.full_clean() inactive_on_election.save() self.all_office_ids = [center.office_id for center in self.all_centers] self.carrier_1 = BackendFactory() self.citizen_1 = CitizenFactory() # Create registrations on the 4 days leading up to election day # Put the registrations at different hours of the day to stress TZ handling. self.registration_dates = [] self.registration_date_strs = [] hour_of_day = 0 for delta_days in range(10, 4, -1): assert hour_of_day < 24 reg_date = astz(self.election_day_dt - timedelta(days=delta_days), tz)\ .replace(hour=hour_of_day) hour_of_day += 4 self.registration_dates.append(reg_date) self.registration_date_strs.append(reg_date.strftime('%Y-%m-%d')) self.yesterday_date, _ = calc_yesterday(self.registration_date_strs) self.yesterday_date_dm = self.yesterday_date.strftime('%d/%m') # yesterday_date is a date; get a datetime form self.yesterday_date_dt = tz.localize( datetime(self.yesterday_date.year, self.yesterday_date.month, self.yesterday_date.day, 0, 0, 0)) self.staff_phone_number = STAFF_PHONE_NUMBER_PATTERN % 12345