From ded68e5b6012c111ef55d8c799bc953f0ce19edf Mon Sep 17 00:00:00 2001 From: merdan Date: Fri, 12 Aug 2022 13:59:03 +0500 Subject: [PATCH] first commit --- .env. example | 16 ++ .gitignore | 5 + db_service.go | 185 +++++++++++++++ example.go | 31 +++ go.mod | 18 ++ go.sum | 45 ++++ gorm_models/brand.go | 43 ++++ gorm_models/category.go | 37 +++ gorm_models/family.go | 78 ++++++ gorm_models/product.go | 76 ++++++ gorm_models/vendor.go | 23 ++ inserter.go | 210 +++++++++++++++++ main.go | 41 ++++ models/category.go | 18 ++ models/count.go | 5 + models/product.go | 79 +++++++ pkg/helper.go | 66 ++++++ pkg/request.go | 101 ++++++++ test/testit.go | 509 ++++++++++++++++++++++++++++++++++++++++ 19 files changed, 1586 insertions(+) create mode 100644 .env. example create mode 100644 .gitignore create mode 100644 db_service.go create mode 100644 example.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 gorm_models/brand.go create mode 100644 gorm_models/category.go create mode 100644 gorm_models/family.go create mode 100644 gorm_models/product.go create mode 100644 gorm_models/vendor.go create mode 100644 inserter.go create mode 100644 main.go create mode 100644 models/category.go create mode 100644 models/count.go create mode 100644 models/product.go create mode 100644 pkg/helper.go create mode 100644 pkg/request.go create mode 100644 test/testit.go diff --git a/.env. example b/.env. example new file mode 100644 index 0000000..0a8b006 --- /dev/null +++ b/.env. example @@ -0,0 +1,16 @@ +couch_db_source=http://admin:adminadmin@192.168.1.2:5984/ + +db_trendyol=trendyol_db_v2 +db_ty_categories=ty_categories + +remote_url=https://sarga.com.tm/api/ +descendant_category_url=descendant-categories + +db=test_baza +du=merdan +dp=qazwsx12 +da=localhost +dp=3306 +#database_url ="merdan:qazwsx12@tcp(127.0.0.1:3306)/test_baza?parseTime=true" +database_url ="orient:orient@tcp(192.168.1.2:3306)/bagisto_sarga?parseTime=true" +couchdb_url = "http://admin:adminadmin@192.168.1.2:5984/" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f287e2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.env +db_service.exe +/vendor +.vscode +db_service diff --git a/db_service.go b/db_service.go new file mode 100644 index 0000000..bfb7318 --- /dev/null +++ b/db_service.go @@ -0,0 +1,185 @@ +package main + +import ( + "context" + gm "db_service/gorm_models" + "db_service/models" + helper "db_service/pkg" + "encoding/json" + "fmt" + "log" + "os" + "sync" + "time" +) + +func dbService() error { + fmt.Println("dbService started") + + tayyarla() + + start := time.Now() + + var resultingChannel = make(chan gm.Category) + var producer_wg sync.WaitGroup + var consumer_wg sync.WaitGroup + + // consumer + for c := 0; c < len(mainCategories); c++ { + producer_wg.Add(1) + go func() { + defer consumer_wg.Done() + + // WaitGroup for iterate functions + const max = 20 + var wg sync.WaitGroup + + responses := make(chan bool, max) + + for item := range resultingChannel { + + wg.Add(1) + + db := "ty_db_" + item.Translations[0].Slug + + dbExists := helper.CheckDBExists("http://admin:adminadmin@localhost:5984/" + db) + + if dbExists { + go iterate(db, responses, func() { wg.Done() }) + } + } + + // close iterate function responses + go func() { + defer close(responses) + wg.Wait() + }() + + for response := range responses { + fmt.Println("response", response) + } + }() + } + + // producer + for _, cat := range mainCategories { + consumer_wg.Add(1) + go func(category gm.Category) { + defer producer_wg.Done() + resultingChannel <- category + }(cat) + } + + producer_wg.Wait() + + close(resultingChannel) + + consumer_wg.Wait() + + elapsed := time.Since(start) + log.Println("DB Time Elapsed:", elapsed) + + return nil +} + +// func init() { +// panic("unimplemented") +// } + +// iterate categories +func iterate(db string, finished chan<- bool, onExit func()) { + + _, cancel := context.WithCancel(context.Background()) + defer cancel() + + defer onExit() + + // start := time.Now() + + // get total product count from couch database + totalDocCount := getTotalDocumentCount(db) + + if totalDocCount == 0 { + cancel() + return + } + + // total document counter + i := 0 + + for i < totalDocCount { + + cdbTime := time.Now() + product, err := getDataFromCouchDb(i, db) + i++ + if err != nil { + fmt.Println(err) + cancel() + return + } + + //TODO: insert product to mysql + fmt.Println("product: ", product.Name, " db: ", db) + + log.Println("Couch DB. Time elapsed:", time.Since(cdbTime), " DB: ", db) + + // gorm.AutoMigrate(&product) + + // // if there is an error inserting, handle it + // if err != nil { + // gorm.Close() + // panic(err.Error()) + // } + // be careful deferring Queries if you are using transactions + // gorm.Close() + + } + + finished <- true +} + +// getTotalDocumentCount gets the total number of documents and returns +func getTotalDocumentCount(db string) int { + + var response models.DBDocCountResponse + + url := os.Getenv("couch_db_source") + db + + body, err := helper.SendRequest("GET", url, nil, "", true) + + if err != nil { + log.Println(err.Error()) + return 0 + } + + err = json.Unmarshal(body, &response) + if err != nil { + log.Println(err.Error()) + return 0 + } + + return response.DocCount +} + +// getDataFromCouchDb gets data from local couch db +func getDataFromCouchDb(i int, db string) (models.Product, error) { + var model models.Product + var response models.BagistoModelResponse + + url := os.Getenv("couch_db_source") + db + fmt.Sprintf("/_all_docs?include_docs=true&limit=1&skip=%v", i) + + body, err := helper.SendRequest("GET", url, nil, "", true) + if err != nil { + return model, err + } + + err = json.Unmarshal(body, &response) + if err != nil { + log.Println(err.Error()) + return model, err + } + + return response.Rows[0].Doc, nil +} + +//https://gorm.io/docs/query.html diff --git a/example.go b/example.go new file mode 100644 index 0000000..9cd8b52 --- /dev/null +++ b/example.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + "sync" + "time" +) + +var garash, garash2 sync.WaitGroup + +func dene() { + + garash.Add(1) + + go func() { + defer garash.Done() + time.Sleep(5 * time.Second) + fmt.Println("5 sekunt garashdyrmakchy") + }() + garash2.Add(1) + go ishlet() + garash2.Wait() + fmt.Println("gutardym") +} + +func ishlet() { + defer garash2.Done() + garash.Wait() + fmt.Println("garashyp boldym") + +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..42648b3 --- /dev/null +++ b/go.mod @@ -0,0 +1,18 @@ +module db_service + +go 1.19 + +require ( + github.com/gosimple/slug v1.12.0 + github.com/jinzhu/gorm v1.9.16 + github.com/joho/godotenv v1.4.0 + gorm.io/driver/mysql v1.3.5 + gorm.io/gorm v1.23.8 +) + +require ( + github.com/go-sql-driver/mysql v1.6.0 // indirect + github.com/gosimple/unidecode v1.0.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..c2a62cd --- /dev/null +++ b/go.sum @@ -0,0 +1,45 @@ +github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= +github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= +github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM= +github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/gosimple/slug v1.12.0 h1:xzuhj7G7cGtd34NXnW/yF0l+AGNfWqwgh/IXgFy7dnc= +github.com/gosimple/slug v1.12.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= +github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= +github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= +github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o= +github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4= +github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= +github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd h1:GGJVjV8waZKRHrgwvtH66z9ZGVurTD1MT0n1Bb+q4aM= +golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gorm.io/driver/mysql v1.3.5 h1:iWBTVW/8Ij5AG4e0G/zqzaJblYkBI1VIL1LG2HUGsvY= +gorm.io/driver/mysql v1.3.5/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c= +gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE= +gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= diff --git a/gorm_models/brand.go b/gorm_models/brand.go new file mode 100644 index 0000000..72fc138 --- /dev/null +++ b/gorm_models/brand.go @@ -0,0 +1,43 @@ +package gorm_models + +import ( + "log" + "time" + + "github.com/gosimple/slug" + "gorm.io/gorm" +) + +type Brand struct { + ID uint `gorm:"primary_key"` + CreatedAt time.Time + UpdatedAt time.Time + Name string + Code string + Status bool + Categories []Category `gorm:"many2many:category_brands;"` +} + +func FindOrCreateBrand(db *gorm.DB, brand string, categories []Category) Brand { + + var brandObject Brand + + if brand != "" { + + code := slug.Make(brand) + + err := db.FirstOrCreate(&brandObject, Brand{Name: brand, Code: code}).Error + // err := db.Model(&Brand{}).Find(&brandObject).Error + + if err != nil { + log.Println(err.Error()) + } else { + db.Model(&brandObject).Association("Categories").Append(categories) + } + + } + + // var brandObject Brand + + return brandObject +} diff --git a/gorm_models/category.go b/gorm_models/category.go new file mode 100644 index 0000000..0468c68 --- /dev/null +++ b/gorm_models/category.go @@ -0,0 +1,37 @@ +package gorm_models + +import ( + "log" + + "gorm.io/gorm" +) + +type CategoryTranslation struct { + ID uint `gorm:"primaryKey"` + Slug string + CategoryID uint + MetaDescription string + MetaKeywords string + Locale string +} + +type Category struct { + ID uint `gorm:"primaryKey"` + Status int8 + Position int + ParentId uint + DisplayMode string + Translations []CategoryTranslation +} + +func GetMainCategories(db *gorm.DB) []Category { + var categories []Category + err := db.Model(&Category{}).Preload("Translations").Find(&categories, "status=1 AND parent_id=1 AND display_mode!='promotion'").Error + + if err != nil { + log.Println(err.Error()) + + } + + return categories +} diff --git a/gorm_models/family.go b/gorm_models/family.go new file mode 100644 index 0000000..cfa0da4 --- /dev/null +++ b/gorm_models/family.go @@ -0,0 +1,78 @@ +package gorm_models + +import ( + "log" + + "gorm.io/gorm" +) + +type AttributeOption struct { + ID uint `gorm:"primaryKey"` + AttributeID uint + AdminName string +} + +type Attribute struct { + ID uint `gorm:"primaryKey"` + Code string + AttributeOptions []AttributeOption +} + +type AttributeFamily struct { + ID uint `gorm:"primaryKey"` + Code string + Groups []AttributeGroup +} + +type AttributeGroup struct { + ID uint `gorm:"primaryKey"` + Name string + AttributeFamilyID uint + Attributes []Attribute `gorm:"many2many:attribute_group_mappings;"` +} + +func GetFamilies(db *gorm.DB) []AttributeFamily { + var families []AttributeFamily + err := db.Model(&AttributeFamily{}).Preload("Groups").Order("id ASC").Find(&families).Error + + if err != nil { + log.Println(err.Error()) + + } else { + log.Println("famililar alyndy", len(families)) + } + + return families +} + +func GetGroups(db *gorm.DB) []AttributeGroup { + var groups []AttributeGroup + err := db.Model(&AttributeGroup{}).Preload("Family").Find(&groups).Error + + if err != nil { + log.Println(err.Error()) + + } + return groups +} + +func GetAttributes(db *gorm.DB) []Attribute { + var attributes []Attribute + err := db.Model(&Attribute{}).Find(&attributes).Error + + if err != nil { + log.Println(err.Error()) + + } + return attributes +} + +func GetAttributeOption(db *gorm.DB, attrbuteID uint, value string) AttributeOption { + var option AttributeOption + err := db.FirstOrCreate(&option, AttributeOption{AttributeID: attrbuteID, AdminName: value}).Error + if err != nil { + log.Println(err.Error()) + + } + return option +} diff --git a/gorm_models/product.go b/gorm_models/product.go new file mode 100644 index 0000000..70bcca5 --- /dev/null +++ b/gorm_models/product.go @@ -0,0 +1,76 @@ +package gorm_models + +import "time" + +type Product struct { + ID uint `gorm:"primary_key"` + CreatedAt time.Time + UpdatedAt time.Time + Sku string + Type string + ParentID uint `sql:"DEFAULT:NULL"` + AttributeFamilyID uint + AttributeFamily AttributeFamily + BrandID uint `sql:"DEFAULT:NULL"` + Brand Brand + Images []ProductImage + Categories []Category `gorm:"many2many:product_categories;"` + AttributeValues []ProductAttributeValue +} + +// $main_attributes = [ +// +// 'sku' => $parentProduct->sku, +// 'product_number' => $data['product_number'], +// 'name' => $data['name'], +// 'weight' => $data['weight'] ?? 0.45, +// 'source' => $data['url_key'], +// 'status' => 1, +// 'visible_individually' => 1, +// 'url_key' => $parentProduct->sku, +// 'short_description' => $desc, +// 'description' => $desc, +// 'favoritesCount' => $data['favorite_count'] +// +// ]; +type ProductFlat struct { + ID uint `gorm:"primary_key"` + Sku string + ProductNumber string + Name string + Weight float64 `gorm:"type:decimal(12,4)"` + Status bool + Source string + VisibleIndividually bool + UrlKey string + ShortDescription string + Description string + FavoritesCount uint + CreatedAt time.Time + UpdatedAt time.Time + Product Product +} + +type ProductImage struct { + ID uint `gorm:"primary_key"` + Type string + Path string + ProductID uint +} + +type ProductAttributeValue struct { + ID uint `gorm:"primary_key"` + Locale string + Channel string + ProductID uint + AttributeID uint + TextValue string + BooleanValue bool + IntegerValue int + FloatValue float64 `gorm:"type:decimal(12,4)"` +} + +type ProductSuperAttribute struct { + ProductID uint + AttributeID uint +} diff --git a/gorm_models/vendor.go b/gorm_models/vendor.go new file mode 100644 index 0000000..be3aebe --- /dev/null +++ b/gorm_models/vendor.go @@ -0,0 +1,23 @@ +package gorm_models + +import ( + "log" + + "gorm.io/gorm" +) + +type MarketplaceSeller struct { + ID uint `gorm:"primaryKey"` + Url string +} + +func GetSellers(db *gorm.DB) []MarketplaceSeller { + var sellers []MarketplaceSeller + err := db.Model(&MarketplaceSeller{}).Find(&sellers).Error + + if err != nil { + log.Println(err.Error()) + + } + return sellers +} diff --git a/inserter.go b/inserter.go new file mode 100644 index 0000000..dfb3b21 --- /dev/null +++ b/inserter.go @@ -0,0 +1,210 @@ +package main + +import ( + "context" + gm "db_service/gorm_models" + cb "db_service/models" + helper "db_service/pkg" + "fmt" + "log" + "os" + "sync" + "time" + + "gorm.io/gorm" +) + +var mainCategories []gm.Category +var baza *gorm.DB +var resultingChannel = make(chan gm.Category) +var producer_wg sync.WaitGroup +var consumer_wg sync.WaitGroup + +func insertToBaza(product cb.Product) bool { + + return true +} + +func tayyarla() { + + // baza, err := gorm.Open("mysql", os.Getenv("database_url")) + + // if err != nil { + // panic(err) + // } + + // mainCategories = gm.GetMainCategories(baza) + +} + +func bashlat() error { + + fmt.Println("Insert service starts") + start := time.Now() + tayyarla() + + fmt.Println("consuming") + for c := 0; c < len(mainCategories); c++ { + producer_wg.Add(1) + go func() { + defer consumer_wg.Done() + + // WaitGroup for iterate functions + const max = 20 + var wg sync.WaitGroup + + responses := make(chan bool, max) + + for item := range resultingChannel { + + wg.Add(1) + + db := "ty_db_" + item.Translations[0].Slug + + dbExists := helper.CheckDBExists(os.Getenv("couchdb_url") + db) + + if dbExists { + go iterateCategories(db, responses, func() { wg.Done() }) + } + } + + // close iterate function responses + go func() { + defer close(responses) + wg.Wait() + }() + + for response := range responses { + fmt.Println("response", response) + } + }() + } + + fmt.Println("producing") + for _, cat := range mainCategories { + consumer_wg.Add(1) + go func(category gm.Category) { + defer producer_wg.Done() + resultingChannel <- category + }(cat) + } + producer_wg.Wait() + + close(resultingChannel) + + consumer_wg.Wait() + + elapsed := time.Since(start) + + log.Println("DB Time Elapsed:", elapsed) + + return nil + +} + +// func consumer() { +// fmt.Println("consuming") +// for c := 0; c < len(mainCategories); c++ { +// producer_wg.Add(1) +// go func() { +// defer consumer_wg.Done() + +// // WaitGroup for iterate functions +// const max = 20 +// var wg sync.WaitGroup + +// responses := make(chan bool, max) + +// for item := range resultingChannel { + +// wg.Add(1) + +// db := "ty_db_" + item.Translations[0].Slug + +// dbExists := helper.CheckDBExists(os.Getenv("couchdb_url") + db) + +// if dbExists { +// go iterateCategories(db, responses, func() { wg.Done() }) +// } +// } + +// // close iterate function responses +// go func() { +// defer close(responses) +// wg.Wait() +// }() + +// for response := range responses { +// fmt.Println("response", response) +// } +// }() +// } +// } + +// func producer() { +// fmt.Println("producing") +// for _, cat := range mainCategories { +// consumer_wg.Add(1) +// go func(category gm.Category) { +// defer producer_wg.Done() +// resultingChannel <- category +// }(cat) +// } +// producer_wg.Wait() + +// close(resultingChannel) + +// consumer_wg.Wait() +// } + +// iterate categories +func iterateCategories(db string, finished chan<- bool, onExit func()) { + + _, cancel := context.WithCancel(context.Background()) + defer cancel() + + defer onExit() + + // start := time.Now() + + // get total product count from couch database + totalDocCount := getTotalDocumentCount(db) + + if totalDocCount == 0 { + cancel() + return + } + + // total document counter + i := 0 + + for i < totalDocCount { + + cdbTime := time.Now() + product, err := getDataFromCouchDb(i, db) + i++ + if err != nil { + fmt.Println(err) + cancel() + return + } + + //TODO: insert product to mysql + fmt.Println("product: ", product.Name, " db: ", db) + + log.Println("Couch DB. Time elapsed:", time.Since(cdbTime), " DB: ", db) + + // gorm.AutoMigrate(&product) + + // // if there is an error inserting, handle it + // if err != nil { + // gorm.Close() + // panic(err.Error()) + // } + // be careful deferring Queries if you are using transactions + // gorm.Close() + + } + + finished <- true +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..afadddb --- /dev/null +++ b/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "db_service/test" + "fmt" + "log" + "math/big" + "time" + + "github.com/joho/godotenv" +) + +func init() { + + log.Println("init function") + + // init .env + err := godotenv.Load() + if err != nil { + log.Fatal(err.Error()) + } +} + +func main() { + + // err := bashlat() + start := time.Now() + + r := new(big.Int) + fmt.Println("start import", r.Binomial(1000, 10)) + + err := test.StartTest() + if err != nil { + fmt.Println("err: ", err.Error()) + } + + elapsed := time.Since(start) + log.Printf("end import took %s", elapsed) + // dene() + +} diff --git a/models/category.go b/models/category.go new file mode 100644 index 0000000..c43afff --- /dev/null +++ b/models/category.go @@ -0,0 +1,18 @@ +package models + +type Category struct { + ID int `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` + TrendyolURL string `json:"trendyol_url"` + LcwURL string `json:"lcw_url"` + DisplayMode string `json:"display_mode"` + ImageURL interface{} `json:"image_url"` + CategoryIconPath interface{} `json:"category_icon_path"` + ProductLimit int `json:"product_limit"` + Children []Category +} + +type BagistoResponse struct { + Data []Category +} diff --git a/models/count.go b/models/count.go new file mode 100644 index 0000000..b40404c --- /dev/null +++ b/models/count.go @@ -0,0 +1,5 @@ +package models + +type DBDocCountResponse struct { + DocCount int `json:"doc_count"` +} diff --git a/models/product.go b/models/product.go new file mode 100644 index 0000000..b4f7a54 --- /dev/null +++ b/models/product.go @@ -0,0 +1,79 @@ +package models + +type Product struct { + Attributes []map[string]string `json:"attributes"` + Brand string `json:"brand"` + Categories []int `json:"categories"` + Cinsiyet string `json:"cinsiyet"` + Color string `json:"color"` + ColorVariantCount int `json:"color_variant_count"` + ColorVariants []Product `json:"color_variants"` + Description string `json:"description"` + IsSellable bool `json:"sellable"` + FavoriteCount int `json:"favorite_count"` + Descriptions []struct { + Description string `json:"description"` + Bold bool `json:"bold"` + } `json:"descriptions"` + Images []string `json:"images"` + Name string `json:"name"` + NameWithProductCode string `json:"name_with_product_code"` + Price Price `json:"price"` + ProductCode string `json:"product_code"` + ProductGroupID string `json:"product_group_id"` + ProductNumber string `json:"product_number"` + ShortDescription string `json:"short_description"` + SizeVariants []Variant `json:"size_variants"` + Sku string `json:"sku"` + Stock interface{} `json:"stock"` + URLKey string `json:"url_key"` + Vendor string `json:"vendor"` + Weight string `json:"weight"` +} + +type Price struct { + ProfitMargin int `json:"profitMargin"` + DiscountedPrice struct { + Text string `json:"text"` + Value float64 `json:"value"` + } `json:"discountedPrice"` + SellingPrice struct { + Text string `json:"text"` + Value float64 `json:"value"` + } `json:"sellingPrice"` + OriginalPrice struct { + Text string `json:"text"` + Value float64 `json:"value"` + } `json:"originalPrice"` + Currency string `json:"currency"` +} + +type Variant struct { + AttributeID int `json:"attributeId"` + AttributeName string `json:"attributeName"` + AttributeType string `json:"attributeType"` + AttributeValue string `json:"attributeValue"` + Stamps []interface{} `json:"stamps"` + Price Price `json:"price"` + FulfilmentType string `json:"fulfilmentType"` + AttributeBeautifiedValue string `json:"attributeBeautifiedValue"` + IsWinner bool `json:"isWinner"` + ListingID string `json:"listingId"` + Stock interface{} `json:"stock"` + Sellable bool `json:"sellable"` + AvailableForClaim bool `json:"availableForClaim"` + Barcode string `json:"barcode"` + ItemNumber int `json:"itemNumber"` + DiscountedPriceInfo string `json:"discountedPriceInfo"` + HasCollectable bool `json:"hasCollectable"` + RushDeliveryMerchantListingExist bool `json:"rushDeliveryMerchantListingExist"` + LowerPriceMerchantListingExist bool `json:"lowerPriceMerchantListingExist"` +} +type Row struct { + Key string `json:"key"` + Value map[string]interface{} `json:"value"` + Doc Product +} +type BagistoModelResponse struct { + Rows []Row `json:"rows"` +} diff --git a/pkg/helper.go b/pkg/helper.go new file mode 100644 index 0000000..7c1db22 --- /dev/null +++ b/pkg/helper.go @@ -0,0 +1,66 @@ +package helper + +import ( + "context" + "log" + "net" + "net/http" + "time" +) + +func CheckDBExists(endpoint string) bool { + + const ConnectMaxWaitTime = 30 * time.Second + const RequestMaxWaitTime = 60 * time.Second + + client := http.Client{ + Transport: &http.Transport{ + DialContext: (&net.Dialer{ + Timeout: ConnectMaxWaitTime, + }).DialContext, + }, + } + + ctx, cancel := context.WithTimeout(context.Background(), RequestMaxWaitTime) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil) + req.Proto = "HTTP/1.0" + if err != nil { + log.Println(err.Error()) + return false + } + + q := req.URL.Query() + req.URL.RawQuery = q.Encode() + req.Header.Set("Connection", "close") + req.Close = true + client.CloseIdleConnections() + + response, err := client.Do(req) + + // handle timeout + if e, ok := err.(net.Error); ok && e.Timeout() { + if response != nil { + response.Body.Close() + } + log.Println(err.Error()) + return false + } else if err != nil { + if response != nil { + response.Body.Close() + } + log.Println(err.Error()) + return false + } + + if response != nil { + response.Body.Close() + } + + if response.StatusCode == http.StatusOK { + return true + } else { + return false + } +} diff --git a/pkg/request.go b/pkg/request.go new file mode 100644 index 0000000..269b4a1 --- /dev/null +++ b/pkg/request.go @@ -0,0 +1,101 @@ +package helper + +import ( + "bytes" + "context" + "fmt" + "io/ioutil" + "log" + "net" + "net/http" + "time" +) + +func SendRequest(method string, endpoint string, values []byte, authKey string, isCouchDbReq bool) ([]byte, error) { + + const ConnectMaxWaitTime = 30 * time.Second + const RequestMaxWaitTime = 60 * time.Second + + client := http.Client{ + Transport: &http.Transport{ + DialContext: (&net.Dialer{ + Timeout: ConnectMaxWaitTime, + }).DialContext, + }, + } + + ctx, cancel := context.WithTimeout(context.Background(), RequestMaxWaitTime) + defer cancel() + + emptyBody := []byte{} + + req, err := http.NewRequestWithContext(ctx, method, endpoint, bytes.NewBuffer(values)) + req.Proto = "HTTP/2.0" + if err != nil { + log.Println(err.Error()) + return emptyBody, err + } + + q := req.URL.Query() + req.URL.RawQuery = q.Encode() + req.Header.Set("Connection", "close") + req.Header.Add("Authorization", authKey) + req.Close = true + client.CloseIdleConnections() + + response, err := client.Do(req) + + if e, ok := err.(net.Error); ok && e.Timeout() { + if response != nil { + response.Body.Close() + } + log.Println(err.Error()) + return emptyBody, err + } else if err != nil { + if response != nil { + response.Body.Close() + } + log.Println(err.Error()) + return emptyBody, err + } + + // 200 OK + // 201 Created + // 202 Accepted + // 404 Not Found + // 409 Conflict + + if isCouchDbReq { + // fmt.Printf("responseStatusCode: %d\n", response.StatusCode) + if response.StatusCode == http.StatusNotFound { + if response != nil { + response.Body.Close() + } + return emptyBody, nil + } + // TODO: handle conflict error + } else { + if response.StatusCode != http.StatusOK { + if response != nil { + response.Body.Close() + } + err := fmt.Errorf("response: code: %d, body: %v", response.StatusCode, response.Body) + log.Println(err.Error()) + return emptyBody, err + } + } + + body, err := ioutil.ReadAll(response.Body) + if err != nil { + log.Println(err.Error()) + if response != nil { + response.Body.Close() + } + return emptyBody, nil + } + + // if you call it in loop, don't use defer + response.Body.Close() + + return body, nil +} diff --git a/test/testit.go b/test/testit.go new file mode 100644 index 0000000..e01b752 --- /dev/null +++ b/test/testit.go @@ -0,0 +1,509 @@ +package test + +import ( + gm "db_service/gorm_models" + "db_service/models" + helper "db_service/pkg" + "encoding/json" + "fmt" + "log" + "os" + "strconv" + "sync" + + "gorm.io/driver/mysql" + "gorm.io/gorm" +) + +var mainCategories []gm.Category +var baza *gorm.DB +var mainImportWG, famAndSellerWG sync.WaitGroup +var families []gm.AttributeFamily +var sellers []gm.MarketplaceSeller +var attributesMap = make(map[string]gm.Attribute) + +func StartTest() error { + baza, err := gorm.Open(mysql.Open(os.Getenv("database_url")), &gorm.Config{}) + + if err != nil { + return err + } + + //get categories from mysql on main thread + mainCategories = gm.GetMainCategories(baza) + + famAndSellerWG.Add(3) + //get attribute families from mysql with gorutine + go func() { + defer famAndSellerWG.Done() + families = gm.GetFamilies(baza) + }() + + //get sellers families from mysql with gorutine + go func() { + defer famAndSellerWG.Done() + sellers = gm.GetSellers(baza) + }() + + go func() { + defer famAndSellerWG.Done() + var attributes = gm.GetAttributes(baza) + for _, atrattribute := range attributes { + attributesMap[atrattribute.Code] = atrattribute + } + }() + + mainImportWG.Add(len(mainCategories)) + + for _, element := range mainCategories { + slug := element.Translations[0].Slug + + go startImport("ty_db_"+slug, baza) + + // fmt.Println(<-result) + } + + mainImportWG.Wait() + fmt.Println("Terminating Program") + + return nil +} + +func startImport(dbName string, db *gorm.DB) { + defer mainImportWG.Done() + + dbExists := helper.CheckDBExists(os.Getenv("couch_db_source") + dbName) + fmt.Println(dbName) + + if dbExists { + + totalDocCount := getTotalDocumentCount(dbName) + skip := 0 + limit := 100 + + for skip < totalDocCount { + + var response models.BagistoModelResponse + + url := fmt.Sprintf("%s%s/_all_docs?include_docs=true&limit=%v&skip=%v", os.Getenv("couch_db_source"), dbName, limit, skip) + + fmt.Println(url) + + skip += limit + + body, err := helper.SendRequest("GET", url, nil, "", true) + + if err != nil { + fmt.Println(err.Error()) + continue + } + + err = json.Unmarshal(body, &response) + + if err != nil { + log.Println(err.Error()) + continue + } + + //itearate 100 row products + for _, element := range response.Rows { + importProduct(element.Doc, db) + } + } + } else { + fmt.Println(dbName + "+doesnt exist") + } + +} + +func getTotalDocumentCount(db string) int { + + var response models.DBDocCountResponse + + url := os.Getenv("couch_db_source") + db + + body, err := helper.SendRequest("GET", url, nil, "", true) + + if err != nil { + log.Println(err.Error()) + return 0 + } + + err = json.Unmarshal(body, &response) + if err != nil { + log.Println(err.Error()) + return 0 + } + + return response.DocCount +} + +func importProduct(product models.Product, db *gorm.DB) { + + famAndSellerWG.Wait() //wait until attribute families and sellers are not get from mysql + + var categories []gm.Category + var brand gm.Brand + + errCat := db.Find(&categories, product.Categories).Error + + if errCat != nil { + log.Println(errCat) + return + } + + if product.Brand != "" { + brand = gm.FindOrCreateBrand(db, product.Brand, categories) + } + + iproduct := gm.Product{ + Sku: product.ProductGroupID, + Type: "simple", + AttributeFamilyID: families[0].ID, + Brand: brand, + } + + for _, element := range product.Images { + iproduct.Images = append(iproduct.Images, gm.ProductImage{Type: "cdn", Path: element}) + } + + if product.ColorVariantCount > 0 || len(product.SizeVariants) > 0 { + iproduct.Type = "configurable" + iproduct.AttributeFamilyID = families[1].ID + } + + err := db.Select("Sku", "Type", "AttributeFamilyID", "CreatedAt", "UpdatedAt").Create(&iproduct).Error + + if err != nil { + log.Println(err) + return + } + + // mainProductFlat := gm.ProductFlat{} + mainProductAttributeValues := assignAttributes(iproduct.ID, &product) + + if iproduct.Type == "simple" { + mainProductAttributeValues = append(mainProductAttributeValues, getPriceAttributes(iproduct.ID, &product.Price)...) + } else if len(product.ColorVariants) > 0 || len(product.SizeVariants) > 0 { + + var productVariants []gm.Product + + if len(product.ColorVariants) > 0 { + //attach super attributes color + superAttribute := gm.ProductSuperAttribute{ProductID: iproduct.ID, AttributeID: attributesMap["color"].ID} + db.Create(&superAttribute) + var description string + for _, colorVariant := range product.ColorVariants { + weight, _ := strconv.ParseFloat(colorVariant.Weight, 64) + + for _, desc := range colorVariant.Descriptions { + description += "

