goCake/cake.go

320 lines
8.2 KiB
Go
Raw Normal View History

2020-11-08 16:00:33 +00:00
package main
import (
"errors"
2020-11-15 19:46:32 +00:00
"fmt"
2020-11-08 16:00:33 +00:00
"io/ioutil"
2020-11-15 19:46:32 +00:00
"log"
"math"
2020-11-08 16:00:33 +00:00
"net/http"
"runtime"
2020-11-08 16:00:33 +00:00
"strconv"
"strings"
"time"
2020-11-15 19:46:32 +00:00
"github.com/lucasb-eyer/go-colorful"
2020-11-08 16:00:33 +00:00
)
type line struct {
Date time.Time
Operation string
Cryptocurrency string
Amount float64
TransactionID string
WithdrawalAddress string
Reference string
}
2020-11-15 19:46:32 +00:00
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
2020-11-15 19:46:32 +00:00
}
// CakeRewards needs a comment
func CakeRewards(w http.ResponseWriter, r *http.Request) {
2020-11-08 16:00:33 +00:00
type data struct {
Title string
Uploaded bool
Success bool
2020-11-15 19:46:32 +00:00
Rewards rewards
2020-11-08 16:00:33 +00:00
CumulativeRewards map[time.Month]string
Colors []string
2020-11-08 16:00:33 +00:00
Currency string
2020-11-15 19:46:32 +00:00
ErrorText string
Lending bool
2020-11-08 16:00:33 +00:00
}
// set common attributes
d := data{
Title: "Pool by Cake Rewards",
2020-11-08 16:00:33 +00:00
}
// check the method used to access this site (GET/POST)
switch r.Method {
case http.MethodGet:
// display upload site
d.Uploaded = false
d.Success = false
render(w, "rewards.html", d)
case http.MethodPost:
// upload the file that was posted here
var success bool = false
2020-11-15 19:46:32 +00:00
2020-11-08 16:00:33 +00:00
lines, err := uploadFile(w, r)
if err == nil {
success = true
2020-11-15 19:46:32 +00:00
} else {
// set ErrorText
d.ErrorText = err.Error()
2020-11-08 16:00:33 +00:00
}
// prepare data for usage
var color string
var currency string
var precision int
2020-11-15 19:46:32 +00:00
var rewards rewards
2020-11-08 16:00:33 +00:00
if success == true {
currency = lines[1].Cryptocurrency
color, precision, d.Lending = getCurrencyOpts(currency)
2020-11-08 16:00:33 +00:00
rewards = monthlyRewardOverview(lines)
d.CumulativeRewards = getCumulative(rewards, precision)
d.Success = success
d.Rewards.Staking = rewards.Staking
d.Rewards.Confectionery = rewards.Confectionery
d.Rewards.Lapis = rewards.Lapis
d.Rewards.LapisDFI = rewards.LapisDFI
d.Rewards.Referral = rewards.Referral
d.Rewards.Airdrop = rewards.Airdrop
d.Rewards.Swapped = rewards.Swapped
d.Colors = getOtherColors(color, 8)
d.Currency = currency
2020-11-08 16:00:33 +00:00
}
d.Uploaded = true
2020-11-08 16:00:33 +00:00
// prettify.Print(rewards)
render(w, "rewards.html", d)
}
// create a new line instance
}
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 {
2020-11-15 19:46:32 +00:00
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
2020-11-15 19:46:32 +00:00
}
2020-11-08 16:00:33 +00:00
func uploadFile(w http.ResponseWriter, r *http.Request) ([]line, error) {
// Maximum upload of 10 MB files
r.ParseMultipartForm(10 << 20)
defer r.Body.Close()
2020-11-08 16:00:33 +00:00
// Get handler for filename, size and headers
file, handler, err := r.FormFile("csvFile")
if err != nil {
2020-11-15 19:46:32 +00:00
return nil, errors.New("please upload a .csv file")
2020-11-08 16:00:33 +00:00
}
defer file.Close()
// check if we got a csv file
switch handler.Header["Content-Type"][0] {
case "text/csv", "application/vnd.ms-excel":
2020-11-08 16:00:33 +00:00
// accept this gift and read content of file
fileContents, err := ioutil.ReadAll(file)
if err != nil {
2020-11-15 19:46:32 +00:00
fmt.Println("file was uploaded but can't be read! 1", err)
2020-11-08 16:00:33 +00:00
return nil, err
}
// read uploaded file
2020-11-15 19:46:32 +00:00
lines, err := readUploadedFile(fileContents)
if err != nil {
return nil, fmt.Errorf("the .csv file could not be read, error was: %s", err)
}
2020-11-08 16:00:33 +00:00
return lines, err
}
return nil, errors.New("please only upload .csv files")
}
2020-11-15 19:46:32 +00:00
func getCurrencyOpts(currency string) (color string, precision int, lending bool) {
2020-11-08 16:00:33 +00:00
// set default precision
precision = 8
2020-11-15 19:46:32 +00:00
lending = false
2020-11-08 16:00:33 +00:00
// set other options according to the coin
switch currency {
case "BTC": // Bitcoin
color = "#F7931A"
2020-11-15 19:46:32 +00:00
lending = true
2020-11-08 16:00:33 +00:00
case "DASH":
color = "#008de4"
case "DFI": // DeFiChain
color = "#fd0bb0"
case "ETH": // Ethereum
color = "#627eea"
2020-11-15 19:46:32 +00:00
lending = true
2020-11-08 16:00:33 +00:00
precision = 18
case "PIVX":
color = "#5e4778"
precision = 9
case "XZC": // Zcoin
color = "#24b852"
case "USDT": // Tether
color = "#26a17b"
2020-11-15 19:46:32 +00:00
lending = true
2020-11-08 16:00:33 +00:00
precision = 6
}
2020-11-15 19:46:32 +00:00
return color, precision, lending
2020-11-08 16:00:33 +00:00
}
2020-11-15 19:46:32 +00:00
func monthlyRewardOverview(lines []line) rewards {
2020-11-08 16:00:33 +00:00
// create a map to hold all months' sums
2020-11-15 19:46:32 +00:00
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 {
2020-11-08 16:00:33 +00:00
// loop through all lines
for i := range lines {
// filter out operations
switch lines[i].Operation {
2020-11-15 19:46:32 +00:00
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
2020-11-08 16:00:33 +00:00
}
// }
2020-11-08 16:00:33 +00:00
}
// get precision for specific coin
2020-11-15 19:46:32 +00:00
_, precision, _ := getCurrencyOpts(lines[0].Cryptocurrency)
2020-11-08 16:00:33 +00:00
// fill empty data with zeroes
2020-11-15 19:46:32 +00:00
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),
2020-11-15 19:46:32 +00:00
}
return r
2020-11-08 16:00:33 +00:00
}
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
}
2020-11-15 19:46:32 +00:00
func readUploadedFile(fileContents []byte) ([]line, error) {
2020-11-08 16:00:33 +00:00
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")
}
2020-11-08 16:00:33 +00:00
// loop through all lines (except headers)
for _, csvLine := range csvLines[1:] {
2020-11-15 19:46:32 +00:00
if csvLine != "" {
// split arguments by comma
csvArgs := strings.Split(csvLine, ",")
2020-11-08 16:00:33 +00:00
2020-11-15 19:46:32 +00:00
// fix stupid " in lines
for i := range csvArgs {
csvArgs[i] = strings.ReplaceAll(csvArgs[i], `"`, "")
}
2020-11-08 16:00:33 +00:00
2020-11-15 19:46:32 +00:00
// create output variable
var l line
2020-11-08 16:00:33 +00:00
2020-11-15 19:46:32 +00:00
// 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)
2020-11-08 16:00:33 +00:00
}
}
2020-11-15 19:46:32 +00:00
return lines, nil
2020-11-08 16:00:33 +00:00
}