add(taskwarrior): uda for last_gitw_update with initial merge
WIP for #3. Creation of entirely new tasks is possible and works as expected. The only remaining issue is the merging of existing tasks.
This commit is contained in:
@@ -23,8 +23,22 @@ type Milestone struct {
|
|||||||
Closed_at time.Time `json:"closed_at"`
|
Closed_at time.Time `json:"closed_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (milestone *Milestone) IntoTask(repository Repository) (task taskwarrior.Task) {
|
||||||
|
task = taskwarrior.NewTask(
|
||||||
|
milestone.Title,
|
||||||
|
repository.Name,
|
||||||
|
milestone.Id,
|
||||||
|
taskwarrior.MILESTONE,
|
||||||
|
)
|
||||||
|
task.Due = taskwarrior.GoTimeToTaskTime(milestone.Due_on)
|
||||||
|
task.Entry = taskwarrior.GoTimeToTaskTime(milestone.Created_at)
|
||||||
|
task.Modified = taskwarrior.GoTimeToTaskTime(milestone.Updated_at)
|
||||||
|
task.AppendComment(milestone.Description, milestone.Updated_at)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: implement merge for milestone tasks
|
// TODO: implement merge for milestone tasks
|
||||||
func (milestone *Milestone) Merge(task taskwarrior.Task) taskwarrior.Task {
|
func (milestone *Milestone) MergeTask(task taskwarrior.Task) taskwarrior.Task {
|
||||||
return task
|
return task
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package gitw
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gitea.yves-biener.de/yves-biener/gitwarrior/internal/gitea"
|
"gitea.yves-biener.de/yves-biener/gitwarrior/internal/gitea"
|
||||||
"gitea.yves-biener.de/yves-biener/gitwarrior/internal/taskwarrior"
|
"gitea.yves-biener.de/yves-biener/gitwarrior/internal/taskwarrior"
|
||||||
@@ -62,8 +63,50 @@ func (project *Project) Fetch() error {
|
|||||||
|
|
||||||
// TODO: tasks should include the corresponding time's of the git related issues and milestones
|
// TODO: tasks should include the corresponding time's of the git related issues and milestones
|
||||||
func (project *Project) merge() error {
|
func (project *Project) merge() error {
|
||||||
|
dry_run := true // make this a parameter / cli flag
|
||||||
var tasks []taskwarrior.Task
|
var tasks []taskwarrior.Task
|
||||||
|
var task taskwarrior.Task
|
||||||
var filter taskwarrior.Filter
|
var filter taskwarrior.Filter
|
||||||
|
// NOTE: merge tasks
|
||||||
|
for _, issue := range project.issues {
|
||||||
|
filter.Reset()
|
||||||
|
filter.IncludeGitId(issue.git_issue.Id)
|
||||||
|
filter.IncludeGitType(taskwarrior.ISSUE)
|
||||||
|
git_tasks, err := taskwarrior.GetTasks(filter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(git_tasks) > 1 {
|
||||||
|
return errors.New("Git issue id was at least used twice in taskwarrior tasks.")
|
||||||
|
} else if len(git_tasks) == 0 {
|
||||||
|
// NOTE: ignore closed issues which do not have a taskwarrior task
|
||||||
|
if issue.git_issue.State == string(gitea.CLOSED) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// NOTE: this task does not yet exist
|
||||||
|
task, err = issue.IntoTask(project.repository)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("\tCreated task: '%s'\n", task.Description)
|
||||||
|
tasks = append(tasks, task)
|
||||||
|
} else {
|
||||||
|
// NOTE: there is excactly one git_task
|
||||||
|
task, err = issue.MergeTask(git_tasks[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("\tUpdated task: '%s'\n", task.Description)
|
||||||
|
tasks = append(tasks, task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !dry_run {
|
||||||
|
if err := taskwarrior.UpdateTasks(tasks); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: reset tasks after successfully updating the issues
|
||||||
|
tasks = nil
|
||||||
// TODO: merge milestones
|
// TODO: merge milestones
|
||||||
for _, milestone := range project.milestones {
|
for _, milestone := range project.milestones {
|
||||||
filter.Reset()
|
filter.Reset()
|
||||||
@@ -73,7 +116,6 @@ func (project *Project) merge() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var task taskwarrior.Task
|
|
||||||
if len(git_tasks) > 1 {
|
if len(git_tasks) > 1 {
|
||||||
return errors.New("Git milestone id was at least used twice in taskwarrior tasks.")
|
return errors.New("Git milestone id was at least used twice in taskwarrior tasks.")
|
||||||
} else if len(git_tasks) == 0 {
|
} else if len(git_tasks) == 0 {
|
||||||
@@ -82,67 +124,34 @@ func (project *Project) merge() error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// NOTE: this milestone does not yet exist
|
// NOTE: this milestone does not yet exist
|
||||||
task = taskwarrior.NewTask(
|
task = milestone.IntoTask(project.repository)
|
||||||
milestone.Title,
|
for _, issue := range project.issues {
|
||||||
project.repository.Name,
|
if issue.git_issue.State == "closed" || issue.git_issue.Milestone.Id != milestone.Id {
|
||||||
milestone.Id,
|
continue
|
||||||
taskwarrior.MILESTONE,
|
}
|
||||||
)
|
// link to the corresponding task
|
||||||
task.Due = taskwarrior.GoTimeToTaskTime(milestone.Due_on)
|
filter.Reset()
|
||||||
task.Entry = taskwarrior.GoTimeToTaskTime(milestone.Created_at)
|
filter.IncludeGitId(issue.git_issue.Id)
|
||||||
task.Modified = taskwarrior.GoTimeToTaskTime(milestone.Updated_at)
|
filter.IncludeGitType(taskwarrior.ISSUE)
|
||||||
task.AppendComment(milestone.Description, milestone.Updated_at)
|
tasks, err := taskwarrior.GetTasks(filter)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(tasks) != 1 {
|
||||||
|
return errors.New("Git issue id used for this milestone does not exist")
|
||||||
|
}
|
||||||
|
task.Depends = append(task.Depends, tasks[0].Uuid)
|
||||||
|
}
|
||||||
fmt.Printf("\tCreated milestone: '%s'\n", task.Description)
|
fmt.Printf("\tCreated milestone: '%s'\n", task.Description)
|
||||||
tasks = append(tasks, task)
|
tasks = append(tasks, task)
|
||||||
} else {
|
} else {
|
||||||
// NOTE: there is exactly one git_task
|
// NOTE: there is exactly one git_task
|
||||||
task = milestone.Merge(git_tasks[0])
|
task = milestone.MergeTask(git_tasks[0])
|
||||||
fmt.Printf("\tUpdated milestone: '%s'\n", task.Description)
|
fmt.Printf("\tUpdated milestone: '%s'\n", task.Description)
|
||||||
tasks = append(tasks, task)
|
tasks = append(tasks, task)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// NOTE: merge tasks
|
if !dry_run {
|
||||||
// TODO: link milestones into tasks
|
|
||||||
for _, issue := range project.issues {
|
|
||||||
filter.Reset()
|
|
||||||
filter.IncludeGitId(issue.git_issue.Id)
|
|
||||||
filter.IncludeGitType(taskwarrior.ISSUE)
|
|
||||||
git_tasks, err := taskwarrior.GetTasks(filter)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
var task taskwarrior.Task
|
|
||||||
if len(git_tasks) > 1 {
|
|
||||||
return errors.New("Git issue id was at least used twice in taskwarrior tasks.")
|
|
||||||
} else if len(git_tasks) == 0 {
|
|
||||||
// NOTE: ignore closed issues which do not have a taskwarrior task
|
|
||||||
if issue.git_issue.State == string(gitea.CLOSED) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// NOTE: this task does not yet exist
|
|
||||||
task = taskwarrior.NewTask(
|
|
||||||
issue.git_issue.Title,
|
|
||||||
project.repository.Name,
|
|
||||||
issue.git_issue.Id,
|
|
||||||
taskwarrior.ISSUE,
|
|
||||||
issue.git_issue.Labels...,
|
|
||||||
)
|
|
||||||
task.Entry = taskwarrior.GoTimeToTaskTime(issue.git_issue.Created_at)
|
|
||||||
task.Modified = taskwarrior.GoTimeToTaskTime(issue.git_issue.Updated_at)
|
|
||||||
for _, comment := range issue.comments {
|
|
||||||
task.AppendComment(comment.Body, comment.Updated_at)
|
|
||||||
}
|
|
||||||
fmt.Printf("\tCreated task: '%s'\n", task.Description)
|
|
||||||
tasks = append(tasks, task)
|
|
||||||
} else {
|
|
||||||
// NOTE: there is excactly one git_task
|
|
||||||
task = issue.merge(git_tasks[0])
|
|
||||||
fmt.Printf("\tUpdated task: '%s'\n", task.Description)
|
|
||||||
tasks = append(tasks, task)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: dry-run switch
|
|
||||||
if false {
|
|
||||||
return taskwarrior.UpdateTasks(tasks)
|
return taskwarrior.UpdateTasks(tasks)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -153,32 +162,56 @@ type Issue struct {
|
|||||||
comments []gitea.Comment
|
comments []gitea.Comment
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Issue) into() (taskwarrior.Task, error) {
|
func (issue *Issue) IntoTask(repository gitea.Repository) (task taskwarrior.Task, err error) {
|
||||||
// TODO: identify if issue is already an existing task
|
// transform current issue into new taskwarrior.Task
|
||||||
var filter taskwarrior.Filter
|
task = taskwarrior.NewTask(
|
||||||
filter.IncludeGitId(i.git_issue.Id)
|
issue.git_issue.Title,
|
||||||
filter.IncludeGitType(taskwarrior.ISSUE)
|
repository.Name,
|
||||||
tasks, err := taskwarrior.GetTasks(filter)
|
issue.git_issue.Id,
|
||||||
if err != nil {
|
taskwarrior.ISSUE,
|
||||||
// this means that a task for this issue does not exist yet
|
issue.git_issue.Labels...,
|
||||||
return taskwarrior.NewTask(
|
)
|
||||||
i.git_issue.Title,
|
task.Entry = taskwarrior.GoTimeToTaskTime(issue.git_issue.Created_at)
|
||||||
i.git_issue.Repository.Name,
|
task.Modified = taskwarrior.GoTimeToTaskTime(issue.git_issue.Updated_at)
|
||||||
i.git_issue.Id,
|
for _, comment := range issue.comments {
|
||||||
taskwarrior.ISSUE,
|
task.AppendComment(comment.Body, comment.Updated_at)
|
||||||
i.git_issue.Labels...,
|
|
||||||
), nil
|
|
||||||
} else {
|
|
||||||
// this means that a task exists and it needs to be merged
|
|
||||||
if len(tasks) != 1 {
|
|
||||||
return taskwarrior.Task{}, errors.New("Did not find exactly one task for a given issue.Id")
|
|
||||||
}
|
|
||||||
return i.merge(tasks[0]), nil
|
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: implement merging of git issue or milestone into a taskwarrior task
|
// TODO: implement merging of git issue or milestone into a taskwarrior task
|
||||||
func (i *Issue) merge(task taskwarrior.Task) taskwarrior.Task {
|
func (issue *Issue) MergeTask(task taskwarrior.Task) (taskwarrior.Task, error) {
|
||||||
|
last_update := taskwarrior.TaskTimeToGoTime(task.Last_gitw_update)
|
||||||
|
if issue.git_issue.Updated_at.After(last_update) {
|
||||||
|
// there are changes we need to merge
|
||||||
|
if taskwarrior.TaskTimeToGoTime(task.Modified).After(last_update) {
|
||||||
|
// TODO: this means that there are local modifications which are not yet pushed
|
||||||
|
} else {
|
||||||
|
task.Description = issue.git_issue.Title
|
||||||
|
task.Tags = issue.git_issue.Labels
|
||||||
|
// NOTE: otherwise do not update the value
|
||||||
|
if issue.git_issue.State == "closed" {
|
||||||
|
task.Status = "completed"
|
||||||
|
task.End = taskwarrior.GoTimeToTaskTime(issue.git_issue.Closed_at)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
task.Last_gitw_update = taskwarrior.GoTimeToTaskTime(time.Now())
|
||||||
|
} else {
|
||||||
|
// there are changes after the previous update here may be merge conflicts
|
||||||
|
// TODO: implement merge conflict resolution
|
||||||
|
}
|
||||||
|
annotations := len(task.Annotations)
|
||||||
|
for i, comment := range issue.comments {
|
||||||
|
if comment.Updated_at.After(last_update) {
|
||||||
|
if i < annotations {
|
||||||
|
task.Annotations[i].Description = comment.Body
|
||||||
|
task.Annotations[i].Entry = taskwarrior.GoTimeToTaskTime(comment.Updated_at)
|
||||||
|
task.Last_gitw_update = taskwarrior.GoTimeToTaskTime(time.Now())
|
||||||
|
} else {
|
||||||
|
task.AppendComment(comment.Body, comment.Updated_at)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
// TODO: issue values into task:
|
// TODO: issue values into task:
|
||||||
// - is the issue more recent than the task?
|
// - is the issue more recent than the task?
|
||||||
// - apply changes into task
|
// - apply changes into task
|
||||||
@@ -186,5 +219,5 @@ func (i *Issue) merge(task taskwarrior.Task) taskwarrior.Task {
|
|||||||
// 1. use theirs
|
// 1. use theirs
|
||||||
// 2. use mine
|
// 2. use mine
|
||||||
// 3. use provided value
|
// 3. use provided value
|
||||||
return task
|
return task, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,20 +14,22 @@ import (
|
|||||||
/// mentioned in the Status field
|
/// mentioned in the Status field
|
||||||
|
|
||||||
type Task struct {
|
type Task struct {
|
||||||
Id uint `json:"id,omitempty"`
|
Id uint `json:"id,omitempty"`
|
||||||
Git_id float32 `json:"git_id,omitempty"` // uda
|
Git_id float32 `json:"git_id,omitempty"` // uda
|
||||||
Git_type string `json:"git_type,omitempty"` // uda
|
Git_type string `json:"git_type,omitempty"` // uda
|
||||||
Project string `json:"project,omitempty"`
|
Project string `json:"project,omitempty"`
|
||||||
Tags []string `json:"tags,omitempty"`
|
Tags []string `json:"tags,omitempty"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
Annotations []Annotation `json:"annotations,omitempty"`
|
Annotations []Annotation `json:"annotations,omitempty"`
|
||||||
Status string `json:"status,omitempty"`
|
Status string `json:"status,omitempty"`
|
||||||
Due string `json:"due,omitempty"`
|
Depends []string `json:"depends,omitempty"`
|
||||||
Entry string `json:"entry,omitempty"`
|
Due string `json:"due,omitempty"`
|
||||||
Modified string `json:"modified,omitempty"`
|
Entry string `json:"entry,omitempty"`
|
||||||
End string `json:"end,omitempty"`
|
Modified string `json:"modified,omitempty"`
|
||||||
Uuid string `json:"uuid,omitempty"`
|
End string `json:"end,omitempty"`
|
||||||
Urgency float32 `json:"urgency,omitempty"`
|
Last_gitw_update string `json:"last_gitw_update,omitempty"` // uda
|
||||||
|
Uuid string `json:"uuid,omitempty"`
|
||||||
|
Urgency float32 `json:"urgency,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Annotation struct {
|
type Annotation struct {
|
||||||
@@ -47,17 +49,21 @@ func NewTask(description string, project string, git_id uint, git_type Type, tag
|
|||||||
// TODO: update task struct to include the new user defined value, which shall
|
// TODO: update task struct to include the new user defined value, which shall
|
||||||
// also be provided as an argument
|
// also be provided as an argument
|
||||||
return Task{
|
return Task{
|
||||||
Project: project,
|
Project: project,
|
||||||
Description: description,
|
Description: description,
|
||||||
Git_id: float32(git_id),
|
Git_id: float32(git_id),
|
||||||
Git_type: string(git_type),
|
Git_type: string(git_type),
|
||||||
Tags: tags,
|
Last_gitw_update: GoTimeToTaskTime(time.Now()),
|
||||||
|
Tags: tags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TaskTimeToGoTime(t string) time.Time {
|
func TaskTimeToGoTime(t string) time.Time {
|
||||||
// TODO: apply required changes to the string for correct parsing
|
// TODO: apply required changes to the string for correct parsing
|
||||||
splits := strings.Split(t, "T")
|
splits := strings.Split(t, "T")
|
||||||
|
if len(t) == 0 {
|
||||||
|
return time.UnixMicro(0)
|
||||||
|
}
|
||||||
if len(splits) != 2 {
|
if len(splits) != 2 {
|
||||||
fmt.Fprintf(os.Stderr, "Expected exactly 2 splits")
|
fmt.Fprintf(os.Stderr, "Expected exactly 2 splits")
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
|
|||||||
Reference in New Issue
Block a user