Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions cmd/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ type tableRow struct {
OpenProjectEntry string
OpenProjectDuration string
DiffInTime string
Warnings string
}

var widthOfFixedColumns = 45 // rough size of all columns that have a fixed width
var widthOfFixedColumns = 61 // rough combined size of all columns that have a fixed width
var userNameFromCmd string

// tries to find out the width of the terminal and returns 80 if it fails
Expand Down Expand Up @@ -76,6 +77,9 @@ var diffCmd = &cobra.Command{
return nil
},
Run: func(cmd *cobra.Command, args []string) {
spinner := newSpinner()
defer spinner.Stop()
spinner.Start()
config := config.NewConfig()

tmetricUserMe := tmetric.NewUser()
Expand Down Expand Up @@ -119,7 +123,7 @@ var diffCmd = &cobra.Command{
outputTable.SetOutputMirror(os.Stdout)

outputTable.AppendHeader(
table.Row{"date", "tmetric entry", "tm\ndur", "OpenProject entry", "OP\ndur", "time\ndiff"},
table.Row{"date", "tmetric entry", "tm\ndur", "OpenProject entry", "OP\ndur", "time\ndiff", "warnings"},
)
widthContentColumns := int((getTerminalWidth() - widthOfFixedColumns) / 2)
outputTable.SetColumnConfigs([]table.ColumnConfig{
Expand All @@ -129,6 +133,9 @@ var diffCmd = &cobra.Command{

totalTimeDiff := 0
for currentDay := start; !currentDay.After(end); currentDay = currentDay.AddDate(0, 0, 1) {
spinner.Stop()
spinner.Suffix = fmt.Sprintf(" %s", currentDay.Format("2006-01-02"))
spinner.Start()
row := tableRow{}
row.Date = currentDay.Format("2006-01-02")
sumDurationTmetric := 0
Expand Down Expand Up @@ -177,7 +184,30 @@ var diffCmd = &cobra.Command{
sumDurationOpenProject += int(duration.Minutes())
humanReadableDuration, _ := entry.GetHumanReadableDuration()
row.OpenProjectDuration += fmt.Sprintf("%v\n\n\n\n\n\n", humanReadableDuration)
workPackage, _ := openproject.GetWorkpackage(
path.Base(entry.Links.WorkPackage.Href), config,
)
countWarnings := 0
if !workPackage.Embedded.Project.Active {
row.Warnings += "- inactive\n project\n"
countWarnings = countWarnings + 2
}
if !workPackage.Embedded.Project.Favorited {
row.Warnings += "- not favorite\n project\n"
countWarnings = countWarnings + 2
}
if workPackage.Embedded.Assignee.Name != tmetricUser.Name && workPackage.Embedded.Assignee.Name != config.OpenProjectTeam {
row.Warnings += "- not my\n assignment\n"
countWarnings = countWarnings + 2
}

// Add the remaining newlines to make it 6 rows total
for i := countWarnings; i < 6; i++ {
row.Warnings += "\n"
}

}

}
if sumDurationTmetric > sumDurationOpenProject {
diff := sumDurationTmetric - sumDurationOpenProject
Expand All @@ -188,17 +218,18 @@ var diffCmd = &cobra.Command{
row.DiffInTime = strconv.Itoa(diff)
totalTimeDiff += diff
}

outputTable.AppendRow(table.Row{
row.Date,
strings.Trim(row.TmetricEntry, "\n"),
strings.Trim(row.TmetricDuration, "\n"),
strings.Trim(row.OpenProjectEntry, "\n"),
strings.Trim(row.OpenProjectDuration, "\n"),
row.DiffInTime,
row.Warnings,
})
outputTable.AppendSeparator()
}
spinner.Stop()
outputTable.AppendRow(table.Row{
"",
"",
Expand Down
62 changes: 55 additions & 7 deletions cmd/tmetric.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ package cmd
import (
"errors"
"fmt"
"os"
"strconv"
"strings"
"time"

"github.com/JankariTech/OpenProjectTmetricIntegration/config"
"github.com/JankariTech/OpenProjectTmetricIntegration/openproject"
"github.com/JankariTech/OpenProjectTmetricIntegration/tmetric"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"os"
"strconv"
"strings"
"time"
)

func validateOpenProjectWorkPackage(input string) error {
Expand All @@ -51,7 +52,7 @@ func handleEntriesWithoutIssue(timeEntries []tmetric.TimeEntry, tmetricUser tmet
defer spinner.Stop()

for _, entry := range entriesWithoutLinkToOpenProject {
prompt := promptui.Prompt{
getWPPrompt := promptui.Prompt{
Label: fmt.Sprintf(
"%v => %v %v-%v. Provide a WP number to be assigned to this time-entry (Enter to skip)",
entry.Project.Name, entry.Note, entry.StartTime, entry.EndTime,
Expand All @@ -62,7 +63,7 @@ func handleEntriesWithoutIssue(timeEntries []tmetric.TimeEntry, tmetricUser tmet
workpackageFoundOnOpenProject := false
for !workpackageFoundOnOpenProject {

workPackageId, err := prompt.Run()
workPackageId, err := getWPPrompt.Run()

if err != nil {
return fmt.Errorf("prompt failed: %v", err)
Expand All @@ -82,7 +83,37 @@ func handleEntriesWithoutIssue(timeEntries []tmetric.TimeEntry, tmetricUser tmet
continue
}

prompt = promptui.Prompt{
if !implausibleProjectConfirmation(
workPackage,
workPackage.Embedded.Project.Active,
"This project is NOT active! ",
"Do you want to use a WP from an INACTIVE project?",
) {
workpackageFoundOnOpenProject = false
continue
}

if !implausibleProjectConfirmation(
workPackage,
workPackage.Embedded.Project.Favorited,
"This project is none of your favorite projects!",
"Do you really want to use a WP from a not-favorite project?",
) {
workpackageFoundOnOpenProject = false
continue
}

if !implausibleProjectConfirmation(
workPackage,
workPackage.Embedded.Assignee.Name == tmetricUser.Name || workPackage.Embedded.Assignee.Name == config.OpenProjectTeam,
fmt.Sprintf("This WP is not assigned to you but to '%s'!", workPackage.Embedded.Assignee.Name),
"Do you really want to use a WP that is not assigned to you?",
) {
workpackageFoundOnOpenProject = false
continue
}

prompt := promptui.Prompt{
Label: fmt.Sprintf(
"WP: %v. Subject: %v. Update t-metric entry?", workPackage.Id, workPackage.Subject,
),
Expand Down Expand Up @@ -112,6 +143,23 @@ func handleEntriesWithoutIssue(timeEntries []tmetric.TimeEntry, tmetricUser tmet
return nil
}

func implausibleProjectConfirmation(
workPackage openproject.WorkPackage, condition bool, issue string, question string,
) bool {
if condition {
return true
}
prompt := promptui.Prompt{
Label: fmt.Sprintf(
"⚠️ Found WP '%s' in the project '%s'. %s %s",
workPackage.Subject, workPackage.Embedded.Project.Name, issue, question,
),
IsConfirm: true,
}
result, err := prompt.Run()
return err == nil && result == "y"
}

func handleEntriesWithoutWorkType(timeEntries []tmetric.TimeEntry, tmetricUser tmetric.User, config *config.Config) error {
entriesWithoutWorkType := tmetric.GetEntriesWithoutWorkType(timeEntries)
if len(entriesWithoutWorkType) > 0 {
Expand Down
8 changes: 6 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ package config

import (
"fmt"
"github.com/spf13/viper"
"os"

"github.com/spf13/viper"
)

type Config struct {
OpenProjectUrl string
OpenProjectToken string
OpenProjectTeam string
TmetricToken string
ClientIdInTmetric int
TmetricAPIBaseUrl string
Expand All @@ -46,6 +48,7 @@ func NewConfig() *Config {
fmt.Fprintln(os.Stderr, "openproject.token not set")
os.Exit(1)
}
openProjectTeam := viper.GetString("openproject.team")
tmetricToken := viper.GetString("tmetric.token")
if tmetricToken == "" {
fmt.Fprintln(os.Stderr, "tmetric.token not set")
Expand All @@ -64,6 +67,7 @@ func NewConfig() *Config {
return &Config{
OpenProjectUrl: openProjectUrl,
OpenProjectToken: openProjectToken,
OpenProjectTeam: openProjectTeam,
TmetricToken: tmetricToken,
ClientIdInTmetric: clientIdInTmetric,
TmetricAPIBaseUrl: "https://app.tmetric.com/api/",
Expand All @@ -72,6 +76,6 @@ func NewConfig() *Config {
TmetricTagTransferredToOpenProject: "transferred-to-openproject",
// this value has always to be "https://community.openproject.org"
// otherwise tmetric does not recognize the integration and does not allow to create the external task
TmetricExternalTaskLink: "https://community.openproject.org/",
TmetricExternalTaskLink: "https://community.openproject.org/",
}
}
18 changes: 15 additions & 3 deletions openproject/workpackage.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,27 @@ package openproject
import (
"encoding/json"
"fmt"
"net/url"

"github.com/JankariTech/OpenProjectTmetricIntegration/config"
"github.com/go-resty/resty/v2"
"github.com/tidwall/gjson"
"net/url"
)

type WorkPackage struct {
Subject string `json:"subject"`
Id int `json:"id"`
Subject string `json:"subject"`
Id int `json:"id"`
Embedded struct {
Project struct {
Id int `json:"id"`
Name string `json:"name"`
Active bool `json:"active"`
Favorited bool `json:"favorited"`
} `json:"project"`
Assignee struct {
Name string `json:"name"`
} `json:"assignee"`
} `json:"_embedded"`
}

func NewWorkPackage(id int, subject string) WorkPackage {
Expand Down