def test_produces_sla_given_failure_with_conflicting_acks_and_duplicate_ehrs():
    failed_acknowledgement_datetime = a_datetime(year=2020,
                                                 month=6,
                                                 day=1,
                                                 hour=13,
                                                 minute=52,
                                                 second=0)
    conversation = Gp2gpConversation(
        messages=test_cases.
        integration_failed_with_conflicting_acks_and_duplicate_ehrs(
            request_completed_time=a_datetime(year=2020,
                                              month=6,
                                              day=1,
                                              hour=12,
                                              minute=42,
                                              second=0),
            ehr_acknowledge_time=failed_acknowledgement_datetime,
        ),
        probe=mock_gp2gp_conversation_observability_probe,
    )
    mock_lookup = Mock()

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)

    expected_sla_duration = timedelta(hours=1, minutes=10)

    assert actual.sla_duration == expected_sla_duration
    assert actual.date_completed == failed_acknowledgement_datetime
def test_produces_sla_of_successful_conversation():
    conversation = build_mock_gp2gp_conversation(
        request_completed_time=a_datetime(year=2020,
                                          month=6,
                                          day=1,
                                          hour=12,
                                          minute=42,
                                          second=0),
        final_acknowledgement_time=a_datetime(year=2020,
                                              month=6,
                                              day=1,
                                              hour=13,
                                              minute=52,
                                              second=0),
    )
    mock_lookup = Mock()

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)

    expected_sla_duration = timedelta(hours=1, minutes=10)

    assert actual.sla_duration == expected_sla_duration
def test_produces_last_sender_message_timestamp_given_request_acked_successfully(
):
    request_acknowledged_date = a_datetime(year=2020,
                                           month=6,
                                           day=1,
                                           hour=12,
                                           minute=42,
                                           second=0)

    conversation = Gp2gpConversation(
        messages=test_cases.request_acknowledged_successfully(
            sender_ack_time=request_acknowledged_date),
        probe=mock_gp2gp_conversation_observability_probe,
    )
    mock_lookup = Mock()

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)
    expected = request_acknowledged_date
    assert actual.last_sender_message_timestamp == expected
def test_produces_last_sender_message_timestamp_from_request_completed_before_integration_only(
):
    request_completed_date = a_datetime(year=2020,
                                        month=6,
                                        day=1,
                                        hour=12,
                                        minute=42,
                                        second=0)

    messages = test_cases.ehr_integrated_with_duplicate_having_second_sender_ack_after_integration(
        request_completed_time=request_completed_date)

    conversation = Gp2gpConversation(
        messages=messages,
        probe=mock_gp2gp_conversation_observability_probe,
    )
    mock_lookup = Mock()

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)

    expected_last_sender_message_timestamp = request_completed_date

    assert actual.last_sender_message_timestamp == expected_last_sender_message_timestamp
def test_produces_last_sender_message_timestamp_given_an_integrated_ontime_transfer_with_copcs(
):
    request_completed_date = a_datetime(year=2020,
                                        month=6,
                                        day=1,
                                        hour=12,
                                        minute=42,
                                        second=0)

    conversation = Gp2gpConversation(
        messages=test_cases.successful_integration_with_copc_fragments(
            request_completed_time=request_completed_date),
        probe=mock_gp2gp_conversation_observability_probe,
    )
    mock_lookup = Mock()

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)

    expected_last_sender_message_timestamp = request_completed_date

    assert actual.last_sender_message_timestamp == expected_last_sender_message_timestamp
def test_produces_last_sender_message_timestamp_given_copc_fragment_failure():
    copc_fragment_time = a_datetime(year=2020,
                                    month=6,
                                    day=1,
                                    hour=12,
                                    minute=42,
                                    second=0)

    conversation = Gp2gpConversation(
        messages=test_cases.copc_fragment_failure(
            copc_fragment_time=copc_fragment_time),
        probe=mock_gp2gp_conversation_observability_probe,
    )
    mock_lookup = Mock()

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)

    expected_last_sender_message_timestamp = copc_fragment_time

    assert actual.last_sender_message_timestamp == expected_last_sender_message_timestamp
