Newer
Older

Anca Ursachi
committed
/* eslint-disable no-return-await */
const uuid = require('uuid')
const { pick, get, set, has, isEmpty, last } = require('lodash')
const config = require('config')
const { v4 } = require('uuid')
const logger = require('@pubsweet/logger')
const {
services,

Sebastian Mihalache
committed
Fragment,

Sebastian Mihalache
committed
authsome: authsomeHelper,
} = require('pubsweet-component-helper-service')

Sebastian Mihalache
committed
const { features = {}, recommendations } = config
const Notification = require('../../notifications/notification')
module.exports = models => async (req, res) => {
const { recommendation, comments, recommendationType } = req.body
if (!services.checkForUndefinedParams(recommendationType))
return res.status(400).json({ error: 'Recommendation type is required.' })
const reqUser = await models.User.find(req.user)
const isEditorInChief = reqUser.editorInChief || reqUser.admin
const { collectionId, fragmentId } = req.params

Anca Ursachi
committed
let collection, fragment, fragments
try {
collection = await models.Collection.find(collectionId)
if (!collection.fragments.includes(fragmentId))
return res.status(400).json({
})
fragment = await models.Fragment.find(fragmentId)
} catch (e) {
const notFoundError = await services.handleNotFoundError(e, 'Item')
return res.status(notFoundError.status).json({
error: notFoundError.message,
})
}
Andrei Cioromila
committed
const collectionHelper = new Collection({ collection })

Anca Ursachi
committed
try {
fragments = await Promise.all(
collection.fragments.map(
async fragment => await models.Fragment.find(fragment),
),
)
} catch (e) {
const notFoundError = await services.handleNotFoundError(e, 'Item')
fragments = []
return res.status(notFoundError.status).json({
error: notFoundError.message,
})
}

Tania Fecheta
committed
const currentUserRecommendation = get(fragment, 'recommendations', []).filter(
r => r.userId === req.user,
)

Tania Fecheta
committed
const authsome = authsomeHelper.getAuthsome(models)
const target = {
path: req.route.path,
}
const canPost = await authsome.can(req.user, 'POST', target)
if (!canPost)
return res.status(403).json({
error: 'Unauthorized.',
})
const fragmentHelper = new Fragment({ fragment })

Sebastian Mihalache
committed
if (
recommendationType === recommendations.type.editor &&
last(collection.fragments) !== fragmentId
) {
return res
.status(400)
.json({ error: 'Cannot make a recommendation on an older version.' })
}

Tania Fecheta
committed
if (
recommendationType === recommendations.type.review &&
last(collection.fragments) !== fragmentId
) {
return res
.status(400)
.json({ error: 'Cannot write a review on an older version.' })
}
if (

Tania Fecheta
committed
last(collection.fragments) === fragmentId &&

Tania Fecheta
committed
!isEmpty(currentUserRecommendation)

Sebastian Mihalache
committed
) {

Tania Fecheta
committed
if (recommendationType === recommendations.type.review) {
return res
.status(400)
.json({ error: 'Cannot write another review on this version.' })

Sebastian Mihalache
committed
}

Tania Fecheta
committed
return res
.status(400)

Tania Fecheta
committed
.json({ error: 'Cannot make another recommendation on this version.' })

Sebastian Mihalache
committed
}
if (
recommendation === recommendations.publish &&

Sebastian Mihalache
committed
recommendationType === recommendations.type.editor &&
collection.handlingEditor &&
collection.handlingEditor.id === req.user
) {
Andrei Cioromila
committed
if (!collectionHelper.canHEMakeRecommendation(fragments, fragmentHelper)) {
return res.status(400).json({
error: 'Cannot publish without at least one reviewer report.',
})

Sebastian Mihalache
committed
}
}
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 || undefined
if (recommendationType === 'editorRecommendation') {
collectionHelper.updateStatusOnRecommendation({
isEditorInChief,
recommendation,
})
if (!isEditorInChief && ['minor', 'major'].includes(recommendation)) {
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) {
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
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' &&
) {
collection.technicalChecks = {}
await collection.save()
}
Andrei Cioromila
committed
const notification = new Notification({
fragment,
collection,
newRecommendation,
UserModel: models.User,
baseUrl: services.getBaseUrl(req),
Andrei Cioromila
committed
const hasPeerReview = !isEmpty(collection.handlingEditor)
if (isEditorInChief) {
if (recommendation === 'publish' && collection.status === 'inQA') {
notification.notifyEAWhenEiCRequestsEQAApproval()
}
if (recommendation === 'publish' && collection.status === 'accepted') {
notification.notifyEAWhenEiCMakesFinalDecision()
Andrei Cioromila
committed
if (hasPeerReview && (recommendation !== 'publish' || hasEQA)) {
if (recommendation === 'return-to-handling-editor') {
notification.notifyHEWhenEiCReturnsToHE()
} else {
notification.notifyHEWhenEiCMakesDecision()
notification.notifyReviewersWhenEiCMakesDecision()
}
}
Andrei Cioromila
committed
if (
recommendation !== 'return-to-handling-editor' &&
(recommendation !== 'publish' || hasEQA)
) {
notification.notifyAuthorsWhenEiCMakesDecision()
} else {
if (collection.status === 'revisionRequested') {
notification.notifySAWhenHERequestsRevision()
}
Andrei Cioromila
committed
Andrei Cioromila
committed
if (hasPeerReview) {
notification.notifyReviewersWhenHEMakesRecommendation()
Andrei Cioromila
committed
notification.notifyEiCWhenHEMakesRecommendation()
}
}
fragment.recommendations.push(newRecommendation)
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,
})
}