2016-09-07 14:15:23 +02:00
|
|
|
import functools
|
2016-08-31 16:01:42 +02:00
|
|
|
import logging
|
|
|
|
|
2016-09-06 18:46:28 +02:00
|
|
|
from flask import Blueprint, render_template
|
2016-09-07 14:15:23 +02:00
|
|
|
|
2016-08-31 11:30:44 +02:00
|
|
|
from pillar.api.utils import jsonify
|
2016-09-07 14:15:23 +02:00
|
|
|
from pillar.web.system_util import pillar_api
|
|
|
|
import pillarsdk
|
2016-07-29 16:48:43 +02:00
|
|
|
|
|
|
|
blueprint = Blueprint('attract', __name__)
|
2016-08-31 16:01:42 +02:00
|
|
|
log = logging.getLogger(__name__)
|
2016-07-29 16:48:43 +02:00
|
|
|
|
|
|
|
|
2016-09-06 18:46:28 +02:00
|
|
|
@blueprint.route('/')
|
|
|
|
def index():
|
|
|
|
return render_template('attract/index.html')
|
2016-08-31 16:01:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
@blueprint.route('/subversion/kick')
|
|
|
|
def subversion_kick():
|
|
|
|
from . import subversion
|
|
|
|
|
|
|
|
# TODO: each project should have its own SVN server.
|
|
|
|
svn_server_url = 'svn://localhost/agent327'
|
|
|
|
log.info('Re-examining SVN server %s', svn_server_url)
|
|
|
|
client = subversion.obtain(svn_server_url)
|
|
|
|
|
|
|
|
# TODO: last_seen_revision should be stored, probably at the project level.
|
|
|
|
last_seen_revision = 0
|
|
|
|
observer = subversion.CommitLogObserver(client, last_seen_revision=last_seen_revision)
|
|
|
|
observer.fetch_and_observe()
|
|
|
|
|
|
|
|
return jsonify({
|
|
|
|
'previous_last_seen_revision': last_seen_revision,
|
|
|
|
'last_seen_revision': observer.last_seen_revision,
|
|
|
|
})
|
2016-09-07 14:15:23 +02:00
|
|
|
|
|
|
|
|
|
|
|
def error_project_not_setup_for_attract():
|
|
|
|
return render_template('attract/errors/project_not_setup.html')
|
|
|
|
|
|
|
|
|
|
|
|
def attract_project_view(extra_project_projections=None):
|
|
|
|
"""Decorator, replaces the first parameter project_url with the actual project.
|
|
|
|
|
|
|
|
Assumes the first parameter to the decorated function is 'project_url'. It then
|
|
|
|
looks up that project, checks that it's set up for Attract, and passes it to the
|
|
|
|
decorated function.
|
|
|
|
|
|
|
|
If not set up for attract, uses error_project_not_setup_for_attract() to render
|
|
|
|
the response.
|
|
|
|
|
|
|
|
:param extra_project_projections: extra projections to use on top of the ones already
|
|
|
|
used by this decorator.
|
|
|
|
:type extra_project_projections: dict
|
|
|
|
"""
|
|
|
|
|
|
|
|
if callable(extra_project_projections):
|
|
|
|
raise TypeError('Use with @attract_project_view() <-- note the parentheses')
|
|
|
|
|
|
|
|
projections = {
|
|
|
|
'_id': 1,
|
|
|
|
'name': 1,
|
|
|
|
'node_types': 1,
|
|
|
|
# We don't need this here, but this way the wrapped function has access
|
|
|
|
# to the orignal URL passed to it.
|
|
|
|
'url': 1,
|
|
|
|
}
|
|
|
|
if extra_project_projections:
|
|
|
|
projections.update(extra_project_projections)
|
|
|
|
|
|
|
|
def decorator(wrapped):
|
|
|
|
@functools.wraps(wrapped)
|
|
|
|
def wrapper(project_url, *args, **kwargs):
|
|
|
|
api = pillar_api()
|
|
|
|
|
|
|
|
project = pillarsdk.Project.find_by_url(
|
|
|
|
project_url,
|
|
|
|
{'projection': projections},
|
|
|
|
api=api)
|
|
|
|
|
|
|
|
node_type = project.get_node_type('attract.task')
|
|
|
|
if not node_type:
|
|
|
|
log.warning('createProject url=%s does not have node type attract.task',
|
|
|
|
project_url)
|
|
|
|
return error_project_not_setup_for_attract()
|
|
|
|
|
|
|
|
return wrapped(project, *args, **kwargs)
|
|
|
|
|
|
|
|
return wrapper
|
|
|
|
|
|
|
|
return decorator
|