From ae62e79695e07c1a24865fbec32e460b9c0540ce Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 30 Mar 2021 17:56:52 +0200 Subject: [PATCH 01/57] Global: empty tasks should return empty dict --- pype/plugins/publish/collect_hierarchy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/plugins/publish/collect_hierarchy.py b/pype/plugins/publish/collect_hierarchy.py index 5c5dbf018c..27332976e9 100644 --- a/pype/plugins/publish/collect_hierarchy.py +++ b/pype/plugins/publish/collect_hierarchy.py @@ -50,7 +50,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): # suppose that all instances are Shots shot_data['entity_type'] = 'Shot' - shot_data['tasks'] = instance.data.get("tasks") or [] + shot_data['tasks'] = instance.data.get("tasks") or {} shot_data["comments"] = instance.data.get("comments", []) shot_data['custom_attributes'] = { From ee6d1db703931faaf987fda61c7388df6d3e7a29 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 30 Mar 2021 18:52:41 +0200 Subject: [PATCH 02/57] Resolve: fixing import path to new `api` format --- pype/hosts/resolve/api/plugin.py | 2 +- pype/hosts/resolve/plugins/create/create_shot_clip.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/hosts/resolve/api/plugin.py b/pype/hosts/resolve/api/plugin.py index 0423f15c2a..974f29cb33 100644 --- a/pype/hosts/resolve/api/plugin.py +++ b/pype/hosts/resolve/api/plugin.py @@ -81,7 +81,7 @@ class CreatorWidget(QtWidgets.QDialog): ok_btn.clicked.connect(self._on_ok_clicked) cancel_btn.clicked.connect(self._on_cancel_clicked) - stylesheet = resolve.menu.load_stylesheet() + stylesheet = resolve.api.menu.load_stylesheet() self.setStyleSheet(stylesheet) def _on_ok_clicked(self): diff --git a/pype/hosts/resolve/plugins/create/create_shot_clip.py b/pype/hosts/resolve/plugins/create/create_shot_clip.py index 09b2b73775..bf860194d3 100644 --- a/pype/hosts/resolve/plugins/create/create_shot_clip.py +++ b/pype/hosts/resolve/plugins/create/create_shot_clip.py @@ -1,6 +1,6 @@ # from pprint import pformat from pype.hosts import resolve -from pype.hosts.resolve import lib +from pype.hosts.resolve.api import lib class CreateShotClip(resolve.Creator): From b24bbe5a5760e055716fbf6f88dfaf8870249ba0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 30 Mar 2021 18:53:59 +0200 Subject: [PATCH 03/57] Resolve and Global: rename `master layer` to `hero track` --- pype/hosts/resolve/api/plugin.py | 30 +++++++++---------- .../plugins/create/create_shot_clip.py | 2 +- .../plugins/publish/collect_instances.py | 4 +-- pype/plugins/publish/collect_hierarchy.py | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/pype/hosts/resolve/api/plugin.py b/pype/hosts/resolve/api/plugin.py index 974f29cb33..ada7549b01 100644 --- a/pype/hosts/resolve/api/plugin.py +++ b/pype/hosts/resolve/api/plugin.py @@ -697,13 +697,13 @@ class PublishClip: Populating the tag data into internal variable self.tag_data """ # define vertical sync attributes - master_layer = True + hero_track = True self.review_layer = "" if self.vertical_sync: # check if track name is not in driving layer if self.track_name not in self.driving_layer: # if it is not then define vertical sync as None - master_layer = False + hero_track = False # increasing steps by index of rename iteration self.count_steps *= self.rename_index @@ -717,7 +717,7 @@ class PublishClip: self.tag_data[_k] = _v["value"] # driving layer is set as positive match - if master_layer or self.vertical_sync: + if hero_track or self.vertical_sync: # mark review layer if self.review_track and ( self.review_track not in self.review_track_default): @@ -751,33 +751,33 @@ class PublishClip: hierarchy_formating_data ) - tag_hierarchy_data.update({"masterLayer": True}) - if master_layer and self.vertical_sync: - # tag_hierarchy_data.update({"masterLayer": True}) + tag_hierarchy_data.update({"heroTrack": True}) + if hero_track and self.vertical_sync: + # tag_hierarchy_data.update({"heroTrack": True}) self.vertical_clip_match.update({ (self.clip_in, self.clip_out): tag_hierarchy_data }) - if not master_layer and self.vertical_sync: + if not hero_track and self.vertical_sync: # driving layer is set as negative match - for (_in, _out), master_data in self.vertical_clip_match.items(): - master_data.update({"masterLayer": False}) + for (_in, _out), hero_data in self.vertical_clip_match.items(): + hero_data.update({"heroTrack": False}) if _in == self.clip_in and _out == self.clip_out: - data_subset = master_data["subset"] - # add track index in case duplicity of names in master data + data_subset = hero_data["subset"] + # add track index in case duplicity of names in hero data if self.subset in data_subset: - master_data["subset"] = self.subset + str( + hero_data["subset"] = self.subset + str( self.track_index) # in case track name and subset name is the same then add if self.subset_name == self.track_name: - master_data["subset"] = self.subset + hero_data["subset"] = self.subset # assing data to return hierarchy data to tag - tag_hierarchy_data = master_data + tag_hierarchy_data = hero_data # add data to return data dict self.tag_data.update(tag_hierarchy_data) - if master_layer and self.review_layer: + if hero_track and self.review_layer: self.tag_data.update({"reviewTrack": self.review_layer}) def _solve_tag_hierarchy_data(self, hierarchy_formating_data): diff --git a/pype/hosts/resolve/plugins/create/create_shot_clip.py b/pype/hosts/resolve/plugins/create/create_shot_clip.py index bf860194d3..575e9f85a9 100644 --- a/pype/hosts/resolve/plugins/create/create_shot_clip.py +++ b/pype/hosts/resolve/plugins/create/create_shot_clip.py @@ -117,7 +117,7 @@ class CreateShotClip(resolve.Creator): "vSyncTrack": { "value": gui_tracks, # noqa "type": "QComboBox", - "label": "Master track", + "label": "Hero track", "target": "ui", "toolTip": "Select driving track name which should be mastering all others", # noqa "order": 1} diff --git a/pype/hosts/resolve/plugins/publish/collect_instances.py b/pype/hosts/resolve/plugins/publish/collect_instances.py index b1eafd512e..e76da13f48 100644 --- a/pype/hosts/resolve/plugins/publish/collect_instances.py +++ b/pype/hosts/resolve/plugins/publish/collect_instances.py @@ -102,10 +102,10 @@ class CollectInstances(pyblish.api.ContextPlugin): }) def create_shot_instance(self, context, timeline_item, **data): - master_layer = data.get("masterLayer") + hero_track = data.get("heroTrack") hierarchy_data = data.get("hierarchyData") - if not master_layer: + if not hero_track: return if not hierarchy_data: diff --git a/pype/plugins/publish/collect_hierarchy.py b/pype/plugins/publish/collect_hierarchy.py index 27332976e9..390ce443b6 100644 --- a/pype/plugins/publish/collect_hierarchy.py +++ b/pype/plugins/publish/collect_hierarchy.py @@ -40,7 +40,7 @@ class CollectHierarchy(pyblish.api.ContextPlugin): continue # exclude if not masterLayer True - if not instance.data.get("masterLayer"): + if not instance.data.get("heroTrack"): continue # get asset build data if any available From ceec8340cf7def24e4305d19d2d1ee66fb1ca238 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 30 Mar 2021 18:54:25 +0200 Subject: [PATCH 04/57] Resolve: fixing renamed function name --- pype/hosts/resolve/plugins/create/create_shot_clip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/hosts/resolve/plugins/create/create_shot_clip.py b/pype/hosts/resolve/plugins/create/create_shot_clip.py index 575e9f85a9..86851c7074 100644 --- a/pype/hosts/resolve/plugins/create/create_shot_clip.py +++ b/pype/hosts/resolve/plugins/create/create_shot_clip.py @@ -244,7 +244,7 @@ class CreateShotClip(resolve.Creator): sq_markers = self.timeline.GetMarkers() # create media bin for compound clips (trackItems) - mp_folder = resolve.create_current_sequence_media_bin(self.timeline) + mp_folder = resolve.create_bin(self.timeline.GetName()) kwargs = { "ui_inputs": widget.result, From d179e3a246c0fa99f99840b588f1428b326d775f Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 30 Mar 2021 18:54:49 +0200 Subject: [PATCH 05/57] Resolve: fixing to new function usage --- pype/hosts/resolve/utility_scripts/OTIO_export.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pype/hosts/resolve/utility_scripts/OTIO_export.py b/pype/hosts/resolve/utility_scripts/OTIO_export.py index a1142f56dd..daa51ea7ac 100644 --- a/pype/hosts/resolve/utility_scripts/OTIO_export.py +++ b/pype/hosts/resolve/utility_scripts/OTIO_export.py @@ -58,9 +58,8 @@ def _close_window(event): def _export_button(event): pm = resolve.GetProjectManager() project = pm.GetCurrentProject() - fps = project.GetSetting("timelineFrameRate") timeline = project.GetCurrentTimeline() - otio_timeline = otio_export.create_otio_timeline(timeline, fps) + otio_timeline = otio_export.create_otio_timeline(project) otio_path = os.path.join( itm["exportfilebttn"].Text, timeline.GetName() + ".otio") From 7354be225584c9f319bd76a65d3287509f1edced Mon Sep 17 00:00:00 2001 From: jezscha Date: Wed, 31 Mar 2021 08:41:12 +0000 Subject: [PATCH 06/57] Create draft PR for #916 From b315df6bbfe09978d11083163008d8d5218f5523 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 31 Mar 2021 11:19:50 +0200 Subject: [PATCH 07/57] Resolve: adding subset manager to menu --- pype/hosts/resolve/api/menu.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pype/hosts/resolve/api/menu.py b/pype/hosts/resolve/api/menu.py index 73ea937513..0b049e4433 100644 --- a/pype/hosts/resolve/api/menu.py +++ b/pype/hosts/resolve/api/menu.py @@ -12,7 +12,8 @@ from avalon.tools import ( creator, loader, sceneinventory, - libraryloader + libraryloader, + subsetmanager ) @@ -64,6 +65,7 @@ class PypeMenu(QtWidgets.QWidget): publish_btn = QtWidgets.QPushButton("Publish", self) load_btn = QtWidgets.QPushButton("Load", self) inventory_btn = QtWidgets.QPushButton("Inventory", self) + subsetm_btn = QtWidgets.QPushButton("Subset Manager", self) libload_btn = QtWidgets.QPushButton("Library", self) rename_btn = QtWidgets.QPushButton("Rename", self) set_colorspace_btn = QtWidgets.QPushButton( @@ -81,6 +83,7 @@ class PypeMenu(QtWidgets.QWidget): layout.addWidget(publish_btn) layout.addWidget(load_btn) layout.addWidget(inventory_btn) + layout.addWidget(subsetm_btn) layout.addWidget(Spacer(15, self)) @@ -102,6 +105,7 @@ class PypeMenu(QtWidgets.QWidget): publish_btn.clicked.connect(self.on_publish_clicked) load_btn.clicked.connect(self.on_load_clicked) inventory_btn.clicked.connect(self.on_inventory_clicked) + subsetm_btn.clicked.connect(self.on_subsetm_clicked) libload_btn.clicked.connect(self.on_libload_clicked) rename_btn.clicked.connect(self.on_rename_clicked) set_colorspace_btn.clicked.connect(self.on_set_colorspace_clicked) @@ -127,6 +131,10 @@ class PypeMenu(QtWidgets.QWidget): print("Clicked Inventory") sceneinventory.show() + def on_subsetm_clicked(self): + print("Clicked Subset Manager") + subsetmanager.show() + def on_libload_clicked(self): print("Clicked Library") libraryloader.show() From 310c9b2b6863f35952e6bc2d9e910141cdbcf1c1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 31 Mar 2021 11:43:16 +0200 Subject: [PATCH 08/57] Resolve: listing instances --- pype/hosts/resolve/__init__.py | 6 +++++- pype/hosts/resolve/api/pipeline.py | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/pype/hosts/resolve/__init__.py b/pype/hosts/resolve/__init__.py index 734e0bc5df..3e49ce3b9b 100644 --- a/pype/hosts/resolve/__init__.py +++ b/pype/hosts/resolve/__init__.py @@ -11,7 +11,9 @@ from .api.pipeline import ( update_container, publish, launch_workfiles_app, - maintained_selection + maintained_selection, + remove_instance, + list_instances ) from .api.lib import ( @@ -73,6 +75,8 @@ __all__ = [ "publish", "launch_workfiles_app", "maintained_selection", + "remove_instance", + "list_instances", # utils "setup", diff --git a/pype/hosts/resolve/api/pipeline.py b/pype/hosts/resolve/api/pipeline.py index 2d08203650..1ee9743518 100644 --- a/pype/hosts/resolve/api/pipeline.py +++ b/pype/hosts/resolve/api/pipeline.py @@ -258,3 +258,26 @@ def on_pyblish_instance_toggled(instance, old_value, new_value): # Whether instances should be passthrough based on new value timeline_item = instance.data["item"] set_publish_attribute(timeline_item, new_value) + + +def remove_instance(instance): + """Remove instance marker from track item.""" + pass + + +def list_instances(): + """List all created instances from current workfile.""" + listed_instances = [] + selected_timeline_items = lib.get_current_timeline_items( + filter=True, selecting_color=lib.publish_clip_color) + + for timeline_item_data in selected_timeline_items: + timeline_item = timeline_item_data["clip"]["item"] + + # get pype tag data + tag_data = lib.get_timeline_item_pype_tag(timeline_item) + + if tag_data: + listed_instances.append(tag_data) + + return listed_instances From b1eb67794cabb0dfad280139f6fdec3cabb4dcf9 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 31 Mar 2021 11:43:44 +0200 Subject: [PATCH 09/57] Resolve: renaming plugins to precollect --- .../{collect_instances.py => precollect_instances.py} | 4 ++-- .../{collect_workfile.py => precollect_workfile.py} | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) rename pype/hosts/resolve/plugins/publish/{collect_instances.py => precollect_instances.py} (97%) rename pype/hosts/resolve/plugins/publish/{collect_workfile.py => precollect_workfile.py} (88%) diff --git a/pype/hosts/resolve/plugins/publish/collect_instances.py b/pype/hosts/resolve/plugins/publish/precollect_instances.py similarity index 97% rename from pype/hosts/resolve/plugins/publish/collect_instances.py rename to pype/hosts/resolve/plugins/publish/precollect_instances.py index b1eafd512e..e54bda0a49 100644 --- a/pype/hosts/resolve/plugins/publish/collect_instances.py +++ b/pype/hosts/resolve/plugins/publish/precollect_instances.py @@ -5,11 +5,11 @@ from pype.hosts import resolve from pprint import pformat -class CollectInstances(pyblish.api.ContextPlugin): +class PrecollectInstances(pyblish.api.ContextPlugin): """Collect all Track items selection.""" order = pyblish.api.CollectorOrder - 0.59 - label = "Collect Instances" + label = "Precollect Instances" hosts = ["resolve"] def process(self, context): diff --git a/pype/hosts/resolve/plugins/publish/collect_workfile.py b/pype/hosts/resolve/plugins/publish/precollect_workfile.py similarity index 88% rename from pype/hosts/resolve/plugins/publish/collect_workfile.py rename to pype/hosts/resolve/plugins/publish/precollect_workfile.py index f7f90c9689..3e9a7f26b9 100644 --- a/pype/hosts/resolve/plugins/publish/collect_workfile.py +++ b/pype/hosts/resolve/plugins/publish/precollect_workfile.py @@ -9,10 +9,10 @@ from pype.hosts.resolve.otio import davinci_export reload(davinci_export) -class CollectWorkfile(pyblish.api.ContextPlugin): - """Inject the current working file into context""" +class PrecollectWorkfile(pyblish.api.ContextPlugin): + """Precollect the current working file into context""" - label = "Collect Workfile" + label = "Precollect Workfile" order = pyblish.api.CollectorOrder - 0.6 def process(self, context): @@ -21,8 +21,6 @@ class CollectWorkfile(pyblish.api.ContextPlugin): subset = "workfile" project = resolve.get_current_project() fps = project.GetSetting("timelineFrameRate") - - active_timeline = resolve.get_current_timeline() video_tracks = resolve.get_video_track_names() # adding otio timeline to context From cd2372300a7e979066a1ab2a0fd3a5e5cf143c1b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 31 Mar 2021 11:52:05 +0200 Subject: [PATCH 10/57] Resolve: improving subset manager labels --- pype/hosts/resolve/api/pipeline.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pype/hosts/resolve/api/pipeline.py b/pype/hosts/resolve/api/pipeline.py index 1ee9743518..d1d1b5185a 100644 --- a/pype/hosts/resolve/api/pipeline.py +++ b/pype/hosts/resolve/api/pipeline.py @@ -273,11 +273,15 @@ def list_instances(): for timeline_item_data in selected_timeline_items: timeline_item = timeline_item_data["clip"]["item"] + ti_name = timeline_item.GetName().split(".")[0] # get pype tag data tag_data = lib.get_timeline_item_pype_tag(timeline_item) if tag_data: + asset = tag_data.get("asset") + subset = tag_data.get("subset") + tag_data["label"] = f"{ti_name} [{asset}-{subset}]" listed_instances.append(tag_data) return listed_instances From 00e3b75feb27d5f94a0a1c7daefd140f8334725a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 31 Mar 2021 12:35:42 +0200 Subject: [PATCH 11/57] Auto stash before merge of "feature/916-resolve-instance-manager" and "3.0/bugfix/resolve-functionality-issues" --- pype/hosts/resolve/api/plugin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pype/hosts/resolve/api/plugin.py b/pype/hosts/resolve/api/plugin.py index ada7549b01..4ed8c5bc4a 100644 --- a/pype/hosts/resolve/api/plugin.py +++ b/pype/hosts/resolve/api/plugin.py @@ -777,9 +777,11 @@ class PublishClip: # add data to return data dict self.tag_data.update(tag_hierarchy_data) + # add review track only to hero track if hero_track and self.review_layer: self.tag_data.update({"reviewTrack": self.review_layer}) + def _solve_tag_hierarchy_data(self, hierarchy_formating_data): """ Solve tag data from hierarchy data and templates. """ # fill up clip name and hierarchy keys From a1b1c43d4faf32d23bb219d7ddf166c75ee35b38 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 31 Mar 2021 12:47:42 +0200 Subject: [PATCH 12/57] Resolve: adding removing instance action --- pype/hosts/resolve/api/pipeline.py | 23 ++++++++++++++++++++++- pype/hosts/resolve/api/plugin.py | 4 ++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/pype/hosts/resolve/api/pipeline.py b/pype/hosts/resolve/api/pipeline.py index d1d1b5185a..9a74e50e6f 100644 --- a/pype/hosts/resolve/api/pipeline.py +++ b/pype/hosts/resolve/api/pipeline.py @@ -262,7 +262,28 @@ def on_pyblish_instance_toggled(instance, old_value, new_value): def remove_instance(instance): """Remove instance marker from track item.""" - pass + instance_id = instance.get("uuid") + + selected_timeline_items = lib.get_current_timeline_items( + filter=True, selecting_color=lib.publish_clip_color) + + found_ti = None + for timeline_item_data in selected_timeline_items: + timeline_item = timeline_item_data["clip"]["item"] + + # get pype tag data + tag_data = lib.get_timeline_item_pype_tag(timeline_item) + _ti_id = tag_data.get("uuid") + if _ti_id == instance_id: + found_ti = timeline_item + break + + if found_ti is None: + return + + # removing instance by marker color + print(f"Removing instance: {found_ti.GetName()}") + found_ti.DeleteMarkersByColor(lib.pype_marker_color) def list_instances(): diff --git a/pype/hosts/resolve/api/plugin.py b/pype/hosts/resolve/api/plugin.py index 4ed8c5bc4a..0ba6c5e745 100644 --- a/pype/hosts/resolve/api/plugin.py +++ b/pype/hosts/resolve/api/plugin.py @@ -1,4 +1,5 @@ import re +import uuid from avalon import api import pype.api as pype from pype.hosts import resolve @@ -777,6 +778,9 @@ class PublishClip: # add data to return data dict self.tag_data.update(tag_hierarchy_data) + # add uuid to tag data + self.tag_data["uuid"] = str(uuid.uuid4()) + # add review track only to hero track if hero_track and self.review_layer: self.tag_data.update({"reviewTrack": self.review_layer}) From 9228508dff48bc15a670f5b24a26302851132b92 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 6 Apr 2021 15:11:00 +0200 Subject: [PATCH 13/57] Resolve: merge openpype fixes --- openpype/hosts/resolve/api/menu.py | 52 ++++--- openpype/hosts/resolve/api/pipeline.py | 48 +++++++ openpype/hosts/resolve/api/plugin.py | 35 +++-- .../plugins/create/create_shot_clip.py | 2 +- .../plugins/publish/collect_instances.py | 129 ------------------ .../plugins/publish/collect_workfile.py | 54 -------- .../resolve/utility_scripts/OTIO_export.py | 3 +- 7 files changed, 100 insertions(+), 223 deletions(-) delete mode 100644 openpype/hosts/resolve/plugins/publish/collect_instances.py delete mode 100644 openpype/hosts/resolve/plugins/publish/collect_workfile.py diff --git a/openpype/hosts/resolve/api/menu.py b/openpype/hosts/resolve/api/menu.py index 5ed7aeab34..ecd2708440 100644 --- a/openpype/hosts/resolve/api/menu.py +++ b/openpype/hosts/resolve/api/menu.py @@ -12,7 +12,8 @@ from avalon.tools import ( creator, loader, sceneinventory, - libraryloader + libraryloader, + subsetmanager ) @@ -59,19 +60,20 @@ class OpenPypeMenu(QtWidgets.QWidget): ) self.setWindowTitle("OpenPype") - workfiles_btn = QtWidgets.QPushButton("Workfiles ...", self) - create_btn = QtWidgets.QPushButton("Create ...", self) - publish_btn = QtWidgets.QPushButton("Publish ...", self) - load_btn = QtWidgets.QPushButton("Load ...", self) - inventory_btn = QtWidgets.QPushButton("Inventory ...", self) - libload_btn = QtWidgets.QPushButton("Library ...", self) - # rename_btn = QtWidgets.QPushButton("Rename ...", self) - # set_colorspace_btn = QtWidgets.QPushButton( - # "Set colorspace from presets", self - # ) - # reset_resolution_btn = QtWidgets.QPushButton( - # "Reset Resolution from peresets", self - # ) + workfiles_btn = QtWidgets.QPushButton("Workfiles", self) + create_btn = QtWidgets.QPushButton("Create", self) + publish_btn = QtWidgets.QPushButton("Publish", self) + load_btn = QtWidgets.QPushButton("Load", self) + inventory_btn = QtWidgets.QPushButton("Inventory", self) + subsetm_btn = QtWidgets.QPushButton("Subset Manager", self) + libload_btn = QtWidgets.QPushButton("Library", self) + rename_btn = QtWidgets.QPushButton("Rename", self) + set_colorspace_btn = QtWidgets.QPushButton( + "Set colorspace from presets", self + ) + reset_resolution_btn = QtWidgets.QPushButton( + "Reset Resolution from peresets", self + ) layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(10, 20, 10, 20) @@ -81,19 +83,20 @@ class OpenPypeMenu(QtWidgets.QWidget): layout.addWidget(publish_btn) layout.addWidget(load_btn) layout.addWidget(inventory_btn) + layout.addWidget(subsetm_btn) layout.addWidget(Spacer(15, self)) layout.addWidget(libload_btn) - # layout.addWidget(Spacer(15, self)) + layout.addWidget(Spacer(15, self)) - # layout.addWidget(rename_btn) + layout.addWidget(rename_btn) - # layout.addWidget(Spacer(15, self)) + layout.addWidget(Spacer(15, self)) - # layout.addWidget(set_colorspace_btn) - # layout.addWidget(reset_resolution_btn) + layout.addWidget(set_colorspace_btn) + layout.addWidget(reset_resolution_btn) self.setLayout(layout) @@ -102,10 +105,11 @@ class OpenPypeMenu(QtWidgets.QWidget): publish_btn.clicked.connect(self.on_publish_clicked) load_btn.clicked.connect(self.on_load_clicked) inventory_btn.clicked.connect(self.on_inventory_clicked) + subsetm_btn.clicked.connect(self.on_subsetm_clicked) libload_btn.clicked.connect(self.on_libload_clicked) - # rename_btn.clicked.connect(self.on_rename_clicked) - # set_colorspace_btn.clicked.connect(self.on_set_colorspace_clicked) - # reset_resolution_btn.clicked.connect(self.on_reset_resolution_clicked) + rename_btn.clicked.connect(self.on_rename_clicked) + set_colorspace_btn.clicked.connect(self.on_set_colorspace_clicked) + reset_resolution_btn.clicked.connect(self.on_reset_resolution_clicked) def on_workfile_clicked(self): print("Clicked Workfile") @@ -127,6 +131,10 @@ class OpenPypeMenu(QtWidgets.QWidget): print("Clicked Inventory") sceneinventory.show() + def on_subsetm_clicked(self): + print("Clicked Subset Manager") + subsetmanager.show() + def on_libload_clicked(self): print("Clicked Library") libraryloader.show() diff --git a/openpype/hosts/resolve/api/pipeline.py b/openpype/hosts/resolve/api/pipeline.py index d4d928a7d9..0e6d5aff4d 100644 --- a/openpype/hosts/resolve/api/pipeline.py +++ b/openpype/hosts/resolve/api/pipeline.py @@ -258,3 +258,51 @@ def on_pyblish_instance_toggled(instance, old_value, new_value): # Whether instances should be passthrough based on new value timeline_item = instance.data["item"] set_publish_attribute(timeline_item, new_value) + + +def remove_instance(instance): + """Remove instance marker from track item.""" + instance_id = instance.get("uuid") + + selected_timeline_items = lib.get_current_timeline_items( + filter=True, selecting_color=lib.publish_clip_color) + + found_ti = None + for timeline_item_data in selected_timeline_items: + timeline_item = timeline_item_data["clip"]["item"] + + # get openpype tag data + tag_data = lib.get_timeline_item_pype_tag(timeline_item) + _ti_id = tag_data.get("uuid") + if _ti_id == instance_id: + found_ti = timeline_item + break + + if found_ti is None: + return + + # removing instance by marker color + print(f"Removing instance: {found_ti.GetName()}") + found_ti.DeleteMarkersByColor(lib.pype_marker_color) + + +def list_instances(): + """List all created instances from current workfile.""" + listed_instances = [] + selected_timeline_items = lib.get_current_timeline_items( + filter=True, selecting_color=lib.publish_clip_color) + + for timeline_item_data in selected_timeline_items: + timeline_item = timeline_item_data["clip"]["item"] + ti_name = timeline_item.GetName().split(".")[0] + + # get openpype tag data + tag_data = lib.get_timeline_item_pype_tag(timeline_item) + + if tag_data: + asset = tag_data.get("asset") + subset = tag_data.get("subset") + tag_data["label"] = f"{ti_name} [{asset}-{subset}]" + listed_instances.append(tag_data) + + return listed_instances diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index 3833795b96..4712d0a8b9 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -1,4 +1,5 @@ import re +import uuid from avalon import api import openpype.api as pype from openpype.hosts import resolve @@ -697,13 +698,13 @@ class PublishClip: Populating the tag data into internal variable self.tag_data """ # define vertical sync attributes - master_layer = True + hero_track = True self.review_layer = "" if self.vertical_sync: # check if track name is not in driving layer if self.track_name not in self.driving_layer: # if it is not then define vertical sync as None - master_layer = False + hero_track = False # increasing steps by index of rename iteration self.count_steps *= self.rename_index @@ -717,7 +718,7 @@ class PublishClip: self.tag_data[_k] = _v["value"] # driving layer is set as positive match - if master_layer or self.vertical_sync: + if hero_track or self.vertical_sync: # mark review layer if self.review_track and ( self.review_track not in self.review_track_default): @@ -751,35 +752,39 @@ class PublishClip: hierarchy_formating_data ) - tag_hierarchy_data.update({"masterLayer": True}) - if master_layer and self.vertical_sync: - # tag_hierarchy_data.update({"masterLayer": True}) + tag_hierarchy_data.update({"heroTrack": True}) + if hero_track and self.vertical_sync: self.vertical_clip_match.update({ (self.clip_in, self.clip_out): tag_hierarchy_data }) - if not master_layer and self.vertical_sync: + if not hero_track and self.vertical_sync: # driving layer is set as negative match - for (_in, _out), master_data in self.vertical_clip_match.items(): - master_data.update({"masterLayer": False}) + for (_in, _out), hero_data in self.vertical_clip_match.items(): + hero_data.update({"heroTrack": False}) if _in == self.clip_in and _out == self.clip_out: - data_subset = master_data["subset"] - # add track index in case duplicity of names in master data + data_subset = hero_data["subset"] + # add track index in case duplicity of names in hero data if self.subset in data_subset: - master_data["subset"] = self.subset + str( + hero_data["subset"] = self.subset + str( self.track_index) # in case track name and subset name is the same then add if self.subset_name == self.track_name: - master_data["subset"] = self.subset + hero_data["subset"] = self.subset # assing data to return hierarchy data to tag - tag_hierarchy_data = master_data + tag_hierarchy_data = hero_data # add data to return data dict self.tag_data.update(tag_hierarchy_data) - if master_layer and self.review_layer: + # add uuid to tag data + self.tag_data["uuid"] = str(uuid.uuid4()) + + # add review track only to hero track + if hero_track and self.review_layer: self.tag_data.update({"reviewTrack": self.review_layer}) + def _solve_tag_hierarchy_data(self, hierarchy_formating_data): """ Solve tag data from hierarchy data and templates. """ # fill up clip name and hierarchy keys diff --git a/openpype/hosts/resolve/plugins/create/create_shot_clip.py b/openpype/hosts/resolve/plugins/create/create_shot_clip.py index 2916a52298..41fdbf5c61 100644 --- a/openpype/hosts/resolve/plugins/create/create_shot_clip.py +++ b/openpype/hosts/resolve/plugins/create/create_shot_clip.py @@ -117,7 +117,7 @@ class CreateShotClip(resolve.Creator): "vSyncTrack": { "value": gui_tracks, # noqa "type": "QComboBox", - "label": "Master track", + "label": "Hero track", "target": "ui", "toolTip": "Select driving track name which should be mastering all others", # noqa "order": 1} diff --git a/openpype/hosts/resolve/plugins/publish/collect_instances.py b/openpype/hosts/resolve/plugins/publish/collect_instances.py deleted file mode 100644 index f4eeb39754..0000000000 --- a/openpype/hosts/resolve/plugins/publish/collect_instances.py +++ /dev/null @@ -1,129 +0,0 @@ -import pyblish -from openpype.hosts import resolve - -# # developer reload modules -from pprint import pformat - - -class CollectInstances(pyblish.api.ContextPlugin): - """Collect all Track items selection.""" - - order = pyblish.api.CollectorOrder - 0.59 - label = "Collect Instances" - hosts = ["resolve"] - - def process(self, context): - otio_timeline = context.data["otioTimeline"] - selected_timeline_items = resolve.get_current_timeline_items( - filter=True, selecting_color=resolve.publish_clip_color) - - self.log.info( - "Processing enabled track items: {}".format( - len(selected_timeline_items))) - - for timeline_item_data in selected_timeline_items: - - data = dict() - timeline_item = timeline_item_data["clip"]["item"] - - # get openpype tag data - tag_data = resolve.get_timeline_item_pype_tag(timeline_item) - self.log.debug(f"__ tag_data: {pformat(tag_data)}") - - if not tag_data: - continue - - if tag_data.get("id") != "pyblish.avalon.instance": - continue - - media_pool_item = timeline_item.GetMediaPoolItem() - clip_property = media_pool_item.GetClipProperty() - self.log.debug(f"clip_property: {clip_property}") - - # add tag data to instance data - data.update({ - k: v for k, v in tag_data.items() - if k not in ("id", "applieswhole", "label") - }) - - asset = tag_data["asset"] - subset = tag_data["subset"] - - # insert family into families - family = tag_data["family"] - families = [str(f) for f in tag_data["families"]] - families.insert(0, str(family)) - - data.update({ - "name": "{} {} {}".format(asset, subset, families), - "asset": asset, - "item": timeline_item, - "families": families, - "publish": resolve.get_publish_attribute(timeline_item), - "fps": context.data["fps"] - }) - - # otio clip data - otio_data = resolve.get_otio_clip_instance_data( - otio_timeline, timeline_item_data) or {} - data.update(otio_data) - - # add resolution - self.get_resolution_to_data(data, context) - - # create instance - instance = context.create_instance(**data) - - # create shot instance for shot attributes create/update - self.create_shot_instance(context, timeline_item, **data) - - self.log.info("Creating instance: {}".format(instance)) - self.log.debug( - "_ instance.data: {}".format(pformat(instance.data))) - - def get_resolution_to_data(self, data, context): - assert data.get("otioClip"), "Missing `otioClip` data" - - # solve source resolution option - if data.get("sourceResolution", None): - otio_clip_metadata = data[ - "otioClip"].media_reference.metadata - data.update({ - "resolutionWidth": otio_clip_metadata["width"], - "resolutionHeight": otio_clip_metadata["height"], - "pixelAspect": otio_clip_metadata["pixelAspect"] - }) - else: - otio_tl_metadata = context.data["otioTimeline"].metadata - data.update({ - "resolutionWidth": otio_tl_metadata["width"], - "resolutionHeight": otio_tl_metadata["height"], - "pixelAspect": otio_tl_metadata["pixelAspect"] - }) - - def create_shot_instance(self, context, timeline_item, **data): - master_layer = data.get("masterLayer") - hierarchy_data = data.get("hierarchyData") - - if not master_layer: - return - - if not hierarchy_data: - return - - asset = data["asset"] - subset = "shotMain" - - # insert family into families - family = "shot" - - data.update({ - "name": "{} {} {}".format(asset, subset, family), - "subset": subset, - "asset": asset, - "family": family, - "families": [], - "publish": resolve.get_publish_attribute(timeline_item) - }) - - context.create_instance(**data) diff --git a/openpype/hosts/resolve/plugins/publish/collect_workfile.py b/openpype/hosts/resolve/plugins/publish/collect_workfile.py deleted file mode 100644 index a66284ed02..0000000000 --- a/openpype/hosts/resolve/plugins/publish/collect_workfile.py +++ /dev/null @@ -1,54 +0,0 @@ -import pyblish.api -from openpype.hosts import resolve -from avalon import api as avalon -from pprint import pformat - -# dev -from importlib import reload -from openpype.hosts.resolve.otio import davinci_export -reload(davinci_export) - - -class CollectWorkfile(pyblish.api.ContextPlugin): - """Inject the current working file into context""" - - label = "Collect Workfile" - order = pyblish.api.CollectorOrder - 0.6 - - def process(self, context): - - asset = avalon.Session["AVALON_ASSET"] - subset = "workfile" - project = resolve.get_current_project() - fps = project.GetSetting("timelineFrameRate") - - active_timeline = resolve.get_current_timeline() - video_tracks = resolve.get_video_track_names() - - # adding otio timeline to context - otio_timeline = davinci_export.create_otio_timeline(project) - - instance_data = { - "name": "{}_{}".format(asset, subset), - "asset": asset, - "subset": "{}{}".format(asset, subset.capitalize()), - "item": project, - "family": "workfile" - } - - # create instance with workfile - instance = context.create_instance(**instance_data) - - # update context with main project attributes - context_data = { - "activeProject": project, - "otioTimeline": otio_timeline, - "videoTracks": video_tracks, - "currentFile": project.GetName(), - "fps": fps, - } - context.data.update(context_data) - - self.log.info("Creating instance: {}".format(instance)) - self.log.debug("__ instance.data: {}".format(pformat(instance.data))) - self.log.debug("__ context_data: {}".format(pformat(context_data))) diff --git a/openpype/hosts/resolve/utility_scripts/OTIO_export.py b/openpype/hosts/resolve/utility_scripts/OTIO_export.py index 91bc2c5700..0431eb7daa 100644 --- a/openpype/hosts/resolve/utility_scripts/OTIO_export.py +++ b/openpype/hosts/resolve/utility_scripts/OTIO_export.py @@ -58,9 +58,8 @@ def _close_window(event): def _export_button(event): pm = resolve.GetProjectManager() project = pm.GetCurrentProject() - fps = project.GetSetting("timelineFrameRate") timeline = project.GetCurrentTimeline() - otio_timeline = otio_export.create_otio_timeline(timeline, fps) + otio_timeline = otio_export.create_otio_timeline(project) otio_path = os.path.join( itm["exportfilebttn"].Text, timeline.GetName() + ".otio") From e46c3a9fa7730e71e679138e4e7861ae3ec14afa Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 6 Apr 2021 15:13:03 +0200 Subject: [PATCH 14/57] Resolve: updating menu to disable unwritten features --- openpype/hosts/resolve/api/menu.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/resolve/api/menu.py b/openpype/hosts/resolve/api/menu.py index ecd2708440..c0471ebfbe 100644 --- a/openpype/hosts/resolve/api/menu.py +++ b/openpype/hosts/resolve/api/menu.py @@ -67,13 +67,13 @@ class OpenPypeMenu(QtWidgets.QWidget): inventory_btn = QtWidgets.QPushButton("Inventory", self) subsetm_btn = QtWidgets.QPushButton("Subset Manager", self) libload_btn = QtWidgets.QPushButton("Library", self) - rename_btn = QtWidgets.QPushButton("Rename", self) - set_colorspace_btn = QtWidgets.QPushButton( - "Set colorspace from presets", self - ) - reset_resolution_btn = QtWidgets.QPushButton( - "Reset Resolution from peresets", self - ) + # rename_btn = QtWidgets.QPushButton("Rename", self) + # set_colorspace_btn = QtWidgets.QPushButton( + # "Set colorspace from presets", self + # ) + # reset_resolution_btn = QtWidgets.QPushButton( + # "Reset Resolution from peresets", self + # ) layout = QtWidgets.QVBoxLayout(self) layout.setContentsMargins(10, 20, 10, 20) @@ -91,12 +91,12 @@ class OpenPypeMenu(QtWidgets.QWidget): layout.addWidget(Spacer(15, self)) - layout.addWidget(rename_btn) + # layout.addWidget(rename_btn) - layout.addWidget(Spacer(15, self)) + # layout.addWidget(Spacer(15, self)) - layout.addWidget(set_colorspace_btn) - layout.addWidget(reset_resolution_btn) + # layout.addWidget(set_colorspace_btn) + # layout.addWidget(reset_resolution_btn) self.setLayout(layout) @@ -107,9 +107,9 @@ class OpenPypeMenu(QtWidgets.QWidget): inventory_btn.clicked.connect(self.on_inventory_clicked) subsetm_btn.clicked.connect(self.on_subsetm_clicked) libload_btn.clicked.connect(self.on_libload_clicked) - rename_btn.clicked.connect(self.on_rename_clicked) - set_colorspace_btn.clicked.connect(self.on_set_colorspace_clicked) - reset_resolution_btn.clicked.connect(self.on_reset_resolution_clicked) + # rename_btn.clicked.connect(self.on_rename_clicked) + # set_colorspace_btn.clicked.connect(self.on_set_colorspace_clicked) + # reset_resolution_btn.clicked.connect(self.on_reset_resolution_clicked) def on_workfile_clicked(self): print("Clicked Workfile") From 9a0f20ef95e18767699a624a575c84e834fd1fa1 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Tue, 6 Apr 2021 15:15:02 +0200 Subject: [PATCH 15/57] Resolve: adding correct ux convention to menu --- openpype/hosts/resolve/api/menu.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/hosts/resolve/api/menu.py b/openpype/hosts/resolve/api/menu.py index c0471ebfbe..e7be3fc963 100644 --- a/openpype/hosts/resolve/api/menu.py +++ b/openpype/hosts/resolve/api/menu.py @@ -60,13 +60,13 @@ class OpenPypeMenu(QtWidgets.QWidget): ) self.setWindowTitle("OpenPype") - workfiles_btn = QtWidgets.QPushButton("Workfiles", self) - create_btn = QtWidgets.QPushButton("Create", self) - publish_btn = QtWidgets.QPushButton("Publish", self) - load_btn = QtWidgets.QPushButton("Load", self) - inventory_btn = QtWidgets.QPushButton("Inventory", self) - subsetm_btn = QtWidgets.QPushButton("Subset Manager", self) - libload_btn = QtWidgets.QPushButton("Library", self) + workfiles_btn = QtWidgets.QPushButton("Workfiles ...", self) + create_btn = QtWidgets.QPushButton("Create ...", self) + publish_btn = QtWidgets.QPushButton("Publish ...", self) + load_btn = QtWidgets.QPushButton("Load ...", self) + inventory_btn = QtWidgets.QPushButton("Inventory ...", self) + subsetm_btn = QtWidgets.QPushButton("Subset Manager ...", self) + libload_btn = QtWidgets.QPushButton("Library ...", self) # rename_btn = QtWidgets.QPushButton("Rename", self) # set_colorspace_btn = QtWidgets.QPushButton( # "Set colorspace from presets", self @@ -89,7 +89,7 @@ class OpenPypeMenu(QtWidgets.QWidget): layout.addWidget(libload_btn) - layout.addWidget(Spacer(15, self)) + # layout.addWidget(Spacer(15, self)) # layout.addWidget(rename_btn) From d503395818ec8113bb0c402535e1c44aac13d61a Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 7 Apr 2021 10:43:36 +0200 Subject: [PATCH 16/57] resolve: fix transition merge --- openpype/hosts/resolve/hooks/pre_resolve_setup.py | 2 +- .../hosts/resolve/plugins/publish/precollect_instances.py | 2 +- openpype/hosts/resolve/plugins/publish/precollect_workfile.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/resolve/hooks/pre_resolve_setup.py b/openpype/hosts/resolve/hooks/pre_resolve_setup.py index 0ee55d3790..bcb27e24fc 100644 --- a/openpype/hosts/resolve/hooks/pre_resolve_setup.py +++ b/openpype/hosts/resolve/hooks/pre_resolve_setup.py @@ -44,7 +44,7 @@ class ResolvePrelaunch(PreLaunchHook): self.launch_context.env["PRE_PYTHON_SCRIPT"] = pre_py_sc self.log.debug(f"-- pre_py_sc: `{pre_py_sc}`...") try: - __import__("pype.hosts.resolve") + __import__("openpype.hosts.resolve") __import__("pyblish") except ImportError: diff --git a/openpype/hosts/resolve/plugins/publish/precollect_instances.py b/openpype/hosts/resolve/plugins/publish/precollect_instances.py index 4fabe2b8a0..c38cbc4f73 100644 --- a/openpype/hosts/resolve/plugins/publish/precollect_instances.py +++ b/openpype/hosts/resolve/plugins/publish/precollect_instances.py @@ -1,5 +1,5 @@ import pyblish -from pype.hosts import resolve +from openpype.hosts import resolve # # developer reload modules from pprint import pformat diff --git a/openpype/hosts/resolve/plugins/publish/precollect_workfile.py b/openpype/hosts/resolve/plugins/publish/precollect_workfile.py index 3e9a7f26b9..ee05fb6f13 100644 --- a/openpype/hosts/resolve/plugins/publish/precollect_workfile.py +++ b/openpype/hosts/resolve/plugins/publish/precollect_workfile.py @@ -1,11 +1,11 @@ import pyblish.api -from pype.hosts import resolve +from openpype.hosts import resolve from avalon import api as avalon from pprint import pformat # dev from importlib import reload -from pype.hosts.resolve.otio import davinci_export +from openpype.hosts.resolve.otio import davinci_export reload(davinci_export) From 2e1548b0b37705322a648af46f37d5681f0df726 Mon Sep 17 00:00:00 2001 From: mkolar Date: Wed, 7 Apr 2021 14:40:05 +0000 Subject: [PATCH 17/57] Create draft PR for #1173 From 73abae2f265f604bce9c7ffa6b405eed3796beef Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 8 Apr 2021 09:59:58 +0200 Subject: [PATCH 18/57] removed unused function --- openpype/modules/ftrack/lib/credentials.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/openpype/modules/ftrack/lib/credentials.py b/openpype/modules/ftrack/lib/credentials.py index 16b1fb25fb..6ca9b730da 100644 --- a/openpype/modules/ftrack/lib/credentials.py +++ b/openpype/modules/ftrack/lib/credentials.py @@ -113,13 +113,6 @@ def set_env(ft_user=None, ft_api_key=None): os.environ["FTRACK_API_KEY"] = ft_api_key or "" -def get_env_credentials(): - return ( - os.environ.get("FTRACK_API_USER"), - os.environ.get("FTRACK_API_KEY") - ) - - def check_credentials(ft_user, ft_api_key, ftrack_server=None): if not ftrack_server: ftrack_server = os.environ["FTRACK_SERVER"] From d1e047d40c1d604db61db0022d280db897961ca2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 8 Apr 2021 10:32:46 +0200 Subject: [PATCH 19/57] moved one method from credentials.py to ftrack module --- openpype/modules/ftrack/ftrack_module.py | 4 ++++ openpype/modules/ftrack/lib/credentials.py | 5 ----- openpype/modules/ftrack/tray/ftrack_tray.py | 6 +++--- openpype/modules/ftrack/tray/login_dialog.py | 6 ++++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/openpype/modules/ftrack/ftrack_module.py b/openpype/modules/ftrack/ftrack_module.py index e1af9c15a7..cd383cbdc6 100644 --- a/openpype/modules/ftrack/ftrack_module.py +++ b/openpype/modules/ftrack/ftrack_module.py @@ -210,3 +210,7 @@ class FtrackModule( def tray_exit(self): return self.tray_module.stop_action_server() + + def set_credentials_to_env(self, username, api_key): + os.environ["FTRACK_API_USER"] = username or "" + os.environ["FTRACK_API_KEY"] = api_key or "" diff --git a/openpype/modules/ftrack/lib/credentials.py b/openpype/modules/ftrack/lib/credentials.py index 6ca9b730da..3d9aa75e84 100644 --- a/openpype/modules/ftrack/lib/credentials.py +++ b/openpype/modules/ftrack/lib/credentials.py @@ -108,11 +108,6 @@ def clear_credentials(ft_user=None, ftrack_server=None, user=None): file.write(json.dumps(content_json)) -def set_env(ft_user=None, ft_api_key=None): - os.environ["FTRACK_API_USER"] = ft_user or "" - os.environ["FTRACK_API_KEY"] = ft_api_key or "" - - def check_credentials(ft_user, ft_api_key, ftrack_server=None): if not ftrack_server: ftrack_server = os.environ["FTRACK_SERVER"] diff --git a/openpype/modules/ftrack/tray/ftrack_tray.py b/openpype/modules/ftrack/tray/ftrack_tray.py index 9da5db835b..ee27d8b730 100644 --- a/openpype/modules/ftrack/tray/ftrack_tray.py +++ b/openpype/modules/ftrack/tray/ftrack_tray.py @@ -30,7 +30,7 @@ class FtrackTrayWrapper: self.bool_action_thread_running = False self.bool_timer_event = False - self.widget_login = login_dialog.CredentialsDialog() + self.widget_login = login_dialog.CredentialsDialog(module) self.widget_login.login_changed.connect(self.on_login_change) self.widget_login.logout_signal.connect(self.on_logout) @@ -56,7 +56,7 @@ class FtrackTrayWrapper: validation = credentials.check_credentials(ft_user, ft_api_key) if validation: self.widget_login.set_credentials(ft_user, ft_api_key) - credentials.set_env(ft_user, ft_api_key) + self.module.set_credentials_to_env(ft_user, ft_api_key) log.info("Connected to Ftrack successfully") self.on_login_change() @@ -337,7 +337,7 @@ class FtrackTrayWrapper: def changed_user(self): self.stop_action_server() - credentials.set_env() + self.module.set_credentials_to_env(None, None) self.validate() def start_timer_manager(self, data): diff --git a/openpype/modules/ftrack/tray/login_dialog.py b/openpype/modules/ftrack/tray/login_dialog.py index ca409ebcaa..ce91c6d012 100644 --- a/openpype/modules/ftrack/tray/login_dialog.py +++ b/openpype/modules/ftrack/tray/login_dialog.py @@ -14,11 +14,13 @@ class CredentialsDialog(QtWidgets.QDialog): login_changed = QtCore.Signal() logout_signal = QtCore.Signal() - def __init__(self, parent=None): + def __init__(self, module, parent=None): super(CredentialsDialog, self).__init__(parent) self.setWindowTitle("OpenPype - Ftrack Login") + self._module = module + self._login_server_thread = None self._is_logged = False self._in_advance_mode = False @@ -268,7 +270,7 @@ class CredentialsDialog(QtWidgets.QDialog): verification = credentials.check_credentials(username, api_key) if verification: credentials.save_credentials(username, api_key, False) - credentials.set_env(username, api_key) + self._module.set_credentials_to_env(username, api_key) self.set_credentials(username, api_key) self.login_changed.emit() return verification From 6339ed1b308eed1820f6a33935f674e554e3b244 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Thu, 8 Apr 2021 11:30:21 +0200 Subject: [PATCH 20/57] update packages to include coolname --- poetry.lock | 322 +++++++++++++++++++++++++------------------------ pyproject.toml | 1 + 2 files changed, 167 insertions(+), 156 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6695a7bcca..767aeee500 100644 --- a/poetry.lock +++ b/poetry.lock @@ -80,7 +80,7 @@ python-dateutil = ">=2.7.0" [[package]] name = "astroid" -version = "2.5.1" +version = "2.5.2" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false @@ -123,14 +123,14 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (> [[package]] name = "autopep8" -version = "1.5.5" +version = "1.5.6" description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" category = "dev" optional = false python-versions = "*" [package.dependencies] -pycodestyle = ">=2.6.0" +pycodestyle = ">=2.7.0" toml = "*" [[package]] @@ -232,6 +232,14 @@ python-versions = "*" [package.extras] test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] +[[package]] +name = "coolname" +version = "1.1.0" +description = "Random name and slug generator" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "coverage" version = "5.5" @@ -245,7 +253,7 @@ toml = ["toml"] [[package]] name = "cryptography" -version = "3.4.6" +version = "3.4.7" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" optional = false @@ -290,7 +298,7 @@ trio = ["trio (>=0.14.0)", "sniffio (>=1.1)"] [[package]] name = "docutils" -version = "0.16" +version = "0.17" description = "Docutils -- Python Documentation Utilities" category = "dev" optional = false @@ -306,17 +314,17 @@ python-versions = "*" [[package]] name = "flake8" -version = "3.8.4" +version = "3.9.0" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [package.dependencies] importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.6.0a1,<2.7.0" -pyflakes = ">=2.2.0,<2.3.0" +pycodestyle = ">=2.7.0,<2.8.0" +pyflakes = ">=2.3.0,<2.4.0" [[package]] name = "ftrack-python-api" @@ -346,7 +354,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "google-api-core" -version = "1.26.1" +version = "1.26.3" description = "Google API client core library" category = "main" optional = false @@ -384,7 +392,7 @@ uritemplate = ">=3.0.0,<4dev" [[package]] name = "google-auth" -version = "1.27.1" +version = "1.28.0" description = "Google Authentication Library" category = "main" optional = false @@ -429,7 +437,7 @@ grpc = ["grpcio (>=1.0.0)"] [[package]] name = "httplib2" -version = "0.19.0" +version = "0.19.1" description = "A comprehensive HTTP client library." category = "main" optional = false @@ -456,7 +464,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "importlib-metadata" -version = "3.7.2" +version = "3.10.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -468,7 +476,7 @@ zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" @@ -480,7 +488,7 @@ python-versions = "*" [[package]] name = "isort" -version = "5.7.0" +version = "5.8.0" description = "A Python utility / library to sort Python imports." category = "dev" optional = false @@ -579,11 +587,11 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt [[package]] name = "lazy-object-proxy" -version = "1.5.2" +version = "1.6.0" description = "A fast and thorough lazy object proxy." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] name = "log4mongo" @@ -653,7 +661,7 @@ pyparsing = ">=2.0.2" [[package]] name = "parso" -version = "0.8.1" +version = "0.8.2" description = "A Python Parser" category = "dev" optional = false @@ -676,7 +684,7 @@ six = "*" [[package]] name = "pillow" -version = "8.1.2" +version = "8.2.0" description = "Python Imaging Library (Fork)" category = "main" optional = false @@ -698,7 +706,7 @@ dev = ["pre-commit", "tox"] [[package]] name = "protobuf" -version = "3.15.6" +version = "3.15.7" description = "Protocol Buffers" category = "main" optional = false @@ -752,7 +760,7 @@ python-versions = "*" [[package]] name = "pycodestyle" -version = "2.6.0" +version = "2.7.0" description = "Python style guide checker" category = "dev" optional = false @@ -780,7 +788,7 @@ snowballstemmer = "*" [[package]] name = "pyflakes" -version = "2.2.0" +version = "2.3.1" description = "passive checker of Python programs" category = "dev" optional = false @@ -796,14 +804,14 @@ python-versions = ">=3.5" [[package]] name = "pylint" -version = "2.7.2" +version = "2.7.4" description = "python code static checker" category = "dev" optional = false python-versions = "~=3.6" [package.dependencies] -astroid = ">=2.5.1,<2.6" +astroid = ">=2.5.2,<2.7" colorama = {version = "*", markers = "sys_platform == \"win32\""} isort = ">=4.2.5,<6" mccabe = ">=0.6,<0.7" @@ -921,7 +929,7 @@ python-versions = ">=3.5" [[package]] name = "pytest" -version = "6.2.2" +version = "6.2.3" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -1112,7 +1120,7 @@ python-versions = "*" [[package]] name = "sphinx" -version = "3.5.2" +version = "3.5.3" description = "Python documentation generator" category = "dev" optional = false @@ -1271,7 +1279,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tqdm" -version = "4.59.0" +version = "4.60.0" description = "Fast, Extensible Progress Meter" category = "dev" optional = false @@ -1308,16 +1316,16 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "urllib3" -version = "1.26.3" +version = "1.26.4" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] -brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotlipy (>=0.6.0)"] [[package]] name = "wcwidth" @@ -1348,7 +1356,7 @@ python-versions = "*" [[package]] name = "wsrpc-aiohttp" -version = "3.1.1" +version = "3.1.2" description = "WSRPC is the RPC over WebSocket for aiohttp" category = "main" optional = false @@ -1391,7 +1399,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt [metadata] lock-version = "1.1" python-versions = "3.7.*" -content-hash = "4905515073ad2bf2a8517d513d68e48669b6a829f24e540b2dd60bc70cbea26b" +content-hash = "a8c9915ce3096b74b9328a632911a759780844d368fa1d6d0fbd7c5d7d4536cf" [metadata.files] acre = [] @@ -1455,8 +1463,8 @@ arrow = [ {file = "arrow-0.17.0.tar.gz", hash = "sha256:ff08d10cda1d36c68657d6ad20d74fbea493d980f8b2d45344e00d6ed2bf6ed4"}, ] astroid = [ - {file = "astroid-2.5.1-py3-none-any.whl", hash = "sha256:21d735aab248253531bb0f1e1e6d068f0ee23533e18ae8a6171ff892b98297cf"}, - {file = "astroid-2.5.1.tar.gz", hash = "sha256:cfc35498ee64017be059ceffab0a25bedf7548ab76f2bea691c5565896e7128d"}, + {file = "astroid-2.5.2-py3-none-any.whl", hash = "sha256:cd80bf957c49765dce6d92c43163ff9d2abc43132ce64d4b1b47717c6d2522df"}, + {file = "astroid-2.5.2.tar.gz", hash = "sha256:6b0ed1af831570e500e2437625979eaa3b36011f66ddfc4ce930128610258ca9"}, ] async-timeout = [ {file = "async-timeout-3.0.1.tar.gz", hash = "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f"}, @@ -1471,8 +1479,8 @@ attrs = [ {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, ] autopep8 = [ - {file = "autopep8-1.5.5-py2.py3-none-any.whl", hash = "sha256:9e136c472c475f4ee4978b51a88a494bfcd4e3ed17950a44a988d9e434837bea"}, - {file = "autopep8-1.5.5.tar.gz", hash = "sha256:cae4bc0fb616408191af41d062d7ec7ef8679c7f27b068875ca3a9e2878d5443"}, + {file = "autopep8-1.5.6-py2.py3-none-any.whl", hash = "sha256:f01b06a6808bc31698db907761e5890eb2295e287af53f6693b39ce55454034a"}, + {file = "autopep8-1.5.6.tar.gz", hash = "sha256:5454e6e9a3d02aae38f866eec0d9a7de4ab9f93c10a273fb0340f3d6d09f7514"}, ] babel = [ {file = "Babel-2.9.0-py2.py3-none-any.whl", hash = "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5"}, @@ -1549,6 +1557,10 @@ commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] +coolname = [ + {file = "coolname-1.1.0-py2.py3-none-any.whl", hash = "sha256:e6a83a0ac88640f4f3d2070438dbe112fe80cfebc119c93bd402976ec84c0978"}, + {file = "coolname-1.1.0.tar.gz", hash = "sha256:410fe6ea9999bf96f2856ef0c726d5f38782bbefb7bb1aca0e91e0dc98ed09e3"}, +] coverage = [ {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, @@ -1604,18 +1616,18 @@ coverage = [ {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, ] cryptography = [ - {file = "cryptography-3.4.6-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:57ad77d32917bc55299b16d3b996ffa42a1c73c6cfa829b14043c561288d2799"}, - {file = "cryptography-3.4.6-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:4169a27b818de4a1860720108b55a2801f32b6ae79e7f99c00d79f2a2822eeb7"}, - {file = "cryptography-3.4.6-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:93cfe5b7ff006de13e1e89830810ecbd014791b042cbe5eec253be11ac2b28f3"}, - {file = "cryptography-3.4.6-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:5ecf2bcb34d17415e89b546dbb44e73080f747e504273e4d4987630493cded1b"}, - {file = "cryptography-3.4.6-cp36-abi3-manylinux2014_x86_64.whl", hash = "sha256:fec7fb46b10da10d9e1d078d1ff8ed9e05ae14f431fdbd11145edd0550b9a964"}, - {file = "cryptography-3.4.6-cp36-abi3-win32.whl", hash = "sha256:df186fcbf86dc1ce56305becb8434e4b6b7504bc724b71ad7a3239e0c9d14ef2"}, - {file = "cryptography-3.4.6-cp36-abi3-win_amd64.whl", hash = "sha256:66b57a9ca4b3221d51b237094b0303843b914b7d5afd4349970bb26518e350b0"}, - {file = "cryptography-3.4.6-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:066bc53f052dfeda2f2d7c195cf16fb3e5ff13e1b6b7415b468514b40b381a5b"}, - {file = "cryptography-3.4.6-pp36-pypy36_pp73-manylinux2014_x86_64.whl", hash = "sha256:600cf9bfe75e96d965509a4c0b2b183f74a4fa6f5331dcb40fb7b77b7c2484df"}, - {file = "cryptography-3.4.6-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:0923ba600d00718d63a3976f23cab19aef10c1765038945628cd9be047ad0336"}, - {file = "cryptography-3.4.6-pp37-pypy37_pp73-manylinux2014_x86_64.whl", hash = "sha256:9e98b452132963678e3ac6c73f7010fe53adf72209a32854d55690acac3f6724"}, - {file = "cryptography-3.4.6.tar.gz", hash = "sha256:2d32223e5b0ee02943f32b19245b61a62db83a882f0e76cc564e1cec60d48f87"}, + {file = "cryptography-3.4.7-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1"}, + {file = "cryptography-3.4.7-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250"}, + {file = "cryptography-3.4.7-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2"}, + {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6"}, + {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_x86_64.whl", hash = "sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959"}, + {file = "cryptography-3.4.7-cp36-abi3-win32.whl", hash = "sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d"}, + {file = "cryptography-3.4.7-cp36-abi3-win_amd64.whl", hash = "sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca"}, + {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873"}, + {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2014_x86_64.whl", hash = "sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d"}, + {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177"}, + {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2014_x86_64.whl", hash = "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"}, + {file = "cryptography-3.4.7.tar.gz", hash = "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713"}, ] cx-freeze = [ {file = "cx_Freeze-6.5.3-cp36-cp36m-win32.whl", hash = "sha256:0a1babae574546b622303da53e1a9829aa3a7e53e62b41eb260250220f83164b"}, @@ -1633,15 +1645,15 @@ dnspython = [ {file = "dnspython-2.1.0.zip", hash = "sha256:e4a87f0b573201a0f3727fa18a516b055fd1107e0e5477cded4a2de497df1dd4"}, ] docutils = [ - {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"}, - {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, + {file = "docutils-0.17-py2.py3-none-any.whl", hash = "sha256:a71042bb7207c03d5647f280427f14bfbd1a65c9eb84f4b341d85fafb6bb4bdf"}, + {file = "docutils-0.17.tar.gz", hash = "sha256:e2ffeea817964356ba4470efba7c2f42b6b0de0b04e66378507e3e2504bbff4c"}, ] evdev = [ {file = "evdev-1.4.0.tar.gz", hash = "sha256:8782740eb1a86b187334c07feb5127d3faa0b236e113206dfe3ae8f77fb1aaf1"}, ] flake8 = [ - {file = "flake8-3.8.4-py2.py3-none-any.whl", hash = "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839"}, - {file = "flake8-3.8.4.tar.gz", hash = "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b"}, + {file = "flake8-3.9.0-py2.py3-none-any.whl", hash = "sha256:12d05ab02614b6aee8df7c36b97d1a3b2372761222b19b58621355e82acddcff"}, + {file = "flake8-3.9.0.tar.gz", hash = "sha256:78873e372b12b093da7b5e5ed302e8ad9e988b38b063b61ad937f26ca58fc5f0"}, ] ftrack-python-api = [ {file = "ftrack-python-api-2.0.0.tar.gz", hash = "sha256:dd6f02c31daf5a10078196dc9eac4671e4297c762fbbf4df98de668ac12281d9"}, @@ -1651,16 +1663,16 @@ future = [ {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, ] google-api-core = [ - {file = "google-api-core-1.26.1.tar.gz", hash = "sha256:23b0df512c4cc8729793f8992edb350e3211f5fd0ec007afb1599864b421beef"}, - {file = "google_api_core-1.26.1-py2.py3-none-any.whl", hash = "sha256:c383206f0f87545d3e658c4f8dc3b18a8457610fdbd791a15757c5b42d1e0e7f"}, + {file = "google-api-core-1.26.3.tar.gz", hash = "sha256:b914345c7ea23861162693a27703bab804a55504f7e6e9abcaff174d80df32ac"}, + {file = "google_api_core-1.26.3-py2.py3-none-any.whl", hash = "sha256:099762d4b4018cd536bcf85136bf337957da438807572db52f21dc61251be089"}, ] google-api-python-client = [ {file = "google-api-python-client-1.12.8.tar.gz", hash = "sha256:f3b9684442eec2cfe9f9bb48e796ef919456b82142c7528c5fd527e5224f08bb"}, {file = "google_api_python_client-1.12.8-py2.py3-none-any.whl", hash = "sha256:3c4c4ca46b5c21196bec7ee93453443e477d82cbfa79234d1ce0645f81170eaf"}, ] google-auth = [ - {file = "google-auth-1.27.1.tar.gz", hash = "sha256:d8958af6968e4ecd599f82357ebcfeb126f826ed0656126ad68416f810f7531e"}, - {file = "google_auth-1.27.1-py2.py3-none-any.whl", hash = "sha256:63a5636d7eacfe6ef5b7e36e112b3149fa1c5b5ad77dd6df54910459bcd6b89f"}, + {file = "google-auth-1.28.0.tar.gz", hash = "sha256:9bd436d19ab047001a1340720d2b629eb96dd503258c524921ec2af3ee88a80e"}, + {file = "google_auth-1.28.0-py2.py3-none-any.whl", hash = "sha256:dcaba3aa9d4e0e96fd945bf25a86b6f878fcb05770b67adbeb50a63ca4d28a5e"}, ] google-auth-httplib2 = [ {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, @@ -1671,8 +1683,8 @@ googleapis-common-protos = [ {file = "googleapis_common_protos-1.53.0-py2.py3-none-any.whl", hash = "sha256:f6d561ab8fb16b30020b940e2dd01cd80082f4762fa9f3ee670f4419b4b8dbd0"}, ] httplib2 = [ - {file = "httplib2-0.19.0-py3-none-any.whl", hash = "sha256:749c32603f9bf16c1277f59531d502e8f1c2ca19901ae653b49c4ed698f0820e"}, - {file = "httplib2-0.19.0.tar.gz", hash = "sha256:e0d428dad43c72dbce7d163b7753ffc7a39c097e6788ef10f4198db69b92f08e"}, + {file = "httplib2-0.19.1-py3-none-any.whl", hash = "sha256:2ad195faf9faf079723f6714926e9a9061f694d07724b846658ce08d40f522b4"}, + {file = "httplib2-0.19.1.tar.gz", hash = "sha256:0b12617eeca7433d4c396a100eaecfa4b08ee99aa881e6df6e257a7aad5d533d"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, @@ -1683,16 +1695,16 @@ imagesize = [ {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"}, ] importlib-metadata = [ - {file = "importlib_metadata-3.7.2-py3-none-any.whl", hash = "sha256:407d13f55dc6f2a844e62325d18ad7019a436c4bfcaee34cda35f2be6e7c3e34"}, - {file = "importlib_metadata-3.7.2.tar.gz", hash = "sha256:18d5ff601069f98d5d605b6a4b50c18a34811d655c55548adc833e687289acde"}, + {file = "importlib_metadata-3.10.0-py3-none-any.whl", hash = "sha256:d2d46ef77ffc85cbf7dac7e81dd663fde71c45326131bea8033b9bad42268ebe"}, + {file = "importlib_metadata-3.10.0.tar.gz", hash = "sha256:c9db46394197244adf2f0b08ec5bc3cf16757e9590b02af1fca085c16c0d600a"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] isort = [ - {file = "isort-5.7.0-py3-none-any.whl", hash = "sha256:fff4f0c04e1825522ce6949973e83110a6e907750cd92d128b0d14aaaadbffdc"}, - {file = "isort-5.7.0.tar.gz", hash = "sha256:c729845434366216d320e936b8ad6f9d681aab72dc7cbc2d51bedc3582f3ad1e"}, + {file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"}, + {file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"}, ] jedi = [ {file = "jedi-0.13.3-py2.py3-none-any.whl", hash = "sha256:2c6bcd9545c7d6440951b12b44d373479bf18123a401a52025cf98563fbd826c"}, @@ -1719,30 +1731,28 @@ keyring = [ {file = "keyring-22.4.0.tar.gz", hash = "sha256:d981e02d134cc3d636a716fbc3ca967bc9609bae5dc21b0063e4409355993ddf"}, ] lazy-object-proxy = [ - {file = "lazy-object-proxy-1.5.2.tar.gz", hash = "sha256:5944a9b95e97de1980c65f03b79b356f30a43de48682b8bdd90aa5089f0ec1f4"}, - {file = "lazy_object_proxy-1.5.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:e960e8be509e8d6d618300a6c189555c24efde63e85acaf0b14b2cd1ac743315"}, - {file = "lazy_object_proxy-1.5.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:522b7c94b524389f4a4094c4bf04c2b02228454ddd17c1a9b2801fac1d754871"}, - {file = "lazy_object_proxy-1.5.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:3782931963dc89e0e9a0ae4348b44762e868ea280e4f8c233b537852a8996ab9"}, - {file = "lazy_object_proxy-1.5.2-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:429c4d1862f3fc37cd56304d880f2eae5bd0da83bdef889f3bd66458aac49128"}, - {file = "lazy_object_proxy-1.5.2-cp35-cp35m-win32.whl", hash = "sha256:cd1bdace1a8762534e9a36c073cd54e97d517a17d69a17985961265be6d22847"}, - {file = "lazy_object_proxy-1.5.2-cp35-cp35m-win_amd64.whl", hash = "sha256:ddbdcd10eb999d7ab292677f588b658372aadb9a52790f82484a37127a390108"}, - {file = "lazy_object_proxy-1.5.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ecb5dd5990cec6e7f5c9c1124a37cb2c710c6d69b0c1a5c4aa4b35eba0ada068"}, - {file = "lazy_object_proxy-1.5.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b6577f15d5516d7d209c1a8cde23062c0f10625f19e8dc9fb59268859778d7d7"}, - {file = "lazy_object_proxy-1.5.2-cp36-cp36m-win32.whl", hash = "sha256:c8fe2d6ff0ff583784039d0255ea7da076efd08507f2be6f68583b0da32e3afb"}, - {file = "lazy_object_proxy-1.5.2-cp36-cp36m-win_amd64.whl", hash = "sha256:fa5b2dee0e231fa4ad117be114251bdfe6afe39213bd629d43deb117b6a6c40a"}, - {file = "lazy_object_proxy-1.5.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1d33d6f789697f401b75ce08e73b1de567b947740f768376631079290118ad39"}, - {file = "lazy_object_proxy-1.5.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:57fb5c5504ddd45ed420b5b6461a78f58cbb0c1b0cbd9cd5a43ad30a4a3ee4d0"}, - {file = "lazy_object_proxy-1.5.2-cp37-cp37m-win32.whl", hash = "sha256:e7273c64bccfd9310e9601b8f4511d84730239516bada26a0c9846c9697617ef"}, - {file = "lazy_object_proxy-1.5.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f4e5e68b7af950ed7fdb594b3f19a0014a3ace0fedb86acb896e140ffb24302"}, - {file = "lazy_object_proxy-1.5.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cadfa2c2cf54d35d13dc8d231253b7985b97d629ab9ca6e7d672c35539d38163"}, - {file = "lazy_object_proxy-1.5.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e7428977763150b4cf83255625a80a23dfdc94d43be7791ce90799d446b4e26f"}, - {file = "lazy_object_proxy-1.5.2-cp38-cp38-win32.whl", hash = "sha256:2f2de8f8ac0be3e40d17730e0600619d35c78c13a099ea91ef7fb4ad944ce694"}, - {file = "lazy_object_proxy-1.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:38c3865bd220bd983fcaa9aa11462619e84a71233bafd9c880f7b1cb753ca7fa"}, - {file = "lazy_object_proxy-1.5.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:8a44e9901c0555f95ac401377032f6e6af66d8fc1fbfad77a7a8b1a826e0b93c"}, - {file = "lazy_object_proxy-1.5.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fa7fb7973c622b9e725bee1db569d2c2ee64d2f9a089201c5e8185d482c7352d"}, - {file = "lazy_object_proxy-1.5.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:71a1ef23f22fa8437974b2d60fedb947c99a957ad625f83f43fd3de70f77f458"}, - {file = "lazy_object_proxy-1.5.2-cp39-cp39-win32.whl", hash = "sha256:ef3f5e288aa57b73b034ce9c1f1ac753d968f9069cd0742d1d69c698a0167166"}, - {file = "lazy_object_proxy-1.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:37d9c34b96cca6787fe014aeb651217944a967a5b165e2cacb6b858d2997ab84"}, + {file = "lazy-object-proxy-1.6.0.tar.gz", hash = "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726"}, + {file = "lazy_object_proxy-1.6.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b"}, + {file = "lazy_object_proxy-1.6.0-cp27-cp27m-win32.whl", hash = "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e"}, + {file = "lazy_object_proxy-1.6.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93"}, + {file = "lazy_object_proxy-1.6.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741"}, + {file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587"}, + {file = "lazy_object_proxy-1.6.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4"}, + {file = "lazy_object_proxy-1.6.0-cp36-cp36m-win32.whl", hash = "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f"}, + {file = "lazy_object_proxy-1.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3"}, + {file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981"}, + {file = "lazy_object_proxy-1.6.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2"}, + {file = "lazy_object_proxy-1.6.0-cp37-cp37m-win32.whl", hash = "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd"}, + {file = "lazy_object_proxy-1.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837"}, + {file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653"}, + {file = "lazy_object_proxy-1.6.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3"}, + {file = "lazy_object_proxy-1.6.0-cp38-cp38-win32.whl", hash = "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8"}, + {file = "lazy_object_proxy-1.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61"}, + {file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"}, ] log4mongo = [ {file = "log4mongo-1.7.0.tar.gz", hash = "sha256:dc374617206162a0b14167fbb5feac01dbef587539a235dadba6200362984a68"}, @@ -1850,73 +1860,73 @@ packaging = [ {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] parso = [ - {file = "parso-0.8.1-py2.py3-none-any.whl", hash = "sha256:15b00182f472319383252c18d5913b69269590616c947747bc50bf4ac768f410"}, - {file = "parso-0.8.1.tar.gz", hash = "sha256:8519430ad07087d4c997fda3a7918f7cfa27cb58972a8c89c2a0295a1c940e9e"}, + {file = "parso-0.8.2-py2.py3-none-any.whl", hash = "sha256:a8c4922db71e4fdb90e0d0bc6e50f9b273d3397925e5e60a717e719201778d22"}, + {file = "parso-0.8.2.tar.gz", hash = "sha256:12b83492c6239ce32ff5eed6d3639d6a536170723c6f3f1506869f1ace413398"}, ] pathlib2 = [ {file = "pathlib2-2.3.5-py2.py3-none-any.whl", hash = "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db"}, {file = "pathlib2-2.3.5.tar.gz", hash = "sha256:6cd9a47b597b37cc57de1c05e56fb1a1c9cc9fab04fe78c29acd090418529868"}, ] pillow = [ - {file = "Pillow-8.1.2-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:5cf03b9534aca63b192856aa601c68d0764810857786ea5da652581f3a44c2b0"}, - {file = "Pillow-8.1.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f91b50ad88048d795c0ad004abbe1390aa1882073b1dca10bfd55d0b8cf18ec5"}, - {file = "Pillow-8.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5762ebb4436f46b566fc6351d67a9b5386b5e5de4e58fdaa18a1c83e0e20f1a8"}, - {file = "Pillow-8.1.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e2cd8ac157c1e5ae88b6dd790648ee5d2777e76f1e5c7d184eaddb2938594f34"}, - {file = "Pillow-8.1.2-cp36-cp36m-win32.whl", hash = "sha256:72027ebf682abc9bafd93b43edc44279f641e8996fb2945104471419113cfc71"}, - {file = "Pillow-8.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d1d6bca39bb6dd94fba23cdb3eeaea5e30c7717c5343004d900e2a63b132c341"}, - {file = "Pillow-8.1.2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:90882c6f084ef68b71bba190209a734bf90abb82ab5e8f64444c71d5974008c6"}, - {file = "Pillow-8.1.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:89e4c757a91b8c55d97c91fa09c69b3677c227b942fa749e9a66eef602f59c28"}, - {file = "Pillow-8.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8c4e32218c764bc27fe49b7328195579581aa419920edcc321c4cb877c65258d"}, - {file = "Pillow-8.1.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:a01da2c266d9868c4f91a9c6faf47a251f23b9a862dce81d2ff583135206f5be"}, - {file = "Pillow-8.1.2-cp37-cp37m-win32.whl", hash = "sha256:30d33a1a6400132e6f521640dd3f64578ac9bfb79a619416d7e8802b4ce1dd55"}, - {file = "Pillow-8.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:71b01ee69e7df527439d7752a2ce8fb89e19a32df484a308eca3e81f673d3a03"}, - {file = "Pillow-8.1.2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:5a2d957eb4aba9d48170b8fe6538ec1fbc2119ffe6373782c03d8acad3323f2e"}, - {file = "Pillow-8.1.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:87f42c976f91ca2fc21a3293e25bd3cd895918597db1b95b93cbd949f7d019ce"}, - {file = "Pillow-8.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:15306d71a1e96d7e271fd2a0737038b5a92ca2978d2e38b6ced7966583e3d5af"}, - {file = "Pillow-8.1.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:71f31ee4df3d5e0b366dd362007740106d3210fb6a56ec4b581a5324ba254f06"}, - {file = "Pillow-8.1.2-cp38-cp38-win32.whl", hash = "sha256:98afcac3205d31ab6a10c5006b0cf040d0026a68ec051edd3517b776c1d78b09"}, - {file = "Pillow-8.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:328240f7dddf77783e72d5ed79899a6b48bc6681f8d1f6001f55933cb4905060"}, - {file = "Pillow-8.1.2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:bead24c0ae3f1f6afcb915a057943ccf65fc755d11a1410a909c1fefb6c06ad1"}, - {file = "Pillow-8.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81b3716cc9744ffdf76b39afb6247eae754186838cedad0b0ac63b2571253fe6"}, - {file = "Pillow-8.1.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:63cd413ac52ee3f67057223d363f4f82ce966e64906aea046daf46695e3c8238"}, - {file = "Pillow-8.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:8565355a29655b28fdc2c666fd9a3890fe5edc6639d128814fafecfae2d70910"}, - {file = "Pillow-8.1.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1940fc4d361f9cc7e558d6f56ff38d7351b53052fd7911f4b60cd7bc091ea3b1"}, - {file = "Pillow-8.1.2-cp39-cp39-win32.whl", hash = "sha256:46c2bcf8e1e75d154e78417b3e3c64e96def738c2a25435e74909e127a8cba5e"}, - {file = "Pillow-8.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:aeab4cd016e11e7aa5cfc49dcff8e51561fa64818a0be86efa82c7038e9369d0"}, - {file = "Pillow-8.1.2-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:74cd9aa648ed6dd25e572453eb09b08817a1e3d9f8d1bd4d8403d99e42ea790b"}, - {file = "Pillow-8.1.2-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:e5739ae63636a52b706a0facec77b2b58e485637e1638202556156e424a02dc2"}, - {file = "Pillow-8.1.2-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:903293320efe2466c1ab3509a33d6b866dc850cfd0c5d9cc92632014cec185fb"}, - {file = "Pillow-8.1.2-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:5daba2b40782c1c5157a788ec4454067c6616f5a0c1b70e26ac326a880c2d328"}, - {file = "Pillow-8.1.2-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:1f93f2fe211f1ef75e6f589327f4d4f8545d5c8e826231b042b483d8383e8a7c"}, - {file = "Pillow-8.1.2-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:6efac40344d8f668b6c4533ae02a48d52fd852ef0654cc6f19f6ac146399c733"}, - {file = "Pillow-8.1.2-pp37-pypy37_pp73-win32.whl", hash = "sha256:f36c3ff63d6fc509ce599a2f5b0d0732189eed653420e7294c039d342c6e204a"}, - {file = "Pillow-8.1.2.tar.gz", hash = "sha256:b07c660e014852d98a00a91adfbe25033898a9d90a8f39beb2437d22a203fc44"}, + {file = "Pillow-8.2.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:dc38f57d8f20f06dd7c3161c59ca2c86893632623f33a42d592f097b00f720a9"}, + {file = "Pillow-8.2.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a013cbe25d20c2e0c4e85a9daf438f85121a4d0344ddc76e33fd7e3965d9af4b"}, + {file = "Pillow-8.2.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8bb1e155a74e1bfbacd84555ea62fa21c58e0b4e7e6b20e4447b8d07990ac78b"}, + {file = "Pillow-8.2.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c5236606e8570542ed424849f7852a0ff0bce2c4c8d0ba05cc202a5a9c97dee9"}, + {file = "Pillow-8.2.0-cp36-cp36m-win32.whl", hash = "sha256:12e5e7471f9b637762453da74e390e56cc43e486a88289995c1f4c1dc0bfe727"}, + {file = "Pillow-8.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5afe6b237a0b81bd54b53f835a153770802f164c5570bab5e005aad693dab87f"}, + {file = "Pillow-8.2.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:cb7a09e173903541fa888ba010c345893cd9fc1b5891aaf060f6ca77b6a3722d"}, + {file = "Pillow-8.2.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0d19d70ee7c2ba97631bae1e7d4725cdb2ecf238178096e8c82ee481e189168a"}, + {file = "Pillow-8.2.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:083781abd261bdabf090ad07bb69f8f5599943ddb539d64497ed021b2a67e5a9"}, + {file = "Pillow-8.2.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c6b39294464b03457f9064e98c124e09008b35a62e3189d3513e5148611c9388"}, + {file = "Pillow-8.2.0-cp37-cp37m-win32.whl", hash = "sha256:01425106e4e8cee195a411f729cff2a7d61813b0b11737c12bd5991f5f14bcd5"}, + {file = "Pillow-8.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3b570f84a6161cf8865c4e08adf629441f56e32f180f7aa4ccbd2e0a5a02cba2"}, + {file = "Pillow-8.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:031a6c88c77d08aab84fecc05c3cde8414cd6f8406f4d2b16fed1e97634cc8a4"}, + {file = "Pillow-8.2.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:66cc56579fd91f517290ab02c51e3a80f581aba45fd924fcdee01fa06e635812"}, + {file = "Pillow-8.2.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6c32cc3145928c4305d142ebec682419a6c0a8ce9e33db900027ddca1ec39178"}, + {file = "Pillow-8.2.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:624b977355cde8b065f6d51b98497d6cd5fbdd4f36405f7a8790e3376125e2bb"}, + {file = "Pillow-8.2.0-cp38-cp38-win32.whl", hash = "sha256:5cbf3e3b1014dddc45496e8cf38b9f099c95a326275885199f427825c6522232"}, + {file = "Pillow-8.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:463822e2f0d81459e113372a168f2ff59723e78528f91f0bd25680ac185cf797"}, + {file = "Pillow-8.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:95d5ef984eff897850f3a83883363da64aae1000e79cb3c321915468e8c6add5"}, + {file = "Pillow-8.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b91c36492a4bbb1ee855b7d16fe51379e5f96b85692dc8210831fbb24c43e484"}, + {file = "Pillow-8.2.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d68cb92c408261f806b15923834203f024110a2e2872ecb0bd2a110f89d3c602"}, + {file = "Pillow-8.2.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f217c3954ce5fd88303fc0c317af55d5e0204106d86dea17eb8205700d47dec2"}, + {file = "Pillow-8.2.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:5b70110acb39f3aff6b74cf09bb4169b167e2660dabc304c1e25b6555fa781ef"}, + {file = "Pillow-8.2.0-cp39-cp39-win32.whl", hash = "sha256:a7d5e9fad90eff8f6f6106d3b98b553a88b6f976e51fce287192a5d2d5363713"}, + {file = "Pillow-8.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:238c197fc275b475e87c1453b05b467d2d02c2915fdfdd4af126145ff2e4610c"}, + {file = "Pillow-8.2.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0e04d61f0064b545b989126197930807c86bcbd4534d39168f4aa5fda39bb8f9"}, + {file = "Pillow-8.2.0-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:63728564c1410d99e6d1ae8e3b810fe012bc440952168af0a2877e8ff5ab96b9"}, + {file = "Pillow-8.2.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:c03c07ed32c5324939b19e36ae5f75c660c81461e312a41aea30acdd46f93a7c"}, + {file = "Pillow-8.2.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:4d98abdd6b1e3bf1a1cbb14c3895226816e666749ac040c4e2554231068c639b"}, + {file = "Pillow-8.2.0-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:aac00e4bc94d1b7813fe882c28990c1bc2f9d0e1aa765a5f2b516e8a6a16a9e4"}, + {file = "Pillow-8.2.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:22fd0f42ad15dfdde6c581347eaa4adb9a6fc4b865f90b23378aa7914895e120"}, + {file = "Pillow-8.2.0-pp37-pypy37_pp73-win32.whl", hash = "sha256:e98eca29a05913e82177b3ba3d198b1728e164869c613d76d0de4bde6768a50e"}, + {file = "Pillow-8.2.0.tar.gz", hash = "sha256:a787ab10d7bb5494e5f76536ac460741788f1fbce851068d73a87ca7c35fc3e1"}, ] pluggy = [ {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] protobuf = [ - {file = "protobuf-3.15.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1771ef20e88759c4d81db213e89b7a1fc53937968e12af6603c658ee4bcbfa38"}, - {file = "protobuf-3.15.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1a66261a402d05c8ad8c1fde8631837307bf8d7e7740a4f3941fc3277c2e1528"}, - {file = "protobuf-3.15.6-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:eac23a3e56175b710f3da9a9e8e2aa571891fbec60e0c5a06db1c7b1613b5cfd"}, - {file = "protobuf-3.15.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ec220d90eda8bb7a7a1434a8aed4fe26d7e648c1a051c2885f3f5725b6aa71a"}, - {file = "protobuf-3.15.6-cp35-cp35m-win32.whl", hash = "sha256:88d8f21d1ac205eedb6dea943f8204ed08201b081dba2a966ab5612788b9bb1e"}, - {file = "protobuf-3.15.6-cp35-cp35m-win_amd64.whl", hash = "sha256:eaada29bbf087dea7d8bce4d1d604fc768749e8809e9c295922accd7c8fce4d5"}, - {file = "protobuf-3.15.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:256c0b2e338c1f3228d3280707606fe5531fde85ab9d704cde6fdeb55112531f"}, - {file = "protobuf-3.15.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:b9069e45b6e78412fba4a314ea38b4a478686060acf470d2b131b3a2c50484ec"}, - {file = "protobuf-3.15.6-cp36-cp36m-win32.whl", hash = "sha256:24f4697f57b8520c897a401b7f9a5ae45c369e22c572e305dfaf8053ecb49687"}, - {file = "protobuf-3.15.6-cp36-cp36m-win_amd64.whl", hash = "sha256:d9ed0955b794f1e5f367e27f8a8ff25501eabe34573f003f06639c366ca75f73"}, - {file = "protobuf-3.15.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:822ac7f87fc2fb9b24edd2db390538b60ef50256e421ca30d65250fad5a3d477"}, - {file = "protobuf-3.15.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:74ac159989e2b02d761188a2b6f4601ff5e494d9b9d863f5ad6e98e5e0c54328"}, - {file = "protobuf-3.15.6-cp37-cp37m-win32.whl", hash = "sha256:30fe4249a364576f9594180589c3f9c4771952014b5f77f0372923fc7bafbbe2"}, - {file = "protobuf-3.15.6-cp37-cp37m-win_amd64.whl", hash = "sha256:45a91fc6f9aa86d3effdeda6751882b02de628519ba06d7160daffde0c889ff8"}, - {file = "protobuf-3.15.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83c7c7534f050cb25383bb817159416601d1cc46c40bc5e851ec8bbddfc34a2f"}, - {file = "protobuf-3.15.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9ec20a6ded7d0888e767ad029dbb126e604e18db744ac0a428cf746e040ccecd"}, - {file = "protobuf-3.15.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0f2da2fcc4102b6c3b57f03c9d8d5e37c63f8bc74deaa6cb54e0cc4524a77247"}, - {file = "protobuf-3.15.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:70054ae1ce5dea7dec7357db931fcf487f40ea45b02cb719ee6af07eb1e906fb"}, - {file = "protobuf-3.15.6-py2.py3-none-any.whl", hash = "sha256:1655fc0ba7402560d749de13edbfca1ac45d1753d8f4e5292989f18f5a00c215"}, - {file = "protobuf-3.15.6.tar.gz", hash = "sha256:2b974519a2ae83aa1e31cff9018c70bbe0e303a46a598f982943c49ae1d4fcd3"}, + {file = "protobuf-3.15.7-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a14141d5c967362d2eedff8825d2b69cc36a5b3ed6b1f618557a04e58a3cf787"}, + {file = "protobuf-3.15.7-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d54d78f621852ec4fdd1484d1263ca04d4bf5ffdf7abffdbb939e444b6ff3385"}, + {file = "protobuf-3.15.7-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:462085acdb410b06335315fe7e63cb281a1902856e0f4657f341c283cedc1d56"}, + {file = "protobuf-3.15.7-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:849c92ce112e1ef648705c29ce044248e350f71d9d54a2026830623198f0bd38"}, + {file = "protobuf-3.15.7-cp35-cp35m-win32.whl", hash = "sha256:1f6083382f7714700deadf3014e921711e2f807de7f27e40c32b744701ae5b99"}, + {file = "protobuf-3.15.7-cp35-cp35m-win_amd64.whl", hash = "sha256:e17f60f00081adcb32068ee0bb51e418f6474acf83424244ff3512ffd2166385"}, + {file = "protobuf-3.15.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c75e563c6fb2ca5b8f21dd75c15659aa2c4a0025b9da3a7711ae661cd6a488d"}, + {file = "protobuf-3.15.7-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d939f41b4108350841c4790ebbadb61729e1363522fdb8434eb4e6f2065d0db1"}, + {file = "protobuf-3.15.7-cp36-cp36m-win32.whl", hash = "sha256:24f14c09d4c0a3641f1b0e9b552d026361de65b01686fdd3e5fdf8f9512cd79b"}, + {file = "protobuf-3.15.7-cp36-cp36m-win_amd64.whl", hash = "sha256:1247170191bcb2a8d978d11a58afe391004ec6c2184e4d961baf8102d43ff500"}, + {file = "protobuf-3.15.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:364cadaeec0756afdc099cbd88cb5659bd1bb7d547168d063abcb0272ccbb2f6"}, + {file = "protobuf-3.15.7-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0c3a6941b1e6e6e22d812a8e5c46bfe83082ea60d262a46f2cfb22d9b9fb17db"}, + {file = "protobuf-3.15.7-cp37-cp37m-win32.whl", hash = "sha256:eb5668f3f6a83b6603ca2e09be5b20de89521ea5914aabe032cce981e4129cc8"}, + {file = "protobuf-3.15.7-cp37-cp37m-win_amd64.whl", hash = "sha256:1001e671cf8476edce7fb72778358d026390649cc35a79d47b2a291684ccfbb2"}, + {file = "protobuf-3.15.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a5ba7dd6f97964655aa7b234c95d80886425a31b7010764f042cdeb985314d18"}, + {file = "protobuf-3.15.7-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:46674bd6fcf8c63b4b9869ba579685db67cf51ae966443dd6bd9a8fa00fcef62"}, + {file = "protobuf-3.15.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4c4399156fb27e3768313b7a59352c861a893252bda6fb9f3643beb3ebb7047e"}, + {file = "protobuf-3.15.7-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:85cd29faf056036167d87445d5a5059034c298881c044e71a73d3b61a4be1c23"}, + {file = "protobuf-3.15.7-py2.py3-none-any.whl", hash = "sha256:22054432b923c0086f9cf1e1c0c52d39bf3c6e31014ea42eec2dabc22ee26d78"}, + {file = "protobuf-3.15.7.tar.gz", hash = "sha256:2d03fc2591543cd2456d0b72230b50c4519546a8d379ac6fd3ecd84c6df61e5d"}, ] py = [ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, @@ -1960,8 +1970,8 @@ pyblish-base = [ {file = "pyblish_base-1.8.8-py2.py3-none-any.whl", hash = "sha256:67ea253a05d007ab4a175e44e778928ea7bdb0e9707573e1100417bbf0451a53"}, ] pycodestyle = [ - {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, - {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, + {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, + {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, ] pycparser = [ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, @@ -1973,16 +1983,16 @@ pydocstyle = [ {file = "pydocstyle-3.0.0.tar.gz", hash = "sha256:5741c85e408f9e0ddf873611085e819b809fca90b619f5fd7f34bd4959da3dd4"}, ] pyflakes = [ - {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, - {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] pygments = [ {file = "Pygments-2.8.1-py3-none-any.whl", hash = "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"}, {file = "Pygments-2.8.1.tar.gz", hash = "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94"}, ] pylint = [ - {file = "pylint-2.7.2-py3-none-any.whl", hash = "sha256:d09b0b07ba06bcdff463958f53f23df25e740ecd81895f7d2699ec04bbd8dc3b"}, - {file = "pylint-2.7.2.tar.gz", hash = "sha256:0e21d3b80b96740909d77206d741aa3ce0b06b41be375d92e1f3244a274c1f8a"}, + {file = "pylint-2.7.4-py3-none-any.whl", hash = "sha256:209d712ec870a0182df034ae19f347e725c1e615b2269519ab58a35b3fcbbe7a"}, + {file = "pylint-2.7.4.tar.gz", hash = "sha256:bd38914c7731cdc518634a8d3c5585951302b6e2b6de60fbb3f7a0220e21eeee"}, ] pymongo = [ {file = "pymongo-3.11.3-cp27-cp27m-macosx_10_14_intel.whl", hash = "sha256:4d959e929cec805c2bf391418b1121590b4e7d5cb00af7b1ba521443d45a0918"}, @@ -2123,8 +2133,8 @@ pyrsistent = [ {file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"}, ] pytest = [ - {file = "pytest-6.2.2-py3-none-any.whl", hash = "sha256:b574b57423e818210672e07ca1fa90aaf194a4f63f3ab909a2c67ebb22913839"}, - {file = "pytest-6.2.2.tar.gz", hash = "sha256:9d1edf9e7d0b84d72ea3dbcdfd22b35fb543a5e8f2a60092dd578936bf63d7f9"}, + {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, + {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, ] pytest-cov = [ {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, @@ -2198,8 +2208,8 @@ speedcopy = [ {file = "speedcopy-2.1.0.tar.gz", hash = "sha256:8bb1a6c735900b83901a7be84ba2175ed3887c13c6786f97dea48f2ea7d504c2"}, ] sphinx = [ - {file = "Sphinx-3.5.2-py3-none-any.whl", hash = "sha256:ef64a814576f46ec7de06adf11b433a0d6049be007fefe7fd0d183d28b581fac"}, - {file = "Sphinx-3.5.2.tar.gz", hash = "sha256:672cfcc24b6b69235c97c750cb190a44ecd72696b4452acaf75c2d9cc78ca5ff"}, + {file = "Sphinx-3.5.3-py3-none-any.whl", hash = "sha256:3f01732296465648da43dec8fb40dc451ba79eb3e2cc5c6d79005fd98197107d"}, + {file = "Sphinx-3.5.3.tar.gz", hash = "sha256:ce9c228456131bab09a3d7d10ae58474de562a6f79abb3dc811ae401cf8c1abc"}, ] sphinx-qt-documentation = [ {file = "sphinx_qt_documentation-0.3-py3-none-any.whl", hash = "sha256:bee247cb9e4fc03fc496d07adfdb943100e1103320c3e5e820e0cfa7c790d9b6"}, @@ -2245,8 +2255,8 @@ toml = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tqdm = [ - {file = "tqdm-4.59.0-py2.py3-none-any.whl", hash = "sha256:9fdf349068d047d4cfbe24862c425883af1db29bcddf4b0eeb2524f6fbdb23c7"}, - {file = "tqdm-4.59.0.tar.gz", hash = "sha256:d666ae29164da3e517fcf125e41d4fe96e5bb375cd87ff9763f6b38b5592fe33"}, + {file = "tqdm-4.60.0-py2.py3-none-any.whl", hash = "sha256:daec693491c52e9498632dfbe9ccfc4882a557f5fa08982db1b4d3adbe0887c3"}, + {file = "tqdm-4.60.0.tar.gz", hash = "sha256:ebdebdb95e3477ceea267decfc0784859aa3df3e27e22d23b83e9b272bf157ae"}, ] typed-ast = [ {file = "typed_ast-1.4.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70"}, @@ -2290,8 +2300,8 @@ uritemplate = [ {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, ] urllib3 = [ - {file = "urllib3-1.26.3-py2.py3-none-any.whl", hash = "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80"}, - {file = "urllib3-1.26.3.tar.gz", hash = "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"}, + {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, + {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, @@ -2305,8 +2315,8 @@ wrapt = [ {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, ] wsrpc-aiohttp = [ - {file = "wsrpc-aiohttp-3.1.1.tar.gz", hash = "sha256:a17e1d91624a437e759d4f276b73de1db2071b1681e992cade025e91d31b2a9f"}, - {file = "wsrpc_aiohttp-3.1.1-py3-none-any.whl", hash = "sha256:f3f1ee31aed5145a7fafe8d6c778b914b7e6ec131500395c9c85b0d8676f7302"}, + {file = "wsrpc-aiohttp-3.1.2.tar.gz", hash = "sha256:891164dfe06a8d8d846b485d04b1e56b2c397ff1b46ef0348e6f62bd8efb1693"}, + {file = "wsrpc_aiohttp-3.1.2-py3-none-any.whl", hash = "sha256:4ba64e02b12dcbc09d02544f35bceba49bd04cbc496db47aa8559ae4609ada8e"}, ] yarl = [ {file = "yarl-1.6.3-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:0355a701b3998dcd832d0dc47cc5dedf3874f966ac7f870e0f3a6788d802d434"}, diff --git a/pyproject.toml b/pyproject.toml index ec2d9c7e3b..6df6db5a18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ acre = { git = "https://github.com/pypeclub/acre.git" } opentimelineio = { version = "0.14.0.dev1", source = "openpype" } appdirs = "^1.4.3" blessed = "^1.17" # openpype terminal formatting +coolname = "*" clique = "1.5.*" Click = "^7" dnspython = "^2.1.0" From b742f3a9c56000d4d23693b05600267c9c5adb91 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 8 Apr 2021 12:42:53 +0200 Subject: [PATCH 21/57] credentials are not stored per user --- openpype/modules/ftrack/lib/credentials.py | 29 +++++----------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/openpype/modules/ftrack/lib/credentials.py b/openpype/modules/ftrack/lib/credentials.py index 3d9aa75e84..c2812083ba 100644 --- a/openpype/modules/ftrack/lib/credentials.py +++ b/openpype/modules/ftrack/lib/credentials.py @@ -17,8 +17,6 @@ CREDENTIALS_FOLDER = os.path.dirname(CREDENTIALS_PATH) if not os.path.isdir(CREDENTIALS_FOLDER): os.makedirs(CREDENTIALS_FOLDER) -USER_GETTER = None - def get_ftrack_hostname(ftrack_server=None): if not ftrack_server: @@ -30,13 +28,7 @@ def get_ftrack_hostname(ftrack_server=None): return urlparse(ftrack_server).hostname -def get_user(): - if USER_GETTER: - return USER_GETTER() - return getpass.getuser() - - -def get_credentials(ftrack_server=None, user=None): +def get_credentials(ftrack_server=None): credentials = {} if not os.path.exists(CREDENTIALS_PATH): with open(CREDENTIALS_PATH, "w") as file: @@ -48,28 +40,22 @@ def get_credentials(ftrack_server=None, user=None): content = file.read() hostname = get_ftrack_hostname(ftrack_server) - if not user: - user = get_user() content_json = json.loads(content or "{}") - credentials = content_json.get(hostname, {}).get(user) or {} + credentials = content_json.get(hostname) or {} return credentials -def save_credentials(ft_user, ft_api_key, ftrack_server=None, user=None): +def save_credentials(ft_user, ft_api_key, ftrack_server=None): hostname = get_ftrack_hostname(ftrack_server) - if not user: - user = get_user() with open(CREDENTIALS_PATH, "r") as file: content = file.read() content_json = json.loads(content or "{}") - if hostname not in content_json: - content_json[hostname] = {} - content_json[hostname][user] = { + content_json[hostname] = { "username": ft_user, "api_key": ft_api_key } @@ -84,7 +70,7 @@ def save_credentials(ft_user, ft_api_key, ftrack_server=None, user=None): file.write(json.dumps(content_json, indent=4)) -def clear_credentials(ft_user=None, ftrack_server=None, user=None): +def clear_credentials(ft_user=None, ftrack_server=None): if not ft_user: ft_user = os.environ.get("FTRACK_API_USER") @@ -92,9 +78,6 @@ def clear_credentials(ft_user=None, ftrack_server=None, user=None): return hostname = get_ftrack_hostname(ftrack_server) - if not user: - user = get_user() - with open(CREDENTIALS_PATH, "r") as file: content = file.read() @@ -102,7 +85,7 @@ def clear_credentials(ft_user=None, ftrack_server=None, user=None): if hostname not in content_json: content_json[hostname] = {} - content_json[hostname].pop(user, None) + content_json.pop(hostname, None) with open(CREDENTIALS_PATH, "w") as file: file.write(json.dumps(content_json)) From 5a7d19337cdc8efee1e60f6f59fb72c603206f41 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 8 Apr 2021 12:43:27 +0200 Subject: [PATCH 22/57] added helper function to get secure item key the same way --- openpype/modules/ftrack/lib/credentials.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/modules/ftrack/lib/credentials.py b/openpype/modules/ftrack/lib/credentials.py index c2812083ba..7a0d93f120 100644 --- a/openpype/modules/ftrack/lib/credentials.py +++ b/openpype/modules/ftrack/lib/credentials.py @@ -28,6 +28,11 @@ def get_ftrack_hostname(ftrack_server=None): return urlparse(ftrack_server).hostname +def _get_ftrack_secure_key(hostname): + """Secure item key for entered hostname.""" + return "/".join(("ftrack", hostname)) + + def get_credentials(ftrack_server=None): credentials = {} if not os.path.exists(CREDENTIALS_PATH): From 153160f85d1ae2a994462eabfdc0cb9f224f9ee6 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 8 Apr 2021 12:58:45 +0200 Subject: [PATCH 23/57] use OpenPypeRegistry to set, get and remove ftrack credentials --- openpype/modules/ftrack/lib/credentials.py | 94 +++++++--------------- 1 file changed, 27 insertions(+), 67 deletions(-) diff --git a/openpype/modules/ftrack/lib/credentials.py b/openpype/modules/ftrack/lib/credentials.py index 7a0d93f120..05a74c0875 100644 --- a/openpype/modules/ftrack/lib/credentials.py +++ b/openpype/modules/ftrack/lib/credentials.py @@ -1,21 +1,16 @@ import os -import json import ftrack_api -import appdirs -import getpass + try: from urllib.parse import urlparse except ImportError: from urlparse import urlparse -CONFIG_PATH = os.path.normpath(appdirs.user_data_dir("pype-app", "pype")) -CREDENTIALS_FILE_NAME = "ftrack_cred.json" -CREDENTIALS_PATH = os.path.join(CONFIG_PATH, CREDENTIALS_FILE_NAME) -CREDENTIALS_FOLDER = os.path.dirname(CREDENTIALS_PATH) +from openpype.lib import OpenPypeSettingsRegistry -if not os.path.isdir(CREDENTIALS_FOLDER): - os.makedirs(CREDENTIALS_FOLDER) +USERNAME_KEY = "username" +API_KEY_KEY = "api_key" def get_ftrack_hostname(ftrack_server=None): @@ -34,84 +29,49 @@ def _get_ftrack_secure_key(hostname): def get_credentials(ftrack_server=None): - credentials = {} - if not os.path.exists(CREDENTIALS_PATH): - with open(CREDENTIALS_PATH, "w") as file: - file.write(json.dumps(credentials)) - file.close() - return credentials - - with open(CREDENTIALS_PATH, "r") as file: - content = file.read() - hostname = get_ftrack_hostname(ftrack_server) + secure_key = _get_ftrack_secure_key(hostname) - content_json = json.loads(content or "{}") - credentials = content_json.get(hostname) or {} - - return credentials - - -def save_credentials(ft_user, ft_api_key, ftrack_server=None): - hostname = get_ftrack_hostname(ftrack_server) - - with open(CREDENTIALS_PATH, "r") as file: - content = file.read() - - content_json = json.loads(content or "{}") - - content_json[hostname] = { - "username": ft_user, - "api_key": ft_api_key + registry = OpenPypeSettingsRegistry(secure_key) + return { + USERNAME_KEY: registry.get_secure_item(USERNAME_KEY, None), + API_KEY_KEY: registry.get_secure_item(API_KEY_KEY, None) } - # Deprecated keys - if "username" in content_json: - content_json.pop("username") - if "apiKey" in content_json: - content_json.pop("apiKey") - - with open(CREDENTIALS_PATH, "w") as file: - file.write(json.dumps(content_json, indent=4)) - - -def clear_credentials(ft_user=None, ftrack_server=None): - if not ft_user: - ft_user = os.environ.get("FTRACK_API_USER") - - if not ft_user: - return +def save_credentials(username, api_key, ftrack_server=None): hostname = get_ftrack_hostname(ftrack_server) - with open(CREDENTIALS_PATH, "r") as file: - content = file.read() + secure_key = _get_ftrack_secure_key(hostname) - content_json = json.loads(content or "{}") - if hostname not in content_json: - content_json[hostname] = {} - - content_json.pop(hostname, None) - - with open(CREDENTIALS_PATH, "w") as file: - file.write(json.dumps(content_json)) + registry = OpenPypeSettingsRegistry(secure_key) + registry.set_secure_item(USERNAME_KEY, username) + registry.set_secure_item(API_KEY_KEY, api_key) -def check_credentials(ft_user, ft_api_key, ftrack_server=None): +def clear_credentials(ftrack_server=None): + hostname = get_ftrack_hostname(ftrack_server) + secure_key = _get_ftrack_secure_key(hostname) + + registry = OpenPypeSettingsRegistry(secure_key) + registry.delete_secure_item(USERNAME_KEY) + registry.delete_secure_item(API_KEY_KEY) + + +def check_credentials(username, api_key, ftrack_server=None): if not ftrack_server: ftrack_server = os.environ["FTRACK_SERVER"] - if not ft_user or not ft_api_key: + if not username or not api_key: return False try: session = ftrack_api.Session( server_url=ftrack_server, - api_key=ft_api_key, - api_user=ft_user + api_key=api_key, + api_user=username ) session.close() except Exception: return False - return True From 983b9264ec38c024fcfaab47b8b416a5c1b551f1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 8 Apr 2021 14:11:22 +0200 Subject: [PATCH 24/57] clockify is using keyring to store and receive api key --- openpype/modules/clockify/clockify_api.py | 26 +++++++++-------------- openpype/modules/clockify/constants.py | 9 ++------ 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/openpype/modules/clockify/clockify_api.py b/openpype/modules/clockify/clockify_api.py index d88b2ef8df..e2de726f39 100644 --- a/openpype/modules/clockify/clockify_api.py +++ b/openpype/modules/clockify/clockify_api.py @@ -1,13 +1,17 @@ import os import re import time -import requests import json import datetime +import requests from .constants import ( - CLOCKIFY_ENDPOINT, ADMIN_PERMISSION_NAMES, CREDENTIALS_JSON_PATH + CLOCKIFY_ENDPOINT, + ADMIN_PERMISSION_NAMES ) +# from openpype.lib import OpenPypeSettingsRegistry +from openpype.lib.local_settings import OpenPypeSecureRegistry as OpenPypeSettingsRegistry + def time_check(obj): if obj.request_counter < 10: @@ -31,6 +35,8 @@ class ClockifyAPI: self.request_counter = 0 self.request_time = time.time() + self.secure_registry = OpenPypeSettingsRegistry("clockify") + @property def headers(self): return {"X-Api-Key": self.api_key} @@ -129,22 +135,10 @@ class ClockifyAPI: return False def get_api_key(self): - api_key = None - try: - file = open(CREDENTIALS_JSON_PATH, 'r') - api_key = json.load(file).get('api_key', None) - if api_key == '': - api_key = None - except Exception: - file = open(CREDENTIALS_JSON_PATH, 'w') - file.close() - return api_key + return self.secure_registry.get_secure_item("api_key", None) def save_api_key(self, api_key): - data = {'api_key': api_key} - file = open(CREDENTIALS_JSON_PATH, 'w') - file.write(json.dumps(data)) - file.close() + self.secure_registry.set_secure_item("api_key", api_key) def get_workspaces(self): action_url = 'workspaces/' diff --git a/openpype/modules/clockify/constants.py b/openpype/modules/clockify/constants.py index 38ad4b64cf..66f6cb899a 100644 --- a/openpype/modules/clockify/constants.py +++ b/openpype/modules/clockify/constants.py @@ -1,17 +1,12 @@ import os -import appdirs CLOCKIFY_FTRACK_SERVER_PATH = os.path.join( - os.path.dirname(__file__), "ftrack", "server" + os.path.dirname(os.path.abspath(__file__)), "ftrack", "server" ) CLOCKIFY_FTRACK_USER_PATH = os.path.join( - os.path.dirname(__file__), "ftrack", "user" + os.path.dirname(os.path.abspath(__file__)), "ftrack", "user" ) -CREDENTIALS_JSON_PATH = os.path.normpath(os.path.join( - appdirs.user_data_dir("pype-app", "pype"), - "clockify.json" -)) ADMIN_PERMISSION_NAMES = ["WORKSPACE_OWN", "WORKSPACE_ADMIN"] CLOCKIFY_ENDPOINT = "https://api.clockify.me/api/" From 96eec587dbb48fb93b44d17a48481cc9db109cc8 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 9 Apr 2021 09:54:07 +0200 Subject: [PATCH 25/57] change ID generation to coolname --- openpype/lib/local_settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/lib/local_settings.py b/openpype/lib/local_settings.py index 5d2955532a..56bdd047c9 100644 --- a/openpype/lib/local_settings.py +++ b/openpype/lib/local_settings.py @@ -498,12 +498,12 @@ class OpenPypeSettingsRegistry(JSONSettingRegistry): def _create_local_site_id(registry=None): """Create a local site identifier.""" - from uuid import uuid4 + from coolname import generate_slug if registry is None: registry = OpenPypeSettingsRegistry() - new_id = str(uuid4()) + new_id = generate_slug(3) print("Created local site id \"{}\"".format(new_id)) From 31bd9616ec631d2bf88a81514331d3d8e7bb77f9 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 9 Apr 2021 10:05:16 +0200 Subject: [PATCH 26/57] extra space in info widget --- openpype/tools/tray/pype_info_widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/tray/pype_info_widget.py b/openpype/tools/tray/pype_info_widget.py index dbff36eca7..a70a360378 100644 --- a/openpype/tools/tray/pype_info_widget.py +++ b/openpype/tools/tray/pype_info_widget.py @@ -363,7 +363,7 @@ class PypeInfoWidget(QtWidgets.QWidget): "version_value": "OpenPype version:", "executable": "OpenPype executable:", "pype_root": "OpenPype location:", - "mongo_url": "OpenPype Mongo URL:" + "mongo_url": "OpenPype Mongo URL:" } # Prepare keys order keys_order = ["version_value", "executable", "pype_root", "mongo_url"] From a9857ee49262d767d0f8c032088a05baaf301391 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Fri, 9 Apr 2021 10:30:21 +0200 Subject: [PATCH 27/57] remove local site label from local settings --- .../settings/local_settings/general_widget.py | 22 +++++-------------- .../tools/settings/local_settings/window.py | 13 ++++++----- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/openpype/tools/settings/local_settings/general_widget.py b/openpype/tools/settings/local_settings/general_widget.py index 7732157122..e820d8ab8b 100644 --- a/openpype/tools/settings/local_settings/general_widget.py +++ b/openpype/tools/settings/local_settings/general_widget.py @@ -5,28 +5,16 @@ class LocalGeneralWidgets(QtWidgets.QWidget): def __init__(self, parent): super(LocalGeneralWidgets, self).__init__(parent) - local_site_name_input = QtWidgets.QLineEdit(self) - - layout = QtWidgets.QFormLayout(self) - layout.setContentsMargins(0, 0, 0, 0) - - layout.addRow("Local site label", local_site_name_input) - - self.local_site_name_input = local_site_name_input def update_local_settings(self, value): - site_label = "" - if value: - site_label = value.get("site_label", site_label) - self.local_site_name_input.setText(site_label) + return + + # RETURNING EARLY TO HIDE WIDGET WITHOUT CONTENT def settings_value(self): # Add changed # If these have changed then output = {} - local_site_name = self.local_site_name_input.text() - if local_site_name: - output["site_label"] = local_site_name - # Do not return output yet since we don't have mechanism to save or - # load these data through api calls + # TEMPORARILY EMPTY AS THERE IS NOTHING TO PUT HERE + return output diff --git a/openpype/tools/settings/local_settings/window.py b/openpype/tools/settings/local_settings/window.py index b6ca56d348..a12a2289b5 100644 --- a/openpype/tools/settings/local_settings/window.py +++ b/openpype/tools/settings/local_settings/window.py @@ -80,6 +80,7 @@ class LocalSettingsWidget(QtWidgets.QWidget): general_widget = LocalGeneralWidgets(general_content) general_layout.addWidget(general_widget) + general_expand_widget.hide() self.main_layout.addWidget(general_expand_widget) @@ -126,9 +127,9 @@ class LocalSettingsWidget(QtWidgets.QWidget): self.system_settings.reset() self.project_settings.reset() - self.general_widget.update_local_settings( - value.get(LOCAL_GENERAL_KEY) - ) + # self.general_widget.update_local_settings( + # value.get(LOCAL_GENERAL_KEY) + # ) self.app_widget.update_local_settings( value.get(LOCAL_APPS_KEY) ) @@ -138,9 +139,9 @@ class LocalSettingsWidget(QtWidgets.QWidget): def settings_value(self): output = {} - general_value = self.general_widget.settings_value() - if general_value: - output[LOCAL_GENERAL_KEY] = general_value + # general_value = self.general_widget.settings_value() + # if general_value: + # output[LOCAL_GENERAL_KEY] = general_value app_value = self.app_widget.settings_value() if app_value: From 065b64d1e08868ad063dcf6b2308cbfafff9d9c7 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 9 Apr 2021 10:36:20 +0200 Subject: [PATCH 28/57] use OpenPypeSecureRegistry --- openpype/modules/clockify/clockify_api.py | 9 ++-- openpype/modules/ftrack/lib/credentials.py | 48 +++++++++++++++------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/openpype/modules/clockify/clockify_api.py b/openpype/modules/clockify/clockify_api.py index e2de726f39..29de5de0c9 100644 --- a/openpype/modules/clockify/clockify_api.py +++ b/openpype/modules/clockify/clockify_api.py @@ -9,8 +9,7 @@ from .constants import ( ADMIN_PERMISSION_NAMES ) -# from openpype.lib import OpenPypeSettingsRegistry -from openpype.lib.local_settings import OpenPypeSecureRegistry as OpenPypeSettingsRegistry +from openpype.lib.local_settings import OpenPypeSecureRegistry def time_check(obj): @@ -35,7 +34,7 @@ class ClockifyAPI: self.request_counter = 0 self.request_time = time.time() - self.secure_registry = OpenPypeSettingsRegistry("clockify") + self.secure_registry = OpenPypeSecureRegistry("clockify") @property def headers(self): @@ -135,10 +134,10 @@ class ClockifyAPI: return False def get_api_key(self): - return self.secure_registry.get_secure_item("api_key", None) + return self.secure_registry.get_item("api_key", None) def save_api_key(self, api_key): - self.secure_registry.set_secure_item("api_key", api_key) + self.secure_registry.set_item("api_key", api_key) def get_workspaces(self): action_url = 'workspaces/' diff --git a/openpype/modules/ftrack/lib/credentials.py b/openpype/modules/ftrack/lib/credentials.py index 05a74c0875..2d719347e7 100644 --- a/openpype/modules/ftrack/lib/credentials.py +++ b/openpype/modules/ftrack/lib/credentials.py @@ -7,7 +7,7 @@ except ImportError: from urlparse import urlparse -from openpype.lib import OpenPypeSettingsRegistry +from openpype.lib import OpenPypeSecureRegistry USERNAME_KEY = "username" API_KEY_KEY = "api_key" @@ -23,38 +23,56 @@ def get_ftrack_hostname(ftrack_server=None): return urlparse(ftrack_server).hostname -def _get_ftrack_secure_key(hostname): +def _get_ftrack_secure_key(hostname, key): """Secure item key for entered hostname.""" - return "/".join(("ftrack", hostname)) + return "/".join(("ftrack", hostname, key)) def get_credentials(ftrack_server=None): hostname = get_ftrack_hostname(ftrack_server) - secure_key = _get_ftrack_secure_key(hostname) + username_name = _get_ftrack_secure_key(hostname, USERNAME_KEY) + api_key_name = _get_ftrack_secure_key(hostname, API_KEY_KEY) + + username_registry = OpenPypeSecureRegistry(username_name) + api_key_registry = OpenPypeSecureRegistry(api_key_name) - registry = OpenPypeSettingsRegistry(secure_key) return { - USERNAME_KEY: registry.get_secure_item(USERNAME_KEY, None), - API_KEY_KEY: registry.get_secure_item(API_KEY_KEY, None) + USERNAME_KEY: username_registry.get_item(USERNAME_KEY, None), + API_KEY_KEY: api_key_registry.get_item(API_KEY_KEY, None) } def save_credentials(username, api_key, ftrack_server=None): hostname = get_ftrack_hostname(ftrack_server) - secure_key = _get_ftrack_secure_key(hostname) + username_name = _get_ftrack_secure_key(hostname, USERNAME_KEY) + api_key_name = _get_ftrack_secure_key(hostname, API_KEY_KEY) - registry = OpenPypeSettingsRegistry(secure_key) - registry.set_secure_item(USERNAME_KEY, username) - registry.set_secure_item(API_KEY_KEY, api_key) + # Clear credentials + clear_credentials(ftrack_server) + + username_registry = OpenPypeSecureRegistry(username_name) + api_key_registry = OpenPypeSecureRegistry(api_key_name) + + username_registry.set_item(USERNAME_KEY, username) + api_key_registry.set_item(API_KEY_KEY, api_key) def clear_credentials(ftrack_server=None): hostname = get_ftrack_hostname(ftrack_server) - secure_key = _get_ftrack_secure_key(hostname) + username_name = _get_ftrack_secure_key(hostname, USERNAME_KEY) + api_key_name = _get_ftrack_secure_key(hostname, API_KEY_KEY) - registry = OpenPypeSettingsRegistry(secure_key) - registry.delete_secure_item(USERNAME_KEY) - registry.delete_secure_item(API_KEY_KEY) + username_registry = OpenPypeSecureRegistry(username_name) + api_key_registry = OpenPypeSecureRegistry(api_key_name) + + current_username = username_registry.get_item(USERNAME_KEY, None) + current_api_key = api_key_registry.get_item(API_KEY_KEY, None) + + if current_username is not None: + username_registry.delete_item(USERNAME_KEY) + + if current_api_key is not None: + api_key_registry.delete_item(API_KEY_KEY) def check_credentials(username, api_key, ftrack_server=None): From 8e183b05e982f2d7063493610cbc25f4e8a280fc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 9 Apr 2021 15:29:28 +0200 Subject: [PATCH 29/57] added default task type mapping settings to ftrack module --- .../defaults/system_settings/modules.json | 16 ++++++++++++++++ .../module_settings/schema_ftrack.json | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/openpype/settings/defaults/system_settings/modules.json b/openpype/settings/defaults/system_settings/modules.json index b3065058a1..09198a3ad9 100644 --- a/openpype/settings/defaults/system_settings/modules.json +++ b/openpype/settings/defaults/system_settings/modules.json @@ -21,6 +21,22 @@ }, "default": "-" }, + "task_short_names": { + "Generic": "gener", + "Art": "art", + "Modeling": "mdl", + "Texture": "tex", + "Lookdev": "look", + "Rigging": "rig", + "Edit": "edit", + "Layout": "lay", + "Setdress": "dress", + "Animation": "anim", + "FX": "fx", + "Lighting": "lgt", + "Paint": "paint", + "Compositing": "comp" + }, "custom_attributes": { "show": { "avalon_auto_sync": { diff --git a/openpype/settings/entities/schemas/system_schema/module_settings/schema_ftrack.json b/openpype/settings/entities/schemas/system_schema/module_settings/schema_ftrack.json index 50ec330a11..2ddb3be795 100644 --- a/openpype/settings/entities/schemas/system_schema/module_settings/schema_ftrack.json +++ b/openpype/settings/entities/schemas/system_schema/module_settings/schema_ftrack.json @@ -66,6 +66,12 @@ } ] }, + { + "type": "dict-modifiable", + "key": "task_short_names", + "label": "Default task short names (by Task type)", + "object_type": "text" + }, { "key": "custom_attributes", "label": "Custom Attributes", From 3eab325ca83956c71993f7f21448e958c588b363 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 9 Apr 2021 15:30:41 +0200 Subject: [PATCH 30/57] use short code mapping during synchronization --- openpype/modules/ftrack/lib/avalon_sync.py | 15 +++++++++++---- openpype/modules/ftrack/lib/settings.py | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/openpype/modules/ftrack/lib/avalon_sync.py b/openpype/modules/ftrack/lib/avalon_sync.py index 7511c2627b..8a453f8935 100644 --- a/openpype/modules/ftrack/lib/avalon_sync.py +++ b/openpype/modules/ftrack/lib/avalon_sync.py @@ -26,6 +26,8 @@ from pymongo import UpdateOne import ftrack_api from openpype.lib import ApplicationManager +from .settings import get_ftrack_settings + log = Logger.get_logger(__name__) @@ -1221,6 +1223,12 @@ class SyncEntitiesFactory: def prepare_ftrack_ent_data(self): not_set_ids = [] + # Prepare short task type mapping + _task_short_names = get_ftrack_settings()["task_short_names"] + task_short_names = { + key.lower(): value + for key, value in _task_short_names.items() + } for id, entity_dict in self.entities_dict.items(): entity = entity_dict["entity"] if entity is None: @@ -1259,11 +1267,10 @@ class SyncEntitiesFactory: tasks = {} for task_type in task_types: task_type_name = task_type["name"] - # Set short name to empty string - # QUESTION Maybe better would be to lower and remove spaces - # from task type name. + # Set short name to mapping from settings or empty string + short_name = task_short_names.get(task_type_name.lower()) tasks[task_type_name] = { - "short_name": "" + "short_name": short_name or "" } current_project_anatomy_data = get_anatomy_settings( diff --git a/openpype/modules/ftrack/lib/settings.py b/openpype/modules/ftrack/lib/settings.py index f6967411db..027356edc6 100644 --- a/openpype/modules/ftrack/lib/settings.py +++ b/openpype/modules/ftrack/lib/settings.py @@ -1,6 +1,7 @@ import os from openpype.api import get_system_settings + def get_ftrack_settings(): return get_system_settings()["modules"]["ftrack"] @@ -10,7 +11,6 @@ def get_ftrack_url_from_settings(): def get_ftrack_event_mongo_info(): - ftrack_settings = get_ftrack_settings() database_name = os.environ["OPENPYPE_DATABASE_NAME"] collection_name = "ftrack_events" return database_name, collection_name From 1023f82139996f6a58dd0a586d717a83e4704038 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 9 Apr 2021 16:22:04 +0200 Subject: [PATCH 31/57] removed added settings --- .../defaults/system_settings/modules.json | 16 ---------------- .../module_settings/schema_ftrack.json | 6 ------ 2 files changed, 22 deletions(-) diff --git a/openpype/settings/defaults/system_settings/modules.json b/openpype/settings/defaults/system_settings/modules.json index 09198a3ad9..b3065058a1 100644 --- a/openpype/settings/defaults/system_settings/modules.json +++ b/openpype/settings/defaults/system_settings/modules.json @@ -21,22 +21,6 @@ }, "default": "-" }, - "task_short_names": { - "Generic": "gener", - "Art": "art", - "Modeling": "mdl", - "Texture": "tex", - "Lookdev": "look", - "Rigging": "rig", - "Edit": "edit", - "Layout": "lay", - "Setdress": "dress", - "Animation": "anim", - "FX": "fx", - "Lighting": "lgt", - "Paint": "paint", - "Compositing": "comp" - }, "custom_attributes": { "show": { "avalon_auto_sync": { diff --git a/openpype/settings/entities/schemas/system_schema/module_settings/schema_ftrack.json b/openpype/settings/entities/schemas/system_schema/module_settings/schema_ftrack.json index 2ddb3be795..50ec330a11 100644 --- a/openpype/settings/entities/schemas/system_schema/module_settings/schema_ftrack.json +++ b/openpype/settings/entities/schemas/system_schema/module_settings/schema_ftrack.json @@ -66,12 +66,6 @@ } ] }, - { - "type": "dict-modifiable", - "key": "task_short_names", - "label": "Default task short names (by Task type)", - "object_type": "text" - }, { "key": "custom_attributes", "label": "Custom Attributes", From 1359109e44dd6f0caa8c67ebcb8ca87c5e48ed5e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 9 Apr 2021 16:59:49 +0200 Subject: [PATCH 32/57] Use default anatomy task types instead of empty on project sync --- openpype/modules/ftrack/lib/avalon_sync.py | 25 ++++++++++------------ 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/openpype/modules/ftrack/lib/avalon_sync.py b/openpype/modules/ftrack/lib/avalon_sync.py index 8a453f8935..b9702c1560 100644 --- a/openpype/modules/ftrack/lib/avalon_sync.py +++ b/openpype/modules/ftrack/lib/avalon_sync.py @@ -1223,12 +1223,6 @@ class SyncEntitiesFactory: def prepare_ftrack_ent_data(self): not_set_ids = [] - # Prepare short task type mapping - _task_short_names = get_ftrack_settings()["task_short_names"] - task_short_names = { - key.lower(): value - for key, value in _task_short_names.items() - } for id, entity_dict in self.entities_dict.items(): entity = entity_dict["entity"] if entity is None: @@ -1264,18 +1258,21 @@ class SyncEntitiesFactory: if not msg or not items: continue self.report_items["warning"][msg] = items - tasks = {} - for task_type in task_types: - task_type_name = task_type["name"] - # Set short name to mapping from settings or empty string - short_name = task_short_names.get(task_type_name.lower()) - tasks[task_type_name] = { - "short_name": short_name or "" - } current_project_anatomy_data = get_anatomy_settings( project_name, exclude_locals=True ) + anatomy_tasks = current_project_anatomy_data["tasks"] + tasks = {} + default_type_data = { + "short_name": "" + } + for task_type in task_types: + task_type_name = task_type["name"] + tasks[task_type_name] = copy.deepcopy( + anatomy_tasks.get(task_type_name) + or default_type_data + ) project_config = { "tasks": tasks, From 656a432c103bdfd9c02cd283b4c60df0f2f55335 Mon Sep 17 00:00:00 2001 From: jezscha Date: Fri, 9 Apr 2021 15:28:17 +0000 Subject: [PATCH 33/57] Create draft PR for #1300 From 667b977fca277d11afd70e4121bfa5f3d72fec21 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 9 Apr 2021 19:05:23 +0200 Subject: [PATCH 34/57] removed unused import --- openpype/modules/ftrack/lib/avalon_sync.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/modules/ftrack/lib/avalon_sync.py b/openpype/modules/ftrack/lib/avalon_sync.py index b9702c1560..fbe65efb35 100644 --- a/openpype/modules/ftrack/lib/avalon_sync.py +++ b/openpype/modules/ftrack/lib/avalon_sync.py @@ -26,8 +26,6 @@ from pymongo import UpdateOne import ftrack_api from openpype.lib import ApplicationManager -from .settings import get_ftrack_settings - log = Logger.get_logger(__name__) From 7a9a7b54176893f23e3757d4d04da470c3b7adde Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 12 Apr 2021 12:39:57 +0200 Subject: [PATCH 35/57] Nuke: fixing mixup with families in version, enabling prerender publish #cf4de238f596019ca9fcdee2e25992acca1165bf --- .../nuke/plugins/publish/precollect_instances.py | 16 +++++++++++----- .../nuke/plugins/publish/precollect_writes.py | 2 ++ .../plugins/publish/submit_publish_job.py | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/precollect_instances.py b/openpype/hosts/nuke/plugins/publish/precollect_instances.py index 2d25b29826..92f96ea48d 100644 --- a/openpype/hosts/nuke/plugins/publish/precollect_instances.py +++ b/openpype/hosts/nuke/plugins/publish/precollect_instances.py @@ -80,25 +80,31 @@ class PreCollectNukeInstances(pyblish.api.ContextPlugin): # Add all nodes in group instances. if node.Class() == "Group": - # check if it is write node in family - if "write" in families: + # only alter families for render family + if "write" in families_ak: target = node["render"].value() if target == "Use existing frames": # Local rendering self.log.info("flagged for no render") - families.append("render") + families.append(family) elif target == "Local": # Local rendering self.log.info("flagged for local render") - families.append("{}.local".format("render")) + families.append("{}.local".format(family)) elif target == "On farm": # Farm rendering self.log.info("flagged for farm render") instance.data["transfer"] = False - families.append("{}.farm".format("render")) + families.append("{}.farm".format(family)) + + # suffle family to `write` as it is main family + # this will be changed later on in process if "render" in families: families.remove("render") family = "write" + elif "prerender" in families: + families.remove("prerender") + family = "write" node.begin() for i in nuke.allNodes(): diff --git a/openpype/hosts/nuke/plugins/publish/precollect_writes.py b/openpype/hosts/nuke/plugins/publish/precollect_writes.py index a519609f52..57303bd42e 100644 --- a/openpype/hosts/nuke/plugins/publish/precollect_writes.py +++ b/openpype/hosts/nuke/plugins/publish/precollect_writes.py @@ -108,6 +108,8 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): # Add version data to instance version_data = { + "families": [f.replace(".local", "").replace(".farm", "") + for f in families if "write" not in f], "colorspace": node["colorspace"].value(), } diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index a2e21fb766..ea953441a2 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -102,7 +102,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): hosts = ["fusion", "maya", "nuke", "celaction", "aftereffects", "harmony"] - families = ["render.farm", "prerender", + families = ["render.farm", "prerender.farm", "renderlayer", "imagesequence", "vrayscene"] aov_filter = {"maya": [r".+(?:\.|_)([Bb]eauty)(?:\.|_).*"], From 64436505d16d7de77f1fcd2c2a22199596525e3b Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 12 Apr 2021 12:39:57 +0200 Subject: [PATCH 36/57] Nuke: fixing mixup with families in version, enabling prerender publish https://github.com/pypeclub/pype/commit/cf4de238f596019ca9fcdee2e25992acca1165bf --- .../nuke/plugins/publish/precollect_instances.py | 16 +++++++++++----- .../nuke/plugins/publish/precollect_writes.py | 2 ++ .../plugins/publish/submit_publish_job.py | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/precollect_instances.py b/openpype/hosts/nuke/plugins/publish/precollect_instances.py index 2d25b29826..92f96ea48d 100644 --- a/openpype/hosts/nuke/plugins/publish/precollect_instances.py +++ b/openpype/hosts/nuke/plugins/publish/precollect_instances.py @@ -80,25 +80,31 @@ class PreCollectNukeInstances(pyblish.api.ContextPlugin): # Add all nodes in group instances. if node.Class() == "Group": - # check if it is write node in family - if "write" in families: + # only alter families for render family + if "write" in families_ak: target = node["render"].value() if target == "Use existing frames": # Local rendering self.log.info("flagged for no render") - families.append("render") + families.append(family) elif target == "Local": # Local rendering self.log.info("flagged for local render") - families.append("{}.local".format("render")) + families.append("{}.local".format(family)) elif target == "On farm": # Farm rendering self.log.info("flagged for farm render") instance.data["transfer"] = False - families.append("{}.farm".format("render")) + families.append("{}.farm".format(family)) + + # suffle family to `write` as it is main family + # this will be changed later on in process if "render" in families: families.remove("render") family = "write" + elif "prerender" in families: + families.remove("prerender") + family = "write" node.begin() for i in nuke.allNodes(): diff --git a/openpype/hosts/nuke/plugins/publish/precollect_writes.py b/openpype/hosts/nuke/plugins/publish/precollect_writes.py index a519609f52..57303bd42e 100644 --- a/openpype/hosts/nuke/plugins/publish/precollect_writes.py +++ b/openpype/hosts/nuke/plugins/publish/precollect_writes.py @@ -108,6 +108,8 @@ class CollectNukeWrites(pyblish.api.InstancePlugin): # Add version data to instance version_data = { + "families": [f.replace(".local", "").replace(".farm", "") + for f in families if "write" not in f], "colorspace": node["colorspace"].value(), } diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index a2e21fb766..ea953441a2 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -102,7 +102,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin): hosts = ["fusion", "maya", "nuke", "celaction", "aftereffects", "harmony"] - families = ["render.farm", "prerender", + families = ["render.farm", "prerender.farm", "renderlayer", "imagesequence", "vrayscene"] aov_filter = {"maya": [r".+(?:\.|_)([Bb]eauty)(?:\.|_).*"], From a0c98549ebd32ad1ae24b6aa85f6e9905a135f46 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 12 Apr 2021 14:49:28 +0200 Subject: [PATCH 37/57] implemented method that can queyr custom attribute values in chunks --- openpype/modules/ftrack/lib/avalon_sync.py | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/openpype/modules/ftrack/lib/avalon_sync.py b/openpype/modules/ftrack/lib/avalon_sync.py index fbe65efb35..c2c3f0bd0d 100644 --- a/openpype/modules/ftrack/lib/avalon_sync.py +++ b/openpype/modules/ftrack/lib/avalon_sync.py @@ -891,6 +891,41 @@ class SyncEntitiesFactory: self.entities_dict[parent_id]["children"].remove(id) + def _query_custom_attributes(self, session, conf_ids, entity_ids): + output = [] + # Prepare values to query + attributes_joined = ", ".join([ + "\"{}\"".format(conf_id) for conf_id in conf_ids + ]) + attributes_len = len(conf_ids) + chunk_size = int(5000 / attributes_len) + if chunk_size < 1: + chunk_size = 1 + for idx in range(0, attributes_len, chunk_size): + _entity_ids = entity_ids[idx:idx + chunk_size] + if not _entity_ids: + continue + entity_ids_joined = ", ".join([ + "\"{}\"".format(entity_id) + for entity_id in _entity_ids + ]) + + call_expr = [{ + "action": "query", + "expression": ( + "select value, entity_id from ContextCustomAttributeValue " + "where entity_id in ({}) and configuration_id in ({})" + ).format(entity_ids_joined, attributes_joined) + }] + if hasattr(session, "call"): + [result] = session.call(call_expr) + else: + [result] = session._call(call_expr) + + for item in result["data"]: + output.append(item) + return output + def set_cutom_attributes(self): self.log.debug("* Preparing custom attributes") # Get custom attributes and values From b1563b51ddf5e71972e4d049d946e2b599882dad Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 12 Apr 2021 14:49:48 +0200 Subject: [PATCH 38/57] use `_query_custom_attributes` to get custom attribute values --- openpype/modules/ftrack/lib/avalon_sync.py | 56 +++++----------------- 1 file changed, 12 insertions(+), 44 deletions(-) diff --git a/openpype/modules/ftrack/lib/avalon_sync.py b/openpype/modules/ftrack/lib/avalon_sync.py index c2c3f0bd0d..2411d8da82 100644 --- a/openpype/modules/ftrack/lib/avalon_sync.py +++ b/openpype/modules/ftrack/lib/avalon_sync.py @@ -1035,31 +1035,13 @@ class SyncEntitiesFactory: copy.deepcopy(prepared_avalon_attr_ca_id) ) - # TODO query custom attributes by entity_id - entity_ids_joined = ", ".join([ - "\"{}\"".format(id) for id in sync_ids - ]) - attributes_joined = ", ".join([ - "\"{}\"".format(attr_id) for attr_id in attribute_key_by_id.keys() - ]) - - cust_attr_query = ( - "select value, configuration_id, entity_id" - " from ContextCustomAttributeValue" - " where entity_id in ({}) and configuration_id in ({})" + items = self._query_custom_attributes( + self.session, + list(attribute_key_by_id.keys()), + sync_ids ) - call_expr = [{ - "action": "query", - "expression": cust_attr_query.format( - entity_ids_joined, attributes_joined - ) - }] - if hasattr(self.session, "call"): - [values] = self.session.call(call_expr) - else: - [values] = self.session._call(call_expr) - for item in values["data"]: + for item in items: entity_id = item["entity_id"] attr_id = item["configuration_id"] key = attribute_key_by_id[attr_id] @@ -1141,28 +1123,14 @@ class SyncEntitiesFactory: for key, val in prepare_dict_avalon.items(): entity_dict["avalon_attrs"][key] = val - # Prepare values to query - entity_ids_joined = ", ".join([ - "\"{}\"".format(id) for id in sync_ids - ]) - attributes_joined = ", ".join([ - "\"{}\"".format(attr_id) for attr_id in attribute_key_by_id.keys() - ]) - avalon_hier = [] - call_expr = [{ - "action": "query", - "expression": ( - "select value, entity_id, configuration_id" - " from ContextCustomAttributeValue" - " where entity_id in ({}) and configuration_id in ({})" - ).format(entity_ids_joined, attributes_joined) - }] - if hasattr(self.session, "call"): - [values] = self.session.call(call_expr) - else: - [values] = self.session._call(call_expr) + items = self._query_custom_attributes( + self.session, + list(attribute_key_by_id.keys()), + sync_ids + ) - for item in values["data"]: + avalon_hier = [] + for item in items: value = item["value"] # WARNING It is not possible to propage enumerate hierachical # attributes with multiselection 100% right. Unseting all values From 46963fdddf9e3cea3da9780bd7ee171c827dfd24 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 12 Apr 2021 16:55:15 +0200 Subject: [PATCH 39/57] fix custom attribute query --- openpype/modules/ftrack/lib/avalon_sync.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/openpype/modules/ftrack/lib/avalon_sync.py b/openpype/modules/ftrack/lib/avalon_sync.py index 2411d8da82..79e1366a0d 100644 --- a/openpype/modules/ftrack/lib/avalon_sync.py +++ b/openpype/modules/ftrack/lib/avalon_sync.py @@ -894,21 +894,13 @@ class SyncEntitiesFactory: def _query_custom_attributes(self, session, conf_ids, entity_ids): output = [] # Prepare values to query - attributes_joined = ", ".join([ - "\"{}\"".format(conf_id) for conf_id in conf_ids - ]) + attributes_joined = join_query_keys(conf_ids) attributes_len = len(conf_ids) chunk_size = int(5000 / attributes_len) - if chunk_size < 1: - chunk_size = 1 - for idx in range(0, attributes_len, chunk_size): - _entity_ids = entity_ids[idx:idx + chunk_size] - if not _entity_ids: - continue - entity_ids_joined = ", ".join([ - "\"{}\"".format(entity_id) - for entity_id in _entity_ids - ]) + for idx in range(0, len(entity_ids), chunk_size): + entity_ids_joined = join_query_keys( + entity_ids[idx:idx + chunk_size] + ) call_expr = [{ "action": "query", From 6a599da37a0cc5ce7a695d7a9f2ca33d236824f0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 12 Apr 2021 17:50:27 +0200 Subject: [PATCH 40/57] disabled attributes options for now --- .../event_handlers_user/action_prepare_project.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py index 7f674310fc..367b4b9e69 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py @@ -63,9 +63,9 @@ class PrepareProject(BaseAction): root_items = self.prepare_root_items(anatomy) - ca_items, multiselect_enumerators = ( - self.prepare_custom_attribute_items(project_defaults) - ) + # ca_items, multiselect_enumerators = ( + # self.prepare_custom_attribute_items(project_defaults) + # ) self.log.debug("Heavy items are ready. Preparing last items group.") @@ -94,7 +94,7 @@ class PrepareProject(BaseAction): "value": "

Set basic Attributes:

" }) - items.extend(ca_items) + # items.extend(ca_items) # This item will be last (before enumerators) # - sets value of auto synchronization @@ -108,9 +108,9 @@ class PrepareProject(BaseAction): # Add autosync attribute items.append(auto_sync_item) - # Add enumerator items at the end - for item in multiselect_enumerators: - items.append(item) + # # Add enumerator items at the end + # for item in multiselect_enumerators: + # items.append(item) return { "items": items, From dfe35176f6cbb9e13cf928f3e2ea5e5358f4259f Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 12 Apr 2021 17:50:43 +0200 Subject: [PATCH 41/57] roots are filled with current values --- .../action_prepare_project.py | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py index 367b4b9e69..16921f1bda 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py @@ -3,7 +3,10 @@ import json from openpype.modules.ftrack.lib import BaseAction, statics_icon from openpype.api import config, Anatomy -from openpype.modules.ftrack.lib.avalon_sync import get_pype_attr +from openpype.modules.ftrack.lib.avalon_sync import ( + get_pype_attr, + CUST_ATTR_AUTO_SYNC +) class PrepareProject(BaseAction): @@ -44,24 +47,10 @@ class PrepareProject(BaseAction): self.log.debug("Loading custom attributes") - project_name = entities[0]["full_name"] + project_entity = entities[0] + project_name = project_entity["full_name"] - project_defaults = ( - config.get_presets(project_name) - .get("ftrack", {}) - .get("project_defaults", {}) - ) - - anatomy = Anatomy(project_name) - if not anatomy.roots: - return { - "success": False, - "message": ( - "Have issues with loading Roots for project \"{}\"." - ).format(anatomy.project_name) - } - - root_items = self.prepare_root_items(anatomy) + root_items = self.prepare_root_items(project_name) # ca_items, multiselect_enumerators = ( # self.prepare_custom_attribute_items(project_defaults) @@ -99,10 +88,13 @@ class PrepareProject(BaseAction): # This item will be last (before enumerators) # - sets value of auto synchronization auto_sync_name = "avalon_auto_sync" + auto_sync_value = project_entity["custom_attributes"].get( + CUST_ATTR_AUTO_SYNC, False + ) auto_sync_item = { "name": auto_sync_name, "type": "boolean", - "value": project_defaults.get(auto_sync_name, False), + "value": auto_sync_value, "label": "AutoSync to Avalon" } # Add autosync attribute @@ -117,10 +109,10 @@ class PrepareProject(BaseAction): "title": title } - def prepare_root_items(self, anatomy): + def prepare_root_items(self, project_name): root_items = [] self.log.debug("Root items preparation begins.") - + anatomy = Anatomy(project_name) root_names = anatomy.root_names() roots = anatomy.roots From 6695fa7382a2058ab7b9a01d3bab63997e74b57e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 12 Apr 2021 19:06:33 +0200 Subject: [PATCH 42/57] interface inputs are created using project settings --- .../action_prepare_project.py | 127 +++++++----------- 1 file changed, 46 insertions(+), 81 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py index 16921f1bda..5b3828f554 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py @@ -1,8 +1,12 @@ import os import json -from openpype.modules.ftrack.lib import BaseAction, statics_icon -from openpype.api import config, Anatomy +from openpype.api import config, ProjectSettings + +from openpype.modules.ftrack.lib import ( + BaseAction, + statics_icon +) from openpype.modules.ftrack.lib.avalon_sync import ( get_pype_attr, CUST_ATTR_AUTO_SYNC @@ -50,11 +54,20 @@ class PrepareProject(BaseAction): project_entity = entities[0] project_name = project_entity["full_name"] - root_items = self.prepare_root_items(project_name) + try: + project_settings = ProjectSettings(project_name) + except ValueError: + return { + "message": "Project is not synchronized yet", + "success": False + } - # ca_items, multiselect_enumerators = ( - # self.prepare_custom_attribute_items(project_defaults) - # ) + project_anatom_settings = project_settings["project_anatomy"] + root_items = self.prepare_root_items(project_anatom_settings) + + ca_items, multiselect_enumerators = ( + self.prepare_custom_attribute_items(project_anatom_settings) + ) self.log.debug("Heavy items are ready. Preparing last items group.") @@ -83,7 +96,7 @@ class PrepareProject(BaseAction): "value": "

