def phone_call_connect(self): # speak to someone - go from here to a more complex assist/outcome section called_at = self.env.now # at this time/date what type of adviser is available? # is one of these types available? adviser = self.adviser_check(called_at) current_ad = yield self.rep.adviser_store.get(lambda item: item.type == adviser) wait_time = self.env.now - called_at # renege time a fixed value for now. Determine suitable distribution from paradata or call centre MI. if wait_time >= self.input_data["renege"]: # hang up if oo.record_call_renege: self.output_data['Call_renege'].append(oo.generic_output(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now)) self.rep.adviser_store.put(current_ad) # record wait times for stats generation - wait time her eis how long they would of had to wait # not how long they did wait - this is given by the renege time if wait_time > 0: self.record_wait_time(wait_time, self.input_data["renege"]) # so hh didn't get through - some may now respond with no extra tries but more will call again or give up. action = self.action_test("renege") # but when? if not action[0] == "Do nothing": function_to_call = getattr(self, action[0]) yield self.env.timeout(action[1]) yield self.env.process(function_to_call()) else: # got through if oo.record_call_contact: self.output_data['Call_contact'].append(oo.generic_output(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now)) if wait_time > 0: self.record_wait_time(wait_time, self.input_data["renege"]) yield self.env.process(self.phone_call_assist(current_ad))
def phone_call_assist(self, current_ad): da_test = self.rnd.uniform(0, 100) # how effective current adviser type at conversion to digital da_effectiveness = current_ad.input_data['da_effectiveness'][self.hh_type] # wait to determine if digital or paper preference yield self.env.timeout(current_ad.input_data['call_times']['query'] / 60) if self.digital or self.paper_allowed: # no need to persuade go straight to the outcome yield self.rep.env.process(self.phone_call_outcome(current_ad)) elif not self.digital and da_test <= da_effectiveness: # not digital and adviser converts household to digital but takes some time yield self.env.timeout(current_ad.input_data['call_times']['convert'] / 60) # record event if oo.record_call_convert: self.rep.output_data['Call_convert'].append(oo.generic_output(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.rep.env.now)) self.digital = True yield self.env.process(self.phone_call_outcome(current_ad)) elif not self.digital and da_test > da_effectiveness: # not digital and not converted so arrange a visit or something else when we know what! yield self.env.timeout(current_ad.input_data['call_times']['failed'] / 60) if oo.record_call_request: self.rep.output_data['Call_request'].append(oo.generic_output(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now)) # up priority and tag so a visit happens at time likely time to be in - or just set it to succeed - or both? self.priority -= 10 self.rep.adviser_store.put(current_ad) # self.arranged_visit = True # basically has requested a visit so go at optimal time and move to front.. self.arranged_visit = False
def action(self): # directs the household to follow the COA set on initialisation at the relevant time yield self.env.timeout(self.initial_time) if self.initial_status == 'late' and not self.return_sent: # normal response yield self.env.process(self.household_returns(self.calc_delay())) elif self.initial_status == 'help' and not self.return_sent: # help yield self.env.process(self.contact()) else: # nowt if oo.record_do_nothing: self.output_data['Do_nothing'].append(oo.generic_output(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now)) yield self.env.timeout(0) # do nothing more
def schedule_paper_drop(obj, contact_type, reminder_type, delay): # add to summary of paper given out if reminder_type == 'pq' and oo.record_paper_summary: for key, value in obj.rep.paper_summary.items(): value[str(getattr(obj, key))][math.floor(obj.rep.env.now / 24)] += 1 for key, value in obj.rep.paper_totals.items(): value[str(getattr(obj, key))] += 1 output_type = contact_type + "_" + reminder_type + "_posted" # use this as output key if oo.record_posted: obj.rep.output_data[output_type].append( oo.generic_output(obj.rep.reps, obj.district.district, obj.la, obj.lsoa, obj.digital, obj.hh_type, obj.hh_id, obj.env.now)) if delay > 0: start_delayed(obj.env, obj.receive_reminder(reminder_type), delay) else: obj.env.process(obj.receive_reminder(reminder_type)) yield obj.env.timeout(0)
def ret_rec(household, rep): # print out every 100000 returns? #if rep.total_responses % 100000 == 0: #print(rep.total_responses) if oo.record_active_summary: # add household to summary of responses for key, value in rep.active_summary.items(): value[str(getattr(household, key))][math.floor(rep.env.now / 24)] += 1 for key, value in rep.active_totals.items(): value[str(getattr(household, key))] += 1 if oo.record_active_paper_summary and not household.digital: for key, value in rep.active_paper_summary.items(): value[str(getattr(household, key))][math.floor(rep.env.now / 24)] += 1 for key, value in rep.active_paper_totals.items(): value[str(getattr(household, key))] += 1 household.return_received = True if oo.record_return_received: rep.output_data['Return_received'].append( oo.generic_output(rep.reps, household.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, rep.env.now)) # currently every return gets counted as a response as soon as it is received - this may need to change household.responded = True rep.total_responses += 1 household.district.total_responses += 1 # check size of output data - if over an amount, size or length write to file? if oo.record_responded: rep.output_data['Responded'].append( oo.generic_output(rep.reps, household.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, rep.env.now)) # checks size of output and writes to file if too large if (h.dict_size(rep.output_data)) > rep.max_output_file_size: h.write_output(rep.output_data, rep.output_path, rep.run) yield rep.env.timeout(0)
def co_send_letter(self, household, letter_type, delay): if oo.record_letters: self.rep.output_data[letter_type + '_sent'].append( oo.generic_output(self.rep.reps, household.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, self.env.now)) yield self.env.timeout(delay) self.env.process(household.receive_reminder(letter_type))
def non_response(self): for household in self.households: if not household.return_sent: if oo.record_non_response: self.rep.output_data['Non_response'].append( oo.generic_output(self.rep.reps, self.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, self.env.now)) yield self.env.timeout(0)
def phone_call(self): if oo.record_call: self.output_data['Call'].append(oo.generic_output(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now)) self.calls += 1 if (not self.digital and not self.paper_allowed and h.responses_to_date(self.district) < self.district.input_data['paper_trigger'] and self.paper_on_request): # so provide paper if the conditions are met... self.paper_allowed = True self.priority += 5 # lower the priority as more likely to reply # call to ask so may need to up the level of response above default here? Extra optional variable? yield self.env.process(hq.schedule_paper_drop(self, 'Call', 'pq', self.district.postal_delay)) elif isinstance(self.adviser_check(self.env.now), str): # otherwise carry on to the call centre to speak to an adviser # unless outside times advisers are available - use check times and if none returned gracefully defer yield self.env.process(self.phone_call_connect()) else: # no one available - gracefully defer - some will call back again? if oo.record_call_defer: self.output_data['Call_defer'].append(oo.generic_output(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now))
def phone_call_outcome(self, current_ad): # digital by this point so just can you convince them to reply? outcome_test = self.rnd.uniform(0, 100) conversion_dict = self.input_data['success_rate'][str(h.current_day(self))] if outcome_test <= conversion_dict[h.return_time_key(conversion_dict, self.env.now)]: yield self.env.timeout(current_ad.input_data['call_times']['success'] / 60) if oo.record_call_success: self.rep.output_data['Call_success'].append(oo.generic_output(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now)) self.resp_planned = True self.rep.adviser_store.put(current_ad) self.rep.env.process(self.household_returns(self.calc_delay())) else: yield self.env.timeout(current_ad.input_data['call_times']['failed'] / 60) if oo.record_call_failed: self.rep.output_data['Call_failed'].append(oo.generic_output(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now)) self.rep.adviser_store.put(current_ad)
def household_returns(self, delay=0): """represents the hh returning their form - not the return being counted as a response by census""" if not self.return_sent: self.return_sent = True self.resp_time = self.env.now # add to hh response event log if oo.record_return_sent: self.output_data['Return_sent'].append(oo.generic_output(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.resp_time)) if self.calc_delay() == 0: # digital self.env.process(hq.ret_rec(self, self.rep)) else: # paper start_delayed(self.env, hq.ret_rec(self, self.rep), delay) yield self.env.timeout(0) # hh does no more (without intervention)
def fu_visit_outcome(self, household): household_returns = self.household_test(household, "success_rate") if not household.return_sent and household_returns: # hh have not responded yet and respond there and then either by paper or digital. if oo.record_visit_success: self.rep.output_data['Visit_success'].append( oo.visit_output(self.rep.reps, self.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, household.visits, self.env.now)) household.resp_planned = True yield self.rep.env.process( household.household_returns(household.calc_delay())) time_worked = self.input_data['visit_times']['success'] / 60 + \ self.district.travel_dist / self.input_data["travel_speed"] if oo.record_time_summary: for key, value in self.rep.time_summary.items(): value[str(getattr(household, key))][math.floor( self.rep.env.now / 24)] += time_worked for key, value in self.rep.time_totals.items(): value[str(getattr(household, key))] += time_worked yield self.rep.env.timeout(time_worked) elif (not household.return_sent and not household_returns and h.responses_to_date( self.district) < self.district.input_data['paper_trigger'] and household.visits == household.input_data['max_visits'] and h.str2bool(household.input_data['paper_after_max_visits']) and not household.paper_allowed): # hh have not responded but do not respond as a result of the visit. # need extra here for when you fail but not at max visits? if oo.record_visit_failed: self.rep.output_data['Visit_failed'].append( oo.generic_output(self.rep.reps, household.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, self.env.now)) # leave paper in hope they respond? household.paper_allowed = True hq.schedule_paper_drop(household, 'Visit', 'pq', self.has_pq) if oo.record_paper_summary: # add to the summary of the amount of paper given for key, value in self.rep.paper_summary.items(): value[str(getattr(household, key))][0] += 1 for key, value in self.rep.paper_totals.items(): value[str(getattr(household, key))] += 1 time_worked = self.input_data['visit_times']['failed'] / 60 + \ self.district.travel_dist / self.input_data["travel_speed"] for key, value in self.rep.time_summary.items(): value[str(getattr(household, key))][math.floor( self.rep.env.now / 24)] += time_worked for key, value in self.rep.time_totals.items(): value[str(getattr(household, key))] += time_worked yield self.rep.env.timeout(time_worked) elif not household.return_sent and not household_returns: # failed but no max visits so do no more if oo.record_visit_failed: self.rep.output_data['Visit_failed'].append( oo.generic_output(self.rep.reps, household.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, self.env.now)) time_worked = self.input_data['visit_times']['failed'] / 60 + \ self.district.travel_dist / self.input_data["travel_speed"] if oo.record_time_summary: for key, value in self.rep.time_summary.items(): value[str(getattr(household, key))][math.floor( self.rep.env.now / 24)] += time_worked for key, value in self.rep.time_totals.items(): value[str(getattr(household, key))] += time_worked yield self.rep.env.timeout(time_worked)
def fu_visit_assist(self, household): da_test = self.rnd.uniform(0, 100) da_effectiveness = self.input_data['da_effectiveness'][ household.hh_type] time_worked = self.input_data['visit_times']['query'] / 60 if oo.record_time_summary: for key, value in self.rep.time_summary.items(): value[str(getattr(household, key))][math.floor( self.rep.env.now / 24)] += time_worked for key, value in self.rep.time_totals.items(): value[str(getattr(household, key))] += time_worked yield self.rep.env.timeout(time_worked) # if digital, have already responded or can use paper skip straight to the outcome of the visit if household.digital or household.return_sent or household.paper_allowed: yield self.rep.env.process(self.fu_visit_outcome(household)) # if not digital try to persuade them to complete online. elif not household.digital and da_test <= da_effectiveness: time_worked = self.input_data['visit_times']['convert'] / 60 if oo.record_time_summary: for key, value in self.rep.time_summary.items(): value[str(getattr(household, key))][math.floor( self.rep.env.now / 24)] += time_worked for key, value in self.rep.time_totals.items(): value[str(getattr(household, key))] += time_worked yield self.rep.env.timeout(time_worked) household.digital = True if oo.record_visit_convert: self.rep.output_data['Visit_convert'].append( oo.generic_output(self.rep.reps, household.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, self.env.now)) yield self.rep.env.process(self.fu_visit_outcome(household)) # not digital, do not convince to go online but allowed to use paper so go to outcome elif not household.digital and da_test > da_effectiveness and household.paper_allowed: yield self.rep.env.process(self.fu_visit_outcome(household)) # if not digital, do not convince to complete online, and trigger and max visits not reached give paper if on. elif (not household.digital and da_test > da_effectiveness and h.responses_to_date( self.district) < self.district.input_data['paper_trigger'] and household.visits == household.input_data['max_visits'] and h.str2bool(household.input_data['paper_after_max_visits'])): household.paper_allowed = True self.env.process( hq.schedule_paper_drop(household, 'Visit', 'pq', self.has_pq)) time_worked = self.input_data['visit_times']['paper'] / 60 +\ self.district.travel_dist/self.input_data["travel_speed"] if oo.record_time_summary: for key, value in self.rep.time_summary.items(): value[str(getattr(household, key))][math.floor( self.rep.env.now / 24)] += time_worked for key, value in self.rep.time_totals.items(): value[str(getattr(household, key))] += time_worked yield self.rep.env.timeout(time_worked) else: # or suggest other forms of assistance to be decided... # non implemented at present so another visit will be scheduled. if oo.record_visit_assist: self.rep.output_data['Visit_assist'].append( oo.generic_output(self.rep.reps, self.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, self.env.now)) time_worked = self.input_data['visit_times']['paper'] / 60 + \ self.district.travel_dist / self.input_data["travel_speed"] if oo.record_time_summary: for key, value in self.rep.time_summary.items(): value[str(getattr(household, key))][math.floor( self.rep.env.now / 24)] += time_worked for key, value in self.rep.time_totals.items(): value[str(getattr(household, key))] += time_worked yield self.rep.env.timeout(time_worked)
def fu_visit_contact(self, household): household.priority += 1 # automatically lower the priority of this hh after a visit household.visits += 1 if oo.record_visit_summary: for key, value in self.rep.visit_summary.items(): value[str(getattr(household, key))][math.floor( self.rep.env.now / 24)] += 1 for key, value in self.rep.visit_totals.items(): value[str(getattr(household, key))] += 1 if oo.record_visit: self.rep.output_data['Visit'].append( oo.visit_output(self.rep.reps, self.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, household.visits, self.env.now)) if household.responded: if oo.record_visit_wasted: self.rep.output_data['Visit_wasted'].append( oo.generic_output(self.rep.reps, self.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, self.env.now)) elif household.resp_planned: if oo.record_visit_unnecessary: self.rep.output_data['Visit_unnecessary'].append( oo.generic_output(self.rep.reps, self.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, self.env.now)) household_is_in = self.household_test(household, "contact_rate") if household_is_in: if oo.record_visit_contact: self.rep.output_data['Visit_contact'].append( oo.generic_output(self.rep.reps, self.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, self.env.now)) # household.visits_contacted += 1 yield self.rep.env.process(self.fu_visit_assist(household)) elif (not household_is_in and household.visits == household.input_data['max_visits'] and h.str2bool(household.input_data['paper_after_max_visits'])): household.paper_allowed = True hq.schedule_paper_drop(household, 'Visit', 'pq', self.has_pq) time_worked = self.input_data["visit_times"]["out_paper"] / 60 +\ self.district.travel_dist / self.input_data["travel_speed"] if oo.record_time_summary: for key, value in self.rep.time_summary.items(): value[str(getattr(household, key))][math.floor( self.rep.env.now / 24)] += time_worked for key, value in self.rep.time_totals.items(): value[str(getattr(household, key))] += time_worked yield self.rep.env.timeout(time_worked) else: # out - add drop off of a note if oo.record_visit_out: self.rep.output_data['Visit_out'].append( oo.generic_output(self.rep.reps, self.district.district, household.la, household.lsoa, household.digital, household.hh_type, household.hh_id, self.env.now)) self.env.process( hq.schedule_paper_drop(household, 'Visit', 'postcard', self.has_postcard)) time_worked = self.input_data["visit_times"][ "out"] / 60 + self.district.travel_dist / self.input_data[ "travel_speed"] if oo.record_time_summary: for key, value in self.rep.time_summary.items(): value[str(getattr(household, key))][math.floor( self.rep.env.now / 24)] += time_worked for key, value in self.rep.time_totals.items(): value[str(getattr(household, key))] += time_worked yield self.rep.env.timeout(time_worked)
def create_households(self): list_of_hh_types = sorted(list(self.input_data['households'].keys())) for hh_type in list_of_hh_types: # get hh data for current type hh_input_data = self.input_data['households'][hh_type] for i in range(hh_input_data['number']): # if allowed paper use different paper prop to if not... if h.str2bool(hh_input_data['paper_allowed']): paper_prop = hh_input_data['paper_prop_pf'] else: paper_prop = hh_input_data['paper_prop_df'] # set if digital here? hh_digital = h.set_preference(paper_prop, self.rnd) # define where the hh is located hh_geog = self.return_household_geog( hh_input_data['cca_makeup'], hh_type, hh_digital) self.total_households += 1 # determine initial HH action hh_action = self.initial_action(hh_input_data, self.first_interaction, hh_type, hh_geog, hh_digital) if hh_action.digital: time_to_use = hh_action.time + hh_input_data['delay'][ 'digital'] else: time_to_use = hh_action.time + hh_input_data['delay'][ 'paper'] if h.str2bool(hh_input_data['paper_allowed'] ) and oo.record_paper_summary: # add to the summary of the amount of paper given for key, value in self.rep.paper_summary.items(): value[str(getattr(hh_geog, key))][0] += 1 for key, value in self.rep.paper_totals.items(): value[str(getattr(hh_geog, key))] += 1 if hh_action.type == 'early': # don't need an instance of a household just directly record response/return at correct time self.rep.total_responses += 1 self.total_responses += 1 if oo.record_responded: self.rep.output_data['Return_sent'].append( oo.generic_output(self.rep.reps, self.district, hh_geog.la, hh_geog.lsoa, hh_action.digital, hh_type, self.rep.total_hh, hh_action.time)) if oo.record_return_received: self.rep.output_data['Return_received'].append( oo.generic_output(self.rep.reps, self.district, hh_geog.la, hh_geog.lsoa, hh_action.digital, hh_type, self.rep.total_hh, time_to_use)) # add household to summary of total responses if oo.record_active_summary: for key, value in self.rep.active_summary.items(): value[str(getattr(hh_geog, key))][math.floor( time_to_use / 24)] += 1 for key, value in self.rep.active_totals.items(): value[str(getattr(hh_geog, key))] += 1 # if paper and early also need to record??? if oo.record_active_paper_summary and not hh_action.digital: for key, value in self.rep.active_paper_summary.items( ): value[str(getattr(hh_geog, key))][math.floor( time_to_use / 24)] += 1 for key, value in self.rep.active_paper_totals.items(): value[str(getattr(hh_geog, key))] += 1 if oo.record_responded: self.rep.output_data['Responded'].append( oo.generic_output(self.rep.reps, self.district, hh_geog.la, hh_geog.lsoa, hh_action.digital, hh_type, self.rep.total_hh, time_to_use)) else: # create a household instance passing initial state self.households.append( household.Household(self.rep, self.env, self, self.rep.total_hh, hh_type, hh_input_data, hh_action, hh_geog.la, hh_geog.lsoa)) # if self.rep.reps == 1: if self.rep.reps > 0 and oo.record_hh_record: self.rep.output_data['hh_record'].append( oo.hh_record(self.rep.reps, self.district, hh_geog.la, hh_geog.lsoa, hh_type, hh_action.type, hh_action.digital, hh_input_data['paper_allowed'], hh_action.time)) if hh_action.type not in ['do_nothing', 'help' ] and oo.record_passive_summary: for key, value in self.rep.passive_summary.items(): value[str(getattr(hh_geog, key))][math.floor( time_to_use / 24)] += 1 for key, value in self.rep.passive_totals.items(): value[str(getattr(hh_geog, key))] += 1 self.rep.total_hh += 1
def receive_reminder(self, reminder_type): # a reminder has been received. This determines the outcome fo that reminder and if it was worthwhile. if oo.record_reminder_received: self.rep.output_data[reminder_type + '_received'].append(oo.reminder_received(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now, reminder_type)) # set resp according to type of hh and reminder if not self.resp_planned and reminder_type == 'pq' and self.engaged: self.paper_allowed = True self.resp_level = 100 # so this assumes if you provide paper to those engaged they will respond elif not self.resp_planned: behaviour = self.default_behaviour() # and get relevant figures try: response_data = self.input_data["behaviours"][reminder_type][behaviour] except: pass self.resp_level = response_data["response"] if self.rep.total_ad_instances > 0: self.help_level = response_data["help"] else: self.help_level = 0 # recorded if wasted, unnecessary or successful if self.responded: if oo.record_reminder_wasted: self.rep.output_data[reminder_type + '_wasted'].append(oo.reminder_wasted(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now, reminder_type)) elif self.resp_planned: if oo.record_reminder_unnecessary: self.rep.output_data[reminder_type + '_unnecessary'].append(oo.reminder_unnecessary(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now, reminder_type)) # now move on to the relevant action based on extracted values reminder_test = self.rnd.uniform(0, 100) if not self.resp_planned and reminder_test <= self.resp_level: if oo.record_reminder_success: self.rep.output_data[reminder_type + '_success'].append(oo.reminder_success(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now, reminder_type)) # change to a start delayed at appropriate time depending on day.... delay = h.get_time_of_return(self.env.now, self.rep) # yield self.env.process(self.household_returns(self.calc_delay())) ########## # if this is a pq, but a digital household, calculate if the household retunrs via paper or digital? # Or assume use preferred method for now? # could use paper first paper prop to set this? so set digital to true or false... ########## start_delayed(self.env, self.household_returns(self.calc_delay()), delay) yield self.env.timeout(0) elif not self.resp_planned and (self.resp_level < reminder_test <= self.resp_level + self.help_level): # call for help...needs to be based on appropriate distribution...not a hardcoded uniform function! # also may not do this if intend to respond? yield self.env.timeout(self.rnd.uniform(0, 8)) if oo.record_do_nothing: self.output_data[reminder_type + '_contact'].append(oo.generic_output(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now)) yield self.env.process(self.contact()) else: # nowt if oo.record_do_nothing: self.output_data[reminder_type + '_failed'].append(oo.generic_output(self.rep.reps, self.district.district, self.la, self.lsoa, self.digital, self.hh_type, self.hh_id, self.env.now)) yield self.env.timeout(0)