add additional rows to CSV importer

This commit is contained in:
Hayden 2022-09-12 20:29:54 -08:00
parent b857eec243
commit 1a13e59c5e
5 changed files with 109 additions and 91 deletions

View file

@ -156,7 +156,8 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s
if len(row) == 0 { if len(row) == 0 {
continue continue
} }
if len(row) != 14 {
if len(row) != NumOfCols {
return ErrInvalidCsv return ErrInvalidCsv
} }
@ -227,15 +228,16 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s
} }
log.Info(). log.Info().
Str("name", row.Name). Str("name", row.Item.Name).
Str("location", row.Location). Str("location", row.Location).
Strs("labels", row.getLabels()). Strs("labels", row.getLabels()).
Str("locationId", locationID.String()). Str("locationId", locationID.String()).
Msgf("Creating Item: %s", row.Name) Msgf("Creating Item: %s", row.Item.Name)
result, err := svc.repo.Items.Create(ctx, gid, types.ItemCreate{ result, err := svc.repo.Items.Create(ctx, gid, types.ItemCreate{
Name: row.Name, ImportRef: row.Item.ImportRef,
Description: row.Description, Name: row.Item.Name,
Description: row.Item.Description,
LabelIDs: labelIDs, LabelIDs: labelIDs,
LocationID: locationID, LocationID: locationID,
}) })
@ -246,21 +248,36 @@ func (svc *ItemService) CsvImport(ctx context.Context, gid uuid.UUID, data [][]s
// Update the item with the rest of the data // Update the item with the rest of the data
_, err = svc.repo.Items.Update(ctx, types.ItemUpdate{ _, err = svc.repo.Items.Update(ctx, types.ItemUpdate{
ID: result.ID, // Edges
Name: result.Name, LocationID: locationID,
LocationID: locationID, LabelIDs: labelIDs,
LabelIDs: labelIDs,
Description: result.Description, // General Fields
SerialNumber: row.SerialNumber, ID: result.ID,
ModelNumber: row.ModelNumber, Name: result.Name,
Manufacturer: row.Manufacturer, Description: result.Description,
Notes: row.Notes, Insured: row.Item.Insured,
PurchaseFrom: row.PurchaseFrom, Notes: row.Item.Notes,
PurchasePrice: row.parsedPurchasedPrice(),
PurchaseTime: row.parsedPurchasedAt(), // Identifies the item as imported
SoldTo: row.SoldTo, SerialNumber: row.Item.SerialNumber,
SoldPrice: row.parsedSoldPrice(), ModelNumber: row.Item.ModelNumber,
SoldTime: row.parsedSoldAt(), Manufacturer: row.Item.Manufacturer,
// Purchase
PurchaseFrom: row.Item.PurchaseFrom,
PurchasePrice: row.Item.PurchasePrice,
PurchaseTime: row.Item.PurchaseTime,
// Warranty
LifetimeWarranty: row.Item.LifetimeWarranty,
WarrantyExpires: row.Item.WarrantyExpires,
WarrantyDetails: row.Item.WarrantyDetails,
SoldTo: row.Item.SoldTo,
SoldPrice: row.Item.SoldPrice,
SoldTime: row.Item.SoldTime,
SoldNotes: row.Item.SoldNotes,
}) })
if err != nil { if err != nil {

View file

@ -5,10 +5,14 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/hay-kot/content/backend/internal/types"
) )
var ErrInvalidCsv = errors.New("invalid csv") var ErrInvalidCsv = errors.New("invalid csv")
const NumOfCols = 21
func parseFloat(s string) float64 { func parseFloat(s string) float64 {
if s == "" { if s == "" {
return 0 return 0
@ -26,60 +30,56 @@ func parseDate(s string) time.Time {
return p return p
} }
func parseBool(s string) bool {
switch strings.ToLower(s) {
case "true", "yes", "1":
return true
default:
return false
}
}
func parseInt(s string) int {
i, _ := strconv.Atoi(s)
return i
}
type csvRow struct { type csvRow struct {
Location string Item types.ItemSummary
Labels string Location string
Name string LabelStr string
Description string
SerialNumber string
ModelNumber string
Manufacturer string
Notes string
PurchaseFrom string
PurchasedPrice string
PurchasedAt string
SoldTo string
SoldPrice string
SoldAt string
} }
func newCsvRow(row []string) csvRow { func newCsvRow(row []string) csvRow {
return csvRow{ return csvRow{
Location: row[0], Location: row[1],
Labels: row[1], LabelStr: row[2],
Name: row[2], Item: types.ItemSummary{
Description: row[3], ImportRef: row[0],
SerialNumber: row[4], Quantity: parseInt(row[3]),
ModelNumber: row[5], Name: row[4],
Manufacturer: row[6], Description: row[5],
Notes: row[7], Insured: parseBool(row[6]),
PurchaseFrom: row[8], SerialNumber: row[7],
PurchasedPrice: row[9], ModelNumber: row[8],
PurchasedAt: row[10], Manufacturer: row[9],
SoldTo: row[11], Notes: row[10],
SoldPrice: row[12], PurchaseFrom: row[11],
SoldAt: row[13], PurchasePrice: parseFloat(row[12]),
PurchaseTime: parseDate(row[13]),
LifetimeWarranty: parseBool(row[14]),
WarrantyExpires: parseDate(row[15]),
WarrantyDetails: row[16],
SoldTo: row[17],
SoldPrice: parseFloat(row[18]),
SoldTime: parseDate(row[19]),
SoldNotes: row[20],
},
} }
} }
func (c csvRow) parsedSoldPrice() float64 {
return parseFloat(c.SoldPrice)
}
func (c csvRow) parsedPurchasedPrice() float64 {
return parseFloat(c.PurchasedPrice)
}
func (c csvRow) parsedPurchasedAt() time.Time {
return parseDate(c.PurchasedAt)
}
func (c csvRow) parsedSoldAt() time.Time {
return parseDate(c.SoldAt)
}
func (c csvRow) getLabels() []string { func (c csvRow) getLabels() []string {
split := strings.Split(c.Labels, ";") split := strings.Split(c.LabelStr, ";")
// Trim each // Trim each
for i, s := range split { for i, s := range split {

View file

@ -8,14 +8,13 @@ import (
) )
const CSV_DATA = ` const CSV_DATA = `
Location,Labels,Name,Description,Serial Number,Mode Number,Manufacturer,Notes,Purchase From,Purchased Price,Purchased At,Sold To,Sold Price,Sold At Import Ref,Location,Labels,Quantity,Name,Description,Insured,Serial Number,Mode Number,Manufacturer,Notes,Purchase From,Purchased Price,Purchased Time,Lifetime Warranty,Warranty Expires,Warranty Details,Sold To,Sold Price,Sold Time,Sold Notes
Garage,IOT;Home Assistant; Z-Wave,Zooz Universal Relay ZEN17,"Zooz 700 Series Z-Wave Universal Relay ZEN17 for Awnings, Garage Doors, Sprinklers, and More | 2 NO-C-NC Relays (20A, 10A) | Signal Repeater | Hub Required (Compatible with SmartThings and Hubitat)",,ZEN17,Zooz,,Amazon,39.95,10/13/2021,,, ,Garage,IOT;Home Assistant; Z-Wave,1,Zooz Universal Relay ZEN17,"Zooz 700 Series Z-Wave Universal Relay ZEN17 for Awnings, Garage Doors, Sprinklers, and More | 2 NO-C-NC Relays (20A, 10A) | Signal Repeater | Hub Required (Compatible with SmartThings and Hubitat)",,,ZEN17,Zooz,,Amazon,39.95,10/13/2021,,,,,,,
Living Room,IOT;Home Assistant; Z-Wave,Zooz Motion Sensor,"Zooz Z-Wave Plus S2 Motion Sensor ZSE18 with Magnetic Mount, Works with Vera and SmartThings",,ZSE18,Zooz,,Amazon,29.95,10/15/2021,,, ,Living Room,IOT;Home Assistant; Z-Wave,1,Zooz Motion Sensor,"Zooz Z-Wave Plus S2 Motion Sensor ZSE18 with Magnetic Mount, Works with Vera and SmartThings",,,ZSE18,Zooz,,Amazon,29.95,10/15/2021,,,,,,,
Office,IOT;Home Assistant; Z-Wave,Zooz 110v Power Switch,"Zooz Z-Wave Plus Power Switch ZEN15 for 110V AC Units, Sump Pumps, Humidifiers, and More",,ZEN15,Zooz,,Amazon,39.95,10/13/2021,,, ,Office,IOT;Home Assistant; Z-Wave,1,Zooz 110v Power Switch,"Zooz Z-Wave Plus Power Switch ZEN15 for 110V AC Units, Sump Pumps, Humidifiers, and More",,,ZEN15,Zooz,,Amazon,39.95,10/13/2021,,,,,,,
Downstairs,IOT;Home Assistant; Z-Wave,Ecolink Z-Wave PIR Motion Sensor,"Ecolink Z-Wave PIR Motion Detector Pet Immune, White (PIRZWAVE2.5-ECO)",,PIRZWAVE2.5-ECO,Ecolink,,Amazon,35.58,10/21/2020,,, ,Downstairs,IOT;Home Assistant; Z-Wave,1,Ecolink Z-Wave PIR Motion Sensor,"Ecolink Z-Wave PIR Motion Detector Pet Immune, White (PIRZWAVE2.5-ECO)",,,PIRZWAVE2.5-ECO,Ecolink,,Amazon,35.58,10/21/2020,,,,,,,
Entry,IOT;Home Assistant; Z-Wave,Yale Security Touchscreen Deadbolt,"Yale Security YRD226-ZW2-619 YRD226ZW2619 Touchscreen Deadbolt, Satin Nickel",,YRD226ZW2619,Yale,,Amazon,120.39,10/14/2020,,, ,Entry,IOT;Home Assistant; Z-Wave,1,Yale Security Touchscreen Deadbolt,"Yale Security YRD226-ZW2-619 YRD226ZW2619 Touchscreen Deadbolt, Satin Nickel",,,YRD226ZW2619,Yale,,Amazon,120.39,10/14/2020,,,,,,,
Kitchen,IOT;Home Assistant; Z-Wave,Smart Rocker Light Dimmer,"UltraPro Z-Wave Smart Rocker Light Dimmer with QuickFit and SimpleWire, 3-Way Ready, Compatible with Alexa, Google Assistant, ZWave Hub Required, Repeater/Range Extender, White Paddle Only, 39351",,39351,Honeywell,,Amazon,65.98,09/30/0202,,, ,Kitchen,IOT;Home Assistant; Z-Wave,1,Smart Rocker Light Dimmer,"UltraPro Z-Wave Smart Rocker Light Dimmer with QuickFit and SimpleWire, 3-Way Ready, Compatible with Alexa, Google Assistant, ZWave Hub Required, Repeater/Range Extender, White Paddle Only, 39351",,,39351,Honeywell,,Amazon,65.98,09/30/0202,,,,,,,`
`
func loadcsv() [][]string { func loadcsv() [][]string {
reader := csv.NewReader(bytes.NewBuffer([]byte(CSV_DATA))) reader := csv.NewReader(bytes.NewBuffer([]byte(CSV_DATA)))
@ -30,7 +29,7 @@ func loadcsv() [][]string {
func Test_csvRow_getLabels(t *testing.T) { func Test_csvRow_getLabels(t *testing.T) {
type fields struct { type fields struct {
Labels string LabelStr string
} }
tests := []struct { tests := []struct {
name string name string
@ -40,28 +39,28 @@ func Test_csvRow_getLabels(t *testing.T) {
{ {
name: "basic test", name: "basic test",
fields: fields{ fields: fields{
Labels: "IOT;Home Assistant;Z-Wave", LabelStr: "IOT;Home Assistant;Z-Wave",
}, },
want: []string{"IOT", "Home Assistant", "Z-Wave"}, want: []string{"IOT", "Home Assistant", "Z-Wave"},
}, },
{ {
name: "no labels", name: "no labels",
fields: fields{ fields: fields{
Labels: "", LabelStr: "",
}, },
want: []string{}, want: []string{},
}, },
{ {
name: "single label", name: "single label",
fields: fields{ fields: fields{
Labels: "IOT", LabelStr: "IOT",
}, },
want: []string{"IOT"}, want: []string{"IOT"},
}, },
{ {
name: "trailing semicolon", name: "trailing semicolon",
fields: fields{ fields: fields{
Labels: "IOT;", LabelStr: "IOT;",
}, },
want: []string{"IOT"}, want: []string{"IOT"},
}, },
@ -69,7 +68,7 @@ func Test_csvRow_getLabels(t *testing.T) {
{ {
name: "whitespace", name: "whitespace",
fields: fields{ fields: fields{
Labels: " IOT; Home Assistant; Z-Wave ", LabelStr: " IOT; Home Assistant; Z-Wave ",
}, },
want: []string{"IOT", "Home Assistant", "Z-Wave"}, want: []string{"IOT", "Home Assistant", "Z-Wave"},
}, },
@ -77,7 +76,7 @@ func Test_csvRow_getLabels(t *testing.T) {
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
c := csvRow{ c := csvRow{
Labels: tt.fields.Labels, LabelStr: tt.fields.LabelStr,
} }
if got := c.getLabels(); !reflect.DeepEqual(got, tt.want) { if got := c.getLabels(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("csvRow.getLabels() = %v, want %v", got, tt.want) t.Errorf("csvRow.getLabels() = %v, want %v", got, tt.want)

View file

@ -73,21 +73,21 @@ func TestItemService_CsvImport(t *testing.T) {
} }
for _, csvRow := range dataCsv { for _, csvRow := range dataCsv {
if csvRow.Name == item.Name { if csvRow.Item.Name == item.Name {
assert.Equal(t, csvRow.Description, item.Description) assert.Equal(t, csvRow.Item.Description, item.Description)
assert.Equal(t, csvRow.SerialNumber, item.SerialNumber) assert.Equal(t, csvRow.Item.SerialNumber, item.SerialNumber)
assert.Equal(t, csvRow.Manufacturer, item.Manufacturer) assert.Equal(t, csvRow.Item.Manufacturer, item.Manufacturer)
assert.Equal(t, csvRow.Notes, item.Notes) assert.Equal(t, csvRow.Item.Notes, item.Notes)
// Purchase Fields // Purchase Fields
assert.Equal(t, csvRow.parsedPurchasedAt(), item.PurchaseTime) assert.Equal(t, csvRow.Item.PurchaseTime, item.PurchaseTime)
assert.Equal(t, csvRow.PurchaseFrom, item.PurchaseFrom) assert.Equal(t, csvRow.Item.PurchaseFrom, item.PurchaseFrom)
assert.Equal(t, csvRow.parsedPurchasedPrice(), item.PurchasePrice) assert.Equal(t, csvRow.Item.PurchasePrice, item.PurchasePrice)
// Sold Fields // Sold Fields
assert.Equal(t, csvRow.parsedSoldAt(), item.SoldTime) assert.Equal(t, csvRow.Item.SoldTime, item.SoldTime)
assert.Equal(t, csvRow.SoldTo, item.SoldTo) assert.Equal(t, csvRow.Item.SoldTo, item.SoldTo)
assert.Equal(t, csvRow.parsedSoldPrice(), item.SoldPrice) assert.Equal(t, csvRow.Item.SoldPrice, item.SoldPrice)
} }
} }
} }

View file

@ -7,6 +7,7 @@ import (
) )
type ItemCreate struct { type ItemCreate struct {
ImportRef string `json:"-"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`
@ -53,6 +54,7 @@ type ItemUpdate struct {
} }
type ItemSummary struct { type ItemSummary struct {
ImportRef string `json:"-"`
ID uuid.UUID `json:"id"` ID uuid.UUID `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Description string `json:"description"` Description string `json:"description"`