Set basic Attributes:

" }) - # items.extend(ca_items) + items.extend(ca_items) # This item will be last (before enumerators) # - sets value of auto synchronization @@ -100,22 +113,19 @@ class PrepareProject(BaseAction): # Add autosync attribute items.append(auto_sync_item) - # # Add enumerator items at the end - # for item in multiselect_enumerators: - # items.append(item) + # Add enumerator items at the end + for item in multiselect_enumerators: + items.append(item) return { "items": items, "title": title } - def prepare_root_items(self, project_name): - root_items = [] + def prepare_root_items(self, project_anatom_settings): self.log.debug("Root items preparation begins.") - anatomy = Anatomy(project_name) - root_names = anatomy.root_names() - roots = anatomy.roots + root_items = [] root_items.append({ "type": "label", "value": "

Check your Project root settings

" @@ -135,85 +145,40 @@ class PrepareProject(BaseAction): ) }) - default_roots = anatomy.roots - while isinstance(default_roots, dict): - key = tuple(default_roots.keys())[0] - default_roots = default_roots[key] - empty_text = "Enter root path here..." - # Root names is None when anatomy templates contain "{root}" - all_platforms = ["windows", "linux", "darwin"] - if root_names is None: - root_items.append(self.item_splitter) - # find first possible key - for platform in all_platforms: - value = default_roots.raw_data.get(platform) or "" - root_items.append({ - "label": platform, - "name": "__root__{}".format(platform), - "type": "text", - "value": value, - "empty_text": empty_text - }) - return root_items - - root_name_data = {} - missing_roots = [] - for root_name in root_names: - root_name_data[root_name] = {} - if not isinstance(roots, dict): - missing_roots.append(root_name) - continue - - root_item = roots.get(root_name) - if not root_item: - missing_roots.append(root_name) - continue - - for platform in all_platforms: - root_name_data[root_name][platform] = ( - root_item.raw_data.get(platform) or "" - ) - - if missing_roots: - default_values = {} - for platform in all_platforms: - default_values[platform] = ( - default_roots.raw_data.get(platform) or "" - ) - - for root_name in missing_roots: - root_name_data[root_name] = default_values - - root_names = list(root_name_data.keys()) - root_items.append({ - "type": "hidden", - "name": "__rootnames__", - "value": json.dumps(root_names) - }) - - for root_name, values in root_name_data.items(): + roots_entity = project_anatom_settings["roots"] + for root_name, root_entity in roots_entity.items(): root_items.append(self.item_splitter) root_items.append({ "type": "label", "value": "Root: \"{}\"".format(root_name) }) - for platform, value in values.items(): + for platform_name, value_entity in root_entity.items(): root_items.append({ - "label": platform, - "name": "__root__{}{}".format(root_name, platform), + "label": platform_name, + "name": "__root__{}{}".format(root_name, platform_name), "type": "text", - "value": value, + "value": value_entity.value, "empty_text": empty_text }) + root_items.append({ + "type": "hidden", + "name": "__rootnames__", + "value": json.dumps(list(roots_entity.keys())) + }) + self.log.debug("Root items preparation ended.") return root_items - def _attributes_to_set(self, project_defaults): + def _attributes_to_set(self, project_anatom_settings): attributes_to_set = {} + attribute_values_by_key = {} + for key, entity in project_anatom_settings["attributes"].items(): + attribute_values_by_key[key] = entity.value + cust_attrs, hier_cust_attrs = get_pype_attr(self.session, True) for attr in hier_cust_attrs: @@ -223,7 +188,7 @@ class PrepareProject(BaseAction): attributes_to_set[key] = { "label": attr["label"], "object": attr, - "default": project_defaults.get(key) + "default": attribute_values_by_key.get(key) } for attr in cust_attrs: @@ -235,7 +200,7 @@ class PrepareProject(BaseAction): attributes_to_set[key] = { "label": attr["label"], "object": attr, - "default": project_defaults.get(key) + "default": attribute_values_by_key.get(key) } # Sort by label @@ -245,10 +210,10 @@ class PrepareProject(BaseAction): )) return attributes_to_set - def prepare_custom_attribute_items(self, project_defaults): + def prepare_custom_attribute_items(self, project_anatom_settings): items = [] multiselect_enumerators = [] - attributes_to_set = self._attributes_to_set(project_defaults) + attributes_to_set = self._attributes_to_set(project_anatom_settings) self.log.debug("Preparing interface for keys: \"{}\"".format( str([key for key in attributes_to_set]) From 610abac38191933ec065ab94e30ed418575c6955 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 12 Apr 2021 19:49:19 +0200 Subject: [PATCH 43/57] using new way of storing anatomy data --- .../action_prepare_project.py | 100 +++++------------- 1 file changed, 27 insertions(+), 73 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py index 5b3828f554..e7b8bfc3e1 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py @@ -28,7 +28,6 @@ class PrepareProject(BaseAction): settings_key = "prepare_project" # Key to store info about trigerring create folder structure - create_project_structure_key = "create_folder_structure" item_splitter = {'type': 'label', 'value': '---'} def discover(self, session, entities, event): @@ -76,19 +75,6 @@ class PrepareProject(BaseAction): # Add root items items.extend(root_items) - items.append(self.item_splitter) - - # Ask if want to trigger Action Create Folder Structure - items.append({ - "type": "label", - "value": "

