class SummaryCareRecord:
    summaryCareRecordPath = Path(ROOT_DIR) / "data/templates"

    def __init__(self):
        self.builder = PystacheMessageBuilder(str(self.summaryCareRecordPath),
                                              "16UK05")

    def populate_template_with_file(self, json_file):
        """
        Given a file path to a Json file, this method will parse the json and populate the template
        :param json_file:
        :return: populated template xml string
        """
        with open(json_file) as file:
            data = json.load(file)
            return self.populate_template(data)

    def populate_template_with_json_string(self, json_string):
        """
        Parses a json string to a python dictionary and renders the template
        :param json_string:
        :return: populated template xml string
        """
        data = json.loads(json_string)
        return self.populate_template(data)

    def populate_template(self, input_hash):
        """
        Given a python dictionary this method returns a xml string containing the populated template of the
        GP Summary Update template
        :param input_hash:
        :return: xml string containing populated template
        """
        return self.builder.build_message(input_hash)
class TestPystacheMessageBuilder(TestCase):
    def setUp(self):
        current_dir = os.path.dirname(__file__)
        templates_dir = os.path.join(current_dir, TEMPLATES_DIR)

        self.builder = PystacheMessageBuilder(templates_dir, TEMPLATE_FILENAME)

    def test_build_message(self):
        message = self.builder.build_message(dict(to="world"))

        self.assertEqual(
            "Hello, world!\r\n", message,
            "Message returned should be the rendered string returned by Pystache"
        )
示例#3
0
 def build_error_message(self, error):
     builder = PystacheMessageBuilder(str(TEMPLATE_PATH),
                                      'base_error_template')
     return builder.build_message({"errorMessage": error})
 def __init__(self):
     self.builder = PystacheMessageBuilder(str(self.summaryCareRecordPath),
                                           "16UK05")
class MessageHandlerTest(unittest.TestCase):
    expectedXmlFileDir = Path(ROOT_DIR) / 'reciever' / 'tests' / 'expected_output_xmls'
    inputXmlFileDir = Path(ROOT_DIR) / 'reciever' / 'tests' / 'input_xmls'

    builder = PystacheMessageBuilder(str(inputXmlFileDir), 'base_input')
    success_response = FileUtilities.get_file_string(str(expectedXmlFileDir / 'basic_success_response.xml'))

    def test_action_not_matching_service(self):
        with self.subTest("Two differing services result in a 500 error"):
            service_dict = {'action': "urn:nhs-itk:services:201005:SendNHS111Report-v2-0-ThisDoesNotMatchBelow",
                            'service': "urn:nhs-itk:services:201005:SendNHS111Report-Bad_Service-ThisDoesNotMatchAbove",
                            'manifestCount': 0,
                            'payloadCount': 0
                            }

            expected = FileUtilities.get_file_string(
                str(self.expectedXmlFileDir / 'invalid_action_service_values_response.xml'))

            msg = self.builder.build_message(service_dict)
            message_handler = MessageHandler(msg)

            self.assertEqual(message_handler.error_flag, True)
            XmlUtilities.assert_xml_equal_utf_8(expected, message_handler.get_response())

        with self.subTest("Two services which are the same should return 200 code"):
            service_dict = {'action': "urn:nhs-itk:services:201005:SendNHS111Report",
                            'service': "urn:nhs-itk:services:201005:SendNHS111Report",
                            'manifestCount': 0,
                            'payloadCount': 0
                            }

            msg = self.builder.build_message(service_dict)
            message_handler = MessageHandler(msg)

            self.assertEqual(message_handler.error_flag, False)
            XmlUtilities.assert_xml_equal_utf_8(self.success_response, message_handler.get_response())

    def test_manifest_payload_count(self):
        with self.subTest("Mismatched counts: 500 response"):
            counts = {
                'action': "urn:nhs-itk:services:201005:SendNHS111Report",
                'service': "urn:nhs-itk:services:201005:SendNHS111Report",
                'manifestCount': "1",
                'manifests': [{"id": 'one'}],
                'payloadCount': "2",
                'payloads': [{"id": 'one'}, {'id': "two"}]
            }

            expected = FileUtilities.get_file_string(
                str(self.expectedXmlFileDir / 'manifest_not_equal_to_payload_count.xml'))

            msg = self.builder.build_message(counts)
            message_handler = MessageHandler(msg)

            self.assertEqual(message_handler.error_flag, True)
            XmlUtilities.assert_xml_equal_utf_8(expected, message_handler.get_response())

        with self.subTest("Equal counts: 200 response"):
            counts = {
                'action': "urn:nhs-itk:services:201005:SendNHS111Report",
                'service': "urn:nhs-itk:services:201005:SendNHS111Report",
                'manifestCount': "2",
                'manifests': [{"id": 'one'}, {"id": "two"}],
                'payloadCount': "2",
                'payloads': [{"id": 'one'}, {'id': "two"}]
            }

            msg = self.builder.build_message(counts)
            message_handler = MessageHandler(msg)

            self.assertEqual(message_handler.error_flag, False)
            XmlUtilities.assert_xml_equal_utf_8(self.success_response, message_handler.get_response())

    def test_payload_id_matches_manifest_id(self):
        with self.subTest("Incorrect manifest occurrences returns 500 error"):
            dictionary = {
                'action': "urn:nhs-itk:services:201005:SendNHS111Report",
                'service': "urn:nhs-itk:services:201005:SendNHS111Report",
                'manifestCount': "2",
                'manifests': [{"id": 'one'}, {'id': 'one'}],
                'payloadCount': "2",
                'payloads': [{"id": 'one'}, {'id': "two"}]
            }
            expected = FileUtilities.get_file_string(
                str(self.expectedXmlFileDir / 'payloadID_does_not_match_manifestID.xml'))

            msg = self.builder.build_message(dictionary)
            message_handler = MessageHandler(msg)

            self.assertEqual(message_handler.error_flag, True)
            XmlUtilities.assert_xml_equal_utf_8(expected, message_handler.get_response())

        with self.subTest("Incorrect manifest occurrences returns 500 error"):
            dictionary = {
                'action': "urn:nhs-itk:services:201005:SendNHS111Report",
                'service': "urn:nhs-itk:services:201005:SendNHS111Report",
                'manifestCount': "2",
                'manifests': [{"id": 'one'}, {'id': "two"}],
                'payloadCount': "2",
                'payloads': [{"id": 'one'}, {'id': "two"}]
            }

            msg = self.builder.build_message(dictionary)
            message_handler = MessageHandler(msg)

            self.assertEqual(message_handler.error_flag, False)
            XmlUtilities.assert_xml_equal_utf_8(self.success_response, message_handler.get_response())
