From 5e5db076d1795256ecf7b1cebe064651452cefaa Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 21 Sep 2023 11:42:42 +0800 Subject: [PATCH 01/37] remove unrelated codes to this pull request --- openpype/hosts/houdini/api/lib.py | 50 +- .../plugins/create/create_arnold_ass.py | 16 + .../houdini/plugins/create/create_bgeo.py | 16 +- .../plugins/create/create_mantra_ifd.py | 56 ++ .../plugins/create/create_pointcache.py | 17 + .../plugins/create/create_redshift_proxy.py | 16 + .../plugins/create/create_vbd_cache.py | 22 +- .../hosts/houdini/plugins/load/load_camera.py | 81 ++- .../plugins/publish/collect_cache_farm.py | 75 +++ .../plugins/publish/collect_chunk_size.py | 39 ++ .../houdini/plugins/publish/collect_frames.py | 3 +- .../plugins/publish/extract_alembic.py | 4 + .../houdini/plugins/publish/extract_ass.py | 5 +- .../houdini/plugins/publish/extract_bgeo.py | 4 +- .../plugins/publish/extract_mantra_ifd.py | 51 ++ .../plugins/publish/extract_redshift_proxy.py | 5 +- .../plugins/publish/extract_vdb_cache.py | 4 +- .../plugins/publish/increment_current_file.py | 3 +- .../publish/submit_houdini_cache_deadline.py | 183 +++++++ .../publish/submit_publish_cache_job.py | 501 ++++++++++++++++++ .../publish/validate_deadline_pools.py | 3 +- openpype/pipeline/farm/pyblish_functions.py | 232 ++++++++ .../defaults/project_settings/deadline.json | 8 + .../defaults/project_settings/houdini.json | 5 + .../schemas/schema_houdini_publish.json | 25 + .../server/settings/publish_plugins.py | 22 + server_addon/deadline/server/version.py | 2 +- .../server/settings/publish_plugins.py | 15 + website/docs/artist_hosts_houdini.md | 24 + .../assets/houdini_farm_cache_creator.png | Bin 0 -> 95572 bytes .../docs/assets/houdini_farm_cache_loader.png | Bin 0 -> 134816 bytes .../docs/assets/houdini_frame_per_task.png | Bin 0 -> 59257 bytes 32 files changed, 1380 insertions(+), 107 deletions(-) create mode 100644 openpype/hosts/houdini/plugins/create/create_mantra_ifd.py create mode 100644 openpype/hosts/houdini/plugins/publish/collect_cache_farm.py create mode 100644 openpype/hosts/houdini/plugins/publish/collect_chunk_size.py create mode 100644 openpype/hosts/houdini/plugins/publish/extract_mantra_ifd.py create mode 100644 openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py create mode 100644 openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py create mode 100644 website/docs/assets/houdini_farm_cache_creator.png create mode 100644 website/docs/assets/houdini_farm_cache_loader.png create mode 100644 website/docs/assets/houdini_frame_per_task.png diff --git a/openpype/hosts/houdini/api/lib.py b/openpype/hosts/houdini/api/lib.py index eff98c05f17..856e7abbd33 100644 --- a/openpype/hosts/houdini/api/lib.py +++ b/openpype/hosts/houdini/api/lib.py @@ -138,6 +138,9 @@ def get_output_parameter(node): return node.parm("ar_ass_file") elif node_type == "Redshift_Proxy_Output": return node.parm("RS_archive_file") + elif node_type == "ifd": + if node.evalParm("soho_outputmode"): + return node.parm("soho_diskfile") raise TypeError("Node type '%s' not supported" % node_type) @@ -649,50 +652,3 @@ def get_color_management_preferences(): "display": hou.Color.ocio_defaultDisplay(), "view": hou.Color.ocio_defaultView() } - - -def get_resolution_from_doc(doc): - """Get resolution from the given asset document. """ - - if not doc or "data" not in doc: - print("Entered document is not valid. \"{}\"".format(str(doc))) - return None - - resolution_width = doc["data"].get("resolutionWidth") - resolution_height = doc["data"].get("resolutionHeight") - - # Make sure both width and height are set - if resolution_width is None or resolution_height is None: - print("No resolution information found for \"{}\"".format(doc["name"])) - return None - - return int(resolution_width), int(resolution_height) - - -def set_camera_resolution(camera, asset_doc=None): - """Apply resolution to camera from asset document of the publish""" - - if not asset_doc: - asset_doc = get_current_project_asset() - - resolution = get_resolution_from_doc(asset_doc) - - if resolution: - print("Setting camera resolution: {} -> {}x{}".format( - camera.name(), resolution[0], resolution[1] - )) - camera.parm("resx").set(resolution[0]) - camera.parm("resy").set(resolution[1]) - - -def get_camera_from_container(container): - """Get camera from container node. """ - - cameras = container.recursiveGlob( - "*", - filter=hou.nodeTypeFilter.ObjCamera, - include_subnets=False - ) - - assert len(cameras) == 1, "Camera instance must have only one camera" - return cameras[0] diff --git a/openpype/hosts/houdini/plugins/create/create_arnold_ass.py b/openpype/hosts/houdini/plugins/create/create_arnold_ass.py index 12d08f7d838..437a14c7231 100644 --- a/openpype/hosts/houdini/plugins/create/create_arnold_ass.py +++ b/openpype/hosts/houdini/plugins/create/create_arnold_ass.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- """Creator plugin for creating Arnold ASS files.""" from openpype.hosts.houdini.api import plugin +from openpype.lib import BoolDef class CreateArnoldAss(plugin.HoudiniCreator): @@ -21,6 +22,9 @@ def create(self, subset_name, instance_data, pre_create_data): instance_data.pop("active", None) instance_data.update({"node_type": "arnold"}) + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["farm"] = pre_create_data["farm"] instance = super(CreateArnoldAss, self).create( subset_name, @@ -52,3 +56,15 @@ def create(self, subset_name, instance_data, pre_create_data): # Lock any parameters in this list to_lock = ["ar_ass_export_enable", "family", "id"] self.lock_parameters(instance_node, to_lock) + + def get_instance_attr_defs(self): + return [ + BoolDef("farm", + label="Submitting to Farm", + default=False) + ] + + def get_pre_create_attr_defs(self): + attrs = super().get_pre_create_attr_defs() + # Use same attributes as for instance attributes + return attrs + self.get_instance_attr_defs() diff --git a/openpype/hosts/houdini/plugins/create/create_bgeo.py b/openpype/hosts/houdini/plugins/create/create_bgeo.py index a3f31e7e94c..6d903fb02dd 100644 --- a/openpype/hosts/houdini/plugins/create/create_bgeo.py +++ b/openpype/hosts/houdini/plugins/create/create_bgeo.py @@ -2,7 +2,7 @@ """Creator plugin for creating pointcache bgeo files.""" from openpype.hosts.houdini.api import plugin from openpype.pipeline import CreatedInstance, CreatorError -from openpype.lib import EnumDef +from openpype.lib import EnumDef, BoolDef class CreateBGEO(plugin.HoudiniCreator): @@ -18,6 +18,9 @@ def create(self, subset_name, instance_data, pre_create_data): instance_data.pop("active", None) instance_data.update({"node_type": "geometry"}) + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["farm"] = pre_create_data["farm"] instance = super(CreateBGEO, self).create( subset_name, @@ -58,6 +61,13 @@ def create(self, subset_name, instance_data, pre_create_data): instance_node.setParms(parms) + def get_instance_attr_defs(self): + return [ + BoolDef("farm", + label="Submitting to Farm", + default=False) + ] + def get_pre_create_attr_defs(self): attrs = super().get_pre_create_attr_defs() bgeo_enum = [ @@ -88,5 +98,5 @@ def get_pre_create_attr_defs(self): ] return attrs + [ - EnumDef("bgeo_type", bgeo_enum, label="BGEO Options"), - ] + EnumDef("bgeo_type", bgeo_enum, label="BGEO Options") + ] + self.get_instance_attr_defs() diff --git a/openpype/hosts/houdini/plugins/create/create_mantra_ifd.py b/openpype/hosts/houdini/plugins/create/create_mantra_ifd.py new file mode 100644 index 00000000000..7ea7d1042fe --- /dev/null +++ b/openpype/hosts/houdini/plugins/create/create_mantra_ifd.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +"""Creator plugin for creating pointcache alembics.""" +from openpype.hosts.houdini.api import plugin +from openpype.pipeline import CreatedInstance +from openpype.lib import BoolDef + + +class CreateMantraIFD(plugin.HoudiniCreator): + """Mantra .ifd Archive""" + identifier = "io.openpype.creators.houdini.mantraifd" + label = "Mantra IFD" + family = "mantraifd" + icon = "gears" + + def create(self, subset_name, instance_data, pre_create_data): + import hou + instance_data.pop("active", None) + instance_data.update({"node_type": "ifd"}) + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["farm"] = pre_create_data["farm"] + instance = super(CreateMantraIFD, self).create( + subset_name, + instance_data, + pre_create_data) # type: CreatedInstance + + instance_node = hou.node(instance.get("instance_node")) + + filepath = "{}{}".format( + hou.text.expandString("$HIP/pyblish/"), + "{}.$F4.ifd".format(subset_name)) + parms = { + # Render frame range + "trange": 1, + # Arnold ROP settings + "soho_diskfile": filepath, + "soho_outputmode": 1 + } + + instance_node.setParms(parms) + + # Lock any parameters in this list + to_lock = ["soho_outputmode", "family", "id"] + self.lock_parameters(instance_node, to_lock) + + def get_instance_attr_defs(self): + return [ + BoolDef("farm", + label="Submitting to Farm", + default=False) + ] + + def get_pre_create_attr_defs(self): + attrs = super().get_pre_create_attr_defs() + # Use same attributes as for instance attributes + return attrs + self.get_instance_attr_defs() diff --git a/openpype/hosts/houdini/plugins/create/create_pointcache.py b/openpype/hosts/houdini/plugins/create/create_pointcache.py index 7eaf2aff2ba..2d2f89cc48e 100644 --- a/openpype/hosts/houdini/plugins/create/create_pointcache.py +++ b/openpype/hosts/houdini/plugins/create/create_pointcache.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- """Creator plugin for creating pointcache alembics.""" from openpype.hosts.houdini.api import plugin +from openpype.lib import BoolDef import hou + class CreatePointCache(plugin.HoudiniCreator): """Alembic ROP to pointcache""" identifier = "io.openpype.creators.houdini.pointcache" @@ -15,6 +17,9 @@ class CreatePointCache(plugin.HoudiniCreator): def create(self, subset_name, instance_data, pre_create_data): instance_data.pop("active", None) instance_data.update({"node_type": "alembic"}) + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["farm"] = pre_create_data["farm"] instance = super(CreatePointCache, self).create( subset_name, @@ -105,3 +110,15 @@ def get_obj_output(self, obj_node): else: return min(outputs, key=lambda node: node.evalParm('outputidx')) + + def get_instance_attr_defs(self): + return [ + BoolDef("farm", + label="Submitting to Farm", + default=False) + ] + + def get_pre_create_attr_defs(self): + attrs = super().get_pre_create_attr_defs() + # Use same attributes as for instance attributes + return attrs + self.get_instance_attr_defs() diff --git a/openpype/hosts/houdini/plugins/create/create_redshift_proxy.py b/openpype/hosts/houdini/plugins/create/create_redshift_proxy.py index b814dd9d572..de1f1719fbe 100644 --- a/openpype/hosts/houdini/plugins/create/create_redshift_proxy.py +++ b/openpype/hosts/houdini/plugins/create/create_redshift_proxy.py @@ -2,6 +2,7 @@ """Creator plugin for creating Redshift proxies.""" from openpype.hosts.houdini.api import plugin from openpype.pipeline import CreatedInstance +from openpype.lib import BoolDef class CreateRedshiftProxy(plugin.HoudiniCreator): @@ -24,6 +25,9 @@ def create(self, subset_name, instance_data, pre_create_data): # TODO: Somehow enforce so that it only shows the original limited # attributes of the Redshift_Proxy_Output node type instance_data.update({"node_type": "Redshift_Proxy_Output"}) + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["farm"] = pre_create_data["farm"] instance = super(CreateRedshiftProxy, self).create( subset_name, @@ -44,3 +48,15 @@ def create(self, subset_name, instance_data, pre_create_data): # Lock some Avalon attributes to_lock = ["family", "id", "prim_to_detail_pattern"] self.lock_parameters(instance_node, to_lock) + + def get_instance_attr_defs(self): + return [ + BoolDef("farm", + label="Submitting to Farm", + default=False) + ] + + def get_pre_create_attr_defs(self): + attrs = super().get_pre_create_attr_defs() + # Use same attributes as for instance attributes + return attrs + self.get_instance_attr_defs() diff --git a/openpype/hosts/houdini/plugins/create/create_vbd_cache.py b/openpype/hosts/houdini/plugins/create/create_vbd_cache.py index 9c96e48e3a4..8589600fc60 100644 --- a/openpype/hosts/houdini/plugins/create/create_vbd_cache.py +++ b/openpype/hosts/houdini/plugins/create/create_vbd_cache.py @@ -2,6 +2,7 @@ """Creator plugin for creating VDB Caches.""" from openpype.hosts.houdini.api import plugin from openpype.pipeline import CreatedInstance +from openpype.lib import BoolDef import hou @@ -19,15 +20,20 @@ def create(self, subset_name, instance_data, pre_create_data): instance_data.pop("active", None) instance_data.update({"node_type": "geometry"}) - + creator_attributes = instance_data.setdefault( + "creator_attributes", dict()) + creator_attributes["farm"] = pre_create_data["farm"] instance = super(CreateVDBCache, self).create( subset_name, instance_data, pre_create_data) # type: CreatedInstance instance_node = hou.node(instance.get("instance_node")) + file_path = "{}{}".format( + hou.text.expandString("$HIP/pyblish/"), + "{}.$F4.vdb".format(subset_name)) parms = { - "sopoutput": "$HIP/pyblish/{}.$F4.vdb".format(subset_name), + "sopoutput": file_path, "initsim": True, "trange": 1 } @@ -102,3 +108,15 @@ def get_obj_output(self, obj_node): else: return min(outputs, key=lambda node: node.evalParm('outputidx')) + + def get_instance_attr_defs(self): + return [ + BoolDef("farm", + label="Submitting to Farm", + default=False) + ] + + def get_pre_create_attr_defs(self): + attrs = super().get_pre_create_attr_defs() + # Use same attributes as for instance attributes + return attrs + self.get_instance_attr_defs() diff --git a/openpype/hosts/houdini/plugins/load/load_camera.py b/openpype/hosts/houdini/plugins/load/load_camera.py index e16146a2676..7b4a04809e5 100644 --- a/openpype/hosts/houdini/plugins/load/load_camera.py +++ b/openpype/hosts/houdini/plugins/load/load_camera.py @@ -4,13 +4,6 @@ ) from openpype.hosts.houdini.api import pipeline -from openpype.hosts.houdini.api.lib import ( - set_camera_resolution, - get_camera_from_container -) - -import hou - ARCHIVE_EXPRESSION = ('__import__("_alembic_hom_extensions")' '.alembicGetCameraDict') @@ -32,15 +25,7 @@ def transfer_non_default_values(src, dest, ignore=None): channel expression and ignore certain Parm types. """ - - ignore_types = { - hou.parmTemplateType.Toggle, - hou.parmTemplateType.Menu, - hou.parmTemplateType.Button, - hou.parmTemplateType.FolderSet, - hou.parmTemplateType.Separator, - hou.parmTemplateType.Label, - } + import hou src.updateParmStates() @@ -77,6 +62,14 @@ def transfer_non_default_values(src, dest, ignore=None): continue # Ignore folders, separators, etc. + ignore_types = { + hou.parmTemplateType.Toggle, + hou.parmTemplateType.Menu, + hou.parmTemplateType.Button, + hou.parmTemplateType.FolderSet, + hou.parmTemplateType.Separator, + hou.parmTemplateType.Label, + } if parm.parmTemplate().type() in ignore_types: continue @@ -97,8 +90,13 @@ class CameraLoader(load.LoaderPlugin): def load(self, context, name=None, namespace=None, data=None): + import os + import hou + # Format file name, Houdini only wants forward slashes - file_path = self.filepath_from_context(context).replace("\\", "/") + file_path = self.filepath_from_context(context) + file_path = os.path.normpath(file_path) + file_path = file_path.replace("\\", "/") # Get the root node obj = hou.node("/obj") @@ -108,21 +106,19 @@ def load(self, context, name=None, namespace=None, data=None): node_name = "{}_{}".format(namespace, name) if namespace else name # Create a archive node - node = self.create_and_connect(obj, "alembicarchive", node_name) + container = self.create_and_connect(obj, "alembicarchive", node_name) # TODO: add FPS of project / asset - node.setParms({"fileName": file_path, "channelRef": True}) + container.setParms({"fileName": file_path, + "channelRef": True}) # Apply some magic - node.parm("buildHierarchy").pressButton() - node.moveToGoodPosition() + container.parm("buildHierarchy").pressButton() + container.moveToGoodPosition() # Create an alembic xform node - nodes = [node] + nodes = [container] - camera = get_camera_from_container(node) - self._match_maya_render_mask(camera) - set_camera_resolution(camera, asset_doc=context["asset"]) self[:] = nodes return pipeline.containerise(node_name, @@ -147,14 +143,14 @@ def update(self, container, representation): # Store the cam temporarily next to the Alembic Archive # so that we can preserve parm values the user set on it # after build hierarchy was triggered. - old_camera = get_camera_from_container(node) + old_camera = self._get_camera(node) temp_camera = old_camera.copyTo(node.parent()) # Rebuild node.parm("buildHierarchy").pressButton() # Apply values to the new camera - new_camera = get_camera_from_container(node) + new_camera = self._get_camera(node) transfer_non_default_values(temp_camera, new_camera, # The hidden uniform scale attribute @@ -162,9 +158,6 @@ def update(self, container, representation): # "icon_scale" just skip that completely ignore={"scale"}) - self._match_maya_render_mask(new_camera) - set_camera_resolution(new_camera) - temp_camera.destroy() def remove(self, container): @@ -172,6 +165,15 @@ def remove(self, container): node = container["node"] node.destroy() + def _get_camera(self, node): + import hou + cameras = node.recursiveGlob("*", + filter=hou.nodeTypeFilter.ObjCamera, + include_subnets=False) + + assert len(cameras) == 1, "Camera instance must have only one camera" + return cameras[0] + def create_and_connect(self, node, node_type, name=None): """Create a node within a node which and connect it to the input @@ -192,20 +194,5 @@ def create_and_connect(self, node, node_type, name=None): new_node.moveToGoodPosition() return new_node - def _match_maya_render_mask(self, camera): - """Workaround to match Maya render mask in Houdini""" - - # print("Setting match maya render mask ") - parm = camera.parm("aperture") - expression = parm.expression() - expression = expression.replace("return ", "aperture = ") - expression += """ -# Match maya render mask (logic from Houdini's own FBX importer) -node = hou.pwd() -resx = node.evalParm('resx') -resy = node.evalParm('resy') -aspect = node.evalParm('aspect') -aperture *= min(1, (resx / resy * aspect) / 1.5) -return aperture -""" - parm.setExpression(expression, language=hou.exprLanguage.Python) + def switch(self, container, representation): + self.update(container, representation) diff --git a/openpype/hosts/houdini/plugins/publish/collect_cache_farm.py b/openpype/hosts/houdini/plugins/publish/collect_cache_farm.py new file mode 100644 index 00000000000..36ade32a35d --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/collect_cache_farm.py @@ -0,0 +1,75 @@ +import os +import pyblish.api +import hou +from openpype.hosts.houdini.api import lib + + +class CollectDataforCache(pyblish.api.InstancePlugin): + """Collect data for caching to Deadline.""" + + order = pyblish.api.CollectorOrder + 0.04 + families = ["ass", "pointcache", + "mantraifd", "redshiftproxy", + "vdbcache"] + hosts = ["houdini"] + targets = ["local", "remote"] + label = "Collect Data for Cache" + + def process(self, instance): + creator_attribute = instance.data["creator_attributes"] + farm_enabled = creator_attribute["farm"] + instance.data["farm"] = farm_enabled + if not farm_enabled: + self.log.debug("Caching on farm is disabled. " + "Skipping farm collecting.") + return + # Why do we need this particular collector to collect the expected + # output files from a ROP node. Don't we have a dedicated collector + # for that yet? + # Collect expected files + ropnode = hou.node(instance.data["instance_node"]) + output_parm = lib.get_output_parameter(ropnode) + expected_filepath = output_parm.eval() + instance.data.setdefault("files", list()) + instance.data.setdefault("expectedFiles", list()) + if instance.data.get("frames"): + files = self.get_files(instance, expected_filepath) + # list of files + instance.data["files"].extend(files) + else: + # single file + instance.data["files"].append(output_parm.eval()) + cache_files = {"_": instance.data["files"]} + # Convert instance family to pointcache if it is bgeo or abc + # because ??? + for family in instance.data["families"]: + if family == "bgeo" or "abc": + instance.data["family"] = "pointcache" + break + instance.data.update({ + "plugin": "Houdini", + "publish": True + }) + instance.data["families"].append("publish.hou") + instance.data["expectedFiles"].append(cache_files) + + self.log.debug("{}".format(instance.data)) + + def get_files(self, instance, output_parm): + """Get the files with the frame range data + + Args: + instance (_type_): instance + output_parm (_type_): path of output parameter + + Returns: + files: a list of files + """ + directory = os.path.dirname(output_parm) + + files = [ + os.path.join(directory, frame).replace("\\", "/") + for frame in instance.data["frames"] + ] + + return files diff --git a/openpype/hosts/houdini/plugins/publish/collect_chunk_size.py b/openpype/hosts/houdini/plugins/publish/collect_chunk_size.py new file mode 100644 index 00000000000..1c867e930ad --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/collect_chunk_size.py @@ -0,0 +1,39 @@ +import pyblish.api +from openpype.lib import NumberDef +from openpype.pipeline import OpenPypePyblishPluginMixin + + +class CollectChunkSize(pyblish.api.InstancePlugin, + OpenPypePyblishPluginMixin): + """Collect chunk size for cache submission to Deadline.""" + + order = pyblish.api.CollectorOrder + 0.05 + families = ["ass", "pointcache", + "vdbcache", "mantraifd", + "redshiftproxy"] + hosts = ["houdini"] + targets = ["local", "remote"] + label = "Collect Chunk Size" + chunkSize = 999999 + + def process(self, instance): + # need to get the chunk size info from the setting + attr_values = self.get_attr_values_from_data(instance.data) + instance.data["chunkSize"] = attr_values.get("chunkSize") + + @classmethod + def apply_settings(cls, project_settings): + project_setting = project_settings["houdini"]["publish"]["CollectChunkSize"] # noqa + cls.chunkSize = project_setting["chunk_size"] + + @classmethod + def get_attribute_defs(cls): + return [ + NumberDef("chunkSize", + minimum=1, + maximum=999999, + decimals=0, + default=cls.chunkSize, + label="Frame Per Task") + + ] diff --git a/openpype/hosts/houdini/plugins/publish/collect_frames.py b/openpype/hosts/houdini/plugins/publish/collect_frames.py index 01df809d4c7..cb4ff1a45bd 100644 --- a/openpype/hosts/houdini/plugins/publish/collect_frames.py +++ b/openpype/hosts/houdini/plugins/publish/collect_frames.py @@ -14,7 +14,8 @@ class CollectFrames(pyblish.api.InstancePlugin): order = pyblish.api.CollectorOrder + 0.01 label = "Collect Frames" families = ["vdbcache", "imagesequence", "ass", - "redshiftproxy", "review", "bgeo"] + "mantraifd", "redshiftproxy", "review", + "bgeo"] def process(self, instance): diff --git a/openpype/hosts/houdini/plugins/publish/extract_alembic.py b/openpype/hosts/houdini/plugins/publish/extract_alembic.py index bdd19b23d4d..df2fdda2419 100644 --- a/openpype/hosts/houdini/plugins/publish/extract_alembic.py +++ b/openpype/hosts/houdini/plugins/publish/extract_alembic.py @@ -14,8 +14,12 @@ class ExtractAlembic(publish.Extractor): label = "Extract Alembic" hosts = ["houdini"] families = ["abc", "camera"] + targets = ["local", "remote"] def process(self, instance): + if instance.data.get("farm"): + self.log.debug("Should be processed on farm, skipping.") + return ropnode = hou.node(instance.data["instance_node"]) diff --git a/openpype/hosts/houdini/plugins/publish/extract_ass.py b/openpype/hosts/houdini/plugins/publish/extract_ass.py index 0d246625bab..cf04a3994ef 100644 --- a/openpype/hosts/houdini/plugins/publish/extract_ass.py +++ b/openpype/hosts/houdini/plugins/publish/extract_ass.py @@ -14,9 +14,12 @@ class ExtractAss(publish.Extractor): label = "Extract Ass" families = ["ass"] hosts = ["houdini"] + targets = ["local", "remote"] def process(self, instance): - + if instance.data.get("farm"): + self.log.debug("Should be processed on farm, skipping.") + return ropnode = hou.node(instance.data["instance_node"]) # Get the filename from the filename parameter diff --git a/openpype/hosts/houdini/plugins/publish/extract_bgeo.py b/openpype/hosts/houdini/plugins/publish/extract_bgeo.py index c9625ec8803..fc5cf0bb65e 100644 --- a/openpype/hosts/houdini/plugins/publish/extract_bgeo.py +++ b/openpype/hosts/houdini/plugins/publish/extract_bgeo.py @@ -17,7 +17,9 @@ class ExtractBGEO(publish.Extractor): families = ["bgeo"] def process(self, instance): - + if instance.data.get("farm"): + self.log.debug("Should be processed on farm, skipping.") + return ropnode = hou.node(instance.data["instance_node"]) # Get the filename from the filename parameter diff --git a/openpype/hosts/houdini/plugins/publish/extract_mantra_ifd.py b/openpype/hosts/houdini/plugins/publish/extract_mantra_ifd.py new file mode 100644 index 00000000000..894260d1bf0 --- /dev/null +++ b/openpype/hosts/houdini/plugins/publish/extract_mantra_ifd.py @@ -0,0 +1,51 @@ +import os + +import pyblish.api + +from openpype.pipeline import publish + +import hou + + +class ExtractMantraIFD(publish.Extractor): + + order = pyblish.api.ExtractorOrder + label = "Extract Mantra ifd" + hosts = ["houdini"] + families = ["mantraifd"] + targets = ["local", "remote"] + + def process(self, instance): + if instance.data.get("farm"): + self.log.debug("Should be processed on farm, skipping.") + return + + ropnode = hou.node(instance.data.get("instance_node")) + output = ropnode.evalParm("soho_diskfile") + staging_dir = os.path.dirname(output) + instance.data["stagingDir"] = staging_dir + + files = instance.data["frames"] + missing_frames = [ + frame + for frame in instance.data["frames"] + if not os.path.exists( + os.path.normpath(os.path.join(staging_dir, frame))) + ] + if missing_frames: + raise RuntimeError("Failed to complete Mantra ifd extraction. " + "Missing output files: {}".format( + missing_frames)) + + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'ifd', + 'ext': 'ifd', + 'files': files, + "stagingDir": staging_dir, + "frameStart": instance.data["frameStart"], + "frameEnd": instance.data["frameEnd"], + } + instance.data["representations"].append(representation) diff --git a/openpype/hosts/houdini/plugins/publish/extract_redshift_proxy.py b/openpype/hosts/houdini/plugins/publish/extract_redshift_proxy.py index 1d99ac665c2..0ca0a01c5ca 100644 --- a/openpype/hosts/houdini/plugins/publish/extract_redshift_proxy.py +++ b/openpype/hosts/houdini/plugins/publish/extract_redshift_proxy.py @@ -14,9 +14,12 @@ class ExtractRedshiftProxy(publish.Extractor): label = "Extract Redshift Proxy" families = ["redshiftproxy"] hosts = ["houdini"] + targets = ["local", "remote"] def process(self, instance): - + if instance.data.get("farm"): + self.log.debug("Should be processed on farm, skipping.") + return ropnode = hou.node(instance.data.get("instance_node")) # Get the filename from the filename parameter diff --git a/openpype/hosts/houdini/plugins/publish/extract_vdb_cache.py b/openpype/hosts/houdini/plugins/publish/extract_vdb_cache.py index 4bca758f08f..7d8634b83ca 100644 --- a/openpype/hosts/houdini/plugins/publish/extract_vdb_cache.py +++ b/openpype/hosts/houdini/plugins/publish/extract_vdb_cache.py @@ -16,7 +16,9 @@ class ExtractVDBCache(publish.Extractor): hosts = ["houdini"] def process(self, instance): - + if instance.data.get("farm"): + self.log.debug("Should be processed on farm, skipping.") + return ropnode = hou.node(instance.data["instance_node"]) # Get the filename from the filename parameter diff --git a/openpype/hosts/houdini/plugins/publish/increment_current_file.py b/openpype/hosts/houdini/plugins/publish/increment_current_file.py index 3569de7693b..4788cca3cf3 100644 --- a/openpype/hosts/houdini/plugins/publish/increment_current_file.py +++ b/openpype/hosts/houdini/plugins/publish/increment_current_file.py @@ -22,7 +22,8 @@ class IncrementCurrentFile(pyblish.api.ContextPlugin): "arnold_rop", "mantra_rop", "karma_rop", - "usdrender"] + "usdrender", + "publish.hou"] optional = True def process(self, context): diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py b/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py new file mode 100644 index 00000000000..b1717d09ee0 --- /dev/null +++ b/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py @@ -0,0 +1,183 @@ +import os +import getpass +from datetime import datetime + +import hou + +import attr +import pyblish.api +from openpype.lib import ( + TextDef, + NumberDef, +) +from openpype.pipeline import ( + legacy_io, + OpenPypePyblishPluginMixin +) +from openpype.tests.lib import is_in_tests +from openpype.lib import is_running_from_build +from openpype_modules.deadline import abstract_submit_deadline +from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo + + +@attr.s +class HoudiniPluginInfo(object): + Build = attr.ib(default=None) + IgnoreInputs = attr.ib(default=True) + ScriptJob = attr.ib(default=True) + SceneFile = attr.ib(default=None) # Input + SaveFile = attr.ib(default=True) + ScriptFilename = attr.ib(default=None) + OutputDriver = attr.ib(default=None) + Version = attr.ib(default=None) # Mandatory for Deadline + ProjectPath = attr.ib(default=None) + + +class HoudiniCacheSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline, # noqa + OpenPypePyblishPluginMixin): + """Submit Houdini scene to perform a local publish in Deadline. + + Publishing in Deadline can be helpful for scenes that publish very slow. + This way it can process in the background on another machine without the + Artist having to wait for the publish to finish on their local machine. + + Submission is done through the Deadline Web Service as + supplied via the environment variable AVALON_DEADLINE. + + """ + + label = "Submit Scene to Deadline" + order = pyblish.api.IntegratorOrder + hosts = ["houdini"] + families = ["publish.hou"] + targets = ["local"] + + priority = 50 + jobInfo = {} + pluginInfo = {} + group = None + + def get_job_info(self): + job_info = DeadlineJobInfo(Plugin="Houdini") + + job_info.update(self.jobInfo) + instance = self._instance + context = instance.context + assert all( + result["success"] for result in context.data["results"] + ), "Errors found, aborting integration.." + + # Deadline connection + AVALON_DEADLINE = legacy_io.Session.get( + "AVALON_DEADLINE", "http://localhost:8082" + ) + assert AVALON_DEADLINE, "Requires AVALON_DEADLINE" + + project_name = instance.context.data["projectName"] + filepath = context.data["currentFile"] + scenename = os.path.basename(filepath) + job_name = "{scene} - {instance} [PUBLISH]".format( + scene=scenename, instance=instance.name) + batch_name = "{code} - {scene}".format(code=project_name, + scene=scenename) + if is_in_tests(): + batch_name += datetime.now().strftime("%d%m%Y%H%M%S") + + job_info.Name = job_name + job_info.BatchName = batch_name + job_info.Plugin = instance.data["plugin"] + job_info.UserName = context.data.get("deadlineUser", getpass.getuser()) + rop_node = self.get_rop_node(instance) + if rop_node.type().name() != "alembic": + frames = "{start}-{end}x{step}".format( + start=int(instance.data["frameStart"]), + end=int(instance.data["frameEnd"]), + step=int(instance.data["byFrameStep"]), + ) + + job_info.Frames = frames + + job_info.Pool = instance.data.get("primaryPool") + job_info.SecondaryPool = instance.data.get("secondaryPool") + + attr_values = self.get_attr_values_from_data(instance.data) + + job_info.ChunkSize = instance.data["chunkSize"] + job_info.Comment = context.data.get("comment") + job_info.Priority = attr_values.get("priority", self.priority) + job_info.Group = attr_values.get("group", self.group) + + keys = [ + "FTRACK_API_USER", + "FTRACK_API_KEY", + "FTRACK_SERVER" + ] + + # Add OpenPype version if we are running from build. + if is_running_from_build(): + keys.append("OPENPYPE_VERSION") + # Add mongo url if it's enabled + if self._instance.context.data.get("deadlinePassMongoUrl"): + keys.append("OPENPYPE_MONGO") + + environment = dict({key: os.environ[key] for key in keys + if key in os.environ}, **legacy_io.Session) + + for key in keys: + value = environment.get(key) + if not value: + continue + job_info.EnvironmentKeyValue[key] = value + # to recognize render jobs + job_info.add_render_job_env_var() + + return job_info + + def get_plugin_info(self): + instance = self._instance + version = hou.applicationVersionString() + version = ".".join(version.split(".")[:2]) + rop = self.get_rop_node(instance) + plugin_info = HoudiniPluginInfo( + Build=None, + IgnoreInputs=True, + ScriptJob=True, + SceneFile=self.scene_path, + SaveFile=True, + OutputDriver=rop.path(), + Version=version, + ProjectPath=os.path.dirname(self.scene_path) + ) + + plugin_payload = attr.asdict(plugin_info) + + return plugin_payload + + def process(self, instance): + super(HoudiniCacheSubmitDeadline, self).process(instance) + output_dir = os.path.dirname(instance.data["files"][0]) + instance.data["outputDir"] = output_dir + instance.data["toBeRenderedOn"] = "deadline" + + def get_rop_node(self, instance): + rop = instance.data.get("instance_node") + rop_node = hou.node(rop) + + return rop_node + + @classmethod + def get_attribute_defs(cls): + defs = super(HoudiniCacheSubmitDeadline, cls).get_attribute_defs() + defs.extend([ + NumberDef("priority", + minimum=1, + maximum=250, + decimals=0, + default=cls.priority, + label="Priority"), + TextDef("group", + default=cls.group, + label="Group Name"), + ]) + + return defs diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py new file mode 100644 index 00000000000..5651ff4c832 --- /dev/null +++ b/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py @@ -0,0 +1,501 @@ +# -*- coding: utf-8 -*- +"""Submit publishing job to farm.""" +import os +import json +import re +from copy import deepcopy +import requests + +import pyblish.api + +from openpype import AYON_SERVER_ENABLED +from openpype.client import ( + get_last_version_by_subset_name, +) +from openpype.pipeline import publish, legacy_io +from openpype.lib import EnumDef, is_running_from_build +from openpype.tests.lib import is_in_tests +from openpype.pipeline.version_start import get_versioning_start + +from openpype.pipeline.farm.pyblish_functions import ( + create_skeleton_instance_cache, + create_instances_for_cache, + attach_instances_to_subset, + prepare_cache_representations, + create_metadata_path +) + + +class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, + publish.OpenPypePyblishPluginMixin, + publish.ColormanagedPyblishPluginMixin): + """Process Cache Job submitted on farm + This is replicated version of submit publish job + specifically for cache(s). + + These jobs are dependent on a deadline job + submission prior to this plug-in. + + - In case of Deadline, it creates dependent job on farm publishing + rendered image sequence. + + Options in instance.data: + - deadlineSubmissionJob (dict, Required): The returned .json + data from the job submission to deadline. + + - outputDir (str, Required): The output directory where the metadata + file should be generated. It's assumed that this will also be + final folder containing the output files. + + - ext (str, Optional): The extension (including `.`) that is required + in the output filename to be picked up for image sequence + publishing. + + - expectedFiles (list or dict): explained below + + """ + + label = "Submit cache jobs to Deadline" + order = pyblish.api.IntegratorOrder + 0.2 + icon = "tractor" + + targets = ["local"] + + hosts = ["houdini"] + + families = ["publish.hou"] + + environ_job_filter = [ + "OPENPYPE_METADATA_FILE" + ] + + environ_keys = [ + "AVALON_APP_NAME", + "OPENPYPE_USERNAME", + "OPENPYPE_SG_USER", + ] + + # custom deadline attributes + deadline_department = "" + deadline_pool = "" + deadline_pool_secondary = "" + deadline_group = "" + deadline_chunk_size = 1 + deadline_priority = None + + # regex for finding frame number in string + R_FRAME_NUMBER = re.compile(r'.+\.(?P[0-9]+)\..+') + + plugin_pype_version = "3.0" + + # script path for publish_filesequence.py + publishing_script = None + + def _submit_deadline_post_job(self, instance, job): + """Submit publish job to Deadline. + + Deadline specific code separated from :meth:`process` for sake of + more universal code. Muster post job is sent directly by Muster + submitter, so this type of code isn't necessary for it. + + Returns: + (str): deadline_publish_job_id + """ + data = instance.data.copy() + subset = data["subset"] + job_name = "Publish - {subset}".format(subset=subset) + + anatomy = instance.context.data['anatomy'] + + # instance.data.get("subset") != instances[0]["subset"] + # 'Main' vs 'renderMain' + override_version = None + instance_version = instance.data.get("version") # take this if exists + if instance_version != 1: + override_version = instance_version + + output_dir = self._get_publish_folder( + anatomy, + deepcopy(instance.data["anatomyData"]), + instance.data.get("asset"), + instance.data["subset"], + instance.context, + instance.data["family"], + override_version + ) + + # Transfer the environment from the original job to this dependent + # job so they use the same environment + metadata_path, rootless_metadata_path = \ + create_metadata_path(instance, anatomy) + + environment = { + "AVALON_PROJECT": instance.context.data["projectName"], + "AVALON_ASSET": instance.context.data["asset"], + "AVALON_TASK": instance.context.data["task"], + "OPENPYPE_USERNAME": instance.context.data["user"], + "OPENPYPE_LOG_NO_COLORS": "1", + "IS_TEST": str(int(is_in_tests())) + } + + if AYON_SERVER_ENABLED: + environment["AYON_PUBLISH_JOB"] = "1" + environment["AYON_RENDER_JOB"] = "0" + environment["AYON_REMOTE_PUBLISH"] = "0" + environment["AYON_BUNDLE_NAME"] = os.environ["AYON_BUNDLE_NAME"] + deadline_plugin = "Ayon" + else: + environment["OPENPYPE_PUBLISH_JOB"] = "1" + environment["OPENPYPE_RENDER_JOB"] = "0" + environment["OPENPYPE_REMOTE_PUBLISH"] = "0" + deadline_plugin = "OpenPype" + # Add OpenPype version if we are running from build. + if is_running_from_build(): + self.environ_keys.append("OPENPYPE_VERSION") + + # add environments from self.environ_keys + for env_key in self.environ_keys: + if os.getenv(env_key): + environment[env_key] = os.environ[env_key] + + # pass environment keys from self.environ_job_filter + job_environ = job["Props"].get("Env", {}) + for env_j_key in self.environ_job_filter: + if job_environ.get(env_j_key): + environment[env_j_key] = job_environ[env_j_key] + + # Add mongo url if it's enabled + if instance.context.data.get("deadlinePassMongoUrl"): + mongo_url = os.environ.get("OPENPYPE_MONGO") + if mongo_url: + environment["OPENPYPE_MONGO"] = mongo_url + + priority = self.deadline_priority or instance.data.get("priority", 50) + + instance_settings = self.get_attr_values_from_data(instance.data) + initial_status = instance_settings.get("publishJobState", "Active") + # TODO: Remove this backwards compatibility of `suspend_publish` + if instance.data.get("suspend_publish"): + initial_status = "Suspended" + + args = [ + "--headless", + 'publish', + '"{}"'.format(rootless_metadata_path), + "--targets", "deadline", + "--targets", "farm" + ] + + if is_in_tests(): + args.append("--automatic-tests") + + # Generate the payload for Deadline submission + secondary_pool = ( + self.deadline_pool_secondary or instance.data.get("secondaryPool") + ) + payload = { + "JobInfo": { + "Plugin": deadline_plugin, + "BatchName": job["Props"]["Batch"], + "Name": job_name, + "UserName": job["Props"]["User"], + "Comment": instance.context.data.get("comment", ""), + + "Department": self.deadline_department, + "ChunkSize": self.deadline_chunk_size, + "Priority": priority, + "InitialStatus": initial_status, + + "Group": self.deadline_group, + "Pool": self.deadline_pool or instance.data.get("primaryPool"), + "SecondaryPool": secondary_pool, + # ensure the outputdirectory with correct slashes + "OutputDirectory0": output_dir.replace("\\", "/") + }, + "PluginInfo": { + "Version": self.plugin_pype_version, + "Arguments": " ".join(args), + "SingleFrameOnly": "True", + }, + # Mandatory for Deadline, may be empty + "AuxFiles": [], + } + + if job.get("_id"): + payload["JobInfo"]["JobDependency0"] = job["_id"] + + for index, (key_, value_) in enumerate(environment.items()): + payload["JobInfo"].update( + { + "EnvironmentKeyValue%d" + % index: "{key}={value}".format( + key=key_, value=value_ + ) + } + ) + # remove secondary pool + payload["JobInfo"].pop("SecondaryPool", None) + + self.log.debug("Submitting Deadline publish job ...") + + url = "{}/api/jobs".format(self.deadline_url) + response = requests.post(url, json=payload, timeout=10) + if not response.ok: + raise Exception(response.text) + + deadline_publish_job_id = response.json()["_id"] + + return deadline_publish_job_id + + def process(self, instance): + # type: (pyblish.api.Instance) -> None + """Process plugin. + + Detect type of render farm submission and create and post dependent + job in case of Deadline. It creates json file with metadata needed for + publishing in directory of render. + + Args: + instance (pyblish.api.Instance): Instance data. + + """ + if not instance.data.get("farm"): + self.log.debug("Skipping local instance.") + return + + anatomy = instance.context.data["anatomy"] + + instance_skeleton_data = create_skeleton_instance_cache(instance) + """ + if content of `expectedFiles` list are dictionaries, we will handle + it as list of AOVs, creating instance for every one of them. + + Example: + -------- + + expectedFiles = [ + { + "beauty": [ + "foo_v01.0001.exr", + "foo_v01.0002.exr" + ], + + "Z": [ + "boo_v01.0001.exr", + "boo_v01.0002.exr" + ] + } + ] + + This will create instances for `beauty` and `Z` subset + adding those files to their respective representations. + + If we have only list of files, we collect all file sequences. + More then one doesn't probably make sense, but we'll handle it + like creating one instance with multiple representations. + + Example: + -------- + + expectedFiles = [ + "foo_v01.0001.exr", + "foo_v01.0002.exr", + "xxx_v01.0001.exr", + "xxx_v01.0002.exr" + ] + + This will result in one instance with two representations: + `foo` and `xxx` + """ + + if isinstance(instance.data.get("expectedFiles")[0], dict): + instances = create_instances_for_cache( + instance, instance_skeleton_data) + else: + representations = prepare_cache_representations( + instance_skeleton_data, + instance.data.get("expectedFiles"), + anatomy + ) + + if "representations" not in instance_skeleton_data.keys(): + instance_skeleton_data["representations"] = [] + + # add representation + instance_skeleton_data["representations"] += representations + instances = [instance_skeleton_data] + + # attach instances to subset + if instance.data.get("attachTo"): + instances = attach_instances_to_subset( + instance.data.get("attachTo"), instances + ) + + r''' SUBMiT PUBLiSH JOB 2 D34DLiN3 + ____ + ' ' .---. .---. .--. .---. .--..--..--..--. .---. + | | --= \ | . \/ _|/ \| . \ || || \ |/ _| + | JOB | --= / | | || __| .. | | | |;_ || \ || __| + | | |____./ \.__|._||_.|___./|_____|||__|\__|\.___| + ._____. + + ''' + + render_job = None + submission_type = "" + if instance.data.get("toBeRenderedOn") == "deadline": + render_job = instance.data.pop("deadlineSubmissionJob", None) + submission_type = "deadline" + + if not render_job: + import getpass + + render_job = {} + self.log.debug("Faking job data ...") + render_job["Props"] = {} + # Render job doesn't exist because we do not have prior submission. + # We still use data from it so lets fake it. + # + # Batch name reflect original scene name + + if instance.data.get("assemblySubmissionJobs"): + render_job["Props"]["Batch"] = instance.data.get( + "jobBatchName") + else: + batch = os.path.splitext(os.path.basename( + instance.context.data.get("currentFile")))[0] + render_job["Props"]["Batch"] = batch + # User is deadline user + render_job["Props"]["User"] = instance.context.data.get( + "deadlineUser", getpass.getuser()) + + deadline_publish_job_id = None + if submission_type == "deadline": + # get default deadline webservice url from deadline module + self.deadline_url = instance.context.data["defaultDeadline"] + # if custom one is set in instance, use that + if instance.data.get("deadlineUrl"): + self.deadline_url = instance.data.get("deadlineUrl") + assert self.deadline_url, "Requires Deadline Webservice URL" + + deadline_publish_job_id = \ + self._submit_deadline_post_job(instance, render_job) + + # Inject deadline url to instances. + for inst in instances: + inst["deadlineUrl"] = self.deadline_url + + # publish job file + publish_job = { + "asset": instance_skeleton_data["asset"], + "frameStart": instance_skeleton_data["frameStart"], + "frameEnd": instance_skeleton_data["frameEnd"], + "fps": instance_skeleton_data["fps"], + "source": instance_skeleton_data["source"], + "user": instance.context.data["user"], + "version": instance.context.data["version"], # workfile version + "intent": instance.context.data.get("intent"), + "comment": instance.context.data.get("comment"), + "job": render_job or None, + "session": legacy_io.Session.copy(), + "instances": instances + } + + if deadline_publish_job_id: + publish_job["deadline_publish_job_id"] = deadline_publish_job_id + + metadata_path, rootless_metadata_path = \ + create_metadata_path(instance, anatomy) + + with open(metadata_path, "w") as f: + json.dump(publish_job, f, indent=4, sort_keys=True) + + def _get_publish_folder(self, anatomy, template_data, + asset, subset, context, + family, version=None): + """ + Extracted logic to pre-calculate real publish folder, which is + calculated in IntegrateNew inside of Deadline process. + This should match logic in: + 'collect_anatomy_instance_data' - to + get correct anatomy, family, version for subset and + 'collect_resources_path' + get publish_path + + Args: + anatomy (openpype.pipeline.anatomy.Anatomy): + template_data (dict): pre-calculated collected data for process + asset (string): asset name + subset (string): subset name (actually group name of subset) + family (string): for current deadline process it's always 'render' + TODO - for generic use family needs to be dynamically + calculated like IntegrateNew does + version (int): override version from instance if exists + + Returns: + (string): publish folder where rendered and published files will + be stored + based on 'publish' template + """ + + project_name = context.data["projectName"] + if not version: + version = get_last_version_by_subset_name( + project_name, + subset, + asset_name=asset + ) + if version: + version = int(version["name"]) + 1 + else: + version = get_versioning_start( + project_name, + template_data["app"], + task_name=template_data["task"]["name"], + task_type=template_data["task"]["type"], + family="render", + subset=subset, + project_settings=context.data["project_settings"] + ) + + host_name = context.data["hostName"] + task_info = template_data.get("task") or {} + + template_name = publish.get_publish_template_name( + project_name, + host_name, + family, + task_info.get("name"), + task_info.get("type"), + ) + + template_data["subset"] = subset + template_data["family"] = family + template_data["version"] = version + + render_templates = anatomy.templates_obj[template_name] + if "folder" in render_templates: + publish_folder = render_templates["folder"].format_strict( + template_data + ) + else: + # solve deprecated situation when `folder` key is not underneath + # `publish` anatomy + self.log.warning(( + "Deprecation warning: Anatomy does not have set `folder`" + " key underneath `publish` (in global of for project `{}`)." + ).format(project_name)) + + file_path = render_templates["path"].format_strict(template_data) + publish_folder = os.path.dirname(file_path) + + return publish_folder + + @classmethod + def get_attribute_defs(cls): + return [ + EnumDef("publishJobState", + label="Publish Job State", + items=["Active", "Suspended"], + default="Active") + ] diff --git a/openpype/modules/deadline/plugins/publish/validate_deadline_pools.py b/openpype/modules/deadline/plugins/publish/validate_deadline_pools.py index 949caff7d8c..90b82418032 100644 --- a/openpype/modules/deadline/plugins/publish/validate_deadline_pools.py +++ b/openpype/modules/deadline/plugins/publish/validate_deadline_pools.py @@ -22,7 +22,8 @@ class ValidateDeadlinePools(OptionalPyblishPluginMixin, "render.frames_farm", "renderFarm", "renderlayer", - "maxrender"] + "maxrender", + "publish.hou"] optional = True # cache diff --git a/openpype/pipeline/farm/pyblish_functions.py b/openpype/pipeline/farm/pyblish_functions.py index fe3ab97de89..33ec770be08 100644 --- a/openpype/pipeline/farm/pyblish_functions.py +++ b/openpype/pipeline/farm/pyblish_functions.py @@ -744,6 +744,238 @@ def get_resources(project_name, version, extension=None): return resources +def create_skeleton_instance_cache(instance): + # type: (pyblish.api.Instance, list, dict) -> dict + """Create skeleton instance from original instance data. + + This will create dictionary containing skeleton + - common - data used for publishing rendered instances. + This skeleton instance is then extended with additional data + and serialized to be processed by farm job. + + Args: + instance (pyblish.api.Instance): Original instance to + be used as a source of data. + + Returns: + dict: Dictionary with skeleton instance data. + + """ + # list of family names to transfer to new family if present + + context = instance.context + data = instance.data.copy() + anatomy = instance.context.data["anatomy"] # type: Anatomy + + # get time related data from instance (or context) + time_data = get_time_data_from_instance_or_context(instance) + + if data.get("extendFrames", False): + time_data.start, time_data.end = extend_frames( + data["asset"], + data["subset"], + time_data.start, + time_data.end, + ) + + source = data.get("source") or context.data.get("currentFile") + success, rootless_path = ( + anatomy.find_root_template_from_path(source) + ) + if success: + source = rootless_path + else: + # `rootless_path` is not set to `source` if none of roots match + log = Logger.get_logger("farm_publishing") + log.warning(("Could not find root path for remapping \"{}\". " + "This may cause issues.").format(source)) + + family = instance.data["family"] + # Make sure "render" is in the families to go through + # validating expected and rendered files + # during publishing job. + families = ["render", family] + + instance_skeleton_data = { + "family": family, + "subset": data["subset"], + "families": families, + "asset": data["asset"], + "frameStart": time_data.start, + "frameEnd": time_data.end, + "handleStart": time_data.handle_start, + "handleEnd": time_data.handle_end, + "frameStartHandle": time_data.start - time_data.handle_start, + "frameEndHandle": time_data.end + time_data.handle_end, + "comment": data.get("comment"), + "fps": time_data.fps, + "source": source, + "extendFrames": data.get("extendFrames"), + "overrideExistingFrame": data.get("overrideExistingFrame"), + "jobBatchName": data.get("jobBatchName", ""), + # map inputVersions `ObjectId` -> `str` so json supports it + "inputVersions": list(map(str, data.get("inputVersions", []))), + } + + # skip locking version if we are creating v01 + instance_version = data.get("version") # take this if exists + if instance_version != 1: + instance_skeleton_data["version"] = instance_version + + representations = get_transferable_representations(instance) + instance_skeleton_data["representations"] = representations + + persistent = instance.data.get("stagingDir_persistent") is True + instance_skeleton_data["stagingDir_persistent"] = persistent + + return instance_skeleton_data + + +def prepare_cache_representations(skeleton_data, exp_files, anatomy): + """Create representations for file sequences. + + This will return representations of expected files if they are not + in hierarchy of aovs. There should be only one sequence of files for + most cases, but if not - we create representation from each of them. + + Arguments: + skeleton_data (dict): instance data for which we are + setting representations + exp_files (list): list of expected files + anatomy (Anatomy) + Returns: + list of representations + + """ + representations = [] + collections, remainders = clique.assemble(exp_files) + + log = Logger.get_logger("farm_publishing") + + # create representation for every collected sequence + for collection in collections: + ext = collection.tail.lstrip(".") + + staging = os.path.dirname(list(collection)[0]) + success, rootless_staging_dir = ( + anatomy.find_root_template_from_path(staging) + ) + if success: + staging = rootless_staging_dir + else: + log.warning(( + "Could not find root path for remapping \"{}\"." + " This may cause issues on farm." + ).format(staging)) + + frame_start = int(skeleton_data.get("frameStartHandle")) + rep = { + "name": ext, + "ext": ext, + "files": [os.path.basename(f) for f in list(collection)], + "frameStart": frame_start, + "frameEnd": int(skeleton_data.get("frameEndHandle")), + # If expectedFile are absolute, we need only filenames + "stagingDir": staging, + "fps": skeleton_data.get("fps") + } + + representations.append(rep) + + return representations + + +def create_instances_for_cache(instance, skeleton): + """Create instance for cache. + + This will create new instance for every AOV it can detect in expected + files list. + + Args: + instance (pyblish.api.Instance): Original instance. + skeleton (dict): Skeleton data for instance (those needed) later + by collector. + + + Returns: + list of instances + + Throws: + ValueError: + + """ + anatomy = instance.context.data["anatomy"] + subset = skeleton["subset"] + family = skeleton["family"] + exp_files = instance.data["expectedFiles"] + log = Logger.get_logger("farm_publishing") + + instances = [] + # go through AOVs in expected files + for _, files in exp_files[0].items(): + cols, rem = clique.assemble(files) + # we shouldn't have any reminders. And if we do, it should + # be just one item for single frame renders. + if not cols and rem: + if len(rem) != 1: + raise ValueError("Found multiple non related files " + "to render, don't know what to do " + "with them.") + col = rem[0] + ext = os.path.splitext(col)[1].lstrip(".") + else: + # but we really expect only one collection. + # Nothing else make sense. + if len(cols) != 1: + raise ValueError("Only one image sequence type is expected.") # noqa: E501 + ext = cols[0].tail.lstrip(".") + col = list(cols[0]) + + if isinstance(col, (list, tuple)): + staging = os.path.dirname(col[0]) + else: + staging = os.path.dirname(col) + + try: + staging = remap_source(staging, anatomy) + except ValueError as e: + log.warning(e) + + new_instance = deepcopy(skeleton) + + new_instance["subset"] = subset + log.info("Creating data for: {}".format(subset)) + new_instance["family"] = family + new_instance["families"] = skeleton["families"] + # create representation + if isinstance(col, (list, tuple)): + files = [os.path.basename(f) for f in col] + else: + files = os.path.basename(col) + + rep = { + "name": ext, + "ext": ext, + "files": files, + "frameStart": int(skeleton["frameStartHandle"]), + "frameEnd": int(skeleton["frameEndHandle"]), + # If expectedFile are absolute, we need only filenames + "stagingDir": staging, + "fps": new_instance.get("fps"), + "tags": [], + } + + new_instance["representations"] = [rep] + + # if extending frames from existing version, copy files from there + # into our destination directory + if new_instance.get("extendFrames", False): + copy_extend_frames(new_instance, rep) + instances.append(new_instance) + log.debug("instances:{}".format(instances)) + return instances + + def copy_extend_frames(instance, representation): """Copy existing frames from latest version. diff --git a/openpype/settings/defaults/project_settings/deadline.json b/openpype/settings/defaults/project_settings/deadline.json index 1b8c8397d76..3becb4e32df 100644 --- a/openpype/settings/defaults/project_settings/deadline.json +++ b/openpype/settings/defaults/project_settings/deadline.json @@ -99,6 +99,14 @@ "deadline_chunk_size": 10, "deadline_job_delay": "00:00:00:00" }, + "ProcessSubmittedCacheJobOnFarm": { + "enabled": true, + "deadline_department": "", + "deadline_pool": "", + "deadline_group": "", + "deadline_chunk_size": 1, + "deadline_priority": 50 + }, "ProcessSubmittedJobOnFarm": { "enabled": true, "deadline_department": "", diff --git a/openpype/settings/defaults/project_settings/houdini.json b/openpype/settings/defaults/project_settings/houdini.json index 93d5c50d5e0..c0052185e82 100644 --- a/openpype/settings/defaults/project_settings/houdini.json +++ b/openpype/settings/defaults/project_settings/houdini.json @@ -81,6 +81,11 @@ } }, "publish": { + "CollectChunkSize": { + "enabled": true, + "optional": true, + "chunk_size": 999999 + }, "ValidateWorkfilePaths": { "enabled": true, "optional": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_houdini_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_houdini_publish.json index b57089007ef..e2f1be8e3e3 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_houdini_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_houdini_publish.json @@ -4,6 +4,31 @@ "key": "publish", "label": "Publish plugins", "children": [ + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "CollectChunkSize", + "label": "Collect Chunk Size", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "number", + "key": "chunk_size", + "label": "Frames Per Task" + } + ] + }, { "type": "dict", "collapsible": true, diff --git a/server_addon/deadline/server/settings/publish_plugins.py b/server_addon/deadline/server/settings/publish_plugins.py index 8d1b6673452..2a60ab4aa8f 100644 --- a/server_addon/deadline/server/settings/publish_plugins.py +++ b/server_addon/deadline/server/settings/publish_plugins.py @@ -217,6 +217,17 @@ class AOVFilterSubmodel(BaseSettingsModel): ) +class ProcessCacheJobFarmModel(BaseSettingsModel): + """Process submitted job on farm.""" + + enabled: bool = Field(title="Enabled") + deadline_department: str = Field(title="Department") + deadline_pool: str = Field(title="Pool") + deadline_group: str = Field(title="Group") + deadline_chunk_size: int = Field(title="Chunk Size") + deadline_priority: int = Field(title="Priority") + + class ProcessSubmittedJobOnFarmModel(BaseSettingsModel): """Process submitted job on farm.""" @@ -278,6 +289,9 @@ class PublishPluginsModel(BaseSettingsModel): default_factory=CelactionSubmitDeadlineModel, title="Celaction Submit Deadline" ) + ProcessSubmittedCacheJobOnFarm: ProcessCacheJobFarmModel = Field( + default_factory=ProcessCacheJobFarmModel, + title="Process submitted cache Job on farm.") ProcessSubmittedJobOnFarm: ProcessSubmittedJobOnFarmModel = Field( default_factory=ProcessSubmittedJobOnFarmModel, title="Process submitted job on farm.") @@ -384,6 +398,14 @@ class PublishPluginsModel(BaseSettingsModel): "deadline_chunk_size": 10, "deadline_job_delay": "00:00:00:00" }, + "ProcessSubmittedCacheJobOnFarm": { + "enabled": True, + "deadline_department": "", + "deadline_pool": "", + "deadline_group": "", + "deadline_chunk_size": 1, + "deadline_priority": 50 + }, "ProcessSubmittedJobOnFarm": { "enabled": True, "deadline_department": "", diff --git a/server_addon/deadline/server/version.py b/server_addon/deadline/server/version.py index 485f44ac21b..b3f4756216d 100644 --- a/server_addon/deadline/server/version.py +++ b/server_addon/deadline/server/version.py @@ -1 +1 @@ -__version__ = "0.1.1" +__version__ = "0.1.2" diff --git a/server_addon/houdini/server/settings/publish_plugins.py b/server_addon/houdini/server/settings/publish_plugins.py index 4534d8d0d9b..e3092c103e4 100644 --- a/server_addon/houdini/server/settings/publish_plugins.py +++ b/server_addon/houdini/server/settings/publish_plugins.py @@ -107,6 +107,12 @@ class CreatePluginsModel(BaseSettingsModel): # Publish Plugins +class CollectChunkSizeModel(BaseSettingsModel): + enabled: bool = Field(title="Enabled") + optional: bool = Field(title="Optional") + chunk_size: int = Field(title="Frame Per Task") + + class ValidateWorkfilePathsModel(BaseSettingsModel): enabled: bool = Field(title="Enabled") optional: bool = Field(title="Optional") @@ -127,6 +133,10 @@ class BasicValidateModel(BaseSettingsModel): class PublishPluginsModel(BaseSettingsModel): + CollectChunkSize: CollectChunkSizeModel = Field( + default_factory=CollectChunkSizeModel, + title="Collect Chunk Size" + ) ValidateWorkfilePaths: ValidateWorkfilePathsModel = Field( default_factory=ValidateWorkfilePathsModel, title="Validate workfile paths settings.") @@ -139,6 +149,11 @@ class PublishPluginsModel(BaseSettingsModel): DEFAULT_HOUDINI_PUBLISH_SETTINGS = { + "CollectChunkSize": { + "enabled": True, + "optional": True, + "chunk_size": 999999 + }, "ValidateWorkfilePaths": { "enabled": True, "optional": True, diff --git a/website/docs/artist_hosts_houdini.md b/website/docs/artist_hosts_houdini.md index 940d5ac3514..c4e8293c36f 100644 --- a/website/docs/artist_hosts_houdini.md +++ b/website/docs/artist_hosts_houdini.md @@ -83,6 +83,30 @@ select your render camera. All the render outputs are stored in the pyblish/render directory within your project path.\ For Karma-specific render, it also outputs the USD render as default. +## Publishing cache to Deadline +Artist can publish cache to deadline which increases productivity as artist can use local machine +could be used for other tasks. +Caching on the farm is supported for: + +**Arnold ASS (.ass)** +**Pointcache (.bgeo and .abc)** +**VDB (.vdb)** +**Redshift Proxy (.rs)** + +To submit your cache to deadline, you need to create the instance(s) with clicking +**Submitting to Farm** and you can also enable **Use selection** to +select the object for caching in farm. +![Houdini Farm Cache Creator](assets/houdini_farm_cache_creator.png) + +When you go to Publish Tab and click the instance(s), you can set up your preferred +**Frame per task**. +![Houdini Farm Per Task](assets/houdini_frame_per_task.png) + +Once you hit **Publish**, the cache would be submitted and rendered in deadline. +When the render is finished, all the caches would be located in your publish folder. +You can see them in the Loader. +![Houdini Farm Per Task](assets/houdini_farm_cache_loader.png) + ## USD (experimental support) ### Publishing USD You can publish your Solaris Stage as USD file. diff --git a/website/docs/assets/houdini_farm_cache_creator.png b/website/docs/assets/houdini_farm_cache_creator.png new file mode 100644 index 0000000000000000000000000000000000000000..bf0af8033614d37d8e988bf1b060ce17bc07b085 GIT binary patch literal 95572 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 Z5bNbauk6me<0ZB`p9u~>B|3TG`d_-PdpH09 literal 0 HcmV?d00001 diff --git a/website/docs/assets/houdini_farm_cache_loader.png b/website/docs/assets/houdini_farm_cache_loader.png new file mode 100644 index 0000000000000000000000000000000000000000..27b0122fab3e2f1b0509148fd1c7c00246c63264 GIT binary patch literal 134816 zcmbSzWmH>R*KSLZB838_xDjjJ-)#)}CvwDbF(}(dw#luP{h3o;`c^N=}0N-=F7$E@hU_o;?*S zNK0z_m>jL31iaCmM?GawG@?rQW3s%mQZAw7=FOpQt5hyU;2mim?stFXpk$aQ;g*iX zhTTusN0s9;vGUy#-zzyz5@|AbE#bRxdp%EmeLauOWaqr|?fJG_wvZfgay-|ll}ZZwf42pf*q*3kpbCzOg#Lk0OOX;;+>AX>XUwYxnl1`2cmq4LjW%c=OE<=7W)cTmpXpqN`UbUyHI@J*ooI^I`P{HR3b_IL*y z$`uzUd2gGSZrK%hf3<$R(rya|rJ5wUHMy)+!%J-ZEs0Cja&y+%FZVC#YT;-k9vLCE0Hb4*|_|TG^hQvABWZZ0`HJE(txPP#%Kp# z5i5t{&ufsDsjB~kB$_SYD|ALmqo)3cXFa_B0_H0>4o9( z;F?7lUI@89VjY>>KX?6vdKDDe!m$RXOmGKVm(MMiY0x}euLa`(c=;yD9A--5Mx>Jm zNhNs;?JF#I$c4R>Q;ET!$16}VH*X`hKzy0q)yRTzgO1BBTpBw?$Y)qPhk!au1=##c z)Qrgr_53dw&q!)0iHOR+lqK;w#3jbrR)HGE(zi<+?du(DD${Fm;ymA^T~!^Sp*xcR z6L(4P`CA{EL(GYbNSbOX;QVL^!`ZxJM3SW{34k^Tg$eNv3KCJ%=2U`>?qv~H3 zE2V3N0@SQaxjV! zcoH=2n!bFg*=2Jr_$e;fbh{CoOyG65__k-9FP`3VomC8mI-lc$x~ObCx^FzI;hg?R zWmREiXh(W~3?VR>!|HLXMnv~Zg58Sgf0Z7+g5hp(QF z;cgM_fcx1m6kc|>w`w>zyg9glW)Vu>rvujrQco9R!sQhSPl{|(JndU(0xxq zCf}|d_H0PPyYNiwSr{ma9TQnJp$VOYbfpdw5r=bkyKj%2%_NyIT~u*RTBZyRH`I#L zX8!)hKnzm21|I3^GkDC+O-=hN{6`4DcuVLaxB38A&4i=LxL{$?vTQ*`DX1-$ru(@S z$uH-h0a~pwXs-znS|5dcQ%I%U!C^BW>i%rOic3Fk@{Wau<2_S~zik^%Fv=UJTJ)ywT%ryUxR_UE zceRUd3}lfMTYQrJ!zm(Sq@o1gnmxa|nj6%l2meeSIl9jMkH=xGP=e5#PxImPd>9Fl z#-#wk7r+4&37|1!`d;TDJn(bO>PH8A8jXSe?5EFrj59vmv=!=gQ)4eAxaAXmi#Vu$ z`GD7ugvA?bIZ3=|C!1D4hxduQ?$FT;d2x(~f=+<`kZJ5igzV3mk8rXJSmG{;6B$IY z$rQ9_V2`hX6kBd=VnEmI5~uvZ*Ch;B@BLLO@6Jih5f8eA0fzHiMs*3O#f63BoXp6$ zzM-7bNwMUr+{O04mLDDAsSr}%)51v>NEV^5P#}?hZ~2LntA9dAI?Iww6@;ekfYUy4 za#);^ARLvsq_h;%^Mmw>?N2JHoA@{=(G`6%rF3>|pI_3qo2yKkI-hrb`-MeuGDlPe@LP?}FSqZf{eI&CFbH;O$JKwwG zIqL}GcE58>>bdv^>ZdiTDvSvea}C`Q-w%omF)nf3vs95sA}*KM$4a#z&9agF145Y` z3{1YUSa>4ukvazMsYjD1bjB*&7*=ow9MBEtG`^kq7aCpVlNp{<)YTLG$GUSLt#%sr z(`;<8Mvl@(G1f*y8XXSl^n2$`gX8>QhlWr+nJ-bo@H))PVPU6v}S)!_g{eYac_#zAysq6N0nv|LDrXYbxtRWwgK)Y#h3Hpm@ zwcMibVIS%g?-F)H40!fI^a76^p@pATy6)Ro6c{~HTY`?W33Iq>{9{1F8c$eG>n!1Q)kBD&&7BRK$G|{ol`|P`9504hHr&|9eOJ zEf>xI$26#T|B5k+Q3i|lU;V|^`1=2|-l6J%)HXznr?RoJ@zKQvhftRvS`%ubczM&` zC+#4GRX|O1nG`7~An>zy6m4Q+;=9+wz`y_%H#c6sU)|pNI z<#=UIDXNYFM~%|6azI*cpN@XDtM$*u21remWPKgy^&20#T<&4!!$gCJhre5|p<@ZX z>sHvjBNEBu?+_7%;Vp%QMVPNAr7oQBn);!Osu0N+mFh>J;jJu-xb9APT2pJJ(Gv%E z1wHq171!qA8iwXxC+ZHyMaT6+Np)dN$(3~!<^7Z5e^tjyX0G~azDEB`RFr&{2&~4$ zJd?XPS2CV#VsF|a&*Xy|J}Tb6UD|qxv^NgNaz~JUF7-&;Ir1B6J=-5*RvWp^pT(|h zS6Z%e#jO7y$&O6Jo+iaB2b;RR);A}u_vxB^10QVPSDK8%ycB~yTetJD(#R)d-F?{!pri=;`eKB zQ82dTppeGjKg?dFaOl3BfOPh6&emCBZe7B_rBRG5O5RjcV6aM~pqZGwS_8YF`_K6! zY4r|E^p^JaeJ$xlsdAgI6`!*U3uzS7ne)@dNLt7z1Tkb7$;NQbo*ZeCS)4bgh} z1b)FJe3CjV2-j%V@q8o(%6UCK2n!1R6vO}WqpM5M>?z7`oBr>Sx*Sek!bx89eL+ES zJXC7D=9ghSSh97!#=rh}UD}5u?0q!5`)y%yu?IYU*I7u5JOn$F+E8Xo5EK+%3NU7h zMjKA7>_x9p`#GM;&t<>G@{QLap2Q>&#yd6zr>8q)pT9bvr#hsbzuPyn6BWPzL^6>h zTB=5u5NP9c4{dNKOuwwc#4R?7Sl(Z4^+20|mFHwPty|8?Xl`x>CwnL| zD13JbqE=#V-8IA{5)jZcEh*mK9?2$Uug1y!u~l2gI&7S_nuj-#D)<%4+TWMs_F2WQ+a=9~hZA>-e~0D$>yQ5*UawWl|jgcDkT z+*f~tRyj(Qo(|u7uHYWB4)_2v!TZ=!W1cP{XDsW!fGTgUE%dKd)p__5j5OnPvQxpH#+Y67|?GO6U#vI=r;*=V7 z3OL+XyR|*C{VHpk@m86sak-!$Pu23=A!RIid)RN}T&64Fb{)E1rTQS-`tU`&L)s0t zpjkXx2K#YSfoJ7wEYz6?d3Ur@m~3+X!#P{HZ157gsLwI%6Gu9$5lqNo8uKUNHLtU6 z(YS+pO_~@A!N=IRI7Ge`A$NP*Ji|_l@rE%kO&8P#UpE#2pKZi4*R1=C)_pj$o zX)W^2n=YoPc4z(WcGAWB90hYrcYbEZhVAD%&EREg6O(4c%Y*qELUA9z@6}S|S!5bD zCdM6na8E39ZX|K|DZby;LG@0wVY7?H>H`W9x1~S?4rw`vdA>@^Fc&k08HNRa@Tl-R zZgIoce*HsrCF~Xz2L~sXSQsOQkfZ+;w~*7~P8o}WThCaW0Z^jMlqKSZf$$rC(N=ZG zf%mYUS-Y93dK=2+E)O)0&Ksoj8?=u%ry4{ewl5Kx+86ZA!GY$gy-MTB%t1jbqYY~}J#J4vJ0H#7SW>Sv z=@cU2w=27X{(0Bi;CYK5N5VU6a&h+lTKckKxIv=8I4is9GLpxI)^L9!yB)G70j2dq z(ycK9Y=>hIO53_dF$;U1r?h?N|KfcG8TUmX7H-$a#$>{NSRvjo0IjX}W;m`pmv*p+ zV7O?Ui0`=s#6FqSA03`8=pk!(tdMnc@3K6VOZocs=S+TQ(D~+I{N_k-iHQ?ka+zyl zK}*1Goxy59CgE%i8{Sci9}MGHFQsj84aisW`pci)bWDvz4SR#MVEFQ4WYBtk<)p^_ zuYccW!+bVuBB6Dx0gox9S`F(oK2Q%y=wndqiq4O=Ju6!ym6rVQnFht2gXMdYw4Clixv>S#!I?97}Wms@hiD@eIyd|15A# zYBK`8GjT+8tq;AX4~;0jS}(L}313&KNd_KptKMjzdLxPCAEA=Tw+RnLvw!%VFLjXd z`!xY^^b`A2DaFaj3DB_lq>QrmRkQQzFZc^I@J(l)|3cRz2Z4l=qKQ^DP)bS)jB~n< zi+gZ(ZsD!zd`$5Y17o^052-AP$qq}FO6}n!_^EVmQoYo~_#s!v8u{JRjYaa64;sv( zwF>_GD)Q{%r27dM4uO3N60M~wf4s-}1A!gdKl}>6fbgN%UEh>6jXTViML?Il%AS=9 zDl!I>ESn7Q@%K#cr9=En zY;=I+ONW92YQX8l)G#_W1f7$a*}$+mxvdsBt2+JsIox$(J$7M1K>E;b33q*c=sS;1 z0K@BZf%Wa}VrO_^p~dW7U*7<=qG3~eYel7a z^x7G@#O}?^%pm=%fiNX^_g!pLN>ivgo3!!g5uvs2VAIFcBPpfIY=^(02BXI?aC#ay zPaBisdW3VfT3;m~++4wCJ%J>7ZM)?~xbe$S2+3 zSLVqn53?wb7x6l>6?uB_7|P|Z@$$$yweVJV{UK(J_vs|3-)o>4QfriXwGZYAYVkPa zD_}EnK}AEUn9#wRj-75l=>Xnd5Q26h$AW{p-7X>!q2lN~q4#)`95U)EV7?`d_L0jp z%zpk76}>1TY(=-mLE&SP!xPbBu-}Yi(sxD$)E!njQe2H4#O7^`lg^)AuQ6;yqTSW7 zH@SPcYx6^I8IZzkOI}LT_I1Nbql;p()Uz&=cB0^iwTPR1z(wTJeVKk|UB>m) zy~`dQaBvg;prn3$^u6BI!&7Rp&Pw}jelcb`a`djuAngQ2e0;1wpQZ5@y$$<9AbDGmbe3PEO`GGv&nFxFMNoJFIKPc?*Db})T zBEd)ro01DlDVtqSslhXop)LO30*1~>)JxLugPxEwc7vq`{f^n^Elc|q9VZ`MID{^O$t{b zrR}6&8vX5*HY$%rp>V_Y>ML%;G*^#Z81GbJ(z}qpg%*LnfxbQ*yf^Z%hAC1=gBA3} z{I47zTp#^5OeHH5?IT(r9<%|!5NKhAD#e(tq-uh0wb7Jn7Q-wDaX1En=5}39v$LXS zuU3o4H8ui80|n82{Ti>?o$#$D2J4K$88edskcEJ_%S@4 zPO^P3{#r(Cd&o7F(;c|R!QLfc4KDSK*E^9!jl~~&r3mManzd1jc4idn_y|X7<{zE1A%=T*_#aFI%*- zU2f5MxUkZnZ#>+58siplqPx$R>})%OYNRo0AcbRQR?kE;UWld%FMiu20v{$za(_LJ z4DS)Aa(Nh5o=z*ysAJoTFd`Y`70ODu0>@HlS^3IdpL2^#3+t0!6nP-OZ?RRL1=D7Zs zujn%xuHH&74|YdV69D!JraW=VQn!_g;G^FOe=H}-HC1FlZt}dupzmv26iiS`fw)2 zmQ-^svfr1|IKA$=UKRc(1jEN7`;8O)a1|*wFAb4)T&)wd9?ObYtS{NTIkNz+|cZIC? z2Nf>3J|`4!ZAa5{5F5IN&C@s^H|u~MSQ`}D1a;!_*!jdegtMm*{88NFinS#0D*6D1 zki*5!gq#t{)l;=`yW8n++_4NUmCiKB_J=YxX<&r$EwiVSDy94N>96s_XszlP0+xt` z->+eqVXawRg6oIr_%>>}8Z(;Eo008Nh=mlm<~7|{Lb)tsvJYQAXb%994~mjs2(HTv z_JMvbZ!4wGj{E0-K*7Z&2q%RMui^PtUVdTbf2^(L0)5RdEW~$SX)A`X`tV*stj8bK{%10zMn5J|%lD^eDn8Y@O8I9Y<37GPFPnI4>6_w~blx=}x?2{>>rTk?;`~ zbBy33&Lo8S%dF(2GazAZKOzf5UsRiRR@b3!3nBvd4}MNpI4{-pzv10<1dC(sPG;*~ z6dB8OFSmjP*MHq!fDY=5J>n|;&+zCPF>`q!XCx(lB?wT)=iNU+y--^9Q!w02wCS-C zlQNi3iG$0NQ?tSv@KEQLtcX;*1v@L8`VmCl5ZD!Ck|bzkW*^3KQ(qnlBnpczdY9O6^@9LlXLi-H&4pvGvbDgKyrqx-?Im@Z+VPM^kc)&362 zVXZ_kgWu^hbvOp--~}PaW8`@7@lPCaCuMML3Nd04Lqn6@XTB+uImIzlS`p@zO-6@= z^5+jlxgKWFWwQim0#;wqXqrdNzHf<@f$+TikD0vy855XjS0g$83{$2262Hk<& zW^ySUtmLRwa>)al(@&>(0mD<|K&NFl>CmPF7p2ppwzRdBHNETSnM~=+Os3yxJgceT zqnwHPi&k0PN>FQ94-ZM0`ym$pjd+?={*#-h+-c`h0>$({AZHzhoSco%{mzG*&^~>4 z6_WYEKhHbYx9#keCUrC)eW#`;Yv~k(w^O``iRH&KIXG6cZX&L_gv(0ejFydlm_5!f zG}tA>qb|+<0~uOUh19)pAjE5Zy!f@cnqqcWCVyhp>|z8iRjwQHD@sfD!0zjpreWNx zHu>;Zu`X{~TUu!%V}uJ}S9gqu!{1pLK7>j+f(o2i5C{-JcxV>Yci^ANn~?ETM8g*3 zL07Gl}fK8{RPUZAwy_T2UXwjTfF7$v{o9JoxzgqqLzNPwbvs0wy{-^+0S9-&co?z;`QAPY|pA>!1-X z8tEV4((b?bb^*hy7txpRM^E|w7m(iD|9`=7L|MQP{V|2f9TD#LcHc+tUs5Lp>d}gR z=vEP>PU06Wes`y$&E(gqP@m%fcgudK(J@bq`rp1gg}*@K|1)*(htSK~Sr*NrZ(_32 zz+MDgbXnVgkoXYy>TfZU4&o99@->eoH`B%V-3bOco#m!6BL)OQPJz>u_%B|s7WUwK zL1ijI5%@KV0`0%>Iir%tk7l_z(6&asfiJJBG;omS>5_m+hm38hQ7UscM_1~C&nc1L zR4kDF9|0glU~Z*I5E&B_Lo$-WUY(9NRd^-;BKj8NjdXKY;QG#Se)_XjF|j>iOQMdz zKp5UXU@GDTAIG~{xO^7(nk+VDm4uOABMZ_p54ME1_ zFv0ni{Zn09X0VL=_MS?F#253e6=&ieuE$Vv^Pl!RpRa%6|JvD$voo3Xg8o0p>mkX5 z!2M$SC*!uLPGN67GOwpb@q@`-2V+er1wzdQxdurwY z{)l#`2SFgFzX$O~b2&Ih(wIV@1qb)pn(mohpB#E5&9}I{KmaT+r=~PoT-TFK6jL6< z!cl32&YyR9pzhJ3eesahp^Au#y6U#`O}x~i?~bunA+@;J z5r-vJa#Un_*S|2lPOY)GW@hi5OfOGey{KSYBK^cc#Kl$O-%BRwZo5WPyq>>Oh>E(Z zWkpQIlzZS<8|xZa!_krTLt}D$RzT7@4AxUN zMRRtw2Jtq{^V@t_dI)ND zFRx?I7T%{Lo&@(_1|IEL--%7cp6qT_4^s|$x^?(*;<#e`)4mN`{#jq|JT%Smpg`fwGSQy zMwg@6O5+R#mx(bQX>1uA6D#fC1RW@LIP_x0pO)jLB7tSMt0pF=m*?7>ADn%P#`@`o zfk~pf#Mke)b_+M_p$wV@S(9qqCwwsk9(B!er^LJj5G1OY9*-zy*HEGYy zp#BPz)u=`K)jbZ`tkU*^w-&!6f`ExEEG#qu1tKWAGK+bQd-mjWY8ec=wyDWvVgB>o z(NYSVh30Fj0bp)ar~lO<9lE=~uKFm?6WQ>t(g-)r?<+>=lDEWTl|f_D9gAfacsDkAJlXcF2E;hfnF^0t;$uYulrv zKNlk%O`(*u^2K|j7!%hGk0>71*|?_+(wyOUUe=!Be(=8Sj*NeCF`s53U$+=*p%Rbz z{p$Qub7p6&gZT9yoF@@qAM9~zX5H$tAU*M5_?J7wR!x~e?AbGx!CKt1*Y0B(@zAjA z`~G-x^?Y+dfOC-Y;2y8{mQQ-mv_CsAebX??qcbu(alwBg>^I?u?V_|IGa{J zVX){_59D;-&k9O@`P%A{iOAH`9lLJ@mAd$g#un7<7HbNemiWpHOVbR}&Wuwa7Y^37 zO{>w}x#Y7#`WStqyQ=Z81g*`@&0*79blx4b48;!TTh%e!jawwNb_p6?!XhxrBKMt3 z1bprb_AaXGdOVhMgfz&+oh-If=GGV#4vYf{1m}m`oo?e5$|sTK_kO`&R-Abs}Jbh-yO$bL;8OM#o=Yyd&^*Sru5ugA#@6HI6t#Jq3$_KgZHEf>_*u8 zIL~O2J1X|;09}11MnS2JtgcfUW<|uS1H0VO!<*xKN>|u{Cz+@h+4}mrJ-nlpN4-0U z{Ne8K#J(-Z3zx_2m!5!-;KoU}8t6dZYBWVwS{k{{`y|#{C9_O98gW=EKwAn4meZLv z`Nm6gwzFl=4Vvvke*ECf>UPyIwvD6UB|{{JACd=Nfjzc)g0@Eh4R$Md937`wZF?2E zHP{G)6yFI!QMR=$Gr%c`O)~-%4nS%Tq0sQg!>+!?`dwGReMeZ>xA^|u9EZp$n#k|m zVGWxtlD&Qc{yStyFJ5Rg*y1X~|B$!X@mH+0dFl)X$Ly$p<8 z59Wy8za}6EPh-nmPt}#B5}_tT)WXF*#b%56DsI3Krci?)E|IC zcMau7*{tJsxYrlEXES)OOH3d9P^+H`E;q(R6mk8`HEdp=Ee}o-wIR9}LU38xCI?aR z6Ee7E_fOX5;LRoB?`DzXaU={G~cm{}s;9TsbZ@^$Y zM;R5+GI{u%#J;!t>Dz>Nn%>gtQuE25?x$)h&31Z+g&$pQbNb!uEP%Jw*=orYXaf#P zfQ5W1)PlsTtcbV+yFs&;vOtRbc$U`Zolk#4Fw~=y=#*>+2Lu|S;~BiNRR-X*lkQSn zGO~gltsVJtNC8{1{^l%a#*hT~xOmVd4f{_6OPv9de6m?DR-h`cd?6BX^8OLC&+n8~S5ANnb++2LA^+JOc3pEXeGoff<5v)VlVa~?B9kJG% zKOv8Qw)TpujR)e0!;c}YO(9;!G=l+F&N@5ZcM4`+L9(y{3_iFQ4q}z*%Yc42ki26qmj~PPq6$2krRbxnz1*k)gM;2HB;n zX&OJLpq!5{>{iRt?su;o!U=420ITcgsWfcsRB0-pkZya9e;=d>2w+1y97jP}og176 z0y6jQuB5e{nq-5ffHSJsCYn06dqFElGT%Fu1wDU+rLS>14LCkLjLIVF zAb=SR0Ie-*$Cb8M-_F-dTS$F$0TwBy6_Yu_HC6L$uojoDM-!`kWE3maOn&Jm=0{}( zsCio!$(8`&dEvbG+7nK0fRM3OsC?Sb7!I+T9pbHD#_Tuz0kF8ciRAr`+jFzU^GU#; zp*3;f$d3H5_|roLlIrY2wq>=~roK`if#! z4Mn&_EjLuZez*MRhwiRUaDbl(?Tc!o*1DEY`$-sBCM~YG+oP#k`ch2@L;b7gMI^Wd zxp%wW?IJCV4|lu6^5kR92%XQn@7kV+a~c;Jd=9a)uSox7T|F}EJ20qaf2hjf)qXa; zJhU#Q-5$5}1d0CrJJz9swTd;{Tc#=l+z2f%4GI&?N8|T`E`|as<~Is~CFX@#i_&LBNz{erMNhl<(b`4zvq$cH@E!4(KE>Fg)XrYuvfiWwzxPFD$2-cQQs zoh*)me?-*RAvE5F+z<+Uzh-;dpcI@P@@iJJrwv>Qf7FpWm9R zj5L0j*KfG@2YFDA|jDqPR|n4p3vFu{GAnis;tjqL7h zG{lKXt`gFVa!s#jM{3N^M@OdVl(@W)J8sr@bAzWS^Nj@<&`Iv>O;_JN@Mbeg4b?>O z*h~QUi#1=#1}UT}XU;Y)HfXg=BvWl1G#&z)fZ8;Pq>KL7%HkNaWdL&D#}}tDUOd52 zh^Ob7N$m%eut>QwEE4phA(ifj0%L9eYRhq=?`U#BvccmOe=rKg)aGonY0Zt-7mT-j zE4EqU!MF^nIogsW=y({zwww%Lo$lKm{|ERIy=O0T*W-?`r$?RVk{55b7H>jsozs%? z_qTWwDIhcVcN}ZcNsS3B-*sKmD#eE@+kzh3+9VbBv&>#%wgbJ_GAohjbgNAo{je1k zK{bFfbrY2Lxh*$d|@yj4bB5 zZ2Q3nwym?tsycp|>B&;$A=+f<(n*l|%+wSY#3m^2ZtUst8XZ8-TcRnnOQb)9Cjipx zBEz;j{8%ZcL$&+C%p`U3ZTU)#?CE=EAw_cUG;VG!k06y-!+JG7j!84S73 z*5*okR%Pjz#tw`1t6WFiNyCT=g2kC6ap_bURLsd-0f)wS26F`5?O5iUqtOECt^61H z5*P_32|0|^$w5R=<`#RxmZT{&M$@j25u-Q#2t>K(L-@uFD4OxndATDRD3$|)ShNP- z^MSO>!%E`Vyql;A71P2ZzSX_mD(NlbW9Z(1-yOOz|77f59j$>=2H$ku^-nL28a7T* z6Y6O~rBR2c6HHKzjVM&w1*OZaR&~;mn`v!nz}5_%w`~U^g^C1P_gz;Ina#m;;n-5QXy!4|4hx|anjKlb9vdas-|%(?YlW!}m+ zrMLa7oh5$?s&=u4_c))XdLV*~OfF7ctfn9g(tGZ`oPAMl$OHF~ucka|{EuSevRG;- zo9Hsy!NjQFqeUw6rj%M~);(WPHm}nytr5lLC6;?<37;bq>>DXnd`$spZyFu+Po$;A z(3+Ag=J!y9KGdk%lI6-S*IdHK zNBVd!+cwy)BGX+Saovi1;}MgRO@;pcq3*egKzPt|^HU}S>vHD;&*%`0%T$|+Qb1W} zZqj%fK;A}$x|Jd}&WnG%*t46Sfp>R)_S@y$JrSqtHrJF^Fu!+9{h+KkFqOr z=Sp35b@hPB-`+2(u7Bh#pCyr$x6%`y)0C8HRM|fqj(BTLUqoo2h%ly#vNJHWUc3@j zP}b=Efk}v4o+}>5Jp!lLXW*lyeR+%dMjc$|zB6Jqqn@Y!ZfgSnR2?iGZHr)B6%BPv z0v@I!eXcvbPv;DiXX{=i>4<;)Bhm4AwS*`cq2jAt`z(AXBZ6BDv@9nwz0;JrICSaK z-JG5PCj88MV0I9Z4Kiz_P0!sYl(#$5ch(7iuR--3h;R+-jr^;c&=UCww@kjO*JZ{M zBjNfBkM~56BR;WYyzD^-iBS#4PucB8Hc29~R{`pxR!6MK?vIMtaktiGbC7{ZBB!?TcyXZdg zTH(5}(L-I+J(OiMJ(GKLM+o794{93RtS07AuX&^#r#A-_4Sr?|mt?kY4gwhV6Gda&E&W$)v~rA=j=?J%Pw8n3^-wImKh?d<$W|a&K?~Pe|6{Al*et_mb;56fImr ztd|!&Bwe7w3_-&7OEok=zz?oBMVz|n* z#wM)Nt2*y8u~#XctMytd7$(`})R$@+JP=*3<>m($30vzZpDa8&MqqwG3ZXDW&{FX9 z1DELR068-v=LPEeO=7P=@XoraARUX_#8E3kPYu8Ec?zutT(D(LS2#trd!PKNR}=4@ zoh;da&9D7DUP0%_l1>o=ROk%YS#o07BnTRG8d& z1{h#5r5WB!)s2K{_sn=si6ozXM6pOQDwos@1zh*p1t=7?l{> zlYi!G&ww;rlk7Lg%OV^{1$+Nu5Jj|F+MvDRTctE6a(t0{BSWa?W}Vf2vL>%8XiB$I zPs3_-u1rIPcW$j*OYRZ(vP{80%-!*1u~V(l${Nex>qK{ZdVhMHw0V|`0eolbv#t+q zZ?y^VcS5y$E1$yth=stlsvt&<{iedo;sQgyAeGFty|uMjlM;=bmHxfmO&i5j^}+jk zNTZZr(Wi-WgJSMB&p>_HFDGpVUhWCXbV6Y*h#f?^tJM+B9hdQ(E zM@lirgpoWY1;u;77wqZEFAWz=yXomB2YHhUrvei-`=S(<*~P}@<-VMjLlQ9kPn*!2Z#BME6QN1tp$zcZlCAt2(pmQo1#xTtd}XBGRw&^ zKC-S$jzm5-F1kn8VgBQJQMAuf+`J=+n4dph5PV%Y5Is#sMz-8En8F#@e@VyCz07^C zl2Tg)Z7M6X1(3_Jvvzg{Uh=gl^S?cu4=(1;v+TNzxCYO!=|7MKJ#DI$Y1K5@6B#%^ z%vT%Mj1=4V*Fzy6_kM>Q%)7OgJ)R-TlXet(x2EY;iob4i*?tti+*<_zg1H{xgVum; zd5~wrobzXoA0s?gT$%*B5VoXvwJoBwcy#WHK~;}Z_^n)^*EYSY9N&p09n-#@CU-;8 z-QsFsNsi+yrfeQog=4am6z%*JFOt!8mclPC?$wC2kdED`rAVCmK7n+!_2TUYCG^Iz zB3m5IfxijwTU~QPzbsI8Jfp;Ze44$WwbyUp8}hRv$a?cyCgj+mhP)ZkxiknArx!D+ zG3hL+bD(pj5kFNxd)t&S8BcaUou7!zbiR$Lp8eWsdTD9w7{1V;4=_x7mV5cB(`q#L zO#ho*>aw65_adU$Fec>1eL-;dDhR=)Rm_2WfiXrL3ZHL}vn9aq25q!#26UIi$^!Wn z_}VSIMi2or_l2XggX3b2yO9D_6<~Cy%%!+KPA)KLJZjSnmccQL!##c9io72*TMbN3 zPCH@rU@}fNY-3N`9n0>P=jEaeaoYEh^^4_O^$soHJ=QshjNTlC&naomHR#i5l`@m; zBg;-GU0Qyf7US>^zJKKgsl_iOx51gn>FFdFcHQ00b-P${K@{&L)Vt#PykUOXuKm7M zQa|$_$~^u@cz8z8xO@z?%-4MjpzL+d3IGOtx*tB55ucjbNz*rEw_86>{0%P?w98r> zUR*SnlTBh;-`+1b?g%TlC!HJG+!~M-GUQ?)j?*-jB8hIkbm&BuLL{Va;twHrl08}4 zXQy+fhR(6k+@s@%>9`brgyD+c{4i8s4NF8#$E%fR+%w82Kzo@^K-kX_mCl|7N0aQ+ z^|`MQc^<36K0*x|+_sOlDxDXmS!kO0JBti*+B4g|vcf=_fg?u5XE~%N`tH#03MDCp zp@AUrCwF+lDp2Z!6vaKxCGq1eXSHsTpPj4;kDl-@(LJ{iy+O7qx0WoFWf&vnpkJW| zoMbT?lzLH_>7|V7u=_nS`2*g9A7W>q<7JzY5Ty=EueF^E8^tvZCMUzX`K@6i!qE$| z#_RPizDZluO#~*D7tmUFb6Tk8cD7-KAWsdJuZbV^-xI#gaHU`|s%#LmUg`Hh=f$5n zT#(dygipb{G%K{^`tEp&6GCNSUiP8Kt70qM>^JT8WVLLzncHHGL~pLBgPZ8)vGi%(m`F z2mVv{TAI=3PqlQWCYn}JlF}n1Te5HsWL&xyDt(MX*;iVkQc9B*i%e19G?IjE^pd6# z@t&`dEHZ9AM)?2jswLIX;P;YHbgp+qTG=sM3<=7bLD&;x} zdy+rV1~zNlm7hF!YH7E_Hy;Z!;vS*1B`PI%@WQ?-p~}~JONbxy?WY8?^U_y`3f;}) zG`1+k5*q3eNY27Py*u9p1O!0LcADezAay00^S{imv>mSd0}*w#@yq~iraI2sk=LX# z0_e2%Pe(A&m?pcVC(UmnLh|tYQZ<^>n(oLD zgev5QgEIuAQ8N5+N{@CE8xdqCFugqz8pmg7UKJ-@P-<-5gd>ff)ATsDd9dGmz3jED z+qBtwF`P)y%*Hbx?L=#B{Ek75%J$xix4I51u3IxuaAcM&wy+{I1@zHe+Y;Hl(Bk9C zUfC;bP_~|#jSaA))2}Cl+`^hk&6D)MT}vr$1yV(9s>0>Pl@N*da0Ph{Aw#!MZXum1 z4x!p^I<>M;ahRSRj|M}JoPHW^Y|&BZf9YXrMGfrkWo8N6K4buqI%&GH)c!{qdh<=? z=h6fMv2h`x4e5-VccChhGE_S~FPQQicM{R?l+xKE*+u>H<<{W#d$Uyx7=%oqNxFXs z%fIbKL&dIU0=GNbAsqJ;Ug=Aa7T#JPp6cv?hG0cB-{+9;@V+*t>*@ z)v5h6pCTvPw`?XzE?weuU8)_DZ5P28U|rYUIkwKQC~gD4CHW4I#|{L!ykjKd4!R0q z^bmEr|2_zwi9BhAw$wNddh90Cus9o9S8G*&4zr|bO8nL+|BUnD0I&>Sd!qePHnOD_ zKwt%Raa691Eprq0*WxQcCc*zU;L&LJYW_>OHdRs`sv$&PS(mC-0{*afsfos_--MT+ zpYK=h>6GjLoWm1Ztwwis0bJY6O%6WPLPtEV8+?pddOyw!PXkiKpD;9$_P165Q7pjO z?QKTIb(olEwy;0*Qq&7eqge&r3{Nkq2^ZXgt34~9pk4yiJ}9^q5k~&Bk0i(8LPMdT zFHwSqcQ{)e-dK^gzEvLl+vTVFa*MB4uEkc4pA7?uyLNR zblyIi^`D1doXyX%-K9jEI*Z4W3uF6UY=SP8HeVYuA`aq2T5+-Ns_PcZOV|rLpQ4 z%x^OIasS(@SNFo2*4p%}r}rC?G$J1#V$pr=_DE82yd099%+p~%_t~&^<43Yd&UVO{ z{Fo-UD9zLlk9p^gQ>D>E*|CgHcQ-1(=n z<8OJ!fdasa_=uc*J%fX?!(mn;$0k>*Yc(*T*^u8SB>?pb8@q&g9E;joiCCwGpN~(6 zRM~TlQil9`xz(dkd9hBw=adSAfE|m{)b!zEQoPKl7^9%D&<%C~__RMq@cj96g#P_p z`5CraRBhTGFY13)c4pY@Y&Nmhqpe0_p;+zMt#(G*U>>p7!&7x`(>GE90*^ z@38ywd*v<#zYik%GammBX>T2s_11NND+mZkDjP;kl+e`417V#i#tjv=3ecg z>E7hd_))O^P$8b^cZE|kFNOVlqnoNBuS_BnT=YBXZ^V;Dnz`!;@bG#gZGM|mHS`}p z`Y;;xt0~jDd%Q$IA@8OPe+(}E^U*hoC03}O#c3q{@XB~7RkTY++xGet*F$E#-sL0* z+B;XFD*Kx&fmM)Gre`bpB@pVnb)L#Q3<)9$;!&I&>pWIiB@e-Ow^W?&?hll zeQR`+R!$AtJW~7V2pi{h6pLrEC(T1U^e=EK}K;b5HdDI?cR!!t-SIVF3Zu__JZ zP8jOr1N+lSWhin(LbK@aWsYeqn=;DmYL9~?k70>P(ISY7zQE*{hkdCk- zFTJy4a@U@A;3#Pym_TWL(;DRCoko;aT2UitV^a!&!4k6=DTjl2?XlE?BuFmQ}dwwT7mhEC7$nOi7`y$a)h$Q)t* zT3*@n6%0V*Ib8`daEPZ<#+xe9=>mw3Q-vK+rG?MeJ1!5-bGafuI&|c(K}Pq~*`SVJ zPld$0Bd$o}PZi?JQ_RdvPnE@847h^IN|;`JxWY}R^`e96qlTJl-KNX$~;8OCJZWtT0tO7USOwxRWMoDsS2XyInv3{D{?XB^+vGdWJ0pjQ|Ti2(D?8OJ*yJbeVO-O}1tcKj2=zBpyeUPz;(G(BUaN14%pM!!z! z6Cg6n?^r~5yqSF`5<(CJ`qKKH{kYp@JI!<7nF#)gRB!ftn$>24%^xtjSt21q7sd0X9ydD94!6!& z1gGamz$LNRF`6w2vRr7u*z^o&aUc8QicXDZTYGvChw;553|Y*^pOcD)@E<@L4+o}n zvL3!XHCq50h0Xn(3k{^Eqc&v1rGtZ#b%(7vI5=eNj}J|E!&htZN9*`6S8D3Cu8w~dEQ*ZsLdiT7UMxyOR8Q z7dT#LPXnp-s8>xf!tBV0gHkhxxi^;9*YCtAzkEc$Yh`$u)a=&w#$l!8dH8cx5T7|C zzdgCk;`jE+gEYd^=`@AF-bnAkf%wZx0I?VUm{CrlfTZW$8O|IC<%Ay0)}Vp)Q*kA3 zJP-0xwL}~ELvpHB+S=Qd*VBQYj@8G(X98><;rI*GY;SukLEc`e^Hhwtv7nNlfFF7r#;_`NJkNW>toyw24<&6WGtmCHug{WU z>^{zLy+A?s3lB%TwOebOuSXQ%wB0x-EShs|>AyV!sz*;T$a`?u-kj5dH6R~}W+GZt zf>kd7XK8;O)vYnKQ)|Yx$5jmz&~w5Qg?7vUguU@Fep)NJzJckNKHoe z?CDdNv-kP;9&(0vppB|6-RCMf0wyr9w(SA6YPg5{y z)jC6}dj7TX%2GV?GiJ1KXlaVsTPH?bv65N z)zH+CiU@6O>@OFEXG^>CJ-m<9UHYD;z2fUz9mSu;Et&>8yeFfEeGDK<1%j)uz}W@l z6DK#wBB~$A6y=zZ$XgOXm@Mq@^wd3%R#4p&SYW_|eu`Nn#f2auEc(l-B`W6hHSKip z=zB@C0#r%-h|#*>7pCMl!f~XDLGtqQuJ0e8E%|lEW~cZ6Y1`zUmYs4r4v*)*85zs_ zIN^Go;1JFn@%y|EM-HsY2|Q;n8#`80(fC>yyTblF7K7yfDF*l8Su^$+1h<&7ZBMQT zgc=jSfg?*E4Srpalf9E*;;7*%$GcJ1glsc+A=&a>bTMX_Kum(lY+51BbW-mQ4*T_` z=X@7`j|A#O07?=r8i!2AJG(5rO0V`D*?4GJQc9!R16x|Nvhp{6UIB=t!N4+J?Xh7* zk`)+D?oMCs*IOTL#POYBM%SIJFZZF~mje4+43#ooy8AVvObUC(P%K0x>*!q58gLzC z0}6@6`lbG5lNb_%Pk4x?-$f)4^sru=tP{A`8L^=5&KJce{l*i!)$K)azRH%l*2>Hs z8zT_wUyDb6)A|N#|MKs`(mMmz6<2NxM6#I$^lAOK%2>WI2W=A*$^cL%LAZ7JRp4LV z3LqsMdv}B^=^lLPl*a2q!=hFm{r80d_aTxtEsQ_Y7uIY%50@{7PFW&d@INJmP<8jD zw^07MMZPaK27dgoo4J7!8Sl;M*yVB`ee?ML_3tQmAUT>WO74#C(n)73b1Dr;Pbaq0 zYR-uN*P{*Yx2xd}|DSWq*^y#xu2+s0;dk3KgXF30iQz%l?XYeKGCL1I$ZYo$C5S@4@cTOuSNWHeYc zQ7YEZ4xN1qpf6n2_dyeV@tfpvE-o(1_jfmeVPSy2(oOI$=_ZDd;omd(TMQoFf1dz( z#}@v#BP*XINfOqSsi@k4yeN&TZQ20TxPl0Bs}d2k=eBIBUQL1*;Ud4SRZ^P1uTP2xc!owY7t7g7fk#4P{x1D$qQrAFN&}>~NPE_`UQeIJ7waW? zz9^qLil4SKVS977Js}Ltb4y!W`SD4$b@drCdow1(-+6&FoE@LkeV?zDkD0_~ZFF*U zlZQp6hJJOd4ygfN<;+j*@Ojz=agbvMHa6AqLU~wl$eAS4k)oH5m>rVcOFmxfRlju) z)|&CutTgeSxQbz19aBv{JqAT=JRhQ4Pzy-_;q4eGb0FVLyC1qIDmAeKx^muroWAN-b zuZarDHd<=3u1uwSwY6llpGNhQwg!VEQ)|z(IK4O|aWs2e+Q6u1`45cFVkN=>aGLtF zExN-hLzN~aF)OVm6b8(vGyNb@4OGh1q*E_niG`HeDZ1vWAgAEoTK)Mn%yoI-_cYMf zc8oZd4!Z0Q<{2@ht5|#`kfyCgXgu@ch@R2kbV51G|4*!j_aKW2hC^zo9AH#KGx=YF2mD&s4|IDq@#}5P0)?xbD~!#m!wHU|>Abtz^pzfctuTKvJbh-~!?yr&O<7 zT_ZbhI-&vZ8{s1Up&7VrkFN2p-!i>b0F_mjcmzd$wdE|d|D~kCRotYi58`CCU}Ko0 zw?$Azg<|=vwWX$PDz{&bG*MHJ33T3>fX`7YU7~-qJV)y4dG1ikNG@i#r}*AY)Sk(K zq((Vb#q7}qWskJJoXztE*Bfp3<1ORgRrYM0&9_lWPU~|V4JZ3<3EYNAz(W_L<9UZQ z#5)BUpjOX(230P~IUfs2tSHr8etRmp?tdb`1L^`ZpyL71;*=!j>GlXLBcr^tVLv;K zdikq@slt(sgz|9iIiB%L5KLsWx;1xxxa&!1)_ux5&g*75Q>lVmkSUuEQL8jzT<=c{ zK6(jJ?Gil!hGK4WvI^-1H**@o@ccJB?=~oX8`84%S>L-z>(|qWo8oS1AB4PX4Pm2b z${>tKw*7PP67XU~+|O6p%U&@L(na04lT{fX@P>FAzQ`F$$R9*-I+zn!@kas2?5NQ7 z?|X`-idW*8n3z7(_UoU3WT3y_aJ8*<5kkNL6M#w#SgQ0KIY3_bH~nCw zJGsl@d^b=toa11=T82p=wSVKQF!Uy|-x$~rvh8Uwrq>ahKajpH-*04FY-xe;{6WyP zeziW38hXAr`)S$7MeV0SMpE38uRwXi`aoi+F!IKyi4E#;`uom-RD&<>b<4q*+Xly4 z`J>dz{eWAeUZ$7W8-{*&b0!Ro)(pD*d^DQnVxh?TSJbLBzpm*gp=RPAp{Bid>qDto zkQ$H|fogS!RvIW5YSwFLZjf(Zb#ngt)uFo@xU5!d^K`Nh;s;<_(w{%f6kBT2=rp}L z*%;^y*-|8i!c>_L5h)dDkjqaH_go^Op>*4-#=xm>-UcTug07`m~BjeEJ*VY_jPseVM`m** zV+wR+fgcPG`C~cAh6vepzsw8*BB$=J(d+~keSa#pFm-!ObT{)+Ar`OuTMnw5UT#Xd ziKaCIvaX%N#1F&yW0J3V%zp5)U7dMqk!kMl)lhtDJSRF>XcVQdx}T1=&r2B^aM-$K zf_Pc_@WzsWza)Wo?*onY&)0qNwDKjyzWLcFjSl|xMegRo&ThyqHcW`YYB z6XugVk=|jzyeCRLR?Wk%=5am{Zp+~rM#S?CEF;_G)2fkee#@Infh%3od{z>}m;KWf zl-Akb?Clnb4U)eZersJE!inw0p6U$FlDLXGJpVkkk>uGxr=5uojiJsMp!SXuKB2(3 zAn6{oFRr8ofHW%c>(@5< zY#1E(=d!KHfQMl3C;f1hCfe!tylQ>MToIAib-(G}yIY4B!$e$0{p{Qia}`Ie;qf~A zKjwvSI$8t-hl||Mq+D-OFLiw@u|6-g2%KHRBA|4B03j$OJf4R(``2t%+}CKYztVbH z{rX*|Kh7;!>IiFh`rDS-Vwj)a8_M+4bj)Zkhs@R}_!${IDsCo3w7R6Esp#XmU#cg@ z*3#-1R!dF&nM&NvDK8QLE35vQ;TAO`CsnZPd}|nIIde3gSN1;QJ(*^~=X`}rK)<-W zyj}`(tT3qsj!C=gYmFDr$MY2tZY~JrR(NOh_va9mR={yif^em-a?KD9^g#Hfr1}MC zBiTqwelqq&Q=l!l9cVY4O(sK8gxZ1TaMT|DFqSLjEsEG8PU6@d(W(Z@L?94M*gV{O z>{YB6@L_hR&Z0G`RO`T?xtzf1esT2NoUci*PPfj|@4^{xhzS$zIkmHs(i(u|WNQIn zS=J{(MG>_cA|Alw`WQp4h>cZ+f9(ki4?XG5lP_87=!xDwS~nmry3uRo6j_^dP;is# zLd`ilo>*96ED7l>RJR{@^*BB1GFdFvc7F$v*tugD`|v7yAtjJ!W&_+|uPI6CLXUG+ z>uDcwS|NiLQ@2iA&Jqt!);ru_8x1CfHqD#^YoUiyGo&}P$e0D}wC^K{+j`@v!+jA@ zx5!%#KM5YL#p~qjUyD;LULl|ohBVVf%iLmUn^w!h-TEq*hq-I&YjA;#XjK3EV=>qs2S zRVF=Of67y*HrHSp4DF@)L6{E?Y5KF(&6O@tO3?F-$9PGY#fkbH8O2v8N`|g~Z@vOe zwIq=VZzz>3z->mF|BKhUQ(!cqLCp$%h1l7zDDrPz2sCTX^CgzP7EXYQ82BwjVGYzk z9}V-#Es2lojAuy$Dn0}EHa$_3aab9oqT#M~`+rC-xJysoz%#_h@R)n{a4zI-%L zYHO0(4mj{qdiuOhobU5tgyV}u&N{Kaql9r^go`r0wGAqEvS|T;5%!6X$4PVB=a|Td zHi+r5HR%Bvq@_Y&1^wi5GpPMYAzK4op4MM|@qY>|AYfSmJ!tS!kxp`DIcy#_aI#9q zzc27Xu7F|+=nOL`BMh;Tf7aJ?QdgyM42}zKQfGsv`be5blY_qn zg$+HA;@1JBp5bO4K9bGV}6M3UnwiN9;SntyJ^YzvO|Whez8p?rX&MJX<(d;PG@ z-k@QI4`B*F96P65oQ?(i1!uX2W(?vvG9Lyp~^S zeKR8+>=Lo>^pUVl3?&)SER8$dk3gaKehFZ7Um5!k3-&JmCRtrC~ z*VXkrQG>pP1(l@5#jP-Im7uX%Eqoh_!aQ#8(zI^&9nF@|>-i&0HEfzM&3Cmd2yOCW zUxWR;H;Nlet@5?8$>Qn_*_N&QhE?0uJL#R&9?vU7Qi#_I%AWmxC>?!sVSEzV#dP%U zuN-OVJxvMOg{V*yf}Qo%3I^*1KrWndo(K^F)UZDnfWVzwf(DG>nf{~MX(N)-KX4H^ zCV>MoKPS8Ecz^yUc+)-k0c-!+CZE#f%|=BD@rtg#p0yk;!~`1c=a5D06ONMp8>W&rI3I?OvME85gI_NawFD1&h^Ix~geK z)!DVvsZ)q67s^2#;z*PuvSATTh30RKnDz|V(`$^6M7|@j;#-v{Qll# zMxjvt35|d|uh)9MA?S8Sx?EtUtKL#Yxk&F9(rboNKHdawa`_0U&XZ(ALy0Q6o^FukR33Y={Q?f9I?;~FrBKm~ka-n8R#D5e;-pfVBWquxjO4wPe+ND2piu=24EJm2xXCJP0 zmcmxqvS<9mk2U+4X@gX<8>j8@E$92XjX!R%o-rw6MS_51rs;uJFKh1&p0<3^8B$~${8A$9z} zo7Zrm`q-ua=337F^#K0m!8Vq%p%fiEwJP+-de49JandlQk)v>cBgreq?YdOs3q@0r{qb?Qx4g82#wpeX1_(V=LEiVTXm(+xl86i zBcRbdZl{3<0%}B>^^UYGY1rj5<*D>dzcGL)jz>Hm#Y%|JoQFnFFB@C-&eCdswqACC zTE2lDgKyQeJ97KEjnK0N_SPt z5?$cEH_|RA38kS|7e#a?7p<0DP<*quoXX{foi@W58&d%NW8OO#yT-;^5a+(xjj{Kd zG6(t@iNk*) zh5MepFcld%Zf|@?>ABxxenM-D99lf}4K;YXj&>-h8=*ydENYOs`{nPlae2{?+n>k* z3+R|aCppp#_MJ2nya@&t(y5#%f&nP4nQ$kiy7yYQAk}kw+*fk_L2)BD-LnmOh%cO@ z!Wg<>OP;_e=vIW$9snhBR4uvIs?u~iToTZ#ca$y)SCWmkpw6E$i5LVnk&mTzYfKZQ z8>>B!#0d(^K+E5Uu}~t)K1ib(`Stl$_K@`MT;0h`=$(Vb)ydGMZdhHq*ButQL`;jj zWjf99N*$Q<&OY-{1N<-xP_fHM)}Hjh6&~#sGA~&;c2nP6*C&QF3T=^lM8A zzSr1-mm*1-V=AX}zP>06odDC;FEv;F_zT52eE-9wC9{_)g(51;OML+$e03 z>aCfv1q_us#KGQ0KP)_UGlh{W@vE=c9d~Q5m33wo#3eknV^N=e{3tjMXX4sjvK(bg z%2u;ZhK_ke0)r8WsBRGzgh@zk$kTh z5BKZ)$U%_?Zf*?IPxAxfj55qx3@kq)$Tl$laHk`lR_5;x9gG0g|&y8|gnPF)-yZ-vh7oIBOgV)nABA420 zI(eg&BheU71wW1QJipu8H-r_1563$~iNUhpJ_!auEz&_$79dB2)QQ0!6OI6Wd39il zLS_UHZ?|dt=kbrGKeF5+S=q^+0bLp7_3(k!(@gQ))$vUu2bo={f&Ii6d{mf*U z7JlABnO%yT zl#SoL?S=kFf_j{rkf{`fO7XfQqsy<0SiUpjoCg*bHoY78r5#Ohg~)(QHKg-@%>w`k zq%_RJ8L7Xr%XI6?8o834J=xMG9Pmp%fJVy4f-d2W}U#ZOTT0 zsEIR_`uOK6{yqrZsNNj{JoS1<+w^~c{2K8j6UeW9|6HTL4iWiDF!^aj@$CoLAh_cV z(cE}W-JbH;=O`!>l8O}d#$;jmY^Wez)-uWWFfKXTIWd8SMXl1Y(CGHpG{zzLie8=o z+m#`Mw2(S$0qs$2b3Fw&viGNj?9H3E<-lGA^P0o`Icwn! z!M)4zW_G^*Gnmj9?Q1&jhPDhtD$HJkD}y4ZXJ>xN1qHmZjsT$01psCl?pMdI;_HV; zM*_a^D5vkEf%RY+IKf0Fpvq@a&nz`+lutCOtg_<`EOE~~|Ir2IVZhDeA!n*LJ3$_* z>B=RR?gTm5VW$@K6kdCkbbYRq?SVKb75>RUA8o9S(_ z*2E>x7<@tU zMH`XLjPhnaYsIG52zevu-$q|U43CEQm3?pVciBn=n+}AXgJZcS6u)fyM0xv2I*C=F z3Occr7aLEjiA`5b$Nu>cofo(lENAtU_{(J%n@lFkv517YTm0-dBHMq#&DK~3qiI!H zKhSVC&LNiBmOV#B>O3_adz=BZ+7pdn-F$-DG*|jm5cH32Y|sI+A~Q`944-Panj{M$ z66%wc1q-wT+v9QZhKv1rsuQcWuZYiwtJ_CFgfwy) z1WXHRoet7wR3vAAm&%jAz-N5{%%V`LYn|DLwsJ#BWs;*MZ`jR|0t)!kz3?UUF`P(X zocD$Qv*;yD3JQ4Mh~HVpvOwj+i;RpkyF$k9?*hPS`65>bfV+ap5)B`%rhk2Zf1kkV zkW_u^gpNs|XM0mrRM6dj;rr+^Do^Xe6+0c$FjXkNFOedXm79IQ zFic3TcU)O3ZdB@=+K!c!dqnNXbVe?HYA#NVLLqp!27GNkfzi2AB3-D2HanBFA^2RV z0ceCD7Z*Gjq>`t;3kZ%EsLJvzEDQ?30gYw;8-&L#(>k4(64LN`d$OoteQJVeD$kY_ z91*2>bbOT8l_Rr5+ffO*bo)0|$8W(f0{z>sh}T+Nt9|ikkGj-%Z&*epH%r0bINDOf z6YM#ZhG4)2&$7+xye9Z=98Pq(T~NaC{o%=@$c2nUBexc%?!2-teZ~o_$Sps_qm|I* z)n-Meix)j-^X}$zs-=Ab{l72i67bk5SvLRI%C+WH5?=W5(sIeVCrS+M^J?GwY9Y^ZEWNYJ&mJP z{|2I4OQ417AD#8G>3UnzW3YYpq4u9w9k>+$_D?A0)Ao3AP|0M8e|<8(>ltAn;9PCm zE673_DW*mw%(Ux0rHKXITM7VIn2K8FAE$R?L9g)AJikXj8Pz+^`!oCX(_g8xH50YV z#nbkNS8KiTp*oH=R`WKe-5Bg^HlxHDMMG6z&oBUL>PtZ~b&mVx8LONR*#JWdw=*E$2Wt4J8;JjL zOT;iey7mOQXPanwb(&shWPEbYmqgp0uGdMAb`?1@%tEps-8zjTiGJ3YBbAs5g~4wJ z?$%RL{;CX<(9a@o)$P~0xw%gnm%^%V!2|nFqxywN^>=cGBA2yqSWpi^+|DrE0_btI z#iwm@bxzf*^<--XU0pAWxl3xW7;8^321UpSgRk+;2VP4^qgw_rbiTLBN_%b^S;<9- z8#s`}2GmJfC4W5yFkSC!Zaj`ZDy~mB?=AWcTjA73g0txf1T@~7Z*POmnupa?CCmTZ z2>kP-p#jfw;AoK%P`_F36jOMdhzPkHer=5zpK{=m0*0maLPI`u@O*bd@$8g(j%O}% zj4c;Wwg+ee!?m{h&Nm5o-f`QVe#kf~D<5ILD6rH5+p;SnW++80pO+^PpkeU6X7h?> zL;q4YM8N8)gf0UG2xn=goAccnSYuJ+2|?S1hMYlh(DJ9Sy%;2Pc6H9F4ISMI^7_(@ zS0e;fljQOTwu9E?_JFg|XIA_BRFd*A^~aD(^GCiglijM=?o6GeLyO(=E!^pWwHesh}iVSRW2_Ky3jQW#;u&Cn-_#p!tEM;aBX8oybXaGg_2*4@@Tgs~% z+nq^1Wpulq(d^gU9pmPebrr@#f3DAVem59a{Gu67e>iwhq8|&!rdMhA3*K09OR$=N zvLIno{a~W;`}{(&-qCWrP+d`);*5PU90m?~>6Z+BM#Q~Fkw%S*Z-ixp3*2$V2Jhox z>FwK3s!bB}22{1=*w}bK!ZG9>SN1>kPD&K?0|GmPjoeW2QLBKa_;>j<(jjTdD&wAW zdq8Bj5jzyzyUZ41daA9hHDOlAOGe)rh`6zL-S?pCNzNw)_Bd0cfb( z*@%UfS-Zy8APfR$+9%oG6#YVcnitVTL|Aw$d+UUK0nN+n;d0QNOi&&wk1rtKJm*>K zL2Y{z{P_IF)b$@|BjHdNg{wOq&As=axw}0%7G3Q}hiYdnK2BHqyX}2Z7X%!_e{t8L!K0v3{+;t9;)!s1p8uwDSLn4)E^iKcj>Vs-7YS z=lj1Ou1J8Z0uMkY#XGRYy{nep(oKU>UiCiX`w7EL@Yy~)$7-~%kdTI5w zZ)*%E74OxUyg0Wfi!p^Yf`{>vP0WEwljM8gQKf47qXQDJ~qzEgYD6mfDu zyA(uKNcyDd6o*De1RRD<1`;baCVnn7IN$>psSbE)%iXtHhEpo+Q9-sR2=J^{01z~Y zlQyQS*W3ue3+~(F%~f>=z6mFMutah^`27us>w>@O77h^gZ5-_;yJ#e>nzEwx%WoJ0 zg3mm5H#eUlAcz95u;cl8&tUxdU1~~ENpfKg4Qavu0+%wr?X^ltE%M6v4b-B4?%ONy zxVXFDoigtDz*z6-Z*44ZWrveH-gVM`-a3T=HsPRpZ*OR!yszuwP&7W6<=~rSH4Ei{ z;PWBvX|RcA`JdTrFOEG&Vye<)Gwu|v1)b@IBz9D7E+>#T;TkR#AZ)nbrUVwD=h~3{ z?@V(tDYpn<)QxaDMEhu?nsB=g@SQq;uytmvoLE+65Nc^_A6_6T)<@Lc-5zcQSho)h zVCjpX{5sjw%o1pvVRv7B-L}7|8qTO%DfT9YM*c;!^$av;tqL+6M?JJPUxCc&;EoNP zWvyu*2odzwPZkmw@Hjj?h5_qZsYnaa3&f|m>+Tplcb}hbA2FqQRntmf(|^nAk_7|Y zBxuK?#P&Q#HSj&*kg&XKYS3f|_zL4qZf>@*F0a-^0PxlJ_#g{fD7T!sIhw_12GZn7 z4tON&?9ahyHl-8z(AUUrTwL5!Wyoez)*%wg1uNTL@yfFyZDX_e^bT90*P%&SVXTY4 zCa`dZ(v?pDiWbA=1dD$R+p@|?|poIh8xZ*Krg7Xs^B5NE3lXcp>1M7dbU{7RUF1t z^;CYo;=_OE@KNGoKA)|4K*t-G?Ut}mE5WYbV=yF@C_T8^(9pldKhSF#X1uhYl?fr_ zP#SnCBNO)ecQXZV8Yh0L8}PD!Cpq1jM|rt;k1i-0CfY1evJ^frJFZVoElot<(Aao* zU66HjwM{>4eP|Lwa073qT=ETj05AV4iaVaIg%|=!d(E(lbxO#L1O)jGP)t8cvpyE088+wznvfD_#g)r(+h^xG1kd{|&Hm&o#-=+vlo`H@C?s?w1UFG`#T@Q9 z`V>F)bdN8JUc`z5!ftMgj?4D+pF92Ra~Z8WMb)^iIeTLYzA6|}=eeApqvok(p?Y3A z@~q1|pzo;AiL}3?UCKv@#b>q3XQJOQIWcH@Ml`K8fT~F*hx^)DcHo0*k%X{T zlN}ZUYGV1ZkFVfibGnz{ipbe`<#N}9rI3~ufu6p;?O`|LD)RH;jUT(~MVh}Gjhpe} zX*$8QBj1LnE&zHq`YG6e+4F25eESTfQ%jc%4&+K?q}jPBim_86QG3QpRdkSYW}rzP zEw#d4t|9U(DZK(w3%OFckDB38Z(ieK<##&WSHUgtazu^|5knSusdnVcpRCpG6Q$Lj zFOt8aRr$!Hl5)QR@{^2))sSXKxQtI3+gF(e`Aokn%mb>6M$B9n_nXIc1na&j+pI4} z9}cVqXnDFEE@mTBYE=HzAC{6;xFhy^S zC;Xk8gQ{RWj`YNL`vk*c@ulH8t^+S01d#IFi@rGQyd3GyrX_uks0P2SoR4yx%16f!&JUkQ%R@wU#u=Jf@NR(VDH?Ae?4JQ>I#`4AT{{B;0dTSCoknX7o z^C~uY?(a|bZ0#}J!WgYKjewdHk=S5mV$*gXs|ylPD3FsCCj)N-s zUmD%zd#86h%WHnR6^8g^a@QASqb^$ZDkERfE5t-#qKQko#e&St5_8y3uS zV}JZaFb7RN*5GFEcNM}!fr`jpl5*Kz$yXT_vSgb@?{?^Q$yEfivkBcH1U(K6h4OCi zBq3vdmF15D$V0U_uL^yF*2xl_D8y|>Xo_Q9^PTO68+B{z>)vh4y~`~rK`Mk>kZh3I zdP5kk1XX}FeqgDB!v*HSrSEQ+dS~TAzDlsHV#C8EsnAs$TR}CG=WS$x#>N5GtcBvI zbIsNF8GIU!-e(@@j(jhyh!Fb*m;YqKa_)oKjeSS9{=?O%>*F(oR=#y%DsS)u|t@wDow^ovunRka_gu z#{kb(zmS;9pJmc6<*Lme;Nle0QV>(A(&Xl32W&VNC9RaCx=oydp)J=ADO_F{^i9JM zX-WRQ?64s{FbJl{v09JX6j|N@I%@x9mvFb#yel1gySo*Acy=b3XqV%N;#Y}J`BYHw zSMS71uKA=@;P)1;TDOgNK0f=)+p&En;tE4Jz~mYxelX5sRJ*G)88`c_hETwC-hgJ%9j0Dj&qNK^W9!JgWj%CrRk#lnM#A% zG|X2%=kM!_Xvpm~I>lghE@H6TO@7V0j`Dej2Yql5F)>oLYU9OgPV9D(<{ap=hs$~R ziFX+W`1h9su9s`5EqSVoox}1k8B96~y7#O#xuO6SdK}V)n^oz6&*|!ebSfV3-k)%m zZDq0u|8)+n>Uy~A8Ep)$j(U1>L&Orv?ZGUH$;n)>S$$`Smtifm&e_J{R4DypAvE0IIUEk&tp5XoSkJP!}obbE=6 zL*edX{jBd==~`?(J@x#c)%PoZ@iPRk`^B2t&&xDgT3Rm$KIB5V&AX<2Z$43VC*W~_ z;UF7g0hDoIE^~5nz&3j4W&$}289y!-R{o4Ab4-yOZq*KpLZdtdg)G0oSHE|a?Foqq z|4lW-2>j3%)bt5_%gjvk3#bBxKS?a>)X4)W4dyKo-x@LZC{(_pjFIRoPcMYS@cZ0` ziA~k|dmIi!SjhS71o(PGiR1uN#JAsWF4{5SFtWSM5Z~@EM%Tt~c7^_@CIK1)&7UtE=AsAX#LQIv~&?`uK14VqzC*`H>e)<_Y}ls}a^n z`%hxVv>p5-vO$HADs>+pK`^q=XN_7B>lMLVZoF$4Hybqz_djCCR zb>@{lkukbFfZex%(Y5Ik^*Pc>e?r+Hf_3GFXHoqikRA%kZ4k(xuXhrB@bm!5+adka zO?kEI&lpL}riFl<-`vbc6+@pZn_Vcir9fqqsBTwV+cL$L`yY$}t~a>65;fXx=gWYO z^)cwrufvI-vD?X=EdT;eqlwQn~; z;22vxm{YJ?&E>_700Vi$rGjXO=?)f{rIlD=1VHwZ-GGGuPC6;Dr0HHnGyUmf{~wIu z9JiNd%@AYdYX9fa-Jq&EjZ_SSTZ6GlZrZ7ZIC2?7EvNBZ#*#2(ZKFR{k=CoVO_(ZA zBiGULbZZtLb;gZeU_giM9{T~ZW;5swqT8T%3?GrS>!l3yTM3`iu~j0s(W6en&>e zk?ng{DyEOp#C(-6*9yj!S9-uumd*D^d7PT>wYPC;beItq79g9GZX{s@JjFxeiL~BA zS=`xONg!hM6wJRk8tEcl86{V&Cs5>dCJqQEkblYpEI97xJzc=A7*a<-koe&j%-RVf6ANlwCQ zH~zfgdx0pRIxz;HFtSoRfv zN%3bX!}szEXDq&`@F0g)vlOIV(HTA4 z2~sqVjFXdfdbw~caoC`8?G*^2FQoB0&ukVvSHDhi#{jtejkD>&0m4CJz{AZI&MlbF zVU{kAz19(A4c+MFZFak$Hi65jyk48j^Ntu~nH8nMv zrQ;uD?CPsSMlT}%szb#<9g5MFsOxoD-d+#e>=yg_72fOulMy5^DFULSD;(1|!U$%{ zJo%VA*7@J=xMIlre*N^k9DV_v=K!Ag?I)Y`p$m=%iA9HAHK&(2$6S6aP?O2xB{1{e4$Qolo(~ zD-XCHfy_~U8A6gtJ8L2ik|@gPr*{{NzbN+(z|c($TwK3L^EBUH6)>XB!(5B9n`3)UdeZ zlX5VU{cSHpmAOjS^+ezD`no?aY{1H4r2civcyvhnDyN2c(GFYjj(#B8$FtpOSP-aD z^gUH^AEJ5gaIwJiS0~X>F2ceb{#k^8fufK-xdg_Q_r}q_Y~BTW;L7HRJ``HIeEler zIPCP4q!nK-`CgRj{bG7S#QYr48$u5v47SR#Kz&Nz8Ace4JC+ND4t^}(i8DO`qop}$ z)Em%&^rrMzBm%-q+mXK4%B*!$`%*@`QmlTYAr?uzG;64=(_SaSj1U-X22x4*${gHQt@|F2|T^HQfj zoc|AdZygrp+pl{YAc80Y(j_8-fFK|tDWG)2(A^zU(gFg~-2wtqL&wkqN_Xeb(jC(I zUZcO?dY)(Rwbx$HvDcsP{NZuvp)%aub6?*$&(HY~KYcoHxj$RJm7DHK=fkbZiq=5t zUy8AsE995OPO~@NrPmXj%AZU)iDeIMNYC-cyg1#XNu2|sH9-{hDh&djpA3}Nsqg`$ zA{Ue2ZkwF^EWYBR)(N&Xn`$z_LO9_aD|$2!=p&GGKbAVKc{YpLD}qE0R)-oO&k5P7 zKPUI`f6!vxa2@J2|9Y|w~(d(l+u^aAbr?fWewQIeSDr>itK5#Q*PXv5eo)%2&T_JBJraJ`?J zj!r!F-qcV^+)(4Mw-)c|-yqLR1K(sgFDv$1E~Pu^w`ZnE&8&DO@kpLttFeO3(G+Uk zFbqpN^>tof2O&{%@`sIOsf+WShSeE((z>&%D2Yq#E!Eh~SP3WeJbqlSyrz&XJr6jOVCV9!DkP1Pau;hCP=3ZMts4-2oiUP-3`13b9{6$e zs)Bx)RB_>b|!ADfVfRePpy1sODNOd?f~8%AQkIwhByA zNPfX?`(OM5Q4xCNKBl8JiCrHb1CQ<#gkCu(Q~)^4qG$=@zce^*eg{4X$O_fH<-Vq_ zWbM{M&8(wGEon{uCAv+@CpMjEDs>+ym5S5?>Jg4AL+Prln=@rJa(BLR+U`epR^=&I zGS=8{1;^0G>MsYc#j|g*=4-&j4Lt!!mW)1@sU=^Pwb*`5?+dq+H0q^1q7_s}RF5Cu z2*U4f0=>SX>iehqHtWNtgj^P%U3$Gw?U0eWyFoKVl4h3&Loy%02ZY)9ND^#%RbU*i zh16$}az*HLp0ax(LMl2Xwn(!!>u90Q*-o}Z4;8j^-+atH%{!f5nXzf#P~xTY{yAVL zK=Ra3nHr|VV9L*hvZa}FS>#}95Fn=)L$&(*Lc0F0$Dn}QB<}4c%+#HWjv@YaAT}kb zAi{#5gFqR@-!I3aVK`j!&6+0X)2DBloa4hPD=T)M{A67p6w>N~kaz=~d_f*c<5LMH z1_nik;K*p0W%T9d)>0-HONqm*m`#s_tF$Qbk+QrQoR^2(tV@;!n~MDH<0mYFOkRlk z)zzGn2P%<~$AT_tv6dxDmXlxVD&mf2rbZ@fqRl38%0mVw-$z?0{0db^9!0AI&Qoyq zF|&SRj-n(kv!^b^i|DkUc_%$#xno9P8IgiBK^bm>@{pR-L_&xDKZHi0J4qJ-%91B8 zq5$lKELgCy(ZOFQFBnF_u`J^MJNWJGTbaK(cF3>xUzy6mX6R2*3x0jB2R5RAzvTZV z(5}7EEvCj6Ahs3gWXRu@eIlclv{!(kngPyJ*UWV-HfYOWUH-<5N zv-4k7UV}R)X~{VwU)ij*fN|j=?U~qTn1vA$diGRJp%*-0i8IV zcq{`tP(`^*Baq8hP=+5o?*i%x|1m93%~Tf^uUn}7<)#Ym7+}ud7|*P~cM64^tY(D$ zO<8bx^czn@uTp$N_yL9Y{S7{U&lDc8^A+8dz2zh9mnLWUdQFG?)xV+o(w+M%usU8s zg``Qvqo)oQ z9FpOORgwL<>7rl%es`0=U(y=t36L+3+BmceA)Z5B@=AIxKE9DlfMk+FJK6Wz->#G> zgz>e8BK{ZZqX$PewzdP=3{flUR7D%hv78%4@4E2E>zr8uMJ6}y=`*6B&`>P2l`qN3 z1%*MGmiY?V4*=9GU50x6WTPnll_`#J7_qMm-x&&+Rw|x%YrH>~9Bo2e;gsetrX<UtCzP#2+=#Mv%Z7EG7qB_#Y!Phiwl)XY*x1PaqJB70sm-6SevS%4emz!_opg%&Sj(-i z^PwKKI(vfMiM%983BpojCWq4u&nt`ZhvuV&$l4oH9lqW?!#AS>+qWBrYI4inD6*)2 zk8osY{8DXK4A@}6RbC6#V4=(m+)1YnDlzDgXV9!rG+aYB|1I7+*d7u&pL_^*@Nchy z2gX^xEtCxML%u=7!;Gui{n-d&*5jNkrlsnwI-0770f=5CFqtfTp;UNAT4vPKNU$-M zVHd}EM*5ij*&QIoUV&87G8zz-RnTbwFd>-!{KqU@j;kM$gvP0H zTTZvthi6CIKDvmyJNJp9M`xel$v()nNev!wEWe%QbC<3Az&u=FZuX&~@Q5>4kK3*J z<>3X;6jIAU7C|eU{ue2XO`8R?O(y;Qr?VX<|B?~4~ zv}rv6$>cJZS5)8l@#=Pefx0q-M)8}NO4Fe`z$&F!Gz6$69D*Po2=~%v22tWI!mz1Z4{Kn;JU<|vZ zfB4cXt0_XT#af*@HSA927Wtb7Q-OT+vqYnu=;<#gj?OHszHCmkZn(scW7JYysJ2@X zPAE0B1@3}JbQ*+w_4OwpNJh3=To7-9gu_Z_r8n7313Hkx-8O+v>K1YQ8q>pP7vxeIQ&R+Gg7Aj`L0 zOv)NjhyJ1)%AsB3Mg*8+f(a~!!+f!axN)$3@fiAC$6M6;`)9BX=v1s_`J(RwGR4+K z=t$mxxGUUg)*THf2%j^`aD6`AKHtH{!^_O_JlRG&R0d1byvw0Xb!A{d1X~qBREhhg zFSs2yWf#Pb9fWJH=lEio)cxY)<4-Q7|2(5AkUAk2$z%lE-pJXAjgy^FQT=V()m?04 z(XOm3Z3!`wDAeRI96`@TFq|E%Wt84B1eP%2x#>@HO8;%ym#7JMfBQb`aMA3ILaRa$ z73EL&nZtfn7Kze1{KgQ=pcM?5kNOuU=ul|6q60n<;j|65Nc5-ai!i8{kb>(Y_q&}@ zue{3TVxBl#Nxkd2d&9Ud1+oN=KaLJQ^g4ogT9xDF<^aV0KM_Y+Q6awN4ko>B+Q#32 zbw8^%S?af7AU&H1S)Ed*SkB_7GHD0*_rI(g&O|%foG$7UOSlC>;4_g&9D#>&z9O9cpeuoc4QBSV>hAXr9)p5(KEQ9q+ z@|Oq>>zNaF6o8Nn04X!yhniShEHTj^c>SgLqF~Z4k#wM=5*Un*t@NDr;5nbN+uI2$ zpEe1n8w0pM<%Q#&*|szveg#HGWK;2JOBdO94Q)EUiQ?9ERw}(R*V$DIxLpDs^Y~g8IY)48BWU zTF`+uBN%2rs=HQds8Vjjj6ooF_s%op^CL?-n`iJhOn+&JDNvA8AJRt+jrGc6d$#vv^Zs_{~?(MPl@H<%n<(0vaqO-5>9#BV{I&~V+Y(|b9lf22&eHanGb&Fxx> z|4|7l9BHT-9<6A5d-=FHhG0EFh~Em?Cv~<`gYrsLq$cH&cnf($Dzwc+pMA{>%5y1X zlb_}Q)06va)LvZ5%F3WUr&d#5t83{Rx(C+MQ2pp{|Dy0}sh6-y6}yuft%}gwoDV-q612OQVTXtOg&$P`((QtwgqQ zn0efvnfz}Dei%OZs2_v_cG=U;yMtEx^4Rm(&@y7=W$GjcN3Tn{Jg#Ut{0b1=gSCG4*a*pxPu{|6eO|E)oZre;v9#sKIbIUhLOGCQlB$N$ee{XE$E zpEpgvzZ({R6jcAC4Ik7lx&Ev6s-o>#)gqW#J5f#ro56d^HLo{?l=UfMVx+SC>Mn zIDn~!0jkrgLO_DG-87d2!1HplUC|z_4UzQmALswQ$_cymdwAiCvDmgtd3(17n%N#j zQ;B@6@I-(A8lh{Vs4C4Bk9NN&^;cCHw!53#-1>T!Os3}0GpW5+lA<(?iPGP0k|SY| zw0ZD+t{PCg&mFhG8s_$$1EFLD>>#(fyYjVv&^3nJYB|HS2MG+k7-15@tPBg$-58y(K~;0`%~A3K?J9toCC|CbN?< zGc(hfi~z$zxy6jeR@^V3`fX_;LIxy;kZ?c^fsh!VseTyS7U6=5hNi!qKT-8z4`+X| zHv}-ee&!W542&Az06H>ew`SVIFOk&ZITp>fMbh6dob6uf%;|jc_04q#4uBt@=bPph z=WaMTVSPC}rf}Q+@d&6?=0~E9`HT)`Poe4mCnOxCCDexkiP7>?4220o`CA~6D_fDn!AwYZD&1y5M zwG$?l1VqB?C&yWuwG|(#6=?7tMK8g*bZ-@ERR>y4*QAeJmpM`B$Z2R2e&l}__)+hj zVt+FZ3I1mKcCjr$e~)*_*U^{t=MK>54oI$KLbcRwH-lgynZcx|730_ z-I6T7kPhUUR$L9-B%iec;OAIhA?Jl39$np~kLwodz?nIVCTNS#jSt^!wm&+e`^>o) z#MJ-uQmM*$1OLu_!jB+hS8&XVx4%-`^8|7P*}4b-LpP|->u+L>tySh&t{CWM(!6C)czmxHq#;GZ=%zLQXN2A^mfDq7K= z?5K+TRG{zo{@ggowLJqz1DsR2jWL+341itT3VTBSWA^$6^z2<-clg;`qzvDvOgTB|b| zG_j28p}m@1x$l_``fq5|+oM||9N%nISe7_WbGc*8DZ^O6F3g8fvsQn2l^LLOUa%PV z=4Zjj>Ry+cH1WECJnmFJ_pDp7e?F=Q7s!s2V11%2o35RP$T$|7RF5%xy-UTY&3*xm z=T&Ea#3)aA^QK4yO<0`S#?CCOv23ZL7@H=_bQN|C9*wF+_7!izt&f6SLJ{3npjOY@ z0~7dpgoBW*q^R&hNk&^ZrB9yAbs55SWp7_Fmcfr5g__EG{%6bG`}amF;wYbMu&hHB z3%xBTopSapxhsG?NX<+|<8%iD&=E4c9ZKB|PcfIS(+oOr=yYg^0=&HZ_D}NlElQpeGIB)X20lg@I=dDy@Ii8-+%a+oovHh$E)>e=oD#-S}&oexgXb;=>6Ou4~1+n z8p@FK%T4&}rs=k4^{BU>s9VE9{Pbv^gU8w8bMM2)xtw#JFU^R0Q~ASO5w6Pz?P;yl ztIluKTfe;Kom)#Fp^-^>`<#B`?~VyMRAiED**7fFJ!IkJ=FU(!=HqnPw00M76k{_o zCo6cWI2ZE9{Vue#$Yi#jtj2LIo!p9O+Gb~46T-MXRhx}P^p?*VliU3&^3YKND~D(r zZYM%9IKywQ^yr1!({(tAunK6nI#Y*KvSHMb!+9DjOPL0_vjLGiq-B2zu*XHP>VTJ| zy{M9-+FoNklsQrlRxRSldCgA5cN#=AihK{?2x2> zFqJYP=IkR82p^zp>WenTYOLIu8W_En4L*+bXIAeg1up9&luUOaXH2rIG^n>mZEV_5 zOj{Vm!ugKp8?d3+Yg_I?0@Bup-)>Gf6g5;`UYobdZ6DH-EMETJP(uGKdXLlT z>dB1xG`ewb%20M4Art7vjAS1o7gAtelcV+7_h8w=Lc1?k$o~NtjY4hgSnYMFMnZlq zcVzhaqTHY+=^YwUU=j$PAAi5p>wD{Te?IJm`fw=`1#ox@LK^7kbZ8+N)Guj&@OfVQ z6w9pKK--EKYNCQF|3q$rdbeuZ=hn^a$b@!gMIOM3i=XG#Ym~^pTXbb*I@)Ga+`_LI zRjpcSPvgqP9$Y8`_}d5THw{faOF(wX`fEWKtJs-XYFD;buM|;H4O7{3X5UB=2ncXM zU@OP7)@gB%p)OS~2Cwvg8THJs>)mw}hgoB}yJzOR*E*UJ`6}Mys0d47*1yBnGtr_N z^2e(q_X^po4isv^{haT`es%CKtg@vNWs)M)59?lbF=98OYV`nSonG=W0>ngFCck51K<(Agzj^y>c3TW~V)tQ-k9nWvgIAeXvceEAspazwcHxR@f-@$G5 z^aJZsokJ+zYJPz~wS(D6qlc#_&t2EAafBCk`_X2{MWsbdz}6dp*A=G=+@rvGiJ_FQ zmRZW*6+<7i)D^3<+`Xn!Y7k*YzdQFshHPv@TPNGb#8Kie@rrkzNhv)*}0{oQ#(Y3k^Ahy$%D=WDJtp*J&bzK4`ujLH0&SkyCn zAK^K3$`k@#7TQT#6x-xlyR&fg8NPa#07Fne!=K3uJ{}-^6FsNnc1ykQ*YA$+iTDgS z`^43d^)+b+?_cYor@#LU9sOx*1F{YL>jkd>fBff)f?t#VpMNv>;rxVe$lns!???SV z(`MS*It=-zVDAm;ms`ngb9d{0^iKv@A-e`H*%%L23yK16;~vOqDkx?aak+Q_y76|ys59i74AFG zO#-{JLjdbG)zFweNL%?;+UJRmabz+}9HdkvBP(01J{h~twvPb!zCB_-Hhf@MZ0Khzsr^WH4f=({uZ2%6?Xx~WT4LV+W zJs=*<5D-Mb_!zkuWd?HcH-G~S0)R~6PYpXFsL*(Ld2N?Bi9VsUX>iCv6`z0d_g@&! zB{*LCeT?CNg?{?Cm~bGNxrHN_4(QnF+NoM6X4B$}Hcn#_xUGbDUl|b+=P|T-#2e8k z)-OYhsf>ER5cRGNZRqzEDHR|HPdjLKkB&wqv_d5lSdd8)6t@%W&lzVt&Mn;;)Q-t{ zWpm}J-5Hj0Q<9=$f1#o$K!whshSd z@o+moR6FD^dCEDS=@3L1)_mvQ&^NK$1j+=5v+X|yewabbXhdknCnq=bnKNM$_W)?t zy_?4wPR*hX7y<6xU!|wHkkT`xvpcT&DN)ecVCGO;`fE;36%Bm54cZ?+Fl%%yg7h48&6A(gSY@E5M^lJT6_!1mJ!Wy>2#(?`3UcoUMy_7aSZ-2fm`rQCpdv{2hWOeFYYa&mGa zT?Zn^gzAUl*8 zZK+8n@kzx5d86KId=W*WRG<=ofkzM3Nt7GVYbife`3#6^`aOv-LSCa_x;V-9fKu`x z0^Plz)F3n`pp*Sk?5Nx=Kj5q#yi#rfQE2@zbB>eq^Soo1je^QyyHD0jomKCbFc`IK z3~Z|>Vb(>l#-M>Rn>;%smY)X(`=I$Ie#ItXFyC(d96@zZQq{dh19qrulOjEKU_KcS zX1Bu?kKE4>h0Y(4l`kcPz6-j4E?&wdrFps&2rbpjl00C3zN0QT*$R@`$0YvcVo%gs z^UqjM-<%Hv0c99qsZS=URT_2Dace5z!PaDkUAA<4D!;a3xa2L15va0IYCT2H*fr3- zF+-n7koZd_v9)}?d3(gZ<0UDu-Se^Cf4|h3$@0L}ekXJWQ5W)*#P+;mtIF>9LFlzR z=|~=AEc?dRB&-QAz4Gg7?W?hJAwfu&d^Wp`ME6!USVvO=kId>|E-M0NnHTFioSmU~C#zV(*dL@C>F$3M zTOCikoh6wVTz`4iNR{oM$T*k>!~N!CH`b26aE4cA_hjHt0(sdl!h`54N1&e8UC zo?nw-lg;|#b39R8yiVosp^90=LB?H?cN`(v2Q<3ChL%2F!djzNR=or^fh;LJ9=Dv1N$=jf zr=a>tFjffqsH@`PAbFaB3i^L}?zq-@s= zsw@jf1?pzwtDzm{OP4{;1R*Xv?3ReRBKcEufbjCz86?_ltBQs9+a=}y1} zefUA{pA+B_GIL|#-k#%vpGvg_HZmelE6;J4-$pbw(k6?nK=ep+bFVq_Ybr`zYZyfl z;1y5+Cd5$cO`f4EhH3)Re3lDIAjMc0!db1k4KQ8IGwS5676NwP)P4mnburtw%N~Mw z`C^PZ;82l~^1L_!e!?Sl`;8gAhU}M#as-@SbAk?$Ul6oP7yi%Y6HXU@5QpNMQjvcZH?$hQyz% zu!oGFqE}jftqH8ghtHW>gLhkb@!a2!Mw|nYP|$#)nf!` zZVKfRzwi=3_Yi|<`P)CRDRz=gaA?)aL~gja!Q5je*0NyXGW@)KK0@ez5~9St7UM~M z!j9BM^|1_mn8%oa0MeuCU0cde%x3XThWW$(B6@|zv^P@bskO`dh1*p+6~}WhRDURI zM;KKKGMb@D40OzbAwln#y7EE|KN*I7le zP69J3Z44_d8(JDLyQ6^W9g0nZefOR9$rVoVIx$knUDiBL&~G0_`mlv0sgg zw4jor=iU(!*mpT`z9Rg}jTVX;peb3I$|lYlb@r5i8sJ*`ApmE*)UZq|@+Gh-w1C-U ziC&91?7?6_4*~8oXs>Q?C@GhB1QoyBx!y2M27s(i<*?&QiRst@sYjLlDNdngEi_Iz z>Z5tyXn|~@+iNz)YjvbxBh$Sdf2Sd=)G%XG>reUbGW~7uXb-00$rM}MAHOAwE!R2! z)2uzkRg3BTB2Gsq>YCE zJH*8y))6B$4iH>6H@AMG(4k=xe+CvVU9k3ox}1W090zi#34Paf6b@Z1)igXOoHXhF z@|smpP*CwrB(glvA|G;%t2@Qm5Q^30@)V~KuUok3D)*y^&-+#pV6x?=*7_4%I9YpF zh81b)A(!Mu0ns3L=nfF;5vJ<&|ElwgRGRg;)Jg3nAIg?dSO7q2VZgx{F3x4v7tZ^r zBPjM*j_Z?ZnG5WXOXoQff-Qzr^#xK@qv1SvFrBP;VKxfQbWtSdb$KP|eP1;R=Okt( zSKswyZr%>n8~BO9r4V=j-ID!o(=eaP%SW2c_A>ScKzm6kh**u9qq1en7lvG1Ly>oU z^m`Ca6J3sjBO2@}WB~qP@97qG{U&f-vETd6ZbSZm#Q)Dtk|}6RQrDW;eSPaM$TBDD;1)1lU# z!2_P6zXM8FqQHdUIDyYXLB*t)4`?Gpg7T914%`|kd^d!c!1B)3Y3-xsCaKpIX)Iooooe$r&6nFE&zJ8~7L6=(xx;E77 z>7==>g}gWwU@jgo|7my!$8M$97r!S_r(+)i6fr{*xWcyrT9L`23%HNmQpUbLvR>>7 z;+S!}4SLP05Az_UyP;4isN~OUJeQLUFBNJU86NQ)+ zGXxT=&o5bqC#9|b;F!p3I`TcbBPtw7 zcio|ep!|@{6gLD76iCY=e7&!KauPi#kv>wStSptMo*k;Cmn^d;-=0Vv1oEeUzy5k8 zADvP(_lF6my}5EbF`CBs)4IWhY6-(udO+P#ObMFN!;*MXDUYr4ci!=qlQ037>;o6E zMo#W*5w+U*xasj*t@6|1hP5&^B$wseh;>r!}zV= zj-_1AfniR)S}mqw*U74aR{kf);z5{%@yVHzV5OC2vHhMP8v{7hK7&+FFo0792@?{) z?j;iAWHO5n6mf#UGdEmiohcsA?Zgh;elKksKNxgGq5?6E{7wv;xsn^F8{v#cZHY_a z05qm6f+>|U{!CzJCP%%Ju{HSW5fEn!LHPM0R;E{uXKo?C2*8Ou;Q!HLqV(slzE-`f zsn>|}7w-V}22!1>jb{l0!;8%DU>4vMkYCbEZQ$>WJ`w=)sRNv^fUIyAB#az>kB4MU zmYaseAl~;T@G8JPk>0uI-=|)e$xcnG-&Zcuy6=N_zj=3F2n4)q02ic&aKpU1=^gh zl|gMvtUG@I-iTk}w2RJpXfh$shj5pxv-{dqc3IsfvJHGwQ&atL5svQE$~Uk}PM2<# z!!No`e2O~`K#($?UFNTu|1rzQhj(z@>kYRnH{O@28cUKH>j`3TT9$Hw!`~{`kd$Lt z0WUx5n1#*c`wdd=qc|5$@PwXMo=`>wqv;u+9`y>H<3e7uiC8A>w1!)E(b0csds2j9 z=mi21uI|RDC7AIfYv~n>xTbEyT}(Ho6)bCeHPdfLu5E5b2>0bkMm;OQ(CWP4a@vNz zkdayJJAOt734FlwXXqri{-^WlOW}nG@O9*{_CO`1pT2S0TxB+Bp9|!1uCqDnCR*Iv zm!I$hs8f+ZZc+3j#z6oObg3pc#B*74)t}B*BIoWZ zr5~_~*>j3I%Z$7H8sA}1J)X;P0c~%>ZqX4UGE*G77TfLtI-XjPVgSo3noY*bVTr7vBHL4=;-!e96Q88}A% zPm7%dC0%Jz$nr_}oXSA@yMCAi!J)}SDfQLa#!k*=gG3zT0+Z!L&?ClRpq7=3p%%?w z)tCz*IScnHIF)#;REmm*N%|?F%s2y}%yyqc#3APRELLs>2kmairI1z64+?hj@=H3Bau2s4zlUmKP~B`-kr+4*~aL`uW`*P=xpYi197;NC!$ zt~-M^k1}euTE&LyOE zstLG4__>_cb2^*L;;l1yoR3gU2Zbx|(N1`TJ_tRFcdl5rUf)flqFYtoE%2^#KTg5e zbZ0h|4<0L+-HmZ6G~iZyvc&)vpz5e-U|}BsJQN!W>0);b{DO)4E3G{x6M0azJ%5Gp zweZlF0S+Q~r7I$ZCjY19NI;J?CJ;;lE%E_#j#(?rZ1nR$RGIDgL<`Yvw1c+BmLO)Q z8Mm0%v_x)Cp8SkB`uz4m*y=Gx4fZF!cY$wT$RH^Tp62;Iv%nNXxb&KrN48i;K!els zCWnu=4H*2DTTVWM9)sGPz@Ybw>V5y21`7QNy~zr9ZnLq%f@RyyqK1P(lSnbTNpWvj zDc}9|NiBlkZ~KXhJMtC@a6DXR>sBvDDd4tm2_X?c&LoRvy_H+9g|j9;mr(qQD|mUH zIPP(}L{K;R4c(|O(TkW3N;S7C=XN;45&!avl|ii>1Q!>)F}CDOSMDR{Jna)3nGZNH zMslH|#L zya{z{I%89*cYFR!=JZy>WrY|-D)v<7f7_XmIKym9%*U)geE6U4IDGC=p zFkL!Kixcwpg4fyyVmxS#Lm^&b+gCFmNg2wN@W(7HtRl(NY>>3FLe%ie3pAZXR-kMR zW(5tj>VPqo%*-zRY7kn)!xXGGdke2g9TDf!-Fp?~g5hIn6kwGH! zC^8ml%VkYJt+?G5jcS$sdg-|sftL2Ji?B7y3sC>5%wzMtrDDDcI&z3F z5hZ+e>vVoQMTmlRcYj~Lt?^f+z6qcUDpgvF#{xaW2lttp+STl}k;6vPYRC5K zr)>&mIJ3W+rcQygv515F_=}c4*z-K0B40!Pz&SbNuf0jN#T>30hU!o~uCCN$+zFFx z5iP$~c27F=>n78w9j#}xWTeOgA3eWSDQN{bErrB=aZLPt=bLt0&tBaFW}krNo>`w% zZ3qw-5rFyNHTw&d-4{r`(AD|nA&$5e`SSD6HhmXoD&_uKsJ2CJTk&Dt?|5_Y^^OO z>QtEmj`ludn@m+P4}S9nm^Y-Z52-FqeD=fFHr3M)3}(K{&NrHj)$=-TPGPSkLds3# zBvV*WftcUhHIx)QW4W&`kF`-yi4s$Cv>R^T!6g0M76z=Z&#bA`k1X!qy(>G!&@($T zLteJhE$7u{f3zlxWH~}1_mC2J&~uHLhvfHYd-Jmz^}d;Jd^fkc`U!+Un*0(Ein_kY zXh86wTShN*_1?GFUU_kSjl9`$V6Xr>U)q)`$3uPw)pNYqg6lh5+)hIEw@^p! zzi|_@`9AndQ$w>`S|XJX-m-~nH*qgy)PNL z{Ig+`*iy>w*KQ{dje6sA!`?}>yUMcclTHLi{-ACvFntyW)1Ug%*Ba6Uri2H~x1HfA zcJejgCr<_Py{SZkzc-`f79aP)O(vk3iAtbahz1bqCjSR*z#YYGz0G64H42f6!Yl~D zZFpm%Ld&RG^Q}`p0{~?)C=Rv@3aOg>v4r+;s1=XC=FEP*S${rTX;#}_Q441KbV@ln zSqWUO+YG{{V-+R(Z9lu7D-_!<6n)4R!)22yOaf5b?~e-HKv{BPbK-9@UX1;I@uk72 zYNDrs!P8dL9S(zCo$dr)5|D%FDLn8^}rPqI}6CNRJEeF%E(mF4E;34ug8_<>L}PY|M0r zGRy5XD%eIUGzl-jl3a2_={PtdBlHZ`c!fhr1Ds|pp?)nNZ{8lRc>w zIZ7)yd#{~TtJvlya?uIQXSwcxNX{O!My*Qq*Df+6bT2fSD>s-#0VIn40EJqP-x56d zCNW6NJ=hxi&2=p`>>4Sd@)GY5RV@P_XXYnf!PoBR1&$V@$_skU?bs&!g_IwoqVtof zW@cJ!cfWh(Y&6ffyTTk^@Punf$zRlp8=Vl&@}>W+E0o`4kZ zlgB~`$MDfPaGwuXp_ulj@ZJL}jFBqXDcYSo&}+dXZNw|ny_Y;1OaVrgSB>#MXuN%V zM)nEA7Nil?0Pb^(&0@Ghlbe*+5fk*vBNCW_c*m_bJ%x@c4Jgmdn_47V?_1Pc5KXh_ zb&r>*G&sUL3)xB!h{h@`)iEioILAVsmPz-Hjen~SCPJ)yb#v@6PVW}E74^Iae2mh) zz4tm!;%=M*uiOFiYmaTpY69=>w(G4j0fzVl(i)nZ4FyAmt6<=jr9X)&>mEoe2< z@45vN2u#Sggm=8~;lr~TuD5$c|AoR(91k*tf!EK+DzEwr6o5oKqye-e-0sWeXCrXC z73tO4hE$AvvF~r{UrF@uH0d@C0L7}k$t-!2=d}eIMAnI)p3E)gdPe34l2fOksQ>3H+Z_G<+m?xnsc|~{fK;p)F2G{$ zJB2xx55eVz>4PVsm;*cLS??Kid_mQ$wHE;6@7;q5RtH>n5M1uCDeB-tY1xdb)lhg@ z^tIJcts^pXcZ^x+JMD8-ZV@0O4OXwT-ca}mhRl=_u?%J;2ECC)mDVE4E3tM@IG?IW z5ww!5ERyqtTQWh)q&lihUq0p&kD>GR4Xe!<6^!aVKtJ4n`uIt)( zJY_A)+{+KhIdl9B&u8JT%BV&m;5_!tKVxUdbG;HoTt_)#i2wsG|MirQr!-+l&+*l3pwa)od)GId$E-Tl&Ip?dvVr3t@hNKg-p>k#Ai z2bpv}?%=pPp5<7}yc5f}P+>J0h=FjXyD3WbT03~9A@^&NKs-oLI3tbT`26MGdonT8 zr*|(GJ8-wAM(+I-<4XboMqW*5SZma8X0O}> zgbBK0Er{1oCKLyS69+Or+c=PeFpNCQheSlYQg1(rn2h<46^U4$Ql^6>2rv^0fGRO8 zs(lcuRa!ji4&l*qwprWH%(>x19T`EVTHtxHS4-?<|30|nvL$0zM6^iLM=BwyVYgs2 z2?#K3XA$^ve%Z!F3nT(2@SdSXD=e-+wakdlN2GK~-};_Jsx5Mfp5)ZJUh}?CJhp_nb}%}>QbWPObFn)? z0mk}#EOuCsZy|Gt!WqOrW-X%KMkltKEz#6u^<^G%{zYLsNg!njZlzIhD}4gv?95x| zKYQU1zl{}TOCwh7?rf>qA12P%!m8ZZ9;&j|QlI$0eB#3PAqz(iN;6W6&!p<@5t%!1 zpjpLP`?w&b@bOl*FF&R-q=t`(jSkA8m^)epOm-lqLIa{5o5VpmLIHN-@4~jHM}(jO zR}Pzmn`HV%s0opDu2D2Ht;0;&(7C1Aw)+ir7|#6Fr>ymZ! z6kg2M7zPou8-g5k?fT&2bKMU><^tqP??}1q@A{V?M@!zWUSrhwc6X9r+^Y~x*y)P$ zTw4?(wQ$w9_>Am|(EF!4yY;&eVn6*H;gCcq>&*oMSA~5(Kr#z-343qYH6Q4B@tNwT zh%8938;J-%n5mxiY%o)o^NtzJE0pxa1Lq}<3dkPd8mC%;qd*s1RkUb`w6eY(YXhI? zb5%i>lBYVmYwmSqj&dj{&ml2Wie-uh({4>g(rVFmEv?+D|m ztXYmUQ2&@!B|3Tlusv%pJ5K5SOlQmqr5*U#t(27Nh{XN=r$QC>2`uZBHz5jxK(!V` zA8R%#D|!P4;(kw#JZGT`0^Z?`RQ~>4ehRpDEB>%wUbK`?Bn#R4QiM4~p0b&J!)07+ zZlitjL>~2!Y*WFCq~L84F7lZJiGsG@C*hC>c^v{CrsMpLPjSUqj`0~)J~Dz`^ws4< zV8c;~k@zlC3ISPRP-sL55x1}*=XAXt+SL`BB8g-c)&jwC z>tqcnP-{OIizyx70lYS|uGk)=QmnHklFkw5Zg9`2t0+_M;4=*{!vdUpt&3lM{7^)h z!NpXK1FnF;K*L>yPAXo;uDG4Og|@cwYamuo7^1d$%xl-#Q=(`$W08fXO}X=HM43dd z&c7}@Tt7!Pq7mA3pD0%6ivd$7Gj*HN_jzT4;0pUtl~Dxx%^@-U@#=2UKF zdp!Ewvjj?^J5x2urO=mr?njSI^&s5c(a+2H43Z6uJ%Ef1X$b=Sn9Mlh&L#gSsfNp} zR%!`#F5>6fX?4V51#dedSAmls%&N#n)XTyx8Ge`!J@`7zS7kbm7Q0N5WFUg{g&R&L z>YH9Y0n<-HZYu_*LKUwjqrzliYdGW~pP{&Z=P=VqMxrB+7);B#089jkW~+hVUm~)^ zoF_K2lXt6Z9b_QH^O&+E)=$2o6B-{io~nnRgR%w}7~aC@V)9H@B&NWiqRg_rKMi_2 z4jaLjz-|deFy(fDY=ym+=74p|HlK*{AdPZSx!~)IUpegDkRb{LS>!9CCv8nDxR_%C zYaLVLS2kukYZt|id=IIEPgAL$!0cXVa&eDc*zA#&o0B_(=wL{BCfE;u{BLT^gE6cBYVMDGssaUG261(qg+OlL=+8@hkndGH zvF^)0OEp)FQQltEfYTp+9R9t}wP#kJ{ij6qk=#@-rG*$t#O|2r?pr8JB>>IV*N=^U)e_FskhWrIzKCEq>3{6-Lap8+A&urRLVHP?h8c19)dW zbEL&fbUot97&b%8EBSX$n^m?amJ8N&@kU>sZBQAg6LDt}vD*_OV5TI8<0q3vx8!U^8AenI-3`$0633pJoEi0B_1MWjV1`{v4w#lD;UTo_ZZH8$U$bY&*UFJAaAkv!4hxYxO~;B^x#b~<)GPs zT?@LHC9}F#tS2aCot~Zo-vN=Cnz&B48=uX0Z9^_5^%_zD@f5B-+A489wqolc?vRe5 zAfQvs`MPynw0p=go8ym?-3)Zj{iP1{4h*79bAu*V$|USLrdh;nR^LirXQDYTxV8(i z95n~84UO0tkYHW!3L)nNFl|$CW4%xYa@z(EAB59T`EfV{*F;;1YEr|i*~k2M#rl@A zhMs)h4aWCwZ7@W$xo2?sdTs(G}kWPhv z4kpr2Eb1{72Q_7KpZ0|eFwS&n5?fQuewdmkM8=H$>y(+=yg?8MZ59rL3a?0&Wjm==b9&d3N3J-Wx?BAX*o->k#SB>z%_UxBM%bx%yn5**-5wJ3g%w97`lH2r z8dVY;jU*21LNk&<(LzXM&vE+g|Bt!1jH}6Vk#31eNJ@8i zDk;mlHTrJ;8W zV(1eK%%p9J45umWeANX67{oa^ImaYA3sOhtDIrFjq0~GVC)?Q`o8g9H0+F(R zpc%xc^}VbZ!i=>9Rm-ltU})&d_LU z(lry^r!wlvC*WTM&hUf)KKhmN&GXrxwnTg%u;BZ%=@WvzcU3l(0L$W`Z3G7N&#Tn$ z|Kf|+K~*7p%&wDJ#h*^Dgd$~Y{#rs@$y5V~1@SsM_Hd@@<054fSAIBfEX$Ty=n;>SjGK9;5y+3Oz`bvW0`FAdoJvj zNS=O?`P{Y^zba5nv65QEFUuv#!z|w4R}<`)+}6au`QZK(u|8=7xQ@)Qn8=GTP;p5u zIMwLL%zAV6#K(;bX{xktJ9Av}$6FFXH2mR?aD|(z^G~pj2Zrz^bu%?p)zNa*8tYBU zaRiT}T&gqFL0waKt03y-yut|Np(ngtzEmI4E}yk0kK}PB$q@>}Uu9V7YRCiff`Cb9 zQ2bE>*wW-`37(xp_@eWbkB5H@8(ijtV(+eldh3C(vmE>~)b$kW0qjN58ZU|>~Z$YLZfJXa56*ZDQY&fN_P?z2{V zVx$==dcoA4CsXXD>E}ZY$K|pRu)={Bjojb98b_`;@vq zf2&-f?Y8*qT#q+i_MUi$naOyt40(;+nF2H;xM#E#=BNi186tB4V>aNBBO`+|w{mAs zJOdYN=Xc>h7r^7kYfUdYCYGr}z{+$RZXdriLF)c#?1wh9RGq_O{pX}teR0|Y{lpjY zJ#+3&_a8q%UT6<`?j$PAzS zbSewK&e1_zL$LbCuBm%PNDe=?C0%J@`~z3ja+OZK!m&oML-b#(ORqCriE?>I>J=Ti zvMF#ZF08sK92}j-NfxO=l!d&wwvCiS4i0V2pQiq94jnQ;Xl--rf^)Z_suelr(;*GBimPrQ|wLEKh zcC!1>+{nc-duFj+QY;NH;_=*?GAYpdv%Ex3kn?Utp{jsE^HjHc=kWoI6+VFh*DoA4D}b(y*Y@jcrYJ zMU4)`7JeYy@PfYUQ)#U$gQM?gR88HUP+k(}BDFH`7RA0SM(kduxG}C7GOBd7L<1CDUL8 zVJXo3hD0q_i@|f@K#?`@!w(c=rio_<8^M6|3-|+6F+eaK%(wuLUZgC?0zX)N^NeYS zKk)e}`&wUe2r7&P;px_!kYi${k~$*-p_&o#rPA^c-MaVhpB3g>(Tki^_gc4p58DF4 zU^=5X8CjAy?B&OV>`U6F@pM0VscweE9}xj^?l#uon@(=*Tz&St=wLPLeByMW@B)Ku z(~;4qCKYx`1YM{8Zr2xN3)J_%N4~dHU`k%C3*`DvtqTO%-(>?!$a#fMAH&l~P|vkA zK#Yv`b2ZS2(*5j>9ob)tABxq&dn5nTe^s2(yx*iZfl*Bt>+#0CbTS(5h}k4Cj0M$L z>uGI<3qt)P!e)MgM933>8p`cmuSRxRLLb5pPbbXcT(#s2A_H@N|F%%Y42;dBp5l2E z!_bm2#XQC&IwqbDzC&ttRTW`$e0;Mfiq_fhRHb4~dT_QaWO^&B0@fRSa+hcK_taPS z6A~mYw2-#Oa|=QUTu!Ex@J9FPKV$elCibtD+Bu!aMC=7DTGJn@);q{N3RqZJc-HKI zTKH&V9{-JE!SJrt(JvhVD47_RWk+ygj@p~G3-`e~Hdv*p=WDrFUm9y!^9Peiulao> zVqR?j7)mzk=wgyTJ{-vwn6BPubK%gVCDR%c*graGEtsxKD^1%ooQ6-%yCcH>Ml~*>qlD(GNpOYh)l_xK#{7Uqe|2qb&Mk-zZn>L?I_B%x^yDznvYDKH z)}0{iTKAHdi?((f>s~bg=nswgNG%>{Sj_hcqCnqI$MoIS9FX?*7UQJ1XTbf zaUsaB1^$3D+tl& z8R<=x62Rn*rw+Is999IX%o;|W&F4z-$a!z5Y2pRF3^WFBFD@MdpP&4V{=t}9d#@(T zoH1)px4d_`owPPfS2t!dBY5t^9tCp`Z)lWed`>NgS2|CS*o;Tl8CNBBChtkgLve{6 zGDf!{Bm-St^l+D4g@#%lz0UD)(=z?N8G3Akv1#$E0=q&=whExgssw4Ljo)8Zx})SO9_Z`noBKN1ZZCj6cB*)c*_qYW zJMjD(Q>pQYk0W+2)m6o0w(`+ll}S({Z2w8Te0ZH100t-zwqO&NWeh7;AJ5pc*Y6a;)sPz>zABgDEwOdN-jfeA-W(1dmVYl z3N#RuSPYQ`KYmPWL)UoLk&aG(f7*gi z;(st&N8dOO^Q(zNToEU1h%7pgtV3xA z1W+m0HOFZQEU)I#AHH`(nea3tFUnrD7QyX|NB@FJvhs%7jh@_bCQw zEl41U-}m^D6m_;L#OvOHewCA=~5;`E(W+$Qo@*<|np zLPaI(TB7;wN+#9>hl1T^vj6o%$QU>MXECOAzJOX z`{?+F>qBl=$EWi-=?!L+#i>T0!LJ<2cvhL|f?_-R_QGI#KC3S3h=t^LhA;S(&n-Xn z-+}?WAY>wHAyj^Z$KiJOC5}VwtR|Mj+wwsl9PxQ7<5;r6WP*-%j zB9(31HC<*fY6I6Z)lhXKI4-oqS@30Ju6T8GB?!)6gkqn>twh1G4(>sMRs` zAIKyHx{#R~dudXp?=B$rvnr4~pU0CFfu2kFM%i%;N?7We2g&mX&}iOb=p{^2{Nl(+ zsro%rHtksLyyLp5MBF=NNx3ok2S+W^ytn=s1ggMo!4Gll&oC|Pja|~~X+W#k z8dYdc$`pU9eSVN>lt}s2D2~$M)~&aFkjOR*qmC%3fmmGHN~7CGYiKQY$+{F0u^27CC_Y-Dg;?nKC1oOM357QRD^b_8 z{VwV#1n*GT1AXm; zB6y?s(brN39+2jk2uwvA9y^WW;DT*Z7GZQ!CFS!WRo|E3Q6=I5sq1@;zIYbFUDcY< z@$PkLu?WFBo{O`y9O1=}=3s?c5+bwtGut$t6;aqQ=5{>jp3@oDGOIUT_ByEMFyHmE z@;yjYef01gyXdJH5mYf>#$*-P$NOSc;AnJ^9>veW$>nxHL32+*K!8VK-VrXz)AODB ze2JI}yW#1K1k|qUQB|JZGQh^Uk>}BURPy6_x+dR-KmVPg!Uo~r$qKo?@j1ywj1bu( zvr2Wk{5fHL6|ZxRDpcyeMJ%Muj1qVfXMuQop`CToT@VOUOyqaD?qj>aJ;VW5T5d1m zt$&i6rZQIKzx9*9)D@GIJ+0du<^Ibmy9kq&&DZct!3WCXk<`Ivvvo^l10BN0;|a|y zo?DAGx;;;U+BnV0!D$V&m&4OxiOx|g9YAJJqjfZC`Rrz~V~f$^Hx`$}?>l%j0{Y6j z{GSdW5I%t} zbTHI7`W$*EsYERg5Kp-nakn>h{y?bp>4iHFq(Zp!QGt0InvoI_j|*}Qmlc%uBe^|% zZD{ggL;27vf*0+Uo1-6~x>twNGtZ3;i=aS;Aib0H40YQ3Fd8V0L95YLN8N3SDs zrZ&espOBemz#k(CSiMh*2>ALnokj@;ywy81CCoG}U-sr)p~ORh1k5>_#-2XMfP3L% zDfn=6^^C~?OHU&C+jBM#`8%>C;fs^0+Uw6kfEH70EngWLv!vKJP((woTJIGBy9xb} zyI*2EZ8thpNGCLG<54o(LnavTqN%dcH$>@iCpYHiu?lP=GFZ0o0m@e9PzFE#W`Wyq zBm-nxvD7t)+nbcb;UU5>2Qh7%4tj1peeQuqAy&D%A}^fsSGE%)0roor00a5pLL@#JPf%PJ0cT0(?cm2QC#j~FbrA6y*4i<;u7Eq{>; zzS#Nl-fH!&i&R$yOO-I|%%kYNpI1={)RdZtBS}-X^ga+7Yk~BIQn@C{oY^SWh<@*< zyANtmlbbZL0o>{beh!*f`pfbvo$#`%D1`U?vSkv}9pALgfm$M|PKc#&A9DHuAzdXDe0EP35#WE%E zpk)8-*%v1sOIpUY!6T4vBcF_r2wq=V+KkACo@#Y*vS)yI+HXD~C{E=x|-5XXx8Sfa+^HdT^ur0tjuM&uTzn$$1lYY6W zuYr~k87>b@>S<}&ZjH(u<2H(L@_6idD`CVnOzfzQH3tx-x5MikhFn-Aow^g*%RaoRooA3_ z4X<_>JEY7fmBXT6l?2MRLK%RsH3nUUb^u7LQR35dI0LMx$*gsgS$_zOL|~0P;8C|I zPklVos8k0=!lTpK%@V%VXXWw(w7I`4L*&J^mb_4Za2uoWY>YjLXT2^2LvwKQ%uTE~ z45WUY(2(M~ZfUE~z#215a&oe1vEOa$N`p;l|A)oDmZYW~{gmkWU6urYV&kMFN%+iK zN0B?k`;9`XeT*)M=es^C6FtmFND@c z)J%v!<*&7|^Lu>N#iVCtJz}==tG7~HP$v=xyjvfj_p-(H&F#45(m~(U(n+6+QWaPp zbYPP3d_QGj;>jRKF_e~qicy`oW=irY!Y3ntYPsCAwlWcKSJTl!1`ETSm`0`WHkt{I zTrt`VwKfp{mVcns#gaomy|TZ&q034+l?y7w_)bY0Mnh1eMTFvX$i?*q1q4K!rRo~H zkLOiir9~vQ(I2e6$;VW-+q`=1O1&TaU`OzN)5Sxy`m`9VOt*ize}-6MXup6l3gw%L zLUqX9Uu}8glP)v$mgIcOFAr3$n~@DU#mv5Ep27lQeM-6-8VMI{W)Su+STSy3sJfYk zz@T8XOf(p2mr_y^4Rn1&R#1<5p%wYg$1Ycgw5bzBN1ZL~iXKeTaxO-I&r0zF06Gm7vBF6?O*nZTnMT`3L`z0a| z-U3%9OPDMZj)pflH0LIv@|Qnn?BbYeaK;CU=YwD7KmEdyLe?kZyG20VHrR_#h;P_Z zHn1xe$q=9Ne|&pVHb6i@k%sFH=GUTcUK05INj5waDVKt7v)8>R2V|CRGFGMzSCWPX z*!VDUO*Ybqj#+ND_9(m}!&Uwuao?!#_{NgT00mj`}HQ0D-G zAo=|~47r(Ru!69`j(WFE6GYQm(`pzs`n@-JrfR zW9<|)wa6%%@f%79=>#L?QpfiIA=P9Q&(j^ zlAT2lEaX75=nfJa!0%G0ZUly3UkS?efM?C=Y3vf3Tp2FSzFcgB}puq)$<+@KI%fCN%et#mGa z+vTn$fBTyQkY4$76Ok3?n9ttd>5F4i++K#OVI+dg@y+$63IB5J>chIPD}Dg5ES<(w;O*J)(Hep}mHIORtI?94xglnCfZgmeJPbelwK(<;C4I zR8Z>IHIn__YUR7SSmZmfH)Yc82aU{6T~4EcIZZZmirM@wNPYe>8oZ;jI6eS+!=qtw zT5vl9_gz9HKixM9AM5&O2Ui+SWrnlgG3j@CXwY&^5Y=>5R8@6=^Y{_Yy$8Y%j!B~D zs9Z0OXa;}FUy{+SWVH+p4K4N6>9B~Tj1V~%)NAPI9QA##IoI!rmb{NmX}mI++Vb#m zuvvPke=;dBNg{)Ji_2jj8)Gy-6UNFHRRzoI3zjlI0mr0@_hog^5 zLA$Gk$XhU5^CERSyX$S*6G!LpK7@0YA=BM!$ss-1Sy3? z$USSIRPqI$4pcBOmkXhRKy$X+K`A?gKw__H9*5LrP{ou%*+&Y%VZx?$ZLcO$Y>g+h z=I2}|OROItFl3O9SOnif;S_wX#Whc3G~8rXaU5LIh?~G8Slb%!&EPs=AM8rE$^Gzn zVv8Ht*-*{*mT1t&o_!5By{6_`TTj+0UhRtz)(v#Ogjdm2>zFD($#V)Ktj2zpgXCv5 z8$%i!*in${mEcTIU+9qC#R=l^dM{+6l}W~f16K0Es)1(vsFnBj#nmEz-nZ7X9cJrG z4i<+D*KAHO`56bnA9r_m)oP2oK!4kMdvnTrP4He&FMSgA({v`b_}TpQhn1k1X;pmB&6Km8Fy@Wg9nD3aNm_tN|5Nu2pU=HKY!p zb6yq36RDs=1*+QF*An`Y|EfbBzqdo1#arf2Ia{F-r*7vx6NOLT6xC-ZO&$4}1c?_pms|)@WgYS+%6- z@hSnczSg-vo>GYsCD6jMBEnJV%tq4#Bn9SB3dx{Glq zY52U>km74_ItGy!R4i7bQKkD{0J_GqO`NPVXAp~|&hMy9T+;{pTspn|L{4XVnorj+cJ7i0 z^9a@2_?(thCWCkr)6<}-%#UUQbh@|7@wcG$RvWo*#f??fJsAjNv_aYyN?0N@POCgr zsOFqM-2Z4KUc54)pj zd?d^Wj&i@Id*1WM>knbd^oJUHqt$=2ft{+8i4pwaoy<3iQvpdx_PJpm2Yh}$)-kGP zc1we2g;ai@l)pj;q;sxSxkmO98Isz*Mq_!}J!^dk!59xQ3t9o-*T+ceY_+gW+&)q>mg+0p{G8?u|EwpQyPj|KlS|ydJ&+$#63`Z(eCj8)M zl-4*Sjzv`!LlsOC6swDE!~Ae0OOoIB1U_~}DYCS#2og-TeviE}`1%vrE1t`;Rj9)& zo_qiN`Qe>=Q4LRGcwM9uR})5^-R{Rn5huKqD&kCz_Ip#_;M1}^4xhfjT^R3E*{Toy z`c-}cY^yyggZn^(9WI_p80%Q?et{~%15BdPj&NT)GyVb{aIvhC%+Jnn4^3mY40n@I~o?1kWhy~L)!vkelB|Yj#ld+-&`$AgRY}e zI|*9>PPprOe{6b|vK&~dak(5}tq(jer7nO|$(5`#P!uavsw%%T-zNb^8E+tQ-Ik*b z;C6pGrH2P@?*kwoSvhsqQv|(AI3emPj?_$-`HSJdc&1wtt1{kdC6-JAWEIVg4bJ;u z(0>Hh`s;J!rYbU^)~g~haK&XRH<7JG`4U4^bmdT@wEPL-DRb4pQdyB)0H~N>K!g$a zYz^JzkT&xa3d$W#UocSxoy2e(>k)BU68Z)w{jB);qCSl3;z1rrk}SAw+#^DJ|IPDm z$5_ef+Vi9fc`~uxKbMbWX%(XUkR?wskKwg+G8cqE=%bm^s^S@KY*~(7ZH8F7Jg~C4 zU2`BIA&nN7Us8^-u^F;i9PotCS}nC&f>n?Fi1BF7xAeQhi*bzi4uD35Ia@j}-nq*F z>p=V#2vi^_L5zp(<5#QVlQq`*kaht;h${kF^6A1LOn{w3b;$0nwJulWhbnpr9MdMI}EqoTS_}-lr&N>`O zBIuPztKs~>xmvf|tEwD+rZkNLD3lgM)9d!&=h;w)c%uWeCwN|$%OOADk&R+FS5f)D zzNQP0x=lA&+b84;7}G%Vz4j^&c|T6GmhLrPv)0n2Q{s$9q;#JUPeZV#H@1%RfPqUu zW={+DXwkLewjgXJH_>oG=9w8nwHL33#`*6 zbbpI<;FoZUl-TUvSI3k$o$ETM)wxzxRkQfXYbJ+}dW|7!xFU>lzRyoGT;l)Q`jVFC zlA|JY09Sb)?+wN`hnmM?&+`!}f+@P`n5J^;AIqZjoqXeaqfC9)9dLiO}oXf1uSn)r$^YC0{9(dJUYhEw){-y1oi zU^9yt>?h`AO>YTB$Bn>Nn$hI!6VSy}L-nrD&j#oee4`dxv$c63i3wLV`XTB@uDk>Q z(hp^wmQQcTmjrekuwhF{w|kI z=8zOgBsoCT3kN18(Sdi}6qHT0_&kzJyi;K|E5yK3Y`5DLav%IMmT3u(qV3cD!I|{r zf*^cYWX*UJ6n2r@nNO6QD!ZK88oijRE*${2+d`xP(5n{wK08qbCn5US-qLQYGUQNv z;BEFO)vuM$hdYKg)&jEPLXFPdr;pPg!kLvC*EuFtZfMEVR{ki%$9CuH0*?AOmvsoc zBrRIngXG5~YrZA0YiFH!Q>`)V4U{*X)w-@fzPiTDsf?8%ZD&|3Y16+E0)b&+fSW9? zuV-^S0M=1|p0*EWGrkzaP$-_5?|tygpsg)noO9=`UYJaGIAQQQKB#5;3)h#h0riTh z{7z%j&-psx1c`=k;4`zs_Y5XLaz-xS^lW7_B4r{Cdv6APmvYwWq;+BKZG0oiP= zgN!bN3o|)zo>h&S<~YspB$#>yY?;DQ-H`G_%3VyEfkVW5`Sb+;2^F-X8}H4L?BSo! zFiWVEH@Io#Rr)6M{Dbl5*~SR7z%OXVW>#wiP_=K?zl*?C#bf#(+r3KW4t9F#B(M_D zV23l*(~PhW7+FO)oge6y8%>EZFuqHnCq@<-YxG^jgv8nqy)>oZX6QNyHQ^f(I+yeX z8NI8_xV52l;eEbjp2^awUdfaO&u$^K$Baj`R6%RvoSH{QnXb5O#)Hx2*y9=zi1$v? z2`6eRj&9C8)nm7Prxpa|(&>a*#=Zq6kUo0O;tw5u-4MAp-X9Tu;Yh|uz{bWl_9_V( z+vjfD#Vqcqtprt3^61e! z4enA`>Ft@&VjESmZ%Ugp8EWyBG9hMawa|5u_Ck60ipqS3@OSnlz^IEs!X%KWUD?l_ zt5B`G{S@@&D&G&wT#lVopBTUrzw8}H93k_R^phrB3=|tYCL6`iwwv~=G)fFd zUL9>siMzANEP46zew_65?M&;%DSI2A@)|_0KRO352feYlUN2VH zR5xNz(x5P;l7)_m=@Vp)-B2n~-~9%ZTY09(64Q2nSaO?0E<+rYBT|rhR@Ce)PW{1S z8rQvJJX#&LmGIH2{RX62(@2sOka9&cf$o{+9y2Q{uIL8|*x=P>M{;Njx$jn(+KI3H zNaRA<5SZ{le97_@3K4~tp|jj|Xacc^>H%xNJ2z94^k*K6M>9Z1z|P;NOus)TDH)$m zJ-@U{=ed}QUzW~IIOFHkkM~W^w(@dL!#?~dMm*U!ef0KMYOo7hJX0-`y59(*1u0pv zr7X9srqhT59)-hDl7Q7`N|n8DLp;p!ZRt)rJ< z`(Jh^v5U6#MPzr&R)jcY!|cpC?Y3|$b`L@qXk=vHyPl9S=~>8tQTc0Sd5Ara`#$f# zjmp&~Uek^`k;4QcUH(Rffw$y866F-r0!-2?SzRJbY-FUuP#SFpfBB*a)}7u8ZT#tg zgr@FNOPfywfsq%PC(Gx8VOu`|o(OUFU+ZL2iHi;w^+`s4NC#(Qcgyg*mx~5gp11tj zpi*=M6_t`eqL4s!xx6{{Q{_nnd$=IgCBr+~QLVS{cqo=X!SL$wt)%Z~Ns400lyXII z8n3HY?o5IH9WtBE;i)aRCH;rklod8G{qN;dSECt^1j+x58?`D;jrubWvw>xSlcX3Q z?`n&j`mLVzM}gefe+h2@8T+@u5l4iG&5?$y;b23193*{x!uU~28+ggm9KwUiDU_&U z?LRu*i{5ITttEt-%WjQ;N>;p#D3Wf!@3LqhJQj{9P{S1la4`-x5a^n29PiAchmi1m zB&>UfXz*bMTct#qPg`US?BXG}FJCr417b(!M*toMXdJa6!YXHp;0y#lLR-acbvD1Y z{)iRH4Jl_|+gVE3Fj@z)5k%eApR>1JtSL z!kor^N|^s=g1NMng~^@#TX^weu!qIzO2EO%aZ{TSD6TNp2NIjZ&(STm^+NxySIhPL zpZ!0;Pu58?Bl5%2FTA|LI&ms4*9&P=5=w^)A0JfV8~L!=8P_kc>(co1cg zy}n#}`W+6cuiPMHWv2YG-6H0ZJ^~(>F#&rB$^tKB9Oa7G@3N6_aFkyj;Qg}EK^{t1 z2j;1>UZ~45z_jSvTZwdjsNEeY3UUj(>{cn|A(%|Hx})M+G|HvOXn6kf^*xbvi7BfY zEASf?O6=XR4lDs&*zIqkq=&C}w4b?4F&sKbnG#jRhjvG~$}~8!=yZni{qgdI$Wn@@ zfkL6qD9#u#z};C26vKP-YoG|NRHqLb^9s|>&d#U?oR)&?9V?c5yvnSdA{i8=Bzs~Q zlGTmWNEjGay78>9y@}YsaZK3K+`OWow+m_-?~T4rUO?O=)hGd;Ao%T0e@AcSeTKU4Vb4T3~H9(DXXOC&@a zQg;AAxzDZF5e3}TiiNa#0||El#5-E9DCv<}rY9vF&srdpZy*!+oEZgBN{|tXoN9|^ z7f=O<2{8X=s^q{ARfTG!@5F(wkugj15(glAgW?!GR~FBF9us2#Sag(?kX_4bN>l4I zCug<(z~k0BGj#3{P;8T8mFa)+<7M&to5KOkn0_e+(f7NY;PdOG4lWZJe@pll1k&KR z4`nf!PJJ@hn@kEkrMFD4*nWkl>i9)OM2wwUTiCqiydLe6%TXvWN-zXFh1S0rXXhf& z@j?hzreMqyp<7v1HPJy0aSAqRi?QX(ehQ}1h-#z23mc4Al1Uyw0g%3WxtD8rA078Y z?6B_dp$>9J*LI0sjbbb{4qN$={v_UzA?^01!~JDOQy9iorQwq~x9pO*6X$q-0m9ec(|_p>&(w!FolPR7=us0OUK6664H_e z<~}(}ja-1305Ye``P)XpdAEC`^*n@(IADXl+{ZRQm>owIYicE-Ox6L->}@{_Jq}H& z4(pX>5;m1t_iKFATXIa|r?!hP>9I&I?2=DhhW>lN&;3Z&6W>y*$1>e>Pb|#0muv_3RF-(v<<1p$BaOtE^`;!B}m8-eABv?Fr`#s#SRh_dD4t$dt2@gQ6I}d5`tFRAxsHZ zm_*>c&u^G`(BPb|+jBZrEZ;`NW$?lWlhhyZOWiNR#b-THv1Er1M9D|~W!ecDpzV9BDIuOySeVJmxo&5Mh{xY7ig z>jCsWV5pWxgWufT+(i?5yVrEP#Sbg>_R2{KK@QR3_B5Lr%zkFpPr~cwK_#7>vkxi? zzVs)=8Y!8~oIU3uW(Kfl$Wgcf;HxLpFO?qgK6L)t5f%n>={3J+I8X`9_&_BPW=Bge zJGKGIikD*IS?^Es_J-L?1haKzy+f&^AeSvP&eBos_Evr1e7qAZmB;~|u-hA#ypIJ2 z?cS^6pdjdI6FPGQGp3m6mPro$G_u|oU%Ubz#@|4f?WPr)`B}|jj_{4Kf}I<<0lmW`UiaM ziBKMscmQ$HqHnTWFw~5eZQ2v%kyn$Q+<30F;?XAO9dGnDlfm=eDP~w7TzF%=A^T+J zrNKe}G|hr$k#{k4KGggc>EA0?p_~ zKAj?H>ibj2rz6`Cq^^OuQ;XWc=RCpKb6eZTq~Mn~+LwVj-mur!uK<6il8gl6;Lnwr z^cAM5BTjBl;B^Ypc7dfY;L(wI#}9PLG`^CEMO}|$;XNc6}6~P zEJ{+UHjt73faqsBli^edG@GgZ1_Wn0@Gn8gxtV1JfFjzUWE8ca)2GL@*&e?~%xOPV zc}VxpSkGXq8K@wDO6p~Ffs)yKlFMVov0mc|R%Ca0`2?&3?NfJkBznzbaNCp&sxq7) zIb37-P(mgsljdM)vpG&Y>wJXhj^7{Xir6~O(wQAEnE(2inA3Oy2pw~|vz6sqtaQgu zZb6s&TkAEG3e_h~XGlTdXa+Q!6*EO1=@CzO2-rVzR-J-osQ6>V=&{^|K8ituUg&uv zPQq*jr`gl3J{2}b?KRr#!z^Ez5QIi?JR1j_GSH?AFjlBKDcZ)m6W} z##_E1-eI#J?w8m0EG86pA9h`ZJ%5vnLUq^IWGanQGxYK|NO&}NWaBdFy|dZ)l&AN| z1X#TLC2I>nSKO2!0DFb7pFZ`GyG;uIZ06L^fcVUZ8P(iJUkO3;5(`}kt>mWQpBx&x z8-^VQRpoLz61l$xC*FayTdXWUboFme7seLZBTA%uIpI zr5ka*{Zu!;JDum#7NE3?UjPo5Th(-`898Xr;!AC$dYlru9MQ(|6d(f;cFwx-qMz7V zyIP~R_Dcu2xwtIkru&Pur=bEzOUSde*=lupP)=C}%>PRe4I8;bONZ)0_Petqz8GZ5T*2L=sT0#Z zJV|nBxp6Ll$d}LC%5Kjr82(RmuEF-}=C`OnW|~NGwBthIScHVokh_iGQ%Y=l30U1J zBP=*1R6Q%xX)AxKOuo2Ngy{`v#v>r-A@BwVU+te?-5kJ1^f(ulobOF8VZHG{@?@Po zc=(!Ai!g?)fc3}NNQV9bFdXN6NuRDVGhS6dk}q*{sR26;nq}EeOxkoF;vjGn(b`Y* zhVm0wn_jGTEoVX-rJvmre6BL`lcf}UQE)Q{B8W0QU$!c{91m1`uEVcSruWV^mP5i` zM?C-BU~Yia(g|ayn*t@>gr%!_M(ch$C1j!D6rUMB55 zP{l&dPqoQ3UZ|EG^I6Y{ z*?{J3jvFtMISM4`W@@x2`wwd0*)e)TaHz{x!Po$y?J9pf5}`^s6qm@WIOF(ZR3iS}tuI90Y%;wt{7 zl839#PoiAk2;PTDimH4c4miJZME~8DGX(WN%(GeT!DzLIlW)BNEJ zp{+hkESUq4NX2d#N4j?p+kqPkRQt*TC53FA9vus9LPdNJc=pc0U;vOMC>>3`uriTV zlVDN+YVsV9=YW4}pk6I=)@iKGWHBUPwsQ%?VmNS+dQG83!IuI`j}TfU89utMv^@oOtA4=o|FNZh3 zmluHPY@j^eGpLCGY48oY}(VQ>$cIFPV37fESGniflaZ~j$iT&(}>y2d+ zWO?}#&XvS#|Cpd0j-%oPX`2{~4X$^$jWFuE+Yy}mI3UBRI`l*7ACh#-W+?SyEWMI5djux{L)MOPSW(6$y?Pw5W>V|^o44)xc{zlwK368-a8`a>^n z4BEkh;mh(3pGGagfya^N=FQ)L1w?>DY8xp*iZxaTyk%WVZnt`dR z#9#;s0vBhBdQ8fs!`1eR&7{r_{wwp1q6Hkr((vx{$GHI6MvUx`=GVWiu4Kh4))C3$ zcDZQg-lb(hl7ZLWIybwMB><~!9HW@t0z5r*B(@D5A3RawZ8>VVURT7uf^YT3SKUbt zzCZ>gVP7?(x__`Z2gi*Ue{scABILBg7%x(Ja_o@Q+lc=c&q>YwHlALkq1G0%Anx=^ zz0w2~7|{Je?S$sq&Xf!!`Hce9>lAnqTE=w-0KK8v6Z6Km!dOALPkZ6#-3#U=X(*Tb z`KtWUO-f8^%)XqUduUOW6-0sdQ zRl#SB_yARCxX*qET`6hi^O#HEvva(*)Ybs0YghwL@FgG{Yz3~$a#K$JihvqJx!y{M z-*@$lGW_yAuEKuk#F{LuWsZlKUcIJJnlf#L9>nF%>sxOAErDK{7obeuVE4-VZ%9-| zS#X?UrDp$S^S#Z4pOZbgue*LbT)mft(23{cpQ{T*Q;h8=%w$UMdD;9Kca=;{Pq&P< z4Sw;pSqU2!9naXA?(F=Y)9(Oy&k7SmwSBl#7IL+ZFq(Gs{XioMLJ1OywH~#>kmBj+^ z2k`e{{kq@3hkKOgzDaIcW5Q1SpZ8B7ObNmv$q%I&7+ia6pHCENj|#WQ3Eue%zHhL6 zDv$e(2VwupS{8@i)bHb4Egkx-rwF%asjFUpTtN&LHl;gavO$M%DFsL3ASO{xQ$RzX z;WU9+<4zho!}R5JR}DOhcA@uJNI%YCTL;j>vYW`mcDEDu|7sS7+c(rKW7~>Dgp;t7 zN6(9yTMPE$1<6rcy`U-^?>~;i1d0-Y=+xqLcUGQ202k3R6G&0+m)rkr(?vZyKKQ7-5v^ z+fQilMRIZ5+Wb0RHn0`!ffOEJX}DUw4nsvn|GuHc5{) z3;g4mzPAI1Ga3wP=BrkqJ)x2s71#b;ZotT8<&*=S!>v+4InbYqx()@vIRNtUGDZ#C zS(*CgzXmt2SoUc`Uj7Gm^T#?rp{e`X*%_kMu``jgN}W&+bQp#*rk@{fKq3z7MZ(A~ zCIfW!^|LF3cB<+f54SQcyG|StPhut2K*=;#37SLFVE9@2rtZdv_I7qcpxbA(9H9Oi znbBRxWndl+iXzq&)d^x8N1 zw($10tJ#&yblbpIA#9x9b`EW&E8;v%W4v$O+*#Di z6eMkzK|bik&U_Q;;^HE7Mjps+i^1c%mzP(HTMNp@L{XDnx`}W4U2yluSZO~5)1a|O zXM0WN!{T%S4YauKn1{Fm)%tD-t3r`SwPK@PuW-$0haabO?~{4`%0tMH^&6cSmK~&M za4h0DTnX#z4{|j5I;4n8%;$N$e0-pr{JxL)XKQT{^-09#hJaCT0n)+jEcW*tDekv- zH^vK|2LlOVp+-|-cSOX^J(Lt%kn}oVPu>UMX~VmH3IirUMZ!PJ{7psP8_tmYBZB5_ zN%`;%>Bn13vLr57e{$jAjm#lX!IGv{=9BYP4dhD@vLLVsCk`PY;1Q=qyu&e?9lcX! zp_JkZK;@nmRCxnTr5#}{zGKequP$AIe9B2?uFCZMVw`pe}9pbxaXMBiXK zix|`|`T|N4x|!wwl+M3PE-S~nnOpd=&E6a_IA$?;cz6tgNhIEP+BG=wai}9Pw-W{% z(!FDQk@~y$P>hln|3!vc5#L(xsn3i?FFWd3^yLuq7LW-ZQzMn^O?nwYq1#^?Q;32iVHC{V>B68&?diCoX7+Y50-_s&JT`cracGpg*?``KNs);e#p zMwi)hM~gHx?vD!xKl=uZ8=8B`&O@aTao|O0_Mld&r1ilh4$hU!)|#7f+<+D2#l4!g zU20{(B7Eu(YDaQQSK2{uX+4-HZVcUtqSW_GPfzF3N#an~(#`PxPnu{RjkGprx}#ha zt675g7Aa#tJ+3x}0Edj&Z}Ob4JORU)=PX8}pGN-N0b;(MEPV5Pt9N98@J}ZPJ@*BX zp;$HPuE~UVj!a;dogX96vmEu{e)&KMkbyr8cuN1P+)!=^JRu$D`oPI16fI9?lmmyO z-NAKdq{eD?g|`Q;$gBd5CK4bAAvq2`4cFdn-Cs7k=vi1?{3es^=3isI)*2g9V9mwT zo7nQ&?S z1vNFbPRWln+`Uha80H~)5dIFS#PVB6iAx)rEkC)=jckQks$B#Ke-Y{c@)vTNz-XQ2 zH0KX_!zffI8WsQHB^!OXDH*K7JmlhdE04+1i~zniJvN4y?8ZU(Mxn43#E?h1$jQh@ z4}%J?>;-2B#aKAWYo?-~)7CgL>6*Gq#t4==gD8j>X!e8ZD%ZLYfCrJxsE&Lq;ekCv zE+${OgZ-;C$#W|-FcirSAqWITP(g$&K2`RXL|hgxwm3@c=hY*rWn>>YJ3BA0bh(fA zm4~i(If?w<1Y?mOthO{yjXRvI9_5TemC^sfJkyEMvviUx_Xh8dWJxP_bij_cKjkTy zxaj z)tlYSvsJgG9z1IiQj24H#>OfO%48 z_lJ&v_XB>k`>$o5k?c-=sujRK!)5R&&Gdg6p@QNHJxLsGzT1YjE*vW>I9C4g{@e$S zy3sM0J2FOy(`cp>SkC69hyY>0kou(-EgL4zq*A%TOAaHp8E{vW<-=LV-x$%C%G1~c zV+=1q$}i5^#|zCaz%tAD)7yg>bq{cDwg$0D-Om`oHIi-i9?`J-SGO&CBB$*yaC;p! z*0}-f?)cg{dmM|YKQNGyZRi1aO1#gXCf*LI&TvZft1FCHLBo+r!P%TbdVi?x+Ij5Z~B_U!xG9?L}b{~_(IqpDi__Fn@5%S5 zx>G=;8|m&kH}1WkcR#=J{?0gK9EX1xu-0O(HRrtV>w8_FOG!~2*!@Du2URZGK5SVn?iGR^7Iz!Ll|J{Daf}45IYa>y>cc@%;q!p$y1N zA%bQ3?@+j~oTU*h97{)&e<%%4n`?A-3yxviD(YXNy>hs{psB$6V2_J!zs$(1a0sTM z0S(zYN^h&mcqFT+z8~%ra0yJYuC#fzIr_TFzyZ+;-RW>a1Rh~m!{y0kLZ6}>8%K+Q zHM;!e-K(wj9@W{K5Ck)Z<68pHx5hb{C3ysGj|%r3WO~avB^3R*ERN_z?T}FPJ+1AP zHLVr*NB5W1EaJ7f)iMb@pz{uzEPm3Sm!b!FkyY3E^Bl$XWdWV#g#_CfaCs^}v%DNxu&G48zr6nTIRrqf3b^?P z;^@Eibgz&EX1{np&D;b7Sj;aRX;f*|)?%h;Em=msa4GGdUPZl4hcK?S_?ZoG(?yk6 zpB`sUF@U9}(0o_%6&sL(d4tY7#ocvUJkj7UA}Dusq??jk%0`e#peFa12-1!}Os+4G z7Y$TF%oxSeE|UaC7VK0o2!VmA^6N-@ccdon##}qzBiFCyn;q&pYCb{}xb4p$>-XbZ zi|jZH`yydu`son1)w|;wj3$$|o3A75Ro0YJFl^FMl2I`2X{e3vo{-H>%&bByIUf8U z#!5xh%0xaS>9t}96(=ubc|w4Fc=!iu@b=~m#Ek#wm9rKgZ9FzMVh`2P6k-!Cgs*k( zM?Q-kipV?c5NT?Vu|W1{GMbRhbk1tYfS@dHz{jkrdfgANU2aXpkIyBjLUw*O6n^!v zSyt)tNrZ!v&YQrl{aK+8#XAU)OW;tjwZlY2tSceW+Qpem;~7sDj;wKz8!V!fpl8Dy z4JJ}CBD1F4_lCZ1a~MQtqg7G)k?*A1DLAd=b7*TsTNVq;poE;t;@B0FI#sBq_R3AQ zSUp(fn_9MO_)gJHy&2nHRMhXUNm0wHc|*x(OhVHMbQ7_ad!yhq{q{NT_gDqVNE+Wa zA5~jE8yiRe(AtGCZ*4oEPL}fZqDj>ljZyN?EzRHgLyYx;JWwS6%^T1d)bq97H=03@ z-^2V4+^=5Xaocy5uL!Bk1t^MWGk+{m*@!o~TOgNFaZBkTun9As<1n2;YUFSwe;Vi4lkWlOJdXDC#-SjSZUFN6;Kxu3)ALnJ}*+f+cB9CXO^c`X`crj-8(AE_&n7q@B8&x&@#@$hi7k;Cd6fW?F=P8U@J~(F{9*$ zJc9T@QSdtspQt$&ZmsFTL72L;XYTT2aO)#zI^B{*o+f=!&+9%>98v?)XTI9=425h_ zgWpk^JAilX#S5mLA@NyN#nQe3PiW$L3>y89;k;gZ^%d$3hW_TV?Y~RvpgZ!V&7_lW za$Wl(Q5El1(k)~MRZHam;RrvgE3dhy8x~;i&xtqBPoHg-2|^~5D!Od(7>we(8e|ao zM%(|#ylE7>b@}@jicwFPYO-sEL?Fimc?m1tab@3w+tm>fx!jkqyhC=8 zkR$3_Pk{Q{im9yX`myW=R~CAL=R`lf3R^II8( zrwldD3R_&!?>F00M9sG}L2S7Q^lFPi3ZYOUMQ^4Q5oWFXKY=AtCazkIn|Wcq3VJqP zE6mD&dCNR_@RFWt}<8(cjy_(*z%^6%~5%I?u+V>{MEe-v)xBwwb*>4ekUH8rKnTY zeM?sk$QK3dtq*s0k+rQ;%2S>itpVZKA}ZT)><5cL7c3acA(vU~20wq+3#opU8uKZe z<2#W%yH!8v9xQ?we~r3N_W3Me<0=NO@{{Gt{_)@Y03v#-o-Sse({Y~juJ)J%%`axT zn3q(_VP?$0^k<&sq|usHn;~%d{+_BzeAqFa$PBfIX0cfnu92I(<;0$~t*N~)8P=Qi ziQ8!sow!zpzr^MAxY27N^?dQNEc-LQGrD5U52_ZFQau*qS;;NdlSRSWg(HJbrt?7t z6}Oz84W<%LinL;J3Id6DUkKD2Y+t=bz{(nVueDXLOcUqm2-Z2po_@^i-ul!P84^_S zxkk#|l}vmiO%6an{Q`?_(Tm{y`7>E}iG&hm znG#@NSFSNxIXopw%nN)0lgh-el0ot=bxHA5i%=d0qx~n?=1gQa=Tx{tMY#bufD6n_o-0K1V}r@$on%TpsJ&F|)mOX*%7P839y-Gr z6VTnNufA5-=HVa0<01a@4%Qg4*0_O_ZTyt`|J__E208&vQg}cS^Z%5>{g=5iMLuR6 z=^CiYWKxv<$~Ja(O8YPxTI^7-CF7a5F;`v0wPcWmp%CQU(X|Yx6Jy%e?RljfxLp_u zMJd~bT&#>B(Iz62m66!kM;?g99rSPQFN$f1T8>%#f|Pq|khACr;yzp2YvTR_~! zlCTQ}RNP|v^ZvIM5|M-t?uEmGc|_`ae9X|JzX``Tt26_`g#L{$>x56IW6`A%f3r!|?@> z(sgxj)TKdPZ70B$lp8+)_X$=`;-4d-V7|KkI|t#p%PHD)AZ?TVLmUVb_rAQy^3n5v zc%?fo6{INOgurlhxCmiIpx4q>3GoWKQ_%OEh}aeFk9MAusO;VK_h`lW=B!W`Stie; z#glGl(&94%g{(efbWBY?ylnmSSGW#4XS0l2Gg4&u{IB2o8qDzaItYG`KABsyX8!Ze znX`Z%!#NJCo}LV^jNfhY#Yr6q{Tr~b<{Xr zwl1<#e+-#?fjqETHZs4L$`)U_;!A`*Xj4I6!FPduC!BeYFuD>P)xjTdj7rFbH#*v> z&;%8|QKs91&1~HFH4&S$i8EWVJLKr}n-A@*D|X9q56(_=@O`;Geham&C&d((nUxhn zhGK3MyX8<>IJ0h%u>>en6td*7fL{n*V7P89HJ<=oxc-o`rI zt>dcPN2gDXtafZh1J0l1`w5SI&a)fNetc>m_ z)=%?qB0C8FaA8?uR6*itEPoVo$K(-2h+GT3=RZH8VF4qc^AGJWLFN3^7bG51`P^g~ zh_vvX(Y|+TTkRN)pDy`60;{Tzay}5Dap1 z#-r(Y`#ja15BCoskt{S$?9XJ=0a4%Z-lxgU(SCp8RsJJi_}3H;g*IKC$pQ@&P}m#p z%4#_a0RT)e9KijybU(E_clomV<2AqMy#om_!nyqX0JZJ`+-0-u&KOS{!E|0G<4~z* z=4biY@|DU^mU~YY34b8wuS^!!V-XQ}a>9mVv%dwnDfxU^%Qz~X*Pxb4;Yaa)e%;ci zFz@@@Zx-sutBcNU?_u`lYk=>YPq0Ljjmdq6(ZPMw+DQy}LlK=x5YhZ~%bu%g9u&?2F6fANe+02RipyLfYa_^ zLaK>K9NxQ&d1D~kvRBqQ-;5=WQbY1qF&eAw;moM{r^YAuw6ru}DZ(p=#t~{@V!y