Want to create basic Folder Structure?

" - }) - items.append({ - "name": self.create_project_structure_key, - "type": "boolean", - "value": False, - "label": "Check if Yes" - }) items.append(self.item_splitter) items.append({ @@ -157,7 +143,7 @@ class PrepareProject(BaseAction): for platform_name, value_entity in root_entity.items(): root_items.append({ "label": platform_name, - "name": "__root__{}{}".format(root_name, platform_name), + "name": "__root__{}__{}".format(root_name, platform_name), "type": "text", "value": value_entity.value, "empty_text": empty_text @@ -320,24 +306,15 @@ class PrepareProject(BaseAction): root_names = in_data.pop("__rootnames__", None) root_data = {} - if root_names: - for root_name in json.loads(root_names): - root_data[root_name] = {} - for key, value in tuple(root_values.items()): - if key.startswith(root_name): - _key = key[len(root_name):] - root_data[root_name][_key] = value + for root_name in json.loads(root_names): + root_data[root_name] = {} + for key, value in tuple(root_values.items()): + prefix = "{}__".format(root_name) + if not key.startswith(prefix): + continue - else: - for key, value in root_values.items(): - root_data[key] = value - - # TODO implement creating of anatomy for new projects - # project_name = entities[0]["full_name"] - # anatomy = Anatomy(project_name) - - # pop out info about creating project structure - create_proj_struct = in_data.pop(self.create_project_structure_key) + _key = key[len(prefix):] + root_data[root_name][_key] = value # Find hidden items for multiselect enumerators keys_to_process = [] @@ -364,53 +341,30 @@ class PrepareProject(BaseAction): new_key = item.replace(name, "") in_data[key].append(new_key) - self.log.debug("Setting Custom Attribute values:") - entity = entities[0] + self.log.debug("Setting Custom Attribute values") + + project_name = entities[0]["full_name"] + project_settings = ProjectSettings(project_name) + project_anatomy_settings = project_settings["project_anatomy"] + project_anatomy_settings["roots"] = root_data + + custom_attribute_values = {} + attributes_entity = project_anatomy_settings["attributes"] for key, value in in_data.items(): + if key not in attributes_entity: + custom_attribute_values[key] = value + else: + attributes_entity[key] = value + + project_settings.save() + + entity = entities[0] + for key, value in custom_attribute_values.items(): entity["custom_attributes"][key] = value self.log.debug("- Key \"{}\" set to \"{}\"".format(key, value)) - session.commit() - - # Create project structure - self.create_project_specific_config(entities[0]["full_name"], in_data) - - # Trigger Create Project Structure action - if create_proj_struct is True: - self.trigger_action("create.project.structure", event) - return True - def create_project_specific_config(self, project_name, json_data): - self.log.debug("*** Creating project specifig configs ***") - project_specific_path = project_overrides_dir_path(project_name) - if not os.path.exists(project_specific_path): - os.makedirs(project_specific_path) - self.log.debug(( - "Project specific config folder for project \"{}\" created." - ).format(project_name)) - - # Presets #################################### - self.log.debug("--- Processing Presets Begins: ---") - - project_defaults_dir = os.path.normpath(os.path.join( - project_specific_path, "presets", "ftrack" - )) - project_defaults_path = os.path.normpath(os.path.join( - project_defaults_dir, "project_defaults.json" - )) - # Create folder if not exist - if not os.path.exists(project_defaults_dir): - self.log.debug("Creating Ftrack Presets folder: \"{}\"".format( - project_defaults_dir - )) - os.makedirs(project_defaults_dir) - - with open(project_defaults_path, 'w') as file_stream: - json.dump(json_data, file_stream, indent=4) - - self.log.debug("*** Creating project specifig configs Finished ***") - def register(session): '''Register plugin. Called when used as an plugin.''' From 6d68cfa579ea07b52c310f7e2d9013f05de28c46 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 13 Apr 2021 10:12:00 +0100 Subject: [PATCH 44/57] Let Blender load scene settings from Ftrack --- openpype/hosts/blender/api/__init__.py | 40 +++++++++++++++++++------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/openpype/hosts/blender/api/__init__.py b/openpype/hosts/blender/api/__init__.py index c5b0a44072..66102a2ae1 100644 --- a/openpype/hosts/blender/api/__init__.py +++ b/openpype/hosts/blender/api/__init__.py @@ -51,18 +51,38 @@ def set_start_end_frames(): "name": asset_name }) - # Default frame start/end - frameStart = 0 - frameEnd = 100 + scene = bpy.context.scene - # Check if frameStart/frameEnd are set - if asset_doc["data"]["frameStart"]: - frameStart = asset_doc["data"]["frameStart"] - if asset_doc["data"]["frameEnd"]: - frameEnd = asset_doc["data"]["frameEnd"] + # Default scene settings + frameStart = scene.frame_start + frameEnd = scene.frame_end + fps = scene.render.fps + resolution_x = scene.render.resolution_x + resolution_y = scene.render.resolution_y + + # Check if settings are set + data = asset_doc.get("data") + + if not data: + return + + if data.get("frameStart"): + frameStart = data.get("frameStart") + if data.get("frameEnd"): + frameEnd = data.get("frameEnd") + if data.get("fps"): + fps = data.get("fps") + if data.get("resolutionWidth"): + resolution_x = data.get("resolutionWidth") + if data.get("resolutionHeight"): + resolution_y = data.get("resolutionHeight") + + scene.frame_start = frameStart + scene.frame_end = frameEnd + scene.render.fps = fps + scene.render.resolution_x = resolution_x + scene.render.resolution_y = resolution_y - bpy.context.scene.frame_start = frameStart - bpy.context.scene.frame_end = frameEnd def on_new(arg1, arg2): set_start_end_frames() From db777ee3f46254fc17939357c8fae9f6c98aaff5 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 13 Apr 2021 10:12:26 +0100 Subject: [PATCH 45/57] Added new validator for Object Mode for objects in the scene --- .../plugins/publish/validate_object_mode.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 openpype/hosts/blender/plugins/publish/validate_object_mode.py diff --git a/openpype/hosts/blender/plugins/publish/validate_object_mode.py b/openpype/hosts/blender/plugins/publish/validate_object_mode.py new file mode 100644 index 0000000000..97456a581e --- /dev/null +++ b/openpype/hosts/blender/plugins/publish/validate_object_mode.py @@ -0,0 +1,36 @@ +from typing import List + +import bpy + +import pyblish.api +import openpype.hosts.blender.api.action + + +class ValidateObjectIsInObjectMode(pyblish.api.InstancePlugin): + """Validate that the current object is in Object Mode.""" + + order = pyblish.api.ValidatorOrder - 0.01 + hosts = ["blender"] + families = ["model", "rig"] + category = "geometry" + label = "Object is in Object Mode" + actions = [openpype.hosts.blender.api.action.SelectInvalidAction] + optional = True + + @classmethod + def get_invalid(cls, instance) -> List: + invalid = [] + for obj in [obj for obj in instance]: + try: + if obj.type == 'MESH' or obj.type == 'ARMATURE': + # Check if the object is in object mode. + if not obj.mode == 'OBJECT': + invalid.append(obj) + except: + continue + return invalid + + def process(self, instance): + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError(f"Object found in instance is not in Object Mode: {invalid}") From 2a872de0df42bf088fc3680a9ff0ea6bee9efb1b Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Tue, 13 Apr 2021 10:23:30 +0100 Subject: [PATCH 46/57] Fixed Hound violations --- .../hosts/blender/plugins/publish/validate_object_mode.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/blender/plugins/publish/validate_object_mode.py b/openpype/hosts/blender/plugins/publish/validate_object_mode.py index 97456a581e..1c82628c1c 100644 --- a/openpype/hosts/blender/plugins/publish/validate_object_mode.py +++ b/openpype/hosts/blender/plugins/publish/validate_object_mode.py @@ -1,7 +1,5 @@ from typing import List -import bpy - import pyblish.api import openpype.hosts.blender.api.action @@ -26,11 +24,12 @@ class ValidateObjectIsInObjectMode(pyblish.api.InstancePlugin): # Check if the object is in object mode. if not obj.mode == 'OBJECT': invalid.append(obj) - except: + except Exception: continue return invalid def process(self, instance): invalid = self.get_invalid(instance) if invalid: - raise RuntimeError(f"Object found in instance is not in Object Mode: {invalid}") + raise RuntimeError( + f"Object found in instance is not in Object Mode: {invalid}") From 1d0f18666fb6f075248d3d90140615de87b5632a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 9 Apr 2021 11:02:11 +0100 Subject: [PATCH 47/57] Validate project settings - resolution - framerate - pixel aspect - frame range # Conflicts: # openpype/hosts/tvpaint/plugins/publish/validate_project_settings.py # pype/plugins/tvpaint/publish/collect_instances.py --- .../plugins/publish/collect_instances.py | 11 ++++-- .../plugins/publish/collect_workfile_data.py | 10 +++--- .../publish/validate_project_settings.py | 36 +++++++++++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 openpype/hosts/tvpaint/plugins/publish/validate_project_settings.py diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_instances.py b/openpype/hosts/tvpaint/plugins/publish/collect_instances.py index 57602d9610..68c142c005 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_instances.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_instances.py @@ -18,7 +18,7 @@ class CollectInstances(pyblish.api.ContextPlugin): )) for instance_data in workfile_instances: - instance_data["fps"] = context.data["fps"] + instance_data["fps"] = context.data["sceneFps"] # Store workfile instance data to instance data instance_data["originData"] = copy.deepcopy(instance_data) @@ -32,6 +32,11 @@ class CollectInstances(pyblish.api.ContextPlugin): subset_name = instance_data["subset"] name = instance_data.get("name", subset_name) instance_data["name"] = name + instance_data["label"] = "{} [{}-{}]".format( + name, + context.data["sceneFrameStart"], + context.data["sceneFrameEnd"] + ) active = instance_data.get("active", True) instance_data["active"] = active @@ -73,8 +78,8 @@ class CollectInstances(pyblish.api.ContextPlugin): if instance is None: continue - instance.data["frameStart"] = context.data["frameStart"] - instance.data["frameEnd"] = context.data["frameEnd"] + instance.data["frameStart"] = context.data["sceneFrameStart"] + instance.data["frameEnd"] = context.data["sceneFrameEnd"] self.log.debug("Created instance: {}\n{}".format( instance, json.dumps(instance.data, indent=4) diff --git a/openpype/hosts/tvpaint/plugins/publish/collect_workfile_data.py b/openpype/hosts/tvpaint/plugins/publish/collect_workfile_data.py index 7965112136..e683c66ea9 100644 --- a/openpype/hosts/tvpaint/plugins/publish/collect_workfile_data.py +++ b/openpype/hosts/tvpaint/plugins/publish/collect_workfile_data.py @@ -127,11 +127,11 @@ class CollectWorkfileData(pyblish.api.ContextPlugin): "currentFile": workfile_path, "sceneWidth": width, "sceneHeight": height, - "pixelAspect": pixel_apsect, - "frameStart": frame_start, - "frameEnd": frame_end, - "fps": frame_rate, - "fieldOrder": field_order + "scenePixelAspect": pixel_apsect, + "sceneFrameStart": frame_start, + "sceneFrameEnd": frame_end, + "sceneFps": frame_rate, + "sceneFieldOrder": field_order } self.log.debug( "Scene data: {}".format(json.dumps(scene_data, indent=4)) diff --git a/openpype/hosts/tvpaint/plugins/publish/validate_project_settings.py b/openpype/hosts/tvpaint/plugins/publish/validate_project_settings.py new file mode 100644 index 0000000000..fead3393ae --- /dev/null +++ b/openpype/hosts/tvpaint/plugins/publish/validate_project_settings.py @@ -0,0 +1,36 @@ +import json + +import pyblish.api + + +class ValidateProjectSettings(pyblish.api.ContextPlugin): + """Validate project settings against database. + """ + + label = "Validate Project Settings" + order = pyblish.api.ValidatorOrder + optional = True + + def process(self, context): + scene_data = { + "frameStart": context.data.get("sceneFrameStart"), + "frameEnd": context.data.get("sceneFrameEnd"), + "fps": context.data.get("sceneFps"), + "resolutionWidth": context.data.get("sceneWidth"), + "resolutionHeight": context.data.get("sceneHeight"), + "pixelAspect": context.data.get("scenePixelAspect") + } + invalid = {} + for k in scene_data.keys(): + expected_value = context.data["assetEntity"]["data"][k] + if scene_data[k] != expected_value: + invalid[k] = { + "current": scene_data[k], "expected": expected_value + } + + if invalid: + raise AssertionError( + "Project settings does not match database:\n{}".format( + json.dumps(invalid, sort_keys=True, indent=4) + ) + ) From 12d714ce749191b0928d0ed6f4bdf3b1e06deb57 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 13 Apr 2021 13:30:52 +0200 Subject: [PATCH 48/57] added schemas for tvpaint plugins --- .../schemas/projects_schema/schema_main.json | 4 +++ .../schema_project_tvpaint.json | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json diff --git a/openpype/settings/entities/schemas/projects_schema/schema_main.json b/openpype/settings/entities/schemas/projects_schema/schema_main.json index 565500edd2..6bc158aa60 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_main.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_main.json @@ -82,6 +82,10 @@ "type": "schema", "name": "schema_project_harmony" }, + { + "type": "schema", + "name": "schema_project_tvpaint" + }, { "type": "schema", "name": "schema_project_celaction" diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json new file mode 100644 index 0000000000..b9fe26a57c --- /dev/null +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_tvpaint.json @@ -0,0 +1,32 @@ +{ + "type": "dict", + "collapsible": true, + "key": "tvpaint", + "label": "TVPaint", + "is_file": true, + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "publish", + "label": "Publish plugins", + "is_file": true, + "children": [ + { + "type": "schema_template", + "name": "template_publish_plugin", + "template_data": [ + { + "key": "ValidateMissingLayers", + "label": "ValidateMissingLayers" + } + ] + } + ] + }, + { + "type": "schema", + "name": "schema_publish_gui_filter" + } + ] +} From f7c8b818f88b19aa5c3a6fc2f8f284fe5dda525e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 13 Apr 2021 13:31:05 +0200 Subject: [PATCH 49/57] saved default tvpaint project settings --- .../settings/defaults/project_settings/tvpaint.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 openpype/settings/defaults/project_settings/tvpaint.json diff --git a/openpype/settings/defaults/project_settings/tvpaint.json b/openpype/settings/defaults/project_settings/tvpaint.json new file mode 100644 index 0000000000..d4130c88be --- /dev/null +++ b/openpype/settings/defaults/project_settings/tvpaint.json @@ -0,0 +1,10 @@ +{ + "publish": { + "ValidateMissingLayers": { + "enabled": true, + "optional": true, + "active": true + } + }, + "filters": {} +} \ No newline at end of file From ef8601422aa884ce8279dfe7b4d271f706f00074 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 13 Apr 2021 13:57:08 +0200 Subject: [PATCH 50/57] renamed "Prepare Project" to "Prepare Project (Local)" --- .../action_prepare_project.py | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py index e7b8bfc3e1..adc6a97fb8 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py @@ -13,25 +13,21 @@ from openpype.modules.ftrack.lib.avalon_sync import ( ) -class PrepareProject(BaseAction): - '''Edit meta data action.''' +class PrepareProjectLocal(BaseAction): + """Prepare project attributes in Anatomy.""" - #: Action identifier. - identifier = 'prepare.project' - #: Action label. - label = 'Prepare Project' - #: Action description. - description = 'Set basic attributes on the project' - #: roles that are allowed to register this action + identifier = "prepare.project.local" + label = "Prepare Project (Local)" + description = "Set basic attributes on the project" icon = statics_icon("ftrack", "action_icons", "PrepareProject.svg") settings_key = "prepare_project" # Key to store info about trigerring create folder structure - item_splitter = {'type': 'label', 'value': '---'} + item_splitter = {"type": "label", "value": "---"} def discover(self, session, entities, event): - ''' Validation ''' + """Show only on project.""" if ( len(entities) != 1 or entities[0].entity_type.lower() != "project" @@ -368,4 +364,4 @@ class PrepareProject(BaseAction): def register(session): '''Register plugin. Called when used as an plugin.''' - PrepareProject(session).register() + PrepareProjectLocal(session).register() From 4d1ad337cc2d324018cb35254047fdcc8a0b42e0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 13 Apr 2021 14:09:28 +0200 Subject: [PATCH 51/57] created server version of prepare project --- .../action_prepare_project.py | 365 ++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 openpype/modules/ftrack/event_handlers_server/action_prepare_project.py diff --git a/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py new file mode 100644 index 0000000000..6a1066c39d --- /dev/null +++ b/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py @@ -0,0 +1,365 @@ +import os +import json + +from openpype.api import config, ProjectSettings + +from openpype.modules.ftrack.lib import ServerAction +from openpype.modules.ftrack.lib.avalon_sync import ( + get_pype_attr, + CUST_ATTR_AUTO_SYNC +) + + +class PrepareProjectServer(ServerAction): + """Prepare project attributes in Anatomy.""" + + identifier = "prepare.project.server" + label = "Prepare Project (Server)" + description = "Set basic attributes on the project" + + settings_key = "prepare_project" + + role_list = ["Pypeclub", "Administrator", "Project Manager"] + + # Key to store info about trigerring create folder structure + item_splitter = {"type": "label", "value": "---"} + + def discover(self, session, entities, event): + """Show only on project.""" + if ( + len(entities) != 1 + or entities[0].entity_type.lower() != "project" + ): + return False + + return self.valid_roles(session, entities, event) + + def interface(self, session, entities, event): + if event['data'].get('values', {}): + return + + # Inform user that this may take a while + self.show_message(event, "Preparing data... Please wait", True) + self.log.debug("Preparing data which will be shown") + + self.log.debug("Loading custom attributes") + + project_entity = entities[0] + project_name = project_entity["full_name"] + + try: + project_settings = ProjectSettings(project_name) + except ValueError: + return { + "message": "Project is not synchronized yet", + "success": False + } + + project_anatom_settings = project_settings["project_anatomy"] + root_items = self.prepare_root_items(project_anatom_settings) + + ca_items, multiselect_enumerators = ( + self.prepare_custom_attribute_items(project_anatom_settings) + ) + + self.log.debug("Heavy items are ready. Preparing last items group.") + + title = "Prepare Project" + items = [] + + # Add root items + items.extend(root_items) + + items.append(self.item_splitter) + items.append({ + "type": "label", + "value": "

Set basic Attributes:

" + }) + + items.extend(ca_items) + + # This item will be last (before enumerators) + # - sets value of auto synchronization + auto_sync_name = "avalon_auto_sync" + auto_sync_value = project_entity["custom_attributes"].get( + CUST_ATTR_AUTO_SYNC, False + ) + auto_sync_item = { + "name": auto_sync_name, + "type": "boolean", + "value": auto_sync_value, + "label": "AutoSync to Avalon" + } + # Add autosync attribute + items.append(auto_sync_item) + + # Add enumerator items at the end + for item in multiselect_enumerators: + items.append(item) + + return { + "items": items, + "title": title + } + + def prepare_root_items(self, project_anatom_settings): + self.log.debug("Root items preparation begins.") + + root_items = [] + root_items.append({ + "type": "label", + "value": "

Check your Project root settings

" + }) + root_items.append({ + "type": "label", + "value": ( + "

NOTE: Roots are crutial for path filling" + " (and creating folder structure).

" + ) + }) + root_items.append({ + "type": "label", + "value": ( + "

WARNING: Do not change roots on running project," + " that will cause workflow issues.

" + ) + }) + + empty_text = "Enter root path here..." + + roots_entity = project_anatom_settings["roots"] + for root_name, root_entity in roots_entity.items(): + root_items.append(self.item_splitter) + root_items.append({ + "type": "label", + "value": "Root: \"{}\"".format(root_name) + }) + for platform_name, value_entity in root_entity.items(): + root_items.append({ + "label": platform_name, + "name": "__root__{}__{}".format(root_name, platform_name), + "type": "text", + "value": value_entity.value, + "empty_text": empty_text + }) + + root_items.append({ + "type": "hidden", + "name": "__rootnames__", + "value": json.dumps(list(roots_entity.keys())) + }) + + self.log.debug("Root items preparation ended.") + return root_items + + def _attributes_to_set(self, project_anatom_settings): + attributes_to_set = {} + + attribute_values_by_key = {} + for key, entity in project_anatom_settings["attributes"].items(): + attribute_values_by_key[key] = entity.value + + cust_attrs, hier_cust_attrs = get_pype_attr(self.session, True) + + for attr in hier_cust_attrs: + key = attr["key"] + if key.startswith("avalon_"): + continue + attributes_to_set[key] = { + "label": attr["label"], + "object": attr, + "default": attribute_values_by_key.get(key) + } + + for attr in cust_attrs: + if attr["entity_type"].lower() != "show": + continue + key = attr["key"] + if key.startswith("avalon_"): + continue + attributes_to_set[key] = { + "label": attr["label"], + "object": attr, + "default": attribute_values_by_key.get(key) + } + + # Sort by label + attributes_to_set = dict(sorted( + attributes_to_set.items(), + key=lambda x: x[1]["label"] + )) + return attributes_to_set + + def prepare_custom_attribute_items(self, project_anatom_settings): + items = [] + multiselect_enumerators = [] + attributes_to_set = self._attributes_to_set(project_anatom_settings) + + self.log.debug("Preparing interface for keys: \"{}\"".format( + str([key for key in attributes_to_set]) + )) + + for key, in_data in attributes_to_set.items(): + attr = in_data["object"] + + # initial item definition + item = { + "name": key, + "label": in_data["label"] + } + + # cust attr type - may have different visualization + type_name = attr["type"]["name"].lower() + easy_types = ["text", "boolean", "date", "number"] + + easy_type = False + if type_name in easy_types: + easy_type = True + + elif type_name == "enumerator": + + attr_config = json.loads(attr["config"]) + attr_config_data = json.loads(attr_config["data"]) + + if attr_config["multiSelect"] is True: + multiselect_enumerators.append(self.item_splitter) + multiselect_enumerators.append({ + "type": "label", + "value": in_data["label"] + }) + + default = in_data["default"] + names = [] + for option in sorted( + attr_config_data, key=lambda x: x["menu"] + ): + name = option["value"] + new_name = "__{}__{}".format(key, name) + names.append(new_name) + item = { + "name": new_name, + "type": "boolean", + "label": "- {}".format(option["menu"]) + } + if default: + if isinstance(default, (list, tuple)): + if name in default: + item["value"] = True + else: + if name == default: + item["value"] = True + + multiselect_enumerators.append(item) + + multiselect_enumerators.append({ + "type": "hidden", + "name": "__hidden__{}".format(key), + "value": json.dumps(names) + }) + else: + easy_type = True + item["data"] = attr_config_data + + else: + self.log.warning(( + "Custom attribute \"{}\" has type \"{}\"." + " I don't know how to handle" + ).format(key, type_name)) + items.append({ + "type": "label", + "value": ( + "!!! Can't handle Custom attritubte type \"{}\"" + " (key: \"{}\")" + ).format(type_name, key) + }) + + if easy_type: + item["type"] = type_name + + # default value in interface + default = in_data["default"] + if default is not None: + item["value"] = default + + items.append(item) + + return items, multiselect_enumerators + + def launch(self, session, entities, event): + if not event['data'].get('values', {}): + return + + in_data = event['data']['values'] + + root_values = {} + root_key = "__root__" + for key in tuple(in_data.keys()): + if key.startswith(root_key): + _key = key[len(root_key):] + root_values[_key] = in_data.pop(key) + + root_names = in_data.pop("__rootnames__", None) + root_data = {} + for root_name in json.loads(root_names): + root_data[root_name] = {} + for key, value in tuple(root_values.items()): + prefix = "{}__".format(root_name) + if not key.startswith(prefix): + continue + + _key = key[len(prefix):] + root_data[root_name][_key] = value + + # Find hidden items for multiselect enumerators + keys_to_process = [] + for key in in_data: + if key.startswith("__hidden__"): + keys_to_process.append(key) + + self.log.debug("Preparing data for Multiselect Enumerators") + enumerators = {} + for key in keys_to_process: + new_key = key.replace("__hidden__", "") + enumerator_items = in_data.pop(key) + enumerators[new_key] = json.loads(enumerator_items) + + # find values set for multiselect enumerator + for key, enumerator_items in enumerators.items(): + in_data[key] = [] + + name = "__{}__".format(key) + + for item in enumerator_items: + value = in_data.pop(item) + if value is True: + new_key = item.replace(name, "") + in_data[key].append(new_key) + + self.log.debug("Setting Custom Attribute values") + + project_name = entities[0]["full_name"] + project_settings = ProjectSettings(project_name) + project_anatomy_settings = project_settings["project_anatomy"] + project_anatomy_settings["roots"] = root_data + + custom_attribute_values = {} + attributes_entity = project_anatomy_settings["attributes"] + for key, value in in_data.items(): + if key not in attributes_entity: + custom_attribute_values[key] = value + else: + attributes_entity[key] = value + + project_settings.save() + + entity = entities[0] + for key, value in custom_attribute_values.items(): + entity["custom_attributes"][key] = value + self.log.debug("- Key \"{}\" set to \"{}\"".format(key, value)) + + return True + + +def register(session): + '''Register plugin. Called when used as an plugin.''' + PrepareProjectServer(session).register() From 35d8059a3a4ccfd4749c3bf26e048a8caf1e53cf Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 13 Apr 2021 14:09:41 +0200 Subject: [PATCH 52/57] defined roles in prepare project action --- .../ftrack/event_handlers_user/action_prepare_project.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py index adc6a97fb8..dc1bb7adce 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py @@ -21,6 +21,8 @@ class PrepareProjectLocal(BaseAction): description = "Set basic attributes on the project" icon = statics_icon("ftrack", "action_icons", "PrepareProject.svg") + role_list = ["Pypeclub", "Administrator", "Project Manager"] + settings_key = "prepare_project" # Key to store info about trigerring create folder structure From 43187d8b110bd8f872ac987dec24af766bb0d841 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 13 Apr 2021 14:19:40 +0200 Subject: [PATCH 53/57] removed unused imports --- .../ftrack/event_handlers_server/action_prepare_project.py | 2 +- .../ftrack/event_handlers_user/action_prepare_project.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py index 6a1066c39d..e274ce8eb3 100644 --- a/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py @@ -1,7 +1,7 @@ import os import json -from openpype.api import config, ProjectSettings +from openpype.api import ProjectSettings from openpype.modules.ftrack.lib import ServerAction from openpype.modules.ftrack.lib.avalon_sync import ( diff --git a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py index dc1bb7adce..a885f8571c 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py @@ -1,7 +1,7 @@ import os import json -from openpype.api import config, ProjectSettings +from openpype.api import ProjectSettings from openpype.modules.ftrack.lib import ( BaseAction, From 0fe1bc64f2b55b28b955c36a9cbf4d746bd74e10 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 13 Apr 2021 14:23:53 +0200 Subject: [PATCH 54/57] added PrepareProject to OpenPype Admin group --- .../ftrack/event_handlers_server/action_prepare_project.py | 3 ++- .../ftrack/event_handlers_user/action_prepare_project.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py index e274ce8eb3..decb6c6a75 100644 --- a/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py @@ -14,7 +14,8 @@ class PrepareProjectServer(ServerAction): """Prepare project attributes in Anatomy.""" identifier = "prepare.project.server" - label = "Prepare Project (Server)" + label = "OpenPype Admin" + variant = "- Prepare Project (Server)" description = "Set basic attributes on the project" settings_key = "prepare_project" diff --git a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py index a885f8571c..c2f0533c33 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py @@ -17,7 +17,8 @@ class PrepareProjectLocal(BaseAction): """Prepare project attributes in Anatomy.""" identifier = "prepare.project.local" - label = "Prepare Project (Local)" + label = "OpenPype Admin" + variant = "- Prepare Project (Local)" description = "Set basic attributes on the project" icon = statics_icon("ftrack", "action_icons", "PrepareProject.svg") From 03b2337bf442d686927ce7d4748f76ccdcf7ce29 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 13 Apr 2021 14:24:27 +0200 Subject: [PATCH 55/57] added prepare project settings to server settings --- .../defaults/project_settings/ftrack.json | 8 ++++++++ .../schema_project_ftrack.json | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/openpype/settings/defaults/project_settings/ftrack.json b/openpype/settings/defaults/project_settings/ftrack.json index 03ac8f309f..04f2842b93 100644 --- a/openpype/settings/defaults/project_settings/ftrack.json +++ b/openpype/settings/defaults/project_settings/ftrack.json @@ -7,6 +7,14 @@ "not ready" ] }, + "prepare_project": { + "enabled": true, + "role_list": [ + "Pypeclub", + "Administrator", + "Project manager" + ] + }, "sync_hier_entity_attributes": { "enabled": true, "interest_entity_types": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json b/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json index eefc0e12b7..a801175031 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json @@ -36,6 +36,25 @@ } ] }, + { + "type": "dict", + "key": "prepare_project", + "label": "Prepare Project", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "role_list", + "label": "Roles", + "object_type": "text" + } + ] + }, { "type": "dict", "key": "sync_hier_entity_attributes", From 684dcb918d406404211562e2e1823eafd10f0a5e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 13 Apr 2021 14:35:14 +0200 Subject: [PATCH 56/57] local version is not under OpenPype Admin group --- .../ftrack/event_handlers_user/action_prepare_project.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py index c2f0533c33..e6a161d95f 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py @@ -17,8 +17,7 @@ class PrepareProjectLocal(BaseAction): """Prepare project attributes in Anatomy.""" identifier = "prepare.project.local" - label = "OpenPype Admin" - variant = "- Prepare Project (Local)" + label = "Prepare Project" description = "Set basic attributes on the project" icon = statics_icon("ftrack", "action_icons", "PrepareProject.svg") From d49a93a58a78a22dc9b85336836be9c2846d4d28 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Tue, 13 Apr 2021 14:38:19 +0200 Subject: [PATCH 57/57] removed unused import --- .../ftrack/event_handlers_server/action_prepare_project.py | 1 - .../modules/ftrack/event_handlers_user/action_prepare_project.py | 1 - 2 files changed, 2 deletions(-) diff --git a/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py index decb6c6a75..8248bf532e 100644 --- a/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_server/action_prepare_project.py @@ -1,4 +1,3 @@ -import os import json from openpype.api import ProjectSettings diff --git a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py index e6a161d95f..bd25f995fe 100644 --- a/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py +++ b/openpype/modules/ftrack/event_handlers_user/action_prepare_project.py @@ -1,4 +1,3 @@ -import os import json from openpype.api import ProjectSettings