From 88c20bf341f94e72bb61d0033a567648a7f1df05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 30 Sep 2016 12:50:13 +0200 Subject: [PATCH] Allow creation of shots without status. --- attract/__init__.py | 3 ++- attract/eve_hooks.py | 54 ++++++++++++++++++++++++++++++++++++++ attract/node_types/shot.py | 1 + tests/test_shots.py | 43 ++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 attract/eve_hooks.py diff --git a/attract/__init__.py b/attract/__init__.py index 51b6c7f..aec4f44 100644 --- a/attract/__init__.py +++ b/attract/__init__.py @@ -80,10 +80,11 @@ class AttractExtension(PillarExtension): def setup_app(self, app): """Connects Blinker signals.""" - from . import subversion, tasks + from . import subversion, tasks, eve_hooks subversion.task_logged.connect(self.task_manager.task_logged_in_svn) tasks.setup_app(app) + eve_hooks.setup_app(app) def _get_current_attract(): diff --git a/attract/eve_hooks.py b/attract/eve_hooks.py new file mode 100644 index 0000000..635ca2c --- /dev/null +++ b/attract/eve_hooks.py @@ -0,0 +1,54 @@ +"""Attract-wide Eve hooks.""" + +import logging + +import flask + +log = logging.getLogger(__name__) + + +def set_default_status(node): + """Sets the default status based on the project node type dynamic schema.""" + + # FIXME: After upgrading to Eve 0.6.5 (which hopefully uses Cerberus 1.0+) this + # should be moved to Pillar's ValidateCustomFields class. The new Cerberus should + # (according to the docs, at least) be able to do normalisation of data based on + # the schema. So at that point in the code, the node property and its schema are + # already known, and we won't have to query for it again here. + + status = node.get('properties', {}).get('status', None) + if status: + log.debug('Node already has status=%r, not setting default', status) + return + + proj_id = node.get('project', None) + node_type_name = node.get('node_type', None) + if not proj_id or not node_type_name: + log.debug('Node %s has no project or node type, not setting status', node['_id']) + return + + proj_coll = flask.current_app.db()['projects'] + lookup = {'_id': proj_id, + 'node_types': {'$elemMatch': {'name': node_type_name}}} + + project = proj_coll.find_one(lookup, { + 'node_types.$': 1, + }) + + schema = project['node_types'][0]['dyn_schema'] + default_status = schema['status'].get('default', None) + if not default_status: + log.debug('Node type %s of project %s has no default value for status property', + node_type_name, proj_id) + return + + node.setdefault('properties', {})['status'] = default_status + + +def set_default_status_nodes(nodes): + for node in nodes: + set_default_status(node) + + +def setup_app(app): + app.on_insert_nodes += set_default_status_nodes diff --git a/attract/node_types/shot.py b/attract/node_types/shot.py index 09fc878..8a18f0b 100644 --- a/attract/node_types/shot.py +++ b/attract/node_types/shot.py @@ -24,6 +24,7 @@ node_type_shot = { 'final' ], 'default': 'todo', + 'required_after_creation': True, }, 'notes': { 'type': 'string', diff --git a/tests/test_shots.py b/tests/test_shots.py index 7a26343..782e0ba 100644 --- a/tests/test_shots.py +++ b/tests/test_shots.py @@ -261,3 +261,46 @@ class PatchShotTest(AbstractShotTest): } } self.patch(url, json=patch, auth_token='other', expected_status=403) + + +class RequiredAfterCreationTest(AbstractShotTest): + """ + This tests Pillar stuff, but requires attract_shot since that's what the + required_after_creation=False was created for. + + Placing the test here was easier than creating a node type in Pillar + specifically for this test case. Once we use that validator in Pillar + itself, we can move this test there too. + """ + + def test_create_shot(self): + from attract.node_types import node_type_shot + + self.user_id = self.create_project_admin(self.project) + self.create_valid_auth_token(self.user_id, 'token') + + node_type_name = node_type_shot['name'] + + shot = {'name': u'test shot', + 'description': u'', + 'properties': {u'trim_start_in_frames': 0, + u'duration_in_edit_in_frames': 1, + u'cut_in_timeline_in_frames': 0}, + 'node_type': node_type_name, + 'project': unicode(self.proj_id), + 'user': unicode(self.user_id)} + + resp = self.post('/api/nodes', json=shot, + auth_token='token', expected_status=201) + info = resp.json() + + resp = self.get('/api/nodes/%(_id)s' % info, auth_token='token') + json_shot = resp.json() + + self.assertEqual(node_type_shot['dyn_schema']['status']['default'], + json_shot['properties']['status']) + + return json_shot + + # TODO: should test editing a shot as well, but I had issues with the PillarSDK + # not handling deleting of properties.