package data import ( "fmt" "log" "os" "path" "recipe-manager/helpers" "recipe-manager/models" "recipe-manager/services/logger" "strconv" "strings" "time" "reflect" "go.uber.org/zap" ) type RecipeWithTimeStamps struct { Recipe models.Recipe TimeStamps int64 } type Data struct { CurrentFile string CurrentCountryID string AllRecipeFiles map[string][]helpers.RecipePath currentRecipe *models.Recipe recipeMap map[string]RecipeWithTimeStamps Countries []helpers.CountryName taoLogger *logger.TaoLogger } var ( countries = []helpers.CountryName{{ CountryID: "tha", CountryName: "Thailand", }, { CountryID: "mys", CountryName: "Malaysia", }, { CountryID: "aus", CountryName: "Australia", }, } ) func NewData(taoLogger *logger.TaoLogger) *Data { allRecipeFiles := helpers.ScanRecipeFiles(countries) defaultFile := "coffeethai02_600.json" defaultCountry := "tha" // TODO: read 'version' file versionPath := path.Join("cofffeemachineConfig", defaultCountry, "version") taoLogger.Log.Debug("version", zap.Any("version path", versionPath)) // versionFile, err := os.Open(versionPath) content, err := os.ReadFile(versionPath) if err != nil { taoLogger.Log.Debug("Error when open version file", zap.Error(err)) } initVersion := string(content) // read latest version // set latest to default version latest_version, err := strconv.Atoi(initVersion) if err != nil { latest_version = 600 } for _, v := range allRecipeFiles[defaultCountry] { // extract filename as version current_version_iter, err := strconv.Atoi(strings.Split(strings.Split(v.Name, "_")[1], ".")[0]) if err != nil { continue } if current_version_iter == latest_version { // taoLogger.Log.Debug("current_version_iter", zap.Any("current_version_iter", current_version_iter)) // set latest latest_version = current_version_iter defaultFile = v.Name break } } taoLogger.Log.Debug("defaultFile", zap.Any("defaultFile", defaultFile), zap.Any("latest_version", latest_version)) defaultRecipe, err := helpers.ReadRecipeFile(defaultCountry, defaultFile) if err != nil { log.Panic("Error when read default recipe file:", err) } return &Data{ CurrentFile: defaultFile, CurrentCountryID: defaultCountry, AllRecipeFiles: allRecipeFiles, currentRecipe: defaultRecipe, recipeMap: map[string]RecipeWithTimeStamps{ defaultFile: { Recipe: *defaultRecipe, TimeStamps: time.Now().Unix(), }, }, Countries: countries, taoLogger: taoLogger, } } func (d *Data) GetRecipe(countryID, filename string) *models.Recipe { if countryID == "" { return d.currentRecipe } if filename == "" || filename == d.CurrentFile { return d.currentRecipe } if recipe, ok := d.recipeMap[filename]; ok { d.CurrentFile = filename d.CurrentCountryID = countryID return &recipe.Recipe } // change current version and read new recipe d.CurrentFile = filename d.taoLogger.Log.Debug("GetRecipe", zap.String("filename", filename), zap.String("countryID", countryID)) d.CurrentCountryID = countryID recipe, err := helpers.ReadRecipeFile(countryID, filename) if err != nil { d.taoLogger.Log.Error("Error when read recipe file, Return default recipe", zap.Error(err)) return d.currentRecipe } d.currentRecipe = recipe // save to map if len(d.recipeMap) > 5 { // limit keep in memory 5 version // remove oldest version var oldestVersion string var oldestTime int64 for k, v := range d.recipeMap { if oldestTime == 0 || v.TimeStamps < oldestTime { oldestTime = v.TimeStamps oldestVersion = k } } delete(d.recipeMap, oldestVersion) } d.recipeMap[filename] = RecipeWithTimeStamps{ Recipe: *d.currentRecipe, TimeStamps: time.Now().Unix(), } return d.currentRecipe } func (d *Data) GetRecipe01() []models.Recipe01 { return d.currentRecipe.Recipe01 } func (d *Data) GetCurrentRecipe() *models.Recipe { return d.currentRecipe } func (d *Data) GetRecipe01ByProductCode(filename, countryID, productCode string) (models.Recipe01, error) { if !strings.Contains(filename, "tmp") { if filename == "" || filename == d.CurrentFile { fmt.Println("GetRecipe01ByProductCode.ReadCurrent", filename, d.CurrentFile) for _, v := range d.currentRecipe.Recipe01 { if v.ProductCode == productCode { return v, nil } } } else if recipe, ok := d.recipeMap[filename]; ok { fmt.Println("GetRecipe01ByProductCode.ReadMap", filename, d.CurrentFile) for _, v := range recipe.Recipe.Recipe01 { if v.ProductCode == productCode { return v, nil } } } } d.CurrentFile = filename d.CurrentCountryID = countryID for _, v := range countries { if v.CountryName == countryID { d.CurrentCountryID = v.CountryID countryID = v.CountryID break } } recipe, err := helpers.ReadRecipeFile(countryID, filename) if err != nil { d.taoLogger.Log.Error("Error when read recipe file, Return default recipe", zap.Error(err)) for _, v := range d.currentRecipe.Recipe01 { if v.ProductCode == productCode { return v, nil } } } d.taoLogger.Log.Debug("GetRecipe01ByProductCode", zap.Any("productCode", productCode), zap.Any("version", recipe.MachineSetting.ConfigNumber)) d.currentRecipe = recipe // save to map if len(d.recipeMap) > 5 { // limit keep in memory 5 version // remove oldest version var oldestVersion string var oldestTime int64 for k, v := range d.recipeMap { if oldestTime == 0 || v.TimeStamps < oldestTime { oldestTime = v.TimeStamps oldestVersion = k } } delete(d.recipeMap, oldestVersion) } d.recipeMap[filename] = RecipeWithTimeStamps{ Recipe: *d.currentRecipe, TimeStamps: time.Now().Unix(), } for _, v := range d.currentRecipe.Recipe01 { if v.ProductCode == productCode { // d.taoLogger.Log.Debug("GetRecipe01ByProductCode", zap.Any("productCode", productCode), zap.Any("result", v)) return v, nil } } return models.Recipe01{}, fmt.Errorf("product code: %s not found", productCode) } func (d *Data) SetValuesToRecipe(base_recipe []models.Recipe01, recipe models.Recipe01) { not_found := false global_idx := 0 for index, v := range base_recipe { if v.ProductCode == recipe.ProductCode { // Log.Debug("SetValuesToRecipe", zap.Any("old", v), zap.Any("new", recipe)) // v = recipe // TODO: change only changed values // transform to map base_recipe01_Map := v.ToMap() recipe01_Map := recipe.ToMap() for k, v := range recipe01_Map { if !reflect.DeepEqual(base_recipe01_Map[k], v) { d.taoLogger.Log.Debug("SetValuesToRecipe", zap.Any("key", k)) base_recipe01_Map[k] = v } } base_recipe[index] = base_recipe[index].FromMap(base_recipe01_Map) not_found = false break } else { not_found = true global_idx = index } } if not_found { base_recipe[global_idx+1] = recipe } } func (d *Data) GetMaterialSetting(countryID, filename string) []models.MaterialSetting { result := make([]models.MaterialSetting, 0) if countryID == "" { copy(result, d.currentRecipe.MaterialSetting) return result } if !strings.Contains(filename, "tmp") { if filename == "" || filename == d.CurrentFile { copy(result, d.currentRecipe.MaterialSetting) d.taoLogger.Log.Debug("GetMaterialSetting", zap.Any("result", result)) return d.currentRecipe.MaterialSetting } if recipe, ok := d.recipeMap[filename]; ok { copy(result, recipe.Recipe.MaterialSetting) d.CurrentFile = filename d.CurrentCountryID = countryID return d.currentRecipe.MaterialSetting } } d.CurrentFile = filename d.CurrentCountryID = countryID recipe, err := helpers.ReadRecipeFile(countryID, filename) if err != nil { d.taoLogger.Log.Error("Error when read recipe file, Return default recipe", zap.Error(err)) copy(result, d.currentRecipe.MaterialSetting) return d.currentRecipe.MaterialSetting } d.taoLogger.Log.Debug("GetMaterialSetting", zap.Any("recipe", recipe.MaterialSetting)) d.currentRecipe = recipe // save to map if len(d.recipeMap) > 5 { // limit keep in memory 5 version // remove oldest version var oldestVersion string var oldestTime int64 for k, v := range d.recipeMap { if oldestTime == 0 || v.TimeStamps < oldestTime { oldestTime = v.TimeStamps oldestVersion = k } } delete(d.recipeMap, oldestVersion) } d.recipeMap[filename] = RecipeWithTimeStamps{ Recipe: *d.currentRecipe, TimeStamps: time.Now().Unix(), } // copy(result, recipe.MaterialSetting) return recipe.MaterialSetting } func (d *Data) GetMaterialCode(ids []uint64, countryID, filename string) []models.MaterialCode { var result []models.MaterialCode if filename == "" || filename == d.CurrentFile { result = d.currentRecipe.MaterialCode } else if recipe, ok := d.recipeMap[filename]; ok { d.CurrentFile = filename return recipe.Recipe.MaterialCode } else { d.CurrentFile = filename d.CurrentCountryID = countryID recipe, err := helpers.ReadRecipeFile(countryID, filename) if err != nil { d.taoLogger.Log.Error("Error when read recipe file, Return default recipe", zap.Error(err)) return d.currentRecipe.MaterialCode } d.currentRecipe = recipe // save to map if len(d.recipeMap) > 5 { // limit keep in memory 5 version // remove oldest version var oldestVersion string var oldestTime int64 for k, v := range d.recipeMap { if oldestTime == 0 || v.TimeStamps < oldestTime { oldestTime = v.TimeStamps oldestVersion = k } } delete(d.recipeMap, oldestVersion) } d.recipeMap[filename] = RecipeWithTimeStamps{ Recipe: *d.currentRecipe, TimeStamps: time.Now().Unix(), } result = d.currentRecipe.MaterialCode } if len(ids) == 0 { return result } resultFilter := make([]models.MaterialCode, len(ids)) for _, id := range ids { if id == 0 { continue } for _, m := range result { if m.MaterialID == id { resultFilter = append(resultFilter, m) break } } } return resultFilter } func (d *Data) GetToppings(countryID, filename string) models.Topping { if filename == "" || filename == d.CurrentFile { return d.currentRecipe.Topping } else if recipe, ok := d.recipeMap[filename]; ok { d.CurrentFile = filename return recipe.Recipe.Topping } d.CurrentFile = filename d.CurrentCountryID = countryID recipe, err := helpers.ReadRecipeFile(countryID, filename) if err != nil { d.taoLogger.Log.Error("Error when read recipe file, Return default recipe", zap.Error(err)) return d.currentRecipe.Topping } d.currentRecipe = recipe return recipe.Topping } func (d *Data) GetToppingsOfRecipe(countryID, filename string, productCode string) ([]models.ToppingSet, error) { recipe, err := d.GetRecipe01ByProductCode(filename, countryID, productCode) if err != nil { d.taoLogger.Log.Error("Error when read recipe file, Return default recipe", zap.Error(err)) return []models.ToppingSet{}, err } return recipe.ToppingSet, nil } func (d *Data) GetSubmenusOfRecipe(countryID, filename, productCode string) ([]models.Recipe01, error) { recipe, err := d.GetRecipe01ByProductCode(filename, countryID, productCode) if err != nil { d.taoLogger.Log.Error("Error when read recipe file, Return default recipe", zap.Error(err)) return []models.Recipe01{}, err } submenu := recipe.SubMenu if submenu == nil { return []models.Recipe01{}, fmt.Errorf("no submenu") } return submenu, nil } func (d *Data) GetCountryNameByID(countryID string) (string, error) { for _, country := range d.Countries { if country.CountryID == countryID { return country.CountryName, nil } } return "", fmt.Errorf("country ID: %s not found", countryID) } func (d *Data) GetCountryIDByName(countryName string) (string, error) { for _, country := range d.Countries { if country.CountryName == countryName { return country.CountryID, nil } } return "", fmt.Errorf("country name: %s not found", countryName) }