refs/refs/FhirToCdm/FhirToCdmMappings.cs

lines 453–480 625 lines · cs
1using Hl7.Fhir.Model;
2using org.ohdsi.cdm.framework.common.Lookups;
3using org.ohdsi.cdm.framework.common.Omop;
4using System;
5using System.Collections.Generic;
6using System.Linq;
7using omop = org.ohdsi.cdm.framework.common.Omop;
9namespace FHIRtoCDM
11 public class FhirToCdmMappings
12 {
13 private Vocabulary _vocabulary;
15 public FhirToCdmMappings(Vocabulary vocabulary)
16 {
17 _vocabulary = vocabulary;
18 }
20 public IEnumerable<Tuple<omop.Person, List<omop.Location>>> CreatePersonAndLocations(Bundle fhir)
21 {
22 //provider_id Patient.generalPractitioner Patient
23 //care_site_id BodySite.patient BodySite
24 //birth_datetime Patient.birthDate us-core - patient
25 //location_id Patient.address Patient
27 var locations = new HashSet<omop.Location>();
28 //var pat = fhir.Entry.FirstOrDefault(e => e.Resource.TypeName == "Patient");
29 foreach (var pat in fhir.Entry.Where(e => e.Resource.TypeName == "Patient"))
30 {
31 var patient = (Patient)pat.Resource;
32 var person = new omop.Person
33 {
34 GenderSourceValue = patient.Gender.ToString(),
35 PersonSourceValue = patient.Id,
36 YearOfBirth = DateTime.Parse(patient.BirthDate).Year,
37 MonthOfBirth = DateTime.Parse(patient.BirthDate).Month,
38 DayOfBirth = DateTime.Parse(patient.BirthDate).Day,
39 EthnicityConceptId = 0
40 };
42 foreach (var ex in patient.Extension)
43 {
44 if (ex.Url.ToLower().Contains("death"))
45 {
47 }
48 }
50 switch (person.GenderSourceValue)
51 {
52 case "Male":
53 person.GenderConceptId = 8507;
54 break;
56 case "Female":
57 person.GenderConceptId = 8532;
58 break;
60 default:
61 person.GenderConceptId = 0;
62 break;
63 }
65 if (patient.GeneralPractitioner.Count > 0)
66 {
68 }
70 foreach (var address in patient.Address)
71 {
72 var location = new omop.Location
73 {
74 City = address.City,
75 State = address.State,
76 Zip = address.PostalCode,
77 Country = address.Country
78 };
80 //var line = address.Line.ToList();
82 //if (line.Count > 0)
83 // location.Address1 = line[0];
85 //if (line.Count > 1)
86 // location.Address2 = line[1];
88 locations.Add(location);
89 }
91 foreach (var item in patient.Extension)
92 {
93 if (item.Url == "http://hl7.org/fhir/StructureDefinition/us-core-race")
94 {
95 if (item.Value != null)
96 {
97 person.RaceSourceValue = ((Hl7.Fhir.Model.Coding)item.Value).Display;
98 }
99 else if (item.Extension.Count > 0)
100 {
101 person.RaceSourceValue = ((Hl7.Fhir.Model.Coding)item.Extension[0].Value).Display;
102 }
103 break;
104 }
105 else if (item.Url == "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity")
106 {
107 if (item.Value != null)
108 {
109 person.EthnicitySourceValue = ((Hl7.Fhir.Model.Coding)item.Value).Display;
110 }
111 else if (item.Extension.Count > 0)
112 {
113 person.RaceSourceValue = ((Hl7.Fhir.Model.Coding)item.Extension[0].Value).Display;
114 }
115 break;
116 }
117 }
119 if (!string.IsNullOrEmpty(person.EthnicitySourceValue))
120 {
121 switch (person.EthnicitySourceValue.ToUpper())
122 {
123 case "CENTRAL_AMERICAN":
124 case "DOMINICAN":
125 case "MEXICAN":
126 case "PUERTO_RICAN":
127 case "SOUTH_AMERICAN":
128 person.EthnicityConceptId = 38003563;
129 break;
131 default:
132 person.EthnicityConceptId = 0;
133 break;
134 }
135 }
137 if (!string.IsNullOrEmpty(person.RaceSourceValue))
138 {
139 switch (person.RaceSourceValue.ToUpper())
140 {
141 case "ASIAN":
142 person.RaceConceptId = 8515;
143 break;
145 case "BLACK":
146 person.RaceConceptId = 8516;
147 break;
149 case "OTHER":
150 person.RaceConceptId = 8522;
151 break;
153 case "WHITE":
154 person.RaceConceptId = 8527;
155 break;
157 case "HISPANIC":
158 person.RaceConceptId = 0;
159 person.EthnicityConceptId = 38003563;
160 break;
162 default:
163 person.RaceConceptId = 0;
164 break;
165 }
166 }
168 yield return new Tuple<omop.Person, List<omop.Location>>(person, locations.ToList());
169 }
170 }
173 public IEnumerable<Tuple<KeyValuePair<string, VisitOccurrence>, Provider>> CreateVisitOccurenceAndProvider(Bundle fhir, Dictionary<string, long> personIds)
174 {
175 //care_site_id Encounter.location.location.identifier us-core - encounter, us - core - location
176 //admitting_source_concept_id Encounter.hospitalization.admitSource or Encounter.hospitalization.origin(location).type us - core - encounter, us - core - location
177 //discharge_to_concept_id Encounter.location.location.type us-core - encounter,us - core - location
178 //preceding_visit_occurence Encounter.partOf us-core - encounter
179 foreach (var item in fhir.Entry.Where(e => e.Resource.TypeName == "Encounter"))
180 {
181 var encounter = (Encounter)item.Resource;
183 var conceptId = 9202;
185 if (encounter.Class.Code.ToUpper() == "IMP")
186 conceptId = 9201;
187 else if (encounter.Class.Code.ToUpper() == "EMER")
188 conceptId = 9203;
190 if (encounter.Diagnosis.Count > 0)
191 {
193 }
195 if (encounter.ReasonCode.Count > 0)
196 {
197 foreach (var reasonCode in encounter.ReasonCode)
198 {
199 foreach (var code in reasonCode.Coding)
200 {
201 if (code.Code == "308646001")
202 {
204 }
205 }
207 }
208 }
210 if (encounter.Extension.Count > 0)
211 {
213 }
215 Provider provider = null;
216 if (encounter.ServiceProvider != null)
217 {
218 provider = new Provider
219 {
220 Id = Entity.GetId(encounter.ServiceProvider.Display),
221 Name = encounter.ServiceProvider.Display,
222 SourceValue = encounter.ServiceProvider.Display
223 };
224 }
226 var personId = GetPersonId1(encounter.Subject, personIds);
228 VisitOccurrence vo = null;
229 if (personId.HasValue)
230 {
231 vo = new VisitOccurrence(new Entity())
232 {
233 PersonId = personId.Value,
234 SourceValue = encounter.Class.Code,
235 StartDate = DateTime.Parse(encounter.Period.Start),
236 EndDate = DateTime.Parse(encounter.Period.End),
237 TypeConceptId = 32817,
238 ConceptId = conceptId
239 };
241 if (provider != null)
242 {
243 vo.ProviderId = provider.Id;
244 }
245 }
247 yield return new Tuple<KeyValuePair<string, VisitOccurrence>, Provider>(
248 new KeyValuePair<string, VisitOccurrence>(encounter.Id, vo), provider);
249 }
250 }
252 public IEnumerable<omop.ConditionOccurrence> CreateConditionOccurrence(Bundle fhir, Dictionary<string, long> personIds, Dictionary<string, VisitOccurrence> visits)
253 {
254 //provider_id Condition.asserter us-core - condition
255 //visit_occurrence_id Condition.encounter us-core - condition
256 //condition_status_concept_id Condition.clinicalStatus us-core - condition
257 //stop_reason Condition.Extension(Proposed Name: abatement - reason : CodeableConcept) us - core - condition
259 foreach (var item in fhir.Entry.Where(e => e.Resource.TypeName == "Condition"))
260 {
261 var condition = (Condition)item.Resource;
263 foreach (var code in condition.Code.Coding)
264 {
265 var date = DateTime.Parse(((Hl7.Fhir.Model.FhirDateTime)condition.Onset).Value);
267 var personId = GetPersonId1(condition.Subject, personIds);
268 if (!personId.HasValue)
269 continue;
271 var co = new ConditionOccurrence(new Entity())
272 {
273 PersonId = personId.Value,
274 TypeConceptId = 32817,
275 StartDate = date,
276 SourceValue = code.Code
277 };
279 if (condition.Abatement != null)
280 {
281 co.EndDate = DateTime.Parse(((Hl7.Fhir.Model.FhirDateTime)condition.Abatement).Value);
282 }
284 var result = LookupCode(code);
285 if (result.Any())
286 SetConceptId(co, result[0]);
288 if (co.Domain == "Drug")
289 {
290 co.TypeConceptId = 32817;
291 }
292 else if (co.Domain == "Observation")
293 {
294 co.TypeConceptId = 32817;
295 }
297 var vo = GetVisitOccurrence(condition.Encounter, visits);
298 co.VisitOccurrenceId = vo.Id;
299 co.VisitDetailId = vo.Id;
301 if (!co.EndDate.HasValue)
302 co.EndDate = vo.EndDate;
304 yield return co;
305 }
306 }
308 }
310 public IEnumerable<DrugExposure> CreateDrugExposure(Bundle fhir, Dictionary<string, long> personIds, Dictionary<string, VisitOccurrence> visits)
311 {
312 //stop_reason MedicationStatement.statusReason us-core - medicationstatement
313 //refills MedicationStatement.basedOn(MedicationRequest).dispenseRequest.numberOfRepeatsAllowed us - core - medicationstatement, us - core - medicationrequest
314 //quantity MedicationStatement.basedOn(MedicationRequest).dispenseRequest.quantity us - core - medicationstatement, us - core - medicationrequest
315 //days_supply MedicationStatement.basedOn(MedicationRequest).dispenseRequest.expectedSupplyDuration us - core - medicationstatement, us - core - medicationrequest
316 //lot_number MedicationStatement.medication.batch.lotNumber us-core - medicationstatement, us - core - medication
317 //route_concept_id MedicationStatement.basedOn(MedicationRequest).dosageInstruction.route us - core - medicationstatement, us - core - medicationrequest
318 //provider_id MedicationStatement.basedOn(MedicationRequest).requester us - core - medicationstatement, us - core - medicationrequest
319 //verbatim_end_date MedicationStatement.basedOn(MedicationRequest).validityPeriod us - core - medicationstatement, us - core - medicationrequest
322 foreach (var item in fhir.Entry.Where(e => e.Resource.TypeName == "MedicationRequest"))
323 {
324 var medication = (MedicationRequest)item.Resource;
325 var cc = medication.Medication as Hl7.Fhir.Model.CodeableConcept;
326 if (cc == null)
327 continue;
329 foreach (var code in cc.Coding)
330 {
331 var personId = GetPersonId1(medication.Subject, personIds);
332 if (!personId.HasValue)
333 continue;
335 var de = new DrugExposure(new Entity())
336 {
337 PersonId = personId.Value,
338 TypeConceptId = 32817
339 };
341 if (medication.BasedOn.Count > 0)
342 {
344 }
346 if (medication.StatusReason != null)
347 {
349 }
351 if (medication.DosageInstruction != null && medication.DosageInstruction.Count > 0)
352 {
353 de.Sig = medication.DosageInstruction[0].Text;
354 }
356 if (medication.DispenseRequest != null)
357 {
359 }
361 var result = LookupCode(code);
362 if (result.Any())
363 SetConceptId(de, result[0]);
365 var vo = GetVisitOccurrence(medication.Encounter, visits);
366 de.VisitOccurrenceId = vo.Id;
367 de.VisitDetailId = vo.Id;
368 de.StartDate = vo.StartDate;
369 de.EndDate = vo.EndDate;
371 yield return de;
372 }
373 }
376 foreach (var item in fhir.Entry.Where(e => e.Resource.TypeName == "Immunization"))
377 {
378 var immunization = (Immunization)item.Resource;
380 foreach (var code in ((Hl7.Fhir.Model.CodeableConcept)immunization.VaccineCode).Coding)
381 {
382 var personId = GetPersonId1(immunization.Patient, personIds);
383 if (!personId.HasValue)
384 continue;
386 var de = new omop.DrugExposure(new omop.Entity())
387 {
388 PersonId = personId.Value,
389 TypeConceptId = 32817
390 };
392 var result = LookupCode(code);
393 if (result.Any())
394 SetConceptId(de, result[0]);
396 var vo = GetVisitOccurrence(immunization.Encounter, visits);
397 de.VisitOccurrenceId = vo.Id;
398 de.VisitDetailId = vo.Id;
399 de.StartDate = vo.StartDate;
400 de.EndDate = vo.EndDate;
402 yield return de;
403 }
404 }
405 }
407 public IEnumerable<ProcedureOccurrence> CreateProcedureOccurrence(Bundle fhir, Dictionary<string, long> personIds, Dictionary<string, VisitOccurrence> visits)
408 {
409 //quantity Procedure.Extension(Proposed Name: num - of - procedures : CodeableConcept) us - core - procedure
410 //provider_id Procedure.performer.actor
412 foreach (var item in fhir.Entry.Where(e => e.Resource.TypeName == "Procedure"))
413 {
414 var procedure = (Procedure)item.Resource;
416 foreach (var code in ((Hl7.Fhir.Model.CodeableConcept)procedure.Code).Coding)
417 {
418 var personId = GetPersonId1(procedure.Subject, personIds);
419 if (!personId.HasValue)
420 continue;
422 var po = new ProcedureOccurrence(new Entity())
423 {
424 PersonId = personId.Value,
425 TypeConceptId = 32817
426 };
428 var result = LookupCode(code);
429 if (result.Any())
430 SetConceptId(po, result[0]);
432 if (procedure.Extension.Count > 0)
433 {
435 }
437 if (po.Domain == "Measurement")
438 {
439 po.TypeConceptId = 32817;
440 }
442 var vo = GetVisitOccurrence(procedure.Encounter, visits);
443 po.VisitOccurrenceId = vo.Id;
444 po.VisitDetailId = vo.Id;
445 po.StartDate = DateTime.Parse(((Hl7.Fhir.Model.Period)procedure.Performed).Start);
446 po.EndDate = DateTime.Parse(((Hl7.Fhir.Model.Period)procedure.Performed).End);
448 yield return po;
449 }
450 }
451 }
453 public IEnumerable<omop.Observation> CreateObservation(Bundle fhir, Dictionary<string, long> personIds, Dictionary<string, VisitOccurrence> visits)
454 {
455 foreach (var item in fhir.Entry.Where(e => e.Resource.TypeName == "AllergyIntolerance"))
456 {
457 var allergy = (AllergyIntolerance)item.Resource;
459 foreach (var code in ((Hl7.Fhir.Model.CodeableConcept)allergy.Code).Coding)
460 {
461 var personId = GetPersonId1(allergy.Patient, personIds);
462 if (!personId.HasValue)
463 continue;
465 var o = new omop.Observation(new Entity())
466 {
467 PersonId = personId.Value,
468 TypeConceptId = 32817
469 };
471 var result = LookupCode(code);
472 if (result.Any())
473 SetConceptId(o, result[0]);
475 o.StartDate = DateTime.Parse(allergy.RecordedDate);
477 yield return o;
478 }
479 }
480 }
482 public IEnumerable<Measurement> CreateMeasurement(Bundle fhir, Dictionary<string, long> personIds, Dictionary<string, VisitOccurrence> visits)
483 {
484 //provider_id Observation.performer(Practitioner) us - core - observationresults
485 //value_as_concept_id Observation.valueCodeableConcept us-core - observationresults
487 foreach (var item in fhir.Entry.Where(e => e.Resource.TypeName == "Observation"))
488 {
489 var observation = (Hl7.Fhir.Model.Observation)item.Resource;
491 foreach (var code in ((CodeableConcept)observation.Code).Coding)
492 {
493 var personId = GetPersonId1(observation.Subject, personIds);
494 if (!personId.HasValue)
495 continue;
497 var m = new Measurement(new Entity())
498 {
499 PersonId = personId.Value,
500 TypeConceptId = 32817
501 };
503 var result = LookupCode(code);
504 if (result.Any())
505 SetConceptId(m, result[0]);
507 if (observation.ReferenceRange != null && observation.ReferenceRange.Count > 0)
508 {
509 if (observation.ReferenceRange[0].Low != null)
510 m.RangeLow = observation.ReferenceRange[0].Low.Value;
512 if (observation.ReferenceRange[0].High != null)
513 m.RangeHigh = observation.ReferenceRange[0].High.Value;
514 }
516 var vo = GetVisitOccurrence(observation.Encounter, visits);
517 m.VisitOccurrenceId = vo.Id;
518 m.VisitDetailId = vo.Id;
519 m.StartDate = DateTime.Parse(((FhirDateTime)observation.Effective).Value);
521 if (observation.Value != null)
522 {
523 var quantity = observation.Value as Quantity;
524 if (quantity != null)
525 {
526 m.UnitSourceValue = quantity.Unit;
527 var unit = LookupCode(m.UnitSourceValue, "Unit");
528 if (unit.Any() && unit[0].ConceptId.HasValue)
529 {
530 m.UnitConceptId = unit[0].ConceptId.Value;
531 }
533 m.ValueAsNumber = quantity.Value;
534 m.ValueSourceValue = m.ValueAsNumber.ToString();
535 }
536 else
537 {
538 var cc = observation.Value as CodeableConcept;
540 if (cc != null && cc.Coding.Count > 0)
541 {
542 m.ValueSourceValue = cc.Coding[0].Display;
544 var conceptId = LookupCode(cc.Coding[0]);
545 if (conceptId.Any() && conceptId[0].ConceptId.HasValue)
546 m.ValueAsConceptId = conceptId[0].ConceptId.Value;
547 }
548 else
549 {
550 m.ValueSourceValue = observation.Value.ToString();
551 }
552 }
553 }
555 yield return m;
556 }
557 }
558 }
560 private long? GetPersonId1(ResourceReference e, Dictionary<string, long> personIds)
561 {
562 var key = e.Reference.Replace("urn:uuid:", "");
563 if (personIds.ContainsKey(key))
564 return personIds[key];
566 return null;
567 }
569 private VisitOccurrence GetVisitOccurrence(ResourceReference e, Dictionary<string, VisitOccurrence> visits)
570 {
571 return visits[e.Reference.Replace("urn:uuid:", "")];
572 }
574 private void SetConceptId(IEntity e, LookupValue value)
575 {
576 if (value.ConceptId.HasValue)
577 {
578 e.ConceptId = value.ConceptId.Value;
579 e.Domain = value.Domain;
581 if (value.Ingredients != null)
582 {
583 e.Ingredients = new List<int>(value.Ingredients.Count);
584 e.Ingredients.AddRange(value.Ingredients);
585 }
586 }
588 e.SourceConceptId = value.SourceConceptId;
589 }
591 private List<LookupValue> LookupCode(string code, string vocabularyName)
592 {
593 return _vocabulary.Lookup(code, vocabularyName, DateTime.MinValue);
594 }
596 private List<LookupValue> LookupCode(Coding code)
597 {
598 var vocabularyName = "";
599 switch (code.System)
600 {
601 case "http://snomed.info/sct":
602 vocabularyName = "Snomed";
603 break;
605 case "http://www.nlm.nih.gov/research/umls/rxnorm":
606 vocabularyName = "Rxnorm";
607 break;
609 case "http://hl7.org/fhir/sid/cvx":
610 vocabularyName = "Cvx";
611 break;
613 case "http://loinc.org":
614 vocabularyName = "Loinc";
615 break;
617 default:
618 throw new Exception("unknown vocabulary " + code.System);
619 }
621 return LookupCode(code.Code, vocabularyName);
622 }
623 }