class MessageCheckTests(unittest.TestCase):
    inputXmlFileDir = Path(ROOT_DIR) / 'reciever' / 'tests' / 'input_xmls'
    builder = PystacheMessageBuilder(str(inputXmlFileDir), 'base_input')

    def generate_message_tree(self, input_hash):
        msg = self.builder.build_message(input_hash)
        return ET.fromstring(msg)

    def test_action_not_matching_service(self):

        with self.subTest("Two differing services result in a 500 error"):
            service_dict = {
                'action':
                "urn:nhs-itk:services:201005:SendNHS111Report-v2-0-ThisDoesNotMatchBelow",
                'service':
                "urn:nhs-itk:services:201005:SendNHS111Report-Bad_Service-ThisDoesNotMatchAbove"
            }

            fail_flag, response = CheckActionTypes(
                self.generate_message_tree(service_dict)).check()

            self.assertTrue(fail_flag)
            self.assertEqual("Manifest action does not match service action",
                             response)

        with self.subTest(
                "Two services which are the same should return 200 code"):
            service_dict = {
                'action': "urn:nhs-itk:services:201005:SendNHS111Report",
                'service': "urn:nhs-itk:services:201005:SendNHS111Report"
            }

            fail_flag, response = CheckActionTypes(
                self.generate_message_tree(service_dict)).check()

            self.assertFalse(fail_flag)
            self.assertEqual(response, None)

    def test_manifest_payload_count(self):

        with self.subTest("Mismatched counts: 500 response"):
            counts = {'manifestCount': "2", 'payloadCount': "5"}
            fail_flag, response = CheckManifestPayloadCounts(
                self.generate_message_tree(counts)).check()

            self.assertTrue(fail_flag)
            self.assertEqual("Manifest count does not match payload count",
                             response)

        with self.subTest("Equal counts: 200 response"):
            counts = {'manifestCount': "2", 'payloadCount': "2"}
            fail_flag, response = CheckManifestPayloadCounts(
                self.generate_message_tree(counts)).check()

            self.assertFalse(fail_flag)
            self.assertEqual(None, response)

    def test_manifest_count_matches_manifest_instances(self):

        with self.subTest("Incorrect manifest occurrences returns 500 error"):
            manifests = {'manifestCount': "2", 'manifests': [{"id": 'one'}]}
            fail_flag, response = CheckManifestCountInstances(
                self.generate_message_tree(manifests)).check()

            self.assertTrue(fail_flag)
            self.assertEqual(
                "The number of manifest instances does not match the manifest count specified",
                response)

        with self.subTest("Correct manifest occurrences returns 500 error"):
            manifests = {'manifestCount': "1", 'manifests': [{"id": 'one'}]}

            fail_flag, response = CheckManifestCountInstances(
                self.generate_message_tree(manifests)).check()

            self.assertFalse(fail_flag)
            self.assertEqual(None, response)

    def test_payload_count_against_instances(self):

        with self.subTest("Incorrect manifest occurrences returns 500 error"):
            manifests = {'payloadCount': "2", 'payloads': [{"id": 'one'}]}
            fail_flag, response = CheckPayloadCountAgainstActual(
                self.generate_message_tree(manifests)).check()

            self.assertTrue(fail_flag)
            self.assertEqual("Invalid message", response)

        with self.subTest("Incorrect manifest occurrences returns 500 error"):
            manifests = {'payloadCount': "1", 'payloads': [{"id": 'one'}]}
            fail_flag, response = CheckPayloadCountAgainstActual(
                self.generate_message_tree(manifests)).check()

            self.assertFalse(fail_flag)
            self.assertEqual(None, response)

    def test_payload_id_matches_manifest_id(self):

        with self.subTest("Incorrect manifest occurrences returns 500 error"):
            dictionary = {
                'payloadCount': "2",
                'payloads': [{
                    "id": 'one'
                }, {
                    "id": 'three'
                }],
                'manifestCount': "2",
                'manifests': [{
                    "id": 'one'
                }, {
                    "id": 'two'
                }]
            }
            fail_flag, response = CheckPayloadIdAgainstManifestId(
                self.generate_message_tree(dictionary)).check()

            self.assertTrue(fail_flag)
            self.assertEqual("Payload IDs do not map to Manifest IDs",
                             response)

        with self.subTest("Incorrect manifest occurrences returns 500 error"):
            dictionary = {
                'payloadCount': "2",
                'payloads': [{
                    "id": 'one'
                }],
                'manifestCount': "2",
                'manifests': [{
                    "id": 'one'
                }]
            }
            fail_flag, response = CheckPayloadIdAgainstManifestId(
                self.generate_message_tree(dictionary)).check()

            self.assertFalse(fail_flag)
            self.assertEqual(None, response)
    def setUp(self):
        current_dir = os.path.dirname(__file__)
        templates_dir = os.path.join(current_dir, TEMPLATES_DIR)

        self.builder = PystacheMessageBuilder(templates_dir, TEMPLATE_FILENAME)
 def __init__(self):
     self.builder = PystacheMessageBuilder(str(self.summaryCareRecordPath), self.file_template_name)
