From 3149fd888cb91b169429b344ed8bde71c139ba6e Mon Sep 17 00:00:00 2001
From: Angie-540 <96350406+Angie-540@users.noreply.github.com>
Date: Wed, 29 Nov 2023 10:14:37 +0300
Subject: [PATCH 01/13] POC-492: Modified pre-appointment to display failed
phone attempts (#1681)
---
.../pre-appointment-outreach.component.ts | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/app/clinic-dashboard/general/pre-appointment-outreach/pre-appointment-outreach.component.ts b/src/app/clinic-dashboard/general/pre-appointment-outreach/pre-appointment-outreach.component.ts
index d81010b4a..7bcd5431c 100644
--- a/src/app/clinic-dashboard/general/pre-appointment-outreach/pre-appointment-outreach.component.ts
+++ b/src/app/clinic-dashboard/general/pre-appointment-outreach/pre-appointment-outreach.component.ts
@@ -215,6 +215,11 @@ export class PreAppointmentOutreachComponent implements OnInit {
width: 100,
field: 'rescheduled_date'
},
+ {
+ headerName: 'No. of Failed Phone Attempts',
+ width: 100,
+ field: 'number_of_failed_phone_attempts'
+ },
{
headerName: 'Contact Reached',
width: 100,
From ecd37da9ec441bc6f5cb6790dd848a5ac527fa97 Mon Sep 17 00:00:00 2001
From: Angie-540 <96350406+Angie-540@users.noreply.github.com>
Date: Wed, 29 Nov 2023 12:19:53 +0300
Subject: [PATCH 02/13] POC-570:remove program snapshot tag after patient has
made clinical visit (#1687)
* remove program snapshot tag after patient has made clinical visit
* fixed pre-appoinment-reminder tag on program snapshot
* POC-570:fixed pre-appoinment-reminder tag on program snapshot
* Update src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.ts
---------
Co-authored-by: Drizzentic
---
.../hiv/program-snapshot/hiv-program-snapshot.component.html | 5 ++++-
.../hiv/program-snapshot/hiv-program-snapshot.component.ts | 3 ++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html
index bcde35032..a9a4f621c 100644
--- a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html
+++ b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html
@@ -57,7 +57,10 @@
diff --git a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.ts b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.ts
index 050f7b703..d4150513d 100644
--- a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.ts
+++ b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.ts
@@ -299,7 +299,8 @@ export class HivProgramSnapshotComponent implements OnInit {
if (
result &&
result.predicted_prob_disengage &&
- result.predicted_risk
+ result.predicted_risk &&
+ result.observed_rtc_date === null
) {
this.hasPredictedScore = true;
this.prediction = result;
From 44f627e18e967198142afef1726cad37dcd0b688 Mon Sep 17 00:00:00 2001
From: Angie-540 <96350406+Angie-540@users.noreply.github.com>
Date: Thu, 30 Nov 2023 10:49:05 +0300
Subject: [PATCH 03/13] POC-495 (#1685)
* customised toastr for pre-appointment rescheduled appointments
* added pre-appointment banner for patients who rescheduled appointments
* added customised toastr for predicted patients who rescheduled appointments
* POC-495: added customised toastr for predicted patients with rescheduled appointments
added customised toastr for predicted patients who rescheduled appointments
POC-495: added customised toastr for predicted patients with rescheduled appointments
---------
Co-authored-by: Drizzentic
---
.../patient-reminder-custom.component.css | 30 +++++++++++++++++++
.../patient-reminder.component.css | 5 ++++
.../patient-reminders.component.ts | 10 ++++++-
.../patient-reminders.components.html | 14 +++++++++
4 files changed, 58 insertions(+), 1 deletion(-)
diff --git a/src/app/patient-dashboard/common/patient-reminders/patient-reminder-custom.component.css b/src/app/patient-dashboard/common/patient-reminders/patient-reminder-custom.component.css
index 4354e7953..8da650363 100644
--- a/src/app/patient-dashboard/common/patient-reminders/patient-reminder-custom.component.css
+++ b/src/app/patient-dashboard/common/patient-reminders/patient-reminder-custom.component.css
@@ -20,3 +20,33 @@
:host.toast.toast-warning {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=') !important;
}
+
+/* Custom toast class for ml custom notification type */
+::ng-deep .ngx-toastr-custom-type {
+ position: relative;
+ overflow: hidden;
+ margin: 0 0 6px;
+ padding: 15px 15px 15px 50px;
+ width: 300px;
+ border-radius: 3px 3px 3px 3px;
+ background-position: 15px center;
+ background-repeat: no-repeat;
+ background-size: 24px;
+ box-shadow: 0 0 12px #999999;
+ color: #271e1e;
+ background-color: #ffff00;
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=') !important;
+}
+
+/* Additional styles for toastr */
+.ngx-toastr-custom-type .toast-title {
+ font-weight: bold;
+}
+
+.ngx-toastr-custom-type .toast-close-button {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 8px;
+ cursor: pointer;
+}
diff --git a/src/app/patient-dashboard/common/patient-reminders/patient-reminder.component.css b/src/app/patient-dashboard/common/patient-reminders/patient-reminder.component.css
index b0af0f6c7..cdca47d99 100644
--- a/src/app/patient-dashboard/common/patient-reminders/patient-reminder.component.css
+++ b/src/app/patient-dashboard/common/patient-reminders/patient-reminder.component.css
@@ -1,3 +1,8 @@
patient-reminders .panel-body {
padding: 0px !important;
}
+
+:host.toast.alert-ml {
+ background-color: #ffff00;
+ border-color: #bdaaad;
+}
diff --git a/src/app/patient-dashboard/common/patient-reminders/patient-reminders.component.ts b/src/app/patient-dashboard/common/patient-reminders/patient-reminders.component.ts
index 2c879f7b5..b5d2c0eb0 100644
--- a/src/app/patient-dashboard/common/patient-reminders/patient-reminders.component.ts
+++ b/src/app/patient-dashboard/common/patient-reminders/patient-reminders.component.ts
@@ -136,7 +136,15 @@ export class PatientRemindersComponent implements OnInit, OnDestroy {
this.toastrConfig
);
}
-
+ if (reminder.type === 'ml') {
+ toast = this.toastrService.show(reminder.message, reminder.title, {
+ extendedTimeOut: 0,
+ timeOut: 0,
+ positionClass: 'toast-bottom-right',
+ closeButton: true,
+ toastClass: 'ngx-toastr-custom-type'
+ });
+ }
if (reminder.type === 'info') {
toast = this.toastrService.info(
reminder.message,
diff --git a/src/app/patient-dashboard/common/patient-reminders/patient-reminders.components.html b/src/app/patient-dashboard/common/patient-reminders/patient-reminders.components.html
index 2331b28d7..a0b69ee7d 100644
--- a/src/app/patient-dashboard/common/patient-reminders/patient-reminders.components.html
+++ b/src/app/patient-dashboard/common/patient-reminders/patient-reminders.components.html
@@ -45,4 +45,18 @@
>
{{reminder.message}}
+
+
×
+ {{reminder.message}}
+
From f6b6a4d698a0de6344e7259f66767e004ad26a96 Mon Sep 17 00:00:00 2001
From: Angie-540 <96350406+Angie-540@users.noreply.github.com>
Date: Thu, 14 Dec 2023 11:19:34 +0300
Subject: [PATCH 04/13] POC-599:Add Jua Mtoto Wako to program snapshot (#1691)
* POC-599:Add Jua Mtoto Wako to program snapshot
* POC-599: Add JMW to program snapshot
* POC-599: Add Jua Mtoto Wako to program snapshot
---------
Co-authored-by: Rugut Kibet Enock
---
.../hiv-program-snapshot.component.html | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html
index a9a4f621c..520c5fd7f 100644
--- a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html
+++ b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html
@@ -72,6 +72,20 @@
>
+ 200 && this.patient.person.age <= 19"
+ style="display: inline-block"
+ >
+
+ Jua Mtoto Wako
+
+
Date: Thu, 14 Dec 2023 12:10:54 +0300
Subject: [PATCH 05/13] POC-605:Remove CovidAlert from program snapshot (#1690)
Co-authored-by: Rugut Kibet Enock
---
.../hiv-program-snapshot.component.html | 36 -------------------
1 file changed, 36 deletions(-)
diff --git a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html
index 520c5fd7f..838eaba9c 100644
--- a/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html
+++ b/src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.html
@@ -19,42 +19,6 @@
>
-
-
- COVID-19 Assessment Status :
- {{ covid19VaccinationSummary?.vaccination_status }}
-
-
-
-
- COVID-19 Screening Outcome :
- {{
- covid19VaccinationSummary?.covid_screening_outcome_this_visit
- }}
-
-
Date: Thu, 14 Dec 2023 18:33:17 +0300
Subject: [PATCH 06/13] POC-571 (#1686)
* Fix type annotation
* Work on OTZ program snapshot
* work on group manager
* wip
* Work on requested changes
* wip
* for unsuccessful contact attempts, added maroon filter (#1682)
* for unsuccessfulcontact attempts, added pink filter (#1683)
* POC-558 (#1678)
* Change the Male/Female gender disaagregation to start with Female then Male
* Change the Male/Female gender disaagregation to start with Female then Male
---------
Co-authored-by: kantush
Co-authored-by: Drizzentic
* POC-492: Modified pre-appointment to display failed phone attempts (#1681)
* POC-570:remove program snapshot tag after patient has made clinical visit (#1687)
* remove program snapshot tag after patient has made clinical visit
* fixed pre-appoinment-reminder tag on program snapshot
* POC-570:fixed pre-appoinment-reminder tag on program snapshot
* Update src/app/patient-dashboard/hiv/program-snapshot/hiv-program-snapshot.component.ts
---------
Co-authored-by: Drizzentic
* POC-495 (#1685)
* customised toastr for pre-appointment rescheduled appointments
* added pre-appointment banner for patients who rescheduled appointments
* added customised toastr for predicted patients who rescheduled appointments
* POC-495: added customised toastr for predicted patients with rescheduled appointments
added customised toastr for predicted patients who rescheduled appointments
POC-495: added customised toastr for predicted patients with rescheduled appointments
---------
Co-authored-by: Drizzentic
* wip
* Add cohort viral load suppression rate
* Rename variables
* Refactoring
* POC-599:Add Jua Mtoto Wako to program snapshot (#1691)
* POC-599:Add Jua Mtoto Wako to program snapshot
* POC-599: Add JMW to program snapshot
* POC-599: Add Jua Mtoto Wako to program snapshot
---------
Co-authored-by: Rugut Kibet Enock
* POC-605:Remove CovidAlert from program snapshot (#1690)
Co-authored-by: Rugut Kibet Enock
* Modify encounter uuid
---------
Co-authored-by: kantush
Co-authored-by: Angie-540 <96350406+Angie-540@users.noreply.github.com>
Co-authored-by: Drizzentic
Co-authored-by: Rugut Kibet Enock
---
.../cohort-otz-module-resource.service.ts | 61 +++
.../group-detail-summary.component.html | 34 +-
.../group-detail-summary.component.ts | 14 +
.../group-detail/group-detail.component.html | 18 +-
.../group-detail/group-detail.component.ts | 371 ++++++++++++++----
.../group-editor/group-editor-component.html | 57 +++
.../group-editor/group-editor-component.ts | 76 +++-
.../group-manager-search.component.html | 24 +-
.../group-manager-search.component.ts | 114 +++++-
src/app/group-manager/group-manager.module.ts | 3 +-
.../modals/group-transfer-modal.component.ts | 12 +-
src/app/models/group.model.ts | 67 ++++
.../community-group-resource.service.ts | 2 +-
.../openmrs-api/encounter-resource.service.ts | 1 +
.../common/formentry/formentry.component.html | 17 +-
.../common/formentry/formentry.component.ts | 41 +-
.../otz-snapshot/otz-snapshot.component.css | 260 ++++++++++++
.../otz-snapshot/otz-snapshot.component.html | 216 ++++++++++
.../otz-snapshot.component.spec.ts | 24 ++
.../otz-snapshot/otz-snapshot.component.ts | 215 ++++++++++
.../patient-banner.component.html | 17 +
.../patient-banner.component.ts | 27 +-
.../common/patient-dashboard.common.module.ts | 4 +-
.../visit-starter.component.html | 3 +-
.../visit-starter/visit-starter.component.ts | 4 +-
.../common/visit/visit.component.ts | 6 +-
.../group-enrollment.component.ts | 7 +
.../patient-dashboard.routes.ts | 5 +
.../new-program/new-program.component.html | 2 +-
.../new-program/new-program.component.ts | 33 +-
.../program-visits-config.json | 9 +
.../schema/patient.dashboard.conf.json | 35 ++
32 files changed, 1676 insertions(+), 103 deletions(-)
create mode 100644 src/app/etl-api/cohort-otz-module-resource.service.ts
create mode 100644 src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.css
create mode 100644 src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.html
create mode 100644 src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.spec.ts
create mode 100644 src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.ts
diff --git a/src/app/etl-api/cohort-otz-module-resource.service.ts b/src/app/etl-api/cohort-otz-module-resource.service.ts
new file mode 100644
index 000000000..72b85ba45
--- /dev/null
+++ b/src/app/etl-api/cohort-otz-module-resource.service.ts
@@ -0,0 +1,61 @@
+import { Injectable } from '@angular/core';
+import { AppSettingsService } from '../app-settings/app-settings.service';
+import { Observable } from 'rxjs';
+import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
+
+@Injectable()
+export class CohortOtzModuleResourceService {
+ constructor(
+ private http: HttpClient,
+ private appSettingsService: AppSettingsService
+ ) {}
+ public getUrl(): string {
+ return (
+ this.appSettingsService.getEtlRestbaseurl().trim() + 'cohort-modules'
+ );
+ }
+
+ public getSummaryUrl(): string {
+ return (
+ this.appSettingsService.getEtlRestbaseurl().trim() +
+ 'hiv-latest-summaries'
+ );
+ }
+
+ public getCohortSuppressionsUrl(): string {
+ return (
+ this.appSettingsService.getEtlRestbaseurl().trim() +
+ 'viral-load-suppression-rate'
+ );
+ }
+
+ public getCohortOtzModule(cohortUuid: string): Observable {
+ return this.http.get(this.getUrl() + '/' + cohortUuid);
+ }
+ public getUrlRequestParams(patientUuids: string[]): HttpParams {
+ let urlParams: HttpParams = new HttpParams();
+
+ if (patientUuids && patientUuids.length > 0) {
+ urlParams = urlParams.set('uuid', patientUuids.join(','));
+ }
+ return urlParams;
+ }
+
+ public getPatientsLatestHivSummaries(payload: string[]) {
+ if (!payload || payload.length === 0) {
+ return null;
+ }
+ return this.http.get(this.getSummaryUrl(), {
+ params: this.getUrlRequestParams(payload)
+ });
+ }
+
+ public getCohortSuppressionStatus(payload: string[]) {
+ if (!payload || payload.length === 0) {
+ return null;
+ }
+ return this.http.get(this.getCohortSuppressionsUrl(), {
+ params: this.getUrlRequestParams(payload)
+ });
+ }
+}
diff --git a/src/app/group-manager/group-detail/group-detail-summary.component.html b/src/app/group-manager/group-detail/group-detail-summary.component.html
index 73ea4407d..f7a444eb1 100644
--- a/src/app/group-manager/group-detail/group-detail-summary.component.html
+++ b/src/app/group-manager/group-detail/group-detail-summary.component.html
@@ -72,7 +72,7 @@ Group Summary
Leader:
- {{ currentLeader.person.display }} ({{ leadershipType }}),
+ {{ currentLeader.person.display }},
since {{ currentLeader.startDate | date: 'medium' }}
@@ -108,6 +108,12 @@ Group Summary
+
+
+ Group Activity:
+ {{ groupActivity?.value }}
+
+
@@ -195,10 +201,23 @@ Edit Leadership
>
- OTZ Champion
+ Peer Leadership
- Community Staff Leadership
@@ -349,12 +368,21 @@
Add Leader
OTZ Champion
+ Peer Leadership
= new Subject();
public editLeaderForm: FormGroup;
+ public validOTZProgram = false;
public endDate = {
date: {
month: this.currentMonth,
@@ -104,6 +106,10 @@ export class GroupDetailSummaryComponent implements OnInit, OnDestroy {
'landmark',
this.group.attributes
);
+ this.groupActivity = this.communityGroupService.getGroupAttribute(
+ 'groupActivity',
+ this.group.attributes
+ );
this.currentLeader = this.getCurrentLeader(
group.cohortLeaders,
group.cohortMembers
@@ -347,6 +353,10 @@ export class GroupDetailSummaryComponent implements OnInit, OnDestroy {
'landmark',
this.group.attributes
);
+ this.groupActivity = this.communityGroupService.getGroupAttribute(
+ 'groupActivity',
+ this.group.attributes
+ );
this.currentLeader = this.getCurrentLeader(
group.cohortLeaders,
group.cohortMembers
@@ -561,6 +571,7 @@ export class GroupDetailSummaryComponent implements OnInit, OnDestroy {
groupProgram: { label: program['name'], value: program['uuid'] },
provider: { label: provider.person.display, value: provider.person.uuid },
address: this.landmark.value,
+ groupActivity: this.groupActivity.value,
groupUuid: this.group.uuid,
actionButtonText: 'Save Changes'
};
@@ -716,6 +727,9 @@ export class GroupDetailSummaryComponent implements OnInit, OnDestroy {
const sub = this.programService.getProgramByUuid(program.value).subscribe(
(prog) => {
this.program = prog;
+ if (this.program.name === 'OTZ PROGRAM') {
+ this.validOTZProgram = true;
+ }
},
(error) => {
console.log(error);
diff --git a/src/app/group-manager/group-detail/group-detail.component.html b/src/app/group-manager/group-detail/group-detail.component.html
index 68323ecd4..133b77188 100644
--- a/src/app/group-manager/group-detail/group-detail.component.html
+++ b/src/app/group-manager/group-detail/group-detail.component.html
@@ -240,7 +240,10 @@ Add Membership
{{ enrollmentErrorMessage }}
-
+
Add Membership
The patient is not enrolled in this program.
+
+ Patient is not eligible for OTZ enrollment
+
+
Start Group Meeting
>
{{ item.display }}
diff --git a/src/app/group-manager/group-detail/group-detail.component.ts b/src/app/group-manager/group-detail/group-detail.component.ts
index 962339ea7..a47c92f87 100644
--- a/src/app/group-manager/group-detail/group-detail.component.ts
+++ b/src/app/group-manager/group-detail/group-detail.component.ts
@@ -10,7 +10,7 @@ import { ActivatedRoute, Router } from '@angular/router';
import { DatePipe } from '@angular/common';
import { BsModalService } from 'ngx-bootstrap';
import { BsModalRef } from 'ngx-bootstrap';
-import { Subscription } from 'rxjs';
+import { Observable, Subscription, forkJoin } from 'rxjs';
import * as Moment from 'moment';
import * as _ from 'lodash';
import { AgGridNg2 } from 'ag-grid-angular';
@@ -24,6 +24,7 @@ import { SuccessModalComponent } from '../modals/success-modal.component';
import { GridOptions } from 'ag-grid';
import { GroupTransferModalComponent } from '../modals/group-transfer-modal.component';
import { RetrospectiveDataEntryService } from '../../retrospective-data-entry/services/retrospective-data-entry.service';
+import { CohortOtzModuleResourceService } from 'src/app/etl-api/cohort-otz-module-resource.service';
import { RisonService } from '../../shared/services/rison-service';
import { HttpClient } from '@angular/common/http';
import { flatMap, map } from 'rxjs/operators';
@@ -42,6 +43,8 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
errorMessage: string;
enrollmentErrorMessage: string;
validatingEnrollment: boolean;
+ patientModels: any[] = [];
+
public successMessage: string;
@ViewChild(AgGridNg2) dataGrid: AgGridNg2;
@ViewChild('startGroupVisitModal') startGroupVisitModal: TemplateRef;
@@ -83,6 +86,7 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
public isFiltered = true;
public subscriptions = new Subscription();
public visitType = '0d608b80-1cb5-4c85-835a-29072683ca27';
+ public otzVisitType = 'd3d5fd4a-508c-4610-97b7-5197a0bdb88d';
public currentMonth = Moment().month() + 1;
public today = {
year: Moment().year(),
@@ -95,6 +99,7 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
formatted: Moment().format('YYYY-MM-DD')
};
public cohortVisits = [];
+ public numberOfMembers: number;
public patientVisitPayload: any;
public visitTypes = [];
public selectedPatient: Patient;
@@ -109,6 +114,11 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
public visitStartedToday: boolean;
public visitStartedRetro: boolean;
public showEnrollmentButton = false;
+ public showOTZEnrollmentMsg = false;
+ public isOtzProgram = false;
+ public isActivityForm = false;
+ public cohortVisitArray = [];
+ public hivSummary: any;
public enrollMentModel = {
enrollMentUrl: [],
queryParams: {}
@@ -124,6 +134,8 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
private modalService: BsModalService,
private risonService: RisonService,
private retrospectiveService: RetrospectiveDataEntryService,
+ private cohortOtzModuleResourceService: CohortOtzModuleResourceService,
+
private http: HttpClient
) {}
@@ -153,41 +165,42 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
}
public loadGroup() {
const uuid = this.activatedRoute.snapshot.paramMap.get('uuid');
- this.subscriptions.add(
- this.communityGroupService.getGroupByUuid(uuid).subscribe(
- (res) => {
- this.group = res;
- _.forEach(this.group.cohortMembers, (member) => {
- member['phoneNumber'] = _.filter(
- member.patient.person.attributes,
- (attribute) =>
- attribute.attributeType.uuid ===
- '72a759a8-1359-11df-a1f1-0026b9348838'
- )[0];
- });
- this.activeMembers = _.filter(
- res.cohortMembers,
- (member) => !member.endDate
- );
- this.cohortVisits = res.cohortVisits.sort((a: any, b: any) => {
- return Math.abs(
- new Date(b.startDate).getTime() - new Date(a.startDate).getTime()
- );
- });
- this.groupVisitDate = {
- date: this.today,
- jsdate: new Date()
- };
- this.checkIfTodayVisitStarted(this.cohortVisits);
- this.generateMembersData(res.cohortMembers, res.cohortVisits);
- },
- (error) => {
- this.errorMessage =
- 'An error occurred while trying to load the group, please check your connection and refresh the page.';
- this.error = true;
- }
- )
- );
+ const dcOtzSubs = forkJoin([
+ this.communityGroupService.getGroupByUuid(uuid)
+ ]);
+ dcOtzSubs.subscribe((results) => {
+ const res = results[0];
+ this.numberOfMembers = res.cohortMembers.length;
+ this.group = res;
+ _.forEach(this.group.cohortMembers, (member) => {
+ member['phoneNumber'] = _.filter(
+ member.patient.person.attributes,
+ (attribute) =>
+ attribute.attributeType.uuid ===
+ '72a759a8-1359-11df-a1f1-0026b9348838'
+ )[0];
+ });
+ this.activeMembers = _.filter(
+ res.cohortMembers,
+ (member) => !member.endDate
+ );
+ this.cohortVisits = res.cohortVisits.sort((a: any, b: any) => {
+ return Math.abs(
+ new Date(b.startDate).getTime() - new Date(a.startDate).getTime()
+ );
+ });
+ this.groupVisitDate = {
+ date: this.today,
+ jsdate: new Date()
+ };
+ const isOtz =
+ this.group.attributes.find((a) => {
+ return a.cohortAttributeType.name === 'programUuid';
+ }).value === '203571d6-a4f2-4953-9e8b-e1105e2340f5';
+ this.isOtzProgram = isOtz;
+ this.checkIfTodayVisitStarted(this.cohortVisits);
+ this.generateMembersData(res.cohortMembers, res.cohortVisits);
+ });
}
public checkIfTodayVisitStarted(cohortVisits: any[]) {
@@ -210,9 +223,33 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
public generateMembersData(cohortMembers, cohortVisits) {
this.membersData = [];
this.columns = [];
- const members = this.generateMemberObject(cohortMembers);
- this.membersData = this.generateRowData(members, cohortVisits);
- this.columns = this.generateColumns(cohortVisits);
+ const memberUuids = this.generatePatientUuids(cohortMembers);
+ this.cohortOtzModuleResourceService
+ .getPatientsLatestHivSummaries(memberUuids)
+ .subscribe((result) => {
+ const summaryMap = this.generateSummaryObject(result);
+ const members = this.generateMemberObject(cohortMembers);
+ this.membersData = this.generateRowData(
+ members,
+ cohortVisits,
+ summaryMap
+ );
+ this.columns = this.generateColumns(cohortVisits);
+ });
+ }
+
+ public generatePatientUuids(cohortMembers) {
+ return cohortMembers.map((member) => {
+ return member.patient.person.uuid;
+ });
+ }
+
+ public generateSummaryObject(hivSummaries) {
+ const summaryMap = new Map();
+ hivSummaries.forEach((summary) => {
+ summaryMap.set(summary.uuid, summary);
+ });
+ return summaryMap;
}
public gridOnCellClick($event) {
@@ -284,6 +321,11 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
}
public showModal(templateRef: TemplateRef) {
+ if (this.numberOfMembers > 15 && this.isOtzProgram) {
+ this.successMessage =
+ 'You have reached Maximum number of members in this club';
+ return;
+ }
if (this.modalRef) {
this.modalRef.hide();
}
@@ -386,14 +428,16 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
public saveGroupVisit() {
this.savingVisit = true;
const groupVisit = {
- visitType: this.visitType,
+ visitType: this.isOtzProgram ? this.otzVisitType : this.visitType,
location: this.group.location.uuid,
startDate: this.groupVisitDate.jsdate,
cohort: this.group.uuid
};
this.communityGroupService.startGroupVisit(groupVisit).subscribe(
(result) => {
- this.showSuccessModal('Visit started successfully!');
+ this.showSuccessModal(
+ `${this.isOtzProgram ? 'OTZ' : ''} Visit started successfully!`
+ );
this.savingVisit = false;
this.closeModal(this.startGroupVisitModal);
this.reloadData();
@@ -451,8 +495,19 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
return present;
}
- private generateRowData(cohortMembers, cohortVisits) {
+ private generateRowData(cohortMembers, cohortVisits, summaryMap) {
const membersData = [];
+ const cohortMemberVisit = [];
+ const conceptStrings = [
+ 'OTZ ORIENTATION: YES',
+ 'OTZ TREATMENT LITERACY: YES',
+ 'OTZ PARTICIPATION: YES',
+ 'PEER TO PEER MENTORSHIP DONE: YES',
+ 'OTZ LEADERSHIP: YES',
+ 'EDUCATION ON PREVENTION METHODS DONE: YES',
+ 'OTZ FUTURE DECISION MAKING: YES',
+ 'TRANSITION TO ADULT CLINIC: YES'
+ ];
for (const member of cohortMembers) {
const memberRow = {
name: member.person.display,
@@ -460,8 +515,29 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
identifiers: member.allIdentifiers,
contacts: member.person.patientPhoneNumber,
member_since: this.datePipe.transform(member.startDate),
- member_to: this.datePipe.transform(member.endDate)
+ member_to: this.datePipe.transform(member.endDate),
+ gender: member.person.gender,
+ age: member.person.age,
+ latest_vl: summaryMap.has(member.person.uuid)
+ ? summaryMap.get(member.person.uuid).latest_vl
+ : '',
+ latest_vl_date: summaryMap.has(member.person.uuid)
+ ? summaryMap.get(member.person.uuid).latest_vl_date
+ : '',
+ vl_category: summaryMap.has(member.person.uuid)
+ ? this.getVlCategory(summaryMap.get(member.person.uuid).latest_vl)
+ : '',
+ latest_rtc: summaryMap.has(member.person.uuid)
+ ? summaryMap.get(member.person.uuid).rtc_date
+ : '',
+ latest_appointment_date: summaryMap.has(member.person.uuid)
+ ? summaryMap.get(member.person.uuid).latest_appointment
+ : '',
+ adherence_type: summaryMap.has(member.person.uuid)
+ ? summaryMap.get(member.person.uuid).adherence
+ : ''
};
+
let i = 0;
for (const cohortVisit of cohortVisits) {
memberRow[`group_visit_${i}`] = this.patientPresent(
@@ -469,13 +545,92 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
cohortVisit
);
memberRow[`group_visit_${i}_uuid`] = cohortVisit.uuid;
+ if (cohortVisit.cohortMemberVisits.length > 0) {
+ cohortMemberVisit.push(cohortVisit.cohortMemberVisits);
+ }
i++;
}
+ const newArray = this.getVisitsArray(cohortMemberVisit);
+ for (let j = 0; j < conceptStrings.length; j++) {
+ const concept = conceptStrings[j];
+ newArray.forEach((item) => {
+ if (item.patient_uuid === member.person.uuid) {
+ const value = item.obs.some(
+ (obs: { display: string }) => obs.display === concept
+ );
+ memberRow[`mod${j + 1}`] = value;
+ }
+ });
+ }
+
membersData.push(memberRow);
}
return membersData;
}
+ private getVlCategory(value: number): string {
+ if (value === null || value === undefined) {
+ return '';
+ }
+ if (value <= 50) {
+ return 'LDL';
+ } else if (value <= 200) {
+ return 'Low Risk Low Level Viremia';
+ } else if (value <= 500) {
+ return 'High Risk Low Level Viremia';
+ } else {
+ return 'Suspected Treatment Failure';
+ }
+ }
+
+ private getVisitsArray(cohortVisitArray) {
+ const filteredArray = [];
+
+ cohortVisitArray.forEach((items) => {
+ items.forEach((item) => {
+ if (item.visit.encounters.length > 0) {
+ const newObject = {
+ patient_uuid: item.visit.patient.uuid,
+ obs: item.visit.encounters.map((enc) => enc.obs)
+ };
+
+ filteredArray.push(newObject);
+ }
+ });
+ });
+
+ const uniqueObsMap = new Map();
+
+ filteredArray.forEach((patient) => {
+ const { patient_uuid, obs } = patient;
+
+ let uniqueObsArray = uniqueObsMap.get(patient_uuid) || [];
+
+ const flattenedObs = [].concat(...obs);
+ const uniqueObs = flattenedObs.filter(
+ (obsItem, index, self) =>
+ index === self.findIndex((t) => t.uuid === obsItem.uuid)
+ );
+
+ uniqueObsArray = uniqueObsArray.concat(uniqueObs);
+ uniqueObsMap.set(patient_uuid, uniqueObsArray);
+ });
+
+ uniqueObsMap.forEach((obsArray, patient_uuid) => {
+ const uniqueNestedObs = obsArray.filter((obsItem, index, self) => {
+ return index === self.findIndex((t) => t.uuid === obsItem.uuid);
+ });
+
+ uniqueObsMap.set(patient_uuid, uniqueNestedObs);
+ });
+
+ const resultArray = Array.from(uniqueObsMap, ([patient_uuid, obs]) => ({
+ patient_uuid,
+ obs
+ }));
+ return resultArray;
+ }
+
onGroupDetailsChanged(updatedGroup) {
this.group = updatedGroup;
this.generateMembersData(this.group.cohortMembers, this.group.cohortVisits);
@@ -492,34 +647,107 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
width: 100
},
{ headerName: 'Name', field: 'name', pinned: 'left', width: 100 },
- { headerName: 'Contacts', field: 'contacts', pinned: 'left', width: 100 },
- {
- headerName: 'Member From',
- field: 'member_since',
- pinned: 'left',
- width: 100
- },
- {
- headerName: 'Member To',
- field: 'member_to',
- pinned: 'left',
- width: 100
- }
+ { headerName: 'Contacts', field: 'contacts', pinned: 'left', width: 100 }
);
let index = 0;
- for (const cohortVisit of cohortVisits) {
- columns.push({
- headerName: `${this.datePipe.transform(cohortVisit.startDate)} Meeting`,
- field: `group_visit_${index}`,
- cellRenderer: (column) => {
- if (column.value) {
- return ` `;
- } else {
- return ` `;
+ if (!this.isOtzProgram) {
+ columns.push(
+ {
+ headerName: 'Member From',
+ field: 'member_since',
+ pinned: 'left',
+ width: 100
+ },
+ {
+ headerName: 'Member To',
+ field: 'member_to',
+ pinned: 'left',
+ width: 100
+ }
+ );
+ for (const cohortVisit of cohortVisits) {
+ columns.push({
+ headerName: `${this.datePipe.transform(
+ cohortVisit.startDate
+ )} Meeting`,
+ field: `group_visit_${index}`,
+ cellRenderer: (column) => {
+ if (column.value) {
+ return ` `;
+ } else {
+ return ` `;
+ }
}
+ });
+ index = index + 1;
+ }
+ }
+
+ if (this.isOtzProgram) {
+ columns.push(
+ {
+ headerName: 'Gender',
+ field: 'gender',
+ pinned: 'left',
+ width: 80
+ },
+ {
+ headerName: 'Age',
+ field: 'age',
+ pinned: 'left',
+ width: 80
+ },
+ {
+ headerName: 'Latest VL',
+ field: 'latest_vl',
+ pinned: 'left',
+ width: 80
+ },
+ {
+ headerName: 'Latest VL Date',
+ field: 'latest_vl_date',
+ pinned: 'left',
+ width: 80
+ },
+ {
+ headerName: 'VL Category',
+ field: 'vl_category',
+ pinned: 'left',
+ width: 80
+ },
+ {
+ headerName: 'Latest RTC',
+ field: 'latest_rtc',
+ pinned: 'left',
+ width: 80
+ },
+ {
+ headerName: 'Latest Appointment Date',
+ field: 'latest_appointment_date',
+ pinned: 'left',
+ width: 80
+ },
+ {
+ headerName: 'Adherence Type',
+ field: 'adherence_type',
+ pinned: 'left',
+ width: 80
}
- });
- index = index + 1;
+ );
+ for (let i = 1; i <= 8; i++) {
+ columns.push({
+ headerName: `Mod ${i}`,
+ field: `mod${i}`,
+ width: 70,
+ cellRenderer: (column) => {
+ if (column.value) {
+ return ` `;
+ } else {
+ return ` `;
+ }
+ }
+ });
+ }
}
return columns;
}
@@ -552,6 +780,7 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
break;
case !validation.notEnrolledInGroupProgram.found:
this.validatingEnrollment = false;
+ this.validateAge(patient);
this.showEnrollButton(patient);
break;
case validation.enrolledInAnotherGroupInSameProgram.found:
@@ -608,6 +837,14 @@ export class GroupDetailComponent implements OnInit, OnDestroy, AfterViewInit {
queryParams
};
}
+
+ private validateAge(patient) {
+ if (patient._person.age > 9 && patient._person.age <= 24) {
+ this.showOTZEnrollmentMsg = true;
+ } else {
+ this.showOTZEnrollmentMsg = false;
+ }
+ }
private enrollPatientToGroup(group: Group, patient: Patient) {
this.communityGroupMemberService
.createMember(group.uuid, patient.uuid)
diff --git a/src/app/group-manager/group-editor/group-editor-component.html b/src/app/group-manager/group-editor/group-editor-component.html
index 3edff8a87..9c7043b7f 100644
--- a/src/app/group-manager/group-editor/group-editor-component.html
+++ b/src/app/group-manager/group-editor/group-editor-component.html
@@ -146,6 +146,7 @@
placeholder="Please type to search"
id="groupProgram"
[(ngModel)]="groupProgram"
+ (ngModelChange)="onProgramChanged($event)"
[items]="groupPrograms"
name="groupProgramInput"
#groupProgramInput="ngModel"
@@ -210,6 +211,62 @@
+
+
diff --git a/src/app/group-manager/group-editor/group-editor-component.ts b/src/app/group-manager/group-editor/group-editor-component.ts
index 33d317591..79c3260f0 100644
--- a/src/app/group-manager/group-editor/group-editor-component.ts
+++ b/src/app/group-manager/group-editor/group-editor-component.ts
@@ -38,12 +38,44 @@ export class GroupEditorComponent implements OnInit {
public provider: any;
public address: string;
public groupType: any;
+ public visitStartedToday: boolean;
+ retroVisitDate: any;
+ visitStartedForThisDate: boolean;
+ retrospectiveOn: boolean;
+ public visitStartedRetro: boolean;
+ selectedFutureGroupVisitDate: boolean;
+ public visitTypes = [];
+ selectedPastGroupVisitDate: boolean;
+ public currentMonth = Moment().month() + 1;
+ public today = {
+ year: Moment().year(),
+ month: this.currentMonth,
+ day: Moment().date()
+ };
+ public groupDateCreated: any = {
+ date: this.today,
+ jsdate: new Date(),
+ formatted: Moment().format('YYYY-MM-DD')
+ };
public editType = 'create';
public actionButtonText = `${this.editType} Group`;
public groupTypes: any = [];
public groupPrograms: any = [];
+ public groupActivities: string[] = [
+ 'Tailoring',
+ 'Art WorldTable',
+ 'Tennis',
+ 'Chess',
+ 'Football',
+ 'ReadingDarts',
+ 'Farming',
+ 'None'
+ ];
public groupProgram: any;
+ public groupActivity: any;
public success = false;
+ public showGroupActivity = false;
+ public otzProgramUuid = '203571d6-a4f2-4953-9e8b-e1105e2340f5';
public message = '';
public busy = false;
public providerLoading;
@@ -60,6 +92,7 @@ export class GroupEditorComponent implements OnInit {
this.groupUuid = state.groupUuid;
this.groupName = state.groupName;
this.groupProgram = state.groupProgram;
+ this.groupActivity = state.groupActivity;
this.facility = state.facility;
this.provider = state.provider;
this.address = state.address;
@@ -98,6 +131,12 @@ export class GroupEditorComponent implements OnInit {
this.setDefaultProgram();
this.autoGenerateGroupNumber();
}
+ if (
+ this.editType.toLowerCase() === 'edit' &&
+ this.groupProgram.value === this.otzProgramUuid
+ ) {
+ this.showGroupActivity = true;
+ }
this.route.parent.parent.parent.url.subscribe((urlSegment: any) => {
if (!_.isEmpty(this.facilities)) {
@@ -119,6 +158,14 @@ export class GroupEditorComponent implements OnInit {
});
}
+ private autoGenerateOTZGroupNumber() {
+ this._communityService
+ .generateGroupNumber(this.facility.value ? this.facility.value : '')
+ .subscribe((res: any) => {
+ this.groupNo = res.groupNumber.replace(/DC/g, 'OTZ');
+ });
+ }
+
public setUpProviderTypeAhead() {
this.providerSuggest
.pipe(
@@ -195,7 +242,6 @@ export class GroupEditorComponent implements OnInit {
this.creatingGroup.emit(true);
this.saving = true;
if (this.editType.toLowerCase() === 'edit') {
- console.log(this.editType);
this.updateGroup();
}
if (this.editType.toLowerCase() === 'create') {
@@ -247,11 +293,18 @@ export class GroupEditorComponent implements OnInit {
value: this.groupProgram['value']
});
}
+ if (this.groupActivity !== '') {
+ attributes.push({
+ cohortAttributeType: 'groupActivity',
+ value: this.groupActivity
+ });
+ }
const payLoad = {
name: this.groupName,
description: '',
location: this.facility.value,
- startDate: Moment().format('YYYY-MM-DD'),
+ startDate:
+ this.groupDateCreated.formatted || Moment().format('YYYY-MM-DD'),
cohortType: DEFAULT_GROUP_TYPE,
groupCohort: true,
attributes: attributes
@@ -285,6 +338,7 @@ export class GroupEditorComponent implements OnInit {
this.groupNo = '';
this.groupType = {};
this.groupProgram = {};
+ this.groupActivity = {};
this.address = '';
this.provider = '';
this.success = false;
@@ -441,4 +495,22 @@ export class GroupEditorComponent implements OnInit {
this.facility = event;
this.autoGenerateGroupNumber();
}
+
+ public onGroupActivityChanged(event) {
+ this.groupActivity = event;
+ this.showGroupActivity = true;
+ }
+
+ public changedGroupVisitDate(event) {
+ this.groupDateCreated = event;
+ }
+ public onProgramChanged(event) {
+ if (event.value === this.otzProgramUuid) {
+ this.autoGenerateOTZGroupNumber();
+ this.showGroupActivity = true;
+ } else {
+ this.autoGenerateGroupNumber();
+ this.showGroupActivity = false;
+ }
+ }
}
diff --git a/src/app/group-manager/group-manager-search/group-manager-search.component.html b/src/app/group-manager/group-manager-search/group-manager-search.component.html
index bffb2f931..1af579c73 100644
--- a/src/app/group-manager/group-manager-search/group-manager-search.component.html
+++ b/src/app/group-manager/group-manager-search/group-manager-search.component.html
@@ -53,8 +53,28 @@ Group Search
margin-left: 15px;
"
>
- View All Groups In This Facility
-
+ View All DC Groups In This Facility
+
+
+
+ View All OTZ Clubs In This Facility
+
diff --git a/src/app/group-manager/group-manager-search/group-manager-search.component.ts b/src/app/group-manager/group-manager-search/group-manager-search.component.ts
index 964abbbc9..ac6e6982d 100644
--- a/src/app/group-manager/group-manager-search/group-manager-search.component.ts
+++ b/src/app/group-manager/group-manager-search/group-manager-search.component.ts
@@ -1,4 +1,11 @@
-import { Component, OnInit, OnDestroy, TemplateRef } from '@angular/core';
+import {
+ Component,
+ OnInit,
+ OnDestroy,
+ TemplateRef,
+ ViewChild,
+ AfterViewInit
+} from '@angular/core';
import { CommunityGroupService } from '../../openmrs-api/community-group-resource.service';
import { ToastrFunctionService } from 'src/app/shared/services/toastr-function.service';
import * as _ from 'lodash';
@@ -17,6 +24,7 @@ import { Group } from '../../models/group.model';
import { GridOptions, RowNode } from 'ag-grid';
import { ProgramResourceService } from 'src/app/openmrs-api/program-resource.service';
import { IndividualConfig, ToastrService } from 'ngx-toastr';
+import { CohortOtzModuleResourceService } from 'src/app/etl-api/cohort-otz-module-resource.service';
@Component({
selector: 'group-manager-search',
templateUrl: './group-manager-search.component.html',
@@ -40,11 +48,13 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy {
public routeLoading = false;
fetchingGroups: boolean;
previousLocationUuid: string;
- columnDefs = this.generateColumns();
rowData: any;
+ columnDefs = this.generateColumns();
public gridOptions: GridOptions = this.getGridOptions();
public filterText = '';
hideGroupsInCurrentFacility: boolean;
+ public isOTZprogram = false;
+ public filterOTZ = '';
constructor(
private groupService: CommunityGroupService,
@@ -52,7 +62,8 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy {
private bsModalService: BsModalService,
private route: ActivatedRoute,
private programResourceService: ProgramResourceService,
- private toastrService: ToastrFunctionService
+ private toastrService: ToastrFunctionService,
+ private cohortOtzModuleResourceService: CohortOtzModuleResourceService
) {}
ngOnInit(): void {
@@ -91,6 +102,8 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy {
public showGroupsInFacilty() {
this.rowData = [];
this.fetchingGroups = true;
+ this.isOTZprogram = false;
+ this.filterText = '';
const locationUuid = this.router.url.split('/')[2];
if (locationUuid !== this.previousLocationUuid) {
this.fetchingGroups = true;
@@ -100,9 +113,9 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy {
this.groupsInCurrentFacility = res.map((result) => new Group(result));
this.hideGroupsInCurrentFacility = false;
this.fetchingGroups = false;
+ this.isOTZprogram = false;
this.previousLocationUuid = locationUuid;
this.rowData = this.groupsInCurrentFacility;
- console.log(this.rowData, 'rowData');
});
this.subscription.add(sub);
} else {
@@ -110,6 +123,52 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy {
}
}
+ public showOTZGroupsInFacilty() {
+ this.rowData = [];
+ this.fetchingGroups = true;
+ this.isOTZprogram = true;
+ const locationUuid = this.router.url.split('/')[2];
+ this.fetchingGroups = true;
+ const sub = this.groupService
+ .getGroupsByLocationUuid(locationUuid)
+ .subscribe((res) => {
+ this.groupsInCurrentFacility = res.map((result) => {
+ const groupInstance = new Group(result);
+ const cohortUuid = this.generateCohortUuids([groupInstance]);
+ this.cohortOtzModuleResourceService
+ .getCohortSuppressionStatus(Array.from(cohortUuid.keys()))
+ .subscribe((supressionRate: any) => {
+ if (supressionRate.result.length > 0) {
+ groupInstance.viralSuppression =
+ supressionRate.result[0].suppression_rate_percentage.toFixed(
+ 2
+ ) + '%';
+ }
+ });
+ return groupInstance;
+ });
+ this.hideGroupsInCurrentFacility = false;
+ this.fetchingGroups = false;
+ this.isOTZprogram = false;
+ this.rowData = this.groupsInCurrentFacility;
+ this.filterText = 'OTZ PROGRAM';
+ if (this.gridOptions.api) {
+ this.gridOptions.api.onFilterChanged();
+ }
+ });
+
+ this.columnDefs = this.generateColumns();
+ this.subscription.add(sub);
+ }
+
+ public generateCohortUuids(cohort) {
+ const patientUuids = new Map();
+ cohort.forEach((uuid) => {
+ patientUuids.set(uuid._openmrsModel.uuid, uuid._openmrsModel);
+ });
+ return patientUuids;
+ }
+
public navigateToGroupDetails(group, newGroup?) {
if (this.modalRef) {
this.modalRef.hide();
@@ -162,7 +221,8 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy {
return (
_.includes(node.data.display.toLowerCase(), filterCaseLowercase) ||
_.includes(node.data.facility.toLowerCase(), filterCaseLowercase) ||
- _.includes(node.data.status.toLowerCase(), filterCaseLowercase)
+ _.includes(node.data.status.toLowerCase(), filterCaseLowercase) ||
+ _.includes(node.data.program, this.filterText)
);
}
@@ -241,6 +301,50 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy {
caseSensitive: false
}
},
+ ...(this.isOTZprogram
+ ? [
+ {
+ headerName: 'Viral Suppression',
+ field: 'viralSuppression',
+ sortable: true,
+ filter: 'agTextColumnFilter',
+ width: 200,
+ filterParams: {
+ caseSensitive: false
+ }
+ },
+ {
+ headerName: 'Last Meeting Date',
+ field: 'lastMeetingDate',
+ sortable: true,
+ filter: 'agTextColumnFilter',
+ width: 200,
+ filterParams: {
+ caseSensitive: false
+ }
+ },
+ {
+ headerName: 'OTZ Champion',
+ field: 'otzChampion',
+ sortable: true,
+ filter: 'agTextColumnFilter',
+ width: 200,
+ filterParams: {
+ caseSensitive: false
+ }
+ },
+ {
+ headerName: 'Group Activity',
+ field: 'groupActivity',
+ sortable: true,
+ filter: 'agTextColumnFilter',
+ width: 200,
+ filterParams: {
+ caseSensitive: false
+ }
+ }
+ ]
+ : []),
{
headerName: 'Actions',
field: 'endDate',
diff --git a/src/app/group-manager/group-manager.module.ts b/src/app/group-manager/group-manager.module.ts
index b06933f05..144b1cad0 100644
--- a/src/app/group-manager/group-manager.module.ts
+++ b/src/app/group-manager/group-manager.module.ts
@@ -13,6 +13,7 @@ import { SuccessModalComponent } from './modals/success-modal.component';
import { GroupEditorComponent } from './group-editor/group-editor-component';
import { GroupSearchInputComponent } from './group-manager-search/group-search-input/group-search-input.component';
import { PatientSearchModule } from '../patient-search/patient-search.module';
+import { CohortOtzModuleResourceService } from '../etl-api/cohort-otz-module-resource.service';
@NgModule({
declarations: [
@@ -34,7 +35,7 @@ import { PatientSearchModule } from '../patient-search/patient-search.module';
PatientSearchModule
],
exports: [GroupSearchInputComponent, GroupEditorComponent],
- providers: [DatePipe],
+ providers: [DatePipe, CohortOtzModuleResourceService],
entryComponents: [
DatePickerModalComponent,
SuccessModalComponent,
diff --git a/src/app/group-manager/modals/group-transfer-modal.component.ts b/src/app/group-manager/modals/group-transfer-modal.component.ts
index 1d97862b0..7d4fcf1a3 100644
--- a/src/app/group-manager/modals/group-transfer-modal.component.ts
+++ b/src/app/group-manager/modals/group-transfer-modal.component.ts
@@ -21,9 +21,11 @@ import { Patient } from '../../models/patient.model';
{{ patient.person.display }} is enrolled in
- {{ groupToUnenroll.cohort.name }} under DC program. Would you like to
- unenroll from {{ groupToUnenroll.cohort.name }} and
- enroll in {{ groupToEnroll.name }} {{ groupToEnroll.startDate | date: 'yyyy-MM-dd' }}. Would you like to unenroll from
+ {{ groupToUnenroll.cohort.name }} and enroll in
+ {{ groupToEnroll.name }} ?
@@ -42,7 +44,9 @@ export class GroupTransferModalComponent implements OnInit {
constructor(public modalRef: BsModalRef) {}
- ngOnInit() {}
+ ngOnInit() {
+ console.log('groupToEnroll', this.groupToUnenroll);
+ }
confirm() {
this.modalRef.hide();
diff --git a/src/app/models/group.model.ts b/src/app/models/group.model.ts
index 5c205a6c8..eaecc745a 100644
--- a/src/app/models/group.model.ts
+++ b/src/app/models/group.model.ts
@@ -4,6 +4,7 @@ import * as _ from 'lodash';
const program_visits_config = require('../program-visit-encounter-search/program-visits-config.json');
export class Group extends BaseModel {
+ private _viralSuppression: string;
constructor(openmrsModel?: any) {
super(openmrsModel);
this._openmrsModel.display = this._openmrsModel.name;
@@ -38,6 +39,17 @@ export class Group extends BaseModel {
@serializable()
public get status() {
+ const lastMeetingDate = this.getLatestMeetingDate(
+ this._openmrsModel.cohortVisits
+ );
+ // if last meeting date is more than 3 months ago, group is inactive
+ if (lastMeetingDate) {
+ const today = new Date();
+ const threeMonthsAgo = new Date(today.setMonth(today.getMonth() - 3));
+ if (lastMeetingDate < threeMonthsAgo) {
+ return 'Inactive';
+ }
+ }
return this._openmrsModel.endDate ? 'Disbanded' : 'Active';
}
@@ -62,6 +74,34 @@ export class Group extends BaseModel {
return this.getGroupMembersCount(this._openmrsModel.cohortMembers);
}
+ @serializable()
+ public get otzChampion() {
+ const attrType = this.getCurrentLeader(this._openmrsModel.cohortLeaders);
+ if (attrType) {
+ return attrType.person.display.replace(/\d+|-/g, '');
+ }
+ return null;
+ }
+
+ @serializable()
+ public get groupActivity() {
+ const attrType = 'groupActivity';
+ return this.getAttribute(attrType, this._openmrsModel.attributes);
+ }
+
+ public get viralSuppression() {
+ return this._viralSuppression || 'Unkown %';
+ }
+
+ public set viralSuppression(value: string) {
+ this._viralSuppression = value;
+ }
+
+ @serializable()
+ public get lastMeetingDate() {
+ return this.getLatestMeetingDate(this._openmrsModel.cohortVisits);
+ }
+
public getAttribute(attributeType, attributes) {
const attr = _.filter(
attributes,
@@ -73,10 +113,37 @@ export class Group extends BaseModel {
return null;
}
+ public getLatestMeetingDate(startDates) {
+ if (startDates.length > 0) {
+ const latestStartDateString = startDates.reduce(
+ (maxDate, currentDateObject) => {
+ const currentStartDate = new Date(currentDateObject.startDate);
+ const maxStartDate = maxDate ? new Date(maxDate.startDate) : null;
+
+ if (!maxStartDate || currentStartDate > maxStartDate) {
+ return currentDateObject;
+ } else {
+ return maxDate;
+ }
+ },
+ null
+ ).startDate;
+ return new Date(latestStartDateString);
+ }
+ }
+
public getGroupMembersCount(cohortMembers) {
const active_members = cohortMembers.filter(
(current) => current.endDate == null
);
return active_members ? active_members.length : 0;
}
+
+ public getCurrentLeader(allLeaders: any[]) {
+ const currentLeader = _.filter(
+ allLeaders,
+ (leader) => leader.endDate == null
+ )[0];
+ return currentLeader;
+ }
}
diff --git a/src/app/openmrs-api/community-group-resource.service.ts b/src/app/openmrs-api/community-group-resource.service.ts
index 1c3f759a2..01e928abf 100644
--- a/src/app/openmrs-api/community-group-resource.service.ts
+++ b/src/app/openmrs-api/community-group-resource.service.ts
@@ -12,7 +12,7 @@ export class CommunityGroupService {
public cachedResults: BehaviorSubject = new BehaviorSubject([]);
public v = 'full';
public _v =
- 'custom:(uuid,name,description,startDate,endDate,location:(display),attributes,cohortMembers:(uuid,endDate))';
+ 'custom:(uuid,name,description,cohortLeaders,startDate,endDate,location:(display),cohortVisits:(startDate),attributes,cohortMembers:(uuid,endDate))';
constructor(
private http: HttpClient,
diff --git a/src/app/openmrs-api/encounter-resource.service.ts b/src/app/openmrs-api/encounter-resource.service.ts
index ce1b9f197..a9f6f8181 100644
--- a/src/app/openmrs-api/encounter-resource.service.ts
+++ b/src/app/openmrs-api/encounter-resource.service.ts
@@ -11,6 +11,7 @@ export class EncounterResourceService {
'patient:(uuid,uuid),form:(uuid,name),' +
'visit:(uuid,display,auditInfo,startDatetime,stopDatetime,location:(uuid,display)' +
',visitType:(uuid,name)),' +
+ 'obs:(uuid,obsDatetime,concept:(uuid,uuid,name:(display),datatype),value:ref,groupMembers),' +
'location:ref,encounterType:ref,encounterProviders:(uuid,display,provider:(uuid,display)))';
constructor(
diff --git a/src/app/patient-dashboard/common/formentry/formentry.component.html b/src/app/patient-dashboard/common/formentry/formentry.component.html
index fc281b3f1..f8725d48d 100644
--- a/src/app/patient-dashboard/common/formentry/formentry.component.html
+++ b/src/app/patient-dashboard/common/formentry/formentry.component.html
@@ -142,13 +142,20 @@
minWidth="300"
closeresponsive="true"
>
-
+
Form submitted successfully.
+
+
+
+
+
+ Patient has been enrolled to OTZ program.
+
Differentiated Care Referral
@@ -261,6 +268,14 @@
>
Group Manager
+
+ Group Manager
+
{
console.error('error', err);
@@ -1282,6 +1297,30 @@ export class FormentryComponent implements OnInit, OnDestroy {
);
}
+ private enrollPatientToOtzProgram() {
+ const formattedDate = new Date(this.extractEncounterDate())
+ .toISOString()
+ .slice(0, 10);
+ const payload = {
+ programUuid: '203571d6-a4f2-4953-9e8b-e1105e2340f5',
+ patient: this.patient,
+ dateEnrolled: formattedDate,
+ dateCompleted: '',
+ location: this.encounterLocation.value,
+ enrollmentUuid: ''
+ };
+
+ this.programManagerService.enrollPatient(payload).subscribe(
+ (enrollment) => {
+ console.log('response', enrollment);
+ this.isBusyIndicator(false);
+ },
+ (error) => {
+ console.log('error', error);
+ }
+ );
+ }
+
private checkDuplicate(payloadTypes) {
this.patientService.currentlyLoadedPatientUuid
.pipe(
diff --git a/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.css b/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.css
new file mode 100644
index 000000000..4afd9ba5d
--- /dev/null
+++ b/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.css
@@ -0,0 +1,260 @@
+.btn-success.text-white {
+ color: white !important;
+}
+
+@media screen and (min-width: 600px) {
+ .program-header {
+ width: 70%;
+ }
+}
+
+.program-header-wrapper {
+ /* background-color: #203864; */
+ color: #ccc;
+}
+
+.program .program-header,
+.program .program-footer {
+ margin: 0 20px 0 20px;
+ font-size: 1.72rem;
+ border: none;
+ padding: 0 0;
+ font-family: Lato, 'Helvetica Neue', Arial, Helvetica, sans-serif;
+ font-weight: 700;
+ line-height: 36px;
+ text-transform: none;
+ color: #fff;
+ opacity: 0.9;
+}
+
+.program.non-enrolled .program-body {
+ padding: 12px;
+}
+
+.program.non-enrolled .program-header-wrapper {
+ color: inherit;
+}
+
+.program-container-fluid {
+ padding-right: 15px;
+ padding-left: 15px;
+}
+
+.program .program-body {
+ padding-top: 12px;
+ padding-bottom: 12px;
+}
+
+.program.non-enrolled .program-body {
+ border-top: 1px solid rgba(34, 36, 38, 0.15);
+}
+
+.program .program-footer {
+ /* background-color: whitesmoke; */
+ color: #fff;
+ margin: 0;
+ padding: 5px 12px;
+ /* border: 1px solid rgba(34, 36, 38, 0.15); */
+ border-top: 0;
+}
+
+.program-footer a.btn {
+ color: #fff !important;
+ display: inline-block;
+ margin-right: 10px;
+ padding: 6px 9px;
+}
+
+.program-footer a.btn:last-child {
+ margin-right: 0;
+}
+
+a.btn.btn-info {
+ margin-bottom: 0;
+ margin-left: 0;
+}
+
+.component-title {
+ font-size: 22px;
+ text-transform: capitalize;
+ padding-bottom: 8px;
+ border-bottom: 1px solid rgba(34, 36, 38, 0.15);
+ margin-bottom: 20px;
+ padding-left: 2.5rem;
+}
+
+.program hr {
+ margin: 1rem 0;
+ line-height: 1;
+ height: 0;
+ font-weight: 700;
+ font-size: 1rem;
+ clear: both;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ color: rgba(0, 0, 0, 0.85);
+ user-select: none;
+ border-top: 1px solid rgba(34, 36, 38, 0.15);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.program .enrolled {
+ margin-bottom: 20px;
+}
+
+.clear {
+ clear: both;
+}
+
+h3 {
+ padding-top: 0;
+}
+
+.program-header-wrapper {
+ background-color: #203864;
+ color: #ccc;
+ border: 2px solid rgba(34, 36, 38, 0.15);
+ border-radius: 4px;
+ margin-bottom: 8px;
+}
+
+.program.non-enrolled .program-body {
+ padding: 12px;
+}
+
+.program.non-enrolled .program-header-wrapper {
+ background-color: darkred;
+ color: #ccc;
+}
+
+.program .program-body {
+ padding-top: 12px;
+ padding-bottom: 12px;
+ border-left: 1px solid rgba(34, 36, 38, 0.15);
+ border-right: 1px solid rgba(34, 36, 38, 0.15);
+ border-bottom: 1px solid rgba(34, 36, 38, 0.15);
+}
+
+.program .program-footer {
+ /* background-color: whitesmoke; */
+ color: #fff;
+ margin: 0;
+ padding: 5px;
+ /* border: 1px solid rgba(34, 36, 38, 0.15); */
+ border-top: 0;
+}
+
+.program-container-fluid {
+ padding: 0 10px 0 10px;
+}
+
+.program hr {
+ margin: 1rem 0;
+ line-height: 1;
+ height: 0;
+ font-weight: 700;
+ font-size: 1rem;
+ clear: both;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ color: rgba(0, 0, 0, 0.85);
+ user-select: none;
+ border-top: 1px solid rgba(34, 36, 38, 0.15);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.program .enrolled {
+ margin-bottom: 20px;
+}
+
+.clear {
+ clear: both;
+}
+
+.fa-pencil:before {
+ margin-right: 5px;
+}
+
+label.buttons {
+ height: 22px;
+ display: block;
+}
+
+.enroll-button {
+ margin-top: -2px;
+}
+
+.buttons button {
+ float: left;
+}
+
+.otz-header {
+ display: flex;
+ justify-content: space-between;
+}
+
+ul li {
+ list-style: none !important;
+ padding-top: 24px;
+ font-weight: 400;
+ font-size: 18px;
+ font-style: bold;
+}
+
+.otz-nav-header li {
+ list-style: none !important;
+ padding: 6px 22px;
+ margin: 0 2px;
+ border: 1px solid #1081c2;
+ border-radius: 4px;
+ font-size: 18px;
+ color: rgb(0, 140, 255);
+ cursor: pointer;
+}
+
+.otz-nav-header {
+ display: flex;
+ justify-content: space-between;
+ margin-left: -40px;
+}
+
+.otz-content-wrapper {
+ margin-left: -38px;
+}
+
+.thead-blue {
+ background-color: #086becee;
+ color: #fff;
+}
+
+.theader-appoint {
+ padding: 8px 4px;
+ font-size: 18px;
+ margin: 0 2px;
+}
+
+.ldl-col {
+ color: green;
+}
+
+.low-risk-col {
+ color: orangered;
+}
+
+.high-risk-col {
+ color: orange;
+}
+
+.treatment-col {
+ color: red;
+}
+
+.disc-form {
+ color: black;
+ background-color: MediumSeaGreen;
+ padding: 8px;
+ border-radius: 4px;
+ width: 50%;
+ font-size: 20px;
+ font-weight: bold;
+}
diff --git a/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.html b/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.html
new file mode 100644
index 000000000..f9364cfb6
--- /dev/null
+++ b/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.html
@@ -0,0 +1,216 @@
+
+
+
Patient not eligible for OTZ Program (age 10 and 24)
+
+
+
+
+ Reason for Discontinuation:
+ {{ reasonForDiscontinuation }}
+
+
+
+
+
+
+
+
+
+
+ Last Encounter Date:
+ {{ patientData?.encounter_datetime }}
+
+
+ ARV Regimen: {{ patientData?.arv_first_regimen }}
+
+
+ RTC Date: {{ patientData?.rtc_date }}
+
+ Module:
+
+ Last Viral Load:
+ {{ patientData?.vl_1 | zeroVl }}
+
+
+ Last Viral Load Date:
+
+
+ {{ patientData?.vl_1_date | date: 'dd-MM-yyyy' }}
+
+
+
+
+ Viral Load Categorization:
+
+
+ {{ viralLoadCategory }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ patientData?.prev_rtc_date | date: 'dd-MM-yyyy' }}
+
+ {{ patientData?.encounter_type_name }}
+ {{ patientData?.rtc_date | date: 'dd-MM-yyyy' }}
+
+ {{ patientData?.med_pickup_rtc_date | date: 'dd-MM-yyyy' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ patientData?.encounter_datetime }}
+ {{ patientData?.cur_arv_adherence }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.test_datetime | date: 'dd-MM-yyyy' }}
+
+ {{ item.hiv_viral_load }}
+ 50 && item.hiv_viral_load <= 200,
+ 'high-risk-col':
+ item.hiv_viral_load > 200 && item.hiv_viral_load <= 500,
+ 'treatment-col': item.hiv_viral_load > 500
+ }"
+ >
+ {{
+ item.hiv_viral_load <= 50
+ ? 'LDL'
+ : item.hiv_viral_load <= 200
+ ? 'Low Risk Low Level Viremia'
+ : item.hiv_viral_load <= 500
+ ? 'High Risk Low Level Viremia'
+ : 'Suspected Treatment Failure'
+ }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.spec.ts b/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.spec.ts
new file mode 100644
index 000000000..16e1099be
--- /dev/null
+++ b/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.spec.ts
@@ -0,0 +1,24 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { OtzSnapshotComponent } from './otz-snapshot.component';
+
+describe('OtzSnapshotComponent', () => {
+ let component: OtzSnapshotComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [OtzSnapshotComponent]
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(OtzSnapshotComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.ts b/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.ts
new file mode 100644
index 000000000..3974c4851
--- /dev/null
+++ b/src/app/patient-dashboard/common/otz-snapshot/otz-snapshot.component.ts
@@ -0,0 +1,215 @@
+import { Component, OnInit } from '@angular/core';
+import { Router, ActivatedRoute } from '@angular/router';
+import { Patient } from 'src/app/models/patient.model';
+import { PatientService } from '../../services/patient.service';
+import { HivSummaryResourceService } from 'src/app/etl-api/hiv-summary-resource.service';
+import { EncounterResourceService } from 'src/app/openmrs-api/encounter-resource.service';
+import { LabsResourceService } from 'src/app/etl-api/labs-resource.service';
+import { take } from 'rxjs/operators';
+import * as moment from 'moment';
+import * as _ from 'lodash';
+
+@Component({
+ selector: 'app-otz-snapshot',
+ templateUrl: './otz-snapshot.component.html',
+ styleUrls: ['./otz-snapshot.component.css']
+})
+export class OtzSnapshotComponent implements OnInit {
+ selectedItem = 'summary';
+ subscription: any;
+ patient: Patient;
+ otzEnrollment = false;
+ programManagerUrl: any;
+ groupManagerUrl: any;
+ otzProgramExit: any;
+ dateEnrolled: any;
+ dateCompleted: any;
+ loadingData: boolean;
+ hasLoadedData: boolean;
+ patientCareStatus: any;
+ clinicalEncounters: any;
+ patientData: any;
+ hasData: boolean;
+ isHEIActive: boolean;
+ viralLoadCategory: string;
+ isOtzDiscontinued = false;
+ reasonForDiscontinuation: string;
+ otzDiscontinuationDate: any;
+ viralLoadHistory: any[];
+ isPatientEligibleForOtz = false;
+
+ constructor(
+ private patientService: PatientService,
+ private hivSummaryResourceService: HivSummaryResourceService,
+ private labsResourceService: LabsResourceService,
+ private encounterResource: EncounterResourceService,
+ private router: Router,
+ private route: ActivatedRoute
+ ) {}
+
+ ngOnInit() {
+ this.subscription = this.patientService.currentlyLoadedPatient.subscribe(
+ (patient) => {
+ this.patient = new Patient({});
+ if (patient) {
+ this.isHEIActive = patient.enrolledPrograms.some((program) => {
+ return (
+ program.programUuid === 'a8e7c30d-6d2f-401c-bb52-d4433689a36b' &&
+ program.isEnrolled === true
+ );
+ });
+ this.programManagerUrl =
+ '/patient-dashboard/patient/' +
+ patient.uuid +
+ '/general/general/program-manager/new-program';
+ this.otzProgramExit =
+ '/patient-dashboard/patient/' +
+ patient.uuid +
+ '/general/general/formentry/ab16711d-890d-4128-95ce-0e955babd711';
+ this.groupManagerUrl =
+ '/patient-dashboard/patient/' +
+ patient.uuid +
+ '/general/general/group-enrollment';
+ this.getOtzEnrollments(patient.person.age, patient.enrolledPrograms);
+ this.getHivSummary(patient);
+ this.getHistoricalPatientLabResults(patient);
+ this.getOtzDiscontinuation(patient);
+ }
+ }
+ );
+ }
+
+ selectItem(item: string) {
+ this.selectedItem = item;
+ }
+
+ private getOtzEnrollments(age, enrolledPrograms) {
+ const otz = enrolledPrograms.filter(
+ (program) =>
+ program.concept.uuid === 'fd90d6b2-7302-4a9c-ad1b-1f93eff77afb'
+ );
+ if (otz.length > 0 && otz[0].isEnrolled) {
+ this.dateEnrolled = otz[0].dateEnrolled;
+ this.otzEnrollment = true;
+ } else {
+ this.dateCompleted = otz[0].dateCompleted;
+ }
+ if (age > 9 && age <= 24) {
+ this.isPatientEligibleForOtz = true;
+ }
+ }
+
+ private getOtzDiscontinuation(patient) {
+ patient.encounters.filter((encounter) => {
+ console.log(encounter);
+ const reasonForDiscontinuation = encounter.obs.filter((obs) => {
+ return obs.concept.uuid === 'a89e3f94-1350-11df-a1f1-0026b9348838';
+ });
+ console.log(reasonForDiscontinuation);
+ if (reasonForDiscontinuation.length > 0) {
+ this.isOtzDiscontinued = true;
+ this.reasonForDiscontinuation =
+ reasonForDiscontinuation[0].value.display;
+ this.otzDiscontinuationDate = encounter.encounterDatetime;
+ }
+ });
+ }
+
+ public getHivSummary(patient) {
+ this.loadingData = true;
+ this.hivSummaryResourceService
+ .getHivSummary(patient.uuid, 0, 10, false, this.isHEIActive)
+ .pipe(take(1))
+ .subscribe((results) => {
+ let latestVlResult: any;
+ let latestVlDate = '';
+ let latestVl = null;
+ this.loadingData = false;
+ this.hasLoadedData = true;
+ if (results[0]) {
+ latestVlResult = this.getlatestVlResult(results);
+ latestVlDate = latestVlResult.vl_1_date;
+ latestVl = latestVlResult.vl_1;
+ this.patientCareStatus = results[0].patient_care_status;
+ }
+ this.clinicalEncounters = this.getClinicalEncounters(results);
+ this.patientData = _.first(this.clinicalEncounters);
+ const patientDataCopy = this.patientData;
+
+ if (!_.isNil(this.patientData)) {
+ // assign latest vl and vl_1_date
+ this.patientData = Object.assign(patientDataCopy, {
+ vl_1_date: latestVlDate,
+ vl_1: latestVl
+ });
+ this.hasData = true;
+ }
+ if (latestVl) {
+ this.viralLoadCategory = this.getCategory(latestVl);
+ }
+ });
+ }
+
+ private getCategory(value: number): string {
+ if (value <= 50) {
+ return 'LDL';
+ } else if (value <= 200) {
+ return 'Low Risk Low Level Viremia';
+ } else if (value <= 500) {
+ return 'High Risk Low Level Viremia';
+ } else {
+ return 'Suspected Treatment Failure';
+ }
+ }
+
+ public getHistoricalPatientLabResults(patient) {
+ this.labsResourceService
+ .getHistoricalPatientLabResults(patient.uuid, {
+ startIndex: '0',
+ limit: '20'
+ })
+ .pipe(take(1))
+ .subscribe((results) => {
+ this.getViralLoadHistory(results);
+ });
+ }
+
+ private getViralLoadHistory(labResults: any[]): any {
+ const filteredArray = labResults.filter((item) => {
+ return item.hiv_viral_load !== null && item.test_datetime !== null;
+ });
+
+ filteredArray.sort((a, b) => {
+ const dateA = new Date(a.test_datetime).getTime();
+ const dateB = new Date(b.test_datetime).getTime();
+ return dateB - dateA;
+ });
+
+ const result = filteredArray.map((item) => {
+ return {
+ hiv_viral_load: item.hiv_viral_load,
+ test_datetime: item.test_datetime
+ };
+ });
+ this.viralLoadHistory = result;
+ }
+
+ private getClinicalEncounters(summaries: any[]): any[] {
+ if (summaries) {
+ return _.filter(summaries, (summary: any) => {
+ return summary.is_clinical_encounter === 1;
+ });
+ }
+ }
+
+ private getlatestVlResult(hivSummaryData) {
+ const orderByVlDate = _.orderBy(
+ hivSummaryData,
+ (hivSummary) => {
+ return moment(hivSummary.vl_1_date);
+ },
+ ['desc']
+ );
+ return orderByVlDate[0];
+ }
+}
diff --git a/src/app/patient-dashboard/common/patient-banner/patient-banner.component.html b/src/app/patient-dashboard/common/patient-banner/patient-banner.component.html
index eb55436c8..249cabc65 100644
--- a/src/app/patient-dashboard/common/patient-banner/patient-banner.component.html
+++ b/src/app/patient-dashboard/common/patient-banner/patient-banner.component.html
@@ -403,6 +403,23 @@
OVC ID: Not assigned
+
+
+ Enroll client to OTZ
+
+
Family History
diff --git a/src/app/patient-dashboard/common/patient-banner/patient-banner.component.ts b/src/app/patient-dashboard/common/patient-banner/patient-banner.component.ts
index 7fe36d0a1..6b90d1e65 100644
--- a/src/app/patient-dashboard/common/patient-banner/patient-banner.component.ts
+++ b/src/app/patient-dashboard/common/patient-banner/patient-banner.component.ts
@@ -48,6 +48,8 @@ export class PatientBannerComponent implements OnInit, OnDestroy, OnChanges {
public relationship: Relationship;
public isStaging = true;
public ovcEnrollment = false;
+ public otzEnrollmentBtn = false;
+ public isPatientEligibleForOtz = false;
public isPatientVerified = false;
public verificationStatus = false;
modalRef: BsModalRef;
@@ -90,11 +92,11 @@ export class PatientBannerComponent implements OnInit, OnDestroy, OnChanges {
this.patient = patient;
this.searchIdentifiers = patient.searchIdentifiers;
this.getVerificationStatus();
+ this.getOtzEnrollments(patient.person.age, patient.enrolledPrograms);
this.getOvcEnrollments(
patient.enrolledPrograms,
patient.person.birthdate
);
-
const attributes = patient.person.attributes;
_.each(attributes, (attribute) => {
// get the test patient attribute
@@ -305,6 +307,29 @@ export class PatientBannerComponent implements OnInit, OnDestroy, OnChanges {
}
}
+ private getOtzEnrollments(age, enrolledPrograms) {
+ if (age > 9 && age <= 24) {
+ this.isPatientEligibleForOtz = true;
+ }
+ const otz = enrolledPrograms.filter(
+ (program) =>
+ program.concept.uuid === 'fd90d6b2-7302-4a9c-ad1b-1f93eff77afb'
+ );
+ if (otz.length > 0 && otz[0].isEnrolled) {
+ this.otzEnrollmentBtn = true;
+ }
+ }
+
+ public enrollToOtz() {
+ const otzEnrollmentFormUuid = 'ca5ccb72-5623-4b94-97a3-6b5dac5f8560';
+ this.router.navigate([
+ '/patient-dashboard/patient/' +
+ this.patient.uuid +
+ '/general/general/formentry/' +
+ otzEnrollmentFormUuid
+ ]);
+ }
+
/* Family History */
public navigateToFamilyHistory() {
if (this.familyTestingEncounterUuid == null) {
diff --git a/src/app/patient-dashboard/common/patient-dashboard.common.module.ts b/src/app/patient-dashboard/common/patient-dashboard.common.module.ts
index 4c8ee52de..cda1544f9 100644
--- a/src/app/patient-dashboard/common/patient-dashboard.common.module.ts
+++ b/src/app/patient-dashboard/common/patient-dashboard.common.module.ts
@@ -135,6 +135,7 @@ import { AddPatientEducationComponent } from './patient-info/education/add-patie
import { EditPatientEducationComponent } from './patient-info/education/edit-patient-education.component';
import { OvcSnapshotComponent } from './ovc-snapshot/ovc-snapshot.component';
import { UserDefaultPropertiesService } from 'src/app/user-default-properties/user-default-properties.service';
+import { OtzSnapshotComponent } from './otz-snapshot/otz-snapshot.component';
@NgModule({
imports: [
@@ -291,7 +292,8 @@ import { UserDefaultPropertiesService } from 'src/app/user-default-properties/us
PatientEducationComponent,
AddPatientEducationComponent,
EditPatientEducationComponent,
- OvcSnapshotComponent
+ OvcSnapshotComponent,
+ OtzSnapshotComponent
],
providers: [
{
diff --git a/src/app/patient-dashboard/common/visit/visit-starter/visit-starter.component.html b/src/app/patient-dashboard/common/visit/visit-starter/visit-starter.component.html
index 3cf806eab..c815fec69 100644
--- a/src/app/patient-dashboard/common/visit/visit-starter/visit-starter.component.html
+++ b/src/app/patient-dashboard/common/visit/visit-starter/visit-starter.component.html
@@ -54,7 +54,8 @@