2024-02-19 18:27:12 +00:00
|
|
|
|
package service
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"github.com/gofiber/fiber/v2"
|
2024-06-07 14:58:39 +00:00
|
|
|
|
"penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw"
|
2024-06-12 18:14:14 +00:00
|
|
|
|
"penahub.gitlab.yandexcloud.net/backend/quiz/answerer/dal"
|
|
|
|
|
"penahub.gitlab.yandexcloud.net/backend/quiz/answerer/models"
|
2024-02-19 18:27:12 +00:00
|
|
|
|
quizdal "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal"
|
2024-03-13 17:04:47 +00:00
|
|
|
|
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware"
|
2024-02-19 18:27:12 +00:00
|
|
|
|
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/rs/xid"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
quizIdCookie = "qud"
|
|
|
|
|
fingerprintCookie = "fp"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Service struct {
|
|
|
|
|
store *dal.Storer
|
|
|
|
|
dal *quizdal.DAL
|
|
|
|
|
batch []model.Answer
|
|
|
|
|
m sync.Mutex
|
|
|
|
|
workerRespondentCh chan<- []model.Answer
|
|
|
|
|
workerSendClientCh chan<- model.Answer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func New(s *dal.Storer, q *quizdal.DAL, workerRespondentCh chan<- []model.Answer, workerSendClientCh chan<- model.Answer) *Service {
|
|
|
|
|
return &Service{
|
|
|
|
|
store: s,
|
|
|
|
|
dal: q,
|
|
|
|
|
m: sync.Mutex{},
|
|
|
|
|
batch: []model.Answer{},
|
|
|
|
|
workerRespondentCh: workerRespondentCh,
|
|
|
|
|
workerSendClientCh: workerSendClientCh,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Service) Register(app *fiber.App) *fiber.App {
|
|
|
|
|
app.Post("/answer", s.PutAnswersOnePiece)
|
|
|
|
|
app.Post("/settings", s.GetQuizData)
|
|
|
|
|
return app
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetQuizDataReq request data for get data for user
|
|
|
|
|
type GetQuizDataReq struct {
|
|
|
|
|
QuizId string `json:"quiz_id"` // relation to quiz
|
|
|
|
|
Limit uint64 `json:"limit"`
|
|
|
|
|
Page uint64 `json:"page"`
|
|
|
|
|
NeedConfig bool `json:"need_config"` // true if you need not only question page
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetQuizDataResp response with prepared data for user
|
|
|
|
|
type GetQuizDataResp struct {
|
2024-03-13 17:04:47 +00:00
|
|
|
|
Settings ShavedQuiz `json:"settings"`
|
|
|
|
|
Items []ShavedQuestion `json:"items"`
|
|
|
|
|
Count uint64 `json:"cnt"`
|
|
|
|
|
ShowBadge bool `json:"show_badge"`
|
2024-02-19 18:27:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ShavedQuiz shortened struct for delivery data to customer
|
|
|
|
|
type ShavedQuiz struct {
|
|
|
|
|
Fingerprinting bool `json:"fp"`
|
|
|
|
|
Repeatable bool `json:"rep"`
|
|
|
|
|
Name string `json:"name"`
|
|
|
|
|
Config string `json:"cfg"`
|
|
|
|
|
Limit uint64 `json:"lim"`
|
|
|
|
|
DueTo uint64 `json:"due"`
|
|
|
|
|
TimeOfPassing uint64 `json:"delay"`
|
|
|
|
|
Pausable bool `json:"pausable"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ShavedQuestion shortened struct for delivery data to customer
|
|
|
|
|
type ShavedQuestion struct {
|
|
|
|
|
Id uint64 `json:"id"`
|
|
|
|
|
Title string `json:"title"`
|
|
|
|
|
Description string `json:"desc"`
|
|
|
|
|
Type string `json:"typ"`
|
|
|
|
|
Required bool `json:"req"`
|
|
|
|
|
Page int `json:"p"`
|
|
|
|
|
Content string `json:"c"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetQuizData handler for obtaining data for quiz front rendering
|
|
|
|
|
func (s *Service) GetQuizData(c *fiber.Ctx) error {
|
2024-06-07 14:58:39 +00:00
|
|
|
|
hlogger := log_mw.ExtractLogger(c)
|
2024-02-19 18:27:12 +00:00
|
|
|
|
var req GetQuizDataReq
|
|
|
|
|
if err := c.BodyParser(&req); err != nil {
|
|
|
|
|
return c.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.QuizId == "" {
|
|
|
|
|
return c.Status(fiber.StatusBadRequest).SendString("Invalid request data")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.Limit == 0 && !req.NeedConfig {
|
|
|
|
|
return c.Status(fiber.StatusLengthRequired).SendString("no data requested")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
quiz, err := s.dal.QuizRepo.GetQuizByQid(c.Context(), req.QuizId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.Limit == 0 && req.NeedConfig {
|
|
|
|
|
return c.Status(fiber.StatusOK).JSON(GetQuizDataResp{
|
|
|
|
|
Settings: dao2dtoQuiz(quiz),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if quiz.UniqueAnswers {
|
|
|
|
|
//todo implement after creating store answers
|
|
|
|
|
}
|
|
|
|
|
if quiz.Status != model.StatusStart {
|
|
|
|
|
return c.Status(fiber.StatusLocked).SendString("quiz is inactive")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if quiz.Limit > 0 {
|
|
|
|
|
// todo implement after creating store answer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if quiz.DueTo < uint64(time.Now().Unix()) && quiz.DueTo > 0 {
|
|
|
|
|
return c.Status(fiber.StatusGone).SendString("quiz timeouted")
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-13 17:04:47 +00:00
|
|
|
|
account, err := s.dal.AccountRepo.GetAccountByID(c.Context(), quiz.AccountId)
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return c.Status(fiber.StatusInternalServerError).SendString("can`t get account by quiz.AccountId")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
showBadge := true
|
|
|
|
|
|
2024-04-13 19:13:48 +00:00
|
|
|
|
fmt.Println("PRIVRRRR", account.ID, account.Privileges)
|
2024-03-13 17:04:47 +00:00
|
|
|
|
if priv, ok := account.Privileges["squizHideBadge"]; ok {
|
|
|
|
|
expiration := priv.CreatedAt.Add(time.Duration(priv.Amount) * 24 * time.Hour)
|
|
|
|
|
|
|
|
|
|
if time.Now().Before(expiration) {
|
|
|
|
|
showBadge = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-19 18:27:12 +00:00
|
|
|
|
questions, cnt, err := s.dal.QuestionRepo.GetQuestionList(
|
|
|
|
|
c.Context(),
|
|
|
|
|
req.Limit,
|
|
|
|
|
req.Page*req.Limit,
|
|
|
|
|
0, 0, quiz.Id, false, false, "", "",
|
|
|
|
|
)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result := GetQuizDataResp{
|
2024-03-13 17:04:47 +00:00
|
|
|
|
Count: cnt,
|
|
|
|
|
Items: []ShavedQuestion{},
|
|
|
|
|
ShowBadge: showBadge,
|
2024-02-19 18:27:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if req.NeedConfig {
|
|
|
|
|
result.Settings = dao2dtoQuiz(quiz)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, q := range questions {
|
|
|
|
|
result.Items = append(result.Items, dao2dtoQuestion(q))
|
|
|
|
|
}
|
2024-05-17 18:13:08 +00:00
|
|
|
|
|
|
|
|
|
utmData := model.UTMSavingMap{
|
|
|
|
|
"utm_content": c.Query("utm_content"),
|
|
|
|
|
"utm_medium": c.Query("utm_medium"),
|
|
|
|
|
"utm_campaign": c.Query("utm_campaign"),
|
|
|
|
|
"utm_source": c.Query("utm_source"),
|
|
|
|
|
"utm_term": c.Query("utm_term"),
|
|
|
|
|
"utm_referrer": c.Query("utm_referrer"),
|
|
|
|
|
"roistat": c.Query("roistat"),
|
|
|
|
|
"referrer": c.Query("referrer"),
|
|
|
|
|
"openstat_service": c.Query("openstat_service"),
|
|
|
|
|
"openstat_campaign": c.Query("openstat_campaign"),
|
|
|
|
|
"openstat_ad": c.Query("openstat_ad"),
|
|
|
|
|
"openstat_source": c.Query("openstat_source"),
|
|
|
|
|
"from": c.Query("from"),
|
|
|
|
|
"gclientid": c.Query("gclientid"),
|
|
|
|
|
"_ym_uid": c.Query("_ym_uid"),
|
|
|
|
|
"_ym_counter": c.Query("_ym_counter"),
|
|
|
|
|
"gclid": c.Query("gclid"),
|
|
|
|
|
"yclid": c.Query("yclid"),
|
|
|
|
|
"fbclid": c.Query("fbclid"),
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-29 21:50:59 +00:00
|
|
|
|
deviceType := c.Get("DeviceType")
|
|
|
|
|
os := c.Get("OS")
|
|
|
|
|
browser := c.Get("Browser")
|
|
|
|
|
ip := c.IP()
|
2024-06-01 18:40:32 +00:00
|
|
|
|
device := c.Get("Device")
|
|
|
|
|
referrer := c.Get("Referer")
|
2024-03-29 21:50:59 +00:00
|
|
|
|
fp := ""
|
|
|
|
|
if cfp := c.Cookies(fingerprintCookie); cfp != "" {
|
|
|
|
|
fp = cfp
|
|
|
|
|
}
|
|
|
|
|
cs, ok := c.Context().Value(middleware.ContextKey(middleware.SessionKey)).(string)
|
|
|
|
|
if !ok {
|
|
|
|
|
return c.Status(fiber.StatusUnauthorized).SendString("no session in cookie")
|
|
|
|
|
}
|
2024-05-17 18:13:08 +00:00
|
|
|
|
|
2024-06-01 18:40:32 +00:00
|
|
|
|
answers, errs := s.dal.AnswerRepo.CreateAnswers(c.Context(), []model.Answer{{
|
2024-05-17 18:13:08 +00:00
|
|
|
|
Content: "start",
|
|
|
|
|
QuestionId: questions[0].Id,
|
|
|
|
|
QuizId: quiz.Id,
|
|
|
|
|
Start: true,
|
|
|
|
|
DeviceType: deviceType,
|
2024-06-01 18:40:32 +00:00
|
|
|
|
Device: device,
|
2024-05-17 18:13:08 +00:00
|
|
|
|
Browser: browser,
|
|
|
|
|
IP: ip,
|
|
|
|
|
OS: os,
|
|
|
|
|
Utm: utmData,
|
2024-06-01 18:40:32 +00:00
|
|
|
|
}}, cs, fp, quiz.Id)
|
|
|
|
|
if len(errs) != 0 {
|
|
|
|
|
return c.Status(fiber.StatusInternalServerError).SendString(errs[0].Error())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hlogger.Emit(models.InfoQuizOpen{
|
|
|
|
|
KeyOS: os,
|
|
|
|
|
KeyDevice: device,
|
|
|
|
|
KeyDeviceType: deviceType,
|
|
|
|
|
KeyBrowser: browser,
|
|
|
|
|
CtxQuiz: req.QuizId,
|
2024-06-12 18:14:14 +00:00
|
|
|
|
CtxQuizID: int64(quiz.Id),
|
2024-06-01 18:40:32 +00:00
|
|
|
|
CtxReferrer: referrer,
|
2024-06-12 18:14:14 +00:00
|
|
|
|
CtxIDInt: int64(answers[0].Id),
|
2024-06-01 18:40:32 +00:00
|
|
|
|
CtxSession: cs,
|
|
|
|
|
})
|
2024-02-19 18:27:12 +00:00
|
|
|
|
|
|
|
|
|
if cnt <= req.Limit {
|
|
|
|
|
return c.Status(fiber.StatusOK).JSON(result)
|
|
|
|
|
} else {
|
|
|
|
|
return c.Status(fiber.StatusPartialContent).JSON(result)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func dao2dtoQuestion(question model.Question) ShavedQuestion {
|
|
|
|
|
return ShavedQuestion{
|
|
|
|
|
Id: question.Id,
|
|
|
|
|
Title: question.Title,
|
|
|
|
|
Description: question.Description,
|
|
|
|
|
Type: question.Type,
|
|
|
|
|
Required: false,
|
|
|
|
|
Page: question.Page,
|
|
|
|
|
Content: question.Content,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func dao2dtoQuiz(quiz model.Quiz) ShavedQuiz {
|
|
|
|
|
|
|
|
|
|
return ShavedQuiz{
|
|
|
|
|
Fingerprinting: quiz.Fingerprinting,
|
|
|
|
|
Repeatable: quiz.Repeatable,
|
|
|
|
|
Name: quiz.Name,
|
|
|
|
|
Config: quiz.Config,
|
|
|
|
|
Limit: quiz.Limit,
|
|
|
|
|
DueTo: quiz.DueTo,
|
|
|
|
|
TimeOfPassing: quiz.TimeOfPassing,
|
|
|
|
|
Pausable: quiz.Pausable,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MB Size constants
|
|
|
|
|
const (
|
|
|
|
|
MB = 1 << 20
|
|
|
|
|
filePrefix = "file:"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type PutAnswersResponse struct {
|
|
|
|
|
FileIDMap map[uint64]string `json:"fileIDMap"`
|
|
|
|
|
Stored []uint64 `json:"stored"`
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-14 19:04:30 +00:00
|
|
|
|
// todo не отдавать ответы с типом start но сохранять брать из контента ответа
|
|
|
|
|
// поле бул также в GetQuizdata не отдавать его, но по запросам идет GetQuizdata получаем данные квиза
|
|
|
|
|
// аотом отправляем ответы в PutAnswers и сохраяняем их подумать как делать
|
2024-02-19 18:27:12 +00:00
|
|
|
|
func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error {
|
|
|
|
|
cs, ok := c.Context().Value(middleware.ContextKey(middleware.SessionKey)).(string)
|
|
|
|
|
if !ok {
|
|
|
|
|
return c.Status(fiber.StatusUnauthorized).SendString("no session in cookie")
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-07 14:58:39 +00:00
|
|
|
|
hlogger := log_mw.ExtractLogger(c)
|
2024-06-01 18:40:32 +00:00
|
|
|
|
|
2024-02-19 18:27:12 +00:00
|
|
|
|
form, err := c.MultipartForm()
|
|
|
|
|
if err != nil || form == nil || form.File == nil {
|
|
|
|
|
return c.Status(fiber.StatusBadRequest).SendString("expecting multipart form file")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
answersStr := form.Value["answers"]
|
|
|
|
|
if len(answersStr) == 0 {
|
|
|
|
|
return c.Status(fiber.StatusFailedDependency).SendString("no answers provided")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
answersRaw, answers, trueRes []model.Answer
|
|
|
|
|
errs []error
|
|
|
|
|
)
|
|
|
|
|
|
2024-03-14 13:13:17 +00:00
|
|
|
|
deviceType := c.Get("DeviceType")
|
|
|
|
|
os := c.Get("OS")
|
|
|
|
|
browser := c.Get("Browser")
|
|
|
|
|
ip := c.IP()
|
2024-06-01 18:40:32 +00:00
|
|
|
|
device := c.Get("Device")
|
|
|
|
|
referrer := c.Get("Referer")
|
2024-03-14 13:13:17 +00:00
|
|
|
|
|
2024-02-19 18:27:12 +00:00
|
|
|
|
if err := json.Unmarshal([]byte(answersStr[0]), &answersRaw); err != nil {
|
|
|
|
|
return c.Status(fiber.StatusBadRequest).SendString("not valid answers string")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
quizID, ok := form.Value["qid"]
|
|
|
|
|
if !ok {
|
|
|
|
|
return c.Status(fiber.StatusFailedDependency).SendString("no quiz id provided")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fp := ""
|
|
|
|
|
if cfp := c.Cookies(fingerprintCookie); cfp != "" {
|
|
|
|
|
fp = cfp
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
quiz, err := s.dal.QuizRepo.GetQuizByQid(c.Context(), quizID[0])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return c.Status(fiber.StatusInternalServerError).SendString("can not get quiz")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fileIDMap := make(map[uint64]string)
|
|
|
|
|
|
|
|
|
|
for _, ans := range answersRaw {
|
2024-03-15 10:52:19 +00:00
|
|
|
|
|
2024-03-14 13:13:17 +00:00
|
|
|
|
ans.DeviceType = deviceType
|
|
|
|
|
ans.OS = os
|
|
|
|
|
ans.Browser = browser
|
|
|
|
|
ans.IP = ip
|
2024-06-01 18:40:32 +00:00
|
|
|
|
ans.Device = device
|
2024-02-19 18:27:12 +00:00
|
|
|
|
if strings.HasPrefix(ans.Content, filePrefix) {
|
|
|
|
|
filekey := strings.TrimPrefix(ans.Content, filePrefix)
|
|
|
|
|
filenameparts := strings.Split(filekey, ".")
|
|
|
|
|
filetail := filenameparts[len(filenameparts)-1]
|
|
|
|
|
fileparts := form.File[filekey]
|
|
|
|
|
|
|
|
|
|
if len(fileparts) == 0 {
|
|
|
|
|
errs = append(errs, fmt.Errorf("no parts for file: %s", filekey))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r, err := fileparts[0].Open()
|
|
|
|
|
if err != nil {
|
|
|
|
|
errs = append(errs, fmt.Errorf("can not open part for file: %s", filekey))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fname := fmt.Sprintf("%s.%s", xid.New().String(), filetail)
|
|
|
|
|
if err := s.store.PutAnswer(c.Context(), r, quizID[0], fname, ans.QuestionId, fileparts[0].Size); err != nil {
|
|
|
|
|
return c.Status(fiber.StatusInternalServerError).SendString("can not upload file answers")
|
|
|
|
|
}
|
|
|
|
|
ans.Content = fname
|
|
|
|
|
|
|
|
|
|
fileIDMap[ans.QuestionId] = fname
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ans.Session = cs
|
|
|
|
|
ans.QuizId = quiz.Id
|
|
|
|
|
ans.CreatedAt = time.Now()
|
|
|
|
|
answers = append(answers, ans)
|
|
|
|
|
if ans.Result {
|
2024-03-13 17:04:47 +00:00
|
|
|
|
content := model.ResultContent{}
|
|
|
|
|
err := json.Unmarshal([]byte(ans.Content), &content)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return c.Status(fiber.StatusInternalServerError).SendString("error unmarshalling answer content: " + err.Error())
|
|
|
|
|
}
|
|
|
|
|
ans.Email = content.Email
|
|
|
|
|
|
2024-02-19 18:27:12 +00:00
|
|
|
|
s.workerSendClientCh <- ans
|
|
|
|
|
trueRes = append(trueRes, ans)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
quizConfig := model.QuizConfig{}
|
|
|
|
|
err = json.Unmarshal([]byte(quiz.Config), &quizConfig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return c.Status(fiber.StatusInternalServerError).SendString("can not unmarshal quiz config")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if quizConfig.Mailing.When == "email" && len(trueRes) > 0 {
|
|
|
|
|
s.workerRespondentCh <- trueRes
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stored, ers := s.dal.AnswerRepo.CreateAnswers(c.Context(), answers, cs, fp, quiz.Id)
|
|
|
|
|
if len(ers) != 0 {
|
2024-03-13 17:04:47 +00:00
|
|
|
|
for _, err := range ers {
|
|
|
|
|
if strings.Contains(err.Error(), "duplicate key value") {
|
|
|
|
|
return c.Status(fiber.StatusAlreadyReported).SendString("User has already passed the quiz")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-19 18:27:12 +00:00
|
|
|
|
return c.Status(fiber.StatusInternalServerError).SendString("some errors are casualted: " + fmt.Sprint(ers))
|
|
|
|
|
}
|
2024-06-01 18:40:32 +00:00
|
|
|
|
var questionIDs []uint64
|
|
|
|
|
for _, ans := range stored {
|
|
|
|
|
questionIDs = append(questionIDs, ans.QuestionId)
|
|
|
|
|
if ans.Result {
|
|
|
|
|
hlogger.Emit(models.InfoResult{
|
|
|
|
|
KeyOS: os,
|
|
|
|
|
KeyDevice: device,
|
|
|
|
|
KeyDeviceType: deviceType,
|
|
|
|
|
KeyBrowser: browser,
|
|
|
|
|
CtxQuiz: quizID[0],
|
2024-06-12 18:14:14 +00:00
|
|
|
|
CtxQuizID: int64(quiz.Id),
|
2024-06-01 18:40:32 +00:00
|
|
|
|
CtxReferrer: referrer,
|
2024-06-12 18:14:14 +00:00
|
|
|
|
CtxQuestionID: int64(ans.QuestionId),
|
|
|
|
|
CtxIDInt: int64(ans.Id),
|
2024-06-01 18:40:32 +00:00
|
|
|
|
CtxSession: cs,
|
|
|
|
|
})
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
hlogger.Emit(models.InfoAnswer{
|
|
|
|
|
KeyOS: os,
|
|
|
|
|
KeyDevice: device,
|
|
|
|
|
KeyDeviceType: deviceType,
|
|
|
|
|
KeyBrowser: browser,
|
|
|
|
|
CtxQuiz: quizID[0],
|
2024-06-12 18:14:14 +00:00
|
|
|
|
CtxQuizID: int64(quiz.Id),
|
2024-06-01 18:40:32 +00:00
|
|
|
|
CtxReferrer: referrer,
|
2024-06-12 18:14:14 +00:00
|
|
|
|
CtxQuestionID: int64(ans.QuestionId),
|
|
|
|
|
CtxIDInt: int64(ans.Id),
|
2024-06-01 18:40:32 +00:00
|
|
|
|
CtxSession: cs,
|
|
|
|
|
})
|
|
|
|
|
}
|
2024-02-19 18:27:12 +00:00
|
|
|
|
|
|
|
|
|
response := PutAnswersResponse{
|
|
|
|
|
FileIDMap: fileIDMap,
|
2024-06-01 18:40:32 +00:00
|
|
|
|
Stored: questionIDs,
|
2024-02-19 18:27:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return c.Status(fiber.StatusOK).JSON(response)
|
|
|
|
|
}
|