Skip to content

Commit

Permalink
stats function
Browse files Browse the repository at this point in the history
  • Loading branch information
bernardini687 committed Aug 6, 2021
1 parent e29a775 commit 36f54c5
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 91 deletions.
216 changes: 127 additions & 89 deletions kakebo.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,18 @@ func FormatEntries(entryData string) (string, error) {
return strings.Join(formattedEntries, ""), nil
}

// CalcBalance
//
// Input example:
//
// -120 y foo
// -34.5 m bar
// -6 M baz
// 789 Y xyzzy
//
// Output example:
//
// 15.25
//
func CalcBalance(dueData string) (decimal.Decimal, error) {
return sumValues(dueData, extractDueValue)
func formatEntry(fields []string) (string, error) {
if len(fields) < 2 {
return "", fmt.Errorf("at least 2 fields required")
}

s := strings.Replace(fields[0], ",", ".", 1)
amount, err := decimal.NewFromString(s)
if err != nil {
return "", err
}

return fmt.Sprintf("%s\t%s\n", strings.Title(fields[1]), money(amount)), nil
}

// CalcMonth
Expand All @@ -73,6 +70,57 @@ func CalcMonth(monthData string) (decimal.Decimal, error) {
return sumValues(monthData, extractFormattedEntryValue)
}

func extractFormattedEntryValue(fields []string) (decimal.Decimal, error) {
return decimal.NewFromString(fields[1])
}

// CalcBalance
//
// Input example:
//
// -120 y foo
// -34.5 m bar
// -6 M baz
// 789 Y xyzzy
//
// Output example:
//
// 15.25
//
func CalcBalance(dueData string) (decimal.Decimal, error) {
return sumValues(dueData, extractDueValue)
}

const (
monthly int64 = 1
yearly int64 = 12
)

var intervalDictionary = map[string]int64{
"m": monthly,
"M": monthly,
"y": yearly,
"Y": yearly,
}

func extractDueValue(fields []string) (decimal.Decimal, error) {
if len(fields) < 2 {
return decimal.Decimal{}, fmt.Errorf("at least 2 fields required")
}

amount, err := decimal.NewFromString(fields[0])
if err != nil {
return decimal.Decimal{}, err
}

interval, ok := intervalDictionary[fields[1]]
if !ok {
return decimal.Decimal{}, fmt.Errorf("unknown interval '%s'", fields[1])
}

return amount.Div(decimal.NewFromInt(interval)), nil
}

// DisplayMonth
//
// Input example:
Expand All @@ -85,116 +133,106 @@ func CalcMonth(monthData string) (decimal.Decimal, error) {
//
// January 2020
//
// Foo 1,2
// Foo 1,20
// Bar 3,45
// Baz 6,00
//
// Tot 10,65
//
func DisplayMonth(period time.Time, monthData string, monthTot decimal.Decimal) string {
func DisplayMonth(date time.Time, monthData string, monthTot decimal.Decimal) string {
var lines []string

lines = append(lines, fmt.Sprintln(period.Month(), period.Year())) // header
lines = append(lines, monthData) // body
lines = append(lines, fmt.Sprintf("Tot\t%s\n", monthTot.StringFixed(2))) // footer
lines = append(lines, fmt.Sprintln(date.Month(), date.Year())) // header
lines = append(lines, monthData) // body
lines = append(lines, fmt.Sprintf("Tot\t%s\n", money(monthTot))) // footer

display := strings.Join(lines, "\n")
text := strings.Join(lines, "\n")

return strings.ReplaceAll(display, ".", ",")
return replaceDotsWithCommas(text)
}

// DisplayStats
//
// Input example:
//
// TODO: proper input example
// CalcBalance(), CalcMonth(), 35
// time.Time{2009/11/10}, decimal.Decimal{1000}, decimal.Decimal{100}, 10
//
// Output example:
//
// Save goal 100,00
// 10 November 2009
//
// Monthly budget 500,00
// Daily budget 5,00
// Save goal 100,00
// Monthly budget 900,00
// Daily budget 30,00
//
// End of month 70%
// Amount spent 90%
// End of month 33%
// Amount spent 11%
//
func DisplayStats(balance, monthTot decimal.Decimal, savePercentage int) (string, error) {
// check savePercentage

// saveGoal := balance / 100 * savePercentage

// monthlyBudget := balance - saveGoal

// now := time.Now()

// monthDays := ?

// dailyBudget := monthlyBudget / monthDays
func DisplayStats(date time.Time, balance, monthTot decimal.Decimal, savePercentage int) string {
var lines []string

// endOfMonthPercentage := 100 * now.Day() / monthDays
hundred := decimal.NewFromInt(100)
savePercent := decimal.NewFromInt(int64(savePercentage))
today := decimal.NewFromInt(int64(date.Day()))

saveGoal := balance.Div(hundred).Mul(savePercent)
monthlyBudget := balance.Sub(saveGoal)
monthDays := daysOfMonth(date)
dailyBudget := monthlyBudget.DivRound(monthDays, 2)
endOfMonthPercentage := hundred.Mul(today).DivRound(monthDays, 0)
spentAmountPercentage := hundred.Mul(monthTot).DivRound(monthlyBudget, 0)

y, m, d := date.Date()
body := [][]string{
{"Save goal", money(saveGoal)},
{"Monthly budget", money(monthlyBudget)},
{"Daily budget", money(dailyBudget)},
}
footer := [][]string{
{"End of month", percentage(endOfMonthPercentage)},
{"Amount spent", percentage(spentAmountPercentage)},
}

// spentAmountPercentage := 100 * monthTot / monthlyBudget
lines = append(lines, fmt.Sprintln(d, m, y))
lines = append(lines, formatStats(body))
lines = append(lines, formatStats(footer))

// stats := map[string]string{
// "Save goal": saveGoal,
// "Monthly budget": monthlyBudget,
// "Daily budget": dailyBudget,
// "End of month": endOfMonthPercentage,
// "Amount spent": spentAmountPercentage,
// }
// formatStats(stats)
text := strings.Join(lines, "\n")

return "", nil
return replaceDotsWithCommas(text)
}

const (
monthly int64 = 1
yearly int64 = 12
)
func daysOfMonth(date time.Time) decimal.Decimal {
y, m, _ := date.Date()
endOfMonth := time.Date(y, m+1, 0, 0, 0, 0, 0, date.Location())

var intervalDictionary = map[string]int64{
"m": monthly,
"M": monthly,
"y": yearly,
"Y": yearly,
return decimal.NewFromInt(int64(endOfMonth.Day()))
}

func extractDueValue(fields []string) (decimal.Decimal, error) {
if len(fields) < 2 {
return decimal.Decimal{}, fmt.Errorf("at least 2 fields required")
}

amount, err := decimal.NewFromString(fields[0])
if err != nil {
return decimal.Decimal{}, err
}
func formatStats(stats [][]string) string {
var text string

interval, ok := intervalDictionary[fields[1]]
if !ok {
return decimal.Decimal{}, fmt.Errorf("unknown interval '%s'", fields[1])
for _, cols := range stats {
text += fmt.Sprintf("%s\t%s\n", cols[0], cols[1])
}

return amount.Div(decimal.NewFromInt(interval)), nil
return text
}

func extractFormattedEntryValue(fields []string) (decimal.Decimal, error) {
return decimal.NewFromString(fields[1])
}
//
// Common stuff
//

func formatEntry(fields []string) (string, error) {
if len(fields) < 2 {
return "", fmt.Errorf("at least 2 fields required")
}
func lines(data string) []string {
return strings.Split(strings.Trim(data, "\n"), "\n")
}

s := strings.Replace(fields[0], ",", ".", 1)
amount, err := decimal.NewFromString(s)
if err != nil {
return "", err
}
func money(amount decimal.Decimal) string {
return amount.StringFixed(2)
}

return fmt.Sprintf("%s\t%s\n", strings.Title(fields[1]), amount.StringFixed(2)), nil
func percentage(amount decimal.Decimal) string {
return amount.String() + "%"
}

func sumValues(data string, extractor func([]string) (decimal.Decimal, error)) (decimal.Decimal, error) {
Expand All @@ -212,6 +250,6 @@ func sumValues(data string, extractor func([]string) (decimal.Decimal, error)) (
return tot, nil
}

func lines(data string) []string {
return strings.Split(strings.Trim(data, "\n"), "\n")
func replaceDotsWithCommas(text string) string {
return strings.ReplaceAll(text, ".", ",")
}
29 changes: 27 additions & 2 deletions kakebo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package kakebo
import (
"testing"
"time"

"github.com/shopspring/decimal"
)

// Test FormatEntries
Expand Down Expand Up @@ -201,14 +203,37 @@ Bar 3,45
Baz 6,00
Xyzzy 78,09
Tot 88,74
Tot 100,00
`

date := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
tot, _ := CalcMonth(monthData)
tot := decimal.NewFromInt(100)
got := DisplayMonth(date, monthData, tot)

if got != want {
t.Fatalf("\n GOT: %#v\nWANT: %#v", got, want)
}
}

// Test DisplayStats
//
func TestDisplayStats(t *testing.T) {
want := `10 November 2009
Save goal 100,00
Monthly budget 900,00
Daily budget 30,00
End of month 33%
Amount spent 11%
`

date := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
bal := decimal.NewFromInt(1000)
tot := decimal.NewFromInt(100)
got := DisplayStats(date, bal, tot, 10)

if got != want {
t.Fatalf("\n GOT: %#v\nWANT: %#v", got, want)
}
}

0 comments on commit 36f54c5

Please sign in to comment.