" + desc.Description + "

" + } + + colorOption := gm.GetAttributeOption(db, attributesMap["color"].ID, colorVariant.Color) + + if len(colorVariant.SizeVariants) > 0 { + + for _, sizeVariant := range colorVariant.SizeVariants { + + if sizeVariant.Sellable { + sku := fmt.Sprintf("%s-%s-%d-col-size", colorVariant.ProductGroupID, colorVariant.ProductNumber, sizeVariant.ItemNumber) + sizeOption := gm.GetAttributeOption(db, attributesMap["size"].ID, sizeVariant.AttributeValue) + attributes := []gm.ProductAttributeValue{ + {AttributeID: attributesMap["sku"].ID, TextValue: sku, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["product_number"].ID, TextValue: fmt.Sprintf("%s-%d", colorVariant.ProductNumber, sizeVariant.ItemNumber), Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["name"].ID, TextValue: colorVariant.Name, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["weight"].ID, FloatValue: weight, Channel: "default", Locale: "tm"}, + // {AttributeID: attributesMap["source"].ID, TextValue: colorVariant.URLKey, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["status"].ID, BooleanValue: true, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["visible_individually"].ID, BooleanValue: true, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["url_key"].ID, TextValue: sku, Channel: "default", Locale: "tm"}, + // {AttributeID: attributesMap["favoritesCount"].ID, IntegerValue: colorVariant.FavoriteCount, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["description"].ID, TextValue: description, Channel: "default", Locale: "tm"}, + { + AttributeID: attributesMap["color"].ID, + IntegerValue: int(colorOption.ID), + Channel: "default", + Locale: "tm", + }, { + AttributeID: attributesMap["size"].ID, + IntegerValue: int(sizeOption.ID), + Channel: "default", + Locale: "tm", + }, + } + + if sizeVariant.Price.OriginalPrice.Value > sizeVariant.Price.DiscountedPrice.Value { + attributes = append(attributes, []gm.ProductAttributeValue{ + {AttributeID: attributesMap["price"].ID, FloatValue: sizeVariant.Price.OriginalPrice.Value, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["special_price"].ID, FloatValue: sizeVariant.Price.DiscountedPrice.Value, Channel: "default", Locale: "tm"}, + }...) + + } else { + attributes = append(attributes, gm.ProductAttributeValue{AttributeID: attributesMap["price"].ID, FloatValue: sizeVariant.Price.OriginalPrice.Value, Channel: "default", Locale: "tm"}) + } + + productVariant := gm.Product{ + ParentID: iproduct.ID, + Type: "simple", + AttributeFamily: iproduct.AttributeFamily, + Sku: sku, + Brand: iproduct.Brand, + AttributeValues: attributes, + AttributeFamilyID: iproduct.AttributeFamilyID, + } + + for _, element := range colorVariant.Images { + productVariant.Images = append(iproduct.Images, gm.ProductImage{Type: "cdn", Path: element}) + } + //todo assign images + productVariants = append(productVariants, productVariant) + } + + } + } else if colorVariant.IsSellable { + + colorOption := gm.GetAttributeOption(db, attributesMap["color"].ID, colorVariant.Color) + attributes := collectAttributes(&colorVariant, &colorOption) + + if colorVariant.Price.OriginalPrice.Value > colorVariant.Price.DiscountedPrice.Value { + attributes = append(attributes, []gm.ProductAttributeValue{ + {AttributeID: attributesMap["price"].ID, FloatValue: colorVariant.Price.OriginalPrice.Value, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["special_price"].ID, FloatValue: colorVariant.Price.DiscountedPrice.Value, Channel: "default", Locale: "tm"}, + }...) + + } else { + attributes = append(attributes, gm.ProductAttributeValue{AttributeID: attributesMap["price"].ID, FloatValue: colorVariant.Price.OriginalPrice.Value, Channel: "default", Locale: "tm"}) + } + + sku := fmt.Sprintf("%s-%s-col", iproduct.Sku, colorVariant.ProductNumber) + + productVariant := gm.Product{ + ParentID: iproduct.ID, + Type: "simple", + AttributeFamily: iproduct.AttributeFamily, + Sku: sku, + Brand: iproduct.Brand, + AttributeValues: attributes, + AttributeFamilyID: iproduct.AttributeFamilyID, + } + + for _, element := range colorVariant.Images { + productVariant.Images = append(iproduct.Images, gm.ProductImage{Type: "cdn", Path: element}) + } + + productVariants = append(productVariants, productVariant) + } + } + } + + if len(product.SizeVariants) > 0 { + //attach super attributes size + superAttribute := gm.ProductSuperAttribute{ProductID: iproduct.ID, AttributeID: attributesMap["size"].ID} + db.Create(&superAttribute) + + var description string + for _, desc := range product.Descriptions { + description += "

