You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by he...@apache.org on 2016/01/27 22:39:26 UTC
[24/29] allura git commit: [#8054] delete Google Code importers and
tests
[#8054] delete Google Code importers and tests
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/b956fe30
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/b956fe30
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/b956fe30
Branch: refs/heads/hs/8046
Commit: b956fe30b11639df2cb68374eae5b221fa72e88c
Parents: c5b6ce5
Author: Dave Brondsema <da...@brondsema.net>
Authored: Tue Jan 26 13:58:43 2016 -0500
Committer: Heith Seewald <he...@gmail.com>
Committed: Wed Jan 27 13:42:50 2016 -0500
----------------------------------------------------------------------
ForgeImporters/docs/importers/google.rst | 30 -
.../forgeimporters/google/__init__.py | 449 --------------
ForgeImporters/forgeimporters/google/code.py | 154 -----
ForgeImporters/forgeimporters/google/project.py | 68 ---
ForgeImporters/forgeimporters/google/tasks.py | 39 --
.../google/templates/code/index.html | 31 -
.../google/templates/project.html | 68 ---
.../google/templates/tracker/index.html | 27 -
.../forgeimporters/google/tests/__init__.py | 16 -
.../forgeimporters/google/tests/test_code.py | 146 -----
.../forgeimporters/google/tests/test_init.py | 130 ----
ForgeImporters/forgeimporters/google/tracker.py | 224 -------
.../tests/data/google/empty-issue.html | 306 ----------
.../data/google/test-issue-first-page.html | 548 -----------------
.../tests/data/google/test-issue-prev-page.html | 431 -------------
.../tests/data/google/test-issue.html | 535 ----------------
.../forgeimporters/tests/google/__init__.py | 16 -
.../tests/google/functional/__init__.py | 16 -
.../tests/google/functional/test_tracker.py | 316 ----------
.../tests/google/test_extractor.py | 607 -------------------
.../forgeimporters/tests/google/test_tasks.py | 33 -
.../forgeimporters/tests/google/test_tracker.py | 336 ----------
22 files changed, 4526 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/docs/importers/google.rst
----------------------------------------------------------------------
diff --git a/ForgeImporters/docs/importers/google.rst b/ForgeImporters/docs/importers/google.rst
deleted file mode 100644
index 5ed7306..0000000
--- a/ForgeImporters/docs/importers/google.rst
+++ /dev/null
@@ -1,30 +0,0 @@
-.. 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.
-
-Google Code
-===========
-
-This importer imports projects and tools from Google Code.
-
-:mod:`forgeimporters.google`
-----------------------------
-
-.. autoclass:: forgeimporters.google.project.GoogleCodeProjectImporter
- :members:
-
-.. autoclass:: forgeimporters.google.tracker.GoogleCodeTrackerImporter
- :members:
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/forgeimporters/google/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/__init__.py b/ForgeImporters/forgeimporters/google/__init__.py
deleted file mode 100644
index 302544e..0000000
--- a/ForgeImporters/forgeimporters/google/__init__.py
+++ /dev/null
@@ -1,449 +0,0 @@
-# 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.
-
-import re
-import urllib
-from urllib2 import HTTPError
-from urlparse import urlparse, urljoin, parse_qs
-from collections import defaultdict
-import logging
-import os
-
-import requests
-from BeautifulSoup import BeautifulSoup
-from formencode import validators as fev
-
-from allura.lib import helpers as h
-from allura import model as M
-from forgeimporters.base import ProjectExtractor
-from forgeimporters.base import File
-
-
-log = logging.getLogger(__name__)
-
-
-def _as_text(node, chunks=None):
- """
- Similar to node.text, but preserves whitespace around tags,
- and converts <br/>s to \n.
- """
- if chunks is None:
- chunks = []
- for n in node:
- if isinstance(n, basestring):
- chunks.append(n)
- elif n.name == 'br':
- chunks.append('\n')
- else:
- _as_text(n, chunks)
- return ''.join(chunks)
-
-
-def _as_markdown(tag, project_name):
- fragments = []
- for fragment in tag:
- if getattr(fragment, 'name', None) == 'a':
- href = urlparse(fragment['href'])
- qs = parse_qs(href.query)
- gc_link = not href.netloc or href.netloc == 'code.google.com'
- path_parts = href.path.split('/')
- target_project = None
- if gc_link:
- if len(path_parts) >= 5 and path_parts[1] == 'a':
- target_project = '/'.join(path_parts[1:5])
- elif len(path_parts) >= 3:
- target_project = path_parts[2]
- internal_link = target_project == project_name
- if gc_link and internal_link and 'id' in qs:
- # rewrite issue 123 project-internal issue links
- fragment = '[%s](#%s)' % (fragment.text, qs['id'][0])
- elif gc_link and internal_link and 'r' in qs:
- # rewrite r123 project-internal revision links
- fragment = '[r%s]' % qs['r'][0]
- elif gc_link:
- # preserve GC-internal links (probably issue PROJECT:123
- # inter-project issue links)
- fragment = '[%s](%s)' % (
- h.plain2markdown(
- fragment.text, preserve_multiple_spaces=True, has_html_entities=True),
- # possibly need to adjust this URL for /a/ hosted domain URLs,
- # but it seems fragment['href'] always starts with / so it replaces the given path
- urljoin('https://code.google.com/p/%s/issues/' %
- project_name, fragment['href']),
- )
- else:
- # convert all other links to Markdown syntax
- fragment = '[%s](%s)' % (fragment.text, fragment['href'])
- elif getattr(fragment, 'name', None) == 'i':
- # preserve styling of "(No comment was entered for this change.)"
- # messages
- fragment = '*%s*' % h.plain2markdown(fragment.text,
- preserve_multiple_spaces=True, has_html_entities=True)
- elif getattr(fragment, 'name', None) == 'b':
- # preserve styling of issue template
- fragment = '**%s**' % h.plain2markdown(fragment.text,
- preserve_multiple_spaces=True, has_html_entities=True)
- elif getattr(fragment, 'name', None) == 'br':
- # preserve forced line-breaks
- fragment = '\n'
- else:
- # convert all others to plain MD
- fragment = h.plain2markdown(
- unicode(fragment), preserve_multiple_spaces=True, has_html_entities=True)
- fragments.append(fragment)
- return ''.join(fragments).strip()
-
-
-def csv_parser(page):
- lines = page.readlines()
- if not lines:
- return []
- # skip CSV header
- lines = lines[1:]
- # skip "next page here" info footer
- if not lines[-1].startswith('"'):
- lines.pop()
- # remove CSV wrapping (quotes, commas, newlines)
- return [line.strip('",\n') for line in lines]
-
-
-class GoogleCodeProjectNameValidator(fev.FancyValidator):
- not_empty = True
- messages = {
- 'invalid': 'Please enter a project URL, or a project name containing '
- 'only letters, numbers, and dashes.',
- 'unavailable': 'This project is unavailable for import',
- }
-
- def _to_python(self, value, state=None):
- project_name_re = re.compile(r'^[a-z0-9][a-z0-9-]{,61}$')
- if project_name_re.match(value):
- # just a name
- project_name = value
- else:
- # try as a URL
- project_name = None
- project_name_simple = None
- url = urlparse(value.strip())
- if url.netloc.endswith('.googlecode.com'):
- project_name = url.netloc.split('.')[0]
- elif url.netloc == 'code.google.com':
- path_parts = url.path.lstrip('/').split('/')
- if len(path_parts) >= 2 and path_parts[0] == 'p':
- project_name = path_parts[1]
- elif len(path_parts) >= 4 and path_parts[0] == 'a' and path_parts[2] == 'p':
- project_name_simple = path_parts[3]
- project_name = '/'.join(path_parts[0:4])
-
- if not project_name_simple:
- project_name_simple = project_name
-
- if not project_name or not project_name_re.match(project_name_simple):
- raise fev.Invalid(self.message('invalid', state), value, state)
-
- if not GoogleCodeProjectExtractor(project_name).check_readable():
- raise fev.Invalid(self.message('unavailable', state), value, state)
- return project_name
-
-
-def split_project_name(project_name):
- '''
- For hosted projects, the project_name includes the hosted domain. Split, like:
-
- :param str project_name: "a/eclipselabs.org/p/restclient-tool"
- :return: ``("/a/eclipselabs.org", "restclient-tool")``
- '''
- if project_name.startswith('a/'):
- hosted_domain_prefix = '/a/' + project_name.split('/')[1]
- project_name = project_name.split('/')[3]
- else:
- hosted_domain_prefix = ''
- project_name = project_name
- return hosted_domain_prefix, project_name
-
-
-class GoogleCodeProjectExtractor(ProjectExtractor):
- BASE_URL = 'http://code.google.com'
- RE_REPO_TYPE = re.compile(r'(svn|hg|git)')
-
- PAGE_MAP = {
- 'project_info': BASE_URL + '{hosted_domain_prefix}/p/{project_name}/',
- 'source_browse': BASE_URL + '{hosted_domain_prefix}/p/{project_name}/source/browse/',
- 'issues_csv': BASE_URL + '{hosted_domain_prefix}/p/{project_name}/issues/csv?can=1&colspec=ID&sort=ID&start={start}',
- 'issue': BASE_URL + '{hosted_domain_prefix}/p/{project_name}/issues/detail?id={issue_id}',
- }
-
- LICENSE_MAP = defaultdict(lambda: 'Other/Proprietary License', {
- 'Apache License 2.0': 'Apache License V2.0',
- 'Artistic License/GPL': 'Artistic License',
- 'Eclipse Public License 1.0': 'Eclipse Public License',
- 'GNU GPL v2': 'GNU General Public License version 2.0 (GPLv2)',
- 'GNU GPL v3': 'GNU General Public License version 3.0 (GPLv3)',
- 'GNU Lesser GPL': 'GNU Library or Lesser General Public License version 2.0 (LGPLv2)',
- 'MIT License': 'MIT License',
- 'Mozilla Public License 1.1': 'Mozilla Public License 1.1 (MPL 1.1)',
- 'New BSD License': 'BSD License',
- 'Other Open Source': 'Open Software License',
- })
-
- DEFAULT_ICON = 'http://www.gstatic.com/codesite/ph/images/defaultlogo.png'
-
- def get_page_url(self, page_name, **kw):
- # override, to handle hosted domains
- hosted_domain_prefix, project_name = split_project_name(self.project_name)
- return self.PAGE_MAP[page_name].format(
- project_name=urllib.quote(project_name),
- hosted_domain_prefix=hosted_domain_prefix,
- **kw)
-
- def check_readable(self):
- resp = requests.head(self.get_page_url('project_info'))
- return resp.status_code == 200
-
- def get_short_description(self, project):
- page = self.get_page('project_info')
- project.short_description = page.find(
- itemprop='description').text.strip()
-
- def get_icon(self, project):
- page = self.get_page('project_info')
- icon_url = urljoin(self.url, page.find(itemprop='image').get('src'))
- if icon_url == self.DEFAULT_ICON:
- return
- icon_name = urllib.unquote(urlparse(icon_url).path).split('/')[-1]
- icon = File(icon_url, icon_name)
- filetype = icon.type
- # work around Google Code giving us bogus file type
- if filetype.startswith('text/html'):
- filetype = 'image/png'
- M.ProjectFile.save_image(
- icon_name, icon.file, filetype,
- square=True, thumbnail_size=(48, 48),
- thumbnail_meta={'project_id': project._id, 'category': 'icon'})
-
- def get_license(self, project):
- page = self.get_page('project_info')
- license = page.find(text='Code license').findNext().find(
- 'a').text.strip()
- trove = M.TroveCategory.query.get(fullname=self.LICENSE_MAP[license])
- project.trove_license.append(trove._id)
-
- def get_repo_type(self):
- page = self.get_page('source_browse')
- repo_type = page.find(id="crumb_root")
- if not repo_type:
- raise Exception("Couldn't detect repo type: no #crumb_root in "
- "{0}".format(self.url))
- re_match = self.RE_REPO_TYPE.match(repo_type.text.lower())
- if re_match:
- return re_match.group(0)
- else:
- raise Exception("Unknown repo type: {0}".format(repo_type.text))
-
- @classmethod
- def iter_issues(cls, project_name):
- """
- Iterate over all issues for a project,
- using paging to keep the responses reasonable.
- """
- extractor = cls(project_name)
- issue_ids = extractor.get_issue_ids(start=0)
- while issue_ids:
- for issue_id in sorted(issue_ids):
- try:
- yield (int(issue_id), cls(project_name, 'issue', issue_id=issue_id))
- except HTTPError as e:
- if e.code == 404:
- log.warn('Unable to load GC issue: %s #%s: %s: %s',
- project_name, issue_id, e, e.url)
- continue
- else:
- raise
- # get any new issues that were created while importing
- # (jumping back a few in case some were deleted and new ones added)
- new_ids = extractor.get_issue_ids(start=len(issue_ids) - 10)
- issue_ids = new_ids - issue_ids
-
- def get_issue_ids(self, start=0):
- limit = 100
-
- issue_ids = set()
- page = self.get_page('issues_csv', parser=csv_parser, start=start)
- while page:
- if len(page) <= 0:
- return
- issue_ids.update(page)
- start += limit
- page = self.get_page('issues_csv', parser=csv_parser, start=start)
- return issue_ids
-
- def get_issue_summary(self):
- text = self.page.find(id='issueheader').findAll(
- 'td', limit=2)[1].span.text.strip()
- bs = BeautifulSoup(text, convertEntities=BeautifulSoup.HTML_ENTITIES)
- return bs.text
-
- def get_issue_description(self):
- return _as_markdown(self.page.find(id='hc0').pre, self.project_name)
-
- def get_issue_created_date(self):
- return self.page.find(id='hc0').find('span', 'date').get('title')
-
- def get_issue_mod_date(self):
- comments = list(self.iter_comments())
- if comments:
- last_update = comments[-1]
- return last_update.created_date
- else:
- return self.get_issue_created_date()
-
- def get_issue_creator(self):
- a = self.page.find(id='hc0').find(True, 'userlink')
- return UserLink(a)
-
- def get_issue_status(self):
- tag = self.page.find(id='issuemeta').find(
- 'th', text=re.compile('Status:')).findNext().span
- if tag:
- return tag.text.strip()
- else:
- return ''
-
- def get_issue_owner(self):
- tag = self.page.find(id='issuemeta').find(
- 'th', text=re.compile('Owner:')).findNext().find(True, 'userlink')
- if tag:
- return UserLink(tag)
- else:
- return None
-
- def get_issue_labels(self):
- label_nodes = self.page.find(id='issuemeta').findAll('a', 'label')
- return [_as_text(l) for l in label_nodes]
-
- def get_issue_attachments(self):
- return _get_attachments(self.page.find(id='hc0'))
-
- def get_issue_stars(self):
- stars_re = re.compile(r'(\d+) (person|people) starred this issue')
- stars = self.page.find(id='issueheader').find(text=stars_re)
- if stars:
- return int(stars_re.search(stars).group(1))
- return 0
-
- def iter_comments(self):
- # first, get all pages if there are multiple pages of comments
- looking_for_comment_pages = True
- comment_page_urls = [self.url]
- while looking_for_comment_pages:
- first_comment = self.page.find('div', 'vt issuecomment')
- looking_for_comment_pages = False
- if first_comment and 'cursor_off' not in first_comment['class']:
- # this is not a real comment, just forward/back links
- for link in first_comment.findAll('a'):
- if link.text.startswith('Older'):
- prev_comments_page = urljoin(self.url, link['href'])
- comment_page_urls.insert(0, prev_comments_page)
- looking_for_comment_pages = True
- self.get_page(prev_comments_page) # prep for next iteration of loop
-
- # then go through those to get the actual comments
- for comment_page_url in comment_page_urls:
- self.get_page(comment_page_url)
- # regular comments have cursor_off class
- for comment in self.page.findAll('div', 'cursor_off vt issuecomment'):
- yield Comment(comment, self.project_name)
-
-
-
-
-
-class UserLink(object):
-
- def __init__(self, tag):
- self.name = tag.text.strip()
- if tag.get('href'):
- self.url = urljoin(
- GoogleCodeProjectExtractor.BASE_URL, tag.get('href'))
- else:
- self.url = None
-
- def __str__(self):
- if self.url:
- return '[{name}]({url})'.format(name=self.name, url=self.url)
- else:
- return self.name
-
-
-def _get_attachments(tag):
- attachment_links = tag.find('div', 'attachments')
- if attachment_links:
- attachments = []
- for a in attachment_links.findAll('a', text='Download'):
- url = a.parent.get('href')
- try:
- attachment = Attachment(url)
- except Exception:
- log.exception('Could not get attachment: %s', url)
- else:
- attachments.append(attachment)
- return attachments
- else:
- return []
-
-
-class Comment(object):
-
- def __init__(self, tag, project_name):
- self.author = UserLink(
- tag.find('span', 'author').find(True, 'userlink'))
- self.created_date = tag.find('span', 'date').get('title')
- self.body = _as_markdown(tag.find('pre'), project_name)
- self._get_updates(tag)
- self.attachments = _get_attachments(tag)
-
- def _get_updates(self, tag):
- _updates = tag.find('div', 'updates')
- self.updates = {
- b.text: b.nextSibling.strip()
- for b in _updates.findAll('b')} if _updates else {}
-
- @property
- def annotated_text(self):
- text = (
- u'*Originally posted by:* {author}\n'
- u'\n'
- u'{body}\n'
- u'\n'
- u'{updates}'
- ).format(
- author=self.author,
- body=self.body,
- updates='\n'.join(
- '**%s** %s' % (k, v)
- for k, v in self.updates.items()
- ),
- )
- return text
-
-
-class Attachment(File):
-
- def __init__(self, url):
- url = urljoin(GoogleCodeProjectExtractor.BASE_URL, url)
- filename = parse_qs(urlparse(url).query)['name'][0]
- super(Attachment, self).__init__(url, filename)
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/forgeimporters/google/code.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/code.py b/ForgeImporters/forgeimporters/google/code.py
deleted file mode 100644
index 6a3f43c..0000000
--- a/ForgeImporters/forgeimporters/google/code.py
+++ /dev/null
@@ -1,154 +0,0 @@
-# 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.
-
-import urllib2
-
-import formencode as fe
-from formencode import validators as fev
-
-from pylons import tmpl_context as c
-from pylons import app_globals as g
-from tg import (
- expose,
- flash,
- redirect,
-)
-from tg.decorators import (
- with_trailing_slash,
- without_trailing_slash,
-)
-
-from allura.lib import validators as v
-from allura.lib.decorators import require_post
-from allura import model as M
-
-from forgeimporters.base import (
- ToolImporter,
- ToolImportController,
-)
-from forgeimporters.google import GoogleCodeProjectExtractor
-from forgeimporters.google import GoogleCodeProjectNameValidator
-from forgeimporters.google import split_project_name
-
-REPO_URLS = {
- 'svn': 'http://{project_name}.googlecode.com/svn/',
- 'svn-hosted': 'http://svn.codespot.com{hosted_domain_prefix}/{project_name}/',
- 'git': 'https://code.google.com{hosted_domain_prefix}/p/{project_name}/',
- 'hg': 'https://code.google.com{hosted_domain_prefix}/p/{project_name}/',
-}
-
-
-def get_repo_url(project_name, type_):
- hosted_domain_prefix, project_name = split_project_name(project_name)
- if hosted_domain_prefix and type_ == 'svn':
- type_ = 'svn-hosted'
- return REPO_URLS[type_].format(project_name=project_name,
- hosted_domain_prefix=hosted_domain_prefix)
-
-
-class GoogleRepoImportForm(fe.schema.Schema):
- gc_project_name = GoogleCodeProjectNameValidator()
- mount_point = fev.UnicodeString()
- mount_label = fev.UnicodeString()
-
- def __init__(self, *args):
- pass
-
- def _to_python(self, value, state):
- value = super(self.__class__, self)._to_python(value, state)
-
- gc_project_name = value['gc_project_name']
- mount_point = value['mount_point']
- try:
- repo_type = GoogleCodeProjectExtractor(
- gc_project_name).get_repo_type()
- except urllib2.HTTPError as e:
- if e.code == 404:
- msg = 'No such project'
- else:
- msg = str(e)
- msg = 'gc_project_name:' + msg
- raise fe.Invalid(msg, value, state)
- except Exception:
- raise
- tool_class = g.entry_points['tool'][repo_type]
- try:
- value['mount_point'] = v.MountPointValidator(
- tool_class).to_python(mount_point)
- except fe.Invalid as e:
- raise fe.Invalid('mount_point:' + str(e), value, state)
- return value
-
-
-class GoogleRepoImportController(ToolImportController):
- import_form = GoogleRepoImportForm
-
- @with_trailing_slash
- @expose('jinja:forgeimporters.google:templates/code/index.html')
- def index(self, **kw):
- return dict(importer=self.importer,
- target_app=self.target_app)
-
- @without_trailing_slash
- @expose()
- @require_post()
- def create(self, gc_project_name, mount_point, mount_label, **kw):
- if self.importer.enforce_limit(c.project):
- self.importer.post(
- project_name=gc_project_name,
- mount_point=mount_point,
- mount_label=mount_label)
- flash('Repo import has begun. Your new repo will be available '
- 'when the import is complete.')
- else:
- flash(
- 'There are too many imports pending at this time. Please wait and try again.', 'error')
- redirect(c.project.url() + 'admin/')
-
-
-class GoogleRepoImporter(ToolImporter):
- target_app_ep_names = ('git', 'hg', 'svn')
- source = 'Google Code'
- controller = GoogleRepoImportController
- tool_label = 'Source Code'
- tool_description = 'Import your primary SVN, Git, or Hg repo from Google Code'
-
- def import_tool(self, project, user, project_name=None, mount_point=None,
- mount_label=None, **kw):
- """ Import a Google Code repo into a new SVN, Git, or Hg Allura tool.
-
- """
- extractor = GoogleCodeProjectExtractor(project_name, 'source_browse')
- repo_type = extractor.get_repo_type()
- repo_url = get_repo_url(project_name, repo_type)
- app = project.install_app(
- repo_type,
- mount_point=mount_point or 'code',
- mount_label=mount_label or 'Code',
- init_from_url=repo_url,
- import_id={
- 'source': self.source,
- 'project_name': project_name,
- }
- )
- M.AuditLog.log(
- 'import tool %s from %s on %s' % (
- app.config.options.mount_point,
- project_name, self.source,
- ), project=project, user=user, url=app.url)
- g.post_event('project_updated')
- return app
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/forgeimporters/google/project.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/project.py b/ForgeImporters/forgeimporters/google/project.py
deleted file mode 100644
index 9ffade1..0000000
--- a/ForgeImporters/forgeimporters/google/project.py
+++ /dev/null
@@ -1,68 +0,0 @@
-# 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.
-
-import logging
-
-from tg import expose, validate
-from tg.decorators import with_trailing_slash
-
-from allura.lib.decorators import require_post
-
-from forgeimporters import base
-from forgeimporters.google import tasks
-from forgeimporters.google import GoogleCodeProjectNameValidator
-
-
-log = logging.getLogger(__name__)
-
-
-class GoogleCodeProjectForm(base.ProjectImportForm):
- project_name = GoogleCodeProjectNameValidator()
-
-
-class GoogleCodeProjectImporter(base.ProjectImporter):
-
- """
- Project importer for Google Code.
-
- This imports project metadata, including summary, icon, and license,
- as well as providing the UI for importing individual tools during project
- import.
- """
- source = 'Google Code'
- process_validator = GoogleCodeProjectForm(source)
- index_template = 'jinja:forgeimporters.google:templates/project.html'
-
- def after_project_create(self, project, **kw):
- project.set_tool_data('google-code', project_name=project.name)
- tasks.import_project_info.post(project.name)
-
- @with_trailing_slash
- @expose(index_template)
- def index(self, **kw):
- return super(self.__class__, self).index(**kw)
-
- @require_post()
- @expose()
- @validate(process_validator, error_handler=index)
- def process(self, **kw):
- return super(self.__class__, self).process(**kw)
-
- @expose('json:')
- @validate(process_validator)
- def check_names(self, **kw):
- return super(self.__class__, self).check_names(**kw)
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/forgeimporters/google/tasks.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/tasks.py b/ForgeImporters/forgeimporters/google/tasks.py
deleted file mode 100644
index b0cb76f..0000000
--- a/ForgeImporters/forgeimporters/google/tasks.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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 pylons import tmpl_context as c
-from pylons import app_globals as g
-
-from ming.orm import ThreadLocalORMSession
-
-from allura.lib.decorators import task
-
-from forgeimporters.base import ImportErrorHandler
-from forgeimporters.google import GoogleCodeProjectExtractor
-
-
-@task
-def import_project_info(project_name):
- from forgeimporters.google.project import GoogleCodeProjectImporter
- importer = GoogleCodeProjectImporter(None)
- with ImportErrorHandler(importer, project_name, c.project) as handler:
- extractor = GoogleCodeProjectExtractor(project_name, 'project_info')
- extractor.get_short_description(c.project)
- extractor.get_icon(c.project)
- extractor.get_license(c.project)
- ThreadLocalORMSession.flush_all()
- g.post_event('project_updated')
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/forgeimporters/google/templates/code/index.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/templates/code/index.html b/ForgeImporters/forgeimporters/google/templates/code/index.html
deleted file mode 100644
index e2cef0c..0000000
--- a/ForgeImporters/forgeimporters/google/templates/code/index.html
+++ /dev/null
@@ -1,31 +0,0 @@
-{#-
- 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 repo from Google Code
-{% endblock %}
-
-{% block importer_fields %}
-<div>
- <label for="gc_project_name">Google Code project name (as it appears in a URL)</label>
- <input name="gc_project_name" value="{{ c.form_values['gc_project_name'] }}" />
- {{ error('gc_project_name') }}
-</div>
-{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/forgeimporters/google/templates/project.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/templates/project.html b/ForgeImporters/forgeimporters/google/templates/project.html
deleted file mode 100644
index a00c0dd..0000000
--- a/ForgeImporters/forgeimporters/google/templates/project.html
+++ /dev/null
@@ -1,68 +0,0 @@
-{#-
- 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/project_base.html' %}
-
-{% block extra_js %}
- {{super()}}
- <script type="text/javascript">
- function suggest_name() { // overrides base suggest_names
- var $project_shortname = $('#project_shortname');
- if (!manual) {
- var name_or_url = $('#project_name').val().replace(/\s/g, '').toLowerCase();
- name_or_url = name_or_url.replace(/\/$/, ''); // strip any trailing slash
- var a = $('<a>', {href: name_or_url})[0]; // leverage DOM to parse URL
- var project_name;
- var old_style = a.hostname.match(/^(.*)\.googlecode\.com$/);
- if (old_style) {
- project_name = old_style[1];
- } else {
- var parts = a.pathname.split('/');
- project_name = parts.pop();
- }
- $project_shortname.val(project_name);
- }
- $project_shortname.trigger('change');
- }
- </script>
-{% endblock %}
-
-{% block project_fields %}
- <div class="grid-7">
- <label>Google Project Name or URL</label>
- </div>
- <div class="grid-10">
- <input id="project_name" name="project_name" value="{{c.form_values['project_name']}}" autofocus/>
- <div id="project_name_error" class="error{% if not c.form_errors['project_name'] %} hidden{% endif %}">
- {{c.form_errors['project_name']}}
- </div>
- </div>
-
- <div class="grid-7" style="clear:left">
- <label>{{config.site_name}} URL Name</label>
- </div>
- <div class="grid-10">
- <input id="project_shortname" name="project_shortname" value="{{c.form_values['project_shortname']}}"/>
- <div id="project_shortname_error" class="error{% if not c.form_errors['project_shortname'] %} hidden{% endif %}">
- {{c.form_errors['project_shortname']}}
- </div>
- <div id="project-url">
- http://{{request.environ['HTTP_HOST']}}{{importer.neighborhood.url()}}<span id="url-fragment">{{c.form_values['project_shortname']}}</span>
- </div>
- </div>
-{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/forgeimporters/google/templates/tracker/index.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/templates/tracker/index.html b/ForgeImporters/forgeimporters/google/templates/tracker/index.html
deleted file mode 100644
index 08d0084..0000000
--- a/ForgeImporters/forgeimporters/google/templates/tracker/index.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{#-
- 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 importer_fields %}
-<div>
- <label for="gc_project_name">Google Code project name (as it appears in a URL)</label>
- <input name="gc_project_name" value="{{ c.form_values['gc_project_name'] }}" />
- {{ error('gc_project_name') }}
-</div>
-{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/forgeimporters/google/tests/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/tests/__init__.py b/ForgeImporters/forgeimporters/google/tests/__init__.py
deleted file mode 100644
index 144e298..0000000
--- a/ForgeImporters/forgeimporters/google/tests/__init__.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# 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.
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/forgeimporters/google/tests/test_code.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/tests/test_code.py b/ForgeImporters/forgeimporters/google/tests/test_code.py
deleted file mode 100644
index 1b6cacc..0000000
--- a/ForgeImporters/forgeimporters/google/tests/test_code.py
+++ /dev/null
@@ -1,146 +0,0 @@
-# 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 Mock, patch
-from ming.odm import ThreadLocalORMSession
-
-from allura.tests import TestController
-from allura import model as M
-
-
-# important to be distinct from 'test' which ForgeSVN uses, so that the
-# tests can run in parallel and not clobber each other
-test_project_with_repo = 'test2'
-
-
-from forgeimporters.google.code import (
- get_repo_url,
- GoogleRepoImporter,
-)
-
-
-class TestGetRepoUrl(TestCase):
-
- def test_svn(self):
- r = get_repo_url('projname', 'svn')
- self.assertEqual(r, 'http://projname.googlecode.com/svn/')
-
- def test_git(self):
- r = get_repo_url('projname', 'git')
- self.assertEqual(r, 'https://code.google.com/p/projname/')
-
- def test_hg(self):
- r = get_repo_url('projname', 'hg')
- self.assertEqual(r, 'https://code.google.com/p/projname/')
-
- def test_svn_hosted(self):
- r = get_repo_url('a/eclipselabs.org/p/projname', 'svn')
- self.assertEqual(r, 'http://svn.codespot.com/a/eclipselabs.org/projname/')
-
- def test_git_hosted(self):
- r = get_repo_url('a/eclipselabs.org/p/projname', 'git')
- self.assertEqual(r, 'https://code.google.com/a/eclipselabs.org/p/projname/')
-
- def test_hg_hosted(self):
- r = get_repo_url('a/eclipselabs.org/p/projname', 'hg')
- self.assertEqual(r, 'https://code.google.com/a/eclipselabs.org/p/projname/')
-
-
-class TestGoogleRepoImporter(TestCase):
-
- def _make_project(self, gc_proj_name=None):
- project = Mock()
- project.get_tool_data.side_effect = lambda *args: gc_proj_name
- return project
-
- @patch('forgeimporters.google.code.g')
- @patch('forgeimporters.google.code.M')
- @patch('forgeimporters.google.code.GoogleCodeProjectExtractor')
- @patch('forgeimporters.google.code.get_repo_url')
- def test_import_tool_happy_path(self, get_repo_url, gcpe, M, g):
- gcpe.return_value.get_repo_type.return_value = 'git'
- get_repo_url.return_value = 'http://remote/clone/url/'
- p = self._make_project(gc_proj_name='myproject')
- u = Mock(name='c.user')
- app = p.install_app.return_value
- app.config.options.mount_point = 'code'
- app.url = 'foo'
- GoogleRepoImporter().import_tool(p, u, project_name='project_name')
- get_repo_url.assert_called_once_with('project_name', 'git')
- p.install_app.assert_called_once_with('git',
- mount_point='code',
- mount_label='Code',
- init_from_url='http://remote/clone/url/',
- import_id={
- 'source': 'Google Code',
- 'project_name': 'project_name',
- },
- )
- M.AuditLog.log.assert_called_once_with(
- 'import tool code from project_name on Google Code',
- project=p, user=u, url='foo')
- g.post_event.assert_called_once_with('project_updated')
-
-
-class TestGoogleRepoImportController(TestController, TestCase):
-
- def test_index(self):
- r = self.app.get(
- '/p/{}/admin/ext/import/google-code-repo/'.format(test_project_with_repo))
- self.assertIsNotNone(r.html.find(attrs=dict(name="gc_project_name")))
- self.assertIsNotNone(r.html.find(attrs=dict(name="mount_label")))
- self.assertIsNotNone(r.html.find(attrs=dict(name="mount_point")))
-
- @patch('forgeimporters.google.code.GoogleCodeProjectExtractor')
- @patch('forgeimporters.base.import_tool')
- def test_create(self, import_tool, extractor):
- extractor.return_value.get_repo_type.return_value = 'git'
- params = dict(gc_project_name='poop',
- mount_label='mylabel',
- mount_point='mymount',
- )
- r = self.app.post(
- '/p/{}/admin/ext/import/google-code-repo/create'.format(test_project_with_repo),
- params,
- status=302)
- self.assertEqual(
- r.location, 'http://localhost/p/{}/admin/'.format(test_project_with_repo))
- self.assertEqual(
- u'mymount', import_tool.post.call_args[1]['mount_point'])
- self.assertEqual(
- u'mylabel', import_tool.post.call_args[1]['mount_label'])
- self.assertEqual(
- u'poop', import_tool.post.call_args[1]['project_name'])
-
- @patch('forgeimporters.google.code.GoogleCodeProjectExtractor')
- @patch('forgeimporters.base.import_tool')
- def test_create_limit(self, import_tool, extractor):
- extractor.return_value.get_repo_type.return_value = 'git'
- project = M.Project.query.get(shortname=test_project_with_repo)
- project.set_tool_data('GoogleRepoImporter', pending=1)
- ThreadLocalORMSession.flush_all()
- params = dict(gc_project_name='poop',
- mount_label='mylabel',
- mount_point='mymount',
- )
- r = self.app.post(
- '/p/{}/admin/ext/import/google-code-repo/create'.format(test_project_with_repo),
- params,
- status=302).follow()
- self.assertIn('Please wait and try again', r)
- self.assertEqual(import_tool.post.call_count, 0)
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/forgeimporters/google/tests/test_init.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/tests/test_init.py b/ForgeImporters/forgeimporters/google/tests/test_init.py
deleted file mode 100644
index 37bf4c2..0000000
--- a/ForgeImporters/forgeimporters/google/tests/test_init.py
+++ /dev/null
@@ -1,130 +0,0 @@
-# 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 nose.tools import assert_equal
-from mock import patch
-from formencode.validators import Invalid
-from BeautifulSoup import BeautifulSoup
-from IPython.testing.decorators import skipif, module_not_available
-
-from allura.tests import decorators as td
-from forgeimporters.google import GoogleCodeProjectNameValidator, GoogleCodeProjectExtractor
-from forgeimporters.google import _as_markdown
-
-
-class TestGoogleCodeProjectNameValidator(object):
-
- def setUp(self):
- self.readable_patcher = patch.object(GoogleCodeProjectExtractor, 'check_readable')
- self.readable_mock = self.readable_patcher.start()
- self.readable_mock.return_value = True
-
- def tearDown(self):
- self.readable_patcher.stop()
-
- def test_simple(self):
- assert_equal(
- GoogleCodeProjectNameValidator()._to_python('gmapcatcher'),
- 'gmapcatcher'
- )
-
- def test_code_dot_google(self):
- assert_equal(
- GoogleCodeProjectNameValidator()._to_python('http://code.google.com/p/gmapcatcher/'),
- 'gmapcatcher'
- )
- assert_equal(
- GoogleCodeProjectNameValidator()._to_python('https://code.google.com/p/gmapcatcher/'),
- 'gmapcatcher'
- )
-
- def test_googlecode_com(self):
- assert_equal(
- GoogleCodeProjectNameValidator()._to_python('http://gmapcatcher.googlecode.com/'),
- 'gmapcatcher'
- )
- assert_equal(
- GoogleCodeProjectNameValidator()._to_python('https://gmapcatcher.googlecode.com/'),
- 'gmapcatcher'
- )
-
- def test_not_readable(self):
- self.readable_mock.return_value = False
- with td.raises(Invalid):
- GoogleCodeProjectNameValidator()._to_python('gmapcatcher')
-
- def test_invalid(self):
- with td.raises(Invalid):
- GoogleCodeProjectNameValidator()._to_python('http://code.google.com/')
- with td.raises(Invalid):
- GoogleCodeProjectNameValidator()._to_python('http://foobar.com/p/gmapcatcher')
- with td.raises(Invalid):
- GoogleCodeProjectNameValidator()._to_python('http://code.google.com/p/asdf_asdf')
- with td.raises(Invalid):
- GoogleCodeProjectNameValidator()._to_python('http://code.google.com/x/y/z')
-
- def test_hosted_domain(self):
- assert_equal(
- GoogleCodeProjectNameValidator()._to_python('https://code.google.com/a/eclipselabs.org/p/restclient-tool'),
- 'a/eclipselabs.org/p/restclient-tool'
- )
- with td.raises(Invalid):
- GoogleCodeProjectNameValidator()._to_python('http://code.google.com/a/eclipselabs.org/bogus')
-
-
-class Test_as_markdown(object):
-
- # this is covered by functional tests (useing test-issue.html)
- # but adding some unit tests for easier verification of hosted domain link rewriting
-
- def test_link_within_proj(self):
- html = BeautifulSoup('''<pre>Foo: <a href="/p/myproj/issues/detail?id=1">issue 1</a></pre>''')
- assert_equal(
- _as_markdown(html.first(), 'myproj'),
- 'Foo: [issue 1](#1)'
- )
-
- @skipif(module_not_available('html2text'))
- def test_link_other_proj_has_html2text(self):
- html = BeautifulSoup('''<pre>Foo: <a href="/p/other-project/issues/detail?id=1">issue other-project:1</a></pre>''')
- assert_equal(
- _as_markdown(html.first(), 'myproj'),
- 'Foo: [issue other-project:1](https://code.google.com/p/other-project/issues/detail?id=1)'
- )
-
- @td.without_module('html2text')
- def test_link_other_proj_no_html2text(self):
- # without html2text, the dash in other-project doesn't get escaped right
- html = BeautifulSoup('''<pre>Foo: <a href="/p/other-project/issues/detail?id=1">issue other-project:1</a></pre>''')
- assert_equal(
- _as_markdown(html.first(), 'myproj'),
- 'Foo: [issue other\\-project:1](https://code.google.com/p/other-project/issues/detail?id=1)'
- )
-
- def test_link_hosted_domain_within_proj(self):
- html = BeautifulSoup('''<pre>Foo: <a href="/a/eclipselabs.org/p/myproj/issues/detail?id=1">issue 1</a></pre>''')
- assert_equal(
- _as_markdown(html.first(), 'a/eclipselabs.org/p/myproj'),
- 'Foo: [issue 1](#1)'
- )
-
- def test_link_hosted_domain_other_proj(self):
- html = BeautifulSoup('''<pre>Foo: <a href="/a/eclipselabs.org/p/other-proj/issues/detail?id=1">issue 1</a></pre>''')
- assert_equal(
- _as_markdown(html.first(), 'a/eclipselabs.org/p/myproj'),
- 'Foo: [issue 1](https://code.google.com/a/eclipselabs.org/p/other-proj/issues/detail?id=1)'
- )
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/forgeimporters/google/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/tracker.py b/ForgeImporters/forgeimporters/google/tracker.py
deleted file mode 100644
index 7873aa6..0000000
--- a/ForgeImporters/forgeimporters/google/tracker.py
+++ /dev/null
@@ -1,224 +0,0 @@
-# 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 collections import defaultdict
-
-from pylons import tmpl_context as c
-from pylons import app_globals as g
-from ming.orm import session, ThreadLocalORMSession
-import dateutil.parser
-
-from tg import (
- expose,
- flash,
- redirect,
-)
-from tg.decorators import (
- with_trailing_slash,
- without_trailing_slash,
-)
-
-from allura.lib import helpers as h
-from allura.lib.plugin import ImportIdConverter
-from allura.lib.decorators import require_post
-from allura import model as M
-
-from forgetracker import model as TM
-from forgeimporters.google import GoogleCodeProjectExtractor
-from forgeimporters.google import GoogleCodeProjectNameValidator
-from forgeimporters.base import (
- ToolImporter,
- ToolImportForm,
- ToolImportController,
-)
-
-
-class GoogleCodeTrackerImportForm(ToolImportForm):
- gc_project_name = GoogleCodeProjectNameValidator()
-
-
-class GoogleCodeTrackerImportController(ToolImportController):
- import_form = GoogleCodeTrackerImportForm
-
- @with_trailing_slash
- @expose('jinja:forgeimporters.google:templates/tracker/index.html')
- def index(self, **kw):
- return dict(importer=self.importer,
- target_app=self.target_app)
-
- @without_trailing_slash
- @expose()
- @require_post()
- def create(self, gc_project_name, mount_point, mount_label, **kw):
- if self.importer.enforce_limit(c.project):
- self.importer.post(
- project_name=gc_project_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.')
- else:
- flash(
- 'There are too many imports pending at this time. Please wait and try again.', 'error')
- redirect(c.project.url() + 'admin/')
-
-
-class GoogleCodeTrackerImporter(ToolImporter):
- source = 'Google Code'
- target_app_ep_names = 'tickets'
- controller = GoogleCodeTrackerImportController
- tool_label = 'Issues'
- tool_description = 'Import your public tickets from Google Code'
-
- field_types = defaultdict(lambda: 'string',
- milestone='milestone',
- priority='select',
- type='select',
- )
-
- def __init__(self, *args, **kwargs):
- super(GoogleCodeTrackerImporter, self).__init__(*args, **kwargs)
- self.open_milestones = set()
- self.custom_fields = {}
- self.max_ticket_num = 0
-
- def import_tool(self, project, user, project_name, mount_point=None,
- mount_label=None, **kw):
- import_id_converter = ImportIdConverter.get()
- 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',
- import_id={
- 'source': self.source,
- 'project_name': project_name,
- },
- )
- ThreadLocalORMSession.flush_all()
- 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 GoogleCodeProjectExtractor.iter_issues(project_name):
- 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,
- import_id=import_id_converter.expand(ticket_num, app))
- 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()
- M.AuditLog.log(
- 'import tool %s from %s on %s' % (
- app.config.options.mount_point,
- project_name, self.source,
- ),
- project=project,
- user=user,
- url=app.url,
- )
- g.post_event('project_updated')
- app.globals.invalidate_bin_counts()
- return app
- except Exception:
- h.make_app_admin_only(app)
- raise
- finally:
- M.session.artifact_orm_session._get().skip_mod_date = False
-
- def custom_field(self, name):
- if name not in self.custom_fields:
- self.custom_fields[name] = {
- 'type': self.field_types[name.lower()],
- 'label': name,
- 'name': u'_%s' % name.lower(),
- 'options': set(),
- }
- return self.custom_fields[name]
-
- def process_fields(self, ticket, issue):
- ticket.summary = issue.get_issue_summary()
- ticket.status = issue.get_issue_status()
- ticket.created_date = dateutil.parser.parse(
- issue.get_issue_created_date())
- ticket.mod_date = dateutil.parser.parse(issue.get_issue_mod_date())
- ticket.votes_up = issue.get_issue_stars()
- ticket.votes = issue.get_issue_stars()
- owner = issue.get_issue_owner()
- if owner:
- owner_line = '*Originally owned by:* {owner}\n'.format(owner=owner)
- else:
- owner_line = ''
- ticket.description = (
- u'*Originally created by:* {creator}\n'
- u'{owner}'
- u'\n'
- u'{body}').format(
- creator=issue.get_issue_creator(),
- owner=owner_line,
- body=issue.get_issue_description(),
- )
- ticket.add_multiple_attachments(issue.get_issue_attachments())
-
- 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=dateutil.parser.parse(comment.created_date),
- )
- 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
http://git-wip-us.apache.org/repos/asf/allura/blob/b956fe30/ForgeImporters/forgeimporters/tests/data/google/empty-issue.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/data/google/empty-issue.html b/ForgeImporters/forgeimporters/tests/data/google/empty-issue.html
deleted file mode 100644
index b2eef20..0000000
--- a/ForgeImporters/forgeimporters/tests/data/google/empty-issue.html
+++ /dev/null
@@ -1,306 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
-<meta name="ROBOTS" content="NOARCHIVE" />
-<link rel="icon" type="image/vnd.microsoft.icon" href="http://www.gstatic.com/codesite/ph/images/phosting.ico" />
-<script type="text/javascript">
-
-
-
-
- var codesite_token = null;
-
-
- var CS_env = {"assetHostPath":"http://www.gstatic.com/codesite/ph","projectHomeUrl":"/p/allura-google-importer","relativeBaseUrl":"","domainName":null,"projectName":"allura-google-importer","loggedInUserEmail":null,"profileUrl":null,"token":null,"assetVersionPath":"http://www.gstatic.com/codesite/ph/3783617020303179221"};
- var _gaq = _gaq || [];
- _gaq.push(
- ['siteTracker._setAccount', 'UA-18071-1'],
- ['siteTracker._trackPageview']);
-
- (function() {
- var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
- ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
- (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
- })();
-
- </script>
-<title>Issue 5 -
- allura-google-importer -
-
- Empty Issue -
- Import Google Code projects to an Allura forge - Google Project Hosting
- </title>
-<link type="text/css" rel="stylesheet" href="http://www.gstatic.com/codesite/ph/3783617020303179221/css/core.css" />
-<link type="text/css" rel="stylesheet" href="http://www.gstatic.com/codesite/ph/3783617020303179221/css/ph_detail.css" />
-<!--[if IE]>
- <link type="text/css" rel="stylesheet" href="http://www.gstatic.com/codesite/ph/3783617020303179221/css/d_ie.css" >
-<![endif]-->
-<style type="text/css">
- .menuIcon.off { background: no-repeat url(http://www.gstatic.com/codesite/ph/images/dropdown_sprite.gif) 0 -42px }
- .menuIcon.on { background: no-repeat url(http://www.gstatic.com/codesite/ph/images/dropdown_sprite.gif) 0 -28px }
- .menuIcon.down { background: no-repeat url(http://www.gstatic.com/codesite/ph/images/dropdown_sprite.gif) 0 0; }
-
-
- .attachments { width:33%; border-top:2px solid #999; padding-top: 3px; margin-left: .7em;}
- .attachments table { margin-bottom: 0.75em; }
- .attachments table tr td { padding: 0; margin: 0; font-size: 95%; }
- .preview { border: 2px solid #c3d9ff; padding: 1px; }
- .preview:hover { border: 2px solid blue; }
- .label { white-space: nowrap; }
- .derived { font-style:italic }
- .cursor_on .author {
- background: url(http://www.gstatic.com/codesite/ph/images/show-arrow.gif) no-repeat 2px;
- }
- .hiddenform {
- display: none;
- }
-
-
- </style>
-</head>
-<body class="t3">
-<script type="text/javascript">
- window.___gcfg = {lang: 'en'};
- (function()
- {var po = document.createElement("script");
- po.type = "text/javascript"; po.async = true;po.src = "https://apis.google.com/js/plusone.js";
- var s = document.getElementsByTagName("script")[0];
- s.parentNode.insertBefore(po, s);
- })();
-</script>
-<div class="headbg">
-<div id="gaia">
-<span>
-<a href="#" id="projects-dropdown" onclick="return false;"><u>My favorites</u> <small>▼</small></a>
- | <a href="https://www.google.com/accounts/ServiceLogin?service=code&ltmpl=phosting&continue=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D5&followup=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D5" onclick="_CS_click('/gb/ph/signin');"><u>Sign in</u></a>
-</span>
-</div>
-<div class="gbh" style="left: 0pt;"></div>
-<div class="gbh" style="right: 0pt;"></div>
-<div style="height: 1px"></div>
-<!--[if lte IE 7]>
-<div style="text-align:center;">
-Your version of Internet Explorer is not supported. Try a browser that
-contributes to open source, such as <a href="http://www.firefox.com">Firefox</a>,
-<a href="http://www.google.com/chrome">Google Chrome</a>, or
-<a href="http://code.google.com/chrome/chromeframe/">Google Chrome Frame</a>.
-</div>
-<![endif]-->
-<table style="padding:0px; margin: 0px 0px 10px 0px; width:100%" cellpadding="0" cellspacing="0" itemscope="itemscope" itemtype="http://schema.org/CreativeWork">
-<tr style="height: 58px;">
-<td id="plogo">
-<link itemprop="url" href="/p/allura-google-importer" />
-<a href="/p/allura-google-importer/">
-<img src="/p/allura-google-importer/logo?cct=1374769571" alt="Logo" itemprop="image" />
-</a>
-</td>
-<td style="padding-left: 0.5em">
-<div id="pname">
-<a href="/p/allura-google-importer/"><span itemprop="name">allura-google-importer</span></a>
-</div>
-<div id="psum">
-<a id="project_summary_link" href="/p/allura-google-importer/"><span itemprop="description">Import Google Code projects to an Allura forge</span></a>
-</div>
-</td>
-<td style="white-space:nowrap;text-align:right; vertical-align:bottom;">
-<form action="/hosting/search">
-<input size="30" name="q" value="" type="text" />
-<input type="submit" name="projectsearch" value="Search projects" />
-</form>
-</td></tr>
-</table>
-</div>
-<div id="mt" class="gtb">
-<a href="/p/allura-google-importer/" class="tab ">Project Home</a>
-<a href="/p/allura-google-importer/wiki/TestPage?tm=6" class="tab ">Wiki</a>
-<a href="/p/allura-google-importer/issues/list" class="tab active">Issues</a>
-<a href="/p/allura-google-importer/source/checkout" class="tab ">Source</a>
-<div class="gtbc"></div>
-</div>
-<table cellspacing="0" cellpadding="0" width="100%" align="center" border="0" class="st">
-<tr>
-<td class="subt">
-<div class="issueDetail">
-<div class="isf">
-<span class="inIssueEntry">
-<a class="buttonify" href="entry" onclick="return _newIssuePrompt();">New issue</a>
-</span>
-
- <span class="inIssueList">
-<span>Search</span>
-</span><form action="list" method="GET" style="display:inline">
-<select id="can" name="can">
-<option disabled="disabled">Search within:</option>
-<option value="1"> All issues</option>
-<option value="2" selected="selected"> Open issues</option>
-<option value="6"> New issues</option>
-<option value="7"> Issues to verify</option>
-</select>
-<span>for</span>
-<span id="qq"><input type="text" size="38" id="searchq" name="q" value="" autocomplete="off" onkeydown="_blurOnEsc(event)" /></span>
-<span id="search_colspec"><input type="hidden" name="colspec" value="ID Type Status Priority Milestone Owner Summary" /></span>
-<input type="hidden" name="cells" value="tiles" />
-<input type="submit" value="Search" />
-</form>
-
- <span class="inIssueAdvSearch">
-<a href="advsearch">Advanced search</a>
-</span>
- <span class="inIssueSearchTips">
-<a href="searchtips">Search tips</a>
-</span>
- <span class="inIssueSubscriptions">
-<a href="/p/allura-google-importer/issues/subscriptions">Subscriptions</a>
-</span>
-</div>
-</div>
-</td>
-<td align="right" valign="top" class="bevel-right"></td>
-</tr>
-</table>
-<script type="text/javascript">
- var cancelBubble = false;
- function _go(url) { document.location = url; }
-</script>
-<div id="maincol">
-<div id="color_control" class="">
-<div id="issueheader">
-<table cellpadding="0" cellspacing="0" width="100%"><tbody>
-<tr>
-<td class="vt h3" nowrap="nowrap" style="padding:0 5px">
-
-
- Issue <a href="detail?id=5">5</a>:
- </td>
-<td width="90%" class="vt">
-<span class="h3">Empty Issue</span>
-</td>
-<td>
-<div class="pagination">
-<a href="../../allura-google-importer/issues/detail?id=4" title="Prev">‹ Prev</a>
- 5 of 5
-
- </div>
-</td>
-</tr>
-<tr>
-<td></td>
-<td nowrap="nowrap">
-
-
- 1 person starred this issue and may be notified of changes.
-
-
-
- </td>
-<td align="center" nowrap="nowrap">
-<a href="http://code.google.com/p/allura-google-importer/issues/list?cursor=allura-google-importer%3A5">Back to list</a>
-</td>
-</tr>
-</tbody></table>
-</div>
-<table width="100%" cellpadding="0" cellspacing="0" border="0" class="issuepage" id="meta-container">
-<tbody class="collapse">
-<tr>
-<td id="issuemeta">
-<div id="meta-float">
-<table cellspacing="0" cellpadding="0">
-<tr><th align="left">Status: </th>
-<td width="100%">
-
-
- ----
-
-
- </td>
-</tr>
-<tr><th align="left">Owner: </th><td>
-
-
- ----
-
-
- </td>
-</tr>
-<tr><td colspan="2">
-</td></tr>
-</table>
-<div class="rel_issues">
-</div>
-<br /><br />
-<div style="white-space:nowrap"><a href="https://www.google.com/accounts/ServiceLogin?service=code&ltmpl=phosting&continue=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D5&followup=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D5">Sign in</a> to add a comment</div>
-</div>
- </td>
-<td class="vt issuedescription" width="100%" id="cursorarea">
-<div class="cursor_off vt issuedescription" id="hc0">
-<div class="author">
-<span class="role_label">Project Member</span>
- Reported by
-
-
- <span class="userlink">john...@gmail.com</span>,
- <span class="date" title="Thu Aug 8 14:56:23 2013">Today (15 minutes ago)</span>
-</div>
-<pre>
-Empty
-</pre>
-</div>
-</td>
-</tr>
-<tr>
-<td></td>
-<td class="vt issuecomment">
-<span class="indicator">►</span> <a href="https://www.google.com/accounts/ServiceLogin?service=code&ltmpl=phosting&continue=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D5&followup=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D5">Sign in</a> to add a comment
- </td>
-</tr>
-</tbody>
-</table>
-<br />
-<script type="text/javascript" src="http://www.gstatic.com/codesite/ph/3783617020303179221/js/dit_scripts.js"></script>
-</div>
-<form name="delcom" action="delComment.do?q=&can=2&groupby=&sort=&colspec=ID+Type+Status+Priority+Milestone+Owner+Summary" method="POST">
-<input type="hidden" name="sequence_num" value="" />
-<input type="hidden" name="mode" value="" />
-<input type="hidden" name="id" value="5" />
-<input type="hidden" name="token" value="" />
-</form>
-<div id="helparea"></div>
-<script type="text/javascript">
- _onload();
- function delComment(sequence_num, delete_mode) {
- var f = document.forms["delcom"];
- f.sequence_num.value = sequence_num;
- f.mode.value = delete_mode;
-
- f.submit();
- return false;
- }
-
- _floatMetadata();
-</script>
-<script type="text/javascript" src="http://www.gstatic.com/codesite/ph/3783617020303179221/js/kibbles.js"></script>
-<script type="text/javascript">
- _setupKibblesOnDetailPage(
- 'http://code.google.com/p/allura-google-importer/issues/list?cursor\x3dallura-google-importer%3A5',
- '/p/allura-google-importer/issues/entry',
- '../../allura-google-importer/issues/detail?id\x3d4',
- '',
- '', 'allura-google-importer', 5,
- false, false, codesite_token);
-</script>
-<script type="text/javascript" src="http://www.gstatic.com/codesite/ph/3783617020303179221/js/ph_core.js"></script>
-</div>
-<div id="footer" dir="ltr">
-<div class="text">
-<a href="/projecthosting/terms.html">Terms</a> -
- <a href="http://www.google.com/privacy.html">Privacy</a> -
- <a href="/p/support/">Project Hosting Help</a>
-</div>
-</div>
-<div class="hostedBy" style="margin-top: -20px;">
-<span style="vertical-align: top;">Powered by <a href="http://code.google.com/projecthosting/">Google Project Hosting</a></span>
-</div>
-</body>
-</html>