예제 #1
0
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)
예제 #2
0
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]
예제 #4
0
    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
예제 #5
0
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') == []
예제 #6
0
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