From b2f9289c45ba885dbbc500db56ad09a82c735863 Mon Sep 17 00:00:00 2001 From: Emily Michaud <59289146+emichaud998@users.noreply.github.com> Date: Thu, 20 Oct 2022 15:46:10 -0400 Subject: [PATCH 1/3] Add pregnancy status dart file that grabs pregnancy status from the OHM FHIR server and sends status to the USPSTF querier to include the pregnancy status as a parameter in recommendations API request --- lib/src/open_health_manager/patient_data.dart | 6 ++ .../open_health_manager/pregnancy_status.dart | 91 +++++++++++++++++++ .../preventative_services_task_force.dart | 9 +- 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 lib/src/open_health_manager/pregnancy_status.dart diff --git a/lib/src/open_health_manager/patient_data.dart b/lib/src/open_health_manager/patient_data.dart index 6e577fa..7e7c27a 100644 --- a/lib/src/open_health_manager/patient_data.dart +++ b/lib/src/open_health_manager/patient_data.dart @@ -17,6 +17,7 @@ // useful as a Flutter model. import 'package:flutter/foundation.dart'; +import 'package:rosie/src/open_health_manager/pregnancy_status.dart'; import 'account.dart'; import 'open_health_manager.dart'; import 'blood_pressure.dart'; @@ -127,6 +128,10 @@ class PatientData extends ChangeNotifier { CachedData>(() async { return await healthManager.querySmokingStatus(); }); + late final pregnancyStatus = + CachedData>(() async { + return await healthManager.queryPregnancyStatus(); + }); late final account = CachedData(() async { return await healthManager.getAccount(); }); @@ -135,6 +140,7 @@ class PatientData extends ChangeNotifier { patientDemographics.reload(); bloodPressure.reload(); smokingStatus.reload(); + pregnancyStatus.reload(); // Reloading the account data may be excessive account.reload(); } diff --git a/lib/src/open_health_manager/pregnancy_status.dart b/lib/src/open_health_manager/pregnancy_status.dart new file mode 100644 index 0000000..040f852 --- /dev/null +++ b/lib/src/open_health_manager/pregnancy_status.dart @@ -0,0 +1,91 @@ +// Copyright 2022 The MITRE Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'dart:developer'; +import 'dart:html'; +import 'package:fhir/r4/resource_types/clinical/summary/summary.dart'; +import 'open_health_manager.dart'; + +/// A condition of a Pregnancy Status. +class PregnancyStatusCondition { + final bool pregnancyStatus; + + const PregnancyStatusCondition(this.pregnancyStatus); + + /// Attempts to parse a Pregnancy Status condition from a given FHIR condition. + /// + /// The coding in the condition itself is **ignored**. This will assume it's a valid coding, and will look for the + /// pregnancy status value + factory PregnancyStatusCondition.fromCondition(Condition condition) { + final conditionCodes = condition.code?.coding; + if (conditionCodes == null || conditionCodes.isEmpty) { + throw const InvalidResourceException( + "No conditionCodes in pregnancy status condition."); + } + bool pregnancyStatus = false; + + var period = condition.onsetPeriod; + var startDate = period?.start?.valueDateTime; + var endDate = period?.end?.valueDateTime; + + final now = DateTime.now(); + + if (startDate != null) { + for (final conditionCode in conditionCodes) { + final theCode = conditionCode.code?.value; + + if (theCode == "77386006") { + if (now.isAfter(startDate) && + (endDate == null || now.isBefore(endDate))) { + pregnancyStatus = true; + } else { + pregnancyStatus = false; + } + } + } + } + + return PregnancyStatusCondition(pregnancyStatus); + } +} + +extension PregnancyStatusQuerying on OpenHealthManager { + /// Loads a list of Pregnancy conditions. + /// + /// Any exceptions during loading are thrown, and any exceptions during parsing are logged to the FINE (500) log level + /// but otherwise eaten and simply left out of the result. + Future> queryPregnancyStatus() async { + // return most recent ones first + final bundle = await queryResource("Condition", + {"code": "http://snomed.info/sct|77386006", "_sort": "-date"}); + final results = []; + final entries = bundle.entry; + if (entries == null) { + return results; + } + for (final entry in entries) { + final resource = entry.resource; + if (resource != null && resource is Condition) { + try { + // add to front so that most recent entries are last in the list + results.insert(0, PregnancyStatusCondition.fromCondition(resource)); + } on InvalidResourceException catch (error) { + log('Unable to parse Condition for a Pregnancy Status', + level: 500, error: error); + } + } + } + return results; + } +} diff --git a/lib/src/service/preventative_services_task_force.dart b/lib/src/service/preventative_services_task_force.dart index e0c70ff..0e9af5b 100644 --- a/lib/src/service/preventative_services_task_force.dart +++ b/lib/src/service/preventative_services_task_force.dart @@ -130,9 +130,14 @@ class PreventativeServicesTaskForce with ChangeNotifier { String? gender = (demographics != null && demographics.gender != null) ? demographics.gender : null; + final pregnancyStatusList = await patientData?.pregnancyStatus.get(); + final pregnancyStatusValue = + (pregnancyStatusList != null && pregnancyStatusList.isNotEmpty) + ? pregnancyStatusList[0].pregnancyStatus + : null; - return await getRecommendedServices( - age, gender, null, smokingStatusValue?.toTaskForceAPIParam(), null); + return await getRecommendedServices(age, gender, pregnancyStatusValue, + smokingStatusValue?.toTaskForceAPIParam(), null); } Future> getRecommendedServices(int? age, String? sex, From cb293ba264e13f98af902df0a7351b067e6da7fb Mon Sep 17 00:00:00 2001 From: Emily Michaud <59289146+emichaud998@users.noreply.github.com> Date: Fri, 21 Oct 2022 11:58:51 -0400 Subject: [PATCH 2/3] Fix wrong sort parameter for Condition resource --- lib/src/open_health_manager/pregnancy_status.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/src/open_health_manager/pregnancy_status.dart b/lib/src/open_health_manager/pregnancy_status.dart index 040f852..6ee7a4e 100644 --- a/lib/src/open_health_manager/pregnancy_status.dart +++ b/lib/src/open_health_manager/pregnancy_status.dart @@ -13,7 +13,6 @@ // limitations under the License. import 'dart:developer'; -import 'dart:html'; import 'package:fhir/r4/resource_types/clinical/summary/summary.dart'; import 'open_health_manager.dart'; @@ -68,7 +67,7 @@ extension PregnancyStatusQuerying on OpenHealthManager { Future> queryPregnancyStatus() async { // return most recent ones first final bundle = await queryResource("Condition", - {"code": "http://snomed.info/sct|77386006", "_sort": "-date"}); + {"code": "http://snomed.info/sct|77386006", "_sort": "-onset-date"}); final results = []; final entries = bundle.entry; if (entries == null) { From 15c3379b2c284e6845ca2fec80502090e79106b1 Mon Sep 17 00:00:00 2001 From: Emily Michaud <59289146+emichaud998@users.noreply.github.com> Date: Wed, 23 Nov 2022 09:42:17 -0500 Subject: [PATCH 3/3] Update pregnancy USPSTF recommendation to grab pregnancy Observation resources rather than Condition resources --- lib/src/open_health_manager/patient_data.dart | 2 +- .../open_health_manager/pregnancy_status.dart | 87 +++++++++++-------- 2 files changed, 54 insertions(+), 35 deletions(-) diff --git a/lib/src/open_health_manager/patient_data.dart b/lib/src/open_health_manager/patient_data.dart index 7e7c27a..7a5c45d 100644 --- a/lib/src/open_health_manager/patient_data.dart +++ b/lib/src/open_health_manager/patient_data.dart @@ -129,7 +129,7 @@ class PatientData extends ChangeNotifier { return await healthManager.querySmokingStatus(); }); late final pregnancyStatus = - CachedData>(() async { + CachedData>(() async { return await healthManager.queryPregnancyStatus(); }); late final account = CachedData(() async { diff --git a/lib/src/open_health_manager/pregnancy_status.dart b/lib/src/open_health_manager/pregnancy_status.dart index 6ee7a4e..2ff862a 100644 --- a/lib/src/open_health_manager/pregnancy_status.dart +++ b/lib/src/open_health_manager/pregnancy_status.dart @@ -13,40 +13,55 @@ // limitations under the License. import 'dart:developer'; -import 'package:fhir/r4/resource_types/clinical/summary/summary.dart'; +import 'package:fhir/r4.dart' + show + Code, + CodeableConcept, + Coding, + FhirDateTime, + FhirUri, + Observation, + ObservationStatus, + Reference; import 'open_health_manager.dart'; +import 'util.dart'; /// A condition of a Pregnancy Status. -class PregnancyStatusCondition { +class PregnancyStatusObservation { final bool pregnancyStatus; - const PregnancyStatusCondition(this.pregnancyStatus); + const PregnancyStatusObservation(this.pregnancyStatus); /// Attempts to parse a Pregnancy Status condition from a given FHIR condition. /// /// The coding in the condition itself is **ignored**. This will assume it's a valid coding, and will look for the /// pregnancy status value - factory PregnancyStatusCondition.fromCondition(Condition condition) { - final conditionCodes = condition.code?.coding; - if (conditionCodes == null || conditionCodes.isEmpty) { + factory PregnancyStatusObservation.fromObservation(Observation observation) { + final observationCodes = observation.valueCodeableConcept?.coding; + if (observationCodes == null || observationCodes.isEmpty) { throw const InvalidResourceException( - "No conditionCodes in pregnancy status condition."); + "No observationCodes in pregnancy status observation."); } bool pregnancyStatus = false; - var period = condition.onsetPeriod; - var startDate = period?.start?.valueDateTime; - var endDate = period?.end?.valueDateTime; + var dateTime = observation.effectiveDateTime?.valueDateTime; final now = DateTime.now(); - if (startDate != null) { - for (final conditionCode in conditionCodes) { - final theCode = conditionCode.code?.value; + if (dateTime != null) { + for (final observationCode in observationCodes) { + final theCode = observationCode.code?.value; if (theCode == "77386006") { - if (now.isAfter(startDate) && - (endDate == null || now.isBefore(endDate))) { + // Pregnant Status Code + if (now.isAfter(dateTime)) { + pregnancyStatus = true; + } else { + pregnancyStatus = false; + } + } else if (theCode == "60001007") { + // Not Pregnant Status Code + if (now.isBefore(dateTime)) { pregnancyStatus = true; } else { pregnancyStatus = false; @@ -54,37 +69,41 @@ class PregnancyStatusCondition { } } } - - return PregnancyStatusCondition(pregnancyStatus); + return PregnancyStatusObservation(pregnancyStatus); } } extension PregnancyStatusQuerying on OpenHealthManager { - /// Loads a list of Pregnancy conditions. + /// Loads a list of Pregnancy observations. /// /// Any exceptions during loading are thrown, and any exceptions during parsing are logged to the FINE (500) log level /// but otherwise eaten and simply left out of the result. - Future> queryPregnancyStatus() async { - // return most recent ones first - final bundle = await queryResource("Condition", - {"code": "http://snomed.info/sct|77386006", "_sort": "-onset-date"}); - final results = []; - final entries = bundle.entry; + Future> queryPregnancyStatus() async { + // returns most recent Observations first + final bundle = await queryResource( + "Observation", {"code": "http://loinc.org|82810-3", "_sort": "-date"}); + final results = []; + var entries = bundle.entry; if (entries == null) { return results; } - for (final entry in entries) { - final resource = entry.resource; - if (resource != null && resource is Condition) { - try { - // add to front so that most recent entries are last in the list - results.insert(0, PregnancyStatusCondition.fromCondition(resource)); - } on InvalidResourceException catch (error) { - log('Unable to parse Condition for a Pregnancy Status', - level: 500, error: error); - } + + // Get latest entry - first Observation + var finalEntry = entries.first; + + final resource = finalEntry.resource; + if (resource != null && resource is Observation) { + try { + var pregnancyStatusObservation = + PregnancyStatusObservation.fromObservation(resource); + // add to result list + results.insert(0, pregnancyStatusObservation); + } on InvalidResourceException catch (error) { + log('Unable to parse Observation for a Pregnancy Status', + level: 500, error: error); } } + return results; } }