Templates & Flask end-points for managing assets.

This commit is contained in:
2016-11-09 14:57:46 +01:00
parent 3346bb1364
commit 0e170464e6
8 changed files with 507 additions and 65 deletions

View File

@@ -64,14 +64,16 @@ class AttractExtension(PillarExtension):
from . import routes
import attract.tasks.routes
import attract.shots_and_assets.routes
import attract.shots_and_assets.routes_assets
import attract.shots_and_assets.routes_shots
import attract.subversion.routes
return [
routes.blueprint,
attract.tasks.routes.blueprint,
attract.tasks.routes.perproject_blueprint,
attract.shots_and_assets.routes.perproject_blueprint,
attract.shots_and_assets.routes_assets.perproject_blueprint,
attract.shots_and_assets.routes_shots.perproject_blueprint,
attract.subversion.routes.blueprint,
attract.subversion.routes.api_blueprint,
]

View File

@@ -0,0 +1,97 @@
import logging
import flask_login
from flask import Blueprint, render_template, request
import flask
import werkzeug.exceptions as wz_exceptions
import pillarsdk
import pillar.api.utils
from pillar.web.system_util import pillar_api
from attract.routes import attract_project_view
from attract.node_types.asset import node_type_asset, task_types
from attract import current_attract, ROLES_REQUIRED_TO_VIEW_ITEMS
from pillar.web.utils import get_file
from . import routes_common
perproject_blueprint = Blueprint('attract.assets.perproject', __name__,
url_prefix='/<project_url>/assets')
log = logging.getLogger(__name__)
@perproject_blueprint.route('/', endpoint='index')
@perproject_blueprint.route('/with-task/<task_id>', endpoint='with_task')
@attract_project_view(extension_props=True)
def for_project(project, attract_props, task_id=None, asset_id=None):
assets, tasks_for_assets, task_types_for_template = routes_common.for_project(
node_type_asset['name'],
task_types,
project, attract_props, task_id, asset_id)
return render_template('attract/assets/for_project.html',
assets=assets,
tasks_for_assets=tasks_for_assets,
task_types=task_types_for_template,
open_task_id=task_id,
open_asset_id=asset_id,
project=project,
attract_props=attract_props)
@perproject_blueprint.route('/<asset_id>')
@attract_project_view(extension_props=True)
def view_asset(project, attract_props, asset_id):
if not request.is_xhr:
return for_project(project, attract_props, asset_id=asset_id)
asset, node_type = routes_common.view_node(project, asset_id, node_type_asset['name'])
return render_template('attract/assets/view_asset_embed.html',
asset=asset,
project=project,
asset_node_type=node_type,
attract_props=attract_props)
@perproject_blueprint.route('/<asset_id>', methods=['POST'])
@attract_project_view()
def save(project, asset_id):
log.info('Saving asset %s', asset_id)
log.debug('Form data: %s', request.form)
asset_dict = request.form.to_dict()
current_attract.shot_manager.edit_asset(asset_id, **asset_dict)
# Return the patched node in all its glory.
api = pillar_api()
asset = pillarsdk.Node.find(asset_id, api=api)
return pillar.api.utils.jsonify(asset.to_dict())
@perproject_blueprint.route('/create', methods=['POST'])
@attract_project_view()
def create_asset(project):
asset = current_attract.shot_manager.create_asset(project)
resp = flask.make_response()
resp.headers['Location'] = flask.url_for('.view_asset',
project_url=project['url'],
asset_id=asset['_id'])
resp.status_code = 201
return flask.make_response(flask.jsonify({'asset_id': asset['_id']}), 201)
@perproject_blueprint.route('/<asset_id>/activities')
@attract_project_view()
def activities(project, asset_id):
if not request.is_xhr:
return flask.redirect(flask.url_for('.view_asset',
project_url=project.url,
asset_id=asset_id))
acts = current_attract.activities_for_node(asset_id)
# NOTE: this uses the 'shots' template, because it has everything we ever wanted.
return flask.render_template('attract/shots/view_activities_embed.html',
activities=acts)

View File

