package main import ( "errors" "math" "regexp" "strconv" "strings" "github.com/gocolly/colly" ) func stringInSlice(a string, list []string) bool { for _, b := range list { if b == a { return true } } return false } func detect_spirit_type(name string) string { matched, err := regexp.MatchString(`(^|\s)Gin(\s|$)`, name) if err != nil { Fatal(err, "Gin regex failed") } if matched { return "Gin" } matched, err = regexp.MatchString(`(^|\s)Rh?um(\s|$)`, name) if err != nil { Fatal(err, "Rum regex failed") } if matched { return "Rum" } matched, err = regexp.MatchString(`(^|\s)[VW]odka(\s|$)`, name) if err != nil { Fatal(err, "Wodka regex failed") } if matched { return "Wodka" } matched, err = regexp.MatchString(`(^|\s)Whiske?y(\s|$)`, name) if err != nil { Fatal(err, "Whisky regex failed") } if matched { return "Whisky" } matched, err = regexp.MatchString(`(^|\s)Wein(\s|$)`, name) if err != nil { Fatal(err, "Wein regex failed") } if matched { return "Wein" } matched, err = regexp.MatchString(`(^|\s)(Champagner)|(Champagne)(\s|$)`, name) if err != nil { Fatal(err, "Champagner regex failed") } if matched { return "Champagner" } matched, err = regexp.MatchString(`(^|\s)Armagnac(\s|$)`, name) if err != nil { Fatal(err, "Armagnac regex failed") } if matched { return "Armagnac" } matched, err = regexp.MatchString(`(^|\s)Cognac(\s|$)`, name) if err != nil { Fatal(err, "Cognac regex failed") } if matched { return "Cognac" } matched, err = regexp.MatchString(`(^|\s)(Brandy)|(Weinbrand)(\s|$)`, name) if err != nil { Fatal(err, "Brandy regex failed") } if matched { return "Brandy" } matched, err = regexp.MatchString(`(^|\s)Calvados(\s|$)`, name) if err != nil { Fatal(err, "Calvados regex failed") } if matched { return "Calvados" } matched, err = regexp.MatchString(`(^|\s)Grappa(\s|$)`, name) if err != nil { Fatal(err, "Grappa regex failed") } if matched { return "Grappa" } matched, err = regexp.MatchString(`(^|\s)(\wlikör)|(Likör)(\s|$)`, name) if err != nil { Fatal(err, "Likör regex failed") } if matched { return "Likör" } matched, err = regexp.MatchString(`(^|\s)(Vermouth)|(Wermut)(\s|$)`, name) if err != nil { Fatal(err, "Vermouth|Wermut regex failed") } if matched { return "Wermut" } matched, err = regexp.MatchString(`(^|\s)([G|J]enever)|(Korenwijn)(\s|$)`, name) if err != nil { Fatal(err, "Genever regex failed") } if matched { return "Genever" } matched, err = regexp.MatchString(`(^|\s)Baijiu(\s|$)`, name) if err != nil { Fatal(err, "Baijiu regex failed") } if matched { return "Baijiu" } matched, err = regexp.MatchString(`(^|\s)(Sherry|Oloroso|Fino|Amontillado)(\s|$)`, name) if err != nil { Fatal(err, "Sherry regex failed") } if matched { return "Sherry" } matched, err = regexp.MatchString(`((^|\s)Port(wein)?(\s|$))|((^|\s)(Ruby|Tawny)(\s|$))`, name) if err != nil { Fatal(err, "Portwein regex failed") } if matched { return "Portwein" } matched, err = regexp.MatchString(`(^|\s)Obstler|Obstbrand|Edelbrand(\s|$)`, name) if err != nil { Fatal(err, "Obstbrand regex failed") } if matched { return "Obstbrand" } matched, err = regexp.MatchString(`(^|\s)Sake(\s|$)`, name) if err != nil { Fatal(err, "Sake regex failed") } if matched { return "Sake" } matched, err = regexp.MatchString(`(^|\s)Shochu(\s|$)`, name) if err != nil { Fatal(err, "Shochu regex failed") } if matched { return "Shochu" } matched, err = regexp.MatchString(`(^|\s)Pisco(\s|$)`, name) if err != nil { Fatal(err, "Pisco regex failed") } if matched { return "Pisco" } matched, err = regexp.MatchString(`(^|\s)Absint(h?)(\s|$)`, name) if err != nil { Fatal(err, "Absinth regex failed") } if matched { return "Absinth" } matched, err = regexp.MatchString(`(^|\s)Tequila(\s|$)`, name) if err != nil { Fatal(err, "Tequila regex failed") } if matched { return "Tequila" } matched, err = regexp.MatchString(`(^|\s)Mezcal(\s|$)`, name) if err != nil { Fatal(err, "Mezcal regex failed") } if matched { return "Mezcal" } return "Verschiedenes" } func extract_volume(volume string) (float32, error) { var volume_noisy string var is_litre_instead_of_cl bool // difference between cl... r_cl, err := regexp.Compile(`[0-9]+([,.][0-9]+)?( )?[cC][lL]`) if err != nil { Fatal(err, "Extract volume (centiliter) regex failed") } volume_noisy = r_cl.FindString(volume) if volume_noisy == "" { // ...and litre is_litre_instead_of_cl = true r_liter, err := regexp.Compile(`[0-9]+([,.][0-9]+)?( )?[lL](iter)?`) if err != nil { Fatal(err, "Extract volume regex failed") } volume_noisy = r_liter.FindString(volume) } // extract numbers r_liter2, err := regexp.Compile(`[0-9]+([,.][0-9]+)?`) if err != nil { Fatal(err, "2nd extract volume regex failed") } volume_noisy = r_liter2.FindString(volume_noisy) volume_noisy = strings.Replace(volume_noisy, ",", ".", 1) volume64, err := strconv.ParseFloat(volume_noisy, 0) if err != nil { Println(err, "Parsing volume to float failed") return 0, err } // converting from cl to litre if !is_litre_instead_of_cl { volume64 = volume64 / 100 } return float32(volume64), err } func extract_abv(abv_noisy string) (float32, error) { if strings.Contains(abv_noisy, "%") { abv_noisy = strings.Replace(abv_noisy, "%", "", 1) } if strings.Contains(abv_noisy, "vol") { abv_noisy = strings.Replace(abv_noisy, "vol", "", 1) } if strings.Contains(abv_noisy, "Vol") { abv_noisy = strings.Replace(abv_noisy, "Vol", "", 1) } abv_noisy = strings.Replace(abv_noisy, ",", ".", 1) abv_noisy = strings.TrimSpace(abv_noisy) r_abv, err := regexp.Compile(`[0-9]+([,.][0-9]+)?`) if err != nil { Fatal(err, "Extract abv regex failed") } abv_noisy = r_abv.FindString(abv_noisy) abv64, err := strconv.ParseFloat(abv_noisy, 0) if err != nil { Println(err, "Parsing abv to float failed") return 0, err } return float32(abv64), nil } /* * In litre, but float. */ func get_volume(e *colly.HTMLElement) (float32, string) { volume_noisy := e.Request.Ctx.Get("volume") matched, err := regexp.MatchString(`[lL](iter)?`, volume_noisy) if err != nil { Fatal(err, "Get volume regex failed") } if !matched { return 0, volume_noisy } volume, err := extract_volume(volume_noisy) if err != nil { Warn(err, "Get Volume: Extract Volume failed: "+volume_noisy) return 0, err.Error() + " // volume_noisy: " + volume_noisy } return volume, "" } /* * In procent. (float) */ func get_abv(e *colly.HTMLElement) (float32, string) { abv_noisy := e.Request.Ctx.Get("abv") debug_info := e.Request.Ctx.Get("debug_info") if abv_noisy == "" { return 0, debug_info } // abv_noisy = strings.Replace(abv_noisy, ".", ",", 1) abv, err := extract_abv(abv_noisy) if err != nil { Warn(err, "Get ABV: Extract ABV failed: "+abv_noisy) return 0, err.Error() + " // abv_noisy: " + abv_noisy } return abv, "" } /* * In cents. (int) */ func get_base_price(e *colly.HTMLElement) (int, error) { base_price_noisy := e.Request.Ctx.Get("base_price") if base_price_noisy == "" { return 0, errors.New("Base price empty") } base_price, err := sanitize_base_price(base_price_noisy) if err != nil { return 0, err } return base_price, nil } /* * Source: https://golang.org/src/math/floor.go?s=2165:2200#L104 * Will use std lib with go version >= 1.10 */ func RoundToEven(x float64) float64 { t := math.Trunc(x) odd := math.Remainder(t, 2) != 0 if d := math.Abs(x - t); d > 0.5 || (d == 0.5 && odd) { return t + math.Copysign(1, x) } return t }