|
1 | 1 | package quiz
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "encoding/json" |
4 | 5 | "fmt"
|
| 6 | + "io" |
5 | 7 | "math"
|
| 8 | + "net/http" |
6 | 9 | "quiz_backend/google"
|
| 10 | + "regexp" |
7 | 11 |
|
8 | 12 | "google.golang.org/api/sheets/v4"
|
9 | 13 | )
|
10 | 14 |
|
| 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 | + |
11 | 25 | func ParseFromGoogleSheets(ID string) (categories map[int]CategoryGroup, err error) {
|
12 | 26 | sheets, err := google.GetQuizFromSpreadsheet(ID)
|
13 | 27 | if err != nil {
|
14 | 28 | return nil, err
|
15 | 29 | }
|
| 30 | + log.Printf("Parsing %d Spreadsheets from Google", len(sheets)) |
16 | 31 |
|
17 | 32 | categories = make(map[int]CategoryGroup)
|
18 | 33 | for _, s := range sheets {
|
@@ -93,31 +108,35 @@ func getQuestionFromRow(row *sheets.RowData) (qq *Question, err error) {
|
93 | 108 | qq = &Question{}
|
94 | 109 | for cellNum, cell := range row.Values {
|
95 | 110 | // skip empty cells
|
96 |
| - if cell == nil || cell.FormattedValue == "" { |
| 111 | + if cell == nil { |
| 112 | + continue |
| 113 | + } |
| 114 | + cellContent := getContentFromCell(cell) |
| 115 | + if cellContent == (DisplayableContent{}) { |
97 | 116 | continue
|
98 | 117 | }
|
99 | 118 |
|
100 | 119 | // only read contents of the first cell and save it as the question
|
101 | 120 | if cellNum == 0 {
|
102 |
| - qq.Question = cell.FormattedValue |
| 121 | + qq.Question = cellContent |
103 | 122 | continue
|
104 | 123 | }
|
105 | 124 |
|
106 | 125 | color, err := getColorFromCell(cell)
|
107 | 126 | 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) |
109 | 128 | continue
|
110 | 129 | }
|
111 | 130 |
|
112 | 131 | if color.Green > color.Red {
|
113 |
| - qq.Correct = append(qq.Correct, cell.FormattedValue) |
| 132 | + qq.Correct = append(qq.Correct, cellContent) |
114 | 133 | } else {
|
115 |
| - qq.Wrong = append(qq.Wrong, cell.FormattedValue) |
| 134 | + qq.Wrong = append(qq.Wrong, cellContent) |
116 | 135 | }
|
117 | 136 | }
|
118 | 137 |
|
119 | 138 | // validation
|
120 |
| - if qq.Question == "" { |
| 139 | + if qq.Question == (DisplayableContent{}) { |
121 | 140 | if len(qq.Correct) == 0 && len(qq.Wrong) == 0 {
|
122 | 141 | return nil, nil
|
123 | 142 | }
|
@@ -172,3 +191,47 @@ func intFromColor(color *sheets.Color) int {
|
172 | 191 | int(math.Ceil(color.Blue*255))&0xFF<<8 |
|
173 | 192 | int(math.Ceil(color.Alpha*255))&0xFF
|
174 | 193 | }
|
| 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 | +} |
0 commit comments