Advanced: Implementating a Custom Template Engine
Advanced.TemplateEngine.
package main
import (
"bytes"
"fmt"
"github.com/PuerkitoBio/goquery"
"github.com/nndi-oss/greypot/models"
"github.com/nndi-oss/greypot/template/engine"
"github.com/sirupsen/logrus"
"github.com/xuri/excelize/v2"
)
type html2ExcelTemplateEngine struct {
pongo engine.TemplateEngine
}
func NewHtml2ExcelTemplateEngine() *html2ExcelTemplateEngine {
return &html2ExcelTemplateEngine{
pongo: engine.NewDjangoTemplateEngine(),
}
}
func (pte *html2ExcelTemplateEngine) Render(templateContent []byte, ctx *models.TemplateContext) ([]byte, error) {
out, err := pte.pongo.Render(templateContent, ctx)
if err != nil {
return nil, err
}
return pte.ConvertHtmlToExcel(out)
}
func (pte *html2ExcelTemplateEngine) ConvertHtmlToExcel(htmlData []byte) ([]byte, error) {
var outExcel bytes.Buffer
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(htmlData))
if err != nil {
return nil, err
}
st := &state{}
f := excelize.NewFile()
defer func() {
if err := f.Close(); err != nil {
logrus.WithError(err)
return
}
}()
doc.Find("table").Each(func(i int, tbl *goquery.Selection) {
st.NumTablesFound = st.NumTablesFound + 1
if st.CurrentRowNum == 0 {
st.IsFirstRow = true
}
sheetName := tbl.AttrOr("data-sheet-name", fmt.Sprintf("Sheet %d", st.NumTablesFound))
_, err := f.NewSheet(sheetName)
if err != nil {
logrus.WithError(err)
return
}
for idx, row := range toExcelSheetData(tbl) {
cell, err := excelize.CoordinatesToCellName(1, idx+1)
if err != nil {
return
}
f.SetSheetRow(sheetName, cell, &row)
}
st.CurrentRowNum++
})
// Set active sheet of the workbook.
f.SetActiveSheet(st.NumTablesFound)
_, err = f.WriteTo(&outExcel)
if err != nil {
return nil, err
}
return outExcel.Bytes(), err
}
type state struct {
TableNameInferred string
IsFirstRow bool
CurrentRowNum int
NumTablesFound int
NumRowsProcessed int
}
func toExcelSheetData(tbl *goquery.Selection) [][]any {
sheetData := make([][]any, 0)
bodyRows := tbl.Find("tbody > tr")
if bodyRows != nil {
if bodyRows.Length() > 0 {
bodyRows.Each(func(i int, tr *goquery.Selection) {
tds := tr.Children()
tdsExcelRow := make([]any, 0)
tds.Each(func(i int, td *goquery.Selection) {
tdsExcelRow = append(tdsExcelRow, td.Text())
})
sheetData = append(sheetData, tdsExcelRow)
})
}
}
return sheetData
}