@@ -4,9 +4,12 @@ import (
44 "context"
55 "time"
66
7+ "github.com/sirupsen/logrus"
78 "go.mongodb.org/mongo-driver/bson"
89 "go.mongodb.org/mongo-driver/bson/primitive"
910 "go.mongodb.org/mongo-driver/mongo"
11+
12+ "github.com/computersciencehouse/vote/logging"
1013)
1114
1215type Poll struct {
@@ -178,6 +181,88 @@ func GetClosedVotedPolls(ctx context.Context, userId string) ([]*Poll, error) {
178181 return polls , nil
179182}
180183
184+ func calculateRankedResult (votesRaw []RankedVote ) ([]map [string ]int , error ) {
185+ // We want to store those that were eliminated
186+ eliminated := make ([]string , 0 )
187+ votes := make ([][]string , 0 )
188+ finalResult := make ([]map [string ]int , 0 )
189+
190+ //change ranked votes from a map (which is unordered) to a slice of votes (which is ordered)
191+ //order is from first preference to last preference
192+ for _ , vote := range votesRaw {
193+ temp , cf := context .WithTimeout (context .Background (), 1 * time .Second )
194+ optionList := orderOptions (vote .Options , temp )
195+ cf ()
196+ votes = append (votes , optionList )
197+ }
198+
199+ round := 0
200+ // Iterate until we have a winner
201+ for {
202+ round = round + 1
203+ // Contains candidates to number of votes in this round
204+ tallied := make (map [string ]int )
205+ voteCount := 0
206+ for _ , picks := range votes {
207+ // Go over picks until we find a non-eliminated candidate
208+ for _ , candidate := range picks {
209+ if ! containsValue (eliminated , candidate ) {
210+ if _ , ok := tallied [candidate ]; ok {
211+ tallied [candidate ]++
212+ } else {
213+ tallied [candidate ] = 1
214+ }
215+ voteCount += 1
216+ break
217+ }
218+ }
219+ }
220+ // Eliminate lowest vote getter
221+ minVote := 1000000 //the smallest number of votes received thus far (to find who is in last)
222+ minPerson := make ([]string , 0 ) //the person(s) with the least votes that need removed
223+ for person , vote := range tallied {
224+ if vote < minVote { // this should always be true round one, to set a true "who is in last"
225+ minVote = vote
226+ minPerson = make ([]string , 0 )
227+ minPerson = append (minPerson , person )
228+ } else if vote == minVote {
229+ minPerson = append (minPerson , person )
230+ }
231+ }
232+ eliminated = append (eliminated , minPerson ... )
233+ finalResult = append (finalResult , tallied )
234+
235+ // TODO this should probably include some poll identifier
236+ logging .Logger .WithFields (logrus.Fields {"round" : round , "tallies" : tallied , "threshold" : voteCount / 2 }).Debug ("round report" )
237+
238+ // If one person has all the votes, they win
239+ if len (tallied ) == 1 {
240+ break
241+ }
242+
243+ end := true
244+ for str , val := range tallied {
245+ // if any particular entry is above half remaining votes, they win and it ends
246+ if val > (voteCount / 2 ) {
247+ finalResult = append (finalResult , map [string ]int {str : val })
248+ end = true
249+ break
250+ }
251+ // Check if all values in tallied are the same
252+ // In that case, it's a tie?
253+ if val != minVote {
254+ end = false
255+ break
256+ }
257+ }
258+ if end {
259+ break
260+ }
261+ }
262+ return finalResult , nil
263+
264+ }
265+
181266func (poll * Poll ) GetResult (ctx context.Context ) ([]map [string ]int , error ) {
182267 ctx , cancel := context .WithTimeout (ctx , 10 * time .Second )
183268 defer cancel ()
@@ -223,9 +308,6 @@ func (poll *Poll) GetResult(ctx context.Context) ([]map[string]int, error) {
223308 return finalResult , nil
224309
225310 case POLL_TYPE_RANKED :
226- // We want to store those that were eliminated
227- eliminated := make ([]string , 0 )
228-
229311 // Get all votes
230312 cursor , err := Client .Database (db ).Collection ("votes" ).Aggregate (ctx , mongo.Pipeline {
231313 {{
@@ -239,76 +321,7 @@ func (poll *Poll) GetResult(ctx context.Context) ([]map[string]int, error) {
239321 }
240322 var votesRaw []RankedVote
241323 cursor .All (ctx , & votesRaw )
242-
243- votes := make ([][]string , 0 )
244-
245- //change ranked votes from a map (which is unordered) to a slice of votes (which is ordered)
246- //order is from first preference to last preference
247- for _ , vote := range votesRaw {
248- temp , cf := context .WithTimeout (context .Background (), 1 * time .Second )
249- optionList := orderOptions (vote .Options , temp )
250- cf ()
251- votes = append (votes , optionList )
252- }
253-
254- // Iterate until we have a winner
255- for {
256- // Contains candidates to number of votes in this round
257- tallied := make (map [string ]int )
258- voteCount := 0
259- for _ , picks := range votes {
260- // Go over picks until we find a non-eliminated candidate
261- for _ , candidate := range picks {
262- if ! containsValue (eliminated , candidate ) {
263- if _ , ok := tallied [candidate ]; ok {
264- tallied [candidate ]++
265- } else {
266- tallied [candidate ] = 1
267- }
268- voteCount += 1
269- break
270- }
271- }
272- }
273- // Eliminate lowest vote getter
274- minVote := 1000000 //the smallest number of votes received thus far (to find who is in last)
275- minPerson := make ([]string , 0 ) //the person(s) with the least votes that need removed
276- for person , vote := range tallied {
277- if vote < minVote { // this should always be true round one, to set a true "who is in last"
278- minVote = vote
279- minPerson = make ([]string , 0 )
280- minPerson = append (minPerson , person )
281- } else if vote == minVote {
282- minPerson = append (minPerson , person )
283- }
284- }
285- eliminated = append (eliminated , minPerson ... )
286- finalResult = append (finalResult , tallied )
287- // If one person has all the votes, they win
288- if len (tallied ) == 1 {
289- break
290- }
291-
292- end := true
293- for str , val := range tallied {
294- // if any particular entry is above half remaining votes, they win and it ends
295- if val > (voteCount / 2 ) {
296- finalResult = append (finalResult , map [string ]int {str : val })
297- end = true
298- break
299- }
300- // Check if all values in tallied are the same
301- // In that case, it's a tie?
302- if val != minVote {
303- end = false
304- break
305- }
306- }
307- if end {
308- break
309- }
310- }
311- return finalResult , nil
324+ return calculateRankedResult (votesRaw )
312325 }
313326 return nil , nil
314327}
0 commit comments