You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2013/09/19 17:31:43 UTC

[01/23] git commit: [#6535] ticket:417 rebased master and 42cc_6533

Updated Branches:
  refs/heads/master 8c07c39de -> 871731e94


[#6535] ticket:417 rebased master and 42cc_6533


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/fd9404a6
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/fd9404a6
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/fd9404a6

Branch: refs/heads/master
Commit: fd9404a699ac4ad6ac268836c32dabddbba6eb3e
Parents: 6062bc7
Author: Anton Kasyanov <mi...@gmail.com>
Authored: Tue Aug 27 14:03:32 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:49 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/__init__.py | 17 +++++------------
 ForgeImporters/forgeimporters/github/tracker.py  |  2 --
 2 files changed, 5 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fd9404a6/ForgeImporters/forgeimporters/github/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/__init__.py b/ForgeImporters/forgeimporters/github/__init__.py
index 8e1a802..13d32e4 100644
--- a/ForgeImporters/forgeimporters/github/__init__.py
+++ b/ForgeImporters/forgeimporters/github/__init__.py
@@ -27,21 +27,14 @@ log = logging.getLogger(__name__)
 
 class GitHubProjectExtractor(base.ProjectExtractor):
     PAGE_MAP = {
-            'project_info': 'https://api.github.com/repos/{project}',
-            'issues': 'https://api.github.com/repos/{project}/issues',
+            'project_info': 'https://api.github.com/repos/{project_name}',
+            'issues': 'https://api.github.com/repos/{project_name}/issues',
         }
     POSSIBLE_STATES = ('opened', 'closed')
 
     def parse_page(self, page):
         return json.loads(page.read().decode('utf8'))
 
-    def __init__(self, allura_project, gh_project_name, page):
-        self.project = allura_project
-        self.gh_project_name = gh_project_name
-        self.url = self.PAGE_MAP[page].format(
-            project=urllib.quote(gh_project_name),
-        )
-
     def get_summary(self):
         return self.get_page('project_info').get('description')
 
@@ -54,12 +47,12 @@ class GitHubProjectExtractor(base.ProjectExtractor):
     def iter_issues(self):
         # github api doesn't allow getting closed and opened tickets in one query
         issues = []
-        self.url += '?state={state}'
+        url = self.get_page_url('issues') + '?state={state}'
         for state in self.POSSIBLE_STATES:
-            issue_list_url = self.url.format(
+            issue_list_url = url.format(
                 state=state,
             )
-            issues += json.loads(urllib2.urlopen(issue_list_url).read().decode('utf8'))
+            issues += json.loads(self.urlopen(issue_list_url).read().decode('utf8'))
         issues.sort(key=lambda x: x['number'])
         for issue in issues:
             yield (issue['number'], issue)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fd9404a6/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index 4cd433b..af5363d 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -35,9 +35,7 @@ class GitHubTrackerImporter(ToolImporter):
             )
         ThreadLocalORMSession.flush_all()
         extractor = GitHubProjectExtractor(
-            project,
             '{}/{}'.format(kw['user_name'],project_name),
-            'issues',
         )
         try:
             M.session.artifact_orm_session._get().skip_mod_date = True


[06/23] git commit: [#6535] ticket:423 added exception handling

Posted by br...@apache.org.
[#6535] ticket:423 added exception handling


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/da98a804
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/da98a804
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/da98a804

Branch: refs/heads/master
Commit: da98a8048132b8203058e6545cbe165d72977fac
Parents: 51b9042
Author: coldmind <so...@yandex.ru>
Authored: Wed Sep 11 16:08:12 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:50 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/tracker.py | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/da98a804/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index 01a08e4..ec9729f 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -132,7 +132,12 @@ class GitHubTrackerImporter(ToolImporter):
         REGEXP = r'!\[[\w0-9]+?\]\(((?:https?:\/\/)?[\da-z\.-]+\.[a-z\.]{2,6}'\
             '(?:[\/\w\.-]+)*.(jpg|jpeg|png|gif))\)\r\n'
         attachments = []
-        found_matches = re.finditer(REGEXP, body, re.IGNORECASE)
+
+        try:
+            found_matches = re.finditer(REGEXP, body, re.IGNORECASE)
+        except TypeError:
+            found_matches = re.finditer(REGEXP, str(body), re.IGNORECASE)
+
         for i, match in enumerate(found_matches):
             # removing attach text from comment
             body = body.replace(match.group(0), '')


[23/23] git commit: [#6535] minor code cleanup

Posted by br...@apache.org.
[#6535] minor code cleanup


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/871731e9
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/871731e9
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/871731e9

Branch: refs/heads/master
Commit: 871731e9422907711980024c6d65313c6879e0c4
Parents: 503c4f7
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Thu Sep 19 14:46:45 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:52 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/tracker.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/871731e9/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index 655980a..e441516 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -154,8 +154,7 @@ class GitHubTrackerImporter(ToolImporter):
             if comment['user']:
                 posted_by = u'*Originally posted by:* {}\n'.format(
                     self.get_user_link(comment['user']['login']))
-                posted_by += body
-                body = posted_by
+                body = posted_by + body
             p = ticket.discussion_thread.add_post(
                     text = body,
                     ignore_security = True,


[04/23] git commit: [#6535] ticket:417 controller for github import

Posted by br...@apache.org.
[#6535] ticket:417 controller for github import

Conflicts:
	ForgeImporters/forgeimporters/base.py


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/8a32af87
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/8a32af87
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/8a32af87

Branch: refs/heads/master
Commit: 8a32af87366189a757d99109a933cd2b19f091fc
Parents: 8c07c39
Author: Anton Kasyanov <mi...@gmail.com>
Authored: Mon Aug 26 16:44:34 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:49 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/base.py           | 1 +
 ForgeImporters/forgeimporters/github/project.py | 2 ++
 ForgeImporters/forgeimporters/github/tracker.py | 8 ++++++++
 ForgeImporters/setup.py                         | 1 +
 4 files changed, 12 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/8a32af87/ForgeImporters/forgeimporters/base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/base.py b/ForgeImporters/forgeimporters/base.py
index ea01968..2f296a0 100644
--- a/ForgeImporters/forgeimporters/base.py
+++ b/ForgeImporters/forgeimporters/base.py
@@ -210,6 +210,7 @@ class ProjectImporter(BaseController):
         tools = {}
         for ep in h.iter_entry_points('allura.importers'):
             epv = ep.load()
+            print epv, epv.source, self.source
             if epv.source == self.source:
                 tools[ep.name] = epv()
         return tools

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/8a32af87/ForgeImporters/forgeimporters/github/project.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/project.py b/ForgeImporters/forgeimporters/github/project.py
index 64a499e..1255933 100644
--- a/ForgeImporters/forgeimporters/github/project.py
+++ b/ForgeImporters/forgeimporters/github/project.py
@@ -57,6 +57,8 @@ class GitHubProjectImporter(base.ProjectImporter):
     @expose()
     @validate(process_validator)
     def process(self, **kw):
+        if not type(kw['tools']) == list:
+            kw['tools'] = [kw['tools']]
         return super(self.__class__, self).process(**kw)
 
     @expose('json:')

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/8a32af87/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
new file mode 100644
index 0000000..798da5c
--- /dev/null
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -0,0 +1,8 @@
+from ..base import ToolImporter
+from forgetracker.tracker_main import ForgeTrackerApp
+
+class GitHubTrackerImporter(ToolImporter):
+    source = 'GitHub'
+    target_app = ForgeTrackerApp
+    controller = None
+    tool_label = 'Issues'
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/8a32af87/ForgeImporters/setup.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/setup.py b/ForgeImporters/setup.py
index eaeb7df..12811f2 100644
--- a/ForgeImporters/setup.py
+++ b/ForgeImporters/setup.py
@@ -39,6 +39,7 @@ setup(name='ForgeImporters',
       github = forgeimporters.github.project:GitHubProjectImporter
 
       [allura.importers]
+      github-tracker = forgeimporters.github.tracker:GitHubTrackerImporter
       google-code-tracker = forgeimporters.google.tracker:GoogleCodeTrackerImporter
       google-code-repo = forgeimporters.google.code:GoogleRepoImporter
       github-repo = forgeimporters.github.code:GitHubRepoImporter


[20/23] git commit: [#6535] ticket:424 Refactoring: use self.get_user_link everywhere

Posted by br...@apache.org.
[#6535] ticket:424 Refactoring: use self.get_user_link everywhere


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/2ca21ae5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/2ca21ae5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/2ca21ae5

Branch: refs/heads/master
Commit: 2ca21ae5f50bc5214f823cb17c1126995c4cf7b8
Parents: 7861638
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 17 15:03:31 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:51 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/tracker.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/2ca21ae5/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index 96e8be6..1b37e5b 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -151,8 +151,8 @@ class GitHubTrackerImporter(ToolImporter):
         for comment in extractor.iter_comments(issue):
             body, attachments = self._get_attachments(comment['body'])
             if comment['user']:
-                posted_by = u'*Originally posted by: [{0}](https://github.com/{0})*\n'.format(
-                    comment['user']['login'])
+                posted_by = u'*Originally posted by: {}*\n'.format(
+                    self.get_user_link(comment['user']['login']))
                 posted_by += body
                 body = posted_by
             p = ticket.discussion_thread.add_post(


[03/23] git commit: [#6535] ticket:417 basic github tickets import

Posted by br...@apache.org.
[#6535] ticket:417 basic github tickets import

Conflicts:
	ForgeImporters/forgeimporters/base.py


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/5a82f13a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/5a82f13a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/5a82f13a

Branch: refs/heads/master
Commit: 5a82f13a32c497cf6d858a3c8155c5e274d34fc6
Parents: 8a32af8
Author: Anton Kasyanov <mi...@gmail.com>
Authored: Mon Aug 26 18:56:42 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:49 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/base.py           |   1 -
 .../forgeimporters/github/__init__.py           |  25 ++++-
 ForgeImporters/forgeimporters/github/tracker.py | 112 ++++++++++++++++++-
 3 files changed, 135 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5a82f13a/ForgeImporters/forgeimporters/base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/base.py b/ForgeImporters/forgeimporters/base.py
index 2f296a0..ea01968 100644
--- a/ForgeImporters/forgeimporters/base.py
+++ b/ForgeImporters/forgeimporters/base.py
@@ -210,7 +210,6 @@ class ProjectImporter(BaseController):
         tools = {}
         for ep in h.iter_entry_points('allura.importers'):
             epv = ep.load()
-            print epv, epv.source, self.source
             if epv.source == self.source:
                 tools[ep.name] = epv()
         return tools

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5a82f13a/ForgeImporters/forgeimporters/github/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/__init__.py b/ForgeImporters/forgeimporters/github/__init__.py
index 5cd6dc1..35c5d03 100644
--- a/ForgeImporters/forgeimporters/github/__init__.py
+++ b/ForgeImporters/forgeimporters/github/__init__.py
@@ -25,12 +25,22 @@ log = logging.getLogger(__name__)
 
 class GitHubProjectExtractor(base.ProjectExtractor):
     PAGE_MAP = {
-            'project_info': 'https://api.github.com/repos/{project_name}',
+            'project_info': 'https://api.github.com/repos/{project}',
+            'issues': 'https://api.github.com/repos/{project}/issues',
         }
+    POSSIBLE_STATES = ('opened', 'closed')
 
     def parse_page(self, page):
         return json.loads(page.read().decode('utf8'))
 
+    def __init__(self, allura_project, gh_project_name, page):
+        self.project = allura_project
+        self.gh_project_name = gh_project_name
+        self.url = self.PAGE_MAP[page].format(
+            project=urllib.quote(gh_project_name),
+        )
+        self.page = json.loads(urllib2.urlopen(self.url).read().decode('utf8'))
+
     def get_summary(self):
         return self.get_page('project_info').get('description')
 
@@ -39,3 +49,16 @@ class GitHubProjectExtractor(base.ProjectExtractor):
 
     def get_repo_url(self):
         return self.get_page('project_info').get('clone_url')
+
+    def iter_issues(self):
+        # github api doesn't allow getting closed and opened tickets in one query
+        issues = []
+        self.url += '?state={state}'
+        for state in self.POSSIBLE_STATES:
+            issue_list_url = self.url.format(
+                state=state,
+            )
+            issues += json.loads(urllib2.urlopen(issue_list_url).read().decode('utf8'))
+        issues.sort(key=lambda x: x['number'])
+        for issue in issues:
+            yield (issue['number'], issue)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/5a82f13a/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index 798da5c..4aa73a4 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -1,8 +1,118 @@
+from datetime import datetime
+
+from allura import model as M
+from allura.lib import helpers as h
+from ming.orm import session, ThreadLocalORMSession
+from pylons import tmpl_context as c
+from pylons import app_globals as g
+
+from . import GitHubProjectExtractor
 from ..base import ToolImporter
 from forgetracker.tracker_main import ForgeTrackerApp
+from forgetracker import model as TM
+
+
 
 class GitHubTrackerImporter(ToolImporter):
     source = 'GitHub'
     target_app = ForgeTrackerApp
     controller = None
-    tool_label = 'Issues'
\ No newline at end of file
+    tool_label = 'Issues'
+    max_ticket_num = 0
+
+    def import_tool(self, project, user, project_name, mount_point=None,
+            mount_label=None, **kw):
+        app = project.install_app('tickets', mount_point, mount_label,
+                EnableVoting=True,
+                open_status_names='New Accepted',
+                closed_status_names='Done',
+            )
+        ThreadLocalORMSession.flush_all()
+        extractor = GitHubProjectExtractor(
+            project,
+            '{}/{}'.format(kw['user_name'],project_name),
+            'issues',
+        )
+        try:
+            M.session.artifact_orm_session._get().skip_mod_date = True
+            with h.push_config(c, user=M.User.anonymous(), app=app):
+                for ticket_num, issue in extractor.iter_issues():
+                    self.max_ticket_num = max(ticket_num, self.max_ticket_num)
+                    ticket = TM.Ticket(
+                        app_config_id=app.config._id,
+                        custom_fields=dict(),
+                        ticket_num=ticket_num)
+                    self.process_fields(ticket, issue)
+                    #self.process_labels(ticket, issue)
+                    #self.process_comments(ticket, issue)
+                    session(ticket).flush(ticket)
+                    session(ticket).expunge(ticket)
+                #app.globals.custom_fields = self.postprocess_custom_fields()
+                app.globals.last_ticket_num = self.max_ticket_num
+                ThreadLocalORMSession.flush_all()
+            g.post_event('project_updated')
+            app.globals.invalidate_bin_counts()
+            return app
+        finally:
+            M.session.artifact_orm_session._get().skip_mod_date = False
+
+    def process_fields(self, ticket, issue):
+        ticket.summary = issue['title']
+        ticket.status = issue['state']
+        ticket.created_date = datetime.strptime(issue['created_at'], '%Y-%m-%dT%H:%M:%SZ')
+        ticket.mod_date = datetime.strptime(issue['updated_at'], '%Y-%m-%dT%H:%M:%SZ')
+        if issue['assignee']:
+            owner_line = '*Originally owned by:* {}\n'.format(issue['assignee']['login'])
+        else:
+            owner_line = ''
+        ticket.description = (
+                u'*Originally created by:* {creator}\n'
+                u'{owner}'
+                u'\n'
+                u'{body}').format(
+                    creator=issue['user']['login'],
+                    owner=owner_line,
+                    body=issue['body'],
+                )
+
+    def process_labels(self, ticket, issue):
+        labels = set()
+        custom_fields = defaultdict(set)
+        for label in issue.get_issue_labels():
+            if u'-' in label:
+                name, value = label.split(u'-', 1)
+                cf = self.custom_field(name)
+                cf['options'].add(value)
+                custom_fields[cf['name']].add(value)
+                if cf['name'] == '_milestone' and ticket.status in c.app.globals.open_status_names:
+                    self.open_milestones.add(value)
+            else:
+                labels.add(label)
+        ticket.labels = list(labels)
+        ticket.custom_fields = {n: u', '.join(sorted(v)) for n,v in custom_fields.iteritems()}
+
+    def process_comments(self, ticket, issue):
+        for comment in issue.iter_comments():
+            p = ticket.discussion_thread.add_post(
+                    text = comment.annotated_text,
+                    ignore_security = True,
+                    timestamp = datetime.strptime(comment.created_date, '%c'),
+                )
+            p.add_multiple_attachments(comment.attachments)
+
+    def postprocess_custom_fields(self):
+        custom_fields = []
+        for name, field in self.custom_fields.iteritems():
+            if field['name'] == '_milestone':
+                field['milestones'] = [{
+                        'name': milestone,
+                        'due_date': None,
+                        'complete': milestone not in self.open_milestones,
+                    } for milestone in sorted(field['options'])]
+                field['options'] = ''
+            elif field['type'] == 'select':
+                field['options'] = ' '.join(field['options'])
+            else:
+                field['options'] = ''
+            custom_fields.append(field)
+        return custom_fields
\ No newline at end of file


[09/23] git commit: [#6535] ticket:423 Fix validator message

Posted by br...@apache.org.
[#6535] ticket:423 Fix validator message


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/aefd7c91
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/aefd7c91
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/aefd7c91

Branch: refs/heads/master
Commit: aefd7c916de9f02f16b74a6f0bfd223851b38ef0
Parents: b2c864b
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Sep 12 13:24:59 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:50 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/project.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/aefd7c91/ForgeImporters/forgeimporters/github/project.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/project.py b/ForgeImporters/forgeimporters/github/project.py
index 282fdcd..c969294 100644
--- a/ForgeImporters/forgeimporters/github/project.py
+++ b/ForgeImporters/forgeimporters/github/project.py
@@ -34,7 +34,7 @@ class GitHubProjectForm(base.ProjectImportForm):
     project_name = fev.Regex(r'^[a-z0-9][a-z0-9-_.]{,61}$',
             not_empty=True,
             messages={
-                'invalid': 'Valid symbols are: letters, numbers, dashes underscores, periods',
+                'invalid': 'Valid symbols are: letters, numbers, dashes, underscores and periods',
             })
 
 class GitHubProjectImporter(base.ProjectImporter):


[08/23] git commit: [#6535] ticket:317 github comments import

Posted by br...@apache.org.
[#6535] ticket:317 github comments import

Conflicts:
	ForgeImporters/forgeimporters/github/__init__.py


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/b766bdd0
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/b766bdd0
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/b766bdd0

Branch: refs/heads/master
Commit: b766bdd0712b5d511f8aeb90afa8cfffcd5c1a21
Parents: fd9404a
Author: Anton Kasyanov <mi...@gmail.com>
Authored: Tue Aug 27 15:41:19 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:50 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/__init__.py |  6 ++++++
 ForgeImporters/forgeimporters/github/tracker.py  | 15 +++++++++------
 2 files changed, 15 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b766bdd0/ForgeImporters/forgeimporters/github/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/__init__.py b/ForgeImporters/forgeimporters/github/__init__.py
index 13d32e4..438fc8a 100644
--- a/ForgeImporters/forgeimporters/github/__init__.py
+++ b/ForgeImporters/forgeimporters/github/__init__.py
@@ -56,3 +56,9 @@ class GitHubProjectExtractor(base.ProjectExtractor):
         issues.sort(key=lambda x: x['number'])
         for issue in issues:
             yield (issue['number'], issue)
+
+    def iter_comments(self, issue):
+        comments_url = issue['comments_url']
+        comments = self.get_page(comments_url)
+        for comment in comments:
+            yield comment

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b766bdd0/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index af5363d..3266cc8 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -47,7 +47,7 @@ class GitHubTrackerImporter(ToolImporter):
                         custom_fields=dict(),
                         ticket_num=ticket_num)
                     self.process_fields(ticket, issue)
-                    #self.process_comments(ticket, issue)
+                    self.process_comments(extractor, ticket, issue)
                     session(ticket).flush(ticket)
                     session(ticket).expunge(ticket)
                 #app.globals.custom_fields = self.get_milestones()
@@ -82,14 +82,17 @@ class GitHubTrackerImporter(ToolImporter):
                 )
         ticket.labels = [label['name'] for label in issue['labels']]
 
-    def process_comments(self, ticket, issue):
-        for comment in issue.iter_comments():
+    def process_comments(self, extractor, ticket, issue):
+        for comment in extractor.iter_comments(issue):
+            body, attachments = self._get_attachments(comment['body'])
+            if comment['user']:
+                body += u'\n*Originally posted by: {}*'.format(comment['user']['login'])
             p = ticket.discussion_thread.add_post(
-                    text = comment.annotated_text,
+                    text = body,
                     ignore_security = True,
-                    timestamp = datetime.strptime(comment.created_date, '%c'),
+                    timestamp = datetime.strptime(comment['created_at'], '%Y-%m-%dT%H:%M:%SZ'),
                 )
-            p.add_multiple_attachments(comment.attachments)
+            p.add_multiple_attachments(attachments)
 
     def get_milestones(self):
         custom_fields = []


[10/23] git commit: [#6535] ticket:423 added regexp for project name

Posted by br...@apache.org.
[#6535] ticket:423 added regexp for project name


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/30da04e3
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/30da04e3
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/30da04e3

Branch: refs/heads/master
Commit: 30da04e347f358450c3e248973d1b1ac5febfa3e
Parents: 30d4021
Author: coldmind <so...@yandex.ru>
Authored: Wed Sep 11 12:41:26 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:50 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/project.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/30da04e3/ForgeImporters/forgeimporters/github/project.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/project.py b/ForgeImporters/forgeimporters/github/project.py
index 1255933..282fdcd 100644
--- a/ForgeImporters/forgeimporters/github/project.py
+++ b/ForgeImporters/forgeimporters/github/project.py
@@ -31,10 +31,10 @@ from . import tasks
 log = logging.getLogger(__name__)
 
 class GitHubProjectForm(base.ProjectImportForm):
-    project_name = fev.Regex(r'^[a-z0-9][a-z0-9-]{,61}$',
+    project_name = fev.Regex(r'^[a-z0-9][a-z0-9-_.]{,61}$',
             not_empty=True,
             messages={
-                'invalid': 'Please use only letters, numbers, and dashes.',
+                'invalid': 'Valid symbols are: letters, numbers, dashes underscores, periods',
             })
 
 class GitHubProjectImporter(base.ProjectImporter):


[19/23] git commit: [#6535] ticket:424 Fetch issue events from github

Posted by br...@apache.org.
[#6535] ticket:424 Fetch issue events from github


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/2c831e31
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/2c831e31
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/2c831e31

Branch: refs/heads/master
Commit: 2c831e3156c91d135156c4d219952d7af4757522
Parents: 961b141
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 17 14:03:52 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:51 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/__init__.py  |  8 ++++++++
 .../forgeimporters/tests/github/test_extractor.py | 18 ++++++++++++++++++
 2 files changed, 26 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/2c831e31/ForgeImporters/forgeimporters/github/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/__init__.py b/ForgeImporters/forgeimporters/github/__init__.py
index 35bbff7..3d3caac 100644
--- a/ForgeImporters/forgeimporters/github/__init__.py
+++ b/ForgeImporters/forgeimporters/github/__init__.py
@@ -32,6 +32,7 @@ class GitHubProjectExtractor(base.ProjectExtractor):
             'issues': 'https://api.github.com/repos/{project_name}/issues',
         }
     POSSIBLE_STATES = ('opened', 'closed')
+    SUPPORTED_ISSUE_EVENTS = ('closed', 'reopened', 'assigned')
     NEXT_PAGE_URL_RE = re.compile(r'<([^>]*)>; rel="next"')
 
     def get_next_page_url(self, link):
@@ -83,3 +84,10 @@ class GitHubProjectExtractor(base.ProjectExtractor):
         comments = self.get_page(comments_url)
         for comment in comments:
             yield comment
+
+    def iter_events(self, issue):
+        events_url = issue['events_url']
+        events = self.get_page(events_url)
+        for event in events:
+            if event.get('event') in self.SUPPORTED_ISSUE_EVENTS:
+                yield event

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/2c831e31/ForgeImporters/forgeimporters/tests/github/test_extractor.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/github/test_extractor.py b/ForgeImporters/forgeimporters/tests/github/test_extractor.py
index c7734fa..e29a533 100644
--- a/ForgeImporters/forgeimporters/tests/github/test_extractor.py
+++ b/ForgeImporters/forgeimporters/tests/github/test_extractor.py
@@ -46,6 +46,14 @@ class TestGitHubProjectExtractor(TestCase):
     ]
     ISSUE_COMMENTS = [u'hello', u'mocked_comment']
     ISSUE_COMMENTS_PAGE2 = [u'hello2', u'mocked_comment2']
+    ISSUE_EVENTS = [
+        {u'event': u'closed'},
+        {u'event': u'reopened'},
+    ]
+    ISSUE_EVENTS_PAGE2 = [
+        {u'event': u'assigned'},
+        {u'event': u'not-supported-event'},
+    ]
 
     def mocked_urlopen(self, url):
         headers = {}
@@ -63,6 +71,11 @@ class TestGitHubProjectExtractor(TestCase):
             headers = {'Link': '</comments?page=2>; rel="next"'}
         elif url.endswith('/comments?page=2'):
             response = StringIO(json.dumps(self.ISSUE_COMMENTS_PAGE2))
+        elif url.endswith('/events'):
+            response = StringIO(json.dumps(self.ISSUE_EVENTS))
+            headers = {'Link': '</events?page=2>; rel="next"'}
+        elif url.endswith('/events?page=2'):
+            response = StringIO(json.dumps(self.ISSUE_EVENTS_PAGE2))
 
         response.info = lambda: headers
         return response
@@ -95,3 +108,8 @@ class TestGitHubProjectExtractor(TestCase):
         mock_issue = {'comments_url': '/issues/1/comments'}
         comments = list(self.extractor.iter_comments(mock_issue))
         self.assertEqual(comments, self.ISSUE_COMMENTS + self.ISSUE_COMMENTS_PAGE2)
+
+    def test_iter_events(self):
+        mock_issue = {'events_url': '/issues/1/events'}
+        events = list(self.extractor.iter_events(mock_issue))
+        self.assertEqual(events, self.ISSUE_EVENTS + self.ISSUE_EVENTS_PAGE2[:1])


[13/23] git commit: [#6535] ticket:424 Import events

Posted by br...@apache.org.
[#6535] ticket:424 Import events


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/ae6b9e1f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/ae6b9e1f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/ae6b9e1f

Branch: refs/heads/master
Commit: ae6b9e1f0811a0f21541cb1fd6d093c66f9b793c
Parents: 2c831e3
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 17 14:54:34 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:51 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/tracker.py | 30 ++++++++++++++
 .../forgeimporters/tests/github/test_tracker.py | 41 +++++++++++++++++++-
 2 files changed, 70 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/ae6b9e1f/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index 36014be..6c7cf73 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -105,6 +105,7 @@ class GitHubTrackerImporter(ToolImporter):
                         ticket_num=ticket_num)
                     self.process_fields(ticket, issue)
                     self.process_comments(extractor, ticket, issue)
+                    self.process_events(extractor, ticket, issue)
                     self.process_milestones(ticket, issue)
                     session(ticket).flush(ticket)
                     session(ticket).expunge(ticket)
@@ -117,6 +118,12 @@ class GitHubTrackerImporter(ToolImporter):
         finally:
             M.session.artifact_orm_session._get().skip_mod_date = False
 
+    def parse_datetime(self, datetime_string):
+        return datetime.strptime(datetime_string, '%Y-%m-%dT%H:%M:%SZ')
+
+    def get_user_link(self, user):
+        return u'[{0}](https://github.com/{0})'.format(user)
+
     def process_fields(self, ticket, issue):
         ticket.summary = issue['title']
         ticket.status = issue['state']
@@ -155,6 +162,29 @@ class GitHubTrackerImporter(ToolImporter):
                 )
             p.add_multiple_attachments(attachments)
 
+    def process_events(self, extractor, ticket, issue):
+        for event in extractor.iter_events(issue):
+            prefix = text = ''
+            if event['event'] in ('reopened', 'closed'):
+                prefix = '*Ticket changed by: {}*\n\n'.format(
+                        self.get_user_link(event['actor']['login']))
+            if event['event'] == 'reopened':
+                text = '- **status**: closed --> open'
+            elif event['event'] == 'closed':
+                text = '- **status**: open --> closed'
+            elif event['event'] == 'assigned':
+                text = '- **assigned_to**: {}'.format(
+                        self.get_user_link(event['actor']['login']))
+
+            text = prefix + text
+            if not text:
+                continue
+            ticket.discussion_thread.add_post(
+                text = text,
+                ignore_security = True,
+                timestamp = self.parse_datetime(event['created_at'])
+            )
+
     def process_milestones(self, ticket, issue):
         if issue['milestone']:
             title = issue['milestone']['title']

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/ae6b9e1f/ForgeImporters/forgeimporters/tests/github/test_tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/github/test_tracker.py b/ForgeImporters/forgeimporters/tests/github/test_tracker.py
index 9059a99..1376e55 100644
--- a/ForgeImporters/forgeimporters/tests/github/test_tracker.py
+++ b/ForgeImporters/forgeimporters/tests/github/test_tracker.py
@@ -130,4 +130,43 @@ class TestTrackerImporter(TestCase):
                 text='*Originally posted by: [me](https://github.com/me)*\nhello',
                 timestamp=datetime(2013, 8, 26, 16, 57, 53),
                 ignore_security=True,
-            ))
\ No newline at end of file
+            ))
+
+    def test_process_events(self):
+        ticket = mock.Mock()
+        extractor = mock.Mock()
+        issue = {'events_url': '/events'}
+        extractor.iter_events.return_value = [
+            {
+                'actor': {'login': 'darth'},
+                'created_at': '2013-09-12T09:58:49Z',
+                'event': 'closed',
+            },
+            {
+                'actor': {'login': 'yoda'},
+                'created_at': '2013-09-12T10:13:20Z',
+                'event': 'reopened',
+            },
+            {
+                'actor': {'login': 'luke'},
+                'created_at': '2013-09-12T10:14:00Z',
+                'event': 'assigned',
+            },
+        ]
+        importer = tracker.GitHubTrackerImporter()
+        importer.process_events(extractor, ticket, issue)
+        args = ticket.discussion_thread.add_post.call_args_list
+        self.assertEqual(args[0], mock.call(
+            text='*Ticket changed by: [darth](https://github.com/darth)*\n\n'
+                 '- **status**: open --> closed',
+            timestamp=datetime(2013, 9, 12, 9, 58, 49),
+            ignore_security=True))
+        self.assertEqual(args[1], mock.call(
+            text='*Ticket changed by: [yoda](https://github.com/yoda)*\n\n'
+                 '- **status**: closed --> open',
+            timestamp=datetime(2013, 9, 12, 10, 13, 20),
+            ignore_security=True))
+        self.assertEqual(args[2], mock.call(
+            text='- **assigned_to**: [luke](https://github.com/luke)',
+            timestamp=datetime(2013, 9, 12, 10, 14, 0),
+            ignore_security=True))


[02/23] git commit: [#6535] ticket:417 github tickets import labels and attaches

Posted by br...@apache.org.
[#6535] ticket:417 github tickets import labels and attaches

Conflicts:
	ForgeImporters/forgeimporters/github/__init__.py


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/6062bc73
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/6062bc73
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/6062bc73

Branch: refs/heads/master
Commit: 6062bc73b3502f4e7ba2dbd9ebef506ec987638f
Parents: 5a82f13
Author: Anton Kasyanov <mi...@gmail.com>
Authored: Mon Aug 26 21:24:38 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:49 2013 +0000

----------------------------------------------------------------------
 .../forgeimporters/github/__init__.py           |  3 +-
 ForgeImporters/forgeimporters/github/tracker.py | 66 +++++++++++++-------
 2 files changed, 47 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/6062bc73/ForgeImporters/forgeimporters/github/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/__init__.py b/ForgeImporters/forgeimporters/github/__init__.py
index 35c5d03..8e1a802 100644
--- a/ForgeImporters/forgeimporters/github/__init__.py
+++ b/ForgeImporters/forgeimporters/github/__init__.py
@@ -17,6 +17,8 @@
 
 import logging
 import json
+import urllib
+import urllib2
 
 from forgeimporters import base
 
@@ -39,7 +41,6 @@ class GitHubProjectExtractor(base.ProjectExtractor):
         self.url = self.PAGE_MAP[page].format(
             project=urllib.quote(gh_project_name),
         )
-        self.page = json.loads(urllib2.urlopen(self.url).read().decode('utf8'))
 
     def get_summary(self):
         return self.get_page('project_info').get('description')

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/6062bc73/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index 4aa73a4..4cd433b 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -1,5 +1,11 @@
+import re
 from datetime import datetime
 
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from StringIO import StringIO
+
 from allura import model as M
 from allura.lib import helpers as h
 from ming.orm import session, ThreadLocalORMSession
@@ -43,11 +49,10 @@ class GitHubTrackerImporter(ToolImporter):
                         custom_fields=dict(),
                         ticket_num=ticket_num)
                     self.process_fields(ticket, issue)
-                    #self.process_labels(ticket, issue)
                     #self.process_comments(ticket, issue)
                     session(ticket).flush(ticket)
                     session(ticket).expunge(ticket)
-                #app.globals.custom_fields = self.postprocess_custom_fields()
+                #app.globals.custom_fields = self.get_milestones()
                 app.globals.last_ticket_num = self.max_ticket_num
                 ThreadLocalORMSession.flush_all()
             g.post_event('project_updated')
@@ -65,6 +70,9 @@ class GitHubTrackerImporter(ToolImporter):
             owner_line = '*Originally owned by:* {}\n'.format(issue['assignee']['login'])
         else:
             owner_line = ''
+        # body processing happens here
+        body, attachments = self._get_attachments(issue['body'])
+        ticket.add_multiple_attachments(attachments)
         ticket.description = (
                 u'*Originally created by:* {creator}\n'
                 u'{owner}'
@@ -72,24 +80,9 @@ class GitHubTrackerImporter(ToolImporter):
                 u'{body}').format(
                     creator=issue['user']['login'],
                     owner=owner_line,
-                    body=issue['body'],
+                    body=body,
                 )
-
-    def process_labels(self, ticket, issue):
-        labels = set()
-        custom_fields = defaultdict(set)
-        for label in issue.get_issue_labels():
-            if u'-' in label:
-                name, value = label.split(u'-', 1)
-                cf = self.custom_field(name)
-                cf['options'].add(value)
-                custom_fields[cf['name']].add(value)
-                if cf['name'] == '_milestone' and ticket.status in c.app.globals.open_status_names:
-                    self.open_milestones.add(value)
-            else:
-                labels.add(label)
-        ticket.labels = list(labels)
-        ticket.custom_fields = {n: u', '.join(sorted(v)) for n,v in custom_fields.iteritems()}
+        ticket.labels = [label['name'] for label in issue['labels']]
 
     def process_comments(self, ticket, issue):
         for comment in issue.iter_comments():
@@ -100,8 +93,9 @@ class GitHubTrackerImporter(ToolImporter):
                 )
             p.add_multiple_attachments(comment.attachments)
 
-    def postprocess_custom_fields(self):
+    def get_milestones(self):
         custom_fields = []
+        milestones = []
         for name, field in self.custom_fields.iteritems():
             if field['name'] == '_milestone':
                 field['milestones'] = [{
@@ -115,4 +109,34 @@ class GitHubTrackerImporter(ToolImporter):
             else:
                 field['options'] = ''
             custom_fields.append(field)
-        return custom_fields
\ No newline at end of file
+        return custom_fields
+
+    def _get_attachments(self, body):
+        # at github, attachments are images only and are included into comment's body
+        # usual syntax is
+        # ![cdbpzjc5ex4](https://f.cloud.github.com/assets/979771/1027411/a393ab5e-0e70-11e3-8a38-b93a3df904cf.jpg)\r\n
+        REGEXP = r'!\[[\w0-9]+?\]\(((?:https?:\/\/)?[\da-z\.-]+\.[a-z\.]{2,6}'\
+            '(?:[\/\w\.-]+)*.(jpg|jpeg|png|gif))\)\r\n'
+        attachments = []
+        found_matches = re.finditer(REGEXP, body, re.IGNORECASE)
+        for i, match in enumerate(found_matches):
+            # removing attach text from comment
+            body = body.replace(match.group(0), '')
+            # stripping url and extension
+            attachments.append(Attachment(
+                match.group(1),  # url
+                'attach{}.{}'.format(i + 1, match.group(2)) # extension
+            ))
+        return (body, attachments)
+
+class Attachment(object):
+    def __init__(self, url, filename):
+        self.url = url
+        self.filename = filename
+        self.type = None
+
+    @property
+    def file(self):
+        fp_ish = GitHubProjectExtractor.urlopen(self.url)
+        fp = StringIO(fp_ish.read())
+        return fp


[05/23] git commit: [#6535] ticket:423 fixed test

Posted by br...@apache.org.
[#6535] ticket:423 fixed test


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/b2c864be
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/b2c864be
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/b2c864be

Branch: refs/heads/master
Commit: b2c864be4680fe92dcf2a574b97caed9f5b5cd7d
Parents: da98a80
Author: coldmind <so...@yandex.ru>
Authored: Wed Sep 11 16:44:49 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:50 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/tests/github/test_tracker.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b2c864be/ForgeImporters/forgeimporters/tests/github/test_tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/github/test_tracker.py b/ForgeImporters/forgeimporters/tests/github/test_tracker.py
index 382be2b..9059a99 100644
--- a/ForgeImporters/forgeimporters/tests/github/test_tracker.py
+++ b/ForgeImporters/forgeimporters/tests/github/test_tracker.py
@@ -45,9 +45,9 @@ class TestTrackerImporter(TestCase):
                 mount_point='mount_point', mount_label='mount_label', user_name='me')
 
         project.install_app.assert_called_once_with('tickets', 'mount_point', 'mount_label',
-                EnableVoting=True,
-                open_status_names='New Accepted Started',
-                closed_status_names='Fixed Verified Invalid Duplicate WontFix Done',
+                EnableVoting=False,
+                open_status_names='Open',
+                closed_status_names='Closed',
             )
         gpe.iter_issues.assert_called_once()
         self.assertEqual(tlos.flush_all.call_args_list, [
@@ -127,7 +127,7 @@ class TestTrackerImporter(TestCase):
         importer = tracker.GitHubTrackerImporter()
         importer.process_comments(extractor, ticket, issue)
         self.assertEqual(ticket.discussion_thread.add_post.call_args_list[0], mock.call(
-                text='hello\n*Originally posted by: me*',
+                text='*Originally posted by: [me](https://github.com/me)*\nhello',
                 timestamp=datetime(2013, 8, 26, 16, 57, 53),
                 ignore_security=True,
             ))
\ No newline at end of file


[16/23] git commit: [#6535] ticket:424 Refactoring: use self.parse_datetime everywhere

Posted by br...@apache.org.
[#6535] ticket:424 Refactoring: use self.parse_datetime everywhere


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/78616389
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/78616389
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/78616389

Branch: refs/heads/master
Commit: 78616389fdf03b6ebac1f895708d3787dbc03a39
Parents: ae6b9e1
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 17 14:59:35 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:51 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/tracker.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/78616389/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index 6c7cf73..96e8be6 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -127,8 +127,8 @@ class GitHubTrackerImporter(ToolImporter):
     def process_fields(self, ticket, issue):
         ticket.summary = issue['title']
         ticket.status = issue['state']
-        ticket.created_date = datetime.strptime(issue['created_at'], '%Y-%m-%dT%H:%M:%SZ')
-        ticket.mod_date = datetime.strptime(issue['updated_at'], '%Y-%m-%dT%H:%M:%SZ')
+        ticket.created_date = self.parse_datetime(issue['created_at'])
+        ticket.mod_date = self.parse_datetime(issue['updated_at'])
         if issue['assignee']:
             owner_line = '*Originally owned by:* {}\n'.format(issue['assignee']['login'])
         else:
@@ -158,7 +158,7 @@ class GitHubTrackerImporter(ToolImporter):
             p = ticket.discussion_thread.add_post(
                     text = body,
                     ignore_security = True,
-                    timestamp = datetime.strptime(comment['created_at'], '%Y-%m-%dT%H:%M:%SZ'),
+                    timestamp = self.parse_datetime(comment['created_at']),
                 )
             p.add_multiple_attachments(attachments)
 
@@ -190,7 +190,7 @@ class GitHubTrackerImporter(ToolImporter):
             title = issue['milestone']['title']
             due = None
             if issue['milestone']['due_on']:
-                due = datetime.strptime(issue['milestone']['due_on'], '%Y-%m-%dT%H:%M:%SZ')
+                due = self.parse_datetime(issue['milestone']['due_on'])
             ticket.custom_fields = {
                 '_milestone': title,
             }


[07/23] git commit: [#6535] ticket:423 fixed user link

Posted by br...@apache.org.
[#6535] ticket:423 fixed user link


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/51b90429
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/51b90429
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/51b90429

Branch: refs/heads/master
Commit: 51b90429bd3283b0049f9a9ad4029549a5a73ea1
Parents: 30da04e
Author: coldmind <so...@yandex.ru>
Authored: Wed Sep 11 14:07:23 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:50 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/tracker.py | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/51b90429/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index fea18ed..01a08e4 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -30,9 +30,9 @@ class GitHubTrackerImporter(ToolImporter):
     def import_tool(self, project, user, project_name, mount_point=None,
             mount_label=None, **kw):
         app = project.install_app('tickets', mount_point, mount_label,
-                EnableVoting=True,
-                open_status_names='New Accepted Started',
-                closed_status_names='Fixed Verified Invalid Duplicate WontFix Done',
+                EnableVoting=False,
+                open_status_names='Open',
+                closed_status_names='Closed',
             )
         ThreadLocalORMSession.flush_all()
         extractor = GitHubProjectExtractor(
@@ -88,7 +88,10 @@ class GitHubTrackerImporter(ToolImporter):
         for comment in extractor.iter_comments(issue):
             body, attachments = self._get_attachments(comment['body'])
             if comment['user']:
-                body += u'\n*Originally posted by: {}*'.format(comment['user']['login'])
+                posted_by = u'*Originally posted by: [{0}](https://github.com/{0})*\n'.format(
+                    comment['user']['login'])
+                posted_by += body
+                body = posted_by
             p = ticket.discussion_thread.add_post(
                     text = body,
                     ignore_security = True,


[14/23] git commit: [#6535] ticket:424 Fix exception due to ImportErrorHandler changes in 3c0a7e0f5fd179096d48239e3db8c014960a2181

Posted by br...@apache.org.
[#6535] ticket:424 Fix exception due to ImportErrorHandler changes in 3c0a7e0f5fd179096d48239e3db8c014960a2181


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/961b1415
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/961b1415
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/961b1415

Branch: refs/heads/master
Commit: 961b1415d52190929b8a397fa83db92b1d550343
Parents: 3775165
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 17 12:54:54 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:51 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/tracker.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/961b1415/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index 20ba465..36014be 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -36,7 +36,7 @@ from forgeimporters.base import ToolImportForm, ImportErrorHandler
 @task(notifications_disabled=True)
 def import_tool(**kw):
     importer = GitHubTrackerImporter()
-    with ImportErrorHandler(importer, kw.get('project_name')):
+    with ImportErrorHandler(importer, kw.get('project_name'), c.project):
         importer.import_tool(c.project, c.user, **kw)
 
 


[12/23] git commit: [#6535] ticket:417 milestone processing added

Posted by br...@apache.org.
[#6535] ticket:417 milestone processing added


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/fedf201b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/fedf201b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/fedf201b

Branch: refs/heads/master
Commit: fedf201bc4e046a54f0476f2b8f0ae5f603def14
Parents: b766bdd
Author: Anton Kasyanov <mi...@gmail.com>
Authored: Tue Aug 27 19:02:08 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:50 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/tracker.py | 46 ++++++++++++--------
 1 file changed, 28 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/fedf201b/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index 3266cc8..8c402d0 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -25,6 +25,7 @@ class GitHubTrackerImporter(ToolImporter):
     controller = None
     tool_label = 'Issues'
     max_ticket_num = 0
+    open_milestones = set()
 
     def import_tool(self, project, user, project_name, mount_point=None,
             mount_label=None, **kw):
@@ -48,9 +49,10 @@ class GitHubTrackerImporter(ToolImporter):
                         ticket_num=ticket_num)
                     self.process_fields(ticket, issue)
                     self.process_comments(extractor, ticket, issue)
+                    self.process_milestones(ticket, issue)
                     session(ticket).flush(ticket)
                     session(ticket).expunge(ticket)
-                #app.globals.custom_fields = self.get_milestones()
+                app.globals.custom_fields = self.postprocess_milestones()
                 app.globals.last_ticket_num = self.max_ticket_num
                 ThreadLocalORMSession.flush_all()
             g.post_event('project_updated')
@@ -94,23 +96,31 @@ class GitHubTrackerImporter(ToolImporter):
                 )
             p.add_multiple_attachments(attachments)
 
-    def get_milestones(self):
-        custom_fields = []
-        milestones = []
-        for name, field in self.custom_fields.iteritems():
-            if field['name'] == '_milestone':
-                field['milestones'] = [{
-                        'name': milestone,
-                        'due_date': None,
-                        'complete': milestone not in self.open_milestones,
-                    } for milestone in sorted(field['options'])]
-                field['options'] = ''
-            elif field['type'] == 'select':
-                field['options'] = ' '.join(field['options'])
-            else:
-                field['options'] = ''
-            custom_fields.append(field)
-        return custom_fields
+    def process_milestones(self, ticket, issue):
+        if issue['milestone']:
+            title = issue['milestone']['title']
+            due = None
+            if issue['milestone']['due_on']:
+                due = datetime.strptime(issue['milestone']['due_on'], '%Y-%m-%dT%H:%M:%SZ')
+            ticket.custom_fields = {
+                '_milestone': title,
+            }
+            self.open_milestones.add((title, due,))
+
+    def postprocess_milestones(self):
+        global_milestones = {
+            'milestones': [],
+            'type': 'milestone',
+            'name': '_milestone',
+            'label': 'Milestone'
+        }
+        for milestone in self.open_milestones:
+            global_milestones['milestones'].append({
+                'name': milestone[0],
+                'due_date': unicode(milestone[1].date()),
+                'complete': False,
+            })
+        return [global_milestones]
 
     def _get_attachments(self, body):
         # at github, attachments are images only and are included into comment's body


[18/23] git commit: [#6535] ticket:424 Handle pagination

Posted by br...@apache.org.
[#6535] ticket:424 Handle pagination


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/37751659
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/37751659
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/37751659

Branch: refs/heads/master
Commit: 37751659315b4300b27bbd2be0e544991ab42954
Parents: a2833b3
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 17 12:35:11 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:51 2013 +0000

----------------------------------------------------------------------
 .../forgeimporters/github/__init__.py           | 25 ++++++++++-
 .../tests/github/test_extractor.py              | 47 +++++++++++++++-----
 2 files changed, 58 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/37751659/ForgeImporters/forgeimporters/github/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/__init__.py b/ForgeImporters/forgeimporters/github/__init__.py
index 438fc8a..35bbff7 100644
--- a/ForgeImporters/forgeimporters/github/__init__.py
+++ b/ForgeImporters/forgeimporters/github/__init__.py
@@ -15,6 +15,7 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+import re
 import logging
 import json
 import urllib
@@ -31,9 +32,29 @@ class GitHubProjectExtractor(base.ProjectExtractor):
             'issues': 'https://api.github.com/repos/{project_name}/issues',
         }
     POSSIBLE_STATES = ('opened', 'closed')
+    NEXT_PAGE_URL_RE = re.compile(r'<([^>]*)>; rel="next"')
+
+    def get_next_page_url(self, link):
+        if not link:
+            return
+        m = self.NEXT_PAGE_URL_RE.match(link)
+        return m.group(1) if m else None
 
     def parse_page(self, page):
-        return json.loads(page.read().decode('utf8'))
+        # Look at link header to handle pagination
+        link = page.info().get('Link')
+        next_page_url = self.get_next_page_url(link)
+        return json.loads(page.read().decode('utf8')), next_page_url
+
+    def get_page(self, page_name_or_url, **kw):
+        page = super(GitHubProjectExtractor, self).get_page(page_name_or_url, **kw)
+        page, next_page_url = page
+        while next_page_url:
+            p = super(GitHubProjectExtractor, self).get_page(next_page_url, **kw)
+            p, next_page_url = p
+            page += p
+        self.page = page
+        return self.page
 
     def get_summary(self):
         return self.get_page('project_info').get('description')
@@ -52,7 +73,7 @@ class GitHubProjectExtractor(base.ProjectExtractor):
             issue_list_url = url.format(
                 state=state,
             )
-            issues += json.loads(self.urlopen(issue_list_url).read().decode('utf8'))
+            issues += self.get_page(issue_list_url)
         issues.sort(key=lambda x: x['number'])
         for issue in issues:
             yield (issue['number'], issue)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/37751659/ForgeImporters/forgeimporters/tests/github/test_extractor.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/github/test_extractor.py b/ForgeImporters/forgeimporters/tests/github/test_extractor.py
index 8eb5811..c7734fa 100644
--- a/ForgeImporters/forgeimporters/tests/github/test_extractor.py
+++ b/ForgeImporters/forgeimporters/tests/github/test_extractor.py
@@ -20,12 +20,9 @@ from unittest import TestCase
 
 from ... import github
 
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-
-
+# Can't use cStringIO here, because we cannot set attributes or subclass it,
+# and this is needed in mocked_urlopen below
+from StringIO import StringIO
 
 
 class TestGitHubProjectExtractor(TestCase):
@@ -39,23 +36,48 @@ class TestGitHubProjectExtractor(TestCase):
     ]
     OPENED_ISSUES_LIST = [
         {u'number': 3},
+        {u'number': 4},
+        {u'number': 5},
+    ]
+    OPENED_ISSUES_LIST_PAGE2 = [
+        {u'number': 6},
+        {u'number': 7},
+        {u'number': 8},
     ]
     ISSUE_COMMENTS = [u'hello', u'mocked_comment']
+    ISSUE_COMMENTS_PAGE2 = [u'hello2', u'mocked_comment2']
 
     def mocked_urlopen(self, url):
+        headers = {}
         if url.endswith('/test_project'):
-            return StringIO(json.dumps(self.PROJECT_INFO))
+            response = StringIO(json.dumps(self.PROJECT_INFO))
         elif url.endswith('/issues?state=closed'):
-            return StringIO(json.dumps(self.CLOSED_ISSUES_LIST))
+            response = StringIO(json.dumps(self.CLOSED_ISSUES_LIST))
         elif url.endswith('/issues?state=opened'):
-            return StringIO(json.dumps(self.OPENED_ISSUES_LIST))
+            response = StringIO(json.dumps(self.OPENED_ISSUES_LIST))
+            headers = {'Link': '</issues?state=opened&page=2>; rel="next"'}
+        elif url.endswith('/issues?state=opened&page=2'):
+            response = StringIO(json.dumps(self.OPENED_ISSUES_LIST_PAGE2))
         elif url.endswith('/comments'):
-            return StringIO(json.dumps(self.ISSUE_COMMENTS))
+            response = StringIO(json.dumps(self.ISSUE_COMMENTS))
+            headers = {'Link': '</comments?page=2>; rel="next"'}
+        elif url.endswith('/comments?page=2'):
+            response = StringIO(json.dumps(self.ISSUE_COMMENTS_PAGE2))
+
+        response.info = lambda: headers
+        return response
 
     def setUp(self):
         self.extractor = github.GitHubProjectExtractor('test_project')
         self.extractor.urlopen = self.mocked_urlopen
 
+    def test_get_next_page_url(self):
+        self.assertIsNone(self.extractor.get_next_page_url(None))
+        self.assertIsNone(self.extractor.get_next_page_url(''))
+        link = '<https://api.github.com/repositories/8560576/issues?state=open&page=2>; rel="next", <https://api.github.com/repositories/8560576/issues?state=open&page=2>; rel="last"'
+        self.assertEqual(self.extractor.get_next_page_url(link),
+                'https://api.github.com/repositories/8560576/issues?state=open&page=2')
+
     def test_get_summary(self):
         self.assertEqual(self.extractor.get_summary(), 'project description')
 
@@ -65,10 +87,11 @@ class TestGitHubProjectExtractor(TestCase):
     def test_iter_issues(self):
         issues = list(self.extractor.iter_issues())
         all_issues = zip((1,2), self.CLOSED_ISSUES_LIST)
-        all_issues.append((3, self.OPENED_ISSUES_LIST[0]))
+        all_issues += zip((3, 4, 5), self.OPENED_ISSUES_LIST)
+        all_issues += zip((6, 7, 8), self.OPENED_ISSUES_LIST_PAGE2)
         self.assertEqual(issues, all_issues)
 
     def test_iter_comments(self):
         mock_issue = {'comments_url': '/issues/1/comments'}
         comments = list(self.extractor.iter_comments(mock_issue))
-        self.assertEqual(comments, self.ISSUE_COMMENTS)
+        self.assertEqual(comments, self.ISSUE_COMMENTS + self.ISSUE_COMMENTS_PAGE2)


[11/23] git commit: [#6535] ticket:417 tests for github tracker import

Posted by br...@apache.org.
[#6535] ticket:417 tests for github tracker import


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/30d40210
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/30d40210
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/30d40210

Branch: refs/heads/master
Commit: 30d40210d89a8434cf628b89c7edd8d6e925c193
Parents: fedf201
Author: Anton Kasyanov <mi...@gmail.com>
Authored: Wed Aug 28 15:41:33 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:50 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/tracker.py |   4 +-
 .../tests/github/test_extractor.py              |  50 ++++++-
 .../forgeimporters/tests/github/test_tracker.py | 133 +++++++++++++++++++
 3 files changed, 179 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/30d40210/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index 8c402d0..fea18ed 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -31,8 +31,8 @@ class GitHubTrackerImporter(ToolImporter):
             mount_label=None, **kw):
         app = project.install_app('tickets', mount_point, mount_label,
                 EnableVoting=True,
-                open_status_names='New Accepted',
-                closed_status_names='Done',
+                open_status_names='New Accepted Started',
+                closed_status_names='Fixed Verified Invalid Duplicate WontFix Done',
             )
         ThreadLocalORMSession.flush_all()
         extractor = GitHubProjectExtractor(

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/30d40210/ForgeImporters/forgeimporters/tests/github/test_extractor.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/github/test_extractor.py b/ForgeImporters/forgeimporters/tests/github/test_extractor.py
index cffbf81..8eb5811 100644
--- a/ForgeImporters/forgeimporters/tests/github/test_extractor.py
+++ b/ForgeImporters/forgeimporters/tests/github/test_extractor.py
@@ -15,22 +15,60 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+import json
 from unittest import TestCase
 
 from ... import github
 
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from StringIO import StringIO
+
+
+
 
 class TestGitHubProjectExtractor(TestCase):
+    PROJECT_INFO = {
+        'description': 'project description',
+        'homepage': 'http://example.com',
+    }
+    CLOSED_ISSUES_LIST = [
+        {u'number': 1},
+        {u'number': 2},
+    ]
+    OPENED_ISSUES_LIST = [
+        {u'number': 3},
+    ]
+    ISSUE_COMMENTS = [u'hello', u'mocked_comment']
+
+    def mocked_urlopen(self, url):
+        if url.endswith('/test_project'):
+            return StringIO(json.dumps(self.PROJECT_INFO))
+        elif url.endswith('/issues?state=closed'):
+            return StringIO(json.dumps(self.CLOSED_ISSUES_LIST))
+        elif url.endswith('/issues?state=opened'):
+            return StringIO(json.dumps(self.OPENED_ISSUES_LIST))
+        elif url.endswith('/comments'):
+            return StringIO(json.dumps(self.ISSUE_COMMENTS))
+
     def setUp(self):
-        import json
-        from StringIO import StringIO
-        self.extractor = github.GitHubProjectExtractor('testproject')
-        d = dict(description='project description',
-                homepage='http://example.com')
-        self.extractor.urlopen = lambda url: StringIO(json.dumps(d))
+        self.extractor = github.GitHubProjectExtractor('test_project')
+        self.extractor.urlopen = self.mocked_urlopen
 
     def test_get_summary(self):
         self.assertEqual(self.extractor.get_summary(), 'project description')
 
     def test_get_homepage(self):
         self.assertEqual(self.extractor.get_homepage(), 'http://example.com')
+
+    def test_iter_issues(self):
+        issues = list(self.extractor.iter_issues())
+        all_issues = zip((1,2), self.CLOSED_ISSUES_LIST)
+        all_issues.append((3, self.OPENED_ISSUES_LIST[0]))
+        self.assertEqual(issues, all_issues)
+
+    def test_iter_comments(self):
+        mock_issue = {'comments_url': '/issues/1/comments'}
+        comments = list(self.extractor.iter_comments(mock_issue))
+        self.assertEqual(comments, self.ISSUE_COMMENTS)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/30d40210/ForgeImporters/forgeimporters/tests/github/test_tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/github/test_tracker.py b/ForgeImporters/forgeimporters/tests/github/test_tracker.py
new file mode 100644
index 0000000..382be2b
--- /dev/null
+++ b/ForgeImporters/forgeimporters/tests/github/test_tracker.py
@@ -0,0 +1,133 @@
+#       Licensed to the Apache Software Foundation (ASF) under one
+#       or more contributor license agreements.  See the NOTICE file
+#       distributed with this work for additional information
+#       regarding copyright ownership.  The ASF licenses this file
+#       to you under the Apache License, Version 2.0 (the
+#       "License"); you may not use this file except in compliance
+#       with the License.  You may obtain a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#       Unless required by applicable law or agreed to in writing,
+#       software distributed under the License is distributed on an
+#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#       KIND, either express or implied.  See the License for the
+#       specific language governing permissions and limitations
+#       under the License.
+
+from datetime import datetime
+from operator import itemgetter
+from unittest import TestCase
+import mock
+
+from ...github import tracker
+
+
+class TestTrackerImporter(TestCase):
+    @mock.patch.object(tracker, 'g')
+    @mock.patch.object(tracker, 'c')
+    @mock.patch.object(tracker, 'ThreadLocalORMSession')
+    @mock.patch.object(tracker, 'session')
+    @mock.patch.object(tracker, 'M')
+    @mock.patch.object(tracker, 'TM')
+    @mock.patch.object(tracker, 'GitHubProjectExtractor')
+    def test_import_tool(self, gpe, TM, M, session, tlos, c, g):
+        importer = tracker.GitHubTrackerImporter()
+        importer.process_fields = mock.Mock()
+        importer.process_milestones = mock.Mock()
+        importer.process_comments = mock.Mock()
+        importer.postprocess_milestones= mock.Mock()
+        project, user = mock.Mock(), mock.Mock()
+        app = project.install_app.return_value
+        gpe.iter_issues.return_value = [(50, mock.Mock()), (100, mock.Mock())]
+
+        importer.import_tool(project, user, project_name='project_name',
+                mount_point='mount_point', mount_label='mount_label', user_name='me')
+
+        project.install_app.assert_called_once_with('tickets', 'mount_point', 'mount_label',
+                EnableVoting=True,
+                open_status_names='New Accepted Started',
+                closed_status_names='Fixed Verified Invalid Duplicate WontFix Done',
+            )
+        gpe.iter_issues.assert_called_once()
+        self.assertEqual(tlos.flush_all.call_args_list, [
+                mock.call(),
+                mock.call(),
+            ])
+        g.post_event.assert_called_once_with('project_updated')
+        app.globals.invalidate_bin_counts.assert_called_once_with()
+
+    def test_process_fields(self):
+        ticket = mock.Mock()
+        issue = {
+            'title': 'title',
+            'state': 'New',
+            'created_at': 'created_at',
+            'updated_at': 'updated_at',
+            'assignee': {'login': 'owner'},
+            'user': {'login': 'creator'},
+            'body': 'hello',
+            'labels': [{'name': 'first'}, {'name': 'second'}],
+        }
+        importer = tracker.GitHubTrackerImporter()
+        with mock.patch.object(tracker, 'datetime') as dt:
+            dt.strptime.side_effect = lambda s,f: s
+            importer.process_fields(ticket, issue)
+            self.assertEqual(ticket.summary, 'title')
+            self.assertEqual(ticket.description, '*Originally created by:* creator\n*Originally owned by:* owner\n\nhello')
+            self.assertEqual(ticket.status, 'New')
+            self.assertEqual(ticket.created_date, 'created_at')
+            self.assertEqual(ticket.mod_date, 'updated_at')
+            self.assertEqual(dt.strptime.call_args_list, [
+                    mock.call('created_at', '%Y-%m-%dT%H:%M:%SZ'),
+                    mock.call('updated_at', '%Y-%m-%dT%H:%M:%SZ'),
+                ])
+            self.assertEqual(ticket.labels, ['first', 'second'])
+
+    @mock.patch.object(tracker, 'c')
+    def test_postprocess_milestones(self, c):
+        importer = tracker.GitHubTrackerImporter()
+        importer.open_milestones = set([
+            ('first', datetime(day=23, month=4, year=2015)),
+            ('second',datetime(day=25, month=4, year=2015))
+        ])
+        milestones = importer.postprocess_milestones()
+        self.assertItemsEqual(milestones, [
+                {
+                    'name': '_milestone',
+                    'type': 'milestone',
+                    'label': 'Milestone',
+                    'milestones': [
+                        {'name': 'first', 'due_date': u'2015-04-23', 'complete': False},
+                        {'name': 'second', 'due_date': u'2015-04-25', 'complete': False},
+                    ],
+                },
+            ])
+
+    def test_get_attachments(self):
+        importer = tracker.GitHubTrackerImporter()
+        body = 'hello\n' \
+        '![cdbpzjc5ex4](https://f.cloud.github.com/assets/979771/1027411/a393ab5e-0e70-11e3-8a38-b93a3df904cf.jpg)\r\n'
+        new_body, attachments = importer._get_attachments(body)
+        self.assertEqual(new_body, 'hello\n')
+        self.assertEqual(len(attachments), 1)
+        self.assertEqual(attachments[0].url, 'https://f.cloud.github.com/assets/979771/1027411/a393ab5e-0e70-11e3-8a38-b93a3df904cf.jpg')
+
+    def test_process_comments(self):
+        ticket = mock.Mock()
+        extractor = mock.Mock()
+        issue = {'comments_url': '/comments'}
+        extractor.iter_comments.return_value = [
+                {
+                    'body': 'hello',
+                    'created_at': '2013-08-26T16:57:53Z',
+                    'user': {'login': 'me'},
+                }
+            ]
+        importer = tracker.GitHubTrackerImporter()
+        importer.process_comments(extractor, ticket, issue)
+        self.assertEqual(ticket.discussion_thread.add_post.call_args_list[0], mock.call(
+                text='hello\n*Originally posted by: me*',
+                timestamp=datetime(2013, 8, 26, 16, 57, 53),
+                ignore_security=True,
+            ))
\ No newline at end of file


[22/23] git commit: [#6535] ticket:424 Improve test for get_next_page_url

Posted by br...@apache.org.
[#6535] ticket:424 Improve test for get_next_page_url


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/503c4f79
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/503c4f79
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/503c4f79

Branch: refs/heads/master
Commit: 503c4f795de506f4bcade371ac3f8c6a220ea96a
Parents: 42b6445
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 17 15:29:04 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:52 2013 +0000

----------------------------------------------------------------------
 .../forgeimporters/tests/github/test_extractor.py           | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/503c4f79/ForgeImporters/forgeimporters/tests/github/test_extractor.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/github/test_extractor.py b/ForgeImporters/forgeimporters/tests/github/test_extractor.py
index e29a533..fbbdc3d 100644
--- a/ForgeImporters/forgeimporters/tests/github/test_extractor.py
+++ b/ForgeImporters/forgeimporters/tests/github/test_extractor.py
@@ -87,10 +87,17 @@ class TestGitHubProjectExtractor(TestCase):
     def test_get_next_page_url(self):
         self.assertIsNone(self.extractor.get_next_page_url(None))
         self.assertIsNone(self.extractor.get_next_page_url(''))
-        link = '<https://api.github.com/repositories/8560576/issues?state=open&page=2>; rel="next", <https://api.github.com/repositories/8560576/issues?state=open&page=2>; rel="last"'
+        link = '<https://api.github.com/repositories/8560576/issues?state=open&page=2>; rel="next", <https://api.github.com/repositories/8560576/issues?state=open&page=10>; rel="last"'
         self.assertEqual(self.extractor.get_next_page_url(link),
                 'https://api.github.com/repositories/8560576/issues?state=open&page=2')
 
+        link = '<https://api.github.com/repositories/8560576/issues?state=open&page=2>; rel="next"'
+        self.assertEqual(self.extractor.get_next_page_url(link),
+                'https://api.github.com/repositories/8560576/issues?state=open&page=2')
+
+        link = '<https://api.github.com/repositories/8560576/issues?state=open&page=1>; rel="prev"'
+        self.assertIsNone(self.extractor.get_next_page_url(link))
+
     def test_get_summary(self):
         self.assertEqual(self.extractor.get_summary(), 'project description')
 


[15/23] git commit: [#6535] ticket:424 Fix status names for tracker

Posted by br...@apache.org.
[#6535] ticket:424 Fix status names for tracker

Fixes default saved searches for closed and open ticket.


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/be9d1197
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/be9d1197
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/be9d1197

Branch: refs/heads/master
Commit: be9d119704afb513ec764125ef8095e872b22f1f
Parents: 2ca21ae
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 17 15:06:57 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:51 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/tracker.py            | 4 ++--
 ForgeImporters/forgeimporters/tests/github/test_tracker.py | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/be9d1197/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index 1b37e5b..d068e47 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -87,8 +87,8 @@ class GitHubTrackerImporter(ToolImporter):
             mount_label=None, **kw):
         app = project.install_app('tickets', mount_point, mount_label,
                 EnableVoting=False,
-                open_status_names='Open',
-                closed_status_names='Closed',
+                open_status_names='open',
+                closed_status_names='closed',
             )
         ThreadLocalORMSession.flush_all()
         extractor = GitHubProjectExtractor(

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/be9d1197/ForgeImporters/forgeimporters/tests/github/test_tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/github/test_tracker.py b/ForgeImporters/forgeimporters/tests/github/test_tracker.py
index 1376e55..7ff8c9b 100644
--- a/ForgeImporters/forgeimporters/tests/github/test_tracker.py
+++ b/ForgeImporters/forgeimporters/tests/github/test_tracker.py
@@ -46,8 +46,8 @@ class TestTrackerImporter(TestCase):
 
         project.install_app.assert_called_once_with('tickets', 'mount_point', 'mount_label',
                 EnableVoting=False,
-                open_status_names='Open',
-                closed_status_names='Closed',
+                open_status_names='open',
+                closed_status_names='closed',
             )
         gpe.iter_issues.assert_called_once()
         self.assertEqual(tlos.flush_all.call_args_list, [


[21/23] git commit: [#6535] ticket:424 Fix user link formatting

Posted by br...@apache.org.
[#6535] ticket:424 Fix user link formatting


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/42b6445e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/42b6445e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/42b6445e

Branch: refs/heads/master
Commit: 42b6445e9e0893da5b44ff04ea3911dd4f7d205c
Parents: be9d119
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Sep 17 15:18:04 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:52 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/github/tracker.py            | 9 +++++----
 ForgeImporters/forgeimporters/tests/github/test_tracker.py | 8 ++++----
 2 files changed, 9 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/42b6445e/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index d068e47..655980a 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -130,7 +130,8 @@ class GitHubTrackerImporter(ToolImporter):
         ticket.created_date = self.parse_datetime(issue['created_at'])
         ticket.mod_date = self.parse_datetime(issue['updated_at'])
         if issue['assignee']:
-            owner_line = '*Originally owned by:* {}\n'.format(issue['assignee']['login'])
+            owner_line = '*Originally owned by:* {}\n'.format(
+                    self.get_user_link(issue['assignee']['login']))
         else:
             owner_line = ''
         # body processing happens here
@@ -141,7 +142,7 @@ class GitHubTrackerImporter(ToolImporter):
                 u'{owner}'
                 u'\n'
                 u'{body}').format(
-                    creator=issue['user']['login'],
+                    creator=self.get_user_link(issue['user']['login']),
                     owner=owner_line,
                     body=body,
                 )
@@ -151,7 +152,7 @@ class GitHubTrackerImporter(ToolImporter):
         for comment in extractor.iter_comments(issue):
             body, attachments = self._get_attachments(comment['body'])
             if comment['user']:
-                posted_by = u'*Originally posted by: {}*\n'.format(
+                posted_by = u'*Originally posted by:* {}\n'.format(
                     self.get_user_link(comment['user']['login']))
                 posted_by += body
                 body = posted_by
@@ -166,7 +167,7 @@ class GitHubTrackerImporter(ToolImporter):
         for event in extractor.iter_events(issue):
             prefix = text = ''
             if event['event'] in ('reopened', 'closed'):
-                prefix = '*Ticket changed by: {}*\n\n'.format(
+                prefix = '*Ticket changed by:* {}\n\n'.format(
                         self.get_user_link(event['actor']['login']))
             if event['event'] == 'reopened':
                 text = '- **status**: closed --> open'

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/42b6445e/ForgeImporters/forgeimporters/tests/github/test_tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/github/test_tracker.py b/ForgeImporters/forgeimporters/tests/github/test_tracker.py
index 7ff8c9b..876c71e 100644
--- a/ForgeImporters/forgeimporters/tests/github/test_tracker.py
+++ b/ForgeImporters/forgeimporters/tests/github/test_tracker.py
@@ -74,7 +74,7 @@ class TestTrackerImporter(TestCase):
             dt.strptime.side_effect = lambda s,f: s
             importer.process_fields(ticket, issue)
             self.assertEqual(ticket.summary, 'title')
-            self.assertEqual(ticket.description, '*Originally created by:* creator\n*Originally owned by:* owner\n\nhello')
+            self.assertEqual(ticket.description, '*Originally created by:* [creator](https://github.com/creator)\n*Originally owned by:* [owner](https://github.com/owner)\n\nhello')
             self.assertEqual(ticket.status, 'New')
             self.assertEqual(ticket.created_date, 'created_at')
             self.assertEqual(ticket.mod_date, 'updated_at')
@@ -127,7 +127,7 @@ class TestTrackerImporter(TestCase):
         importer = tracker.GitHubTrackerImporter()
         importer.process_comments(extractor, ticket, issue)
         self.assertEqual(ticket.discussion_thread.add_post.call_args_list[0], mock.call(
-                text='*Originally posted by: [me](https://github.com/me)*\nhello',
+                text='*Originally posted by:* [me](https://github.com/me)\nhello',
                 timestamp=datetime(2013, 8, 26, 16, 57, 53),
                 ignore_security=True,
             ))
@@ -157,12 +157,12 @@ class TestTrackerImporter(TestCase):
         importer.process_events(extractor, ticket, issue)
         args = ticket.discussion_thread.add_post.call_args_list
         self.assertEqual(args[0], mock.call(
-            text='*Ticket changed by: [darth](https://github.com/darth)*\n\n'
+            text='*Ticket changed by:* [darth](https://github.com/darth)\n\n'
                  '- **status**: open --> closed',
             timestamp=datetime(2013, 9, 12, 9, 58, 49),
             ignore_security=True))
         self.assertEqual(args[1], mock.call(
-            text='*Ticket changed by: [yoda](https://github.com/yoda)*\n\n'
+            text='*Ticket changed by:* [yoda](https://github.com/yoda)\n\n'
                  '- **status**: closed --> open',
             timestamp=datetime(2013, 9, 12, 10, 13, 20),
             ignore_security=True))


[17/23] git commit: [#6535] ticket:425 Import tracker into existing project

Posted by br...@apache.org.
[#6535] ticket:425 Import tracker into existing project


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/a2833b3b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/a2833b3b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/a2833b3b

Branch: refs/heads/master
Commit: a2833b3ba8a4eb27cd13b29ac7672cbb1822d855
Parents: aefd7c9
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Sep 16 15:37:50 2013 +0300
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 19 14:46:51 2013 +0000

----------------------------------------------------------------------
 .../github/templates/tracker/index.html         | 36 ++++++++++++
 .../forgeimporters/github/tests/test_tracker.py | 54 ++++++++++++++++++
 ForgeImporters/forgeimporters/github/tracker.py | 58 +++++++++++++++++++-
 3 files changed, 147 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/a2833b3b/ForgeImporters/forgeimporters/github/templates/tracker/index.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/templates/tracker/index.html b/ForgeImporters/forgeimporters/github/templates/tracker/index.html
new file mode 100644
index 0000000..6536628
--- /dev/null
+++ b/ForgeImporters/forgeimporters/github/templates/tracker/index.html
@@ -0,0 +1,36 @@
+{#-
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+-#}
+{% extends 'forgeimporters:templates/importer_base.html' %}
+
+{% block title %}
+{{c.project.name}} / Import your tickets from GitHub
+{% endblock %}
+
+{% block importer_fields %}
+<div>
+  <label for="gh_user_name">GitHub User Name</label>
+  <input name="gh_user_name" value="{{ c.form_values['gh_user_name'] }}" />
+  {{ error('gh_user_name') }}
+</div>
+<div>
+  <label for="gh_project_name">GitHub Project Name</label>
+  <input name="gh_project_name" value="{{ c.form_values['gh_project_name'] }}" />
+  {{ error('gh_project_name') }}
+</div>
+{% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/a2833b3b/ForgeImporters/forgeimporters/github/tests/test_tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tests/test_tracker.py b/ForgeImporters/forgeimporters/github/tests/test_tracker.py
new file mode 100644
index 0000000..ed20581
--- /dev/null
+++ b/ForgeImporters/forgeimporters/github/tests/test_tracker.py
@@ -0,0 +1,54 @@
+#       Licensed to the Apache Software Foundation (ASF) under one
+#       or more contributor license agreements.  See the NOTICE file
+#       distributed with this work for additional information
+#       regarding copyright ownership.  The ASF licenses this file
+#       to you under the Apache License, Version 2.0 (the
+#       "License"); you may not use this file except in compliance
+#       with the License.  You may obtain a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#       Unless required by applicable law or agreed to in writing,
+#       software distributed under the License is distributed on an
+#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#       KIND, either express or implied.  See the License for the
+#       specific language governing permissions and limitations
+#       under the License.
+
+from unittest import TestCase
+from mock import patch
+
+from allura.tests import TestController
+from allura.tests.decorators import with_tool
+
+# important to be distinct from 'test' which ForgeTracker uses, so that the tests can run in parallel and not clobber each other
+test_project_with_tracker = 'test2'
+with_tracker = with_tool(test_project_with_tracker, 'tickets', 'spooky-issues', 'tickets')
+
+
+class TestGitHubTrackerImportController(TestController, TestCase):
+
+    url = '/p/%s/admin/ext/import/github-tracker/' % test_project_with_tracker
+
+    @with_tracker
+    def test_index(self):
+        r = self.app.get(self.url)
+        self.assertIsNotNone(r.html.find(attrs=dict(name='gh_user_name')))
+        self.assertIsNotNone(r.html.find(attrs=dict(name='gh_project_name')))
+        self.assertIsNotNone(r.html.find(attrs=dict(name='mount_label')))
+        self.assertIsNotNone(r.html.find(attrs=dict(name='mount_point')))
+
+    @with_tracker
+    @patch('forgeimporters.github.tracker.import_tool')
+    def test_create(self, import_tool):
+        params = dict(
+            gh_user_name='spooky',
+            gh_project_name='mulder',
+            mount_point='issues',
+            mount_label='Issues')
+        r = self.app.post(self.url + 'create', params, status=302)
+        self.assertEqual(r.location, 'http://localhost/p/%s/admin/' % test_project_with_tracker)
+        self.assertEqual(u'Issues', import_tool.post.call_args[1]['mount_label'])
+        self.assertEqual(u'issues', import_tool.post.call_args[1]['mount_point'])
+        self.assertEqual(u'mulder', import_tool.post.call_args[1]['project_name'])
+        self.assertEqual(u'spooky', import_tool.post.call_args[1]['user_name'])

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/a2833b3b/ForgeImporters/forgeimporters/github/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tracker.py b/ForgeImporters/forgeimporters/github/tracker.py
index ec9729f..20ba465 100644
--- a/ForgeImporters/forgeimporters/github/tracker.py
+++ b/ForgeImporters/forgeimporters/github/tracker.py
@@ -6,8 +6,22 @@ try:
 except ImportError:
     from StringIO import StringIO
 
+from formencode import validators as fev
+from tg import (
+        expose,
+        validate,
+        flash,
+        redirect
+        )
+from tg.decorators import (
+        with_trailing_slash,
+        without_trailing_slash
+        )
+
 from allura import model as M
+from allura.controllers import BaseController
 from allura.lib import helpers as h
+from allura.lib.decorators import require_post, task
 from ming.orm import session, ThreadLocalORMSession
 from pylons import tmpl_context as c
 from pylons import app_globals as g
@@ -16,13 +30,55 @@ from . import GitHubProjectExtractor
 from ..base import ToolImporter
 from forgetracker.tracker_main import ForgeTrackerApp
 from forgetracker import model as TM
+from forgeimporters.base import ToolImportForm, ImportErrorHandler
+
+
+@task(notifications_disabled=True)
+def import_tool(**kw):
+    importer = GitHubTrackerImporter()
+    with ImportErrorHandler(importer, kw.get('project_name')):
+        importer.import_tool(c.project, c.user, **kw)
+
 
+class GitHubTrackerImportForm(ToolImportForm):
+    gh_project_name = fev.UnicodeString(not_empty=True)
+    gh_user_name = fev.UnicodeString(not_empty=True)
+
+
+class GitHubTrackerImportController(BaseController):
+
+    def __init__(self):
+        self.importer = GitHubTrackerImporter()
+
+    @property
+    def target_app(self):
+        return self.importer.target_app
+
+    @with_trailing_slash
+    @expose('jinja:forgeimporters.github:templates/tracker/index.html')
+    def index(self, **kw):
+        return dict(importer=self.importer,
+                    target_app=self.target_app)
+
+    @without_trailing_slash
+    @expose()
+    @require_post()
+    @validate(GitHubTrackerImportForm(ForgeTrackerApp), error_handler=index)
+    def create(self, gh_project_name, gh_user_name, mount_point, mount_label, **kw):
+        import_tool.post(
+                project_name=gh_project_name,
+                user_name=gh_user_name,
+                mount_point=mount_point,
+                mount_label=mount_label)
+        flash('Ticket import has begun. Your new tracker will be available '
+                'when the import is complete.')
+        redirect(c.project.url() + 'admin/')
 
 
 class GitHubTrackerImporter(ToolImporter):
     source = 'GitHub'
     target_app = ForgeTrackerApp
-    controller = None
+    controller = GitHubTrackerImportController
     tool_label = 'Issues'
     max_ticket_num = 0
     open_milestones = set()