Skip to content

Commit cde3e2d

Browse files
committed
allow to export only particular projects
1 parent 67014af commit cde3e2d

File tree

4 files changed

+86
-11
lines changed

4 files changed

+86
-11
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,6 @@ go run main.go export --template template.tmpl
8989

9090
#### work for a specific time period
9191
By default, the script will work with the current calendar month, but the start and end date can be configured with the `--start` and `--end` flags. The date format is `YYYY-MM-DD`.
92+
93+
#### limit by project
94+
To limit the time entries to a specific project, use the `--project` flag with the name of the project. This flag can be used multiple times to include multiple projects.

cmd/export.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,21 @@ package cmd
1919

2020
import (
2121
"fmt"
22-
"github.com/JankariTech/OpenProjectTmetricIntegration/config"
23-
"github.com/JankariTech/OpenProjectTmetricIntegration/openproject"
24-
"github.com/JankariTech/OpenProjectTmetricIntegration/tmetric"
25-
"github.com/Masterminds/sprig/v3"
26-
"github.com/spf13/cobra"
2722
"os"
2823
"path/filepath"
2924
"strings"
3025
"text/template"
3126
"time"
27+
28+
"github.com/JankariTech/OpenProjectTmetricIntegration/config"
29+
"github.com/JankariTech/OpenProjectTmetricIntegration/openproject"
30+
"github.com/JankariTech/OpenProjectTmetricIntegration/tmetric"
31+
"github.com/Masterminds/sprig/v3"
32+
"github.com/spf13/cobra"
3233
)
3334

3435
var arbitraryString []string
36+
var projects []string
3537
var tmplFile string
3638

