Set default picture on image asset and texture nodes.

This commit is contained in:
Sybren A. Stüvel 2016-06-30 11:55:36 +02:00
parent c3e9d43838
commit 387cee227a
4 changed files with 173 additions and 27 deletions

View File

@ -207,6 +207,43 @@ def before_returning_node_resource_permissions(response):
check_permissions('nodes', item, 'GET', append_allowed_methods=True)
def node_set_default_picture(node, original=None):
"""Uses the image of an image asset or colour map of texture node as picture."""
if node.get('picture'):
log.debug('Node %s already has a picture, not overriding', node.get('_id'))
return
node_type = node.get('node_type')
props = node.get('properties', {})
content = props.get('content_type')
if node_type == 'asset' and content == 'image':
image_file_id = props.get('file')
elif node_type == 'texture':
# Find the colour map, defaulting to the first image map available.
image_file_id = None
for image in props.get('files', []):
if image_file_id is None or image.get('map_type') == u'color':
image_file_id = image.get('file')
else:
log.debug('Not setting default picture on node type %s content type %s',
node_type, content)
return
if image_file_id is None:
log.debug('Nothing to set the picture to.')
return
log.debug('Setting default picture for node %s to %s', node.get('_id'), image_file_id)
node['picture'] = image_file_id
def nodes_set_default_picture(nodes):
for node in nodes:
node_set_default_picture(node)
def setup_app(app):
# Permission hooks
app.on_fetched_item_nodes += before_returning_node_permissions
@ -214,9 +251,13 @@ def setup_app(app):
app.on_fetched_item_nodes += item_parse_attachments
app.on_fetched_resource_nodes += resource_parse_attachments
app.on_replace_nodes += before_replacing_node
app.on_replace_nodes += deduct_content_type
app.on_replace_nodes += node_set_default_picture
app.on_replaced_nodes += after_replacing_node
app.on_insert_nodes += before_inserting_nodes
app.on_insert_nodes += nodes_deduct_content_type
app.on_insert_nodes += nodes_set_default_picture
app.on_inserted_nodes += after_inserting_nodes

View File

