Skip to content

Commit c1b0578

Browse files
committed
Send Weekly Surveys
1 parent 07082f4 commit c1b0578

13 files changed

+613
-9
lines changed

app.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const logger = require('./src/common/logger')
1414
const eventHandlers = require('./src/eventHandlers')
1515
const interviewService = require('./src/services/InterviewService')
1616
const { processScheduler } = require('./src/services/PaymentSchedulerService')
17+
const { sendSurveys } = require('./src/services/SurveyService')
1718

1819
// setup express app
1920
const app = express()
@@ -98,7 +99,8 @@ const server = app.listen(app.get('port'), () => {
9899
eventHandlers.init()
99100
// schedule updateCompletedInterviews to run every hour
100101
schedule.scheduleJob('0 0 * * * *', interviewService.updateCompletedInterviews)
101-
102+
// schedule sendSurveys
103+
schedule.scheduleJob(config.WEEKLY_SURVEY.CRON, sendSurveys)
102104
// schedule payment processing
103105
schedule.scheduleJob(config.PAYMENT_PROCESSING.CRON, processScheduler)
104106
})

config/default.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,16 @@ module.exports = {
180180
INTERNAL_MEMBER_GROUPS: process.env.INTERNAL_MEMBER_GROUPS || ['20000000', '20000001', '20000003', '20000010', '20000015'],
181181
// Topcoder skills cache time in minutes
182182
TOPCODER_SKILLS_CACHE_TIME: process.env.TOPCODER_SKILLS_CACHE_TIME || 60,
183+
// weekly survey scheduler config
184+
WEEKLY_SURVEY: {
185+
CRON: process.env.WEEKLY_SURVEY_CRON || '0 1 * * 7',
186+
BASE_URL: process.env.WEEKLY_SURVEY_BASE_URL || 'https://api.surveymonkey.net/v3/surveys',
187+
JWT_TOKEN: process.env.WEEKLY_SURVEY_JWT_TOKEN || '',
188+
SURVEY_ID: process.env.WEEKLY_SURVEY_SURVEY_ID || '',
189+
SURVEY_MASTER_COLLECTOR_ID: process.env.WEEKLY_SURVEY_SURVEY_MASTER_COLLECTOR_ID || '',
190+
SURVEY_MASTER_MESSAGE_ID: process.env.WEEKLY_SURVEY_SURVEY_MASTER_MESSAGE_ID || '',
191+
SURVEY_CONTACT_GROUP_ID: process.env.WEEKLY_SURVEY_SURVEY_CONTACT_GROUP_ID || ''
192+
},
183193
// payment scheduler config
184194
PAYMENT_PROCESSING: {
185195
// switch off actual API calls in Payment Scheduler

docs/swagger.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4470,6 +4470,10 @@ components:
44704470
format: float
44714471
example: 13
44724472
description: "The member rate."
4473+
sendWeeklySurvey:
4474+
type: boolean
4475+
example: true,
4476+
description: "whether we should send weekly survey to this ResourceBooking or no"
44734477
customerRate:
44744478
type: integer
44754479
format: float
@@ -4527,6 +4531,10 @@ components:
45274531
format: uuid
45284532
example: "a55fe1bc-1754-45fa-9adc-cf3d6d7c377a"
45294533
description: "The external id."
4534+
sendWeeklySurvey:
4535+
type: boolean
4536+
example: true,
4537+
description: "whether we should send weekly survey to this ResourceBooking or no"
45304538
jobId:
45314539
type: string
45324540
format: uuid
@@ -4584,6 +4592,10 @@ components:
45844592
format: float
45854593
example: 13.23
45864594
description: "The member rate."
4595+
sendWeeklySurvey:
4596+
type: boolean
4597+
example: true,
4598+
description: "whether we should send weekly survey to this ResourceBooking or no"
45874599
customerRate:
45884600
type: number
45894601
format: float
@@ -4620,6 +4632,22 @@ components:
46204632
type: string
46214633
format: uuid
46224634
description: "The resource booking id."
4635+
sentSurvey:
4636+
type: boolean
4637+
example: true
4638+
description: "whether we've already sent a survey for this WorkPeriod of no"
4639+
sentSurveyError:
4640+
description: "error details if error happened during sending survey"
4641+
type: object
4642+
properties:
4643+
errorMessage:
4644+
type: string
4645+
example: "error message"
4646+
description: "The error message"
4647+
errorCode:
4648+
type: integer
4649+
example: 429
4650+
description: "HTTP code of error"
46234651
userHandle:
46244652
type: string
46254653
example: "eisbilir"
@@ -4695,6 +4723,22 @@ components:
46954723
type: integer
46964724
example: 2
46974725
description: "The count of the days worked for that work period."
4726+
sentSurvey:
4727+
type: boolean
4728+
example: true
4729+
description: "whether we've already sent a survey for this WorkPeriod of no"
4730+
sentSurveyError:
4731+
description: "error details if error happened during sending survey"
4732+
type: object
4733+
properties:
4734+
errorMessage:
4735+
type: string
4736+
example: "error message"
4737+
description: "The error message"
4738+
errorCode:
4739+
type: integer
4740+
example: 429
4741+
description: "HTTP code of error"
46984742
WorkPeriodPayment:
46994743
required:
47004744
- id
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const config = require('config')
2+
3+
module.exports = {
4+
up: async (queryInterface, Sequelize) => {
5+
const transaction = await queryInterface.sequelize.transaction()
6+
try {
7+
await queryInterface.addColumn({ tableName: 'resource_bookings', schema: config.DB_SCHEMA_NAME }, 'send_weekly_survey',
8+
{ type: Sequelize.BOOLEAN, allowNull: false, defaultValue: true },
9+
{ transaction })
10+
await queryInterface.addColumn({ tableName: 'work_periods', schema: config.DB_SCHEMA_NAME }, 'sent_survey',
11+
{ type: Sequelize.BOOLEAN, allowNull: false, defaultValue: false },
12+
{ transaction })
13+
await queryInterface.addColumn({ tableName: 'work_periods', schema: config.DB_SCHEMA_NAME }, 'sent_survey_error',
14+
{
15+
type: Sequelize.JSONB({
16+
errorCode: {
17+
field: 'error_code',
18+
type: Sequelize.INTEGER,
19+
},
20+
errorMessage: {
21+
field: 'error_message',
22+
type: Sequelize.STRING(255)
23+
},
24+
}), allowNull: true }, { transaction })
25+
await transaction.commit()
26+
} catch (err) {
27+
await transaction.rollback()
28+
throw err
29+
}
30+
},
31+
down: async (queryInterface, Sequelize) => {
32+
const transaction = await queryInterface.sequelize.transaction()
33+
try {
34+
await queryInterface.removeColumn({ tableName: 'resource_bookings', schema: config.DB_SCHEMA_NAME }, 'send_weekly_survey', { transaction })
35+
await queryInterface.removeColumn({ tableName: 'work_periods', schema: config.DB_SCHEMA_NAME }, 'send_survey', { transaction })
36+
await queryInterface.removeColumn({ tableName: 'work_periods', schema: config.DB_SCHEMA_NAME }, 'sent_survey_error', { transaction } )
37+
await transaction.commit()
38+
} catch (err) {
39+
await transaction.rollback()
40+
throw err
41+
}
42+
},
43+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"index:roles": "node scripts/es/reIndexRoles.js",
2020
"data:export": "node scripts/data/exportData.js",
2121
"data:import": "node scripts/data/importData.js",
22+
"data:workperiod": "node scripts/data/updateWorkPeriodSentSurveyField.js",
2223
"migrate": "npx sequelize db:migrate",
2324
"migrate:undo": "npx sequelize db:migrate:undo",
2425
"test": "mocha test/unit/*.test.js --timeout 30000 --require test/prepare.js --exit",
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* update WorkPeriod field `sentSurvey=true`
3+
*/
4+
const _ = require('lodash')
5+
const moment = require('moment')
6+
const logger = require('../../src/common/logger')
7+
const { Op } = require('sequelize')
8+
const models = require('../../src/models')
9+
10+
const ResourceBooking = models.ResourceBooking
11+
const WorkPeriod = models.WorkPeriod
12+
13+
async function updateWorkPeriod () {
14+
const transaction = await models.sequelize.transaction()
15+
try {
16+
// Start a transaction
17+
const queryCriteria = {
18+
attributes: ['sendWeeklySurvey', 'id'],
19+
include: [{
20+
as: 'workPeriods',
21+
model: WorkPeriod,
22+
required: true,
23+
where: {
24+
[Op.and]: [
25+
{ sentSurveyError: null },
26+
{ sentSurvey: false },
27+
{ paymentStatus: ['completed'] },
28+
{ endDate: { [Op.lte]: moment().subtract(7, 'days').format('YYYY-MM-DD') } }
29+
]
30+
}
31+
}],
32+
where: {
33+
[Op.and]: [{ sendWeeklySurvey: true }]
34+
},
35+
transaction
36+
}
37+
38+
const resourceBookings = await ResourceBooking.findAll(queryCriteria)
39+
40+
_.forEach(resourceBookings, r => {
41+
_.forEach(r.workPeriods, async w => {
42+
// await w.update({sentSurvey: true}, {transaction: transaction} )
43+
await w.update({ sentSurvey: true })
44+
})
45+
})
46+
47+
// commit transaction only if all things went ok
48+
logger.info({
49+
component: 'importData',
50+
message: 'committing transaction to database...'
51+
})
52+
await transaction.commit()
53+
} catch (error) {
54+
// logger.error({
55+
// component: 'importData',
56+
// message: `Error while writing data of model: WorkPeriod`
57+
// })
58+
// rollback all insert operations
59+
if (transaction) {
60+
logger.info({
61+
component: 'importData',
62+
message: 'rollback database transaction...'
63+
})
64+
transaction.rollback()
65+
}
66+
}
67+
}
68+
updateWorkPeriod()

src/common/helper.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ esIndexPropertyMapping[config.get('esConfig.ES_INDEX_RESOURCE_BOOKING')] = {
176176
endDate: { type: 'date', format: 'yyyy-MM-dd' },
177177
memberRate: { type: 'float' },
178178
customerRate: { type: 'float' },
179+
sendWeeklySurvey: { type: 'boolean' },
179180
rateType: { type: 'keyword' },
180181
billingAccountId: { type: 'integer', null_value: 0 },
181182
workPeriods: {
@@ -189,6 +190,14 @@ esIndexPropertyMapping[config.get('esConfig.ES_INDEX_RESOURCE_BOOKING')] = {
189190
},
190191
projectId: { type: 'integer' },
191192
userId: { type: 'keyword' },
193+
sentSurvey: { type: 'boolean' },
194+
sentSurveyError: {
195+
type: 'nested',
196+
properties: {
197+
errorCode: { type: 'integer' },
198+
errorMessage: { type: 'keyword' }
199+
}
200+
},
192201
startDate: { type: 'date', format: 'yyyy-MM-dd' },
193202
endDate: { type: 'date', format: 'yyyy-MM-dd' },
194203
daysWorked: { type: 'integer' },
@@ -2012,6 +2021,7 @@ async function getMembersSuggest (fragment) {
20122021
}
20132022

20142023
module.exports = {
2024+
encodeQueryString,
20152025
getParamFromCliArgs,
20162026
promptUser,
20172027
sleep,

0 commit comments

Comments
 (0)