diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8b350b2 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module challenge2019 + +go 1.17 + +require github.com/gocarina/gocsv v0.0.0-20221105105431-c8ef78125b99 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3aee0f7 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/gocarina/gocsv v0.0.0-20221105105431-c8ef78125b99 h1:qNAaZUnCulf2xIQc7rM6F3uGYr80h40rtilsVKyAHoM= +github.com/gocarina/gocsv v0.0.0-20221105105431-c8ef78125b99/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= diff --git a/main.go b/main.go new file mode 100644 index 0000000..8ed3cd5 --- /dev/null +++ b/main.go @@ -0,0 +1,93 @@ +package main + +import ( + "challenge2019/models" + "github.com/gocarina/gocsv" + "os" +) + +func main() { + options := models.Options{} + if err := ReadCsv("partners.csv", &options); err != nil { + panic(err) + } + deliveriesInput := []*models.DeliveryInput{} + if err := ReadCsvWithoutHeaders("input.csv", &deliveriesInput); err != nil { + panic(err) + } + output1 := Task1(deliveriesInput, options) + if err := WriteCsvWithoutHeaders("myoutput1.csv", output1); err != nil { + panic(err) + } + +} + +func Task1(deliveriesInput []*models.DeliveryInput, options models.Options) []*models.DeliveryOutput { + output := []*models.DeliveryOutput{} + for _, delivery := range deliveriesInput { + optionsByTheater, err := options.GetOptionsByTheater(delivery.TheatreID) + if err != nil { + output = append(output, &models.DeliveryOutput{ + Delivery: delivery.Delivery, + IsPossible: false, + PartnerID: "", + Cost: 0, + }) + continue + } + optionsBySize, err := optionsByTheater.GetOptionsBySize(delivery.Size) + if err != nil { + output = append(output, &models.DeliveryOutput{ + Delivery: delivery.Delivery, + IsPossible: false, + PartnerID: "", + Cost: 0, + }) + continue + } + bestOption := optionsBySize.GetOptionWithBestPrice(delivery.Size) + output = append(output, &models.DeliveryOutput{ + Delivery: delivery.Delivery, + IsPossible: true, + PartnerID: models.PartnerID(bestOption.PartnerID), + Cost: models.Cost(bestOption.GetPrice(delivery.Size)), + }) + } + return output +} + +func ReadCsv(fileName string, model interface{}) error { + f, err := os.Open(fileName) + if err != nil { + return err + } + defer f.Close() + if err = gocsv.UnmarshalFile(f, model); err != nil { + return err + } + return nil +} + +func ReadCsvWithoutHeaders(fileName string, model interface{}) error { + f, err := os.Open(fileName) + if err != nil { + return err + } + defer f.Close() + if err = gocsv.UnmarshalWithoutHeaders(f, model); err != nil { + return err + } + return nil +} + +func WriteCsvWithoutHeaders(fileName string, model interface{}) error { + f, err := os.Create(fileName) + if err != nil { + return err + } + defer f.Close() + if err = gocsv.MarshalWithoutHeaders(model, f); err != nil { + return err + } + return nil +} diff --git a/models/delivery.go b/models/delivery.go new file mode 100644 index 0000000..c8a522e --- /dev/null +++ b/models/delivery.go @@ -0,0 +1,47 @@ +package models + +import ( + "strconv" +) + +type DeliveryInput struct { + Delivery string + Size int + TheatreID string +} + +type Cost int +type PartnerID string + +type DeliveryOutput struct { + Delivery string + IsPossible bool + PartnerID PartnerID + Cost Cost +} + +type DeliveryOutputs []*DeliveryOutput + +func (do *DeliveryOutputs) GetPartnersDeals(pId PartnerID) DeliveryOutputs { + partnersDeals := []*DeliveryOutput{} + for _, output := range *do { + if output.PartnerID == pId { + partnersDeals = append(partnersDeals, output) + } + } + return partnersDeals +} + +func (c *Cost) MarshalCSV() (string, error) { + if *c == 0 { + return " ", nil + } + return strconv.Itoa(int(*c)), nil +} + +func (p *PartnerID) MarshalCSV() (string, error) { + if *p == "" { + return " ", nil + } + return string(*p), nil +} diff --git a/models/options.go b/models/options.go new file mode 100644 index 0000000..457bb2b --- /dev/null +++ b/models/options.go @@ -0,0 +1,87 @@ +package models + +import ( + "errors" + "sort" + "strconv" + "strings" +) + +type Option struct { + Theatre string `csv:"Theatre"` + Size Size `csv:"Size Slab (in GB)"` + MinimumCost int `csv:"Minimum cost"` + CostPerGB int `csv:"Cost Per GB"` + PartnerID string `csv:"Partner ID"` +} + +func (o Option) GetPrice(size int) int { + sizeCost := o.CostPerGB * size + if sizeCost < o.MinimumCost { + return o.MinimumCost + } + return sizeCost +} + +type Size struct { + SizeSlab string + MinSize int + MaxSize int +} + +func (s *Size) UnmarshalCSV(csv string) (err error) { + s.SizeSlab = csv + sizeRange := strings.Split(csv, "-") + s.MinSize, err = strconv.Atoi(sizeRange[0]) + if err != nil { + return err + } + s.MaxSize, err = strconv.Atoi(strings.TrimSpace(sizeRange[1])) + if err != nil { + return err + } + return nil +} + +func (s *Size) MarshalCSV() (string, error) { + return s.SizeSlab, nil +} + +type Options []*Option + +func (o Options) GetOptionsByTheater(theaterID string) (Options, error) { + optionsByTheater := []*Option{} + for _, option := range o { + if strings.TrimSpace(option.Theatre) == theaterID { + optionsByTheater = append(optionsByTheater, option) + } + } + if len(optionsByTheater) == 0 { + return nil, errors.New("there is no appropriate data") + } + return optionsByTheater, nil +} + +func (o Options) GetOptionsBySize(size int) (Options, error) { + optionsBySize := []*Option{} + for _, option := range o { + if option.Size.MinSize < size && option.Size.MaxSize > size { + optionsBySize = append(optionsBySize, option) + } + } + if len(optionsBySize) == 0 { + return nil, errors.New("there is no appropriate data") + } + return optionsBySize, nil +} + +func (o Options) GetOptionWithBestPrice(size int) *Option { + if len(o) == 1 { + return o[0] + } + sort.SliceStable(o, func(i, j int) bool { + return o[i].GetPrice(size) < o[j].GetPrice(size) + }) + + return o[0] +} diff --git a/myoutput1.csv b/myoutput1.csv new file mode 100644 index 0000000..f812f29 --- /dev/null +++ b/myoutput1.csv @@ -0,0 +1,4 @@ +D1,true,P1,2000 +D2,true,P1,3250 +D3,true,P3,15300 +D4,false," "," "