diff --git a/packages/component-helper-service/src/services/Collection.js b/packages/component-helper-service/src/services/Collection.js index 741461e4ef2b933dbd03a8bcf952f2f34bf2cd9b..857f5859a86e3d37aa985b247413ad7d179d0ca7 100644 --- a/packages/component-helper-service/src/services/Collection.js +++ b/packages/component-helper-service/src/services/Collection.js @@ -1,7 +1,19 @@ const config = require('config') -const { findLast, isEmpty, maxBy, get, flatMap, last } = require('lodash') +const { v4 } = require('uuid') +const logger = require('@pubsweet/logger') -const { features = {}, recommendations: configRecommendations } = config +const { + findLast, + isEmpty, + maxBy, + get, + flatMap, + last, + has, + set, +} = require('lodash') + +// const { features = {}, recommendations: configRecommendations } = config const Fragment = require('./Fragment') @@ -176,6 +188,79 @@ class Collection { isLatestVersion(fragmentId) { return last(this.collection.fragments) === fragmentId } + + hasEQA() { + const technicalChecks = get(this.collection, 'technicalChecks', {}) + return has(technicalChecks, 'eqa') + } + + async setTechnicalChecks() { + set(this.collection, 'technicalChecks.token', v4()) + set(this.collection, 'technicalChecks.eqa', false) + await this.collection.save() + } + + async sendToMTS({ FragmentModel, UserModel, fragmentHelper }) { + await Promise.all( + this.collection.fragments.map(async fragmentId => { + const fragment = await FragmentModel.find(fragmentId) + + let fragmentUsers = [] + try { + fragmentUsers = await fragmentHelper.getReviewersAndEditorsData({ + collection: this.collection, + UserModel, + }) + + await sendMTSPackage({ + collection: this.collection, + fragment, + isEQA: true, + fragmentUsers, + }) + } catch (e) { + logger.error(e) + } + }), + ).catch(e => { + throw new Error('Something went wrong.') + }) + } + + async removeTechnicalChecks() { + this.collection.technicalChecks = {} + await this.collection.save() + } +} + +const sendMTSPackage = async ({ + fragment, + collection, + isEQA = false, + fragmentUsers = [], +}) => { + const s3Config = get(config, 'pubsweet-component-aws-s3', {}) + const mtsConfig = get(config, 'mts-service', {}) + const { sendPackage } = require('pubsweet-component-mts-package') + + const { journal, xmlParser, ftp } = mtsConfig + const packageFragment = { + ...fragment, + metadata: { + ...fragment.metadata, + customId: collection.customId, + }, + } + + await sendPackage({ + isEQA, + s3Config, + fragmentUsers, + ftpConfig: ftp, + config: journal, + options: xmlParser, + fragment: packageFragment, + }) } module.exports = Collection diff --git a/packages/component-helper-service/src/services/Fragment.js b/packages/component-helper-service/src/services/Fragment.js index c95fce7fec085e78da45654a08d795692db65ff7..83eec9b921ada1c16cd7894253d3fb736faf0e9d 100644 --- a/packages/component-helper-service/src/services/Fragment.js +++ b/packages/component-helper-service/src/services/Fragment.js @@ -1,4 +1,5 @@ const { get, remove, findLast } = require('lodash') + const config = require('config') const User = require('./User') @@ -222,6 +223,13 @@ class Fragment { return revAndEditorData } + + async addRecommendation(newRecommendation) { + this.fragment.recommendations = this.fragment.recommendations || [] + + this.fragment.recommendations.push(newRecommendation) + await this.fragment.save() + } } module.exports = Fragment diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js index 5f2bfbed5cc51996469d6191be2a3b8352d6bf74..f999d4e66b74f6d5504efe87ec4a7dfb84d72ad0 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/post.js @@ -1,17 +1,6 @@ -const uuid = require('uuid') -const { - pick, - get, - set, - has, - isEmpty, - last, - chain, - findLast, -} = require('lodash') +const { pick, isEmpty, chain, findLast } = require('lodash') const config = require('config') const { v4 } = require('uuid') -const logger = require('@pubsweet/logger') const { services, @@ -20,10 +9,13 @@ const { authsome: authsomeHelper, } = require('pubsweet-component-helper-service') -const { features = {}, recommendations } = config +const { recommendations } = config const Notification = require('../../notifications/notification') const publishAsHE = require('./strategies/hePublish') +const publishAsEiC = require('./strategies/eicPublish') +const rejectAsEiC = require('./strategies/eicReject') +const returnToHE = require('./strategies/eicReturnToHE') module.exports = models => async (req, res) => { const { recommendation, comments, recommendationType } = req.body @@ -130,47 +122,61 @@ module.exports = models => async (req, res) => { } } + let role = '' + switch (recommendationType) { + case 'review': + role = 'reviewer' + break + case 'editorRecommendation': + role = isEditorInChief ? 'eic' : 'he' + break + default: + return res.status(400).json({ + error: `Recommendation ${recommendation} is not defined.`, + }) + } + + const newRecommendation = { + id: v4(), + userId: reqUser.id, + createdOn: Date.now(), + updatedOn: Date.now(), + recommendationType, + recommendation, + comments: comments || [], + } + + const notification = new Notification({ + fragment, + collection, + newRecommendation, + UserModel: models.User, + baseUrl: services.getBaseUrl(req), + }) + const strategies = { he: { publish: publishAsHE, }, + eic: { + publish: publishAsEiC, + reject: rejectAsEiC, + 'return-to-handling-editor': returnToHE, + }, } - const role = 'he' + try { strategies[role][recommendation].execute({ - collectionHelper, + models, fragments, + notification, fragmentHelper, + collectionHelper, }) } catch (e) { return res.status(400).json({ error: e.message }) } - // if ( - // recommendation === recommendations.publish && - // recommendationType === recommendations.type.editor && - // collection.handlingEditor && - // collection.handlingEditor.id === req.user - // ) { - // if (!collectionHelper.canHEMakeRecommendation(fragments, fragmentHelper)) { - // return res.status(400).json({ - // error: 'Cannot publish without at least one reviewer report.', - // }) - // } - // } - - fragment.recommendations = fragment.recommendations || [] - const newRecommendation = { - id: uuid.v4(), - userId: reqUser.id, - createdOn: Date.now(), - updatedOn: Date.now(), - recommendationType, - } - - // newRecommendation.recommendation = recommendation || undefined - newRecommendation.comments = comments || [] - if (recommendationType === 'editorRecommendation') { await collectionHelper.updateStatusOnRecommendation({ isEditorInChief, @@ -181,102 +187,15 @@ module.exports = models => async (req, res) => { fragment.revision = pick(fragment, ['authors', 'files', 'metadata']) } - const technicalChecks = get(collection, 'technicalChecks', {}) - const hasEQA = has(technicalChecks, 'eqa') - // the manuscript has not yet passed through the EQA process so we need to upload it to the FTP server - if (isEditorInChief && recommendation === 'publish' && !hasEQA) { - if (features.mts) { - await Promise.all( - collection.fragments.map(async fragmentId => { - const fragment = await models.Fragment.find(fragmentId) - const fragmentHelper = new Fragment({ fragment }) - - let fragmentUsers = [] - try { - fragmentUsers = await fragmentHelper.getReviewersAndEditorsData({ - collection, - UserModel: models.User, - }) - - await sendMTSPackage({ - collection, - fragment, - isEQA: true, - fragmentUsers, - }) - } catch (e) { - logger.error(e) - } - }), - ).catch(e => - res.status(500).json({ - error: 'Something went wrong.', - }), - ) - } - - collection.status = 'inQA' - set(collection, 'technicalChecks.token', v4()) - set(collection, 'technicalChecks.eqa', false) - await collection.save() - } - - /* if the EiC returns the manuscript to the HE after the EQA has been performed - then remove all properties from the technicalChecks property so that the manuscript - can go through the EQA process again - */ - if ( - isEditorInChief && - recommendation === 'return-to-handling-editor' && - hasEQA - ) { - collection.technicalChecks = {} - await collection.save() - } - - const notification = new Notification({ - fragment, - collection, - newRecommendation, - UserModel: models.User, - baseUrl: services.getBaseUrl(req), - }) - const hasPeerReview = !isEmpty(collection.handlingEditor) - if (isEditorInChief) { - if (recommendation === 'publish' && collection.status === 'inQA') { - notification.notifyEAWhenEiCRequestsEQAApproval() - } - - if (recommendation === 'publish' && collection.status === 'accepted') { - notification.notifyEAWhenEiCMakesFinalDecision() - } - - if (hasPeerReview && (recommendation !== 'publish' || hasEQA)) { - if (recommendation === 'return-to-handling-editor') { - notification.notifyHEWhenEiCReturnsToHE() - } else { - notification.notifyHEWhenEiCMakesDecision() - notification.notifyReviewersWhenEiCMakesDecision() - } - } - - if ( - recommendation !== 'return-to-handling-editor' && - (recommendation !== 'publish' || hasEQA) - ) { - notification.notifyAuthorsWhenEiCMakesDecision() - } - } else { - if (collection.status === 'revisionRequested') { - notification.notifySAWhenHERequestsRevision() - } + if (collection.status === 'revisionRequested') { + notification.notifySAWhenHERequestsRevision() + } - if (hasPeerReview) { - notification.notifyReviewersWhenHEMakesRecommendation() - notification.notifyEiCWhenHEMakesRecommendation() - } + if (hasPeerReview) { + notification.notifyReviewersWhenHEMakesRecommendation() + notification.notifyEiCWhenHEMakesRecommendation() } } @@ -285,33 +204,3 @@ module.exports = models => async (req, res) => { return res.status(200).json(newRecommendation) } - -const sendMTSPackage = async ({ - fragment, - collection, - isEQA = false, - fragmentUsers = [], -}) => { - const s3Config = get(config, 'pubsweet-component-aws-s3', {}) - const mtsConfig = get(config, 'mts-service', {}) - const { sendPackage } = require('pubsweet-component-mts-package') - - const { journal, xmlParser, ftp } = mtsConfig - const packageFragment = { - ...fragment, - metadata: { - ...fragment.metadata, - customId: collection.customId, - }, - } - - await sendPackage({ - isEQA, - s3Config, - fragmentUsers, - ftpConfig: ftp, - config: journal, - options: xmlParser, - fragment: packageFragment, - }) -} diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/eicPublish.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/eicPublish.js index 27c02f47892b24bcad11ee63a977d8a024f1eaf5..204815c5c9c7bb8d0080bb2afbe03fde82b14faf 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/eicPublish.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/eicPublish.js @@ -1,3 +1,38 @@ +const config = require('config') + +const { features = {} } = config + module.exports = { - execute: ({ fragment, collection, recommendation }) => {}, + execute: async ({ + models, + notification, + fragmentHelper, + collectionHelper, + newRecommendation, + }) => { + await fragmentHelper.addRecommendation(newRecommendation) + + let newStatus = '' + if (collectionHelper.hasEQA()) { + newStatus = 'accepted' + notification.notifyEAWhenEiCMakesFinalDecision() + notification.notifyAuthorsWhenEiCMakesDecision() + notification.notifyHEWhenEiCMakesDecision() + notification.notifyReviewersWhenEiCMakesDecision() + } else { + if (features.mts) { + await collectionHelper.sendToMTS({ + fragmentHelper, + UserModel: models.User, + FragmentModel: models.Fragment, + }) + } + + newStatus = 'inQA' + await collectionHelper.setTechnicalChecks() + notification.notifyEAWhenEiCRequestsEQAApproval() + } + + await collectionHelper.updateStatus({ newStatus }) + }, } diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/eicReject.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/eicReject.js new file mode 100644 index 0000000000000000000000000000000000000000..e7f2201919916d8e69e1ca4698cc7d6775cd9263 --- /dev/null +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/eicReject.js @@ -0,0 +1,15 @@ +module.exports = { + execute: async ({ + notification, + fragmentHelper, + collectionHelper, + newRecommendation, + }) => { + await fragmentHelper.addRecommendation(newRecommendation) + await collectionHelper.updateStatus({ newStatus: 'rejected' }) + + notification.notifyAuthorsWhenEiCMakesDecision() + notification.notifyHEWhenEiCMakesDecision() + notification.notifyReviewersWhenEiCMakesDecision() + }, +} diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/eicReturnToHE.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/eicReturnToHE.js new file mode 100644 index 0000000000000000000000000000000000000000..34561326b8857efe49a17ab2e0592830f9ada96b --- /dev/null +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/eicReturnToHE.js @@ -0,0 +1,17 @@ +module.exports = { + execute: async ({ + notification, + fragmentHelper, + collectionHelper, + newRecommendation, + }) => { + if (collectionHelper.hasEQA()) { + await collectionHelper.removeTechnicalChecks() + } + await collectionHelper.updateStatus({ newStatus: 'reviewCompleted' }) + + await fragmentHelper.addRecommendation(newRecommendation) + + notification.notifyHEWhenEiCReturnsToHE() + }, +} diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/hePublish.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/hePublish.js index 0decbde6373f03ea6300c13c730085530ff520be..e90e1c747251c93d84af16054fb5bc5dd80715c0 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/hePublish.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/strategies/hePublish.js @@ -1,7 +1,19 @@ module.exports = { - execute: ({ collectionHelper, fragments, fragmentHelper }) => { + execute: async ({ + collectionHelper, + fragments, + fragmentHelper, + notification, + newRecommendation, + }) => { if (!collectionHelper.canHEMakeRecommendation(fragments, fragmentHelper)) { throw new Error('Cannot publish without at least one reviewer report.') } + + await fragmentHelper.addRecommendation(newRecommendation) + await collectionHelper.updateStatus({ newStatus: 'pendingApproval' }) + + notification.notifyReviewersWhenHEMakesRecommendation() + notification.notifyEiCWhenHEMakesRecommendation() }, }