const { get, remove, findLast, pick, chain, omit } = require('lodash') const config = require('config') const User = require('./User') const { recommendations: configRecommendations } = config class Fragment { constructor({ fragment }) { this.fragment = fragment } set _fragment(newFragment) { this.fragment = newFragment } static setFragmentOwners(fragment = {}, author = {}) { const { owners = [] } = fragment if (author.isSubmitting) { const authorAlreadyOwner = owners.includes(author.id) if (!authorAlreadyOwner) { return [author.id, ...owners] } } return owners } async getFragmentData({ handlingEditor } = {}) { const { fragment: { metadata = {}, recommendations = [], id } } = this let heRecommendation if (handlingEditor) { heRecommendation = recommendations.find( rec => rec.userId === handlingEditor.id, ) } let { title = '', abstract = '' } = metadata const { type } = metadata title = title.replace(/<(.|\n)*?>/g, '') abstract = abstract ? abstract.replace(/<(.|\n)*?>/g, '') : '' return { id, type, title, abstract, recommendations, heRecommendation, } } async addAuthor({ user, isSubmitting, isCorresponding }) { const { fragment } = this fragment.authors = fragment.authors || [] const author = { id: user.id, firstName: user.firstName || '', lastName: user.lastName || '', email: user.email, title: user.title || '', affiliation: user.affiliation || '', country: user.country || '', isSubmitting, isCorresponding, } fragment.authors.push(author) fragment.owners = this.constructor.setFragmentOwners(fragment, author) await fragment.save() return author } async getAuthorData({ UserModel }) { const { fragment: { authors = [] } } = this const submittingAuthorData = authors.find(author => author.isSubmitting) try { const submittingAuthor = await UserModel.find( get(submittingAuthorData, 'id'), ) const userHelper = new User({ UserModel }) const activeAuthors = await userHelper.getActiveAuthors({ fragmentAuthors: authors, }) return { activeAuthors, submittingAuthor, } } catch (e) { throw e } } getInvitations({ isAccepted = true, role = 'reviewer', type }) { const { fragment: { invitations = [], recommendations = [] } } = this const filteredInvitations = isAccepted ? invitations.filter( inv => inv.role === role && inv.hasAnswer && inv.isAccepted, ) : invitations.filter(inv => inv.role === role && !inv.hasAnswer) if (type === 'submitted') { return filteredInvitations.filter(inv => recommendations.find( rec => rec.recommendationType === 'review' && rec.submittedOn && inv.userId === rec.userId, ), ) } else if (type === 'accepted') { recommendations.forEach(rec => { if (rec.recommendationType === 'review' && rec.submittedOn) { remove(filteredInvitations, inv => inv.userId === rec.userId) } }) } return filteredInvitations } getLatestHERequestToRevision() { const { fragment: { recommendations = [] } } = this return findLast( recommendations, rec => rec.recommendationType === 'editorRecommendation' && (rec.recommendation === 'minor' || rec.recommendation === 'major'), ) } async getReviewers({ UserModel, type }) { const isAccepted = type !== 'pending' const invitations = await this.getInvitations({ isAccepted, type }) return (await Promise.all( invitations.map(inv => UserModel.find(inv.userId)), )) .filter(rev => rev.isActive) .filter(rev => get(rev, 'notifications.email.user')) } hasReviewReport() { const { fragment: { recommendations = [] } } = this return recommendations.some( rec => rec.recommendationType === 'review' && rec.submittedOn, ) } canHEMakeAnotherRecommendation(lastHERecommendation) { const { fragment: { recommendations = [] } } = this const returnToHERecommendation = findLast( recommendations, r => r.recommendation === 'return-to-handling-editor', ) if (!returnToHERecommendation) return false return returnToHERecommendation.createdOn > lastHERecommendation.createdOn } async getReviewersAndEditorsData({ collection, UserModel }) { const { invitations = [], recommendations = [], submitted = Date.now(), } = this.fragment const revAndEditorData = await Promise.all( recommendations .filter( rec => rec.recommendationType === configRecommendations.type.editor || (rec.recommendationType === configRecommendations.type.review && rec.submittedOn), ) .map(async rec => { const user = await UserModel.find(rec.userId) let assignmentDate, isReviewer if (rec.recommendationType === configRecommendations.type.editor) { if (!collection.handlingEditor) { throw new Error( `Collection ${collection.id} does not have a Handling Editor`, ) } if (user.id === collection.handlingEditor.id) { const editorInvitation = collection.invitations.find( inv => inv.userId === user.id, ) assignmentDate = editorInvitation.respondedOn } else { assignmentDate = submitted } isReviewer = false } else { const reviewerInvitation = invitations.find( inv => inv.userId === user.id, ) assignmentDate = reviewerInvitation.respondedOn isReviewer = true } return { isReviewer, assignmentDate, email: user.email, title: user.title, recommendation: rec, country: user.country, lastName: user.lastName, firstName: user.firstName, affiliation: user.affiliation, submissionDate: rec.createdOn, } }), ) return revAndEditorData } async addRecommendation(newRecommendation) { this.fragment.recommendations = this.fragment.recommendations || [] this.fragment.recommendations.push(newRecommendation) await this.fragment.save() } async addRevision() { this.fragment.revision = pick(this.fragment, [ 'authors', 'files', 'metadata', ]) await this.fragment.save() } hasReviewers() { const { fragment: invitations = [] } = this return invitations.length > 0 } getLatestUserRecommendation(userId) { return findLast(this.fragment.recommendations, r => r.userId === userId) } getLatestRecommendation() { return chain(this.fragment) .get('recommendations', []) .last() .value() } async createFragmentFromRevision(FragmentModel) { const newFragmentBody = { ...omit(this.fragment, ['revision', 'recommendations', 'id']), ...this.fragment.revision, invitations: this.getInvitations({ isAccepted: true, type: 'submitted', }), version: this.fragment.version + 1, created: new Date(), } let newFragment = new FragmentModel(newFragmentBody) newFragment = await newFragment.save() return newFragment } async removeRevision() { delete this.fragment.revision await this.fragment.save() } getLatestEiCRequestToRevision() { const { fragment: { recommendations = [] } } = this return findLast( recommendations, rec => rec.recommendationType === 'editorRecommendation' && rec.recommendation === 'revision', ) } } module.exports = Fragment