parent
4551a854bf
commit
798154bdf0
2
go.mod
2
go.mod
|
@ -47,6 +47,7 @@ require (
|
|||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kelindar/binary v1.0.18 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
||||
github.com/labstack/echo/v4 v4.11.1 // indirect
|
||||
github.com/labstack/gommon v0.4.0 // indirect
|
||||
|
@ -57,6 +58,7 @@ require (
|
|||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.19 // indirect
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
|
|
4
go.sum
4
go.sum
|
@ -97,6 +97,8 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
|||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kelindar/binary v1.0.18 h1:xGKmvb6Q3bpvvhKULfB0rG5vLOjtpoZn/FgPpnTl+aI=
|
||||
github.com/kelindar/binary v1.0.18/go.mod h1:/twdz8gRLNMffx0U4UOgqm1LywPs6nd9YK2TX52MDh8=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
|
@ -136,6 +138,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
|||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
|
||||
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/kelindar/binary"
|
||||
"github.com/mitchellh/hashstructure/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
HashKey string `json:"hash_key"`
|
||||
Value []byte `gorm:"type:BLOB" json:"item"`
|
||||
}
|
||||
|
||||
func DeleteExpired(db *gorm.DB) error {
|
||||
err := db.Exec("DELETE FROM caches WHERE expires_at < ?", time.Now()).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Lookup[I any, K any](db *gorm.DB, key K, fallback func() I) I {
|
||||
var item I
|
||||
var cache Cache
|
||||
|
||||
hash, err := hashstructure.Hash(key, hashstructure.FormatV2, nil)
|
||||
hashKey := fmt.Sprintf("%d", hash)
|
||||
if err == nil {
|
||||
err := db.Where("hash_key = ?", hashKey).First(&cache).Error
|
||||
if err == nil {
|
||||
if time.Now().Before(cache.ExpiresAt) {
|
||||
err := binary.Unmarshal(cache.Value, &item)
|
||||
if err == nil {
|
||||
return item
|
||||
}
|
||||
} else {
|
||||
DeleteExpired(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item = fallback()
|
||||
bytes, err := binary.Marshal(item)
|
||||
if err == nil {
|
||||
cache = Cache{
|
||||
ExpiresAt: time.Now().Add(24 * time.Hour),
|
||||
HashKey: hashKey,
|
||||
Value: bytes,
|
||||
}
|
||||
db.Save(&cache)
|
||||
}
|
||||
return item
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/ananthakumaran/paisa/internal/config"
|
||||
"github.com/ananthakumaran/paisa/internal/ledger"
|
||||
"github.com/ananthakumaran/paisa/internal/model/cache"
|
||||
"github.com/ananthakumaran/paisa/internal/model/cii"
|
||||
"github.com/ananthakumaran/paisa/internal/model/commodity"
|
||||
mutualfundModel "github.com/ananthakumaran/paisa/internal/model/mutualfund/scheme"
|
||||
|
@ -29,6 +30,7 @@ func AutoMigrate(db *gorm.DB) {
|
|||
db.AutoMigrate(&portfolio.Portfolio{})
|
||||
db.AutoMigrate(&price.Price{})
|
||||
db.AutoMigrate(&cii.CII{})
|
||||
db.AutoMigrate(&cache.Cache{})
|
||||
}
|
||||
|
||||
func SyncJournal(db *gorm.DB) (string, error) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"github.com/ananthakumaran/paisa/internal/model/cache"
|
||||
"github.com/ananthakumaran/paisa/internal/model/posting"
|
||||
"github.com/ananthakumaran/paisa/internal/utils"
|
||||
"github.com/ananthakumaran/paisa/internal/xirr"
|
||||
|
@ -26,7 +27,9 @@ func XIRR(db *gorm.DB, ps []posting.Posting) decimal.Decimal {
|
|||
}))
|
||||
|
||||
cashflows = append(cashflows, xirr.Cashflow{Date: today, Amount: marketAmount.Round(4).InexactFloat64()})
|
||||
return xirr.XIRR(cashflows)
|
||||
return cache.Lookup(db, cashflows, func() decimal.Decimal {
|
||||
return xirr.XIRR(cashflows)
|
||||
})
|
||||
}
|
||||
|
||||
func APR(db *gorm.DB, ps []posting.Posting) decimal.Decimal {
|
||||
|
@ -38,5 +41,8 @@ func APR(db *gorm.DB, ps []posting.Posting) decimal.Decimal {
|
|||
return xirr.Cashflow{Date: p.Date, Amount: p.Amount.Round(4).InexactFloat64()}
|
||||
})
|
||||
cashflows = append(cashflows, xirr.Cashflow{Date: today, Amount: marketAmount.Neg().Round(4).InexactFloat64()})
|
||||
return xirr.XIRR(cashflows)
|
||||
|
||||
return cache.Lookup(db, cashflows, func() decimal.Decimal {
|
||||
return xirr.XIRR(cashflows)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@
|
|||
<div class="tile is-child">
|
||||
<div class="content">
|
||||
<p class="subtitle">
|
||||
<a class="secondary-link" href="/assets/networth">Assets</a>
|
||||
<a class="secondary-link has-text-grey" href="/assets/networth">Assets</a>
|
||||
</p>
|
||||
<div class="content">
|
||||
<div>
|
||||
|
@ -201,7 +201,8 @@
|
|||
<article class="tile is-child">
|
||||
<div class="content">
|
||||
<p class="subtitle">
|
||||
<a class="secondary-link" href="/assets/balance">Checking Balance</a>
|
||||
<a class="secondary-link has-text-grey" href="/assets/balance">Checking Balance</a
|
||||
>
|
||||
</p>
|
||||
<div class="content">
|
||||
<UntypedMasonryGrid gap={10} maxStretchColumnSize={400} align="stretch">
|
||||
|
@ -220,7 +221,7 @@
|
|||
<div class="tile is-parent">
|
||||
<article class="tile is-child min-w-0">
|
||||
<p class="subtitle">
|
||||
<a class="secondary-link" href="/cash_flow/monthly">Cash Flow</a>
|
||||
<a class="secondary-link has-text-grey" href="/cash_flow/monthly">Cash Flow</a>
|
||||
</p>
|
||||
<div class="content box px-2 pb-0">
|
||||
<ZeroState item={cashFlows}>
|
||||
|
@ -243,7 +244,7 @@
|
|||
<div class="tile is-child">
|
||||
<div class="content">
|
||||
<p class="subtitle">
|
||||
<a class="secondary-link" href="/expense/budget">Budget</a>
|
||||
<a class="secondary-link has-text-grey" href="/expense/budget">Budget</a>
|
||||
</p>
|
||||
<div class="content">
|
||||
<div>
|
||||
|
@ -262,7 +263,7 @@
|
|||
<article class="tile is-child">
|
||||
<div class="content">
|
||||
<p class="subtitle">
|
||||
<a class="secondary-link" href="/more/goals">Goals</a>
|
||||
<a class="secondary-link has-text-grey" href="/more/goals">Goals</a>
|
||||
</p>
|
||||
<div class="content">
|
||||
{#each goalSummaries as goal}
|
||||
|
@ -280,7 +281,7 @@
|
|||
<article class="tile is-child">
|
||||
<p class="subtitle is-flex is-justify-content-space-between is-align-items-end">
|
||||
<span
|
||||
><a class="secondary-link" href="/expense/monthly">Expenses</a>
|
||||
><a class="secondary-link has-text-grey" href="/expense/monthly">Expenses</a>
|
||||
<span class="is-size-5 has-text-weight-bold px-2" style="color: {COLORS.expenses}"
|
||||
>{formatCurrency(totalExpense)}</span
|
||||
></span
|
||||
|
@ -301,7 +302,8 @@
|
|||
<article class="tile is-child">
|
||||
<div class="content">
|
||||
<p class="subtitle">
|
||||
<a class="secondary-link" href="/cash_flow/recurring">Recurring</a>
|
||||
<a class="secondary-link has-text-grey" href="/cash_flow/recurring">Recurring</a
|
||||
>
|
||||
</p>
|
||||
<div class="content box">
|
||||
<div
|
||||
|
@ -324,7 +326,9 @@
|
|||
<article class="tile is-child">
|
||||
<div class="content">
|
||||
<p class="subtitle">
|
||||
<a class="secondary-link" href="/ledger/transaction">Recent Transactions</a>
|
||||
<a class="secondary-link has-text-grey" href="/ledger/transaction"
|
||||
>Recent Transactions</a
|
||||
>
|
||||
</p>
|
||||
<div>
|
||||
<UntypedMasonryGrid gap={10} maxStretchColumnSize={500} align="stretch">
|
||||
|
@ -349,4 +353,9 @@
|
|||
p.subtitle {
|
||||
margin-bottom: 0.5rem !important;
|
||||
}
|
||||
|
||||
p.subtitle a.secondary-link {
|
||||
text-transform: uppercase;
|
||||
font-size: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue