Esempio n. 1
0
class OntologyRoot(OntologyEntry):
    _t = DynElements(OntologyEntry)

    def __init__(self, base: str, **kwargs) -> None:
        """
        Initialize an ontology header.
        :param kwargs: Additional arguments for i2b2_core
        """
        if base.startswith('\\'):
            base = base[1:-1]
        path = '\\' + base + '\\'
        super().__init__(path,
                         EmptyQuery(),
                         VisualAttributes("CA"),
                         sourcesystem_cd=base,
                         **kwargs)
        self._base = base

    @DynObject.entry(_t)
    def c_hlevel(self) -> int:
        return 0

    @DynObject.entry(_t)
    def c_name(self) -> str:
        return self._base

    @DynObject.entry(_t)
    def c_basecode(self) -> Optional[str]:
        return self._base + ':'
Esempio n. 2
0
class ConceptOntologyEntry(OntologyEntry):
    _t = DynElements(OntologyEntry)

    def __init__(self,
                 subject: URIRef,
                 navigational_path: str,
                 ontological_path: str,
                 is_leaf: bool,
                 is_draggable: bool = True,
                 primitive_type: Optional[URIRef] = None):
        """
        Construct a concept entry in the ontology space
        :param subject: URI of concept
        :param navigational_path: path to concept for navigational purposes.
          Example: \\FHIR\\administrative\\individual\\Patient
        :param ontological_path: concept_dimension path to concept.  Example: \\FHIR\\Patient
        :param is_leaf: true if there are no additional children.
        :param is_draggable: If not a leaf, whether this is an i2b2 container (False) or folder (True)
        :param primitive_type: Type used to construct c_metadataxml
        """
        self._subject = subject

        visattr = VisualAttributes()
        visattr.leaf = is_leaf
        visattr.concept = True
        visattr.draggable = is_draggable
        visattr.editable = False

        full_path = navigational_path
        self._m_applied_path = '@'

        query = ConceptQuery(
            ontological_path) if is_draggable else EmptyQuery()
        super().__init__(full_path, query, visattr, concept_code(subject),
                         primitive_type)

    # Level in ontology concept references are relative to base path
    @DynObject.entry(_t)
    def c_hlevel(self) -> int:
        return self.c_fullname[:-1].count('\\') - 1

    @DynObject.entry(_t)
    def c_name(self) -> str:
        return concept_name(self.graph, self._subject)

    @DynObject.entry(_t)
    def c_comment(self) -> Optional[str]:
        rval = self.graph.comment(self._subject)
        return str(rval) if rval else None

    @DynObject.entry(_t)
    def c_tooltip(self) -> Optional[str]:
        rval = self.graph.value(self._subject, DC.title, None,
                                self.graph.comment(self._subject))
        return str(rval) if rval else None
Esempio n. 3
0
        class t2(t1):
            _t = DynElements(t1)

            def __init__(self, v):
                super().__init__(v)

            @DynObject.entry(_t)
            @classmethod
            def v3(cls):
                return "Overridden"

            @DynObject.entry(_t)
            def v8(self):
                return self._v
Esempio n. 4
0
class InnerClass(MiddleClass):
    _t = DynElements(MiddleClass)

    def __init__(self):
        super().__init__()

    def yet_another_method(self):
        _ = self
        return "YAM"

    @staticmethod
    def another_method():
        return "YA"

    _t.add(yet_another_method)
    _t.add(another_method)
Esempio n. 5
0
class MiddleClass(OneClass):
    _t = DynElements(OneClass)
    classvalue = "Middle Class Value"

    def __init__(self):
        super().__init__()
        self._av = "Another Value"

    @classmethod
    def class_method(cls):
        return cls.classvalue

    def another_method(self):
        return self._av

    _t.add(class_method)
    _t.add(another_method)
Esempio n. 6
0
class ModifierDimensionRoot(ModifierDimension):
    _t = DynElements(ModifierDimension)

    def __init__(self, base: str, **kwargs) -> None:
        super().__init__(**kwargs)
        self._base = base

    @DynObject.entry(_t)
    def modifier_path(self) -> str:
        return '\\' + self._base + '\\'

    @DynObject.entry(_t)
    def modifier_cd(self) -> str:
        return self._base

    @DynObject.entry(_t)
    def name_char(self) -> str:
        return self._base + " root"

    @DynObject.entry(_t)
    def modifier_blob(self) -> str:
        return ''
Esempio n. 7
0
class ConceptDimensionRoot(ConceptDimension):
    _t = DynElements(ConceptDimension)

    def __init__(self, base: str, **kwargs) -> None:
        super().__init__(OWL.Class, **kwargs)
        self._base = base

    @DynObject.entry(_t)
    def concept_path(self) -> str:
        return '\\' + self._base + '\\'

    @DynObject.entry(_t)
    def concept_cd(self) -> str:
        return self._base

    @DynObject.entry(_t)
    def name_char(self) -> str:
        return self._base + " root"

    @DynObject.entry(_t)
    def concept_blob(self) -> str:
        return ''
Esempio n. 8
0
        class t1(DynObject):
            _t = DynElements()
            clsv = "Class value"

            def __init__(self, v2):
                self._v = v2

            @DynObject.entry(_t)
            @staticmethod
            def v1():
                return "Static value"

            @DynObject.entry(_t)
            @classmethod
            def v2(cls):
                return cls.clsv

            @DynObject.entry(_t)
            def v3(self):
                return self._v

            _t.add_const('v4', "Constant")
            _t.add_func(const_f)
