def test_absolute_garbage(s: str) -> None: """ Test that none of the filters raise errors when absolute garbage input strings are passed in. """ print(s) cf = CustomerFilter() df = DurationFilter() lf = LocationFilter() # test that nothing crashes cf.apply(CUSTOMERS, CALL_LIST, s) df.apply(CUSTOMERS, CALL_LIST, s) lf.apply(CUSTOMERS, CALL_LIST, s)
def test_location_filter(x1: float, y1: float, x2: float, y2: float): """ Test that LocationFilter works in the general case - with valid inputs, aka coordinates within the range of the map. """ filt = LocationFilter() results = filt.apply(CUSTOMERS, CALL_LIST, f'{x1}, {y1}, {x2}, {y2}') if x1 > x2 or y1 > y2: assert results == CALL_LIST else: for call in results: src_in_range = x1 <= call.src_loc[0] <= x2 \ and y1 <= call.src_loc[1] <= y2 dst_in_range = x1 <= call.dst_loc[0] <= x2 \ and y1 <= call.dst_loc[1] <= y2 assert src_in_range or dst_in_range
def test_location_filter_with_large_data() -> None: """ Test the functionality of the location filters. We are only giving you a couple of tests here, you should expand both the dataset and the tests for the different types of applicable filters """ # loading up the large data input_dictionary = import_data() customers = create_customers(input_dictionary) process_event_history(input_dictionary, customers) # Populate the list of calls: calls = [] for cust in customers: hist = cust.get_history() # only look at outgoing calls, we don't want to duplicate calls in test calls.extend(hist[0]) # The different filters we are testing filters = [CustomerFilter(), LocationFilter()] # These are the inputs to each of the above filters in order. # Each list is a test for this input to the filter filter_strings = [ # contents: non-existent id, valid id, valid id ["5555", "5524", "9210"], # contents: loc of one call, max and min, one cord out, letters, valid loc but no calls [ "-79.54717029563305, 43.58020061333403, -79.54717029563303, 43.58020061333405", # location of one call "-79.697878, 43.576959, -79.196382, 43.799568", # entire map "-80.697877, 43.576960, -79.196383, 43.799567", # one coordinate in not within range "hellolol, erferer, fefergerger, ferereeev", # isalpaha == true "-79.697878, 43.6882635, -79.196382, 43.799568", # half the map (this one took me hours to count) "-79.54717029563305,43.58020061333403,-79.54717029563303,43.58020061333405", # location of one call but no spaces "-79.54717029563305 , 43.58020061333403 , -79.54717029563303, 43.58020061333405", # ^ spaces "-79.196382, 43.799568, -79.697878, 43.576959", # both cross "-79.296382, 43.576959, -79.597878, 43.799568", # x coords cross "-79.697878, 43.576959, -79.196382, 43.499568", # y coords cross "-80.697877, 69.576960, -89.196383, 69.799567", # all coords not within range "hellolol, erferer, fefergergerferereeev", # alpha + nums "#@%#@%#@%,#%@#%@#%,#%#@%#@%#@$%#@%", # symbols "", # empty "SDF(*@$)(*&#!)(*&#HFLKDSJF:LDSJFLKJDSF", # no commas " " # just spaces....... ] ] # These are the expected outputs from the above filter application # onto the full list of calls expected_return_lengths = [[1000, 45, 33], [ 1, 1000, 1000, 1000, 755, 1000, 1, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000 ]] for i in range(len(filters)): for j in range(len(filter_strings[i])): result = filters[i].apply(customers, calls, filter_strings[i][j]) assert len(result) == expected_return_lengths[i][j]
def handle_window_events(self, customers: List[Customer], drawables: List[Call]) \ -> List[Call]: """Handle any user events triggered through the pygame window. The <drawables> are the objects currently displayed, while the <customers> list contains all customers from the input data. Return a new list of Calls, according to user input actions. """ new_drawables = drawables for event in pygame.event.get(): if event.type == pygame.QUIT: self._quit = True elif event.type == pygame.KEYDOWN: f = None num_threads = 1 if event.unicode == "d": f = DurationFilter() elif event.unicode == "l": f = LocationFilter() elif event.unicode == "c": f = CustomerFilter() elif event.unicode == "r": f = ResetFilter() num_threads = 1 if f is not None: def result_wrapper(fun: Callable[ [List[Customer], List[Call], str], List[Call]], customers: List[Customer], data: List[Call], filter_string: str, res: List) -> None: """A final wrapper to return the result of the operation """ res.append(fun(customers, data, filter_string)) def threading_wrapper(customers: List[Customer], data: List[Call], filter_string: str) -> List[Call]: """A wrapper for the application of filters with threading """ chunk_sz_calls = math.ceil( (len(data) + num_threads - 1) / num_threads) print("Num_threads:", num_threads) print("Chunk_calls:", chunk_sz_calls) threads = [] results = [] for i in range(num_threads): res = [] results.append(res) t = threading.Thread( target=result_wrapper, args=(f.apply, customers, data[i * chunk_sz_calls:(i + 1) * chunk_sz_calls], filter_string, res)) t.daemon = True t.start() threads.append(t) # f.apply(customers, data, filter_string) # Wait to finish for t in threads: t.join() # Now reconstruct the data new_data = [] for res in results: new_data.extend(res[0]) return new_data new_drawables = self.entry_window(str(f), customers, drawables, threading_wrapper) # Perform the billing for a selected customer: if event.unicode == "m": try: def get_customer(customers: List[Customer], found_customer: List[Customer], input_string: str) -> None: """ A helper to find the customer specified in the input string appends to the found_customer the matching customer """ try: for c in customers: if c.get_id() == int(input_string): found_customer.append(c) except ValueError: pass customer = [] self.entry_window( "Generate the bill for the customer " "with ID:", customers, customer, get_customer) if len(customer) == 0: raise ValueError # Just want to return the parsed input string def get_input_date(customer: List[Customer], drawables: List[Call], input_string: str) \ -> Optional[List[int]]: """ A helper to get the input date """ try: return [ int(s.strip()) for s in input_string.split(',') ] except ValueError: return None date = self.entry_window( "Bill month and year: " "month, year", customers, drawables, get_input_date) if date is None or date == ([], []): raise ValueError customer[0].print_bill(date[0], date[1]) except ValueError: print("ERROR: bad formatting for input string") except IndexError: print("Customer not found") elif event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: self._mouse_down = True elif event.button == 4: self._map.zoom(-0.1) elif event.button == 5: self._map.zoom(0.1) elif event.type == pygame.MOUSEBUTTONUP: self._mouse_down = False elif event.type == pygame.MOUSEMOTION: if self._mouse_down: self._map.pan(pygame.mouse.get_rel()) else: pygame.mouse.get_rel() return new_drawables
def test_combined_filters() -> None: log = create_task4_log() customers = create_customers(log) process_event_history(log, customers) all_calls = [] for c in customers: hist = c.get_history() all_calls.extend(hist[0]) fil = LocationFilter() filtered = fil.apply(customers, all_calls, f'{x2}, {y3}, {x2}, {y3}') fil = DurationFilter() filtered = fil.apply(customers, filtered, f'G{69 * 60 - 1}') filtered = fil.apply(customers, filtered, f'L{69 * 60 + 1}') assert len(filtered) == 3 count = 0 for call in filtered: assert call.src_number == '001-3111' if call.time.month == 1: count += 1 assert count == 2 fil = CustomerFilter() filtered = fil.apply(customers, all_calls, "1020") fil = DurationFilter() filtered = fil.apply(customers, filtered, f'G{10 * 60 - 1}') filtered = fil.apply(customers, filtered, f'L{130 * 60 + 1}') for call in filtered: assert 10 * 60 - 1 < call.duration < 130 * 60 + 1 print( f'src: {call.src_number}, dst: {call.dst_number}, dur: {call.duration}' ) assert len(filtered) == 23 + 11 + 6 fil = LocationFilter() filtered = fil.apply(customers, all_calls, f'{x3}, {y2}, {x3}, {y2}') #2101 fil = CustomerFilter() filtered = fil.apply(customers, filtered, "1002") assert len(filtered) == 3 * 2 * 3 for call in filtered: assert call.src_number[4:] == '1002' or call.src_number[4:] == '2101' assert fil.apply(customers, filtered, "3111") == [] fil = DurationFilter() assert fil.apply(customers, filtered, 'L60') == []
def test_location_filter() -> None: log = create_pure_log() customers = create_customers(log) process_event_history(log, customers) all_calls = [] for c in customers: hist = c.get_history() all_calls.extend(hist[0]) rx = (x2 - x1) / 4 ry = (y1 - y2) / 4 fil = LocationFilter() MIN_LONGITUDE = -79.697878 MAX_LONGITUDE = -79.196382 MIN_LATITUDE = 43.576959 MAX_LATITUDE = 43.799568 invalid_inputs = [ '', f'{MIN_LONGITUDE}, {MIN_LATITUDE},{MAX_LONGITUDE},{MAX_LATITUDE}', f'-79.698, {MIN_LATITUDE}, {MAX_LONGITUDE}, {MAX_LATITUDE}', f'{MIN_LONGITUDE}, 43.576, {MAX_LONGITUDE}, {MAX_LATITUDE}', f'{MIN_LONGITUDE}, {MIN_LATITUDE}, -79.195, {MAX_LATITUDE}', f'{MIN_LONGITUDE}, {MIN_LATITUDE}, {MAX_LONGITUDE}, 43.8', f'{MIN_LONGITUDE},{MIN_LATITUDE}, -79.54, {MAX_LATITUDE}', f'{MIN_LONGITUDE}, {MIN_LATITUDE}, {MAX_LATITUDE}', f'-79.6, 43.60, -79.2, 43.75, -79.5', 'klsjdfohg[we', ' ' ] for input in invalid_inputs: filtered = fil.apply(customers, all_calls, input) assert filtered == all_calls for key in loc.keys(): x = loc[key][0] y = loc[key][1] fil_string = f'{x - rx}, {y - ry}, {x + rx}, {y + ry}' filtered = fil.apply(customers, all_calls, fil_string) lines_in_area = [] if key == 3111: assert len(filtered) == (24 * 3 + 6) * 3 else: assert len(filtered) == 27 * 2 * 3 for cust in customers: if cust.get_id() == key: lines_in_area = cust.get_phone_numbers() break for call in filtered: assert call.src_number in lines_in_area \ or call.dst_number in lines_in_area assert loc[int(call.src_number[4:])] == call.src_loc \ or loc[int(call.dst_number[4:])] == call.dst_loc fil_string = f'{x1 - rx}, {y2 - ry}, {x1 + rx}, {y1 + ry}' filtered = fil.apply(customers, all_calls, fil_string) lines_in_area = ['100-1200', '200-1200', '100-2110', '010-2110'] assert len(filtered) == (11 * 4 * 2 + 12) * 3 for call in filtered: assert call.src_number in lines_in_area \ or call.dst_number in lines_in_area assert loc[int(call.src_number[4:])] == call.src_loc \ or loc[int(call.dst_number[4:])] == call.dst_loc fil_string = f'{x1}, {y2}, {x2}, {y1}' filtered = fil.apply(customers, all_calls, fil_string) lines_in_area = [ '100-1200', '200-1200', '100-2110', '010-2110', '010-1020', '020-1020', '010-2011', '001-2011' ] assert len(filtered) == (7 * 8 * 3) * 3 for call in filtered: assert call.src_number in lines_in_area \ or call.dst_number in lines_in_area assert loc[int(call.src_number[4:])] == call.src_loc \ or loc[int(call.dst_number[4:])] == call.dst_loc