fix(filter): make sure tasks are in a correct bucket and position when they are part of a date filter
Whenever a task is part of a date filter, it might fall in or out of a filter bucket without anything changing, other than the current time. For example, a filter condition like due_date > now may include different tasks depending on the current time. For these kinds of tasks to properly show up in the kanban view of a filter, there has to be an entry in the task_buckets table. These entries only got updated when either a task was updated or the filter itself was updated. To account for th changing of time, we also need to check periodically if tasks are now part or not anymore part of that filter. This change adds a cron task to do precisely that. We'll have to see if this works resource-wise, but the cron is not the only one doing a bunch of sql queries so it might be fine after all. Resolves https://community.vikunja.io/t/tasks-in-saved-filter-appear-in-list-view-but-are-not-visible-in-kanban-view/2800 (cherry picked from commit bc52da4029170bfd10cddea28cf5c6983969cb42)
This commit is contained in:
parent
d27b62db6e
commit
62c238e4bc
@ -96,6 +96,7 @@ func FullInit() {
|
||||
models.RegisterOldExportCleanupCron()
|
||||
openid.CleanupSavedOpenIDProviders()
|
||||
openid.RegisterEmptyOpenIDTeamCleanupCron()
|
||||
models.RegisterAddTaskToFilterViewCron()
|
||||
|
||||
// Start processing events
|
||||
go func() {
|
||||
|
@ -415,6 +415,6 @@ func RegisterOldExportCleanupCron() {
|
||||
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Could not old export cleanup cron: %s", err)
|
||||
log.Fatalf("Could not register old export cleanup cron: %s", err)
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,12 @@ package models
|
||||
import (
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/cron"
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
||||
"code.vikunja.io/api/pkg/web"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
@ -347,3 +349,166 @@ func addTaskToFilter(s *xorm.Session, filter *SavedFilter, view *ProjectView, fa
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func RegisterAddTaskToFilterViewCron() {
|
||||
const logPrefix = "[Add Task To Filter View Cron] "
|
||||
|
||||
err := cron.Schedule("* * * * *", func() {
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
// Get all filters with a date clause and a manual kanban view
|
||||
filters := map[int64]*SavedFilter{}
|
||||
err := s.Where("filters LIKE '%_date%'").Find(&filters)
|
||||
if err != nil {
|
||||
log.Errorf("%sError fetching filters: %s", logPrefix, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(filters) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
filterProjectIDs := []int64{}
|
||||
for _, f := range filters {
|
||||
filterProjectIDs = append(filterProjectIDs, getProjectIDFromSavedFilterID(f.ID))
|
||||
}
|
||||
|
||||
kanbanFilterViews := []*ProjectView{}
|
||||
err = s.And(
|
||||
builder.Eq{"view_kind": ProjectViewKindKanban},
|
||||
builder.Eq{"bucket_configuration_mode": BucketConfigurationModeManual},
|
||||
builder.In("project_id", filterProjectIDs),
|
||||
).
|
||||
Find(&kanbanFilterViews)
|
||||
if err != nil {
|
||||
log.Errorf("%sError fetching kanban filter views: %s", logPrefix, err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(kanbanFilterViews) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("%sFound %d kanban filter views with dates", logPrefix, len(kanbanFilterViews))
|
||||
|
||||
filterTasksCache := make(map[int64][]*Task)
|
||||
newTaskBuckets := []*TaskBucket{}
|
||||
newTaskPositions := []*TaskPosition{}
|
||||
deleteCond := []builder.Cond{}
|
||||
for _, view := range kanbanFilterViews {
|
||||
filterID := getSavedFilterIDFromProjectID(view.ProjectID)
|
||||
filter, exists := filters[filterID]
|
||||
if !exists {
|
||||
log.Debugf("%sDid not find filter for view %d", logPrefix, view.ID)
|
||||
continue
|
||||
}
|
||||
|
||||
// currently saved
|
||||
tasks, has := filterTasksCache[filterID]
|
||||
if !has {
|
||||
tc := &TaskCollection{
|
||||
ProjectID: view.ProjectID,
|
||||
}
|
||||
resultTasks, _, _, err := tc.ReadAll(s, &user.User{ID: filter.OwnerID}, "", 1, -1)
|
||||
if err != nil {
|
||||
log.Errorf("%sError fetching tasks for filter %d: %s", logPrefix, filterID, err)
|
||||
return
|
||||
}
|
||||
tasks = resultTasks.([]*Task)
|
||||
}
|
||||
|
||||
// Get saved tasks in task_buckets and task_positions
|
||||
savedTaskBuckets := []*TaskBucket{}
|
||||
err = s.Where("project_view_id = ?", view.ID).Find(&savedTaskBuckets)
|
||||
if err != nil {
|
||||
log.Errorf("%sError fetching saved task buckets: %s", logPrefix, err)
|
||||
continue
|
||||
}
|
||||
savedTaskBucketMap := make(map[int64]*TaskBucket)
|
||||
for _, tb := range savedTaskBuckets {
|
||||
savedTaskBucketMap[tb.TaskID] = tb
|
||||
}
|
||||
|
||||
savedTaskPositions := []*TaskPosition{}
|
||||
err = s.Where("project_view_id = ?", view.ID).Find(&savedTaskPositions)
|
||||
if err != nil {
|
||||
log.Errorf("%sError fetching saved task positions: %s", logPrefix, err)
|
||||
continue
|
||||
}
|
||||
savedTaskPositionMap := make(map[int64]*TaskPosition)
|
||||
for _, tp := range savedTaskPositions {
|
||||
savedTaskPositionMap[tp.TaskID] = tp
|
||||
}
|
||||
|
||||
// Collect new tasks to task_buckets and task_positions
|
||||
for _, task := range tasks {
|
||||
if _, exists := savedTaskBucketMap[task.ID]; !exists {
|
||||
view.DefaultBucketID, err = getDefaultBucketID(s, view)
|
||||
if err != nil {
|
||||
log.Errorf("%sError fetching default bucket for view %d: %s", logPrefix, view.ID, err)
|
||||
continue
|
||||
}
|
||||
tb := &TaskBucket{
|
||||
TaskID: task.ID,
|
||||
ProjectViewID: view.ID,
|
||||
BucketID: view.DefaultBucketID,
|
||||
}
|
||||
newTaskBuckets = append(newTaskBuckets, tb)
|
||||
}
|
||||
if _, exists := savedTaskPositionMap[task.ID]; !exists {
|
||||
tp := &TaskPosition{
|
||||
TaskID: task.ID,
|
||||
ProjectViewID: view.ID,
|
||||
Position: task.Position,
|
||||
}
|
||||
newTaskPositions = append(newTaskPositions, tp)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove tasks that should not be there
|
||||
for taskID := range savedTaskBucketMap {
|
||||
found := false
|
||||
for _, task := range tasks {
|
||||
if task.ID == taskID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
deleteCond = append(deleteCond, builder.And(
|
||||
builder.Eq{"task_id": taskID},
|
||||
builder.Eq{"project_view_id": view.ID},
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(newTaskBuckets) > 0 {
|
||||
_, err = s.Insert(newTaskBuckets)
|
||||
if err != nil {
|
||||
log.Errorf("%sError inserting task buckets: %s", logPrefix, err)
|
||||
}
|
||||
}
|
||||
if len(newTaskPositions) > 0 {
|
||||
_, err = s.Insert(newTaskPositions)
|
||||
if err != nil {
|
||||
log.Errorf("%sError inserting task positions: %s", logPrefix, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(deleteCond) > 0 {
|
||||
_, err = s.Where(builder.Or(deleteCond...)).Delete(&TaskBucket{})
|
||||
if err != nil {
|
||||
log.Errorf("%sError deleting task buckets: %s", logPrefix, err)
|
||||
}
|
||||
_, err = s.Where(builder.Or(deleteCond...)).Delete(&TaskPosition{})
|
||||
if err != nil {
|
||||
log.Errorf("%sError deleting task positions: %s", logPrefix, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Could register add task to filter view cron: %s", err)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user