@ -106,7 +106,8 @@ class AbstractPillarTest(TestMinimal):
return found['_id'], found
def create_user(self, user_id='cafef00dc379cf10c4aaceaf', roles=('subscriber',)):
def create_user(self, user_id='cafef00dc379cf10c4aaceaf', roles=('subscriber',),
groups=None):
from application.utils.authentication import make_unique_username
with self.app.test_request_context():
@ -118,7 +119,7 @@ class AbstractPillarTest(TestMinimal):
'_updated': datetime.datetime(2016, 4, 15, 13, 15, 11, tzinfo=tz_util.utc),
'_created': datetime.datetime(2016, 4, 15, 13, 15, 11, tzinfo=tz_util.utc),
'username': make_unique_username('tester'),
'groups': [],
'groups': groups or [],
'roles': list(roles),
'settings': {'email_communications': 1},
'auth': [{'token': '',

View File

@ -2,6 +2,8 @@ import datetime
from bson import tz_util, ObjectId
EXAMPLE_ADMIN_GROUP_ID = ObjectId('5596e975ea893b269af85c0e')
EXAMPLE_PROJECT_ID = ObjectId('5672beecc0261b2005ed1a33')
EXAMPLE_FILE = {u'_id': ObjectId('5672e2c1c379cf0007b31995'),
@ -52,7 +54,7 @@ EXAMPLE_PROJECT = {
u'form_schema': {u'order': {}, u'status': {}, u'url': {}},
u'name': u'group_texture',
u'parent': [u'group_texture', u'project'],
u'permissions': {u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID,
u'methods': [u'GET', u'PUT', u'POST']},
{u'group': ObjectId('5596e975ea893b269af85c0f'),
u'methods': [u'GET']},
@ -71,7 +73,7 @@ EXAMPLE_PROJECT = {
u'form_schema': {u'notes': {}, u'order': {}, u'status': {}, u'url': {}},
u'name': u'group',
u'parent': [u'group', u'project'],
u'permissions': {u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID,
u'methods': [u'GET', u'PUT', u'POST']},
{u'group': ObjectId('5596e975ea893b269af85c0f'),
u'methods': [u'GET']},
@ -119,7 +121,7 @@ EXAMPLE_PROJECT = {
u'tags': {}},
u'name': u'asset',
u'parent': [u'group'],
u'permissions': {u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID,
u'methods': [u'GET', u'PUT', u'POST']},
{u'group': ObjectId('5596e975ea893b269af85c0f'),
u'methods': [u'DELETE', u'GET']},
@ -133,7 +135,7 @@ EXAMPLE_PROJECT = {
u'form_schema': {u'backend': {}, u'subdir': {}},
u'name': u'storage',
u'parent': [u'group', u'project'],
u'permissions': {u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID,
u'methods': [u'GET', u'PUT', u'POST']},
{u'group': ObjectId('5596e975ea893b269af85c0f'),
u'methods': [u'GET']},
@ -164,7 +166,7 @@ EXAMPLE_PROJECT = {
u'status': {}},
u'name': u'comment',
u'parent': [u'asset', u'comment'],
u'permissions': {u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID,
u'methods': [u'GET', u'PUT', u'POST']},
{u'group': ObjectId('5596e975ea893b269af85c0f'),
u'methods': [u'GET', u'POST']},
@ -179,7 +181,7 @@ EXAMPLE_PROJECT = {
u'form_schema': {u'categories': {}, u'template': {}},
u'name': u'blog',
u'parent': [u'project'],
u'permissions': {u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID,
u'methods': [u'GET', u'PUT', u'POST']}],
u'users': [],
u'world': [u'GET']}},
@ -218,7 +220,7 @@ EXAMPLE_PROJECT = {
u'url': {}},
u'name': u'post',
u'parent': [u'blog'],
u'permissions': {u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID,
u'methods': [u'GET', u'PUT', u'POST']}],
u'users': [],
u'world': [u'GET']}},
@ -231,10 +233,10 @@ EXAMPLE_PROJECT = {
u'resource': u'files'},
u'type': u'objectid'},
u'is_tileable': {u'type': u'boolean'},
u'map_type': {u'allowed': [u'spec',
u'map_type': {u'allowed': [u'color',
u'specular',
u'bump',
u'nor',
u'col',
u'normal',
u'translucency',
u'emission',
u'alpha'],
@ -245,7 +247,7 @@ EXAMPLE_PROJECT = {
u'is_tileable': {u'type': u'boolean'},
u'order': {u'type': u'integer'},
u'resolution': {u'type': u'string'},
u'stat_ensure_file_existsus': {u'allowed': [u'published',
u'status': {u'allowed': [u'published',
u'pending',
u'processing'],
u'type': u'string'},
@ -262,7 +264,7 @@ EXAMPLE_PROJECT = {
u'tags': {}},
u'name': u'texture',
u'parent': [u'group'],
u'permissions': {u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID,
u'methods': [u'GET', u'PUT', u'POST']},
{u'group': ObjectId('5596e975ea893b269af85c0f'),
u'methods': [u'GET']},
@ -275,7 +277,7 @@ EXAMPLE_PROJECT = {
u'nodes_latest': [],
u'organization': ObjectId('55a99fb43004867fb9934f01'),
u'owners': {u'groups': [], u'users': []},
u'permissions': {u'groups': [{u'group': ObjectId('5596e975ea893b269af85c0e'),
u'permissions': {u'groups': [{u'group': EXAMPLE_ADMIN_GROUP_ID,
u'methods': [u'GET', u'POST', u'PUT']}],
u'users': [],
u'world': [u'GET']},

View File

@ -7,21 +7,22 @@ from flask import g
from werkzeug.exceptions import UnprocessableEntity
from common_test_class import AbstractPillarTest
import common_test_data as ctd
class NodeContentTypeTest(AbstractPillarTest):
def test_node_types(self):
"""Tests that the node's content_type properties is updated correctly from its file."""
def mkfile(file_id, content_type):
def mkfile(self, file_id, content_type):
file_id, _ = self.ensure_file_exists(file_overrides={
'_id': ObjectId(file_id),
'content_type': content_type})
return file_id
file_id_image = mkfile('cafef00dcafef00dcafef00d', 'image/jpeg')
file_id_video = mkfile('cafef00dcafef00dcafecafe', 'video/matroska')
file_id_blend = mkfile('cafef00dcafef00ddeadbeef', 'application/x-blender')
def test_node_types(self):
"""Tests that the node's content_type properties is updated correctly from its file."""
file_id_image = self.mkfile('cafef00dcafef00dcafef00d', 'image/jpeg')
file_id_video = self.mkfile('cafef00dcafef00dcafecafe', 'video/matroska')
file_id_blend = self.mkfile('cafef00dcafef00ddeadbeef', 'application/x-blender')
user_id = self.create_user()
project_id, _ = self.ensure_project_exists()
@ -81,3 +82,104 @@ class NodeContentTypeTest(AbstractPillarTest):
data = json.loads(resp.data)
self.assertEqual([u'GET'], data['allowed_methods'])
def test_default_picture_image_asset(self):
from application.utils import dumps
file_id_image = self.mkfile(24 * 'a', 'image/jpeg')
file_id_video = self.mkfile(24 * 'b', 'video/matroska')
file_id_image_spec = self.mkfile(24 * 'c', 'image/jpeg')
file_id_image_bump = self.mkfile(24 * 'd', 'image/jpeg')
user_id = self.create_user(groups=[ctd.EXAMPLE_ADMIN_GROUP_ID])
self.create_valid_auth_token(user_id, 'token')
project_id, _ = self.ensure_project_exists()
def test_for(node, expected_picture_id):
# Create the node
resp = self.client.post('/nodes',
data=dumps(node),
headers={'Authorization': self.make_header('token'),
'Content-Type': 'application/json'})
self.assertEqual(resp.status_code, 201, resp.data)
node_id = json.loads(resp.data)['_id']
# Test that the node has the attached file as picture.
resp = self.client.get('/nodes/%s' % node_id,
headers={'Authorization': self.make_header('token')})
self.assertEqual(resp.status_code, 200, resp.data)
json_node = json.loads(resp.data)
if expected_picture_id:
self.assertEqual(ObjectId(json_node['picture']), expected_picture_id)
else:
self.assertNotIn('picture', json_node)
# Image asset node
test_for({'description': '',
'project': project_id,
'node_type': 'asset',
'user': user_id,
'properties': {'status': 'published',
'tags': [],
'order': 0,
'categories': '',
'file': file_id_image},
'name': 'Image asset'},
file_id_image)
# Video asset node, should not get default picture
test_for({'description': '',
'project': project_id,
'node_type': 'asset',
'user': user_id,
'properties': {'status': 'published',
'tags': [],
'order': 0,
'categories': '',
'file': file_id_video},
'name': 'Video asset'},
None)
# Texture node, should default to colour map.
test_for({'description': '',
'project': project_id,
'node_type': 'texture',
'user': user_id,
'properties': {'status': 'published',
'tags': [],
'order': 0,
'categories': '',
'files': [
{'file': file_id_image_bump, 'map_type': 'bump'},
{'file': file_id_image_spec, 'map_type': 'specular'},
{'file': file_id_image, 'map_type': 'color'},
],
'is_tileable': False,
'aspect_ratio': 0.0,
'is_landscape': False,
'resolution': '',
},
'name': 'Texture node'},
file_id_image)
# Texture node, should default to first image if there is no colour map.
test_for({'description': '',
'project': project_id,
'node_type': 'texture',
'user': user_id,
'properties': {'status': 'published',
'tags': [],
'order': 0,
'categories': '',
'files': [
{'file': file_id_image_bump, 'map_type': 'bump'},
{'file': file_id_image_spec, 'map_type': 'specular'},
],
'is_tileable': False,
'aspect_ratio': 0.0,
'is_landscape': False,
'resolution': '',
},
'name': 'Texture node'},
file_id_image_bump)