-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- issue listing - issue get/set/toggle of labels, milestones and open-close status - issue edit of issue - parsing of notification mail to extract repo_slug and issue number fixes #104 Signed-off-by: Guyzmo <[email protected]>
- Loading branch information
Showing
3 changed files
with
417 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,12 +3,14 @@ | |
import logging | ||
log = logging.getLogger('git_repo.github') | ||
|
||
from ..service import register_target, RepositoryService, os | ||
from ...exceptions import ResourceError, ResourceExistsError, ResourceNotFoundError | ||
from ..service import register_target, RepositoryService, os, parse_comma_string_to_list | ||
from ...exceptions import ResourceError, ResourceExistsError, ResourceNotFoundError, ArgumentError | ||
|
||
import github3 | ||
|
||
from git.exc import GitCommandError | ||
from collections import namedtuple | ||
|
||
|
||
@register_target('hub', 'github') | ||
class GithubService(RepositoryService): | ||
|
@@ -304,6 +306,184 @@ def request_fetch(self, user, repo, request, pull=False, force=False): | |
raise ResourceNotFoundError('Could not find opened request #{}'.format(request)) from err | ||
raise err | ||
|
||
'''Issues''' | ||
|
||
ISSUE_FILTER_DEFAULTS=dict( | ||
state = 'all', | ||
milestone = None, | ||
assignee = None, | ||
mentioned = None, | ||
labels = [], | ||
sort = None, | ||
direction = 'desc', | ||
since = None, | ||
) | ||
|
||
def issue_list_parse_filter_statement(self, filter_stmt, transform=None): | ||
from copy import deepcopy | ||
|
||
params = deepcopy(self.ISSUE_FILTER_DEFAULTS) | ||
|
||
for f in parse_comma_string_to_list(filter_stmt): | ||
if ':' in f: | ||
param, value_head, *value_tail = f.split(':') | ||
value = "".join([value_head] + value_tail) # fix labels containing : | ||
if transform: | ||
param, value = transform(param, value) | ||
if not param in params.keys(): | ||
raise ArgumentError('Unknown filter key {}'.format(param)) | ||
if isinstance(params[param], list): | ||
params[param].append(value) | ||
else: | ||
params[param] = value | ||
return params | ||
|
||
def issue_extract_from_file(self, it): | ||
# Message-ID: <guyzmo/git-repo/issues/1/[email protected]> | ||
for line in it: | ||
if line.lower().startswith('message-id:'): | ||
_, line = line.lower().split('message-id: <') | ||
user, repo, _, issue, *_ = line.lower().split('/') | ||
return user, repo, [issue] | ||
|
||
def issue_label_list(self, user, repo): | ||
repository = self.gh.repository(user, repo) | ||
yield ("Name",) | ||
return [(yield l.name) for l in repository.iter_labels()] | ||
|
||
def issue_milestone_list(self, user, repo): | ||
repository = self.gh.repository(user, repo) | ||
yield ("Name",) | ||
return [(yield l.title) for l in repository.iter_milestones()] | ||
|
||
def issue_grab(self, user, repo, issue_id): | ||
repository = self.gh.repository(user, repo) | ||
issue = repository.issue(issue_id) | ||
return dict( | ||
id=issue.number, | ||
state=issue.state, | ||
title=issue.title, | ||
uri=issue.html_url, | ||
poster=issue.user.login, | ||
milestone=issue.milestone, | ||
labels=[label.name for label in issue.labels], | ||
creation=issue.created_at.isoformat(), | ||
closed_at=issue.closed_at, | ||
closed_by=issue.closed_by, | ||
body=issue.body, | ||
assignee=issue.assignee.login if issue.assignee else None, | ||
repository='/'.join(issue.repository) | ||
) | ||
|
||
def issue_list(self, user, repo, filter_str=''): | ||
params = self.issue_list_parse_filter_statement( | ||
filter_stmt=filter_str, | ||
transform=lambda k,v: (k.replace('status', 'state').replace('label', 'labels'), v) | ||
) | ||
|
||
repository = self.gh.repository(user, repo) | ||
yield (None, "Id", "Labels", "Title", "URL") | ||
for issue in repository.iter_issues(**params): | ||
yield ( not issue.is_closed(), | ||
str(issue.number), | ||
','.join([l.name for l in issue.labels]), | ||
issue.title, | ||
issue.html_url, | ||
issue.pull_request) | ||
|
||
def issue_edit(self, user, repo, issue, edit_cb): | ||
repository = self.gh.repository(user, repo) | ||
issue_obj = repository.issue(issue) | ||
updated_issue = edit_cb(issue_obj.title, issue_obj.body) | ||
if not updated_issue: | ||
return False | ||
return issue_obj.edit(title=updated_issue['title'], body=updated_issue['body']) | ||
|
||
def issue_action(self, user, repo, action, value, filter_str, issues, application): | ||
repository = self.gh.repository(user, repo) | ||
params = self.issue_list_parse_filter_statement( | ||
filter_stmt=filter_str, | ||
transform=lambda k,v: (k.replace('label', 'labels'), v) | ||
) | ||
for issue in repository.iter_issues(**params): | ||
if not issues or str(issue.number) in issues: | ||
if action == "mark": | ||
if value.lower() in ('opened', 'open', 'o'): | ||
return application['mark'](issue, opened=True) | ||
elif value.lower() in ('closed', 'close', 'c'): | ||
return application['mark'](issue, opened=False) | ||
|
||
if action == "label": | ||
labels = set() | ||
labels_avail = {l.name: l for l in repository.iter_labels()} | ||
for label in parse_comma_string_to_list(value): | ||
if label in labels_avail: | ||
labels.add(labels_avail[label]) | ||
else: | ||
raise ArgumentError("Label '{}' is invalid.".format(value)) | ||
return application['label'](issue, list(labels)) | ||
|
||
if action == "milestone": | ||
milestones = list(repository.iter_milestones()) | ||
for milestone in milestones: | ||
if value == milestone.title: | ||
return application['milestone'](issue, milestone) | ||
raise ArgumentError("Milestone '{}' is invalid.".format(value)) | ||
|
||
def issue_set(self, user, repo, action, value, filter_str, issues): | ||
def set_mark(issue, opened): | ||
if opened: | ||
return issue.reopen() | ||
return issue.close() | ||
def add_labels(issue, labels): | ||
return issue.add_labels(*[l.name for l in labels]) | ||
def set_milestone(issue, milestone): | ||
return issue.edit(milestone=milestone.number) | ||
|
||
return self.issue_action(user, repo, action, value, filter_str, issues, dict( | ||
mark=set_mark, | ||
label=add_labels, | ||
milestone=set_milestone | ||
) | ||
) | ||
|
||
def issue_unset(self, user, repo, action, value, filter_str, issues): | ||
def unset_mark(issue, opened): | ||
raise ArgumentError('Cannot unset marks.') | ||
def remove_labels(issue, labels): | ||
for l in labels: | ||
if not issue.remove_label(l.name): | ||
return False | ||
def unset_milestone(issue, milestone): | ||
return issue.edit(milestone=0) | ||
|
||
return self.issue_action(user, repo, action, value, filter_str, issues, dict( | ||
mark=unset_mark, | ||
label=remove_labels, | ||
milestone=unset_milestone | ||
) | ||
) | ||
|
||
def issue_toggle(self, user, repo, action, filter_str, issues): | ||
def toggle_mark(issue, opened): | ||
if issue.is_closed(): | ||
return issue.reopen() | ||
return issue.close() | ||
def toggle_labels(issue, labels): | ||
issue_labels = set(issue.iter_labels()).symmetric_difference(labels) | ||
return issue.replace_labels(*[l.name for l in labels]) | ||
def set_milestone(issue, milestone): | ||
if issue.milestone: | ||
return issue.edit(milestone=0) | ||
return issue.edit(milestone=milestone.number) | ||
|
||
return self.issue_action(user, repo, action, filter_str, issues, dict( | ||
mark=unset_mark, | ||
label=remove_labels, | ||
milestone=unset_milestone | ||
) | ||
) | ||
|
||
@classmethod | ||
def get_auth_token(cls, login, password, prompt=None): | ||
import platform | ||
|
Oops, something went wrong.