Merge branch 'develop' into enhancement/add_missing_repair_action_validate_resolution_setting_3dsmax

This commit is contained in:
Libor Batek 2023-12-06 19:48:59 +01:00 committed by GitHub
commit b6c698aa12
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 190 additions and 237 deletions

View file

@ -296,12 +296,15 @@ def run(script):
@click.option("--mongo_url",
help="MongoDB for testing.",
default=None)
@click.option("--dump_databases",
help="Dump all databases to data folder.",
default=None)
def runtests(folder, mark, pyargs, test_data_folder, persist, app_variant,
timeout, setup_only, mongo_url, app_group):
timeout, setup_only, mongo_url, app_group, dump_databases):
"""Run all automatic tests after proper initialization via start.py"""
PypeCommands().run_tests(folder, mark, pyargs, test_data_folder,
persist, app_variant, timeout, setup_only,
mongo_url, app_group)
mongo_url, app_group, dump_databases)
@main.command(help="DEPRECATED - run sync server")

View file

@ -1,6 +1,6 @@
# AfterEffects Integration
Requirements: This extension requires use of Javascript engine, which is
Requirements: This extension requires use of Javascript engine, which is
available since CC 16.0.
Please check your File>Project Settings>Expressions>Expressions Engine
@ -13,28 +13,26 @@ The After Effects integration requires two components to work; `extension` and `
To install the extension download [Extension Manager Command Line tool (ExManCmd)](https://github.com/Adobe-CEP/Getting-Started-guides/tree/master/Package%20Distribute%20Install#option-2---exmancmd).
```
ExManCmd /install {path to addon}/api/extension.zxp
ExManCmd /install {path to avalon-core}\avalon\photoshop\extension.zxp
```
OR
download [Anastasiys Extension Manager](https://install.anastasiy.com/)
`{path to addon}` will be most likely in your AppData (on Windows, in your user data folder in Linux and MacOS.)
### Server
The easiest way to get the server and After Effects launch is with:
```
python -c ^"import openpype.hosts.photoshop;openpype.hosts..aftereffects.launch(""c:\Program Files\Adobe\Adobe After Effects 2020\Support Files\AfterFX.exe"")^"
python -c ^"import avalon.photoshop;avalon.aftereffects.launch(""c:\Program Files\Adobe\Adobe After Effects 2020\Support Files\AfterFX.exe"")^"
```
`avalon.aftereffects.launch` launches the application and server, and also closes the server when After Effects exists.
## Usage
The After Effects extension can be found under `Window > Extensions > AYON`. Once launched you should be presented with a panel like this:
The After Effects extension can be found under `Window > Extensions > OpenPype`. Once launched you should be presented with a panel like this:
![Ayon Panel](panel.png "Ayon Panel")
![Avalon Panel](panel.PNG "Avalon Panel")
## Developing
@ -45,8 +43,8 @@ When developing the extension you can load it [unsigned](https://github.com/Adob
When signing the extension you can use this [guide](https://github.com/Adobe-CEP/Getting-Started-guides/tree/master/Package%20Distribute%20Install#package-distribute-install-guide).
```
ZXPSignCmd -selfSignedCert NA NA Ayon Avalon-After-Effects Ayon extension.p12
ZXPSignCmd -sign {path to addon}/api/extension {path to addon}/api/extension.zxp extension.p12 Ayon
ZXPSignCmd -selfSignedCert NA NA Avalon Avalon-After-Effects avalon extension.p12
ZXPSignCmd -sign {path to avalon-core}\avalon\aftereffects\extension {path to avalon-core}\avalon\aftereffects\extension.zxp extension.p12 avalon
```
### Plugin Examples
@ -54,14 +52,14 @@ ZXPSignCmd -sign {path to addon}/api/extension {path to addon}/api/extension.zxp
These plugins were made with the [polly config](https://github.com/mindbender-studio/config). To fully integrate and load, you will have to use this config and add `image` to the [integration plugin](https://github.com/mindbender-studio/config/blob/master/polly/plugins/publish/integrate_asset.py).
Expected deployed extension location on default Windows:
`c:\Program Files (x86)\Common Files\Adobe\CEP\extensions\io.ynput.AE.panel`
`c:\Program Files (x86)\Common Files\Adobe\CEP\extensions\com.openpype.AE.panel`
For easier debugging of Javascript:
https://community.adobe.com/t5/download-install/adobe-extension-debuger-problem/td-p/10911704?page=1
Add (optional) --enable-blink-features=ShadowDOMV0,CustomElementsV0 when starting Chrome
then localhost:8092
Or use Visual Studio Code https://medium.com/adobetech/extendscript-debugger-for-visual-studio-code-public-release-a2ff6161fa01
Or use Visual Studio Code https://medium.com/adobetech/extendscript-debugger-for-visual-studio-code-public-release-a2ff6161fa01
## Resources
- https://javascript-tools-guide.readthedocs.io/introduction/index.html
- https://github.com/Adobe-CEP/Getting-Started-guides

View file

@ -1,31 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionList>
<Extension Id="io.ynput.AE.panel">
<Extension Id="com.openpype.AE.panel">
<HostList>
<!-- Comment Host tags according to the apps you want your panel to support -->
<!-- Photoshop -->
<Host Name="PHXS" Port="8088"/>
<!-- Illustrator -->
<Host Name="ILST" Port="8089"/>
<!-- InDesign -->
<Host Name="IDSN" Port="8090" />
<!-- Premiere -->
<Host Name="PPRO" Port="8091" />
<!-- AfterEffects -->
<Host Name="AEFT" Port="8092" />
<!-- PRELUDE -->
<Host Name="PRLD" Port="8093" />
<!-- FLASH Pro -->
<Host Name="FLPR" Port="8094" />
</HostList>
</Extension>
</ExtensionList>

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionManifest Version="8.0" ExtensionBundleId="io.ynput.AE.panel" ExtensionBundleVersion="1.1.0"
ExtensionBundleName="io.ynput.AE.panel" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ExtensionManifest Version="8.0" ExtensionBundleId="com.openpype.AE.panel" ExtensionBundleVersion="1.0.27"
ExtensionBundleName="com.openpype.AE.panel" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ExtensionList>
<Extension Id="io.ynput.AE.panel" Version="1.0" />
<Extension Id="com.openpype.AE.panel" Version="1.0" />
</ExtensionList>
<ExecutionEnvironment>
<HostList>
@ -38,7 +38,7 @@
</RequiredRuntimeList>
</ExecutionEnvironment>
<DispatchInfoList>
<Extension Id="io.ynput.AE.panel">
<Extension Id="com.openpype.AE.panel">
<DispatchInfo >
<Resources>
<MainPath>./index.html</MainPath>
@ -49,7 +49,7 @@
</Lifecycle>
<UI>
<Type>Panel</Type>
<Menu>AYON</Menu>
<Menu>OpenPype</Menu>
<Geometry>
<Size>
<Height>200</Height>
@ -66,7 +66,7 @@
</Geometry>
<Icons>
<Icon Type="Normal">./icons/ayon_logo.png</Icon>
<Icon Type="Normal">./icons/iconNormal.png</Icon>
<Icon Type="RollOver">./icons/iconRollover.png</Icon>
<Icon Type="Disabled">./icons/iconDisabled.png</Icon>
<Icon Type="DarkNormal">./icons/iconDarkNormal.png</Icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

View file

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -2,10 +2,12 @@
"""OpenPype startup script."""
from openpype.pipeline import install_host
from openpype.hosts.houdini.api import HoudiniHost
from openpype import AYON_SERVER_ENABLED
def main():
print("Installing OpenPype ...")
print("Installing {} ...".format(
"AYON" if AYON_SERVER_ENABLED else "OpenPype"))
install_host(HoudiniHost())

View file

@ -2,10 +2,12 @@
"""OpenPype startup script."""
from openpype.pipeline import install_host
from openpype.hosts.houdini.api import HoudiniHost
from openpype import AYON_SERVER_ENABLED
def main():
print("Installing OpenPype ...")
print("Installing {} ...".format(
"AYON" if AYON_SERVER_ENABLED else "OpenPype"))
install_host(HoudiniHost())

View file

@ -2,10 +2,12 @@
"""OpenPype startup script."""
from openpype.pipeline import install_host
from openpype.hosts.houdini.api import HoudiniHost
from openpype import AYON_SERVER_ENABLED
def main():
print("Installing OpenPype ...")
print("Installing {} ...".format(
"AYON" if AYON_SERVER_ENABLED else "OpenPype"))
install_host(HoudiniHost())

View file

@ -2,10 +2,12 @@
"""OpenPype startup script."""
from openpype.pipeline import install_host
from openpype.hosts.houdini.api import HoudiniHost
from openpype import AYON_SERVER_ENABLED
def main():
print("Installing OpenPype ...")
print("Installing {} ...".format(
"AYON" if AYON_SERVER_ENABLED else "OpenPype"))
install_host(HoudiniHost())

View file

@ -9,7 +9,7 @@ from openpype.pipeline import (
)
from openpype.pipeline.load.utils import get_representation_path_from_context
from openpype.pipeline.colorspace import (
get_imageio_colorspace_from_filepath,
get_imageio_file_rules_colorspace_from_filepath,
get_imageio_config,
get_imageio_file_rules
)
@ -285,10 +285,10 @@ class FileNodeLoader(load.LoaderPlugin):
)
path = get_representation_path_from_context(context)
colorspace = get_imageio_colorspace_from_filepath(
path=path,
host_name=host_name,
project_name=project_name,
colorspace = get_imageio_file_rules_colorspace_from_filepath(
path,
host_name,
project_name,
config_data=config_data,
file_rules=file_rules,
project_settings=project_settings

View file

@ -371,7 +371,6 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
continue
for node in data["nodes"]:
lib.set_attribute(data["attribute"], data["values"][0], node)
with lib.renderlayer(layer_node):
# Repair animation must be enabled
@ -392,13 +391,11 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
if renderer != "renderman":
prefix_attr = RenderSettings.get_image_prefix_attr(renderer)
fname_prefix = default_prefix
cmds.setAttr("{}.{}".format(node, prefix_attr),
fname_prefix, type="string")
cmds.setAttr(prefix_attr, fname_prefix, type="string")
# Repair padding
padding_attr = RenderSettings.get_padding_attr(renderer)
cmds.setAttr("{}.{}".format(node, padding_attr),
cls.DEFAULT_PADDING)
cmds.setAttr(padding_attr, cls.DEFAULT_PADDING)
else:
# renderman handles stuff differently
cmds.setAttr("rmanGlobals.imageFileFormat",

View file

@ -9,7 +9,7 @@ The Photoshop integration requires two components to work; `extension` and `serv
To install the extension download [Extension Manager Command Line tool (ExManCmd)](https://github.com/Adobe-CEP/Getting-Started-guides/tree/master/Package%20Distribute%20Install#option-2---exmancmd).
```
ExManCmd /install {path to addon}/api/extension.zxp
ExManCmd /install {path to avalon-core}\avalon\photoshop\extension.zxp
```
### Server
@ -17,16 +17,16 @@ ExManCmd /install {path to addon}/api/extension.zxp
The easiest way to get the server and Photoshop launch is with:
```
python -c ^"import openpype.hosts.photoshop;openpype.hosts.photoshop.launch(""C:\Program Files\Adobe\Adobe Photoshop 2020\Photoshop.exe"")^"
python -c ^"import avalon.photoshop;avalon.photoshop.launch(""C:\Program Files\Adobe\Adobe Photoshop 2020\Photoshop.exe"")^"
```
`avalon.photoshop.launch` launches the application and server, and also closes the server when Photoshop exists.
## Usage
The Photoshop extension can be found under `Window > Extensions > Ayon`. Once launched you should be presented with a panel like this:
The Photoshop extension can be found under `Window > Extensions > Avalon`. Once launched you should be presented with a panel like this:
![Ayon Panel](panel.png "AYON Panel")
![Avalon Panel](panel.PNG "Avalon Panel")
## Developing
@ -37,7 +37,7 @@ When developing the extension you can load it [unsigned](https://github.com/Adob
When signing the extension you can use this [guide](https://github.com/Adobe-CEP/Getting-Started-guides/tree/master/Package%20Distribute%20Install#package-distribute-install-guide).
```
ZXPSignCmd -selfSignedCert NA NA Ayon Ayon-Photoshop Ayon extension.p12
ZXPSignCmd -selfSignedCert NA NA Avalon Avalon-Photoshop avalon extension.p12
ZXPSignCmd -sign {path to avalon-core}\avalon\photoshop\extension {path to avalon-core}\avalon\photoshop\extension.zxp extension.p12 avalon
```

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<ExtensionList>
<Extension Id="io.ynput.PS.panel">
<Extension Id="com.openpype.PS.panel">
<HostList>
<Host Name="PHXS" Port="8078"/>
<Host Name="FLPR" Port="8078"/>
</HostList>
</Extension>
</ExtensionList>
</ExtensionList>

View file

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<ExtensionManifest ExtensionBundleId="io.ynput.PS.panel" ExtensionBundleVersion="1.1.0" Version="7.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ExtensionManifest ExtensionBundleId="com.openpype.PS.panel" ExtensionBundleVersion="1.0.12" Version="7.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ExtensionList>
<Extension Id="io.ynput.PS.panel" Version="1.0.1" />
<Extension Id="com.openpype.PS.panel" Version="1.0.1" />
</ExtensionList>
<ExecutionEnvironment>
<HostList>
@ -16,7 +16,7 @@
</RequiredRuntimeList>
</ExecutionEnvironment>
<DispatchInfoList>
<Extension Id="io.ynput.PS.panel">
<Extension Id="com.openpype.PS.panel">
<DispatchInfo>
<Resources>
<MainPath>./index.html</MainPath>
@ -32,7 +32,7 @@
</Lifecycle>
<UI>
<Type>Panel</Type>
<Menu>AYON</Menu>
<Menu>OpenPype</Menu>
<Geometry>
<Size>
<Width>300</Width>
@ -44,7 +44,7 @@
</MaxSize>
</Geometry>
<Icons>
<Icon Type="Normal">./icons/ayon_logo.png</Icon>
<Icon Type="Normal">./icons/avalon-logo-48.png</Icon>
</Icons>
</UI>
</DispatchInfo>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
"""Collect original base name for use in templates."""
from pathlib import Path
import pyblish.api
class CollectOriginalBasename(pyblish.api.InstancePlugin):
"""Collect original file base name."""
order = pyblish.api.CollectorOrder + 0.498
label = "Collect Base Name"
hosts = ["standalonepublisher"]
families = ["simpleUnrealTexture"]
def process(self, instance):
file_name = Path(instance.data["representations"][0]["files"])
instance.data["originalBasename"] = file_name.stem

View file

@ -1,29 +0,0 @@
# -*- coding: utf-8 -*-
"""Validator for correct file naming."""
import re
import pyblish.api
from openpype.pipeline.publish import (
ValidateContentsOrder,
PublishXmlValidationError,
)
class ValidateSimpleUnrealTextureNaming(pyblish.api.InstancePlugin):
label = "Validate Unreal Texture Names"
hosts = ["standalonepublisher"]
families = ["simpleUnrealTexture"]
order = ValidateContentsOrder
regex = "^T_{asset}.*"
def process(self, instance):
file_name = instance.data.get("originalBasename")
self.log.info(file_name)
pattern = self.regex.format(asset=instance.data.get("asset"))
if not re.match(pattern, file_name):
msg = f"Invalid file name {file_name}"
raise PublishXmlValidationError(
self, msg, formatting_data={
"invalid_file": file_name,
"asset": instance.data.get("asset")
})

View file

@ -73,7 +73,7 @@ class CollectRenderInstances(pyblish.api.InstancePlugin):
render_layer_id = creator_attributes["render_layer_instance_id"]
for in_data in instance.context.data["workfileInstances"]:
if (
in_data["creator_identifier"] == "render.layer"
in_data.get("creator_identifier") == "render.layer"
and in_data["instance_id"] == render_layer_id
):
render_layer_data = in_data

View file

@ -464,8 +464,7 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin,
self.log.info("Submitted job to Deadline: {}.".format(job_id))
# TODO: Find a way that's more generic and not render type specific
export_job = instance.data["exportJob"]
if export_job:
if "exportJob" in instance.data:
self.log.info("Splitting export and render in two jobs")
self.log.info("Export job id: %s", job_id)
render_job_info = self.get_job_info(dependency_job_ids=[job_id])

View file

@ -2,6 +2,8 @@
"""Collect default Deadline server."""
import pyblish.api
from openpype import AYON_SERVER_ENABLED
class CollectDefaultDeadlineServer(pyblish.api.ContextPlugin):
"""Collect default Deadline Webservice URL.
@ -30,24 +32,26 @@ class CollectDefaultDeadlineServer(pyblish.api.ContextPlugin):
self.log.error("Cannot get OpenPype Deadline module.")
raise AssertionError("OpenPype Deadline module not found.")
# get default deadline webservice url from deadline module
self.log.debug(deadline_module.deadline_urls)
context.data["defaultDeadline"] = deadline_module.deadline_urls["default"] # noqa: E501
deadline_settings = context.data["project_settings"]["deadline"]
deadline_server_name = None
if AYON_SERVER_ENABLED:
deadline_server_name = deadline_settings["deadline_server"]
else:
deadline_servers = deadline_settings["deadline_servers"]
if deadline_servers:
deadline_server_name = deadline_servers[0]
context.data["deadlinePassMongoUrl"] = self.pass_mongo_url
context.data["deadlinePassMongoUrl"] = self.pass_mongo_url
deadline_servers = (context.data
["project_settings"]
["deadline"]
["deadline_servers"])
if deadline_servers:
deadline_server_name = deadline_servers[0]
deadline_webservice = None
if deadline_server_name:
deadline_webservice = deadline_module.deadline_urls.get(
deadline_server_name)
if deadline_webservice:
context.data["defaultDeadline"] = deadline_webservice
self.log.debug("Overriding from project settings with {}".format( # noqa: E501
deadline_webservice))
context.data["defaultDeadline"] = \
context.data["defaultDeadline"].strip().rstrip("/")
default_deadline_webservice = deadline_module.deadline_urls["default"]
deadline_webservice = (
deadline_webservice
or default_deadline_webservice
)
context.data["defaultDeadline"] = deadline_webservice.strip().rstrip("/") # noqa

View file

@ -137,7 +137,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
"mvUsd",
"mvUsdComposition",
"mvUsdOverride",
"simpleUnrealTexture",
"online",
"uasset",
"blendScene",

View file

@ -185,8 +185,7 @@ class PypeCommands:
task,
app,
env_group=env_group,
launch_type=LaunchTypes.farm_render,
env={}
launch_type=LaunchTypes.farm_render
)
else:
env = os.environ.copy()
@ -215,7 +214,7 @@ class PypeCommands:
def run_tests(self, folder, mark, pyargs,
test_data_folder, persist, app_variant, timeout, setup_only,
mongo_url, app_group):
mongo_url, app_group, dump_databases):
"""
Runs tests from 'folder'
@ -276,6 +275,13 @@ class PypeCommands:
if mongo_url:
args.extend(["--mongo_url", mongo_url])
if dump_databases:
msg = "dump_databases format is not recognized: {}".format(
dump_databases
)
assert dump_databases in ["bson", "json"], msg
args.extend(["--dump_databases", dump_databases])
print("run_tests args: {}".format(args))
import pytest
pytest.main(args)

View file

@ -38,16 +38,6 @@
"file": "{subset}_{@version}<_{output}><.{@frame}>.{ext}",
"path": "{@folder}/{@file}"
},
"simpleUnrealTextureHero": {
"folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/hero",
"file": "{originalBasename}.{ext}",
"path": "{@folder}/{@file}"
},
"simpleUnrealTexture": {
"folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{@version}",
"file": "{originalBasename}_{@version}.{ext}",
"path": "{@folder}/{@file}"
},
"online": {
"folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}",
"file": "{originalBasename}<.{@frame}><_{udim}>.{ext}",
@ -68,8 +58,6 @@
},
"__dynamic_keys_labels__": {
"maya2unreal": "Maya to Unreal",
"simpleUnrealTextureHero": "Simple Unreal Texture - Hero",
"simpleUnrealTexture": "Simple Unreal Texture",
"online": "online",
"tycache": "tycache",
"source": "source",

View file

@ -322,22 +322,9 @@
"animation",
"setdress",
"layout",
"mayaScene",
"simpleUnrealTexture"
"mayaScene"
],
"template_name_profiles": [
{
"families": [
"simpleUnrealTexture"
],
"hosts": [
"standalonepublisher"
],
"task_types": [],
"task_names": [],
"template_name": "simpleUnrealTextureHero"
}
]
"template_name_profiles": []
},
"CleanUp": {
"paterns": [],
@ -519,17 +506,6 @@
"task_names": [],
"template_name": "render"
},
{
"families": [
"simpleUnrealTexture"
],
"hosts": [
"standalonepublisher"
],
"task_types": [],
"task_names": [],
"template_name": "simpleUnrealTexture"
},
{
"families": [
"staticMesh",
@ -565,19 +541,7 @@
"template_name": "tycache"
}
],
"hero_template_name_profiles": [
{
"families": [
"simpleUnrealTexture"
],
"hosts": [
"standalonepublisher"
],
"task_types": [],
"task_names": [],
"template_name": "simpleUnrealTextureHero"
}
],
"hero_template_name_profiles": [],
"custom_staging_dir_profiles": []
}
},