" + desc.Description + "

" + } + weight, _ := strconv.ParseFloat(product.Weight, 64) + + for _, sizeVariant := range product.SizeVariants { + + if sizeVariant.Sellable { + sku := fmt.Sprintf("%s-%s-%d-size", iproduct.Sku, product.ProductNumber, sizeVariant.ItemNumber) + sizeOption := gm.GetAttributeOption(db, attributesMap["size"].ID, sizeVariant.AttributeValue) + + attributes := []gm.ProductAttributeValue{ + {AttributeID: attributesMap["sku"].ID, TextValue: sku, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["product_number"].ID, TextValue: string(sizeVariant.ItemNumber), Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["name"].ID, TextValue: product.Name, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["weight"].ID, FloatValue: weight, Channel: "default", Locale: "tm"}, + // {AttributeID: attributesMap["source"].ID, TextValue: product.URLKey, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["status"].ID, BooleanValue: true, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["visible_individually"].ID, BooleanValue: true, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["url_key"].ID, TextValue: sku, Channel: "default", Locale: "tm"}, + // {AttributeID: attributesMap["favoritesCount"].ID, IntegerValue: product.FavoriteCount, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["description"].ID, TextValue: description, Channel: "default", Locale: "tm"}, + { + AttributeID: attributesMap["size"].ID, + IntegerValue: int(sizeOption.ID), + Channel: "default", + Locale: "tm", + }, + } + + if sizeVariant.Price.OriginalPrice.Value > sizeVariant.Price.DiscountedPrice.Value { + attributes = append(attributes, []gm.ProductAttributeValue{ + {AttributeID: attributesMap["price"].ID, FloatValue: sizeVariant.Price.OriginalPrice.Value, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["special_price"].ID, FloatValue: sizeVariant.Price.DiscountedPrice.Value, Channel: "default", Locale: "tm"}, + }...) + + } else { + attributes = append(attributes, gm.ProductAttributeValue{AttributeID: attributesMap["price"].ID, FloatValue: sizeVariant.Price.OriginalPrice.Value, Channel: "default", Locale: "tm"}) + } + + productVariant := gm.Product{ + ParentID: iproduct.ID, + Type: "simple", + AttributeFamily: iproduct.AttributeFamily, + Sku: sku, + Brand: iproduct.Brand, + AttributeValues: attributes, + AttributeFamilyID: iproduct.AttributeFamilyID, + } + + // for _, element := range colorVariant.Images { + // productVariant.Images = append(iproduct.Images, gm.ProductImage{Type: "cdn", Path: element}) + // } + + productVariants = append(productVariants, productVariant) + + } + } + } + + db.Create(&productVariants) //todo assign all with categories + } + + db.Model(&iproduct).Association("Categories").Append(categories) + var err2 = db.Create(&mainProductAttributeValues).Error + + if err2 != nil { + log.Println(err) + } + // panic("'hassiktir'") + //todo gorm transactions + //todo Attributes source, cinsiyet + + // if len(product.Attributes) > 0 { + // iproduct.AttrbuteFamily = getAttributeFamily() + // } + + //todo seller product, meta keys + // if($data['vendor'] && $seller = $this->vendorRepository->findOneByField('url',$data['vendor'])){ + // $this->createSellerProduct($parentProduct, $seller->id); + // } + + if product.Vendor != "" { + for i := range sellers { + if sellers[i].Url == product.Vendor { + createSellerProduct() + break + } + } + } + +} +func createSellerProduct() { + /* + Event::dispatch('marketplace.catalog.product.create.before'); + + $sellerProduct = $this->vendorProductRepository->create([ + 'marketplace_seller_id' => $seller_id, + 'is_approved' => 1, + 'condition' => 'new', + 'description' => 'scraped product', + 'is_owner' => 1, + 'product_id' => $product->id, + ]); + + foreach ($sellerProduct->product->variants as $baseVariant) { + $this->vendorProductRepository->create([ + 'parent_id' => $sellerProduct->id, + 'product_id' => $baseVariant->id, + 'is_owner' => 1, + 'marketplace_seller_id' => $seller_id, + 'is_approved' => 1, + 'condition' => 'new', + ]); + } + + Event::dispatch('marketplace.catalog.product.create.after', $sellerProduct); + + return $sellerProduct; + */ +} +func collectAttributes(variant *models.Product, option *gm.AttributeOption) []gm.ProductAttributeValue { + sku := fmt.Sprintf("%s-%s", variant.ProductGroupID, variant.ProductNumber) + weight, _ := strconv.ParseFloat(variant.Weight, 64) + var description string + + for _, desc := range variant.Descriptions { + description += "

