]> git.immae.eu Git - perso/Immae/Config/Nix.git/blob - overlays/bugwarrior/mantisbt.patch
Upgrade syden peertube to flake
[perso/Immae/Config/Nix.git] / overlays / bugwarrior / mantisbt.patch
1 diff --git a/bugwarrior/services/mantisbt.py b/bugwarrior/services/mantisbt.py
2 new file mode 100644
3 index 0000000..e54af0d
4 --- /dev/null
5 +++ b/bugwarrior/services/mantisbt.py
6 @@ -0,0 +1,361 @@
7 +from builtins import filter
8 +import re
9 +import six
10 +
11 +import requests
12 +from jinja2 import Template
13 +
14 +from bugwarrior.config import asbool, aslist, die
15 +from bugwarrior.services import IssueService, Issue, ServiceClient
16 +
17 +import logging
18 +log = logging.getLogger(__name__)
19 +
20 +
21 +class MantisbtClient(ServiceClient):
22 + def __init__(self, host, token):
23 + self.host = host
24 + self.session = requests.Session()
25 + self.session.headers['Authorization'] = token
26 +
27 + def _api_url(self, path, **context):
28 + """ Build the full url to the API endpoint """
29 + baseurl = "https://{}/api/rest".format(self.host)
30 + return baseurl + path.format(**context)
31 +
32 + def get_user(self):
33 + return self.json_response(self.session.get(self._api_url("/users/me")))
34 +
35 + def get_projects(self):
36 + return self._getter(self._api_url("/projects"), subkey="projects")
37 +
38 + def get_issues(self):
39 + url = self._api_url("/issues?page_size=50")
40 + return self._getter(url, page_size=50, subkey="issues")
41 +
42 + def get_assigned_issues(self):
43 + """ Returns all issues assigned to authenticated user.
44 + """
45 + url = self._api_url("/issues?page_size=50&filter_id=assigned")
46 + return self._getter(url, page_size=50, subkey="issues")
47 +
48 + def get_monitored_issues(self):
49 + """ Returns all issues monitored by authenticated user.
50 + """
51 + url = self._api_url("/issues?page_size=50&filter_id=monitored")
52 + return self._getter(url, page_size=50, subkey="issues")
53 +
54 + def get_reported_issues(self):
55 + """ Returns all issues reported by authenticated user.
56 + """
57 + url = self._api_url("/issues?page_size=50&filter_id=reported")
58 + return self._getter(url, page_size=50, subkey="issues")
59 +
60 + def _getter(self, url, page_size=None, subkey=None):
61 + """ Pagination utility. Obnoxious. """
62 +
63 + results = []
64 + link = dict(next=url)
65 + page_number = 1
66 +
67 + while 'next' in link:
68 + if page_size is not None:
69 + response = self.session.get(link['next'] + "&page=" + str(page_number))
70 + else:
71 + response = self.session.get(link['next'])
72 +
73 + json_res = self.json_response(response)
74 +
75 + if subkey is not None:
76 + json_res = json_res[subkey]
77 +
78 + results += json_res
79 +
80 + if page_size is not None and len(json_res) == page_size:
81 + page_number += 1
82 + else:
83 + break
84 +
85 + return results
86 +
87 +class MantisbtIssue(Issue):
88 + TITLE = 'mantisbttitle'
89 + BODY = 'mantisbtbody'
90 + CREATED_AT = 'mantisbtcreatedon'
91 + UPDATED_AT = 'mantisbtupdatedat'
92 + CLOSED_AT = 'mantisbtclosedon'
93 + URL = 'mantisbturl'
94 + PROJECT = 'mantisbtproject'
95 + NUMBER = 'mantisbtnumber'
96 + USER = 'mantisbtuser'
97 + CATEGORY = 'mantisbtcategory'
98 + STATE = 'mantisbtstate'
99 +
100 + UDAS = {
101 + TITLE: {
102 + 'type': 'string',
103 + 'label': 'Mantisbt Title',
104 + },
105 + BODY: {
106 + 'type': 'string',
107 + 'label': 'Mantisbt Body',
108 + },
109 + CREATED_AT: {
110 + 'type': 'date',
111 + 'label': 'Mantisbt Created',
112 + },
113 + UPDATED_AT: {
114 + 'type': 'date',
115 + 'label': 'Mantisbt Updated',
116 + },
117 + CLOSED_AT: {
118 + 'type': 'date',
119 + 'label': 'Mantisbt Closed',
120 + },
121 + PROJECT: {
122 + 'type': 'string',
123 + 'label': 'Mantisbt Project',
124 + },
125 + URL: {
126 + 'type': 'string',
127 + 'label': 'Mantisbt URL',
128 + },
129 + NUMBER: {
130 + 'type': 'numeric',
131 + 'label': 'Mantisbt Issue #',
132 + },
133 + USER: {
134 + 'type': 'string',
135 + 'label': 'Mantisbt User',
136 + },
137 + CATEGORY: {
138 + 'type': 'string',
139 + 'label': 'Mantisbt Category',
140 + },
141 + STATE: {
142 + 'type': 'string',
143 + 'label': 'Mantisbt State',
144 + }
145 + }
146 + UNIQUE_KEY = (URL, NUMBER, )
147 +
148 + def _normalize_tag(self, label):
149 + return re.sub(r'[^a-zA-Z0-9]', '_', label)
150 +
151 + def to_taskwarrior(self):
152 + body = self.record.get('description')
153 + if body:
154 + body = body.replace('\r\n', '\n')
155 +
156 + created = self.parse_date(self.record.get('created_at'))
157 + updated = self.parse_date(self.record.get('updated_at'))
158 + closed_date = None
159 + if self.record["status"]["name"] in ["closed", "resolved"]:
160 + for history in self.record.get("history", []):
161 + if history.get("field", {}).get("name", "") == "status"\
162 + and history.get("new_value", {}).get("name", "") in ["closed", "resolved"]:
163 + closed_date = history["created_at"]
164 + closed = self.parse_date(closed_date)
165 +
166 + return {
167 + 'project': self.record['project']['name'],
168 + 'priority': self.origin['default_priority'],
169 + 'annotations': self.get_annotations(),
170 + 'tags': self.get_tags(),
171 + 'entry': created,
172 + 'end': closed,
173 +
174 + self.TITLE: self.record.get('summary'),
175 + self.BODY: body,
176 + self.CREATED_AT: created,
177 + self.UPDATED_AT: updated,
178 + self.CLOSED_AT: closed,
179 + self.URL: self.get_url(),
180 + self.PROJECT: self.record['project'].get('name'),
181 + self.NUMBER: self.record['id'],
182 + self.USER: self.record['reporter'].get('name'),
183 + self.CATEGORY: self.record['category'].get('name'),
184 + self.STATE: self.record['status'].get('label'),
185 + }
186 +
187 + def get_url(self):
188 + return "https://{}view.php?id={}".format(self.extra['host'], self.record["id"])
189 +
190 + def get_annotations(self):
191 + annotations = []
192 +
193 + context = self.record.copy()
194 + annotation_template = Template(self.origin['annotation_template'])
195 +
196 + for annotation_dict in self.record.get('notes', []):
197 + context.update({
198 + 'text': annotation_dict['text'],
199 + 'date': annotation_dict['created_at'],
200 + 'author': annotation_dict['reporter'].get('name', 'unknown'),
201 + 'view': annotation_dict['view_state']['label'],
202 + })
203 + annotations.append(
204 + annotation_template.render(context)
205 + )
206 + return annotations
207 +
208 + def get_tags(self):
209 + tags = []
210 +
211 + context = self.record.copy()
212 + tag_template = Template(self.origin['tag_template'])
213 +
214 + for tag_dict in self.record.get('tags', []):
215 + context.update({
216 + 'tag': self._normalize_tag(tag_dict['name'])
217 + })
218 + tags.append(
219 + tag_template.render(context)
220 + )
221 +
222 + return tags
223 +
224 + def get_default_description(self):
225 + return self.build_default_description(
226 + title=self.record['summary'],
227 + url=self.get_processed_url(self.get_url()),
228 + number=self.record['id'],
229 + )
230 +
231 +
232 +class MantisbtService(IssueService):
233 + ISSUE_CLASS = MantisbtIssue
234 + CONFIG_PREFIX = 'mantisbt'
235 +
236 + def __init__(self, *args, **kw):
237 + super(MantisbtService, self).__init__(*args, **kw)
238 +
239 + self.host = self.config.get('host', 'www.mantisbt.org/bugs/')
240 +
241 + token = self.get_password('token')
242 +
243 + self.client = MantisbtClient(self.host, token)
244 + self.user = None
245 +
246 + self.exclude_projects = self.config.get('exclude_projects', [], aslist)
247 + self.include_projects = self.config.get('include_projects', [], aslist)
248 +
249 + self.involved_issues = self.config.get(
250 + 'involved_issues', default=True, to_type=asbool
251 + )
252 + self.assigned_issues = self.config.get(
253 + 'assigned_issues', default=False, to_type=asbool
254 + )
255 + self.monitored_issues = self.config.get(
256 + 'monitored_issues', default=False, to_type=asbool
257 + )
258 + self.reported_issues = self.config.get(
259 + 'reported_issues', default=False, to_type=asbool
260 + )
261 + self.tag_template = self.config.get(
262 + 'tag_template', default='{{tag}}', to_type=six.text_type
263 + )
264 + self.annotation_template = self.config.get(
265 + 'annotation_template', default='{{date}} {{author}} ({{view}}): {{text}}', to_type=six.text_type
266 + )
267 +
268 + def get_service_metadata(self):
269 + return {
270 + 'tag_template': self.tag_template,
271 + 'annotation_template': self.annotation_template,
272 + }
273 +
274 + def filter_involved_issues(self, issue):
275 + _, issue = issue
276 + user = self.client.get_user()
277 + uid = user["id"]
278 + if issue["reporter"]["id"] != uid and \
279 + issue.get("handler", {}).get("id") != uid and \
280 + all([ x.get("user", {}).get("id") != uid for x in issue.get("history", [])]) and \
281 + all([ x.get("user", {}).get("id") != uid for x in issue.get("monitors", [])]):
282 + return False
283 + return self.filter_project_name(issue["project"]["name"])
284 +
285 + def filter_issues(self, issue):
286 + _, issue = issue
287 + return self.filter_project_name(issue["project"]["name"])
288 +
289 + def filter_project_name(self, name):
290 + if self.exclude_projects:
291 + if name in self.exclude_projects:
292 + return False
293 +
294 + if self.include_projects:
295 + if name in self.include_projects:
296 + return True
297 + else:
298 + return False
299 +
300 + return True
301 +
302 + @staticmethod
303 + def get_keyring_service(service_config):
304 + host = service_config.get('host', 'www.mantisbt.org/bugs/')
305 + username = service_config.get('username', default='nousername')
306 + return "mantisbt://{username}@{host}".format(username=username,
307 + host=host)
308 +
309 + @staticmethod
310 + def to_issue_dict(issues):
311 + return { i['id']: i for i in issues }
312 +
313 + def get_owner(self, issue):
314 + return issue.get("handler", {}).get("name")
315 +
316 + def get_author(self, issue):
317 + return issue.get("reporter", {}).get("name")
318 +
319 + def issues(self):
320 + issues = {}
321 + is_limited = self.assigned_issues or self.monitored_issues or self.reported_issues
322 +
323 + if self.assigned_issues:
324 + issues.update(
325 + filter(self.filter_issues, self.to_issue_dict(self.client.get_assigned_issues()).items())
326 + )
327 + if self.monitored_issues:
328 + issues.update(
329 + filter(self.filter_issues, self.to_issue_dict(self.client.get_monitored_issues()).items())
330 + )
331 + if self.reported_issues:
332 + issues.update(
333 + filter(self.filter_issues, self.to_issue_dict(self.client.get_reported_issues()).items())
334 + )
335 +
336 + if not is_limited:
337 + all_issues = self.to_issue_dict(self.client.get_issues())
338 + if self.involved_issues:
339 + issues.update(
340 + filter(self.filter_involved_issues, all_issues.items())
341 + )
342 + else:
343 + issues.update(
344 + filter(self.filter_issues, all_issues.items())
345 + )
346 +
347 + log.debug(" Found %i issues.", len(issues))
348 + if not is_limited:
349 + issues = list(filter(self.include, issues.values()))
350 + else:
351 + issues = list(issues.values())
352 + log.debug(" Pruned down to %i issues.", len(issues))
353 +
354 + for issue in issues:
355 + issue_obj = self.get_issue_for_record(issue)
356 + extra = {
357 + 'host': self.host
358 + }
359 + issue_obj.update_extra(extra)
360 + yield issue_obj
361 +
362 + @classmethod
363 + def validate_config(cls, service_config, target):
364 + if 'token' not in service_config:
365 + die("[%s] has no 'mantisbt.token'" % target)
366 +
367 + super(MantisbtService, cls).validate_config(service_config, target)
368 diff --git a/setup.py b/setup.py
369 index d6d957a..665e36e 100644
370 --- a/setup.py
371 +++ b/setup.py
372 @@ -80,6 +80,7 @@ setup(name='bugwarrior',
373 activecollab2=bugwarrior.services.activecollab2:ActiveCollab2Service
374 activecollab=bugwarrior.services.activecollab:ActiveCollabService
375 jira=bugwarrior.services.jira:JiraService
376 + mantisbt=bugwarrior.services.mantisbt:MantisbtService
377 megaplan=bugwarrior.services.megaplan:MegaplanService
378 phabricator=bugwarrior.services.phab:PhabricatorService
379 versionone=bugwarrior.services.versionone:VersionOneService