Skip to content
This repository has been archived by the owner on Jan 9, 2023. It is now read-only.

Introduce ember-concurrency with examples #977

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 28 additions & 20 deletions app/admin/address/route.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,39 @@
import AbstractEditRoute from 'hospitalrun/routes/abstract-edit-route';
import Ember from 'ember';
import AbstractEditRoute from 'hospitalrun/routes/abstract-edit-route';
import { task } from 'ember-concurrency';
import { translationMacro as t } from 'ember-i18n';
import UnauthorizedError from 'hospitalrun/utils/unauthorized-error';

const { computed } = Ember;

export default AbstractEditRoute.extend({
hideNewButton: true,
newTitle: t('admin.address.newTitle'),
editTitle: t('admin.address.editTitle'),

model() {
return new Ember.RSVP.Promise((resolve, reject) => {
this.get('store').find('option', 'address_options').then((addressOptions) => {
resolve(addressOptions);
}, (err) => {
if (err instanceof UnauthorizedError) {
reject(err);
} else {
let store = this.get('store');
let newConfig = store.push(store.normalize('option', {
id: 'address_options',
value: {
address1Label: this.get('i18n').t('admin.address.addressLabel'),
address1Include: true
}
}));
resolve(newConfig);
}
});
return this.get('fetchAddressOptions').perform();
},

fetchAddressOptions: task(function* () {
let store = this.get('store');
try {
return yield store.find('option', 'address_options');
} catch(err) {
if (err instanceof UnauthorizedError) {
throw err;
}
return store.push(this.get('defaultAddressOption'));
}
}).keepLatest().cancelOn('deactivate'),

defaultAddressOption: computed(function() {
return this.get('store').normalize('option', {
id: 'address_options',
value: {
address1Label: this.get('i18n').t('admin.address.addressLabel'),
address1Include: true
}
});
}
})
});
183 changes: 94 additions & 89 deletions app/patients/delete/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,116 +6,121 @@ import PouchDbMixin from 'hospitalrun/mixins/pouchdb';
import ProgressDialog from 'hospitalrun/mixins/progress-dialog';
import Ember from 'ember';
import { translationMacro as t } from 'ember-i18n';
import { task, taskGroup, all } from 'ember-concurrency';

function deleteMany(manyArray) {
if (!manyArray) {
return Ember.RSVP.resolve();
}
if (manyArray.then) {
// recursive call after resolving async model
return manyArray.then(deleteMany);
}
let recordsCount = manyArray.get('length');
if (!recordsCount) {
// empty array: no records to delete
return Ember.RSVP.resolve();
}
let archivePromises = manyArray.map((recordToDelete) => {
recordToDelete.set('archived', true);
return recordToDelete.save().then(() => {
return recordToDelete.unloadRecord();
});
});
return Ember.RSVP.all(archivePromises, 'async array deletion');
}
const MAX_CONCURRENCY = 5;

export default AbstractDeleteController.extend(PatientVisitsMixin, PatientInvoicesMixin, PouchDbMixin, ProgressDialog, PatientAppointmentsMixin, {
title: t('patients.titles.delete'),
progressTitle: t('patients.titles.deletePatientRecord'),
progressMessage: t('patients.messages.deletingPatient'),
deleting: taskGroup(),

deleteMany(manyArray) {
return this.get('deleteManyTask').perform(manyArray);
},

deleteManyTask: task(function* (manyArray) {
if (!manyArray) {
return;
}
let resolvedArray = yield manyArray;
if (Ember.isEmpty(resolvedArray)) {
// empty array: no records to delete
return;
}
let deleteRecordTask = this.get('deleteRecordTask');
let archivePromises = [];
for (let recordToDelete of resolvedArray) {
archivePromises.push(deleteRecordTask.perform(recordToDelete));
}
return yield all(archivePromises, 'async array deletion');
}).group('deleting'),

deleteRecordTask: task(function* (recordToDelete) {
recordToDelete.set('archived', true);
yield recordToDelete.save();
return yield recordToDelete.unloadRecord();
}).maxConcurrency(MAX_CONCURRENCY).enqueue().group('deleting'),

// Override delete action on controller; we must delete
// all related records before deleting patient record
// otherwise errors will occur
deletePatient() {
let controller = this;
let patient = this.get('model');
let visits = this.getPatientVisits(patient);
let invoices = this.getPatientInvoices(patient);
let appointments = this.getPatientAppointments(patient);
let payments = patient.get('payments');
// resolve all async models first since they reference each other, then delete
return Ember.RSVP.all([visits, invoices, appointments, payments]).then(function(records) {
let promises = [];
promises.push(controller.deleteVisits(records[0]));
promises.push(controller.deleteInvoices(records[1]));
promises.push(deleteMany(records[2])); // appointments
promises.push(deleteMany(records[3])); // payments
return Ember.RSVP.all(promises)
.then(function() {
return patient.destroyRecord();
});
});
return this.get('deletePatientTask').perform();
},

deletePatientTask: task(function* () {
let patient = this.get('model');
let visits = yield this.getPatientVisits(patient);
let invoices = yield this.getPatientInvoices(patient);
let appointments = yield this.getPatientAppointments(patient);
let payments = yield patient.get('payments');
yield all([
this.deleteVisits(visits),
this.deleteInvoices(invoices),
this.deleteMany(appointments),
this.deleteMany(payments)
]);
return yield patient.destroyRecord();
}).group('deleting'),

deleteVisits(visits) {
let promises = [];
visits.forEach(function(visit) {
let labs = visit.get('labs');
let procedures = visit.get('procedures');
let imaging = visit.get('imaging');
let procCharges = procedures.then(function(p) {
return p.get('charges');
});
let labCharges = labs.then(function(l) {
return l.get('charges');
});
let imagingCharges = imaging.then(function(i) {
return i.get('charges');
});
let visitCharges = visit.get('charges');
promises.push(deleteMany(labs));
promises.push(deleteMany(labCharges));
promises.push(deleteMany(visit.get('patientNotes')));
promises.push(deleteMany(visit.get('vitals')));
promises.push(deleteMany(procedures));
promises.push(deleteMany(procCharges));
promises.push(deleteMany(visit.get('medication')));
promises.push(deleteMany(imaging));
promises.push(deleteMany(imagingCharges));
promises.push(deleteMany(visitCharges));
});
return Ember.RSVP.all(promises).then(function() {
return deleteMany(visits);
});
return this.get('deleteVisitsTask').perform(visits);
},

deleteVisitsTask: task(function* (visits) {
let pendingTasks = [];
for (let visit of visits) {
let labs = yield visit.get('labs');
let procedures = yield visit.get('procedures');
let imaging = yield visit.get('imaging');
let procCharges = procedures.get('charges');
let labCharges = labs.get('charges');
let imagingCharges = imaging.get('charges');
let visitCharges = visit.get('charges');
pendingTasks.push(this.deleteMany(labs));
pendingTasks.push(this.deleteMany(labCharges));
pendingTasks.push(this.deleteMany(visit.get('patientNotes')));
pendingTasks.push(this.deleteMany(visit.get('vitals')));
pendingTasks.push(this.deleteMany(procedures));
pendingTasks.push(this.deleteMany(procCharges));
pendingTasks.push(this.deleteMany(visit.get('medication')));
pendingTasks.push(this.deleteMany(imaging));
pendingTasks.push(this.deleteMany(imagingCharges));
pendingTasks.push(this.deleteMany(visitCharges));
}
yield all(pendingTasks);
return yield this.deleteMany(visits);
}).group('deleting'),

deleteInvoices(patientInvoices) {
return Ember.RSVP.resolve(patientInvoices).then(function(invoices) {
let lineItems = Ember.A();
invoices.forEach(function(i) {
lineItems.addObjects(i.get('lineItems'));
});
let lineItemDetails = Ember.A();
lineItems.forEach(function(li) {
lineItemDetails.addObjects(li.get('details'));
});
return Ember.RSVP.all([lineItems, lineItemDetails]).then(function() {
return Ember.RSVP.all([deleteMany(invoices), deleteMany(lineItems), deleteMany(lineItemDetails)]);
});
});
return this.get('deleteInvoicesTask').perform(patientInvoices);
},

actions: {
deleteInvoicesTask: task(function* (patientInvoices) {
let invoices = yield patientInvoices;
let lineItems = yield all(invoices.mapBy('lineItems'));
let lineItemDetails = yield all(lineItems.mapBy('details'));
return yield all([
this.deleteMany(invoices),
this.deleteMany(lineItems),
this.deleteMany(lineItemDetails)
]);
}).group('deleting'),

deleteActionTask: task(function* (patient) {
// delete related records without modal dialogs
this.send('closeModal');
this.showProgressModal();
yield this.deletePatient(patient);
this.closeProgressModal();
this.send(this.get('afterDeleteAction'), patient);
}).drop(),

actions: {
delete(patient) {
let controller = this;
this.send('closeModal');
this.showProgressModal();
this.deletePatient(patient).then(function() {
controller.closeProgressModal();
controller.send(controller.get('afterDeleteAction'), patient);
});
this.get('deleteActionTask').perform(patient);
}
}
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"ember-cli-template-lint": "0.4.12",
"ember-cli-test-loader": "^1.1.0",
"ember-cli-uglify": "^1.2.0",
"ember-concurrency": "0.7.19",
"ember-data": "^2.10.0",
"ember-export-application-global": "^1.0.5",
"ember-fullcalendar": "1.3.0",
Expand Down