Esempio n. 9
0
class I2B2Core(DynObject):
    _t = DynElements()
    _check_dups = False

    def __init__(self,
                 update_date: Optional[DynamicPropType] = None,
                 download_date: Optional[DynamicPropType] = None,
                 sourcesystem_cd: Optional[DynamicPropType] = None,
                 import_date: Optional[DynamicPropType] = None):
        self._update_date = update_date
        self._download_date = download_date
        self._sourcesystem_cd = sourcesystem_cd
        self._import_date = import_date

    @DynObject.entry(_t)
    def update_date(self) -> datetime:
        return self._resolve(
            self._update_date
        ) if self._update_date is not None else datetime.now()

    @DynObject.entry(_t)
    def download_date(self) -> datetime:
        return self._resolve(
            self._download_date
        ) if self._download_date is not None else self.update_date

    @DynObject.entry(_t)
    def import_date(self) -> datetime:
        return self._resolve(
            self._import_date
        ) if self._import_date is not None else self.update_date

    @DynObject.entry(_t)
    def sourcesystem_cd(self) -> str:
        return self._resolve(
            self._sourcesystem_cd
        ) if self._sourcesystem_cd is not None else "Unspecified"
Esempio n. 10
0
class OneClass(DynObject):
    _t = DynElements()
    classvalue = "Class Value"

    def __init__(self):
        super().__init__()
        self._rv = "Regular Value"

    @classmethod
    def class_method(cls):
        return cls.classvalue

    @staticmethod
    def static_method():
        return "Static Value"

    def regular_method(self):
        return self._rv

    _t.add(class_method)
    _t.add(static_method)
    _t.add(regular_method)
    _t.add_func(const_f)
    _t.add_const("const", 1173)
Esempio n. 11
0
class ModifierDimension(CommonDimension):
    _t = DynElements(CommonDimension)

    @DynObject.entry(_t)
    def modifier_path(self) -> str:
        return self.path()

    @DynObject.entry(_t)
    def modifier_cd(self) -> str:
        return self.cd()

    @DynObject.entry(_t)
    def name_char(self) -> str:
        return self.name_char_()

    @DynObject.entry(_t)
    def modifier_blob(self) -> str:
        return self.blob()

    def __lt__(self, other):
        return self.modifier_path < other.modifier_path

    def __eq__(self, other):
        return self.modifier_path == other.modifier_path
Esempio n. 12
0
class ConceptDimension(CommonDimension):
    _t = DynElements(CommonDimension)

    @DynObject.entry(_t)
    def concept_path(self) -> str:
        return self.path()

    @DynObject.entry(_t)
    def concept_cd(self) -> str:
        return self.cd()

    @DynObject.entry(_t)
    def name_char(self) -> str:
        return self.name_char_()

    @DynObject.entry(_t)
    def concept_blob(self) -> str:
        return self.blob()

    def __lt__(self, other):
        return self.concept_path < other.concept_path

    def __eq__(self, other):
        return self.concept_path == other.concept_path
Esempio n. 13
0
        class t1(DynObject):
            _t = DynElements()

            @DynObject.entry(_t)
            def clsv(self):
                return None
Esempio n. 14
0
class ModifierOntologyEntry(OntologyEntry):
    _t = DynElements(OntologyEntry)

    def __init__(self, depth: int, subject: URIRef, mod: URIRef,
                 full_path: str, applied_path: str, is_leaf: bool,
                 mod_pred: URIRef, mod_type: URIRef) -> None:
        """
        Construct a concept entry in the ontology space
        :param depth: Relative one-based depth of entry
        :param subject: URI of concept being modified
        :param mod: URI of modifier itself
        :param full_path: modifier path
        :param applied_path: path that modifier applies to
        :param is_leaf: true if there are no additional children
        :param mod_pred: real modifier predicate (for dimcode)
        :param mod_type: Actual type of path
        """
        assert (depth > 0)

        # TODO: Fix the hard coded reference below
        query = ModifierQuery(
            '\\FHIR\\' + concept_path(mod_pred)) if is_leaf else EmptyQuery()

        visattr = VisualAttributes()
        visattr.leaf = is_leaf
        visattr.concept = False
        visattr.draggable = True
        visattr.editable = False

        super().__init__(full_path, query, visattr, modifier_code(mod),
                         mod_type)

        self._subject = subject
        self._mod = mod
        self._depth = depth
        self._m_applied_path = applied_path
        self._mod_pred = mod_pred

    # Levels in ontology modifier references start at 1
    @DynObject.entry(_t)
    def c_hlevel(self) -> int:
        return self._depth

    @DynObject.entry(_t)
    def c_name(self) -> str:
        return modifier_name(self.graph, self._mod)

    @DynObject.entry(_t)
    def c_comment(self) -> Optional[str]:
        rval = self.graph.comment(self._mod)
        if not rval and self._primitive_type:
            rval = self.graph.comment(self._primitive_type)
        return str(rval) if rval else None

    @DynObject.entry(_t)
    def c_tooltip(self) -> Optional[str]:
        rval = self.graph.value(self._mod, DC.title, None,
                                self.graph.comment(self._mod))
        if not rval and self._primitive_type:
            rval = self.graph.value(self._primitive_type, DC.title, None,
                                    self.graph.comment(self._primitive_type))
        return str(rval) if rval else None

    @DynObject.entry(_t)
    def m_applied_path(self) -> str:
        return self._m_applied_path
