Skip to content

Commit

Permalink
add Go implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
showwin committed Jun 17, 2018
1 parent eafa60b commit 148f9e1
Show file tree
Hide file tree
Showing 10 changed files with 597 additions and 0 deletions.
131 changes: 131 additions & 0 deletions webapp/go/candidate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package main

// Candidate Model
type Candidate struct {
ID int
Name string
PoliticalParty string
Sex string
}

// CandidateElectionResult type
type CandidateElectionResult struct {
ID int
Name string
PoliticalParty string
Sex string
VoteCount int
}

// PartyElectionResult type
type PartyElectionResult struct {
PoliticalParty string
VoteCount int
}

// PartyResults type for sort
type PartyResults []PartyElectionResult

func (r PartyResults) Len() int {
return len(r)
}

func (r PartyResults) Swap(i, j int) {
r[i], r[j] = r[j], r[i]
}

func (r PartyResults) Less(i, j int) bool {
return r[i].VoteCount < r[j].VoteCount
}

func getAllCandidate() (candidates []Candidate) {
rows, err := db.Query("SELECT * FROM candidates")
if err != nil {
return nil
}

defer rows.Close()
for rows.Next() {
c := Candidate{}
err = rows.Scan(&c.ID, &c.Name, &c.PoliticalParty, &c.Sex)
if err != nil {
panic(err.Error())
}
candidates = append(candidates, c)
}
return
}

func getCandidate(candidateID int) (c Candidate, err error) {
row := db.QueryRow("SELECT * FROM candidates WHERE id = ?", candidateID)
err = row.Scan(&c.ID, &c.Name, &c.PoliticalParty, &c.Sex)
return
}

func getCandidateByName(name string) (c Candidate, err error) {
row := db.QueryRow("SELECT * FROM candidates WHERE name = ?", name)
err = row.Scan(&c.ID, &c.Name, &c.PoliticalParty, &c.Sex)
return
}

func getAllPartyName() (partyNames []string) {
rows, err := db.Query("SELECT political_party FROM candidates GROUP BY political_party")
if err != nil {
return nil
}

defer rows.Close()
for rows.Next() {
var name string
err = rows.Scan(&name)
if err != nil {
panic(err.Error())
}
partyNames = append(partyNames, name)
}
return
}

func getCandidatesByPoliticalParty(party string) (candidates []Candidate) {
rows, err := db.Query("SELECT * FROM candidates WHERE political_party = ?", party)
if err != nil {
return nil
}

defer rows.Close()
for rows.Next() {
c := Candidate{}
err = rows.Scan(&c.ID, &c.Name, &c.PoliticalParty, &c.Sex)
if err != nil {
panic(err.Error())
}
candidates = append(candidates, c)
}
return
}

func getElectionResult() (result []CandidateElectionResult){
rows, err := db.Query(`
SELECT c.id, c.name, c.political_party, c.sex, IFNULL(v.count, 0)
FROM candidates AS c
LEFT OUTER JOIN
(SELECT candidate_id, COUNT(*) AS count
FROM votes
GROUP BY candidate_id) AS v
ON c.id = v.candidate_id
ORDER BY v.count DESC`)
if err != nil {
return nil
}

defer rows.Close()
for rows.Next() {
r := CandidateElectionResult{}
err = rows.Scan(&r.ID, &r.Name, &r.PoliticalParty, &r.Sex, &r.VoteCount)
if err != nil {
panic(err.Error())
}
result = append(result, r)
}
return
}
190 changes: 190 additions & 0 deletions webapp/go/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package main

