mirror of
https://github.com/aclindsa/moneygo.git
synced 2025-06-14 13:58:37 -04:00
Split Lua reports into own package
This commit is contained in:
@ -1,223 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/aclindsa/moneygo/internal/models"
|
||||
"github.com/aclindsa/moneygo/internal/store"
|
||||
"github.com/yuin/gopher-lua"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const luaAccountTypeName = "account"
|
||||
|
||||
func luaContextGetAccounts(L *lua.LState) (map[int64]*models.Account, error) {
|
||||
var account_map map[int64]*models.Account
|
||||
|
||||
ctx := L.Context()
|
||||
|
||||
tx, ok := ctx.Value(dbContextKey).(store.Tx)
|
||||
if !ok {
|
||||
return nil, errors.New("Couldn't find tx in lua's Context")
|
||||
}
|
||||
|
||||
account_map, ok = ctx.Value(accountsContextKey).(map[int64]*models.Account)
|
||||
if !ok {
|
||||
user, ok := ctx.Value(userContextKey).(*models.User)
|
||||
if !ok {
|
||||
return nil, errors.New("Couldn't find User in lua's Context")
|
||||
}
|
||||
|
||||
accounts, err := tx.GetAccounts(user.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
account_map = make(map[int64]*models.Account)
|
||||
for i := range *accounts {
|
||||
account_map[(*accounts)[i].AccountId] = (*accounts)[i]
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, accountsContextKey, account_map)
|
||||
L.SetContext(ctx)
|
||||
}
|
||||
|
||||
return account_map, nil
|
||||
}
|
||||
|
||||
func luaGetAccounts(L *lua.LState) int {
|
||||
account_map, err := luaContextGetAccounts(L)
|
||||
if err != nil {
|
||||
panic("luaGetAccounts couldn't fetch accounts")
|
||||
}
|
||||
|
||||
table := L.NewTable()
|
||||
|
||||
for accountid := range account_map {
|
||||
table.RawSetInt(int(accountid), AccountToLua(L, account_map[accountid]))
|
||||
}
|
||||
|
||||
L.Push(table)
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaRegisterAccounts(L *lua.LState) {
|
||||
mt := L.NewTypeMetatable(luaAccountTypeName)
|
||||
L.SetGlobal("account", mt)
|
||||
L.SetField(mt, "__index", L.NewFunction(luaAccount__index))
|
||||
L.SetField(mt, "__tostring", L.NewFunction(luaAccount__tostring))
|
||||
L.SetField(mt, "__eq", L.NewFunction(luaAccount__eq))
|
||||
L.SetField(mt, "__metatable", lua.LString("protected"))
|
||||
|
||||
for _, accttype := range models.AccountTypes {
|
||||
L.SetField(mt, accttype.String(), lua.LNumber(float64(accttype)))
|
||||
}
|
||||
|
||||
getAccountsFn := L.NewFunction(luaGetAccounts)
|
||||
L.SetField(mt, "get_all", getAccountsFn)
|
||||
// also register the get_accounts function as a global in its own right
|
||||
L.SetGlobal("get_accounts", getAccountsFn)
|
||||
}
|
||||
|
||||
func AccountToLua(L *lua.LState, account *models.Account) *lua.LUserData {
|
||||
ud := L.NewUserData()
|
||||
ud.Value = account
|
||||
L.SetMetatable(ud, L.GetTypeMetatable(luaAccountTypeName))
|
||||
return ud
|
||||
}
|
||||
|
||||
// Checks whether the first lua argument is a *LUserData with *Account and returns this *Account.
|
||||
func luaCheckAccount(L *lua.LState, n int) *models.Account {
|
||||
ud := L.CheckUserData(n)
|
||||
if account, ok := ud.Value.(*models.Account); ok {
|
||||
return account
|
||||
}
|
||||
L.ArgError(n, "account expected")
|
||||
return nil
|
||||
}
|
||||
|
||||
func luaAccount__index(L *lua.LState) int {
|
||||
a := luaCheckAccount(L, 1)
|
||||
field := L.CheckString(2)
|
||||
|
||||
switch field {
|
||||
case "AccountId", "accountid":
|
||||
L.Push(lua.LNumber(float64(a.AccountId)))
|
||||
case "Security", "security":
|
||||
security_map, err := luaContextGetSecurities(L)
|
||||
if err != nil {
|
||||
panic("account.security couldn't fetch securities")
|
||||
}
|
||||
if security, ok := security_map[a.SecurityId]; ok {
|
||||
L.Push(SecurityToLua(L, security))
|
||||
} else {
|
||||
panic("SecurityId not in lua security_map")
|
||||
}
|
||||
case "SecurityId", "securityid":
|
||||
L.Push(lua.LNumber(float64(a.SecurityId)))
|
||||
case "Parent", "parent", "ParentAccount", "parentaccount":
|
||||
if a.ParentAccountId == -1 {
|
||||
L.Push(lua.LNil)
|
||||
} else {
|
||||
account_map, err := luaContextGetAccounts(L)
|
||||
if err != nil {
|
||||
panic("account.parent couldn't fetch accounts")
|
||||
}
|
||||
if parent, ok := account_map[a.ParentAccountId]; ok {
|
||||
L.Push(AccountToLua(L, parent))
|
||||
} else {
|
||||
panic("ParentAccountId not in lua account_map")
|
||||
}
|
||||
}
|
||||
case "Name", "name":
|
||||
L.Push(lua.LString(a.Name))
|
||||
case "Type", "type":
|
||||
L.Push(lua.LNumber(float64(a.Type)))
|
||||
case "TypeName", "Typename":
|
||||
L.Push(lua.LString(a.Type.String()))
|
||||
case "typename":
|
||||
L.Push(lua.LString(strings.ToLower(a.Type.String())))
|
||||
case "Balance", "balance":
|
||||
L.Push(L.NewFunction(luaAccountBalance))
|
||||
default:
|
||||
L.ArgError(2, "unexpected account attribute: "+field)
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaAccountBalance(L *lua.LState) int {
|
||||
a := luaCheckAccount(L, 1)
|
||||
|
||||
ctx := L.Context()
|
||||
tx, ok := ctx.Value(dbContextKey).(store.Tx)
|
||||
if !ok {
|
||||
panic("Couldn't find tx in lua's Context")
|
||||
}
|
||||
user, ok := ctx.Value(userContextKey).(*models.User)
|
||||
if !ok {
|
||||
panic("Couldn't find User in lua's Context")
|
||||
}
|
||||
security_map, err := luaContextGetSecurities(L)
|
||||
if err != nil {
|
||||
panic("account.security couldn't fetch securities")
|
||||
}
|
||||
security, ok := security_map[a.SecurityId]
|
||||
if !ok {
|
||||
panic("SecurityId not in lua security_map")
|
||||
}
|
||||
date := luaWeakCheckTime(L, 2)
|
||||
var splits *[]*models.Split
|
||||
if date != nil {
|
||||
end := luaWeakCheckTime(L, 3)
|
||||
if end != nil {
|
||||
splits, err = tx.GetAccountSplitsDateRange(user, a.AccountId, date, end)
|
||||
} else {
|
||||
splits, err = tx.GetAccountSplitsDate(user, a.AccountId, date)
|
||||
}
|
||||
} else {
|
||||
splits, err = tx.GetAccountSplits(user, a.AccountId)
|
||||
}
|
||||
if err != nil {
|
||||
panic("Failed to fetch splits for account:" + err.Error())
|
||||
}
|
||||
rat, err := BalanceFromSplits(splits)
|
||||
if err != nil {
|
||||
panic("Failed to calculate balance for account:" + err.Error())
|
||||
}
|
||||
b := &Balance{
|
||||
Amount: rat,
|
||||
Security: security,
|
||||
}
|
||||
L.Push(BalanceToLua(L, b))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaAccount__tostring(L *lua.LState) int {
|
||||
a := luaCheckAccount(L, 1)
|
||||
|
||||
account_map, err := luaContextGetAccounts(L)
|
||||
if err != nil {
|
||||
panic("luaGetAccounts couldn't fetch accounts")
|
||||
}
|
||||
|
||||
full_name := a.Name
|
||||
for a.ParentAccountId != -1 {
|
||||
a = account_map[a.ParentAccountId]
|
||||
full_name = a.Name + "/" + full_name
|
||||
}
|
||||
|
||||
L.Push(lua.LString(full_name))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaAccount__eq(L *lua.LState) int {
|
||||
a := luaCheckAccount(L, 1)
|
||||
b := luaCheckAccount(L, 2)
|
||||
|
||||
L.Push(lua.LBool(a.AccountId == b.AccountId))
|
||||
|
||||
return 1
|
||||
}
|
@ -1,225 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/aclindsa/moneygo/internal/models"
|
||||
"github.com/yuin/gopher-lua"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type Balance struct {
|
||||
Security *models.Security
|
||||
Amount *big.Rat
|
||||
}
|
||||
|
||||
const luaBalanceTypeName = "balance"
|
||||
|
||||
func luaRegisterBalances(L *lua.LState) {
|
||||
mt := L.NewTypeMetatable(luaBalanceTypeName)
|
||||
L.SetGlobal("balance", mt)
|
||||
L.SetField(mt, "__index", L.NewFunction(luaBalance__index))
|
||||
L.SetField(mt, "__tostring", L.NewFunction(luaBalance__tostring))
|
||||
L.SetField(mt, "__eq", L.NewFunction(luaBalance__eq))
|
||||
L.SetField(mt, "__lt", L.NewFunction(luaBalance__lt))
|
||||
L.SetField(mt, "__le", L.NewFunction(luaBalance__le))
|
||||
L.SetField(mt, "__add", L.NewFunction(luaBalance__add))
|
||||
L.SetField(mt, "__sub", L.NewFunction(luaBalance__sub))
|
||||
L.SetField(mt, "__mul", L.NewFunction(luaBalance__mul))
|
||||
L.SetField(mt, "__div", L.NewFunction(luaBalance__div))
|
||||
L.SetField(mt, "__unm", L.NewFunction(luaBalance__unm))
|
||||
L.SetField(mt, "__metatable", lua.LString("protected"))
|
||||
}
|
||||
|
||||
func BalanceToLua(L *lua.LState, balance *Balance) *lua.LUserData {
|
||||
ud := L.NewUserData()
|
||||
ud.Value = balance
|
||||
L.SetMetatable(ud, L.GetTypeMetatable(luaBalanceTypeName))
|
||||
return ud
|
||||
}
|
||||
|
||||
// Checks whether the first lua argument is a *LUserData with *Balance and returns this *Balance.
|
||||
func luaCheckBalance(L *lua.LState, n int) *Balance {
|
||||
ud := L.CheckUserData(n)
|
||||
if balance, ok := ud.Value.(*Balance); ok {
|
||||
return balance
|
||||
}
|
||||
L.ArgError(n, "balance expected")
|
||||
return nil
|
||||
}
|
||||
|
||||
func luaWeakCheckBalance(L *lua.LState, n int) *Balance {
|
||||
v := L.Get(n)
|
||||
if ud, ok := v.(*lua.LUserData); ok {
|
||||
if balance, ok := ud.Value.(*Balance); ok {
|
||||
return balance
|
||||
}
|
||||
L.ArgError(n, "balance expected")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func luaGetBalanceOperands(L *lua.LState, m int, n int) (*Balance, *Balance) {
|
||||
bm := luaWeakCheckBalance(L, m)
|
||||
bn := luaWeakCheckBalance(L, n)
|
||||
|
||||
if bm != nil && bn != nil {
|
||||
return bm, bn
|
||||
} else if bm != nil {
|
||||
nn := L.CheckNumber(n)
|
||||
var balance Balance
|
||||
var rat big.Rat
|
||||
balance.Security = bm.Security
|
||||
balance.Amount = rat.SetFloat64(float64(nn))
|
||||
if balance.Amount == nil {
|
||||
L.ArgError(n, "non-finite float invalid for operand to balance arithemetic")
|
||||
return nil, nil
|
||||
}
|
||||
return bm, &balance
|
||||
} else if bn != nil {
|
||||
nm := L.CheckNumber(m)
|
||||
var balance Balance
|
||||
var rat big.Rat
|
||||
balance.Security = bn.Security
|
||||
balance.Amount = rat.SetFloat64(float64(nm))
|
||||
if balance.Amount == nil {
|
||||
L.ArgError(m, "non-finite float invalid for operand to balance arithemetic")
|
||||
return nil, nil
|
||||
}
|
||||
return &balance, bn
|
||||
}
|
||||
L.ArgError(m, "balance expected")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func luaBalance__index(L *lua.LState) int {
|
||||
a := luaCheckBalance(L, 1)
|
||||
field := L.CheckString(2)
|
||||
|
||||
switch field {
|
||||
case "Security", "security":
|
||||
L.Push(SecurityToLua(L, a.Security))
|
||||
case "Amount", "amount":
|
||||
float, _ := a.Amount.Float64()
|
||||
L.Push(lua.LNumber(float))
|
||||
default:
|
||||
L.ArgError(2, "unexpected balance attribute: "+field)
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaBalance__tostring(L *lua.LState) int {
|
||||
b := luaCheckBalance(L, 1)
|
||||
|
||||
L.Push(lua.LString(b.Security.Symbol + " " + b.Amount.FloatString(b.Security.Precision)))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaBalance__eq(L *lua.LState) int {
|
||||
a := luaCheckBalance(L, 1)
|
||||
b := luaCheckBalance(L, 2)
|
||||
|
||||
L.Push(lua.LBool(a.Security.SecurityId == b.Security.SecurityId && a.Amount.Cmp(b.Amount) == 0))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaBalance__lt(L *lua.LState) int {
|
||||
a := luaCheckBalance(L, 1)
|
||||
b := luaCheckBalance(L, 2)
|
||||
if a.Security.SecurityId != b.Security.SecurityId {
|
||||
L.ArgError(2, "Can't compare balances with different securities")
|
||||
}
|
||||
|
||||
L.Push(lua.LBool(a.Amount.Cmp(b.Amount) < 0))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaBalance__le(L *lua.LState) int {
|
||||
a := luaCheckBalance(L, 1)
|
||||
b := luaCheckBalance(L, 2)
|
||||
if a.Security.SecurityId != b.Security.SecurityId {
|
||||
L.ArgError(2, "Can't compare balances with different securities")
|
||||
}
|
||||
|
||||
L.Push(lua.LBool(a.Amount.Cmp(b.Amount) <= 0))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaBalance__add(L *lua.LState) int {
|
||||
a, b := luaGetBalanceOperands(L, 1, 2)
|
||||
|
||||
if a.Security.SecurityId != b.Security.SecurityId {
|
||||
L.ArgError(2, "Can't add balances with different securities")
|
||||
}
|
||||
|
||||
var balance Balance
|
||||
var rat big.Rat
|
||||
balance.Security = a.Security
|
||||
balance.Amount = rat.Add(a.Amount, b.Amount)
|
||||
L.Push(BalanceToLua(L, &balance))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaBalance__sub(L *lua.LState) int {
|
||||
a, b := luaGetBalanceOperands(L, 1, 2)
|
||||
|
||||
if a.Security.SecurityId != b.Security.SecurityId {
|
||||
L.ArgError(2, "Can't subtract balances with different securities")
|
||||
}
|
||||
|
||||
var balance Balance
|
||||
var rat big.Rat
|
||||
balance.Security = a.Security
|
||||
balance.Amount = rat.Sub(a.Amount, b.Amount)
|
||||
L.Push(BalanceToLua(L, &balance))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaBalance__mul(L *lua.LState) int {
|
||||
a, b := luaGetBalanceOperands(L, 1, 2)
|
||||
|
||||
if a.Security.SecurityId != b.Security.SecurityId {
|
||||
L.ArgError(2, "Can't multiply balances with different securities")
|
||||
}
|
||||
|
||||
var balance Balance
|
||||
var rat big.Rat
|
||||
balance.Security = a.Security
|
||||
balance.Amount = rat.Mul(a.Amount, b.Amount)
|
||||
L.Push(BalanceToLua(L, &balance))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaBalance__div(L *lua.LState) int {
|
||||
a, b := luaGetBalanceOperands(L, 1, 2)
|
||||
|
||||
if a.Security.SecurityId != b.Security.SecurityId {
|
||||
L.ArgError(2, "Can't divide balances with different securities")
|
||||
}
|
||||
|
||||
var balance Balance
|
||||
var rat big.Rat
|
||||
balance.Security = a.Security
|
||||
balance.Amount = rat.Quo(a.Amount, b.Amount)
|
||||
L.Push(BalanceToLua(L, &balance))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaBalance__unm(L *lua.LState) int {
|
||||
b := luaCheckBalance(L, 1)
|
||||
|
||||
var balance Balance
|
||||
var rat big.Rat
|
||||
balance.Security = b.Security
|
||||
balance.Amount = rat.Neg(b.Amount)
|
||||
L.Push(BalanceToLua(L, &balance))
|
||||
|
||||
return 1
|
||||
}
|
@ -1,170 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/yuin/gopher-lua"
|
||||
"time"
|
||||
)
|
||||
|
||||
const luaDateTypeName = "date"
|
||||
const timeFormat = "2006-01-02"
|
||||
|
||||
func luaRegisterDates(L *lua.LState) {
|
||||
mt := L.NewTypeMetatable(luaDateTypeName)
|
||||
L.SetGlobal("date", mt)
|
||||
L.SetField(mt, "new", L.NewFunction(luaDateNew))
|
||||
L.SetField(mt, "now", L.NewFunction(luaDateNow))
|
||||
L.SetField(mt, "__index", L.NewFunction(luaDate__index))
|
||||
L.SetField(mt, "__tostring", L.NewFunction(luaDate__tostring))
|
||||
L.SetField(mt, "__eq", L.NewFunction(luaDate__eq))
|
||||
L.SetField(mt, "__lt", L.NewFunction(luaDate__lt))
|
||||
L.SetField(mt, "__le", L.NewFunction(luaDate__le))
|
||||
L.SetField(mt, "__add", L.NewFunction(luaDate__add))
|
||||
L.SetField(mt, "__sub", L.NewFunction(luaDate__sub))
|
||||
L.SetField(mt, "__metatable", lua.LString("protected"))
|
||||
}
|
||||
|
||||
func TimeToLua(L *lua.LState, date *time.Time) *lua.LUserData {
|
||||
ud := L.NewUserData()
|
||||
ud.Value = date
|
||||
L.SetMetatable(ud, L.GetTypeMetatable(luaDateTypeName))
|
||||
return ud
|
||||
}
|
||||
|
||||
// Checks whether the first lua argument is a *LUserData with *Time and returns this *Time.
|
||||
func luaCheckTime(L *lua.LState, n int) *time.Time {
|
||||
ud := L.CheckUserData(n)
|
||||
if date, ok := ud.Value.(*time.Time); ok {
|
||||
return date
|
||||
}
|
||||
L.ArgError(n, "date expected")
|
||||
return nil
|
||||
}
|
||||
|
||||
func luaWeakCheckTime(L *lua.LState, n int) *time.Time {
|
||||
v := L.Get(n)
|
||||
if ud, ok := v.(*lua.LUserData); ok {
|
||||
if date, ok := ud.Value.(*time.Time); ok {
|
||||
return date
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func luaWeakCheckTableFieldInt(L *lua.LState, T *lua.LTable, n int, name string, def int) int {
|
||||
lv := T.RawGetString(name)
|
||||
if lv == lua.LNil {
|
||||
return def
|
||||
}
|
||||
if i, ok := lv.(lua.LNumber); ok {
|
||||
return int(i)
|
||||
}
|
||||
L.ArgError(n, "table field '"+name+"' expected to be int")
|
||||
return def
|
||||
}
|
||||
|
||||
func luaDateNew(L *lua.LState) int {
|
||||
// TODO make this track the user's timezone
|
||||
v := L.Get(1)
|
||||
if s, ok := v.(lua.LString); ok {
|
||||
date, err := time.ParseInLocation(timeFormat, s.String(), time.Local)
|
||||
if err != nil {
|
||||
L.ArgError(1, "error parsing date string: "+err.Error())
|
||||
return 0
|
||||
}
|
||||
L.Push(TimeToLua(L, &date))
|
||||
return 1
|
||||
}
|
||||
var year, month, day int
|
||||
if t, ok := v.(*lua.LTable); ok {
|
||||
year = luaWeakCheckTableFieldInt(L, t, 1, "year", 0)
|
||||
month = luaWeakCheckTableFieldInt(L, t, 1, "month", 1)
|
||||
day = luaWeakCheckTableFieldInt(L, t, 1, "day", 1)
|
||||
} else {
|
||||
year = L.CheckInt(1)
|
||||
month = L.CheckInt(2)
|
||||
day = L.CheckInt(3)
|
||||
}
|
||||
date := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local)
|
||||
L.Push(TimeToLua(L, &date))
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaDateNow(L *lua.LState) int {
|
||||
now := time.Now()
|
||||
date := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
|
||||
L.Push(TimeToLua(L, &date))
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaDate__index(L *lua.LState) int {
|
||||
d := luaCheckTime(L, 1)
|
||||
field := L.CheckString(2)
|
||||
|
||||
switch field {
|
||||
case "Year", "year":
|
||||
L.Push(lua.LNumber(d.Year()))
|
||||
case "Month", "month":
|
||||
L.Push(lua.LNumber(float64(d.Month())))
|
||||
case "Day", "day":
|
||||
L.Push(lua.LNumber(float64(d.Day())))
|
||||
default:
|
||||
L.ArgError(2, "unexpected date attribute: "+field)
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaDate__tostring(L *lua.LState) int {
|
||||
a := luaCheckTime(L, 1)
|
||||
|
||||
L.Push(lua.LString(a.Format(timeFormat)))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaDate__eq(L *lua.LState) int {
|
||||
a := luaCheckTime(L, 1)
|
||||
b := luaCheckTime(L, 2)
|
||||
|
||||
L.Push(lua.LBool(a.Equal(*b)))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaDate__lt(L *lua.LState) int {
|
||||
a := luaCheckTime(L, 1)
|
||||
b := luaCheckTime(L, 2)
|
||||
|
||||
L.Push(lua.LBool(a.Before(*b)))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaDate__le(L *lua.LState) int {
|
||||
a := luaCheckTime(L, 1)
|
||||
b := luaCheckTime(L, 2)
|
||||
|
||||
L.Push(lua.LBool(a.Equal(*b) || a.Before(*b)))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaDate__add(L *lua.LState) int {
|
||||
a := luaCheckTime(L, 1)
|
||||
b := luaCheckTime(L, 2)
|
||||
|
||||
date := a.AddDate(b.Year(), int(b.Month()), b.Day())
|
||||
L.Push(TimeToLua(L, &date))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaDate__sub(L *lua.LState) int {
|
||||
a := luaCheckTime(L, 1)
|
||||
b := luaCheckTime(L, 2)
|
||||
|
||||
date := a.AddDate(-b.Year(), -int(b.Month()), -b.Day())
|
||||
L.Push(TimeToLua(L, &date))
|
||||
|
||||
return 1
|
||||
}
|
@ -5,7 +5,6 @@ import (
|
||||
"github.com/aclindsa/moneygo/internal/store"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func CreatePriceIfNotExist(tx store.Tx, price *models.Price) error {
|
||||
@ -33,27 +32,6 @@ func CreatePriceIfNotExist(tx store.Tx, price *models.Price) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return the price for security in currency closest to date
|
||||
func GetClosestPrice(tx store.Tx, security, currency *models.Security, date *time.Time) (*models.Price, error) {
|
||||
earliest, _ := tx.GetEarliestPrice(security, currency, date)
|
||||
latest, err := tx.GetLatestPrice(security, currency, date)
|
||||
|
||||
// Return early if either earliest or latest are invalid
|
||||
if earliest == nil {
|
||||
return latest, err
|
||||
} else if err != nil {
|
||||
return earliest, nil
|
||||
}
|
||||
|
||||
howlate := earliest.Date.Sub(*date)
|
||||
howearly := date.Sub(latest.Date)
|
||||
if howearly < howlate {
|
||||
return latest, nil
|
||||
} else {
|
||||
return earliest, nil
|
||||
}
|
||||
}
|
||||
|
||||
func PriceHandler(r *http.Request, context *Context, user *models.User, securityid int64) ResponseWriterWriter {
|
||||
security, err := context.Tx.GetSecurity(securityid, user.UserId)
|
||||
if err != nil {
|
||||
|
@ -1,92 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/aclindsa/moneygo/internal/models"
|
||||
"github.com/yuin/gopher-lua"
|
||||
)
|
||||
|
||||
const luaPriceTypeName = "price"
|
||||
|
||||
func luaRegisterPrices(L *lua.LState) {
|
||||
mt := L.NewTypeMetatable(luaPriceTypeName)
|
||||
L.SetGlobal("price", mt)
|
||||
L.SetField(mt, "__index", L.NewFunction(luaPrice__index))
|
||||
L.SetField(mt, "__tostring", L.NewFunction(luaPrice__tostring))
|
||||
L.SetField(mt, "__metatable", lua.LString("protected"))
|
||||
}
|
||||
|
||||
func PriceToLua(L *lua.LState, price *models.Price) *lua.LUserData {
|
||||
ud := L.NewUserData()
|
||||
ud.Value = price
|
||||
L.SetMetatable(ud, L.GetTypeMetatable(luaPriceTypeName))
|
||||
return ud
|
||||
}
|
||||
|
||||
// Checks whether the first lua argument is a *LUserData with *Price and returns this *Price.
|
||||
func luaCheckPrice(L *lua.LState, n int) *models.Price {
|
||||
ud := L.CheckUserData(n)
|
||||
if price, ok := ud.Value.(*models.Price); ok {
|
||||
return price
|
||||
}
|
||||
L.ArgError(n, "price expected")
|
||||
return nil
|
||||
}
|
||||
|
||||
func luaPrice__index(L *lua.LState) int {
|
||||
p := luaCheckPrice(L, 1)
|
||||
field := L.CheckString(2)
|
||||
|
||||
switch field {
|
||||
case "PriceId", "priceid":
|
||||
L.Push(lua.LNumber(float64(p.PriceId)))
|
||||
case "Security", "security":
|
||||
security_map, err := luaContextGetSecurities(L)
|
||||
if err != nil {
|
||||
panic("luaContextGetSecurities couldn't fetch securities")
|
||||
}
|
||||
s, ok := security_map[p.SecurityId]
|
||||
if !ok {
|
||||
panic("Price's security not found for user")
|
||||
}
|
||||
L.Push(SecurityToLua(L, s))
|
||||
case "Currency", "currency":
|
||||
security_map, err := luaContextGetSecurities(L)
|
||||
if err != nil {
|
||||
panic("luaContextGetSecurities couldn't fetch securities")
|
||||
}
|
||||
c, ok := security_map[p.CurrencyId]
|
||||
if !ok {
|
||||
panic("Price's currency not found for user")
|
||||
}
|
||||
L.Push(SecurityToLua(L, c))
|
||||
case "Value", "value":
|
||||
amt, err := models.GetBigAmount(p.Value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
float, _ := amt.Float64()
|
||||
L.Push(lua.LNumber(float))
|
||||
default:
|
||||
L.ArgError(2, "unexpected price attribute: "+field)
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaPrice__tostring(L *lua.LState) int {
|
||||
p := luaCheckPrice(L, 1)
|
||||
|
||||
security_map, err := luaContextGetSecurities(L)
|
||||
if err != nil {
|
||||
panic("luaContextGetSecurities couldn't fetch securities")
|
||||
}
|
||||
s, ok1 := security_map[p.SecurityId]
|
||||
c, ok2 := security_map[p.CurrencyId]
|
||||
if !ok1 || !ok2 {
|
||||
panic("Price's currency or security not found for user")
|
||||
}
|
||||
|
||||
L.Push(lua.LString(p.Value + " " + c.Symbol + " (" + s.Symbol + ")"))
|
||||
|
||||
return 1
|
||||
}
|
@ -1,104 +1,23 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/aclindsa/moneygo/internal/models"
|
||||
"github.com/aclindsa/moneygo/internal/reports"
|
||||
"github.com/aclindsa/moneygo/internal/store"
|
||||
"github.com/yuin/gopher-lua"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
//type and value to store user in lua's Context
|
||||
type key int
|
||||
|
||||
const (
|
||||
userContextKey key = iota
|
||||
accountsContextKey
|
||||
securitiesContextKey
|
||||
balanceContextKey
|
||||
dbContextKey
|
||||
)
|
||||
|
||||
const luaTimeoutSeconds time.Duration = 30 // maximum time a lua request can run for
|
||||
|
||||
func runReport(tx store.Tx, user *models.User, report *models.Report) (*models.Tabulation, error) {
|
||||
// Create a new LState without opening the default libs for security
|
||||
L := lua.NewState(lua.Options{SkipOpenLibs: true})
|
||||
defer L.Close()
|
||||
|
||||
// Create a new context holding the current user with a timeout
|
||||
ctx := context.WithValue(context.Background(), userContextKey, user)
|
||||
ctx = context.WithValue(ctx, dbContextKey, tx)
|
||||
ctx, cancel := context.WithTimeout(ctx, luaTimeoutSeconds*time.Second)
|
||||
defer cancel()
|
||||
L.SetContext(ctx)
|
||||
|
||||
for _, pair := range []struct {
|
||||
n string
|
||||
f lua.LGFunction
|
||||
}{
|
||||
{lua.LoadLibName, lua.OpenPackage}, // Must be first
|
||||
{lua.BaseLibName, lua.OpenBase},
|
||||
{lua.TabLibName, lua.OpenTable},
|
||||
{lua.StringLibName, lua.OpenString},
|
||||
{lua.MathLibName, lua.OpenMath},
|
||||
} {
|
||||
if err := L.CallByParam(lua.P{
|
||||
Fn: L.NewFunction(pair.f),
|
||||
NRet: 0,
|
||||
Protect: true,
|
||||
}, lua.LString(pair.n)); err != nil {
|
||||
return nil, errors.New("Error initializing Lua packages")
|
||||
}
|
||||
}
|
||||
|
||||
luaRegisterAccounts(L)
|
||||
luaRegisterSecurities(L)
|
||||
luaRegisterBalances(L)
|
||||
luaRegisterDates(L)
|
||||
luaRegisterTabulations(L)
|
||||
luaRegisterPrices(L)
|
||||
|
||||
err := L.DoString(report.Lua)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := L.CallByParam(lua.P{
|
||||
Fn: L.GetGlobal("generate"),
|
||||
NRet: 1,
|
||||
Protect: true,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value := L.Get(-1)
|
||||
if ud, ok := value.(*lua.LUserData); ok {
|
||||
if tabulation, ok := ud.Value.(*models.Tabulation); ok {
|
||||
return tabulation, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("generate() for %s (Id: %d) didn't return a tabulation", report.Name, report.ReportId)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("generate() for %s (Id: %d) didn't even return LUserData", report.Name, report.ReportId)
|
||||
}
|
||||
}
|
||||
|
||||
func ReportTabulationHandler(tx store.Tx, r *http.Request, user *models.User, reportid int64) ResponseWriterWriter {
|
||||
report, err := tx.GetReport(reportid, user.UserId)
|
||||
if err != nil {
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
|
||||
tabulation, err := runReport(tx, user, report)
|
||||
tabulation, err := reports.RunReport(tx, user, report)
|
||||
if err != nil {
|
||||
// TODO handle different failure cases differently
|
||||
log.Print("runReport returned:", err)
|
||||
log.Print("reports.RunReport returned:", err)
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
|
||||
|
@ -1,188 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/aclindsa/moneygo/internal/models"
|
||||
"github.com/yuin/gopher-lua"
|
||||
)
|
||||
|
||||
const luaTabulationTypeName = "tabulation"
|
||||
const luaSeriesTypeName = "series"
|
||||
|
||||
func luaRegisterTabulations(L *lua.LState) {
|
||||
mtr := L.NewTypeMetatable(luaTabulationTypeName)
|
||||
L.SetGlobal("tabulation", mtr)
|
||||
L.SetField(mtr, "new", L.NewFunction(luaTabulationNew))
|
||||
L.SetField(mtr, "__index", L.NewFunction(luaTabulation__index))
|
||||
L.SetField(mtr, "__metatable", lua.LString("protected"))
|
||||
|
||||
mts := L.NewTypeMetatable(luaSeriesTypeName)
|
||||
L.SetGlobal("series", mts)
|
||||
L.SetField(mts, "__index", L.NewFunction(luaSeries__index))
|
||||
L.SetField(mts, "__metatable", lua.LString("protected"))
|
||||
}
|
||||
|
||||
// Checks whether the first lua argument is a *LUserData with *Tabulation and returns *Tabulation
|
||||
func luaCheckTabulation(L *lua.LState, n int) *models.Tabulation {
|
||||
ud := L.CheckUserData(n)
|
||||
if tabulation, ok := ud.Value.(*models.Tabulation); ok {
|
||||
return tabulation
|
||||
}
|
||||
L.ArgError(n, "tabulation expected")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Checks whether the first lua argument is a *LUserData with *Series and returns *Series
|
||||
func luaCheckSeries(L *lua.LState, n int) *models.Series {
|
||||
ud := L.CheckUserData(n)
|
||||
if series, ok := ud.Value.(*models.Series); ok {
|
||||
return series
|
||||
}
|
||||
L.ArgError(n, "series expected")
|
||||
return nil
|
||||
}
|
||||
|
||||
func luaTabulationNew(L *lua.LState) int {
|
||||
numvalues := L.CheckInt(1)
|
||||
ud := L.NewUserData()
|
||||
ud.Value = &models.Tabulation{
|
||||
Labels: make([]string, numvalues),
|
||||
Series: make(map[string]*models.Series),
|
||||
}
|
||||
L.SetMetatable(ud, L.GetTypeMetatable(luaTabulationTypeName))
|
||||
L.Push(ud)
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaTabulation__index(L *lua.LState) int {
|
||||
field := L.CheckString(2)
|
||||
|
||||
switch field {
|
||||
case "Label", "label":
|
||||
L.Push(L.NewFunction(luaTabulationLabel))
|
||||
case "Series", "series":
|
||||
L.Push(L.NewFunction(luaTabulationSeries))
|
||||
case "Title", "title":
|
||||
L.Push(L.NewFunction(luaTabulationTitle))
|
||||
case "Subtitle", "subtitle":
|
||||
L.Push(L.NewFunction(luaTabulationSubtitle))
|
||||
case "Units", "units":
|
||||
L.Push(L.NewFunction(luaTabulationUnits))
|
||||
default:
|
||||
L.ArgError(2, "unexpected tabulation attribute: "+field)
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaTabulationLabel(L *lua.LState) int {
|
||||
tabulation := luaCheckTabulation(L, 1)
|
||||
labelnumber := L.CheckInt(2)
|
||||
label := L.CheckString(3)
|
||||
|
||||
if labelnumber > cap(tabulation.Labels) || labelnumber < 1 {
|
||||
L.ArgError(2, "Label index must be between 1 and the number of data points, inclusive")
|
||||
}
|
||||
tabulation.Labels[labelnumber-1] = label
|
||||
return 0
|
||||
}
|
||||
|
||||
func luaTabulationSeries(L *lua.LState) int {
|
||||
tabulation := luaCheckTabulation(L, 1)
|
||||
name := L.CheckString(2)
|
||||
ud := L.NewUserData()
|
||||
|
||||
s, ok := tabulation.Series[name]
|
||||
if ok {
|
||||
ud.Value = s
|
||||
} else {
|
||||
tabulation.Series[name] = &models.Series{
|
||||
Series: make(map[string]*models.Series),
|
||||
Values: make([]float64, cap(tabulation.Labels)),
|
||||
}
|
||||
ud.Value = tabulation.Series[name]
|
||||
}
|
||||
L.SetMetatable(ud, L.GetTypeMetatable(luaSeriesTypeName))
|
||||
L.Push(ud)
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaTabulationTitle(L *lua.LState) int {
|
||||
tabulation := luaCheckTabulation(L, 1)
|
||||
|
||||
if L.GetTop() == 2 {
|
||||
tabulation.Title = L.CheckString(2)
|
||||
return 0
|
||||
}
|
||||
L.Push(lua.LString(tabulation.Title))
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaTabulationSubtitle(L *lua.LState) int {
|
||||
tabulation := luaCheckTabulation(L, 1)
|
||||
|
||||
if L.GetTop() == 2 {
|
||||
tabulation.Subtitle = L.CheckString(2)
|
||||
return 0
|
||||
}
|
||||
L.Push(lua.LString(tabulation.Subtitle))
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaTabulationUnits(L *lua.LState) int {
|
||||
tabulation := luaCheckTabulation(L, 1)
|
||||
|
||||
if L.GetTop() == 2 {
|
||||
tabulation.Units = L.CheckString(2)
|
||||
return 0
|
||||
}
|
||||
L.Push(lua.LString(tabulation.Units))
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaSeries__index(L *lua.LState) int {
|
||||
field := L.CheckString(2)
|
||||
|
||||
switch field {
|
||||
case "Value", "value":
|
||||
L.Push(L.NewFunction(luaSeriesValue))
|
||||
case "Series", "series":
|
||||
L.Push(L.NewFunction(luaSeriesSeries))
|
||||
default:
|
||||
L.ArgError(2, "unexpected series attribute: "+field)
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaSeriesValue(L *lua.LState) int {
|
||||
series := luaCheckSeries(L, 1)
|
||||
valuenumber := L.CheckInt(2)
|
||||
value := float64(L.CheckNumber(3))
|
||||
|
||||
if valuenumber > cap(series.Values) || valuenumber < 1 {
|
||||
L.ArgError(2, "value index must be between 1 and the number of data points, inclusive")
|
||||
}
|
||||
series.Values[valuenumber-1] = value
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func luaSeriesSeries(L *lua.LState) int {
|
||||
parent := luaCheckSeries(L, 1)
|
||||
name := L.CheckString(2)
|
||||
ud := L.NewUserData()
|
||||
|
||||
s, ok := parent.Series[name]
|
||||
if ok {
|
||||
ud.Value = s
|
||||
} else {
|
||||
parent.Series[name] = &models.Series{
|
||||
Series: make(map[string]*models.Series),
|
||||
Values: make([]float64, cap(parent.Values)),
|
||||
}
|
||||
ud.Value = parent.Series[name]
|
||||
}
|
||||
L.SetMetatable(ud, L.GetTypeMetatable(luaSeriesTypeName))
|
||||
L.Push(ud)
|
||||
return 1
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/aclindsa/moneygo/internal/models"
|
||||
"github.com/aclindsa/moneygo/internal/store"
|
||||
"github.com/yuin/gopher-lua"
|
||||
)
|
||||
|
||||
const luaSecurityTypeName = "security"
|
||||
|
||||
func luaContextGetSecurities(L *lua.LState) (map[int64]*models.Security, error) {
|
||||
var security_map map[int64]*models.Security
|
||||
|
||||
ctx := L.Context()
|
||||
|
||||
tx, ok := ctx.Value(dbContextKey).(store.Tx)
|
||||
if !ok {
|
||||
return nil, errors.New("Couldn't find tx in lua's Context")
|
||||
}
|
||||
|
||||
security_map, ok = ctx.Value(securitiesContextKey).(map[int64]*models.Security)
|
||||
if !ok {
|
||||
user, ok := ctx.Value(userContextKey).(*models.User)
|
||||
if !ok {
|
||||
return nil, errors.New("Couldn't find User in lua's Context")
|
||||
}
|
||||
|
||||
securities, err := tx.GetSecurities(user.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
security_map = make(map[int64]*models.Security)
|
||||
for i := range *securities {
|
||||
security_map[(*securities)[i].SecurityId] = (*securities)[i]
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, securitiesContextKey, security_map)
|
||||
L.SetContext(ctx)
|
||||
}
|
||||
|
||||
return security_map, nil
|
||||
}
|
||||
|
||||
func luaContextGetDefaultCurrency(L *lua.LState) (*models.Security, error) {
|
||||
security_map, err := luaContextGetSecurities(L)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := L.Context()
|
||||
|
||||
user, ok := ctx.Value(userContextKey).(*models.User)
|
||||
if !ok {
|
||||
return nil, errors.New("Couldn't find User in lua's Context")
|
||||
}
|
||||
|
||||
if security, ok := security_map[user.DefaultCurrency]; ok {
|
||||
return security, nil
|
||||
} else {
|
||||
return nil, errors.New("DefaultCurrency not in lua security_map")
|
||||
}
|
||||
}
|
||||
|
||||
func luaGetDefaultCurrency(L *lua.LState) int {
|
||||
defcurrency, err := luaContextGetDefaultCurrency(L)
|
||||
if err != nil {
|
||||
panic("luaGetDefaultCurrency couldn't fetch default currency")
|
||||
}
|
||||
|
||||
L.Push(SecurityToLua(L, defcurrency))
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaGetSecurities(L *lua.LState) int {
|
||||
security_map, err := luaContextGetSecurities(L)
|
||||
if err != nil {
|
||||
panic("luaGetSecurities couldn't fetch securities")
|
||||
}
|
||||
|
||||
table := L.NewTable()
|
||||
|
||||
for securityid := range security_map {
|
||||
table.RawSetInt(int(securityid), SecurityToLua(L, security_map[securityid]))
|
||||
}
|
||||
|
||||
L.Push(table)
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaRegisterSecurities(L *lua.LState) {
|
||||
mt := L.NewTypeMetatable(luaSecurityTypeName)
|
||||
L.SetGlobal("security", mt)
|
||||
L.SetField(mt, "__index", L.NewFunction(luaSecurity__index))
|
||||
L.SetField(mt, "__tostring", L.NewFunction(luaSecurity__tostring))
|
||||
L.SetField(mt, "__eq", L.NewFunction(luaSecurity__eq))
|
||||
L.SetField(mt, "__metatable", lua.LString("protected"))
|
||||
getSecuritiesFn := L.NewFunction(luaGetSecurities)
|
||||
L.SetField(mt, "get_all", getSecuritiesFn)
|
||||
getDefaultCurrencyFn := L.NewFunction(luaGetDefaultCurrency)
|
||||
L.SetField(mt, "get_default", getDefaultCurrencyFn)
|
||||
|
||||
// also register the get_securities and get_default functions as globals in
|
||||
// their own right
|
||||
L.SetGlobal("get_securities", getSecuritiesFn)
|
||||
L.SetGlobal("get_default_currency", getDefaultCurrencyFn)
|
||||
}
|
||||
|
||||
func SecurityToLua(L *lua.LState, security *models.Security) *lua.LUserData {
|
||||
ud := L.NewUserData()
|
||||
ud.Value = security
|
||||
L.SetMetatable(ud, L.GetTypeMetatable(luaSecurityTypeName))
|
||||
return ud
|
||||
}
|
||||
|
||||
// Checks whether the first lua argument is a *LUserData with *Security and returns this *Security.
|
||||
func luaCheckSecurity(L *lua.LState, n int) *models.Security {
|
||||
ud := L.CheckUserData(n)
|
||||
if security, ok := ud.Value.(*models.Security); ok {
|
||||
return security
|
||||
}
|
||||
L.ArgError(n, "security expected")
|
||||
return nil
|
||||
}
|
||||
|
||||
func luaSecurity__index(L *lua.LState) int {
|
||||
a := luaCheckSecurity(L, 1)
|
||||
field := L.CheckString(2)
|
||||
|
||||
switch field {
|
||||
case "SecurityId", "securityid":
|
||||
L.Push(lua.LNumber(float64(a.SecurityId)))
|
||||
case "Name", "name":
|
||||
L.Push(lua.LString(a.Name))
|
||||
case "Description", "description":
|
||||
L.Push(lua.LString(a.Description))
|
||||
case "Symbol", "symbol":
|
||||
L.Push(lua.LString(a.Symbol))
|
||||
case "Precision", "precision":
|
||||
L.Push(lua.LNumber(float64(a.Precision)))
|
||||
case "Type", "type":
|
||||
L.Push(lua.LNumber(float64(a.Type)))
|
||||
case "ClosestPrice", "closestprice":
|
||||
L.Push(L.NewFunction(luaClosestPrice))
|
||||
case "AlternateId", "alternateid":
|
||||
L.Push(lua.LString(a.AlternateId))
|
||||
default:
|
||||
L.ArgError(2, "unexpected security attribute: "+field)
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaClosestPrice(L *lua.LState) int {
|
||||
s := luaCheckSecurity(L, 1)
|
||||
c := luaCheckSecurity(L, 2)
|
||||
date := luaCheckTime(L, 3)
|
||||
|
||||
ctx := L.Context()
|
||||
tx, ok := ctx.Value(dbContextKey).(store.Tx)
|
||||
if !ok {
|
||||
panic("Couldn't find tx in lua's Context")
|
||||
}
|
||||
|
||||
p, err := GetClosestPrice(tx, s, c, date)
|
||||
if err != nil {
|
||||
L.Push(lua.LNil)
|
||||
} else {
|
||||
L.Push(PriceToLua(L, p))
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaSecurity__tostring(L *lua.LState) int {
|
||||
s := luaCheckSecurity(L, 1)
|
||||
|
||||
L.Push(lua.LString(s.Name + " - " + s.Description + " (" + s.Symbol + ")"))
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
func luaSecurity__eq(L *lua.LState) int {
|
||||
a := luaCheckSecurity(L, 1)
|
||||
b := luaCheckSecurity(L, 2)
|
||||
|
||||
L.Push(lua.LBool(a.SecurityId == b.SecurityId))
|
||||
|
||||
return 1
|
||||
}
|
@ -182,20 +182,6 @@ func TransactionHandler(r *http.Request, context *Context) ResponseWriterWriter
|
||||
return NewError(3 /*Invalid Request*/)
|
||||
}
|
||||
|
||||
func BalanceFromSplits(splits *[]*models.Split) (*big.Rat, error) {
|
||||
var balance, tmp big.Rat
|
||||
for _, s := range *splits {
|
||||
rat_amount, err := models.GetBigAmount(s.Amount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tmp.Add(&balance, rat_amount)
|
||||
balance.Set(&tmp)
|
||||
}
|
||||
|
||||
return &balance, nil
|
||||
}
|
||||
|
||||
// Return only those transactions which have at least one split pertaining to
|
||||
// an account
|
||||
func AccountTransactionsHandler(context *Context, r *http.Request, user *models.User, accountid int64) ResponseWriterWriter {
|
||||
|
Reference in New Issue
Block a user