" + desc.Description + "

" + } + + return []gm.ProductAttributeValue{ + {AttributeID: attributesMap["sku"].ID, TextValue: sku, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["product_number"].ID, TextValue: variant.ProductNumber, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["name"].ID, TextValue: variant.Name, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["weight"].ID, FloatValue: weight, Channel: "default", Locale: "tm"}, + // {AttributeID: attributesMap["source"].ID, TextValue: variant.URLKey, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["status"].ID, BooleanValue: true, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["visible_individually"].ID, BooleanValue: true, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["url_key"].ID, TextValue: sku, Channel: "default", Locale: "tm"}, + // {AttributeID: attributesMap["favoritesCount"].ID, IntegerValue: variant.FavoriteCount, Channel: "default", Locale: "tm"}, + {AttributeID: attributesMap["description"].ID, TextValue: description, Channel: "default", Locale: "tm"}, + {AttributeID: option.AttributeID, IntegerValue: int(option.ID), Channel: "default", Locale: "tm"}, + } +} + +func assignAttributes(productID uint, data *models.Product) []gm.ProductAttributeValue { + + weight, _ := strconv.ParseFloat(data.Weight, 64) + + var description string + + for _, desc := range data.Descriptions { + description += "

" + desc.Description + "

" + } + //$desc = implode(array_map(fn($value): string => '

' . $value['description'] . '

', $data['descriptions'])); + + var productAttributeValues = []gm.ProductAttributeValue{ + {ProductID: productID, AttributeID: attributesMap["sku"].ID, TextValue: data.ProductGroupID, Channel: "default", Locale: "tm"}, + {ProductID: productID, AttributeID: attributesMap["product_number"].ID, TextValue: data.ProductNumber, Channel: "default", Locale: "tm"}, + {ProductID: productID, AttributeID: attributesMap["name"].ID, TextValue: data.Name, Channel: "default", Locale: "tm"}, + {ProductID: productID, AttributeID: attributesMap["weight"].ID, FloatValue: weight, Channel: "default", Locale: "tm"}, + // {ProductID: productID, AttributeID: attributesMap["source"].ID, TextValue: data.URLKey, Channel: "default", Locale: "tm"}, + {ProductID: productID, AttributeID: attributesMap["status"].ID, BooleanValue: true, Channel: "default", Locale: "tm"}, + {ProductID: productID, AttributeID: attributesMap["visible_individually"].ID, BooleanValue: true, Channel: "default", Locale: "tm"}, + {ProductID: productID, AttributeID: attributesMap["url_key"].ID, TextValue: data.ProductGroupID, Channel: "default", Locale: "tm"}, + // {ProductID: productID, AttributeID: attributesMap["short_description"].ID, TextValue: description, Channel: "default", Locale: "tm"}, + {ProductID: productID, AttributeID: attributesMap["description"].ID, TextValue: description, Channel: "default", Locale: "tm"}, + // {ProductID: productID, AttributeID: attributesMap["favoritesCount"].ID, IntegerValue: data.FavoriteCount, Channel: "default", Locale: "tm"}, + } + + // if data.Price.OriginalPrice.Value > data.Price.DiscountedPrice.Value { + // productAttributeValues = append(productAttributeValues, []gm.ProductAttributeValue{ + // {ProductID: productID, AttributeID: attributesMap["price"].ID, FloatValue: data.Price.OriginalPrice.Value, Channel: "default", Locale: "tm"}, + // {ProductID: productID, AttributeID: attributesMap["special_price"].ID, FloatValue: data.Price.DiscountedPrice.Value, Channel: "default", Locale: "tm"}, + // }...) + + // } else { + // productAttributeValues = append(productAttributeValues, gm.ProductAttributeValue{ProductID: productID, AttributeID: attributesMap["price"].ID, FloatValue: data.Price.OriginalPrice.Value, Channel: "default", Locale: "tm"}) + + // } + + return productAttributeValues +} + +func getPriceAttributes(productID uint, price *models.Price) []gm.ProductAttributeValue { + var productAttributeValues []gm.ProductAttributeValue + if price.OriginalPrice.Value > price.DiscountedPrice.Value { + productAttributeValues = []gm.ProductAttributeValue{ + {ProductID: productID, AttributeID: attributesMap["price"].ID, FloatValue: price.OriginalPrice.Value, Channel: "default", Locale: "tm"}, + {ProductID: productID, AttributeID: attributesMap["special_price"].ID, FloatValue: price.DiscountedPrice.Value, Channel: "default", Locale: "tm"}, + } + + } else { + productAttributeValues = []gm.ProductAttributeValue{{ProductID: productID, AttributeID: attributesMap["price"].ID, FloatValue: price.OriginalPrice.Value, Channel: "default", Locale: "tm"}} + + } + + return productAttributeValues +} + +func getAttributeFamily() gm.AttributeFamily { + + return families[0] +}