import (
"database/sql"
"html/template"
"net/http"
"os"
"sort"
"strconv"

"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/contrib/static"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

func getEnv(key, fallback string) string {
if value, ok := os.LookupEnv(key); ok {
return value
}
return fallback
}

func main() {
// database setting
user := getEnv("ISHOCON2_DB_USER", "ishocon")
pass := getEnv("ISHOCON2_DB_PASSWORD", "ishocon")
dbname := getEnv("ISHOCON2_DB_NAME", "ishocon2")
db, _ = sql.Open("mysql", user+":"+pass+"@/"+dbname)
db.SetMaxIdleConns(5)

gin.SetMode(gin.DebugMode)
r := gin.Default()
// load templates
r.Use(static.Serve("/css", static.LocalFile("public/css", true)))
layout := "templates/layout.tmpl"

// session store
store := sessions.NewCookieStore([]byte("mysession"))
store.Options(sessions.Options{HttpOnly: true})
r.Use(sessions.Sessions("showwin_happy", store))

// GET /
r.GET("/", func(c *gin.Context) {
electionResults := getElectionResult()

// 上位10人と最下位のみ表示
tmp := make([]CandidateElectionResult, len(electionResults))
copy(tmp, electionResults)
candidates := tmp[:10]
candidates = append(candidates, tmp[len(tmp)-1])

partyNames := getAllPartyName()
partyResultMap := map[string]int{}
for _, name := range partyNames {
partyResultMap[name] = 0
}
for _, r := range electionResults {
partyResultMap[r.PoliticalParty] += r.VoteCount
}
partyResults := []PartyElectionResult{}
for name, count := range partyResultMap {
r := PartyElectionResult{}
r.PoliticalParty = name
r.VoteCount = count
partyResults = append(partyResults, r)
}
// 投票数でソート
sort.Slice(partyResults, func(i, j int) bool { return partyResults[i].VoteCount > partyResults[j].VoteCount })

sexRatio := map[string]int{
"men": 0,
"women": 0,
}
for _, r := range electionResults {
if r.Sex == "男" {
sexRatio["men"] += r.VoteCount
} else if r.Sex == "女" {
sexRatio["women"] += r.VoteCount
}
}

funcs := template.FuncMap{"indexPlus1": func(i int) int { return i + 1 }}
r.SetHTMLTemplate(template.Must(template.New("main").Funcs(funcs).ParseFiles(layout, "templates/index.tmpl")))
c.HTML(http.StatusOK, "base", gin.H{
"candidates": candidates,
"parties": partyResults,
"sexRatio": sexRatio,
})
})

// GET /candidates/:candidateID(int)
r.GET("/candidates/:candidateID", func(c *gin.Context) {
candidateID, _ := strconv.Atoi(c.Param("candidateID"))
candidate, err := getCandidate(candidateID)
if err != nil {
c.Redirect(http.StatusFound, "/")
}
votes := getVoteCountByCandidateID(candidateID)
candidateIDs := []int{candidateID}
keywords := getVoiceOfSupporter(candidateIDs)

r.SetHTMLTemplate(template.Must(template.ParseFiles(layout, "templates/candidate.tmpl")))
c.HTML(http.StatusOK, "base", gin.H{
"candidate": candidate,
"votes": votes,
"keywords": keywords,
})
})

// GET /political_parties/:name(string)
r.GET("/political_parties/:name", func(c *gin.Context) {
partyName := c.Param("name")
var votes int
electionResults := getElectionResult()
for _, r := range electionResults {
if r.PoliticalParty == partyName {
votes += r.VoteCount
}
}

candidates := getCandidatesByPoliticalParty(partyName)
candidateIDs := []int{}
for _, c := range candidates {
candidateIDs = append(candidateIDs, c.ID)
}
keywords := getVoiceOfSupporter(candidateIDs)

r.SetHTMLTemplate(template.Must(template.ParseFiles(layout, "templates/political_party.tmpl")))
c.HTML(http.StatusOK, "base", gin.H{
"politicalParty": partyName,
"votes": votes,
"candidates": candidates,
"keywords": keywords,
})
})

// GET /vote
r.GET("/vote", func(c *gin.Context) {
candidates := getAllCandidate()

r.SetHTMLTemplate(template.Must(template.ParseFiles(layout, "templates/vote.tmpl")))
c.HTML(http.StatusOK, "base", gin.H{
"candidates": candidates,
"message": "",
})
})

// POST /vote
r.POST("/vote", func(c *gin.Context) {
user, userErr := getUser(c.PostForm("name"), c.PostForm("address"), c.PostForm("mynumber"))
candidate, cndErr := getCandidateByName(c.PostForm("candidate"))
votedCount := getUserVotedCount(user.ID)
candidates := getAllCandidate()
voteCount, _ := strconv.Atoi(c.PostForm("vote_count"))

var message string
r.SetHTMLTemplate(template.Must(template.ParseFiles(layout, "templates/vote.tmpl")))
if userErr != nil {
message = "個人情報に誤りがあります"
} else if user.Votes < voteCount+votedCount {
message = "投票数が上限を超えています"
} else if c.PostForm("candidate") == "" {
message = "候補者を記入してください"
} else if cndErr != nil {
message = "候補者を正しく記入してください"
} else if c.PostForm("keyword") == "" {
message = "投票理由を記入してください"
} else {
for i := 1; i <= voteCount; i++ {
createVote(user.ID, candidate.ID, c.PostForm("keyword"))
}
message = "投票に成功しました"
}
c.HTML(http.StatusOK, "base", gin.H{
"candidates": candidates,
"message": message,
})
})

r.GET("/initialize", func(c *gin.Context) {
db.Exec("DELETE FROM votes")

c.String(http.StatusOK, "Finish")
})

r.Run(":8080")
}
5 changes: 5 additions & 0 deletions webapp/go/public/css/bootstrap.min.css

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions webapp/go/templates/candidate.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{{ define "content" }}
<div class="jumbotron">
<div class="container">
<h1>{{ .candidate.Name }}</h1>
</div>
</div>
<div class="container">
<div class="row">
<div id="info" class="jumbotron">
<h2>得票数</h2>
<p id="votes" >{{ .votes }}</p>
<h2>政党</h2>
<p id="party" >{{ .candidate.PoliticalParty }}</p>
<h2>性別</h2>
<p id="sex" >{{ .candidate.Sex }}</p>
<h2>支持者の声</h2>
<ul id="voice" >
{{ range $index, $keyword := .keywords }}
<li>{{ $keyword }}</li>
{{ end }}
</ul>
</div>
</div>
</div>
{{ end }}
Loading

0 comments on commit 148f9e1

Please sign in to comment.