Esempio n. 15
0
class VisitDimension(I2B2CoreWithUploadId):
    _t = DynElements(I2B2CoreWithUploadId)

    key_fields = ["encounter_num", "patient_num"]

    def __init__(self,
                 encounter_num: int,
                 patient_num: int,
                 active_status_cd: Optional[ActiveStatusCd] = None,
                 start_date: Optional[datetime] = None,
                 end_date: Optional[datetime] = None,
                 inout_cd: Optional[str] = None,
                 location_cd: Optional[str] = None,
                 location_path: Optional[str] = None,
                 length_of_stay: Optional[int] = None,
                 visit_blob: Optional[str] = None,
                 **kwargs):
        self._encounter_num = encounter_num
        self._patient_num = patient_num
        self._active_status_cd = active_status_cd
        self._start_date = start_date
        self._end_date = end_date
        self._inout_cd = inout_cd
        self._location_cd = location_cd
        self._location_path = location_path
        self._length_of_stay = length_of_stay
        self._visit_blob = visit_blob
        super().__init__(**kwargs)

    @DynObject.entry(_t)
    def encounter_num(self) -> int:
        """
        Reference number for the encounter/visit
        """
        return self._encounter_num

    @DynObject.entry(_t)
    def patient_num(self) -> int:
        """
        Reference number for the patient
        """
        return self._patient_num

    @DynObject.entry(_t)
    def active_status_cd(self) -> Optional[str]:
        """
        Reference number for the patient
        """
        return self._active_status_cd.code if self._active_status_cd else None

    @DynObject.entry(_t)
    def start_date(self) -> Optional[datetime]:
        """
        The date the event began
        """
        return self._start_date

    @DynObject.entry(_t)
    def end_date(self) -> Optional[datetime]:
        """
        The date the event ended
        """
        return self._end_date

    @DynObject.entry(_t)
    def inout_cd(self) -> Optional[str]:
        """
        The date the event ended
        """
        return self._inout_cd

    @DynObject.entry(_t)
    def location_cd(self) -> Optional[str]:
        return self._location_cd

    @DynObject.entry(_t)
    def location_path(self) -> Optional[str]:
        return self._location_path

    @DynObject.entry(_t)
    def length_of_stay(self) -> Optional[int]:
        return self._length_of_stay

    @DynObject.entry(_t)
    def visit_blob(self) -> Optional[str]:
        return self._visit_blob

    @classmethod
    def delete_upload_id(cls, tables: I2B2Tables, upload_id: int) -> int:
        """
        Delete all patient_dimension records with the supplied upload_id
        :param tables: i2b2 sql connection
        :param upload_id: upload identifier to remove
        :return: number or records that were deleted
        """
        return cls._delete_upload_id(tables.crc_connection,
                                     tables.visit_dimension, upload_id)

    @classmethod
    def delete_sourcesystem_cd(cls, tables: I2B2Tables,
                               sourcesystem_cd: str) -> int:
        """
        Delete all records with the supplied sourcesystem_cd
        :param tables: i2b2 sql connection
        :param sourcesystem_cd: sourcesystem_cd to remove
        :return: number or records that were deleted
        """
        return cls._delete_sourcesystem_cd(tables.crc_connection,
                                           tables.visit_dimension,
                                           sourcesystem_cd)

    @classmethod
    def add_or_update_records(
            cls, tables: I2B2Tables,
            records: List["VisitDimension"]) -> Tuple[int, int]:
        """
        Add or update the patient_dimension table as needed to reflect the contents of records
        :param tables: i2b2 sql connection
        :param records: records to apply
        :return: number of records added / modified
        """
        return cls._add_or_update_records(tables.crc_connection,
                                          tables.visit_dimension, records)
