Advertisement
Guest User

SPSV Script v0.2

a guest
Apr 7th, 2020
222
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. var ADDON_TITLE = 'SPSV Calculator';
  2. var NUM_KP_BALLOTS = 5;
  3.  
  4. function onOpen(e) {
  5.   FormApp.getUi()
  6.       .createAddonMenu()
  7.       .addItem('Run Election', 'runElection')
  8.       .addToUi();
  9. }
  10.  
  11. function onInstall(e) {
  12.   onOpen(e);
  13. }
  14.  
  15. function runElection() {
  16.   var form = FormApp.getActiveForm();
  17.   var responses = form.getResponses();
  18.   if(responses.length == 0)
  19.     return;
  20.  
  21.   formItems = form.getItems();
  22.  
  23.   num_candidates = 0;
  24.   candidate_names = [];
  25.   for(z = 0; z < formItems.length; z++)
  26.     if(formItems[z].getType() == FormApp.ItemType.SCALE)
  27.     {
  28.       num_candidates++;
  29.       candidate_names.push(formItems[z].getTitle());
  30.     }
  31.  
  32.   rounds = []
  33.   for (var i = 0; i < num_candidates; i++)
  34.     rounds[i] = "Round " + (i + 1);
  35.  
  36.   var spreadsheet = SpreadsheetApp.create(form.getTitle() + " Results");
  37.   url = spreadsheet.getUrl();
  38.   SpreadsheetApp.openByUrl(url);
  39.  
  40.   result_sheet = spreadsheet.getActiveSheet();
  41.   result_sheet.setName("Results");
  42.  
  43.   result_range = result_sheet.getDataRange();
  44.   result_values = result_range.getValues();
  45.    
  46.   num_ballots = responses.length;
  47.  
  48.   // create empty score lists
  49.   candidate_scores = [];
  50.   for(c = 0; c < num_candidates; c++)
  51.   {
  52.     candidate_scores[c] = [];
  53.     for(r = 0; r < num_candidates; r++)
  54.       candidate_scores[c][r] = 0;
  55.   }
  56.  
  57.   // perform the KP transform
  58.   approval_ballots = [];
  59.   for(a = 0; a < num_ballots; a++)
  60.   {
  61.     var response = responses[a];
  62.     var score_ballot = [];
  63.    
  64.     for(z = 0; z < formItems.length; z++)
  65.       if(formItems[z].getType() == FormApp.ItemType.SCALE)
  66.       {
  67.         var scoreResponse = response.getResponseForItem(formItems[z]);
  68.         if (scoreResponse != null)
  69.           score_ballot.push(parseInt(scoreResponse.getResponse()));
  70.         else
  71.           score_ballot.push(0);
  72.       }
  73.    
  74.     for(b = 0; b < NUM_KP_BALLOTS; b++)
  75.     {
  76.       var approval_ballot = [];
  77.       for(c = 0; c < num_candidates; c++)
  78.         approval_ballot[c] = score_ballot[c] > b;
  79.      
  80.       approval_ballots[a * NUM_KP_BALLOTS + b] = approval_ballot;
  81.     }
  82.   }
  83.  
  84.   // create initial ballot weight list
  85.   weights = [];
  86.   for(k = 0; k < approval_ballots.length; k++)
  87.     weights[k] = 1.0;
  88.  
  89.   // calculate scores and winners for each round
  90.   elected_candidates = [];
  91.   for(r = 0; r < num_candidates; r++)
  92.   {
  93.     // calculate scores
  94.     for(c = 0; c < num_candidates; c++)
  95.     {
  96.       if(elected_candidates.includes(c))
  97.         candidate_scores[c][r] = -1;
  98.       else
  99.         for(k = 0; k < approval_ballots.length; k++)
  100.           if(approval_ballots[k][c])
  101.             candidate_scores[c][r] += weights[k];
  102.     }
  103.    
  104.     // calculate winner
  105.     max_score = -1;
  106.     options = [];
  107.     for(c = 0; c < num_candidates; c++)
  108.     {
  109.       if(candidate_scores[c][r] > max_score)
  110.       {
  111.         max_score = candidate_scores[c][r];
  112.         options = [c];
  113.       }
  114.       else if(candidate_scores[c][r] == max_score)
  115.         options.push(c);
  116.     }
  117.    
  118.     if(options.length > 1)
  119.     {
  120.       // break tie by highest unweighted score
  121.       max_unweighted_score = -1;
  122.       new_options = [];
  123.       for(o = 0; o < options.length; o++)
  124.       {
  125.         option = options[o]
  126.         if(candidate_scores[option][0] > max_unweighted_score)
  127.         {
  128.           max_unweighted_score = candidate_scores[option][0];
  129.           new_options = [option];
  130.         }
  131.         else if(candidate_scores[option][0] == max_unweighted_score)
  132.           new_options.push(option);
  133.       }
  134.      
  135.       // break any remaining ties pseudorandomly
  136.       elected_candidates[r] = new_options[Math.floor(Math.random() * new_options.length)];
  137.     }
  138.     else
  139.       elected_candidates[r] = options[0];
  140.    
  141.     // calculate new weights
  142.     for(k = 0; k < approval_ballots.length; k++)
  143.     {
  144.       elected_candidates_approved = 0;
  145.       for(c = 0; c < num_candidates; c++)
  146.         if(approval_ballots[k][c] && elected_candidates.includes(c))
  147.           elected_candidates_approved++;
  148.      
  149.       weights[k] = 1.0 / (1 + elected_candidates_approved);
  150.     }
  151.   }
  152.  
  153.   // write results to sheet
  154.   result_sheet.appendRow(['Election Results:']);
  155.   result_sheet.appendRow(['Candidate'].concat(rounds));
  156.  
  157.   for(c = 0; c < num_candidates; c++)
  158.   {
  159.     result_sheet.appendRow([candidate_names[c]].concat(candidate_scores[c]));
  160.     result_sheet.getRange(3 + c, 2 + elected_candidates.indexOf(c)).setBackgroundRGB(128, 255, 128);
  161.   }
  162.  
  163.   result_sheet.getRange(3, 2, num_candidates, num_candidates).setNumberFormat("0.##");
  164.  
  165.   result_sheet.appendRow([' ']);
  166.   result_sheet.appendRow(['Winners'].concat(elected_candidates));
  167. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement