435 lines
12 KiB
Go
435 lines
12 KiB
Go
package tools
|
||
|
||
import (
|
||
"fmt"
|
||
"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"
|
||
"sync"
|
||
)
|
||
|
||
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
|
||
}
|
||
}
|
||
|
||
sort.Slice(answers, func(i, j int) bool {
|
||
return answers[i].QuestionId < answers[j].QuestionId
|
||
})
|
||
|
||
// мапа для хранения обычных ответов респондентов
|
||
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)
|
||
}
|
||
}
|
||
|
||
processSession := func(session string, response []model.Answer, row int) {
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
fmt.Println("Recovered from panic:", r)
|
||
}
|
||
}()
|
||
|
||
if err := file.SetCellValue(sheet, "A"+strconv.Itoa(row), results[session].Content); err != nil {
|
||
fmt.Println(err.Error())
|
||
}
|
||
count := 2
|
||
for _, q := range questions {
|
||
if !q.Deleted && q.Type != model.TypeResult {
|
||
index := binarySearch(response, q.Id)
|
||
if index != -1 {
|
||
cell := ToAlphaString(count) + strconv.Itoa(row)
|
||
tipe := FileSearch(response[index].Content)
|
||
noAccept := make(map[string]struct{})
|
||
todoMap := make(map[string]string)
|
||
if tipe != "Text" && q.Type == model.TypeImages || q.Type == model.TypeVarImages {
|
||
urle := ExtractImageURL(response[index].Content)
|
||
urlData := strings.Split(urle, " ")
|
||
if len(urlData) == 1 {
|
||
u, err := url.Parse(urle)
|
||
if err == nil && u.Scheme != "" && u.Host != "" {
|
||
picture, err := downloadImage(urle)
|
||
if err != nil {
|
||
fmt.Println(err.Error())
|
||
}
|
||
err = file.SetColWidth(sheet, ToAlphaString(count), ToAlphaString(count), 50)
|
||
if err != nil {
|
||
fmt.Println(err.Error())
|
||
}
|
||
err = file.SetRowHeight(sheet, row, 150)
|
||
if err != nil {
|
||
fmt.Println(err.Error())
|
||
}
|
||
if err := file.AddPictureFromBytes(sheet, cell, picture); err != nil {
|
||
fmt.Println(err.Error())
|
||
}
|
||
noAccept[response[index].Content] = struct{}{}
|
||
} else {
|
||
todoMap[response[index].Content] = cell
|
||
}
|
||
} else {
|
||
todoMap[response[index].Content] = cell
|
||
}
|
||
} 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 {
|
||
fmt.Println(err.Error())
|
||
}
|
||
if err := file.SetCellHyperLink(sheet, cell, urle, "External", excelize.HyperlinkOpts{
|
||
Display: &display,
|
||
Tooltip: &tooltip,
|
||
}); err != nil {
|
||
fmt.Println(err.Error())
|
||
}
|
||
noAccept[response[index].Content] = struct{}{}
|
||
} else {
|
||
todoMap[response[index].Content] = cell
|
||
}
|
||
for cnt, cel := range todoMap {
|
||
if _, ok := noAccept[cnt]; !ok {
|
||
if err := file.SetCellValue(sheet, cel, cnt); err != nil {
|
||
fmt.Println(err.Error())
|
||
}
|
||
}
|
||
}
|
||
|
||
} else {
|
||
cell := ToAlphaString(count) + strconv.Itoa(row)
|
||
if err := file.SetCellValue(sheet, cell, "-"); err != nil {
|
||
fmt.Println(err.Error())
|
||
}
|
||
}
|
||
count++
|
||
}
|
||
}
|
||
cell := ToAlphaString(len(headers)) + strconv.Itoa(row)
|
||
if err := file.SetCellValue(sheet, cell, mapQueRes[results[session].QuestionId]); err != nil {
|
||
fmt.Println(err.Error())
|
||
}
|
||
}
|
||
|
||
row := 2
|
||
var wg sync.WaitGroup
|
||
for session, _ := range results {
|
||
wg.Add(1)
|
||
go func(session string, response []model.Answer, row int) {
|
||
defer wg.Done()
|
||
processSession(session, standart[session], row)
|
||
}(session, standart[session], row)
|
||
row++
|
||
}
|
||
wg.Wait()
|
||
|
||
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) string {
|
||
if strings.Contains(content, bucketImages) {
|
||
return FileType(content)
|
||
} else if strings.Contains(content, bucketFonts) {
|
||
return FileType(content)
|
||
} else if strings.Contains(content, bucketScripts) {
|
||
return FileType(content)
|
||
} else if strings.Contains(content, bucketStyle) {
|
||
return FileType(content)
|
||
} else if strings.Contains(content, bucketAnswers) {
|
||
return FileType(content)
|
||
}
|
||
|
||
return "Text"
|
||
}
|
||
|
||
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 func() {
|
||
if derr := resp.Body.Close(); derr != nil {
|
||
fmt.Printf("error close response body in downloadImage: %v", derr)
|
||
}
|
||
}()
|
||
|
||
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
|
||
}
|
||
|
||
//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)
|
||
// noAccept := make(map[string]struct{})
|
||
// todoMap := make(map[string]string)
|
||
// 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
|
||
// }
|
||
// noAccept[response[index].Content] = struct{}{}
|
||
// }
|
||
// }
|
||
// } 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
|
||
// }
|
||
// noAccept[response[index].Content] = struct{}{}
|
||
// } else {
|
||
// todoMap[response[index].Content] = cell
|
||
// }
|
||
// }
|
||
// for cnt, cel := range todoMap {
|
||
// if _, ok := noAccept[cnt]; !ok {
|
||
// if err := file.SetCellValue(sheet, cel, cnt); 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
|
||
//}
|