From 0d306b9d06a1230f46703f28cf2ae91fc65bb279 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 3 May 2019 14:40:35 -0700 Subject: [PATCH 1/3] Add schema writing functionality to SchemaFactory class This works opposite to the get_schemas function --- shotgun_api3/lib/mockgun/schema.py | 45 ++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/shotgun_api3/lib/mockgun/schema.py b/shotgun_api3/lib/mockgun/schema.py index edd4b889a..013a18728 100644 --- a/shotgun_api3/lib/mockgun/schema.py +++ b/shotgun_api3/lib/mockgun/schema.py @@ -79,6 +79,27 @@ def get_schemas(cls, schema_path, schema_entity_path): return cls._schema_cache, cls._schema_entity_cache + @classmethod + def set_schemas(cls, schema, schema_path, schema_entity, schema_entity_path): + """ + Retrieves the schemas from disk. + + :param str schema: Schema data. + :param str schema_path: Path to the schema on disk. + :param str schema_entity: Entities schema data. + :param str schema_entity_path: Path to the entities schema on disk. + """ + + if schema_path != cls._schema_cache_path: + cls._schema_cache_path = schema_path + cls._schema_cache = schema + cls._write_file(schema, schema_path) + + if schema_entity_path != cls._schema_entity_cache_path: + cls._schema_entity_cache_path = schema_entity_path + cls._schema_entity_cache = schema_entity + cls._write_file(schema_entity, schema_entity_path) + @classmethod def _read_file(cls, path): fh = open(path, "rb") @@ -87,6 +108,14 @@ def _read_file(cls, path): finally: fh.close() + @classmethod + def _write_file(cls, data, path): + fh = open(path, "rb") + try: + return pickle.dump(data, fh, protocol=_HIGHEST_24_PICKLE_PROTOCOL) + finally: + fh.close() + # Highest protocol that Python 2.4 supports, which is the earliest version of Python we support. # Actually, this is the same version that Python 2.7 supports at the moment! @@ -102,23 +131,11 @@ def generate_schema(shotgun, schema_file_path, schema_entity_file_path): and downloading the schema information for that site. Once the generated schema files are being passed to mockgun, it will mimic the site's schema structure. - :param sg_url: Shotgun site url - :param sg_script: Script name to connect with - :param sg_key: Script key to connect with + :param shotgun: Shotgun instance :param schema_file_path: Path where to write the main schema file to :param schema_entity_file_path: Path where to write the entity schema file to """ schema = shotgun.schema_read() - fh = open(schema_file_path, "wb") - try: - pickle.dump(schema, fh, protocol=_HIGHEST_24_PICKLE_PROTOCOL) - finally: - fh.close() - schema_entity = shotgun.schema_entity_read() - fh = open(schema_entity_file_path, "wb") - try: - pickle.dump(schema_entity, fh, protocol=_HIGHEST_24_PICKLE_PROTOCOL) - finally: - fh.close() + SchemaFactory.set_schemas(schema, schema_file_path, schema_entity, schema_entity_file_path) From 6a6e9736b07e21cd245de033ae3beee8eec9de9d Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 3 May 2019 14:48:30 -0700 Subject: [PATCH 2/3] Implement schema_field_create/update/delete We implement the create and update using a new internal helper called `_set_property`. This new method will recursively search the passed current property dictionary and attempt to find all keys matching the key you pass in and update it's value to the value you have passed in. If it cannot find the value in the current dictionary but it does find a "properties" dictionary, it will add a new entry in the "properties" dictionary. This "properties" dictionary appears to only exist on the root of the properties dictionary. A `schema_field_update` does a deep copy of the properties before updating them whereas the `schema_field_create` method starts from a base schema_field dictionary and updates it with what you pass in. This does not deal with setting "editable" values in each of these keys. We are assuming, in Mockgun, that you are allowed to change anything in the schema. Perhaps someone from the team at Shotgun will have some clear logic that will allow us to implement that. --- shotgun_api3/lib/mockgun/mockgun.py | 63 +++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/shotgun_api3/lib/mockgun/mockgun.py b/shotgun_api3/lib/mockgun/mockgun.py index d8f33cebb..07a333787 100644 --- a/shotgun_api3/lib/mockgun/mockgun.py +++ b/shotgun_api3/lib/mockgun/mockgun.py @@ -114,6 +114,7 @@ """ +import copy import datetime from ... import ShotgunError @@ -227,13 +228,48 @@ def schema_read(self): return self._schema def schema_field_create(self, entity_type, data_type, display_name, properties=None): - raise NotImplementedError + _properties = { + "data_type": {"editable": True, "value": None}, + "description": {"editable": True, "value": ""}, + "editable": {"editable": True, "value": True}, + "entity_type": {"editable": True, "value": None}, + "mandatory": {"editable": True, "value": False}, + "name": {"editable": True, "value": None}, + "properties": { + "default_value": {"editable": True, "value": None}, + "regex_validation": {"editable": True, "value": ""}, + "regex_validation_enabled": {"editable": True, "value": None}, + "summary_default": {"editable": True, "value": "none"} + }, + "ui_value_displayable": {"editable": True, "value": True}, + "unique": {"editable": True, "value": True}, + "visible": {"editable": True, "value": True} + } + _properties["entity_type"]["value"] = entity_type + _properties["entity_type"]["editable"] = False + + _properties["data_type"]["value"] = data_type + _properties["data_type"]["editable"] = False + + _properties["name"]["value"] = display_name + _properties["name"]["editable"] = False + + if isinstance(properties, dict) and properties: + for prop in properties: + self._set_property(_properties, prop, properties[prop]) + + self._schema[entity_type][display_name] = _properties def schema_field_update(self, entity_type, field_name, properties): - raise NotImplementedError + _properties = copy.deepcopy(self._schema[entity_type][field_name]) + for key in properties: + self._set_property(_properties, key, properties[key]) + self._schema[entity_type][field_name] = _properties def schema_field_delete(self, entity_type, field_name): - raise NotImplementedError + if field_name not in self._schema[entity_type]: + return + del self._schema[entity_type][field_name] def schema_entity_read(self): return self._schema_entity @@ -422,8 +458,29 @@ def upload(self, entity_type, entity_id, path, field_name=None, display_name=Non def upload_thumbnail(self, entity_type, entity_id, path, **kwargs): pass + def dump_schemas(self): + schema_path, schema_entity_path = self.get_schema_paths() + SchemaFactory.set_schemas(self.schema_read(), schema_path, self.schema_entity_read(), schema_entity_path) + ################################################################################################### # internal methods and members + @classmethod + def _set_property(cls, property_data, property_key, property_value): + result = False + for key in property_data: + if key == property_key: + property_data[key]["value"] = property_value + result = True + break + elif isinstance(property_data[key], dict): + _result = cls._set_property(property_data[key], property_key, property_value) + if _result is True: + result = _result + break + if result is False and "properties" in property_data: + property_data["properties"][property_key] = {"editable": True, "value": property_value} + result = True + return result def _validate_entity_type(self, entity_type): if entity_type not in self._schema: From 43c519e7c03f75657a9a6972ba36ca0a069c395e Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 21 May 2019 21:00:22 +0100 Subject: [PATCH 3/3] Update file descriptor when writing --- shotgun_api3/lib/mockgun/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shotgun_api3/lib/mockgun/schema.py b/shotgun_api3/lib/mockgun/schema.py index 013a18728..f367975cc 100644 --- a/shotgun_api3/lib/mockgun/schema.py +++ b/shotgun_api3/lib/mockgun/schema.py @@ -110,7 +110,7 @@ def _read_file(cls, path): @classmethod def _write_file(cls, data, path): - fh = open(path, "rb") + fh = open(path, "wb") try: return pickle.dump(data, fh, protocol=_HIGHEST_24_PICKLE_PROTOCOL) finally: