4.3.0: Автовыполнение задач в конце дня
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m11s

This commit is contained in:
poignatov
2026-01-29 17:47:47 +03:00
parent 5c5fc07481
commit f266508d04
7 changed files with 382 additions and 298 deletions

View File

@@ -232,6 +232,7 @@ type Task struct {
ProjectNames []string `json:"project_names"`
SubtasksCount int `json:"subtasks_count"`
HasProgression bool `json:"has_progression"`
AutoComplete bool `json:"auto_complete"`
}
type Reward struct {
@@ -6567,8 +6568,10 @@ func (a *App) getTasksHandler(w http.ResponseWriter, r *http.Request) {
JOIN projects p ON rc.project_id = p.id
WHERE st.parent_task_id = t.id AND st.deleted = FALSE),
ARRAY[]::text[]
) as subtask_project_names
) as subtask_project_names,
COALESCE(td.auto_complete, FALSE) as auto_complete
FROM tasks t
LEFT JOIN task_drafts td ON td.task_id = t.id AND td.user_id = $1
WHERE t.user_id = $1 AND t.parent_task_id IS NULL AND t.deleted = FALSE
ORDER BY
CASE WHEN t.last_completed_at IS NULL OR t.last_completed_at::date < CURRENT_DATE THEN 0 ELSE 1 END,
@@ -6596,6 +6599,7 @@ func (a *App) getTasksHandler(w http.ResponseWriter, r *http.Request) {
var rewardPolicy sql.NullString
var projectNames pq.StringArray
var subtaskProjectNames pq.StringArray
var autoComplete bool
err := rows.Scan(
&task.ID,
@@ -6612,6 +6616,7 @@ func (a *App) getTasksHandler(w http.ResponseWriter, r *http.Request) {
&task.SubtasksCount,
&projectNames,
&subtaskProjectNames,
&autoComplete,
)
if err != nil {
log.Printf("Error scanning task: %v", err)
@@ -6647,6 +6652,7 @@ func (a *App) getTasksHandler(w http.ResponseWriter, r *http.Request) {
if rewardPolicy.Valid {
task.RewardPolicy = &rewardPolicy.String
}
task.AutoComplete = autoComplete
// Объединяем проекты из основной задачи и подзадач
allProjects := make(map[string]bool)
@@ -6894,12 +6900,86 @@ func (a *App) getTaskDetailHandler(w http.ResponseWriter, r *http.Request) {
}
}
// Инициализируем auto_complete значением по умолчанию
task.AutoComplete = false
// Загружаем данные из драфта, если он существует
var draftProgressionValue sql.NullFloat64
var draftAutoComplete sql.NullBool
var draftProgressionValuePtr *float64
var draftSubtasks []DraftSubtask
err = a.DB.QueryRow(`
SELECT progression_value, auto_complete
FROM task_drafts
WHERE task_id = $1 AND user_id = $2
`, taskID, userID).Scan(&draftProgressionValue, &draftAutoComplete)
if err == nil {
// Драфт существует, загружаем данные
if draftProgressionValue.Valid {
draftProgressionValuePtr = &draftProgressionValue.Float64
}
// Устанавливаем auto_complete из драфта (если Valid, иначе остается false)
if draftAutoComplete.Valid {
task.AutoComplete = draftAutoComplete.Bool
log.Printf("Task %d: auto_complete set to %v from draft", taskID, task.AutoComplete)
} else {
log.Printf("Task %d: draft exists but auto_complete is NULL, keeping default false", taskID)
}
// Загружаем подзадачи из драфта
draftSubtaskRows, err := a.DB.Query(`
SELECT subtask_id
FROM task_draft_subtasks
WHERE task_draft_id = (SELECT id FROM task_drafts WHERE task_id = $1 AND user_id = $2)
`, taskID, userID)
if err == nil {
defer draftSubtaskRows.Close()
draftSubtasks = make([]DraftSubtask, 0)
validSubtaskIDs := make(map[int]bool)
// Создаем map валидных подзадач для фильтрации
for _, subtask := range subtasks {
validSubtaskIDs[subtask.Task.ID] = true
}
for draftSubtaskRows.Next() {
var subtaskID int
if err := draftSubtaskRows.Scan(&subtaskID); err == nil {
// Игнорируем подзадачи, которых больше нет в основной задаче
if validSubtaskIDs[subtaskID] {
draftSubtasks = append(draftSubtasks, DraftSubtask{
SubtaskID: subtaskID,
})
}
}
}
} else if err != sql.ErrNoRows {
log.Printf("Error loading draft subtasks for task %d: %v", taskID, err)
}
} else if err != sql.ErrNoRows {
log.Printf("Error loading draft for task %d: %v", taskID, err)
} else {
log.Printf("Task %d: no draft found, auto_complete remains false", taskID)
}
// Если драфта нет (err == sql.ErrNoRows), auto_complete остается false
log.Printf("Task %d: final auto_complete value = %v", taskID, task.AutoComplete)
response := TaskDetail{
Task: task,
Rewards: rewards,
Subtasks: subtasks,
}
// Устанавливаем DraftProgressionValue если он был загружен
if draftProgressionValuePtr != nil {
response.DraftProgressionValue = draftProgressionValuePtr
}
// Устанавливаем DraftSubtasks если они были загружены
if len(draftSubtasks) > 0 {
response.DraftSubtasks = draftSubtasks
}
// Если задача связана с wishlist, загружаем базовую информацию о wishlist
if wishlistID.Valid {
var wishlistName string
@@ -6926,56 +7006,6 @@ func (a *App) getTaskDetailHandler(w http.ResponseWriter, r *http.Request) {
}
}
// Загружаем данные из драфта, если он существует
var draftProgressionValue sql.NullFloat64
err = a.DB.QueryRow(`
SELECT progression_value
FROM task_drafts
WHERE task_id = $1 AND user_id = $2
`, taskID, userID).Scan(&draftProgressionValue)
if err == nil {
// Драфт существует, загружаем данные
if draftProgressionValue.Valid {
response.DraftProgressionValue = &draftProgressionValue.Float64
}
// Загружаем подзадачи из драфта
draftSubtaskRows, err := a.DB.Query(`
SELECT subtask_id
FROM task_draft_subtasks
WHERE task_draft_id = (SELECT id FROM task_drafts WHERE task_id = $1 AND user_id = $2)
`, taskID, userID)
if err == nil {
defer draftSubtaskRows.Close()
draftSubtasks := make([]DraftSubtask, 0)
validSubtaskIDs := make(map[int]bool)
// Создаем map валидных подзадач для фильтрации
for _, subtask := range subtasks {
validSubtaskIDs[subtask.Task.ID] = true
}
for draftSubtaskRows.Next() {
var subtaskID int
if err := draftSubtaskRows.Scan(&subtaskID); err == nil {
// Игнорируем подзадачи, которых больше нет в основной задаче
if validSubtaskIDs[subtaskID] {
draftSubtasks = append(draftSubtasks, DraftSubtask{
SubtaskID: subtaskID,
})
}
}
}
if len(draftSubtasks) > 0 {
response.DraftSubtasks = draftSubtasks
}
} else if err != sql.ErrNoRows {
log.Printf("Error loading draft subtasks for task %d: %v", taskID, err)
}
} else if err != sql.ErrNoRows {
log.Printf("Error loading draft for task %d: %v", taskID, err)
}
// Если задача - тест (есть config_id), загружаем данные конфигурации
if configID.Valid {
var wordsCount int
@@ -7017,6 +7047,7 @@ func (a *App) getTaskDetailHandler(w http.ResponseWriter, r *http.Request) {
}
}
log.Printf("Task %d: Sending response with auto_complete = %v (task.AutoComplete = %v)", taskID, response.Task.AutoComplete, task.AutoComplete)
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}