core/tools/tools.go
2024-03-26 20:48:49 +03:00

272 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package tools
import (
"github.com/xuri/excelize/v2"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"io"
"io/ioutil"
"net/http"
"net/url"
"path/filepath"
"penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model"
"regexp"
"sort"
"strconv"
"strings"
)
const (
bucketImages = "squizimages"
bucketFonts = "squizfonts"
bucketScripts = "squizscript"
bucketStyle = "squizstyle"
bucketAnswers = "squizanswer"
)
func WriteDataToExcel(buffer io.Writer, questions []model.Question, answers []model.Answer) error {
file := excelize.NewFile()
sheet := "Sheet1"
_, err := file.NewSheet(sheet)
if err != nil {
return err
}
sort.Slice(questions, func(i, j int) bool {
return questions[i].Page > questions[j].Page
})
headers := []string{"Данные респондента"}
mapQueRes := make(map[uint64]string)
for _, q := range questions {
if !q.Deleted {
if q.Type == model.TypeResult {
mapQueRes[q.Id] = q.Title + "\n" + q.Description
} else {
headers = append(headers, q.Title)
}
}
}
headers = append(headers, "Результат")
// добавляем заголовки в первую строку
for col, header := range headers {
cell := ToAlphaString(col+1) + "1"
if err := file.SetCellValue(sheet, cell, header); err != nil {
return err
}
}
// мапа для хранения обычных ответов респондентов
standart := make(map[string][]model.Answer)
// мапа для хранения данных респондентов
results := make(map[string]model.Answer)
// заполняем мапу ответами и данными респондентов
for _, answer := range answers {
if answer.Result {
results[answer.Session] = answer
} else {
standart[answer.Session] = append(standart[answer.Session], answer)
}
}
// записываем данные в файл
row := 2
for session, _ := range results {
response := standart[session]
if err := file.SetCellValue(sheet, "A"+strconv.Itoa(row), results[session].Content); err != nil {
return err
}
count := 2
for _, q := range questions {
if !q.Deleted && q.Type != model.TypeResult {
sort.Slice(response, func(i, j int) bool {
return response[i].QuestionId < response[j].QuestionId
})
index := binarySearch(response, q.Id)
if index != -1 {
cell := ToAlphaString(count) + strconv.Itoa(row)
typeMap := FileSearch(response[index].Content)
for _, tipe := range typeMap {
if tipe != "Text" && q.Type == model.TypeImages || q.Type == model.TypeVarImages {
urle := ExtractImageURL(response[index].Content)
urlData := strings.Split(urle, " ")
for _, k := range urlData {
u, err := url.Parse(k)
if err == nil && u.Scheme != "" && u.Host != "" {
picture, err := downloadImage(k)
if err != nil {
return err
}
file.SetColWidth(sheet, ToAlphaString(count), ToAlphaString(count), 50)
file.SetRowHeight(sheet, row, 150)
if err := file.AddPictureFromBytes(sheet, cell, picture); err != nil {
return err
}
} else {
if err := file.SetCellValue(sheet, cell, k); err != nil {
return err
}
}
}
} else if tipe != "Text" && q.Type == model.TypeFile {
urle := ExtractImageURL(response[index].Content)
display, tooltip := urle, urle
if err := file.SetCellValue(sheet, cell, response[index].Content); err != nil {
return err
}
if err := file.SetCellHyperLink(sheet, cell, urle, "External", excelize.HyperlinkOpts{
Display: &display,
Tooltip: &tooltip,
}); err != nil {
return err
}
} else if q.Type != model.TypeFile {
if err := file.SetCellValue(sheet, cell, response[index].Content); err != nil {
return err
}
}
}
} else {
cell := ToAlphaString(count) + strconv.Itoa(row)
if err := file.SetCellValue(sheet, cell, "-"); err != nil {
return err
}
}
count++
}
}
cell := ToAlphaString(len(headers)) + strconv.Itoa(row)
if err := file.SetCellValue(sheet, cell, mapQueRes[results[session].QuestionId]); err != nil {
return err
}
row++
}
// cохраняем данные в буфер
if err := file.Write(buffer); err != nil {
return err
}
return nil
}
func binarySearch(answers []model.Answer, questionID uint64) int {
left := 0
right := len(answers) - 1
for left <= right {
mid := left + (right-left)/2
if answers[mid].QuestionId == questionID {
return mid
} else if answers[mid].QuestionId < questionID {
left = mid + 1
} else {
right = mid - 1
}
}
return -1
}
func FileSearch(content string) map[string]string {
types := make(map[string]string)
words := strings.Fields(content)
for _, word := range words {
if strings.Contains(word, bucketImages) {
types[word] = FileType(word)
} else if strings.Contains(word, bucketFonts) {
types[word] = FileType(word)
} else if strings.Contains(word, bucketScripts) {
types[word] = FileType(word)
} else if strings.Contains(word, bucketStyle) {
types[word] = FileType(word)
} else if strings.Contains(word, bucketAnswers) {
types[word] = FileType(word)
} else {
types[word] = "Text"
}
}
return types
}
func FileType(filename string) string {
parts := strings.Split(filename, ".")
extension := parts[len(parts)-1]
switch extension {
case "png", "jpg", "jpeg", "gif", "bmp", "svg", "webp", "tiff", "ico":
return "Image"
default:
return "File"
}
}
func downloadImage(url string) (*excelize.Picture, error) {
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
imgData, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
ext := filepath.Ext(url)
if ext == "" {
contentType := resp.Header.Get("Content-Type")
switch {
case strings.HasPrefix(contentType, "image/jpeg"):
ext = ".jpg"
case strings.HasPrefix(contentType, "image/png"):
ext = ".png"
default:
ext = ".png"
}
}
pic := &excelize.Picture{
Extension: ext,
File: imgData,
Format: &excelize.GraphicOptions{
AutoFit: true,
Positioning: "oneCell",
},
}
return pic, nil
}
func ToAlphaString(col int) string {
var result string
for col > 0 {
col--
result = string(rune('A'+col%26)) + result
col /= 26
}
return result
}
func ExtractImageURL(htmlContent string) string {
re := regexp.MustCompile(`(?:<img[^>]*src="([^"]+)"[^>]*>)|(?:<td[^>]*>.*?<img[^>]*src="([^"]+)"[^>]*>.*?</td>)|(?:<tr[^>]*>.*?<td[^>]*>.*?<img[^>]*src="([^"]+)"[^>]*>.*?</td>.*?</tr>)|(?:<a[^>]*\s+download[^>]*>([^<]+)<\/a>)`)
matches := re.FindAllStringSubmatch(htmlContent, -1)
for _, match := range matches {
for i := 1; i < len(match); i++ {
if match[i] != "" {
return strings.TrimSpace(match[i])
}
}
}
return htmlContent
}