1
0

fix(filters): add task to buckets of saved filters when creating the task

This commit is contained in:
kolaente 2024-07-17 12:10:34 +02:00
parent 327905bb90
commit 5cc420b289
No known key found for this signature in database
GPG Key ID: F40E70337AB24C9B
2 changed files with 140 additions and 1 deletions

View File

@ -23,7 +23,6 @@ import (
"time" "time"
"code.vikunja.io/api/pkg/config" "code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/db" "code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/events" "code.vikunja.io/api/pkg/events"
"code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/log"
@ -66,6 +65,7 @@ func RegisterListeners() {
events.RegisterListener((&TaskAttachmentDeletedEvent{}).Name(), &HandleTaskUpdateLastUpdated{}) events.RegisterListener((&TaskAttachmentDeletedEvent{}).Name(), &HandleTaskUpdateLastUpdated{})
events.RegisterListener((&TaskRelationCreatedEvent{}).Name(), &HandleTaskUpdateLastUpdated{}) events.RegisterListener((&TaskRelationCreatedEvent{}).Name(), &HandleTaskUpdateLastUpdated{})
events.RegisterListener((&TaskRelationDeletedEvent{}).Name(), &HandleTaskUpdateLastUpdated{}) events.RegisterListener((&TaskRelationDeletedEvent{}).Name(), &HandleTaskUpdateLastUpdated{})
events.RegisterListener((&TaskCreatedEvent{}).Name(), &UpdateTaskInSavedFilterViews{})
if config.TypesenseEnabled.GetBool() { if config.TypesenseEnabled.GetBool() {
events.RegisterListener((&TaskDeletedEvent{}).Name(), &RemoveTaskFromTypesense{}) events.RegisterListener((&TaskDeletedEvent{}).Name(), &RemoveTaskFromTypesense{})
events.RegisterListener((&TaskCreatedEvent{}).Name(), &AddTaskToTypesense{}) events.RegisterListener((&TaskCreatedEvent{}).Name(), &AddTaskToTypesense{})
@ -671,6 +671,91 @@ func (s *DecreaseAttachmentCounter) Handle(_ *message.Message) (err error) {
return keyvalue.DecrBy(metrics.AttachmentsCountKey, 1) return keyvalue.DecrBy(metrics.AttachmentsCountKey, 1)
} }
// UpdateTaskInSavedFilterViews represents a listener
type UpdateTaskInSavedFilterViews struct {
}
// Name defines the name for the UpdateTaskInSavedFilterViews listener
func (l *UpdateTaskInSavedFilterViews) Name() string {
return "task.set.saved.filter.views"
}
// Handle is executed when the event UpdateTaskInSavedFilterViews listens on is fired
func (l *UpdateTaskInSavedFilterViews) Handle(msg *message.Message) (err error) {
event := &TaskCreatedEvent{}
err = json.Unmarshal(msg.Payload, event)
if err != nil {
return err
}
// This operation is potentially very resource-heavy, because we don't know if a task is included
// in a filter until we evaluate that filter. We need to evaluate each filter individually - since
// there can be many filters, this can take a while to execute.
// For this reason, we do this in an asynchronous event listener.
s := db.NewSession()
defer s.Close()
// Get all saved filters with a manual kanban view
kanbanFilterViews := []*ProjectView{}
err = s.Where("project_id < 0 and view_kind = ? and bucket_configuration_mode = ?", ProjectViewKindKanban, BucketConfigurationModeManual).
Find(&kanbanFilterViews)
if err != nil {
return err
}
filterIDs := []int64{}
for _, view := range kanbanFilterViews {
filterIDs = append(filterIDs, getSavedFilterIDFromProjectID(view.ProjectID))
}
filters := map[int64]*SavedFilter{}
err = s.In("id", filterIDs).Find(&filters)
if err != nil {
return err
}
u, err := user.GetUserByID(s, event.Doer.GetID())
if err != nil {
return err
}
doerTimezone := u.Timezone
taskBuckets := []*TaskBucket{}
taskPositions := []*TaskPosition{}
for _, view := range kanbanFilterViews {
filter, exists := filters[getSavedFilterIDFromProjectID(view.ProjectID)]
if !exists {
log.Debugf("Did not find filter for view %d", view.ID)
continue
}
taskBucket, taskPosition, err := addTaskToFilter(s, filter, view, doerTimezone, event.Task)
if err != nil {
return err
}
if taskBucket != nil && taskPosition != nil {
taskBuckets = append(taskBuckets, taskBucket)
taskPositions = append(taskPositions, taskPosition)
}
}
if len(taskBuckets) > 0 || len(taskPositions) > 0 {
_, err = s.Insert(taskBuckets)
if err != nil {
return
}
_, err = s.Insert(taskPositions)
if err != nil {
return
}
}
return nil
}
/////// ///////
// Project Event Listeners // Project Event Listeners

View File

@ -18,8 +18,11 @@ package models
import ( import (
"time" "time"
"xorm.io/builder"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/user" "code.vikunja.io/api/pkg/user"
"code.vikunja.io/web" "code.vikunja.io/web"
"xorm.io/xorm" "xorm.io/xorm"
) )
@ -214,3 +217,54 @@ func (sf *SavedFilter) Delete(s *xorm.Session, _ web.Auth) error {
Delete(sf) Delete(sf)
return err return err
} }
func addTaskToFilter(s *xorm.Session, filter *SavedFilter, view *ProjectView, fallbackTimezone string, task *Task) (taskBucket *TaskBucket, taskPosition *TaskPosition, err error) {
filterString := filter.Filters.Filter
if filter.Filters.FilterTimezone == "" {
filter.Filters.FilterTimezone = fallbackTimezone
}
parsedFilters, err := getTaskFiltersFromFilterString(filterString, filter.Filters.FilterTimezone)
if err != nil {
log.Errorf("Could not parse filter string '%s' from view %d and saved filter %d: %v", filterString, view.ID, filter.ID, err)
return
}
filterCond, err := convertFiltersToDBFilterCond(parsedFilters, filter.Filters.FilterIncludeNulls)
if err != nil {
log.Errorf("Could not convert filter string '%s' from view %d and saved filter %d to db conditions: %v", filterString, view.ID, filter.ID, err)
return
}
taskIsInCurrentFilterAndView, err := s.Where(builder.And(
filterCond,
builder.Eq{"id": task.ID},
)).Exist(&Task{})
if !taskIsInCurrentFilterAndView {
return
}
if err != nil {
return nil, nil, err
}
bucketID, err := getDefaultBucketID(s, view)
if err != nil {
return nil, nil, err
}
taskBucket = &TaskBucket{
BucketID: bucketID,
TaskID: task.ID,
ProjectViewID: view.ID,
}
taskPosition = &TaskPosition{
TaskID: task.ID,
ProjectViewID: view.ID,
Position: calculateDefaultPosition(task.Index, task.Position),
}
return
}