diff --git a/openpype/lib/delivery.py b/openpype/lib/delivery.py
new file mode 100644
index 0000000000..d5b18bca0a
--- /dev/null
+++ b/openpype/lib/delivery.py
@@ -0,0 +1,296 @@
+"""Functions useful for delivery action or loader"""
+import os
+from avalon import pipeline
+from avalon.vendor import filelink
+import shutil
+import clique
+import collections
+
+
+def sizeof_fmt(num, suffix='B'):
+ """Returns formatted string with size in appropriate unit"""
+ for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
+ if abs(num) < 1024.0:
+ return "%3.1f%s%s" % (num, unit, suffix)
+ num /= 1024.0
+ return "%.1f%s%s" % (num, 'Yi', suffix)
+
+
+def path_from_represenation(representation, anatomy):
+ try:
+ template = representation["data"]["template"]
+
+ except KeyError:
+ return None
+
+ try:
+ context = representation["context"]
+ context["root"] = anatomy.roots
+ path = pipeline.format_template_with_optional_keys(
+ context, template
+ )
+
+ except KeyError:
+ # Template references unavailable data
+ return None
+
+ return os.path.normpath(path)
+
+
+def copy_file(src_path, dst_path):
+ """Hardlink file if possible(to save space), copy if not"""
+ if os.path.exists(dst_path):
+ return
+ try:
+ filelink.create(
+ src_path,
+ dst_path,
+ filelink.HARDLINK
+ )
+ except OSError:
+ shutil.copyfile(src_path, dst_path)
+
+
+def get_format_dict(anatomy, location_path):
+ """Returns replaced root values from user provider value.
+
+ Args:
+ anatomy (Anatomy)
+ location_path (str): user provided value
+ Returns:
+ (dict): prepared for formatting of a template
+ """
+ format_dict = {}
+ if location_path:
+ location_path = location_path.replace("\\", "/")
+ root_names = anatomy.root_names_from_templates(
+ anatomy.templates["delivery"]
+ )
+ if root_names is None:
+ format_dict["root"] = location_path
+ else:
+ format_dict["root"] = {}
+ for name in root_names:
+ format_dict["root"][name] = location_path
+ return format_dict
+
+
+def check_destination_path(repre_id,
+ anatomy, anatomy_data,
+ datetime_data, template_name):
+ """ Try to create destination path based on 'template_name'.
+
+ In the case that path cannot be filled, template contains unmatched
+ keys, provide error message to filter out repre later.
+
+ Args:
+ anatomy (Anatomy)
+ anatomy_data (dict): context to fill anatomy
+ datetime_data (dict): values with actual date
+ template_name (str): to pick correct delivery template
+ Returns:
+ (collections.defauldict): {"TYPE_OF_ERROR":"ERROR_DETAIL"}
+ """
+ anatomy_data.update(datetime_data)
+ anatomy_filled = anatomy.format_all(anatomy_data)
+ dest_path = anatomy_filled["delivery"][template_name]
+ report_items = collections.defaultdict(list)
+ sub_msg = None
+ if not dest_path.solved:
+ msg = (
+ "Missing keys in Representation's context"
+ " for anatomy template \"{}\"."
+ ).format(template_name)
+
+ if dest_path.missing_keys:
+ keys = ", ".join(dest_path.missing_keys)
+ sub_msg = (
+ "Representation: {}
- Missing keys: \"{}\"
"
+ ).format(repre_id, keys)
+
+ if dest_path.invalid_types:
+ items = []
+ for key, value in dest_path.invalid_types.items():
+ items.append("\"{}\" {}".format(key, str(value)))
+
+ keys = ", ".join(items)
+ sub_msg = (
+ "Representation: {}
"
+ "- Invalid value DataType: \"{}\"
"
+ ).format(repre_id, keys)
+
+ report_items[msg].append(sub_msg)
+
+ return report_items
+
+
+def process_single_file(
+ src_path, _repre, anatomy, template_name, anatomy_data, format_dict,
+ report_items, log
+):
+ """Copy single file to calculated path based on template
+
+ Args:
+ src_path(str): path of source representation file
+ _repre (dict): full repre, used only in process_sequence, here only
+ as to share same signature
+ anatomy (Anatomy)
+ template_name (string): user selected delivery template name
+ anatomy_data (dict): data from repre to fill anatomy with
+ format_dict (dict): root dictionary with names and values
+ report_items (collections.defaultdict): to return error messages
+ log (Logger): for log printing
+ Returns:
+ (collections.defaultdict , int)
+ """
+ anatomy_filled = anatomy.format(anatomy_data)
+ if format_dict:
+ template_result = anatomy_filled["delivery"][template_name]
+ delivery_path = template_result.rootless.format(**format_dict)
+ else:
+ delivery_path = anatomy_filled["delivery"][template_name]
+
+ delivery_folder = os.path.dirname(delivery_path)
+ if not os.path.exists(delivery_folder):
+ os.makedirs(delivery_folder)
+
+ log.debug("Copying single: {} -> {}".format(src_path, delivery_path))
+ print("Copying single: {} -> {}".format(src_path, delivery_path))
+ copy_file(src_path, delivery_path)
+
+ return report_items, 1
+
+
+def process_sequence(
+ src_path, repre, anatomy, template_name, anatomy_data, format_dict,
+ report_items, log
+):
+ """ For Pype2(mainly - works in 3 too) where representation might not
+ contain files.
+
+ Uses listing physical files (not 'files' on repre as a)might not be
+ present, b)might not be reliable for representation and copying them.
+
+ TODO Should be refactored when files are sufficient to drive all
+ representations.
+
+ Args:
+ src_path(str): path of source representation file
+ repre (dict): full representation
+ anatomy (Anatomy)
+ template_name (string): user selected delivery template name
+ anatomy_data (dict): data from repre to fill anatomy with
+ format_dict (dict): root dictionary with names and values
+ report_items (collections.defaultdict): to return error messages
+ log (Logger): for log printing
+ Returns:
+ (collections.defaultdict , int)
+ """
+ dir_path, file_name = os.path.split(str(src_path))
+
+ context = repre["context"]
+ ext = context.get("ext", context.get("representation"))
+
+ if not ext:
+ msg = "Source extension not found, cannot find collection"
+ report_items[msg].append(src_path)
+ log.warning("{} <{}>".format(msg, context))
+ return report_items, 0
+
+ ext = "." + ext
+
+ src_collections, remainder = clique.assemble(os.listdir(dir_path))
+ src_collection = None
+ for col in src_collections:
+ if col.tail != ext:
+ continue
+
+ src_collection = col
+ break
+
+ if src_collection is None:
+ msg = "Source collection of files was not found"
+ report_items[msg].append(src_path)
+ log.warning("{} <{}>".format(msg, src_path))
+ return report_items, 0
+
+ frame_indicator = "@####@"
+
+ anatomy_data["frame"] = frame_indicator
+ anatomy_filled = anatomy.format(anatomy_data)
+
+ if format_dict:
+ template_result = anatomy_filled["delivery"][template_name]
+ delivery_path = template_result.rootless.format(**format_dict)
+ else:
+ delivery_path = anatomy_filled["delivery"][template_name]
+
+ delivery_folder = os.path.dirname(delivery_path)
+ dst_head, dst_tail = delivery_path.split(frame_indicator)
+ dst_padding = src_collection.padding
+ dst_collection = clique.Collection(
+ head=dst_head,
+ tail=dst_tail,
+ padding=dst_padding
+ )
+
+ if not os.path.exists(delivery_folder):
+ os.makedirs(delivery_folder)
+
+ src_head = src_collection.head
+ src_tail = src_collection.tail
+ uploaded = 0
+ for index in src_collection.indexes:
+ src_padding = src_collection.format("{padding}") % index
+ src_file_name = "{}{}{}".format(src_head, src_padding, src_tail)
+ src = os.path.normpath(
+ os.path.join(dir_path, src_file_name)
+ )
+
+ dst_padding = dst_collection.format("{padding}") % index
+ dst = "{}{}{}".format(dst_head, dst_padding, dst_tail)
+ log.debug("Copying single: {} -> {}".format(src, dst))
+ copy_file(src, dst)
+ uploaded += 1
+
+ return report_items, uploaded
+
+
+def report(report_items):
+ """Returns dict with final status of delivery (succes, fail etc.)."""
+ items = []
+ title = "Delivery report"
+ for msg, _items in report_items.items():
+ if not _items:
+ continue
+
+ if items:
+ items.append({"type": "label", "value": "---"})
+
+ items.append({
+ "type": "label",
+ "value": "# {}".format(msg)
+ })
+ if not isinstance(_items, (list, tuple)):
+ _items = [_items]
+ __items = []
+ for item in _items:
+ __items.append(str(item))
+
+ items.append({
+ "type": "label",
+ "value": '
{}
'.format("
".join(__items))
+ })
+
+ if not items:
+ return {
+ "success": True,
+ "message": "Delivery Finished"
+ }
+
+ return {
+ "items": items,
+ "title": title,
+ "success": False,
+ "message": "Delivery Finished"
+ }
diff --git a/openpype/modules/ftrack/event_handlers_user/action_delivery.py b/openpype/modules/ftrack/event_handlers_user/action_delivery.py
index 88fdbe3669..1cf2a701d8 100644
--- a/openpype/modules/ftrack/event_handlers_user/action_delivery.py
+++ b/openpype/modules/ftrack/event_handlers_user/action_delivery.py
@@ -1,18 +1,21 @@
import os
import copy
import json
-import shutil
import collections
-import clique
from bson.objectid import ObjectId
-from avalon import pipeline
-from avalon.vendor import filelink
-
from openpype.api import Anatomy, config
from openpype.modules.ftrack.lib import BaseAction, statics_icon
from openpype.modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
+from openpype.lib.delivery import (
+ path_from_represenation,
+ get_format_dict,
+ check_destination_path,
+ process_single_file,
+ process_sequence,
+ report
+)
from avalon.api import AvalonMongoDB
@@ -450,18 +453,7 @@ class Delivery(BaseAction):
anatomy = Anatomy(project_name)
- format_dict = {}
- if location_path:
- location_path = location_path.replace("\\", "/")
- root_names = anatomy.root_names_from_templates(
- anatomy.templates["delivery"]
- )
- if root_names is None:
- format_dict["root"] = location_path
- else:
- format_dict["root"] = {}
- for name in root_names:
- format_dict["root"][name] = location_path
+ format_dict = get_format_dict(anatomy, location_path)
datetime_data = config.get_datetime_data()
for repre in repres_to_deliver:
@@ -471,41 +463,14 @@ class Delivery(BaseAction):
debug_msg += " with published path {}.".format(source_path)
self.log.debug(debug_msg)
- # Get destination repre path
anatomy_data = copy.deepcopy(repre["context"])
- anatomy_data.update(datetime_data)
- anatomy_filled = anatomy.format_all(anatomy_data)
- test_path = anatomy_filled["delivery"][anatomy_name]
+ repre_report_items = check_destination_path(anatomy,
+ anatomy_data,
+ datetime_data,
+ anatomy_name)
- if not test_path.solved:
- msg = (
- "Missing keys in Representation's context"
- " for anatomy template \"{}\"."
- ).format(anatomy_name)
-
- if test_path.missing_keys:
- keys = ", ".join(test_path.missing_keys)
- sub_msg = (
- "Representation: {}
- Missing keys: \"{}\"
"
- ).format(str(repre["_id"]), keys)
-
- if test_path.invalid_types:
- items = []
- for key, value in test_path.invalid_types.items():
- items.append("\"{}\" {}".format(key, str(value)))
-
- keys = ", ".join(items)
- sub_msg = (
- "Representation: {}
"
- "- Invalid value DataType: \"{}\"
"
- ).format(str(repre["_id"]), keys)
-
- report_items[msg].append(sub_msg)
- self.log.warning(
- "{} Representation: \"{}\" Filled: <{}>".format(
- msg, str(repre["_id"]), str(test_path)
- )
- )
+ if repre_report_items:
+ report_items.update(repre_report_items)
continue
# Get source repre path
@@ -514,187 +479,25 @@ class Delivery(BaseAction):
if frame:
repre["context"]["frame"] = len(str(frame)) * "#"
- repre_path = self.path_from_represenation(repre, anatomy)
+ repre_path = path_from_represenation(repre, anatomy)
# TODO add backup solution where root of path from component
- # is repalced with root
+ # is replaced with root
args = (
repre_path,
+ repre,
anatomy,
anatomy_name,
anatomy_data,
format_dict,
- report_items
+ report_items,
+ self.log
)
if not frame:
- self.process_single_file(*args)
+ process_single_file(*args)
else:
- self.process_sequence(*args)
+ process_sequence(*args)
- return self.report(report_items)
-
- def process_single_file(
- self, repre_path, anatomy, anatomy_name, anatomy_data, format_dict,
- report_items
- ):
- anatomy_filled = anatomy.format(anatomy_data)
- if format_dict:
- template_result = anatomy_filled["delivery"][anatomy_name]
- delivery_path = template_result.rootless.format(**format_dict)
- else:
- delivery_path = anatomy_filled["delivery"][anatomy_name]
-
- delivery_folder = os.path.dirname(delivery_path)
- if not os.path.exists(delivery_folder):
- os.makedirs(delivery_folder)
-
- self.copy_file(repre_path, delivery_path)
-
- def process_sequence(
- self, repre_path, anatomy, anatomy_name, anatomy_data, format_dict,
- report_items
- ):
- dir_path, file_name = os.path.split(str(repre_path))
-
- base_name, ext = os.path.splitext(file_name)
- file_name_items = None
- if "#" in base_name:
- file_name_items = [part for part in base_name.split("#") if part]
-
- elif "%" in base_name:
- file_name_items = base_name.split("%")
-
- if not file_name_items:
- msg = "Source file was not found"
- report_items[msg].append(repre_path)
- self.log.warning("{} <{}>".format(msg, repre_path))
- return
-
- src_collections, remainder = clique.assemble(os.listdir(dir_path))
- src_collection = None
- for col in src_collections:
- if col.tail != ext:
- continue
-
- # skip if collection don't have same basename
- if not col.head.startswith(file_name_items[0]):
- continue
-
- src_collection = col
- break
-
- if src_collection is None:
- # TODO log error!
- msg = "Source collection of files was not found"
- report_items[msg].append(repre_path)
- self.log.warning("{} <{}>".format(msg, repre_path))
- return
-
- frame_indicator = "@####@"
-
- anatomy_data["frame"] = frame_indicator
- anatomy_filled = anatomy.format(anatomy_data)
-
- if format_dict:
- template_result = anatomy_filled["delivery"][anatomy_name]
- delivery_path = template_result.rootless.format(**format_dict)
- else:
- delivery_path = anatomy_filled["delivery"][anatomy_name]
-
- delivery_folder = os.path.dirname(delivery_path)
- dst_head, dst_tail = delivery_path.split(frame_indicator)
- dst_padding = src_collection.padding
- dst_collection = clique.Collection(
- head=dst_head,
- tail=dst_tail,
- padding=dst_padding
- )
-
- if not os.path.exists(delivery_folder):
- os.makedirs(delivery_folder)
-
- src_head = src_collection.head
- src_tail = src_collection.tail
- for index in src_collection.indexes:
- src_padding = src_collection.format("{padding}") % index
- src_file_name = "{}{}{}".format(src_head, src_padding, src_tail)
- src = os.path.normpath(
- os.path.join(dir_path, src_file_name)
- )
-
- dst_padding = dst_collection.format("{padding}") % index
- dst = "{}{}{}".format(dst_head, dst_padding, dst_tail)
-
- self.copy_file(src, dst)
-
- def path_from_represenation(self, representation, anatomy):
- try:
- template = representation["data"]["template"]
-
- except KeyError:
- return None
-
- try:
- context = representation["context"]
- context["root"] = anatomy.roots
- path = pipeline.format_template_with_optional_keys(
- context, template
- )
-
- except KeyError:
- # Template references unavailable data
- return None
-
- return os.path.normpath(path)
-
- def copy_file(self, src_path, dst_path):
- if os.path.exists(dst_path):
- return
- try:
- filelink.create(
- src_path,
- dst_path,
- filelink.HARDLINK
- )
- except OSError:
- shutil.copyfile(src_path, dst_path)
-
- def report(self, report_items):
- items = []
- title = "Delivery report"
- for msg, _items in report_items.items():
- if not _items:
- continue
-
- if items:
- items.append({"type": "label", "value": "---"})
-
- items.append({
- "type": "label",
- "value": "# {}".format(msg)
- })
- if not isinstance(_items, (list, tuple)):
- _items = [_items]
- __items = []
- for item in _items:
- __items.append(str(item))
-
- items.append({
- "type": "label",
- "value": '{}
'.format("
".join(__items))
- })
-
- if not items:
- return {
- "success": True,
- "message": "Delivery Finished"
- }
-
- return {
- "items": items,
- "title": title,
- "success": False,
- "message": "Delivery Finished"
- }
+ return report(report_items)
def register(session):
diff --git a/openpype/plugins/load/delivery.py b/openpype/plugins/load/delivery.py
new file mode 100644
index 0000000000..013ea73dd2
--- /dev/null
+++ b/openpype/plugins/load/delivery.py
@@ -0,0 +1,309 @@
+import collections
+import os
+import copy
+
+from avalon import api, style
+from avalon.vendor.Qt import QtWidgets, QtCore, QtGui
+from avalon.api import AvalonMongoDB
+from openpype.api import Anatomy, config
+from openpype import resources
+from openpype.api import get_anatomy_settings
+
+from openpype.lib.delivery import (
+ sizeof_fmt,
+ path_from_represenation,
+ get_format_dict,
+ check_destination_path,
+ process_single_file,
+ process_sequence,
+ report
+)
+
+
+class Delivery(api.SubsetLoader):
+ """Export selected versions to folder structure from Template"""
+
+ is_multiple_contexts_compatible = True
+ sequence_splitter = "__sequence_splitter__"
+
+ representations = ["*"]
+ families = ["*"]
+ # tool_names = ["library_loader"]
+
+ label = "Delivery Versions"
+ order = 35
+ icon = "upload"
+ color = "#d8d8d8"
+
+ def message(self, text):
+ msgBox = QtWidgets.QMessageBox()
+ msgBox.setText(text)
+ msgBox.setStyleSheet(style.load_stylesheet())
+ msgBox.setWindowFlags(
+ msgBox.windowFlags() | QtCore.Qt.FramelessWindowHint
+ )
+ msgBox.exec_()
+
+ def load(self, contexts, name=None, namespace=None, options=None):
+ try:
+ dialog = DeliveryOptionsDialog(contexts, self.log)
+ dialog.exec_()
+ except Exception:
+ self.log.error("Failed to deliver versions.", exc_info=True)
+
+
+class DeliveryOptionsDialog(QtWidgets.QDialog):
+ """Dialog to select template where to deliver selected representations."""
+ SIZE_W = 950
+ SIZE_H = 350
+
+ def __init__(self, contexts, log=None, parent=None):
+ super(DeliveryOptionsDialog, self).__init__(parent=parent)
+
+ self.project = contexts[0]["project"]["name"]
+ self._representations = None
+ self.log = log
+ self.currently_uploaded = 0
+
+ self.dbcon = AvalonMongoDB()
+ self.dbcon.Session["AVALON_PROJECT"] = self.project
+ self.dbcon.install()
+
+ self._set_representations(contexts)
+
+ self.setWindowTitle("OpenPype - Deliver versions")
+ icon = QtGui.QIcon(resources.pype_icon_filepath())
+ self.setWindowIcon(icon)
+
+ self.setWindowFlags(
+ QtCore.Qt.WindowCloseButtonHint |
+ QtCore.Qt.WindowMinimizeButtonHint
+ )
+ self.setStyleSheet(style.load_stylesheet())
+ self.setMinimumSize(QtCore.QSize(self.SIZE_W, self.SIZE_H))
+
+ layout = QtWidgets.QVBoxLayout()
+
+ input_layout = QtWidgets.QFormLayout()
+ input_layout.setContentsMargins(10, 15, 5, 5)
+
+ dropdown = QtWidgets.QComboBox()
+ self.templates = self._get_templates(self.project)
+ for name, _ in self.templates.items():
+ dropdown.addItem(name)
+
+ template_label = QtWidgets.QLabel()
+ template_label.setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor))
+ template_label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
+
+ root_line_edit = QtWidgets.QLineEdit()
+ root_line_edit.setText("c:/PETR_TEST") # TEMP
+
+ repre_checkboxes_layout = QtWidgets.QFormLayout()
+ repre_checkboxes_layout.setContentsMargins(10, 5, 5, 20)
+
+ self._representation_checkboxes = {}
+ for repre in self._get_representation_names():
+ checkbox = QtWidgets.QCheckBox()
+ checkbox.setChecked(True)
+ self._representation_checkboxes[repre] = checkbox
+
+ checkbox.stateChanged.connect(self._update_selected_label)
+ repre_checkboxes_layout.addRow(repre, checkbox)
+
+ selected_label = QtWidgets.QLabel()
+
+ input_layout.addRow("Selected representations", selected_label)
+ input_layout.addRow("Delivery template", dropdown)
+ input_layout.addRow("Template value", template_label)
+ input_layout.addRow("Root", root_line_edit)
+ input_layout.addRow("Representations", repre_checkboxes_layout)
+
+ btn_delivery = QtWidgets.QPushButton("Deliver")
+
+ progress_bar = QtWidgets.QProgressBar(self)
+ progress_bar.setMinimum = 0
+ progress_bar.setMaximum = 100
+ progress_bar.hide()
+
+ text_area = QtWidgets.QTextEdit()
+ text_area.setReadOnly(True)
+ text_area.hide()
+ text_area.setMinimumHeight(100)
+
+ layout.addLayout(input_layout)
+ layout.addWidget(btn_delivery)
+ layout.addWidget(progress_bar)
+ layout.addWidget(text_area)
+
+ self.setLayout(layout)
+
+ self.selected_label = selected_label
+ self.template_label = template_label
+ self.dropdown = dropdown
+ self.root_line_edit = root_line_edit
+ self.progress_bar = progress_bar
+ self.text_area = text_area
+ self.btn_delivery = btn_delivery
+
+ self.files_selected, self.size_selected = \
+ self._get_counts(self._get_selected_repres())
+
+ self._update_selected_label()
+ self._update_template_value()
+
+ btn_delivery.clicked.connect(self.deliver)
+ dropdown.currentIndexChanged.connect(self._update_template_value)
+
+ def deliver(self):
+ """Main method to loop through all selected representations"""
+ self.progress_bar.show()
+ self.btn_delivery.setEnabled(False)
+ # self.resize(self.width(), self.height() + 50)
+
+ report_items = collections.defaultdict(list)
+
+ selected_repres = self._get_selected_repres()
+ anatomy = Anatomy(self.project)
+ datetime_data = config.get_datetime_data()
+ template_name = self.dropdown.currentText()
+ format_dict = get_format_dict(anatomy, self.root_line_edit.text())
+ for repre in self._representations:
+ if repre["name"] not in selected_repres:
+ continue
+
+ repre_path = path_from_represenation(repre, anatomy)
+ if not os.path.exists(repre_path):
+ msg = "{} doesn't exist for {}".format(repre_path,
+ repre["_id"])
+ report_items["Source file was not found"].append(msg)
+ continue
+
+ anatomy_data = copy.deepcopy(repre["context"])
+ new_report_items = check_destination_path(str(repre["_id"]),
+ anatomy,
+ anatomy_data,
+ datetime_data,
+ template_name)
+
+ report_items.update(new_report_items)
+ if new_report_items:
+ continue
+
+ args = [
+ repre_path,
+ repre,
+ anatomy,
+ template_name,
+ anatomy_data,
+ format_dict,
+ report_items,
+ self.log
+ ]
+
+ if repre.get("files"):
+ for repre_file in repre["files"]:
+ src_path = anatomy.fill_root(repre_file["path"])
+ args[0] = src_path
+ new_report_items, uploaded = process_single_file(*args)
+ report_items.update(new_report_items)
+ self._update_progress(uploaded)
+ else: # fallback for Pype2 and representations without files
+ frame = repre['context'].get('frame')
+ if frame:
+ repre["context"]["frame"] = len(str(frame)) * "#"
+
+ if not frame:
+ new_report_items, uploaded = process_single_file(*args)
+ else:
+ new_report_items, uploaded = process_sequence(*args)
+ report_items.update(new_report_items)
+ self._update_progress(uploaded)
+
+ self.text_area.setText(self._format_report(report(report_items),
+ report_items))
+ self.text_area.show()
+
+ self.resize(self.width(), self.height() + 125)
+
+ def _get_representation_names(self):
+ """Get set of representation names for checkbox filtering."""
+ return set([repre["name"] for repre in self._representations])
+
+ def _get_templates(self, project_name):
+ """Adds list of delivery templates from Anatomy to dropdown."""
+ settings = get_anatomy_settings(project_name)
+ templates = {}
+ for template_name, value in settings["templates"]["delivery"].items():
+ templates[template_name] = value
+
+ return templates
+
+ def _set_representations(self, contexts):
+ version_ids = [context["version"]["_id"] for context in contexts]
+
+ repres = list(self.dbcon.find({
+ "type": "representation",
+ "parent": {"$in": version_ids}
+ }))
+
+ self._representations = repres
+
+ def _get_counts(self, selected_repres=None):
+ """Returns tuple of number of selected files and their size."""
+ files_selected = 0
+ size_selected = 0
+ for repre in self._representations:
+ if repre["name"] in selected_repres:
+ for repre_file in repre.get("files", []):
+
+ files_selected += 1
+ size_selected += repre_file["size"]
+
+ return files_selected, size_selected
+
+ def _prepare_label(self):
+ """Provides text with no of selected files and their size."""
+ label = "{} files, size {}".format(self.files_selected,
+ sizeof_fmt(self.size_selected))
+ return label
+
+ def _get_selected_repres(self):
+ """Returns list of representation names filtered from checkboxes."""
+ selected_repres = []
+ for repre_name, chckbox in self._representation_checkboxes.items():
+ if chckbox.isChecked():
+ selected_repres.append(repre_name)
+
+ return selected_repres
+
+ def _update_selected_label(self):
+ """Updates label with list of number of selected files."""
+ selected_repres = self._get_selected_repres()
+ self.files_selected, self.size_selected = \
+ self._get_counts(selected_repres)
+ self.selected_label.setText(self._prepare_label())
+
+ def _update_template_value(self, _index=None):
+ """Sets template value to label after selection in dropdown."""
+ name = self.dropdown.currentText()
+ template_value = self.templates.get(name)
+ if template_value:
+ self.template_label.setText(template_value)
+
+ def _update_progress(self, uploaded):
+ """Update progress bar after each repre copied."""
+ self.currently_uploaded += uploaded
+
+ ratio = self.currently_uploaded / self.files_selected
+ self.progress_bar.setValue(ratio * self.progress_bar.maximum())
+
+ def _format_report(self, result, report_items):
+ """Format final result and error details as html."""
+ txt = "{}
".format(result["message"])
+ for header, data in report_items.items():
+ txt += "{}
".format(header)
+ for item in data:
+ txt += "{}
".format(item)
+
+ return txt
diff --git a/vendor/debug_library_loader.py b/vendor/debug_library_loader.py
new file mode 100644
index 0000000000..1bbd963f16
--- /dev/null
+++ b/vendor/debug_library_loader.py
@@ -0,0 +1,31 @@
+import os
+import pyblish
+import pyblish.cli
+import pyblish.plugin
+
+os.environ["AVALON_MONGO"] = "mongodb://localhost:27017"
+os.environ["OPENPYPE_MONGO"] = "mongodb://localhost:27017"
+os.environ["AVALON_DB"] = "avalon"
+os.environ["OPENPYPE_DATABASE_NAME"] = "avalon"
+os.environ["AVALON_TIMEOUT"] = '3000'
+os.environ["OPENPYPE_DEBUG"] = "3"
+os.environ["AVALON_CONFIG"] = "pype"
+os.environ["AVALON_ASSET"] = "Jungle"
+os.environ["AVALON_PROJECT"] = "petr_second"
+
+from avalon.tools.libraryloader import show
+import openpype
+openpype.install()
+
+# REGISTERED = pyblish.plugin.registered_paths()
+# PACKAGEPATH = pyblish.lib.main_package_path()
+# ENVIRONMENT = os.environ.get("PYBLISHPLUGINPATH", "")
+# PLUGINPATH = os.path.join(PACKAGEPATH, '..', 'tests', 'plugins')
+#
+# REGISTERED.append("C:\\Users\\petrk\\PycharmProjects\\Pype3.0\\pype\\openpype\\plugins\\load")
+# pyblish.plugin.deregister_all_paths()
+# for path in REGISTERED:
+# register_plugin_path(avalon.Loader, LOAD_PATH)
+# pyblish.plugin.register_plugin_path(path)
+
+show(debug=True, show_projects=True, show_libraries=True)
\ No newline at end of file