View file

@ -133,14 +133,6 @@
],
"help": "Texture files with UDIM together with worfile"
},
"create_simple_unreal_texture": {
"name": "simple_unreal_texture",
"label": "Simple Unreal Texture",
"family": "simpleUnrealTexture",
"icon": "Image",
"defaults": [],
"help": "Texture files with Unreal naming convention"
},
"create_vdb": {
"name": "vdb",
"label": "VDB Volumetric Data",

View file

@ -244,19 +244,6 @@
".hda"
]
},
{
"family": "simpleUnrealTexture",
"identifier": "",
"label": "Simple UE texture",
"icon": "fa.image",
"default_variants": [],
"description": "Simple Unreal Engine texture",
"detailed_description": "Texture files with Unreal Engine naming conventions",
"allow_sequences": false,
"allow_multiple_items": true,
"allow_version_control": false,
"extensions": []
},
{
"family": "audio",
"identifier": "",

View file

@ -632,6 +632,14 @@ class TrayManager:
self.exit()
elif result.restart or result.token_changed:
# Remove environment variables from current connection
# - keep develop, staging, headless values
for key in {
"AYON_SERVER_URL",
"AYON_API_KEY",
"AYON_BUNDLE_NAME",
}:
os.environ.pop(key, None)
self.restart()
def _on_restart_action(self):

View file

@ -14,15 +14,35 @@ class ServerListSubmodel(BaseSettingsModel):
value: str = Field(title="Value")
async def defined_deadline_ws_name_enum_resolver(
addon: "BaseServerAddon",
settings_variant: str = "production",
project_name: str | None = None,
) -> list[str]:
"""Provides list of names of configured Deadline webservice urls."""
if addon is None:
return []
settings = await addon.get_studio_settings(variant=settings_variant)
ws_urls = []
for deadline_url_item in settings.deadline_urls:
ws_urls.append(deadline_url_item.name)
return ws_urls
class DeadlineSettings(BaseSettingsModel):
deadline_urls: list[ServerListSubmodel] = Field(
default_factory=list,
title="System Deadline Webservice URLs",
scope=["studio"],
)
deadline_servers: list[str] = Field(
title="Project deadline servers",
deadline_server: str = Field(
title="Project deadline server",
section="---",
scope=["project"],
enum_resolver=defined_deadline_ws_name_enum_resolver
)
publish: PublishPluginsModel = Field(
default_factory=PublishPluginsModel,
@ -42,7 +62,6 @@ DEFAULT_VALUES = {
"value": "http://127.0.0.1:8082"
}
],
# TODO: this needs to be dynamic from "deadline_urls"
"deadline_servers": [],
"deadline_server": "default",
"publish": DEFAULT_DEADLINE_PLUGINS_SETTINGS
}

View file

@ -3,12 +3,6 @@ from pydantic import Field, validator
from ayon_server.settings import BaseSettingsModel, ensure_unique_names
class CollectDefaultDeadlineServerModel(BaseSettingsModel):
"""Settings for event handlers running in ftrack service."""
pass_mongo_url: bool = Field(title="Pass Mongo url to job")
class CollectDeadlinePoolsModel(BaseSettingsModel):
"""Settings Deadline default pools."""
@ -286,12 +280,6 @@ class ProcessSubmittedJobOnFarmModel(BaseSettingsModel):
class PublishPluginsModel(BaseSettingsModel):
CollectDefaultDeadlineServer: CollectDefaultDeadlineServerModel = Field(
default_factory=CollectDefaultDeadlineServerModel,
title="Default Deadline Webservice")
CollectDefaultDeadlineServer: CollectDefaultDeadlineServerModel = Field(
default_factory=CollectDefaultDeadlineServerModel,
title="Default Deadline Webservice")
CollectDeadlinePools: CollectDeadlinePoolsModel = Field(
default_factory=CollectDeadlinePoolsModel,
title="Default Pools")
@ -332,9 +320,6 @@ class PublishPluginsModel(BaseSettingsModel):
DEFAULT_DEADLINE_PLUGINS_SETTINGS = {
"CollectDefaultDeadlineServer": {
"pass_mongo_url": True
},
"CollectDeadlinePools": {
"primary_pool": "",
"secondary_pool": ""

View file

@ -39,6 +39,11 @@ def pytest_addoption(parser):
help="Provide url of the Mongo database."
)
parser.addoption(
"--dump_databases", action="store", default=None,
help="Dump databases to data folder."
)
@pytest.fixture(scope="module")
def test_data_folder(request):
@ -75,6 +80,11 @@ def mongo_url(request):
return request.config.getoption("--mongo_url")
@pytest.fixture(scope="module")
def dump_databases(request):
return request.config.getoption("--dump_databases")
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
# execute all other hooks to obtain the report object

View file

@ -29,7 +29,7 @@ Command line arguments
- "--timeout" - "Provide specific timeout value for test case",
- "--setup_only" - "Only create dbs, do not run tests",
- "--mongo_url" - "MongoDB for testing.",
- "--dump_databases" - ("json"|"bson") export database in expected format after successful test (to output folder in temp location - which is made persistent by this, must be cleared manually)
Run Tray for test
-----------------
In case of failed test you might want to run it manually and visually debug what happened.

View file

@ -2,7 +2,7 @@
Helper class for automatic testing, provides dump and restore via command
line utilities.
Expect mongodump, mongoimport and mongorestore present at PATH
Expect mongodump, mongoexport, mongoimport and mongorestore present at PATH
"""
import os
import pymongo
@ -148,7 +148,7 @@ class DBHandler:
self.client.drop_database(db_name)
def backup_to_dump(self, db_name, dump_dir, overwrite=False,
collection=None):
collection=None, format="bson"):
"""
Helper method for running mongodump for specific 'db_name'
"""
@ -160,15 +160,24 @@ class DBHandler:
raise RuntimeError("Backup already exists, "
"run with overwrite=True")
query = self._dump_query(self.uri, dump_dir,
db_name=db_name, collection=collection)
print("Mongodump query:: {}".format(query))
subprocess.run(query)
collections = [collection]
if format == "json" and collection is None:
collections = self.client[db_name].list_collection_names()
for collection in collections:
query = self._dump_query(self.uri, dump_dir,
db_name=db_name, collection=collection,
format=format)
print("Mongodump query:: {}".format(query))
process = subprocess.run(query)
assert process.returncode == 0, "Mongo dump failed."
def _db_exists(self, db_name):
return db_name in self.client.list_database_names()
def _dump_query(self, uri, output_path, db_name=None, collection=None):
def _dump_query(
self, uri, output_path, db_name=None, collection=None, format="bson"
):
"""Prepares dump query based on 'db_name' or 'collection'."""
db_part = coll_part = ""
if db_name:
@ -177,11 +186,22 @@ class DBHandler:
if not db_name:
raise ValueError("db_name must be present")
coll_part = "--collection={}".format(collection)
query = "\"{}\" --uri=\"{}\" --out={} {} {}".format(
"mongodump", uri, output_path, db_part, coll_part
)
return query
tool = "mongodump"
query = "{} --uri=\"{}\""
if format == "json":
assert collection, "Collection is needed for json export."
query += " --jsonArray --pretty"
tool = "mongoexport"
output_path = os.path.join(
output_path, "{}.{}.json".format(db_name, collection)
)
query += " --out={} {} {}"
return query.format(tool, uri, output_path, db_part, coll_part)
def _restore_query(self, uri, dump_dir,
db_name=None, db_name_out=None,

View file

@ -70,7 +70,9 @@ class ModuleUnitTest(BaseTest):
)
@pytest.fixture(scope="module")
def download_test_data(self, test_data_folder, persist, request):
def download_test_data(
self, test_data_folder, persist, request, dump_databases
):
test_data_folder = test_data_folder or self.TEST_DATA_FOLDER
if test_data_folder:
print("Using existing folder {}".format(test_data_folder))
@ -100,13 +102,13 @@ class ModuleUnitTest(BaseTest):
if ext and ext.lstrip('.') in handler_class.IMPLEMENTED_ZIP_FORMATS: # noqa: E501
handler_class.unzip(os.path.join(tmpdir, file_name))
yield tmpdir
yield tmpdir
persist = (persist or self.PERSIST or
self.is_test_failed(request))
if not persist:
print("Removing {}".format(tmpdir))
shutil.rmtree(tmpdir)
persist = (persist or self.PERSIST or
self.is_test_failed(request) or dump_databases)
if not persist:
print("Removing {}".format(tmpdir))
shutil.rmtree(tmpdir)
@pytest.fixture(scope="module")
def output_folder_url(self, download_test_data):
@ -163,7 +165,7 @@ class ModuleUnitTest(BaseTest):
@pytest.fixture(scope="module")
def db_setup(self, download_test_data, env_var, monkeypatch_session,
request, mongo_url):
request, mongo_url, dump_databases, persist):
"""Restore prepared MongoDB dumps into selected DB."""
backup_dir = os.path.join(download_test_data, "input", "dumps")
uri = os.environ.get("OPENPYPE_MONGO")
@ -178,7 +180,17 @@ class ModuleUnitTest(BaseTest):
yield db_handler
persist = self.PERSIST or self.is_test_failed(request)
if dump_databases:
print("Dumping databases to {}".format(download_test_data))
output_dir = os.path.join(download_test_data, "output", "dumps")
db_handler.backup_to_dump(
self.TEST_DB_NAME, output_dir, format=dump_databases
)
db_handler.backup_to_dump(
self.TEST_OPENPYPE_NAME, output_dir, format=dump_databases
)
persist = persist or self.PERSIST or self.is_test_failed(request)
if not persist:
db_handler.teardown(self.TEST_DB_NAME)
db_handler.teardown(self.TEST_OPENPYPE_NAME)