Skip to content

Commit

Permalink
Merge branch '6.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
petmongrels committed Dec 20, 2023
2 parents 7558574 + 7460542 commit d30b007
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import _ from "lodash";
import {Concept, Duration, FormElementGroup, ValidationResult} from 'openchs-models';
import {Concept, Duration, FormElementGroup, Observation, ValidationResult} from 'openchs-models';
import RuleEvaluationService from "../../service/RuleEvaluationService";
import General from "../../utility/General";

class ObservationsHolderActions {
static updateFormElements(formElementGroup, state, context) {
Expand Down Expand Up @@ -36,7 +35,10 @@ class ObservationsHolderActions {
const formElementStatuses = ObservationsHolderActions._getFormElementStatuses(newState, context);
const ruleValidationErrors = ObservationsHolderActions.getRuleValidationErrors(formElementStatuses);
const hiddenFormElementStatus = _.filter(formElementStatuses, (form) => form.visibility === false);
newState.observationsHolder.updatePrimitiveCodedObs(newState.filteredFormElements, formElementStatuses);
newState.observationsHolder.updatePrimitiveCodedObs(_.filter(newState.filteredFormElements, (fe) => {
const observation = newState.observationsHolder.findObservation(fe.concept);
return !(_.isNil(observation) || observation.valueJSON.answerSource === Observation.AnswerSource.Manual);
}), formElementStatuses);
newState.removeHiddenFormValidationResults(hiddenFormElementStatus);
let validationResult = action.formElement.validate(value);
if (action.validationResult && validationResult.success) {
Expand Down Expand Up @@ -88,7 +90,10 @@ class ObservationsHolderActions {
const formElementStatuses = ObservationsHolderActions._getFormElementStatuses(newState, context);
const ruleValidationErrors = ObservationsHolderActions.getRuleValidationErrors(formElementStatuses);
const hiddenFormElementStatus = _.filter(formElementStatuses, (form) => form.visibility === false);
newState.observationsHolder.updatePrimitiveCodedObs(newState.filteredFormElements, formElementStatuses);
newState.observationsHolder.updatePrimitiveCodedObs(_.filter(newState.filteredFormElements, (fe) => {
const observation = newState.observationsHolder.findObservation(fe.concept);
return !(_.isNil(observation) || observation.valueJSON.answerSource === Observation.AnswerSource.Manual);
}), formElementStatuses);
const validationResult = action.formElement.validate(_.isNil(observation) ? null : observation.getValueWrapper());
newState.handleValidationResults(ObservationsHolderActions.addPreviousValidationErrors(ruleValidationErrors, validationResult, newState.validationResults), context);
newState.removeHiddenFormValidationResults(hiddenFormElementStatus);
Expand All @@ -111,7 +116,10 @@ class ObservationsHolderActions {
const ruleValidationErrors = ObservationsHolderActions.getRuleValidationErrors(formElementStatuses);
const hiddenFormElementStatus = _.filter(formElementStatuses, (form) => form.visibility === false);
const validationResult = action.formElement.validate(_.isNil(observation) ? null : observation.getValueWrapper());
newState.observationsHolder.updatePrimitiveCodedObs(newState.filteredFormElements, formElementStatuses);
newState.observationsHolder.updatePrimitiveCodedObs(_.filter(newState.filteredFormElements, (fe) => {
const observation = newState.observationsHolder.findObservation(fe.concept);
return !(_.isNil(observation) || observation.valueJSON.answerSource === Observation.AnswerSource.Manual);
}), formElementStatuses);
newState.handleValidationResults(ObservationsHolderActions.addPreviousValidationErrors(ruleValidationErrors, validationResult, newState.validationResults), context);
newState.removeHiddenFormValidationResults(hiddenFormElementStatus);
return newState;
Expand All @@ -131,7 +139,10 @@ class ObservationsHolderActions {
const formElementStatuses = ObservationsHolderActions._getFormElementStatuses(newState, context);
const ruleValidationErrors = ObservationsHolderActions.getRuleValidationErrors(formElementStatuses);
const hiddenFormElementStatus = _.filter(formElementStatuses, (form) => form.visibility === false);
newState.observationsHolder.updatePrimitiveCodedObs(newState.filteredFormElements, formElementStatuses);
newState.observationsHolder.updatePrimitiveCodedObs(_.filter(newState.filteredFormElements, (fe) => {
const observation = newState.observationsHolder.findObservation(fe.concept);
return !(_.isNil(observation) || observation.valueJSON.answerSource === Observation.AnswerSource.Manual);
}), formElementStatuses);
const validationResult = action.formElement.validate(dateValue);
newState.handleValidationResults(ObservationsHolderActions.addPreviousValidationErrors(ruleValidationErrors, validationResult, newState.validationResults), context);
newState.removeHiddenFormValidationResults(hiddenFormElementStatus);
Expand All @@ -146,7 +157,10 @@ class ObservationsHolderActions {
const formElementStatuses = ObservationsHolderActions._getFormElementStatuses(newState, context);
const ruleValidationErrors = ObservationsHolderActions.getRuleValidationErrors(formElementStatuses);
const hiddenFormElementStatus = _.filter(formElementStatuses, (form) => form.visibility === false);
newState.observationsHolder.updatePrimitiveCodedObs(newState.filteredFormElements, formElementStatuses);
newState.observationsHolder.updatePrimitiveCodedObs(_.filter(newState.filteredFormElements, (fe) => {
const observation = newState.observationsHolder.findObservation(fe.concept);
return !(_.isNil(observation) || observation.valueJSON.answerSource === Observation.AnswerSource.Manual);
}), formElementStatuses);
const validationResult = action.formElement.validate(_.isNil(observation) ? null : observation.getValueWrapper());
newState.handleValidationResults(ObservationsHolderActions.addPreviousValidationErrors(ruleValidationErrors, validationResult, newState.validationResults), context);
newState.removeHiddenFormValidationResults(hiddenFormElementStatus);
Expand All @@ -159,7 +173,10 @@ class ObservationsHolderActions {
const formElementStatuses = ObservationsHolderActions._getFormElementStatuses(newState, context);
const ruleValidationErrors = ObservationsHolderActions.getRuleValidationErrors(formElementStatuses);
const hiddenFormElementStatus = _.filter(formElementStatuses, (form) => form.visibility === false);
newState.observationsHolder.updatePrimitiveCodedObs(newState.filteredFormElements, formElementStatuses);
newState.observationsHolder.updatePrimitiveCodedObs(_.filter(newState.filteredFormElements, (fe) => {
const observation = newState.observationsHolder.findObservation(fe.concept);
return !(_.isNil(observation) || observation.valueJSON.answerSource === Observation.AnswerSource.Manual);
}), formElementStatuses);
const value = _.isNil(observation) ? null : observation.getValueWrapper().getValue();
let validationResult = action.formElement.validate(value);
if (action.formElement.isUnique && !_.isNil(value) && validationResult.success) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ class MyDashboardActions {
allIndividuals = methodMap.get(listType)(state.date.value, [], state.dueChecklistFilter)
}
else
allIndividuals = methodMap.get(listType)(state.date.value, filters, state.generalEncountersFilters, queryProgramEncounter, queryGeneralEncounter);
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');
Expand Down
40 changes: 19 additions & 21 deletions packages/openchs-android/src/service/MediaQueueService.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ProgramEnrolmentService from "./ProgramEnrolmentService";
import * as mime from 'react-native-mime-types';
import moment from "moment";
import I18n from 'i18n-js';
import bugsnag from "../utility/bugsnag";
const PARALLEL_UPLOAD_COUNT = 1;

@Service("mediaQueueService")
Expand Down Expand Up @@ -204,11 +205,7 @@ class MediaQueueService extends BaseService {
.catch((error) => {
General.logError("MediaQueueService", `Error while uploading ${mediaQueueItem.uuid} - ${mediaQueueItem.fileName}`);
General.logError("MediaQueueService", error);
if (error.message === 'canceled') {
return Promise.reject(new Error("syncTimeoutError"))
} else {
return Promise.reject(error);
}
return Promise.reject(error);
});
}

Expand All @@ -217,8 +214,8 @@ class MediaQueueService extends BaseService {
}

uploadMedia(statusMessageCallback) {
// Parallel push to S3 ensures maximal usage of existing bandwidth.
// Return only once every media queue item upload succeeds or fails.
// Chunked push to S3 to minimize sync failures on low bandwidth.
// Stops on first chunk with error or when all chunks are processed successfully
const mediaQueueItems = _.map(this.findAll(), (mediaQueueItem) => mediaQueueItem.clone());
General.logDebug("MediaQueueService", `Number of media queue items: ${mediaQueueItems.length}`);
const chunkedMediaQueueItems = _.chunk(mediaQueueItems, PARALLEL_UPLOAD_COUNT);
Expand All @@ -227,21 +224,22 @@ class MediaQueueService extends BaseService {
let current = Promise.resolve();
let count = 0;
for (const mediaQueueItemsChunk of chunkedMediaQueueItems) {
current = current.then(() => Promise.allSettled(
_.map(mediaQueueItemsChunk, (mediaQueueItem) => this.uploadMediaQueueItem(mediaQueueItem))
)).then((results) => {
if (_.some(results, result => result.status === 'rejected')) {
return Promise.reject(new Error("syncTimeoutError"));
} else {
count += PARALLEL_UPLOAD_COUNT
if(statusMessageCallback) {
statusMessageCallback(`${I18n.t("uploadMedia")} (${count}/${mediaQueueItems.length})`)
current = current.then(() => Promise.all(
_.map(mediaQueueItemsChunk, (mediaQueueItem) => {
if (statusMessageCallback) {
statusMessageCallback(`${I18n.t("uploadMedia")} (${count}/${mediaQueueItems.length})`);
}

General.logInfo("MediaQueueService",`MediaUpload: Time taken ${(moment.now() - startTime)}`);
return Promise.resolve();
}
});
return this.uploadMediaQueueItem(mediaQueueItem);
})
)).then(() => {
count += PARALLEL_UPLOAD_COUNT
General.logInfo("MediaQueueService", `MediaUpload: Time taken ${(moment.now() - startTime)}`);
return Promise.resolve();
}).catch((error) => {
// notify bugsnag of the original underlying error, so we can check if there are multiple causes for failure
bugsnag.notify(error);
return Promise.reject(new Error("syncTimeoutError"));
})
}
current.then(() => { General.logInfo("MediaQueueService",`MediaUpload:Total time taken ${(moment.now() - startTime)}`)})
return current;
Expand Down
30 changes: 25 additions & 5 deletions packages/openchs-android/src/service/SyncService.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ import BaseService from "./BaseService";
import EntityService from "./EntityService";
import EntitySyncStatusService from "./EntitySyncStatusService";
import SettingsService from "./SettingsService";
import {EntityMetaData, EntitySyncStatus, RuleFailureTelemetry, SyncTelemetry, Individual, UserInfo} from 'openchs-models';
import {
EntityMetaData,
EntitySyncStatus,
RuleFailureTelemetry,
SyncTelemetry,
IgnorableSyncError
} from 'openchs-models';
import EntityQueueService from "./EntityQueueService";
import MessageService from "./MessageService";
import RuleEvaluationService from "./RuleEvaluationService";
Expand All @@ -29,11 +35,26 @@ import UserSubjectAssignmentService from "./UserSubjectAssignmentService";
import moment from "moment";
import AllSyncableEntityMetaData from "../model/AllSyncableEntityMetaData";
import {IndividualSearchActionNames as IndividualSearchActions} from '../action/individual/IndividualSearchActions';
import {LandingViewActionsNames as Actions, LandingViewActionsNames as LandingViewActions} from '../action/LandingViewActions';
import {LandingViewActionsNames as LandingViewActions} from '../action/LandingViewActions';
import {MyDashboardActionNames} from '../action/mydashboard/MyDashboardActions';
import {CustomDashboardActionNames} from '../action/customDashboard/CustomDashboardActions';
import LocalCacheService from "./LocalCacheService";

function transformResourceToEntity(entityMetaData, entityResources) {
return (acc, resource) => {
try {
return acc.concat([entityMetaData.entityClass.fromResource(resource, this.entityService, entityResources)]);
} catch (error) {
if(error instanceof IgnorableSyncError) {
General.logError("SyncService", error);
} else {
throw error;
}
}
return acc; // since error is IgnorableSyncError, return accumulator as is
}
}

@Service("syncService")
class SyncService extends BaseService {
constructor(db, context) {
Expand Down Expand Up @@ -288,7 +309,7 @@ class SyncService extends BaseService {
if (_.isEmpty(entityResources)) return;
entityResources = _.sortBy(entityResources, 'lastModifiedDateTime');

const entities = entityResources.reduce((acc, resource) => acc.concat([entityMetaData.entityClass.fromResource(resource, this.entityService, entityResources)]), []);
const entities = entityResources.reduce(transformResourceToEntity.call(this, entityMetaData, entityResources), []);
General.logDebug("SyncService", `Creating entity create functions for schema ${entityMetaData.schemaName}`);
let entitiesToCreateFns = this.getCreateEntityFunctions(entityMetaData.schemaName, entities);
if (entityMetaData.nameTranslated) {
Expand Down Expand Up @@ -376,9 +397,8 @@ class SyncService extends BaseService {

this.dispatchAction(IndividualSearchActions.ON_LOAD);
this.dispatchAction(MyDashboardActionNames.ON_LOAD);
this.dispatchAction(LandingViewActions.ON_LOAD, {syncRequired});
LocalCacheService.getPreviouslySelectedSubjectTypeUuid().then(cachedSubjectTypeUUID => {
this.dispatchAction(Actions.ON_LOAD, {cachedSubjectTypeUUID});
this.dispatchAction(LandingViewActions.ON_LOAD, {syncRequired, cachedSubjectTypeUUID});
});
this.dispatchAction(CustomDashboardActionNames.ON_LOAD, {onlyPrimary: false});
this.dispatchAction(CustomDashboardActionNames.REMOVE_OLDER_COUNTS);
Expand Down

0 comments on commit d30b007

Please sign in to comment.