From 892635b5e87812bc5a70e46794913937bb3d6c81 Mon Sep 17 00:00:00 2001
From: icanc0 <44480394+icanc0@users.noreply.github.com>
Date: Wed, 26 Jun 2024 05:35:41 -0700
Subject: [PATCH] fix(backend): moving to csv for consistency with
import/export (#54)
---
backend/app/api/handlers/v1/v1_ctrl_items.go | 8 ++++----
.../app/api/handlers/v1/v1_ctrl_reporting.go | 4 ++--
backend/go.sum | 8 ++++++++
.../services/reporting/bill_of_materials.go | 4 ++--
.../core/services/reporting/io_row.go | 2 +-
.../core/services/reporting/io_sheet.go | 20 +++++++++----------
.../core/services/reporting/io_sheet_test.go | 8 ++++----
.../internal/core/services/service_items.go | 8 ++++----
frontend/pages/tools.vue | 6 +++---
9 files changed, 38 insertions(+), 30 deletions(-)
diff --git a/backend/app/api/handlers/v1/v1_ctrl_items.go b/backend/app/api/handlers/v1/v1_ctrl_items.go
index 272011f8..2619f4d1 100644
--- a/backend/app/api/handlers/v1/v1_ctrl_items.go
+++ b/backend/app/api/handlers/v1/v1_ctrl_items.go
@@ -323,17 +323,17 @@ func (ctrl *V1Controller) HandleItemsExport() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
ctx := services.NewContext(r.Context())
- csvData, err := ctrl.svc.Items.ExportTSV(r.Context(), ctx.GID)
+ csvData, err := ctrl.svc.Items.ExportCSV(r.Context(), ctx.GID)
if err != nil {
log.Err(err).Msg("failed to export items")
return validate.NewRequestError(err, http.StatusInternalServerError)
}
- w.Header().Set("Content-Type", "text/tsv")
- w.Header().Set("Content-Disposition", "attachment;filename=homebox-items.tsv")
+ w.Header().Set("Content-Type", "text/csv")
+ w.Header().Set("Content-Disposition", "attachment;filename=homebox-items.csv")
writer := csv.NewWriter(w)
- writer.Comma = '\t'
+ writer.Comma = ','
return writer.WriteAll(csvData)
}
}
diff --git a/backend/app/api/handlers/v1/v1_ctrl_reporting.go b/backend/app/api/handlers/v1/v1_ctrl_reporting.go
index e51465b3..d3b712f8 100644
--- a/backend/app/api/handlers/v1/v1_ctrl_reporting.go
+++ b/backend/app/api/handlers/v1/v1_ctrl_reporting.go
@@ -1,9 +1,9 @@
package v1
import (
- "net/http"
"github.com/hay-kot/httpkit/errchain"
"github.com/sysadminsmedia/homebox/backend/internal/core/services"
+ "net/http"
)
// HandleBillOfMaterialsExport godoc
@@ -18,7 +18,7 @@ func (ctrl *V1Controller) HandleBillOfMaterialsExport() errchain.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
actor := services.UseUserCtx(r.Context())
- csv, err := ctrl.svc.Items.ExportBillOfMaterialsTSV(r.Context(), actor.GroupID)
+ csv, err := ctrl.svc.Items.ExportBillOfMaterialsCSV(r.Context(), actor.GroupID)
if err != nil {
return err
}
diff --git a/backend/go.sum b/backend/go.sum
index 42650d5e..b7c00b66 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -111,6 +111,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
@@ -120,6 +122,8 @@ github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJm
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/olahol/melody v1.1.4 h1:RQHfKZkQmDxI0+SLZRNBCn4LiXdqxLKRGSkT8Dyoe/E=
github.com/olahol/melody v1.1.4/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
@@ -137,6 +141,10 @@ github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
+github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
+github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
diff --git a/backend/internal/core/services/reporting/bill_of_materials.go b/backend/internal/core/services/reporting/bill_of_materials.go
index 7bdf2143..e6873d70 100644
--- a/backend/internal/core/services/reporting/bill_of_materials.go
+++ b/backend/internal/core/services/reporting/bill_of_materials.go
@@ -20,9 +20,9 @@ type BillOfMaterialsEntry struct {
TotalPrice float64 `csv:"Total Price"`
}
-// BillOfMaterialsTSV returns a byte slice of the Bill of Materials for a given GID in TSV format
+// BillOfMaterialsCSV returns a byte slice of the Bill of Materials for a given GID in CSV format
// See BillOfMaterialsEntry for the format of the output
-func BillOfMaterialsTSV(entities []repo.ItemOut) ([]byte, error) {
+func BillOfMaterialsCSV(entities []repo.ItemOut) ([]byte, error) {
bomEntries := make([]BillOfMaterialsEntry, len(entities))
for i, entity := range entities {
bomEntries[i] = BillOfMaterialsEntry{
diff --git a/backend/internal/core/services/reporting/io_row.go b/backend/internal/core/services/reporting/io_row.go
index e3bc0b1b..ca3c8010 100644
--- a/backend/internal/core/services/reporting/io_row.go
+++ b/backend/internal/core/services/reporting/io_row.go
@@ -12,7 +12,7 @@ type ExportItemFields struct {
Value string
}
-type ExportTSVRow struct {
+type ExportCSVRow struct {
ImportRef string `csv:"HB.import_ref"`
Location LocationString `csv:"HB.location"`
LabelStr LabelString `csv:"HB.labels"`
diff --git a/backend/internal/core/services/reporting/io_sheet.go b/backend/internal/core/services/reporting/io_sheet.go
index c6c52872..08676bd0 100644
--- a/backend/internal/core/services/reporting/io_sheet.go
+++ b/backend/internal/core/services/reporting/io_sheet.go
@@ -19,12 +19,12 @@ import (
// items from homebox. It is used to read/write the data from/to a CSV/TSV file given
// the standard format of the file.
//
-// See ExportTSVRow for the format of the data in the sheet.
+// See ExportCSVRow for the format of the data in the sheet.
type IOSheet struct {
headers []string
custom []int
index map[string]int
- Rows []ExportTSVRow
+ Rows []ExportCSVRow
}
func (s *IOSheet) indexHeaders() {
@@ -70,16 +70,16 @@ func (s *IOSheet) Read(data io.Reader) error {
}
s.headers = sheet[0]
- s.Rows = make([]ExportTSVRow, len(sheet)-1)
+ s.Rows = make([]ExportCSVRow, len(sheet)-1)
for i, row := range sheet[1:] {
if len(row) != len(s.headers) {
return fmt.Errorf("row has %d columns, expected %d", len(row), len(s.headers))
}
- rowData := ExportTSVRow{}
+ rowData := ExportCSVRow{}
- st := reflect.TypeOf(ExportTSVRow{})
+ st := reflect.TypeOf(ExportCSVRow{})
for i := 0; i < st.NumField(); i++ {
field := st.Field(i)
@@ -154,7 +154,7 @@ func (s *IOSheet) Read(data io.Reader) error {
// ReadItems writes the sheet to a writer.
func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.UUID, repos *repo.AllRepos) error {
- s.Rows = make([]ExportTSVRow, len(items))
+ s.Rows = make([]ExportCSVRow, len(items))
extraHeaders := map[string]struct{}{}
@@ -189,7 +189,7 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
}
}
- s.Rows[i] = ExportTSVRow{
+ s.Rows[i] = ExportCSVRow{
// fill struct
Location: locString,
LabelStr: labelString,
@@ -232,7 +232,7 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
sort.Strings(customHeaders)
- st := reflect.TypeOf(ExportTSVRow{})
+ st := reflect.TypeOf(ExportCSVRow{})
// Write headers
for i := 0; i < st.NumField(); i++ {
@@ -252,8 +252,8 @@ func (s *IOSheet) ReadItems(ctx context.Context, items []repo.ItemOut, GID uuid.
return nil
}
-// TSV writes the current sheet to a writer in TSV format.
-func (s *IOSheet) TSV() ([][]string, error) {
+// CSV writes the current sheet to a 2d array, for compatibility with TSV/CSV files.
+func (s *IOSheet) CSV() ([][]string, error) {
memcsv := make([][]string, len(s.Rows)+1)
memcsv[0] = s.headers
diff --git a/backend/internal/core/services/reporting/io_sheet_test.go b/backend/internal/core/services/reporting/io_sheet_test.go
index 5bdbc898..a489d2f8 100644
--- a/backend/internal/core/services/reporting/io_sheet_test.go
+++ b/backend/internal/core/services/reporting/io_sheet_test.go
@@ -27,13 +27,13 @@ func TestSheet_Read(t *testing.T) {
tests := []struct {
name string
data []byte
- want []ExportTSVRow
+ want []ExportCSVRow
wantErr bool
}{
{
name: "minimal import",
data: minimalImportCSV,
- want: []ExportTSVRow{
+ want: []ExportCSVRow{
{Location: LocationString{"loc"}, Name: "Item 1", Quantity: 1, Description: "Description 1"},
{Location: LocationString{"loc"}, Name: "Item 2", Quantity: 2, Description: "Description 2"},
{Location: LocationString{"loc"}, Name: "Item 3", Quantity: 3, Description: "Description 3"},
@@ -42,7 +42,7 @@ func TestSheet_Read(t *testing.T) {
{
name: "custom field import",
data: customFieldImportCSV,
- want: []ExportTSVRow{
+ want: []ExportCSVRow{
{
Location: LocationString{"loc"}, Name: "Item 1", Quantity: 1, Description: "Description 1",
Fields: []ExportItemFields{
@@ -72,7 +72,7 @@ func TestSheet_Read(t *testing.T) {
{
name: "custom types import",
data: customTypesImportCSV,
- want: []ExportTSVRow{
+ want: []ExportCSVRow{
{
Name: "Item 1",
AssetID: repo.AssetID(1),
diff --git a/backend/internal/core/services/service_items.go b/backend/internal/core/services/service_items.go
index 55136cef..d6f1896c 100644
--- a/backend/internal/core/services/service_items.go
+++ b/backend/internal/core/services/service_items.go
@@ -329,7 +329,7 @@ func (svc *ItemService) CsvImport(ctx context.Context, GID uuid.UUID, data io.Re
return finished, nil
}
-func (svc *ItemService) ExportTSV(ctx context.Context, GID uuid.UUID) ([][]string, error) {
+func (svc *ItemService) ExportCSV(ctx context.Context, GID uuid.UUID) ([][]string, error) {
items, err := svc.repo.Items.GetAll(ctx, GID)
if err != nil {
return nil, err
@@ -342,14 +342,14 @@ func (svc *ItemService) ExportTSV(ctx context.Context, GID uuid.UUID) ([][]strin
return nil, err
}
- return sheet.TSV()
+ return sheet.CSV()
}
-func (svc *ItemService) ExportBillOfMaterialsTSV(ctx context.Context, GID uuid.UUID) ([]byte, error) {
+func (svc *ItemService) ExportBillOfMaterialsCSV(ctx context.Context, GID uuid.UUID) ([]byte, error) {
items, err := svc.repo.Items.GetAll(ctx, GID)
if err != nil {
return nil, err
}
- return reporting.BillOfMaterialsTSV(items)
+ return reporting.BillOfMaterialsCSV(items)
}
diff --git a/frontend/pages/tools.vue b/frontend/pages/tools.vue
index 98bfe302..8dcfebe1 100644
--- a/frontend/pages/tools.vue
+++ b/frontend/pages/tools.vue
@@ -22,7 +22,7 @@
Bill of Materials
- Generates a TSV (Tab Separated Values) file that can be imported into a spreadsheet program. This is a
+ Generates a CSV (Comma Separated Values) file that can be imported into a spreadsheet program. This is a
summary of your inventory with basic item and pricing information.
Generate BOM
@@ -45,7 +45,7 @@
Imports the standard CSV format for Homebox. This will not overwrite any existing items in your
inventory. It will only add new items.
-
+
Export Inventory
Exports the standard CSV format for Homebox. This will export all items in your inventory.
@@ -123,7 +123,7 @@
window.open(url, "_blank");
}
- function getExportTSV() {
+ function getExportCSV() {
const url = api.items.exportURL();
window.open(url, "_blank");
}