print as html

This commit is contained in:
natercio committed 2026-10-05 10:33:02 +01:00
1 parent bb00676452
commit 3b971cda61
9 files changed
+339 -7

No files matched your search

+148
View File
@@ -0,0 +1,148 @@
package table
import (
"fmt"
"io"
"github.com/biter777/countries"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/nmoniz/any2anexoj/internal"
)
// translator is the contract the pretty printer relies on for human-readable
// column labels. *Localizer satisfies it.
type translator interface {
Translate(key string, count int, values map[string]any) string
}
// PrettyPrinter writes a simple, human readable, table row to the provided io.Writer for each
// ReportItem received. Render and RenderHTML each build a fresh table.Writer so that
// invoking them (in any order or repeatedly) on the same instance never duplicates rows.
type PrettyPrinter struct {
output io.Writer
translator translator
}
func NewPrettyPrinter(w io.Writer, tr translator) *PrettyPrinter {
return &PrettyPrinter{
output: w,
translator: tr,
}
}
// buildTable constructs a fully configured table.Writer from the items in aw.
// Each call returns a new table.Writer so callers can render the same data
// multiple times without duplicating rows. The returned table is not wired to
// pp.output via an output mirror; callers that want the rendered output
// written to pp.output must set the mirror themselves before calling .Render().
func (pp *PrettyPrinter) buildTable(aw *internal.AggregatorWriter) table.Writer {
tw := table.NewWriter()
tw.SetAutoIndex(true)
tw.SetStyle(table.StyleLight)
tw.SetColumnConfigs([]table.ColumnConfig{
colCountry(1),
colOther(2),
colOther(3),
colOther(4),
colOther(5),
colEuros(6),
colOther(7),
colOther(8),
colOther(9),
colEuros(10),
colEuros(11),
colEuros(12),
colCountry(13),
})
realizationTxt := pp.translator.Translate("realization", 1, nil)
acquisitionTxt := pp.translator.Translate("acquisition", 1, nil)
yearTxt := pp.translator.Translate("year", 1, nil)
monthTxt := pp.translator.Translate("month", 1, nil)
dayTxt := pp.translator.Translate("day", 1, nil)
valorTxt := pp.translator.Translate("value", 1, nil)
tw.AppendHeader(table.Row{"", "", realizationTxt, realizationTxt, realizationTxt, realizationTxt, acquisitionTxt, acquisitionTxt, acquisitionTxt, acquisitionTxt, "", "", ""}, table.RowConfig{AutoMerge: true})
tw.AppendHeader(table.Row{
pp.translator.Translate("source_country", 1, nil), pp.translator.Translate("code", 1, nil),
yearTxt, monthTxt, dayTxt, valorTxt,
yearTxt, monthTxt, dayTxt, valorTxt,
pp.translator.Translate("expenses", 2, nil), pp.translator.Translate("foreign_tax_paid", 1, nil), pp.translator.Translate("counter_country", 1, nil),
})
for ri := range aw.Iter() {
tw.AppendRow(table.Row{
ri.AssetCountry, ri.Nature,
ri.SellTimestamp.Year(), int(ri.SellTimestamp.Month()), ri.SellTimestamp.Day(), ri.SellValue.StringFixed(2),
ri.BuyTimestamp.Year(), int(ri.BuyTimestamp.Month()), ri.BuyTimestamp.Day(), ri.BuyValue.StringFixed(2),
ri.Fees.StringFixed(2), ri.Taxes.StringFixed(2), ri.BrokerCountry,
})
}
sumTxt := pp.translator.Translate("sum", 1, nil)
tw.AppendFooter(table.Row{sumTxt, sumTxt, sumTxt, sumTxt, sumTxt, aw.TotalEarned(), "", "", "", aw.TotalSpent(), aw.TotalFees(), aw.TotalTaxes()}, table.RowConfig{AutoMerge: true, AutoMergeAlign: text.AlignRight})
return tw
}
func (pp *PrettyPrinter) Render(aw *internal.AggregatorWriter) {
t := pp.buildTable(aw)
t.SetOutputMirror(pp.output)
t.Render()
}
// RenderHTML builds a fresh table from aw and renders it as an HTML string
// using go-pretty's HTMLOptions.
func (pp *PrettyPrinter) RenderHTML(aw *internal.AggregatorWriter) string {
t := pp.buildTable(aw)
t.Style().HTML = table.HTMLOptions{
CSSClass: "table table-striped table-hover table-bordered",
EscapeText: true,
Newline: "<br/>",
EmptyColumn: "&nbsp;",
}
return t.RenderHTML()
}
func colEuros(n int) table.ColumnConfig {
return table.ColumnConfig{
Number: n,
Align: text.AlignRight,
AlignFooter: text.AlignRight,
AlignHeader: text.AlignRight,
WidthMin: 12,
WidthMax: 15,
Transformer: func(val any) string {
return fmt.Sprintf("%v €", val)
},
TransformerFooter: func(val any) string {
return fmt.Sprintf("%v €", val)
},
}
}
func colOther(n int) table.ColumnConfig {
return table.ColumnConfig{
Number: n,
Align: text.AlignLeft,
AlignFooter: text.AlignLeft,
AlignHeader: text.AlignLeft,
WidthMax: 12,
}
}
func colCountry(n int) table.ColumnConfig {
return table.ColumnConfig{
Number: n,
Align: text.AlignLeft,
AlignFooter: text.AlignLeft,
AlignHeader: text.AlignLeft,
WidthMax: 24,
WidthMaxEnforcer: text.Trim,
Transformer: func(val any) string {
countryCode := val.(int64)
return fmt.Sprintf("%v - %s", val, countries.ByNumeric(int(countryCode)).Info().Name)
},
}
}