Esempio n. 16
0
class I2B2CoreWithUploadId(I2B2Core):
    _t = DynElements(I2B2Core)

    no_update_fields = [
        "update_date", "download_date", "import_date", "sourcesystem_cd",
        "upload_id"
    ]
    key_fields = None

    def __init__(self, upload_id: Optional[DynamicPropType] = None, **kwargs):
        super().__init__(**kwargs)
        self._upload_id = upload_id

    DynObject._after_root(_t)

    @DynObject.entry(_t)
    def upload_id(self) -> Optional[int]:
        return self._resolve(self._upload_id)

    @staticmethod
    def _delete_upload_id(conn: Connection, table: Table,
                          upload_id: int) -> int:
        """
        Remove all table records with the supplied upload_id
        :param conn: sql connection
        :param table: table to modify
        :param upload_id: target upload_id
        :return: number of records removed
        """
        return conn.execute(
            delete(table).where(
                table.c.upload_id == upload_id)).rowcount if upload_id else 0

    @staticmethod
    def _delete_sourcesystem_cd(conn: Connection, table: Table,
                                sourcesystem_cd: str) -> int:
        """
        Remove all table records with the supplied upload_id
        :param conn: sql connection
        :param table: table to modify
        :param sourcesystem_cd: target sourcesystem code
        :return: number of records removed
        """
        return conn.execute(delete(table).where(table.c.sourcesystem_cd == sourcesystem_cd)).rowcount \
            if sourcesystem_cd else 0

    @staticmethod
    def _nested_fcn(f: Callable, filters: List):
        """
        Distribute binary function f across list L
        :param f: Binary function
        :param filters: function arguments
        :return: chain of binary filters
        """
        return None if len(filters) == 0 \
            else filters[0] if len(filters) == 1 \
            else f(filters[0], I2B2CoreWithUploadId._nested_fcn(f, filters[1:]))

    @classmethod
    def _check_for_dups(cls, records: List["I2B2CoreWithUploadId"]) -> \
            Dict[Tuple, List["I2B2CoreWithUploadId"]]:
        key_map = dict()  # type: Dict[Tuple, I2B2CoreWithUploadId]
        dups = dict()  # type: Dict[Tuple, List[I2B2CoreWithUploadId]]
        for record in records:
            key = tuple(record.get(k) for k in cls.key_fields)
            if key in key_map:
                dups.setdefault(key, [key_map[key]]).append(record)
            else:
                key_map[key] = record
        return dups

    @classmethod
    def _add_or_update_records(
            cls, conn: Connection, table: Table,
            records: List["I2B2CoreWithUploadId"]) -> Tuple[int, int]:
        """
        Add or update the supplied table as needed to reflect the contents of records
        :param table: i2b2 sql connection
        :param records: records to apply
        :return: number of records added / modified
        """
        num_updates = 0
        num_inserts = 0
        inserts = []
        # Iterate over the records doing updates
        # Note: This is slow as molasses - definitely not optimal for batch work, but hopefully we'll be dealing with
        #    thousands to tens of thousands of records.  May want to move to ORM model if this gets to be an issue
        for record in records:
            keys = [(table.c[k] == getattr(record, k)) for k in cls.key_fields]
            key_filter = I2B2CoreWithUploadId._nested_fcn(and_, keys)
            rec_exists = conn.execute(
                select([table.c.upload_id]).where(key_filter)).rowcount
            if rec_exists:
                known_values = {
                    k: v
                    for k, v in record._freeze().items()
                    if v is not None and k not in cls.no_update_fields
                    and k not in cls.key_fields
                }
                vals = [table.c[k] != v for k, v in known_values.items()]
                val_filter = I2B2CoreWithUploadId._nested_fcn(or_, vals)
                known_values['update_date'] = record.update_date
                upd = update(table).where(and_(
                    key_filter, val_filter)).values(known_values)
                num_updates += conn.execute(upd).rowcount
            else:
                inserts.append(record._freeze())
        if inserts:
            if cls._check_dups:
                dups = cls._check_for_dups(inserts)
                nprints = 0
                # TODO: Figure out why duplicates are occuring -- they are very specific
                if dups:
                    print("{} duplicate records encountered".format(len(dups)))
                    for k, vals in dups.items():
                        if len(vals) == 2 and vals[0] == vals[1]:
                            inserts.remove(vals[1])
                        else:
                            if nprints < 20:
                                print("Key: {} has a non-identical dup".format(
                                    k))
                            elif nprints == 20:
                                print(".... more ...")
                            nprints += 1
                            for v in vals[1:]:
                                inserts.remove(v)
            # TODO: refactor this to load on a per-resource basis.  Temporary fix
            for insert in ListChunker(inserts, 500):
                num_inserts += conn.execute(table.insert(), insert).rowcount
        return num_inserts, num_updates
