Page MenuHome
No OneTemporary

File Metadata

Thu, Apr 9, 8:35 PM

import functools
import logging
from flask import Blueprint, render_template
import flask_login
from pillar.api.utils import jsonify
from pillar.web.system_util import pillar_api
import pillarsdk
from attract import current_attract
blueprint = Blueprint('attract', __name__)
log = logging.getLogger(__name__)
def index():
user = flask_login.current_user
if user.is_authenticated:
tasks = current_attract.task_manager.tasks_for_user(user.objectid)
tasks = None
projects = current_attract.attract_projects()
projs_with_summaries = [
(proj, current_attract.shot_manager.shot_status_summary(proj['_id']))
for proj in projects['_items']
return render_template('attract/index.html',
def error_project_not_setup_for_attract():
return render_template('attract/errors/project_not_setup.html')
def attract_project_view(extra_project_projections=None, extension_props=False):
"""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
:param extension_props: whether extension properties should be included. Includes them
in the projections, and verifies that they are there.
:type extension_props: bool
from .node_types.task import node_type_task
from . import EXTENSION_NAME
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:
if extension_props:
projections['extension_props.%s' % EXTENSION_NAME] = 1
def decorator(wrapped):
def wrapper(project_url, *args, **kwargs):
if isinstance(project_url, pillarsdk.Resource):
# This is already a resource, so this call probably is from one
# view to another. Assume the caller knows what he's doing and
# just pass everything along.
return wrapped(project_url, *args, **kwargs)
api = pillar_api()
project = pillarsdk.Project.find_by_url(
{'projection': projections},
node_type_name = node_type_task['name']
node_type = project.get_node_type(node_type_name)
if not node_type:
log.warning('createProject url=%s does not have node type %r',
project_url, node_type_name)
return error_project_not_setup_for_attract()
if extension_props:
pprops = project.extension_props.attract
except AttributeError:
log.warning("attract_project_view(%s): Project url=%r doesn't have any"
" extension properties.", wrapped, project_url)
if log.isEnabledFor(logging.DEBUG):
import pprint
log.debug('Project: %s', pprint.pformat(project.to_dict()))
return error_project_not_setup_for_attract()
if pprops is None:
log.warning("attract_project_view(%s): Project url=%r doesn't have Attract"
" extension properties.", wrapped, project_url)
return error_project_not_setup_for_attract()
return wrapped(project, pprops, *args, **kwargs)
return wrapped(project, *args, **kwargs)
return wrapper
return decorator
def subversion_kick(project, attract_props):
from . import subversion
svn_server_url = attract_props.svn_url # 'svn://localhost/agent327''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)
return jsonify({
'previous_last_seen_revision': last_seen_revision,
'last_seen_revision': observer.last_seen_revision,
def project_index(project, attract_props):
return render_template('attract/project.html',

Event Timeline