Skip to content

Commit

Permalink
Merge pull request #2144 from NMDSdevopsServiceAdm/test
Browse files Browse the repository at this point in the history
Merging from Test to Staging for Sprint 3.1
  • Loading branch information
aaron-russell authored May 18, 2020
2 parents 091a7bf + 9fbaa6a commit ac8087f
Show file tree
Hide file tree
Showing 31 changed files with 1,534 additions and 154 deletions.
17 changes: 17 additions & 0 deletions package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.1",
"scripts": {
"ng": "ng",
"start": "npm run build && node --max-old-space-size=4096 server.js",
"start": "node --max-old-space-size=4096 server.js",
"dev": "npm-run-all -p -l build:watch api:proxy",
"build": "ng build --extract-css",
"build:clean": "rimraf dist",
Expand Down Expand Up @@ -114,6 +114,7 @@
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"mocha": "^6.2.2",
"node-mocks-http": "^1.8.1",
"nodemon": "^1.18.9",
"npm": "^6.11.1",
"npm-run-all": "^4.1.5",
Expand Down
2 changes: 1 addition & 1 deletion server.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ app.use('/api/jobs', [refCacheMiddleware.refcache, jobs]);
app.use('/api/localAuthority', [refCacheMiddleware.refcache, la]);
app.use('/api/worker/leaveReasons', [refCacheMiddleware.refcache, workerLeaveReasons]);
app.use('/api/serviceUsers', [refCacheMiddleware.refcache, serviceUsers]);
app.use('/api/trainingCategories', [refCacheMiddleware.refcache, workingTrainingCategories]);
app.use('/api/trainingCategories', workingTrainingCategories);
app.use('/api/nurseSpecialism', [refCacheMiddleware.refcache, nurseSpecialism]);
app.use('/api/availableQualifications', [refCacheMiddleware.refcache, availableQualifications]);

Expand Down
2 changes: 1 addition & 1 deletion server/models/BulkImport/csv/workers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2526,7 +2526,7 @@ class Worker {
lineNumber: this._lineNumber,
errCode: Worker.DUPLICATE_ERROR,
errType: 'DUPLICATE_ERROR',
error: `CHGUNIQUEWORKERID ${CHGUNIQUEWORKERID} is not unique`,
error: `CHGUNIQUEWRKID ${CHGUNIQUEWORKERID} is not unique`,
source: this._currentLine.UNIQUEWORKERID,
worker: this._currentLine.UNIQUEWORKERID,
name: this._currentLine.LOCALESTID
Expand Down
27 changes: 27 additions & 0 deletions server/models/establishment.js
Original file line number Diff line number Diff line change
Expand Up @@ -707,5 +707,32 @@ module.exports = function(sequelize, DataTypes) {
});
};

Establishment.findWithWorkersAndTraining = function (establishmentId) {
return this.findByPk(establishmentId, {
attributes: ['id'],
include: {
model: sequelize.models.worker,
attributes: ['id', 'uid', 'NameOrIdValue'],
as: 'workers',
where: {
archived: false,
},
include: [
{
model: sequelize.models.job,
as: 'mainJob',
attributes: ['id', 'title'],
required: false,
},
{
model: sequelize.models.workerTraining,
as: 'workerTraining',
attributes: ['id', 'uid', 'title', 'expires', 'categoryFk'],
},
],
},
});
}