Esempio n. 17
0
class ObservationFact(I2B2CoreWithUploadId):
    _t = DynElements(I2B2CoreWithUploadId)

    key_fields = [
        "patient_num", "concept_cd", "modifier_cd", "start_date",
        "encounter_num", "instance_num", "provider_id"
    ]

    def __init__(self, fact_key: ObservationFactKey, concept_cd: str,
                 **kwargs) -> None:
        super().__init__(**kwargs)
        self._patient_num = fact_key.patient_num
        self._encounter_num = fact_key.encounter_num
        self._provider_id = fact_key.provider_id
        self._concept_cd = concept_cd
        self._start_date = fact_key.start_date
        self._modifier_cd = None
        self._instance_num = None
        self._valtype_cd = None
        self._tval_char = None
        self._nval_num = None
        self._valueflag_cd = None
        self._quantity_num = None
        self._units_cd = None
        self._end_date = None
        self._location_cd = None
        self._observation_blob = None
        self._confidence_num = None

    @DynObject.entry(_t)
    def encounter_num(self) -> int:
        """
        Encoded i2b2 patient visit number
        """
        return self._encounter_num

    @DynObject.entry(_t)
    def patient_num(self) -> int:
        """
        Encoded i2b2 patient number
        """
        return self._patient_num

    @DynObject.entry(_t)
    def concept_cd(self) -> str:
        """ Code for the observation of interest (i.e. diagnoses, procedures, medications, lab tests"""
        return self._concept_cd

    @DynObject.entry(_t)
    def provider_id(self) -> str:
        """
        Practitioner or provider id
        :return:
        """
        return self._provider_id

    @DynObject.entry(_t)
    def start_date(self) -> datetime:
        """ Starting date-time of the observation. (mm/dd/yy)"""
        return self._start_date

    @DynObject.entry(_t)
    def modifier_cd(self) -> str:
        """
        Code for modifier of interest (i.e. "Route", "DOSE").
        Note that the value columns are often used to hold the amounts such as "100" (mg) for the modifier of DOSE or
        "PO" for the modifier of ROUTE.
        :return:
        """
        return self._modifier_cd if self._modifier_cd else '@'

    @DynObject.entry(_t)
    def instance_num(self) -> int:
        """
        Encoded instance number that allows more than one modifier to be provided for each CONCEPT_CD.
        Each row will have a different MODIFIER_CD but a similar INSTANCE_NUM
        """
        return self._instance_num if self._instance_num is not None else 0

    @DynObject.entry(_t)
    def valtype_cd(self) -> str:
        """ The place where the value is stored
         'T' -- tval_char (Text (enums / short messages)
         'N' -- nval_num has number, tval_char has optional G, E, L (or other stuff where needed)
         'B' -- Raw text (notes / reports) observation_blob
         'NLP' -- NLP result text
         '@' -- no value
         'D' -- tval_char has a date in text and nval has a floating point representation (
                1996-10-13 00:00 = 19961013.0000
         ''  -- pure nval_num (?) - no tval_char?
         Note that the spec says that this is optional, but it is never null
          """
        return self._valtype_cd.code if isinstance(self._valtype_cd, ValueTypeCd) else \
            self._valtype_cd if self._valtype_cd is not None else '@'

    @DynObject.entry(_t)
    def tval_char(self) -> Optional[str]:
        """
        Used in conjunction with VALTYPE_CD = "T" or "N"
        When "T"
            Stores the text value
        When "N"
            'E' - Equals, 'NE' - Not equal, 'L' - Less than, 'LE' - LE, 'G' - Greater than, 'GE
        :return:
        """
        return self._tval_char

    @DynObject.entry(_t)
    def nval_num(self) -> Optional[float]:
        """
        Used in conjunction with VALTYPE_CD = "N" to store a numeric value
        """
        return self._nval_num

    @DynObject.entry(_t)
    def valueflag_cd(self) -> Optional[str]:
        """
        A code that represents the type of value present in the NVAL_NUM, the TVAL_CHAR or OBSERVATION_BLOB columns
        'X' - Encrypted text in the blob column (B)
        'H' - High      (N or T)
        'L' - Low
        'A' - Abnormal
        """
        return self._valueflag_cd

    @DynObject.entry(_t)
    def quantity_num(self) -> Optional[float]:
        """
        The number of observations represented by this fact (no known examples)
        """
        return self._quantity_num

    @DynObject.entry(_t)
    def units_cd(self) -> Optional[str]:
        """
        A textual description of the units associated with a value
        :return:
        """
        return self._units_cd

    @DynObject.entry(_t)
    def end_date(self) -> Optional[datetime]:
        """
        The date that the observation ended. If the date is derived or calculated from another observation (like a
        report) then the end date is the same as the observation it was derived or calculated from
        WARNING: two meanings for same field
        """
        return self._end_date

    @DynObject.entry(_t)
    def location_cd(self) -> Optional[str]:
        """
        A code representing the hospital associated with this visit
        """
        return self._location_cd

    @DynObject.entry(_t)
    def observation_blob(self) -> Optional[str]:
        """
        XML data that includes partially structured and unstructured data about an observation
        """
        return self._observation_blob

    @DynObject.entry(_t)
    def confidence_num(self) -> Optional[float]:
        """
        A code or number representing the confidence in the accuracy of the data. (No known examples)
        Q: 'code' in a numeric situation?
        """
        return None

    DynObject._after_root(_t)

    # text_search_index is auto-generated (?)
    # @DynObject.entry(_t)
    # def text_search_index(self) -> float:
    #     return None
    @property
    def pk(self) -> Tuple:
        return self.patient_num, self.encounter_num, self.instance_num, self.concept_cd, self.modifier_cd

    def __lt__(self, other: "ObservationFact") -> bool:
        return self.pk < other.pk

    @classmethod
    def delete_upload_id(cls, tables: I2B2Tables, upload_id: int) -> int:
        """
        Delete all observation_fact records with the supplied upload_id
        :param tables: i2b2 sql connection
        :param upload_id: upload identifier to remove
        :return: number or records that were deleted
        """
        return cls._delete_upload_id(tables.crc_connection,
                                     tables.observation_fact, upload_id)

    @classmethod
    def delete_sourcesystem_cd(cls, tables: I2B2Tables,
                               sourcesystem_cd: str) -> int:
        """
        Delete all records with the supplied sourcesystem_cd
        :param tables: i2b2 sql connection
        :param sourcesystem_cd: sourcesystem_cd to remove
        :return: number or records that were deleted
        """
        return cls._delete_sourcesystem_cd(tables.crc_connection,
                                           tables.observation_fact,
                                           sourcesystem_cd)

    @classmethod
    def add_or_update_records(
            cls, tables: I2B2Tables,
            records: List["ObservationFact"]) -> Tuple[int, int]:
        """
        Add or update the observation_fact table as needed to reflect the contents of records
        :param tables: i2b2 sql connection
        :param records: records to apply
        :return: number of records added / modified
        """
        return cls._add_or_update_records(tables.crc_connection,
                                          tables.observation_fact, records)

    def _date_val(self, dt: datetime) -> None:
        """
        Add a date value
        :param dt: datetime to add
        """
        self._tval_char = dt.strftime('%Y-%m-%d %H:%M')
        self._nval_num = (dt.year * 10000) + (dt.month * 100) + dt.day + \
                         (((dt.hour / 100.0) + (dt.minute / 10000.0)) if isinstance(dt, datetime) else 0)

    def summary(self) -> str:
        return f"({self.instance_num}, {self.concept_cd}, {self.modifier_cd}, {self.tval_char}, {self.nval_num})"
