diff --git a/internal/chore/handler.go b/internal/chore/handler.go index bce6d01..5ec8cdd 100644 --- a/internal/chore/handler.go +++ b/internal/chore/handler.go @@ -802,14 +802,33 @@ func (h *Handler) completeChore(c *gin.Context) { }) return } + var nextDueDate *time.Time + if chore.FrequencyType == "adaptive" { + history, err := h.choreRepo.GetChoreHistoryWithLimit(c, chore.ID, 5) + if err != nil { + c.JSON(500, gin.H{ + "error": "Error getting chore history", + }) + return + } + nextDueDate, err = scheduleAdaptiveNextDueDate(chore, completedDate, history) + if err != nil { + log.Printf("Error scheduling next due date: %s", err) + c.JSON(500, gin.H{ + "error": "Error scheduling next due date", + }) + return + } - nextDueDate, err := scheduleNextDueDate(chore, completedDate) - if err != nil { - log.Printf("Error scheduling next due date: %s", err) - c.JSON(500, gin.H{ - "error": "Error scheduling next due date", - }) - return + } else { + nextDueDate, err = scheduleNextDueDate(chore, completedDate) + if err != nil { + log.Printf("Error scheduling next due date: %s", err) + c.JSON(500, gin.H{ + "error": "Error scheduling next due date", + }) + return + } } choreHistory, err := h.choreRepo.GetChoreHistory(c, chore.ID) if err != nil { diff --git a/internal/chore/model/model.go b/internal/chore/model/model.go index 2e7ee90..90f6c29 100644 --- a/internal/chore/model/model.go +++ b/internal/chore/model/model.go @@ -80,5 +80,6 @@ type ChoreDetail struct { LastCompletedDate *time.Time `json:"lastCompletedDate" gorm:"column:last_completed_date"` LastCompletedBy *int `json:"lastCompletedBy" gorm:"column:last_completed_by"` TotalCompletedCount int `json:"totalCompletedCount" gorm:"column:total_completed"` + Notes *string `json:"notes" gorm:"column:notes"` CreatedBy int `json:"createdBy" gorm:"column:created_by"` } diff --git a/internal/chore/repo/repository.go b/internal/chore/repo/repository.go index 4eac59c..7284202 100644 --- a/internal/chore/repo/repository.go +++ b/internal/chore/repo/repository.go @@ -234,6 +234,7 @@ func (r *ChoreRepository) GetChoreDetailByID(c context.Context, choreID int, cir chores.assigned_to, chores.created_by, recent_history.last_completed_date, + recent_history.notes, recent_history.last_assigned_to as last_completed_by, COUNT(chore_histories.id) as total_completed`). Joins("LEFT JOIN chore_histories ON chores.id = chore_histories.chore_id"). @@ -241,7 +242,9 @@ func (r *ChoreRepository) GetChoreDetailByID(c context.Context, choreID int, cir SELECT chore_id, assigned_to AS last_assigned_to, - completed_at AS last_completed_date + completed_at AS last_completed_date, + notes + FROM chore_histories WHERE (chore_id, completed_at) IN ( SELECT chore_id, MAX(completed_at) @@ -250,7 +253,7 @@ func (r *ChoreRepository) GetChoreDetailByID(c context.Context, choreID int, cir ) ) AS recent_history ON chores.id = recent_history.chore_id`). Where("chores.id = ? and chores.circle_id = ?", choreID, circleID). - Group("chores.id, recent_history.last_completed_date, recent_history.last_assigned_to"). + Group("chores.id, recent_history.last_completed_date, recent_history.last_assigned_to, recent_history.notes"). First(&choreDetail).Error; err != nil { return nil, err diff --git a/internal/chore/scheduler.go b/internal/chore/scheduler.go index 55cdf01..c1ff48d 100644 --- a/internal/chore/scheduler.go +++ b/internal/chore/scheduler.go @@ -47,6 +47,7 @@ func scheduleNextDueDate(chore *chModel.Chore, completedDate time.Time) (*time.T } else if chore.FrequencyType == "yearly" { nextDueDate = baseDate.AddDate(1, 0, 0) } else if chore.FrequencyType == "adaptive" { + // TODO: calculate next due date based on the history of the chore // calculate the difference between the due date and now in days: diff := completedDate.UTC().Sub(chore.NextDueDate.UTC()) @@ -129,6 +130,33 @@ func scheduleNextDueDate(chore *chModel.Chore, completedDate time.Time) (*time.T } +func scheduleAdaptiveNextDueDate(chore *chModel.Chore, completedDate time.Time, history []*chModel.ChoreHistory) (*time.Time, error) { + // will generate due date base on history and the different between the completed date and the due date + // the more recent the higher weight + if len(history) <= 1 { + if chore.NextDueDate != nil { + diff := completedDate.UTC().Sub(chore.NextDueDate.UTC()) + nextDueDate := completedDate.UTC().Add(diff) + return &nextDueDate, nil + } + return nil, nil + } + var weight float64 + var totalWeight float64 + var nextDueDate time.Time + for i := 0; i < len(history)-1; i++ { + delay := history[i].CompletedAt.UTC().Sub(history[i+1].CompletedAt.UTC()).Seconds() + weight = delay * float64(len(history)-i) + totalWeight += weight + } + // calculate the average delay + averageDelay := totalWeight / float64(len(history)-1) + // calculate the difference between the completed date and the due date + nextDueDate = completedDate.UTC().Add(time.Duration(averageDelay) * time.Second) + + return &nextDueDate, nil +} + func RemoveAssigneeAndReassign(chore *chModel.Chore, userID int) { for i, assignee := range chore.Assignees { if assignee.UserID == userID {