From 07cd65e72746819b3104ab534928cb2806280372 Mon Sep 17 00:00:00 2001 From: Artur Neumann Date: Thu, 5 Feb 2026 16:43:26 +0545 Subject: [PATCH 1/4] get more details per report item --- cmd/export.go | 3 +++ tmetric/report.go | 18 +++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/cmd/export.go b/cmd/export.go index 1aa0c15..a296539 100644 --- a/cmd/export.go +++ b/cmd/export.go @@ -72,6 +72,9 @@ var exportCmd = &cobra.Command{ } return report }, + "AllSelectedProjects": func() []string { + return projects + }, "AllWorkTypes": func() []tmetric.Tag { workTypes, _ := tmetric.GetAllWorkTypes(config, tmetricUser) return workTypes diff --git a/tmetric/report.go b/tmetric/report.go index cf76fd5..f3d57b2 100644 --- a/tmetric/report.go +++ b/tmetric/report.go @@ -13,11 +13,15 @@ import ( ) type ReportItem struct { - StartTime string `json:"startTime"` - EndTime string `json:"endTime"` - User string `json:"user"` - IssueId string `json:"issueId"` - WorkpackageId string + StartTime string `json:"startTime"` + EndTime string `json:"endTime"` + User string `json:"user"` + IssueId string `json:"issueId"` + Project string `json:"project"` + Client string `json:"client"` + Description string `json:"description"` + WorkpackageId string + CalculatedDuration time.Duration } type Report struct { @@ -122,9 +126,9 @@ func GetDetailedReport( var report Report for _, item := range reportItems { item.WorkpackageId = strings.Trim(item.IssueId, "#") // remove leading '#' from issue id + item.CalculatedDuration, _ = item.getDuration() report.ReportItems = append(report.ReportItems, item) - itemDuration, _ := item.getDuration() - report.Duration += itemDuration + report.Duration += item.CalculatedDuration } return report, nil } From 4ba81b6e194b9fd770146ee027c90d2f83db54a4 Mon Sep 17 00:00:00 2001 From: Artur Neumann Date: Fri, 6 Feb 2026 12:11:29 +0545 Subject: [PATCH 2/4] limit getting projects to client --- tmetric/report.go | 2 +- tmetric/tmetric.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tmetric/report.go b/tmetric/report.go index f3d57b2..ce63d03 100644 --- a/tmetric/report.go +++ b/tmetric/report.go @@ -77,7 +77,7 @@ func GetDetailedReport( var projectsIds []string // we need a slice of strings for the URL parameters, so let's declare it a string slice for _, projectName := range projects { - project, err := getProjectByName(config, tmetricUser, projectName) + project, err := getProjectByName(config, tmetricUser, projectName, client) if err != nil { return Report{}, err } diff --git a/tmetric/tmetric.go b/tmetric/tmetric.go index 06df02b..5e693ae 100644 --- a/tmetric/tmetric.go +++ b/tmetric/tmetric.go @@ -73,12 +73,13 @@ func getTeamByName(config *config.Config, tmetricUser User, name string) (Team, return Team{}, fmt.Errorf("could not find any team with name '%v'", name) } -func getAllProjects(config *config.Config, tmetricUser User) ([]ProjectV2, error) { +func getAllProjects(config *config.Config, tmetricUser User, client Client) ([]ProjectV2, error) { httpClient := resty.New() tmetricUrl, _ := url.JoinPath( config.TmetricAPIBaseUrl, "accounts/", strconv.Itoa(tmetricUser.ActiveAccountId), "/projects", ) resp, err := httpClient.R(). + SetQueryParam("ClientList", strconv.Itoa(client.Id)). SetAuthToken(config.TmetricToken). Get(tmetricUrl) if err != nil || resp.StatusCode() != 200 { @@ -94,8 +95,8 @@ func getAllProjects(config *config.Config, tmetricUser User) ([]ProjectV2, error return projects, nil } -func getProjectByName(config *config.Config, tmetricUser User, name string) (ProjectV2, error) { - projects, err := getAllProjects(config, tmetricUser) +func getProjectByName(config *config.Config, tmetricUser User, name string, client Client) (ProjectV2, error) { + projects, err := getAllProjects(config, tmetricUser, client) if err != nil { return ProjectV2{}, err } From 24303455ec9c02824bb43cbc4014a56b854b71f6 Mon Sep 17 00:00:00 2001 From: Artur Neumann Date: Fri, 6 Feb 2026 13:33:28 +0545 Subject: [PATCH 3/4] give template access to all projects of a client --- cmd/export.go | 17 +++++++++++++++++ tmetric/report.go | 2 +- tmetric/tmetric.go | 6 +++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/cmd/export.go b/cmd/export.go index a296539..a2cc3db 100644 --- a/cmd/export.go +++ b/cmd/export.go @@ -75,6 +75,23 @@ var exportCmd = &cobra.Command{ "AllSelectedProjects": func() []string { return projects }, + "AllProjectsOfClient": func(clientName string) []string { + client, err := tmetric.GetClientByName(config, tmetricUser, clientName) + if err != nil { + _, _ = fmt.Fprint(os.Stderr, err) + os.Exit(1) + } + projects, err := tmetric.GetAllProjects(config, tmetricUser, client) + if err != nil { + _, _ = fmt.Fprint(os.Stderr, err) + os.Exit(1) + } + var projectNames []string + for _, project := range projects { + projectNames = append(projectNames, project.Name) + } + return projectNames + }, "AllWorkTypes": func() []tmetric.Tag { workTypes, _ := tmetric.GetAllWorkTypes(config, tmetricUser) return workTypes diff --git a/tmetric/report.go b/tmetric/report.go index ce63d03..1b76027 100644 --- a/tmetric/report.go +++ b/tmetric/report.go @@ -65,7 +65,7 @@ func (reportItem *ReportItem) getDuration() (time.Duration, error) { func GetDetailedReport( config *config.Config, tmetricUser User, clientName string, tagName string, groupName string, startDate string, endDate string, projects []string, ) (Report, error) { - client, err := getClientByName(config, tmetricUser, clientName) + client, err := GetClientByName(config, tmetricUser, clientName) if err != nil { return Report{}, err } diff --git a/tmetric/tmetric.go b/tmetric/tmetric.go index 5e693ae..766e3c1 100644 --- a/tmetric/tmetric.go +++ b/tmetric/tmetric.go @@ -73,7 +73,7 @@ func getTeamByName(config *config.Config, tmetricUser User, name string) (Team, return Team{}, fmt.Errorf("could not find any team with name '%v'", name) } -func getAllProjects(config *config.Config, tmetricUser User, client Client) ([]ProjectV2, error) { +func GetAllProjects(config *config.Config, tmetricUser User, client Client) ([]ProjectV2, error) { httpClient := resty.New() tmetricUrl, _ := url.JoinPath( config.TmetricAPIBaseUrl, "accounts/", strconv.Itoa(tmetricUser.ActiveAccountId), "/projects", @@ -96,7 +96,7 @@ func getAllProjects(config *config.Config, tmetricUser User, client Client) ([]P } func getProjectByName(config *config.Config, tmetricUser User, name string, client Client) (ProjectV2, error) { - projects, err := getAllProjects(config, tmetricUser, client) + projects, err := GetAllProjects(config, tmetricUser, client) if err != nil { return ProjectV2{}, err } @@ -152,7 +152,7 @@ func getWorkTypeByName(config *config.Config, tmetricUser User, name string) (Ta return Tag{}, fmt.Errorf("could not find any work type with name '%v'", name) } -func getClientByName(config *config.Config, tmetricUser User, name string) (Client, error) { +func GetClientByName(config *config.Config, tmetricUser User, name string) (Client, error) { httpClient := resty.New() tmetricUrl, _ := url.JoinPath( config.TmetricAPIBaseUrl, "accounts/", strconv.Itoa(tmetricUser.ActiveAccountId), "/clients", From 518b4fc8899e400bfa550704ccbcff419037fc20 Mon Sep 17 00:00:00 2001 From: Artur Neumann Date: Fri, 6 Feb 2026 14:02:44 +0545 Subject: [PATCH 4/4] return empty ArbitraryString if the given item does not exist --- cmd/export.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/export.go b/cmd/export.go index a2cc3db..d0ce7c8 100644 --- a/cmd/export.go +++ b/cmd/export.go @@ -57,7 +57,11 @@ var exportCmd = &cobra.Command{ funcMap := template.FuncMap{ "ArbitraryString": func(i int) string { - return arbitraryString[i] + if len(arbitraryString) >= i+1 { + return arbitraryString[i] + } + + return "" }, "ExportDate": func() string { return exportDate