Esempio n. 18
0
class EncounterMapping(I2B2CoreWithUploadId):
    _t = DynElements(I2B2CoreWithUploadId)

    key_fields = [
        "encounter_ide", "encounter_ide_source", "project_id", "patient_ide",
        "patient_ide_source"
    ]

    def __init__(self, encounter_ide: str, encounter_ide_source: str,
                 project_id: str, encounter_num: int, patient_ide: str,
                 patient_ide_source: str, encounter_ide_status: Optional[
                     EncounterIDEStatus.EncounterIDEStatusCode], **kwargs):
        self._encounter_ide = encounter_ide
        self._encounter_ide_source = encounter_ide_source
        self._project_id = project_id
        self._encounter_num = encounter_num
        self._patient_ide = patient_ide
        self._patient_ide_source = patient_ide_source
        self._encounter_ide_status = encounter_ide_status

        super().__init__(**kwargs)

    @DynObject.entry(_t)
    def encounter_ide(self) -> str:
        return self._encounter_ide

    @DynObject.entry(_t)
    def encounter_ide_source(self) -> str:
        return self._encounter_ide_source

    @DynObject.entry(_t)
    def project_id(self) -> Optional[str]:
        return self._project_id

    @DynObject.entry(_t)
    def encounter_num(self) -> int:
        return self._encounter_num

    @DynObject.entry(_t)
    def patient_ide(self) -> str:
        return self._patient_ide

    @DynObject.entry(_t)
    def patient_ide_source(self) -> str:
        return self._patient_ide_source

    @DynObject.entry(_t)
    def encounter_ide_status(self) -> Optional[str]:
        return self._encounter_ide_status.code if self._encounter_ide_status else None

    @classmethod
    def delete_upload_id(cls, tables: I2B2Tables, upload_id: int) -> int:
        """
        Delete all patient_dimension records with the supplied upload_id
        :param tables: i2b2 sql connection
        :param upload_id: upload identifier to remove
        :return: number or records that were deleted
        """
        return cls._delete_upload_id(tables.crc_connection,
                                     tables.encounter_mapping, upload_id)

    @classmethod
    def delete_sourcesystem_cd(cls, tables: I2B2Tables,
                               sourcesystem_cd: str) -> int:
        """
        Delete all records with the supplied sourcesystem_cd
        :param tables: i2b2 sql connection
        :param sourcesystem_cd: sourcesystem_cd to remove
        :return: number or records that were deleted
        """
        return cls._delete_sourcesystem_cd(tables.crc_connection,
                                           tables.encounter_mapping,
                                           sourcesystem_cd)

    @classmethod
    def add_or_update_records(
            cls, tables: I2B2Tables,
            records: List["EncounterMapping"]) -> Tuple[int, int]:
        """
        Add or update the patient_dimension table as needed to reflect the contents of records
        :param tables: i2b2 sql connection
        :param records: records to apply
        :return: number of records added / modified
        """
        return cls._add_or_update_records(tables.crc_connection,
                                          tables.encounter_mapping, records)
Esempio n. 19
0
class OntologyEntry(I2B2Core):
    _t = DynElements(I2B2Core)
    graph = None  # type: Graph
    ontology_name = "FHIR"

    def __init__(self,
                 c_full_name: str,
                 query: Query,
                 visualattributes: VisualAttributes = None,
                 c_basecode: Optional[str] = None,
                 primitive_type: Optional[URIRef] = None,
                 **kwargs):
        """
        Initialize an ontology entry.
        :param c_full_name: Full name of entry (e.g. '\\FHIR\\class\\subclass\\...\\item\\')
        :param query: Dimension table query for item
        :param visualattributes: VisualAttributes for item
        :param c_basecode: "uri" for item
        :param primitive_type: type used to construct c_metadataxml
        :param kwargs: Additional arguments for i2b2_core
        """
        super().__init__(**kwargs)
        assert (c_full_name.endswith('\\'))
        self._c_fullname = c_full_name
        self._query = query
        self._visualattributes = visualattributes if visualattributes else VisualAttributes(
        )
        self._c_basecode = c_basecode
        self._modifier_exclusion = False
        self._primitive_type = primitive_type

    @DynObject.entry(_t)
    def c_hlevel(self) -> int:
        return self.c_fullname[:-1].count('\\') - 1

    @DynObject.entry(_t)
    def c_fullname(self) -> str:
        return self._c_fullname

    @DynObject.entry(_t)
    def c_name(self) -> str:
        return self.c_fullname[:-1].rsplit('\\', 1)[1]

    @DynObject.entry(_t)
    def c_synonym_cd(self) -> str:
        """ Two or more synonyms of each other will have the same c_basecode """
        return "N"

    @DynObject.entry(_t)
    def c_visualattributes(self) -> str:
        return str(self._visualattributes)

    @DynObject.entry(_t)
    def c_totalnum(self) -> Optional[int]:
        return None

    @DynObject.entry(_t)
    def c_basecode(self) -> Optional[str]:
        return self._c_basecode

    @DynObject.entry(_t)
    def c_metadataxml(self) -> Optional[str]:
        return metadata_xml(self._primitive_type, self.c_basecode, self.c_name, self.update_date) \
            if self._primitive_type else None

    @DynObject.entry(_t)
    def c_facttablecolumn(self) -> str:
        return self._query.key

    @DynObject.entry(_t)
    def c_tablename(self) -> str:
        return self._query.table

    @DynObject.entry(_t)
    def c_columnname(self) -> str:
        return self._query.where_subj

    @DynObject.entry(_t)
    def c_columndatatype(self) -> str:
        return 'N' if self._query.numeric_key else 'T'

    @DynObject.entry(_t)
    def c_operator(self) -> str:
        return self._query.where_pred

    @DynObject.entry(_t)
    def c_dimcode(self) -> str:
        return self._query.where_obj

    @DynObject.entry(_t)
    def c_comment(self) -> Optional[str]:
        return None

    @DynObject.entry(_t)
    def c_tooltip(self) -> Optional[str]:
        return None

    @DynObject.entry(_t)
    def m_applied_path(self) -> str:
        return '@'

    DynObject._after_root(_t)

    @DynObject.entry(_t)
    def valuetype_cd(self) -> Optional[str]:
        return None

    @DynObject.entry(_t)
    def m_exclusion_cd(self) -> Optional[str]:
        return 'X' if self._modifier_exclusion else None

    @DynObject.entry(_t)
    def c_path(self) -> Optional[str]:
        # return self.basename[:-1].rsplit('\\', 1)[0]
        return None

    @DynObject.entry(_t)
    def c_symbol(self) -> Optional[str]:
        # return self.basename[:-1].rsplit('\\', 1)[1]
        return None

    def __lt__(self, other):
        return self.c_fullname + self.m_applied_path < other.c_fullname + self.m_applied_path

    def __eq__(self, other):
        return self.c_fullname + self.m_applied_path == other.c_fullname + self.m_applied_path
