fix(taskwarrior): parsing and convertion of times and contents
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,6 +37,9 @@ func (gitea *Gitea) GetComments(repo Repository) ([]Comment, error) {
|
|||||||
if err = decoder.Decode(&data); err != nil {
|
if err = decoder.Decode(&data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
for _, comment := range data {
|
||||||
|
comment.Body = strings.ReplaceAll(comment.Body, "\r\n", "\n")
|
||||||
|
}
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +57,7 @@ func (gitea *Gitea) GetComment(repo Repository, id uint) (Comment, error) {
|
|||||||
if err = decoder.Decode(&comment); err != nil {
|
if err = decoder.Decode(&comment); err != nil {
|
||||||
return comment, err
|
return comment, err
|
||||||
}
|
}
|
||||||
|
comment.Body = strings.ReplaceAll(comment.Body, "\r\n", "\n")
|
||||||
return comment, err
|
return comment, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
internal/gitw/diff.go
Normal file
11
internal/gitw/diff.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package gitw
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func prompt(value, theirs, mine string) string {
|
||||||
|
// TODO: create tmp file with the corresponding contents
|
||||||
|
// open tmp file using the $EDITOR environment variable
|
||||||
|
// parse and return output of tmp file after $EDITOR execution has been completed
|
||||||
|
fmt.Printf("\tPrompting for value: '%s'\n", value)
|
||||||
|
return value
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package gitw
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.yves-biener.de/yves-biener/gitwarrior/internal/gitea"
|
"gitea.yves-biener.de/yves-biener/gitwarrior/internal/gitea"
|
||||||
@@ -48,6 +49,11 @@ func (project *Project) Fetch() error {
|
|||||||
}
|
}
|
||||||
for _, issue := range issues {
|
for _, issue := range issues {
|
||||||
var issue_comments []gitea.Comment
|
var issue_comments []gitea.Comment
|
||||||
|
issue_comments = append(issue_comments, gitea.Comment{
|
||||||
|
Body: strings.ReplaceAll(issue.Body, "\r\n", "\n"),
|
||||||
|
Created_at: issue.Created_at,
|
||||||
|
Updated_at: issue.Updated_at,
|
||||||
|
})
|
||||||
for _, comment := range comments {
|
for _, comment := range comments {
|
||||||
if comment.Issue_url == issue.Html_url {
|
if comment.Issue_url == issue.Html_url {
|
||||||
issue_comments = append(issue_comments, comment)
|
issue_comments = append(issue_comments, comment)
|
||||||
@@ -67,6 +73,7 @@ func (project *Project) merge() error {
|
|||||||
var tasks []taskwarrior.Task
|
var tasks []taskwarrior.Task
|
||||||
var task taskwarrior.Task
|
var task taskwarrior.Task
|
||||||
var filter taskwarrior.Filter
|
var filter taskwarrior.Filter
|
||||||
|
|
||||||
// NOTE: merge tasks
|
// NOTE: merge tasks
|
||||||
for _, issue := range project.issues {
|
for _, issue := range project.issues {
|
||||||
filter.Reset()
|
filter.Reset()
|
||||||
@@ -104,9 +111,14 @@ func (project *Project) merge() error {
|
|||||||
if err := taskwarrior.UpdateTasks(tasks); err != nil {
|
if err := taskwarrior.UpdateTasks(tasks); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for _, task := range tasks {
|
||||||
|
fmt.Printf("\t%#v\n\n", task)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// TODO: reset tasks after successfully updating the issues
|
|
||||||
tasks = nil
|
tasks = nil // NOTE: reset tasks after successfully updating the issues
|
||||||
|
|
||||||
// TODO: merge milestones
|
// TODO: merge milestones
|
||||||
for _, milestone := range project.milestones {
|
for _, milestone := range project.milestones {
|
||||||
filter.Reset()
|
filter.Reset()
|
||||||
@@ -153,6 +165,10 @@ func (project *Project) merge() error {
|
|||||||
}
|
}
|
||||||
if !dry_run {
|
if !dry_run {
|
||||||
return taskwarrior.UpdateTasks(tasks)
|
return taskwarrior.UpdateTasks(tasks)
|
||||||
|
} else {
|
||||||
|
for _, task := range tasks {
|
||||||
|
fmt.Printf("\t%#v\n\n", task)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -185,32 +201,64 @@ func (issue *Issue) MergeTask(task taskwarrior.Task) (taskwarrior.Task, error) {
|
|||||||
if issue.git_issue.Updated_at.After(last_update) {
|
if issue.git_issue.Updated_at.After(last_update) {
|
||||||
// there are changes we need to merge
|
// there are changes we need to merge
|
||||||
if taskwarrior.TaskTimeToGoTime(task.Modified).After(last_update) {
|
if taskwarrior.TaskTimeToGoTime(task.Modified).After(last_update) {
|
||||||
// TODO: this means that there are local modifications which are not yet pushed
|
// NOTE: this means that there are local modifications which are not yet pushed
|
||||||
|
if task.Description != issue.git_issue.Title {
|
||||||
|
task.Description = prompt(task.Description, issue.git_issue.Title, task.Description)
|
||||||
|
}
|
||||||
|
if task.Status == "completed" || issue.git_issue.State == "closed" {
|
||||||
|
if !(task.Status == "completed" && issue.git_issue.State == "closed" || task.Status != "completed" && issue.git_issue.State != "closed") {
|
||||||
|
task.Status = prompt(task.Status, issue.git_issue.State, task.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(task.Annotations) != len(issue.comments)+1 {
|
||||||
|
var annotations []string
|
||||||
|
annotations = append(annotations, issue.git_issue.Body)
|
||||||
|
for _, annotation := range task.Annotations {
|
||||||
|
annotations = append(annotations, annotation.Description)
|
||||||
|
}
|
||||||
|
var comments []string
|
||||||
|
for _, comment := range issue.comments {
|
||||||
|
comments = append(comments, comment.Body)
|
||||||
|
}
|
||||||
|
// TODO: how should the user manually enter the values?
|
||||||
|
// TODO: provide options for theirs, mine and manual edit of the task annotations
|
||||||
|
// annotation_joined := prompt(strings.Join(annotations, "\n\n"), strings.Join(comments, "\n\n"), strings.Join(annotations, "\n\n"))
|
||||||
|
} else {
|
||||||
|
for i := range issue.comments {
|
||||||
|
// check the modification times?
|
||||||
|
annotation := task.Annotations[i]
|
||||||
|
comment := issue.comments[i]
|
||||||
|
modification_time := taskwarrior.TaskTimeToGoTime(annotation.Entry)
|
||||||
|
if comment.Updated_at.After(modification_time) {
|
||||||
|
annotation.Description = comment.Body
|
||||||
|
annotation.Entry = taskwarrior.GoTimeToTaskTime(comment.Updated_at)
|
||||||
|
} else {
|
||||||
|
annotation.Description = prompt(annotation.Description, comment.Body, annotation.Description)
|
||||||
|
}
|
||||||
|
task.Annotations[i] = annotation
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// NOTE: there are no modifications between the last received update and the current version, hence we just accept theirs fully
|
||||||
task.Description = issue.git_issue.Title
|
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" {
|
if issue.git_issue.State == "closed" {
|
||||||
|
// otherwise do not update the value
|
||||||
task.Status = "completed"
|
task.Status = "completed"
|
||||||
task.End = taskwarrior.GoTimeToTaskTime(issue.git_issue.Closed_at)
|
task.End = taskwarrior.GoTimeToTaskTime(issue.git_issue.Closed_at)
|
||||||
}
|
}
|
||||||
}
|
annotations := len(task.Annotations)
|
||||||
task.Last_gitw_update = taskwarrior.GoTimeToTaskTime(time.Now())
|
for i, comment := range issue.comments {
|
||||||
} else {
|
if comment.Updated_at.After(last_update) {
|
||||||
// there are changes after the previous update here may be merge conflicts
|
if i < annotations {
|
||||||
// TODO: implement merge conflict resolution
|
task.Annotations[i].Description = comment.Body
|
||||||
}
|
task.Annotations[i].Entry = taskwarrior.GoTimeToTaskTime(comment.Updated_at)
|
||||||
annotations := len(task.Annotations)
|
} else {
|
||||||
for i, comment := range issue.comments {
|
task.AppendComment(comment.Body, comment.Updated_at)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
task.Last_gitw_update = taskwarrior.GoTimeToTaskTime(time.Now().In(time.Local))
|
||||||
}
|
}
|
||||||
// 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?
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ func NewTask(description string, project string, git_id uint, git_type Type, tag
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TaskTimeToGoTime(t string) time.Time {
|
func TaskTimeToGoTime(t string) time.Time {
|
||||||
// TODO: apply required changes to the string for correct parsing
|
|
||||||
splits := strings.Split(t, "T")
|
splits := strings.Split(t, "T")
|
||||||
if len(t) == 0 {
|
if len(t) == 0 {
|
||||||
return time.UnixMicro(0)
|
return time.UnixMicro(0)
|
||||||
@@ -78,6 +77,7 @@ func TaskTimeToGoTime(t string) time.Time {
|
|||||||
second = timestamp[2:4]
|
second = timestamp[2:4]
|
||||||
third = timestamp[4 : len(timestamp)-1]
|
third = timestamp[4 : len(timestamp)-1]
|
||||||
timestamp = strings.Join([]string{first, second, third}, ":")
|
timestamp = strings.Join([]string{first, second, third}, ":")
|
||||||
|
// TODO: identify current timezone and use correct format string (works for Europe/Berlin)
|
||||||
value := fmt.Sprintf("%sT%s+02:00", date, timestamp)
|
value := fmt.Sprintf("%sT%s+02:00", date, timestamp)
|
||||||
result, err := time.Parse(time.RFC3339, value)
|
result, err := time.Parse(time.RFC3339, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -88,13 +88,9 @@ func TaskTimeToGoTime(t string) time.Time {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GoTimeToTaskTime(t time.Time) string {
|
func GoTimeToTaskTime(t time.Time) string {
|
||||||
result := t.Format(time.RFC3339)
|
result := t.In(time.UTC).Format(time.RFC3339)
|
||||||
// TODO: apply changes to the result
|
|
||||||
// go: 2023-10-10T19:57:22+02:00
|
|
||||||
// task: 20231010T195722Z
|
|
||||||
result = strings.Replace(result, "-", "", 2)
|
result = strings.Replace(result, "-", "", 2)
|
||||||
result = strings.Replace(result, ":", "", 2)
|
result = strings.Replace(result, ":", "", 2)
|
||||||
result = strings.ReplaceAll(result, "+02:00", "Z")
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,18 @@
|
|||||||
package taskwarrior
|
package taskwarrior
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTimeCoversions(t *testing.T) {
|
||||||
|
current_time := time.Now()
|
||||||
|
task_current_time := GoTimeToTaskTime(current_time)
|
||||||
|
go_current_time := TaskTimeToGoTime(task_current_time)
|
||||||
|
task_current_time = GoTimeToTaskTime(go_current_time)
|
||||||
|
current_time = TaskTimeToGoTime(task_current_time)
|
||||||
|
|
||||||
|
if current_time.Compare(go_current_time) != 0 {
|
||||||
|
t.Fatalf("conversion failed: Expected: `%s` but got: `%s`", current_time.String(), go_current_time.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user