tWIr~7{r5kDaf^&o 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 literal 0 HcmV?d00001 diff --git a/website/docs/assets/houdini_frame_per_task.png b/website/docs/assets/houdini_frame_per_task.png new file mode 100644 index 0000000000000000000000000000000000000000..a7d8b33114e640499c892657fcd9ec9f3a79ef3c GIT binary patch literal 59257 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 Kjy4>*aO-bXs4lty literal 0 HcmV?d00001 From 82b75d53b319c2b7ddbc3da7b3773154a255a88a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 21 Sep 2023 15:03:58 +0800 Subject: [PATCH 02/37] update the latest lib --- openpype/hosts/houdini/api/lib.py | 48 ++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/houdini/api/lib.py b/openpype/hosts/houdini/api/lib.py index 856e7abbd33..1ab38ae2c1a 100644 --- a/openpype/hosts/houdini/api/lib.py +++ b/openpype/hosts/houdini/api/lib.py @@ -141,7 +141,6 @@ def get_output_parameter(node): elif node_type == "ifd": if node.evalParm("soho_outputmode"): return node.parm("soho_diskfile") - raise TypeError("Node type '%s' not supported" % node_type) @@ -652,3 +651,50 @@ def get_color_management_preferences(): "display": hou.Color.ocio_defaultDisplay(), "view": hou.Color.ocio_defaultView() } + + +def get_resolution_from_doc(doc): + """Get resolution from the given asset document. """ + + if not doc or "data" not in doc: + print("Entered document is not valid. \"{}\"".format(str(doc))) + return None + + resolution_width = doc["data"].get("resolutionWidth") + resolution_height = doc["data"].get("resolutionHeight") + + # Make sure both width and height are set + if resolution_width is None or resolution_height is None: + print("No resolution information found for \"{}\"".format(doc["name"])) + return None + + return int(resolution_width), int(resolution_height) + + +def set_camera_resolution(camera, asset_doc=None): + """Apply resolution to camera from asset document of the publish""" + + if not asset_doc: + asset_doc = get_current_project_asset() + + resolution = get_resolution_from_doc(asset_doc) + + if resolution: + print("Setting camera resolution: {} -> {}x{}".format( + camera.name(), resolution[0], resolution[1] + )) + camera.parm("resx").set(resolution[0]) + camera.parm("resy").set(resolution[1]) + + +def get_camera_from_container(container): + """Get camera from container node. """ + + cameras = container.recursiveGlob( + "*", + filter=hou.nodeTypeFilter.ObjCamera, + include_subnets=False + ) + + assert len(cameras) == 1, "Camera instance must have only one camera" + return cameras[0] From 6ab3f2d0a9fc3599461b7ea3ad052956b2bcad7a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 21 Sep 2023 15:05:44 +0800 Subject: [PATCH 03/37] update to exlcude unncessary codes --- .../hosts/houdini/plugins/load/load_camera.py | 81 +++++++++++-------- 1 file changed, 47 insertions(+), 34 deletions(-) diff --git a/openpype/hosts/houdini/plugins/load/load_camera.py b/openpype/hosts/houdini/plugins/load/load_camera.py index 7b4a04809e5..e16146a2676 100644 --- a/openpype/hosts/houdini/plugins/load/load_camera.py +++ b/openpype/hosts/houdini/plugins/load/load_camera.py @@ -4,6 +4,13 @@ ) from openpype.hosts.houdini.api import pipeline +from openpype.hosts.houdini.api.lib import ( + set_camera_resolution, + get_camera_from_container +) + +import hou + ARCHIVE_EXPRESSION = ('__import__("_alembic_hom_extensions")' '.alembicGetCameraDict') @@ -25,7 +32,15 @@ def transfer_non_default_values(src, dest, ignore=None): channel expression and ignore certain Parm types. """ - import hou + + ignore_types = { + hou.parmTemplateType.Toggle, + hou.parmTemplateType.Menu, + hou.parmTemplateType.Button, + hou.parmTemplateType.FolderSet, + hou.parmTemplateType.Separator, + hou.parmTemplateType.Label, + } src.updateParmStates() @@ -62,14 +77,6 @@ def transfer_non_default_values(src, dest, ignore=None): continue # Ignore folders, separators, etc. - ignore_types = { - hou.parmTemplateType.Toggle, - hou.parmTemplateType.Menu, - hou.parmTemplateType.Button, - hou.parmTemplateType.FolderSet, - hou.parmTemplateType.Separator, - hou.parmTemplateType.Label, - } if parm.parmTemplate().type() in ignore_types: continue @@ -90,13 +97,8 @@ class CameraLoader(load.LoaderPlugin): def load(self, context, name=None, namespace=None, data=None): - import os - import hou - # Format file name, Houdini only wants forward slashes - file_path = self.filepath_from_context(context) - file_path = os.path.normpath(file_path) - file_path = file_path.replace("\\", "/") + file_path = self.filepath_from_context(context).replace("\\", "/") # Get the root node obj = hou.node("/obj") @@ -106,19 +108,21 @@ def load(self, context, name=None, namespace=None, data=None): node_name = "{}_{}".format(namespace, name) if namespace else name # Create a archive node - container = self.create_and_connect(obj, "alembicarchive", node_name) + node = self.create_and_connect(obj, "alembicarchive", node_name) # TODO: add FPS of project / asset - container.setParms({"fileName": file_path, - "channelRef": True}) + node.setParms({"fileName": file_path, "channelRef": True}) # Apply some magic - container.parm("buildHierarchy").pressButton() - container.moveToGoodPosition() + node.parm("buildHierarchy").pressButton() + node.moveToGoodPosition() # Create an alembic xform node - nodes = [container] + nodes = [node] + camera = get_camera_from_container(node) + self._match_maya_render_mask(camera) + set_camera_resolution(camera, asset_doc=context["asset"]) self[:] = nodes return pipeline.containerise(node_name, @@ -143,14 +147,14 @@ def update(self, container, representation): # Store the cam temporarily next to the Alembic Archive # so that we can preserve parm values the user set on it # after build hierarchy was triggered. - old_camera = self._get_camera(node) + old_camera = get_camera_from_container(node) temp_camera = old_camera.copyTo(node.parent()) # Rebuild node.parm("buildHierarchy").pressButton() # Apply values to the new camera - new_camera = self._get_camera(node) + new_camera = get_camera_from_container(node) transfer_non_default_values(temp_camera, new_camera, # The hidden uniform scale attribute @@ -158,6 +162,9 @@ def update(self, container, representation): # "icon_scale" just skip that completely ignore={"scale"}) + self._match_maya_render_mask(new_camera) + set_camera_resolution(new_camera) + temp_camera.destroy() def remove(self, container): @@ -165,15 +172,6 @@ def remove(self, container): node = container["node"] node.destroy() - def _get_camera(self, node): - import hou - cameras = node.recursiveGlob("*", - filter=hou.nodeTypeFilter.ObjCamera, - include_subnets=False) - - assert len(cameras) == 1, "Camera instance must have only one camera" - return cameras[0] - def create_and_connect(self, node, node_type, name=None): """Create a node within a node which and connect it to the input @@ -194,5 +192,20 @@ def create_and_connect(self, node, node_type, name=None): new_node.moveToGoodPosition() return new_node - def switch(self, container, representation): - self.update(container, representation) + def _match_maya_render_mask(self, camera): + """Workaround to match Maya render mask in Houdini""" + + # print("Setting match maya render mask ") + parm = camera.parm("aperture") + expression = parm.expression() + expression = expression.replace("return ", "aperture = ") + expression += """ +# Match maya render mask (logic from Houdini's own FBX importer) +node = hou.pwd() +resx = node.evalParm('resx') +resy = node.evalParm('resy') +aspect = node.evalParm('aspect') +aperture *= min(1, (resx / resy * aspect) / 1.5) +return aperture +""" + parm.setExpression(expression, language=hou.exprLanguage.Python) From 7a765bd52836f50f90088d7738990130da957bcb Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 21 Sep 2023 20:09:59 +0800 Subject: [PATCH 04/37] mustafa's comment on deadline plugin and fix the bug of not getting right env variable --- .../publish/submit_houdini_cache_deadline.py | 2 - .../publish/submit_publish_cache_job.py | 2 +- .../schema_project_deadline.json | 40 +++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py b/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py index b1717d09ee0..985f29a814a 100644 --- a/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py @@ -128,8 +128,6 @@ def get_job_info(self): if not value: continue job_info.EnvironmentKeyValue[key] = value - # to recognize render jobs - job_info.add_render_job_env_var() return job_info diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py index 5651ff4c832..f8e4613b1cc 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py @@ -239,7 +239,7 @@ def _submit_deadline_post_job(self, instance, job): self.log.debug("Submitting Deadline publish job ...") url = "{}/api/jobs".format(self.deadline_url) - response = requests.post(url, json=payload, timeout=10) + response = requests.post(url, json=payload, timeout=10, verify=False) if not response.ok: raise Exception(response.text) diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json index 6d59b5a92b4..4e582209bbe 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_deadline.json @@ -531,6 +531,46 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "ProcessSubmittedCacheJobOnFarm", + "label": "ProcessSubmittedCacheJobOnFarm", + "checkbox_key": "enabled", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "deadline_department", + "label": "Deadline department" + }, + { + "type": "text", + "key": "deadline_pool", + "label": "Deadline Pool" + }, + { + "type": "text", + "key": "deadline_group", + "label": "Deadline Group" + }, + { + "type": "number", + "key": "deadline_chunk_size", + "label": "Deadline Chunk Size" + }, + { + "type": "number", + "key": "deadline_priority", + "label": "Deadline Priotity" + } + ] + }, { "type": "dict", "collapsible": true, From 98cc7357d30bc1966c8c48d7604dcac3d7ee0c70 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 21 Sep 2023 22:44:38 +0800 Subject: [PATCH 05/37] make sure all the necessary environment variables needed to be there --- .../plugins/publish/submit_houdini_cache_deadline.py | 12 +++++++++--- .../plugins/publish/submit_publish_cache_job.py | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py b/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py index 985f29a814a..f3b4d37a0f0 100644 --- a/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py @@ -108,9 +108,13 @@ def get_job_info(self): job_info.Group = attr_values.get("group", self.group) keys = [ - "FTRACK_API_USER", - "FTRACK_API_KEY", - "FTRACK_SERVER" + "OPENPYPE_SG_USER", + "AVALON_PROJECT", + "AVALON_ASSET", + "AVALON_TASK", + "AVALON_APP_NAME", + "OPENPYPE_DEV", + "OPENPYPE_LOG_NO_COLORS", ] # Add OpenPype version if we are running from build. @@ -128,6 +132,8 @@ def get_job_info(self): if not value: continue job_info.EnvironmentKeyValue[key] = value + # to recognize render jobs + job_info.add_render_job_env_var() return job_info diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py index f8e4613b1cc..5651ff4c832 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py @@ -239,7 +239,7 @@ def _submit_deadline_post_job(self, instance, job): self.log.debug("Submitting Deadline publish job ...") url = "{}/api/jobs".format(self.deadline_url) - response = requests.post(url, json=payload, timeout=10, verify=False) + response = requests.post(url, json=payload, timeout=10) if not response.ok: raise Exception(response.text) From 559e49abccd8bbed9920a069b945ac1d8004c95a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 21 Sep 2023 23:41:54 +0800 Subject: [PATCH 06/37] add ftrack and kitsu environments --- .../plugins/publish/submit_houdini_cache_deadline.py | 3 +++ .../deadline/plugins/publish/submit_publish_cache_job.py | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py b/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py index f3b4d37a0f0..2b6231e916c 100644 --- a/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py @@ -108,6 +108,9 @@ def get_job_info(self): job_info.Group = attr_values.get("group", self.group) keys = [ + "FTRACK_API_KEY", + "FTRACK_API_USER", + "FTRACK_SERVER", "OPENPYPE_SG_USER", "AVALON_PROJECT", "AVALON_ASSET", diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py index 5651ff4c832..b2ebebc3037 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py @@ -70,9 +70,14 @@ class ProcessSubmittedCacheJobOnFarm(pyblish.api.InstancePlugin, ] environ_keys = [ + "FTRACK_API_USER", + "FTRACK_API_KEY", + "FTRACK_SERVER", "AVALON_APP_NAME", "OPENPYPE_USERNAME", "OPENPYPE_SG_USER", + "KITSU_LOGIN", + "KITSU_PWD" ] # custom deadline attributes From 1f1676f5bb98aea465371641693b63ff2326abbd Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 13 Oct 2023 14:36:36 +0200 Subject: [PATCH 07/37] Refactor to use variable `node_value` instead of `value` - `value` only exists as the last variable value in the `for value in values` loop and might not be declared if `values` is an empty iterable. --- openpype/hosts/nuke/plugins/publish/validate_write_nodes.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/validate_write_nodes.py b/openpype/hosts/nuke/plugins/publish/validate_write_nodes.py index 9aae53e59d1..b882e240e6f 100644 --- a/openpype/hosts/nuke/plugins/publish/validate_write_nodes.py +++ b/openpype/hosts/nuke/plugins/publish/validate_write_nodes.py @@ -111,7 +111,6 @@ def process(self, instance): for value in values: if type(node_value) in (int, float): try: - if isinstance(value, list): value = color_gui_to_int(value) else: @@ -130,7 +129,7 @@ def process(self, instance): and key != "file" and key != "tile_color" ): - check.append([key, value, write_node[key].value()]) + check.append([key, node_value, write_node[key].value()]) if check: self._make_error(check) From 246c408ce7036de973b1dc6ba1d918f5670d495c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 30 Oct 2023 22:21:46 +0100 Subject: [PATCH 08/37] Maya: Remove RenderSetup layer observers that are not needed since new publisher --- openpype/hosts/maya/api/lib.py | 153 ---------------------------- openpype/hosts/maya/api/pipeline.py | 10 -- 2 files changed, 163 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 7c49c837e9b..6d785234c55 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3069,159 +3069,6 @@ def _get_render_instances(): return instances -renderItemObserverList = [] - - -class RenderSetupListObserver: - """Observer to catch changes in render setup layers.""" - - def listItemAdded(self, item): - print("--- adding ...") - self._add_render_layer(item) - - def listItemRemoved(self, item): - print("--- removing ...") - self._remove_render_layer(item.name()) - - def _add_render_layer(self, item): - render_sets = _get_render_instances() - layer_name = item.name() - - for render_set in render_sets: - members = cmds.sets(render_set, query=True) or [] - - namespace_name = "_{}".format(render_set) - if not cmds.namespace(exists=namespace_name): - index = 1 - namespace_name = "_{}".format(render_set) - try: - cmds.namespace(rm=namespace_name) - except RuntimeError: - # namespace is not empty, so we leave it untouched - pass - orignal_namespace_name = namespace_name - while(cmds.namespace(exists=namespace_name)): - namespace_name = "{}{}".format( - orignal_namespace_name, index) - index += 1 - - namespace = cmds.namespace(add=namespace_name) - - if members: - # if set already have namespaced members, use the same - # namespace as others. - namespace = members[0].rpartition(":")[0] - else: - namespace = namespace_name - - render_layer_set_name = "{}:{}".format(namespace, layer_name) - if render_layer_set_name in members: - continue - print(" - creating set for {}".format(layer_name)) - maya_set = cmds.sets(n=render_layer_set_name, empty=True) - cmds.sets(maya_set, forceElement=render_set) - rio = RenderSetupItemObserver(item) - print("- adding observer for {}".format(item.name())) - item.addItemObserver(rio.itemChanged) - renderItemObserverList.append(rio) - - def _remove_render_layer(self, layer_name): - render_sets = _get_render_instances() - - for render_set in render_sets: - members = cmds.sets(render_set, query=True) - if not members: - continue - - # all sets under set should have the same namespace - namespace = members[0].rpartition(":")[0] - render_layer_set_name = "{}:{}".format(namespace, layer_name) - - if render_layer_set_name in members: - print(" - removing set for {}".format(layer_name)) - cmds.delete(render_layer_set_name) - - -class RenderSetupItemObserver: - """Handle changes in render setup items.""" - - def __init__(self, item): - self.item = item - self.original_name = item.name() - - def itemChanged(self, *args, **kwargs): - """Item changed callback.""" - if self.item.name() == self.original_name: - return - - render_sets = _get_render_instances() - - for render_set in render_sets: - members = cmds.sets(render_set, query=True) - if not members: - continue - - # all sets under set should have the same namespace - namespace = members[0].rpartition(":")[0] - render_layer_set_name = "{}:{}".format( - namespace, self.original_name) - - if render_layer_set_name in members: - print(" <> renaming {} to {}".format(self.original_name, - self.item.name())) - cmds.rename(render_layer_set_name, - "{}:{}".format( - namespace, self.item.name())) - self.original_name = self.item.name() - - -renderListObserver = RenderSetupListObserver() - - -def add_render_layer_change_observer(): - import maya.app.renderSetup.model.renderSetup as renderSetup - - rs = renderSetup.instance() - render_sets = _get_render_instances() - - layers = rs.getRenderLayers() - for render_set in render_sets: - members = cmds.sets(render_set, query=True) - if not members: - continue - # all sets under set should have the same namespace - namespace = members[0].rpartition(":")[0] - for layer in layers: - render_layer_set_name = "{}:{}".format(namespace, layer.name()) - if render_layer_set_name not in members: - continue - rio = RenderSetupItemObserver(layer) - print("- adding observer for {}".format(layer.name())) - layer.addItemObserver(rio.itemChanged) - renderItemObserverList.append(rio) - - -def add_render_layer_observer(): - import maya.app.renderSetup.model.renderSetup as renderSetup - - print("> adding renderSetup observer ...") - rs = renderSetup.instance() - rs.addListObserver(renderListObserver) - pass - - -def remove_render_layer_observer(): - import maya.app.renderSetup.model.renderSetup as renderSetup - - print("< removing renderSetup observer ...") - rs = renderSetup.instance() - try: - rs.removeListObserver(renderListObserver) - except ValueError: - # no observer set yet - pass - - def update_content_on_context_change(): """ This will update scene content to match new asset on context change diff --git a/openpype/hosts/maya/api/pipeline.py b/openpype/hosts/maya/api/pipeline.py index 6b791c96658..1ecfdfaa404 100644 --- a/openpype/hosts/maya/api/pipeline.py +++ b/openpype/hosts/maya/api/pipeline.py @@ -580,20 +580,11 @@ def on_save(): lib.set_id(node, new_id, overwrite=False) -def _update_render_layer_observers(): - # Helper to trigger update for all renderlayer observer logic - lib.remove_render_layer_observer() - lib.add_render_layer_observer() - lib.add_render_layer_change_observer() - - def on_open(): """On scene open let's assume the containers have changed.""" from openpype.widgets import popup - utils.executeDeferred(_update_render_layer_observers) - # Validate FPS after update_task_from_path to # ensure it is using correct FPS for the asset lib.validate_fps() @@ -630,7 +621,6 @@ def on_new(): with lib.suspended_refresh(): lib.set_context_settings() - utils.executeDeferred(_update_render_layer_observers) _remove_workfile_lock() From 90c8922a8a53f0e2265ca8037d1593d9da6559d3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 30 Oct 2023 22:54:54 +0100 Subject: [PATCH 09/37] Remove remaining unused function --- openpype/hosts/maya/api/lib.py | 37 ---------------------------------- 1 file changed, 37 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 6d785234c55..2d394893cdb 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -115,8 +115,6 @@ INT_FPS = {15, 24, 25, 30, 48, 50, 60, 44100, 48000} FLOAT_FPS = {23.98, 23.976, 29.97, 47.952, 59.94} -RENDERLIKE_INSTANCE_FAMILIES = ["rendering", "vrayscene"] - DISPLAY_LIGHTS_ENUM = [ {"label": "Use Project Settings", "value": "project_settings"}, @@ -3034,41 +3032,6 @@ def _cleanOldShelf(self): cmds.shelfLayout(self.name, p="ShelfLayout") -def _get_render_instances(): - """Return all 'render-like' instances. - - This returns list of instance sets that needs to receive information - about render layer changes. - - Returns: - list: list of instances - - """ - objectset = cmds.ls("*.id", long=True, exactType="objectSet", - recursive=True, objectsOnly=True) - - instances = [] - for objset in objectset: - if not cmds.attributeQuery("id", node=objset, exists=True): - continue - - id_attr = "{}.id".format(objset) - if cmds.getAttr(id_attr) != "pyblish.avalon.instance": - continue - - has_family = cmds.attributeQuery("family", - node=objset, - exists=True) - if not has_family: - continue - - if cmds.getAttr( - "{}.family".format(objset)) in RENDERLIKE_INSTANCE_FAMILIES: - instances.append(objset) - - return instances - - def update_content_on_context_change(): """ This will update scene content to match new asset on context change From d94c19d0ba44a5d48b8964fe3fa6d98a29b3f0da Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 3 Nov 2023 00:59:42 +0100 Subject: [PATCH 10/37] Remove non-working LOPs USD output processors - These do not show up in recent Houdini versions - The recent output processor API is very different and processors should be registered differently - These are non-functional in current OpenPype code --- openpype/hosts/houdini/api/pipeline.py | 4 - .../vendor/husdoutputprocessors/__init__.py | 1 - .../avalon_uri_processor.py | 152 ------------------ .../stagingdir_processor.py | 90 ----------- 4 files changed, 247 deletions(-) delete mode 100644 openpype/hosts/houdini/vendor/husdoutputprocessors/__init__.py delete mode 100644 openpype/hosts/houdini/vendor/husdoutputprocessors/avalon_uri_processor.py delete mode 100644 openpype/hosts/houdini/vendor/husdoutputprocessors/stagingdir_processor.py diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py index f8db45c56bd..095004ea6fc 100644 --- a/openpype/hosts/houdini/api/pipeline.py +++ b/openpype/hosts/houdini/api/pipeline.py @@ -71,10 +71,6 @@ def install(self): ) self._has_been_setup = True - # add houdini vendor packages - hou_pythonpath = os.path.join(HOUDINI_HOST_DIR, "vendor") - - sys.path.append(hou_pythonpath) # Set asset settings for the empty scene directly after launch of # Houdini so it initializes into the correct scene FPS, diff --git a/openpype/hosts/houdini/vendor/husdoutputprocessors/__init__.py b/openpype/hosts/houdini/vendor/husdoutputprocessors/__init__.py deleted file mode 100644 index 69e3be50dac..00000000000 --- a/openpype/hosts/houdini/vendor/husdoutputprocessors/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__path__ = __import__('pkgutil').extend_path(__path__, __name__) diff --git a/openpype/hosts/houdini/vendor/husdoutputprocessors/avalon_uri_processor.py b/openpype/hosts/houdini/vendor/husdoutputprocessors/avalon_uri_processor.py deleted file mode 100644 index 310d057a113..00000000000 --- a/openpype/hosts/houdini/vendor/husdoutputprocessors/avalon_uri_processor.py +++ /dev/null @@ -1,152 +0,0 @@ -import os -import hou -import husdoutputprocessors.base as base - -import colorbleed.usdlib as usdlib - -from openpype.client import get_asset_by_name -from openpype.pipeline import Anatomy, get_current_project_name - - -class AvalonURIOutputProcessor(base.OutputProcessorBase): - """Process Avalon URIs into their full path equivalents. - - """ - - _parameters = None - _param_prefix = 'avalonurioutputprocessor_' - _parms = { - "use_publish_paths": _param_prefix + "use_publish_paths" - } - - def __init__(self): - """ There is only one object of each output processor class that is - ever created in a Houdini session. Therefore be very careful - about what data gets put in this object. - """ - self._use_publish_paths = False - self._cache = dict() - - def displayName(self): - return 'Avalon URI Output Processor' - - def parameters(self): - - if not self._parameters: - parameters = hou.ParmTemplateGroup() - use_publish_path = hou.ToggleParmTemplate( - name=self._parms["use_publish_paths"], - label='Resolve Reference paths to publish paths', - default_value=False, - help=("When enabled any paths for Layers, References or " - "Payloads are resolved to published master versions.\n" - "This is usually only used by the publishing pipeline, " - "but can be used for testing too.")) - parameters.append(use_publish_path) - self._parameters = parameters.asDialogScript() - - return self._parameters - - def beginSave(self, config_node, t): - parm = self._parms["use_publish_paths"] - self._use_publish_paths = config_node.parm(parm).evalAtTime(t) - self._cache.clear() - - def endSave(self): - self._use_publish_paths = None - self._cache.clear() - - def processAsset(self, - asset_path, - asset_path_for_save, - referencing_layer_path, - asset_is_layer, - for_save): - """ - Args: - asset_path (str): The incoming file path you want to alter or not. - asset_path_for_save (bool): Whether the current path is a - referenced path in the USD file. When True, return the path - you want inside USD file. - referencing_layer_path (str): ??? - asset_is_layer (bool): Whether this asset is a USD layer file. - If this is False, the asset is something else (for example, - a texture or volume file). - for_save (bool): Whether the asset path is for a file to be saved - out. If so, then return actual written filepath. - - Returns: - The refactored asset path. - - """ - - # Retrieve from cache if this query occurred before (optimization) - cache_key = (asset_path, asset_path_for_save, asset_is_layer, for_save) - if cache_key in self._cache: - return self._cache[cache_key] - - relative_template = "{asset}_{subset}.{ext}" - uri_data = usdlib.parse_avalon_uri(asset_path) - if uri_data: - - if for_save: - # Set save output path to a relative path so other - # processors can potentially manage it easily? - path = relative_template.format(**uri_data) - - print("Avalon URI Resolver: %s -> %s" % (asset_path, path)) - self._cache[cache_key] = path - return path - - if self._use_publish_paths: - # Resolve to an Avalon published asset for embedded paths - path = self._get_usd_master_path(**uri_data) - else: - path = relative_template.format(**uri_data) - - print("Avalon URI Resolver: %s -> %s" % (asset_path, path)) - self._cache[cache_key] = path - return path - - self._cache[cache_key] = asset_path - return asset_path - - def _get_usd_master_path(self, - asset, - subset, - ext): - """Get the filepath for a .usd file of a subset. - - This will return the path to an unversioned master file generated by - `usd_master_file.py`. - - """ - - PROJECT = get_current_project_name() - anatomy = Anatomy(PROJECT) - asset_doc = get_asset_by_name(PROJECT, asset) - if not asset_doc: - raise RuntimeError("Invalid asset name: '%s'" % asset) - - template_obj = anatomy.templates_obj["publish"]["path"] - path = template_obj.format_strict({ - "project": PROJECT, - "asset": asset_doc["name"], - "subset": subset, - "representation": ext, - "version": 0 # stub version zero - }) - - # Remove the version folder - subset_folder = os.path.dirname(os.path.dirname(path)) - master_folder = os.path.join(subset_folder, "master") - fname = "{0}.{1}".format(subset, ext) - - return os.path.join(master_folder, fname).replace("\\", "/") - - -output_processor = AvalonURIOutputProcessor() - - -def usdOutputProcessor(): - return output_processor diff --git a/openpype/hosts/houdini/vendor/husdoutputprocessors/stagingdir_processor.py b/openpype/hosts/houdini/vendor/husdoutputprocessors/stagingdir_processor.py deleted file mode 100644 index d8e36d5aa81..00000000000 --- a/openpype/hosts/houdini/vendor/husdoutputprocessors/stagingdir_processor.py +++ /dev/null @@ -1,90 +0,0 @@ -import hou -import husdoutputprocessors.base as base -import os - - -class StagingDirOutputProcessor(base.OutputProcessorBase): - """Output all USD Rop file nodes into the Staging Directory - - Ignore any folders and paths set in the Configured Layers - and USD Rop node, just take the filename and save into a - single directory. - - """ - theParameters = None - parameter_prefix = "stagingdiroutputprocessor_" - stagingdir_parm_name = parameter_prefix + "stagingDir" - - def __init__(self): - self.staging_dir = None - - def displayName(self): - return 'StagingDir Output Processor' - - def parameters(self): - if not self.theParameters: - parameters = hou.ParmTemplateGroup() - rootdirparm = hou.StringParmTemplate( - self.stagingdir_parm_name, - 'Staging Directory', 1, - string_type=hou.stringParmType.FileReference, - file_type=hou.fileType.Directory - ) - parameters.append(rootdirparm) - self.theParameters = parameters.asDialogScript() - return self.theParameters - - def beginSave(self, config_node, t): - - # Use the Root Directory parameter if it is set. - root_dir_parm = config_node.parm(self.stagingdir_parm_name) - if root_dir_parm: - self.staging_dir = root_dir_parm.evalAtTime(t) - - if not self.staging_dir: - out_file_parm = config_node.parm('lopoutput') - if out_file_parm: - self.staging_dir = out_file_parm.evalAtTime(t) - if self.staging_dir: - (self.staging_dir, filename) = os.path.split(self.staging_dir) - - def endSave(self): - self.staging_dir = None - - def processAsset(self, asset_path, - asset_path_for_save, - referencing_layer_path, - asset_is_layer, - for_save): - """ - Args: - asset_path (str): The incoming file path you want to alter or not. - asset_path_for_save (bool): Whether the current path is a - referenced path in the USD file. When True, return the path - you want inside USD file. - referencing_layer_path (str): ??? - asset_is_layer (bool): Whether this asset is a USD layer file. - If this is False, the asset is something else (for example, - a texture or volume file). - for_save (bool): Whether the asset path is for a file to be saved - out. If so, then return actual written filepath. - - Returns: - The refactored asset path. - - """ - - # Treat save paths as being relative to the output path. - if for_save and self.staging_dir: - # Whenever we're processing a Save Path make sure to - # resolve it to the Staging Directory - filename = os.path.basename(asset_path) - return os.path.join(self.staging_dir, filename) - - return asset_path - - -output_processor = StagingDirOutputProcessor() -def usdOutputProcessor(): - return output_processor - From eaf3e1d3f8f05fc04f89329aaa9354a9553797d5 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 9 Nov 2023 17:07:45 +0100 Subject: [PATCH 11/37] :bug: fix broken imports maya specific imports were moved to specific methods but not in all cases by #5775. This is just quickly restoring functionality without questioning that decision. --- openpype/hosts/maya/api/lib_rendersettings.py | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/api/lib_rendersettings.py b/openpype/hosts/maya/api/lib_rendersettings.py index 20264c2cdfc..74ec3deea4f 100644 --- a/openpype/hosts/maya/api/lib_rendersettings.py +++ b/openpype/hosts/maya/api/lib_rendersettings.py @@ -62,8 +62,8 @@ def __init__(self, project_settings=None): def set_default_renderer_settings(self, renderer=None): """Set basic settings based on renderer.""" # Not all hosts can import this module. - from maya import cmds - import maya.mel as mel + from maya import cmds # noqa: F401 + import maya.mel as mel # noqa: F401 if not renderer: renderer = cmds.getAttr( @@ -118,6 +118,10 @@ def _set_arnold_settings(self, width, height): """Sets settings for Arnold.""" from mtoa.core import createOptions # noqa from mtoa.aovs import AOVInterface # noqa + # Not all hosts can import this module. + from maya import cmds # noqa: F401 + import maya.mel as mel # noqa: F401 + createOptions() render_settings = self._project_settings["maya"]["RenderSettings"] arnold_render_presets = render_settings["arnold_renderer"] # noqa @@ -164,6 +168,10 @@ def _set_arnold_settings(self, width, height): def _set_redshift_settings(self, width, height): """Sets settings for Redshift.""" + # Not all hosts can import this module. + from maya import cmds # noqa: F401 + import maya.mel as mel # noqa: F401 + render_settings = self._project_settings["maya"]["RenderSettings"] redshift_render_presets = render_settings["redshift_renderer"] @@ -216,6 +224,10 @@ def _set_redshift_settings(self, width, height): def _set_renderman_settings(self, width, height, aov_separator): """Sets settings for Renderman""" + # Not all hosts can import this module. + from maya import cmds # noqa: F401 + import maya.mel as mel # noqa: F401 + rman_render_presets = ( self._project_settings ["maya"] @@ -277,6 +289,11 @@ def _set_renderman_settings(self, width, height, aov_separator): def _set_vray_settings(self, aov_separator, width, height): # type: (str, int, int) -> None """Sets important settings for Vray.""" + # Not all hosts can import this module. + from maya import cmds # noqa: F401 + import maya.mel as mel # noqa: F401 + + settings = cmds.ls(type="VRaySettingsNode") node = settings[0] if settings else cmds.createNode("VRaySettingsNode") render_settings = self._project_settings["maya"]["RenderSettings"] @@ -349,6 +366,10 @@ def _set_vray_settings(self, aov_separator, width, height): @staticmethod def _set_global_output_settings(): + # Not all hosts can import this module. + from maya import cmds # noqa: F401 + import maya.mel as mel # noqa: F401 + # enable animation cmds.setAttr("defaultRenderGlobals.outFormatControl", 0) cmds.setAttr("defaultRenderGlobals.animation", 1) @@ -356,6 +377,10 @@ def _set_global_output_settings(): cmds.setAttr("defaultRenderGlobals.extensionPadding", 4) def _additional_attribs_setter(self, additional_attribs): + # Not all hosts can import this module. + from maya import cmds # noqa: F401 + import maya.mel as mel # noqa: F401 + for item in additional_attribs: attribute, value = item attribute = str(attribute) # ensure str conversion from settings From b0ab09201a61167c8193cf36270cfd622f3e1868 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 20 Nov 2023 16:48:03 +0800 Subject: [PATCH 12/37] add optional validator to check verbosity level in Arnold and plugin info for arnold verbose --- .../validate_arnold_verbosity_level.py | 42 +++++++++++++++++++ .../plugins/publish/submit_maya_deadline.py | 12 +++++- openpype/pipeline/publish/lib.py | 2 +- 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 openpype/hosts/maya/plugins/publish/validate_arnold_verbosity_level.py diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_verbosity_level.py b/openpype/hosts/maya/plugins/publish/validate_arnold_verbosity_level.py new file mode 100644 index 00000000000..b4a08e6bb46 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_verbosity_level.py @@ -0,0 +1,42 @@ +import pyblish.api +from openpype.pipeline import ( + PublishValidationError, + OptionalPyblishPluginMixin +) +from maya import cmds +from openpype.pipeline.publish import RepairAction + + +class ValidateArnoldVerbosityLevel(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): + """Validate Arnold Verbosity Level For Deadline Submission""" + + order = pyblish.api.ValidatorOrder + families = ["renderlayer"] + hosts = ["maya"] + label = "Validate Arnold Verbosity Level" + actions = [RepairAction] + optional = True + + def process(self, instance): + if not self.is_active(instance.data): + return + if instance.data["renderer"] != "arnold": + self.log.debug("The renderer for deadline submission is not Arnold." + " Skipping Validate Arnold Verbosity Level.") + return + current_verbosity_level = cmds.getAttr( + "defaultArnoldRenderOptions.log_verbosity") + + if not current_verbosity_level >= 3: + report = ( + "Arnold verbosity level has invalid value(s).\n\n" + "It must be always greater than 3.\n\n" + "You can use repair action to set the correct value\n" + ) + raise PublishValidationError( + report, title="Invalid Value(s) for Arnold Verbosity Level") + + @classmethod + def repair(cls, instance): + return cmds.setAttr("defaultArnoldRenderOptions.log_verbosity", 3) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 7d532923ffd..cdaf329ef21 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -97,6 +97,7 @@ class VRayPluginInfo(object): @attr.s class ArnoldPluginInfo(object): ArnoldFile = attr.ib(default=None) + ArnoldVerbose = attr.ib(default=2) class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline, @@ -281,6 +282,10 @@ def get_plugin_info(self): plugin_payload = attr.asdict(plugin_info) + if instance.data["renderer"] == "arnold": + plugin_payload["ArnoldVerbose"] = cmds.getAttr( + "defaultArnoldRenderOptions.log_verbosity") + # Patching with pluginInfo from settings for key, value in self.pluginInfo.items(): plugin_payload[key] = value @@ -648,7 +653,7 @@ def _get_vray_render_payload(self, data): return job_info, attr.asdict(plugin_info) def _get_arnold_render_payload(self, data): - + from maya import cmds # Job Info job_info = copy.deepcopy(self.job_info) job_info.Name = self._job_info_label("Render") @@ -658,9 +663,12 @@ def _get_arnold_render_payload(self, data): # Plugin Info ass_file, _ = os.path.splitext(data["output_filename_0"]) ass_filepath = ass_file + ".ass" + current_verbosity_level = cmds.getAttr( + "defaultArnoldRenderOptions.log_verbosity") plugin_info = ArnoldPluginInfo( - ArnoldFile=ass_filepath + ArnoldFile=ass_filepath, + ArnoldVerbose=current_verbosity_level ) return job_info, attr.asdict(plugin_info) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 4ea2f932f14..87ca3323cbd 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -74,7 +74,7 @@ def get_template_name_profiles( project_settings ["global"] ["publish"] - ["IntegrateAssetNew"] + ["IntegrateHeroVersion"] ["template_name_profiles"] ) if legacy_profiles: From be7c5a99dc783be712ae69affc400d630275434b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 20 Nov 2023 16:56:28 +0800 Subject: [PATCH 13/37] restore unnecessary code change --- openpype/pipeline/publish/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 87ca3323cbd..4ea2f932f14 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -74,7 +74,7 @@ def get_template_name_profiles( project_settings ["global"] ["publish"] - ["IntegrateHeroVersion"] + ["IntegrateAssetNew"] ["template_name_profiles"] ) if legacy_profiles: From 1ad7c1bca8b1dc6c3fed56a55febba7fd004fa12 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 20 Nov 2023 16:58:13 +0800 Subject: [PATCH 14/37] hound --- .../maya/plugins/publish/validate_arnold_verbosity_level.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_verbosity_level.py b/openpype/hosts/maya/plugins/publish/validate_arnold_verbosity_level.py index b4a08e6bb46..67e48cd63b8 100644 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_verbosity_level.py +++ b/openpype/hosts/maya/plugins/publish/validate_arnold_verbosity_level.py @@ -22,8 +22,9 @@ def process(self, instance): if not self.is_active(instance.data): return if instance.data["renderer"] != "arnold": - self.log.debug("The renderer for deadline submission is not Arnold." - " Skipping Validate Arnold Verbosity Level.") + self.log.debug( + "The renderer for deadline submission is not Arnold.\n\n" + " Skipping Validate Arnold Verbosity Level.") return current_verbosity_level = cmds.getAttr( "defaultArnoldRenderOptions.log_verbosity") From 252d375bd6f6cbd72a6895b17feac7b72a0c9da1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 20 Nov 2023 11:29:33 +0100 Subject: [PATCH 15/37] Add python3.10 libs for Houdini 20 startup --- .../hosts/houdini/startup/python3.10libs/pythonrc.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 openpype/hosts/houdini/startup/python3.10libs/pythonrc.py diff --git a/openpype/hosts/houdini/startup/python3.10libs/pythonrc.py b/openpype/hosts/houdini/startup/python3.10libs/pythonrc.py new file mode 100644 index 00000000000..683ea6721c1 --- /dev/null +++ b/openpype/hosts/houdini/startup/python3.10libs/pythonrc.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +"""OpenPype startup script.""" +from openpype.pipeline import install_host +from openpype.hosts.houdini.api import HoudiniHost + + +def main(): + print("Installing OpenPype ...") + install_host(HoudiniHost()) + + +main() From 8551110c4e0a5c9a8ec5cca480d0ffdf4ebdf22e Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 20 Nov 2023 14:44:42 +0200 Subject: [PATCH 16/37] add missing families --- .../plugins/publish/validate_houdini_license_category.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/houdini/plugins/publish/validate_houdini_license_category.py b/openpype/hosts/houdini/plugins/publish/validate_houdini_license_category.py index 5076acda601..108a700bbea 100644 --- a/openpype/hosts/houdini/plugins/publish/validate_houdini_license_category.py +++ b/openpype/hosts/houdini/plugins/publish/validate_houdini_license_category.py @@ -20,7 +20,7 @@ class ValidateHoudiniNotApprenticeLicense(pyblish.api.InstancePlugin): """ order = pyblish.api.ValidatorOrder - families = ["usd", "abc"] + families = ["usd", "abc", "fbx", "camera"] hosts = ["houdini"] label = "Houdini Apprentice License" From cd4f603b4e61a7a9f54a1e5420430d14e3307443 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 21 Nov 2023 13:46:13 +0800 Subject: [PATCH 17/37] supports the settings of additional plugin info and job info --- .../deadline/plugins/publish/submit_maya_deadline.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index cdaf329ef21..86de5c620e3 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -133,6 +133,8 @@ def apply_settings(cls, project_settings, system_settings): cls.group = settings.get("group", cls.group) cls.strict_error_checking = settings.get("strict_error_checking", cls.strict_error_checking) + cls.jobInfo = settings.get("jobInfo", cls.jobInfo) + cls.pluginInfo = settings.get("pluginInfo", cls.pluginInfo) def get_job_info(self): job_info = DeadlineJobInfo(Plugin="MayaBatch") @@ -282,10 +284,6 @@ def get_plugin_info(self): plugin_payload = attr.asdict(plugin_info) - if instance.data["renderer"] == "arnold": - plugin_payload["ArnoldVerbose"] = cmds.getAttr( - "defaultArnoldRenderOptions.log_verbosity") - # Patching with pluginInfo from settings for key, value in self.pluginInfo.items(): plugin_payload[key] = value From 460433cd136f50421fce685272d7e711970c227c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 21 Nov 2023 13:47:24 +0800 Subject: [PATCH 18/37] remove the validator for arnold verbosity level --- .../validate_arnold_verbosity_level.py | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/publish/validate_arnold_verbosity_level.py diff --git a/openpype/hosts/maya/plugins/publish/validate_arnold_verbosity_level.py b/openpype/hosts/maya/plugins/publish/validate_arnold_verbosity_level.py deleted file mode 100644 index 67e48cd63b8..00000000000 --- a/openpype/hosts/maya/plugins/publish/validate_arnold_verbosity_level.py +++ /dev/null @@ -1,43 +0,0 @@ -import pyblish.api -from openpype.pipeline import ( - PublishValidationError, - OptionalPyblishPluginMixin -) -from maya import cmds -from openpype.pipeline.publish import RepairAction - - -class ValidateArnoldVerbosityLevel(pyblish.api.InstancePlugin, - OptionalPyblishPluginMixin): - """Validate Arnold Verbosity Level For Deadline Submission""" - - order = pyblish.api.ValidatorOrder - families = ["renderlayer"] - hosts = ["maya"] - label = "Validate Arnold Verbosity Level" - actions = [RepairAction] - optional = True - - def process(self, instance): - if not self.is_active(instance.data): - return - if instance.data["renderer"] != "arnold": - self.log.debug( - "The renderer for deadline submission is not Arnold.\n\n" - " Skipping Validate Arnold Verbosity Level.") - return - current_verbosity_level = cmds.getAttr( - "defaultArnoldRenderOptions.log_verbosity") - - if not current_verbosity_level >= 3: - report = ( - "Arnold verbosity level has invalid value(s).\n\n" - "It must be always greater than 3.\n\n" - "You can use repair action to set the correct value\n" - ) - raise PublishValidationError( - report, title="Invalid Value(s) for Arnold Verbosity Level") - - @classmethod - def repair(cls, instance): - return cmds.setAttr("defaultArnoldRenderOptions.log_verbosity", 3) From 1c6db9d8ae25a61f43c1c89756c93b153f180cee Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Nov 2023 11:45:04 +0100 Subject: [PATCH 19/37] Ftrack: rewriting component creation to support multiple thumbnails --- .../publish/integrate_ftrack_instances.py | 407 ++++++++++++------ 1 file changed, 266 insertions(+), 141 deletions(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py index 75f43cb22fe..334e70ce0cc 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py @@ -1,6 +1,7 @@ import os import json import copy + import pyblish.api from openpype.pipeline.publish import get_publish_repre_path @@ -61,6 +62,7 @@ class IntegrateFtrackInstance(pyblish.api.InstancePlugin): additional_metadata_keys = [] def process(self, instance): + # QUESTION: should this be operating even for `farm` target? self.log.debug("instance {}".format(instance)) instance_repres = instance.data.get("representations") @@ -143,70 +145,87 @@ def process(self, instance): unmanaged_location_name = "ftrack.unmanaged" ftrack_server_location_name = "ftrack.server" + # check if any outputName keys are in review_representations + # also check if any outputName keys are in thumbnail_representations + synced_multiple_output_names = [] + for review_repre in review_representations: + review_output_name = review_repre.get("outputName") + if not review_output_name: + continue + for thumb_repre in thumbnail_representations: + thumb_output_name = thumb_repre.get("outputName") + if not thumb_output_name: + continue + if ( + thumb_output_name == review_output_name + # output name can be added also as tags during intermediate + # files creation + or thumb_output_name in review_repre.get("tags", []) + ): + synced_multiple_output_names.append( + thumb_repre["outputName"]) + self.log.debug("Multiple output names: {}".format( + synced_multiple_output_names + )) + multiple_synced_thumbnails = len(thumbnail_representations) > 1 + # Components data component_list = [] - # Components that will be duplicated to unmanaged location - src_components_to_add = [] + thumbnail_data_items = [] # Create thumbnail components - # TODO what if there is multiple thumbnails? - first_thumbnail_component = None - first_thumbnail_component_repre = None - - if not review_representations or has_movie_review: - for repre in thumbnail_representations: - repre_path = get_publish_repre_path(instance, repre, False) - if not repre_path: - self.log.warning( - "Published path is not set and source was removed." - ) - continue - - # Create copy of base comp item and append it - thumbnail_item = copy.deepcopy(base_component_item) - thumbnail_item["component_path"] = repre_path - thumbnail_item["component_data"] = { - "name": "thumbnail" - } - thumbnail_item["thumbnail"] = True - - # Create copy of item before setting location - if "delete" not in repre.get("tags", []): - src_components_to_add.append(copy.deepcopy(thumbnail_item)) - # Create copy of first thumbnail - if first_thumbnail_component is None: - first_thumbnail_component_repre = repre - first_thumbnail_component = thumbnail_item - # Set location - thumbnail_item["component_location_name"] = ( - ftrack_server_location_name + for repre in thumbnail_representations: + repre_path = get_publish_repre_path(instance, repre, False) + if not repre_path: + self.log.warning( + "Published path is not set and source was removed." ) + continue - # Add item to component list - component_list.append(thumbnail_item) - - if first_thumbnail_component is not None: - metadata = self._prepare_image_component_metadata( - first_thumbnail_component_repre, - first_thumbnail_component["component_path"] - ) + # Create copy of base comp item and append it + thumbnail_item = copy.deepcopy(base_component_item) + thumbnail_item.update({ + "component_path": repre_path, + "component_data": { + "name": ( + "thumbnail" if review_representations + else "ftrackreview-image" + ), + "metadata": self._prepare_image_component_metadata( + repre, + repre_path + ) + }, + "thumbnail": True, + "component_location_name": ftrack_server_location_name + }) + + # add thumbnail to items data for future synchronization + current_item = { + "sync_key": repre.get("outputName"), + "representation": repre, + "item": thumbnail_item + } + # Create copy of item before setting location + if "delete" not in repre.get("tags", []): + src_comp = self._create_src_components( + instance, + repre, + copy.deepcopy(thumbnail_item), + unmanaged_location_name + ) + component_list.append(src_comp) - if metadata: - component_data = first_thumbnail_component["component_data"] - component_data["metadata"] = metadata + current_item["src_component"] = src_comp - if review_representations: - component_data["name"] = "thumbnail" - else: - component_data["name"] = "ftrackreview-image" + # Add item to component list + component_list.append(thumbnail_item) + thumbnail_data_items.append(current_item) # Create review components # Change asset name of each new component for review - is_first_review_repre = True - not_first_components = [] - extended_asset_name = "" multiple_reviewable = len(review_representations) > 1 - for repre in review_representations: + for index, repre in enumerate(review_representations): if not self._is_repre_video(repre) and has_movie_review: self.log.debug("Movie repre has priority " "from {}".format(repre)) @@ -222,45 +241,38 @@ def process(self, instance): # Create copy of base comp item and append it review_item = copy.deepcopy(base_component_item) - # get asset name and define extended name variant - asset_name = review_item["asset_data"]["name"] - extended_asset_name = "_".join( - (asset_name, repre["name"]) + # get first or synchronize thumbnail item + sync_thumbnail_item = self._get_matching_thumbnail_item( + repre, + thumbnail_data_items, + multiple_synced_thumbnails ) - # reset extended if no need for extended asset name - if ( - self.keep_first_subset_name_for_review - and is_first_review_repre - ): - extended_asset_name = "" - else: - # only rename if multiple reviewable - if multiple_reviewable: - review_item["asset_data"]["name"] = extended_asset_name - else: - extended_asset_name = "" - - # rename all already created components - # only if first repre and extended name available - if is_first_review_repre and extended_asset_name: - # and rename all already created components - for _ci in component_list: - _ci["asset_data"]["name"] = extended_asset_name - - # and rename all already created src components - for _sci in src_components_to_add: - _sci["asset_data"]["name"] = extended_asset_name - - # rename also first thumbnail component if any - if first_thumbnail_component is not None: - first_thumbnail_component[ - "asset_data"]["name"] = extended_asset_name - - # Change location - review_item["component_path"] = repre_path - # Change component data + """ + Renaming asset name only to those components which are explicitly + allowed in settings. Usually clients wanted to keep first component + as untouched product name with version and any other assetVersion + to be named with extended form. The renaming will only happen if + there is more than one reviewable component and extended name is + not empty. + """ + extended_asset_name = self._make_extended_component_name( + base_component_item, repre, index) + if multiple_reviewable and extended_asset_name: + review_item["asset_data"]["name"] = extended_asset_name + # rename also thumbnail + if sync_thumbnail_item: + sync_thumbnail_item["item"]["asset_data"]["name"] = ( + extended_asset_name + ) + # rename also src_thumbnail + if sync_thumbnail_item.get("src_component"): + thumb_src_component = sync_thumbnail_item["src_component"] + thumb_src_component["asset_data"]["name"] = ( + extended_asset_name + ) + # add metadata to review component if self._is_repre_video(repre): component_name = "ftrackreview-mp4" metadata = self._prepare_video_component_metadata( @@ -273,28 +285,49 @@ def process(self, instance): ) review_item["thumbnail"] = True - review_item["component_data"] = { - # Default component name is "main". - "name": component_name, - "metadata": metadata - } - - if is_first_review_repre: - is_first_review_repre = False - else: - # later detection for thumbnail duplication - not_first_components.append(review_item) + review_item.update({ + "component_path": repre_path, + "component_data": { + "name": component_name, + "metadata": metadata + }, + "component_location_name": ftrack_server_location_name + }) # Create copy of item before setting location if "delete" not in repre.get("tags", []): - src_components_to_add.append(copy.deepcopy(review_item)) + src_comp = self._create_src_components( + instance, + repre, + copy.deepcopy(review_item), + unmanaged_location_name + ) + component_list.append(src_comp) + + if index > 0: + asset_name = review_item["asset_data"]["name"] + # perhaps it might happen that no thumbnail for + # current iteration is found in thumbnails list + # QUESTION: should this be consider as bug in code? + # NOTE: this was inherited from original code, but perhaps it + # should not be included + if ( + sync_thumbnail_item + and sync_thumbnail_item["item"]["asset_data"]["name"] != asset_name # noqa + ): + new_thumbnail_component = copy.deepcopy( + sync_thumbnail_item["item"] + ) + new_thumbnail_component["asset_data"]["name"] = asset_name + new_thumbnail_component["component_location_name"] = ( + ftrack_server_location_name + ) + component_list.append(new_thumbnail_component) - # Set location - review_item["component_location_name"] = ( - ftrack_server_location_name - ) # Add item to component list component_list.append(review_item) + + if self.upload_reviewable_with_origin_name: origin_name_component = copy.deepcopy(review_item) filename = os.path.basename(repre_path) @@ -303,34 +336,6 @@ def process(self, instance): ) component_list.append(origin_name_component) - # Duplicate thumbnail component for all not first reviews - if first_thumbnail_component is not None: - for component_item in not_first_components: - asset_name = component_item["asset_data"]["name"] - new_thumbnail_component = copy.deepcopy( - first_thumbnail_component - ) - new_thumbnail_component["asset_data"]["name"] = asset_name - new_thumbnail_component["component_location_name"] = ( - ftrack_server_location_name - ) - component_list.append(new_thumbnail_component) - - # Add source components for review and thubmnail components - for copy_src_item in src_components_to_add: - # Make sure thumbnail is disabled - copy_src_item["thumbnail"] = False - # Set location - copy_src_item["component_location_name"] = unmanaged_location_name - # Modify name of component to have suffix "_src" - component_data = copy_src_item["component_data"] - component_name = component_data["name"] - component_data["name"] = component_name + "_src" - component_data["metadata"] = self._prepare_component_metadata( - instance, repre, copy_src_item["component_path"], False - ) - component_list.append(copy_src_item) - # Add others representations as component for repre in other_representations: published_path = get_publish_repre_path(instance, repre, True) @@ -346,15 +351,17 @@ def process(self, instance): ): other_item["asset_data"]["name"] = extended_asset_name - component_data = { - "name": repre["name"], - "metadata": self._prepare_component_metadata( - instance, repre, published_path, False - ) - } - other_item["component_data"] = component_data - other_item["component_location_name"] = unmanaged_location_name - other_item["component_path"] = published_path + other_item.update({ + "component_path": published_path, + "component_data": { + "name": repre["name"], + "metadata": self._prepare_component_metadata( + instance, repre, published_path, False + ) + }, + "component_location_name": unmanaged_location_name, + }) + component_list.append(other_item) def json_obj_parser(obj): @@ -370,6 +377,124 @@ def json_obj_parser(obj): )) instance.data["ftrackComponentsList"] = component_list + def _get_matching_thumbnail_item( + self, + review_representation, + thumbnail_data_items, + are_multiple_synced_thumbnails + ): + """Return matching thumbnail item from list of thumbnail items. + + If a thumbnail item already exists, this should return it. + The benefit is that if an `outputName` key is found in + representation and is also used as a `sync_key` in a thumbnail + data item, it can sync with that item. + + Args: + review_representation (dict): Review representation + thumbnail_data_items (list): List of thumbnail data items + are_multiple_synced_thumbnails (bool): If there are multiple synced + thumbnails + + Returns: + dict: Thumbnail data item or empty dict + """ + output_name = review_representation.get("outputName") + tags = review_representation.get("tags", []) + matching_thumbnail_item = {} + for thumb_item in thumbnail_data_items: + if ( + are_multiple_synced_thumbnails + and ( + thumb_item["sync_key"] == output_name + # intermediate files can have preset name in tags + # this is usually aligned with `outputName` distributed + # during thumbnail creation in `need_thumbnail` tagging + # workflow + or thumb_item["sync_key"] in tags + ) + ): + # return only synchronized thumbnail if multiple + matching_thumbnail_item = thumb_item + break + elif not are_multiple_synced_thumbnails: + # return any first found thumbnail since we need thumbnail + # but dont care which one + matching_thumbnail_item = thumb_item + break + + if not matching_thumbnail_item: + # WARNING: this can only happen if multiple thumbnails + # workflow is broken, since it found multiple matching outputName + # in representation but they do not align with any thumbnail item + self.log.warning( + "No matching thumbnail item found for output name " + "'{}'".format(output_name) + ) + if not thumbnail_data_items: + self.log.warning( + "No thumbnail data items found" + ) + return {} + # as fallback return first thumbnail item + return thumbnail_data_items[0] + + + return matching_thumbnail_item + + def _make_extended_component_name(self, component_item, repre, iteration_index): + """ Returns the extended component name + + Name is based on the asset name and representation name. + + Args: + component_item (dict): The component item dictionary. + repre (dict): The representation dictionary. + iteration_index (int): The index of the iteration. + + Returns: + str: The extended component name. + + """ + # reset extended if no need for extended asset name + if self.keep_first_subset_name_for_review and iteration_index == 0: + return + + # get asset name and define extended name variant + asset_name = component_item["asset_data"]["name"] + return "_".join( + (asset_name, repre["name"]) + ) + + def _create_src_components( + self, instance, repre, component_item, location): + """Create src component for thumbnail. + + This will replicate the input component and change its name to + have suffix "_src". + + Args: + instance (pyblish.api.Instance): Instance + repre (dict): Representation + component_item (dict): Component item + location (str): Location name + + Returns: + dict: Component item + """ + # Make sure thumbnail is disabled + component_item["thumbnail"] = False + # Set location + component_item["component_location_name"] = location + # Modify name of component to have suffix "_src" + component_data = component_item["component_data"] + component_name = component_data["name"] + component_data["name"] = component_name + "_src" + component_data["metadata"] = self._prepare_component_metadata( + instance, repre, component_item["component_path"], False + ) + return component_item + def _collect_additional_metadata(self, streams): pass From 4ba778e73e6123b5fa6ce43fed2110729740f813 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Nov 2023 13:49:57 +0100 Subject: [PATCH 20/37] fixing duplication of single thumbnail for multiple reviewables --- .../publish/integrate_ftrack_instances.py | 46 +++++++------------ 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py index 334e70ce0cc..dcc9d6569a1 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py @@ -1,7 +1,6 @@ import os import json import copy - import pyblish.api from openpype.pipeline.publish import get_publish_repre_path @@ -219,7 +218,6 @@ def process(self, instance): current_item["src_component"] = src_comp # Add item to component list - component_list.append(thumbnail_item) thumbnail_data_items.append(current_item) # Create review components @@ -242,11 +240,16 @@ def process(self, instance): review_item = copy.deepcopy(base_component_item) # get first or synchronize thumbnail item - sync_thumbnail_item = self._get_matching_thumbnail_item( + sync_thumbnail_item = None + sync_thumbnail_item_src = None + sync_thumbnail_data = self._get_matching_thumbnail_item( repre, thumbnail_data_items, multiple_synced_thumbnails ) + if sync_thumbnail_data: + sync_thumbnail_item = sync_thumbnail_data.get("item") + sync_thumbnail_item_src = sync_thumbnail_data.get("src_component") """ Renaming asset name only to those components which are explicitly @@ -258,20 +261,26 @@ def process(self, instance): """ extended_asset_name = self._make_extended_component_name( base_component_item, repre, index) + if multiple_reviewable and extended_asset_name: review_item["asset_data"]["name"] = extended_asset_name # rename also thumbnail if sync_thumbnail_item: - sync_thumbnail_item["item"]["asset_data"]["name"] = ( + sync_thumbnail_item["asset_data"]["name"] = ( extended_asset_name ) # rename also src_thumbnail - if sync_thumbnail_item.get("src_component"): - thumb_src_component = sync_thumbnail_item["src_component"] - thumb_src_component["asset_data"]["name"] = ( + if sync_thumbnail_item_src: + sync_thumbnail_item_src["asset_data"]["name"] = ( extended_asset_name ) + # adding thumbnail component to component list + if sync_thumbnail_item: + component_list.append(copy.deepcopy(sync_thumbnail_item)) + if sync_thumbnail_item_src: + component_list.append(copy.deepcopy(sync_thumbnail_item_src)) + # add metadata to review component if self._is_repre_video(repre): component_name = "ftrackreview-mp4" @@ -304,26 +313,6 @@ def process(self, instance): ) component_list.append(src_comp) - if index > 0: - asset_name = review_item["asset_data"]["name"] - # perhaps it might happen that no thumbnail for - # current iteration is found in thumbnails list - # QUESTION: should this be consider as bug in code? - # NOTE: this was inherited from original code, but perhaps it - # should not be included - if ( - sync_thumbnail_item - and sync_thumbnail_item["item"]["asset_data"]["name"] != asset_name # noqa - ): - new_thumbnail_component = copy.deepcopy( - sync_thumbnail_item["item"] - ) - new_thumbnail_component["asset_data"]["name"] = asset_name - new_thumbnail_component["component_location_name"] = ( - ftrack_server_location_name - ) - component_list.append(new_thumbnail_component) - # Add item to component list component_list.append(review_item) @@ -597,9 +586,6 @@ def _prepare_video_component_metadata( stream_width = tmp_width stream_height = tmp_height - self.log.debug("FPS from stream is {} and duration is {}".format( - input_framerate, stream_duration - )) frame_out = float(stream_duration) * stream_fps break From 73600fb0a8dbf60175f3d9984e89be95743e5859 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Nov 2023 13:57:13 +0100 Subject: [PATCH 21/37] hound --- .../ftrack/plugins/publish/integrate_ftrack_instances.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py index dcc9d6569a1..66d5b73dc65 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py @@ -428,10 +428,10 @@ def _get_matching_thumbnail_item( # as fallback return first thumbnail item return thumbnail_data_items[0] - return matching_thumbnail_item - def _make_extended_component_name(self, component_item, repre, iteration_index): + def _make_extended_component_name( + self, component_item, repre, iteration_index): """ Returns the extended component name Name is based on the asset name and representation name. @@ -445,7 +445,7 @@ def _make_extended_component_name(self, component_item, repre, iteration_index): str: The extended component name. """ - # reset extended if no need for extended asset name + # reset extended if no need for extended asset name if self.keep_first_subset_name_for_review and iteration_index == 0: return From e6c050403871e45ccc7c0423ac107e2b8d9a8b02 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Nov 2023 13:59:33 +0100 Subject: [PATCH 22/37] unused variable comment https://github.com/ynput/OpenPype/pull/5939#discussion_r1400418886 --- .../ftrack/plugins/publish/integrate_ftrack_instances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py index 66d5b73dc65..ffa26725764 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py @@ -166,7 +166,7 @@ def process(self, instance): self.log.debug("Multiple output names: {}".format( synced_multiple_output_names )) - multiple_synced_thumbnails = len(thumbnail_representations) > 1 + multiple_synced_thumbnails = len(synced_multiple_output_names) > 1 # Components data component_list = [] From 166bfb2f31981647a18c4f381a082b54eead9f6d Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 21 Nov 2023 14:06:33 +0100 Subject: [PATCH 23/37] improving code readability --- .../publish/integrate_ftrack_instances.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py index ffa26725764..edad0b01329 100644 --- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py +++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py @@ -174,10 +174,17 @@ def process(self, instance): # Create thumbnail components for repre in thumbnail_representations: - repre_path = get_publish_repre_path(instance, repre, False) + # get repre path from representation + # and return published_path if available + # the path is validated and if it does not exists it returns None + repre_path = get_publish_repre_path( + instance, + repre, + only_published=False + ) if not repre_path: self.log.warning( - "Published path is not set and source was removed." + "Published path is not set or source was removed." ) continue @@ -199,15 +206,15 @@ def process(self, instance): "component_location_name": ftrack_server_location_name }) - # add thumbnail to items data for future synchronization - current_item = { + # add thumbnail data to items for future synchronization + current_item_data = { "sync_key": repre.get("outputName"), "representation": repre, "item": thumbnail_item } # Create copy of item before setting location if "delete" not in repre.get("tags", []): - src_comp = self._create_src_components( + src_comp = self._create_src_component( instance, repre, copy.deepcopy(thumbnail_item), @@ -215,10 +222,10 @@ def process(self, instance): ) component_list.append(src_comp) - current_item["src_component"] = src_comp + current_item_data["src_component"] = src_comp # Add item to component list - thumbnail_data_items.append(current_item) + thumbnail_data_items.append(current_item_data) # Create review components # Change asset name of each new component for review @@ -249,7 +256,8 @@ def process(self, instance): ) if sync_thumbnail_data: sync_thumbnail_item = sync_thumbnail_data.get("item") - sync_thumbnail_item_src = sync_thumbnail_data.get("src_component") + sync_thumbnail_item_src = sync_thumbnail_data.get( + "src_component") """ Renaming asset name only to those components which are explicitly @@ -305,7 +313,7 @@ def process(self, instance): # Create copy of item before setting location if "delete" not in repre.get("tags", []): - src_comp = self._create_src_components( + src_comp = self._create_src_component( instance, repre, copy.deepcopy(review_item), @@ -455,7 +463,7 @@ def _make_extended_component_name( (asset_name, repre["name"]) ) - def _create_src_components( + def _create_src_component( self, instance, repre, component_item, location): """Create src component for thumbnail. From 09ad8cb4f1d9f6e8f7c72f99633e97f0bfa4fd4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Tue, 21 Nov 2023 17:12:19 +0100 Subject: [PATCH 24/37] :art: launcher action this needs to be moved to subprocess to show UI properly --- .../actions/generate_asset_usage_report.py | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 openpype/plugins/actions/generate_asset_usage_report.py diff --git a/openpype/plugins/actions/generate_asset_usage_report.py b/openpype/plugins/actions/generate_asset_usage_report.py new file mode 100644 index 00000000000..60630c5a43e --- /dev/null +++ b/openpype/plugins/actions/generate_asset_usage_report.py @@ -0,0 +1,157 @@ +""" +TODO: we need to move it to subprocess to show UI +""" +import csv +import os +import tempfile +import time + +from qtpy import QtWidgets +from qtpy.QtCore import Qt +from qtpy.QtGui import QClipboard + +from pymongo.collection import Collection + +from openpype.client import OpenPypeMongoConnection +from openpype.pipeline import LauncherAction + + +class ReportWindow(QtWidgets.QWidget): + def __init__(self): + super().__init__() + + self.text_area = QtWidgets.QTextEdit(self) + self.copy_button = QtWidgets.QPushButton('Copy to Clipboard', self) + self.save_button = QtWidgets.QPushButton('Save to CSV File', self) + + self.copy_button.clicked.connect(self.copy_to_clipboard) + self.save_button.clicked.connect(self.save_to_file) + + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(self.text_area) + layout.addWidget(self.copy_button) + layout.addWidget(self.save_button) + + def copy_to_clipboard(self): + clipboard = QtWidgets.QApplication.clipboard() + clipboard.setText(self.text_area.toPlainText(), QClipboard.Clipboard) + + def save_to_file(self): + file_name, _ = QtWidgets.QFileDialog.getSaveFileName(self, 'Save File') + if file_name: + with open(file_name, 'w') as file: + file.write(self.text_area.toPlainText()) + + def set_content(self, content): + self.text_area.setText(content) + + +class OpenTaskPath(LauncherAction): + name = "get_asset_usage_report" + label = "Asset Usage Report" + icon = "list" + order = 500 + + def is_compatible(self, session): + """Return whether the action is compatible with the session""" + return bool(session.get("AVALON_ASSET")) + + def _get_subset(self, version_id, project: Collection): + pipeline = [ + { + "$match": { + "_id": version_id + }, + }, { + "$lookup": { + "from": project.name, + "localField": "parent", + "foreignField": "_id", + "as": "parents" + } + } + ] + + result = project.aggregate(pipeline) + doc = next(result) + # print(doc) + return { + "name": f'{"/".join(doc["parents"][0]["data"]["parents"])}/{doc["parents"][0]["name"]}/{doc["name"]}', + "family": doc["data"].get("family") or doc["data"].get("families")[0] + } + + def process(self, session, **kwargs): + start = time.perf_counter() + project = session["AVALON_PROJECT"] + + pipeline = [ + { + "$match": { + "data.inputLinks": { + "$exists": True, + "$ne": [] + }, + "data.families": {"$in": ["workfile"]} + } + }, { + "$lookup": { + "from": "OP01_CG_demo", + "localField": "data.inputLinks.id", + "foreignField": "_id", + "as": "linked_docs" + } + } + ] + + client = OpenPypeMongoConnection.get_mongo_client() + db = client["avalon"] + + result = db[project].aggregate(pipeline) + + asset_map = [] + for doc in result: + source = { + "source": self._get_subset(doc["parent"], db[project]), + } + source["source"].update({"version": doc["name"]}) + refs = [ + { + "subset": self._get_subset(linked["parent"], db[project]), + "version": linked.get("name") + } + for linked in doc["linked_docs"] + ] + source["refs"] = refs + asset_map.append(source) + + # for ref in asset_map: + # print(ref) + + grouped = {} + + for asset in asset_map: + for ref in asset["refs"]: + key = f'{ref["subset"]["name"]} (v{ref["version"]})' + if key in grouped: + grouped[key].append(asset["source"]) + else: + grouped[key] = [asset["source"]] + + temp = tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".csv") + try: + with open(temp.name, "w", newline="") as csvfile: + writer = csv.writer(csvfile, delimiter=";") + writer.writerow(["Subset", "Used in", "Version"]) + for key, value in grouped.items(): + writer.writerow([key, "", ""]) + for source in value: + writer.writerow(["", source["name"], source["version"]]) + finally: + temp.close() + + end = time.perf_counter() + app = QtWidgets.QApplication.instance() + window = ReportWindow() + # window.set_content(open(temp.name).read()) + window.show() + print(f"Finished in {end - start:0.4f} seconds", 2) From d47128cb1f510845b9706c5ba59fcc56a1f0edd7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 22 Nov 2023 03:26:34 +0000 Subject: [PATCH 25/37] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index f484016bfef..e2afcdaac7a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.17.7-nightly.2 - 3.17.7-nightly.1 - 3.17.6 - 3.17.6-nightly.3 @@ -134,7 +135,6 @@ body: - 3.15.2-nightly.5 - 3.15.2-nightly.4 - 3.15.2-nightly.3 - - 3.15.2-nightly.2 validations: required: true - type: dropdown From 03198fafcf2ae34f7fb1203638e64f69ce7cce82 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 22 Nov 2023 14:30:19 +0100 Subject: [PATCH 26/37] adding back asset_doc variable --- .../hosts/traypublisher/plugins/publish/validate_frame_ranges.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/hosts/traypublisher/plugins/publish/validate_frame_ranges.py b/openpype/hosts/traypublisher/plugins/publish/validate_frame_ranges.py index 95894848a49..56389a927fa 100644 --- a/openpype/hosts/traypublisher/plugins/publish/validate_frame_ranges.py +++ b/openpype/hosts/traypublisher/plugins/publish/validate_frame_ranges.py @@ -41,6 +41,7 @@ def process(self, instance): for pattern in self.skip_timelines_check)): self.log.info("Skipping for {} task".format(instance.data["task"])) + asset_doc = instance.data["assetEntity"] asset_data = asset_doc["data"] frame_start = asset_data["frameStart"] frame_end = asset_data["frameEnd"] From 85c73fa52c478afe441052bf997b4b2752f7b137 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 22 Nov 2023 22:52:00 +0800 Subject: [PATCH 27/37] remove duplicated variable of MTOA verbosity level in submit maya deadline --- .../deadline/plugins/publish/submit_maya_deadline.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 86de5c620e3..26a605a7440 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -97,7 +97,6 @@ class VRayPluginInfo(object): @attr.s class ArnoldPluginInfo(object): ArnoldFile = attr.ib(default=None) - ArnoldVerbose = attr.ib(default=2) class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline, @@ -661,12 +660,9 @@ def _get_arnold_render_payload(self, data): # Plugin Info ass_file, _ = os.path.splitext(data["output_filename_0"]) ass_filepath = ass_file + ".ass" - current_verbosity_level = cmds.getAttr( - "defaultArnoldRenderOptions.log_verbosity") plugin_info = ArnoldPluginInfo( - ArnoldFile=ass_filepath, - ArnoldVerbose=current_verbosity_level + ArnoldFile=ass_filepath ) return job_info, attr.asdict(plugin_info) From 61fe4825e61d707e914387c0c54e1d5a9e667ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 22 Nov 2023 16:25:33 +0100 Subject: [PATCH 28/37] :art: initial work --- openpype/modules/asset_reporter/__init__.py | 8 + openpype/modules/asset_reporter/module.py | 30 ++ openpype/modules/asset_reporter/window.py | 378 ++++++++++++++++++ .../defaults/system_settings/modules.json | 3 + .../schemas/system_schema/schema_modules.json | 14 + 5 files changed, 433 insertions(+) create mode 100644 openpype/modules/asset_reporter/__init__.py create mode 100644 openpype/modules/asset_reporter/module.py create mode 100644 openpype/modules/asset_reporter/window.py diff --git a/openpype/modules/asset_reporter/__init__.py b/openpype/modules/asset_reporter/__init__.py new file mode 100644 index 00000000000..6267b4824bb --- /dev/null +++ b/openpype/modules/asset_reporter/__init__.py @@ -0,0 +1,8 @@ +from .module import ( + AssetReporterAction +) + + +__all__ = ( + "AssetReporterAction", +) diff --git a/openpype/modules/asset_reporter/module.py b/openpype/modules/asset_reporter/module.py new file mode 100644 index 00000000000..34b84795d54 --- /dev/null +++ b/openpype/modules/asset_reporter/module.py @@ -0,0 +1,30 @@ +import os.path + +from openpype import AYON_SERVER_ENABLED +from openpype.modules import OpenPypeModule, ITrayAction +from openpype.lib import run_detached_process, get_openpype_execute_args + + +class AssetReporterAction(OpenPypeModule, ITrayAction): + label = "Asset Usage Report" + name = "asset_reporter" + admin_action = True + + def initialize(self, modules_settings): + self.enabled = not AYON_SERVER_ENABLED + + def tray_init(self): + ... + + def tray_exit(self): + ... + + def on_action_trigger(self): + args = get_openpype_execute_args() + args += ["run", + os.path.join( + os.path.dirname(__file__), + "window.py")] + + print(" ".join(args)) + run_detached_process(args) diff --git a/openpype/modules/asset_reporter/window.py b/openpype/modules/asset_reporter/window.py new file mode 100644 index 00000000000..6cb2f0e0e07 --- /dev/null +++ b/openpype/modules/asset_reporter/window.py @@ -0,0 +1,378 @@ +import appdirs +import qtawesome +import csv +import tempfile +from qtpy import QtCore, QtWidgets +from qtpy.QtGui import QClipboard, QColor +import time + +from openpype import ( + resources, + style +) +from openpype.client import OpenPypeMongoConnection +from pymongo.collection import Collection +from openpype.lib import JSONSettingRegistry +from openpype.tools.utils import PlaceholderLineEdit, get_openpype_qt_app +from openpype.tools.utils.constants import PROJECT_NAME_ROLE +from openpype.tools.utils.models import ProjectModel, ProjectSortFilterProxy + + +class AssetReporterRegistry(JSONSettingRegistry): + """Class handling OpenPype general settings registry. + + Attributes: + vendor (str): Name used for path construction. + product (str): Additional name used for path construction. + + """ + + def __init__(self): + self.vendor = "ynput" + self.product = "openpype" + name = "asset_usage_reporter" + path = appdirs.user_data_dir(self.product, self.vendor) + super(AssetReporterRegistry, self).__init__(name, path) + + +class OverlayWidget(QtWidgets.QFrame): + project_selected = QtCore.Signal(str) + + def __init__(self, publisher_window): + super(OverlayWidget, self).__init__(publisher_window) + self.setObjectName("OverlayFrame") + + middle_frame = QtWidgets.QFrame(self) + middle_frame.setObjectName("ChooseProjectFrame") + + content_widget = QtWidgets.QWidget(middle_frame) + + header_label = QtWidgets.QLabel("Choose project", content_widget) + header_label.setObjectName("ChooseProjectLabel") + # Create project models and view + projects_model = ProjectModel() + projects_proxy = ProjectSortFilterProxy() + projects_proxy.setSourceModel(projects_model) + projects_proxy.setFilterKeyColumn(0) + + projects_view = QtWidgets.QListView(content_widget) + projects_view.setObjectName("ChooseProjectView") + projects_view.setModel(projects_proxy) + projects_view.setEditTriggers( + QtWidgets.QAbstractItemView.NoEditTriggers + ) + + confirm_btn = QtWidgets.QPushButton("Confirm", content_widget) + cancel_btn = QtWidgets.QPushButton("Cancel", content_widget) + cancel_btn.setVisible(False) + btns_layout = QtWidgets.QHBoxLayout() + btns_layout.addStretch(1) + btns_layout.addWidget(cancel_btn, 0) + btns_layout.addWidget(confirm_btn, 0) + + txt_filter = PlaceholderLineEdit(content_widget) + txt_filter.setPlaceholderText("Quick filter projects..") + txt_filter.setClearButtonEnabled(True) + txt_filter.addAction(qtawesome.icon("fa.filter", color="gray"), + QtWidgets.QLineEdit.LeadingPosition) + + content_layout = QtWidgets.QVBoxLayout(content_widget) + content_layout.setContentsMargins(0, 0, 0, 0) + content_layout.setSpacing(20) + content_layout.addWidget(header_label, 0) + content_layout.addWidget(txt_filter, 0) + content_layout.addWidget(projects_view, 1) + content_layout.addLayout(btns_layout, 0) + + middle_layout = QtWidgets.QHBoxLayout(middle_frame) + middle_layout.setContentsMargins(30, 30, 10, 10) + middle_layout.addWidget(content_widget) + + main_layout = QtWidgets.QHBoxLayout(self) + main_layout.setContentsMargins(10, 10, 10, 10) + main_layout.addStretch(1) + main_layout.addWidget(middle_frame, 2) + main_layout.addStretch(1) + + projects_view.doubleClicked.connect(self._on_double_click) + confirm_btn.clicked.connect(self._on_confirm_click) + cancel_btn.clicked.connect(self._on_cancel_click) + txt_filter.textChanged.connect(self._on_text_changed) + + self._projects_view = projects_view + self._projects_model = projects_model + self._projects_proxy = projects_proxy + self._cancel_btn = cancel_btn + self._confirm_btn = confirm_btn + self._txt_filter = txt_filter + + self._publisher_window = publisher_window + self._project_name = None + + def showEvent(self, event): + self._projects_model.refresh() + # Sort projects after refresh + self._projects_proxy.sort(0) + + setting_registry = AssetReporterRegistry() + try: + project_name = setting_registry.get_item("project_name") + except ValueError: + project_name = None + + if project_name: + index = None + src_index = self._projects_model.find_project(project_name) + if src_index is not None: + index = self._projects_proxy.mapFromSource(src_index) + + if index is not None: + selection_model = self._projects_view.selectionModel() + selection_model.select( + index, + QtCore.QItemSelectionModel.SelectCurrent + ) + self._projects_view.setCurrentIndex(index) + + self._cancel_btn.setVisible(self._project_name is not None) + super(OverlayWidget, self).showEvent(event) + + def _on_double_click(self): + self.set_selected_project() + + def _on_confirm_click(self): + self.set_selected_project() + + def _on_cancel_click(self): + self._set_project(self._project_name) + + def _on_text_changed(self): + self._projects_proxy.setFilterRegularExpression( + self._txt_filter.text()) + + def set_selected_project(self): + index = self._projects_view.currentIndex() + + if project_name := index.data(PROJECT_NAME_ROLE): + self._set_project(project_name) + + def _set_project(self, project_name): + self._project_name = project_name + self.setVisible(False) + self.project_selected.emit(project_name) + + setting_registry = AssetReporterRegistry() + setting_registry.set_item("project_name", project_name) + + +class AssetReporterWindow(QtWidgets.QDialog): + default_width = 1300 + default_height = 800 + footer_border = 8 + _content = None + + def __init__(self, parent=None, controller=None, reset_on_show=None): + super(AssetReporterWindow, self).__init__(parent) + + self._result = {} + self.setObjectName("AssetReporterWindow") + + self.setWindowTitle("Asset Usage Reporter") + + if parent is None: + on_top_flag = QtCore.Qt.WindowStaysOnTopHint + else: + on_top_flag = QtCore.Qt.Dialog + + self.setWindowFlags( + QtCore.Qt.WindowTitleHint + | QtCore.Qt.WindowMaximizeButtonHint + | QtCore.Qt.WindowMinimizeButtonHint + | QtCore.Qt.WindowCloseButtonHint + | on_top_flag + ) + self.table = QtWidgets.QTableWidget(self) + self.table.setColumnCount(3) + self.table.setColumnWidth(0, 400) + self.table.setColumnWidth(1, 300) + self.table.setHorizontalHeaderLabels(["Subset", "Used in", "Version"]) + + # self.text_area = QtWidgets.QTextEdit(self) + self.copy_button = QtWidgets.QPushButton('Copy to Clipboard', self) + self.save_button = QtWidgets.QPushButton('Save to CSV File', self) + + self.copy_button.clicked.connect(self.copy_to_clipboard) + self.save_button.clicked.connect(self.save_to_file) + + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(self.table) + # layout.addWidget(self.text_area) + layout.addWidget(self.copy_button) + layout.addWidget(self.save_button) + + self.resize(self.default_width, self.default_height) + self.setStyleSheet(style.load_stylesheet()) + + overlay_widget = OverlayWidget(self) + overlay_widget.project_selected.connect(self._on_project_select) + self._overlay_widget = overlay_widget + + def _on_project_select(self, project_name): + self._project_name = project_name + self.process() + if not self._result: + self.set_content("no result generated") + return + + rows = sum(len(value) for key, value in self._result.items()) + self.table.setRowCount(rows) + + row = 0 + content = [] + for key, value in self._result.items(): + item = QtWidgets.QTableWidgetItem(key) + item.setBackground(QColor(32, 32, 32)) + self.table.setItem(row, 0, item) + for source in value: + self.table.setItem(row, 1, QtWidgets.QTableWidgetItem(source["name"])) + self.table.setItem(row, 2, QtWidgets.QTableWidgetItem(str(source["version"]))) + print(f' - {source["version"]}') + row += 1 + + content.append(f"{key}") + content.extend( + f"\t{source['name']} (v{source['version']})" for source in value + ) + self.set_content("\n".join(content)) + + def copy_to_clipboard(self): + clipboard = QtWidgets.QApplication.clipboard() + clipboard.setText(self._content, QClipboard.Clipboard) + + def save_to_file(self): + file_name, _ = QtWidgets.QFileDialog.getSaveFileName(self, 'Save File') + if file_name: + self._write_csv(file_name) + + def set_content(self, content): + self._content = content + + def get_content(self): + return self._content + + def _resize_overlay(self): + self._overlay_widget.resize( + self.width(), + self.height() + ) + + def resizeEvent(self, event): + super(AssetReporterWindow, self).resizeEvent(event) + self._resize_overlay() + + def _get_subset(self, version_id, project: Collection): + pipeline = [ + { + "$match": { + "_id": version_id + }, + }, { + "$lookup": { + "from": project.name, + "localField": "parent", + "foreignField": "_id", + "as": "parents" + } + } + ] + + result = project.aggregate(pipeline) + doc = next(result) + # print(doc) + return { + "name": f'{"/".join(doc["parents"][0]["data"]["parents"])}/{doc["parents"][0]["name"]}/{doc["name"]}', + "family": doc["data"].get("family") or doc["data"].get("families")[0] + } + + def process(self): + start = time.perf_counter() + project = self._project_name + + pipeline = [ + { + "$match": { + "data.inputLinks": { + "$exists": True, + "$ne": [] + }, + "data.families": {"$in": ["workfile"]} + } + }, { + "$lookup": { + "from": project, + "localField": "data.inputLinks.id", + "foreignField": "_id", + "as": "linked_docs" + } + } + ] + + client = OpenPypeMongoConnection.get_mongo_client() + db = client["avalon"] + + result = db[project].aggregate(pipeline) + + asset_map = [] + for doc in result: + source = { + "source": self._get_subset(doc["parent"], db[project]), + } + source["source"].update({"version": doc["name"]}) + refs = [ + { + "subset": self._get_subset(linked["parent"], db[project]), + "version": linked.get("name") + } + for linked in doc["linked_docs"] + ] + source["refs"] = refs + asset_map.append(source) + + # for ref in asset_map: + # print(ref) + + grouped = {} + + for asset in asset_map: + for ref in asset["refs"]: + key = f'{ref["subset"]["name"]} (v{ref["version"]})' + if key in grouped: + grouped[key].append(asset["source"]) + else: + grouped[key] = [asset["source"]] + self._result = grouped + + end = time.perf_counter() + + print(f"Finished in {end - start:0.4f} seconds", 2) + + def _write_csv(self, file_name): + with open(file_name, "w", newline="") as csvfile: + writer = csv.writer(csvfile, delimiter=";") + writer.writerow(["Subset", "Used in", "Version"]) + for key, value in self._result.items(): + writer.writerow([key, "", ""]) + for source in value: + writer.writerow(["", source["name"], source["version"]]) + + + +def main(): + app_instance = get_openpype_qt_app() + window = AssetReporterWindow() + window.show() + app_instance.exec_() + + +if __name__ == "__main__": + main() diff --git a/openpype/settings/defaults/system_settings/modules.json b/openpype/settings/defaults/system_settings/modules.json index f524f01d455..bb943524f16 100644 --- a/openpype/settings/defaults/system_settings/modules.json +++ b/openpype/settings/defaults/system_settings/modules.json @@ -210,5 +210,8 @@ "darwin": "", "linux": "" } + }, + "asset_reporter": { + "enabled": false } } diff --git a/openpype/settings/entities/schemas/system_schema/schema_modules.json b/openpype/settings/entities/schemas/system_schema/schema_modules.json index 952b38040c6..2258dd42e38 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_modules.json +++ b/openpype/settings/entities/schemas/system_schema/schema_modules.json @@ -355,6 +355,20 @@ { "type": "dynamic_schema", "name": "system_settings/modules" + }, + { + "type": "dict", + "key": "asset_reporter", + "label": "Asset Usage Report", + "collapsible": true, + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + } + ] } ] } From 5125db72b4ad2ece64e20bef0406fb9596df29d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 22 Nov 2023 16:38:09 +0100 Subject: [PATCH 29/37] :memo: add docstrings and some polishing --- openpype/modules/asset_reporter/module.py | 11 ++- openpype/modules/asset_reporter/window.py | 67 +++++++++++++------ .../schemas/system_schema/schema_modules.json | 2 +- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/openpype/modules/asset_reporter/module.py b/openpype/modules/asset_reporter/module.py index 34b84795d54..8c754cc3c04 100644 --- a/openpype/modules/asset_reporter/module.py +++ b/openpype/modules/asset_reporter/module.py @@ -6,18 +6,15 @@ class AssetReporterAction(OpenPypeModule, ITrayAction): + label = "Asset Usage Report" name = "asset_reporter" - admin_action = True - - def initialize(self, modules_settings): - self.enabled = not AYON_SERVER_ENABLED def tray_init(self): - ... + pass - def tray_exit(self): - ... + def initialize(self, modules_settings): + self.enabled = not AYON_SERVER_ENABLED def on_action_trigger(self): args = get_openpype_execute_args() diff --git a/openpype/modules/asset_reporter/window.py b/openpype/modules/asset_reporter/window.py index 6cb2f0e0e07..9dbcc3ea746 100644 --- a/openpype/modules/asset_reporter/window.py +++ b/openpype/modules/asset_reporter/window.py @@ -1,17 +1,22 @@ +"""Tool for generating asset usage report. + +This tool is used to generate asset usage report for a project. +It is using links between published version to find out where +the asset is used. + +""" + +import csv +import time + import appdirs import qtawesome -import csv -import tempfile +from pymongo.collection import Collection from qtpy import QtCore, QtWidgets from qtpy.QtGui import QClipboard, QColor -import time -from openpype import ( - resources, - style -) +from openpype import style from openpype.client import OpenPypeMongoConnection -from pymongo.collection import Collection from openpype.lib import JSONSettingRegistry from openpype.tools.utils import PlaceholderLineEdit, get_openpype_qt_app from openpype.tools.utils.constants import PROJECT_NAME_ROLE @@ -21,6 +26,8 @@ class AssetReporterRegistry(JSONSettingRegistry): """Class handling OpenPype general settings registry. + This is used to store last selected project. + Attributes: vendor (str): Name used for path construction. product (str): Additional name used for path construction. @@ -36,6 +43,10 @@ def __init__(self): class OverlayWidget(QtWidgets.QFrame): + """Overlay widget for choosing project. + + This code is taken from the Tray Publisher tool. + """ project_selected = QtCore.Signal(str) def __init__(self, publisher_window): @@ -116,7 +127,7 @@ def showEvent(self, event): setting_registry = AssetReporterRegistry() try: - project_name = setting_registry.get_item("project_name") + project_name = str(setting_registry.get_item("project_name")) except ValueError: project_name = None @@ -166,9 +177,8 @@ def _set_project(self, project_name): class AssetReporterWindow(QtWidgets.QDialog): - default_width = 1300 + default_width = 1000 default_height = 800 - footer_border = 8 _content = None def __init__(self, parent=None, controller=None, reset_on_show=None): @@ -217,7 +227,13 @@ def __init__(self, parent=None, controller=None, reset_on_show=None): overlay_widget.project_selected.connect(self._on_project_select) self._overlay_widget = overlay_widget - def _on_project_select(self, project_name): + def _on_project_select(self, project_name: str): + """Generate table when project is selected. + + This will generate the table and fill it with data. + Source data are held in memory in `_result` attribute that + is used to transform them into clipboard or csv file. + """ self._project_name = project_name self.process() if not self._result: @@ -231,14 +247,15 @@ def _on_project_select(self, project_name): content = [] for key, value in self._result.items(): item = QtWidgets.QTableWidgetItem(key) - item.setBackground(QColor(32, 32, 32)) + # this doesn't work as it is probably overriden by stylesheet? + # item.setBackground(QColor(32, 32, 32)) self.table.setItem(row, 0, item) for source in value: self.table.setItem(row, 1, QtWidgets.QTableWidgetItem(source["name"])) self.table.setItem(row, 2, QtWidgets.QTableWidgetItem(str(source["version"]))) - print(f' - {source["version"]}') row += 1 + # generate clipboard content content.append(f"{key}") content.extend( f"\t{source['name']} (v{source['version']})" for source in value @@ -295,9 +312,20 @@ def _get_subset(self, version_id, project: Collection): } def process(self): + """Generate asset usage report data. + + This is the main method of the tool. It is using MongoDB + aggregation pipeline to find all published versions that + are used as input for other published versions. Then it + generates a map of assets and their usage. + + """ start = time.perf_counter() project = self._project_name + # get all versions of published workfiles that has non-empty + # inputLinks and connect it with their respective documents + # using ID. pipeline = [ { "$match": { @@ -323,6 +351,9 @@ def process(self): result = db[project].aggregate(pipeline) asset_map = [] + # this is creating the map - for every workfile and its linked + # documents, create a dictionary with "source" and "refs" keys + # and resolve the subset name and version from the document for doc in result: source = { "source": self._get_subset(doc["parent"], db[project]), @@ -338,11 +369,9 @@ def process(self): source["refs"] = refs asset_map.append(source) - # for ref in asset_map: - # print(ref) - grouped = {} + # this will group the assets by subset name and version for asset in asset_map: for ref in asset["refs"]: key = f'{ref["subset"]["name"]} (v{ref["version"]})' @@ -356,7 +385,8 @@ def process(self): print(f"Finished in {end - start:0.4f} seconds", 2) - def _write_csv(self, file_name): + def _write_csv(self, file_name: str) -> None: + """Write CSV file with results.""" with open(file_name, "w", newline="") as csvfile: writer = csv.writer(csvfile, delimiter=";") writer.writerow(["Subset", "Used in", "Version"]) @@ -366,7 +396,6 @@ def _write_csv(self, file_name): writer.writerow(["", source["name"], source["version"]]) - def main(): app_instance = get_openpype_qt_app() window = AssetReporterWindow() diff --git a/openpype/settings/entities/schemas/system_schema/schema_modules.json b/openpype/settings/entities/schemas/system_schema/schema_modules.json index 2258dd42e38..5b189eae888 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_modules.json +++ b/openpype/settings/entities/schemas/system_schema/schema_modules.json @@ -359,7 +359,7 @@ { "type": "dict", "key": "asset_reporter", - "label": "Asset Usage Report", + "label": "Asset Usage Reporter", "collapsible": true, "checkbox_key": "enabled", "children": [ From 455482041080894b1cc78731292f63a7139effdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 22 Nov 2023 16:42:11 +0100 Subject: [PATCH 30/37] :bug: invalid header order --- openpype/modules/asset_reporter/window.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/modules/asset_reporter/window.py b/openpype/modules/asset_reporter/window.py index 9dbcc3ea746..bdcf0a9adf1 100644 --- a/openpype/modules/asset_reporter/window.py +++ b/openpype/modules/asset_reporter/window.py @@ -205,7 +205,7 @@ def __init__(self, parent=None, controller=None, reset_on_show=None): self.table.setColumnCount(3) self.table.setColumnWidth(0, 400) self.table.setColumnWidth(1, 300) - self.table.setHorizontalHeaderLabels(["Subset", "Used in", "Version"]) + self.table.setHorizontalHeaderLabels(["Used in", "Product", "Version"]) # self.text_area = QtWidgets.QTextEdit(self) self.copy_button = QtWidgets.QPushButton('Copy to Clipboard', self) @@ -389,7 +389,7 @@ def _write_csv(self, file_name: str) -> None: """Write CSV file with results.""" with open(file_name, "w", newline="") as csvfile: writer = csv.writer(csvfile, delimiter=";") - writer.writerow(["Subset", "Used in", "Version"]) + writer.writerow(["Used in", "Product", "Version"]) for key, value in self._result.items(): writer.writerow([key, "", ""]) for source in value: From 5ec3f92d7c79165ef324a5028a1125ffd39465cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 22 Nov 2023 17:02:30 +0100 Subject: [PATCH 31/37] :bug: remove unused plugin and hound fixes --- openpype/modules/asset_reporter/window.py | 15 +- .../actions/generate_asset_usage_report.py | 157 ------------------ 2 files changed, 9 insertions(+), 163 deletions(-) delete mode 100644 openpype/plugins/actions/generate_asset_usage_report.py diff --git a/openpype/modules/asset_reporter/window.py b/openpype/modules/asset_reporter/window.py index bdcf0a9adf1..e34037965c1 100644 --- a/openpype/modules/asset_reporter/window.py +++ b/openpype/modules/asset_reporter/window.py @@ -251,14 +251,17 @@ def _on_project_select(self, project_name: str): # item.setBackground(QColor(32, 32, 32)) self.table.setItem(row, 0, item) for source in value: - self.table.setItem(row, 1, QtWidgets.QTableWidgetItem(source["name"])) - self.table.setItem(row, 2, QtWidgets.QTableWidgetItem(str(source["version"]))) + self.table.setItem( + row, 1, QtWidgets.QTableWidgetItem(source["name"])) + self.table.setItem( + row, 2, QtWidgets.QTableWidgetItem( + str(source["version"]))) row += 1 # generate clipboard content - content.append(f"{key}") + content.append(key) content.extend( - f"\t{source['name']} (v{source['version']})" for source in value + f"\t{source['name']} (v{source['version']})" for source in value # noqa: E501 ) self.set_content("\n".join(content)) @@ -307,8 +310,8 @@ def _get_subset(self, version_id, project: Collection): doc = next(result) # print(doc) return { - "name": f'{"/".join(doc["parents"][0]["data"]["parents"])}/{doc["parents"][0]["name"]}/{doc["name"]}', - "family": doc["data"].get("family") or doc["data"].get("families")[0] + "name": f'{"/".join(doc["parents"][0]["data"]["parents"])}/{doc["parents"][0]["name"]}/{doc["name"]}', # noqa: E501 + "family": doc["data"].get("family") or doc["data"].get("families")[0] # noqa: E501 } def process(self): diff --git a/openpype/plugins/actions/generate_asset_usage_report.py b/openpype/plugins/actions/generate_asset_usage_report.py deleted file mode 100644 index 60630c5a43e..00000000000 --- a/openpype/plugins/actions/generate_asset_usage_report.py +++ /dev/null @@ -1,157 +0,0 @@ -""" -TODO: we need to move it to subprocess to show UI -""" -import csv -import os -import tempfile -import time - -from qtpy import QtWidgets -from qtpy.QtCore import Qt -from qtpy.QtGui import QClipboard - -from pymongo.collection import Collection - -from openpype.client import OpenPypeMongoConnection -from openpype.pipeline import LauncherAction - - -class ReportWindow(QtWidgets.QWidget): - def __init__(self): - super().__init__() - - self.text_area = QtWidgets.QTextEdit(self) - self.copy_button = QtWidgets.QPushButton('Copy to Clipboard', self) - self.save_button = QtWidgets.QPushButton('Save to CSV File', self) - - self.copy_button.clicked.connect(self.copy_to_clipboard) - self.save_button.clicked.connect(self.save_to_file) - - layout = QtWidgets.QVBoxLayout(self) - layout.addWidget(self.text_area) - layout.addWidget(self.copy_button) - layout.addWidget(self.save_button) - - def copy_to_clipboard(self): - clipboard = QtWidgets.QApplication.clipboard() - clipboard.setText(self.text_area.toPlainText(), QClipboard.Clipboard) - - def save_to_file(self): - file_name, _ = QtWidgets.QFileDialog.getSaveFileName(self, 'Save File') - if file_name: - with open(file_name, 'w') as file: - file.write(self.text_area.toPlainText()) - - def set_content(self, content): - self.text_area.setText(content) - - -class OpenTaskPath(LauncherAction): - name = "get_asset_usage_report" - label = "Asset Usage Report" - icon = "list" - order = 500 - - def is_compatible(self, session): - """Return whether the action is compatible with the session""" - return bool(session.get("AVALON_ASSET")) - - def _get_subset(self, version_id, project: Collection): - pipeline = [ - { - "$match": { - "_id": version_id - }, - }, { - "$lookup": { - "from": project.name, - "localField": "parent", - "foreignField": "_id", - "as": "parents" - } - } - ] - - result = project.aggregate(pipeline) - doc = next(result) - # print(doc) - return { - "name": f'{"/".join(doc["parents"][0]["data"]["parents"])}/{doc["parents"][0]["name"]}/{doc["name"]}', - "family": doc["data"].get("family") or doc["data"].get("families")[0] - } - - def process(self, session, **kwargs): - start = time.perf_counter() - project = session["AVALON_PROJECT"] - - pipeline = [ - { - "$match": { - "data.inputLinks": { - "$exists": True, - "$ne": [] - }, - "data.families": {"$in": ["workfile"]} - } - }, { - "$lookup": { - "from": "OP01_CG_demo", - "localField": "data.inputLinks.id", - "foreignField": "_id", - "as": "linked_docs" - } - } - ] - - client = OpenPypeMongoConnection.get_mongo_client() - db = client["avalon"] - - result = db[project].aggregate(pipeline) - - asset_map = [] - for doc in result: - source = { - "source": self._get_subset(doc["parent"], db[project]), - } - source["source"].update({"version": doc["name"]}) - refs = [ - { - "subset": self._get_subset(linked["parent"], db[project]), - "version": linked.get("name") - } - for linked in doc["linked_docs"] - ] - source["refs"] = refs - asset_map.append(source) - - # for ref in asset_map: - # print(ref) - - grouped = {} - - for asset in asset_map: - for ref in asset["refs"]: - key = f'{ref["subset"]["name"]} (v{ref["version"]})' - if key in grouped: - grouped[key].append(asset["source"]) - else: - grouped[key] = [asset["source"]] - - temp = tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".csv") - try: - with open(temp.name, "w", newline="") as csvfile: - writer = csv.writer(csvfile, delimiter=";") - writer.writerow(["Subset", "Used in", "Version"]) - for key, value in grouped.items(): - writer.writerow([key, "", ""]) - for source in value: - writer.writerow(["", source["name"], source["version"]]) - finally: - temp.close() - - end = time.perf_counter() - app = QtWidgets.QApplication.instance() - window = ReportWindow() - # window.set_content(open(temp.name).read()) - window.show() - print(f"Finished in {end - start:0.4f} seconds", 2) From 8050d23529d8eded73f21b16c87df7480a359d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Wed, 22 Nov 2023 18:10:56 +0100 Subject: [PATCH 32/37] Revert ":bug: invalid header order" This reverts commit 455482041080894b1cc78731292f63a7139effdb. --- openpype/modules/asset_reporter/window.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/modules/asset_reporter/window.py b/openpype/modules/asset_reporter/window.py index e34037965c1..3dfc5855288 100644 --- a/openpype/modules/asset_reporter/window.py +++ b/openpype/modules/asset_reporter/window.py @@ -205,7 +205,7 @@ def __init__(self, parent=None, controller=None, reset_on_show=None): self.table.setColumnCount(3) self.table.setColumnWidth(0, 400) self.table.setColumnWidth(1, 300) - self.table.setHorizontalHeaderLabels(["Used in", "Product", "Version"]) + self.table.setHorizontalHeaderLabels(["Subset", "Used in", "Version"]) # self.text_area = QtWidgets.QTextEdit(self) self.copy_button = QtWidgets.QPushButton('Copy to Clipboard', self) @@ -392,7 +392,7 @@ def _write_csv(self, file_name: str) -> None: """Write CSV file with results.""" with open(file_name, "w", newline="") as csvfile: writer = csv.writer(csvfile, delimiter=";") - writer.writerow(["Used in", "Product", "Version"]) + writer.writerow(["Subset", "Used in", "Version"]) for key, value in self._result.items(): writer.writerow([key, "", ""]) for source in value: From 00c1288c9d24d7a14e0b6b01da0867465ceae852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Thu, 23 Nov 2023 11:25:45 +0100 Subject: [PATCH 33/37] :bug: handle hero versions --- openpype/modules/asset_reporter/window.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/openpype/modules/asset_reporter/window.py b/openpype/modules/asset_reporter/window.py index 3dfc5855288..9b19d6295cc 100644 --- a/openpype/modules/asset_reporter/window.py +++ b/openpype/modules/asset_reporter/window.py @@ -362,13 +362,20 @@ def process(self): "source": self._get_subset(doc["parent"], db[project]), } source["source"].update({"version": doc["name"]}) - refs = [ - { - "subset": self._get_subset(linked["parent"], db[project]), - "version": linked.get("name") - } - for linked in doc["linked_docs"] - ] + refs = [] + version = '' + for linked in doc["linked_docs"]: + try: + version = f'v{linked["name"]}' + except KeyError: + if linked["type"] == "hero_version": + version = "hero" + finally: + refs.append({ + "subset": self._get_subset(linked["parent"], db[project]), + "version": version + }) + source["refs"] = refs asset_map.append(source) @@ -377,7 +384,7 @@ def process(self): # this will group the assets by subset name and version for asset in asset_map: for ref in asset["refs"]: - key = f'{ref["subset"]["name"]} (v{ref["version"]})' + key = f'{ref["subset"]["name"]} ({ref["version"]})' if key in grouped: grouped[key].append(asset["source"]) else: From 5e5ed5b5177bada4484da014830c765f8be6587b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Thu, 23 Nov 2023 11:26:46 +0100 Subject: [PATCH 34/37] :dog: hound fix --- openpype/modules/asset_reporter/window.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/modules/asset_reporter/window.py b/openpype/modules/asset_reporter/window.py index 9b19d6295cc..ed3bc298e1f 100644 --- a/openpype/modules/asset_reporter/window.py +++ b/openpype/modules/asset_reporter/window.py @@ -372,7 +372,8 @@ def process(self): version = "hero" finally: refs.append({ - "subset": self._get_subset(linked["parent"], db[project]), + "subset": self._get_subset( + linked["parent"], db[project]), "version": version }) From cb938ad58cc56186b2cfa62144821c142bf3bc5f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 23 Nov 2023 12:02:41 +0100 Subject: [PATCH 35/37] remove usage of kwarg to create ModulesManager --- openpype/settings/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/lib.py b/openpype/settings/lib.py index ce62dde43f8..d62e50d3c74 100644 --- a/openpype/settings/lib.py +++ b/openpype/settings/lib.py @@ -172,7 +172,7 @@ def save_studio_settings(data): clear_metadata_from_settings(new_data) changes = calculate_changes(old_data, new_data) - modules_manager = ModulesManager(_system_settings=new_data) + modules_manager = ModulesManager(new_data) warnings = [] for module in modules_manager.get_enabled_modules(): From 1a027a412e5f141ad45dc418d7ddb3cd299873ca Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 24 Nov 2023 12:57:49 +0800 Subject: [PATCH 36/37] hound --- openpype/hosts/houdini/plugins/create/create_bgeo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/houdini/plugins/create/create_bgeo.py b/openpype/hosts/houdini/plugins/create/create_bgeo.py index 8dfb2ebb469..41409192021 100644 --- a/openpype/hosts/houdini/plugins/create/create_bgeo.py +++ b/openpype/hosts/houdini/plugins/create/create_bgeo.py @@ -2,7 +2,6 @@ """Creator plugin for creating pointcache bgeo files.""" from openpype.hosts.houdini.api import plugin from openpype.pipeline import CreatedInstance, CreatorError -from openpype.lib import EnumDef import hou from openpype.lib import EnumDef, BoolDef From 452048d0eef4bb8824ce8325426dc0a3989fd304 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Fri, 24 Nov 2023 10:30:40 +0000 Subject: [PATCH 37/37] Testing: Fix is_test_failed (#5951) * Fix is_test_failed * Fix hound? --- tests/conftest.py | 8 ++++++++ tests/lib/testing_classes.py | 6 +----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index a862030fffd..028c19bc5f5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -85,3 +85,11 @@ def pytest_runtest_makereport(item, call): # be "setup", "call", "teardown" setattr(item, "rep_" + rep.when, rep) + + # In the event of module scoped fixtures, also mark failure in module. + module = item + while module is not None and not isinstance(module, pytest.Module): + module = module.parent + if module is not None: + if rep.when == 'call' and (rep.failed or rep.skipped): + module.module_test_failure = True diff --git a/tests/lib/testing_classes.py b/tests/lib/testing_classes.py index 7dae9a762f2..7700381aa63 100644 --- a/tests/lib/testing_classes.py +++ b/tests/lib/testing_classes.py @@ -218,11 +218,7 @@ def dbcon_openpype(self, db_setup): yield mongo_client[self.TEST_OPENPYPE_NAME]["settings"] def is_test_failed(self, request): - # if request.node doesn't have rep_call, something failed - try: - return request.node.rep_call.failed - except AttributeError: - return True + return getattr(request.node, "module_test_failure", False) class PublishTest(ModuleUnitTest):