Commit 8ee2227d authored by Sebastian Latacz's avatar Sebastian Latacz

Add GitLab time_stats functionality

parent c4abdd4f
Pipeline #25991897 passed with stage
in 1 minute and 2 seconds
......@@ -2,6 +2,7 @@
This contains the Issue implementation for GitLab.
"""
from datetime import datetime
from datetime import timedelta
from typing import List
from typing import Optional
from typing import Set
......@@ -464,3 +465,71 @@ class GitLabIssue(GitLabMixin, Issue):
self.data = put(
self._token, self.url,
{'milestone_id': new_milestone.number if new_milestone else ''})
@property
def time_estimate(self) -> timedelta:
"""
Retrieves the time_estimate from the 'time_estimate' attribute
in the GitLab Issue.
"""
return timedelta(seconds=self.data['time_stats']['time_estimate'])
@time_estimate.setter
def time_estimate(self, new_time_estimate: timedelta):
"""
Setter for the time_estimate.
Sets the time on 'human_time_estimate' since it's not possible to set
'time_estimate'. GitLab then automatically calculates 'time_estimate'.
time_estimate is set to 0 when passing a None or 0.
"""
if new_time_estimate:
human_time_estimate = \
str(int(new_time_estimate.total_seconds())) + 's'
self.data = post(self._token, self.url +
'/time_estimate',
{'duration': human_time_estimate})
else:
self.data = post(self._token, self.url +
'/reset_time_estimate', {'duration': None})
@property
def total_time_spent(self) -> timedelta:
"""
Retrieves the time_estimate from the 'total_time_spent' attribute
in the GitLab Issue.
"""
return timedelta(seconds=self.data['time_stats']['total_time_spent'])
@total_time_spent.setter
def total_time_spent(self, absolute_time_spent: timedelta):
"""
Writes the value of absolute_time_spent into total_time_spent.
Can't be less than 0.
Allows any time unit of the timedelta object.
Allows total_time_spent to be reset to 0 by passing a None or 0.
"""
if absolute_time_spent and absolute_time_spent.total_seconds() != 0:
self.data = post(self._token, self.url +
'/reset_spent_time', {'duration': None})
human_time_spent = \
str(int(absolute_time_spent.total_seconds())) + 's'
self.data = post(self._token, self.url +
'/add_spent_time',
{'duration': human_time_spent})
else:
self.data = post(self._token, self.url +
'/reset_spent_time', {'duration': None})
def add_to_total_time_spent(self, relative_time_spent: timedelta):
"""
Adds the value of relative_time_spent to total_time_spent.
Allows for positive and negative values.
Allows any time unit of the timedelta object.
Does nothing when passing None or 0.
"""
if relative_time_spent and relative_time_spent.total_seconds() != 0:
human_time_spent = \
str(int(relative_time_spent.total_seconds())) + 's'
self.data = post(self._token, self.url +
'/add_spent_time',
{'duration': human_time_spent})
......@@ -3,6 +3,7 @@ This module contains the Issue abstraction class which provides properties and
actions related to issues and bug reports.
"""
from datetime import datetime
from datetime import timedelta
from typing import Set
from typing import List
......@@ -228,3 +229,48 @@ class Issue(IGittObject):
Setter for the Milestone.
"""
raise NotImplementedError
@property
def time_estimate(self) -> timedelta:
"""
Retrieves the time_estimate in seconds.
Writes the time_estimate into
the seconds property of an timedelta object.
"""
raise NotImplementedError
@time_estimate.setter
def time_estimate(self, new_time_estimate: timedelta):
"""
Setter for the time_estimate.
Allows any time unit of the timedelta object.
"""
raise NotImplementedError
@property
def total_time_spent(self) -> timedelta:
"""
Retrieves the total_time_spent in seconds.
Writes the total_time_spent into the
seconds property of an timedelta object.
"""
raise NotImplementedError
@total_time_spent.setter
def total_time_spent(self, absolute_time_spent: timedelta):
"""
Writes the value of absolute_time_spent into total_time_spent.
Can't be less than 0.
Allows any time unit of the timedelta object.
Allows total_time_spent to be reset to 0 by passing a None or 0.
"""
raise NotImplementedError
def add_to_total_time_spent(self, relative_time_spent: timedelta):
"""
Adds the value of relative_time_spent to total_time_spent.
Allows for positive and negative values.
Allows any time unit of the timedelta object.
Does nothing when passing None or 0.
"""
raise NotImplementedError
interactions:
- request:
body: '{}'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
If-None-Match: [W/"0e14fd146ed0c5aa0b33221abdffb0d9"]
User-Agent: [IGitt]
method: GET
uri: https://gitlab.com/api/v4/projects/gitmate-test-user%2Ftest/issues/42?per_page=100
response:
body: {string: '{"id":10698538,"iid":42,"project_id":3439658,"title":"Issue to
test Milestone on Issues Setter. DO NOT DELETE","description":"","state":"opened","created_at":"2018-05-07T14:15:27.639Z","updated_at":"2018-07-12T09:57:07.735Z","closed_at":null,"closed_by":null,"labels":[],"milestone":null,"assignees":[],"author":{"id":463976,"name":"Sebastian
Latacz","username":"seblat","state":"active","avatar_url":"https://assets.gitlab-static.net/uploads/-/system/user/avatar/463976/avatar.png","web_url":"https://gitlab.com/seblat"},"assignee":null,"user_notes_count":0,"upvotes":0,"downvotes":0,"due_date":null,"confidential":false,"discussion_locked":null,"web_url":"https://gitlab.com/gitmate-test-user/test/issues/42","time_stats":{"time_estimate":7320,"total_time_spent":4401,"human_time_estimate":"2h
2m","human_total_time_spent":"1h 13m 21s"},"_links":{"self":"https://gitlab.com/api/v4/projects/3439658/issues/42","notes":"https://gitlab.com/api/v4/projects/3439658/issues/42/notes","award_emoji":"https://gitlab.com/api/v4/projects/3439658/issues/42/award_emoji","project":"https://gitlab.com/api/v4/projects/3439658"},"subscribed":true,"weight":null}'}
headers:
Cache-Control: ['max-age=0, private, must-revalidate']
Content-Length: ['1149']
Content-Type: [application/json]
Date: ['Thu, 12 Jul 2018 09:58:37 GMT']
Etag: [W/"2de88c52087c16ada07e5d0ee728d3ea"]
RateLimit-Limit: ['600']
RateLimit-Observed: ['1']
RateLimit-Remaining: ['599']
RateLimit-Reset: ['1531389577']
RateLimit-ResetTime: ['Fri, 12 Jul 2018 09:59:37 GMT']
Server: [nginx]
Strict-Transport-Security: [max-age=31536000]
Vary: [Origin]
X-Content-Type-Options: [nosniff]
X-Frame-Options: [SAMEORIGIN]
X-Request-Id: [c2936d97-2163-4ab2-881f-4bc9d3a25602]
X-Runtime: ['0.396356']
status: {code: 200, message: OK}
version: 1
interactions:
- request:
body: '{}'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
If-None-Match: [W/"15dbdfd6d54f8e826faee09946f158db"]
User-Agent: [IGitt]
method: GET
uri: https://gitlab.com/api/v4/projects/gitmate-test-user%2Ftest/issues/42?per_page=100
response:
body: {string: '{"id":10698538,"iid":42,"project_id":3439658,"title":"Issue to
test Milestone on Issues Setter. DO NOT DELETE","description":"","state":"opened","created_at":"2018-05-07T14:15:27.639Z","updated_at":"2018-07-12T10:09:36.995Z","closed_at":null,"closed_by":null,"labels":[],"milestone":null,"assignees":[],"author":{"id":463976,"name":"Sebastian
Latacz","username":"seblat","state":"active","avatar_url":"https://assets.gitlab-static.net/uploads/-/system/user/avatar/463976/avatar.png","web_url":"https://gitlab.com/seblat"},"assignee":null,"user_notes_count":0,"upvotes":0,"downvotes":0,"due_date":null,"confidential":false,"discussion_locked":null,"web_url":"https://gitlab.com/gitmate-test-user/test/issues/42","time_stats":{"time_estimate":7320,"total_time_spent":4400,"human_time_estimate":"2h
2m","human_total_time_spent":"1h 13m 20s"},"_links":{"self":"https://gitlab.com/api/v4/projects/3439658/issues/42","notes":"https://gitlab.com/api/v4/projects/3439658/issues/42/notes","award_emoji":"https://gitlab.com/api/v4/projects/3439658/issues/42/award_emoji","project":"https://gitlab.com/api/v4/projects/3439658"},"subscribed":true,"weight":null}'}
headers:
Cache-Control: ['max-age=0, private, must-revalidate']
Content-Length: ['1149']
Content-Type: [application/json]
Date: ['Thu, 12 Jul 2018 10:11:42 GMT']
Etag: [W/"c02ee0c37ba57afb9ba9b9020078eec5"]
RateLimit-Limit: ['600']
RateLimit-Observed: ['1']
RateLimit-Remaining: ['599']
RateLimit-Reset: ['1531390362']
RateLimit-ResetTime: ['Fri, 12 Jul 2018 10:12:42 GMT']
Server: [nginx]
Strict-Transport-Security: [max-age=31536000]
Vary: [Origin]
X-Content-Type-Options: [nosniff]
X-Frame-Options: [SAMEORIGIN]
X-Request-Id: [17852848-8777-42e2-8345-9deba0f2eb9e]
X-Runtime: ['0.426916']
status: {code: 200, message: OK}
version: 1
import os
import datetime
from datetime import timedelta
import pytest
from IGitt.GitLab import GitLabOAuthToken
from IGitt.GitLab.GitLabIssue import GitLabIssue
......@@ -104,3 +106,70 @@ class GitLabIssueTest(IGittTestCase):
'Permanent IGitt test milestone. DO NOT DELETE.')
issue.milestone = None
self.assertEqual(issue.milestone, None)
def test_time_estimate_getter(self):
issue = GitLabIssue(self.token, 'gitmate-test-user/test', 42)
self.assertEqual(issue.time_estimate, timedelta(seconds=7320))
def test_time_estimate_setter(self):
issue = GitLabIssue(self.token, 'gitmate-test-user/test', 42)
issue.time_estimate = None
self.assertEqual(issue.time_estimate, timedelta(seconds=0))
issue.time_estimate = timedelta(seconds=0)
self.assertEqual(issue.time_estimate, timedelta(seconds=0))
issue.time_estimate = timedelta(minutes=1)
self.assertEqual(issue.time_estimate, timedelta(seconds=60))
issue.time_estimate = timedelta(seconds=7320)
self.assertEqual(issue.time_estimate, timedelta(seconds=7320))
def test_total_time_spent_getter(self):
issue = GitLabIssue(self.token, 'gitmate-test-user/test', 42)
self.assertEqual(issue.total_time_spent, timedelta(seconds=4400))
def test_total_time_spent_setter(self):
issue = GitLabIssue(self.token, 'gitmate-test-user/test', 42)
# Writing absolute values
issue.total_time_spent = timedelta(seconds=6600)
self.assertEqual(issue.total_time_spent, timedelta(seconds=6600))
with pytest.raises(RuntimeError):
issue.total_time_spent = timedelta(seconds=-1)
# Different time units
issue.time_estimate = timedelta(minutes=1)
self.assertEqual(issue.time_estimate, timedelta(seconds=60))
# Reseting
issue.total_time_spent = None
self.assertEqual(issue.total_time_spent, timedelta(seconds=0))
issue.total_time_spent = timedelta(seconds=0)
self.assertEqual(issue.total_time_spent, timedelta(seconds=0))
# Restoring original value
issue.total_time_spent = timedelta(seconds=4400)
self.assertEqual(issue.total_time_spent, timedelta(seconds=4400))
def test_add_to_total_time_spent(self):
issue = GitLabIssue(self.token, 'gitmate-test-user/test', 42)
self.assertEqual(issue.total_time_spent, timedelta(seconds=4400))
# Writing relative values
issue.add_to_total_time_spent(timedelta(seconds=1))
self.assertEqual(issue.total_time_spent, timedelta(seconds=4401))
issue.add_to_total_time_spent(timedelta(seconds=-1))
self.assertEqual(issue.total_time_spent, timedelta(seconds=4400))
# Different time units
issue.add_to_total_time_spent(timedelta(minutes=1))
self.assertEqual(issue.total_time_spent, timedelta(seconds=4460))
issue.add_to_total_time_spent(timedelta(minutes=-1))
self.assertEqual(issue.total_time_spent, timedelta(seconds=4400))
# Adding nothing
issue.add_to_total_time_spent = None
self.assertEqual(issue.total_time_spent, timedelta(seconds=4400))
issue.add_to_total_time_spent = timedelta(seconds=0)
self.assertEqual(issue.total_time_spent, timedelta(seconds=4400))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment