package main import ( "bufio" sdk "code.vikunja.io/go-sdk" "context" "log" "os" "strings" "time" ) func main() { const listID = 19 const basePath = `http://localhost:8080/api/v1` const filepath = `../api/Featurecreep.md` now := time.Now() client := sdk.NewAPIClient(&sdk.Configuration{ BasePath: basePath, DefaultHeader: make(map[string]string), UserAgent: "Go migration client", }) token, _, err := client.UserApi.LoginPost(context.Background(), sdk.ModelsUserLogin{ Username: os.Getenv("VIKUNJA_USERNAME"), Password: os.Getenv("VIKUNJA_PASSWORD"), }) if err != nil { log.Fatal("Error auth", err) } log.Println("Auth succeeded, token is ", token.Token) auth := context.WithValue(context.Background(), sdk.ContextAPIKey, sdk.APIKey{ Key: token.Token, Prefix: "Bearer", }) // Read the md file lines, err := scanLines(filepath) if err != nil { log.Fatal("Error reading file: ", err) } tasks := make([]*sdk.ModelsTask, 0, len(lines)) var lastHeadline string var notApplicable []string var lastTask *sdk.ModelsTask var taskRelations []*sdk.ModelsTaskRelation labels := make(map[string]*sdk.ModelsLabel) for in, l := range lines { if l == "" { continue } if strings.HasPrefix(l, "#") { lastHeadline = strings.TrimSpace(strings.ReplaceAll(l, "#", "")) labels[lastHeadline] = &sdk.ModelsLabel{Title: lastHeadline} continue } // Only add tasks to the list if strings.HasPrefix(l, "* [") { t := &sdk.ModelsTask{ Text: l[5:], Done: strings.HasPrefix(l, "* [x]"), Labels: []sdk.ModelsLabel{ { Title: lastHeadline, }, }, } *t, _, err = client.TaskApi.ListsIdPut(auth, listID, *t) if err != nil { log.Fatal("Error creating task: ", err) } log.Printf("Created task %d of %d with ID %d", in, len(lines), t.Id) // Subtask handling lastTask = t tasks = append(tasks, t) continue } // Subtask handling // If we have a line starting with two spaces and then task, we have a subtask of the last task if strings.HasPrefix(l, " * [") { subtask := &sdk.ModelsTask{ Text: l[7:], Done: strings.HasPrefix(l, " * [x]"), Labels: []sdk.ModelsLabel{ { Title: lastHeadline, }, }, } *subtask, _, err = client.TaskApi.ListsIdPut(auth, listID, *subtask) if err != nil { log.Fatal("Error creating task: ", err) } log.Printf("Created task %d of %d with ID %d", in, len(lines), subtask.Id) tasks = append(tasks, subtask) taskRelations = append(taskRelations, &sdk.ModelsTaskRelation{ TaskId: lastTask.Id, OtherTaskId: subtask.Id, RelationKind: "subtask", }) continue } // If we had a last task, every line which follows now is not a subtask but additional description, so we put it in as description if lastTask != nil { lastTask.Description = lastTask.Description + l + "\n" continue } notApplicable = append(notApplicable, l) } log.Println("Done migrating tasks.") // Make sure all labels exist outer: for _, l := range labels { // Check if it exists lls, _, err := client.LabelsApi.LabelsGet(auth, map[string]interface{}{"s": l.Title}) if err != nil { log.Fatal("Error getting label: ", err) } // If the result is empty, we need to create the label if len(lls) == 0 { newLabel, _, err := client.LabelsApi.LabelsPut(auth, *l) if err != nil { log.Fatal("Error creating new label: ", err) } labels[l.Title] = &newLabel continue } // Go through the results of the search and put the right label in our label map for _, ll := range lls { if l.Title == ll.Title { labels[ll.Title] = &ll continue outer } } } // At this point, we have a map with existing labels // Update all tasks which have a description // Update all labels on tasks for _, t := range tasks { label, _ := labels[t.Labels[0].Title] _, _, err := client.LabelsApi.TasksTaskLabelsPut(auth, t.Id, sdk.ModelsLabelTask{LabelId: label.Id}) if err != nil { log.Fatalf("Error adding label %s to task %d: %v", label.Title, t.Id, err) } log.Printf("Added label %s to task %d", label.Title, t.Id) if t.Description == "" { continue } *t, _, err = client.TaskApi.TasksIdPost(auth, t.Id, *t) if err != nil { log.Fatal("Error updating task description: ", err) } log.Printf("Updated description of task %d", t.Id) } // Create all task relations for _, tr := range taskRelations { _, _, err := client.TaskApi.TasksTaskIDRelationsPut(auth, *tr, tr.TaskId) if err != nil { log.Fatalf("Error creating task relation between tasks %d and %d: %v", tr.TaskId, tr.OtherTaskId, err) } } log.Printf("Done in %v", time.Since(now)) } func scanLines(path string) ([]string, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanLines) var lines []string for scanner.Scan() { lines = append(lines, scanner.Text()) } return lines, nil }