class GpSummaryUpload(object):
    """Class for populating a Gp Summary Upload template"""

    summaryCareRecordPath = Path(ROOT_DIR) / "data/templates"
    file_template_name = "16UK05"
    interaction_id = "REPC_IN150016UK05"

    def __init__(self):
        self.builder = PystacheMessageBuilder(str(self.summaryCareRecordPath), self.file_template_name)

    def populate_template_with_file(self, json_file):
        """
        Given a file path to a Json file, this method will parse the json and populate the template
        :param json_file:
        :return: populated template xml string
        """
        with open(json_file) as file:
            data = json.load(file)
            return self.populate_template(data)

    def populate_template_with_json_string(self, json_string):
        """
        Parses a json string to a python dictionary and renders the template
        :param json_string:
        :return: populated template xml string
        """
        data = json.loads(json_string)
        return self.populate_template(data)

    def populate_template(self, input_hash):
        """
        Given a python dictionary this method returns a xml string containing the populated template of the
        GP Summary Update template
        :param input_hash:
        :return: xml string containing populated template
        """
        return self.builder.build_message(input_hash)

    def parse_response(self, response_message: str) -> Dict:
        """
        Parses te key values of a given Gp summary Upload response into a dictionary.
        NOTE: This is purely a success parsing response mechanism, error parsing is currently not supported
        :param response_message: A Successful Gp Summary Upload response acknowledgement
        :return: A dictionary containing the key success details of the message
        """
        root = self._get_root(response_message)
        if not root:
            return {'error': 'Failed to parse response from xml provided'}

        message_id = self._find_hl7_element_attribute(root, './/hl7:id', 'root')
        message_reference = self._find_hl7_element_attribute(root, './/hl7:messageRef//hl7:id', 'root')
        creation_time = self._find_hl7_element_attribute(root, './/hl7:creationTime', 'value')
        message_detail = self._find_hl7_element_text(root,
                                                     './/hl7:ControlActEvent//hl7:requestSuccessDetail//hl7:detail')

        return self._create_response_dictionary(message_id, message_reference, creation_time, message_detail)

    def _get_root(self, message: str) -> Optional[ET.Element]:
        """
        Parses an xml string and returns the root element of that element tree
        :param message: xml input string
        :return: The root element of the element tree
        """
        try:
            return ET.ElementTree(ET.fromstring(message)).getroot()
        except ET.ParseError as e:
            logger.error('001', 'Exception raised while creating XML object from string {exception}',
                         {'exception': e})
            return None

    def _find_hl7_element_attribute(self, root: ET.Element, element_name: str, attribute: str) -> Optional[str]:
        """
        Searches the tree with the given root node for an element with the element name, then looks for the given 
        attribute of that element
        :param root: The root of the element tree
        :param element_name: The element tag to search for
        :param attribute: the attribute on that given element
        :return: The value of the attribute on the element, if it exists
        """
        try:
            return self._get_element(root, element_name, lambda x: x.attrib[attribute])
        except KeyError:
            logger.info('002', 'Failed to find attribute on {element} {attribute}',
                        {'element': element_name, 'attribute': attribute}
                        )
            return None

    def _find_hl7_element_text(self, root: ET.Element, element_name: str):
        """
        :param root: The root of the element tree to search 
        :param element_name:  the name of the element to look for
        :return: the text within the tags of the given element
        """
        return self._get_element(root, element_name, lambda x: x.text)

    def _get_element(self, root: ET.Element, element_name: str, func: Callable[[ET.Element], str]):
        """
        Searches for an element on the element tree of the given root, the passes the element to the found function
        :param root: Root element of the element tree
        :param element_name: element to search for
        :param func: function to call which processes the result of the element search
        :return: the result of the func call 
        """
        element = root.find(element_name, namespaces={'hl7': 'urn:hl7-org:v3'})
        if element is None:
            return None

        return func(element)

    def _create_response_dictionary(self, message_id: str, message_ref: str, creation_time: str, message_detail: str) \
            -> Dict:
        """
        Creates a success/error dictionary based on the given parameters
        :param message_id: 
        :param message_ref: 
        :param creation_time: 
        :param message_detail: 
        :return: A dict containing either the parsed values or the errror response
        """
        if all([message_id, message_ref, creation_time, message_detail]):
            return {
                'messageRef': message_ref,
                'messageId': message_id,
                'creationTime': creation_time,
                'messageDetail': message_detail
            }
        else:
            logger.error('003', 'Failed to parse all necessary elements from xml {message_id} {message_reference}'
                                ' {creation_time} {message_details}',
                         {'message_id': message_id,
                          'message_reference': message_ref,
                          'creation_time': creation_time,
                          'message_details': message_detail
                          }
                         )
            return {'error': 'Failed to parse all the necessary elements from xml returned from MHS'}