Skip to content

Commit

Permalink
API for viewing submissions and leaderboard (#90)
Browse files Browse the repository at this point in the history
* API for viewing submissions and leaderboard

* Remove duplicate check for contest moderator in getAllSubmissions API

Co-authored-by: ridhishjain <[email protected]>
  • Loading branch information
cjchirag7 and ridhishjain authored Dec 27, 2020
1 parent 9038986 commit aea784a
Show file tree
Hide file tree
Showing 19 changed files with 1,002 additions and 0 deletions.
131 changes: 131 additions & 0 deletions server/models/submissions/getAllSubmissions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
const { pool } = require('../database')

/**
*
* @param {Array} filterParams
* @return {String}
*/
function filterQuery(filterParams) {
if (!Array.isArray(filterParams)) {
return ''
}
const index = filterParams.find((param) => param.id === 'type')
if (index) {
filterParams.push(filterParams.splice(index, 1)[0])
}
return filterParams
.map(({ id, value }) =>
value.length
? ` ${id === 'type' ? 'HAVING' : 'AND'} ${id} IN (${value
.map((val) => (typeof val === 'string' ? `'${val}'` : val))
.join(',')}) `
: ''
)
.join('')
}

/**
*
* @param {Array} sortParams
* @return {String}
*/
function sortQuery(sortParams) {
if (!Array.isArray(sortParams)) {
return ''
}
return (
' ORDER BY ' +
sortParams
.map(({ id, desc }) => `${id} ${desc ? 'DESC' : 'ASC'}`)
.join(', ')
)
}

/**
* @param {*} param0
* @param {String} param0.username
* @param {Object} param0.params
* @param {Object} param0.query
* @return {Promise}
*/

function getAllSubmissions({ username, params, query }) {
return new Promise((resolve, reject) => {
const { contestId } = params
const {
search,
sort,
page,
size,
download_csv: downloadCSV,
filters,
} = query

let countQuery = `SELECT COUNT(*) AS total FROM
(SELECT id, 'subjective' AS type FROM subjective_submissions WHERE contest_id=? `
let mainQuery = `SELECT s.id, s.question_id, q.name, s.type, s.username, s.submission_time, s.score, s.judged FROM
(SELECT id, question_id, 'subjective' AS type, username, submission_time, score, judged FROM subjective_submissions
WHERE contest_id=? `

if (search) {
countQuery += ` AND username LIKE '${search}%' `
mainQuery += ` AND username LIKE '${search}%' `
}

if (filters) {
countQuery += filterQuery(JSON.parse(filters))
mainQuery += filterQuery(JSON.parse(filters))
}

countQuery += ` UNION ALL SELECT id, 'mcq' AS type FROM mcq_submissions WHERE contest_id=? `

mainQuery += ` UNION ALL SELECT id, question_id, 'mcq' AS type, username, submission_time, score, judged FROM mcq_submissions
WHERE contest_id=? `
const mainQueryArr = [contestId, contestId]

if (search) {
countQuery += ` AND username LIKE '${search}%' `
mainQuery += ` AND username LIKE '${search}%' `
}

if (filters) {
countQuery += filterQuery(JSON.parse(filters))
mainQuery += filterQuery(JSON.parse(filters))
}

countQuery += `) s WHERE EXISTS (SELECT 1 FROM contests_moderators WHERE contest_id=? AND moderator=?) `
mainQuery += `) s INNER JOIN questions q ON q.id=s.question_id `

const countQueryArr = [contestId, contestId, contestId, username]

if (sort) {
mainQuery += sortQuery(JSON.parse(sort))
}

pool.query(countQuery, countQueryArr, (error, countResults) => {
if (error || !Array.isArray(countResults) || !countResults.length) {
reject(error || 'Invalid Query')
}

const totalRows = countResults[0].total
if (!totalRows) {
return resolve({ submissions: [], page_count: totalRows })
}
const pageSize = downloadCSV ? totalRows : parseInt(size, 10) || 10
const numPages = Math.ceil(totalRows / pageSize)
const pageIndex = downloadCSV ? 0 : parseInt(page, 10) || 0
const offset = pageSize * pageIndex

mainQuery += ` LIMIT ?,?`
mainQueryArr.push(offset, pageSize)
pool.query(mainQuery, mainQueryArr, (error, results) => {
if (error || results === undefined) {
return reject(error)
}
return resolve({ submissions: results, page_count: numPages })
})
})
})
}

module.exports = getAllSubmissions
117 changes: 117 additions & 0 deletions server/models/submissions/getContestLeaderboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
const { pool } = require('../database')

/**
*
* @param {Array} filterParams
* @return {String}
*/
function filterQuery(filterParams) {
if (!Array.isArray(filterParams)) {
return ''
}
return filterParams
.map(({ id, value }) =>
value.length
? ` AND ${id} IN (${value
.map((val) => (typeof val === 'string' ? `'${val}'` : val))
.join(',')}) `
: ''
)
.join('')
}

/**
*
* @param {Array} sortParams
* @return {String}
*/
function sortQuery(sortParams) {
if (!Array.isArray(sortParams)) {
return ''
}
return (
' ORDER BY ' +
sortParams
.map(
({ id, desc }) =>
`${id === 'rank' ? '`rank`' : id} ${desc ? 'DESC' : 'ASC'}`
)
.join(', ')
)
}

/**
* @param {*} param0
* @param {String} param0.username
* @param {Object} param0.params
* @param {Object} param0.query
* @return {Promise}
*/

function getContestLeaderboard({ username, params, query }) {
return new Promise((resolve, reject) => {
const { contestId } = params
const {
search,
sort,
page,
size,
download_csv: downloadCSV,
filters,
} = query

let countQuery = `SELECT COUNT(*) AS total
FROM leaderboard l
INNER JOIN users u ON l.username=u.username
WHERE contest_id=? AND
(EXISTS (SELECT 1 FROM contests_moderators WHERE contest_id=? AND moderator=?)
OR EXISTS(SELECT 1 FROM contests WHERE id=? AND show_leaderboard=1)) `
const countQueryArr = [contestId, contestId, username, contestId]

let mainQuery = `SELECT u.username, full_name, attempted_count, total_score, total_time,
RANK() OVER (PARTITION BY contest_id ORDER BY total_score DESC, total_time ASC) \`rank\` FROM leaderboard l
INNER JOIN users u ON l.username=u.username
WHERE contest_id=? `
const mainQueryArr = [contestId]

if (search) {
countQuery += ` AND (u.username LIKE '${search}%' OR u.full_name LIKE '${search}%' OR u.admission_number LIKE '${search}%') `
mainQuery += ` AND (u.username LIKE '${search}%' OR u.full_name LIKE '${search}%' OR u.admission_number LIKE '${search}%') `
}

if (filters) {
countQuery += filterQuery(JSON.parse(filters))
mainQuery += filterQuery(JSON.parse(filters))
}

if (sort) {
mainQuery += sortQuery(JSON.parse(sort))
}

pool.query(countQuery, countQueryArr, (error, countResults) => {
if (error || !Array.isArray(countResults) || !countResults.length) {
reject(error || 'Invalid Query')
}

const totalRows = countResults[0].total
if (!totalRows) {
return resolve({ leaderboard: [], page_count: totalRows })
}
const pageSize = downloadCSV ? totalRows : parseInt(size, 10) || 10
const numPages = Math.ceil(totalRows / pageSize)
const pageIndex = downloadCSV ? 0 : parseInt(page, 10) || 0
const offset = pageSize * pageIndex

mainQuery += ` LIMIT ?,?`
mainQueryArr.push(offset, pageSize)
pool.query(mainQuery, mainQueryArr, (error, results) => {
if (error || results === undefined) {
return reject(error)
}
return resolve({ leaderboard: results, page_count: numPages })
})
})
})
}

module.exports = getContestLeaderboard
43 changes: 43 additions & 0 deletions server/models/submissions/getMCQSubmission.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const { pool } = require('../database')

/**
* @param {*} param0
* @param {String} param0.username
* @param {Object} param0.params
* @param {Object} param0.query
* @return {Promise}
*/

function getMCQSubmission({ username, params, query }) {
return new Promise((resolve, reject) => {
const { contestId, submissionId } = params
const { moderator } = query
let queryString, queryArr
if (moderator) {
queryString = `SELECT question_id, contest_id, username, response, submission_time, score, judged
FROM mcq_submissions
WHERE id=? AND EXISTS(SELECT 1 FROM contests_moderators WHERE contest_id=? AND moderator=?)`
queryArr = [submissionId, contestId, username]
} else {
queryString = `SELECT question_id, contest_id, username, response, submission_time,
score*EXISTS(SELECT 1 FROM contests WHERE id=? AND (NOW()>end_time OR show_leaderboard=1)) AS score, judged
FROM mcq_submissions
WHERE id=? AND username=? AND EXISTS(SELECT 1 FROM contests WHERE id=?
AND ((NOW()>=start_time AND NOW()<=end_time)
OR (NOW()>end_time AND confidential_questions=0)))`
queryArr = [contestId, submissionId, username, contestId]
}

pool.query(queryString, queryArr, (error, results) => {
if (error) {
reject(error)
}
if (!results.length) {
reject('You do not have permission to view this submission')
}
return resolve(results)
})
})
}

module.exports = getMCQSubmission
Loading

0 comments on commit aea784a

Please sign in to comment.