@@ -0,0 +1,69 @@
import logging
import flask
import flask_login
import werkzeug.exceptions as wz_exceptions
import pillarsdk
from pillar.web.system_util import pillar_api
from pillar.web.utils import get_file
from attract import current_attract, ROLES_REQUIRED_TO_VIEW_ITEMS
log = logging.getLogger(__name__)
def for_project(node_type_name, task_types_for_nt, project, attract_props,
task_id=None, shot_or_asset_id=None):
"""Common view code for assets and shots /attract/<project_url>/{assets,shots}"""
api = pillar_api()
found = pillarsdk.Node.all({
'where': {
'project': project['_id'],
'node_type': node_type_name,
},
'sort': [
('properties.cut_in_timeline_in_frames', 1),
]
}, api=api)
nodes = found['_items']
thumb_placeholder = flask.url_for('static_attract', filename='assets/img/placeholder.jpg')
for node in nodes:
picture = get_file(node.picture, api=api)
if picture:
node._thumbnail = next((var.link for var in picture.variations
if var.size == 't'), thumb_placeholder)
else:
node._thumbnail = thumb_placeholder
# The placeholder can be shown quite small, but otherwise the aspect ratio of
# the actual thumbnail should be taken into account. Since it's different for
# each project, we can't hard-code a proper height.
node._thumbnail_height = '30px' if node._thumbnail is thumb_placeholder else 'auto'
tasks_for_nodes = current_attract.shot_manager.tasks_for_nodes(nodes, task_types_for_nt)
# Append the task type onto which 'other' tasks are mapped.
task_types_for_template = task_types_for_nt + [None]
return nodes, tasks_for_nodes, task_types_for_template
def view_node(project, node_id, node_type_name):
"""Returns the node if the user has access.
Uses attract.ROLES_REQUIRED_TO_VIEW_ITEMS to check permissions.
"""
# asset list is public, asset details are not.
if not flask_login.current_user.has_role(*ROLES_REQUIRED_TO_VIEW_ITEMS):
raise wz_exceptions.Forbidden()
api = pillar_api()
node = pillarsdk.Node.find(node_id, api=api)
node_type = project.get_node_type(node_type_name)
return node, node_type

View File

@@ -10,10 +10,12 @@ import pillar.api.utils
from pillar.web.system_util import pillar_api
from attract.routes import attract_project_view
from attract.node_types.shot import node_type_shot
from attract.node_types.shot import node_type_shot, task_types
from attract import current_attract, ROLES_REQUIRED_TO_VIEW_ITEMS
from pillar.web.utils import get_file
from . import routes_common
perproject_blueprint = Blueprint('attract.shots.perproject', __name__,
url_prefix='/<project_url>/shots')
log = logging.getLogger(__name__)
@@ -23,40 +25,10 @@ log = logging.getLogger(__name__)
@perproject_blueprint.route('/with-task/<task_id>', endpoint='with_task')
@attract_project_view(extension_props=True)
def for_project(project, attract_props, task_id=None, shot_id=None):
api = pillar_api()
found = pillarsdk.Node.all({
'where': {
'project': project['_id'],
'node_type': node_type_shot['name'],
},
'sort': [
('properties.cut_in_timeline_in_frames', 1),
]
}, api=api)
shots = found['_items']
thumb_placeholder = flask.url_for('static_attract', filename='assets/img/placeholder.jpg')
for shot in shots:
picture = get_file(shot.picture, api=api)
if picture:
shot._thumbnail = next((var.link for var in picture.variations
if var.size == 't'), thumb_placeholder)
else:
shot._thumbnail = thumb_placeholder
# The placeholder can be shown quite small, but otherwise the aspect ratio of
# the actual thumbnail should be taken into account. Since it's different for
# each project, we can't hard-code a proper height.
shot._thumbnail_height = '30px' if shot._thumbnail is thumb_placeholder else 'auto'
tasks_for_shots = current_attract.shot_manager.tasks_for_nodes(
shots,
attract_props.task_types.attract_shot,
)
# Append the task type onto which 'other' tasks are mapped.
task_types = attract_props.task_types.attract_shot + [None]
shots, tasks_for_shots, task_types_for_template = routes_common.for_project(
node_type_shot['name'],
task_types,
project, attract_props, task_id, shot_id)
# Some aggregated stats
stats = {
@@ -69,7 +41,7 @@ def for_project(project, attract_props, task_id=None, shot_id=None):
return render_template('attract/shots/for_project.html',
shots=shots,
tasks_for_shots=tasks_for_shots,
task_types=task_types,
task_types=task_types_for_template,
open_task_id=task_id,
open_shot_id=shot_id,
project=project,
@@ -83,14 +55,7 @@ def view_shot(project, attract_props, shot_id):
if not request.is_xhr:
return for_project(project, attract_props, shot_id=shot_id)
# Shot list is public, shot details are not.
if not flask_login.current_user.has_role(*ROLES_REQUIRED_TO_VIEW_ITEMS):
raise wz_exceptions.Forbidden()
api = pillar_api()
shot = pillarsdk.Node.find(shot_id, api=api)
node_type = project.get_node_type(node_type_shot['name'])
shot, node_type = routes_common.view_node(project, shot_id, node_type_shot['name'])
return render_template('attract/shots/view_shot_embed.html',
shot=shot,