forked from PepperSalt42/api
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathquestion.go
110 lines (98 loc) · 2.94 KB
/
question.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package main
import (
"errors"
"math/rand"
"net/http"
"os"
"time"
log "github.com/Sirupsen/logrus"
"github.com/jinzhu/gorm"
)
var errNoQuestionAvailable = errors.New("No question available")
// Question contains information about a question.
type Question struct {
gorm.Model
UserID uint
Sentence string
RightAnswerID uint `json:"-"`
StartedAt time.Time
}
// GetCurrentQuestionAnswer contains the data of get current question request.
type GetCurrentQuestionAnswer struct {
Question *Question
Answers []string
}
// getCurrentQuestion returns current question.
func getCurrentQuestion(w http.ResponseWriter, r *http.Request) {
question, err := GetCurrentQuestion()
if err != nil {
renderJSON(w, http.StatusNotFound, errCurQuestionNotFound)
return
}
answers, err := GetAnswersByQuestionID(question.ID)
if err != nil {
renderJSON(w, http.StatusNotFound, errCurQuestionNotFound)
return
}
resp := GetCurrentQuestionAnswer{Question: question}
for _, answer := range answers {
resp.Answers = append(resp.Answers, answer.Sentence)
}
renderJSON(w, http.StatusOK, &resp)
}
// GetCurrentQuestion returns the current question.
func GetCurrentQuestion() (*Question, error) {
return getCurrentQuestionWithTX(&db)
}
// getCurrentQuestionWithTX returns the current question using database transaction.
func getCurrentQuestionWithTX(tx *gorm.DB) (*Question, error) {
question := &Question{}
err := tx.Order("started_at").Last(question).Error
return question, err
}
// refreshQuestion updates current question every X time.
func refreshQuestion() {
questionRefreshRate := os.Getenv("QUESTION_REFRESH_RATE")
wait, err := time.ParseDuration(questionRefreshRate)
if err != nil {
log.Fatal("Can't convert question refresh rate")
}
for {
time.Sleep(wait)
if err := nextQuestion(); err != nil {
log.WithField("err", err).Error("Can't set nextQuestion")
}
}
}
// nextQuestion selects a new random question and updates users points.
func nextQuestion() error {
tx := db.Begin()
defer tx.Commit()
nextQuestion, err := getNextQuestion(tx)
if err != nil {
return err
}
if err := updateUsersPoints(tx); err != nil {
return err
}
return tx.Model(nextQuestion).UpdateColumn("started_at", time.Now()).Error
}
// updateUsersPoints updates users points.
func updateUsersPoints(tx *gorm.DB) error {
q, err := getCurrentQuestionWithTX(tx)
if err != nil {
return nil
}
return tx.Exec("UPDATE `users` JOIN `answer_entries` ON users.id = answer_entries.user_id SET users.points = users.points + 1 WHERE answer_entries.question_id = ? AND answer_entries.answer_id = ?", q.ID, q.RightAnswerID).Error
}
// getNextQuestion returns the next random question.
func getNextQuestion(tx *gorm.DB) (*Question, error) {
var questions []Question
if err := tx.Where("started_at = ?", 0).Find(&questions).Error; err != nil {
return nil, err
}
if len(questions) == 0 {
return nil, errNoQuestionAvailable
}
return &questions[rand.Intn(len(questions))], nil
}