Add support for migrating task comments

This commit is contained in:
kolaente 2021-09-03 22:41:11 +02:00
parent 9569265405
commit 1773fdf73d
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
13 changed files with 182 additions and 26 deletions

View File

@ -26,7 +26,7 @@ import (
"github.com/laurent22/ical-go"
)
func GetCaldavTodosForTasks(list *models.ListWithTasksAndBuckets, listTasks []*models.Task) string {
func GetCaldavTodosForTasks(list *models.ListWithTasksAndBuckets, listTasks []*models.TaskWithComments) string {
// Make caldav todos from Vikunja todos
var caldavtodos []*Todo

View File

@ -18,6 +18,7 @@
package models
import (
"archive/zip"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/cron"
"code.vikunja.io/api/pkg/db"
@ -27,8 +28,6 @@ import (
"code.vikunja.io/api/pkg/user"
"code.vikunja.io/api/pkg/utils"
"code.vikunja.io/api/pkg/version"
"archive/zip"
"encoding/json"
"fmt"
"io"
@ -146,7 +145,7 @@ func exportListsAndTasks(s *xorm.Session, u *user.User, wr *zip.Writer) (err err
nn.Lists = append(nn.Lists, &ListWithTasksAndBuckets{
List: *l,
BackgroundFileID: l.BackgroundFileID,
Tasks: []*Task{},
Tasks: []*TaskWithComments{},
})
}
@ -181,8 +180,25 @@ func exportListsAndTasks(s *xorm.Session, u *user.User, wr *zip.Writer) (err err
}
}
taskMap := make(map[int64]*TaskWithComments, len(tasks))
for _, t := range tasks {
listMap[t.ListID].Tasks = append(listMap[t.ListID].Tasks, t)
taskMap[t.ID] = &TaskWithComments{
Task: *t,
}
listMap[t.ListID].Tasks = append(listMap[t.ListID].Tasks, taskMap[t.ID])
}
comments := []*TaskComment{}
err = s.
Join("LEFT", "tasks", "tasks.id = task_comments.task_id").
In("tasks.list_id", listIDs).
Find(&comments)
if err != nil {
return
}
for _, c := range comments {
taskMap[c.TaskID].Comments = append(taskMap[c.TaskID].Comments, c)
}
buckets := []*Bucket{}

View File

@ -82,7 +82,7 @@ type List struct {
type ListWithTasksAndBuckets struct {
List
// An array of tasks which belong to the list.
Tasks []*Task `xorm:"-" json:"tasks"`
Tasks []*TaskWithComments `xorm:"-" json:"tasks"`
// Only used for migration.
Buckets []*Bucket `xorm:"-" json:"buckets"`
BackgroundFileID int64 `xorm:"null" json:"background_file_id"`

View File

@ -129,6 +129,11 @@ type Task struct {
web.Rights `xorm:"-" json:"-"`
}
type TaskWithComments struct {
Task
Comments []*TaskComment
}
// TableName returns the table name for listtasks
func (Task) TableName() string {
return "tasks"

View File

@ -129,7 +129,7 @@ func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTas
// Create all tasks
for _, t := range tasks {
setBucketOrDefault(t)
setBucketOrDefault(&t.Task)
t.ListID = l.ID
err = t.Create(s, user)
@ -221,6 +221,15 @@ func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTas
}
log.Debugf("[creating structure] Associated task %d with label %d", t.ID, lb.ID)
}
for _, comment := range t.Comments {
comment.TaskID = t.ID
err = comment.Create(s, user)
if err != nil {
return
}
log.Debugf("[creating structure] Created new comment %d", comment.ID)
}
}
// All tasks brought their own bucket with them, therefore the newly created default bucket is just extra space

View File

@ -342,7 +342,7 @@ func convertMicrosoftTodoData(todoData []*list) (vikunjsStructure []*models.Name
}
}
list.Tasks = append(list.Tasks, task)
list.Tasks = append(list.Tasks, &models.TaskWithComments{Task: *task})
log.Debugf("[Microsoft Todo Migration] Done converted %d tasks", len(l.Tasks))
}

View File

@ -264,7 +264,7 @@ func convertTodoistToVikunja(sync *sync, doneItems map[int64]*doneItem) (fullVik
lists := make(map[int64]*models.ListWithTasksAndBuckets, len(sync.Projects))
// A map for all vikunja tasks with the todoist task id as key to find them easily and add more data
tasks := make(map[int64]*models.Task, len(sync.Items))
tasks := make(map[int64]*models.TaskWithComments, len(sync.Items))
// A map for all vikunja labels with the todoist id as key to find them easier
labels := make(map[int64]*models.Label, len(sync.Labels))
@ -307,11 +307,13 @@ func convertTodoistToVikunja(sync *sync, doneItems map[int64]*doneItem) (fullVik
}
for _, i := range sync.Items {
task := &models.Task{
Title: i.Content,
Created: i.DateAdded.In(config.GetTimeZone()),
Done: i.Checked == 1,
BucketID: i.SectionID,
task := &models.TaskWithComments{
Task: models.Task{
Title: i.Content,
Created: i.DateAdded.In(config.GetTimeZone()),
Done: i.Checked == 1,
BucketID: i.SectionID,
},
}
// Only try to parse the task done at date if the task is actually done
@ -367,7 +369,7 @@ func convertTodoistToVikunja(sync *sync, doneItems map[int64]*doneItem) (fullVik
tasks[i.ParentID].RelatedTasks = make(models.RelatedTaskMap)
}
tasks[i.ParentID].RelatedTasks[models.RelationKindSubtask] = append(tasks[i.ParentID].RelatedTasks[models.RelationKindSubtask], tasks[i.ID])
tasks[i.ParentID].RelatedTasks[models.RelationKindSubtask] = append(tasks[i.ParentID].RelatedTasks[models.RelationKindSubtask], &tasks[i.ID].Task)
// Remove the task from the top level structure, otherwise it is added twice
outer:

View File

@ -271,7 +271,7 @@ func convertTrelloDataToVikunja(trelloData []*trello.Board) (fullVikunjaHierachi
log.Debugf("[Trello Migration] Downloaded card attachment %s", attachment.ID)
}
list.Tasks = append(list.Tasks, task)
list.Tasks = append(list.Tasks, &models.TaskWithComments{Task: *task})
}
list.Buckets = append(list.Buckets, bucket)

View File

@ -0,0 +1,44 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public Licensee as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public Licensee for more details.
//
// You should have received a copy of the GNU Affero General Public Licensee
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package vikunja_file
import (
"os"
"testing"
"code.vikunja.io/api/pkg/events"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/models"
"code.vikunja.io/api/pkg/user"
)
// TestMain is the main test function used to bootstrap the test env
func TestMain(m *testing.M) {
// Set default config
config.InitDefaultConfig()
// We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly
config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH"))
// Some tests use the file engine, so we'll need to initialize that
files.InitTests()
user.InitTests()
models.SetupTests()
events.Fake()
os.Exit(m.Run())
}

View File

@ -83,12 +83,12 @@ func (v *VikunjaFileMigrator) Migrate(user *user.User, file io.ReaderAt, size in
for _, n := range namespaces {
for _, l := range n.Lists {
for _, t := range l.Tasks {
if len(t.Labels) == 0 {
continue
}
for _, label := range t.Labels {
label.ID = 0
}
for _, comment := range t.Comments {
comment.ID = 0
}
}
}
}

View File

@ -0,0 +1,78 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public Licensee as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public Licensee for more details.
//
// You should have received a copy of the GNU Affero General Public Licensee
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package vikunja_file
import (
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/user"
"github.com/stretchr/testify/assert"
"os"
"testing"
)
func TestVikunjaFileMigrator_Migrate(t *testing.T) {
db.LoadAndAssertFixtures(t)
m := &VikunjaFileMigrator{}
u := &user.User{ID: 1}
f, err := os.Open(config.ServiceRootpath.GetString() + "/pkg/modules/migration/vikunja-file/export.zip")
if err != nil {
t.Fatalf("Could not open file: %s", err)
}
defer f.Close()
s, err := f.Stat()
if err != nil {
t.Fatalf("Could not stat file: %s", err)
}
err = m.Migrate(u, f, s.Size())
assert.NoError(t, err)
db.AssertExists(t, "namespaces", map[string]interface{}{
"title": "test",
"owner_id": u.ID,
}, false)
db.AssertExists(t, "lists", map[string]interface{}{
"title": "Test list",
"owner_id": u.ID,
}, false)
db.AssertExists(t, "lists", map[string]interface{}{
"title": "A list with a background",
"owner_id": u.ID,
}, false)
db.AssertExists(t, "tasks", map[string]interface{}{
"title": "Some other task",
"created_by_id": u.ID,
}, false)
db.AssertExists(t, "task_comments", map[string]interface{}{
"comment": "This is a comment",
"author_id": u.ID,
}, false)
db.AssertExists(t, "files", map[string]interface{}{
"name": "cristiano-mozzillo-v3d5uBB26yA-unsplash.jpg",
"created_by_id": u.ID,
}, false)
db.AssertExists(t, "labels", map[string]interface{}{
"title": "test",
"created_by_id": u.ID,
}, false)
db.AssertExists(t, "buckets", map[string]interface{}{
"title": "Test Bucket",
"created_by_id": u.ID,
}, false)
}

View File

@ -235,7 +235,7 @@ func convertListForFolder(listID int, list *list, content *wunderlistContents) (
}
}
l.Tasks = append(l.Tasks, newTask)
l.Tasks = append(l.Tasks, &models.TaskWithComments{Task: *newTask})
}
}
return l, nil

View File

@ -174,10 +174,10 @@ func (vcls *VikunjaCaldavListStorage) GetResourcesByFilters(rpath string, filter
for _, t := range vcls.list.Tasks {
rr := VikunjaListResourceAdapter{
list: vcls.list,
task: t,
task: &t.Task,
isCollection: false,
}
r := data.NewResource(getTaskURL(t), &rr)
r := data.NewResource(getTaskURL(&t.Task), &rr)
r.Name = t.Title
resources = append(resources, r)
}
@ -371,7 +371,7 @@ func (vcls *VikunjaCaldavListStorage) DeleteResource(rpath string) error {
// VikunjaListResourceAdapter holds the actual resource
type VikunjaListResourceAdapter struct {
list *models.ListWithTasksAndBuckets
listTasks []*models.Task
listTasks []*models.TaskWithComments
task *models.Task
isPrincipal bool
@ -417,7 +417,7 @@ func (vlra *VikunjaListResourceAdapter) GetContent() string {
}
if vlra.task != nil {
list := models.ListWithTasksAndBuckets{Tasks: []*models.Task{vlra.task}}
list := models.ListWithTasksAndBuckets{Tasks: []*models.TaskWithComments{{Task: *vlra.task}}}
return caldav.GetCaldavTodosForTasks(&list, list.Tasks)
}
@ -481,8 +481,10 @@ func (vcls *VikunjaCaldavListStorage) getListRessource(isCollection bool) (rr Vi
panic("Tasks returned from TaskCollection.ReadAll are not []*models.Task!")
}
listTasks = tasks
vcls.list.Tasks = tasks
for _, t := range tasks {
listTasks = append(listTasks, &models.TaskWithComments{Task: *t})
}
vcls.list.Tasks = listTasks
}
if err := s.Commit(); err != nil {