Skip to content

Instantly share code, notes, and snippets.

@Allen-B1
Last active April 18, 2020 17:50
Show Gist options
  • Save Allen-B1/32decdcc4f5b01713e36332ba867ffb7 to your computer and use it in GitHub Desktop.
Save Allen-B1/32decdcc4f5b01713e36332ba867ffb7 to your computer and use it in GitHub Desktop.
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