Skip to content

Commit ea6b66f

Browse files
committed
#808 - Fixed performance recentlyRegistered and conditionally not display all subjects based on filters chosen by the user (as it doesn't perform well). Collapse/Expand for the coded fiters as they are slow to render when there are a lot of fields.
1 parent 14e9e34 commit ea6b66f

File tree

10 files changed

+144
-55
lines changed

10 files changed

+144
-55
lines changed

packages/openchs-android/package-lock.json

Lines changed: 29 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/openchs-android/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"lodash": "4.17.21",
5151
"moment": "2.29.4",
5252
"native-base": "3.4.9",
53-
"openchs-models": "1.27.17",
53+
"openchs-models": "1.27.18",
5454
"prop-types": "15.8.1",
5555
"react": "18.2.0",
5656
"react-native": "0.69.7",

packages/openchs-android/src/action/mydashboard/CustomFilterActions.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,23 @@ import _ from "lodash";
22
import CustomFilterService from "../../service/CustomFilterService";
33
import moment from "moment";
44

5-
65
class CustomFilterActions {
7-
86
static getInitialState() {
97
return {
10-
selectedCustomFilters: {}
8+
selectedCustomFilters: {},
9+
openedCodedConceptUuids: []
10+
}
11+
}
1112

13+
static onCodedConceptFilterToggled(state, action, context) {
14+
const newState = {...state};
15+
if (_.some(newState.openedCodedConceptUuids, (x) => action.conceptUuid === x)) {
16+
_.remove(newState["openedCodedConceptUuids"], (x) => action.conceptUuid === x);
17+
newState["openedCodedConceptUuids"] = [...newState["openedCodedConceptUuids"]];
18+
} else {
19+
newState["openedCodedConceptUuids"] = [...newState["openedCodedConceptUuids"], action.conceptUuid];
1220
}
21+
return newState;
1322
}
1423

1524
static onLoad(state, action, context) {
@@ -105,11 +114,11 @@ class CustomFilterActions {
105114
const previousValue = state.selectedCustomFilters[titleKey];
106115
if (_.some(previousValue, pv => pv.groupSubjectUUID === groupSubjectUUID)) {
107116
const newState = _.filter(previousValue, pv => pv.groupSubjectUUID !== groupSubjectUUID);
108-
return {...state, selectedCustomFilters: {...state.selectedCustomFilters, [titleKey]: newState}}
117+
return {...state, selectedCustomFilters: {...state.selectedCustomFilters, [titleKey]: newState}, openedCodedConceptUuids: []}
109118
}
110119
const newState = {subjectTypeUUID, groupSubjectUUID, name};
111120
const selectedCustomFilters = {...state.selectedCustomFilters, [titleKey]: [...previousValue, newState]};
112-
return {...state, selectedCustomFilters};
121+
return {...state, selectedCustomFilters, openedCodedConceptUuids: []};
113122
}
114123
}
115124

@@ -124,6 +133,7 @@ const CustomFilterNames = {
124133
ON_MIN_TIME_CUSTOM_FILTER_SELECT: `${ActionPrefix}.ON_MIN_TIME_CUSTOM_FILTER_SELECT`,
125134
ON_MAX_TIME_CUSTOM_FILTER_SELECT: `${ActionPrefix}.ON_MAX_TIME_CUSTOM_FILTER_SELECT`,
126135
ON_GROUP_SUBJECT_CHANGE: `${ActionPrefix}.ON_GROUP_SUBJECT_CHANGE`,
136+
ON_CODED_CONCEPT_FILTER_TOGGLED: `${ActionPrefix}.ON_CODED_CONCEPT_FILTER_TOGGLED`
127137
};
128138

129139
const CustomFilterMap = new Map([
@@ -136,6 +146,7 @@ const CustomFilterMap = new Map([
136146
[CustomFilterNames.ON_MIN_TIME_CUSTOM_FILTER_SELECT, CustomFilterActions.onMinTimeSelect],
137147
[CustomFilterNames.ON_MAX_TIME_CUSTOM_FILTER_SELECT, CustomFilterActions.onMaxTimeSelect],
138148
[CustomFilterNames.ON_GROUP_SUBJECT_CHANGE, CustomFilterActions.onGroupSubjectFilterChange],
149+
[CustomFilterNames.ON_CODED_CONCEPT_FILTER_TOGGLED, CustomFilterActions.onCodedConceptFilterToggled],
139150
]);
140151

141152
export {

packages/openchs-android/src/action/mydashboard/MyDashboardActions.js

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import UserInfoService from "../../service/UserInfoService";
1010
import DashboardCacheService from "../../service/DashboardCacheService";
1111
import {firebaseEvents, logEvent} from "../../utility/Analytics";
1212

13+
function getApplicableEncounterTypes(state) {
14+
return _.isEmpty(state.selectedGeneralEncounterTypes) ? state.selectedEncounterTypes : state.selectedGeneralEncounterTypes;
15+
}
16+
1317
class MyDashboardActions {
1418
static getInitialState(context) {
1519
return {
@@ -91,9 +95,9 @@ class MyDashboardActions {
9195
MyDashboardActions.commonIndividuals(individualService.allScheduledVisitsIn(state.date.value, encountersFilters, generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter), state.individualUUIDs),
9296
MyDashboardActions.commonIndividuals(individualService.allOverdueVisitsIn(state.date.value, encountersFilters, generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter), state.individualUUIDs),
9397
MyDashboardActions.commonIndividuals(individualService.recentlyCompletedVisitsIn(state.date.value, encountersFilters, generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter), state.individualUUIDs),
94-
MyDashboardActions.commonIndividuals(individualService.recentlyRegistered(state.date.value, individualFilters), state.individualUUIDs),
98+
MyDashboardActions.commonIndividuals(individualService.recentlyRegistered(state.date.value, individualFilters, state.selectedPrograms, getApplicableEncounterTypes(state)), state.individualUUIDs),
9599
MyDashboardActions.commonIndividuals(individualService.recentlyEnrolled(state.date.value, enrolmentFilters), state.individualUUIDs),
96-
MyDashboardActions.commonIndividuals(individualService.allIn(state.date.value, individualFilters), state.individualUUIDs, true)
100+
MyDashboardActions.commonIndividuals(individualService.allIn(state.date.value, individualFilters, state.selectedPrograms, getApplicableEncounterTypes(state)), state.individualUUIDs, true)
97101
]
98102
: [state.scheduled, state.overdue, state.recentlyCompletedVisits, state.recentlyCompletedRegistration, state.recentlyCompletedEnrolment, state.total]);
99103

@@ -175,7 +179,12 @@ class MyDashboardActions {
175179
(listType === 'total' || listType === 'recentlyCompletedRegistration') ? state.individualFilters : state.encountersFilters;
176180
const queryProgramEncounter = MyDashboardActions.shouldQueryProgramEncounter(state);
177181
const queryGeneralEncounter = MyDashboardActions.shouldQueryGeneralEncounter(state);
178-
const allIndividuals = methodMap.get(listType)(state.date.value, filters, state.generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter);
182+
let allIndividuals;
183+
if (listType === "recentlyCompletedRegistration" || listType === "total")
184+
allIndividuals = methodMap.get(listType)(state.date.value, filters, state.selectedPrograms, getApplicableEncounterTypes(state));
185+
else
186+
allIndividuals = methodMap.get(listType)(state.date.value, filters, state.generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter);
187+
179188
const commonIndividuals = MyDashboardActions.commonIndividuals(allIndividuals, state.individualUUIDs, listType === 'total');
180189
const totalToDisplay = listType === 'total' ? commonIndividuals : _.orderBy(commonIndividuals, ({visitInfo}) => visitInfo.sortingBy, 'desc');
181190
return {
@@ -236,9 +245,8 @@ class MyDashboardActions {
236245
const addressUUIDs = action.locationSearchCriteria.clone().getAllAddressLevelUUIDs();
237246
const locationQuery = (path) => _.map(addressUUIDs, (address) => `${path} = \'${address}\'`);
238247
const subjectTypeQuery = (path) => `${path} = "${action.selectedSubjectType.uuid}"`;
239-
const visitQuery = (path) => shouldApplyValidEnrolmentQuery ? action.selectedEncounterTypes.map((encounter) => `${path} = \'${encounter.uuid}\'`) : '';
240-
const generalVisitQuery = (path) => _.map(action.selectedGeneralEncounterTypes, (encounter) => `${path} = \'${encounter.uuid}\'`);
241-
const generalVisitQueryFromIndividual = _.map(action.selectedGeneralEncounterTypes, (encounter) => `$encounter.encounterType.uuid = \'${encounter.uuid}\' AND $encounter.voided = false`);
248+
const visitQuery = (path) => shouldApplyValidEnrolmentQuery ? action.selectedEncounterTypes.map((encounterType) => `${path} = \'${encounterType.uuid}\'`) : '';
249+
const generalVisitQuery = (path) => _.map(action.selectedGeneralEncounterTypes, (encounterType) => `${path} = \'${encounterType.uuid}\'`);
242250
const programQuery = (path) => shouldApplyValidEnrolmentQuery ? _.map(action.selectedPrograms, (program) => `${path} = \'${program.uuid}\'`) : '';
243251
const validEnrolmentQuery = (path) => shouldApplyValidEnrolmentQuery ? `${path}.voided = false and ${path}.programExitDateTime = null` : '';
244252
const genderQuery = (path) => _.map(action.selectedGenders, (gender) => `${path} = "${gender.name}"`);
@@ -256,21 +264,16 @@ class MyDashboardActions {
256264

257265
const restIndividualFilters = [
258266
MyDashboardActions.orQuery(programQuery('$enrolment.program.uuid')),
259-
MyDashboardActions.orQuery(visitQuery('$enrolment.encounters.encounterType.uuid')),
260267
validEnrolmentQuery('$enrolment')
261268
].filter(Boolean).join(" AND ");
262269

263270
const buildEnrolmentSubQueryForIndividual = () => _.isEmpty(restIndividualFilters) ? '' :
264271
'SUBQUERY(enrolments, $enrolment, ' + restIndividualFilters + ' ).@count > 0';
265272

266-
const encounterQuery = () => _.isEmpty(MyDashboardActions.orQuery(generalVisitQueryFromIndividual)) ? '' :
267-
'SUBQUERY(encounters, $encounter, ' + MyDashboardActions.orQuery(generalVisitQueryFromIndividual) + ' ).@count > 0';
268-
269273
const individualFilters = [
270274
subjectTypeQuery('subjectType.uuid'),
271275
MyDashboardActions.orQuery(genderQuery('gender.name')),
272276
MyDashboardActions.orQuery(locationQuery('lowestAddressLevel.uuid')),
273-
encounterQuery(),
274277
buildEnrolmentSubQueryForIndividual()
275278
].filter(Boolean).join(" AND ");
276279

packages/openchs-android/src/service/IndividualService.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ class IndividualService extends BaseService {
112112

113113
_uniqIndividualWithVisitName(individualsWithVisits, individualWithVisit) {
114114
const permissionAllowed = individualWithVisit.visitInfo.allow;
115-
116115
if (individualsWithVisits.has(individualWithVisit.individual.uuid)) {
117116
const prevDate = individualsWithVisits.get(individualWithVisit.individual.uuid).visitInfo.sortingBy;
118117
const smallerDate = moment(prevDate).isBefore(individualWithVisit.visitInfo.sortingBy) ? prevDate : individualWithVisit.visitInfo.sortingBy;
@@ -134,7 +133,10 @@ class IndividualService extends BaseService {
134133
return individualsWithVisits;
135134
}
136135

137-
allIn(ignored, queryAdditions) {
136+
allIn(ignored, queryAdditions, programs = [], encounterTypes = []) {
137+
if (encounterTypes.length > 0 || programs.length > 0)
138+
return null;
139+
138140
return this.db.objects(Individual.schema.name)
139141
.filtered('voided = false ')
140142
.filtered((_.isEmpty(queryAdditions) ? 'uuid != null' : `${queryAdditions}`))
@@ -349,8 +351,7 @@ class IndividualService extends BaseService {
349351
fromDate)
350352
.filtered((_.isEmpty(queryAdditions) ? 'uuid != null' : `${queryAdditions}`))
351353
.map((enc) => {
352-
const individual = enc.programEnrolment.individual;
353-
return individual;
354+
return enc.programEnrolment.individual;
354355
})
355356
.reduce(this._uniqIndividualsFrom, new Map())
356357
.values()]
@@ -411,17 +412,25 @@ class IndividualService extends BaseService {
411412
.map(_.identity);
412413
}
413414

414-
recentlyRegistered(date, addressQuery) {
415+
recentlyRegistered(date, addressQuery, programs = [], encounterTypes = []) {
415416
let fromDate = moment(date).subtract(1, 'day').startOf('day').toDate();
416417
let tillDate = moment(date).endOf('day').toDate();
417-
return [...this.db.objects(Individual.schema.name)
418+
let individuals = this.db.objects(Individual.schema.name)
418419
.filtered('voided = false ' +
419420
'AND registrationDate <= $0 ' +
420421
'AND registrationDate >= $1 ',
421422
tillDate,
422423
fromDate)
423424
.filtered((_.isEmpty(addressQuery) ? 'uuid != null' : `${addressQuery}`))
424-
.map((individual) => {
425+
.map((individual) => individual);
426+
427+
if (encounterTypes.length > 0 && programs.length > 0) {
428+
individuals = _.filter(individuals, (individual) => individual.hasProgramEncounterOfType(encounterTypes));
429+
} else if (encounterTypes.length > 0) {
430+
individuals = _.filter(individuals, (individual) => individual.hasEncounterOfType(encounterTypes));
431+
}
432+
433+
return [...individuals.map((individual) => {
425434
const registrationDate = individual.registrationDate;
426435
return {
427436
individual,
@@ -433,8 +442,7 @@ class IndividualService extends BaseService {
433442
allow: true,
434443
}
435444
};
436-
})
437-
.reduce(this._uniqIndividualWithVisitName, new Map())
445+
}).reduce(this._uniqIndividualWithVisitName, new Map())
438446
.values()]
439447
.map(_.identity);
440448
}

packages/openchs-android/src/views/beneficiaryMode/ExitBeneficiaryModeButton.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {Modal, Text, TouchableNativeFeedback, View} from "react-native";
22
import React from "react";
3-
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
43
import AbstractComponent from "../../framework/view/AbstractComponent";
54
import Colors from "../primitives/Colors";
65
import BeneficiaryModePinService from "../../service/BeneficiaryModePinService";

0 commit comments

Comments
 (0)