class GlossTestCase(TestCase):
    def setUp(self):
        Base.metadata.drop_all(engine)
        Base.metadata.create_all(engine)
        Session = sessionmaker(engine)
        self.session = Session()
        self.patch_session = patch(
            'gloss.models.get_session', return_value=self.session
        )
        self.patch_session.start()
        self.patch_close = patch.object(self.session, 'close')
        self.patch_close.start()
        self.patch_requests_post = patch("requests.post")
        self.mock_requests_post = self.patch_requests_post.start()
        self.patch_socket = patch("socket.socket")
        self.mock_socket = self.patch_socket.start()

        self.patch_mllp_client = patch("hl7.client.MLLPClient")
        self.mock_mllp_client = self.patch_mllp_client.start()

        self.mock_mllp_send = MagicMock()
        client_instance = MagicMock()
        client_instance.send_message = self.mock_mllp_send;
        enter = MagicMock(return_value=client_instance)
        self.mock_mllp_client.name = "client"
        self.mllp_api_client_instance = MagicMock()
        self.mock_mllp_client.return_value = self.mllp_api_client_instance
        self.mllp_api_client_instance.__enter__ = enter

    def tearDown(self):
        self.patch_session.stop()
        self.patch_close.stop()
        self.patch_requests_post.stop()
        self.patch_socket.stop()
        self.session.close()
        self.patch_mllp_client.stop()
        Base.metadata.drop_all(engine)

    def create_subrecord(self, some_class):
        gloss_ref = GlossolaliaReference()
        self.session.add(gloss_ref)
        return some_class(gloss_reference=gloss_ref)

    def create_subrecord_with_id(
        self, some_class, identifier, issuing_source="uclh", subscribed=True
    ):
        subrecord = self.create_subrecord(some_class)
        hospital_identifier = PatientIdentifier(
            identifier=identifier,
            issuing_source=issuing_source,
            gloss_reference=subrecord.gloss_reference
        )
        self.session.add(hospital_identifier)
        subscription = Subscription(
            gloss_reference=subrecord.gloss_reference,
            system=issuing_source,
        )
        self.session.add(subscription)
        return subrecord

    def get_inpatient_admission(self, identifier, issuing_source):
        inpatient_admission = self.create_subrecord_with_id(
            InpatientAdmission, identifier, issuing_source
        )

        inpatient_admission.external_identifier = "940347"
        inpatient_admission.datetime_of_admission = datetime(
            2012, 10, 10, 17, 12
        )
        inpatient_admission.admission_diagnosis = "vertigo"
        return inpatient_admission

    def get_inpatient_location(self, inpatient_admission):
        inpatient_location = InpatientLocation()
        inpatient_location.ward_code = "BBNU"
        inpatient_location.room_code = "BCOT"
        inpatient_location.bed_code = "BCOT-02B"
        inpatient_location.inpatient_admission = inpatient_admission
        return inpatient_location

    def get_allergy_dict(self):
        return dict(
            allergy_type_description="Product Allergy",
            certainty_id="CERT-1",
            certainty_description="Definite",
            allergy_reference_name="Penecillin",
            allergy_description="Penecillin",
            allergen_reference_system="UDM",
            allergen_reference="8e75c6d8-45b7-4b40-913f-8ca1f59b5350",
            status_id="1",
            status_description="Active",
            diagnosis_datetime=datetime(2015, 11, 19, 9, 15),
            allergy_start_datetime=datetime(2015, 11, 19, 9, 14)
        )

    def get_allergy(self, identifier, issuing_source):
        allergy = self.create_subrecord_with_id(
            Allergy, identifier, issuing_source
        )
        allergy_dict = self.get_allergy_dict()
        for k, v in allergy_dict.iteritems():
            setattr(allergy, k, v)
        return allergy

    def get_result_dict(self):
        comments = " ".join([
            "Units: mL/min/1.73sqm Multiply eGFR by 1.21 for people of",
        ])

        observations = [
            {
                "result_status": "FINAL",
                "observation_value": "143",
                "comments": comments,
                "test_code": "NA",
                "value_type": "NM",
                "test_name": "Sodium",
                "units": "mmol/L",
                "reference_range": "135-145"
            },
            {
                "result_status": "FINAL",
                "observation_value": "3.9",
                "comments": None,
                "test_code": "K",
                "value_type": "NM",
                "test_name": "Potassium",
                "units": "mmol/L",
                "reference_range": "3.5-5.1"
            }
        ]

        return {
            "profile_code": "ELU",
            "profile_description": "RENAL PROFILE",
            "result_status": "FINAL",
            "lab_number": None,
            "last_edited": None,
            "observation_datetime": None,
            "observations": json.dumps(observations),
            "request_datetime": None
        }

    def get_result(self, identifier, issuing_source):
        result = self.create_subrecord_with_id(
            Result, identifier, issuing_source
        )
        result_dict = self.get_result_dict()
        for k, v in result_dict.iteritems():
            setattr(result, k, v)
        return result

    def create_patient(self, identifier, issuing_source):
        patient = self.create_subrecord_with_id(
            Patient, identifier, issuing_source
        )
        patient.first_name = "Jane"
        patient.surname = "Smith"
        patient.title = "Ms"
        patient.date_of_birth = date(1983, 12, 12)
        return patient class Field(object):
