Introducing Attract assets, with a pretty similar workflow to shots.

This commit is contained in:
2016-11-09 13:48:01 +01:00
parent 88a4a2fab3
commit 43e6e5cef9
4 changed files with 78 additions and 33 deletions

View File

@@ -2,6 +2,6 @@ from .act import node_type_act
from .scene import node_type_scene
from .shot import node_type_shot
from .task import node_type_task
from .asset import node_type_asset
NODE_TYPES = (node_type_act, node_type_scene, node_type_shot, node_type_task)
NODE_TYPES = (node_type_act, node_type_scene, node_type_shot, node_type_task, node_type_asset)

View File

@@ -0,0 +1,26 @@
node_type_asset = {
'name': 'attract_asset',
'description': 'Attract Asset Node Type, for planning asset creation',
'dyn_schema': {
'status': {
'type': 'string',
'allowed': [
'on_hold',
'todo',
'in_progress',
'review',
'final'
],
'default': 'todo',
'required_after_creation': True,
},
'notes': {
'type': 'string',
'maxlength': 256,
},
},
'form_schema': {},
'parent': ['attract_scene', 'attract_shot']
}
task_types = ['modeling', 'rigging', 'material']

View File

@@ -20,7 +20,7 @@ from pillar import attrs_extra
from attract.node_types import node_type_shot, node_type_task
# From patch operation name to fields that operation may edit.
VALID_PATCH_FIELDS = {
VALID_SHOT_PATCH_FIELDS = {
u'from-blender': {
u'name',
u'picture',
@@ -37,7 +37,7 @@ VALID_PATCH_FIELDS = {
},
}
VALID_PATCH_OPERATIONS = {
VALID_SHOT_PATCH_OPERATIONS = {
u'from-blender', u'from-web', u'unlink', u'relink',
}
@@ -222,7 +222,7 @@ def patch_shot(node_id, patch):
raise wz_exceptions.NotFound('Node %s not found' % node_id)
op = patch['op']
if op in VALID_PATCH_FIELDS:
if op in VALID_SHOT_PATCH_FIELDS:
# Set the fields
for key, value in patch['$set'].items():
node_setattr(node, key, value)
@@ -253,15 +253,15 @@ def assert_is_valid_patch(patch):
except KeyError:
raise wz_exceptions.BadRequest("PATCH should have a key 'op' indicating the operation.")
if op not in VALID_PATCH_OPERATIONS:
valid_ops = u', '.join(sorted(VALID_PATCH_OPERATIONS))
if op not in VALID_SHOT_PATCH_OPERATIONS:
valid_ops = u', '.join(sorted(VALID_SHOT_PATCH_OPERATIONS))
raise wz_exceptions.BadRequest(u'Operation should be one of %s' % valid_ops)
if op not in VALID_PATCH_FIELDS:
if op not in VALID_SHOT_PATCH_FIELDS:
# Valid operation, and we don't have to check the fields.
return
allowed_fields = VALID_PATCH_FIELDS[op]
allowed_fields = VALID_SHOT_PATCH_FIELDS[op]
try:
fields = set(patch['$set'].keys())
except KeyError:

View File

@@ -1,9 +1,15 @@
# -*- encoding: utf-8 -*-
"""Hooks for shots & assets.
These two node types have very similar use, which is why they are covered by the same code.
"""
import itertools
import logging
from attract.node_types.shot import node_type_shot, human_readable_properties
from attract.node_types.asset import node_type_asset
from pillar.api.nodes import only_for_node_type_decorator
import pillar.api.activities
import pillar.api.utils.authentication
@@ -12,9 +18,16 @@ import pillar.web.jinja
log = logging.getLogger(__name__)
only_for_shot = only_for_node_type_decorator(node_type_shot['name'])
for_shot_asset = only_for_node_type_decorator(node_type_shot['name'],
node_type_asset['name'])
typenames = {
node_type_shot['name']: 'shot',
node_type_asset['name']: 'asset',
}
def register_shot_activity(shot, descr):
def register_shot_asset_activity(shot, descr):
user_id = pillar.api.utils.authentication.current_user_id()
pillar.api.activities.register_activity(
user_id,
@@ -26,20 +39,22 @@ def register_shot_activity(shot, descr):
)
@only_for_shot
def activity_after_replacing_shot(shot, original):
@for_shot_asset
def activity_after_replacing_shot_asset(shot_or_asset, original):
"""
Note: this is also used on PATCH, since our custom shot PATCH handler
performs a PUT-internal to run the patched node through Eve for
validation.
"""
typename = typenames[shot_or_asset['node_type']]
# Compare to original, and either mention the things that changed,
# or (if they are equal) don't log an activity at all.
changes = list(itertools.islice(pillar.api.utils.doc_diff(shot, original), 2))
changes = list(itertools.islice(pillar.api.utils.doc_diff(shot_or_asset, original), 2))
if not changes:
log.info('Not registering replacement of shot %s, as it is identical '
'in non-private fields.', shot['_id'])
log.info('Not registering replacement of %s %s, as it is identical '
'in non-private fields.', typename, shot_or_asset['_id'])
return
if len(changes) == 1:
@@ -52,31 +67,33 @@ def activity_after_replacing_shot(shot, original):
# Some key- and value-specific overrides
if val_shot is pillar.api.utils.DoesNotExist:
descr = 'removed "%s" from shot "%s"' % (human_key, shot['name'])
descr = 'removed "%s" from shot "%s"' % (human_key, shot_or_asset['name'])
if key == 'picture':
descr = 'changed the thumbnail of shot "%s"' % shot['name']
descr = 'changed the thumbnail of %s "%s"' % (typename, shot_or_asset['name'])
elif key == 'properties.status':
val_shot = pillar.web.jinja.format_undertitle(val_shot)
elif isinstance(val_shot, basestring) and len(val_shot) > 80:
val_shot = val_shot[:80] + u''
if descr is None:
descr = 'changed "%s" to "%s" in shot "%s"' %\
(human_key, val_shot, shot['name'])
descr = 'changed "%s" to "%s" in %s "%s"' % \
(human_key, val_shot, typename, shot_or_asset['name'])
else:
descr = 'edited shot "%s"' % shot['name']
descr = 'edited %s "%s"' % (typename, shot_or_asset['name'])
register_shot_activity(shot, descr)
register_shot_asset_activity(shot_or_asset, descr)
@only_for_shot
def activity_after_creating_shot(shot):
register_shot_activity(shot, 'created a new shot "%s"' % shot['name'])
@for_shot_asset
def activity_after_creating_shot_asset(shot_or_asset):
typename = typenames[shot_or_asset['node_type']]
register_shot_asset_activity(shot_or_asset, 'created a new %s "%s"' % (
typename, shot_or_asset['name']))
def activity_after_creating_shots(nodes):
def activity_after_creating_shots_assets(nodes):
for node in nodes:
activity_after_creating_shot(node)
activity_after_creating_shot_asset(node)
@only_for_shot
@@ -90,14 +107,16 @@ def nodes_set_default_used_in_edit(nodes):
set_default_used_in_edit(node)
@only_for_shot
def activity_after_deleting_shot(shot):
register_shot_activity(shot, 'deleted shot "%s"' % shot['name'])
@for_shot_asset
def activity_after_deleting_shot_asset(shot_or_asset):
typename = typenames[shot_or_asset['node_type']]
register_shot_asset_activity(shot_or_asset, 'deleted %s "%s"' % (
typename, shot_or_asset['name']))
def setup_app(app):
app.on_replaced_nodes += activity_after_replacing_shot
app.on_replaced_nodes += activity_after_replacing_shot_asset
app.on_insert_nodes += nodes_set_default_used_in_edit
app.on_inserted_nodes += activity_after_creating_shots
app.on_deleted_item_nodes += activity_after_deleting_shot
app.on_deleted_resource_nodes += activity_after_deleting_shot
app.on_inserted_nodes += activity_after_creating_shots_assets
app.on_deleted_item_nodes += activity_after_deleting_shot_asset
app.on_deleted_resource_nodes += activity_after_deleting_shot_asset