Introducing Attract assets, with a pretty similar workflow to shots.
This commit is contained in:
@@ -2,6 +2,6 @@ from .act import node_type_act
|
|||||||
from .scene import node_type_scene
|
from .scene import node_type_scene
|
||||||
from .shot import node_type_shot
|
from .shot import node_type_shot
|
||||||
from .task import node_type_task
|
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)
|
||||||
|
|
||||||
|
26
attract/node_types/asset.py
Normal file
26
attract/node_types/asset.py
Normal 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']
|
@@ -20,7 +20,7 @@ from pillar import attrs_extra
|
|||||||
from attract.node_types import node_type_shot, node_type_task
|
from attract.node_types import node_type_shot, node_type_task
|
||||||
|
|
||||||
# From patch operation name to fields that operation may edit.
|
# From patch operation name to fields that operation may edit.
|
||||||
VALID_PATCH_FIELDS = {
|
VALID_SHOT_PATCH_FIELDS = {
|
||||||
u'from-blender': {
|
u'from-blender': {
|
||||||
u'name',
|
u'name',
|
||||||
u'picture',
|
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',
|
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)
|
raise wz_exceptions.NotFound('Node %s not found' % node_id)
|
||||||
|
|
||||||
op = patch['op']
|
op = patch['op']
|
||||||
if op in VALID_PATCH_FIELDS:
|
if op in VALID_SHOT_PATCH_FIELDS:
|
||||||
# Set the fields
|
# Set the fields
|
||||||
for key, value in patch['$set'].items():
|
for key, value in patch['$set'].items():
|
||||||
node_setattr(node, key, value)
|
node_setattr(node, key, value)
|
||||||
@@ -253,15 +253,15 @@ def assert_is_valid_patch(patch):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
raise wz_exceptions.BadRequest("PATCH should have a key 'op' indicating the operation.")
|
raise wz_exceptions.BadRequest("PATCH should have a key 'op' indicating the operation.")
|
||||||
|
|
||||||
if op not in VALID_PATCH_OPERATIONS:
|
if op not in VALID_SHOT_PATCH_OPERATIONS:
|
||||||
valid_ops = u', '.join(sorted(VALID_PATCH_OPERATIONS))
|
valid_ops = u', '.join(sorted(VALID_SHOT_PATCH_OPERATIONS))
|
||||||
raise wz_exceptions.BadRequest(u'Operation should be one of %s' % valid_ops)
|
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.
|
# Valid operation, and we don't have to check the fields.
|
||||||
return
|
return
|
||||||
|
|
||||||
allowed_fields = VALID_PATCH_FIELDS[op]
|
allowed_fields = VALID_SHOT_PATCH_FIELDS[op]
|
||||||
try:
|
try:
|
||||||
fields = set(patch['$set'].keys())
|
fields = set(patch['$set'].keys())
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@@ -1,9 +1,15 @@
|
|||||||
# -*- encoding: utf-8 -*-
|
# -*- 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 itertools
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from attract.node_types.shot import node_type_shot, human_readable_properties
|
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
|
from pillar.api.nodes import only_for_node_type_decorator
|
||||||
import pillar.api.activities
|
import pillar.api.activities
|
||||||
import pillar.api.utils.authentication
|
import pillar.api.utils.authentication
|
||||||
@@ -12,9 +18,16 @@ import pillar.web.jinja
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
only_for_shot = only_for_node_type_decorator(node_type_shot['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()
|
user_id = pillar.api.utils.authentication.current_user_id()
|
||||||
pillar.api.activities.register_activity(
|
pillar.api.activities.register_activity(
|
||||||
user_id,
|
user_id,
|
||||||
@@ -26,20 +39,22 @@ def register_shot_activity(shot, descr):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@only_for_shot
|
@for_shot_asset
|
||||||
def activity_after_replacing_shot(shot, original):
|
def activity_after_replacing_shot_asset(shot_or_asset, original):
|
||||||
"""
|
"""
|
||||||
Note: this is also used on PATCH, since our custom shot PATCH handler
|
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
|
performs a PUT-internal to run the patched node through Eve for
|
||||||
validation.
|
validation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
typename = typenames[shot_or_asset['node_type']]
|
||||||
|
|
||||||
# Compare to original, and either mention the things that changed,
|
# Compare to original, and either mention the things that changed,
|
||||||
# or (if they are equal) don't log an activity at all.
|
# 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:
|
if not changes:
|
||||||
log.info('Not registering replacement of shot %s, as it is identical '
|
log.info('Not registering replacement of %s %s, as it is identical '
|
||||||
'in non-private fields.', shot['_id'])
|
'in non-private fields.', typename, shot_or_asset['_id'])
|
||||||
return
|
return
|
||||||
|
|
||||||
if len(changes) == 1:
|
if len(changes) == 1:
|
||||||
@@ -52,31 +67,33 @@ def activity_after_replacing_shot(shot, original):
|
|||||||
|
|
||||||
# Some key- and value-specific overrides
|
# Some key- and value-specific overrides
|
||||||
if val_shot is pillar.api.utils.DoesNotExist:
|
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':
|
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':
|
elif key == 'properties.status':
|
||||||
val_shot = pillar.web.jinja.format_undertitle(val_shot)
|
val_shot = pillar.web.jinja.format_undertitle(val_shot)
|
||||||
elif isinstance(val_shot, basestring) and len(val_shot) > 80:
|
elif isinstance(val_shot, basestring) and len(val_shot) > 80:
|
||||||
val_shot = val_shot[:80] + u'…'
|
val_shot = val_shot[:80] + u'…'
|
||||||
|
|
||||||
if descr is None:
|
if descr is None:
|
||||||
descr = 'changed "%s" to "%s" in shot "%s"' %\
|
descr = 'changed "%s" to "%s" in %s "%s"' % \
|
||||||
(human_key, val_shot, shot['name'])
|
(human_key, val_shot, typename, shot_or_asset['name'])
|
||||||
else:
|
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
|
@for_shot_asset
|
||||||
def activity_after_creating_shot(shot):
|
def activity_after_creating_shot_asset(shot_or_asset):
|
||||||
register_shot_activity(shot, 'created a new shot "%s"' % shot['name'])
|
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:
|
for node in nodes:
|
||||||
activity_after_creating_shot(node)
|
activity_after_creating_shot_asset(node)
|
||||||
|
|
||||||
|
|
||||||
@only_for_shot
|
@only_for_shot
|
||||||
@@ -90,14 +107,16 @@ def nodes_set_default_used_in_edit(nodes):
|
|||||||
set_default_used_in_edit(node)
|
set_default_used_in_edit(node)
|
||||||
|
|
||||||
|
|
||||||
@only_for_shot
|
@for_shot_asset
|
||||||
def activity_after_deleting_shot(shot):
|
def activity_after_deleting_shot_asset(shot_or_asset):
|
||||||
register_shot_activity(shot, 'deleted shot "%s"' % shot['name'])
|
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):
|
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_insert_nodes += nodes_set_default_used_in_edit
|
||||||
app.on_inserted_nodes += activity_after_creating_shots
|
app.on_inserted_nodes += activity_after_creating_shots_assets
|
||||||
app.on_deleted_item_nodes += activity_after_deleting_shot
|
app.on_deleted_item_nodes += activity_after_deleting_shot_asset
|
||||||
app.on_deleted_resource_nodes += activity_after_deleting_shot
|
app.on_deleted_resource_nodes += activity_after_deleting_shot_asset
|
||||||
|
Reference in New Issue
Block a user