def test_extracts_conversation_id():
    conversation = build_mock_gp2gp_conversation(conversation_id="1234")
    mock_lookup = Mock()

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)
    expected_conversation_id = "1234"

    assert actual.conversation_id == expected_conversation_id
Example #8
0
def test_produces_correct_conversations():
    message_one = build_message(conversation_id="abc")
    message_two = build_message(conversation_id="xyz")
    messages = [message_one, message_two]

    expected = [Conversation("abc", [message_one]), Conversation("xyz", [message_two])]

    transfer_service = TransferService(
        message_stream=messages,
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )
    actual = transfer_service.group_into_conversations()

    assert list(actual) == expected
Example #9
0
def test_sorts_messages_within_conversations():
    message_one = build_message(conversation_id="abc", time=datetime(year=2020, month=6, day=6))
    message_two = build_message(conversation_id="abc", time=datetime(year=2020, month=6, day=5))
    messages = [message_one, message_two]

    expected = [Conversation("abc", [message_two, message_one])]

    transfer_service = TransferService(
        message_stream=messages,
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )
    actual = transfer_service.group_into_conversations()

    assert list(actual) == expected
def test_produces_sending_ods_code_using_organisation_lookup():
    conversation = build_mock_gp2gp_conversation(sending_practice_asid="100")
    mock_lookup = Mock()
    mock_lookup.practice_ods_code_from_asid.return_value = "AB123"

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)
    expected_sending_ods_code = "AB123"

    assert actual.sending_practice.ods_code == expected_sending_ods_code
    mock_lookup.practice_ods_code_from_asid.assert_any_call("100")
def test_produces_none_as_last_sender_message_timestamp_given_request_made():
    conversation = Gp2gpConversation(
        messages=test_cases.request_made(),
        probe=mock_gp2gp_conversation_observability_probe,
    )
    mock_lookup = Mock()

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)

    assert actual.last_sender_message_timestamp is None
def test_logs_negative_sla_warning():
    conversation_id = a_string()
    mock_probe = Mock()
    conversation = build_mock_gp2gp_conversation(
        conversation_id=conversation_id,
        final_acknowledgement_time=a_datetime(year=2021, month=12, day=1),
        request_completed_time=a_datetime(year=2021, month=12, day=2),
    )
    mock_lookup = Mock()

    transfer_service = TransferService(message_stream=[],
                                       cutoff=timedelta(days=14),
                                       observability_probe=mock_probe)

    transfer_service.derive_transfer(conversation, mock_lookup)

    mock_probe.record_negative_sla.assert_called_once_with(conversation)
Example #13
0
def test_rejects_messages_after_cutoff(cutoff_interval, expected_message_ids):
    messages = [
        build_message(conversation_id="a", guid="1", time=datetime(year=2020, month=6, day=6)),
        build_message(conversation_id="a", guid="2", time=datetime(year=2020, month=6, day=7)),
        build_message(conversation_id="a", guid="3", time=datetime(year=2020, month=6, day=8)),
    ]

    transfer_service = TransferService(
        message_stream=messages,
        cutoff=cutoff_interval,
        observability_probe=mock_transfer_observability_probe,
    )
    conversations = transfer_service.group_into_conversations()

    actual_message_ids = [m.guid for m in next(conversations).messages]

    assert actual_message_ids == expected_message_ids
def test_produces_no_sla_given_no_request_completed_time():
    conversation = build_mock_gp2gp_conversation(
        request_completed_time=None,
        final_acknowledgement_time=None,
    )
    mock_lookup = Mock()

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)

    expected_sla_duration = None

    assert actual.sla_duration == expected_sla_duration
def test_negative_sla_duration_clamped_to_zero():
    conversation = build_mock_gp2gp_conversation(
        request_completed_time=a_datetime(year=2021, month=1, day=5),
        final_acknowledgement_time=a_datetime(year=2021, month=1, day=4),
    )

    expected_sla_duration = timedelta(0)
    mock_lookup = Mock()

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)

    assert actual.sla_duration == expected_sla_duration
