aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGalen Guyer <galen@galenguyer.com>2022-08-11 13:36:23 -0400
committerGalen Guyer <galen@galenguyer.com>2022-08-11 13:36:23 -0400
commit58cf6ea82f3e9e3923ead4d4a39579b92072b7f9 (patch)
tree45180ea4a8438f93a91568f25199315e32cb78ad
parentdf2d07e98c1e7a6fa341d75af7ec035eb8bf031f (diff)
Render ranked choice vote
-rw-r--r--database/ranked_vote.go27
-rw-r--r--database/simple_vote.go19
-rw-r--r--database/vote.go25
-rw-r--r--main.go9
-rw-r--r--templates/poll.tmpl50
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
+}
diff --git a/main.go b/main.go
index 177d9a1..fdba12c 100644
--- a/main.go
+++ b/main.go
@@ -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>