def filter_records(records, codes, resource_name): records_filtered = [] for record in records: logger.info(f"filtering record: {record}") for c in codes: system = c["system"] code = c["code"] is_regex = c["is_regex"] if resource_name == "MedicationRequest": code2 = record.get("medication", {}).get("medicationCodeableConcept") else: code2 = record.get("code") if code2 is None: return Left({ "error": f"malformated record: no code", "record": record }) coding2 = code2.get("coding") if coding2 is None: return Left({ "error": f"malformated record: no coding under code", "record": record }) for c2 in coding2: if c2["system"] == system: if (is_regex and re.search(code, "^" + c2["code"] + "$")) or c2["code"] == code: records_filtered.append(record) return Right(records_filtered)
def address(pat): extensions = pat.get("extension", []) lat = list( map( lambda x: x["valueDecimal"], filter(lambda x: x.get("url", "").lower() == "latitude", extensions))) lon = list( map( lambda x: x["valueDecimal"], filter(lambda x: x.get("url", "").lower() == "longitude", extensions))) if len(lat) > 1: return Left("more than one latitudes") if len(lon) > 1: return Left("more than one longitudes") if len(lat) == 0: if len(lon) == 0: return Right(None) else: return Left("a longitude without a latitude") elif len(lon) == 0: return Left("a latitude without a longitude") else: return Right({"latitude": lat[0], "longitude": lon[0]})
def query_feature(table, feature): feature_def = features_dict[table][feature] ty = feature_def["type"] if ty == "string": if "enum" not in feature_def: return Left("node has type string but has no enum") else: return Right({ "feature_name": feature, "feature_qualifiers": [{ "operator": "=", "value": v } for v in feature_def["enum"]] }) elif ty == "integer": if "maximum" not in feature_def or "minimum" not in feature_def: return Left( "node has type integer but has no maximum or has no minimum") else: return Right({ "feature_name": feature, "feature_qualifiers": [{ "operator": "=", "value": v } for v in range(feature_def["minimum"], feature_def["maximum"] + 1)] }) else: return Left(f"unsupported node type {ty}")
def calculate_age2(born, timestamp): try: today = strtodate(timestamp) except Exception as e: return Left({"error": str(e)}) return Right(today.year - born.year - ((today.month, today.day) < (born.month, born.day)))
def get_features_by_identifier(table, identifier): if table in input_dict: identifier_dict = input_dict[table] else: raise Left(f"Cannot find table {table}, available {input_dict}") return Right([feature for feature, identifiers in identifier_dict.items() if identifier in identifiers])
def update_key(d, ok, nk): curr_keys = list(d.keys()) if ok in curr_keys: i = curr_keys.index(ok) d.insert(i, nk, d.pop(ok)) return Right(()) else: return Left(f"variable {ok} no longer exists")
def merge(a, b, err): if a is None: return Right(b) elif b is None: return Right(a) elif a == b: return Right(a) else: return Left(f"err={err} a={a} b={b}")
def validateTable(ctx, tablename, tfname, kvp): with open(tfname, "r", newline="", encoding="utf-8") as tfi: reader = csv.reader(tfi) header = next(reader) n = len(header) i = 0 for row in reader: n2 = len(row) if n2 != n: return Left(f"row {i} number of items, expected {n}, encountered {n2}") i += 1 seen = set() dups = [] for x in header: if x in seen: dups.append(x) else: seen.add(x) if len(dups) > 0: return Left(f"duplicate header(s) in upload {dups}") header2 = list(kvp.keys()) i2 = [a for a in header if a in header2] if len(i2) > 0: return Left(f"duplicate header(s) in input {i2}") conn = connect(user=ctx["dbuser"], password=ctx["dbpass"], host=ctx["dbhost"], dbname=ctx["dbname"]) cursor = conn.cursor() try: cursor.execute(''' select column_name, data_type from information_schema.columns where table_schema NOT IN ('information_schema', 'pg_catalog') and table_name=%s order by table_schema, table_name ''', (tablename,)) rows = cursor.fetchall() header3 = list(map(lambda r:r[0], rows)) d2 = [a for a in header + header2 if a not in header3] if len(d2) > 0: return Left(f"undefined header(s) in input {d2} available {header3}") return Right(()) finally: cursor.close() conn.close()
def one(xs): if len(xs) == 1: return Right(xs[0]) elif len(xs) == 0: return Right(None) else: return Left({ "variableValue": { "value": None }, "how": "more than one record found", "certitude": 0 })
def lookupClinicalFromRecord(patient_id, data, v, timestamp): clinical = v["id"] unit = v.get("units") filter_data, feature, unit2 = mapping.get(clinical) if unit is None: unit = unit2 if feature is None: return Left((f"cannot find mapping for {clinical}", 400)) else: return filter_data( patient_id, data).bind(lambda records: feature(records, unit, timestamp)).map( add_variable(clinical))
def handle_clinical_feature_variables(config): if len(config) > 0: if len(config) > 1: log(syslog.LOG_ERR, f"more than one configs for plugin {piid}", "pds") clinical_feature_variable_objects = config[0]["settingsDefaults"][ "patientVariables"] def cfvo_to_cfvo2(cfvo): cfvo2 = {**cfvo} return Right(cfvo2) return list_traversable_either_applicative.sequence( list(map(cfvo_to_cfvo2, clinical_feature_variable_objects))) else: return Left(f"no configs for plugin {piid}")
def age(patient, unit, timestamp): if unit is not None and unit != "year": return Left((f"unsupported unit {unit}", 400)) if patient == None: return Right({ "variableValue": { "value": None }, "certitude": 0, "how": "record not found" }) else: if "birthDate" in patient: birth_date = patient["birthDate"] date_of_birth = strtodate2(birth_date) today = timestamp.strftime("%Y-%m-%d") mage = calculate_age2(date_of_birth, timestamp) return mage.map( lambda age: { "variableValue": { "value": age, "unit": "year" }, "certitude": 2, "how": { "request_timestamp": today, "computed_from": ["request_timestamp", "birthDate"], "birthDate": { "computed_from": { "resourceType": "Patient", "field": "birthDate" }, "value": birth_date } } }) else: return Right({ "variableValue": { "value": None }, "certitude": 0, "how": "birthDate not set" })
def handle_src_and_tgt_features(src_features, tgt_features): edge_property_value = [] for src_feature in src_features: for tgt_feature in tgt_features: edge = co_occurrence_feature_edge(conn, table, year, cohort_features, src_feature, tgt_feature) if isinstance(edge, Right): edge_property_value.append({ "src_feature": src_feature, "tgt_feature": tgt_feature, "p_value": edge.value }) else: return edge if len(edge_property_value) == 0: return Left("no edge found") else: return Right(edge_property_value)
def get(self, table, key): FHIR, GEOID, NearestRoad, NearestPoint, Visit = self.get_sub_objects() FHIR_keys, GEOID_keys, NearestRoad_keys, NearestPoint_keys, Visit_keys = self.get_sub_keys( FHIR, GEOID, NearestRoad, NearestPoint, Visit) if key in FHIR_keys: return Right(self.obj["FHIR"][key]) for name, keys in GEOID_keys.items(): if key in keys: return Right(GEOID[name]) for name, keys in NearestRoad_keys.items(): if old_key in keys: return Right(NearestRoad[name]) for name, keys in NearestPoint_keys.items(): if old_key in keys: return Right(NearestPoint[name]) return Left(f"variable {old_key} no longer exists")
def update_key(self, table, old_key, new_key): FHIR, GEOID, NearestRoad, NearestPoint, Visit = self.get_sub_objects() FHIR_keys, GEOID_keys, NearestRoad_keys, NearestPoint_keys, Visit_keys = self.get_sub_keys( FHIR, GEOID, NearestRoad, NearestPoint, Visit) if old_key in FHIR_keys: update_key(self.obj["FHIR"], old_key, new_key) return Right(()) for name, keys in GEOID_keys.items(): if old_key in keys: columns = GEOID[name]["columns"] for column_name, var_name in columns.items(): if var_name == old_key: columns[column_name] = new_key return Right(()) for name, keys in NearestRoad_keys.items(): if old_key in keys: nearest_road = NearestRoad[name] if old_key == nearest_road["distance_feature_name"]: nearest_road["distance_feature_name"] = new_key else: for attribute_name, feature in nearest_road[ "attributes_to_features_map"].items(): if feature["feature_name"] == old_key: feature["feature_name"] = new_key return Right(()) for name, keys in NearestPoint_keys.items(): if old_key in keys: nearest_point = NearestPoint[name] if old_key == nearest_point["distance_feature_name"]: nearest_point["distance_feature_name"] = new_key else: for attribute_name, feature in nearest_point[ "attributes_to_features_map"].items(): if feature["feature_name"] == old_key: feature["feature_name"] = new_key return Right(()) return Left(f"variable {old_key} no longer exists")
def age(patient, unit, timestamp): if unit is not None and unit != "year": return Left((f"unsupported unit {unit}", 400)) if patient == None: return Right({ "variableValue": { "value": None }, "certitude": 0, "how": "record not found" }) else: if "birthDate" in patient: birth_date = patient["birthDate"] date_of_birth = strtodate(birth_date) today = strtodate(timestamp).strftime("%Y-%m-%d") mage = calculate_age2(date_of_birth, timestamp) return mage.map( lambda age: { "variableValue": { "value": age, "units": "year" }, "certitude": 2, "how": f"Current date '{today}' minus patient's birthdate (FHIR resource 'Patient' field>'birthDate' = '{birth_date}')" }) else: return Right({ "variableValue": { "value": None }, "certitude": 0, "how": "birthDate not set" })
def update_value(self, table, key, new_value): FHIR, GEOID, NearestRoad, NearestPoint, Visit = self.get_sub_objects() FHIR_keys, GEOID_keys, NearestRoad_keys, NearestPoint_keys, Visit_keys = self.get_sub_keys( FHIR, GEOID, NearestRoad, NearestPoint, Visit) if key in FHIR_keys: self.obj["FHIR"][key] = new_value return Right(()) for name, keys in GEOID_keys.items(): if key in keys: GEOID[name] = new_value return Right(()) for name, keys in NearestRoad_keys.items(): if key in keys: NearestRoad[name] = new_value return Right(()) for name, keys in NearestPoint_keys.items(): if key in keys: NearestPoint[name] = new_value return Right(()) return Left(f"variable {key} no longer exists")
def get(self, table, key): if key in self.obj[table]: val = self.obj[table][key] return Right(val) else: return Left(f"variable {key} does not exist")
def test_eq_on_Left(): assert Left(1) == Left(1)
def test_sequence(): either = list_traversable(either_monad) assert either.sequence([Right(1), Right(2), Right(3)]) == Right([1,2,3]) assert either.sequence([Left(1), Right(2), Right(3)]) == Left(1)
def test_ne_on_Right(): assert Right(1) != Right(2) assert Right(1) != Left(1)
def get(self, table, key): val = self.obj[table].get(key) if val is None: return Left(f"variable {key} no longer exists") else: return Right(val)
def test_rec(): assert Left(1).rec(lambda x: x, lambda x: x + 1) == 1 assert Right(2).rec(lambda x: x + 1, lambda x: x) == 2
def error_code(result): return result.rec( lambda err: Left(("not found", 404) if err[0]["status_code"] == 404 else err), lambda config: Right(config))
def test_bind_on_Left(): assert Left(1).bind(lambda x: Right(2)) == Left(1)
def test_pickle_Left(): pickle.dumps(Left(1))
def test_map_on_Left(): assert Left(1).map(lambda x: 2) == Left(1)
def update_value(d, k, nv): if k in d: d[k] = nv return Right(()) else: return Left(f"variable {k} does not exist")
def query_records(records, codes, unit, timestamp, clinical_variable, resource_name): if records == None: return Right({"value": None, "certitude": 0, "how": "no record found"}) records_filtered = [] for record in records: for c in codes: system = c["system"] code = c["code"] is_regex = c["is_regex"] code2 = record.get("code") if code2 is None: return Left({ "error": f"malformated record: no code", "record": record }) coding2 = code2.get("coding") if coding2 is None: return Left({ "error": f"malformated record: no coding under code", "record": record }) for c2 in coding2: if c2["system"] == system: if (is_regex and re.search(code, "^" + c2["code"] + "$")) or c2["code"] == code: records_filtered.append(record) if len(records_filtered) == 0: from_code = calculation(codes) return Right({ "variableValue": { "value": None }, "certitude": 0, "how": f"no record found code {from_code}" }) else: ts = strtots(timestamp) def key(a): ext_key = extract_key(a) if ext_key is None: return float("inf") else: return abs(strtots(ext_key) - ts) record = min(records_filtered, key=key) keyr = extract_key(record) if keyr is None: ts = None cert = 1 else: ts = extract_key(record) cert = 2 vq = record.get("valueQuantity") if vq is not None: v = vq["value"] from_u = vq.get("units") if from_u is None: from_u = vq.get("code") mv = convert(v, from_u, unit) if isinstance(mv, Left): return mv else: v = mv.value else: v = True from_u = None c = calculation_template(clinical_variable, resource_name, timestamp, record, unit) return Right({ "variableValue": { "value": v, **({ "units": unit } if unit is not None else { "units": from_u } if from_u is not None else {}), }, "certitude": cert, "timestamp": ts, "how": c })
def feature_names(table, node_curie): return (maybe.from_python(node_curie).rec( Right, Left("no curie specified at node")).bind( partial(get_features_by_identifier, table)))