def test_produces_no_ods_code_using_organisation_lookup_when_no_ods_mapping_exists(
):
    conversation = build_mock_gp2gp_conversation()
    mock_lookup = Mock()
    mock_lookup.practice_ods_code_from_asid.return_value = None

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)
    expected_requesting_ods_code = None
    expected_sending_ods_code = None

    assert actual.requesting_practice.ods_code == expected_requesting_ods_code
    assert actual.sending_practice.ods_code == expected_sending_ods_code
def test_produces_no_sla_given_acks_with_only_duplicate_error():
    conversation = Gp2gpConversation(
        messages=test_cases.acknowledged_duplicate_and_waiting_for_integration(
        ),
        probe=mock_gp2gp_conversation_observability_probe,
    )
    mock_lookup = Mock()

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)

    expected_sla_duration = None
    expected_date_completed = None

    assert actual.sla_duration == expected_sla_duration
    assert actual.date_completed == expected_date_completed
def test_produces_sla_given_suppression_with_conflicting_duplicate_and_error_acks(
):
    successful_acknowledgement_datetime = a_datetime(year=2020,
                                                     month=6,
                                                     day=1,
                                                     hour=16,
                                                     minute=42,
                                                     second=1)

    conversation = Gp2gpConversation(
        messages=test_cases.
        ehr_suppressed_with_conflicting_duplicate_and_conflicting_error_ack(
            request_completed_time=a_datetime(year=2020,
                                              month=6,
                                              day=1,
                                              hour=12,
                                              minute=42,
                                              second=0),
            ehr_acknowledge_time=successful_acknowledgement_datetime,
        ),
        probe=mock_gp2gp_conversation_observability_probe,
    )
    mock_lookup = Mock()

    transfer_service = TransferService(
        message_stream=[],
        cutoff=timedelta(days=14),
        observability_probe=mock_transfer_observability_probe,
    )

    actual = transfer_service.derive_transfer(conversation, mock_lookup)

    expected_sla_duration = timedelta(hours=4, minutes=0, seconds=1)

    assert actual.sla_duration == expected_sla_duration
    assert actual.date_completed == successful_acknowledgement_datetime
    def run(self):
        transfer_observability_probe = TransferObservabilityProbe(
            logger=module_logger)

        log_date_range_info = self._construct_json_log_date_range_info()
        logger.info(
            "Attempting to classify conversations for a date range",
            extra={
                "event": "ATTEMPTING_CLASSIFY_CONVERSATIONS_FOR_A_DATE_RANGE",
                **log_date_range_info,
            },
        )

        spine_messages = self._read_spine_messages()
        ods_metadata_monthly = self._read_ods_metadata()

        transfer_service = TransferService(
            message_stream=spine_messages,
            cutoff=self._config.conversation_cutoff,
            observability_probe=transfer_observability_probe,
        )

        conversations = transfer_service.group_into_conversations()
        gp2gp_conversations = transfer_service.parse_conversations_into_gp2gp_conversations(
            conversations)

        for daily_start_datetime in self._reporting_window.get_dates():
            metadata = {
                "cutoff-days":
                str(self._config.conversation_cutoff.days),
                "build-tag":
                self._config.build_tag,
                "start-datetime":
                convert_to_datetime_string(daily_start_datetime),
                "end-datetime":
                convert_to_datetime_string(daily_start_datetime +
                                           timedelta(days=1)),
                "ods-metadata-month":
                f"{daily_start_datetime.year}-{daily_start_datetime.month}",
            }

            conversations_started_in_reporting_window = filter_conversations_by_day(
                gp2gp_conversations, daily_start_datetime)
            organisation_lookup = ods_metadata_monthly.get_lookup(
                (daily_start_datetime.year, daily_start_datetime.month))
            transfers = transfer_service.convert_to_transfers(
                conversations_started_in_reporting_window,
                organisation_lookup=organisation_lookup)
            self._write_transfers(
                transfers=transfers,
                daily_start_datetime=daily_start_datetime,
                cutoff=self._config.conversation_cutoff,
                metadata=metadata,
            )

        logger.info(
            "Successfully classified conversations for a date range",
            extra={
                "event": "CLASSIFIED_CONVERSATIONS_FOR_A_DATE_RANGE",
                **log_date_range_info,
            },
        )