fix(migration): make sub project hierarchy work when importing from other services

This commit is contained in:
kolaente 2023-11-08 22:56:10 +01:00
parent 8acc42cb0b
commit 707bb6f89e
Signed by untrusted user: konrad
GPG Key ID: F40E70337AB24C9B
8 changed files with 518 additions and 485 deletions

View File

@ -261,19 +261,21 @@ func getMicrosoftTodoData(token string) (microsoftTodoData []*project, err error
func convertMicrosoftTodoData(todoData []*project) (vikunjsStructure []*models.ProjectWithTasksAndBuckets, err error) {
var pseudoParentID int64 = 1
// One project with all child projects
vikunjsStructure = []*models.ProjectWithTasksAndBuckets{
{
Project: models.Project{
ID: pseudoParentID,
Title: "Migrated from Microsoft Todo",
},
ChildProjects: []*models.ProjectWithTasksAndBuckets{},
},
}
log.Debugf("[Microsoft Todo Migration] Converting %d projects", len(todoData))
for _, l := range todoData {
for index, l := range todoData {
log.Debugf("[Microsoft Todo Migration] Converting project %s", l.ID)
@ -281,6 +283,8 @@ func convertMicrosoftTodoData(todoData []*project) (vikunjsStructure []*models.P
project := &models.ProjectWithTasksAndBuckets{
Project: models.Project{
Title: l.DisplayName,
ID: int64(index+1) + pseudoParentID,
ParentProjectID: pseudoParentID,
},
}
@ -364,7 +368,7 @@ func convertMicrosoftTodoData(todoData []*project) (vikunjsStructure []*models.P
log.Debugf("[Microsoft Todo Migration] Done converted %d tasks", len(l.Tasks))
}
vikunjsStructure[0].ChildProjects = append(vikunjsStructure[0].ChildProjects, project)
vikunjsStructure = append(vikunjsStructure, project)
log.Debugf("[Microsoft Todo Migration] Done converting project %s", l.ID)
}

View File

@ -106,10 +106,13 @@ func TestConverting(t *testing.T) {
{
Project: models.Project{
Title: "Migrated from Microsoft Todo",
ID: 1,
},
},
ChildProjects: []*models.ProjectWithTasksAndBuckets{
{
Project: models.Project{
ID: 2,
ParentProjectID: 1,
Title: "Project 1",
},
Tasks: []*models.TaskWithComments{
@ -166,6 +169,8 @@ func TestConverting(t *testing.T) {
{
Project: models.Project{
Title: "Project 2",
ID: 3,
ParentProjectID: 1,
},
Tasks: []*models.TaskWithComments{
{
@ -180,8 +185,6 @@ func TestConverting(t *testing.T) {
},
},
},
},
},
}
hierachie, err := convertMicrosoftTodoData(microsoftTodoData)

View File

@ -75,19 +75,24 @@ func (date *tickTickTime) UnmarshalCSV(csv string) (err error) {
}
func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.ProjectWithTasksAndBuckets) {
parent := &models.ProjectWithTasksAndBuckets{
var pseudoParentID int64 = 1
result = []*models.ProjectWithTasksAndBuckets{
{
Project: models.Project{
ID: pseudoParentID,
Title: "Migrated from TickTick",
},
ChildProjects: []*models.ProjectWithTasksAndBuckets{},
},
}
projects := make(map[string]*models.ProjectWithTasksAndBuckets)
for _, t := range tasks {
for index, t := range tasks {
_, has := projects[t.ProjectName]
if !has {
projects[t.ProjectName] = &models.ProjectWithTasksAndBuckets{
Project: models.Project{
ID: int64(index+1) + pseudoParentID,
ParentProjectID: pseudoParentID,
Title: t.ProjectName,
},
}
@ -134,14 +139,14 @@ func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.ProjectWi
}
for _, l := range projects {
parent.ChildProjects = append(parent.ChildProjects, l)
result = append(result, l)
}
sort.Slice(parent.ChildProjects, func(i, j int) bool {
return parent.ChildProjects[i].Title < parent.ChildProjects[j].Title
sort.Slice(result, func(i, j int) bool {
return result[i].Title < result[j].Title
})
return []*models.ProjectWithTasksAndBuckets{parent}
return
}
// Name is used to get the name of the ticktick migration - we're using the docs here to annotate the status route.

View File

@ -86,31 +86,33 @@ func TestConvertTicktickTasksToVikunja(t *testing.T) {
vikunjaTasks := convertTickTickToVikunja(tickTickTasks)
assert.Len(t, vikunjaTasks, 1)
assert.Len(t, vikunjaTasks[0].ChildProjects, 2)
assert.Len(t, vikunjaTasks, 3)
assert.Len(t, vikunjaTasks[0].ChildProjects[0].Tasks, 3)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Title, tickTickTasks[0].ProjectName)
assert.Equal(t, vikunjaTasks[1].ParentProjectID, vikunjaTasks[0].ID)
assert.Equal(t, vikunjaTasks[2].ParentProjectID, vikunjaTasks[0].ID)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[0].Title, tickTickTasks[0].Title)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[0].Description, tickTickTasks[0].Content)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[0].StartDate, tickTickTasks[0].StartDate.Time)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[0].EndDate, tickTickTasks[0].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[0].DueDate, tickTickTasks[0].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[0].Labels, []*models.Label{
assert.Len(t, vikunjaTasks[1].Tasks, 3)
assert.Equal(t, vikunjaTasks[1].Title, tickTickTasks[0].ProjectName)
assert.Equal(t, vikunjaTasks[1].Tasks[0].Title, tickTickTasks[0].Title)
assert.Equal(t, vikunjaTasks[1].Tasks[0].Description, tickTickTasks[0].Content)
assert.Equal(t, vikunjaTasks[1].Tasks[0].StartDate, tickTickTasks[0].StartDate.Time)
assert.Equal(t, vikunjaTasks[1].Tasks[0].EndDate, tickTickTasks[0].DueDate.Time)
assert.Equal(t, vikunjaTasks[1].Tasks[0].DueDate, tickTickTasks[0].DueDate.Time)
assert.Equal(t, vikunjaTasks[1].Tasks[0].Labels, []*models.Label{
{Title: "label1"},
{Title: "label2"},
})
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[0].Reminders[0].RelativeTo, models.ReminderRelation("due_date"))
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[0].Reminders[0].RelativePeriod, int64(-24*3600))
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[0].Position, tickTickTasks[0].Order)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[0].Done, false)
assert.Equal(t, vikunjaTasks[1].Tasks[0].Reminders[0].RelativeTo, models.ReminderRelation("due_date"))
assert.Equal(t, vikunjaTasks[1].Tasks[0].Reminders[0].RelativePeriod, int64(-24*3600))
assert.Equal(t, vikunjaTasks[1].Tasks[0].Position, tickTickTasks[0].Order)
assert.Equal(t, vikunjaTasks[1].Tasks[0].Done, false)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[1].Title, tickTickTasks[1].Title)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[1].Position, tickTickTasks[1].Order)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[1].Done, true)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[1].DoneAt, tickTickTasks[1].CompletedTime.Time)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[1].RelatedTasks, models.RelatedTaskMap{
assert.Equal(t, vikunjaTasks[1].Tasks[1].Title, tickTickTasks[1].Title)
assert.Equal(t, vikunjaTasks[1].Tasks[1].Position, tickTickTasks[1].Order)
assert.Equal(t, vikunjaTasks[1].Tasks[1].Done, true)
assert.Equal(t, vikunjaTasks[1].Tasks[1].DoneAt, tickTickTasks[1].CompletedTime.Time)
assert.Equal(t, vikunjaTasks[1].Tasks[1].RelatedTasks, models.RelatedTaskMap{
models.RelationKindParenttask: []*models.Task{
{
ID: tickTickTasks[1].ParentID,
@ -118,24 +120,24 @@ func TestConvertTicktickTasksToVikunja(t *testing.T) {
},
})
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[2].Title, tickTickTasks[2].Title)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[2].Description, tickTickTasks[2].Content)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[2].StartDate, tickTickTasks[2].StartDate.Time)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[2].EndDate, tickTickTasks[2].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[2].DueDate, tickTickTasks[2].DueDate.Time)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[2].Labels, []*models.Label{
assert.Equal(t, vikunjaTasks[1].Tasks[2].Title, tickTickTasks[2].Title)
assert.Equal(t, vikunjaTasks[1].Tasks[2].Description, tickTickTasks[2].Content)
assert.Equal(t, vikunjaTasks[1].Tasks[2].StartDate, tickTickTasks[2].StartDate.Time)
assert.Equal(t, vikunjaTasks[1].Tasks[2].EndDate, tickTickTasks[2].DueDate.Time)
assert.Equal(t, vikunjaTasks[1].Tasks[2].DueDate, tickTickTasks[2].DueDate.Time)
assert.Equal(t, vikunjaTasks[1].Tasks[2].Labels, []*models.Label{
{Title: "label1"},
{Title: "label2"},
{Title: "other label"},
})
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[2].Reminders[0].RelativeTo, models.ReminderRelation("due_date"))
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[2].Reminders[0].RelativePeriod, int64(-24*3600))
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[2].Position, tickTickTasks[2].Order)
assert.Equal(t, vikunjaTasks[0].ChildProjects[0].Tasks[2].Done, false)
assert.Equal(t, vikunjaTasks[1].Tasks[2].Reminders[0].RelativeTo, models.ReminderRelation("due_date"))
assert.Equal(t, vikunjaTasks[1].Tasks[2].Reminders[0].RelativePeriod, int64(-24*3600))
assert.Equal(t, vikunjaTasks[1].Tasks[2].Position, tickTickTasks[2].Order)
assert.Equal(t, vikunjaTasks[1].Tasks[2].Done, false)
assert.Len(t, vikunjaTasks[0].ChildProjects[1].Tasks, 1)
assert.Equal(t, vikunjaTasks[0].ChildProjects[1].Title, tickTickTasks[3].ProjectName)
assert.Len(t, vikunjaTasks[2].Tasks, 1)
assert.Equal(t, vikunjaTasks[2].Title, tickTickTasks[3].ProjectName)
assert.Equal(t, vikunjaTasks[0].ChildProjects[1].Tasks[0].Title, tickTickTasks[3].Title)
assert.Equal(t, vikunjaTasks[0].ChildProjects[1].Tasks[0].Position, tickTickTasks[3].Order)
assert.Equal(t, vikunjaTasks[2].Tasks[0].Title, tickTickTasks[3].Title)
assert.Equal(t, vikunjaTasks[2].Tasks[0].Position, tickTickTasks[3].Order)
}

