Merge branch 'bugfix/integrate_fails_editorial_publish' into bugfix/OP-3654_Flame-re-timing-produces-frame-range-discrepancy-

This commit is contained in:
Jakub Jezek 2022-07-27 11:26:00 +02:00
commit 41c71c8b86
No known key found for this signature in database
GPG key ID: 730D7C02726179A7
18 changed files with 192 additions and 216 deletions

View file

@ -1,8 +1,8 @@
# Changelog
## [3.12.2-nightly.4](https://github.com/pypeclub/OpenPype/tree/HEAD)
## [3.12.2](https://github.com/pypeclub/OpenPype/tree/3.12.2) (2022-07-27)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.12.1...HEAD)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.12.1...3.12.2)
### 📖 Documentation
@ -17,7 +17,6 @@
- Ftrack: Automatic daily review session creation can define trigger hour [\#3516](https://github.com/pypeclub/OpenPype/pull/3516)
- Ftrack: add source into Note [\#3509](https://github.com/pypeclub/OpenPype/pull/3509)
- Add pack and unpack convenience scripts [\#3502](https://github.com/pypeclub/OpenPype/pull/3502)
- General: Event system [\#3499](https://github.com/pypeclub/OpenPype/pull/3499)
- NewPublisher: Keep plugins with mismatch target in report [\#3498](https://github.com/pypeclub/OpenPype/pull/3498)
- Nuke: load clip with options from settings [\#3497](https://github.com/pypeclub/OpenPype/pull/3497)
- TrayPublisher: implemented render\_mov\_batch [\#3486](https://github.com/pypeclub/OpenPype/pull/3486)

View file

@ -9,6 +9,7 @@ from .settings import (
)
from .lib import (
PypeLogger,
Logger,
Anatomy,
config,
execute,
@ -58,8 +59,6 @@ from .action import (
RepairContextAction
)
# for backward compatibility with Pype 2
Logger = PypeLogger
__all__ = [
"get_system_settings",

View file

@ -136,7 +136,8 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
"tasks": {
task["name"]: {"type": task["type"]}
for task in self.add_tasks},
"representations": []
"representations": [],
"newAssetPublishing": True
})
self.log.debug("__ inst_data: {}".format(pformat(inst_data)))

View file

@ -109,7 +109,8 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
"clipAnnotations": annotations,
# add all additional tags
"tags": phiero.get_track_item_tags(track_item)
"tags": phiero.get_track_item_tags(track_item),
"newAssetPublishing": True
})
# otio clip data

View file

@ -70,7 +70,8 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
"publish": resolve.get_publish_attribute(timeline_item),
"fps": context.data["fps"],
"handleStart": handle_start,
"handleEnd": handle_end
"handleEnd": handle_end,
"newAssetPublishing": True
})
# otio clip data

View file

@ -170,7 +170,8 @@ class CollectInstances(pyblish.api.InstancePlugin):
"frameStart": frame_start,
"frameEnd": frame_end,
"frameStartH": frame_start - handle_start,
"frameEndH": frame_end + handle_end
"frameEndH": frame_end + handle_end,
"newAssetPublishing": True
}
for data_key in instance_data_filter:

View file

@ -63,7 +63,10 @@ from .execute import (
path_to_subprocess_arg,
CREATE_NO_WINDOW
)
from .log import PypeLogger, timeit
from .log import (
Logger,
PypeLogger,
)
from .path_templates import (
merge_dict,
@ -83,8 +86,9 @@ from .anatomy import (
Anatomy
)
from .config import (
from .dateutils import (
get_datetime_data,
get_timestamp,
get_formatted_current_time
)
@ -370,13 +374,13 @@ __all__ = [
"get_datetime_data",
"get_formatted_current_time",
"Logger",
"PypeLogger",
"get_default_components",
"validate_mongo_connection",
"OpenPypeMongoConnection",
"timeit",
"is_overlapping_otio_ranges",
"otio_range_with_handles",
"convert_to_padded_path",

View file

@ -1,82 +1,41 @@
# -*- coding: utf-8 -*-
"""Get configuration data."""
import datetime
import warnings
import functools
def get_datetime_data(datetime_obj=None):
"""Returns current datetime data as dictionary.
class ConfigDeprecatedWarning(DeprecationWarning):
pass
Args:
datetime_obj (datetime): Specific datetime object
Returns:
dict: prepared date & time data
def deprecated(func):
"""Mark functions as deprecated.
Available keys:
"d" - <Day of month number> in shortest possible way.
"dd" - <Day of month number> with 2 digits.
"ddd" - <Week day name> shortened week day. e.g.: `Mon`, ...
"dddd" - <Week day name> full name of week day. e.g.: `Monday`, ...
"m" - <Month number> in shortest possible way. e.g.: `1` if January
"mm" - <Month number> with 2 digits.
"mmm" - <Month name> shortened month name. e.g.: `Jan`, ...
"mmmm" - <Month name> full month name. e.g.: `January`, ...
"yy" - <Year number> shortened year. e.g.: `19`, `20`, ...
"yyyy" - <Year number> full year. e.g.: `2019`, `2020`, ...
"H" - <Hours number 24-hour> shortened hours.
"HH" - <Hours number 24-hour> with 2 digits.
"h" - <Hours number 12-hour> shortened hours.
"hh" - <Hours number 12-hour> with 2 digits.
"ht" - <Midday type> AM or PM.
"M" - <Minutes number> shortened minutes.
"MM" - <Minutes number> with 2 digits.
"S" - <Seconds number> shortened seconds.
"SS" - <Seconds number> with 2 digits.
It will result in a warning being emitted when the function is used.
"""
if not datetime_obj:
datetime_obj = datetime.datetime.now()
year = datetime_obj.strftime("%Y")
month = datetime_obj.strftime("%m")
month_name_full = datetime_obj.strftime("%B")
month_name_short = datetime_obj.strftime("%b")
day = datetime_obj.strftime("%d")
weekday_full = datetime_obj.strftime("%A")
weekday_short = datetime_obj.strftime("%a")
hours = datetime_obj.strftime("%H")
hours_midday = datetime_obj.strftime("%I")
hour_midday_type = datetime_obj.strftime("%p")
minutes = datetime_obj.strftime("%M")
seconds = datetime_obj.strftime("%S")
return {
"d": str(int(day)),
"dd": str(day),
"ddd": weekday_short,
"dddd": weekday_full,
"m": str(int(month)),
"mm": str(month),
"mmm": month_name_short,
"mmmm": month_name_full,
"yy": str(year[2:]),
"yyyy": str(year),
"H": str(int(hours)),
"HH": str(hours),
"h": str(int(hours_midday)),
"hh": str(hours_midday),
"ht": hour_midday_type,
"M": str(int(minutes)),
"MM": str(minutes),
"S": str(int(seconds)),
"SS": str(seconds),
}
@functools.wraps(func)
def new_func(*args, **kwargs):
warnings.simplefilter("always", ConfigDeprecatedWarning)
warnings.warn(
(
"Deprecated import of function '{}'."
" Class was moved to 'openpype.lib.dateutils.{}'."
" Please change your imports."
).format(func.__name__),
category=ConfigDeprecatedWarning
)
return func(*args, **kwargs)
return new_func
@deprecated
def get_datetime_data(datetime_obj=None):
from .dateutils import get_datetime_data
return get_datetime_data(datetime_obj)
@deprecated
def get_formatted_current_time():
return datetime.datetime.now().strftime(
"%Y%m%dT%H%M%SZ"
)
from .dateutils import get_formatted_current_time
return get_formatted_current_time()

95
openpype/lib/dateutils.py Normal file
View file

@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
"""Get configuration data."""
import datetime
def get_datetime_data(datetime_obj=None):
"""Returns current datetime data as dictionary.
Args:
datetime_obj (datetime): Specific datetime object
Returns:
dict: prepared date & time data
Available keys:
"d" - <Day of month number> in shortest possible way.
"dd" - <Day of month number> with 2 digits.
"ddd" - <Week day name> shortened week day. e.g.: `Mon`, ...
"dddd" - <Week day name> full name of week day. e.g.: `Monday`, ...
"m" - <Month number> in shortest possible way. e.g.: `1` if January
"mm" - <Month number> with 2 digits.
"mmm" - <Month name> shortened month name. e.g.: `Jan`, ...
"mmmm" - <Month name> full month name. e.g.: `January`, ...
"yy" - <Year number> shortened year. e.g.: `19`, `20`, ...
"yyyy" - <Year number> full year. e.g.: `2019`, `2020`, ...
"H" - <Hours number 24-hour> shortened hours.
"HH" - <Hours number 24-hour> with 2 digits.
"h" - <Hours number 12-hour> shortened hours.
"hh" - <Hours number 12-hour> with 2 digits.
"ht" - <Midday type> AM or PM.
"M" - <Minutes number> shortened minutes.
"MM" - <Minutes number> with 2 digits.
"S" - <Seconds number> shortened seconds.
"SS" - <Seconds number> with 2 digits.
"""
if not datetime_obj:
datetime_obj = datetime.datetime.now()
year = datetime_obj.strftime("%Y")
month = datetime_obj.strftime("%m")
month_name_full = datetime_obj.strftime("%B")
month_name_short = datetime_obj.strftime("%b")
day = datetime_obj.strftime("%d")
weekday_full = datetime_obj.strftime("%A")
weekday_short = datetime_obj.strftime("%a")
hours = datetime_obj.strftime("%H")
hours_midday = datetime_obj.strftime("%I")
hour_midday_type = datetime_obj.strftime("%p")
minutes = datetime_obj.strftime("%M")
seconds = datetime_obj.strftime("%S")
return {
"d": str(int(day)),
"dd": str(day),
"ddd": weekday_short,
"dddd": weekday_full,
"m": str(int(month)),
"mm": str(month),
"mmm": month_name_short,
"mmmm": month_name_full,
"yy": str(year[2:]),
"yyyy": str(year),
"H": str(int(hours)),
"HH": str(hours),
"h": str(int(hours_midday)),
"hh": str(hours_midday),
"ht": hour_midday_type,
"M": str(int(minutes)),
"MM": str(minutes),
"S": str(int(seconds)),
"SS": str(seconds),
}
def get_timestamp(datetime_obj=None):
"""Get standardized timestamp from datetime object.
Args:
datetime_obj (datetime.datetime): Object of datetime. Current time
is used if not passed.
"""
if datetime_obj is None:
datetime_obj = datetime.datetime.now()
return datetime_obj.strftime(
"%Y%m%dT%H%M%SZ"
)
def get_formatted_current_time():
return get_timestamp()

View file

@ -1,86 +0,0 @@
import git
from tqdm import tqdm
class _GitProgress(git.remote.RemoteProgress):
""" Class handling displaying progress during git operations.
This is using **tqdm** for showing progress bars. As **GitPython**
is parsing progress directly from git command, it is somehow unreliable
as in some operations it is difficult to get total count of iterations
to display meaningful progress bar.
"""
_t = None
_code = 0
_current_status = ''
_current_max = ''
_description = {
256: "Checking out files",
4: "Counting objects",
128: "Finding sources",
32: "Receiving objects",
64: "Resolving deltas",
16: "Writing objects"
}
def __init__(self):
super().__init__()
def __del__(self):
if self._t is not None:
self._t.close()
def _detroy_tqdm(self):
""" Used to close tqdm when operation ended.
"""
if self._t is not None:
self._t.close()
self._t = None
def _check_mask(self, opcode: int) -> bool:
"""" Add meaningful description to **GitPython** opcodes.
:param opcode: OP_MASK opcode
:type opcode: int
:return: String description of opcode
:rtype: str
.. seealso:: For opcodes look at :class:`git.RemoteProgress`
"""
if opcode & self.COUNTING:
return self._description.get(self.COUNTING)
elif opcode & self.CHECKING_OUT:
return self._description.get(self.CHECKING_OUT)
elif opcode & self.WRITING:
return self._description.get(self.WRITING)
elif opcode & self.RECEIVING:
return self._description.get(self.RECEIVING)
elif opcode & self.RESOLVING:
return self._description.get(self.RESOLVING)
elif opcode & self.FINDING_SOURCES:
return self._description.get(self.FINDING_SOURCES)
else:
return "Processing"
def update(self, op_code, cur_count, max_count=None, message=''):
""" Called when git operation update progress.
.. seealso:: For more details see
:func:`git.objects.submodule.base.Submodule.update`
`Documentation <https://gitpython.readthedocs.io/en/\
stable/reference.html#git.objects.submodule.base.Submodule.update>`_
"""
code = self._check_mask(op_code)
if self._current_status != code or self._current_max != max_count:
self._current_max = max_count
self._current_status = code
self._detroy_tqdm()
self._t = tqdm(total=max_count)
self._t.set_description(" . {}".format(code))
self._t.update(cur_count)

View file

@ -42,13 +42,13 @@ except ImportError:
USE_UNICODE = hasattr(__builtins__, "unicode")
class PypeStreamHandler(logging.StreamHandler):
class LogStreamHandler(logging.StreamHandler):
""" StreamHandler class designed to handle utf errors in python 2.x hosts.
"""
def __init__(self, stream=None):
super(PypeStreamHandler, self).__init__(stream)
super(LogStreamHandler, self).__init__(stream)
self.enabled = True
def enable(self):
@ -57,7 +57,6 @@ class PypeStreamHandler(logging.StreamHandler):
Used to silence output
"""
self.enabled = True
pass
def disable(self):
""" Disable StreamHandler
@ -108,13 +107,13 @@ class PypeStreamHandler(logging.StreamHandler):
self.handleError(record)
class PypeFormatter(logging.Formatter):
class LogFormatter(logging.Formatter):
DFT = '%(levelname)s >>> { %(name)s }: [ %(message)s ]'
default_formatter = logging.Formatter(DFT)
def __init__(self, formats):
super(PypeFormatter, self).__init__()
super(LogFormatter, self).__init__()
self.formatters = {}
for loglevel in formats:
self.formatters[loglevel] = logging.Formatter(formats[loglevel])
@ -142,7 +141,7 @@ class PypeFormatter(logging.Formatter):
return out
class PypeMongoFormatter(logging.Formatter):
class MongoFormatter(logging.Formatter):
DEFAULT_PROPERTIES = logging.LogRecord(
'', '', '', '', '', '', '', '').__dict__.keys()
@ -162,7 +161,7 @@ class PypeMongoFormatter(logging.Formatter):
'method': record.funcName,
'lineNumber': record.lineno
}
document.update(PypeLogger.get_process_data())
document.update(Logger.get_process_data())
# Standard document decorated with exception info
if record.exc_info is not None:
@ -182,7 +181,7 @@ class PypeMongoFormatter(logging.Formatter):
return document
class PypeLogger:
class Logger:
DFT = '%(levelname)s >>> { %(name)s }: [ %(message)s ] '
DBG = " - { %(name)s }: [ %(message)s ] "
INF = ">>> [ %(message)s ] "
@ -240,7 +239,7 @@ class PypeLogger:
for handler in logger.handlers:
if isinstance(handler, MongoHandler):
add_mongo_handler = False
elif isinstance(handler, PypeStreamHandler):
elif isinstance(handler, LogStreamHandler):
add_console_handler = False
if add_console_handler:
@ -293,7 +292,7 @@ class PypeLogger:
"username": components["username"],
"password": components["password"],
"capped": True,
"formatter": PypeMongoFormatter()
"formatter": MongoFormatter()
}
if components["port"] is not None:
kwargs["port"] = int(components["port"])
@ -304,10 +303,10 @@ class PypeLogger:
@classmethod
def _get_console_handler(cls):
formatter = PypeFormatter(cls.FORMAT_FILE)
console_handler = PypeStreamHandler()
formatter = LogFormatter(cls.FORMAT_FILE)
console_handler = LogStreamHandler()
console_handler.set_name("PypeStreamHandler")
console_handler.set_name("LogStreamHandler")
console_handler.setFormatter(formatter)
return console_handler
@ -418,9 +417,9 @@ class PypeLogger:
def get_process_name(cls):
"""Process name that is like "label" of a process.
Pype's logging can be used from pype itseld of from hosts. Even in Pype
it's good to know if logs are from Pype tray or from pype's event
server. This should help to identify that information.
OpenPype's logging can be used from OpenPyppe itself of from hosts.
Even in OpenPype process it's good to know if logs are from tray or
from other cli commands. This should help to identify that information.
"""
if cls._process_name is not None:
return cls._process_name
@ -486,23 +485,13 @@ class PypeLogger:
return OpenPypeMongoConnection.get_mongo_client()
def timeit(method):
"""Print time in function.
For debugging.
"""
log = logging.getLogger()
def timed(*args, **kw):
ts = time.time()
result = method(*args, **kw)
te = time.time()
if 'log_time' in kw:
name = kw.get('log_name', method.__name__.upper())
kw['log_time'][name] = int((te - ts) * 1000)
else:
log.debug('%r %2.2f ms' % (method.__name__, (te - ts) * 1000))
print('%r %2.2f ms' % (method.__name__, (te - ts) * 1000))
return result
return timed
class PypeLogger(Logger):
@classmethod
def get_logger(cls, *args, **kwargs):
logger = Logger.get_logger(*args, **kwargs)
# TODO uncomment when replaced most of places
# logger.warning((
# "'openpype.lib.PypeLogger' is deprecated class."
# " Please use 'openpype.lib.Logger' instead."
# ))
return logger

View file

@ -16,7 +16,7 @@ from openpype_modules.ftrack.lib.avalon_sync import CUST_ATTR_ID_KEY
from openpype_modules.ftrack.lib.custom_attributes import (
query_custom_attributes
)
from openpype.lib import config
from openpype.lib.dateutils import get_datetime_data
from openpype.lib.delivery import (
path_from_representation,
get_format_dict,
@ -555,7 +555,7 @@ class Delivery(BaseAction):
format_dict = get_format_dict(anatomy, location_path)
datetime_data = config.get_datetime_data()
datetime_data = get_datetime_data()
for repre in repres_to_deliver:
source_path = repre.get("data", {}).get("path")
debug_msg = "Processing representation {}".format(repre["_id"])

View file

@ -4,10 +4,10 @@ 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.dateutils import get_datetime_data
from openpype.lib.delivery import (
sizeof_fmt,
path_from_representation,
@ -160,7 +160,7 @@ class DeliveryOptionsDialog(QtWidgets.QDialog):
selected_repres = self._get_selected_repres()
datetime_data = config.get_datetime_data()
datetime_data = 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:

View file

@ -5,7 +5,7 @@ Provides:
"""
import pyblish.api
from openpype.api import config
from openpype.lib.dateutils import get_datetime_data
class CollectDateTimeData(pyblish.api.ContextPlugin):
@ -15,4 +15,4 @@ class CollectDateTimeData(pyblish.api.ContextPlugin):
def process(self, context):
key = "datetimeData"
if key not in context.data:
context.data[key] = config.get_datetime_data()
context.data[key] = get_datetime_data()

View file

@ -12,6 +12,7 @@ import pyblish.api
import openpype.api
from openpype.client import (
get_representations,
get_asset_by_name,
get_subset_by_name,
get_version_by_name,
)
@ -273,6 +274,14 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
def register(self, instance, file_transactions, filtered_repres):
project_name = legacy_io.active_project()
# making sure editorial instances have its `assetEntity`
if instance.data.get("newAssetPublishing"):
asset_doc = get_asset_by_name(
project_name,
instance.data["asset"]
)
instance.data["assetEntity"] = asset_doc
instance_stagingdir = instance.data.get("stagingDir")
if not instance_stagingdir:
self.log.info((
@ -426,7 +435,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
"".format(len(prepared_representations)))
def prepare_subset(self, instance, project_name):
asset_doc = instance.data.get("assetEntity")
asset_doc = instance.data["assetEntity"]
subset_name = instance.data["subset"]
self.log.debug("Subset: {}".format(subset_name))

View file

@ -24,6 +24,10 @@ class ValidateAssetDocs(pyblish.api.InstancePlugin):
if instance.data.get("assetEntity"):
self.log.info("Instance has set asset document in its data.")
elif instance.data.get("newAssetPublishing"):
# skip if it is editorial
self.log.info("Editorial instance is no need to check...")
else:
raise PublishValidationError((
"Instance \"{}\" doesn't have asset document "

View file

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
__version__ = "3.12.2-nightly.4"
__version__ = "3.12.2"

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "OpenPype"
version = "3.12.2-nightly.4" # OpenPype
version = "3.12.2" # OpenPype
description = "Open VFX and Animation pipeline with support."
authors = ["OpenPype Team <info@openpype.io>"]
license = "MIT License"