Skip to content

Commit ffe7464

Browse files
authored
Merge branch 'donetick:main' into main
2 parents 883368f + aceeb74 commit ffe7464

File tree

6 files changed

+121
-22
lines changed

6 files changed

+121
-22
lines changed

internal/chore/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func (h *API) GetAllChores(c *gin.Context) {
3636
c.JSON(401, gin.H{"error": "Unauthorized"})
3737
return
3838
}
39-
chores, err := h.choreRepo.GetChores(c, user.CircleID, user.ID)
39+
chores, err := h.choreRepo.GetChores(c, user.CircleID, user.ID, false)
4040
if err != nil {
4141
c.JSON(500, gin.H{"error": err.Error()})
4242
return

internal/chore/handler.go

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ type ChoreReq struct {
5353
Labels []string `json:"labels"`
5454
LabelsV2 *[]LabelReq `json:"labelsV2"`
5555
ThingTrigger *ThingTrigger `json:"thingTrigger"`
56+
Points *int `json:"points"`
57+
CompletionWindow *int `json:"completionWindow"`
5658
}
5759
type Handler struct {
5860
choreRepo *chRepo.ChoreRepository
@@ -85,7 +87,13 @@ func (h *Handler) getChores(c *gin.Context) {
8587
})
8688
return
8789
}
88-
chores, err := h.choreRepo.GetChores(c, u.CircleID, u.ID)
90+
includeArchived := false
91+
92+
if c.Query("includeArchived") == "true" {
93+
includeArchived = true
94+
}
95+
96+
chores, err := h.choreRepo.GetChores(c, u.CircleID, u.ID, includeArchived)
8997
if err != nil {
9098
c.JSON(500, gin.H{
9199
"error": "Error getting chores",
@@ -271,6 +279,8 @@ func (h *Handler) createChore(c *gin.Context) {
271279
CreatedBy: currentUser.ID,
272280
CreatedAt: time.Now().UTC(),
273281
CircleID: currentUser.CircleID,
282+
Points: choreReq.Points,
283+
CompletionWindow: choreReq.CompletionWindow,
274284
}
275285
id, err := h.choreRepo.CreateChore(c, createdChore)
276286
createdChore.ID = id
@@ -530,6 +540,8 @@ func (h *Handler) editChore(c *gin.Context) {
530540
UpdatedBy: currentUser.ID,
531541
CreatedBy: oldChore.CreatedBy,
532542
CreatedAt: oldChore.CreatedAt,
543+
Points: choreReq.Points,
544+
CompletionWindow: choreReq.CompletionWindow,
533545
}
534546
if err := h.choreRepo.UpsertChore(c, updatedChore); err != nil {
535547
c.JSON(500, gin.H{
@@ -954,6 +966,16 @@ func (h *Handler) completeChore(c *gin.Context) {
954966
})
955967
return
956968
}
969+
// confirm that the chore in completion window:
970+
if chore.CompletionWindow != nil {
971+
if completedDate.After(chore.NextDueDate.Add(time.Hour * time.Duration(*chore.CompletionWindow))) {
972+
c.JSON(400, gin.H{
973+
"error": "Chore is out of completion window",
974+
})
975+
return
976+
}
977+
}
978+
957979
var nextDueDate *time.Time
958980
if chore.FrequencyType == "adaptive" {
959981
history, err := h.choreRepo.GetChoreHistoryWithLimit(c, chore.ID, 5)
@@ -1197,6 +1219,45 @@ func (h *Handler) updatePriority(c *gin.Context) {
11971219
})
11981220
}
11991221

1222+
func (h *Handler) getChoresHistory(c *gin.Context) {
1223+
1224+
currentUser, ok := auth.CurrentUser(c)
1225+
if !ok {
1226+
c.JSON(500, gin.H{
1227+
"error": "Error getting current user",
1228+
})
1229+
return
1230+
}
1231+
durationRaw := c.Query("limit")
1232+
if durationRaw == "" {
1233+
durationRaw = "7"
1234+
}
1235+
1236+
duration, err := strconv.Atoi(durationRaw)
1237+
if err != nil {
1238+
c.JSON(400, gin.H{
1239+
"error": "Invalid duration",
1240+
})
1241+
return
1242+
}
1243+
includeCircleRaw := c.Query("members")
1244+
includeCircle := false
1245+
if includeCircleRaw == "true" {
1246+
includeCircle = true
1247+
}
1248+
1249+
choreHistories, err := h.choreRepo.GetChoresHistoryByUserID(c, currentUser.ID, currentUser.CircleID, duration, includeCircle)
1250+
if err != nil {
1251+
c.JSON(500, gin.H{
1252+
"error": "Error getting chore history",
1253+
})
1254+
return
1255+
}
1256+
c.JSON(200, gin.H{
1257+
"res": choreHistories,
1258+
})
1259+
}
1260+
12001261
func (h *Handler) DeleteHistory(c *gin.Context) {
12011262

12021263
currentUser, ok := auth.CurrentUser(c)
@@ -1361,7 +1422,7 @@ func Routes(router *gin.Engine, h *Handler, auth *jwt.GinJWTMiddleware) {
13611422
{
13621423
choresRoutes.GET("/", h.getChores)
13631424
choresRoutes.GET("/archived", h.getArchivedChores)
1364-
1425+
choresRoutes.GET("/history", h.getChoresHistory)
13651426
choresRoutes.PUT("/", h.editChore)
13661427
choresRoutes.PUT("/:id/priority", h.updatePriority)
13671428
choresRoutes.POST("/", h.createChore)

internal/chore/model/model.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,35 @@ type Chore struct {
4646
ThingChore *tModel.ThingChore `json:"thingChore" gorm:"foreignkey:chore_id;references:id;<-:false"` // ThingChore relationship
4747
Status int `json:"status" gorm:"column:status"`
4848
Priority int `json:"priority" gorm:"column:priority"`
49+
CompletionWindow *int `json:"completionWindow,omitempty" gorm:"column:completion_window"` // Number seconds before the chore is due that it can be completed
50+
Points *int `json:"points,omitempty" gorm:"column:points"` // Points for completing the chore
4951
}
5052
type ChoreAssignees struct {
5153
ID int `json:"-" gorm:"primary_key"`
5254
ChoreID int `json:"-" gorm:"column:chore_id;uniqueIndex:idx_chore_user"` // The chore this assignee is for
5355
UserID int `json:"userId" gorm:"column:user_id;uniqueIndex:idx_chore_user"` // The user this assignee is for
5456
}
5557
type ChoreHistory struct {
56-
ID int `json:"id" gorm:"primary_key"` // Unique identifier
57-
ChoreID int `json:"choreId" gorm:"column:chore_id"` // The chore this history is for
58-
CompletedAt *time.Time `json:"completedAt" gorm:"column:completed_at"` // When the chore was completed
59-
CompletedBy int `json:"completedBy" gorm:"column:completed_by"` // Who completed the chore
60-
AssignedTo int `json:"assignedTo" gorm:"column:assigned_to"` // Who the chore was assigned to
61-
Note *string `json:"notes" gorm:"column:notes"` // Notes about the chore
62-
DueDate *time.Time `json:"dueDate" gorm:"column:due_date"` // When the chore was due
63-
UpdatedAt *time.Time `json:"updatedAt" gorm:"column:updated_at"` // When the record was last updated
58+
ID int `json:"id" gorm:"primary_key"` // Unique identifier
59+
ChoreID int `json:"choreId" gorm:"column:chore_id"` // The chore this history is for
60+
CompletedAt *time.Time `json:"completedAt" gorm:"column:completed_at"` // When the chore was completed
61+
CompletedBy int `json:"completedBy" gorm:"column:completed_by"` // Who completed the chore
62+
AssignedTo int `json:"assignedTo" gorm:"column:assigned_to"` // Who the chore was assigned to
63+
Note *string `json:"notes" gorm:"column:notes"` // Notes about the chore
64+
DueDate *time.Time `json:"dueDate" gorm:"column:due_date"` // When the chore was due
65+
UpdatedAt *time.Time `json:"updatedAt" gorm:"column:updated_at"` // When the record was last updated
66+
Status ChoreHistoryStatus `json:"status" gorm:"column:status"` // Status of the chore
67+
Points *int `json:"points,omitempty" gorm:"column:points"` // Points for completing the chore
6468
}
69+
type ChoreHistoryStatus int8
70+
71+
const (
72+
ChoreHistoryStatusPending ChoreHistoryStatus = 0
73+
ChoreHistoryStatusCompleted ChoreHistoryStatus = 1
74+
ChoreHistoryStatusCompletedLate ChoreHistoryStatus = 2
75+
ChoreHistoryStatusMissed ChoreHistoryStatus = 3
76+
ChoreHistoryStatusSkipped ChoreHistoryStatus = 4
77+
)
6578

6679
type FrequencyMetadata struct {
6780
Days []*string `json:"days,omitempty"`
@@ -96,6 +109,7 @@ type ChoreDetail struct {
96109
Priority int `json:"priority" gorm:"column:priority"`
97110
Notes *string `json:"notes" gorm:"column:notes"`
98111
CreatedBy int `json:"createdBy" gorm:"column:created_by"`
112+
CompletionWindow *int `json:"completionWindow,omitempty" gorm:"column:completion_window"`
99113
}
100114

101115
type Label struct {

internal/chore/repo/repository.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
config "donetick.com/core/config"
1010
chModel "donetick.com/core/internal/chore/model"
11+
cModel "donetick.com/core/internal/circle/model"
1112
"gorm.io/gorm"
1213
)
1314

@@ -49,10 +50,13 @@ func (r *ChoreRepository) GetChore(c context.Context, choreID int) (*chModel.Cho
4950
return &chore, nil
5051
}
5152

52-
func (r *ChoreRepository) GetChores(c context.Context, circleID int, userID int) ([]*chModel.Chore, error) {
53+
func (r *ChoreRepository) GetChores(c context.Context, circleID int, userID int, includeArchived bool) ([]*chModel.Chore, error) {
5354
var chores []*chModel.Chore
54-
// if err := r.db.WithContext(c).Preload("Assignees").Where("is_active = ?", true).Order("next_due_date asc").Find(&chores, "circle_id = ?", circleID).Error; err != nil {
55-
if err := r.db.WithContext(c).Preload("Assignees").Preload("LabelsV2").Joins("left join chore_assignees on chores.id = chore_assignees.chore_id").Where("chores.circle_id = ? AND (chores.created_by = ? OR chore_assignees.user_id = ?)", circleID, userID, userID).Group("chores.id").Order("next_due_date asc").Find(&chores, "circle_id = ? AND is_active = ?", circleID, true).Error; err != nil {
55+
query := r.db.WithContext(c).Preload("Assignees").Preload("LabelsV2").Joins("left join chore_assignees on chores.id = chore_assignees.chore_id").Where("chores.circle_id = ? AND (chores.created_by = ? OR chore_assignees.user_id = ?)", circleID, userID, userID).Group("chores.id").Order("next_due_date asc")
56+
if !includeArchived {
57+
query = query.Where("chores.is_active = ?", true)
58+
}
59+
if err := query.Find(&chores, "circle_id = ?", circleID).Error; err != nil {
5660
return nil, err
5761
}
5862
return chores, nil
@@ -98,6 +102,7 @@ func (r *ChoreRepository) CompleteChore(c context.Context, chore *chModel.Chore,
98102
AssignedTo: chore.AssignedTo,
99103
DueDate: chore.NextDueDate,
100104
Note: note,
105+
Points: chore.Points,
101106
}
102107
if err := tx.Create(ch).Error; err != nil {
103108
return err
@@ -114,6 +119,12 @@ func (r *ChoreRepository) CompleteChore(c context.Context, chore *chModel.Chore,
114119
if err := tx.Model(&chModel.Chore{}).Where("id = ?", chore.ID).Updates(updates).Error; err != nil {
115120
return err
116121
}
122+
// Update UserCirclee Points :
123+
if chore.Points != nil && *chore.Points > 0 {
124+
if err := tx.Debug().Model(&cModel.UserCircle{}).Where("user_id = ? AND circle_id = ?", userID, chore.CircleID).Update("points", gorm.Expr("points + ?", chore.Points)).Error; err != nil {
125+
return err
126+
}
127+
}
117128

118129
return nil
119130
})
@@ -266,6 +277,7 @@ func (r *ChoreRepository) GetChoreDetailByID(c context.Context, choreID int, cir
266277
chores.assigned_to,
267278
chores.created_by,
268279
chores.priority,
280+
chores.completion_window,
269281
recent_history.last_completed_date,
270282
recent_history.notes,
271283
recent_history.last_assigned_to as last_completed_by,
@@ -301,3 +313,13 @@ func (r *ChoreRepository) ArchiveChore(c context.Context, choreID int, userID in
301313
func (r *ChoreRepository) UnarchiveChore(c context.Context, choreID int, userID int) error {
302314
return r.db.WithContext(c).Model(&chModel.Chore{}).Where("id = ? and created_by = ?", choreID, userID).Update("is_active", true).Error
303315
}
316+
317+
func (r *ChoreRepository) GetChoresHistoryByUserID(c context.Context, userID int, circleID int, days int, includeCircle bool) ([]*chModel.ChoreHistory, error) {
318+
319+
var chores []*chModel.ChoreHistory
320+
since := time.Now().AddDate(0, 0, days*-1)
321+
if err := r.db.WithContext(c).Where("completed_by = ? AND completed_at > ?", userID, since).Order("completed_at desc").Find(&chores).Error; err != nil {
322+
return nil, err
323+
}
324+
return chores, nil
325+
}

internal/circle/handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ func (h *Handler) LeaveCircle(c *gin.Context) {
172172
}
173173

174174
func handleUserLeavingCircle(h *Handler, c *gin.Context, leavingUser *uModel.User, orginalCircleID int) error {
175-
userAssignedCircleChores, err := h.choreRepo.GetChores(c, leavingUser.CircleID, leavingUser.ID)
175+
userAssignedCircleChores, err := h.choreRepo.GetChores(c, leavingUser.CircleID, leavingUser.ID, true)
176176
if err != nil {
177177
return err
178178
}

internal/circle/model/model.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ type CircleDetail struct {
2222
}
2323

2424
type UserCircle struct {
25-
ID int `json:"id" gorm:"primary_key"` // Unique identifier
26-
UserID int `json:"userId" gorm:"column:user_id"` // User ID
27-
CircleID int `json:"circleId" gorm:"column:circle_id"` // Circle ID
28-
Role string `json:"role" gorm:"column:role"` // Role
29-
IsActive bool `json:"isActive" gorm:"column:is_active;default:false"`
30-
CreatedAt time.Time `json:"createdAt" gorm:"column:created_at"` // Created at
31-
UpdatedAt time.Time `json:"updatedAt" gorm:"column:updated_at"` // Updated at
25+
ID int `json:"id" gorm:"primary_key"` // Unique identifier
26+
UserID int `json:"userId" gorm:"column:user_id"` // User ID
27+
CircleID int `json:"circleId" gorm:"column:circle_id"` // Circle ID
28+
Role string `json:"role" gorm:"column:role"` // Role
29+
IsActive bool `json:"isActive" gorm:"column:is_active;default:false"`
30+
CreatedAt time.Time `json:"createdAt" gorm:"column:created_at"` // Created at
31+
UpdatedAt time.Time `json:"updatedAt" gorm:"column:updated_at"` // Updated at
32+
Points int `json:"points" gorm:"column:points;default:0;not null"` // Points
33+
PointsRedeemed int `json:"pointsRedeemed" gorm:"column:points_redeemed;default:0;not null"` // Points Redeemed
3234
}
3335

3436
type UserCircleDetail struct {

0 commit comments

Comments
 (0)