diff --git a/packages/component-faraday-selectors/src/index.js b/packages/component-faraday-selectors/src/index.js index 6508a91a4921237406bd5b9e386e167f1e608977..82d6b27cecc05dff19ac1f018acf05305e125fde 100644 --- a/packages/component-faraday-selectors/src/index.js +++ b/packages/component-faraday-selectors/src/index.js @@ -7,7 +7,11 @@ export const isHEToManuscript = (state, collectionId) => { return get(collection, 'handlingEditor.id') === currentUserId } -const canMakeRecommendationStatuses = ['reviewCompleted', 'heAssigned'] +const canMakeRecommendationStatuses = [ + 'reviewCompleted', + 'heAssigned', + 'underReview', +] export const canMakeRecommendation = (state, collection, fragment = {}) => { if (fragment.id !== last(collection.fragments)) return false const isHE = isHEToManuscript(state, collection.id) diff --git a/packages/component-helper-service/src/services/User.js b/packages/component-helper-service/src/services/User.js index 2eb092ceeb1220254e93270c9ef2f8dc2876f182..39d03896853ea00f5bb81425bdda1bb551f7af7f 100644 --- a/packages/component-helper-service/src/services/User.js +++ b/packages/component-helper-service/src/services/User.js @@ -45,7 +45,12 @@ class User { const { UserModel } = this const users = await UserModel.all() - return users.filter(user => user.editorInChief) + const eics = users.filter(user => user.editorInChief) + if (eics.length === 0) { + throw new Error('No Editor in Chief has been found') + } + + return eics } async updateUserTeams({ userId, teamId }) { diff --git a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js index b318595833c0efbf0f46f5b9ff37ad292b504677..b3f2e877a4aad364f605e362495d370193328932 100644 --- a/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js +++ b/packages/component-manuscript-manager/src/routes/fragmentsRecommendations/notifications/notifications.js @@ -1,5 +1,5 @@ const config = require('config') -const { chain, get } = require('lodash') +const { chain, get, isEmpty } = require('lodash') const { User, @@ -58,9 +58,13 @@ module.exports = { comments = eicComments } + + const hasPeerReview = (collection = {}) => + !isEmpty(collection.handlingEditor) + if ( (isEditorInChief || newRecommendation.recommendationType === 'review') && - collection.status !== 'rejected' + hasPeerReview(collection) ) { // the request came from either the Editor in Chief or a reviewer, so the HE needs to be notified sendHandlingEditorEmail({ @@ -81,17 +85,19 @@ module.exports = { newRecommendation.recommendationType !== 'review' && newRecommendation.recommendation !== 'return-to-handling-editor' ) { - sendAuthorsEmail({ - email, - baseUrl, - titleText, - parsedFragment, - fragmentAuthors, - isEditorInChief, - subjectBaseText, - newRecommendation, - }) - if (collection.status !== 'rejected') { + if (isEditorInChief || collection.status === 'revisionRequested') { + sendAuthorsEmail({ + email, + baseUrl, + titleText, + parsedFragment, + fragmentAuthors, + isEditorInChief, + subjectBaseText, + newRecommendation, + }) + } + if (hasPeerReview(collection)) { sendReviewersEmail({ email, baseUrl, diff --git a/packages/component-manuscript/src/components/SideBarActions.js b/packages/component-manuscript/src/components/SideBarActions.js index dbfc3d7cd8c6bcbd6f7ce6a70176913185514c9a..4ab282febce17b1393ae399c75a1153df3fa64cb 100644 --- a/packages/component-manuscript/src/components/SideBarActions.js +++ b/packages/component-manuscript/src/components/SideBarActions.js @@ -40,6 +40,7 @@ const SideBarActions = ({ collectionId={project.id} fragmentId={version.id} modalKey={`decide-${version.id}`} + status={project.status} /> )} diff --git a/packages/component-mts-package/readme.md b/packages/component-mts-package/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..45347c011043112bece9ecd1a88f0fe9bf233775 --- /dev/null +++ b/packages/component-mts-package/readme.md @@ -0,0 +1,45 @@ +## MTS service integration + +This component is running as a service to integrate current Hindawi Manuscript Tracking System for Editorial Quality Screening and for Editorial Quality Assurance. + +### Use-case + +As an Editor in Chief, I want the manuscript to go through Editorial Quality Screening before I assign a Handling Editor to it and start the review process. + +### Workflow + +Stage 1: When an article is submitted, Faraday needs to convert the submission to XML JATS format, and upload as a .zip file to a FTP server. + +Stage 2: When new .zip file has been detected on FTP server, then third party system MTS (Manuscript Tracking System - Hindawi) needs to consume package and populate DB records. + +Stage 3: When check is completed, then Faraday system needs to be updated to move status from Technical Checks to Submitted - which allows the Editor in Chief to assign a Handling Editor. + + + +### Configuration +| Params | Required | Description | +| ------------- |:-------------:| -----| +| journalConfig | Yes | Journal configuration in .xml file as: doctype, dtdVersion, journalTitle, articleType, journal email, journalIdPublisher, issn | +| xmlParserOptions | No | parsing config options used by xml-js library | +| s3Config | Yes | Access to AWS S3 Bucket where fragment files are saved | +| FTPConfig | Yes | FTP server connection credentials | + + +### Usage + +MTS service gathers all the fragment files, creates an .xml file in a specific JATS format out of fragment JSON, creates a .zip archive and sends it to a specific FTP server with a configurable package name (also a backup it's uploaded to AWS S3). + +.xml structure of Hindawi use-case can be checked `/tests/sample.xml` from generated json `/tests/sample.json`. + +#### Example + +```javascript +const fragment = {} //fragment json here +const { journalConfig, xmlParser, s3Config, ftpConfig } = config //import your config +const MTS = new MTSService(journalConfig, xmlParser, s3Config, ftpConfig) +MTS.sendPackage({ fragment }) +``` + +[GIFs Demo](https://gitlab.coko.foundation/xpub/xpub-faraday/wikis/mts-integration) + + diff --git a/packages/component-mts-package/tests/sample.json b/packages/component-mts-package/tests/sample.json index 21ccc5f10edeed4f9c776ff30bc1bb0fd58e6053..17f672cb1edf947a633dac1beb4818bb08662dbc 100644 --- a/packages/component-mts-package/tests/sample.json +++ b/packages/component-mts-package/tests/sample.json @@ -24,12 +24,12 @@ "_attributes": { "journal-id-type": "email" }, - "_text": "trashjo@ariessys.com" + "_text": "faraday@hindawi.com" } ], "journal-title-group": { "journal-title": { - "_text": "Research" + "_text": "Bioinorganic Chemistry and Applications" } }, "issn": [ @@ -37,7 +37,7 @@ "_attributes": { "pub-type": "ppub" }, - "_text": "0000-000Y" + "_text": "2474-7394" }, { "_attributes": { @@ -52,60 +52,28 @@ "_attributes": { "pub-id-type": "publisher-id" }, - "_text": "RESEARCH-D-18-00005" + "_text": "RESEARCH-F-3326913" }, { "_attributes": { "pub-id-type": "manuscript" }, - "_text": "RESEARCH-D-18-00005" + "_text": "RESEARCH-F-3326913" } ], "article-categories": { - "subj-group": [ - { - "_attributes": { - "subj-group-type": "Article Type" - }, - "subject": { - "_text": "Research Article" - } - }, - { - "_attributes": { - "subj-group-type": "Category" - }, - "subject": { - "_text": "Information science" - } - }, - { - "_attributes": { - "subj-group-type": "Classification" - }, - "subject": { - "_text": "Applied sciences and engineering" - } + "subj-group": { + "_attributes": { + "subj-group-type": "Article Type" }, - { - "_attributes": { - "subj-group-type": "Classification" - }, - "subject": { - "_text": "Scientific community" - } + "subject": { + "_text": "clinical-study" } - ] + } }, "title-group": { "article-title": { - "_text": "January 24 sample article with new email trigger in place" - }, - "alt-title": { - "_attributes": { - "alt-title-type": "running-head" - }, - "_text": "let's hope this works" + "_text": "Demo sprint 16 no fun" } }, "contrib-group": { @@ -121,17 +89,17 @@ }, "name": { "surname": { - "_text": "Heckner" + "_text": "Raluca" }, "given-names": { - "_text": "Hannah" + "_text": "Authorescu" }, "prefix": { - "_text": "Ms." + "_text": "miss" } }, "email": { - "_text": "hheckner@aaas.org" + "_text": "raluca.gramschi+auth@thinslices.com" }, "xref": { "_attributes": { @@ -144,8 +112,9 @@ "_attributes": { "id": "aff1" }, - "country": { - "_text": "UNITED STATES" + "country": {}, + "addr-line": { + "_text": "Technical University Gheorghe Asachi Iasi" } } }, @@ -155,10 +124,10 @@ "date-type": "received" }, "day": { - "_text": "24" + "_text": "3" }, "month": { - "_text": "01" + "_text": "8" }, "year": { "_text": "2018" @@ -166,78 +135,58 @@ } }, "abstract": { - "p": { - "_text": "Abstract\nThis article explains and illustrates the use of LATEX in preparing manuscripts for submission to the American Journal of Physics (AJP). While it is not a comprehensive reference, we hope it will suffice for the needs of most AJP authors." - } + "_text": "<p>some abstract here</p>" }, - "kwd-group": { - "kwd": [ - { - "_text": "manuscript" + "funding-group": {} + }, + "files": { + "file": [ + { + "item_type": { + "_text": "coverLetter" }, - { - "_text": "submissions" + "item_description": { + "_text": "sample cover letter_ms 1.doc" }, - { - "_text": "ftp site" - } - ] - }, - "funding-group": {}, - "counts": { - "fig-count": { - "_attributes": { - "count": "0" + "item_name": { + "_text": "sample cover letter_ms 1.doc" } - } - }, - "custom-meta-group": { - "custom-meta": [ - { - "meta-name": { - "_text": "Black and White Image Count" - }, - "meta-value": { - "_text": "0" - } + }, + { + "item_type": { + "_text": "manuscripts" }, - { - "meta-name": { - "_text": "Color Image Count" - }, - "meta-value": { - "_text": "0" - } + "item_description": { + "_text": "manuscript.pdf" + }, + "item_name": { + "_text": "manuscript.pdf" } - ] - } - } - }, - "body": { - "fig": [ - { - "label": { - "_text": "Figure 1" }, - "graphic": { - "_attributes": { - "xlink:href": "GasBulbData.eps", - "xmlns:xlink": "http://www.w3.org/1999/xlink" + { + "item_type": { + "_text": "supplementary" + }, + "item_description": { + "_text": "important-emails.md" + }, + "item_name": { + "_text": "important-emails.md" } - } - }, - { - "label": { - "_text": "Figure 2" }, - "graphic": { - "_attributes": { - "xlink:href": "ThreeSunsets.jpg", - "xmlns:xlink": "http://www.w3.org/1999/xlink" + { + "item_type": { + "_text": "supplementary" + }, + "item_description": { + "_text": "important-emails.md" + }, + "item_name": { + "_text": "important-emails (1).md" } } - } - ] + ] + } } } } \ No newline at end of file diff --git a/packages/component-mts-package/tests/sample.xml b/packages/component-mts-package/tests/sample.xml index 697e0e64c760dc9f1acdda45ff087c984303906e..d91397fb44989adaffbcc7d597b457138b71b714 100644 --- a/packages/component-mts-package/tests/sample.xml +++ b/packages/component-mts-package/tests/sample.xml @@ -4,89 +4,71 @@ <front> <journal-meta> <journal-id journal-id-type="publisher">research</journal-id> - <journal-id journal-id-type="email">trashjo@ariessys.com</journal-id> + <journal-id journal-id-type="email">faraday@hindawi.com</journal-id> <journal-title-group> - <journal-title>Research</journal-title> + <journal-title>Bioinorganic Chemistry and Applications</journal-title> </journal-title-group> - <issn pub-type="ppub">0000-000Y</issn> + <issn pub-type="ppub">2474-7394</issn> <issn pub-type="epub"></issn> </journal-meta> <article-meta> - <article-id pub-id-type="publisher-id">RESEARCH-D-18-00005</article-id> - <article-id pub-id-type="manuscript">RESEARCH-D-18-00005</article-id> + <article-id pub-id-type="publisher-id">RESEARCH-F-3326913</article-id> + <article-id pub-id-type="manuscript">RESEARCH-F-3326913</article-id> <article-categories> <subj-group subj-group-type="Article Type"> - <subject>Research Article</subject> - </subj-group> - <subj-group subj-group-type="Category"> - <subject>Information science</subject> - </subj-group> - <subj-group subj-group-type="Classification"> - <subject>Applied sciences and engineering</subject> - </subj-group> - <subj-group subj-group-type="Classification"> - <subject>Scientific community</subject> + <subject>clinical-study</subject> </subj-group> </article-categories> <title-group> - <article-title>January 24 sample article with new email trigger in place</article-title> - <alt-title alt-title-type="running-head">let's hope this works</alt-title> + <article-title>Demo sprint 16 no fun</article-title> </title-group> <contrib-group> <contrib contrib-type="author" corresp="yes"> - <role content-type="1" /> + <role content-type="1"></role> <name> - <surname>Heckner</surname> - <given-names>Hannah</given-names> - <prefix>Ms.</prefix> + <surname>Raluca</surname> + <given-names>Authorescu</given-names> + <prefix>miss</prefix> </name> - <email>hheckner@aaas.org</email> - <xref ref-type="aff" rid="aff1" /> + <email>raluca.gramschi+auth@thinslices.com</email> + <xref ref-type="aff" rid="aff1"></xref> </contrib> <aff id="aff1"> - <country>UNITED STATES</country> + <country></country> + <addr-line>Technical University Gheorghe Asachi Iasi</addr-line> </aff> </contrib-group> <history> <date date-type="received"> - <day>24</day> - <month>01</month> + <day>3</day> + <month>8</month> <year>2018</year> </date> </history> - <abstract> - <p>Abstract -This article explains and illustrates the use of LATEX in preparing manuscripts for submission to the American Journal of Physics (AJP). While it is not a comprehensive reference, we hope it will suffice for the needs of most AJP authors.</p> - </abstract> - <kwd-group> - <kwd>manuscript</kwd> - <kwd>submissions</kwd> - <kwd>ftp site</kwd> - </kwd-group> - <funding-group /> - <counts> - <fig-count count="0" /> - </counts> - <custom-meta-group> - <custom-meta> - <meta-name>Black and White Image Count</meta-name> - <meta-value>0</meta-value> - </custom-meta> - <custom-meta> - <meta-name>Color Image Count</meta-name> - <meta-value>0</meta-value> - </custom-meta> - </custom-meta-group> + <abstract><p>some abstract here</p></abstract> + <funding-group></funding-group> </article-meta> + <files> + <file> + <item_type>coverLetter</item_type> + <item_description>sample cover letter_ms 1.doc</item_description> + <item_name>sample cover letter_ms 1.doc</item_name> + </file> + <file> + <item_type>manuscripts</item_type> + <item_description>manuscript.pdf</item_description> + <item_name>manuscript.pdf</item_name> + </file> + <file> + <item_type>supplementary</item_type> + <item_description>important-emails.md</item_description> + <item_name>important-emails.md</item_name> + </file> + <file> + <item_type>supplementary</item_type> + <item_description>important-emails.md</item_description> + <item_name>important-emails (1).md</item_name> + </file> + </files> </front> - <body> - <fig> - <label>Figure 1</label> - <graphic xlink:href="GasBulbData.eps" xmlns:xlink="http://www.w3.org/1999/xlink" /> - </fig> - <fig> - <label>Figure 2</label> - <graphic xlink:href="ThreeSunsets.jpg" xmlns:xlink="http://www.w3.org/1999/xlink" /> - </fig> - </body> </article> \ No newline at end of file diff --git a/packages/component-user-manager/src/routes/fragmentsUsers/emails/emailCopy.js b/packages/component-user-manager/src/routes/fragmentsUsers/emails/emailCopy.js index d6e87cc86947dee0dd26537455c9ed458a929500..68132122b47e8b66bed920a01a9afcacc16ba8c6 100644 --- a/packages/component-user-manager/src/routes/fragmentsUsers/emails/emailCopy.js +++ b/packages/component-user-manager/src/routes/fragmentsUsers/emails/emailCopy.js @@ -1,8 +1,8 @@ const getEmailCopy = ({ emailType, titleText }) => { let paragraph switch (emailType) { - case 'co-author-added-to-manuscript': - paragraph = `You have been added as co-author to ${titleText}. The manuscript will become visible on your dashboard once it's submitted. Please click on the link below to access your dashboard.` + case 'author-added-to-manuscript': + paragraph = `You have been added as an author to ${titleText}. The manuscript will become visible on your dashboard once it's submitted. Please click on the link below to access your dashboard.` break case 'new-author-added-to-manuscript': paragraph = `You have been added as an author to ${titleText}. In order to gain access to the manuscript, please confirm your account and set your account details by clicking on the link below.` diff --git a/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js b/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js index 413ea4967f01a4244415681d2505edc1f7847618..9a7af8dbcd3819870c4d749ba3c85cd910db219b 100644 --- a/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js +++ b/packages/component-user-manager/src/routes/fragmentsUsers/emails/notifications.js @@ -9,17 +9,11 @@ const { services, Fragment, } = require('pubsweet-component-helper-service') + const { getEmailCopy } = require('./emailCopy') module.exports = { - async sendNotifications({ - user, - baseUrl, - isSubmitting, - fragment, - UserModel, - collection, - }) { + async sendNotifications({ user, baseUrl, fragment, UserModel, collection }) { const fragmentHelper = new Fragment({ fragment }) const { title } = await fragmentHelper.getFragmentData({ handlingEditor: collection.handlingEditor, @@ -44,21 +38,27 @@ module.exports = { }, }) - if (!isSubmitting) { - sendCoAuthorEmail({ email, baseUrl, user, titleText, subjectBaseText }) + if (!user.isConfirmed) { + sendNewAuthorEmail({ + email, + user, + baseUrl, + titleText, + subjectBaseText, + }) } - sendNewAuthorEmail({ + sendAddedToManuscriptEmail({ email, - user, baseUrl, + user, titleText, subjectBaseText, }) }, } -const sendCoAuthorEmail = ({ +const sendAddedToManuscriptEmail = ({ email, baseUrl, user, @@ -77,7 +77,7 @@ const sendCoAuthorEmail = ({ const { html, text } = email.getBody({ body: getEmailCopy({ - emailType: 'co-author-added-to-manuscript', + emailType: 'author-added-to-manuscript', titleText, }), }) @@ -85,13 +85,7 @@ const sendCoAuthorEmail = ({ email.sendEmail({ html, text }) } -const sendNewAuthorEmail = ({ - email, - baseUrl, - user, - titleText, - subjectBaseText, -}) => { +const sendNewAuthorEmail = ({ email, baseUrl, user, titleText }) => { email.toUser = { email: user.email, name: `${user.firstName} ${user.lastName}`, diff --git a/packages/component-user-manager/src/routes/fragmentsUsers/post.js b/packages/component-user-manager/src/routes/fragmentsUsers/post.js index 0d88c22046537c5d3c63bf08c2ac69d7d855e595..6a7546e6f665ef1b7fb295e92c9b14a88ba8a1b8 100644 --- a/packages/component-user-manager/src/routes/fragmentsUsers/post.js +++ b/packages/component-user-manager/src/routes/fragmentsUsers/post.js @@ -92,6 +92,14 @@ module.exports = models => async (req, res) => { collection.save() } + notifications.sendNotifications({ + user, + baseUrl, + fragment, + collection, + UserModel: models.User, + }) + return res.status(200).json({ ...pick(user, authorKeys), isSubmitting, @@ -128,7 +136,6 @@ module.exports = models => async (req, res) => { collection, user: newUser, UserModel: models.User, - isSubmitting, }) if (!collection.owners.includes(newUser.id)) { diff --git a/packages/components-faraday/src/components/Dashboard/DashboardCard.js b/packages/components-faraday/src/components/Dashboard/DashboardCard.js index 044a3a21e91903f72a444097b665d116eac4aff2..d0b8f03d47f45aa1f8a155754152933b970757a5 100644 --- a/packages/components-faraday/src/components/Dashboard/DashboardCard.js +++ b/packages/components-faraday/src/components/Dashboard/DashboardCard.js @@ -74,7 +74,6 @@ const DashboardCard = ({ collectionId={project.id} fragmentId={version.id} modalKey={`recommend-${version.id}`} - status={project.status} /> )} <ZipFiles diff --git a/packages/components-faraday/src/components/MakeRecommendation/RecommendWizard.js b/packages/components-faraday/src/components/MakeRecommendation/RecommendWizard.js index fb7452baf0ba87d8e6d3edb530511df05a9a4bfa..6ad740fde0799055cbc4a8684935b0e37e8b2511 100644 --- a/packages/components-faraday/src/components/MakeRecommendation/RecommendWizard.js +++ b/packages/components-faraday/src/components/MakeRecommendation/RecommendWizard.js @@ -9,7 +9,10 @@ import { getFormValues, reset as resetForm } from 'redux-form' import { FormItems } from '../UIComponents' import { StepOne, StepTwo, utils } from './' -import { createRecommendation } from '../../redux/recommendations' +import { + createRecommendation, + selectReviewRecommendations, +} from '../../redux/recommendations' const RecommendWizard = ({ step, @@ -40,8 +43,9 @@ const RecommendWizard = ({ export default compose( connect( - state => ({ + (state, { fragmentId }) => ({ decision: get(getFormValues('recommendation')(state), 'decision'), + reviews: selectReviewRecommendations(state, fragmentId), }), { resetForm, diff --git a/packages/components-faraday/src/components/MakeRecommendation/StepOne.js b/packages/components-faraday/src/components/MakeRecommendation/StepOne.js index b6723f9ff0a70dd03fb30668d2e8db606247cd66..1cb644c274d3bf7732c58e245090397fa33f2da6 100644 --- a/packages/components-faraday/src/components/MakeRecommendation/StepOne.js +++ b/packages/components-faraday/src/components/MakeRecommendation/StepOne.js @@ -1,5 +1,6 @@ import React from 'react' import { reduxForm } from 'redux-form' +import { isEmpty } from 'lodash' import { RadioGroup, ValidatedField, Button } from '@pubsweet/ui' import { utils } from './' @@ -7,7 +8,7 @@ import { FormItems } from '../UIComponents' const { Row, Title, RowItem, RootContainer, CustomRadioGroup } = FormItems -const StepOne = ({ hideModal, disabled, onSubmit, status }) => ( +const StepOne = ({ hideModal, disabled, onSubmit, reviews }) => ( <RootContainer> <Title>Recommendation for Next Phase</Title> <Row> @@ -21,7 +22,7 @@ const StepOne = ({ hideModal, disabled, onSubmit, status }) => ( <RadioGroup name="decision" options={ - status === 'reviewCompleted' + !isEmpty(reviews) ? utils.recommendationOptions : utils.recommendationOptions.slice(1) }