mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
314 lines
11 KiB
Python
314 lines
11 KiB
Python
import copy
|
|
from collections import defaultdict
|
|
|
|
from Qt import QtWidgets, QtCore, QtGui
|
|
|
|
from openpype.client import get_representations
|
|
from openpype.lib import config
|
|
from openpype.pipeline import load, Anatomy
|
|
from openpype import resources, style
|
|
|
|
from openpype.lib.delivery import (
|
|
sizeof_fmt,
|
|
path_from_representation,
|
|
get_format_dict,
|
|
check_destination_path,
|
|
process_single_file,
|
|
process_sequence,
|
|
collect_frames
|
|
)
|
|
|
|
|
|
class Delivery(load.SubsetLoaderPlugin):
|
|
"""Export selected versions to folder structure from Template"""
|
|
|
|
is_multiple_contexts_compatible = True
|
|
sequence_splitter = "__sequence_splitter__"
|
|
|
|
representations = ["*"]
|
|
families = ["*"]
|
|
tool_names = ["library_loader"]
|
|
|
|
label = "Deliver 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."""
|
|
|
|
def __init__(self, contexts, log=None, parent=None):
|
|
super(DeliveryOptionsDialog, self).__init__(parent=parent)
|
|
|
|
self.setWindowTitle("OpenPype - Deliver versions")
|
|
icon = QtGui.QIcon(resources.get_openpype_icon_filepath())
|
|
self.setWindowIcon(icon)
|
|
|
|
self.setWindowFlags(
|
|
QtCore.Qt.WindowStaysOnTopHint
|
|
| QtCore.Qt.WindowCloseButtonHint
|
|
| QtCore.Qt.WindowMinimizeButtonHint
|
|
)
|
|
|
|
self.setStyleSheet(style.load_stylesheet())
|
|
|
|
project_name = contexts[0]["project"]["name"]
|
|
self.anatomy = Anatomy(project_name)
|
|
self._representations = None
|
|
self.log = log
|
|
self.currently_uploaded = 0
|
|
|
|
self._set_representations(project_name, contexts)
|
|
|
|
dropdown = QtWidgets.QComboBox()
|
|
self.templates = self._get_templates(self.anatomy)
|
|
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()
|
|
|
|
repre_checkboxes_layout = QtWidgets.QFormLayout()
|
|
repre_checkboxes_layout.setContentsMargins(10, 5, 5, 10)
|
|
|
|
self._representation_checkboxes = {}
|
|
for repre in self._get_representation_names():
|
|
checkbox = QtWidgets.QCheckBox()
|
|
checkbox.setChecked(False)
|
|
self._representation_checkboxes[repre] = checkbox
|
|
|
|
checkbox.stateChanged.connect(self._update_selected_label)
|
|
repre_checkboxes_layout.addRow(repre, checkbox)
|
|
|
|
selected_label = QtWidgets.QLabel()
|
|
|
|
input_widget = QtWidgets.QWidget(self)
|
|
input_layout = QtWidgets.QFormLayout(input_widget)
|
|
input_layout.setContentsMargins(10, 15, 5, 5)
|
|
|
|
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")
|
|
btn_delivery.setEnabled(bool(dropdown.currentText()))
|
|
|
|
progress_bar = QtWidgets.QProgressBar(self)
|
|
progress_bar.setMinimum = 0
|
|
progress_bar.setMaximum = 100
|
|
progress_bar.setVisible(False)
|
|
|
|
text_area = QtWidgets.QTextEdit()
|
|
text_area.setReadOnly(True)
|
|
text_area.setVisible(False)
|
|
text_area.setMinimumHeight(100)
|
|
|
|
layout = QtWidgets.QVBoxLayout(self)
|
|
|
|
layout.addWidget(input_widget)
|
|
layout.addStretch(1)
|
|
layout.addWidget(btn_delivery)
|
|
layout.addWidget(progress_bar)
|
|
layout.addWidget(text_area)
|
|
|
|
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.setVisible(True)
|
|
self.btn_delivery.setEnabled(False)
|
|
QtWidgets.QApplication.processEvents()
|
|
|
|
report_items = defaultdict(list)
|
|
|
|
selected_repres = self._get_selected_repres()
|
|
|
|
datetime_data = config.get_datetime_data()
|
|
template_name = self.dropdown.currentText()
|
|
format_dict = get_format_dict(self.anatomy, self.root_line_edit.text())
|
|
for repre in self._representations:
|
|
if repre["name"] not in selected_repres:
|
|
continue
|
|
|
|
repre_path = path_from_representation(repre, self.anatomy)
|
|
|
|
anatomy_data = copy.deepcopy(repre["context"])
|
|
new_report_items = check_destination_path(str(repre["_id"]),
|
|
self.anatomy,
|
|
anatomy_data,
|
|
datetime_data,
|
|
template_name)
|
|
|
|
report_items.update(new_report_items)
|
|
if new_report_items:
|
|
continue
|
|
|
|
args = [
|
|
repre_path,
|
|
repre,
|
|
self.anatomy,
|
|
template_name,
|
|
anatomy_data,
|
|
format_dict,
|
|
report_items,
|
|
self.log
|
|
]
|
|
|
|
if repre.get("files"):
|
|
src_paths = []
|
|
for repre_file in repre["files"]:
|
|
src_path = self.anatomy.fill_root(repre_file["path"])
|
|
src_paths.append(src_path)
|
|
sources_and_frames = collect_frames(src_paths)
|
|
|
|
for src_path, frame in sources_and_frames.items():
|
|
args[0] = src_path
|
|
if frame:
|
|
anatomy_data["frame"] = frame
|
|
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_items))
|
|
self.text_area.setVisible(True)
|
|
|
|
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, anatomy):
|
|
"""Adds list of delivery templates from Anatomy to dropdown."""
|
|
templates = {}
|
|
for template_name, value in anatomy.templates["delivery"].items():
|
|
if not isinstance(value, str) or not value.startswith('{root'):
|
|
continue
|
|
|
|
templates[template_name] = value
|
|
|
|
return templates
|
|
|
|
def _set_representations(self, project_name, contexts):
|
|
version_ids = [context["version"]["_id"] for context in contexts]
|
|
|
|
repres = list(get_representations(
|
|
project_name, version_ids=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:
|
|
files = repre.get("files", [])
|
|
if not files: # for repre without files, cannot divide by 0
|
|
files_selected += 1
|
|
size_selected += 0
|
|
else:
|
|
for repre_file in 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.btn_delivery.setEnabled(True)
|
|
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, report_items):
|
|
"""Format final result and error details as html."""
|
|
msg = "Delivery finished"
|
|
if not report_items:
|
|
msg += " successfully"
|
|
else:
|
|
msg += " with errors"
|
|
txt = "<h2>{}</h2>".format(msg)
|
|
for header, data in report_items.items():
|
|
txt += "<h3>{}</h3>".format(header)
|
|
for item in data:
|
|
txt += "{}<br>".format(item)
|
|
|
|
return txt
|