Commit b5d99a0c authored by venkanagouda's avatar venkanagouda
Browse files

Issue #DP-509 feat: generate tableData of program

parent 2600911d
SB-23374 SB-25924 copy_issue copy_issue1 dependabot/npm_and_yarn/src/ajv-6.12.6 dependabot/npm_and_yarn/src/async-2.6.4 dependabot/npm_and_yarn/src/axios-0.21.2 dependabot/npm_and_yarn/src/css-what-5.0.1 dependabot/npm_and_yarn/src/decode-uri-component-0.2.2 dependabot/npm_and_yarn/src/elliptic-6.5.4 dependabot/npm_and_yarn/src/jsonwebtoken-9.0.0 dependabot/npm_and_yarn/src/lodash-4.17.21 dependabot/npm_and_yarn/src/moment-2.29.4 dependabot/npm_and_yarn/src/moment-timezone-0.5.37 dependabot/npm_and_yarn/src/node-fetch-2.6.7 dependabot/npm_and_yarn/src/nth-check-2.0.1 dependabot/npm_and_yarn/src/path-parse-1.0.7 dependabot/npm_and_yarn/src/pathval-1.1.1 dependabot/npm_and_yarn/src/qs-and-body-parser-and-express-6.5.3 dependabot/npm_and_yarn/src/redis-3.1.1 dependabot/npm_and_yarn/src/simple-get-2.8.2 dependabot/npm_and_yarn/src/underscore-1.12.1 eslint_fixes log_issue master program-service publish_changes questionset-integration release-3.8.0 release-3.9.0 release-4.0.0 release-4.1.0 release-4.1.1 release-4.2.0 release-4.3.0 release-4.4.0 release-4.5.0 release-4.6.0 release-4.7.0 release-4.8.0 release-4.9.0 release-5.1.0 revert-147-release-3.9.0 sb-22518 sb-25146 sprint-10 sprint-14 sprint-15 sprint-16 sprint-17 sprint-18 sprint-19 sprint-20 sprint-9 test-issue vk-local-ps-4.4.0 sprint20_RC2 sprint20_RC1 sprint19_RC3 sprint19_RC2 sprint19_RC1 sprint18_RC1 sprint17_RC2 sprint17_RC1 sprint16_RC3 sprint16_RC2 sprint16_RC1 sprint15.1_RC2 sprint15.1_RC1 sprint15_RC4 sprint15_RC3 sprint15_RC2 sprint15_RC1 sprint14_RC1 sprint13_RC4 sprint13_RC3 sprint13_RC2 release-5.1.0_RC1 release-4.9.0_RC1 release-4.8.0_RC3 release-4.8.0_RC2 release-4.8.0_RC1 release-4.7.0_RC6 release-4.7.0_RC5 release-4.7.0_RC4 release-4.7.0_RC3 release-4.7.0_RC2 release-4.7.0_RC1 release-4.6.0_RC6 release-4.6.0_RC5 release-4.6.0_RC4 release-4.6.0_RC3 release-4.6.0_RC2 release-4.6.0_RC1 release-4.5.0_RC1 release-4.4.0_RC5 release-4.4.0_RC4 release-4.4.0_RC3 release-4.4.0_RC2 release-4.4.0_RC1 release-4.3.0_RC5 release-4.3.0_RC4 release-4.3.0_RC3 release-4.3.0_RC2 release-4.3.0_RC1 release-4.2.0_RC9 release-4.2.0_RC8 release-4.2.0_RC7 release-4.2.0_RC6 release-4.2.0_RC5 release-4.2.0_RC4 release-4.2.0_RC3 release-4.2.0_RC2 release-4.2.0_RC1 release-4.1.1_RC1 release-4.1.0_RC2 release-4.1.0_RC1 release-4.0.0_RC3 release-4.0.0_RC2 release-4.0.0_RC1 release-3.9.0_RC4 release-3.9.0_RC3 release-3.9.0_RC2 release-3.9.0_RC1 release-3.8.0_RC3 release-3.8.0_RC2 release-3.8.0_RC1 program-service_RC22 program-service_RC21 program-service_RC20 program-service_RC19 program-service_RC18 program-service_RC17 program-service_RC16 program-service_RC15
No related merge requests found
Showing with 279 additions and 35 deletions
+279 -35
const _ = require("lodash");
const envVariables = require("../envVariables");
const axios = require("axios");
const model = require('../models');
const Sequelize = require('sequelize');
class ProgramServiceHelper {
searchWithProgramId(queryFilter, req) {
const headers = {
'content-type': 'application/json',
};
const option = {
url: `${envVariables.baseURL}/api/composite/v1/search`,
method: 'post',
headers: {...req.headers, ...headers},
data: {
request: queryFilter
}
};
return axios(option);
}
getCollectionWithProgramId(program_id, req) {
const queryFilter = {
filters: {
programId: program_id,
objectType: 'content',
status: ['Review', 'Draft'],
contentType: 'Textbook'
},
fields: ['name', 'medium', 'gradeLevel', 'subject', 'chapterCount', 'acceptedContents', 'rejectedContents'],
limit: 1000
};
return this.searchWithProgramId(queryFilter, req);
}
getSampleContentWithProgramId(program_id, req) {
const queryFilter = {
filters: {
programId: program_id,
objectType: 'content',
status: ['Review', 'Draft'],
sampleContent: true
},
facets: [
'sampleContent', 'collectionId', 'status'
],
limit: 0
};
return this.searchWithProgramId(queryFilter, req);
}
getContributionWithProgramId(program_id, req) {
const queryFilter = {
filters: {
programId: program_id,
objectType: 'content',
status: ['Review', 'Draft', 'Live'],
contentType: { '!=': 'Asset' },
mimeType: { '!=': 'application/vnd.ekstep.content-collection' }
},
not_exists: ['sampleContent'],
aggregations: [
{
"l1": "collectionId",
"l2": "status"
}
],
limit: 0
};
return this.searchWithProgramId(queryFilter, req);
}
getNominationWithProgramId(programId) {
const facets = ['collection_ids', 'status'];
const promise = model.nomination.findAll({
where: {
program_id: programId
},
attributes: [...facets, [Sequelize.fn('count', Sequelize.col(facets[0])), 'count']],
group: [...facets]
})
return promise;
}
handleMultiProgramDetails(resGroup) {
const multiProgramDetails = _.map(resGroup, (resData) => {
try {
return this.prepareTableData(resData);
} catch(err) {
throw err
}
});
return multiProgramDetails;
}
prepareTableData (resData) {
try {
const collectionList = resData[0].data.result && resData[0].data.result.content || [],
sampleContentResponse = resData[1].data.result && resData[1].data.result.facets || [],
contributionResponse = resData[2].data.result && resData[2].data.result.aggregations || [],
nominationResponse = _.isArray(resData[3]) && resData[3].length? _.map(resData[3], obj => obj.dataValues) : [];
let tableData = [];
if (collectionList.length) {
tableData = _.map(collectionList, (collection) => {
const result = {};
// sequence of columns in tableData
result['Textbook Name'] = collection.name;
result['Medium'] = collection.medium || '--';
result['Class'] = collection.gradeLevel && collection.gradeLevel.length ? collection.gradeLevel.join(', ') : '';
result['Subject'] = collection.subject || '--';
result['Number of Chapters'] = collection.chapterCount || '--';
result['Nominations Received'] = 0;
result['Samples Received'] = 0;
result['Nominations Accepted'] = 0;
result['Contributions Received'] = 0;
result['Contributions Accepted'] = collection.acceptedContents ? collection.acceptedContents.length : 0;
result['Contributions Rejected'] = collection.rejectedContents ? collection.rejectedContents.length : 0;
result['Contributions Pending'] = 0;
// count of sample contents
if (sampleContentResponse.length) {
const facetObj = _.find(sampleContentResponse, {name: 'collectionId'});
if (facetObj && facetObj.values.length &&
_.find(facetObj.values, {name: collection.identifier})) {
result['Samples Received'] = _.find(facetObj.values, {name: collection.identifier}).count;
}
}
// count of contribution
if (contributionResponse.length && contributionResponse[0].name === 'collectionId'
&& contributionResponse[0].values.length) {
const statusCount = _.find(contributionResponse[0].values, {name: collection.identifier});
if (statusCount && statusCount.aggregations && statusCount.aggregations.length) {
_.forEach(statusCount.aggregations[0].values, (obj) => {
if (obj.name === 'live') {
result['Contributions Received'] = result['Contributions Received'] + obj.count;
// tslint:disable-next-line:max-line-length
result['Contributions Pending'] = result['Contributions Received'] - (result['Contributions Rejected'] + result['Contributions Accepted']);
}
});
}
}
// count of nomination
if (nominationResponse.length) {
_.forEach(nominationResponse, (obj) => {
if (obj.collection_ids && _.includes(obj.collection_ids, collection.identifier) ) {
if (obj.status === 'Approved') {
result['Nominations Accepted'] = result['Nominations Accepted'] + Number(obj.count);
} else if (obj.status !== 'Initiated') {
result['Nominations Received'] = result['Nominations Received'] + Number(obj.count);
}
}
});
result['Nominations Received'] = result['Nominations Accepted'] + result['Nominations Received'];
}
return result;
});
}
return tableData;
} catch (err) {
throw 'error in preparing CSV data'
}
}
}
module.exports = ProgramServiceHelper;
......@@ -23,6 +23,10 @@ module.exports = function (app) {
app.route(BASE_URL + '/list')
.post(requestMiddleware.gzipCompression(), requestMiddleware.createAndValidateRequestBody,
programService.programListAPI)
app.route(BASE_URL + '/list/download')
.post(requestMiddleware.gzipCompression(), requestMiddleware.createAndValidateRequestBody,
programService.downloadProgramDetailsAPI)
app.route(BASE_URL + '/search')
.post(requestMiddleware.gzipCompression(), requestMiddleware.createAndValidateRequestBody,
......
......@@ -672,6 +672,12 @@ exports.PROGRAM = {
FAILED_CODE: 'ERR_LINKING_TEXBOOK_FAILED',
FAILED_MESSAGE: 'Unable to link textbook to program'
},
GENERATE_DETAILS: {
MISSING_CODE: 'ERR_GENERATING_PROGRAM_DATA',
MISSING_MESSAGE: 'Required fields like program_id<Array>, is missing',
FAILED_CODE: 'ERR_GENERATING_PROGRAM_DATA_FAILED',
FAILED_MESSAGE: 'Unable to generate the program data'
},
NOMINATION: {
READ: {
MISSING_CODE: 'ERR_GET_NOMINATION_DATA',
......@@ -690,6 +696,12 @@ exports.PROGRAM = {
MISSING_MESSAGE: 'Required fields like program_id, user_id while updating nomination data are missing',
FAILED_CODE: 'ERR_UPDATE_NOMINATION_DATA_FAILED',
FAILED_MESSAGE: 'Unable to update the nomination data'
},
LIST: {
MISSING_CODE: 'ERR_GET_NOMINATION_LIST',
MISSING_MESSAGE: 'Required fields like program_id/user_id to get nomination are missing',
FAILED_CODE: 'ERR_GET_NOMINATION_LIST_FAILED',
FAILED_MESSAGE: 'Unable to get the nomination list'
}
},
COPY_COLLECTION: {
......
......@@ -15,6 +15,7 @@ const {
const axios = require('axios');
const envVariables = require('../envVariables');
const RegistryService = require('./registryService')
const ProgramServiceHelper = require('../helpers/programHelper');
var async = require('async')
......@@ -22,6 +23,7 @@ const queryRes_Max = 1000;
const queryRes_Min = 300;
const HierarchyService = require('../helpers/updateHierarchy.helper');
const programServiceHelper = new ProgramServiceHelper();
const registryService = new RegistryService()
const hierarchyService = new HierarchyService()
......@@ -485,6 +487,9 @@ function getNominationsList(req, response) {
var rspObj = req.rspObj;
var res_limit = queryRes_Min;
var res_offset = data.request.offset || 0;
rspObj.errCode = programMessages.NOMINATION.LIST.FAILED_CODE
rspObj.errMsg = programMessages.NOMINATION.LIST.FAILED_MESSAGE
rspObj.responseCode = responseCode.SERVER_ERROR
if (data.request.limit) {
res_limit = (data.request.limit < queryRes_Max) ? data.request.limit : (queryRes_Max);
}
......@@ -506,13 +511,9 @@ function getNominationsList(req, response) {
result: result
}))
}).catch((err) => {
return response.status(400).send(errorResponse({
apiId: 'api.nomination.list',
ver: '1.0',
msgid: uuid(),
responseCode: 'ERR_NOMINATION_LIST',
result: err
}));
loggerError('Error fetching nomination count group by facets',
rspObj.errCode, rspObj.errMsg, rspObj.responseCode, err, req);
return response.status(400).send(errorResponse(rspObj));
})
}else if (data.request.limit === 0) {
model.nomination.findAll({
......@@ -531,13 +532,9 @@ function getNominationsList(req, response) {
result: aggregatedRes
}))
}).catch((err) => {
return response.status(400).send(errorResponse({
apiId: 'api.nomination.list',
ver: '1.0',
msgid: uuid(),
responseCode: 'ERR_NOMINATION_LIST',
result: err
}));
loggerError('Error fetching nomination count when limit = 0',
rspObj.errCode, rspObj.errMsg, rspObj.responseCode, err, req);
return response.status(400).send(errorResponse(rspObj));
})
} else {
model.nomination.findAll({
......@@ -599,35 +596,97 @@ function getNominationsList(req, response) {
result: result
}))
}, (error) => {
return response.status(400).send(errorResponse({
apiId: 'api.nomination.list',
ver: '1.0',
msgid: uuid(),
responseCode: 'ERR_NOMINATION_LIST',
result: error
}));
loggerError('Error in fetching user/org details',
rspObj.errCode, rspObj.errMsg, rspObj.responseCode, error, req);
return response.status(400).send(errorResponse(rspObj));
});
} catch (err) {
return response.status(400).send(errorResponse({
apiId: 'api.nomination.list',
ver: '1.0',
msgid: uuid(),
responseCode: 'ERR_NOMINATION_LIST',
result: err.message || err
}));
loggerError('Error fetching nomination with limit and offset',
rspObj.errCode, rspObj.errMsg, rspObj.responseCode, err, req);
return response.status(400).send(errorResponse(rspObj));
}
}).catch(function (err) {
return response.status(400).send(errorResponse({
apiId: 'api.nomination.list',
ver: '1.0',
msgid: uuid(),
responseCode: 'ERR_NOMINATION_LIST',
result: err.message || err
}));
loggerError('Error fetching nomination with limit and offset',
rspObj.errCode, rspObj.errMsg, rspObj.responseCode, err, req);
return response.status(400).send(errorResponse(rspObj));
});
}
}
async function downloadProgramDetails(req, res) {
const data = req.body
const rspObj = req.rspObj
let programArr = [], promiseRequests = [], cacheData = [], filteredPrograms = [];
rspObj.errCode = programMessages.GENERATE_DETAILS.FAILED_CODE
rspObj.errMsg = programMessages.GENERATE_DETAILS.FAILED_MESSAGE
rspObj.responseCode = responseCode.SERVER_ERROR
if (!data.request || !data.request.filters || !data.request.filters.program_id) {
rspObj.errCode = programMessages.GENERATE_DETAILS.MISSING_CODE
rspObj.errMsg = programMessages.GENERATE_DETAILS.MISSING_MESSAGE
rspObj.responseCode = responseCode.CLIENT_ERROR
loggerError('Error due to missing request or request.filters or request.filters.program_id',
rspObj.errCode, rspObj.errMsg, rspObj.responseCode, null, req)
return res.status(400).send(errorResponse(rspObj));
}
programArr = _.isArray(data.request.filters.program_id) ? data.request.filters.program_id : [];
await _.forEach(programArr, (program) => {
cacheManager.get(`program_details_${program}`, (err, cache) => {
if (err || !cache) {
filteredPrograms.push(program);
} else {
cacheData.push(cache);
}
});
});
if (filteredPrograms.length) {
promiseRequests = _.map(filteredPrograms, (program) => {
return [programServiceHelper.getCollectionWithProgramId(program, req), programServiceHelper.getSampleContentWithProgramId(program, req),
programServiceHelper.getContributionWithProgramId(program, req), programServiceHelper.getNominationWithProgramId(program)];
});
forkJoin(..._.flatMapDeep(promiseRequests)).subscribe((responseData) => {
try{
const combainedRes = _.chunk(responseData, 4);
const programDetailsArray = programServiceHelper.handleMultiProgramDetails(combainedRes);
const tableData = _.reduce(programDetailsArray, (final, data, index) => {
final.push({program_id: filteredPrograms[index], values: data});
return final;
}, []);
_.forEach(tableData, (obj) => {
cacheManager.set({ key: `program_details_${obj.program_id}`, value: obj },
function (err, cacheCSVData) {
if (err) {
logger.error({msg: 'Error - caching', err, additionalInfo: {programDetails: obj}}, req)
} else {
logger.debug({msg: 'Caching nomination list - done', additionalInfo: {nominationData: obj}}, req)
}
});
});
rspObj.result = {
tableData: [...tableData, ...cacheData]
}
rspObj.responseCode = 'OK'
return res.status(200).send(successResponse(rspObj));
} catch (err) {
loggerError('Error due to unhandled exception',
rspObj.errCode, rspObj.errMsg, rspObj.responseCode, err, req)
return res.status(400).send(errorResponse(rspObj));
}
}, (err) => {
loggerError('Error http requests or nomination table query promise failure',
rspObj.errCode, rspObj.errMsg, rspObj.responseCode, err, req)
return res.status(400).send(errorResponse(rspObj));
});
}else {
rspObj.result = {
tableData: [...cacheData]
}
rspObj.responseCode = 'OK'
return res.status(200).send(successResponse(rspObj));
}
}
function aggregatedNominationCount(data, result) {
return new Promise((resolve, reject) => {
try {
......@@ -1310,3 +1369,4 @@ module.exports.healthAPI = health
module.exports.programCopyCollectionAPI = programCopyCollections;
module.exports.getAllConfigurationsAPI = getAllConfigurations;
module.exports.getConfigurationByKeyAPI = getConfigurationByKey;
module.exports.downloadProgramDetailsAPI = downloadProgramDetails
Supports Markdown
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