def test_calculate_sla_by_practice_calculates_sla_given_transfers_for_2_practices( ): practice_list = [ PracticeDetails(asids=["121212121212"], ods_code="A12345", name="A Practice"), PracticeDetails(asids=["343434343434"], ods_code="B12345", name="Another Practice"), ] transfers = [ build_transfer( requesting_practice_asid="121212121212", sla_duration=timedelta(days=8, hours=1, minutes=10), ), build_transfer( requesting_practice_asid="343434343434", sla_duration=timedelta(days=4, hours=1, minutes=10), ), build_transfer( requesting_practice_asid="121212121212", sla_duration=timedelta(days=0, hours=1, minutes=10), ), build_transfer( requesting_practice_asid="343434343434", sla_duration=timedelta(days=8, hours=1, minutes=10), ), build_transfer( requesting_practice_asid="343434343434", sla_duration=timedelta(days=5, hours=1, minutes=10), ), ] expected = [ PracticeMetrics( ods_code="A12345", name="A Practice", integrated=IntegratedPracticeMetrics(transfer_count=2, within_3_days=1, within_8_days=0, beyond_8_days=1), ), PracticeMetrics( ods_code="B12345", name="Another Practice", integrated=IntegratedPracticeMetrics(transfer_count=3, within_3_days=0, within_8_days=2, beyond_8_days=1), ), ] actual = calculate_sla_by_practice(practice_list, transfers) actual_sorted = sorted(actual, key=lambda p: p.ods_code) assert actual_sorted == expected
def test_excludes_transfers_missing_sla_duration(): integrated_transfer_1 = build_transfer(status=TransferStatus.INTEGRATED) integrated_transfer_2 = build_transfer(status=TransferStatus.INTEGRATED, sla_duration=None) failed_transfer = build_transfer(status=TransferStatus.FAILED) transfers = [integrated_transfer_1, integrated_transfer_2, failed_transfer] actual = filter_for_successful_transfers(transfers) expected = [integrated_transfer_1] assert list(actual) == expected
def test_excludes_failed_transfers(): integrated_transfer_1 = build_transfer(status=TransferStatus.INTEGRATED) integrated_transfer_2 = build_transfer(status=TransferStatus.INTEGRATED) failed_transfer = build_transfer(status=TransferStatus.FAILED) transfers = [integrated_transfer_1, integrated_transfer_2, failed_transfer] actual = filter_for_successful_transfers(transfers) expected = [integrated_transfer_1, integrated_transfer_2] assert list(actual) == expected
def test_groups_by_asid_given_single_practice_and_transfers_from_the_same_practice( ): practices = [ PracticeDetails(asids=["121212121212"], ods_code="A12345", name=a_string()) ] transfers = [ build_transfer(requesting_practice_asid="121212121212"), build_transfer(requesting_practice_asid="121212121212"), ] actual = calculate_sla_by_practice(practices, transfers) _assert_has_ods_codes(actual, {"A12345"})
def test_table_has_correct_schema(): transfers = [build_transfer()] expected_schema = pa.schema([ ("conversation_id", pa.string()), ("sla_duration", pa.uint64()), ("requesting_practice_asid", pa.string()), ("requesting_practice_ods_code", pa.string()), ("sending_practice_asid", pa.string()), ("sending_practice_ods_code", pa.string()), ("requesting_supplier", pa.string()), ("sending_supplier", pa.string()), ("sender_error_codes", pa.list_(pa.int64())), ("final_error_codes", pa.list_(pa.int64())), ("intermediate_error_codes", pa.list_(pa.int64())), ("status", pa.string()), ("failure_reason", pa.string()), ("date_requested", pa.timestamp("us")), ("date_completed", pa.timestamp("us")), ("last_sender_message_timestamp", pa.timestamp("us")), ]) table = convert_transfers_to_table(transfers) actual_schema = table.schema assert actual_schema == expected_schema
def test_converts_multiple_rows_into_table(): transfers = [ build_transfer(conversation_id="123", final_error_codes=[1]), build_transfer(conversation_id="456", final_error_codes=[2]), build_transfer(conversation_id="789", final_error_codes=[3]), ] expected_columns = { "conversation_id": ["123", "456", "789"], "final_error_codes": [[1], [2], [3]], } table = convert_transfers_to_table(transfers) actual_columns = table.select(["conversation_id", "final_error_codes"]).to_pydict() assert actual_columns == expected_columns
def test_date_completed_is_converted_to_column_when_missing(): transfer = build_transfer(date_completed=None) expected_date_column = {"date_completed": [None]} table = convert_transfers_to_table([transfer]) actual_date_column = table.select(["date_completed"]).to_pydict() assert actual_date_column == expected_date_column
def test_sla_duration_is_converted_to_column_when_missing(): transfer = build_transfer(sla_duration=None) expected_sla_duration_column = {"sla_duration": [None]} table = convert_transfers_to_table([transfer]) actual_sla_duration_column = table.select(["sla_duration"]).to_pydict() assert actual_sla_duration_column == expected_sla_duration_column
def test_final_error_code_is_converted_to_column(): transfer = build_transfer(final_error_code=5) expected_error_code_column = {"final_error_code": [5]} table = convert_transfers_to_table([transfer]) actual_error_code_column = table.select(["final_error_code"]).to_pydict() assert actual_error_code_column == expected_error_code_column
def test_sender_error_code_is_converted_to_column(): transfer = build_transfer(sender_error_code=10) expected_error_code_column = {"sender_error_code": [10]} table = convert_transfers_to_table([transfer]) actual_error_code_column = table.select(["sender_error_code"]).to_pydict() assert actual_error_code_column == expected_error_code_column
def test_sending_practice_asid_is_converted_to_column(): transfer = build_transfer(sending_practice_asid="001112345678") expected_asid_column = {"sending_practice_asid": ["001112345678"]} table = convert_transfers_to_table([transfer]) actual_asid_column = table.select(["sending_practice_asid"]).to_pydict() assert actual_asid_column == expected_asid_column
def test_excludes_pending_transfers(): pending_transfer = build_transfer(status=TransferStatus.PENDING) transfers = [pending_transfer] actual = filter_for_successful_transfers(transfers) expected = [] assert list(actual) == expected
def test_groups_by_asid_given_two_practices_and_two_transfers_from_different_practices( ): practices = [ PracticeDetails(asids=["121212121212"], ods_code="A12345", name=a_string()), PracticeDetails(asids=["343434343434"], ods_code="X67890", name=a_string()), ] transfers = [ build_transfer(requesting_practice_asid="121212121212"), build_transfer(requesting_practice_asid="343434343434"), ] actual = calculate_sla_by_practice(practices, transfers) _assert_has_ods_codes(actual, {"A12345", "X67890"})
def test_conversation_id_is_converted_to_column(): transfer = build_transfer(conversation_id="123") expected_conversation_column = {"conversation_id": ["123"]} table = convert_transfers_to_table([transfer]) actual_conversation_column = table.select(["conversation_id"]).to_pydict() assert actual_conversation_column == expected_conversation_column
def test_final_error_codes_are_converted_to_column_when_missing(): transfer = build_transfer(final_error_codes=[]) expected_error_code_column: dict = {"final_error_codes": [[]]} table = convert_transfers_to_table([transfer]) actual_error_code_column = table.select(["final_error_codes"]).to_pydict() assert actual_error_code_column == expected_error_code_column
def test_status_is_converted_to_column(): transfer = build_transfer(status=TransferStatus.INTEGRATED) expected_status_column = {"status": ["INTEGRATED"]} table = convert_transfers_to_table([transfer]) actual_status_column = table.select(["status"]).to_pydict() assert actual_status_column == expected_status_column
def test_sender_error_codes_are_converted_to_column_when_missing(): transfer = build_transfer(sender_error_codes=[None]) expected_error_code_column = {"sender_error_codes": [[None]]} table = convert_transfers_to_table([transfer]) actual_error_code_column = table.select(["sender_error_codes"]).to_pydict() assert actual_error_code_column == expected_error_code_column
def test_calculates_correct_national_metrics_given_series_of_messages(): sla_duration_within_3_days = timedelta(seconds=THREE_DAYS_IN_SECONDS) sla_duration_within_8_days = timedelta(seconds=EIGHT_DAYS_IN_SECONDS) sla_duration_beyond_8_days = timedelta(seconds=EIGHT_DAYS_IN_SECONDS + 1) transfers = [ build_transfer(status=TransferStatus.PENDING), build_transfer(status=TransferStatus.PENDING_WITH_ERROR), build_transfer(status=TransferStatus.INTEGRATED, sla_duration=sla_duration_within_3_days), build_transfer(status=TransferStatus.INTEGRATED, sla_duration=sla_duration_within_8_days), build_transfer(status=TransferStatus.INTEGRATED, sla_duration=sla_duration_within_8_days), build_transfer(status=TransferStatus.INTEGRATED, sla_duration=sla_duration_beyond_8_days), build_transfer(status=TransferStatus.INTEGRATED, sla_duration=sla_duration_beyond_8_days), build_transfer(status=TransferStatus.INTEGRATED, sla_duration=sla_duration_beyond_8_days), build_transfer(status=TransferStatus.FAILED), ] time_range = DateTimeRange(start=datetime(2019, 12, 1, tzinfo=UTC), end=datetime(2020, 1, 1, tzinfo=UTC)) current_datetime = datetime.now(tzutc()) expected_national_metrics = MonthlyNationalMetrics( transfer_count=9, integrated=IntegratedMetrics( transfer_percentage=66.67, transfer_count=6, within_3_days=1, within_8_days=2, beyond_8_days=3, ), failed=FailedMetrics(transfer_count=1, transfer_percentage=11.11), pending=PendingMetrics(transfer_count=2, transfer_percentage=22.22), paper_fallback=PaperFallbackMetrics(transfer_count=6, transfer_percentage=66.67), year=2019, month=12, ) expected = NationalMetricsPresentation(generated_on=current_datetime, metrics=[expected_national_metrics]) actual = calculate_national_metrics_data(transfers, time_range) assert actual == expected
def test_intermediate_error_codes_are_converted_to_column_when_empty(): transfer = build_transfer(intermediate_error_codes=[]) expected_error_code_column: dict = {"intermediate_error_codes": [[]]} table = convert_transfers_to_table([transfer]) actual_error_code_column = table.select(["intermediate_error_codes" ]).to_pydict() assert actual_error_code_column == expected_error_code_column
def test_intermediate_error_codes_is_converted_to_column(): transfer = build_transfer(intermediate_error_codes=[6]) expected_error_code_column = {"intermediate_error_codes": [[6]]} table = convert_transfers_to_table([transfer]) actual_error_code_column = table.select(["intermediate_error_codes" ]).to_pydict() assert actual_error_code_column == expected_error_code_column
def test_sending_practice_ods_is_converted_to_column(): transfer = build_transfer(sending_practice=Practice( asid="001112345678", supplier="supplier", ods_code="A12")) expected_ods_column = {"sending_practice_ods_code": ["A12"]} table = convert_transfers_to_table([transfer]) actual_ods_column = table.select(["sending_practice_ods_code"]).to_pydict() assert actual_ods_column == expected_ods_column
def test_sla_duration_is_rounded_to_integer(): transfer = build_transfer(sla_duration=timedelta( days=2, hours=1, minutes=3, seconds=6, milliseconds=1)) expected_sla_duration_column = {"sla_duration": [176586]} table = convert_transfers_to_table([transfer]) actual_sla_duration_column = table.select(["sla_duration"]).to_pydict() assert actual_sla_duration_column == expected_sla_duration_column
def test_requesting_practice_asid_is_converted_to_column(): transfer = build_transfer(requesting_practice=Practice( asid="003212345678", supplier="supplier", ods_code="A12")) expected_asid_column = {"requesting_practice_asid": ["003212345678"]} table = convert_transfers_to_table([transfer]) actual_asid_column = table.select(["requesting_practice_asid"]).to_pydict() assert actual_asid_column == expected_asid_column
def test_status_is_converted_to_column(): integrated_transfer_outcome = TransferOutcome( status=TransferStatus.INTEGRATED_ON_TIME, failure_reason=None) transfer = build_transfer(transfer_outcome=integrated_transfer_outcome) expected_status_column = {"status": ["Integrated on time"]} table = convert_transfers_to_table([transfer]) actual_status_column = table.select(["status"]).to_pydict() assert actual_status_column == expected_status_column
def test_date_completed_is_converted_to_column(): transfer = build_transfer( date_completed=datetime(year=2020, month=7, day=28, hour=17)) expected_date_column = { "date_completed": [datetime(year=2020, month=7, day=28, hour=17)] } table = convert_transfers_to_table([transfer]) actual_date_column = table.select(["date_completed"]).to_pydict() assert actual_date_column == expected_date_column
def test_write_transfers_correctly_writes_multiple_rows(): mock_s3 = MockS3() s3_data_manager = S3DataManager(mock_s3) io = TransferClassifierIO(s3_data_manager) transfers = [ build_transfer(conversation_id="a"), build_transfer(conversation_id="b"), build_transfer(conversation_id="c"), ] io.write_transfers(transfers=transfers, s3_uri="s3://a_bucket/multi_row.parquet", metadata=_SOME_METADATA) expected_conversation_ids = ["a", "b", "c"] actual_conversation_ids = (mock_s3.object( "a_bucket", "multi_row.parquet").read_parquet().to_pydict().get("conversation_id")) assert actual_conversation_ids == expected_conversation_ids
def test_counts_both_asids_for_practice_with_two_asids(): practice_list = [ PracticeDetails(asids=["121212121212", "343434343434"], ods_code="A12345", name=a_string()) ] transfers = [ build_transfer( requesting_practice_asid="343434343434", sla_duration=timedelta(hours=1, minutes=10), ), build_transfer( requesting_practice_asid="121212121212", sla_duration=timedelta(days=5, hours=1, minutes=10), ), ] actual = calculate_sla_by_practice(practice_list, transfers) _assert_first_summary_has_sla_counts(actual, within_3_days=1, within_8_days=1, beyond_8_days=0)
def test_returns_sum_of_all_integrated_transfers(): practice_list = [ PracticeDetails(asids=["121212121212"], ods_code="A12345", name=a_string()) ] transfers = [ build_transfer( requesting_practice_asid="121212121212", sla_duration=timedelta(days=0, hours=1, minutes=10), ), build_transfer( requesting_practice_asid="121212121212", sla_duration=timedelta(days=7, hours=1, minutes=10), ), build_transfer( requesting_practice_asid="121212121212", sla_duration=timedelta(days=10, hours=1, minutes=10), ), ] actual = list(calculate_sla_by_practice(practice_list, transfers)) assert actual[0].integrated.transfer_count == 3
def test_calculate_sla_by_practice_calculates_sla_given_one_transfer_within_3_days( ): practice_list = [ PracticeDetails(asids=["121212121212"], ods_code="A12345", name=a_string()) ] transfer = build_transfer(requesting_practice_asid="121212121212", sla_duration=timedelta(hours=1, minutes=10)) actual = calculate_sla_by_practice(practice_list, [transfer]) _assert_first_summary_has_sla_counts(actual, within_3_days=1, within_8_days=0, beyond_8_days=0)
def test_write_transfers_writes_metadata(): mock_s3 = MockS3() s3_data_manager = S3DataManager(mock_s3) metadata = {a_string(): a_string()} io = TransferClassifierIO(s3_data_manager) io.write_transfers(transfers=[build_transfer()], s3_uri="s3://a_bucket/some_data.parquet", metadata=metadata) actual_meta_data = mock_s3.object("a_bucket", "some_data.parquet").get_metadata() assert actual_meta_data == metadata