mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Delivery in LibraryLoader - added new Delivery loader
Extracted methods from existing Delivery Action on FTrack Fixed Delivery Action for sequences
This commit is contained in:
parent
5c8a1a0c94
commit
3e2a16af94
4 changed files with 659 additions and 220 deletions
296
openpype/lib/delivery.py
Normal file
296
openpype/lib/delivery.py
Normal file
|
|
@ -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: {}<br>- Missing keys: \"{}\"<br>"
|
||||
).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: {}<br>"
|
||||
"- Invalid value DataType: \"{}\"<br>"
|
||||
).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": '<p>{}</p>'.format("<br>".join(__items))
|
||||
})
|
||||
|
||||
if not items:
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Delivery Finished"
|
||||
}
|
||||
|
||||
return {
|
||||
"items": items,
|
||||
"title": title,
|
||||
"success": False,
|
||||
"message": "Delivery Finished"
|
||||
}
|
||||
|
|
@ -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: {}<br>- Missing keys: \"{}\"<br>"
|
||||
).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: {}<br>"
|
||||
"- Invalid value DataType: \"{}\"<br>"
|
||||
).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": '<p>{}</p>'.format("<br>".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):
|
||||
|
|
|
|||
309
openpype/plugins/load/delivery.py
Normal file
309
openpype/plugins/load/delivery.py
Normal file
|
|
@ -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 = "<h2>{}</h2>".format(result["message"])
|
||||
for header, data in report_items.items():
|
||||
txt += "<h3>{}</h3>".format(header)
|
||||
for item in data:
|
||||
txt += "{}<br>".format(item)
|
||||
|
||||
return txt
|
||||
31
vendor/debug_library_loader.py
vendored
Normal file
31
vendor/debug_library_loader.py
vendored
Normal file
|
|
@ -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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue