Last active
April 18, 2020 17:50
-
-
Save Allen-B1/32decdcc4f5b01713e36332ba867ffb7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const DEBUG = true; | |
const APP_FORM_URL = ""; | |
const ELECTION_FORM_URL = ""; | |
const SHEET_URL = ""; | |
const WEBHOOK_URL = "https://discordapp.com/api/webhooks/.../"; | |
const ROLE = ""; | |
const ELECTION_NAME = "2020 Computer Elections"; | |
const APP_ELIGIBLE = ["[email protected]"]; | |
const VOTE_ELIGIBLE = ["[email protected]"]; | |
const POSITIONS = { | |
"President": ["The two Presidents are responsible for leading the club. Though they have a say on all decisions, " + | |
"their role is not to make the decisions for the team, but to guide the club towards a consensus, giving advice along the way to ensure the final decision is appropriate and made in a timely manner.", 2], | |
"Treasurer": ["The Treasurer is responsible for planning outside events, handling papers and forms, setting up meetings, and handling any of the club's finances.", 1], | |
"Secretary": ["The Secretary is responsible for attendance, creating detailed records of meetings, and any other administrative tasks necessary for the function of the team.", 1] | |
}; | |
const ROUNDS = [ | |
null, | |
[1, "President", "Treasurer"], | |
[1, "Secretary"] | |
]; | |
// Time between opening & closing of rounds / applications | |
const INTERVAL = "1 week"; | |
function startApplications() { | |
var form = FormApp.openByUrl(APP_FORM_URL); | |
form.deleteAllResponses(); | |
form.setCollectEmail(true); | |
form.setLimitOneResponsePerUser(DEBUG ? false : true); | |
form.setAllowResponseEdits(true); | |
form.setAcceptingResponses(false); | |
form.setTitle(ELECTION_NAME + " Application Form"); | |
form.setDescription("Fill out this form to run in the " + ELECTION_NAME); | |
for (var item of form.getItems()) { | |
form.deleteItem(item); | |
} | |
var nameItem = form.addTextItem(); | |
nameItem.setTitle("Full Name"); | |
nameItem.setRequired(true); | |
for (let i = 0; i < ROUNDS.length; i++) { | |
let round = ROUNDS[i]; | |
if (round == null) continue; | |
let candidacyItem = form.addCheckboxItem(); | |
candidacyItem.setRequired(false); | |
candidacyItem.setTitle("Round " + i + " Positions"); | |
let max = round[0]; | |
if (max == 0) continue; | |
candidacyItem.setValidation( | |
FormApp.createCheckboxValidation() | |
.requireSelectAtMost(max) | |
.build() | |
); | |
candidacyItem.setChoiceValues(round.slice(1)); | |
} | |
Utilities.sleep(1000); | |
form.setAcceptingResponses(true); | |
_sendMessage(ROLE + "Applications for the " + ELECTION_NAME + " are currently open! " + form.getPublishedUrl()); | |
_sendMessage("**Eligible Applicants**\n```\n" + APP_ELIGIBLE.join("\n") + "\n```"); | |
} | |
function showApplications() { | |
var spreadsheet = SpreadsheetApp.openByUrl(SHEET_URL); | |
spreadsheet.getRange("Internal!A1").setValue(0); | |
spreadsheet.getSheetByName("Results").clearContents(); | |
_updateSheetApplicants(); | |
_sendMessage(ROLE + "Application results can be found at " + spreadsheet.getUrl() + ". You may still change your application."); | |
} | |
function startRound() { | |
_updateSheetApplicants(); | |
var spreadsheet = SpreadsheetApp.openByUrl(SHEET_URL); | |
let round = (0|spreadsheet.getRange("Internal!A1").getValue()); | |
round += 1; | |
spreadsheet.getRange("Internal!A1").setValue(round); | |
var form = FormApp.openByUrl(ELECTION_FORM_URL); | |
// Reset Form | |
form.deleteAllResponses(); | |
for (var item of form.getItems()) { | |
form.deleteItem(item); | |
} | |
form.setCollectEmail(true); | |
form.setLimitOneResponsePerUser(DEBUG ? false : true); | |
form.setAllowResponseEdits(true) | |
form.setAcceptingResponses(false); | |
form.setTitle(ELECTION_NAME + " Round " + round); | |
if (round >= ROUNDS.length) { | |
return; | |
} | |
// Get List of Applicants from Sheet | |
let applicants = {}; | |
let applicantsSheet = spreadsheet.getSheetByName("Applicants"); | |
for (let row = 2; true; row++) { | |
let name = applicantsSheet.getRange("A" + row).getValue(); | |
if (!name) break; | |
let positions = String(applicantsSheet.getRange("C" + row).getValue()).split(", "); | |
for (let position of positions) { | |
if (!applicants[position]) applicants[position] = []; | |
applicants[position].push(name); | |
} | |
} | |
// Create Form | |
let roundData = ROUNDS[round]; | |
for (let position of roundData.slice(1)) { | |
let item = form.addCheckboxItem(); | |
item.setTitle(position); | |
item.setHelpText(POSITIONS[position][0]); | |
if (applicants[position] instanceof Array) { | |
item.setChoiceValues(applicants[position]); | |
} | |
} | |
Utilities.sleep(1000); | |
form.setAcceptingResponses(true); | |
_sendMessage(ROLE + "Round " + round + " of the " + ELECTION_NAME + " has begun! " + form.getPublishedUrl()); | |
_sendMessage("**Eligible Voters**\n```\n" + VOTE_ELIGIBLE.join("\n") + "\n```"); | |
} | |
function showResults() { | |
_updateSheetApplicants(); | |
var spreadsheet = SpreadsheetApp.openByUrl(SHEET_URL); | |
let round = (0|spreadsheet.getRange("Internal!A1").getValue()); | |
if (round <= 0) return; | |
var form = FormApp.openByUrl(ELECTION_FORM_URL); | |
form.setAcceptingResponses(false); | |
// Store Previous Results | |
let results = {}; | |
let voters = []; | |
let invalids = []; | |
for (let response of form.getResponses()) { | |
if (VOTE_ELIGIBLE.indexOf(response.getRespondentEmail()) == -1) { | |
invalids.push(response.getRespondentEmail()); | |
continue; | |
} else { | |
voters.push(response.getRespondentEmail()); | |
} | |
for (let item of response.getItemResponses()) { | |
let position = item.getItem().getTitle(); | |
for (let applicant of item.getResponse()) { | |
if (!results[position]) results[position] = {}; | |
results[position][applicant] = (results[position][applicant] | 0) + 1; | |
} | |
} | |
} | |
_updateSheetResults(results); | |
_sendMessage(ROLE + "Results for Round " + round + " are out! " + spreadsheet.getUrl()); | |
_sendMessage("These people voted: `" + voters.join("`, `") + "`"); | |
if (invalids.length != 0) | |
_sendMessage("These people tried to vote but are ineligible: `" + invalids.join("`, `") + "`"); | |
} | |
// Retrieves application form responses and puts onto sheet | |
function _updateSheetApplicants() { | |
// TODO: Check Eligibility | |
var form = FormApp.openByUrl(APP_FORM_URL); | |
var spreadsheet = SpreadsheetApp.openByUrl(SHEET_URL); | |
var sheet = spreadsheet.getSheetByName("Applicants"); | |
sheet.clearContents(); | |
sheet.getRange("A1").setValue("Name"); | |
sheet.getRange("B1").setValue("Email"); | |
sheet.getRange("C1").setValue("Positions"); | |
sheet.getRange("A1:C1").setFontWeight("bold"); | |
let row = 2; | |
for (var response of form.getResponses()) { | |
if (APP_ELIGIBLE.indexOf(response.getRespondentEmail()) == -1) { | |
_sendMessage("`" + response.getRespondentEmail() + "` tried to apply, but is not eligible"); | |
continue; | |
} | |
let items = response.getItemResponses(); | |
let name = items[0].getResponse(); | |
let positions = []; | |
for (let item of items.slice(1)) { | |
positions = positions.concat(item.getResponse()); | |
} | |
sheet.getRange("A"+row).setValue(name); | |
sheet.getRange("B"+row).setValue(response.getRespondentEmail()); | |
sheet.getRange("C"+row).setValue(positions.join(", ")); | |
row++; | |
} | |
} | |
// Puts given data onto sheet | |
function _updateSheetResults(results) { | |
var spreadsheet = SpreadsheetApp.openByUrl(SHEET_URL); | |
var sheet = spreadsheet.getSheetByName("Results"); | |
for (let position in results) { | |
let column; | |
for (column = 0; true; column += 2) { | |
let columnLetter = String.fromCharCode("A".charCodeAt(0) + column); | |
let columnLetterNext = String.fromCharCode("A".charCodeAt(0) + column+1); | |
let cell = sheet.getRange(columnLetter + "1"); | |
if (cell.getValue()) { | |
continue; | |
} | |
cell.setValue(position); | |
cell.setFontWeight("bold"); | |
sheet.getRange(columnLetter + "1:" + columnLetterNext + "1").merge(); | |
// Find threshold (# of votes required to win position) | |
let threshold = 0; | |
{ | |
let votes = Object.values(results[position]); | |
votes.sort().reverse(); | |
threshold = votes[POSITIONS[position][1] - 1]; | |
} | |
let row = 2; | |
for (let applicant in results[position]) { | |
let votes = results[position][applicant]; | |
let weight = votes >= threshold ? "bold" : "normal"; | |
sheet.getRange(columnLetter + row).setValue(applicant).setFontWeight(weight); | |
sheet.getRange(columnLetterNext + row).setValue(votes).setFontWeight(weight); | |
row += 1; | |
} | |
break; | |
} | |
} | |
} | |
function _sendMessage(msg) { | |
var payload = { | |
"username": "Election Bot", | |
"content": msg, | |
}; | |
UrlFetchApp.fetch(WEBHOOK_URL, { | |
"method": "POST", | |
"contentType": "application/json", | |
"payload": JSON.stringify(payload) | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment