diff options
author | Galen Guyer <galen@galenguyer.com> | 2022-08-11 13:36:23 -0400 |
---|---|---|
committer | Galen Guyer <galen@galenguyer.com> | 2022-08-11 13:36:23 -0400 |
commit | 58cf6ea82f3e9e3923ead4d4a39579b92072b7f9 (patch) | |
tree | 45180ea4a8438f93a91568f25199315e32cb78ad | |
parent | df2d07e98c1e7a6fa341d75af7ec035eb8bf031f (diff) |
Render ranked choice vote
-rw-r--r-- | database/ranked_vote.go | 27 | ||||
-rw-r--r-- | database/simple_vote.go | 19 | ||||
-rw-r--r-- | database/vote.go | 25 | ||||
-rw-r--r-- | main.go | 9 | ||||
-rw-r--r-- | templates/poll.tmpl | 50 |
5 files changed, 110 insertions, 20 deletions
diff --git a/database/ranked_vote.go b/database/ranked_vote.go new file mode 100644 index 0000000..5263ede --- /dev/null +++ b/database/ranked_vote.go @@ -0,0 +1,27 @@ +package database + +import ( + "context" + "time" + + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type RankedVote struct { + Id string `bson:"_id,omitempty"` + PollId primitive.ObjectID `bson:"pollId"` + UserId string `bson:"userId"` + Options map[string]int `bson:"option"` +} + +func CastRankedVote(vote *RankedVote) error { + ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second) + defer cancel() + + _, err := Client.Database("vote").Collection("votes").InsertOne(ctx, vote) + if err != nil { + return err + } + + return nil +} diff --git a/database/simple_vote.go b/database/simple_vote.go index ef97f6c..0adea06 100644 --- a/database/simple_vote.go +++ b/database/simple_vote.go @@ -14,7 +14,7 @@ type SimpleVote struct { Option string `bson:"option"` } -func CastVote(vote *SimpleVote) error { +func CastSimpleVote(vote *SimpleVote) error { ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second) defer cancel() @@ -25,20 +25,3 @@ func CastVote(vote *SimpleVote) error { return nil } - -func HasVoted(pollId, userId string) (bool, error) { - ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second) - defer cancel() - - pId, err := primitive.ObjectIDFromHex(pollId) - if err != nil { - return false, err - } - - count, err := Client.Database("vote").Collection("votes").CountDocuments(ctx, map[string]interface{}{"pollId": pId, "userId": userId}) - if err != nil { - return false, err - } - - return count > 0, nil -} diff --git a/database/vote.go b/database/vote.go new file mode 100644 index 0000000..6583820 --- /dev/null +++ b/database/vote.go @@ -0,0 +1,25 @@ +package database + +import ( + "context" + "time" + + "go.mongodb.org/mongo-driver/bson/primitive" +) + +func HasVoted(pollId, userId string) (bool, error) { + ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second) + defer cancel() + + pId, err := primitive.ObjectIDFromHex(pollId) + if err != nil { + return false, err + } + + count, err := Client.Database("vote").Collection("votes").CountDocuments(ctx, map[string]interface{}{"pollId": pId, "userId": userId}) + if err != nil { + return false, err + } + + return count > 0, nil +} @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "net/http" "os" "sort" @@ -175,11 +176,17 @@ func main() { return } + writeInAdj := 0 + if poll.AllowWriteIns { + writeInAdj = 1 + } c.HTML(200, "poll.tmpl", gin.H{ "Id": poll.Id, "ShortDescription": poll.ShortDescription, "LongDescription": poll.LongDescription, "Options": poll.Options, + "PollType": poll.VoteType, + "RankedMax": fmt.Sprint(len(poll.Options) + writeInAdj), "AllowWriteIns": poll.AllowWriteIns, "Username": claims.UserInfo.Username, "FullName": claims.UserInfo.FullName, @@ -234,7 +241,7 @@ func main() { c.JSON(400, gin.H{"error": "Invalid Option"}) return } - database.CastVote(&vote) + database.CastSimpleVote(&vote) } else { c.JSON(500, gin.H{"error": "Unknown Poll Type"}) return diff --git a/templates/poll.tmpl b/templates/poll.tmpl index b81ee05..6c6e2c2 100644 --- a/templates/poll.tmpl +++ b/templates/poll.tmpl @@ -5,6 +5,11 @@ <!-- <link rel="stylesheet" href="https://themeswitcher.csh.rit.edu/api/get" /> --> <link rel="stylesheet" href="https://assets.csh.rit.edu/csh-material-bootstrap/4.3.1/dist/csh-material-bootstrap.min.css" media="screen"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <style> + input[type='number']{ + width: 32px; + } + </style> </head> <body> <nav class="navbar navbar-expand-lg navbar-dark bg-primary"> @@ -25,15 +30,20 @@ {{ if .LongDescription }} <h4>{{ .LongDescription }}</h4> {{ end }} + {{ if eq .PollType "ranked" }} + <p>This is a Ranked Choice vote. Rank the candidates in order of your preference. 1 is most preferred, and {{ .RankedMax }} is least perferred. You may leave an option blank + if you do not prefer it at all.</p> + {{ end }} <br /> <br /> <form action="/poll/{{ .Id }}" method="POST"> + {{ if eq .PollType "simple" }} {{ range $i, $option := .Options }} <div class="form-check"> <input class="form-check-input" type="radio" name="option" value="{{ $option }}" /> - <label style="font-size: 1.25rem; line-height: 1.25;" class="form-check-label" for="{{ $option }}">{{ $option }}</label> + <label style="font-size: 1.25rem; line-height: 1.25; padding-left: 4px;" class="form-check-label" for="{{ $option }}">{{ $option }}</label> </div> <br /> {{ end }} @@ -44,11 +54,49 @@ type="text" name="writeinOption" class="form-control" + style="height: 1.5em; padding-left: 4px;" + placeholder="Write-In" + /> + </div> + {{ end }} + {{ end }} + + {{ if eq .PollType "ranked" }} + {{ $rankedMax := .RankedMax }} + {{ range $i, $option := .Options }} + <div class="form-check" style="display: flex;"> + <input + type="number" + name="{{ $option }}" + class="form-control" + style="height: 1.5em;" + min="0" + max="{{ $rankedMax }}" + /> + <label style="font-size: 1.25rem; line-height: 1.25; padding-left: 12px;" class="form-check-label" for="{{ $option }}">{{ $option }}</label> + </div> + <br /> + {{ end }} + {{ if .AllowWriteIns }} + <div class="form-check" style="display: flex;"> + <input + type="number" + name="writein" + class="form-control" style="height: 1.5em;" + min="0" + max="{{ $rankedMax }}" + /> + <input + type="text" + name="writeinOption" + class="form-control" + style="height: 1.5em; padding-left: 12px;" placeholder="Write-In" /> </div> {{ end }} + {{ end }} <br /> <button type="submit" class="btn btn-primary">Submit</button> </form> |