mirror of
https://github.com/hay-kot/homebox.git
synced 2025-08-05 09:10:26 +00:00
label and location aggregation endpoints
This commit is contained in:
parent
afed0dd0a1
commit
f7c3ac9fad
4 changed files with 109 additions and 5 deletions
|
@ -9,6 +9,46 @@ import (
|
|||
"github.com/hay-kot/homebox/backend/pkgs/server"
|
||||
)
|
||||
|
||||
// HandleGroupGet godoc
|
||||
// @Summary Get the current user's group statistics
|
||||
// @Tags Statistics
|
||||
// @Produce json
|
||||
// @Success 200 {object} []repo.TotalsByOrganizer
|
||||
// @Router /v1/groups/statistics/locations [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleGroupStatisticsLocations() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := services.NewContext(r.Context())
|
||||
|
||||
stats, err := ctrl.repo.Groups.StatsLocationsByPurchasePrice(ctx, ctx.GID)
|
||||
if err != nil {
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return server.Respond(w, http.StatusOK, stats)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleGroupGet godoc
|
||||
// @Summary Get the current user's group statistics
|
||||
// @Tags Statistics
|
||||
// @Produce json
|
||||
// @Success 200 {object} []repo.TotalsByOrganizer
|
||||
// @Router /v1/groups/statistics/labels [GET]
|
||||
// @Security Bearer
|
||||
func (ctrl *V1Controller) HandleGroupStatisticsLabels() server.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := services.NewContext(r.Context())
|
||||
|
||||
stats, err := ctrl.repo.Groups.StatsLabelsByPurchasePrice(ctx, ctx.GID)
|
||||
if err != nil {
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return server.Respond(w, http.StatusOK, stats)
|
||||
}
|
||||
}
|
||||
|
||||
// HandleGroupGet godoc
|
||||
// @Summary Get the current user's group statistics
|
||||
// @Tags Statistics
|
||||
|
@ -20,7 +60,7 @@ func (ctrl *V1Controller) HandleGroupStatistics() server.HandlerFunc {
|
|||
return func(w http.ResponseWriter, r *http.Request) error {
|
||||
ctx := services.NewContext(r.Context())
|
||||
|
||||
stats, err := ctrl.repo.Groups.GroupStatistics(ctx, ctx.GID)
|
||||
stats, err := ctrl.repo.Groups.StatsGroup(ctx, ctx.GID)
|
||||
if err != nil {
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
@ -52,7 +92,7 @@ func (ctrl *V1Controller) HandleGroupStatisticsPriceOverTime() server.HandlerFun
|
|||
endDate = time.Now()
|
||||
}
|
||||
|
||||
stats, err := ctrl.repo.Groups.GroupStatisticsPriceOverTime(ctx, ctx.GID, startDate, endDate)
|
||||
stats, err := ctrl.repo.Groups.StatsPurchasePrice(ctx, ctx.GID, startDate, endDate)
|
||||
if err != nil {
|
||||
return validate.NewRequestError(err, http.StatusInternalServerError)
|
||||
}
|
||||
|
|
|
@ -80,6 +80,8 @@ func (a *app) mountRoutes(repos *repo.AllRepos) {
|
|||
a.server.Post(v1Base("/groups/invitations"), v1Ctrl.HandleGroupInvitationsCreate(), userMW...)
|
||||
a.server.Get(v1Base("/groups/statistics"), v1Ctrl.HandleGroupStatistics(), userMW...)
|
||||
a.server.Get(v1Base("/groups/statistics/purchase-price"), v1Ctrl.HandleGroupStatisticsPriceOverTime(), userMW...)
|
||||
a.server.Get(v1Base("/groups/statistics/locations"), v1Ctrl.HandleGroupStatisticsLocations(), userMW...)
|
||||
a.server.Get(v1Base("/groups/statistics/labels"), v1Ctrl.HandleGroupStatisticsLabels(), userMW...)
|
||||
|
||||
// TODO: I don't like /groups being the URL for users
|
||||
a.server.Get(v1Base("/groups"), v1Ctrl.HandleGroupGet(), userMW...)
|
||||
|
|
|
@ -5,11 +5,14 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"entgo.io/ent/dialect/sql"
|
||||
"github.com/google/uuid"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/group"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/groupinvitationtoken"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/item"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/label"
|
||||
"github.com/hay-kot/homebox/backend/internal/data/ent/location"
|
||||
)
|
||||
|
||||
type GroupRepository struct {
|
||||
|
@ -64,6 +67,12 @@ type (
|
|||
End time.Time `json:"end"`
|
||||
Entries []ValueOverTimeEntry `json:"entries"`
|
||||
}
|
||||
|
||||
TotalsByOrganizer struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Total float64 `json:"total"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -93,7 +102,60 @@ func mapToGroupInvitation(g *ent.GroupInvitationToken) GroupInvitation {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *GroupRepository) GroupStatisticsPriceOverTime(ctx context.Context, GID uuid.UUID, start, end time.Time) (*ValueOverTime, error) {
|
||||
func (r *GroupRepository) StatsLocationsByPurchasePrice(ctx context.Context, GID uuid.UUID) ([]TotalsByOrganizer, error) {
|
||||
var v []TotalsByOrganizer
|
||||
|
||||
err := r.db.Location.Query().
|
||||
Where(
|
||||
location.HasGroupWith(group.ID(GID)),
|
||||
).
|
||||
GroupBy(location.FieldID, location.FieldName).
|
||||
Aggregate(func(sq *sql.Selector) string {
|
||||
t := sql.Table(item.Table)
|
||||
sq.Join(t).On(sq.C(location.FieldID), t.C(item.LocationColumn))
|
||||
|
||||
return sql.As(sql.Sum(t.C(item.FieldPurchasePrice)), "total")
|
||||
}).
|
||||
Scan(ctx, &v)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (r *GroupRepository) StatsLabelsByPurchasePrice(ctx context.Context, GID uuid.UUID) ([]TotalsByOrganizer, error) {
|
||||
var v []TotalsByOrganizer
|
||||
|
||||
err := r.db.Debug().Label.Query().
|
||||
Where(
|
||||
label.HasGroupWith(group.ID(GID)),
|
||||
).
|
||||
GroupBy(label.FieldID, label.FieldName).
|
||||
Aggregate(func(sq *sql.Selector) string {
|
||||
itemTable := sql.Table(item.Table)
|
||||
|
||||
// item to label is a many to many relation
|
||||
// so we need to join the junction table
|
||||
jt := sql.Table(label.ItemsTable)
|
||||
|
||||
// join the junction table to the item table
|
||||
sq.Join(jt).On(sq.C(label.FieldID), jt.C(label.ItemsPrimaryKey[0]))
|
||||
sq.Join(itemTable).On(jt.C(label.ItemsPrimaryKey[1]), itemTable.C(item.FieldID))
|
||||
|
||||
return sql.As(sql.Sum(itemTable.C(item.FieldPurchasePrice)), "total")
|
||||
}).
|
||||
Scan(ctx, &v)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (r *GroupRepository) StatsPurchasePrice(ctx context.Context, GID uuid.UUID, start, end time.Time) (*ValueOverTime, error) {
|
||||
// Get the Totals for the Start and End of the Given Time Period
|
||||
q := `
|
||||
SELECT
|
||||
|
@ -154,7 +216,7 @@ func (r *GroupRepository) GroupStatisticsPriceOverTime(ctx context.Context, GID
|
|||
return &stats, nil
|
||||
}
|
||||
|
||||
func (r *GroupRepository) GroupStatistics(ctx context.Context, GID uuid.UUID) (GroupStatistics, error) {
|
||||
func (r *GroupRepository) StatsGroup(ctx context.Context, GID uuid.UUID) (GroupStatistics, error) {
|
||||
q := `
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM users WHERE group_users = ?) AS total_users,
|
||||
|
|
|
@ -36,7 +36,7 @@ func Test_Group_GroupStatistics(t *testing.T) {
|
|||
useItems(t, 20)
|
||||
useLabels(t, 20)
|
||||
|
||||
stats, err := tRepos.Groups.GroupStatistics(context.Background(), tGroup.ID)
|
||||
stats, err := tRepos.Groups.StatsGroup(context.Background(), tGroup.ID)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 20, stats.TotalItems)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue