diff --git a/src/constants.js b/src/constants.js index 60ec940..0e848a1 100644 --- a/src/constants.js +++ b/src/constants.js @@ -48,6 +48,23 @@ const challengeStatuses = { CancelledPaymentFailed: 'Cancelled - Payment Failed' } +const scorecardQuestionMapping = { + 30002212: [ + { + questionId: 30007531, + description: 'Does the submission sufficiently satisfy the requirements as described in the provided specification?' + }, + { + questionId: 30007533, + description: 'How would you rate the work ethic of this submitter?' + }, + { + questionId: 30007532, + description: 'How would you rate the quality of this submitters work?' + } + ] +} + const PhaseStatusTypes = { Scheduled: 1, Open: 2, @@ -150,5 +167,6 @@ module.exports = { challengeStatuses, PhaseStatusTypes, prizeTypesIds, - supportedMetadata + supportedMetadata, + scorecardQuestionMapping } diff --git a/src/services/ProcessorService.js b/src/services/ProcessorService.js index 64d378b..c5698e1 100644 --- a/src/services/ProcessorService.js +++ b/src/services/ProcessorService.js @@ -19,6 +19,7 @@ const paymentService = require('./paymentService') const { createOrSetNumberOfReviewers } = require('./selfServiceReviewerService') const { disableTimelineNotifications } = require('./selfServiceNotificationService') const legacyChallengeService = require('./legacyChallengeService') +const legacyChallengeReviewService = require('./legacyChallengeReviewService') /** * Drop and recreate phases in ifx @@ -713,6 +714,24 @@ async function processMessage (message) { needSyncV4ES = true } if (message.payload.status === constants.challengeStatuses.Completed && challenge.currentStatus !== constants.challengeStatuses.Completed) { + // Try to read reviews and insert them into informix DB + if (message.payload.metadata && message.payload.legacy.reviewScorecardId) { + let orReviewFeedback = _.find(message.payload.metadata, meta => meta.name === 'or_review_feedback') + let orReviewScore = _.find(message.payload.metadata, meta => meta.name === 'or_review_score') + if (!_.isUndefined(orReviewFeedback) && !_.isUndefined(orReviewScore)) { + orReviewFeedback = JSON.parse(orReviewFeedback) + const reviewResponses = [] + _.each(orReviewFeedback, (value, key) => { + const questionId = _.get(_.find(constants.scorecardQuestionMapping[message.payload.legacy.reviewScorecardId], item => _.toString(item.questionId) === _.toString(key) || _.toLower(item.description) === _.toLower(key)), 'questionId') + reviewResponses.push({ + questionId, + answer: value + }) + }) + orReviewScore = _.toNumber(orReviewFeedback) + await legacyChallengeReviewService.insertReview(legacyId, message.payload.legacy.reviewScorecardId, orReviewScore, reviewResponses, createdByUserId) + } + } if (message.payload.task.isTask) { logger.info('Challenge is a TASK') if (!message.payload.winners || message.payload.winners.length === 0) { diff --git a/src/services/legacyChallengeReviewService.js b/src/services/legacyChallengeReviewService.js new file mode 100644 index 0000000..f003f0a --- /dev/null +++ b/src/services/legacyChallengeReviewService.js @@ -0,0 +1,124 @@ +/** + * Legacy Challenge Service + * Interacts with InformixDB + * Note: this is built to work for topgear challenges and iterative review phases + */ +const _ = require('lodash') +const logger = require('../common/logger') +const util = require('util') +const helper = require('../common/helper') +const IDGenerator = require('../common/idGenerator') +const reviewIdGen = new IDGenerator('review_id_seq') +const reviewItemIdGen = new IDGenerator('review_item_id_seq') + +const ITERATIVE_REVIEWER_RESOURCE_ROLE_ID = 21 +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}` + +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)' + +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)' + +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' + +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' + +/** + * Prepare Informix statement + * @param {Object} connection the Informix connection + * @param {String} sql the sql + * @return {Object} Informix statement + */ +async function prepare (connection, sql) { + // logger.debug(`Preparing SQL ${sql}`) + const stmt = await connection.prepareAsync(sql) + return Promise.promisifyAll(stmt) +} + +/** + * Insert review in IFX + * @param {Number} challengeLegacyId the legacy challenge ID + * @param {Number} createdBy the scorecard ID + * @param {Number} score the review score + * @param {Array} responses the review responses + * @param {Number} createdBy the creator user ID + */ +async function insertReview (challengeLegacyId, scorecardId, score, responses, createdBy) { + const connection = await helper.getInformixConnection() + let result = null + let reviewId + try { + const resourceId = await getIterativeReviewerResourceId(connection, challengeLegacyId) + if (!resourceId) throw new Error('Cannot find Iterative Reviewer') + const submissionId = await getSubmissionId(connection, challengeLegacyId) + if (!submissionId) throw new Error('Cannot find Submission') + const projectPhaseId = await getProjectPhaseId(connection, challengeLegacyId) + if (!projectPhaseId) throw new Error('Cannot find Project Phase Id') + reviewId = await reviewIdGen.getNextId() + await connection.beginTransactionAsync() + const query = await prepare(connection, QUERY_CREATE_REVIEW) + result = await query.executeAsync([reviewId, resourceId, submissionId, projectPhaseId, scorecardId, 1, score, score, createdBy, createdBy]) + for (let i = 0; i < responses.length; i += 1) { + await insertReviewItem(connection, reviewId, responses[i], i, createdBy) + } + await connection.commitTransactionAsync() + } catch (e) { + logger.error(`Error in 'insertReview' ${e}, rolling back transaction`) + await connection.rollbackTransactionAsync() + throw e + } finally { + logger.info(`Review ${challengeLegacyId} has been created`) + await connection.closeAsync() + } + return result +} + +/** + * Insert review item in IFX + * @param {Object} connection + * @param {Number} reviewId the review ID + * @param {Object} response the response + * @param {Number} sort the sort + * @param {Number} createdBy the creator user ID + */ +async function insertReviewItem (connection, reviewId, response, sort, createdBy) { + let result = null + const reviewItemId = await reviewItemIdGen.getNextId() + await connection.beginTransactionAsync() + const query = await prepare(connection, QUERY_CREATE_REVIEW_ITEM) + result = await query.executeAsync([reviewItemId, reviewId, response.questionId, null, response.answer, sort, createdBy, createdBy]) + return result +} + +/** + * Gets the iterative reviewer resource id + * @param {Object} connection + * @param {Number} challengeLegacyId + */ +async function getIterativeReviewerResourceId (connection, challengeLegacyId) { + const result = await connection.queryAsync(util.format(QUERY_GET_ITERATIVE_REVIEW_RESOURCE_FOR_CHALLENGE, challengeLegacyId)) + return _.get(result, '[0].resourceid', null) +} + +/** + * Gets the submission id + * @param {Object} connection + * @param {Number} challengeLegacyId + */ +async function getSubmissionId (connection, challengeLegacyId) { + const result = await connection.queryAsync(util.format(QUERY_GET_SUBMISSION, challengeLegacyId)) + return _.get(result, '[0].submission_id', null) +} + +/** + * Gets the submission id + * @param {Object} connection + * @param {Number} challengeLegacyId + */ +async function getProjectPhaseId (connection, challengeLegacyId) { + const result = await connection.queryAsync(util.format(QUERY_GET_PROJECT_PHASE, challengeLegacyId)) + return _.get(result, '[0].project_phase_id', null) +} + +module.exports = { + insertReview +}