Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,6 @@ go run main.go export --template template.tmpl

#### work for a specific time period
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`.

#### limit by project
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.
Comment thread
individual-it marked this conversation as resolved.
Outdated
27 changes: 21 additions & 6 deletions cmd/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,21 @@ package cmd

import (
"fmt"
"github.com/JankariTech/OpenProjectTmetricIntegration/config"
"github.com/JankariTech/OpenProjectTmetricIntegration/openproject"
"github.com/JankariTech/OpenProjectTmetricIntegration/tmetric"
"github.com/Masterminds/sprig/v3"
"github.com/spf13/cobra"
"os"
"path/filepath"
"strings"
"text/template"
"time"

"github.com/JankariTech/OpenProjectTmetricIntegration/config"
"github.com/JankariTech/OpenProjectTmetricIntegration/openproject"
"github.com/JankariTech/OpenProjectTmetricIntegration/tmetric"
"github.com/Masterminds/sprig/v3"
"github.com/spf13/cobra"
)

var arbitraryString []string
var projects []string
var tmplFile string

var exportCmd = &cobra.Command{
Expand All @@ -58,7 +60,13 @@ var exportCmd = &cobra.Command{
return arbitraryString[i]
},
"DetailedReport": func(clientName string, tagName string, groupName string) tmetric.Report {
report, _ := tmetric.GetDetailedReport(config, tmetricUser, clientName, tagName, groupName, startDate, endDate)
report, err := tmetric.GetDetailedReport(
config, tmetricUser, clientName, tagName, groupName, startDate, endDate, projects,
)
if err != nil {
_, _ = fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
return report
},
"AllWorkTypes": func() []tmetric.Tag {
Expand Down Expand Up @@ -133,4 +141,11 @@ func init() {
exportCmd.MarkFlagRequired("arbitraryString")
exportCmd.Flags().StringVarP(&tmplFile, "template", "t", today, "the template file")
exportCmd.MarkFlagRequired("template")
exportCmd.Flags().StringArrayVarP(
&projects,
"project",
"p",
nil,
"name of the tmetric project to include in the report (can be specified multiple times)",
)
}
21 changes: 18 additions & 3 deletions tmetric/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package tmetric
import (
"encoding/json"
"fmt"
"github.com/JankariTech/OpenProjectTmetricIntegration/config"
"github.com/go-resty/resty/v2"
"net/url"
"strconv"
"time"

"github.com/JankariTech/OpenProjectTmetricIntegration/config"
"github.com/go-resty/resty/v2"
)

type ReportItem struct {
Expand Down Expand Up @@ -55,7 +56,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,
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)
if err != nil {
Expand All @@ -66,6 +67,17 @@ func GetDetailedReport(
if err != nil {
return Report{}, err
}

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)
if err != nil {
return Report{}, err
}

projectsIds = append(projectsIds, strconv.Itoa(project.Id))
}

httpClient := resty.New()
tmetricUrl, _ := url.JoinPath(config.TmetricAPIBaseUrl, "reports/detailed")
request := httpClient.R()
Expand All @@ -87,6 +99,9 @@ func GetDetailedReport(
SetQueryParam("AccountId", strconv.Itoa(tmetricUser.ActiveAccountId)).
SetQueryParam("ClientList", strconv.Itoa(client.Id)).
SetQueryParam("GroupList", strconv.Itoa(team.Id)).
SetQueryParamsFromValues(url.Values{
"ProjectList": projectsIds,
}).
SetQueryParam("StartDate", startDate).
SetQueryParam("EndDate", endDate).
Get(tmetricUrl)
Expand Down
46 changes: 44 additions & 2 deletions tmetric/tmetric.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package tmetric
import (
"encoding/json"
"fmt"
"github.com/JankariTech/OpenProjectTmetricIntegration/config"
"github.com/go-resty/resty/v2"
"net/url"
"sort"
"strconv"
"strings"

"github.com/JankariTech/OpenProjectTmetricIntegration/config"
"github.com/go-resty/resty/v2"
)

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

// ProjectV2 represents a project that is returned by the Tmetric API V2
// see https://app.tmetric.com/api-docs/v2/#/ProjectsV2/projectsv2-get-api-accounts-accountid-projects
type ProjectV2 struct {
Id int `json:"projectId"`
Name string `json:"projectName"`
}

type Team struct {
Name string `json:"name"`
Id int `json:"id"`
Expand Down Expand Up @@ -65,6 +73,40 @@ 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) {
httpClient := resty.New()
tmetricUrl, _ := url.JoinPath(
config.TmetricAPIBaseUrl, "accounts/", strconv.Itoa(tmetricUser.ActiveAccountId), "/projects",
)
resp, err := httpClient.R().
SetAuthToken(config.TmetricToken).
Get(tmetricUrl)
if err != nil || resp.StatusCode() != 200 {
return nil, fmt.Errorf(
"cannot read projects from tmetric. Error: '%v'. HTTP status code: %v", err, resp.StatusCode(),
)
}
var projects []ProjectV2
err = json.Unmarshal(resp.Body(), &projects)
if err != nil {
return nil, fmt.Errorf("error parsing project response: %v\n", err)
}
return projects, nil
}

func getProjectByName(config *config.Config, tmetricUser User, name string) (ProjectV2, error) {
projects, err := getAllProjects(config, tmetricUser)
if err != nil {
return ProjectV2{}, err
}
for _, project := range projects {
if project.Name == name {
return project, nil
}
}
return ProjectV2{}, fmt.Errorf("could not find any project in tmetric with name '%v'", name)
}

func GetAllWorkTypes(config *config.Config, tmetricUser User) ([]Tag, error) {
httpClient := resty.New()
tmetricUrl, _ := url.JoinPath(
Expand Down