Skip to content
Snippets Groups Projects
Commit c16165d7 authored by Sebastian Mihalache's avatar Sebastian Mihalache
Browse files

feat(reviewer-reminders): schedule reminders and removal

parent 2f0f9b4d
No related branches found
No related tags found
3 merge requests!233S26 updates,!230S26 Updates,!214Hin 1170 automatic reminders
......@@ -20,12 +20,13 @@
},
"dependencies": {
"body-parser": "^1.17.2",
"chance": "^1.0.13"
"chance": "^1.0.13",
"moment": "^2.23.0"
},
"peerDependencies": {
"@pubsweet/component-send-email": "0.2.4",
"@pubsweet/logger": "^0.0.1",
"pubsweet-server": "^10.0.0",
"@pubsweet/component-send-email": "0.2.4"
"pubsweet-server": "^10.0.0"
},
"devDependencies": {
"apidoc": "^0.17.6",
......
const config = require('config')
const { get } = require('lodash')
const Email = require('@pubsweet/component-email-templating')
......@@ -10,13 +11,22 @@ const { services, Fragment } = require('pubsweet-component-helper-service')
const { getEmailCopy } = require('./emailCopy')
const { scheduleReminderJob } = require('../jobs/reminders')
const { scheduleRemovalJob } = require('../jobs/removals')
const daysList = [
config.get('reminders.reviewer.first'),
config.get('reminders.reviewer.second'),
config.get('reminders.reviewer.third'),
]
const timeUnit = config.get('reminders.reviewer.timeUnit')
const removalDays = config.get('reminders.reviewer.remove')
module.exports = {
async sendReviewInvitations({
resend,
baseUrl,
fragment,
UserModel,
TeamModel,
timestamp,
collection,
invitation,
......@@ -108,15 +118,25 @@ module.exports = {
bodyProps,
})
scheduleReminderJob({
email,
title,
authorName,
subject: `${subjectBaseText} reminder`,
titleText: `the manuscript titled "${title}" by ${authorName}`,
expectedDate: services.getExpectedDate({ timestamp, daysExpected: 0 }),
})
await email.sendEmail()
daysList.map(days =>
scheduleReminderJob({
days,
email,
timeUnit,
subject: `${subjectBaseText} reminder`,
titleText: `the manuscript titled "${title}" by ${authorName}`,
expectedDate: services.getExpectedDate({ timestamp, daysExpected: 0 }),
}),
)
return email.sendEmail()
scheduleRemovalJob({
timeUnit,
invitation,
days: removalDays,
fragmentId: fragment.id,
collectionId: collection.id,
})
},
}
const moment = require('moment')
const logger = require('@pubsweet/logger')
const { Team, Collection } = require('pubsweet-component-helper-service')
const {
Team: TeamModel,
User: UserModel,
Fragment: FragmentModel,
Collection: CollectionModel,
} = require('pubsweet-server')
const { jobs: { connectToJobQueue } } = require('pubsweet-server/src')
const cancelJob = async ({ name }) => {
const queue = `reviewer-removal`
const jobQueue = await connectToJobQueue()
// Subscribe to the job queue with an async handler
await jobQueue.subscribe(queue, jobHandler)
await jobQueue.onComplete(queue, job => {
console.log(job.data.response.value)
logger.info(job.data.response.value)
})
}
const moment = require('moment')
const { cloneDeep } = require('lodash')
const logger = require('@pubsweet/logger')
const { jobs: { connectToJobQueue } } = require('pubsweet-server/src')
const { getEmailCopy } = require('../emails/emailCopy')
const Email = require('@pubsweet/component-email-templating')
const scheduleReminderJob = async ({
days,
email,
subject,
timeUnit,
titleText,
expectedDate,
}) => {
const executionDate = moment()
.add(days, timeUnit)
.toISOString()
const queue = `reviewer-reminders-${days}-${executionDate}`
const jobQueue = await connectToJobQueue()
const { paragraph, ...bodyProps } = getEmailCopy({
emailType: 'reviewer-resend-invitation',
titleText,
expectedDate,
})
email.bodyProps = bodyProps
email.content.subject = subject
// Add job to the queue
const jobId = await jobQueue.publishAfter(
queue,
{
days,
timeUnit,
executionDate,
emailProps: cloneDeep(email),
},
{},
executionDate,
)
console.log(`published job ${jobId} for the ${days} ${timeUnit} reminder`)
// Subscribe to the job queue with an async handler
await jobQueue.subscribe(queue, jobHandler)
await jobQueue.onComplete(queue, job => {
console.log(job.data.response.value)
logger.info(job.data.response.value)
})
}
const jobHandler = async job => {
const { days, timeUnit, executionDate, emailProps } = job.data
const email = new Email(emailProps)
await email.sendEmail()
module.exports = {
scheduleReminderJob: async ({ email, subject, titleText, expectedDate }) => {
const jobQueue = await connectToJobQueue()
const queue = 'remind-after-4-days'
const { paragraph, ...bodyProps } = getEmailCopy({
emailType: 'reviewer-resend-invitation',
titleText,
expectedDate,
})
email.bodyProps = bodyProps
email.content.subject = subject
const minutes = 2
const currentDate = new Date()
const futureDate = new Date(currentDate.getTime() + minutes * 60000)
console.log('FUT DATE:', futureDate.toISOString())
// Add job to the queue
await jobQueue.publishAfter(queue, {}, {}, futureDate.toISOString())
// Subscribe to the job queue with an async handler
await jobQueue.subscribe(queue, async job => {
await email.sendEmail()
return `a ${subject} notification has been sent to ${email.toUser.email}`
})
await jobQueue.onComplete(queue, job => {
console.log('JOB RESPONSE', job.data.response)
})
},
return `Job ${job.id}: the ${days} ${timeUnit} reminder has been sent to ${
email.toUser.email
} at ${executionDate}`
}
module.exports = { scheduleReminderJob }
const moment = require('moment')
const logger = require('@pubsweet/logger')
const { Team, Collection } = require('pubsweet-component-helper-service')
const {
Team: TeamModel,
User: UserModel,
Fragment: FragmentModel,
Collection: CollectionModel,
} = require('pubsweet-server')
const { jobs: { connectToJobQueue } } = require('pubsweet-server/src')
const scheduleRemovalJob = async ({
days,
timeUnit,
invitation,
fragmentId,
collectionId,
}) => {
const executionDate = moment()
.add(days, timeUnit)
.toISOString()
const queue = `reviewer-removal`
const jobQueue = await connectToJobQueue()
// Add job to the queue
await jobQueue.publishAfter(
queue,
{
days,
timeUnit,
invitation,
fragmentId,
collectionId,
executionDate,
},
{},
executionDate,
)
// Subscribe to the job queue with an async handler
await jobQueue.subscribe(queue, jobHandler)
await jobQueue.onComplete(queue, job => {
console.log(job.data.response.value)
logger.info(job.data.response.value)
})
}
const jobHandler = async job => {
const {
days,
timeUnit,
invitation,
fragmentId,
collectionId,
executionDate,
} = job.data
const collection = await CollectionModel.find(collectionId)
const fragment = await FragmentModel.find(fragmentId)
const collectionHelper = new Collection({ collection })
const teamHelper = new Team({
TeamModel,
collectionId,
fragmentId,
})
const team = await teamHelper.getTeam({
role: invitation.role,
objectType: 'fragment',
})
fragment.invitations = fragment.invitations.filter(
inv => inv.id !== invitation.id,
)
await collectionHelper.updateStatusByNumberOfReviewers({
invitations: fragment.invitations,
})
await teamHelper.removeTeamMember({
teamId: team.id,
userId: invitation.userId,
})
const user = await UserModel.find(invitation.userId)
user.teams = user.teams.filter(userTeamId => team.id !== userTeamId)
await user.save()
await fragment.save()
return `Job ${
job.id
}: the ${days} ${timeUnit} removal has been executed at ${executionDate} for user ${
user.id
}`
}
module.exports = { scheduleRemovalJob }
......@@ -9,7 +9,6 @@ const {
} = require('pubsweet-component-helper-service')
const emailInvitations = require('./emails/invitations')
const reminders = require('./jobs/reminders')
const { last } = require('lodash')
......@@ -118,13 +117,13 @@ module.exports = models => async (req, res) => {
})
emailInvitations.sendReviewInvitations({
// resend,
baseUrl,
fragment,
collection,
invitation,
invitedUser: user,
UserModel: models.User,
TeamModel: models.Team,
timestamp: invitation.invitedOn,
})
}
......@@ -188,6 +187,7 @@ module.exports = models => async (req, res) => {
invitation,
invitedUser: newUser,
UserModel: models.User,
TeamModel: models.Team,
timestamp: invitation.invitedOn,
})
......
......@@ -151,4 +151,13 @@ module.exports = {
passwordStrengthRegex: new RegExp(
'^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&,.?;\'*><)([}{}":`~+=_-\\|/])(?=.{6,128})',
),
reminders: {
reviewer: {
first: process.env.REMINDER_REVIEWER_FIRST || 4,
second: process.env.REMINDER_REVIEWER_SECOND || 7,
third: process.env.REMINDER_REVIEWER_THIRD || 14,
remove: process.env.REMINDER_REMOVE_REVIEWER || 21,
timeUnit: process.env.REMINDER_REVIEWER_TIME_UNIT || 'days',
},
},
}
......@@ -8863,6 +8863,11 @@ moment@^2.22.1:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.1.tgz#529a2e9bf973f259c9643d237fda84de3a26e8ad"
integrity sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==
 
moment@^2.23.0:
version "2.23.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.23.0.tgz#759ea491ac97d54bac5ad776996e2a58cc1bc225"
integrity sha512-3IE39bHVqFbWWaPOMHZF98Q9c3LDKGTmypMiTM2QygGXXElkFWIH7GxfmlwmY2vwa+wmNsoYZmG2iusf1ZjJoA==
morgan@^1.8.2:
version "1.9.0"
resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.0.tgz#d01fa6c65859b76fcf31b3cb53a3821a311d8051"
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment