diff --git a/admin/index.html b/admin/index.html index be11af0..afa0039 100644 --- a/admin/index.html +++ b/admin/index.html @@ -32,6 +32,12 @@

General

second(s)
Set
+
+ Spam Penalty Threshold: + + open PRs/issues (0 = disabled) +
Set
+

Contributors Management

diff --git a/admin/src/index.js b/admin/src/index.js index bcd1573..c040a07 100644 --- a/admin/src/index.js +++ b/admin/src/index.js @@ -36,11 +36,13 @@ submit.addEventListener('click', () => { const table = document.querySelector('.contributors-list') const totalTd = document.querySelector('td.total') const startDateInput = document.querySelector('.set-start-date') + const spamPenaltyInput = document.querySelector('.set-spam-penalty') loginPanel.classList.add('hide') // hide loading animation configPanel.classList.remove('hide') intervalInput.setAttribute('placeholder', delay) startDateInput.setAttribute('value', startDate) + spamPenaltyInput.setAttribute('placeholder', res.data.spamPenaltyThreshold || 0) contributors = sortByAlphabet(contributors, 'username') @@ -182,6 +184,37 @@ submit.addEventListener('click', () => { }) }) + // Set spam penalty threshold + const setSpamPenaltyButton = document.querySelector('.set-spam-penalty-button.button') + setSpamPenaltyButton.addEventListener('click', () => { + const threshold = document.querySelector('.set-spam-penalty').value + + if (threshold === '') { + msgError('your input is empty') + return + } + + if (parseInt(threshold) < 0) { + msgError('Threshold cannot be negative') + return + } + + axios.post('/api/setSpamPenaltyThreshold', { + token: password, + spamPenaltyThreshold: threshold + }).then( res => { + const { message } = res.data + + if (message === 'Success') { + mgsSuccess('Spam penalty threshold updated!') + spamPenaltyInput.value = '' + spamPenaltyInput.setAttribute('placeholder', threshold) + } else { + msgError('Unexpected error') + } + }) + }) + // Set interval value const setIntervalButton = document.querySelector('.set-interval-button.button') setIntervalButton.addEventListener('click', () => { diff --git a/src/index.js b/src/index.js index 2729b02..1f727da 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,8 @@ import axios from 'axios' import moment from 'moment' import { io } from 'socket.io-client' +let spamPenaltyThreshold = 0 + function refreshTable(newData) { const table = document.querySelector('table') const data = newData @@ -24,6 +26,15 @@ function refreshTable(newData) { totalEm.innerText = 'Total: ' + totalNumbers contributors = contributors.sort((a, b) => { + if (spamPenaltyThreshold > 0) { + const aPenalized = a.openPRsNumber >= spamPenaltyThreshold || a.issuesNumber >= spamPenaltyThreshold + const bPenalized = b.openPRsNumber >= spamPenaltyThreshold || b.issuesNumber >= spamPenaltyThreshold + const aTopTier = !aPenalized && a.mergedPRsNumber > 0 + const bTopTier = !bPenalized && b.mergedPRsNumber > 0 + if (aTopTier && !bTopTier) return -1 + if (!aTopTier && bTopTier) return 1 + } + var pref1, pref2, pref3 // preference is specified here const queryString = window.location.search const urlParams = new URLSearchParams(queryString) @@ -145,18 +156,18 @@ function refreshTable(newData) { allContributionsInfoRef.innerText = allMergedPRs + ' Merged PRs, ' + allOpenPRs + ' Open PRs, and ' + allIssues + ' Issues.' } -axios.get('/api/data') - .then(res => { - refreshTable(res.data) - }) - axios.get('/api/config') .then(res => { const { organization, organizationGithubUrl, organizationHomepage } = res.data + spamPenaltyThreshold = res.data.spamPenaltyThreshold || 0 const footer = document.querySelector('.footer .text-muted') footer.innerHTML = ` ${organizationHomepage} | Github(${organization})`.trim() + return axios.get('/api/data') + }) + .then(res => { + refreshTable(res.data) }) axios.get('/api/log') diff --git a/src/server/app.js b/src/server/app.js index 666970f..becaff5 100644 --- a/src/server/app.js +++ b/src/server/app.js @@ -93,6 +93,7 @@ const server = http organization: organization, organizationHomepage: organizationHomepage, organizationGithubUrl: organizationGithubUrl, + spamPenaltyThreshold: parseInt(jsonfile.readFileSync(configPath).spamPenaltyThreshold) || 0, }) ) break @@ -137,6 +138,7 @@ const server = http delay, contributors: contributorsList, startDate, + spamPenaltyThreshold: parseInt(jsonfile.readFileSync(configPath).spamPenaltyThreshold) || 0, }) ) // success jsonfile.writeFileSync(admindataPath, contributorsList) @@ -238,6 +240,27 @@ const server = http } }) break + case '/setSpamPenaltyThreshold': + if (req.method === 'GET') { + res.end('Permission denied\n') + return + } + + Util.post(req, (params) => { + const { token, spamPenaltyThreshold } = params + + if (token !== adminPassword) { + res.end(JSON.stringify({ message: 'Authentication failed' })) + } else { + const Config = jsonfile.readFileSync(configPath) + Config.spamPenaltyThreshold = parseInt(spamPenaltyThreshold) || 0 + jsonfile.writeFileSync(configPath, Config, { spaces: 2 }) + jsonfile.writeFileSync(configBackupPath, Config, { spaces: 2 }) + + res.end(JSON.stringify({ message: 'Success' })) + } + }) + break case '/remove': if (req.method === 'GET') { res.end('Permission denied\n') @@ -350,10 +373,11 @@ const server = http jsonfile.readFile(dataPath, async (err, obj) => { if (err) console.log('[ERROR]' + err) const query = url.parse(req.url, true).query + const Config = jsonfile.readFileSync(configPath) // Gets list of contributors sorted by parameter if provided // else defaults to sorting by mergedprs - const contributors = await API.getRanks(obj, query.parameter) + const contributors = await API.getRanks(obj, query.parameter, Config.spamPenaltyThreshold) // Responds with rank of username if (query.username) { @@ -400,7 +424,8 @@ const server = http } else if (query.rank) { // Gets list of contributors sorted by parameter if provided // else defaults to sorting by mergedprs - const contributors = await API.getRanks(obj, query.parameter) + const Config = jsonfile.readFileSync(configPath) + const contributors = await API.getRanks(obj, query.parameter, Config.spamPenaltyThreshold) res.end(JSON.stringify(obj[contributors[query.rank - 1]])) } else { res.end(JSON.stringify(obj)) diff --git a/src/server/config-example.json b/src/server/config-example.json index 0937fa2..e59dc37 100644 --- a/src/server/config-example.json +++ b/src/server/config-example.json @@ -134,6 +134,7 @@ "abdelazeem777\t" ], "startDate": "1970-01-15", + "spamPenaltyThreshold": 10, "includedRepositories": [ "Rocket.Chat", "Opensource-Contribution-Leaderboard", diff --git a/src/server/util/API.js b/src/server/util/API.js index 71b3c8d..9514a67 100644 --- a/src/server/util/API.js +++ b/src/server/util/API.js @@ -186,7 +186,7 @@ async function getStats(data) { } } -async function getRanks(data, parameter = 'mergedprs') { +async function getRanks(data, parameter = 'mergedprs', spamPenaltyThreshold = 0) { var pref1, pref2, pref3 // preference is specified here switch ( parameter //assigns according to parameter-sort (default 'mergedprs') @@ -209,8 +209,19 @@ async function getRanks(data, parameter = 'mergedprs') { break } + const threshold = parseInt(spamPenaltyThreshold) || 0 + const contributors = Object.keys(data) return contributors.sort((a, b) => { + if (threshold > 0) { + const aPenalized = data[a].openPRsNumber >= threshold || data[a].issuesNumber >= threshold + const bPenalized = data[b].openPRsNumber >= threshold || data[b].issuesNumber >= threshold + const aTopTier = !aPenalized && data[a].mergedPRsNumber > 0 + const bTopTier = !bPenalized && data[b].mergedPRsNumber > 0 + if (aTopTier && !bTopTier) return -1 + if (!aTopTier && bTopTier) return 1 + } + if (data[a][pref1] < data[b][pref1]) { return 1 }