diff --git a/build/entitlements.mac.plist b/build/entitlements.mac.plist
new file mode 100644
index 00000000..d6b93bc0
--- /dev/null
+++ b/build/entitlements.mac.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+
+
diff --git a/build/icon.ico b/build/icon.ico
index 24ffb76b..24e3b073 100644
Binary files a/build/icon.ico and b/build/icon.ico differ
diff --git a/main.js b/main.js
index babcfd86..8cb3189d 100644
--- a/main.js
+++ b/main.js
@@ -28,6 +28,16 @@ try {
// use server for route
mainWindow.loadURL(`http://localhost:${env.PORT}`)
+
+ mainWindow.webContents.session.on(
+ 'will-download',
+ (event, downloadItem, webContents) => {
+ downloadItem.setSaveDialogOptions({
+ filters: [{ name: 'CSV', extensions: ['csv'] }],
+ message: 'Download CSV',
+ })
+ }
+ )
}
app.on('ready', createWindow)
diff --git a/package.json b/package.json
index ce50b21e..4665989f 100644
--- a/package.json
+++ b/package.json
@@ -8,9 +8,15 @@
"private": true,
"build": {
"productName": "Project Reserve",
- "appId": "your.id",
+ "appId": "project.reserve",
"mac": {
- "category": "your.app.category.type"
+ },
+ "win": {
+ "target": [
+ {
+ "target": "nsis"
+ }
+ ]
},
"files": [
"build/*",
@@ -33,7 +39,7 @@
"build:client": "cross-env NODE_ENV=production webpack --config build/webpack.client.config.js --progress",
"build:server": "cross-env NODE_ENV=production webpack --config build/webpack.server.config.js --progress",
"pack": "electron-builder --dir",
- "dist": "yarn build && DEBUG=electron-builder electron-builder",
+ "dist": "yarn build && DEBUG=electron-builder electron-builder --win --mac",
"postinstall": "electron-builder install-app-deps"
},
"dependencies": {
@@ -45,6 +51,8 @@
"body-parser": "^1.19.0",
"compression": "^1.7.4",
"dotenv": "^8.2.0",
+ "electron-builder-squirrel-windows": "^22.10.4",
+ "electron-notarize": "^1.0.0",
"express": "^4.17.1",
"http": "^0.0.1-security",
"isomorphic-fetch": "^3.0.0",
diff --git a/src/api/main/routes/configurations.js b/src/api/main/routes/configurations.js
index 7170cb3a..092f6637 100644
--- a/src/api/main/routes/configurations.js
+++ b/src/api/main/routes/configurations.js
@@ -24,6 +24,7 @@ router.get("/configurations/:id", async (req, res) => {
{
model: db.reserveCategory,
as: "reserveCategories",
+ order: [['reserveCatetories.`order`', 'ASC']],
include: [
{
model: db.priority,
@@ -31,6 +32,7 @@ router.get("/configurations/:id", async (req, res) => {
{
model: db.categoryCriteria,
as: "categoryCriteria",
+ order: [['order', 'ASC']],
include: [
{
model: db.categoryCriteriaElement,
@@ -41,6 +43,7 @@ router.get("/configurations/:id", async (req, res) => {
{
model: db.numericCriteria,
as: "numericCriteria",
+ order: [['order', 'ASC']],
include: [
{
model: db.numericCriteriaBucket,
@@ -51,6 +54,7 @@ router.get("/configurations/:id", async (req, res) => {
],
},
],
+
},
],
})
@@ -106,6 +110,7 @@ router.get("/configurations/:id/fieldNames", async (req, res) => {
where: { configurationId, isDefault: false },
});
+
const categoryCriteriaFields = await db.sequelize.query(
`
SELECT id, name FROM category_criteria
@@ -118,6 +123,30 @@ router.get("/configurations/:id/fieldNames", async (req, res) => {
);
+ const categoryCriteriaFieldsDefault = await db.sequelize.query(
+ `
+ SELECT id, name FROM category_criteria
+ WHERE priority_id IN (
+ SELECT id FROM priority
+ WHERE reserve_category_id in (SELECT id FROM reserve_category WHERE configuration_id = ${configurationId} AND is_default)
+ );
+ `,
+ { type: SELECT }
+ );
+
+
+ const categoryCriteriaFieldsNotDefault = await db.sequelize.query(
+ `
+ SELECT id, name FROM category_criteria
+ WHERE priority_id IN (
+ SELECT id FROM priority
+ WHERE reserve_category_id in (SELECT id FROM reserve_category WHERE configuration_id = ${configurationId} AND NOT is_default)
+ );
+ `,
+ { type: SELECT }
+ );
+
+
const possibleValues = await Promise.all(categoryCriteriaFields[0].map(async (criteria) => {
const values = await db.sequelize.query(
`
@@ -140,17 +169,29 @@ router.get("/configurations/:id/fieldNames", async (req, res) => {
}, {})
- const numericCriteriaFields = await db.sequelize.query(
+ const numericCriteriaFieldsRequired = await db.sequelize.query(
`
SELECT name, min, max FROM numeric_criteria
WHERE priority_id IN (
SELECT id FROM priority
- WHERE reserve_category_id in (SELECT id FROM reserve_category WHERE configuration_id = ${configurationId})
+ WHERE reserve_category_id in (SELECT id FROM reserve_category WHERE configuration_id = ${configurationId} AND is_default)
+ );
+ `,
+ { type: SELECT }
+ );
+
+const numericCriteriaFieldsNotRequired = await db.sequelize.query(
+ `
+ SELECT name, min, max FROM numeric_criteria
+ WHERE priority_id IN (
+ SELECT id FROM priority
+ WHERE reserve_category_id in (SELECT id FROM reserve_category WHERE configuration_id = ${configurationId} AND NOT is_default)
);
`,
{ type: SELECT }
);
+
const names = new Set()
const fieldNames = [
@@ -159,14 +200,27 @@ router.get("/configurations/:id/fieldNames", async (req, res) => {
reserveCategoryNames.forEach((cat) => {
if (!names.has(cat.name)) {
fieldNames.push({
- name: "is_" + cat.name.toLowerCase().split(" ").join("_"),
+ name: "is_eligible_for_reserve_cat_" + cat.name.toLowerCase().split(" ").join("_"),
required: true,
dataType: "BOOLEAN",
})
names.add(cat.name)
}
});
- categoryCriteriaFields[0].forEach((criteria) => {
+
+ categoryCriteriaFieldsDefault[0].forEach((criteria) => {
+ if (!names.has(criteria.name)) {
+ fieldNames.push({
+ name: criteria.name.toLowerCase().split(" ").join("_"),
+ required: true,
+ dataType: "STRING",
+ possibleValues: possibleValuesMap[criteria.id]
+ })
+ names.add(criteria.name)
+ }
+ });
+
+ categoryCriteriaFieldsNotDefault[0].forEach((criteria) => {
if (!names.has(criteria.name)) {
fieldNames.push({
name: criteria.name.toLowerCase().split(" ").join("_"),
@@ -177,7 +231,20 @@ router.get("/configurations/:id/fieldNames", async (req, res) => {
names.add(criteria.name)
}
});
- numericCriteriaFields[0].forEach((criteria) => {
+
+ numericCriteriaFieldsRequired[0].forEach((criteria) => {
+ if (!names.has(criteria.name)) {
+ fieldNames.push({
+ name: criteria.name.toLowerCase().split(" ").join("_"),
+ required: true,
+ dataType: "NUMBER",
+ possibleValues: { min: criteria.min, max: criteria.max }
+ })
+ names.add(criteria.name)
+ }
+ });
+
+ numericCriteriaFieldsNotRequired[0].forEach((criteria) => {
if (!names.has(criteria.name)) {
fieldNames.push({
name: criteria.name.toLowerCase().split(" ").join("_"),
@@ -189,6 +256,9 @@ router.get("/configurations/:id/fieldNames", async (req, res) => {
}
});
+
+
+
res.json(fieldNames);
});
diff --git a/src/api/main/routes/patients.js b/src/api/main/routes/patients.js
index 9d1c8a4c..6656eea6 100755
--- a/src/api/main/routes/patients.js
+++ b/src/api/main/routes/patients.js
@@ -47,8 +47,8 @@ router.post("/patients", async (req, res) => {
// add reserve categories
const reserveCategoryFieldNames = fields
- .filter((f) => f.startsWith("is_") && rawPat[f])
- .map((f) => f.substr(3));
+ .filter((f) => f.startsWith("is_eligible_for_reserve_cat_") && rawPat[f])
+ .map((f) => f.substr(28));
const reserveCategories = db.reserveCategory.findAll({
where: {
configurationId,
@@ -76,7 +76,7 @@ router.post("/patients", async (req, res) => {
// add numeric criteria values
const possibleNumericCriteriaFields = fields.filter(
- (f) => !f.startsWith("is_")
+ (f) => !f.startsWith("is_eligible_for_reserve_cat_")
);
const numericCriteria = db.numericCriteria.findAll({
where: {
@@ -129,7 +129,7 @@ router.post("/patients", async (req, res) => {
// add category criteria values
const possibleCategoryCriteriaFields = fields.filter(
- (f) => !f.startsWith("is_")
+ (f) => !f.startsWith("is_eligible_for_reserve_cat_")
);
const categoryCriteria = db.categoryCriteria.findAll({
where: { name: { [Op.in]: possibleCategoryCriteriaFields } },
diff --git a/src/api/main/routes/source_files.js b/src/api/main/routes/source_files.js
index 71eae7db..79f1aee5 100644
--- a/src/api/main/routes/source_files.js
+++ b/src/api/main/routes/source_files.js
@@ -36,7 +36,7 @@ router.get('/sourceFiles/:id', async (req, res) => {
return res.json(await db.sourceFile.findOne({ where: { id } }))
})
-// GET one source file id
+// GET nth reserve patients
router.get('/sourceFiles/:id/nthReservePatients', async (req, res) => {
const { db } = req
const id = req.params.id
@@ -47,6 +47,13 @@ router.get('/sourceFiles/:id/nthReservePatients', async (req, res) => {
)
})
+// GET the number of left over units for reserve instance
+router.get('/sourceFiles/:id/leftOver', async (req, res) => {
+ const { db } = req
+ const id = req.params.id
+ return res.json((await db.sourceFile.findOne({ where: { id } })).left_over)
+})
+
// GET all patients for a source file
router.get('/sourceFiles/:id/patients', async (req, res) => {
const { db } = req
@@ -55,7 +62,7 @@ router.get('/sourceFiles/:id/patients', async (req, res) => {
if (req.query.givenUnit)
filterLosers += `and ${
req.query.givenUnit === 'false' ? 'not' : ''
- } given_unit`
+ } given_unit`
return res.json(await getPatientsWithAttributes(db, id, filterLosers))
})
@@ -85,7 +92,6 @@ router.post('/sourceFiles/:id/process', async (req, res) => {
})
)
-
const patients = await Promise.all(
(
await db.patient.findAll({
@@ -105,17 +111,13 @@ router.post('/sourceFiles/:id/process', async (req, res) => {
let given = 0
let i = 0
while (i < f.patients.length) {
-
-
if (given < f.size && !selectedPatients.has(f.patients[i])) {
-
selectedPatients.add(f.patients[i])
allocatedPatientGroups[f.patients[i]] = f.name
given += 1
notSelectedPatients.delete(f.patients[i])
if (given == f.size) {
-
nthReservePatients.push({
name: f.name,
nthRecipientPrimaryId: f.patients[i],
@@ -128,20 +130,8 @@ router.post('/sourceFiles/:id/process', async (req, res) => {
leftOver += f.size - given
})
- const notSelectedPatientsArray = Array.from(notSelectedPatients)
- let i = 0
- // give left over to unallocated patients if there are any
- while (leftOver > 0 && i < notSelectedPatientsArray.length) {
- const pat = notSelectedPatientsArray[i]
- selectedPatients.add(pat)
- allocatedPatientGroups[pat] = 'None'
- leftOver -= 1
- i += 1
- }
-
// update patients
selectedPatients.forEach(async (pId) => {
-
const patient = await db.patient.findOne({ where: { id: pId } })
patient.given_unit = true
patient.group_allocated_under = allocatedPatientGroups[pId]
@@ -150,9 +140,9 @@ router.post('/sourceFiles/:id/process', async (req, res) => {
const nthReservePatientsWithNames = await Promise.all(
nthReservePatients.map(async (f) => {
- const name = (
- await db.patient.findOne({ where: { id: f.nthRecipientPrimaryId } })
- )
+ const name = await db.patient.findOne({
+ where: { id: f.nthRecipientPrimaryId },
+ })
return { name: f.name, nthRecipientId: name.dataValues.recipient_id }
})
diff --git a/src/notarize.js b/src/notarize.js
new file mode 100644
index 00000000..09a133e6
--- /dev/null
+++ b/src/notarize.js
@@ -0,0 +1,18 @@
+require('dotenv').config();
+const { notarize } = require('electron-notarize');
+
+exports.default = async function notarizing(context) {
+ const { electronPlatformName, appOutDir } = context;
+ if (electronPlatformName !== 'darwin') {
+ return;
+ }
+
+ const appName = context.packager.appInfo.productFilename;
+
+ return await notarize({
+ appBundleId: 'project.reserve',
+ appPath: `${appOutDir}/${appName}.app`,
+ appleId: process.env.APPLEID,
+ appleIdPassword: process.env.APPLEIDPASS,
+ });
+};
diff --git a/src/public/components/ConfigSummary.vue b/src/public/components/ConfigSummary.vue
index 2064e820..25c1c4f1 100644
--- a/src/public/components/ConfigSummary.vue
+++ b/src/public/components/ConfigSummary.vue
@@ -115,7 +115,7 @@ export default {
},
}
-