diff --git a/bugwarrior/README.rst b/bugwarrior/README.rst index 651f3e898..6987ae795 100644 --- a/bugwarrior/README.rst +++ b/bugwarrior/README.rst @@ -16,6 +16,7 @@ It currently supports the following remote resources: - `redmine `_ - `jira `_ - `activecollab `_ (2.x and 3.x) + - `phabricator `_ Configuring ----------- @@ -268,6 +269,23 @@ Create a ``~/.bugwarriorrc`` file with the following contents. # - jiraid #description_template = {{ jiraid }}: {{ jirasummary }} + # Here's an example of a phabricator target + [my_phabricator] + service = phabricator + + # No need to specify credentials. They are gathered from ~/.arcrc + + add_tags = whatever + + # You can override how an issue's description is created by entering + # a one-line Jinja template like the below; in addition to the default + # taskwarrior issue properties (project, priority, due, etc), the + # following properties are available for Teamlab issues: + # - phabricatorurl + # - phabricatorid + # - phabricatortitle + #description_template = #{{ phabricatorid }}: {{ phabricatortitle }} + # Here's an example of a teamlab target. [my_teamlab] service = teamlab @@ -433,8 +451,6 @@ Hacking on It See the `HACKING.rst `_ file. - - Contributors ------------ diff --git a/bugwarrior/services/__init__.py b/bugwarrior/services/__init__.py index b014b5f93..b26746bf1 100644 --- a/bugwarrior/services/__init__.py +++ b/bugwarrior/services/__init__.py @@ -473,3 +473,9 @@ def aggregate_issues(conf): SERVICES['megaplan'] = MegaplanService except ImportError: pass + +try: + from .phab import PhabricatorService + SERVICES['phabricator'] = PhabricatorService +except ImportError as e: + pass diff --git a/bugwarrior/services/phab.py b/bugwarrior/services/phab.py new file mode 100644 index 000000000..e57a5278e --- /dev/null +++ b/bugwarrior/services/phab.py @@ -0,0 +1,107 @@ +import six +from twiggy import log + +from bugwarrior.services import IssueService, Issue + +# This comes from PyPI +import phabricator + + +class PhabricatorIssue(Issue): + TITLE = 'phabricatortitle' + URL = 'phabricatorurl' + TYPE = 'phabricatortype' + OBJECT_NAME = 'phabricatorid' + + UDAS = { + TITLE: { + 'type': 'string', + 'label': 'Phabricator Title', + }, + URL: { + 'type': 'string', + 'label': 'Phabricator URL', + }, + TYPE: { + 'type': 'string', + 'label': 'Phabricator Type', + }, + OBJECT_NAME: { + 'type': 'string', + 'label': 'Phabricator Object', + }, + } + UNIQUE_KEY = (URL, TYPE,) + + def to_taskwarrior(self): + return { + 'project': self.extra['project'], + 'priority': self.origin['default_priority'], + 'annotations': self.extra.get('annotations', []), + + self.URL: self.record['uri'], + self.TYPE: self.extra['type'], + self.TITLE: self.record['title'], + self.OBJECT_NAME: self.record['uri'].split('/')[-1], + } + + def get_default_description(self): + return self.build_default_description( + title=self.record['title'], + url=self.get_processed_url(self.record['uri']), + number=self.record['uri'].split('/')[-1], + cls=self.extra['type'], + ) + + +class PhabricatorService(IssueService): + ISSUE_CLASS = PhabricatorIssue + CONFIG_PREFIX = 'phabricator' + + def __init__(self, *args, **kw): + super(PhabricatorService, self).__init__(*args, **kw) + # These reads in login credentials from ~/.arcrc + self.api = phabricator.Phabricator() + + def issues(self): + + # TODO -- get a list of these from the api + projects = {} + + issues = self.api.maniphest.query(status='status-open') + issues = list(issues.iteritems()) + + log.name(self.target).info("Found %i issues" % len(issues)) + + for phid, issue in issues: + project = self.target # a sensible default + try: + project = projects.get(issue['projectPHIDs'][0], project) + except IndexError: + pass + + extra = { + 'project': project, + 'type': 'issue', + #'annotations': self.annotations(phid, issue) + } + yield self.get_issue_for_record(issue, extra) + + diffs = self.api.differential.query(status='status-open') + diffs = list(diffs) + + log.name(self.target).info("Found %i differentials" % len(diffs)) + + for diff in list(diffs): + project = self.target # a sensible default + try: + project = projects.get(issue['projectPHIDs'][0], project) + except IndexError: + pass + + extra = { + 'project': project, + 'type': 'pull_request', + #'annotations': self.annotations(phid, issue) + } + yield self.get_issue_for_record(diff, extra)