Skip to content

Commit

Permalink
implement GetExpenseSummaryHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
golfz committed May 19, 2024
1 parent b6722f4 commit 7ddf71c
Show file tree
Hide file tree
Showing 6 changed files with 185 additions and 80 deletions.
7 changes: 7 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"database/sql"
"github.com/KKGo-Software-engineering/workshop-summer/api/summary"
"github.com/KKGo-Software-engineering/workshop-summer/api/transaction"

"github.com/KKGo-Software-engineering/workshop-summer/api/config"
Expand Down Expand Up @@ -43,5 +44,11 @@ func New(db *sql.DB, cfg config.Config, logger *zap.Logger) *Server {
v1.POST("/transactions", h.Create)
}

{
h := summary.New(cfg.FeatureFlag, db)
v1.GET("/spenders/:id/expenses/summary", h.GetExpenseSummaryHandler)
//v1.GET("/spenders/:id/incomes/summary", h.GetIncomeSummaryHandler)
}

return &Server{e}
}
80 changes: 0 additions & 80 deletions api/expense/summary/summary.go

This file was deleted.

131 changes: 131 additions & 0 deletions api/summary/summary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package summary

import (
"database/sql"
"errors"
"github.com/KKGo-Software-engineering/workshop-summer/api/config"
"github.com/kkgo-software-engineering/workshop/mlog"
"github.com/labstack/echo/v4"
"go.uber.org/zap"
"net/http"
"time"
)

const (
typeExpense = "expense"
typeIncome = "income"
)

var (
ErrInvalidSpender = errors.New("invalid spender")
)

type Err struct {
Message string `json:"message"`
}

type Spender struct {
ID int `param:"id"`
}

type RawData struct {
Date time.Time
SumAmount float64
CountExpenses int
}

type Summary struct {
Total float64 `json:"total_amount"`
Average float64 `json:"average_per_day"`
Count int `json:"count_transaction"`
}

type handler struct {
flag config.FeatureFlag
db *sql.DB
}

func New(cfg config.FeatureFlag, db *sql.DB) *handler {
return &handler{cfg, db}
}

func summary(data []RawData) Summary {
if len(data) == 0 {
return Summary{}
}

var total float64
var count int
for _, d := range data {
total += d.SumAmount
count += d.CountExpenses
}

return Summary{
Total: total,
Average: total / float64(len(data)),
Count: count,
}
}

const (
sumSQL = `SELECT
date_trunc('day', date)::date AS transaction_date,
SUM(amount) AS total_amount,
COUNT(*) AS record_count
FROM
"transaction"
WHERE
transaction_type = $1 AND spender_id = $2
GROUP BY
date_trunc('day', date)::date
ORDER BY
transaction_date;`
)

