diff --git a/internal/handlers/reports.go b/internal/handlers/reports.go index 6c313ef..f42567f 100644 --- a/internal/handlers/reports.go +++ b/internal/handlers/reports.go @@ -81,9 +81,14 @@ type Tabulation struct { Series map[string]*Series } -func (r *Tabulation) Write(w http.ResponseWriter) error { +func (t *Tabulation) Write(w http.ResponseWriter) error { enc := json.NewEncoder(w) - return enc.Encode(r) + return enc.Encode(t) +} + +func (t *Tabulation) Read(json_str string) error { + dec := json.NewDecoder(strings.NewReader(json_str)) + return dec.Decode(t) } func GetReport(tx *Tx, reportid int64, userid int64) (*Report, error) { diff --git a/internal/handlers/reports_test.go b/internal/handlers/reports_test.go index a9e6c0e..d1d2172 100644 --- a/internal/handlers/reports_test.go +++ b/internal/handlers/reports_test.go @@ -48,6 +48,15 @@ func deleteReport(client *http.Client, r *handlers.Report) error { return nil } +func tabulateReport(client *http.Client, reportid int64) (*handlers.Tabulation, error) { + var t handlers.Tabulation + err := read(client, &t, "/report/"+strconv.FormatInt(reportid, 10)+"/tabulation", "tabulation") + if err != nil { + return nil, err + } + return &t, nil +} + func TestCreateReport(t *testing.T) { RunWith(t, &data[0], func(t *testing.T, d *TestData) { for i := 0; i < len(data[0].reports); i++ { @@ -205,3 +214,68 @@ func TestDeleteReport(t *testing.T) { } }) } +func seriesEqualityHelper(t *testing.T, orig, curr map[string]*handlers.Series, name string) { + if orig == nil || curr == nil { + if orig != nil { + t.Fatalf("`%s` series unexpectedly nil", name) + } + if curr != nil { + t.Fatalf("`%s` series unexpectedly non-nil", name) + } + return + } + if len(orig) != len(curr) { + t.Errorf("Series in question: %v\n", curr) + t.Fatalf("Series' don't contain the same number of sub-series (found %d, expected %d)", len(curr), len(orig)) + } + for k, os := range orig { + cs := curr[k] + if len(os.Values) != len(cs.Values) { + t.Fatalf("`%s` series doesn't contain the same number of Values (found %d, expected %d)", k, len(cs.Values), len(os.Values)) + } + for i, v := range os.Values { + if v != cs.Values[i] { + t.Errorf("Series doesn't contain the same values (found %f, expected %f)", cs.Values[i], v) + } + } + seriesEqualityHelper(t, os.Series, cs.Series, k) + } +} + +func tabulationEqualityHelper(t *testing.T, orig, curr *handlers.Tabulation) { + if orig.Title != curr.Title { + t.Errorf("Tabulation Title doesn't match") + } + if orig.Subtitle != curr.Subtitle { + t.Errorf("Tabulation Subtitle doesn't match") + } + if orig.Units != curr.Units { + t.Errorf("Tabulation Units doesn't match") + } + if len(orig.Labels) != len(curr.Labels) { + t.Fatalf("Tabulation doesn't contain the same number of labels") + } + for i, label := range orig.Labels { + if label != curr.Labels[i] { + t.Errorf("Label %d doesn't match", i) + } + } + seriesEqualityHelper(t, orig.Series, curr.Series, "top-level") +} + +func TestTabulateReport(t *testing.T) { + RunWith(t, &data[0], func(t *testing.T, d *TestData) { + for i := 0; i < len(data[0].tabulations); i++ { + orig := data[0].tabulations[i] + origReport := data[0].reports[orig.ReportId] + report := d.reports[orig.ReportId] + + rt2, err := tabulateReport(d.clients[origReport.UserId], report.ReportId) + if err != nil { + t.Fatalf("Unexpected error tabulating report") + } + + tabulationEqualityHelper(t, &orig, rt2) + } + }) +} diff --git a/internal/handlers/testdata_test.go b/internal/handlers/testdata_test.go index f380bd7..5bd81b3 100644 --- a/internal/handlers/testdata_test.go +++ b/internal/handlers/testdata_test.go @@ -41,6 +41,7 @@ type TestData struct { transactions []handlers.Transaction prices []handlers.Price reports []handlers.Report + tabulations []handlers.Tabulation } type TestDataFunc func(*testing.T, *TestData) @@ -282,7 +283,7 @@ var data = []TestData{ handlers.Transaction{ UserId: 0, Description: "Cable", - Date: time.Date(2017, time.September, 1, 0, 00, 00, 0, time.UTC), + Date: time.Date(2017, time.September, 2, 0, 00, 00, 0, time.UTC), Splits: []*handlers.Split{ &handlers.Split{ Status: handlers.Reconciled, @@ -321,7 +322,7 @@ var data = []TestData{ reports: []handlers.Report{ handlers.Report{ UserId: 0, - Name: "Monthly Expenses", + Name: "This Year's Monthly Expenses", Lua: ` function account_series_map(accounts, tabulation) map = {} @@ -347,12 +348,14 @@ function account_series_map(accounts, tabulation) end function generate() - year = date.now().year + year = 2017 account_type = account.Expense accounts = get_accounts() t = tabulation.new(12) t:title(year .. " Monthly Expenses") + t:subtitle("This is my subtitle") + t:units(get_default_currency().Symbol) series_map = account_series_map(accounts, t) for month=1,12 do @@ -374,5 +377,38 @@ function generate() end`, }, }, + tabulations: []handlers.Tabulation{ + handlers.Tabulation{ + ReportId: 0, + Title: "2017 Monthly Expenses", + Subtitle: "This is my subtitle", + Units: "USD", + Labels: []string{"2017-01-01", "2017-02-01", "2017-03-01", "2017-04-01", "2017-05-01", "2017-06-01", "2017-07-01", "2017-08-01", "2017-09-01", "2017-10-01", "2017-11-01", "2017-12-01"}, + Series: map[string]*handlers.Series{ + "Assets": &handlers.Series{ + Values: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + Series: map[string]*handlers.Series{ + "Credit Union Checking": &handlers.Series{ + Values: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + Series: map[string]*handlers.Series{}, + }, + }, + }, + "Expenses": &handlers.Series{ + Values: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + Series: map[string]*handlers.Series{ + "Groceries": &handlers.Series{ + Values: []float64{0, 0, 0, 0, 0, 0, 0, 0, 0, 87.19, 0, 0}, + Series: map[string]*handlers.Series{}, + }, + "Cable": &handlers.Series{ + Values: []float64{0, 0, 0, 0, 0, 0, 0, 0, 39.99, 0, 0, 0}, + Series: map[string]*handlers.Series{}, + }, + }, + }, + }, + }, + }, }, }