class GlossMessageMeta(type):
class MessageContainer(object):
class MessageType(six.with_metaclass(GlossMessageMeta)):
class PatientMessage(MessageType):
class PatientMergeMessage(MessageType):
class AllergyMessage(MessageType):
class ObservationMessage(MessageType):
class ResultMessage(MessageType):
class OrderMessage(MessageType):
class InpatientAdmissionMessage(MessageType):
class InpatientAdmissionTransferMessage(InpatientAdmissionMessage):
class InpatientAdmissionDeleteMessage(MessageType):
    def __init__(self, required=False):
    def __new__(cls, name, bases, attrs):
    def __init__(self, messages, hospital_number, issuing_source):
    def to_dict(self):
def construct_message_container(
    someMessages, hospital_number, issuing_source="uclh"
):
    def __init__(self, **kwargs):
    def to_dict(self):
        def to_dict_or_not_to_dict(some_value):
    def __init__(
        self, **kwargs
    ):
    def to_dict(self):
    def __init__(self, **kw): class GlossolaliaReference(Base):
    pass class Subscription(Base, GlossSubrecord):
    PART_OF_BULK_DOWNLOAD = False

    system = Column(String(250))
    active = Column(Boolean, default=True)
    end_point = Column(String(250)) class PatientIdentifier(Base, GlossSubrecord):
    PART_OF_BULK_DOWNLOAD = False

    identifier = Column(String(250))
    issuing_source = Column(String(250))
    active = Column(Boolean, default=True)

    @declared_attr
    def gloss_reference_id(cls):
        return Column(Integer, ForeignKey('glossolaliareference.id'))

    @declared_attr
    def gloss_reference(cls):
        return relationship(
            "GlossolaliaReference",
            foreign_keys=[getattr(cls, "gloss_reference_id")]
        ) def is_subscribed(hospital_number, session=None, issuing_source="uclh"):
    subscription = Subscription.query_from_identifier(
        hospital_number, issuing_source, session
    )
    return subscription.filter(Subscription.active == True).count() def get_gloss_reference(hospital_number, session, issuing_source="uclh"):
    gloss_information = session.query(GlossolaliaReference, PatientIdentifier).\
    filter(PatientIdentifier.gloss_reference_id == GlossolaliaReference.id).\
    filter(PatientIdentifier.issuing_source == issuing_source).\
    filter(PatientIdentifier.identifier == hospital_number).one_or_none()

    # we should change this to only query for gloss id rather than all 3 columns
    if gloss_information:
        return gloss_information[0] @contextmanager
def session_scope():
    """Provide a transactional scope around a series of operations."""
    session = get_session()
    try:
        yield session
        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close() class OutgoingMessage(Base):
    count = Column(BigInteger) @atomic_method
def get_next_message_id(session):
    outgoing_message = session.query(OutgoingMessage).one_or_none()

    if not outgoing_message:
        message_id = 1
        session.add(OutgoingMessage(count=message_id))
    else:
        message_id = outgoing_message.count + 1
        outgoing_message.count = message_id
        session.add(outgoing_message)

    return message_id def patient_to_message_container(hospital_number, issuing_source, session):
    messages = []
    for subRecord in itersubclasses(GlossSubrecord):
        if subRecord.PART_OF_BULK_DOWNLOAD:
            messages.extend(subRecord.to_messages(
                hospital_number, issuing_source, session
            ))

    return message_type.construct_message_container(messages, hospital_number) class InpatientLocation(Base):
    PART_OF_BULK_DOWNLOAD = False

    inpatient_admission_id = Column(Integer, ForeignKey('inpatientadmission.id'))
    inpatient_admission = relationship(
        "InpatientAdmission", foreign_keys=[inpatient_admission_id], cascade="all"
    )

    # when the patient was transferred from this location
    # null when the patient is at that location
    datetime_of_transfer = Column(DateTime)

    # when the patient was transferred from this location
    # None if the patient is currently there
    ward_code = Column(String(250))
    room_code = Column(String(250))
    bed_code = Column(String(250))

    @classmethod
    def get_latest_location(cls, inpatient_admission, session):
        latest = cls.get_location(inpatient_admission, session)
        if latest:
            return latest
        else:
            q = session.query(cls).filter(
                cls.inpatient_admission == inpatient_admission
            )

            q = q.order_by(cls.datetime_of_transfer.desc())

            if q.count():
                return q[0]

    @classmethod
    def get_location(cls, inpatient_admission, session):
        """
            get's the location of a patient
        """
        q = session.query(cls).filter(cls.datetime_of_transfer == None)
        q = q.filter(cls.inpatient_admission == inpatient_admission)
        return q.one_or_none() def subscribe(hospital_number, end_point, session, issuing_source):
    """ subscribe an issuing source and hosptial number to an end point
        don't allow multiple subscriptions
    """
    subscription = Subscription.query_from_identifier(
        hospital_number, issuing_source, session
    ).one_or_none()

    if subscription:
        if not subscription.active:
            subscription.active = True
            session.add(subscription)
    else:
        gloss_reference = get_or_create_identifier(
            hospital_number, session, issuing_source
        )
        session.add(gloss_reference)
        subscription = Subscription(
            gloss_reference=gloss_reference,
            system=issuing_source,
            end_point=end_point
        )
        session.add(subscription) class Merge(Base, GlossSubrecord):
    new_reference_id = Column(Integer, ForeignKey('glossolaliareference.id'))
    new_reference = relationship(
        "GlossolaliaReference", foreign_keys=[new_reference_id]
    )

    @classmethod
    def _to_messages(cls, identifier, issuing_source, session):
        merge = cls.get_latest_merge(session, issuing_source, identifier)

        if merge:
            identifier = session.query(PatientIdentifier).filter(
                PatientIdentifier.gloss_reference == merge.new_reference
            ).filter(
                PatientIdentifier.issuing_source == issuing_source
            ).one_or_none()

            if not identifier:
                err = "unable to find a patient identifier for gloss reference {}"
                raise err.format(identifier)
            return [message_type.PatientMergeMessage(
                new_id=identifier.identifier
            )]

        return []

    @classmethod
    def get_latest_merge(cls, session, issuing_source, identifier):
        """ although an identifier can only have a single merge, that merge
            can have multiple merges, so lets only return the most recent merge
        """
        merge = cls.query_from_identifier(
            identifier, issuing_source, session
        ).one_or_none()

        while merge:
            if merge.gloss_reference == merge.new_reference:
                raise ValueError(
                    "incorrect merge {} points to itself".format(merge.id)
                )

            new_merge = session.query(cls).filter(
                cls.gloss_reference == merge.new_reference
            ).one_or_none()

            if not new_merge:
                return merge
            else:
                merge = new_merge class Patient(Base, GlossSubrecord):
    message_type = message_type.PatientMessage

    surname = Column(String(250), nullable=False)
    first_name = Column(String(250), nullable=False)
    middle_name = Column(String(250))
    title = Column(String(250))
    date_of_birth = Column(Date, nullable=False)
    birth_place = Column(String(250))
    sex = Column(String(250))
    marital_status = Column(String(250))
    religion = Column(String(250))
    date_of_death = Column(Date)
    post_code = Column(String(20))
    gp_practice_code = Column(String(20))
    ethnicity = Column(String(250))

    # I know it seems like we can calculate this from the above
    # however it comes as a seperate field in the feed and
    # therefore might be useful for data validation purposes
    # (also might give us an indicator and the max time of death)
    death_indicator = Column(Boolean, default=False)
from mock import patch
from datetime import datetime, timedelta
from gloss.tests.core import GlossTestCase
from gloss import message_type
from gloss.models import (
    GlossolaliaReference, Subscription, PatientIdentifier,
    is_subscribed, get_gloss_reference, session_scope,
    OutgoingMessage, get_next_message_id, patient_to_message_container,
    InpatientLocation, subscribe, Merge, Patient
)
"""
Unittests for gloss.models
"""




class SessionScopeTestCase(GlossTestCase):
    def test_rollback(self):
        with patch.object(self.session, "add", side_effect=ValueError('Fail')):
            with patch.object(self.session, "rollback") as rollback:
                with self.assertRaises(ValueError):
                    with session_scope() as s:
                        s.add(None)
                rollback.assert_called_once_with()


class IsSubscribedTestCase(GlossTestCase):
    def setUp(self):
        super(IsSubscribedTestCase, self).setUp()
        self.glossolalia_reference = GlossolaliaReference()
        self.session.add(self.glossolalia_reference)

    def test_is_subscribed(self):
        subscription = Subscription(
            system="elcid", gloss_reference=self.glossolalia_reference
        )
        self.session.add(subscription)

        hospital_identifier = PatientIdentifier(
            identifier="12341234",
            issuing_source="uclh",
            gloss_reference=self.glossolalia_reference
        )
        self.session.add(hospital_identifier)
        subscribed = is_subscribed("12341234", session=self.session)
        assert(subscribed)

    def test_is_not_subscribed(self):
        glossolalia_reference = GlossolaliaReference()
        self.session.add(glossolalia_reference)

        subscription = Subscription(
            system="elcid",
            gloss_reference=self.glossolalia_reference,
            active=False
        )
        self.session.add(subscription)

        hospital_identifier = PatientIdentifier(
            identifier="12341234",
            issuing_source="uclh",
            gloss_reference=glossolalia_reference
        )
        self.session.add(hospital_identifier)

        subscribed = is_subscribed("12341234", session=self.session)