Skip to content

Commit

Permalink
#808 - Fixed performance recentlyRegistered and conditionally not dis…
Browse files Browse the repository at this point in the history
…play 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.
  • Loading branch information
petmongrels committed Jan 25, 2023
1 parent 14e9e34 commit ea6b66f
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 55 deletions.
38 changes: 29 additions & 9 deletions packages/openchs-android/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/openchs-android/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"lodash": "4.17.21",
"moment": "2.29.4",
"native-base": "3.4.9",
"openchs-models": "1.27.17",
"openchs-models": "1.27.18",
"prop-types": "15.8.1",
"react": "18.2.0",
"react-native": "0.69.7",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,23 @@ import _ from "lodash";
import CustomFilterService from "../../service/CustomFilterService";
import moment from "moment";


class CustomFilterActions {

static getInitialState() {
return {
selectedCustomFilters: {}
selectedCustomFilters: {},
openedCodedConceptUuids: []
}
}

static onCodedConceptFilterToggled(state, action, context) {
const newState = {...state};
if (_.some(newState.openedCodedConceptUuids, (x) => action.conceptUuid === x)) {
_.remove(newState["openedCodedConceptUuids"], (x) => action.conceptUuid === x);
newState["openedCodedConceptUuids"] = [...newState["openedCodedConceptUuids"]];
} else {
newState["openedCodedConceptUuids"] = [...newState["openedCodedConceptUuids"], action.conceptUuid];
}
return newState;
}

static onLoad(state, action, context) {
Expand Down Expand Up @@ -105,11 +114,11 @@ class CustomFilterActions {
const previousValue = state.selectedCustomFilters[titleKey];
if (_.some(previousValue, pv => pv.groupSubjectUUID === groupSubjectUUID)) {
const newState = _.filter(previousValue, pv => pv.groupSubjectUUID !== groupSubjectUUID);
return {...state, selectedCustomFilters: {...state.selectedCustomFilters, [titleKey]: newState}}
return {...state, selectedCustomFilters: {...state.selectedCustomFilters, [titleKey]: newState}, openedCodedConceptUuids: []}
}
const newState = {subjectTypeUUID, groupSubjectUUID, name};
const selectedCustomFilters = {...state.selectedCustomFilters, [titleKey]: [...previousValue, newState]};
return {...state, selectedCustomFilters};
return {...state, selectedCustomFilters, openedCodedConceptUuids: []};
}
}

Expand All @@ -124,6 +133,7 @@ const CustomFilterNames = {
ON_MIN_TIME_CUSTOM_FILTER_SELECT: `${ActionPrefix}.ON_MIN_TIME_CUSTOM_FILTER_SELECT`,
ON_MAX_TIME_CUSTOM_FILTER_SELECT: `${ActionPrefix}.ON_MAX_TIME_CUSTOM_FILTER_SELECT`,
ON_GROUP_SUBJECT_CHANGE: `${ActionPrefix}.ON_GROUP_SUBJECT_CHANGE`,
ON_CODED_CONCEPT_FILTER_TOGGLED: `${ActionPrefix}.ON_CODED_CONCEPT_FILTER_TOGGLED`
};

const CustomFilterMap = new Map([
Expand All @@ -136,6 +146,7 @@ const CustomFilterMap = new Map([
[CustomFilterNames.ON_MIN_TIME_CUSTOM_FILTER_SELECT, CustomFilterActions.onMinTimeSelect],
[CustomFilterNames.ON_MAX_TIME_CUSTOM_FILTER_SELECT, CustomFilterActions.onMaxTimeSelect],
[CustomFilterNames.ON_GROUP_SUBJECT_CHANGE, CustomFilterActions.onGroupSubjectFilterChange],
[CustomFilterNames.ON_CODED_CONCEPT_FILTER_TOGGLED, CustomFilterActions.onCodedConceptFilterToggled],
]);

export {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import UserInfoService from "../../service/UserInfoService";
import DashboardCacheService from "../../service/DashboardCacheService";
import {firebaseEvents, logEvent} from "../../utility/Analytics";

function getApplicableEncounterTypes(state) {
return _.isEmpty(state.selectedGeneralEncounterTypes) ? state.selectedEncounterTypes : state.selectedGeneralEncounterTypes;
}

class MyDashboardActions {
static getInitialState(context) {
return {
Expand Down Expand Up @@ -91,9 +95,9 @@ class MyDashboardActions {
MyDashboardActions.commonIndividuals(individualService.allScheduledVisitsIn(state.date.value, encountersFilters, generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter), state.individualUUIDs),
MyDashboardActions.commonIndividuals(individualService.allOverdueVisitsIn(state.date.value, encountersFilters, generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter), state.individualUUIDs),
MyDashboardActions.commonIndividuals(individualService.recentlyCompletedVisitsIn(state.date.value, encountersFilters, generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter), state.individualUUIDs),
MyDashboardActions.commonIndividuals(individualService.recentlyRegistered(state.date.value, individualFilters), state.individualUUIDs),
MyDashboardActions.commonIndividuals(individualService.recentlyRegistered(state.date.value, individualFilters, state.selectedPrograms, getApplicableEncounterTypes(state)), state.individualUUIDs),
MyDashboardActions.commonIndividuals(individualService.recentlyEnrolled(state.date.value, enrolmentFilters), state.individualUUIDs),
MyDashboardActions.commonIndividuals(individualService.allIn(state.date.value, individualFilters), state.individualUUIDs, true)
MyDashboardActions.commonIndividuals(individualService.allIn(state.date.value, individualFilters, state.selectedPrograms, getApplicableEncounterTypes(state)), state.individualUUIDs, true)
]
: [state.scheduled, state.overdue, state.recentlyCompletedVisits, state.recentlyCompletedRegistration, state.recentlyCompletedEnrolment, state.total]);

Expand Down Expand Up @@ -175,7 +179,12 @@ class MyDashboardActions {
(listType === 'total' || listType === 'recentlyCompletedRegistration') ? state.individualFilters : state.encountersFilters;
const queryProgramEncounter = MyDashboardActions.shouldQueryProgramEncounter(state);
const queryGeneralEncounter = MyDashboardActions.shouldQueryGeneralEncounter(state);
const allIndividuals = methodMap.get(listType)(state.date.value, filters, state.generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter);
let allIndividuals;
if (listType === "recentlyCompletedRegistration" || listType === "total")
allIndividuals = methodMap.get(listType)(state.date.value, filters, state.selectedPrograms, getApplicableEncounterTypes(state));
else
allIndividuals = methodMap.get(listType)(state.date.value, filters, state.generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter);

const commonIndividuals = MyDashboardActions.commonIndividuals(allIndividuals, state.individualUUIDs, listType === 'total');
const totalToDisplay = listType === 'total' ? commonIndividuals : _.orderBy(commonIndividuals, ({visitInfo}) => visitInfo.sortingBy, 'desc');
return {
Expand Down Expand Up @@ -236,9 +245,8 @@ class MyDashboardActions {
const addressUUIDs = action.locationSearchCriteria.clone().getAllAddressLevelUUIDs();
const locationQuery = (path) => _.map(addressUUIDs, (address) => `${path} = \'${address}\'`);
const subjectTypeQuery = (path) => `${path} = "${action.selectedSubjectType.uuid}"`;
const visitQuery = (path) => shouldApplyValidEnrolmentQuery ? action.selectedEncounterTypes.map((encounter) => `${path} = \'${encounter.uuid}\'`) : '';
const generalVisitQuery = (path) => _.map(action.selectedGeneralEncounterTypes, (encounter) => `${path} = \'${encounter.uuid}\'`);
const generalVisitQueryFromIndividual = _.map(action.selectedGeneralEncounterTypes, (encounter) => `$encounter.encounterType.uuid = \'${encounter.uuid}\' AND $encounter.voided = false`);
const visitQuery = (path) => shouldApplyValidEnrolmentQuery ? action.selectedEncounterTypes.map((encounterType) => `${path} = \'${encounterType.uuid}\'`) : '';
const generalVisitQuery = (path) => _.map(action.selectedGeneralEncounterTypes, (encounterType) => `${path} = \'${encounterType.uuid}\'`);
const programQuery = (path) => shouldApplyValidEnrolmentQuery ? _.map(action.selectedPrograms, (program) => `${path} = \'${program.uuid}\'`) : '';
const validEnrolmentQuery = (path) => shouldApplyValidEnrolmentQuery ? `${path}.voided = false and ${path}.programExitDateTime = null` : '';
const genderQuery = (path) => _.map(action.selectedGenders, (gender) => `${path} = "${gender.name}"`);
Expand All @@ -256,21 +264,16 @@ class MyDashboardActions {

const restIndividualFilters = [
MyDashboardActions.orQuery(programQuery('$enrolment.program.uuid')),
MyDashboardActions.orQuery(visitQuery('$enrolment.encounters.encounterType.uuid')),
validEnrolmentQuery('$enrolment')
].filter(Boolean).join(" AND ");

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

const encounterQuery = () => _.isEmpty(MyDashboardActions.orQuery(generalVisitQueryFromIndividual)) ? '' :
'SUBQUERY(encounters, $encounter, ' + MyDashboardActions.orQuery(generalVisitQueryFromIndividual) + ' ).@count > 0';

const individualFilters = [
subjectTypeQuery('subjectType.uuid'),
MyDashboardActions.orQuery(genderQuery('gender.name')),
MyDashboardActions.orQuery(locationQuery('lowestAddressLevel.uuid')),
encounterQuery(),
buildEnrolmentSubQueryForIndividual()
].filter(Boolean).join(" AND ");

Expand Down
26 changes: 17 additions & 9 deletions packages/openchs-android/src/service/IndividualService.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ class IndividualService extends BaseService {

_uniqIndividualWithVisitName(individualsWithVisits, individualWithVisit) {
const permissionAllowed = individualWithVisit.visitInfo.allow;

if (individualsWithVisits.has(individualWithVisit.individual.uuid)) {
const prevDate = individualsWithVisits.get(individualWithVisit.individual.uuid).visitInfo.sortingBy;
const smallerDate = moment(prevDate).isBefore(individualWithVisit.visitInfo.sortingBy) ? prevDate : individualWithVisit.visitInfo.sortingBy;
Expand All @@ -134,7 +133,10 @@ class IndividualService extends BaseService {
return individualsWithVisits;
}

allIn(ignored, queryAdditions) {
allIn(ignored, queryAdditions, programs = [], encounterTypes = []) {
if (encounterTypes.length > 0 || programs.length > 0)
return null;

return this.db.objects(Individual.schema.name)
.filtered('voided = false ')
.filtered((_.isEmpty(queryAdditions) ? 'uuid != null' : `${queryAdditions}`))
Expand Down Expand Up @@ -349,8 +351,7 @@ class IndividualService extends BaseService {
fromDate)
.filtered((_.isEmpty(queryAdditions) ? 'uuid != null' : `${queryAdditions}`))
.map((enc) => {
const individual = enc.programEnrolment.individual;
return individual;
return enc.programEnrolment.individual;
})
.reduce(this._uniqIndividualsFrom, new Map())
.values()]
Expand Down Expand Up @@ -411,17 +412,25 @@ class IndividualService extends BaseService {
.map(_.identity);
}

recentlyRegistered(date, addressQuery) {
recentlyRegistered(date, addressQuery, programs = [], encounterTypes = []) {
let fromDate = moment(date).subtract(1, 'day').startOf('day').toDate();
let tillDate = moment(date).endOf('day').toDate();
return [...this.db.objects(Individual.schema.name)
let individuals = this.db.objects(Individual.schema.name)
.filtered('voided = false ' +
'AND registrationDate <= $0 ' +
'AND registrationDate >= $1 ',
tillDate,
fromDate)
.filtered((_.isEmpty(addressQuery) ? 'uuid != null' : `${addressQuery}`))
.map((individual) => {
.map((individual) => individual);

if (encounterTypes.length > 0 && programs.length > 0) {
individuals = _.filter(individuals, (individual) => individual.hasProgramEncounterOfType(encounterTypes));
} else if (encounterTypes.length > 0) {
individuals = _.filter(individuals, (individual) => individual.hasEncounterOfType(encounterTypes));
}

return [...individuals.map((individual) => {
const registrationDate = individual.registrationDate;
return {
individual,
Expand All @@ -433,8 +442,7 @@ class IndividualService extends BaseService {
allow: true,
}
};
})
.reduce(this._uniqIndividualWithVisitName, new Map())
}).reduce(this._uniqIndividualWithVisitName, new Map())
.values()]
.map(_.identity);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {Modal, Text, TouchableNativeFeedback, View} from "react-native";
import React from "react";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import AbstractComponent from "../../framework/view/AbstractComponent";
import Colors from "../primitives/Colors";
import BeneficiaryModePinService from "../../service/BeneficiaryModePinService";
Expand Down
Loading

0 comments on commit ea6b66f

Please sign in to comment.