-
Notifications
You must be signed in to change notification settings - Fork 0
/
keeper.py
138 lines (113 loc) · 4.76 KB
/
keeper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/usr/bin/env python3
import os, sys
import json
from urllib import request
from urllib.parse import urlencode
ENTRY = os.environ.get('GITLAB_URL')
TOKEN = os.environ.get('GITLAB_TOKEN')
DOING_LABEL = 'Doing'
GANTT_START = 'GanttStart:'
GANTT_END = 'GanttDue:'
# ToDo: get project by url
# ToDO: group by milestone
# ToDo: issue start date by /spend
class Gitlab:
def __init__(self, gitlab_url, gitlab_token):
self.gitlab_url, self.gitlab_token = gitlab_url, gitlab_token
def req_api(self, path, data=None):
headers = {
'PRIVATE-TOKEN': self.gitlab_token,
'Access-Control-Request-Headers': 'private-token',
'User-Agent': 'gitlab-issue-keeper',
}
if data is None:
r = request.Request(f'{self.gitlab_url}{path}', headers=headers)
else:
params = urlencode(data)
# stupid RESTful
r = request.Request(f'{self.gitlab_url}{path}?{params}', headers=headers, method='PUT')
return json.loads(request.urlopen(r, timeout=4).read())
def get_projects(self):
r = self.req_api('/api/v4/projects/?membership=true&per_page=100')
return dict((
p["path_with_namespace"], {
"path": p["path_with_namespace"],
"name": p["name"],
"namespace": p["namespace"].get("path"),
"last_activity_at": p["last_activity_at"],
"description": p["description"],
"url": p["web_url"],
"id": p["id"],
}
) for p in r if
p["archived"] is False and p["namespace"].get("kind") == "group")
@classmethod
def guess_progress(cls, issue):
total = issue["time_stats"].get("time_estimate")
if not total or total <= 0:
return
spent = issue["time_stats"].get("total_time_spent") or 0
return spent / total * 100
class GitlabProject:
def __init__(self, gitlab_obj, project_id):
self.project_id = project_id
self.gitlab_obj = gitlab_obj
@property
def labels_map(self):
labels = getattr(self, '_labels', None)
self.labels = labels or dict((x['name'], x['id']) for x in
self.gitlab_obj.req_api(f'/api/v4/projects/{self.project_id}/labels') or []) # noqa
return self.labels
def get_issue_notes(self, iid):
r = self.gitlab_obj.req_api(f'/api/v4/projects/{self.project_id}/issues/{iid}/notes')
return r
def get_doing_close_date(self, iid):
label_id = self.labels_map[DOING_LABEL]
issue_notes = list(self.get_issue_notes(iid))
starts = sorted([x for x in issue_notes if
x['system'] and x['body'] == f'added ~{label_id} label' # noqa
], key=lambda x: x['id']) or issue_notes[-2:]
if not starts:
return None, None
ends1 = sorted([x for x in issue_notes if
x['system'] and x['body'] == 'closed' # noqa
], key=lambda x: x['id'])
ends2 = sorted([x for x in issue_notes if
x['system'] and x['body'] == f'removed ~{label_id} label' # noqa
], key=lambda x: x['id'])
ends = ends1[-2:] + ends2[-2:]
return starts[0]['updated_at'], min(ends, key=lambda x: x['id'])['updated_at'] if ends else None
def list_issues(self):
r = self.gitlab_obj.req_api(f'/api/v4/projects/{self.project_id}/issues?page=1&per_page=100&state=all')
for issue in r:
start, end = self.get_doing_close_date(issue['iid'])
yield issue, start, end
def update_issue(self, issue, start, end):
"""issue = {iid: 1, description: "", project_id: 0}"""
gantt_str = ''
if start:
gantt_str = '%s\n%s%s' % (gantt_str, GANTT_START, start)
if end:
gantt_str = '%s\n%s%s' % (gantt_str, GANTT_END, end)
if start or end:
# remove old str
lines = []
inline_edit = False
for l in issue['description'].splitlines():
if l.startswith(GANTT_START) and start:
lines.append(f'{GANTT_START}{start}')
inline_edit = True
elif l.startswith(GANTT_END) and end:
lines.append(f'{GANTT_END}{end}')
inline_edit = True
desc_back = '\n'.join(lines)
desc = '%s\n\n<!--\n下面是issue耗时跟踪不要删\n%s\n-->' % (desc_back, gantt_str)
r = self.gitlab_obj.req_api(f'/api/v4/projects/{issue["project_id"]}/issues/{issue["iid"]}', {"description": desc})
def output_json(data, padding=None):
if padding:
# @ToDo: add sanitize padding
return '%s(%s)' % (padding, json.dumps(data))
else:
return json.dumps(data)
if '__main__' == __name__:
exit(0)