diff --git a/package.json b/package.json index 5b81cbaad..4716e62be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ngx-amrs", - "version": "2.17.2", + "version": "2.18.1", "scripts": { "ng": "ng", "start": "ng serve --host 0.0.0.0 --port 3000", diff --git a/src/app/clinic-dashboard/general/pre-appointment-outreach/pre-appointment-outreach.component.html b/src/app/clinic-dashboard/general/pre-appointment-outreach/pre-appointment-outreach.component.html index dd8b5f9d2..fb76a32b8 100644 --- a/src/app/clinic-dashboard/general/pre-appointment-outreach/pre-appointment-outreach.component.html +++ b/src/app/clinic-dashboard/general/pre-appointment-outreach/pre-appointment-outreach.component.html @@ -57,6 +57,26 @@

+
+
+ Key + Successful contact attempts + Unsuccessful contact attempts + Requested reschedule + +
+
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 7bcd5431c..776052c60 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 @@ -53,22 +53,25 @@ export class PreAppointmentOutreachComponent implements OnInit { private route: ActivatedRoute ) { const today = new Date(); + const currentWeek = this.getISOWeek(today); const currentYear = today.getFullYear(); + const startYear = 2023; const numberOfWeeks = 52; // Set the maximum number of weeks to 52 - for (let weekNumber = 1; weekNumber <= numberOfWeeks; weekNumber++) { - const weekString = `${currentYear}-W${weekNumber - .toString() - .padStart(2, '0')}`; - this.weeks.push({ - label: `${currentYear}-W${weekNumber} - From ${this.getStartDate( - weekString - )} to ${this.getEndDate(weekString)}`, - value: weekString - }); + for (let year = startYear; year <= currentYear; year++) { + const lastWeek = year === currentYear ? currentWeek : numberOfWeeks; + for (let weekNumber = 1; weekNumber <= lastWeek; weekNumber++) { + const weekString = `${year}-W${weekNumber.toString().padStart(2, '0')}`; + this.weeks.push({ + label: `${year}-W${weekNumber} - From ${this.getStartDate( + weekString + )} to ${this.getEndDate(weekString)}`, + value: weekString + }); + } } + this.weeks.reverse(); - const currentWeek = this.getISOWeek(today); this.selectedWeek = `${currentYear}-W${currentWeek .toString() .padStart(2, '0')}`; 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..44c44049b --- /dev/null +++ b/src/app/etl-api/cohort-otz-module-resource.service.ts @@ -0,0 +1,67 @@ +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 getUrlRequestParamsByLocationUuid(locationUuid: string): HttpParams { + let urlParams: HttpParams = new HttpParams(); + + if (locationUuid) { + urlParams = urlParams.set('uuid', locationUuid); + } + 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(locationUuid: string) { + return this.http.get(this.getCohortSuppressionsUrl(), { + params: this.getUrlRequestParamsByLocationUuid(locationUuid) + }); + } +} 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..c493fb42d 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 @@ -27,8 +27,8 @@

Group Search

>
-
-
+
+
+ +
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..4a1dc146e 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,14 @@ 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 = ''; + cohortUuids = new Map(); constructor( private groupService: CommunityGroupService, @@ -52,7 +63,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 { @@ -89,27 +101,88 @@ export class GroupManagerSearchComponent implements OnInit, OnDestroy { } public showGroupsInFacilty() { - this.rowData = []; this.fetchingGroups = true; + this.filterText = ''; const locationUuid = this.router.url.split('/')[2]; - if (locationUuid !== this.previousLocationUuid) { + + if (locationUuid === this.previousLocationUuid && this.isOTZprogram) { + this.rowData = []; + this.groupsInCurrentFacility = []; + this.isOTZprogram = false; + this.showDcGroupsInFacility(locationUuid); + } else if (locationUuid !== this.previousLocationUuid) { this.fetchingGroups = true; - const sub = this.groupService - .getGroupsByLocationUuid(locationUuid) - .subscribe((res) => { - this.groupsInCurrentFacility = res.map((result) => new Group(result)); - this.hideGroupsInCurrentFacility = false; - this.fetchingGroups = false; - this.previousLocationUuid = locationUuid; - this.rowData = this.groupsInCurrentFacility; - console.log(this.rowData, 'rowData'); - }); - this.subscription.add(sub); + this.showDcGroupsInFacility(locationUuid); } else { this.rowData = this.groupsInCurrentFacility; } } + public showDcGroupsInFacility(locationUuid) { + const sub = this.groupService + .getGroupsByLocationUuid(locationUuid) + .subscribe((res) => { + this.groupsInCurrentFacility = res.map((result) => new Group(result)); + this.hideGroupsInCurrentFacility = false; + this.fetchingGroups = false; + this.previousLocationUuid = locationUuid; + this.rowData = this.groupsInCurrentFacility; + this.filterText = 'HIV DIFFERENTIATED CARE PROGRAM'; + if (this.gridOptions.api) { + this.gridOptions.api.onFilterChanged(); + } + }); + this.columnDefs = this.generateColumns(); + this.subscription.add(sub); + } + public showOTZGroupsInFacilty() { + this.rowData = []; + this.fetchingGroups = true; + this.isOTZprogram = true; + const locationUuid = this.router.url.split('/')[2]; + this.fetchingGroups = true; + this.getCohortSuppresionRate(locationUuid); + const sub = this.groupService + .getGroupsByLocationUuid(locationUuid) + .subscribe((res) => { + this.groupsInCurrentFacility = res.map((result) => { + const groupInstance = new Group(result); + groupInstance.viralSuppression = + this.cohortUuids.has(groupInstance.openmrsModel.uuid) && + this.cohortUuids + .get(groupInstance.openmrsModel.uuid) + .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(cohortData) { + cohortData.forEach((cohort) => { + this.cohortUuids.set(cohort.uuid, cohort); + }); + return this.cohortUuids; + } + + public getCohortSuppresionRate(locationUuid: string) { + return this.cohortOtzModuleResourceService + .getCohortSuppressionStatus(locationUuid) + .subscribe((data: any) => { + this.generateCohortUuids(data.result); + }); + } + public navigateToGroupDetails(group, newGroup?) { if (this.modalRef) { this.modalRef.hide(); @@ -162,7 +235,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 +315,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/hiv-care-lib/dqa-reports/chart-abstraction-patientlist/chart-abstraction-patientlist.component.ts b/src/app/hiv-care-lib/dqa-reports/chart-abstraction-patientlist/chart-abstraction-patientlist.component.ts index c413c900e..86c445237 100644 --- a/src/app/hiv-care-lib/dqa-reports/chart-abstraction-patientlist/chart-abstraction-patientlist.component.ts +++ b/src/app/hiv-care-lib/dqa-reports/chart-abstraction-patientlist/chart-abstraction-patientlist.component.ts @@ -42,7 +42,7 @@ export class ChartAbstractionPatientlistComponent implements OnInit { startDate: this.params.startDate, endDate: this.params.endDate, patientType: this.params.patientType, - limit: this.params.limit ? this.params.limit : 10, + limit: this.params.limit ? this.params.limit : 'all', offset: 0 }; this.getPatientList(requestParams); @@ -71,32 +71,48 @@ export class ChartAbstractionPatientlistComponent implements OnInit { hide = false; } const extraColumns = { - birthdate: 'DOB', - sex_gender: 'Gender', - drugs_given: 'Current Regimen', + NUPI: 'NUPI', + sex_gender: 'Sex', + birthdate: 'Date of Birth (DD/MM/YYYY)', + hiv_start_date: 'Date Confirmed HIV Positive (DD/MM/YYYY)', + arv_first_regimen_start_date: 'Date of ART Initiation (DD/MM/YYYY)', + arv_start_date: 'Date of Current ART Initiation (DD/MM/YYYY)', + drugs_given: 'Current ART Regimen', + cur_arv_med_basis: 'Current ART Regimen', drugs_duration: 'Drug dosage given (duration)', - weight: 'Weight', - height: 'Height', - muac: 'MUAC', - BMI: 'BMI', + height: 'Height at Last visit', + weight: 'Weight at Last visit', + BMI: 'BMI at Last visit', + muac: 'MUAC at Last visit', + tb_screened_this_visit: 'Was TB Screening done at last visit', + tb_screening_result: 'TB Screening outcomes', + last_ipt_start_date: 'IPT start date (DD/MM/YYYY)', + tpt_status: 'IPT status', + ipt_completion_date: 'IPT outcome date (DD/MM/YYYY)', + viral_load_validity: 'Does the client have a Valid Viral load result', + vl_suppression: 'Is the client virally suppressed', + cd4_1: 'Baseline screening for CD4', + has_cd4_1: 'Does this client have Baseline screening for CD4', + is_crag_screened: 'Does this client have Baseline screening for CrAG', + last_clinical_encounter: 'Last clinical encounter date (DD/MM/YYYY)', sysBP: 'Systolic BP', dysBP: 'Diastolic BP', nutrition: 'Nutrition Assessment Done', DSD: 'DSD Model', - hiv_start_date: 'Date Confirmed HIV Positive', - arv_start_date: 'Date of ART Initiation', - cd4_1: 'Baseline CD4 Test Result', - vl_1: 'Latest Valid VL', - tpt_status: 'TPT Status', + // cd4_1: 'Baseline CD4 Test Result', + // vl_1: 'Latest Valid VL', + vl_1: 'Does the client have a Valid viral load result', ovcid_id: 'OVCID', - last_ipt_start_date: 'TPT initiated', - ipt_stop_date: 'TPT Stop Date', - ipt_completion_date: 'TPT Completion Date', - last_clinical_encounter: 'Last Clinical Encounter', - last_appointment_date: 'Date of Last Appointment', - next_appointment: 'Date of Next Appointment ', + ipt_stop_date: 'IPT Stop Date (DD/MM/YYYY)', + // last_clinical_encounter: 'Last Clinical Encounter', + // last_appointment_date: 'Date of Last Appointment', + next_appointment: 'Next appointment date (DD/MM/YYYY)', + // next_appointment: 'Date of Next Appointment ', visit_type: 'Visit Type', - tb_screened_this_visit: 'TB screening' + status: 'Status', + // is_crag_screened: 'Baseline CrAG Screened', + cur_who_stage: 'Current Who Stage', + category: 'Category' }; for (const indicator in extraColumns) { if (indicator) { @@ -107,64 +123,79 @@ export class ChartAbstractionPatientlistComponent implements OnInit { } } this.overrideColumns.push( + { field: 'ccc_number', hide: true, pinned: true }, { field: 'birthdate', - cellRenderer: (column) => { - if (column.value != null) { - return moment(column.value).format('YYYY-MM-DD'); - } - }, + // cellRenderer: (column) => { + // if (column.value != null) { + // // return moment(column.value).format('YYYY-MM-DD'); + // return moment(column.value).format('DD-MMM-YYYY'); + // } + // return 'missing'; + // }, pinned: false }, { - field: 'last_appointment_date', - cellRenderer: (column) => { - if (column.value != null) { - return moment(column.value).format('DD-MM-YYYY'); - } - } + field: 'last_appointment_date' + // cellRenderer: (column) => { + // if (column.value != null) { + // return moment(column.value).format('DD-MM-YYYY'); + // } + // } + }, + { + field: 'arv_first_regimen_start_date' + // cellRenderer: (column) => { + // if (column.value != null) { + // if (moment(column.value).isBefore('1900-01-01', 'year')){ + // return column.value; + // } + // return '' + // } + // } }, { field: 'arv_start_date', - cellRenderer: (column) => { - if (column.value != null) { - return moment(column.value).format('DD-MM-YYYY'); - } - } + // cellRenderer: (column) => { + // if (column.value != null) { + // return moment(column.value).format('DD-MM-YYYY'); + // } + // }, + hide: true }, { - field: 'hiv_start_date', - cellRenderer: (column) => { - if (column.value != null) { - return moment(column.value).format('DD-MM-YYYY'); - } - } + field: 'hiv_start_date' + // cellRenderer: (column) => { + // if (column.value != null) { + // return moment(column.value).format('DD-MM-YYYY'); + // } + // } }, { - field: 'last_clinical_encounter', - cellRenderer: (column) => { - if (column.value != null) { - return moment(column.value).format('DD-MM-YYYY'); - } - } + field: 'last_clinical_encounter' + // cellRenderer: (column) => { + // if (column.value != null) { + // return moment(column.value).format('DD-MM-YYYY'); + // } + // } }, { - field: 'next_appointment', - cellRenderer: (column) => { - if (column.value != null) { - return moment(column.value).format('DD-MM-YYYY'); - } - } + field: 'next_appointment' + // cellRenderer: (column) => { + // if (column.value != null) { + // return moment(column.value).format('DD-MM-YYYY'); + // } + // } }, { - field: 'tb_screened_this_visit', - width: 150, - cellRenderer: (column) => { - if (column.value === 0) { - return 'NO'; - } - return 'YES'; - } + field: 'tb_screened_this_visit' + // width: 150, + // cellRenderer: (column) => { + // if (column.value === 0) { + // return 'No'; + // } + // return 'Yes'; + // } }, { field: 'vl_1', @@ -173,7 +204,8 @@ export class ChartAbstractionPatientlistComponent implements OnInit { return 'LDL'; } return column.value; - } + }, + hide: true }, { field: 'nutrition', @@ -187,42 +219,36 @@ export class ChartAbstractionPatientlistComponent implements OnInit { }, { field: 'last_ipt_start_date', - cellRenderer: (column) => { - if (column.value != null) { - return moment(column.value).format('DD-MM-YYYY'); - } - }, - hide: true + // cellRenderer: (column) => { + // if (column.value != null) { + // return moment(column.value).format('DD-MM-YYYY'); + // } + // }, + hide: false }, { field: 'ipt_completion_date', - cellRenderer: (column) => { - if (column.value != null) { - return moment(column.value).format('DD-MM-YYYY'); - } - }, - hide: true + // cellRenderer: (column) => { + // if (column.value != null) { + // return moment(column.value).format('DD-MM-YYYY'); + // } + // }, + hide: false }, { field: 'ipt_stop_date', - cellRenderer: (column) => { - if (column.value != null) { - return moment(column.value).format('DD-MM-YYYY'); - } - }, - hide: true - }, - { - field: 'arv_start_date', - cellRenderer: (column) => { - if (column.value != null) { - return moment(column.value).format('DD-MM-YYYY'); - } - } + // cellRenderer: (column) => { + // if (column.value != null) { + // return moment(column.value).format('DD-MM-YYYY'); + // } + // }, + hide: true, + suppressToolPanel: true }, { field: 'drugs_given', - width: 280 + width: 280, + hide: true }, { field: 'height', @@ -238,7 +264,9 @@ export class ChartAbstractionPatientlistComponent implements OnInit { }, { field: 'visit_type', - width: 150 + width: 150, + hide: true, + suppressToolPanel: true }, { field: 'muac', @@ -247,15 +275,28 @@ export class ChartAbstractionPatientlistComponent implements OnInit { { field: 'person_id', width: 200, - pinned: true + pinned: true, + suppressToolPanel: true }, { field: 'NUPI', + width: 150 + // pinned: true + }, + { + field: 'upi_number', width: 150, - pinned: true + hide: true, + pinned: true, + suppressToolPanel: true }, { field: 'drugs_given', + width: 280, + hide: true + }, + { + field: 'cur_arv_med_basis', width: 280 }, { @@ -264,26 +305,33 @@ export class ChartAbstractionPatientlistComponent implements OnInit { }, { field: 'drugs_duration', - width: 150 + width: 150, + hide: true, + suppressToolPanel: true }, { field: 'person_name', width: 150, - hide: true + hide: true, + suppressToolPanel: true }, { field: 'gender', width: 150, - hide: true + hide: true, + suppressToolPanel: true }, { field: 'identifiers', width: 150, - hide: true + hide: true, + suppressToolPanel: true }, { field: 'age', - width: 150 + width: 150, + hide: true, + suppressToolPanel: true }, { field: 'sex_gender', @@ -292,11 +340,69 @@ export class ChartAbstractionPatientlistComponent implements OnInit { { field: '#', width: 150, - hide: true + hide: true, + suppressToolPanel: true }, { field: 'visit_type', width: 150, + hide: true, + suppressToolPanel: true + }, + { + field: 'sysBP', + width: 150, + hide: true, + suppressToolPanel: true + }, + { + field: 'dysBP', + width: 150, + hide: true, + suppressToolPanel: true + }, + { + field: 'nutrition', + width: 150, + hide: true, + suppressToolPanel: true + }, + { + field: 'DSD', + width: 150, + hide: true, + suppressToolPanel: true + }, + { + field: 'ovcid_id', + width: 150, + hide: true, + suppressToolPanel: true + }, + { + field: 'cur_who_stage', + width: 150, + hide: true, + suppressToolPanel: true + }, + { + field: 'vl_suppression', + width: 150, + hide: false + }, + { + field: 'is_crag_screened', + width: 150, + hide: false + }, + { + field: 'status', + width: 150, + hide: true + }, + { + field: 'cd4_1', + width: 150, hide: true } ); diff --git a/src/app/hiv-care-lib/family-testing/family-testing-contact-list.component.ts b/src/app/hiv-care-lib/family-testing/family-testing-contact-list.component.ts index df5208b7f..10716efd4 100644 --- a/src/app/hiv-care-lib/family-testing/family-testing-contact-list.component.ts +++ b/src/app/hiv-care-lib/family-testing/family-testing-contact-list.component.ts @@ -277,7 +277,7 @@ export class FamilyTestingContactComponent implements OnInit { } public onAddContactClick() { - const defaulterTracingFormV1Uuid = 'bf6d0d9a-e6af-48fd-9245-6d1939adb37d'; + const defaulterTracingFormV1Uuid = 'f3ba9242-9bbb-4284-a0c0-56ac6f0cec65'; const url = `/patient-dashboard/patient/${this.patientUuid}/general/general/formentry/${defaulterTracingFormV1Uuid}`; this.router.navigate([url], {}); } 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 + + +