diff --git a/cmd/conv/main.go b/cmd/conv/main.go index 063baed..6ea6478 100644 --- a/cmd/conv/main.go +++ b/cmd/conv/main.go @@ -1,56 +1,23 @@ package main import ( - "bytes" _ "embed" "fmt" "log" - "strconv" + "os" - "git.ecogood.org/andreas.schroepfer/excelConverter/pkg/ecalc" "git.ecogood.org/andreas.schroepfer/excelConverter/pkg/loader" - "git.ecogood.org/andreas.schroepfer/excelConverter/pkg/set" - excelize "github.com/360EntSecGroup-Skylar/excelize/v2" ) //go:embed default.json var defaultConf []byte func main() { - buf := bytes.NewBuffer(defaultConf) - conf := &loader.Conf{} - err := conf.DecodeJSON(buf) - //fmt.Println(err, conf) - - eBalance := ecalc.Ecalc{ - CompanyFacts: ecalc.CompanyFacts{}, + xf, err := os.Open("../../test/gwb-rechner_5_0_4_vollbilanz.xlsx") + if err != nil { + log.Fatal("cannot open file:", err) } - xFile, err := excelize.OpenFile("../../test/gwb-rechner_5_0_4_vollbilanz.xlsx") - fmt.Println("openFile err :", err) - //sheets := make(map[int]) - for _, v := range conf.Values { - cellValue, err := xFile.GetCellValue( - xFile.GetSheetName(v.Sheet), - v.Cell, - ) - if err != nil { - log.Println("error", err) - } - var inValue interface{} - switch v.Struct { - case "CompanyFacts": - switch v.Type { - case "int": - inValue, err = strconv.Atoi(cellValue) - if err != nil { - fmt.Println(xFile.GetSheetName(v.Sheet)) - fmt.Println("err ATOI", err) - } - default: - inValue = cellValue - } - set.Field(&eBalance.CompanyFacts, v.Field, inValue) - } - } - fmt.Printf("%#v", eBalance) + defer xf.Close() + eBalance, err := loader.XLSX(xf, nil) + fmt.Printf("%s\n%#v", err, eBalance.CompanyFacts.IndustrySectors) } diff --git a/cmd/test/main.go b/cmd/test/main.go deleted file mode 100644 index 6ec5cdd..0000000 --- a/cmd/test/main.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import ( - "fmt" - - xlsx "github.com/tealeg/xlsx/v3" -) - -func main() { - xls, err := xlsx.OpenFile("../../test/gwb-rechner_5_0_4_vollbilanz.xlsx") - fmt.Println("OpenFile err:", err) - sl, err := xls.ToSlice() - fmt.Printf("err: %v\n\n%#v", err, sl[1]) -} diff --git a/cmd/test2/conf.json b/cmd/test2/conf.json deleted file mode 100644 index e1c5d58..0000000 --- a/cmd/test2/conf.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "title": "", - "version": "", - "values": [ - { - "sheet": 0, - "cell": "", - "type": "", - "struct": "", - "field": "", - "default": "" - } - ], - "areas": [ - { - "sheet": 0, - "start_row": 0, - "end_row": 0, - "struct": "", - "cols": [ - { - "col": "", - "field": "", - "type": "", - "default": "" - } - ] - } - ], - "rating": { - "sheet": 0, - "start_row": 0, - "end_row": 0, - "points_col": "", - "max_points_col": "", - "id_col": "", - "short_name_col": "", - "name_col": "", - "estimations_col": "", - "weight_col": "", - "selected_by_user_col": "" - } -} diff --git a/cmd/test2/main.go b/cmd/test2/main.go deleted file mode 100644 index 957052f..0000000 --- a/cmd/test2/main.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "git.ecogood.org/andreas.schroepfer/excelConverter/pkg/loader" - excelize "github.com/360EntSecGroup-Skylar/excelize/v2" -) - -func main() { - xFile, err := excelize.OpenFile("../../test/gwb-rechner_5_0_4_vollbilanz.xlsx") - fmt.Println("openFile err :", err) - sheets := xFile.GetSheetMap() - rows, _ := xFile.GetRows(sheets[4]) - fmt.Printf("%#v", rows) - c := loader.Conf{} - c.Values = append(c.Values, loader.Value{}) - c.Areas = append(c.Areas, loader.Area{ - Cols: []loader.AreaCol{ - loader.AreaCol{}, - }, - }) - fd, err := os.OpenFile("conf.json", os.O_CREATE, 0777) - fmt.Println(err) - defer fd.Close() - c.EncodeJSON(fd) -} diff --git a/pkg/ecalc/ecalc.go b/pkg/ecalc/ecalc.go index 71fdc67..612d72d 100644 --- a/pkg/ecalc/ecalc.go +++ b/pkg/ecalc/ecalc.go @@ -24,7 +24,7 @@ type CompanyFacts struct { NumberOfEmployees int `json:"numberOfEmployees,omitempty"` HasCanteen bool `json:"hasCanteen,omitempty"` IsB2B bool `json:"isB2B,omitempty"` - AverageJourneyToWorkForStaffInKm float32 `json:"averageJourneyToWorkForStaffInKm,omitempty"` + AverageJourneyToWorkForStaffInKm float64 `json:"averageJourneyToWorkForStaffInKm,omitempty"` Rating Rating `json:"rating,omitempty"` } @@ -38,7 +38,7 @@ type SupplyFraction struct { type EmployeesFraction struct { ID int `json:"id,omitempty"` CountryCode string `json:"countryCode,omitempty"` - Percentage float32 `json:"percentage,omitempty"` + Percentage float64 `json:"percentage,omitempty"` } type IndustrySector struct { @@ -54,24 +54,24 @@ type Rating struct { type Topic struct { Points int `json:"points,omitempty"` - MaxPoints float32 `json:"maxPoints,omitempty"` + MaxPoints float64 `json:"maxPoints,omitempty"` ID int `json:"id,omitempty"` ShortName string `json:"shortName,omitempty"` Name string `json:"name,omitempty"` Estimations int `json:"estimations,omitempty"` - Weight float32 `json:"weight,omitempty"` + Weight float64 `json:"weight,omitempty"` IsWeightSelectedByUser bool `json:"isWeightSelectedByUser,omitempty"` Aspects []Aspect `json:"aspects,omitempty"` } type Aspect struct { Points int `json:"points,omitempty"` - MaxPoints float32 `json:"maxPoints,omitempty"` + MaxPoints float64 `json:"maxPoints,omitempty"` ID int `json:"id,omitempty"` ShortName string `json:"shortName,omitempty"` Name string `json:"name,omitempty"` Estimations int `json:"estimations,omitempty"` - Weight float32 `json:"weight,omitempty"` + Weight float64 `json:"weight,omitempty"` IsWeightSelectedByUser bool `json:"isWeightSelectedByUser,omitempty"` Aspects []Aspect `json:"aspects,omitempty"` IsPositive bool `json:"isPositive,omitempty"` diff --git a/pkg/loader/conf.go b/pkg/loader/conf.go index 33092b9..5cdeab5 100644 --- a/pkg/loader/conf.go +++ b/pkg/loader/conf.go @@ -1,10 +1,28 @@ package loader import ( + "bytes" + _ "embed" "encoding/json" "io" + "log" ) +//go:embed conf/default.json +var defaultConf []byte + +// DefaultConf loads the defaultConf of the package. The base +// is the default.json inside the package definition. +func DefaultConf() *Conf { + buf := bytes.NewBuffer(defaultConf) + conf := &Conf{} + err := conf.DecodeJSON(buf) + if err != nil { + log.Println("cannot decode default conf: ", err) + } + return conf +} + // Conf configures the mapping from the excel version // to the eCalc structure type Conf struct { @@ -15,6 +33,20 @@ type Conf struct { Rating Rating `json:"rating"` } +// EncodeJSON writes the JSON of the conf into the Writer +func (c Conf) EncodeJSON(w io.Writer) error { + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + return enc.Encode(c) +} + +// DecodeJSON writes the configuration from the reader into +// the pointer of the conf +func (c *Conf) DecodeJSON(r io.Reader) error { + dec := json.NewDecoder(r) + return dec.Decode(c) +} + // Value defines a single value inside the excel workbook type Value struct { Sheet int `json:"sheet"` @@ -61,17 +93,3 @@ type Rating struct { WeightCol string `json:"weight_col"` SelectedByUserCol string `json:"selected_by_user_col"` } - -// EncodeJSON writes the JSON of the conf into the Writer -func (c Conf) EncodeJSON(w io.Writer) error { - enc := json.NewEncoder(w) - enc.SetIndent("", " ") - return enc.Encode(c) -} - -// DecodeJSON writes the configuration from the reader into -// the pointer of the conf -func (c *Conf) DecodeJSON(r io.Reader) error { - dec := json.NewDecoder(r) - return dec.Decode(c) -} diff --git a/conf/default.json b/pkg/loader/conf/default.json similarity index 91% rename from conf/default.json rename to pkg/loader/conf/default.json index 275b2dd..6184fe6 100644 --- a/conf/default.json +++ b/pkg/loader/conf/default.json @@ -3,7 +3,7 @@ "version": "5.04", "values": [ { - "sheet": 3, + "sheet": 2, "cell": "C7", "type": "int", "struct": "CompanyFacts", @@ -11,7 +11,7 @@ "default": "" }, { - "sheet": 3, + "sheet": 2, "cell": "C27", "type": "int", "struct": "CompanyFacts", @@ -19,7 +19,7 @@ "default": "" }, { - "sheet": 3, + "sheet": 2, "cell": "C18", "type": "int", "struct": "CompanyFacts", @@ -27,7 +27,7 @@ "default": "" }, { - "sheet": 3, + "sheet": 2, "cell": "C19", "type": "int", "struct": "CompanyFacts", @@ -35,7 +35,7 @@ "default": "" }, { - "sheet": 3, + "sheet": 2, "cell": "C20", "type": "int", "struct": "CompanyFacts", @@ -43,7 +43,7 @@ "default": "" }, { - "sheet": 3, + "sheet": 2, "cell": "C22", "type": "int", "struct": "CompanyFacts", @@ -51,7 +51,7 @@ "default": "" }, { - "sheet": 3, + "sheet": 2, "cell": "C37", "type": "int", "struct": "CompanyFacts", @@ -59,7 +59,7 @@ "default": "" }, { - "sheet": 3, + "sheet": 2, "cell": "C21", "type": "int", "struct": "CompanyFacts", @@ -67,7 +67,7 @@ "default": "" }, { - "sheet": 3, + "sheet": 2, "cell": "C23", "type": "int", "struct": "CompanyFacts", @@ -75,7 +75,7 @@ "default": "" }, { - "sheet": 3, + "sheet": 2, "cell": "C26", "type": "int", "struct": "CompanyFacts", @@ -83,7 +83,7 @@ "default": "" }, { - "sheet": 3, + "sheet": 2, "cell": "C24", "type": "bool", "struct": "CompanyFacts", @@ -91,7 +91,7 @@ "default": "" }, { - "sheet": 3, + "sheet": 2, "cell": "C38", "type": "bool", "struct": "CompanyFacts", @@ -99,7 +99,7 @@ "default": "" }, { - "sheet": 3, + "sheet": 2, "cell": "C33", "type": "int", "struct": "CompanyFacts", @@ -109,7 +109,7 @@ ], "areas": [ { - "sheet": "3", + "sheet": 2, "start_row": 10, "end_row": 14, "struct": "SupplyFraction", @@ -135,7 +135,7 @@ ] }, { - "sheet": "3", + "sheet": 2, "start_row": 30, "end_row": 32, "struct": "EmployeesFraction", @@ -149,13 +149,13 @@ { "col": "D", "field": "Percentage", - "type": "float32", + "type": "float64", "default": "" } ] }, { - "sheet": "3", + "sheet": 2, "start_row": 41, "end_row": 43, "struct": "IndustrySector", @@ -182,7 +182,7 @@ } ], "rating": { - "sheet": 4, + "sheet": 3, "start_row": 9, "end_row": 93, "points_col": "I", diff --git a/pkg/loader/loader.go b/pkg/loader/loader.go new file mode 100644 index 0000000..4470237 --- /dev/null +++ b/pkg/loader/loader.go @@ -0,0 +1,99 @@ +package loader + +import ( + "fmt" + "io" + + "git.ecogood.org/andreas.schroepfer/excelConverter/pkg/ecalc" + "git.ecogood.org/andreas.schroepfer/excelConverter/pkg/set" + "github.com/360EntSecGroup-Skylar/excelize/v2" +) + +// XLSX reads the data out of the reader. If conf is nil +// the default configuration is used. +func XLSX(r io.Reader, conf *Conf) (*ecalc.Ecalc, error) { + eBalance := &ecalc.Ecalc{} + var errs []error + + if conf == nil { + conf = DefaultConf() + } + xFile, err := excelize.OpenReader(r) + if err != nil { + return nil, fmt.Errorf("XLSX.OpenReader: %w", err) + } + for _, v := range conf.Values { + cellValue, err := xFile.GetCellValue( + xFile.GetSheetName(v.Sheet), + v.Cell, + ) + if err != nil { + errs = append(errs, err) + } + switch v.Struct { + case "CompanyFacts": + set.Field(&eBalance.CompanyFacts, v.Field, cellValue) + } + } + for _, a := range conf.Areas { + switch a.Struct { + case "SupplyFraction": + for r := a.StartRow; r <= a.EndRow; r++ { + sf := ecalc.SupplyFraction{} + for _, c := range a.Cols { + cellValue, err := xFile.GetCellValue( + xFile.GetSheetName(a.Sheet), + fmt.Sprintf("%s%d", c.Col, r), + ) + if err != nil { + errs = append(errs, err) + } + set.Field(&sf, c.Field, cellValue) + } + eBalance.CompanyFacts.SupplyFractions = + append( + eBalance.CompanyFacts.SupplyFractions, + sf) + } + case "EmployeesFraction": + for r := a.StartRow; r <= a.EndRow; r++ { + ef := ecalc.EmployeesFraction{} + for _, c := range a.Cols { + cellValue, err := xFile.GetCellValue( + xFile.GetSheetName(a.Sheet), + fmt.Sprintf("%s%d", c.Col, r), + ) + if err != nil { + errs = append(errs, err) + } + set.Field(&ef, c.Field, cellValue) + } + eBalance.CompanyFacts.EmployeesFractions = + append( + eBalance.CompanyFacts.EmployeesFractions, + ef) + } + case "IndustrySector": + for r := a.StartRow; r <= a.EndRow; r++ { + is := ecalc.IndustrySector{} + for _, c := range a.Cols { + cellValue, err := xFile.GetCellValue( + xFile.GetSheetName(a.Sheet), + fmt.Sprintf("%s%d", c.Col, r), + ) + if err != nil { + errs = append(errs, err) + } + set.Field(&is, c.Field, cellValue) + } + eBalance.CompanyFacts.IndustrySectors = + append( + eBalance.CompanyFacts.IndustrySectors, + is) + } + } + + } + // TODO: error handling of errs + return eBalance, nil +} diff --git a/pkg/set/set.go b/pkg/set/set.go index 36b42b0..9db6c90 100644 --- a/pkg/set/set.go +++ b/pkg/set/set.go @@ -3,16 +3,37 @@ package set import ( "errors" "reflect" + "strconv" ) -func Field(dst interface{}, fieldName string, value interface{}) error { +var ( + ErrTypeConvert = errors.New("cannot convert input") +) + +func Field(dst interface{}, fieldName string, value string) error { valDst := reflect.ValueOf(dst).Elem() dstField := valDst.FieldByName(fieldName) val := reflect.ValueOf(value) if dstField.Kind() != val.Kind() { - return errors.New("value-Type does not match to field") + // try to convert + switch dstField.Kind() { + case reflect.Int: + d, err := strconv.Atoi(value) + if err != nil { + return ErrTypeConvert + } + val = reflect.ValueOf(d) + case reflect.Float64: + f, err := strconv.ParseFloat(value, 64) + if err != nil { + return ErrTypeConvert + } + val = reflect.ValueOf(f) + default: + return ErrTypeConvert + } } dstField.Set(val) diff --git a/pkg/set/set_test.go b/pkg/set/set_test.go index bd492a6..037a31f 100644 --- a/pkg/set/set_test.go +++ b/pkg/set/set_test.go @@ -6,6 +6,7 @@ func TestField(t *testing.T) { input := struct { MyString string MyInt int + MyFloat float64 MyBool bool }{} value := "abc" @@ -13,15 +14,21 @@ func TestField(t *testing.T) { if input.MyString != value { t.Errorf("got: %s; want: %s", input.MyString, value) } - vInt := 2 - Field(&input, "MyInt", vInt) - if input.MyInt != vInt { - t.Errorf("MyInt: %d, want: %d", input.MyInt, vInt) + err := Field(&input, "MyFloat", "3.14") + if err != nil { + t.Error("expect no error, when can be converted") } - err := Field(&input, "MyInt", "vInt") + if input.MyFloat != 3.14 { + t.Errorf("MyFloat is not 3.14. Got: %#v", input.MyFloat) + } + err = Field(&input, "MyInt", "vInt") if err == nil { t.Error("expect error, when wrong value-Type") } + err = Field(&input, "MyInt", "3") + if err != nil { + t.Error("expect no error, when can be converted") + } err = Field(&input, "NoField", "jjj") if err == nil { t.Error("expect error, when field not exists") diff --git a/test/gwb-rechner_5_0_4_vollbilanz.xlsx b/test/gwb-rechner_5_0_4_vollbilanz.xlsx index beb35dd..6fa4217 100644 Binary files a/test/gwb-rechner_5_0_4_vollbilanz.xlsx and b/test/gwb-rechner_5_0_4_vollbilanz.xlsx differ