package main import ( "errors" "fmt" "io/ioutil" "log" "math" "net/http" "runtime" "strconv" "strings" "time" "github.com/lucasb-eyer/go-colorful" ) type line struct { Date time.Time Operation string Cryptocurrency string Amount float64 TransactionID string WithdrawalAddress string Reference string } type rewards struct { Staking map[time.Month]string Confectionery map[time.Month]string LapisDFI map[time.Month]string Lapis map[time.Month]string Referral map[time.Month]string Airdrop map[time.Month]string Swapped map[time.Month]string } func getCumulative(r rewards, precision int) map[time.Month]string { // create map to calculate cumulative rewards res := make(map[time.Month]string) for i := 1; i < 13; i++ { month := time.Month(i) // parse all fields airdrop, err := strconv.ParseFloat(r.Airdrop[month], 64) confectionery, err := strconv.ParseFloat(r.Confectionery[month], 64) lapis, err := strconv.ParseFloat(r.Lapis[month], 64) lapisDFI, err := strconv.ParseFloat(r.LapisDFI[month], 64) referral, err := strconv.ParseFloat(r.Referral[month], 64) staking, err := strconv.ParseFloat(r.Staking[month], 64) swapped, err := strconv.ParseFloat(r.Swapped[month], 64) // add all fields up and format result as string res[month] = strconv.FormatFloat(airdrop+confectionery+lapis+lapisDFI+referral+staking+swapped, 'g', precision, 64) if err != nil { panic(err) } } // return result return res } func getOtherColors(color string, n int) []string { col, err := colorful.Hex(color) if err != nil { log.Fatal(err) } // get r,g,b from color r, g, b := col.LinearRgb() // return color reduced in "brightness" by factor of f var c []string for i := 1; i < int(math.Pow(2, float64(n))); i *= 2 { c = append(c, colorful.LinearRgb(r/float64(i), g/float64(i), b/float64(i)).Hex()) } return c } func uploadFile(w http.ResponseWriter, r *http.Request) ([]line, error) { // Maximum upload of 10 MB files r.ParseMultipartForm(10 << 20) defer r.Body.Close() // Get handler for filename, size and headers file, handler, err := r.FormFile("csvFile") if err != nil { return nil, errors.New("please upload a .csv file") } defer file.Close() // check if we got a csv file switch handler.Header["Content-Type"][0] { case "text/csv", "application/vnd.ms-excel": // accept this gift and read content of file fileContents, err := ioutil.ReadAll(file) if err != nil { fmt.Println("file was uploaded but can't be read! 1", err) return nil, err } // read uploaded file lines, err := readUploadedFile(fileContents) if err != nil { return nil, fmt.Errorf("the .csv file could not be read, error was: %s", err) } return lines, err } return nil, errors.New("please only upload .csv files") } func getCurrencyOpts(currency string) (color string, precision int, lending bool) { // set default precision precision = 8 lending = false // set other options according to the coin switch currency { case "BTC": // Bitcoin color = "#F7931A" lending = true case "DASH": color = "#008de4" case "DFI": // DeFiChain color = "#fd0bb0" case "ETH": // Ethereum color = "#627eea" lending = true precision = 18 case "PIVX": color = "#5e4778" precision = 9 case "XZC": // Zcoin color = "#24b852" case "USDT": // Tether color = "#26a17b" lending = true precision = 6 } return color, precision, lending } func monthlyRewardOverview(lines []line) rewards { // create a map to hold all months' sums staking := make(map[time.Month]float64) confectionery := make(map[time.Month]float64) lapisDFI := make(map[time.Month]float64) lapis := make(map[time.Month]float64) referral := make(map[time.Month]float64) airdrop := make(map[time.Month]float64) swapped := make(map[time.Month]float64) // coinValue, err := GetCoinValue("bitcoin", "eur") // if err == nil { // loop through all lines for i := range lines { // filter out operations switch lines[i].Operation { case "Staking reward": staking[lines[i].Date.Month()] += lines[i].Amount case "Confectionery Lapis DFI Bonus": confectionery[lines[i].Date.Month()] += lines[i].Amount case "Lapis DFI Bonus": lapisDFI[lines[i].Date.Month()] += lines[i].Amount case "Lapis reward": lapis[lines[i].Date.Month()] += lines[i].Amount case "Referral reward": referral[lines[i].Date.Month()] += lines[i].Amount case "Bonus/Airdrop": airdrop[lines[i].Date.Month()] += lines[i].Amount case "Swapped in": swapped[lines[i].Date.Month()] += lines[i].Amount } // } } // get precision for specific coin _, precision, _ := getCurrencyOpts(lines[0].Cryptocurrency) // fill empty data with zeroes r := rewards{ Staking: fillSums(staking, precision), Confectionery: fillSums(confectionery, precision), LapisDFI: fillSums(lapisDFI, precision), Lapis: fillSums(lapis, precision), Referral: fillSums(referral, precision), Airdrop: fillSums(airdrop, precision), Swapped: fillSums(swapped, precision), } return r } func fillSums(s map[time.Month]float64, precision int) map[time.Month]string { // maps are always referenced by pointers // create empty map sumsString := make(map[time.Month]string) // loop through month for i := 1; i < 13; i++ { // check what month are missing in array if _, ok := s[time.Month(i)]; !ok { s[time.Month(i)] = 0 } // format to strings sumsString[time.Month(i)] = strconv.FormatFloat(s[time.Month(i)], 'g', precision, 64) } return sumsString } func readUploadedFile(fileContents []byte) ([]line, error) { var lines []line var csvLines []string // newlines need to be handled differently on windows switch runtime.GOOS { case "windows": csvLines = strings.Split(string(fileContents), "\r\n") default: csvLines = strings.Split(string(fileContents), "\n") } // loop through all lines (except headers) for _, csvLine := range csvLines[1:] { if csvLine != "" { // split arguments by comma csvArgs := strings.Split(csvLine, ",") // fix stupid " in lines for i := range csvArgs { csvArgs[i] = strings.ReplaceAll(csvArgs[i], `"`, "") } // create output variable var l line // assign values from CSV file to output variable var err error l.Date, err = time.Parse(time.RFC3339, csvArgs[0]) if err != nil { return nil, err } l.Operation = csvArgs[1] l.Cryptocurrency = csvArgs[2] l.Amount, _ = strconv.ParseFloat(csvArgs[3], 64) l.TransactionID = csvArgs[4] l.WithdrawalAddress = csvArgs[5] l.Reference = csvArgs[6] lines = append(lines, l) } } return lines, nil }