1
0

feat: rename lists to projects

This commit is contained in:
kolaente
2022-11-13 17:07:01 +01:00
parent 80266d1383
commit 349e6a5905
113 changed files with 2753 additions and 2750 deletions

View File

@ -31,7 +31,7 @@ import (
// InsertFromStructure takes a fully nested Vikunja data structure and a user and then creates everything for this user
// (Namespaces, tasks, etc. Even attachments and relations.)
func InsertFromStructure(str []*models.NamespaceWithListsAndTasks, user *user.User) (err error) {
func InsertFromStructure(str []*models.NamespaceWithProjectsAndTasks, user *user.User) (err error) {
s := db.NewSession()
defer s.Close()
@ -45,13 +45,13 @@ func InsertFromStructure(str []*models.NamespaceWithListsAndTasks, user *user.Us
return s.Commit()
}
func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTasks, user *user.User) (err error) {
func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithProjectsAndTasks, user *user.User) (err error) {
log.Debugf("[creating structure] Creating %d namespaces", len(str))
labels := make(map[string]*models.Label)
archivedLists := []int64{}
archivedProjects := []int64{}
archivedNamespaces := []int64{}
// Create all namespaces
@ -75,18 +75,18 @@ func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTas
}
log.Debugf("[creating structure] Created namespace %d", n.ID)
log.Debugf("[creating structure] Creating %d lists", len(n.Lists))
log.Debugf("[creating structure] Creating %d projects", len(n.Projects))
// Create all lists
for _, l := range n.Lists {
// The tasks and bucket slices are going to be reset during the creation of the list so we rescue it here
// to be able to still loop over them aftere the list was created.
// Create all projects
for _, l := range n.Projects {
// The tasks and bucket slices are going to be reset during the creation of the project so we rescue it here
// to be able to still loop over them aftere the project was created.
tasks := l.Tasks
originalBuckets := l.Buckets
originalBackgroundInformation := l.BackgroundInformation
needsDefaultBucket := false
// Saving the archived status to archive the list again after creating it
// Saving the archived status to archive the project again after creating it
var wasArchived bool
if l.IsArchived {
wasArchived = true
@ -101,24 +101,24 @@ func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTas
}
if wasArchived {
archivedLists = append(archivedLists, l.ID)
archivedProjects = append(archivedProjects, l.ID)
}
log.Debugf("[creating structure] Created list %d", l.ID)
log.Debugf("[creating structure] Created project %d", l.ID)
bf, is := originalBackgroundInformation.(*bytes.Buffer)
if is {
backgroundFile := bytes.NewReader(bf.Bytes())
log.Debugf("[creating structure] Creating a background file for list %d", l.ID)
log.Debugf("[creating structure] Creating a background file for project %d", l.ID)
err = handler.SaveBackgroundFile(s, user, &l.List, backgroundFile, "", uint64(backgroundFile.Len()))
err = handler.SaveBackgroundFile(s, user, &l.Project, backgroundFile, "", uint64(backgroundFile.Len()))
if err != nil {
return err
}
log.Debugf("[creating structure] Created a background file for list %d", l.ID)
log.Debugf("[creating structure] Created a background file for project %d", l.ID)
}
// Create all buckets
@ -129,7 +129,7 @@ func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTas
for _, bucket := range originalBuckets {
oldID := bucket.ID
bucket.ID = 0 // We want a new id
bucket.ListID = l.ID
bucket.ProjectID = l.ID
err = bucket.Create(s, user)
if err != nil {
return
@ -157,7 +157,7 @@ func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTas
for _, t := range tasks {
setBucketOrDefault(&t.Task)
t.ListID = l.ID
t.ProjectID = l.ID
err = t.Create(s, user)
if err != nil {
return
@ -179,7 +179,7 @@ func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTas
// First create the related tasks if they do not exist
if rt.ID == 0 {
setBucketOrDefault(rt)
rt.ListID = t.ListID
rt.ProjectID = t.ProjectID
err = rt.Create(s, user)
if err != nil {
return
@ -263,7 +263,7 @@ func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTas
// All tasks brought their own bucket with them, therefore the newly created default bucket is just extra space
if !needsDefaultBucket {
b := &models.Bucket{ListID: l.ID}
b := &models.Bucket{ProjectID: l.ID}
bucketsIn, _, _, err := b.ReadAll(s, user, "", 1, 1)
if err != nil {
return err
@ -280,11 +280,11 @@ func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTas
}
}
if len(archivedLists) > 0 {
if len(archivedProjects) > 0 {
_, err = s.
Cols("is_archived").
In("id", archivedLists).
Update(&models.List{IsArchived: true})
In("id", archivedProjects).
Update(&models.Project{IsArchived: true})
if err != nil {
return err
}

View File

@ -32,16 +32,16 @@ func TestInsertFromStructure(t *testing.T) {
}
t.Run("normal", func(t *testing.T) {
db.LoadAndAssertFixtures(t)
testStructure := []*models.NamespaceWithListsAndTasks{
testStructure := []*models.NamespaceWithProjectsAndTasks{
{
Namespace: models.Namespace{
Title: "Test1",
Description: "Lorem Ipsum",
},
Lists: []*models.ListWithTasksAndBuckets{
Projects: []*models.ProjectWithTasksAndBuckets{
{
List: models.List{
Title: "Testlist1",
Project: models.Project{
Title: "Testproject1",
Description: "Something",
},
Buckets: []*models.Bucket{
@ -133,19 +133,19 @@ func TestInsertFromStructure(t *testing.T) {
"title": testStructure[0].Namespace.Title,
"description": testStructure[0].Namespace.Description,
}, false)
db.AssertExists(t, "lists", map[string]interface{}{
"title": testStructure[0].Lists[0].Title,
"description": testStructure[0].Lists[0].Description,
db.AssertExists(t, "projects", map[string]interface{}{
"title": testStructure[0].Projects[0].Title,
"description": testStructure[0].Projects[0].Description,
}, false)
db.AssertExists(t, "tasks", map[string]interface{}{
"title": testStructure[0].Lists[0].Tasks[5].Title,
"bucket_id": testStructure[0].Lists[0].Buckets[0].ID,
"title": testStructure[0].Projects[0].Tasks[5].Title,
"bucket_id": testStructure[0].Projects[0].Buckets[0].ID,
}, false)
db.AssertMissing(t, "tasks", map[string]interface{}{
"title": testStructure[0].Lists[0].Tasks[6].Title,
"title": testStructure[0].Projects[0].Tasks[6].Title,
"bucket_id": 1111, // No task with that bucket should exist
})
assert.NotEqual(t, 0, testStructure[0].Lists[0].Tasks[0].BucketID) // Should get the default bucket
assert.NotEqual(t, 0, testStructure[0].Lists[0].Tasks[6].BucketID) // Should get the default bucket
assert.NotEqual(t, 0, testStructure[0].Projects[0].Tasks[0].BucketID) // Should get the default bucket
assert.NotEqual(t, 0, testStructure[0].Projects[0].Tasks[6].BucketID) // Should get the default bucket
})
}

View File

@ -98,19 +98,19 @@ type tasksResponse struct {
Value []*task `json:"value"`
}
type list struct {
ID string `json:"id"`
OdataEtag string `json:"@odata.etag"`
DisplayName string `json:"displayName"`
IsOwner bool `json:"isOwner"`
IsShared bool `json:"isShared"`
WellknownListName string `json:"wellknownListName"`
Tasks []*task `json:"-"` // This field does not exist in the api, we're just using it to return a structure with everything at once
type project struct {
ID string `json:"id"`
OdataEtag string `json:"@odata.etag"`
DisplayName string `json:"displayName"`
IsOwner bool `json:"isOwner"`
IsShared bool `json:"isShared"`
WellknownProjectName string `json:"wellknownProjectName"`
Tasks []*task `json:"-"` // This field does not exist in the api, we're just using it to return a structure with everything at once
}
type listsResponse struct {
OdataContext string `json:"@odata.context"`
Value []*list `json:"value"`
type projectsResponse struct {
OdataContext string `json:"@odata.context"`
Value []*project `json:"value"`
}
func (dtt *dateTimeTimeZone) toTime() (t time.Time, err error) {
@ -213,22 +213,22 @@ func makeAuthenticatedGetRequest(token, urlPart string, v interface{}) error {
return json.Unmarshal(buf.Bytes(), v)
}
func getMicrosoftTodoData(token string) (microsoftTodoData []*list, err error) {
func getMicrosoftTodoData(token string) (microsoftTodoData []*project, err error) {
microsoftTodoData = []*list{}
microsoftTodoData = []*project{}
lists := &listsResponse{}
err = makeAuthenticatedGetRequest(token, "lists", lists)
projects := &projectsResponse{}
err = makeAuthenticatedGetRequest(token, "projects", projects)
if err != nil {
log.Errorf("[Microsoft Todo Migration] Could not get lists: %s", err)
log.Errorf("[Microsoft Todo Migration] Could not get projects: %s", err)
return
}
log.Debugf("[Microsoft Todo Migration] Got %d lists", len(lists.Value))
log.Debugf("[Microsoft Todo Migration] Got %d projects", len(projects.Value))
for _, list := range lists.Value {
link := "lists/" + list.ID + "/tasks"
list.Tasks = []*task{}
for _, project := range projects.Value {
link := "projects/" + project.ID + "/tasks"
project.Tasks = []*task{}
// Microsoft's Graph API has pagination, so we're going through all pages to get all tasks
for {
@ -236,13 +236,13 @@ func getMicrosoftTodoData(token string) (microsoftTodoData []*list, err error) {
err = makeAuthenticatedGetRequest(token, link, tr)
if err != nil {
log.Errorf("[Microsoft Todo Migration] Could not get tasks for list %s: %s", list.ID, err)
log.Errorf("[Microsoft Todo Migration] Could not get tasks for project %s: %s", project.ID, err)
return
}
log.Debugf("[Microsoft Todo Migration] Got %d tasks for list %s", len(tr.Value), list.ID)
log.Debugf("[Microsoft Todo Migration] Got %d tasks for project %s", len(tr.Value), project.ID)
list.Tasks = append(list.Tasks, tr.Value...)
project.Tasks = append(project.Tasks, tr.Value...)
if tr.Nextlink == "" {
break
@ -251,35 +251,35 @@ func getMicrosoftTodoData(token string) (microsoftTodoData []*list, err error) {
link = strings.ReplaceAll(tr.Nextlink, apiPrefix, "")
}
microsoftTodoData = append(microsoftTodoData, list)
microsoftTodoData = append(microsoftTodoData, project)
}
log.Debugf("[Microsoft Todo Migration] Got all tasks for %d lists", len(lists.Value))
log.Debugf("[Microsoft Todo Migration] Got all tasks for %d projects", len(projects.Value))
return
}
func convertMicrosoftTodoData(todoData []*list) (vikunjsStructure []*models.NamespaceWithListsAndTasks, err error) {
func convertMicrosoftTodoData(todoData []*project) (vikunjsStructure []*models.NamespaceWithProjectsAndTasks, err error) {
// One namespace with all lists
vikunjsStructure = []*models.NamespaceWithListsAndTasks{
// One namespace with all projects
vikunjsStructure = []*models.NamespaceWithProjectsAndTasks{
{
Namespace: models.Namespace{
Title: "Migrated from Microsoft Todo",
},
Lists: []*models.ListWithTasksAndBuckets{},
Projects: []*models.ProjectWithTasksAndBuckets{},
},
}
log.Debugf("[Microsoft Todo Migration] Converting %d lists", len(todoData))
log.Debugf("[Microsoft Todo Migration] Converting %d projects", len(todoData))
for _, l := range todoData {
log.Debugf("[Microsoft Todo Migration] Converting list %s", l.ID)
log.Debugf("[Microsoft Todo Migration] Converting project %s", l.ID)
// Lists only with title
list := &models.ListWithTasksAndBuckets{
List: models.List{
// Projects only with title
project := &models.ProjectWithTasksAndBuckets{
Project: models.Project{
Title: l.DisplayName,
},
}
@ -358,19 +358,19 @@ func convertMicrosoftTodoData(todoData []*list) (vikunjsStructure []*models.Name
}
}
list.Tasks = append(list.Tasks, &models.TaskWithComments{Task: *task})
project.Tasks = append(project.Tasks, &models.TaskWithComments{Task: *task})
log.Debugf("[Microsoft Todo Migration] Done converted %d tasks", len(l.Tasks))
}
vikunjsStructure[0].Lists = append(vikunjsStructure[0].Lists, list)
log.Debugf("[Microsoft Todo Migration] Done converting list %s", l.ID)
vikunjsStructure[0].Projects = append(vikunjsStructure[0].Projects, project)
log.Debugf("[Microsoft Todo Migration] Done converting project %s", l.ID)
}
return
}
// Migrate gets all tasks from Microsoft Todo for a user and puts them into vikunja
// @Summary Migrate all lists, tasks etc. from Microsoft Todo
// @Summary Migrate all projects, tasks etc. from Microsoft Todo
// @Description Migrates all tasklinsts, tasks, notes and reminders from Microsoft Todo to Vikunja.
// @tags migration
// @Accept json

View File

@ -35,9 +35,9 @@ func TestConverting(t *testing.T) {
testtimeTime, err := time.Parse(time.RFC3339Nano, "2020-12-18T03:00:00.4770000Z")
assert.NoError(t, err)
microsoftTodoData := []*list{
microsoftTodoData := []*project{
{
DisplayName: "List 1",
DisplayName: "Project 1",
Tasks: []*task{
{
Title: "Task 1",
@ -88,7 +88,7 @@ func TestConverting(t *testing.T) {
},
},
{
DisplayName: "List 2",
DisplayName: "Project 2",
Tasks: []*task{
{
Title: "Task 1",
@ -102,15 +102,15 @@ func TestConverting(t *testing.T) {
},
}
expectedHierachie := []*models.NamespaceWithListsAndTasks{
expectedHierachie := []*models.NamespaceWithProjectsAndTasks{
{
Namespace: models.Namespace{
Title: "Migrated from Microsoft Todo",
},
Lists: []*models.ListWithTasksAndBuckets{
Projects: []*models.ProjectWithTasksAndBuckets{
{
List: models.List{
Title: "List 1",
Project: models.Project{
Title: "Project 1",
},
Tasks: []*models.TaskWithComments{
{
@ -162,8 +162,8 @@ func TestConverting(t *testing.T) {
},
},
{
List: models.List{
Title: "List 2",
Project: models.Project{
Title: "Project 2",
},
Tasks: []*models.TaskWithComments{
{

View File

@ -43,7 +43,7 @@ type Migrator interface {
// FileMigrator handles importing Vikunja data from a file. The implementation of it determines the format.
type FileMigrator interface {
MigratorName
// Migrate is the interface used to migrate a user's tasks, list and other things from a file to vikunja.
// Migrate is the interface used to migrate a user's tasks, project and other things from a file to vikunja.
// The user object is the user who's tasks will be migrated.
Migrate(user *user.User, file io.ReaderAt, size int64) error
}

View File

@ -105,21 +105,21 @@ func parseDurationPart(value string, unit time.Duration) time.Duration {
return 0
}
func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.NamespaceWithListsAndTasks) {
namespace := &models.NamespaceWithListsAndTasks{
func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.NamespaceWithProjectsAndTasks) {
namespace := &models.NamespaceWithProjectsAndTasks{
Namespace: models.Namespace{
Title: "Migrated from TickTick",
},
Lists: []*models.ListWithTasksAndBuckets{},
Projects: []*models.ProjectWithTasksAndBuckets{},
}
lists := make(map[string]*models.ListWithTasksAndBuckets)
projects := make(map[string]*models.ProjectWithTasksAndBuckets)
for _, t := range tasks {
_, has := lists[t.ListName]
_, has := projects[t.ProjectName]
if !has {
lists[t.ListName] = &models.ListWithTasksAndBuckets{
List: models.List{
Title: t.ListName,
projects[t.ProjectName] = &models.ProjectWithTasksAndBuckets{
Project: models.Project{
Title: t.ProjectName,
},
}
}
@ -158,18 +158,18 @@ func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.Namespace
}
}
lists[t.ListName].Tasks = append(lists[t.ListName].Tasks, task)
projects[t.ProjectName].Tasks = append(projects[t.ProjectName].Tasks, task)
}
for _, l := range lists {
namespace.Lists = append(namespace.Lists, l)
for _, l := range projects {
namespace.Projects = append(namespace.Projects, l)
}
sort.Slice(namespace.Lists, func(i, j int) bool {
return namespace.Lists[i].Title < namespace.Lists[j].Title
sort.Slice(namespace.Projects, func(i, j int) bool {
return namespace.Projects[i].Title < namespace.Projects[j].Title
})
return []*models.NamespaceWithListsAndTasks{namespace}
return []*models.NamespaceWithProjectsAndTasks{namespace}
}
// Name is used to get the name of the ticktick migration - we're using the docs here to annotate the status route.
@ -202,7 +202,7 @@ func newLineSkipDecoder(r io.Reader, linesToSkip int) gocsv.SimpleDecoder {
}
// Migrate takes a ticktick export, parses it and imports everything in it into Vikunja.
// @Summary Import all lists, tasks etc. from a TickTick backup export
// @Summary Import all projects, tasks etc. from a TickTick backup export
// @Description Imports all projects, tasks, notes, reminders, subtasks and files from a TickTick backup export into Vikunja.
// @tags migration
// @Accept json

View File

@ -40,76 +40,76 @@ func TestConvertTicktickTasksToVikunja(t *testing.T) {
tickTickTasks := []*tickTickTask{
{
TaskID: 1,
ParentID: 0,
ListName: "List 1",
Title: "Test task 1",
Tags: []string{"label1", "label2"},
Content: "Lorem Ipsum Dolor sit amet",
StartDate: time1,
DueDate: time2,
Reminder: duration,
Repeat: "FREQ=WEEKLY;INTERVAL=1;UNTIL=20190117T210000Z",
Status: "0",
Order: -1099511627776,
TaskID: 1,
ParentID: 0,
ProjectName: "Project 1",
Title: "Test task 1",
Tags: []string{"label1", "label2"},
Content: "Lorem Ipsum Dolor sit amet",
StartDate: time1,
DueDate: time2,
Reminder: duration,
Repeat: "FREQ=WEEKLY;INTERVAL=1;UNTIL=20190117T210000Z",
Status: "0",
Order: -1099511627776,
},
{
TaskID: 2,
ParentID: 1,
ListName: "List 1",
ProjectName: "Project 1",
Title: "Test task 2",
Status: "1",
CompletedTime: time3,
Order: -1099511626,
},
{
TaskID: 3,
ParentID: 0,
ListName: "List 1",
Title: "Test task 3",
Tags: []string{"label1", "label2", "other label"},
StartDate: time1,
DueDate: time2,
Reminder: duration,
Status: "0",
Order: -109951627776,
TaskID: 3,
ParentID: 0,
ProjectName: "Project 1",
Title: "Test task 3",
Tags: []string{"label1", "label2", "other label"},
StartDate: time1,
DueDate: time2,
Reminder: duration,
Status: "0",
Order: -109951627776,
},
{
TaskID: 4,
ParentID: 0,
ListName: "List 2",
Title: "Test task 4",
Status: "0",
Order: -109951627777,
TaskID: 4,
ParentID: 0,
ProjectName: "Project 2",
Title: "Test task 4",
Status: "0",
Order: -109951627777,
},
}
vikunjaTasks := convertTickTickToVikunja(tickTickTasks)
assert.Len(t, vikunjaTasks, 1)
assert.Len(t, vikunjaTasks[0].Lists, 2)
assert.Len(t, vikunjaTasks[0].Projects, 2)
assert.Len(t, vikunjaTasks[0].Lists[0].Tasks, 3)
assert.Equal(t, vikunjaTasks[0].Lists[0].Title, tickTickTasks[0].ListName)
assert.Len(t, vikunjaTasks[0].Projects[0].Tasks, 3)
assert.Equal(t, vikunjaTasks[0].Projects[0].Title, tickTickTasks[0].ProjectName)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Title, tickTickTasks[0].Title)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Description, tickTickTasks[0].Content)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].StartDate, tickTickTasks[0].StartDate.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].EndDate, tickTickTasks[0].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].DueDate, tickTickTasks[0].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Labels, []*models.Label{
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Title, tickTickTasks[0].Title)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Description, tickTickTasks[0].Content)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].StartDate, tickTickTasks[0].StartDate)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].EndDate, tickTickTasks[0].DueDate)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].DueDate, tickTickTasks[0].DueDate)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Labels, []*models.Label{
{Title: "label1"},
{Title: "label2"},
})
//assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Reminders, tickTickTasks[0].) // TODO
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Position, tickTickTasks[0].Order)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Done, false)
//assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Reminders, tickTickTasks[0].) // TODO
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Position, tickTickTasks[0].Order)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Done, false)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].Title, tickTickTasks[1].Title)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].Position, tickTickTasks[1].Order)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].Done, true)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].DoneAt, tickTickTasks[1].CompletedTime.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[1].RelatedTasks, models.RelatedTaskMap{
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[1].Title, tickTickTasks[1].Title)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[1].Position, tickTickTasks[1].Order)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[1].Done, true)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[1].DoneAt, tickTickTasks[1].CompletedTime.Time)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[1].RelatedTasks, models.RelatedTaskMap{
models.RelationKindParenttask: []*models.Task{
{
ID: tickTickTasks[1].ParentID,
@ -117,23 +117,23 @@ func TestConvertTicktickTasksToVikunja(t *testing.T) {
},
})
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].Title, tickTickTasks[2].Title)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].Description, tickTickTasks[2].Content)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].StartDate, tickTickTasks[2].StartDate.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].EndDate, tickTickTasks[2].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].DueDate, tickTickTasks[2].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].Labels, []*models.Label{
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].Title, tickTickTasks[2].Title)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].Description, tickTickTasks[2].Content)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].StartDate, tickTickTasks[2].StartDate.Time)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].EndDate, tickTickTasks[2].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].DueDate, tickTickTasks[2].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].Labels, []*models.Label{
{Title: "label1"},
{Title: "label2"},
{Title: "other label"},
})
//assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[0].Reminders, tickTickTasks[0].) // TODO
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].Position, tickTickTasks[2].Order)
assert.Equal(t, vikunjaTasks[0].Lists[0].Tasks[2].Done, false)
//assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[0].Reminders, tickTickTasks[0].) // TODO
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].Position, tickTickTasks[2].Order)
assert.Equal(t, vikunjaTasks[0].Projects[0].Tasks[2].Done, false)
assert.Len(t, vikunjaTasks[0].Lists[1].Tasks, 1)
assert.Equal(t, vikunjaTasks[0].Lists[1].Title, tickTickTasks[3].ListName)
assert.Len(t, vikunjaTasks[0].Projects[1].Tasks, 1)
assert.Equal(t, vikunjaTasks[0].Projects[1].Title, tickTickTasks[3].ProjectName)
assert.Equal(t, vikunjaTasks[0].Lists[1].Tasks[0].Title, tickTickTasks[3].Title)
assert.Equal(t, vikunjaTasks[0].Lists[1].Tasks[0].Position, tickTickTasks[3].Order)
assert.Equal(t, vikunjaTasks[0].Projects[1].Tasks[0].Title, tickTickTasks[3].Title)
assert.Equal(t, vikunjaTasks[0].Projects[1].Tasks[0].Position, tickTickTasks[3].Order)
}

View File

@ -363,14 +363,14 @@ func TestConvertTodoistToVikunja(t *testing.T) {
},
}
expectedHierachie := []*models.NamespaceWithListsAndTasks{
expectedHierachie := []*models.NamespaceWithProjectsAndTasks{
{
Namespace: models.Namespace{
Title: "Migrated from todoist",
},
Lists: []*models.ListWithTasksAndBuckets{
Projects: []*models.ProjectWithTasksAndBuckets{
{
List: models.List{
Project: models.Project{
Title: "Project1",
Description: "Lorem Ipsum dolor sit amet\nLorem Ipsum dolor sit amet 2\nLorem Ipsum dolor sit amet 3",
HexColor: todoistColors["berry_red"],
@ -504,7 +504,7 @@ func TestConvertTodoistToVikunja(t *testing.T) {
},
},
{
List: models.List{
Project: models.Project{
Title: "Project2",
Description: "Lorem Ipsum dolor sit amet 4\nLorem Ipsum dolor sit amet 5",
HexColor: todoistColors["mint_green"],
@ -602,7 +602,7 @@ func TestConvertTodoistToVikunja(t *testing.T) {
},
},
{
List: models.List{
Project: models.Project{
Title: "Project3 - Archived",
HexColor: todoistColors["mint_green"],
IsArchived: true,

View File

@ -99,14 +99,14 @@ func getTrelloData(token string) (trelloData []*trello.Board, err error) {
log.Debugf("[Trello Migration] Got %d trello boards", len(trelloData))
for _, board := range trelloData {
log.Debugf("[Trello Migration] Getting lists for board %s", board.ID)
log.Debugf("[Trello Migration] Getting projects for board %s", board.ID)
board.Lists, err = board.GetLists(trello.Defaults())
if err != nil {
return
}
log.Debugf("[Trello Migration] Got %d lists for board %s", len(board.Lists), board.ID)
log.Debugf("[Trello Migration] Got %d projects for board %s", len(board.Lists), board.ID)
listMap := make(map[string]*trello.List, len(board.Lists))
for _, list := range board.Lists {
@ -161,27 +161,27 @@ func getTrelloData(token string) (trelloData []*trello.Board, err error) {
}
// Converts all previously obtained data from trello into the vikunja format.
// `trelloData` should contain all boards with their lists and cards respectively.
func convertTrelloDataToVikunja(trelloData []*trello.Board, token string) (fullVikunjaHierachie []*models.NamespaceWithListsAndTasks, err error) {
// `trelloData` should contain all boards with their projects and cards respectively.
func convertTrelloDataToVikunja(trelloData []*trello.Board, token string) (fullVikunjaHierachie []*models.NamespaceWithProjectsAndTasks, err error) {
log.Debugf("[Trello Migration] ")
fullVikunjaHierachie = []*models.NamespaceWithListsAndTasks{
fullVikunjaHierachie = []*models.NamespaceWithProjectsAndTasks{
{
Namespace: models.Namespace{
Title: "Imported from Trello",
},
Lists: []*models.ListWithTasksAndBuckets{},
Projects: []*models.ProjectWithTasksAndBuckets{},
},
}
var bucketID int64 = 1
log.Debugf("[Trello Migration] Converting %d boards to vikunja lists", len(trelloData))
log.Debugf("[Trello Migration] Converting %d boards to vikunja projects", len(trelloData))
for _, board := range trelloData {
list := &models.ListWithTasksAndBuckets{
List: models.List{
project := &models.ProjectWithTasksAndBuckets{
Project: models.Project{
Title: board.Name,
Description: board.Desc,
IsArchived: board.Closed,
@ -189,7 +189,7 @@ func convertTrelloDataToVikunja(trelloData []*trello.Board, token string) (fullV
}
// Background
// We're pretty much abusing the backgroundinformation field here - not sure if this is really better than adding a new property to the list
// We're pretty much abusing the backgroundinformation field here - not sure if this is really better than adding a new property to the project
if board.Prefs.BackgroundImage != "" {
log.Debugf("[Trello Migration] Downloading background %s for board %s", board.Prefs.BackgroundImage, board.ID)
buf, err := migration.DownloadFile(board.Prefs.BackgroundImage)
@ -197,7 +197,7 @@ func convertTrelloDataToVikunja(trelloData []*trello.Board, token string) (fullV
return nil, err
}
log.Debugf("[Trello Migration] Downloaded background %s for board %s", board.Prefs.BackgroundImage, board.ID)
list.BackgroundInformation = buf
project.BackgroundInformation = buf
} else {
log.Debugf("[Trello Migration] Board %s does not have a background image, not copying...", board.ID)
}
@ -291,23 +291,23 @@ func convertTrelloDataToVikunja(trelloData []*trello.Board, token string) (fullV
log.Debugf("[Trello Migration] Downloaded card attachment %s", attachment.ID)
}
list.Tasks = append(list.Tasks, &models.TaskWithComments{Task: *task})
project.Tasks = append(project.Tasks, &models.TaskWithComments{Task: *task})
}
list.Buckets = append(list.Buckets, bucket)
project.Buckets = append(project.Buckets, bucket)
bucketID++
}
log.Debugf("[Trello Migration] Converted all cards to tasks for board %s", board.ID)
fullVikunjaHierachie[0].Lists = append(fullVikunjaHierachie[0].Lists, list)
fullVikunjaHierachie[0].Projects = append(fullVikunjaHierachie[0].Projects, project)
}
return
}
// Migrate gets all tasks from trello for a user and puts them into vikunja
// @Summary Migrate all lists, tasks etc. from trello
// @Summary Migrate all projects, tasks etc. from trello
// @Description Migrates all projects, tasks, notes, reminders, subtasks and files from trello to vikunja.
// @tags migration
// @Accept json

View File

@ -44,9 +44,9 @@ func TestConvertTrelloToVikunja(t *testing.T) {
Name: "TestBoard",
Desc: "This is a description",
Closed: false,
Lists: []*trello.List{
Projects: []*trello.Project{
{
Name: "Test List 1",
Name: "Test Project 1",
Cards: []*trello.Card{
{
Name: "Test Card 1",
@ -77,9 +77,9 @@ func TestConvertTrelloToVikunja(t *testing.T) {
{
Name: "Test Card 2",
Pos: 124,
Checklists: []*trello.Checklist{
Checkprojects: []*trello.Checkproject{
{
Name: "Checklist 1",
Name: "Checkproject 1",
CheckItems: []trello.CheckItem{
{
State: "pending",
@ -92,7 +92,7 @@ func TestConvertTrelloToVikunja(t *testing.T) {
},
},
{
Name: "Checklist 2",
Name: "Checkproject 2",
CheckItems: []trello.CheckItem{
{
State: "pending",
@ -124,7 +124,7 @@ func TestConvertTrelloToVikunja(t *testing.T) {
},
},
{
Name: "Test List 2",
Name: "Test Project 2",
Cards: []*trello.Card{
{
Name: "Test Card 5",
@ -157,9 +157,9 @@ func TestConvertTrelloToVikunja(t *testing.T) {
{
Name: "TestBoard 2",
Closed: false,
Lists: []*trello.List{
Projects: []*trello.Project{
{
Name: "Test List 4",
Name: "Test Project 4",
Cards: []*trello.Card{
{
Name: "Test Card 634",
@ -172,9 +172,9 @@ func TestConvertTrelloToVikunja(t *testing.T) {
{
Name: "TestBoard Archived",
Closed: true,
Lists: []*trello.List{
Projects: []*trello.Project{
{
Name: "Test List 5",
Name: "Test Project 5",
Cards: []*trello.Card{
{
Name: "Test Card 63423",
@ -187,14 +187,14 @@ func TestConvertTrelloToVikunja(t *testing.T) {
}
trelloData[0].Prefs.BackgroundImage = "https://vikunja.io/testimage.jpg" // Using an image which we are hosting, so it'll still be up
expectedHierachie := []*models.NamespaceWithListsAndTasks{
expectedHierachie := []*models.NamespaceWithProjectsAndTasks{
{
Namespace: models.Namespace{
Title: "Imported from Trello",
},
Lists: []*models.ListWithTasksAndBuckets{
Projects: []*models.ProjectWithTasksAndBuckets{
{
List: models.List{
Project: models.Project{
Title: "TestBoard",
Description: "This is a description",
BackgroundInformation: bytes.NewBuffer(exampleFile),
@ -202,11 +202,11 @@ func TestConvertTrelloToVikunja(t *testing.T) {
Buckets: []*models.Bucket{
{
ID: 1,
Title: "Test List 1",
Title: "Test Project 1",
},
{
ID: 2,
Title: "Test List 2",
Title: "Test Project 2",
},
},
Tasks: []*models.TaskWithComments{
@ -244,12 +244,12 @@ func TestConvertTrelloToVikunja(t *testing.T) {
Title: "Test Card 2",
Description: `
## Checklist 1
## Checkproject 1
* [ ] Pending Task
* [x] Completed Task
## Checklist 2
## Checkproject 2
* [ ] Pending Task
* [ ] Another Pending Task`,
@ -315,13 +315,13 @@ func TestConvertTrelloToVikunja(t *testing.T) {
},
},
{
List: models.List{
Project: models.Project{
Title: "TestBoard 2",
},
Buckets: []*models.Bucket{
{
ID: 3,
Title: "Test List 4",
Title: "Test Project 4",
},
},
Tasks: []*models.TaskWithComments{
@ -335,14 +335,14 @@ func TestConvertTrelloToVikunja(t *testing.T) {
},
},
{
List: models.List{
Project: models.Project{
Title: "TestBoard Archived",
IsArchived: true,
},
Buckets: []*models.Bucket{
{
ID: 4,
Title: "Test List 5",
Title: "Test Project 5",
},
},
Tasks: []*models.TaskWithComments{

View File

@ -51,7 +51,7 @@ func (v *FileMigrator) Name() string {
}
// Migrate takes a vikunja file export, parses it and imports everything in it into Vikunja.
// @Summary Import all lists, tasks etc. from a Vikunja data export
// @Summary Import all projects, tasks etc. from a Vikunja data export
// @Description Imports all projects, tasks, notes, reminders, subtasks and files from a Vikunjda data export into Vikunja.
// @tags migration
// @Accept json
@ -113,21 +113,21 @@ func (v *FileMigrator) Migrate(user *user.User, file io.ReaderAt, size int64) er
return fmt.Errorf("could not read data file: %w", err)
}
namespaces := []*models.NamespaceWithListsAndTasks{}
namespaces := []*models.NamespaceWithProjectsAndTasks{}
if err := json.Unmarshal(bufData.Bytes(), &namespaces); err != nil {
return fmt.Errorf("could not read data: %w", err)
}
for _, n := range namespaces {
for _, l := range n.Lists {
for _, l := range n.Projects {
if b, exists := storedFiles[l.BackgroundFileID]; exists {
bf, err := b.Open()
if err != nil {
return fmt.Errorf("could not open list background file %d for reading: %w", l.BackgroundFileID, err)
return fmt.Errorf("could not open project background file %d for reading: %w", l.BackgroundFileID, err)
}
var buf bytes.Buffer
if _, err := buf.ReadFrom(bf); err != nil {
return fmt.Errorf("could not read list background file %d: %w", l.BackgroundFileID, err)
return fmt.Errorf("could not read project background file %d: %w", l.BackgroundFileID, err)
}
l.BackgroundInformation = &buf

View File

@ -48,12 +48,12 @@ func TestVikunjaFileMigrator_Migrate(t *testing.T) {
"title": "test",
"owner_id": u.ID,
}, false)
db.AssertExists(t, "lists", map[string]interface{}{
"title": "Test list",
db.AssertExists(t, "projects", map[string]interface{}{
"title": "Test project",
"owner_id": u.ID,
}, false)
db.AssertExists(t, "lists", map[string]interface{}{
"title": "A list with a background",
db.AssertExists(t, "projects", map[string]interface{}{
"title": "A project with a background",
"owner_id": u.ID,
}, false)
db.AssertExists(t, "tasks", map[string]interface{}{