Skip to content

Commit 010cb27

Browse files
committedJul 20, 2024
added support of images
currently they are only sended as raw text
1 parent bfc9fc5 commit 010cb27

File tree

3 files changed

+92
-16
lines changed

3 files changed

+92
-16
lines changed
 

‎database/connection.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func Connect() {
6868
log.Fatalf("Could not ping database: %v", err)
6969
}
7070

71-
log.Printf("Connected to database %s@%s:%d/%s\n", config.User, config.Host, config.Port, config.Database)
71+
log.Printf("Connected to database %s@%s:%d/%s", config.User, config.Host, config.Port, config.Database)
7272
}
7373

7474
// Close closes the database and prevents new queries from starting.

‎quiz/sheets.go

+69-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
package quiz
22

33
import (
4+
"encoding/json"
45
"fmt"
6+
"io"
57
"math"
8+
"net/http"
69
"quiz_backend/google"
10+
"regexp"
711

812
"google.golang.org/api/sheets/v4"
913
)
1014

15+
var spreadsheetFormulaRegex *regexp.Regexp
16+
17+
func init() {
18+
var err error
19+
spreadsheetFormulaRegex, err = regexp.Compile("^=([A-Z]+)\\((.*)\\)$")
20+
if err != nil {
21+
panic("failed to compile spreadsheet formula regex: " + err.Error())
22+
}
23+
}
24+
1125
func ParseFromGoogleSheets(ID string) (categories map[int]CategoryGroup, err error) {
1226
sheets, err := google.GetQuizFromSpreadsheet(ID)
1327
if err != nil {
1428
return nil, err
1529
}
30+
log.Printf("Parsing %d Spreadsheets from Google", len(sheets))
1631

1732
categories = make(map[int]CategoryGroup)
1833
for _, s := range sheets {
@@ -93,31 +108,35 @@ func getQuestionFromRow(row *sheets.RowData) (qq *Question, err error) {
93108
qq = &Question{}
94109
for cellNum, cell := range row.Values {
95110
// skip empty cells
96-
if cell == nil || cell.FormattedValue == "" {
111+
if cell == nil {
112+
continue
113+
}
114+
cellContent := getContentFromCell(cell)
115+
if cellContent == (DisplayableContent{}) {
97116
continue
98117
}
99118

100119
// only read contents of the first cell and save it as the question
101120
if cellNum == 0 {
102-
qq.Question = cell.FormattedValue
121+
qq.Question = cellContent
103122
continue
104123
}
105124

106125
color, err := getColorFromCell(cell)
107126
if err != nil {
108-
log.Printf("Warn: in 'question '%s' answer %d ('%s'): %v", qq.Question, cellNum, cell.FormattedValue, err)
127+
log.Printf("Warn: in question (type: %d) '%s' answer %d ('%s'): %v", qq.Question.Type, qq.Question.Text, cellNum, cell.FormattedValue, err)
109128
continue
110129
}
111130

112131
if color.Green > color.Red {
113-
qq.Correct = append(qq.Correct, cell.FormattedValue)
132+
qq.Correct = append(qq.Correct, cellContent)
114133
} else {
115-
qq.Wrong = append(qq.Wrong, cell.FormattedValue)
134+
qq.Wrong = append(qq.Wrong, cellContent)
116135
}
117136
}
118137

119138
// validation
120-
if qq.Question == "" {
139+
if qq.Question == (DisplayableContent{}) {
121140
if len(qq.Correct) == 0 && len(qq.Wrong) == 0 {
122141
return nil, nil
123142
}
@@ -172,3 +191,47 @@ func intFromColor(color *sheets.Color) int {
172191
int(math.Ceil(color.Blue*255))&0xFF<<8 |
173192
int(math.Ceil(color.Alpha*255))&0xFF
174193
}
194+
195+
func getContentFromCell(cell *sheets.CellData) (content DisplayableContent) {
196+
if cell.FormattedValue != "" {
197+
content.Text = cell.FormattedValue
198+
return
199+
}
200+
if cell.UserEnteredValue == nil || cell.UserEnteredValue.FormulaValue == nil || *cell.UserEnteredValue.FormulaValue == "" {
201+
return
202+
}
203+
formulaFound := spreadsheetFormulaRegex.FindStringSubmatch(*cell.UserEnteredValue.FormulaValue)
204+
if formulaFound == nil {
205+
return
206+
}
207+
return parseCellFormula(formulaFound[1], formulaFound[2])
208+
}
209+
210+
func parseCellFormula(formula, parameter string) (content DisplayableContent) {
211+
switch formula {
212+
case "IMAGE":
213+
content.Type = CONTENTIMAGE
214+
var url string
215+
err := json.Unmarshal([]byte(parameter), &url)
216+
if err != nil {
217+
log.Printf("Error: parse image url: %v", err)
218+
return
219+
}
220+
resp, err := http.Get(url)
221+
if err != nil {
222+
log.Printf("Error: get image from url '%s': %v", url, err)
223+
return
224+
}
225+
data, err := io.ReadAll(resp.Body)
226+
if err != nil {
227+
log.Printf("Error: reading image response: %v", err)
228+
return
229+
}
230+
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
231+
log.Printf("Error: could not get image from url: got '%s': %s", resp.Status, string(data))
232+
return
233+
}
234+
content.Text = string(data)
235+
}
236+
return
237+
}

‎quiz/types.go

+22-9
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,23 @@ type Category struct {
4242
}
4343

4444
type Question struct {
45-
Question string `json:"question"`
46-
Correct []string `json:"correct"`
47-
Wrong []string `json:"wrong"`
45+
Question DisplayableContent `json:"question"`
46+
Correct []DisplayableContent `json:"correct"`
47+
Wrong []DisplayableContent `json:"wrong"`
48+
}
49+
50+
type DisplayableContent struct {
51+
Type ContentType
52+
Text string
4853
}
54+
55+
type ContentType uint8
56+
57+
const (
58+
CONTENTTEXT ContentType = iota
59+
CONTENTIMAGE
60+
)
61+
4962
type Round struct {
5063
Question string `json:"question"`
5164
Answers []string `json:"answers"`
@@ -198,19 +211,19 @@ func (q Question) ToRound() Round {
198211
rand.Shuffle(len(q.Correct), func(i, j int) {
199212
q.Correct[i], q.Correct[j] = q.Correct[j], q.Correct[i]
200213
})
201-
answers = append(answers, q.Correct[rand.Intn(len(q.Correct)-1)])
214+
answers = append(answers, q.Correct[rand.Intn(len(q.Correct)-1)].Text)
202215
} else {
203-
answers = append(answers, q.Correct[0])
216+
answers = append(answers, q.Correct[0].Text)
204217
}
205218

206219
// select up to 3 wrong answers
207220
if len(q.Wrong) > 3 {
208221
rand.Shuffle(len(q.Wrong), func(i, j int) {
209222
q.Wrong[i], q.Wrong[j] = q.Wrong[j], q.Wrong[i]
210223
})
211-
answers = append(answers, q.Wrong[:3]...)
212-
} else {
213-
answers = append(answers, q.Wrong...)
224+
}
225+
for _, a := range q.Wrong[:3] {
226+
answers = append(answers, a.Text)
214227
}
215228

216229
var correct int
@@ -222,7 +235,7 @@ func (q Question) ToRound() Round {
222235
})
223236

224237
return Round{
225-
Question: q.Question,
238+
Question: q.Question.Text,
226239
Answers: answers,
227240
Correct: correct + 1,
228241
}

0 commit comments

Comments
 (0)
Please sign in to comment.