From fabaeab8d0b7599b22d1e851376dc8bab34ef321 Mon Sep 17 00:00:00 2001 From: Marvin Preuss Date: Tue, 11 Apr 2023 17:15:16 +0000 Subject: [PATCH] feat: introduces categories --- internal/dirtoexcel/dirtoexcel.go | 81 +++++++++++++++++++++----- internal/dirtoexcel/dirtoexcel_test.go | 66 +++++++++++++++------ 2 files changed, 115 insertions(+), 32 deletions(-) diff --git a/internal/dirtoexcel/dirtoexcel.go b/internal/dirtoexcel/dirtoexcel.go index a07fb21..2b5213f 100644 --- a/internal/dirtoexcel/dirtoexcel.go +++ b/internal/dirtoexcel/dirtoexcel.go @@ -6,6 +6,7 @@ import ( "os" "regexp" "strconv" + "strings" "time" "github.com/lmittmann/tint" @@ -14,8 +15,6 @@ import ( "golang.org/x/exp/slog" ) -const re = `^(?P[a-zA-Z0-9-]+)(?:_[a-zA-Z]+)?_(?P\d+\.\d{1,2})` - func init() { slog.SetDefault(slog.New(tint.Options{ Level: slog.LevelDebug, @@ -23,6 +22,37 @@ func init() { }.NewHandler(os.Stderr))) } +const re = `^(?P[a-zA-Z0-9-]+)(?:_[a-zA-Z]+)?_(?P\d+\.\d{1,2})` + +type Value struct { + Company string + Value float64 +} + +type Categories map[string][]Value + +func (c Categories) Insert(company, value string) error { + fValue, err := strconv.ParseFloat(value, 32) + if err != nil { + return fmt.Errorf("failed to convert string to float: %w", err) + } + + fValue = math.Round(fValue*100) / 100 + + company = strings.ToUpper(company) + + switch company { + case "ALDI", "EDEKA": + c["Food"] = append(c["Food"], Value{Company: company, Value: fValue}) + case "AMAZON": + c["Non-Food"] = append(c["Non-Food"], Value{Company: company, Value: fValue}) + default: + c["Other"] = append(c["Other"], Value{Company: company, Value: fValue}) + } + + return nil +} + func Run(cmd *cobra.Command, args []string) { outfile, err := cmd.Flags().GetString("out") if err != nil { @@ -43,6 +73,8 @@ func Run(cmd *cobra.Command, args []string) { func Create(out, dir string) error { log := slog.With("dir", dir, "out", out) + c := Categories{} + f := excelize.NewFile() defer func() { if err := f.Close(); err != nil { @@ -60,14 +92,15 @@ func Create(out, dir string) error { return fmt.Errorf("failed to compile regex: %w", err) } - for i, j := range files { - if j.IsDir() { + // Extract data from filenames. Filling them into categories. + for _, i := range files { + if i.IsDir() { continue } - log.Info("found", "file", j.Name()) + log.Info("found", "file", i.Name()) - res := r.FindAllStringSubmatch(j.Name(), -1) + res := r.FindAllStringSubmatch(i.Name(), -1) if res == nil { return fmt.Errorf("nothing found") } @@ -77,24 +110,42 @@ func Create(out, dir string) error { } company := res[0][1] + value := res[0][2] - log.Debug("extracted", slog.String("company", company)) + log.Debug("extracted", slog.String("company", company), slog.String("value", value)) - if err := f.SetCellStr("Sheet1", fmt.Sprintf("A%d", i+1), company); err != nil { - return fmt.Errorf("failed to set cell: %w", err) + if err := c.Insert(company, value); err != nil { + return fmt.Errorf("failed to insert data: %w", err) + } + } + + for cat, item := range c { + if err := f.DeleteSheet("Sheet1"); err != nil { + return fmt.Errorf("failed to remove default sheet: %w", err) } - value, err := strconv.ParseFloat(res[0][2], 32) + _, err := f.NewSheet(cat) if err != nil { - return fmt.Errorf("failed to parse value: %w", err) + return fmt.Errorf("failed to create new sheet: %w", err) } - rValue := math.Round(value*100) / 100 + m := map[string]float64{} + for _, i := range item { + m[i.Company] += i.Value + } - log.Debug("extracted", slog.Float64("value", rValue)) + count := 1 - if err := f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i+1), rValue); err != nil { - return fmt.Errorf("failed to set cell: %w", err) + for k, v := range m { + if err := f.SetCellStr(cat, fmt.Sprintf("A%d", count), k); err != nil { + return fmt.Errorf("failed to set cell: %w", err) + } + + if err := f.SetCellFloat(cat, fmt.Sprintf("B%d", count), v, 2, 64); err != nil { + return fmt.Errorf("failed to set cell: %w", err) + } + + count += 1 } } diff --git a/internal/dirtoexcel/dirtoexcel_test.go b/internal/dirtoexcel/dirtoexcel_test.go index 8fe6fbe..5cc5fd4 100644 --- a/internal/dirtoexcel/dirtoexcel_test.go +++ b/internal/dirtoexcel/dirtoexcel_test.go @@ -18,24 +18,36 @@ func TestCreate(t *testing.T) { } tables := []struct { - name string - files []string - want []want + name string + files []string + sheets map[string][]want }{ { "00", []string{ "amazon_foo_12.34_bar.jpg", "bma-zon_12.34_bar.jpg", + "aldi_10.98.jpg", + "aldi_10.00.jpg", }, - []want{ - { - "amazon", - "12.34", + map[string][]want{ + "Food": { + { + "ALDI", + "20.98", + }, }, - { - "bma-zon", - "12.34", + "Non-Food": { + { + "AMAZON", + "12.34", + }, + }, + "Other": { + { + "BMA-ZON", + "12.34", + }, }, }, }, @@ -59,6 +71,14 @@ func TestCreate(t *testing.T) { err := dirtoexcel.Create(outfile, dir) is.NoErr(err) + // NOTE: Uncomment this to view excel file. + // + // eFile, err := os.ReadFile(outfile) + // is.NoErr(err) + // + // err = os.WriteFile(path.Join("/home/marv/wip/amseltools", "foo.xlsx"), eFile, 0o644) + // is.NoErr(err) + // Read the excel. f, err := excelize.OpenFile(outfile) is.NoErr(err) @@ -68,17 +88,29 @@ func TestCreate(t *testing.T) { is.NoErr(err) }() - for i := range tt.files { - company, err := f.GetCellValue("sheet1", fmt.Sprintf("A%d", i+1)) - is.NoErr(err) + for name, values := range tt.sheets { + for i, j := range values { + company, err := f.GetCellValue(name, fmt.Sprintf("A%d", i+1)) + is.NoErr(err) - is.Equal(tt.want[i].Company, company) + is.Equal(j.Company, company) - value, err := f.GetCellValue("sheet1", fmt.Sprintf("B%d", i+1)) - is.NoErr(err) + value, err := f.GetCellValue(name, fmt.Sprintf("B%d", i+1)) + is.NoErr(err) - is.Equal(tt.want[i].Value, value) + is.Equal(j.Value, value) + } } }) } } + +func TestCategoriesInsert(t *testing.T) { + is := is.New(t) + + m := dirtoexcel.Categories{} + err := m.Insert("amazon", "11.11") + is.NoErr(err) + + is.Equal(dirtoexcel.Categories{"Non-Food": []dirtoexcel.Value{{"AMAZON", 11.11}}}, m) +}