Skip to content
This repository was archived by the owner on Jan 23, 2025. It is now read-only.

Commit 3a37d9c

Browse files
support review feedback
1 parent f8d8a45 commit 3a37d9c

File tree

3 files changed

+186
-1
lines changed

3 files changed

+186
-1
lines changed

src/constants.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,23 @@ const challengeStatuses = {
4848
CancelledPaymentFailed: 'Cancelled - Payment Failed'
4949
}
5050

51+
const scorecardQuestionMapping = {
52+
30002212: [
53+
{
54+
questionId: 30007531,
55+
description: 'Does the submission sufficiently satisfy the requirements as described in the provided specification?'
56+
},
57+
{
58+
questionId: 30007533,
59+
description: 'How would you rate the work ethic of this submitter?'
60+
},
61+
{
62+
questionId: 30007532,
63+
description: 'How would you rate the quality of this submitters work?'
64+
}
65+
]
66+
}
67+
5168
const PhaseStatusTypes = {
5269
Scheduled: 1,
5370
Open: 2,
@@ -150,5 +167,6 @@ module.exports = {
150167
challengeStatuses,
151168
PhaseStatusTypes,
152169
prizeTypesIds,
153-
supportedMetadata
170+
supportedMetadata,
171+
scorecardQuestionMapping
154172
}

src/services/ProcessorService.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const paymentService = require('./paymentService')
1919
const { createOrSetNumberOfReviewers } = require('./selfServiceReviewerService')
2020
const { disableTimelineNotifications } = require('./selfServiceNotificationService')
2121
const legacyChallengeService = require('./legacyChallengeService')
22+
const legacyChallengeReviewService = require('./legacyChallengeReviewService')
2223

2324
/**
2425
* Drop and recreate phases in ifx
@@ -713,6 +714,24 @@ async function processMessage (message) {
713714
needSyncV4ES = true
714715
}
715716
if (message.payload.status === constants.challengeStatuses.Completed && challenge.currentStatus !== constants.challengeStatuses.Completed) {
717+
// Try to read reviews and insert them into informix DB
718+
if (message.payload.metadata && message.payload.legacy.reviewScorecardId) {
719+
let orReviewFeedback = _.find(message.payload.metadata, meta => meta.name === 'or_review_feedback')
720+
let orReviewScore = _.find(message.payload.metadata, meta => meta.name === 'or_review_score')
721+
if (!_.isUndefined(orReviewFeedback) && !_.isUndefined(orReviewScore)) {
722+
orReviewFeedback = JSON.parse(orReviewFeedback)
723+
const reviewResponses = []
724+
_.each(orReviewFeedback, (value, key) => {
725+
const questionId = _.get(_.find(constants.scorecardQuestionMapping[message.payload.legacy.reviewScorecardId], item => _.toString(item.questionId) === _.toString(key) || _.toLower(item.description) === _.toLower(key)), 'questionId')
726+
reviewResponses.push({
727+
questionId,
728+
answer: value
729+
})
730+
})
731+
orReviewScore = _.toNumber(orReviewFeedback)
732+
await legacyChallengeReviewService.insertReview(legacyId, message.payload.legacy.reviewScorecardId, orReviewScore, reviewResponses, createdByUserId)
733+
}
734+
}
716735
if (message.payload.task.isTask) {
717736
logger.info('Challenge is a TASK')
718737
if (!message.payload.winners || message.payload.winners.length === 0) {
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/**
2+
* Legacy Challenge Service
3+
* Interacts with InformixDB
4+
* Note: this is built to work for topgear challenges and iterative review phases
5+
*/
6+
const _ = require('lodash')
7+
const logger = require('../common/logger')
8+
const util = require('util')
9+
const helper = require('../common/helper')
10+
const IDGenerator = require('../common/idGenerator')
11+
const reviewIdGen = new IDGenerator('review_id_seq')
12+
const reviewItemIdGen = new IDGenerator('review_item_id_seq')
13+
14+
const ITERATIVE_REVIEWER_RESOURCE_ROLE_ID = 21
15+
const QUERY_GET_ITERATIVE_REVIEW_RESOURCE_FOR_CHALLENGE = `SELECT limit 1 resource_id as resourceid FROM resource WHERE project_id = %d AND resource_role_id = ${ITERATIVE_REVIEWER_RESOURCE_ROLE_ID}`
16+
17+
const QUERY_CREATE_REVIEW = 'INSERT INTO review (review_id, resource_id, submission_id, project_phase_id, scorecard_id, committed, score, initial_score, create_user, create_date, modify_user, modify_date) values (?,?,?,?,?,?,?,?,?,CURRENT,?,CURRENT)'
18+
19+
/**
20+
review_id is a new ID using idGenerator
21+
resource_id, as near as I can tell, should be the universal Id of the resource, not the challenge or project specific IDs used for roles and resources.
22+
submission_id, the most important item, which you should have.
23+
project_phase_id, try null, or any phase that overlaps in time with the challenge phase the reviews are submitted for. I can't find any logic or constraints for this field.
24+
scorecard_id, the second most important item, which you should have.
25+
committed, in the case of a TopGear batch review submission should be true.
26+
score and initial_score are usually aggregated by OR and AP, but you could aggregate them yourself, according to the scorecard weights. Try having AP do it for you.
27+
create_user should be some generic API user name that tells us it was a TopGear batch upload.
28+
create_date should be now (use CURRENT for Informix SQL if you want).
29+
modify_user and modify_date can be null (or just don't include those in the field list).
30+
The ? are just placeholders for your values. I believe Informix uses a single quote for strings.
31+
*/
32+
33+
const QUERY_CREATE_REVIEW_ITEM = 'INSERT INTO review_item (review_item_id, review_id, scorecard_question_id, upload_id, answer, sort, create_user, create_date, modify_user, modify_date) values (?,?,?,?,?,?,?,CURRENT,?,CURRENT)'
34+
35+
/*
36+
review_item_id, use your idGenerator ID.
37+
review_id, the review ID from the previous query.
38+
scorecard_question_id, the most important field, which you should have.
39+
upload_id, this is specific to the submission_id, although you could have multiple uploads and you will want the upload_id, which TopGear should have if someone answered a question about a submission, because the submission must have been downloaded from somewhere.
40+
answer, usually a number.
41+
sort, use a constant here if you wish. From what I can tell, this would be used to display certain comments at the top for review appeals. It may have a default sort from the scorecard definition, but it's probably not important since people won't be looking at individual reviews much, I'm guessing.
42+
Same as above for create_user, etc.
43+
*/
44+
45+
const QUERY_GET_SUBMISSION = 'SELECT FIRST 1 * FROM submission s INNER JOIN upload u on s.upload_id = u.upload_id WHERE u.project_id = %d AND upload_status_id = 1 AND submission_status_id = 1 ORDER BY u.CREATE_DATE ASC'
46+
47+
const QUERY_GET_PROJECT_PHASE = 'select pc.parameter scorecard_id, pp.project_phase_id project_phase_id from project_phase pp inner join phase_criteria pc on pc.project_phase_id = pp.project_phase_id where pp.project_id = %d and pp.phase_type_id = 18 and phase_criteria_type_id = 1'
48+
49+
/**
50+
* Prepare Informix statement
51+
* @param {Object} connection the Informix connection
52+
* @param {String} sql the sql
53+
* @return {Object} Informix statement
54+
*/
55+
async function prepare (connection, sql) {
56+
// logger.debug(`Preparing SQL ${sql}`)
57+
const stmt = await connection.prepareAsync(sql)
58+
return Promise.promisifyAll(stmt)
59+
}
60+
61+
/**
62+
* Insert review in IFX
63+
* @param {Number} challengeLegacyId the legacy challenge ID
64+
* @param {Number} createdBy the scorecard ID
65+
* @param {Number} score the review score
66+
* @param {Array} responses the review responses
67+
* @param {Number} createdBy the creator user ID
68+
*/
69+
async function insertReview (challengeLegacyId, scorecardId, score, responses, createdBy) {
70+
const connection = await helper.getInformixConnection()
71+
let result = null
72+
let reviewId
73+
try {
74+
const resourceId = await getIterativeReviewerResourceId(connection, challengeLegacyId)
75+
if (!resourceId) throw new Error('Cannot find Iterative Reviewer')
76+
const submissionId = await getSubmissionId(connection, challengeLegacyId)
77+
if (!submissionId) throw new Error('Cannot find Submission')
78+
const projectPhaseId = await getProjectPhaseId(connection, challengeLegacyId)
79+
if (!projectPhaseId) throw new Error('Cannot find Project Phase Id')
80+
reviewId = await reviewIdGen.getNextId()
81+
await connection.beginTransactionAsync()
82+
const query = await prepare(connection, QUERY_CREATE_REVIEW)
83+
result = await query.executeAsync([reviewId, resourceId, submissionId, projectPhaseId, scorecardId, 1, score, score, createdBy, createdBy])
84+
for (let i = 0; i < responses.length; i += 1) {
85+
await insertReviewItem(connection, reviewId, responses[i], i, createdBy)
86+
}
87+
await connection.commitTransactionAsync()
88+
} catch (e) {
89+
logger.error(`Error in 'insertReview' ${e}, rolling back transaction`)
90+
await connection.rollbackTransactionAsync()
91+
throw e
92+
} finally {
93+
logger.info(`Review ${challengeLegacyId} has been created`)
94+
await connection.closeAsync()
95+
}
96+
return result
97+
}
98+
99+
/**
100+
* Insert review item in IFX
101+
* @param {Object} connection
102+
* @param {Number} reviewId the review ID
103+
* @param {Object} response the response
104+
* @param {Number} sort the sort
105+
* @param {Number} createdBy the creator user ID
106+
*/
107+
async function insertReviewItem (connection, reviewId, response, sort, createdBy) {
108+
let result = null
109+
const reviewItemId = await reviewItemIdGen.getNextId()
110+
await connection.beginTransactionAsync()
111+
const query = await prepare(connection, QUERY_CREATE_REVIEW_ITEM)
112+
result = await query.executeAsync([reviewItemId, reviewId, response.questionId, null, response.answer, sort, createdBy, createdBy])
113+
return result
114+
}
115+
116+
/**
117+
* Gets the iterative reviewer resource id
118+
* @param {Object} connection
119+
* @param {Number} challengeLegacyId
120+
*/
121+
async function getIterativeReviewerResourceId (connection, challengeLegacyId) {
122+
const result = await connection.queryAsync(util.format(QUERY_GET_ITERATIVE_REVIEW_RESOURCE_FOR_CHALLENGE, challengeLegacyId))
123+
return _.get(result, '[0].resourceid', null)
124+
}
125+
126+
/**
127+
* Gets the submission id
128+
* @param {Object} connection
129+
* @param {Number} challengeLegacyId
130+
*/
131+
async function getSubmissionId (connection, challengeLegacyId) {
132+
const result = await connection.queryAsync(util.format(QUERY_GET_SUBMISSION, challengeLegacyId))
133+
return _.get(result, '[0].submission_id', null)
134+
}
135+
136+
/**
137+
* Gets the submission id
138+
* @param {Object} connection
139+
* @param {Number} challengeLegacyId
140+
*/
141+
async function getProjectPhaseId (connection, challengeLegacyId) {
142+
const result = await connection.queryAsync(util.format(QUERY_GET_PROJECT_PHASE, challengeLegacyId))
143+
return _.get(result, '[0].project_phase_id', null)
144+
}
145+
146+
module.exports = {
147+
insertReview
148+
}

0 commit comments

Comments
 (0)