21

Is there a way to get the total time spend on all issues that a user have spend with the time tracking /spend slash command?

Time tracking stats with the API gets just a small amount of data: https://docs.gitlab.com/ce/api/issues.html#get-time-tracking-stats

Gitlab CE 9.1.4

Ventolinmono
  • 361
  • 2
  • 12
  • 1
    The new time tracking report in GitLab 13.12 (May 2021) could be of interest: see [my answer here](https://stackoverflow.com/a/63223901/6309) – VonC May 23 '21 at 07:43

5 Answers5

8

As I see it is possible to parse comments from API v3 and calculate total.

For example,

https://gitlab.com/api/v3/projects/:id/issues/:issue_id/notes?private_token=your_token

{
  id: 73113225,
  body: "added 1h of time spent at 2018-05-15",
  attachment: null,
  author: {
    ...
    username: "mnvxxx",
  },
  ...
}

More info: https://docs.gitlab.com/ee/api/notes.html

UPDATE

Currently I has created instrument for calculating spent time by every contributor. I hope it will be helpful:

https://github.com/zubroide/gitpab

Nick
  • 9,735
  • 7
  • 59
  • 89
7

Here is a very simple Python script that works with API v4:

import requests

API_KEY = ""  # Enter your API key here
BASE_URL = "https://{{enter gitlab url here}}/api/v4/"

item_counter = 0
total_seconds = 0

for i in range(1, 57):  # manually set range of issues here. All issues doesn't work well.
    issue = requests.get(BASE_URL + 'projects/2/issues/' + str(i) + '/time_stats')
    total_seconds += issue.json()['total_time_spent']
    item_counter += 1

print("Hours on all issues: %.2f" % float((total_seconds / 60) / 60))
print("Total issues: " + str(item_counter))

I'm posting to this thread because this is the first answer that comes up on Google, and there isn't really any other ready-made solutions to be found.

Josh Harkema
  • 250
  • 3
  • 11
  • Looks like we can search for issues using their Global Search API in order to limit which issues are returned: https://docs.gitlab.com/ee/api/search.html#global-search-api – carlin.scott Jan 24 '20 at 19:26
4

Building on top of what @josh-harkema has provided, this is a version that lists all closed issues that were assigned to a specific username that have been updated in a given time period (and do not have the label 'paid' set):

import requests
import os

username = os.environ.get('GITLAB_REPORTING_USERNAME')
project_id = os.environ.get('GITLAB_REPORTING_PROJECTID') # in the top of your project page
access_token = os.environ.get('GITLAB_REPORTING_TOKEN')  # https://gitlab.com/profile/personal_access_tokens
base_url = "https://gitlab.com/api/v4"

updated_after = "2019-06-01T00:00:00.00Z"
updated_before = "2019-07-01T00:00:00.00Z"

item_counter = 0
total_seconds = 0

headers = { 'Private-Token': access_token }
url_template = "{base_url}/projects/{project_id}/issues?" \
               "state=closed&assignee_username={username}&updated_after={updated_after}&updated_before={updated_before}"
url = url_template.format(base_url=base_url, project_id=project_id, username=username,
                          updated_after=updated_after, updated_before=updated_before)

# call API
issues = requests.get(url, headers = headers)

total_seconds = 0
issues_to_pay = []
line_template = "id: {id}    closed: {closed_at}    time spent: {time}\ttitle: {title}\turl: {url}"
print("Issue statistics for {u} from {f} to {t}:\n".format(u=username,f=updated_after, t=updated_before))

for issue in issues.json():

    time_val = issue['time_stats']['human_total_time_spent']
    already_paid = u'paid' in issue['labels'] # you can put a label 'paid' to exclude an issue
    if already_paid:
        time_val = time_val + " *"
    else:
        # if the issue has been paid, already, don't add the time, and don't list as to be paid
        total_seconds += issue['time_stats']['total_time_spent']
        issues_to_pay.append(str(issue['id']))

    line = line_template.format(
        id=issue['id'],
        closed_at=issue['closed_at'],
        title=issue['title'],
        time=time_val,
        url=issue['web_url']
    )
    print(line)
print("")
print("Hours to pay on all issues: %.2f" % float((float(total_seconds) / 60) / 60))
print("")
print("* = issue has been paid for, already")
print("All issue to pay: {issues}".format(issues=",".join(issues_to_pay)))

Note: You need to have environment variables set for the GITLAB_REPORTING_USERNAME, the GITLAB_REPORTING_PROJECTID, as well as the GITLAB_REPORTING_TOKEN.

We use this to pay contractors. Hope this helps!

Lorenz
  • 71
  • 2
3

I was myself looking for the same and after more searching, I found this excellent CLI tool called gitlab-time-tracker. It generates comprehensive reports of tracked-time that you can customise by multiple options and can print them even as PDFs!

For keeping this answer relevant to OP's question, you can print (in your terminal) total time spent by a user using following command**:

gtt report "namespace/project" --user username --closed --from="2017-03-01" --to="2017-04-01"

** This assumes that you've installed this tool (gtt) and setup your Gitlab PAT (with "api" scope activated) in its config file.

Jaladh Singhal
  • 393
  • 4
  • 10
0

You'll have to use both the Gitlab Commits API and the GraphQL API to achieve it. Below is some code that's been shorted for brevity.

You'll need to specify a Gitlab instance flag and your personal token.

Say you have a function used to capture all users in your Gitlab instance called "GetUsers":

package main

    import (
        "bytes"
        "encoding/json"
        "fmt"
        "net/http"
        "time"

    "github.com/sirupsen/logrus"
    "github.com/xanzy/go-gitlab"
    "gopkg.in/alecthomas/kingpin.v2"
)

var (

address = kingpin.Flag("address", "The Gitlab URL to use").Required().String()
token   = kingpin.Flag("token", "Project token").String()
)


func main () {

timeTracker()

}

func timeTracker(git *gitlab.Client) {

names := GetUsers(git)

for _, name := range names {

jsonData := map[string]interface{}{
    "query": `
query($user: String!) {
    timelogs(username: $user ) {
        edges {
            node {
            id
    user {
        id
        username
    }
            timeSpent
            issue{
                labels{
                nodes{
                    title
            }
            }
        }
        }
    }
    }
}
`, "variables": fmt.Sprintf(`{"user":"%s"}`, name),
}
jsonValue, _ := json.Marshal(jsonData)
    request, err := http.NewRequest("POST", "https://" +*address + "/api/graphql", bytes.NewBuffer(jsonValue))
    if err != nil {
        logrus.Error(err)
    }
    request.Header.Set("Authorization", "Bearer "+*token)
    request.Header.Set("Content-Type", "application/json")
    client := &http.Client{Timeout: time.Second * 10}
    response, err := client.Do(request)
    response.Body.Close()
    if err != nil {
        fmt.Printf("The HTTP request failed with error %s\n", err)
    }
    logrus.Print(ioutil.ReadAll(response.Body))

Result (when decode with JSONDecoder):

INFO[0000] User: user1, Time spent: 300 (s)
INFO[0000] User: user2, Time spent: 120 (s)

You can then take this data and decode it into a struct (I would copy and paste the post request to an autogenerator for sanity's sake), then do what you want with it. Or change the POST request to capture users by Project if you're more interested in something granular.

Source: https://docs.gitlab.com/ee/api/graphql/getting_started.html

sixfears7
  • 66
  • 4