From c71859a5fc4de12d87b4bd0b08392f1fe514f084 Mon Sep 17 00:00:00 2001 From: Saningo Lekalantula Date: Thu, 14 Dec 2023 18:33:17 +0300 Subject: [PATCH] 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 @@ >
- OTZ Champion + Peer Leadership - Community Staff Leadership @@ -349,12 +368,21 @@
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 @@
{{ enrollmentErrorMessage }}
-
+
The patient is not enrolled in this program.
+
+ Patient is not eligible for OTZ enrollment +
+ Start Group Meeting >
+
+
+
+ +
+
+
+
+ + +
+ Group Activity is required + +
+
+
+
+
+ +
+ + + + + +
+

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 + + +
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'; @@ -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 + +
+
+ +
+
    +
  • + 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 }} + + +
  • +
+
+
+ +
+ + + + + + + + + + + + + + + + + +
Latest appointmentVisit typeLatest RTC + Medication pick-up date +
+ {{ 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' }} +
+
+
+ +
+ + + + + + + + + + + + + +
Encounter DateAdherence type
{{ patientData?.encounter_datetime }}{{ patientData?.cur_arv_adherence }}
+
+
+ +
+ + + + + + + + + + + + + + + +
DateViral LoadViral Load Category
+ {{ item.test_datetime | date: 'dd-MM-yyyy' }} + {{ item.hiv_viral_load }} + {{ + 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 + + +