Esempio n. 20
0
class TableAccess(DynObject):
    _t = DynElements(DynObject)
    _visualattributes = VisualAttributes("CA")
    _query = ConceptQuery("\\FHIR\\")

    @DynObject.entry(_t)
    def c_table_cd(self) -> str:
        return "FHIR"

    @DynObject.entry(_t)
    def c_table_name(self) -> str:
        return i2b2tablenames.phys_name(i2b2tablenames.ontology_table)

    @DynObject.entry(_t)
    def c_protected_access(self) -> str:
        return "N"

    @DynObject.entry(_t)
    def c_hlevel(self) -> int:
        return 1

    @DynObject.entry(_t)
    def c_fullname(self) -> str:
        return '\\FHIR\\'

    @DynObject.entry(_t)
    def c_name(self) -> str:
        return "FHIR Resources"

    @DynObject.entry(_t)
    def c_synonym_cd(self) -> str:
        return "N"

    @DynObject.entry(_t)
    def c_visualattributes(self) -> str:
        return str(self._visualattributes)

    @DynObject.entry(_t)
    def c_totalnum(self) -> Optional[int]:
        return None

    @DynObject.entry(_t)
    def c_basecode(self) -> Optional[str]:
        return None

    @DynObject.entry(_t)
    def c_metadataxml(self) -> Optional[str]:
        return None

    @DynObject.entry(_t)
    def c_facttablecolumn(self) -> str:
        return self._query.key

    @DynObject.entry(_t)
    def c_dimtablename(self) -> str:
        return self._query.table

    @DynObject.entry(_t)
    def c_columnname(self) -> str:
        return self._query.where_subj

    @DynObject.entry(_t)
    def c_columndatatype(self) -> str:
        return 'N' if self._query.numeric_key else 'T'

    @DynObject.entry(_t)
    def c_operator(self) -> str:
        return self._query.where_pred

    @DynObject.entry(_t)
    def c_dimcode(self) -> str:
        return self._query.where_obj

    @DynObject.entry(_t)
    def c_comment(self) -> Optional[str]:
        return None

    @DynObject.entry(_t)
    def c_tooltip(self) -> Optional[str]:
        return "FHIR Resource"

    @DynObject.entry(_t)
    def c_entry_date(self) -> Optional[str]:
        return None

    @DynObject.entry(_t)
    def c_change_date(self) -> Optional[str]:
        return None

    @DynObject.entry(_t)
    def c_status_cd(self) -> Optional[str]:
        return None

    @DynObject.entry(_t)
    def valuetype_cd(self) -> Optional[str]:
        return None