func getSummary(c echo.Context, db *sql.DB, tnxType string) error {
logger := mlog.L(c)
ctx := c.Request().Context()

var spender Spender
err := c.Bind(&spender)
if err != nil {
logger.Error(ErrInvalidSpender.Error(), zap.Error(err))
return c.JSON(http.StatusBadRequest, Err{Message: ErrInvalidSpender.Error()})
}

stmt, err := db.PrepareContext(ctx, sumSQL)
if err != nil {
logger.Error("prepare statement error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, Err{Message: "prepare statement error"})
}

rows, err := stmt.QueryContext(ctx, tnxType, spender.ID)
if err != nil {
logger.Error("query error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, Err{Message: "query error"})
}
defer rows.Close()

var raws []RawData
for rows.Next() {
var raw RawData
err := rows.Scan(&raw.Date, &raw.SumAmount, &raw.CountExpenses)
if err != nil {
logger.Error("scan error", zap.Error(err))
return c.JSON(http.StatusInternalServerError, Err{Message: "scan error"})
}
raws = append(raws, raw)
}

return c.JSON(http.StatusOK, summary(raws))
}

func (h *handler) GetExpenseSummaryHandler(c echo.Context) error {
return getSummary(c, h.db, typeExpense)
}

//
//func (h *handler) GetIncomeSummaryHandler(c echo.Context) error {
// return getSummary(c, h.db, typeIncome)
//}
File renamed without changes.
35 changes: 35 additions & 0 deletions sql/add-sample-transaction.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
INSERT INTO "transaction" (date, amount, category, transaction_type, note, image_url, spender_id) VALUES
-- Day 1: 2024-05-01
('2024-05-01 10:15:00+00', 23, 'groceries', 'expense', 'Mock transaction 1', 'http://example.com/receipt1.jpg', 1),
('2024-05-01 12:30:00+00', 45, 'rent', 'income', 'Mock transaction 2', 'http://example.com/receipt2.jpg', 1),
('2024-05-01 15:45:00+00', 12, 'utilities', 'expense', 'Mock transaction 3', 'http://example.com/receipt3.jpg', 1),
('2024-05-01 18:00:00+00', 67, 'entertainment', 'income', 'Mock transaction 4', 'http://example.com/receipt4.jpg', 1),
('2024-05-01 20:15:00+00', 54, 'transport', 'expense', 'Mock transaction 5', 'http://example.com/receipt5.jpg', 1),

-- Day 2: 2024-05-02
('2024-05-02 09:00:00+00', 31, 'groceries', 'income', 'Mock transaction 6', 'http://example.com/receipt6.jpg', 1),
('2024-05-02 11:15:00+00', 29, 'rent', 'expense', 'Mock transaction 7', 'http://example.com/receipt7.jpg', 1),
('2024-05-02 13:30:00+00', 46, 'utilities', 'income', 'Mock transaction 8', 'http://example.com/receipt8.jpg', 1),
('2024-05-02 15:45:00+00', 18, 'entertainment', 'expense', 'Mock transaction 9', 'http://example.com/receipt9.jpg', 1),
('2024-05-02 18:00:00+00', 39, 'transport', 'income', 'Mock transaction 10', 'http://example.com/receipt10.jpg', 1),

-- Day 3: 2024-05-03
('2024-05-03 08:00:00+00', 22, 'groceries', 'expense', 'Mock transaction 11', 'http://example.com/receipt11.jpg', 1),
('2024-05-03 10:15:00+00', 53, 'rent', 'income', 'Mock transaction 12', 'http://example.com/receipt12.jpg', 1),
('2024-05-03 12:30:00+00', 44, 'utilities', 'expense', 'Mock transaction 13', 'http://example.com/receipt13.jpg', 1),
('2024-05-03 14:45:00+00', 19, 'entertainment', 'income', 'Mock transaction 14', 'http://example.com/receipt14.jpg', 1),
('2024-05-03 17:00:00+00', 61, 'transport', 'expense', 'Mock transaction 15', 'http://example.com/receipt15.jpg', 1),

-- Day 4: 2024-05-04
('2024-05-04 07:00:00+00', 38, 'groceries', 'income', 'Mock transaction 16', 'http://example.com/receipt16.jpg', 1),
('2024-05-04 09:15:00+00', 27, 'rent', 'expense', 'Mock transaction 17', 'http://example.com/receipt17.jpg', 1),
('2024-05-04 11:30:00+00', 49, 'utilities', 'income', 'Mock transaction 18', 'http://example.com/receipt18.jpg', 1),
('2024-05-04 13:45:00+00', 32, 'entertainment', 'expense', 'Mock transaction 19', 'http://example.com/receipt19.jpg', 1),
('2024-05-04 16:00:00+00', 55, 'transport', 'income', 'Mock transaction 20', 'http://example.com/receipt20.jpg', 1),

-- Day 5: 2024-05-05
('2024-05-05 06:00:00+00', 26, 'groceries', 'expense', 'Mock transaction 21', 'http://example.com/receipt21.jpg', 1),
('2024-05-05 08:15:00+00', 48, 'rent', 'income', 'Mock transaction 22', 'http://example.com/receipt22.jpg', 1),
('2024-05-05 10:30:00+00', 35, 'utilities', 'expense', 'Mock transaction 23', 'http://example.com/receipt23.jpg', 1),
('2024-05-05 12:45:00+00', 43, 'entertainment', 'income', 'Mock transaction 24', 'http://example.com/receipt24.jpg', 1),
('2024-05-05 15:00:00+00', 59, 'transport', 'expense', 'Mock transaction 25', 'http://example.com/receipt25.jpg', 1);
12 changes: 12 additions & 0 deletions sql/get-raw-summary-expense.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
SELECT
date_trunc('day', date)::date AS transaction_date,
SUM(amount) AS total_amount,
COUNT(*) AS record_count
FROM
"transaction"
WHERE
transaction_type = 'expense' AND spender_id = 1
GROUP BY
date_trunc('day', date)::date
ORDER BY
transaction_date;

0 comments on commit 7ddf71c

Please sign in to comment.