3739
var exportCmd = &cobra.Command{
@@ -58,7 +60,13 @@ var exportCmd = &cobra.Command{
5860
return arbitraryString[i]
5961
},
6062
"DetailedReport": func(clientName string, tagName string, groupName string) tmetric.Report {
61-
report, _ := tmetric.GetDetailedReport(config, tmetricUser, clientName, tagName, groupName, startDate, endDate)
63+
report, err := tmetric.GetDetailedReport(
64+
config, tmetricUser, clientName, tagName, groupName, startDate, endDate, projects,
65+
)
66+
if err != nil {
67+
_, _ = fmt.Fprint(os.Stderr, err)
68+
os.Exit(1)
69+
}
6270
return report
6371
},
6472
"AllWorkTypes": func() []tmetric.Tag {
@@ -133,4 +141,11 @@ func init() {
133141
exportCmd.MarkFlagRequired("arbitraryString")
134142
exportCmd.Flags().StringVarP(&tmplFile, "template", "t", today, "the template file")
135143
exportCmd.MarkFlagRequired("template")
144+
exportCmd.Flags().StringArrayVarP(
145+
&projects,
146+
"project",
147+
"p",
148+
nil,
149+
"name of the tmetric project to include in the report (can be specified multiple times)",
150+
)
136151
}

tmetric/report.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ package tmetric
33
import (
44
"encoding/json"
55
"fmt"
6-
"github.com/JankariTech/OpenProjectTmetricIntegration/config"
7-
"github.com/go-resty/resty/v2"
86
"net/url"
97
"strconv"
108
"time"
9+
10+
"github.com/JankariTech/OpenProjectTmetricIntegration/config"
11+
"github.com/go-resty/resty/v2"
1112
)
1213

1314
type ReportItem struct {
@@ -55,7 +56,7 @@ func (reportItem *ReportItem) getDuration() (time.Duration, error) {
5556
}
5657

5758
func GetDetailedReport(
58-
config *config.Config, tmetricUser User, clientName string, tagName string, groupName string, startDate string, endDate string,
59+
config *config.Config, tmetricUser User, clientName string, tagName string, groupName string, startDate string, endDate string, projects []string,
5960
) (Report, error) {
6061
client, err := getClientByName(config, tmetricUser, clientName)
6162
if err != nil {
@@ -66,6 +67,17 @@ func GetDetailedReport(
6667
if err != nil {
6768
return Report{}, err
6869
}
70+
71+
var projectsIds []string // we need a slice of strings for the URL parameters, so let's declare it a string slice
72+
for _, projectName := range projects {
73+
project, err := getProjectByName(config, tmetricUser, projectName)
74+
if err != nil {
75+
return Report{}, err
76+
}
77+
78+
projectsIds = append(projectsIds, strconv.Itoa(project.Id))
79+
}
80+
6981
httpClient := resty.New()
7082
tmetricUrl, _ := url.JoinPath(config.TmetricAPIBaseUrl, "reports/detailed")
7183
request := httpClient.R()
@@ -87,6 +99,9 @@ func GetDetailedReport(
8799
SetQueryParam("AccountId", strconv.Itoa(tmetricUser.ActiveAccountId)).
88100
SetQueryParam("ClientList", strconv.Itoa(client.Id)).
89101
SetQueryParam("GroupList", strconv.Itoa(team.Id)).
102+
SetQueryParamsFromValues(url.Values{
103+
"ProjectList": projectsIds,
104+
}).
90105
SetQueryParam("StartDate", startDate).
91106
SetQueryParam("EndDate", endDate).
92107
Get(tmetricUrl)

tmetric/tmetric.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ package tmetric
33
import (
44
"encoding/json"
55
"fmt"
6-
"github.com/JankariTech/OpenProjectTmetricIntegration/config"
7-
"github.com/go-resty/resty/v2"
86
"net/url"
97
"sort"
108
"strconv"
119
"strings"
10+
11+
"github.com/JankariTech/OpenProjectTmetricIntegration/config"
12+
"github.com/go-resty/resty/v2"
1213
)
1314

1415
// ClientV2 represents a client that is returned by the Tmetric API V2
@@ -26,6 +27,13 @@ type TagV2 struct {
2627
IsWorkType bool `json:"isWorkType"`
2728
}
2829

30+
// ProjectV2 represents a project that is returned by the Tmetric API V2
31+
// see https://app.tmetric.com/api-docs/v2/#/ProjectsV2/projectsv2-get-api-accounts-accountid-projects
32+
type ProjectV2 struct {
33+
Id int `json:"projectId"`
34+
Name string `json:"projectName"`
35+
}
36+
2937
type Team struct {
3038
Name string `json:"name"`
3139
Id int `json:"id"`
@@ -65,6 +73,40 @@ func getTeamByName(config *config.Config, tmetricUser User, name string) (Team,
6573
return Team{}, fmt.Errorf("could not find any team with name '%v'", name)
6674
}
6775

76+
func getAllProjects(config *config.Config, tmetricUser User) ([]ProjectV2, error) {
77+
httpClient := resty.New()
78+
tmetricUrl, _ := url.JoinPath(
79+
config.TmetricAPIBaseUrl, "accounts/", strconv.Itoa(tmetricUser.ActiveAccountId), "/projects",
80+
)
81+
resp, err := httpClient.R().
82+
SetAuthToken(config.TmetricToken).
83+
Get(tmetricUrl)
84+
if err != nil || resp.StatusCode() != 200 {
85+
return nil, fmt.Errorf(
86+
"cannot read projects from tmetric. Error: '%v'. HTTP status code: %v", err, resp.StatusCode(),
87+
)
88+
}
89+
var projects []ProjectV2
90+
err = json.Unmarshal(resp.Body(), &projects)
91+
if err != nil {
92+
return nil, fmt.Errorf("error parsing project response: %v\n", err)
93+
}
94+
return projects, nil
95+
}
96+
97+
func getProjectByName(config *config.Config, tmetricUser User, name string) (ProjectV2, error) {
98+
projects, err := getAllProjects(config, tmetricUser)
99+
if err != nil {
100+
return ProjectV2{}, err
101+
}
102+
for _, project := range projects {
103+
if project.Name == name {
104+
return project, nil
105+
}
106+
}
107+
return ProjectV2{}, fmt.Errorf("could not find any project in tmetric with name '%v'", name)
108+
}
109+
68110
func GetAllWorkTypes(config *config.Config, tmetricUser User) ([]Tag, error) {
69111
httpClient := resty.New()
70112
tmetricUrl, _ := url.JoinPath(

0 commit comments

Comments
 (0)