Esempio n. 21
0
class PatientDimension(I2B2CoreWithUploadId):
    _t = DynElements(I2B2CoreWithUploadId)

    key_fields = ["patient_num"]

    def __init__(self, patient_num, vital_status_cd: VitalStatusCd,
                 **kwargs) -> None:
        self._patient_num = patient_num
        self._vital_status_code = vital_status_cd
        self._birth_date = None
        self._death_date = None
        self._sex_cd = None
        self._age_in_years_num = None
        self._language_cd = None
        self._race_cd = None
        self._marital_status_cd = None
        self._religion_cd = None
        self._zip_cd = None
        self._statecityzip_path = None
        self._income_cd = None
        self._patient_blob = None
        super().__init__(**kwargs)

    @DynObject.entry(_t)
    def patient_num(self) -> int:
        """
        Reference number for the patient
        """
        return self._patient_num

    @DynObject.entry(_t)
    def vital_status_cd(self) -> Optional[str]:
        """
        Encoded i2b2 patient visit number
        """
        return self._vital_status_code.code

    @DynObject.entry(_t)
    def birth_date(self) -> Optional[datetime]:
        """
        Encoded i2b2 patient visit number
        """
        return self._birth_date

    @DynObject.entry(_t)
    def death_date(self) -> Optional[datetime]:
        """
        Encoded i2b2 patient visit number
        """
        return self._death_date

    @DynObject.entry(_t)
    def sex_cd(self) -> Optional[str]:
        """
        Encoded i2b2 patient visit number
        """
        return self._sex_cd

    @DynObject.entry(_t)
    def age_in_years_num(self) -> Optional[int]:
        """
        Encoded i2b2 patient visit number
        """
        return self._age_in_years_num

    @DynObject.entry(_t)
    def language_cd(self) -> Optional[str]:
        return self._language_cd

    @DynObject.entry(_t)
    def race_cd(self) -> Optional[str]:
        """
        Encoded i2b2 patient visit number
        """
        return self._race_cd

    @DynObject.entry(_t)
    def marital_status_cd(self) -> Optional[str]:
        return self._marital_status_cd

    @DynObject.entry(_t)
    def religion_cd(self) -> Optional[str]:
        return self._religion_cd

    @DynObject.entry(_t)
    def zip_cd(self) -> Optional[str]:
        return self._zip_cd

    @DynObject.entry(_t)
    def statecityzip_path(self) -> Optional[str]:
        return self._statecityzip_path

    @DynObject.entry(_t)
    def income_cd(self) -> Optional[str]:
        return self._income_cd

    @DynObject.entry(_t)
    def patient_blob(self) -> Optional[str]:
        return self._patient_blob

    @classmethod
    def delete_upload_id(cls, tables: I2B2Tables, upload_id: int) -> int:
        """
        Delete all patient_dimension records with the supplied upload_id
        :param tables: i2b2 sql connection
        :param upload_id: upload identifier to remove
        :return: number or records that were deleted
        """
        return cls._delete_upload_id(tables.crc_connection,
                                     tables.patient_dimension, upload_id)

    @classmethod
    def delete_sourcesystem_cd(cls, tables: I2B2Tables,
                               sourcesystem_cd: str) -> int:
        """
        Delete all records with the supplied sourcesystem_cd
        :param tables: i2b2 sql connection
        :param sourcesystem_cd: sourcesystem_cd to remove
        :return: number or records that were deleted
        """
        return cls._delete_sourcesystem_cd(tables.crc_connection,
                                           tables.patient_dimension,
                                           sourcesystem_cd)

    @classmethod
    def add_or_update_records(
            cls, tables: I2B2Tables,
            records: List["PatientDimension"]) -> Tuple[int, int]:
        """
        Add or update the patient_dimension table as needed to reflect the contents of records
        :param tables: i2b2 sql connection
        :param records: records to apply
        :return: number of records added / modified
        """
        return cls._add_or_update_records(tables.crc_connection,
                                          tables.patient_dimension, records)
Esempio n. 22
0
class PatientMapping(I2B2CoreWithUploadId):
    _t = DynElements(I2B2CoreWithUploadId)

    key_fields = ["patient_ide", "patient_ide_source", "project_id"]

    def __init__(self, patient_num: int, patient_id: str, patient_ide_status: PatientIDEStatus.PatientIDEStatusCode,
                 patient_ide_source: str, project_id: str, **kwargs):
        """
        Construct a patient mapping entry
        :param patient_num: patient number
        :param patient_id: clear text patient identifier
        :param patient_ide_status: status code
        :param patient_ide_source: clear text patient identifier source
        :param project_id: project identifier
        :param kwargs:

        The patient number is the key to the patient dimension file.  The patient_mapping file has, at a minimum,
        one entry
        """
        self._patient_num = patient_num
        self._patient_ide = patient_id
        self._patient_ide_status = patient_ide_status
        self._patient_ide_source = patient_ide_source
        self._project_id = project_id

        super().__init__(**kwargs)

    @DynObject.entry(_t)
    def patient_ide(self) -> str:
        """
        The encrypted patient identifier
        """
        return self._patient_ide

    @DynObject.entry(_t)
    def patient_ide_source(self) -> str:
        """
        The source system (source of what I don't really know)
        """
        return self._patient_ide_source

    @DynObject.entry(_t)
    def patient_num(self) -> int:
        """
        Patient number in the patient dimension file
        """
        return self._patient_num

    @DynObject.entry(_t)
    def patient_ide_status(self) -> str:
        """
        Patient number in the patient dimension file
        """
        return self._patient_ide_status.code

    @DynObject.entry(_t)
    def project_id(self) -> str:
        """
        Project identifier
        """
        return self._project_id

    @classmethod
    def delete_upload_id(cls, tables: I2B2Tables, upload_id: int) -> int:
        """
        Delete all patient_mapping records with the supplied upload_id
        :param tables: i2b2 sql connection
        :param upload_id: upload identifier to remove
        :return: number or records that were deleted
        """
        return cls._delete_upload_id(tables.crc_connection, tables.patient_mapping, upload_id)

    @classmethod
    def delete_sourcesystem_cd(cls, tables: I2B2Tables, sourcesystem_cd: str) -> int:
        """
        Delete all records with the supplied sourcesystem_cd
        :param tables: i2b2 sql connection
        :param sourcesystem_cd: sourcesystem_cd to remove
        :return: number or records that were deleted
        """
        return cls._delete_sourcesystem_cd(tables.crc_connection, tables.patient_mapping, sourcesystem_cd)

    @classmethod
    def add_or_update_records(cls, tables: I2B2Tables, records: List["PatientMapping"]) -> Tuple[int, int]:
        """
        Add or update the patient_mapping table as needed to reflect the contents of records
        :param tables: i2b2 sql connection
        :param records: records to apply
        :return: number of records added / modified
        """
        return cls._add_or_update_records(tables.crc_connection, tables.patient_mapping, records)