View File

@ -247,11 +247,15 @@ func parseDate(dateString string) (date time.Time, err error) {
func convertTodoistToVikunja(sync *sync, doneItems map[string]*doneItem) (fullVikunjaHierachie []*models.ProjectWithTasksAndBuckets, err error) {
var pseudoParentID int64 = 1
parent := &models.ProjectWithTasksAndBuckets{
Project: models.Project{
ID: pseudoParentID,
Title: "Migrated from todoist",
},
}
fullVikunjaHierachie = append(fullVikunjaHierachie, parent)
// A map for all vikunja lists with the project id they're coming from as key
lists := make(map[string]*models.ProjectWithTasksAndBuckets, len(sync.Projects))
@ -264,9 +268,11 @@ func convertTodoistToVikunja(sync *sync, doneItems map[string]*doneItem) (fullVi
sections := make(map[string]int64)
for _, p := range sync.Projects {
for index, p := range sync.Projects {
project := &models.ProjectWithTasksAndBuckets{
Project: models.Project{
ID: int64(index+1) + pseudoParentID,
ParentProjectID: pseudoParentID,
Title: p.Name,
HexColor: todoistColors[p.Color],
IsArchived: p.IsArchived,
@ -275,7 +281,7 @@ func convertTodoistToVikunja(sync *sync, doneItems map[string]*doneItem) (fullVi
lists[p.ID] = project
parent.ChildProjects = append(parent.ChildProjects, project)
fullVikunjaHierachie = append(fullVikunjaHierachie, project)
}
sort.Slice(sync.Sections, func(i, j int) bool {
@ -471,7 +477,7 @@ func convertTodoistToVikunja(sync *sync, doneItems map[string]*doneItem) (fullVi
)
}
return []*models.ProjectWithTasksAndBuckets{parent}, err
return
}
func getAccessTokenFromAuthToken(authToken string) (accessToken string, err error) {

View File

@ -366,11 +366,14 @@ func TestConvertTodoistToVikunja(t *testing.T) {
expectedHierachie := []*models.ProjectWithTasksAndBuckets{
{
Project: models.Project{
ID: 1,
Title: "Migrated from todoist",
},
ChildProjects: []*models.ProjectWithTasksAndBuckets{
},
{
Project: models.Project{
ID: 2,
ParentProjectID: 1,
Title: "Project1",
Description: "Lorem Ipsum dolor sit amet\nLorem Ipsum dolor sit amet 2\nLorem Ipsum dolor sit amet 3",
HexColor: todoistColors["berry_red"],
@ -505,6 +508,8 @@ func TestConvertTodoistToVikunja(t *testing.T) {
},
{
Project: models.Project{
ID: 3,
ParentProjectID: 1,
Title: "Project2",
Description: "Lorem Ipsum dolor sit amet 4\nLorem Ipsum dolor sit amet 5",
HexColor: todoistColors["mint_green"],
@ -603,6 +608,8 @@ func TestConvertTodoistToVikunja(t *testing.T) {
},
{
Project: models.Project{
ID: 4,
ParentProjectID: 1,
Title: "Project3 - Archived",
HexColor: todoistColors["mint_green"],
IsArchived: true,
@ -618,8 +625,6 @@ func TestConvertTodoistToVikunja(t *testing.T) {
},
},
},
},
},
}
doneItems := make(map[string]*doneItem)

View File

@ -166,12 +166,13 @@ func convertTrelloDataToVikunja(trelloData []*trello.Board, token string) (fullV
log.Debugf("[Trello Migration] ")
var pseudoParentID int64 = 1
fullVikunjaHierachie = []*models.ProjectWithTasksAndBuckets{
{
Project: models.Project{
ID: pseudoParentID,
Title: "Imported from Trello",
},
ChildProjects: []*models.ProjectWithTasksAndBuckets{},
},
}
@ -179,9 +180,11 @@ func convertTrelloDataToVikunja(trelloData []*trello.Board, token string) (fullV
log.Debugf("[Trello Migration] Converting %d boards to vikunja projects", len(trelloData))
for _, board := range trelloData {
for index, board := range trelloData {
project := &models.ProjectWithTasksAndBuckets{
Project: models.Project{
ID: int64(index+1) + pseudoParentID,
ParentProjectID: pseudoParentID,
Title: board.Name,
Description: board.Desc,
IsArchived: board.Closed,
@ -300,7 +303,7 @@ func convertTrelloDataToVikunja(trelloData []*trello.Board, token string) (fullV
log.Debugf("[Trello Migration] Converted all cards to tasks for board %s", board.ID)
fullVikunjaHierachie[0].ChildProjects = append(fullVikunjaHierachie[0].ChildProjects, project)
fullVikunjaHierachie = append(fullVikunjaHierachie, project)
}
return

View File

@ -190,11 +190,14 @@ func TestConvertTrelloToVikunja(t *testing.T) {
expectedHierachie := []*models.ProjectWithTasksAndBuckets{
{
Project: models.Project{
ID: 1,
Title: "Imported from Trello",
},
ChildProjects: []*models.ProjectWithTasksAndBuckets{
},
{
Project: models.Project{
ID: 2,
ParentProjectID: 1,
Title: "TestBoard",
Description: "This is a description",
BackgroundInformation: bytes.NewBuffer(exampleFile),
@ -316,6 +319,8 @@ func TestConvertTrelloToVikunja(t *testing.T) {
},
{
Project: models.Project{
ID: 3,
ParentProjectID: 1,
Title: "TestBoard 2",
},
Buckets: []*models.Bucket{
@ -336,6 +341,8 @@ func TestConvertTrelloToVikunja(t *testing.T) {
},
{
Project: models.Project{
ID: 4,
ParentProjectID: 1,
Title: "TestBoard Archived",
IsArchived: true,
},
@ -355,8 +362,6 @@ func TestConvertTrelloToVikunja(t *testing.T) {
},
},
},
},
},
}
hierachie, err := convertTrelloDataToVikunja(trelloData, "")