return Establishment;
};
6 changes: 6 additions & 0 deletions server/models/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,12 @@ module.exports = function(sequelize, DataTypes) {
as: 'auditEvents',
onDelete: 'CASCADE'
});
Worker.hasMany(models.workerTraining, {
foreignKey: 'workerFk',
sourceKey: 'id',
as: 'workerTraining',
onDelete: 'CASCADE'
});
Worker.belongsTo(models.ethnicity, {
foreignKey: 'EthnicityFkValue',
targetKey: 'id',
Expand Down
23 changes: 23 additions & 0 deletions server/models/workerTrainingCategories.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,28 @@ module.exports = function(sequelize, DataTypes) {
updatedAt: false
});

Categories.findAllWithMandatoryTraining = function (establishmentId) {
return this.findAll({
include: [
{
model: sequelize.models.MandatoryTraining,
as: 'MandatoryTraining',
where: {
EstablishmentFK: establishmentId,
},
required: false,
},
],
});
};

Categories.associate = (models) => {
Categories.hasMany(models.MandatoryTraining, {
foreignKey: 'TrainingCategoryFK',
sourceKey: 'id',
as: 'MandatoryTraining',
});
};

return Categories;
};
129 changes: 72 additions & 57 deletions server/routes/establishments/bulkUpload.js
Original file line number Diff line number Diff line change
Expand Up @@ -1113,77 +1113,70 @@ const validateBulkUploadFiles = async (

keepAlive('workers validated'); // keep connection alive

// having parsed all workers, check for duplicates
// the easiest way to check for duplicates is to build a single object, with the establishment key 'UNIQUEWORKERID`as property name
myWorkers.forEach(thisWorker => {
// uniquness for a worker is across both the establishment and the worker
const keyNoWhitespace = (thisWorker.local + thisWorker.uniqueWorker).replace(/\s/g, '');
const changeKeyNoWhitespace = thisWorker.changeUniqueWorker ? (thisWorker.local + thisWorker.changeUniqueWorker).replace(/\s/g, '') : null;

if (allWorkersByKey[keyNoWhitespace]) {
// this worker is a duplicate
csvWorkerSchemaErrors.push(thisWorker.addDuplicate(thisWorker.uniqueWorker));
// having parsed all workers, check for duplicates
// the easiest way to check for duplicates is to build a single object, with the establishment key 'UNIQUEWORKERID`as property name
const allKeys = [];
myWorkers.map(worker => {
const id = (worker.local + worker.uniqueWorker).replace(/\s/g, '');
allKeys.push(id);
});

// remove the entity
delete myAPIWorkers[thisWorker.lineNumber];
myWorkers.forEach(thisWorker => {
// uniquness for a worker is across both the establishment and the worker
const keyNoWhitespace = (thisWorker.local + thisWorker.uniqueWorker).replace(/\s/g, '');
const changeKeyNoWhitespace = thisWorker.changeUniqueWorker ? (thisWorker.local + thisWorker.changeUniqueWorker).replace(/\s/g, '') : null;

// the worker will be known by LOCALSTID and UNIQUEWORKERID, but if CHGUNIQUEWORKERID is given, then it's combination of LOCALESTID and CHGUNIQUEWORKERID must be unique
} else if (changeKeyNoWhitespace && allWorkersByKey[changeKeyNoWhitespace]) {
// this worker is a duplicate
csvWorkerSchemaErrors.push(thisWorker.addChgDuplicate(thisWorker.changeUniqueWorker));
if(checkDuplicateWorkerID(thisWorker, allKeys, changeKeyNoWhitespace, keyNoWhitespace, allWorkersByKey, myAPIWorkers, csvWorkerSchemaErrors)) {

// remove the entity
delete myAPIWorkers[thisWorker.lineNumber];
} else {
// does not yet exist - check this worker can be associated with a known establishment
const establishmentKeyNoWhitespace = thisWorker.local ? thisWorker.local.replace(/\s/g, '') : '';
// does not yet exist - check this worker can be associated with a known establishment
const establishmentKeyNoWhitespace = thisWorker.local ? thisWorker.local.replace(/\s/g, '') : '';

const myWorkersTotalHours = myWorkers.reduce((sum, thatWorker) => {
if (thisWorker.nationalInsuranceNumber === thatWorker.nationalInsuranceNumber) {
if (thatWorker.weeklyContractedHours) {
return sum + thatWorker.weeklyContractedHours;
}
if (thatWorker.weeklyAverageHours) {
return sum + thatWorker.weeklyAverageHours;
}
const myWorkersTotalHours = myWorkers.reduce((sum, thatWorker) => {
if (thisWorker.nationalInsuranceNumber === thatWorker.nationalInsuranceNumber) {
if (thatWorker.weeklyContractedHours) {
return sum + thatWorker.weeklyContractedHours;
}
if (thatWorker.weeklyAverageHours) {
return sum + thatWorker.weeklyAverageHours;
}
return sum;
}, 0);

if (myWorkersTotalHours > 65) {
csvWorkerSchemaErrors.push(thisWorker.exceedsNationalInsuranceMaximum());
}
return sum;
}, 0);

if (!allEstablishmentsByKey[establishmentKeyNoWhitespace]) {
// not found the associated establishment
csvWorkerSchemaErrors.push(thisWorker.uncheckedEstablishment());
if (myWorkersTotalHours > 65) {
csvWorkerSchemaErrors.push(thisWorker.exceedsNationalInsuranceMaximum());
}

// remove the entity
delete myAPIWorkers[thisWorker.lineNumber];
} else {
// this worker is unique and can be associated to establishment
allWorkersByKey[keyNoWhitespace] = thisWorker.lineNumber;
if (!allEstablishmentsByKey[establishmentKeyNoWhitespace]) {
// not found the associated establishment
csvWorkerSchemaErrors.push(thisWorker.uncheckedEstablishment());

// to prevent subsequent Worker duplicates, add also the change worker id if CHGUNIQUEWORKERID is given
if (changeKeyNoWhitespace) {
allWorkersByKey[changeKeyNoWhitespace] = thisWorker.lineNumber;
}
// remove the entity
delete myAPIWorkers[thisWorker.lineNumber];
} else {
// this worker is unique and can be associated to establishment
allWorkersByKey[keyNoWhitespace] = thisWorker.lineNumber;

// associate this worker to the known establishment
const knownEstablishment = myAPIEstablishments[establishmentKeyNoWhitespace] ? myAPIEstablishments[establishmentKeyNoWhitespace] : null;
// to prevent subsequent Worker duplicates, add also the change worker id if CHGUNIQUEWORKERID is given
if (changeKeyNoWhitespace) {
allWorkersByKey[changeKeyNoWhitespace] = thisWorker.lineNumber;
}

// key workers, to be used in training
const workerKeyNoWhitespace = (thisWorker._currentLine.LOCALESTID + thisWorker._currentLine.UNIQUEWORKERID).replace(/\s/g, '');
workersKeyed[workerKeyNoWhitespace] = thisWorker._currentLine;
// associate this worker to the known establishment
const knownEstablishment = myAPIEstablishments[establishmentKeyNoWhitespace] ? myAPIEstablishments[establishmentKeyNoWhitespace] : null;

if (knownEstablishment && myAPIWorkers[thisWorker.lineNumber]) {
knownEstablishment.associateWorker(myAPIWorkers[thisWorker.lineNumber].key, myAPIWorkers[thisWorker.lineNumber]);
} else {
// this should never happen
console.error(`FATAL: failed to associate worker (line number: ${thisWorker.lineNumber}/unique id (${thisWorker.uniqueWorker})) with a known establishment.`);
}
// key workers, to be used in training
const workerKeyNoWhitespace = (thisWorker._currentLine.LOCALESTID + thisWorker._currentLine.UNIQUEWORKERID).replace(/\s/g, '');
workersKeyed[workerKeyNoWhitespace] = thisWorker._currentLine;

if (knownEstablishment && myAPIWorkers[thisWorker.lineNumber]) {
knownEstablishment.associateWorker(myAPIWorkers[thisWorker.lineNumber].key, myAPIWorkers[thisWorker.lineNumber]);
} else {
// this should never happen
console.error(`FATAL: failed to associate worker (line number: ${thisWorker.lineNumber}/unique id (${thisWorker.uniqueWorker})) with a known establishment.`);
}
}
}
});
} else {
console.info('API bulkupload - validateBulkUploadFiles: no workers records');
Expand Down Expand Up @@ -2442,6 +2435,27 @@ const checkDuplicateLocations = async (
});
};

const checkDuplicateWorkerID = (thisWorker, allKeys, changeKeyNoWhitespace, keyNoWhitespace, allWorkersByKey, myAPIWorkers, csvWorkerSchemaErrors ) => {
// the worker will be known by LOCALSTID and UNIQUEWORKERID, but if CHGUNIQUEWORKERID is given, then it's combination of LOCALESTID and CHGUNIQUEWORKERID must be unique
if (changeKeyNoWhitespace && (allWorkersByKey[changeKeyNoWhitespace] || allKeys.includes(changeKeyNoWhitespace))) {
// this worker is a duplicate
csvWorkerSchemaErrors.push(thisWorker.addChgDuplicate(thisWorker.changeUniqueWorker));

// remove the entity
delete myAPIWorkers[thisWorker.lineNumber];
return false;
} else if (allWorkersByKey[keyNoWhitespace] !== undefined) {
// this worker is a duplicate
csvWorkerSchemaErrors.push(thisWorker.addDuplicate(thisWorker.uniqueWorker));

// remove the entity
delete myAPIWorkers[thisWorker.lineNumber];
return false;
} else {
return true;
};
};

const router = require('express').Router();

router.route('/signedUrl').get(acquireLock.bind(null, signedUrlGet, buStates.DOWNLOADING));
Expand All @@ -2463,4 +2477,5 @@ router.route('/response/:buRequestId').get(responseGet);

module.exports = router;
module.exports.checkDuplicateLocations = checkDuplicateLocations;
module.exports.checkDuplicateWorkerID = checkDuplicateWorkerID;
module.exports.validateEstablishmentCsv = validateEstablishmentCsv;
54 changes: 33 additions & 21 deletions server/routes/workerTrainingCategories.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,50 @@
var express = require('express');
var router = express.Router();
const express = require('express');
const router = express.Router();
const cacheMiddleware = require('../utils/middleware/noCache');
const refCacheMiddleware = require('../utils/middleware/refCache');
const models = require('../models/index');
const {
transformTrainingCategories,
transformTrainingCategoriesWithMandatoryTraining,
} = require('../transformers/trainingCategoryTransformer');

/* GET ALL ethnicities*/
router.route('/').get(async function (req, res) {
const getAllTraining = async function (_req, res) {
try {
let results = await models.workerTrainingCategories.findAll({
order: [
["seq", "ASC"]
]
});
order: [['seq', 'ASC']],
});

res.send({
trainingCategories: trainingCategoriesJSON(results)
trainingCategories: transformTrainingCategories(results),
});
} catch (err) {
console.error(err);
return res.status(503).send();
}
});
};

const getTrainingByCategory = async (req, res) => {
try {
const establishmentId = req.params.establishmentId;

function trainingCategoriesJSON(givenCategories){
let categories=[];
let establishment = await models.establishment.findWithWorkersAndTraining(establishmentId);
if (!establishment) {
return res.sendStatus(404);
}

//Go through any results found from DB and map to JSON
givenCategories.forEach(thisCategory => {
categories.push({
id: thisCategory.id,
seq: thisCategory.seq,
category: thisCategory.category,
});
});
let trainingCategories = await models.workerTrainingCategories.findAllWithMandatoryTraining(establishmentId);

return categories;
res.json({
trainingCategories: transformTrainingCategoriesWithMandatoryTraining(establishment, trainingCategories),
});
} catch (err) {
console.error(err);
return res.status(503).send();
}
};

router.route('/').get([refCacheMiddleware.refcache, getAllTraining]);
router.route('/:establishmentId/with-training').get([cacheMiddleware.nocache, getTrainingByCategory]);

module.exports = router;
module.exports.getTrainingByCategory = getTrainingByCategory;
Loading

0 comments on commit ac8087f

Please sign in to comment.