From c40fdb378c2b3550a95009f08e97bf0adef6c093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 3 Nov 2016 18:08:51 +0100 Subject: [PATCH] Allow Blender to mark shots as used/not used in edit. NOTE: requires schema change, so be careful. --- attract/node_types/shot.py | 4 ++++ attract/shots/__init__.py | 38 +++++++++++++++++++++++++++++--------- tests/test_shots.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/attract/node_types/shot.py b/attract/node_types/shot.py index 3b3aace..a70af49 100644 --- a/attract/node_types/shot.py +++ b/attract/node_types/shot.py @@ -30,6 +30,10 @@ node_type_shot = { 'type': 'string', 'maxlength': 256, }, + 'used_in_edit': { + 'type': 'boolean', + 'default': True, + }, }, 'form_schema': {}, 'parent': ['scene'] diff --git a/attract/shots/__init__.py b/attract/shots/__init__.py index d6d9b83..2d2e0dc 100644 --- a/attract/shots/__init__.py +++ b/attract/shots/__init__.py @@ -19,7 +19,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_OPERATIONS = { +VALID_PATCH_FIELDS = { u'from-blender': { u'name', u'picture', @@ -34,6 +34,11 @@ VALID_PATCH_OPERATIONS = { u'description', }, } + +VALID_PATCH_OPERATIONS = { + u'from-blender', u'from-web', u'unlink', u'relink', +} + log = logging.getLogger(__name__) @@ -220,6 +225,7 @@ def node_setattr(node, key, value): @register_patch_handler(node_type_shot['name']) def patch_shot(node_id, patch): assert_is_valid_patch(patch) + log.info('Patching node %s: %s', node_id, patch) # Find the full node, so we can PUT it through Eve for validation. nodes_coll = flask.current_app.data.driver.db['nodes'] @@ -230,16 +236,27 @@ def patch_shot(node_id, patch): log.warning('How can node %s not be found?', node_id) raise wz_exceptions.NotFound('Node %s not found' % node_id) - # Set the fields - log.info('Patching node %s: %s', node_id, patch) - for key, value in patch['$set'].items(): - node_setattr(node, key, value) + op = patch['op'] + if op in VALID_PATCH_FIELDS: + # Set the fields + for key, value in patch['$set'].items(): + node_setattr(node, key, value) + else: + # Remaining operations are for marking as 'in use' or 'not in use'. + used_in_edit = { + u'unlink': False, + u'relink': True, + }[op] + node['properties']['used_in_edit'] = used_in_edit node = pillar.api.utils.remove_private_keys(node) r, _, _, status = put_internal('nodes', node, _id=node_id) return pillar.api.utils.jsonify(r, status=status) + + + def assert_is_valid_patch(patch): """Raises an exception when the patch isn't valid.""" @@ -248,12 +265,15 @@ def assert_is_valid_patch(patch): except KeyError: raise wz_exceptions.BadRequest("PATCH should have a key 'op' indicating the operation.") - try: - allowed_fields = VALID_PATCH_OPERATIONS[op] - except KeyError: - valid_ops = u', '.join(VALID_PATCH_OPERATIONS.keys()) + if op not in VALID_PATCH_OPERATIONS: + valid_ops = u', '.join(sorted(VALID_PATCH_OPERATIONS)) raise wz_exceptions.BadRequest(u'Operation should be one of %s' % valid_ops) + if op not in VALID_PATCH_FIELDS: + # Valid operation, and we don't have to check the fields. + return + + allowed_fields = VALID_PATCH_FIELDS[op] try: fields = set(patch['$set'].keys()) except KeyError: diff --git a/tests/test_shots.py b/tests/test_shots.py index ab5c2e5..0a73ceb 100644 --- a/tests/test_shots.py +++ b/tests/test_shots.py @@ -339,6 +339,38 @@ class PatchShotTest(AbstractShotTest): } self.patch(url, json=patch, auth_token='other', expected_status=403) + @responses.activate + def test_patch_unlink(self): + shot = self.create_shot() + self.create_valid_auth_token(ctd.EXAMPLE_PROJECT_OWNER_ID, 'token') + + url = '/api/nodes/%s' % shot._id + + dbnode = self.get(url, auth_token='token').json() + self.assertNotIn('used_in_edit', dbnode['properties']) + + patch = {'op': 'unlink'} + self.patch(url, json=patch, auth_token='token') + + dbnode = self.get(url, auth_token='token').json() + self.assertFalse(dbnode['properties']['used_in_edit']) + + @responses.activate + def test_patch_relink(self): + shot = self.create_shot() + self.create_valid_auth_token(ctd.EXAMPLE_PROJECT_OWNER_ID, 'token') + + url = '/api/nodes/%s' % shot._id + + dbnode = self.get(url, auth_token='token').json() + self.assertNotIn('used_in_edit', dbnode['properties']) + + patch = {'op': 'relink'} + self.patch(url, json=patch, auth_token='token') + + dbnode = self.get(url, auth_token='token').json() + self.assertTrue(dbnode['properties']['used_in_edit']) + class RequiredAfterCreationTest(AbstractShotTest): """