Merge branch 'develop' into enhancement/add_missing_repair_action_validate_resolution_setting_3dsmax
|
|
@ -296,12 +296,15 @@ def run(script):
|
||||||
@click.option("--mongo_url",
|
@click.option("--mongo_url",
|
||||||
help="MongoDB for testing.",
|
help="MongoDB for testing.",
|
||||||
default=None)
|
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,
|
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"""
|
"""Run all automatic tests after proper initialization via start.py"""
|
||||||
PypeCommands().run_tests(folder, mark, pyargs, test_data_folder,
|
PypeCommands().run_tests(folder, mark, pyargs, test_data_folder,
|
||||||
persist, app_variant, timeout, setup_only,
|
persist, app_variant, timeout, setup_only,
|
||||||
mongo_url, app_group)
|
mongo_url, app_group, dump_databases)
|
||||||
|
|
||||||
|
|
||||||
@main.command(help="DEPRECATED - run sync server")
|
@main.command(help="DEPRECATED - run sync server")
|
||||||
|
|
|
||||||
|
|
@ -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).
|
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
|
OR
|
||||||
download [Anastasiy’s Extension Manager](https://install.anastasiy.com/)
|
download [Anastasiy’s 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
|
### Server
|
||||||
|
|
||||||
The easiest way to get the server and After Effects launch is with:
|
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.
|
`avalon.aftereffects.launch` launches the application and server, and also closes the server when After Effects exists.
|
||||||
|
|
||||||
## Usage
|
## 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:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Developing
|
## 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).
|
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 -selfSignedCert NA NA Avalon Avalon-After-Effects avalon extension.p12
|
||||||
ZXPSignCmd -sign {path to addon}/api/extension {path to addon}/api/extension.zxp extension.p12 Ayon
|
ZXPSignCmd -sign {path to avalon-core}\avalon\aftereffects\extension {path to avalon-core}\avalon\aftereffects\extension.zxp extension.p12 avalon
|
||||||
```
|
```
|
||||||
|
|
||||||
### Plugin Examples
|
### Plugin Examples
|
||||||
|
|
@ -54,7 +52,7 @@ 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).
|
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:
|
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:
|
For easier debugging of Javascript:
|
||||||
https://community.adobe.com/t5/download-install/adobe-extension-debuger-problem/td-p/10911704?page=1
|
https://community.adobe.com/t5/download-install/adobe-extension-debuger-problem/td-p/10911704?page=1
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ExtensionList>
|
<ExtensionList>
|
||||||
<Extension Id="io.ynput.AE.panel">
|
<Extension Id="com.openpype.AE.panel">
|
||||||
<HostList>
|
<HostList>
|
||||||
|
|
||||||
<!-- Comment Host tags according to the apps you want your panel to support -->
|
<!-- Comment Host tags according to the apps you want your panel to support -->
|
||||||
|
|
@ -29,3 +29,4 @@
|
||||||
</HostList>
|
</HostList>
|
||||||
</Extension>
|
</Extension>
|
||||||
</ExtensionList>
|
</ExtensionList>
|
||||||
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ExtensionManifest Version="8.0" ExtensionBundleId="io.ynput.AE.panel" ExtensionBundleVersion="1.1.0"
|
<ExtensionManifest Version="8.0" ExtensionBundleId="com.openpype.AE.panel" ExtensionBundleVersion="1.0.27"
|
||||||
ExtensionBundleName="io.ynput.AE.panel" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
ExtensionBundleName="com.openpype.AE.panel" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
<ExtensionList>
|
<ExtensionList>
|
||||||
<Extension Id="io.ynput.AE.panel" Version="1.0" />
|
<Extension Id="com.openpype.AE.panel" Version="1.0" />
|
||||||
</ExtensionList>
|
</ExtensionList>
|
||||||
<ExecutionEnvironment>
|
<ExecutionEnvironment>
|
||||||
<HostList>
|
<HostList>
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
</RequiredRuntimeList>
|
</RequiredRuntimeList>
|
||||||
</ExecutionEnvironment>
|
</ExecutionEnvironment>
|
||||||
<DispatchInfoList>
|
<DispatchInfoList>
|
||||||
<Extension Id="io.ynput.AE.panel">
|
<Extension Id="com.openpype.AE.panel">
|
||||||
<DispatchInfo >
|
<DispatchInfo >
|
||||||
<Resources>
|
<Resources>
|
||||||
<MainPath>./index.html</MainPath>
|
<MainPath>./index.html</MainPath>
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
</Lifecycle>
|
</Lifecycle>
|
||||||
<UI>
|
<UI>
|
||||||
<Type>Panel</Type>
|
<Type>Panel</Type>
|
||||||
<Menu>AYON</Menu>
|
<Menu>OpenPype</Menu>
|
||||||
<Geometry>
|
<Geometry>
|
||||||
<Size>
|
<Size>
|
||||||
<Height>200</Height>
|
<Height>200</Height>
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
|
|
||||||
</Geometry>
|
</Geometry>
|
||||||
<Icons>
|
<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="RollOver">./icons/iconRollover.png</Icon>
|
||||||
<Icon Type="Disabled">./icons/iconDisabled.png</Icon>
|
<Icon Type="Disabled">./icons/iconDisabled.png</Icon>
|
||||||
<Icon Type="DarkNormal">./icons/iconDarkNormal.png</Icon>
|
<Icon Type="DarkNormal">./icons/iconDarkNormal.png</Icon>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 16 KiB |
BIN
openpype/hosts/aftereffects/api/panel_failure.PNG
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
|
@ -2,10 +2,12 @@
|
||||||
"""OpenPype startup script."""
|
"""OpenPype startup script."""
|
||||||
from openpype.pipeline import install_host
|
from openpype.pipeline import install_host
|
||||||
from openpype.hosts.houdini.api import HoudiniHost
|
from openpype.hosts.houdini.api import HoudiniHost
|
||||||
|
from openpype import AYON_SERVER_ENABLED
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("Installing OpenPype ...")
|
print("Installing {} ...".format(
|
||||||
|
"AYON" if AYON_SERVER_ENABLED else "OpenPype"))
|
||||||
install_host(HoudiniHost())
|
install_host(HoudiniHost())
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
"""OpenPype startup script."""
|
"""OpenPype startup script."""
|
||||||
from openpype.pipeline import install_host
|
from openpype.pipeline import install_host
|
||||||
from openpype.hosts.houdini.api import HoudiniHost
|
from openpype.hosts.houdini.api import HoudiniHost
|
||||||
|
from openpype import AYON_SERVER_ENABLED
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("Installing OpenPype ...")
|
print("Installing {} ...".format(
|
||||||
|
"AYON" if AYON_SERVER_ENABLED else "OpenPype"))
|
||||||
install_host(HoudiniHost())
|
install_host(HoudiniHost())
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
"""OpenPype startup script."""
|
"""OpenPype startup script."""
|
||||||
from openpype.pipeline import install_host
|
from openpype.pipeline import install_host
|
||||||
from openpype.hosts.houdini.api import HoudiniHost
|
from openpype.hosts.houdini.api import HoudiniHost
|
||||||
|
from openpype import AYON_SERVER_ENABLED
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("Installing OpenPype ...")
|
print("Installing {} ...".format(
|
||||||
|
"AYON" if AYON_SERVER_ENABLED else "OpenPype"))
|
||||||
install_host(HoudiniHost())
|
install_host(HoudiniHost())
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
"""OpenPype startup script."""
|
"""OpenPype startup script."""
|
||||||
from openpype.pipeline import install_host
|
from openpype.pipeline import install_host
|
||||||
from openpype.hosts.houdini.api import HoudiniHost
|
from openpype.hosts.houdini.api import HoudiniHost
|
||||||
|
from openpype import AYON_SERVER_ENABLED
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print("Installing OpenPype ...")
|
print("Installing {} ...".format(
|
||||||
|
"AYON" if AYON_SERVER_ENABLED else "OpenPype"))
|
||||||
install_host(HoudiniHost())
|
install_host(HoudiniHost())
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from openpype.pipeline import (
|
||||||
)
|
)
|
||||||
from openpype.pipeline.load.utils import get_representation_path_from_context
|
from openpype.pipeline.load.utils import get_representation_path_from_context
|
||||||
from openpype.pipeline.colorspace import (
|
from openpype.pipeline.colorspace import (
|
||||||
get_imageio_colorspace_from_filepath,
|
get_imageio_file_rules_colorspace_from_filepath,
|
||||||
get_imageio_config,
|
get_imageio_config,
|
||||||
get_imageio_file_rules
|
get_imageio_file_rules
|
||||||
)
|
)
|
||||||
|
|
@ -285,10 +285,10 @@ class FileNodeLoader(load.LoaderPlugin):
|
||||||
)
|
)
|
||||||
|
|
||||||
path = get_representation_path_from_context(context)
|
path = get_representation_path_from_context(context)
|
||||||
colorspace = get_imageio_colorspace_from_filepath(
|
colorspace = get_imageio_file_rules_colorspace_from_filepath(
|
||||||
path=path,
|
path,
|
||||||
host_name=host_name,
|
host_name,
|
||||||
project_name=project_name,
|
project_name,
|
||||||
config_data=config_data,
|
config_data=config_data,
|
||||||
file_rules=file_rules,
|
file_rules=file_rules,
|
||||||
project_settings=project_settings
|
project_settings=project_settings
|
||||||
|
|
|
||||||
|
|
@ -371,7 +371,6 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
|
||||||
continue
|
continue
|
||||||
for node in data["nodes"]:
|
for node in data["nodes"]:
|
||||||
lib.set_attribute(data["attribute"], data["values"][0], node)
|
lib.set_attribute(data["attribute"], data["values"][0], node)
|
||||||
|
|
||||||
with lib.renderlayer(layer_node):
|
with lib.renderlayer(layer_node):
|
||||||
|
|
||||||
# Repair animation must be enabled
|
# Repair animation must be enabled
|
||||||
|
|
@ -392,13 +391,11 @@ class ValidateRenderSettings(pyblish.api.InstancePlugin):
|
||||||
if renderer != "renderman":
|
if renderer != "renderman":
|
||||||
prefix_attr = RenderSettings.get_image_prefix_attr(renderer)
|
prefix_attr = RenderSettings.get_image_prefix_attr(renderer)
|
||||||
fname_prefix = default_prefix
|
fname_prefix = default_prefix
|
||||||
cmds.setAttr("{}.{}".format(node, prefix_attr),
|
cmds.setAttr(prefix_attr, fname_prefix, type="string")
|
||||||
fname_prefix, type="string")
|
|
||||||
|
|
||||||
# Repair padding
|
# Repair padding
|
||||||
padding_attr = RenderSettings.get_padding_attr(renderer)
|
padding_attr = RenderSettings.get_padding_attr(renderer)
|
||||||
cmds.setAttr("{}.{}".format(node, padding_attr),
|
cmds.setAttr(padding_attr, cls.DEFAULT_PADDING)
|
||||||
cls.DEFAULT_PADDING)
|
|
||||||
else:
|
else:
|
||||||
# renderman handles stuff differently
|
# renderman handles stuff differently
|
||||||
cmds.setAttr("rmanGlobals.imageFileFormat",
|
cmds.setAttr("rmanGlobals.imageFileFormat",
|
||||||
|
|
|
||||||
|
|
@ -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).
|
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
|
### 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:
|
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.
|
`avalon.photoshop.launch` launches the application and server, and also closes the server when Photoshop exists.
|
||||||
|
|
||||||
## Usage
|
## 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:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Developing
|
## 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).
|
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
|
ZXPSignCmd -sign {path to avalon-core}\avalon\photoshop\extension {path to avalon-core}\avalon\photoshop\extension.zxp extension.p12 avalon
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<ExtensionList>
|
<ExtensionList>
|
||||||
<Extension Id="io.ynput.PS.panel">
|
<Extension Id="com.openpype.PS.panel">
|
||||||
<HostList>
|
<HostList>
|
||||||
<Host Name="PHXS" Port="8078"/>
|
<Host Name="PHXS" Port="8078"/>
|
||||||
<Host Name="FLPR" Port="8078"/>
|
<Host Name="FLPR" Port="8078"/>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version='1.0' encoding='UTF-8'?>
|
<?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>
|
<ExtensionList>
|
||||||
<Extension Id="io.ynput.PS.panel" Version="1.0.1" />
|
<Extension Id="com.openpype.PS.panel" Version="1.0.1" />
|
||||||
</ExtensionList>
|
</ExtensionList>
|
||||||
<ExecutionEnvironment>
|
<ExecutionEnvironment>
|
||||||
<HostList>
|
<HostList>
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
</RequiredRuntimeList>
|
</RequiredRuntimeList>
|
||||||
</ExecutionEnvironment>
|
</ExecutionEnvironment>
|
||||||
<DispatchInfoList>
|
<DispatchInfoList>
|
||||||
<Extension Id="io.ynput.PS.panel">
|
<Extension Id="com.openpype.PS.panel">
|
||||||
<DispatchInfo>
|
<DispatchInfo>
|
||||||
<Resources>
|
<Resources>
|
||||||
<MainPath>./index.html</MainPath>
|
<MainPath>./index.html</MainPath>
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
</Lifecycle>
|
</Lifecycle>
|
||||||
<UI>
|
<UI>
|
||||||
<Type>Panel</Type>
|
<Type>Panel</Type>
|
||||||
<Menu>AYON</Menu>
|
<Menu>OpenPype</Menu>
|
||||||
<Geometry>
|
<Geometry>
|
||||||
<Size>
|
<Size>
|
||||||
<Width>300</Width>
|
<Width>300</Width>
|
||||||
|
|
@ -44,7 +44,7 @@
|
||||||
</MaxSize>
|
</MaxSize>
|
||||||
</Geometry>
|
</Geometry>
|
||||||
<Icons>
|
<Icons>
|
||||||
<Icon Type="Normal">./icons/ayon_logo.png</Icon>
|
<Icon Type="Normal">./icons/avalon-logo-48.png</Icon>
|
||||||
</Icons>
|
</Icons>
|
||||||
</UI>
|
</UI>
|
||||||
</DispatchInfo>
|
</DispatchInfo>
|
||||||
|
|
|
||||||
BIN
openpype/hosts/photoshop/api/extension/icons/avalon-logo-48.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
BIN
openpype/hosts/photoshop/api/panel.PNG
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
openpype/hosts/photoshop/api/panel_failure.PNG
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
|
@ -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
|
|
||||||
|
|
@ -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")
|
|
||||||
})
|
|
||||||
|
|
@ -73,7 +73,7 @@ class CollectRenderInstances(pyblish.api.InstancePlugin):
|
||||||
render_layer_id = creator_attributes["render_layer_instance_id"]
|
render_layer_id = creator_attributes["render_layer_instance_id"]
|
||||||
for in_data in instance.context.data["workfileInstances"]:
|
for in_data in instance.context.data["workfileInstances"]:
|
||||||
if (
|
if (
|
||||||
in_data["creator_identifier"] == "render.layer"
|
in_data.get("creator_identifier") == "render.layer"
|
||||||
and in_data["instance_id"] == render_layer_id
|
and in_data["instance_id"] == render_layer_id
|
||||||
):
|
):
|
||||||
render_layer_data = in_data
|
render_layer_data = in_data
|
||||||
|
|
|
||||||
|
|
@ -464,8 +464,7 @@ class AbstractSubmitDeadline(pyblish.api.InstancePlugin,
|
||||||
self.log.info("Submitted job to Deadline: {}.".format(job_id))
|
self.log.info("Submitted job to Deadline: {}.".format(job_id))
|
||||||
|
|
||||||
# TODO: Find a way that's more generic and not render type specific
|
# TODO: Find a way that's more generic and not render type specific
|
||||||
export_job = instance.data["exportJob"]
|
if "exportJob" in instance.data:
|
||||||
if export_job:
|
|
||||||
self.log.info("Splitting export and render in two jobs")
|
self.log.info("Splitting export and render in two jobs")
|
||||||
self.log.info("Export job id: %s", job_id)
|
self.log.info("Export job id: %s", job_id)
|
||||||
render_job_info = self.get_job_info(dependency_job_ids=[job_id])
|
render_job_info = self.get_job_info(dependency_job_ids=[job_id])
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
"""Collect default Deadline server."""
|
"""Collect default Deadline server."""
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
|
from openpype import AYON_SERVER_ENABLED
|
||||||
|
|
||||||
|
|
||||||
class CollectDefaultDeadlineServer(pyblish.api.ContextPlugin):
|
class CollectDefaultDeadlineServer(pyblish.api.ContextPlugin):
|
||||||
"""Collect default Deadline Webservice URL.
|
"""Collect default Deadline Webservice URL.
|
||||||
|
|
@ -30,24 +32,26 @@ class CollectDefaultDeadlineServer(pyblish.api.ContextPlugin):
|
||||||
self.log.error("Cannot get OpenPype Deadline module.")
|
self.log.error("Cannot get OpenPype Deadline module.")
|
||||||
raise AssertionError("OpenPype Deadline module not found.")
|
raise AssertionError("OpenPype Deadline module not found.")
|
||||||
|
|
||||||
# get default deadline webservice url from deadline module
|
deadline_settings = context.data["project_settings"]["deadline"]
|
||||||
self.log.debug(deadline_module.deadline_urls)
|
deadline_server_name = None
|
||||||
context.data["defaultDeadline"] = deadline_module.deadline_urls["default"] # noqa: E501
|
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
|
deadline_webservice = None
|
||||||
["project_settings"]
|
if deadline_server_name:
|
||||||
["deadline"]
|
|
||||||
["deadline_servers"])
|
|
||||||
if deadline_servers:
|
|
||||||
deadline_server_name = deadline_servers[0]
|
|
||||||
deadline_webservice = deadline_module.deadline_urls.get(
|
deadline_webservice = deadline_module.deadline_urls.get(
|
||||||
deadline_server_name)
|
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"] = \
|
default_deadline_webservice = deadline_module.deadline_urls["default"]
|
||||||
context.data["defaultDeadline"].strip().rstrip("/")
|
deadline_webservice = (
|
||||||
|
deadline_webservice
|
||||||
|
or default_deadline_webservice
|
||||||
|
)
|
||||||
|
|
||||||
|
context.data["defaultDeadline"] = deadline_webservice.strip().rstrip("/") # noqa
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin):
|
||||||
"mvUsd",
|
"mvUsd",
|
||||||
"mvUsdComposition",
|
"mvUsdComposition",
|
||||||
"mvUsdOverride",
|
"mvUsdOverride",
|
||||||
"simpleUnrealTexture",
|
|
||||||
"online",
|
"online",
|
||||||
"uasset",
|
"uasset",
|
||||||
"blendScene",
|
"blendScene",
|
||||||
|
|
|
||||||
|
|
@ -185,8 +185,7 @@ class PypeCommands:
|
||||||
task,
|
task,
|
||||||
app,
|
app,
|
||||||
env_group=env_group,
|
env_group=env_group,
|
||||||
launch_type=LaunchTypes.farm_render,
|
launch_type=LaunchTypes.farm_render
|
||||||
env={}
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
|
|
@ -215,7 +214,7 @@ class PypeCommands:
|
||||||
|
|
||||||
def run_tests(self, folder, mark, pyargs,
|
def run_tests(self, folder, mark, pyargs,
|
||||||
test_data_folder, persist, app_variant, timeout, setup_only,
|
test_data_folder, persist, app_variant, timeout, setup_only,
|
||||||
mongo_url, app_group):
|
mongo_url, app_group, dump_databases):
|
||||||
"""
|
"""
|
||||||
Runs tests from 'folder'
|
Runs tests from 'folder'
|
||||||
|
|
||||||
|
|
@ -276,6 +275,13 @@ class PypeCommands:
|
||||||
if mongo_url:
|
if mongo_url:
|
||||||
args.extend(["--mongo_url", 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))
|
print("run_tests args: {}".format(args))
|
||||||
import pytest
|
import pytest
|
||||||
pytest.main(args)
|
pytest.main(args)
|
||||||
|
|
|
||||||
|
|
@ -38,16 +38,6 @@
|
||||||
"file": "{subset}_{@version}<_{output}><.{@frame}>.{ext}",
|
"file": "{subset}_{@version}<_{output}><.{@frame}>.{ext}",
|
||||||
"path": "{@folder}/{@file}"
|
"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": {
|
"online": {
|
||||||
"folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}",
|
"folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}",
|
||||||
"file": "{originalBasename}<.{@frame}><_{udim}>.{ext}",
|
"file": "{originalBasename}<.{@frame}><_{udim}>.{ext}",
|
||||||
|
|
@ -68,8 +58,6 @@
|
||||||
},
|
},
|
||||||
"__dynamic_keys_labels__": {
|
"__dynamic_keys_labels__": {
|
||||||
"maya2unreal": "Maya to Unreal",
|
"maya2unreal": "Maya to Unreal",
|
||||||
"simpleUnrealTextureHero": "Simple Unreal Texture - Hero",
|
|
||||||
"simpleUnrealTexture": "Simple Unreal Texture",
|
|
||||||
"online": "online",
|
"online": "online",
|
||||||
"tycache": "tycache",
|
"tycache": "tycache",
|
||||||
"source": "source",
|
"source": "source",
|
||||||
|
|
|
||||||
|
|
@ -322,22 +322,9 @@
|
||||||
"animation",
|
"animation",
|
||||||
"setdress",
|
"setdress",
|
||||||
"layout",
|
"layout",
|
||||||
"mayaScene",
|
"mayaScene"
|
||||||
"simpleUnrealTexture"
|
|
||||||
],
|
],
|
||||||
"template_name_profiles": [
|
"template_name_profiles": []
|
||||||
{
|
|
||||||
"families": [
|
|
||||||
"simpleUnrealTexture"
|
|
||||||
],
|
|
||||||
"hosts": [
|
|
||||||
"standalonepublisher"
|
|
||||||
],
|
|
||||||
"task_types": [],
|
|
||||||
"task_names": [],
|
|
||||||
"template_name": "simpleUnrealTextureHero"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"CleanUp": {
|
"CleanUp": {
|
||||||
"paterns": [],
|
"paterns": [],
|
||||||
|
|
@ -519,17 +506,6 @@
|
||||||
"task_names": [],
|
"task_names": [],
|
||||||
"template_name": "render"
|
"template_name": "render"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"families": [
|
|
||||||
"simpleUnrealTexture"
|
|
||||||
],
|
|
||||||
"hosts": [
|
|
||||||
"standalonepublisher"
|
|
||||||
],
|
|
||||||
"task_types": [],
|
|
||||||
"task_names": [],
|
|
||||||
"template_name": "simpleUnrealTexture"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"families": [
|
"families": [
|
||||||
"staticMesh",
|
"staticMesh",
|
||||||
|
|
@ -565,19 +541,7 @@
|
||||||
"template_name": "tycache"
|
"template_name": "tycache"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hero_template_name_profiles": [
|
"hero_template_name_profiles": [],
|
||||||
{
|
|
||||||
"families": [
|
|
||||||
"simpleUnrealTexture"
|
|
||||||
],
|
|
||||||
"hosts": [
|
|
||||||
"standalonepublisher"
|
|
||||||
],
|
|
||||||
"task_types": [],
|
|
||||||
"task_names": [],
|
|
||||||
"template_name": "simpleUnrealTextureHero"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"custom_staging_dir_profiles": []
|
"custom_staging_dir_profiles": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -133,14 +133,6 @@
|
||||||
],
|
],
|
||||||
"help": "Texture files with UDIM together with worfile"
|
"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": {
|
"create_vdb": {
|
||||||
"name": "vdb",
|
"name": "vdb",
|
||||||
"label": "VDB Volumetric Data",
|
"label": "VDB Volumetric Data",
|
||||||
|
|
|
||||||
|
|
@ -244,19 +244,6 @@
|
||||||
".hda"
|
".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",
|
"family": "audio",
|
||||||
"identifier": "",
|
"identifier": "",
|
||||||
|
|
|
||||||
|
|
@ -632,6 +632,14 @@ class TrayManager:
|
||||||
self.exit()
|
self.exit()
|
||||||
|
|
||||||
elif result.restart or result.token_changed:
|
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()
|
self.restart()
|
||||||
|
|
||||||
def _on_restart_action(self):
|
def _on_restart_action(self):
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,35 @@ class ServerListSubmodel(BaseSettingsModel):
|
||||||
value: str = Field(title="Value")
|
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):
|
class DeadlineSettings(BaseSettingsModel):
|
||||||
deadline_urls: list[ServerListSubmodel] = Field(
|
deadline_urls: list[ServerListSubmodel] = Field(
|
||||||
default_factory=list,
|
default_factory=list,
|
||||||
title="System Deadline Webservice URLs",
|
title="System Deadline Webservice URLs",
|
||||||
scope=["studio"],
|
scope=["studio"],
|
||||||
)
|
)
|
||||||
deadline_servers: list[str] = Field(
|
deadline_server: str = Field(
|
||||||
title="Project deadline servers",
|
title="Project deadline server",
|
||||||
section="---",
|
section="---",
|
||||||
|
scope=["project"],
|
||||||
|
enum_resolver=defined_deadline_ws_name_enum_resolver
|
||||||
)
|
)
|
||||||
publish: PublishPluginsModel = Field(
|
publish: PublishPluginsModel = Field(
|
||||||
default_factory=PublishPluginsModel,
|
default_factory=PublishPluginsModel,
|
||||||
|
|
@ -42,7 +62,6 @@ DEFAULT_VALUES = {
|
||||||
"value": "http://127.0.0.1:8082"
|
"value": "http://127.0.0.1:8082"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
# TODO: this needs to be dynamic from "deadline_urls"
|
"deadline_server": "default",
|
||||||
"deadline_servers": [],
|
|
||||||
"publish": DEFAULT_DEADLINE_PLUGINS_SETTINGS
|
"publish": DEFAULT_DEADLINE_PLUGINS_SETTINGS
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,6 @@ from pydantic import Field, validator
|
||||||
from ayon_server.settings import BaseSettingsModel, ensure_unique_names
|
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):
|
class CollectDeadlinePoolsModel(BaseSettingsModel):
|
||||||
"""Settings Deadline default pools."""
|
"""Settings Deadline default pools."""
|
||||||
|
|
||||||
|
|
@ -286,12 +280,6 @@ class ProcessSubmittedJobOnFarmModel(BaseSettingsModel):
|
||||||
|
|
||||||
|
|
||||||
class PublishPluginsModel(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(
|
CollectDeadlinePools: CollectDeadlinePoolsModel = Field(
|
||||||
default_factory=CollectDeadlinePoolsModel,
|
default_factory=CollectDeadlinePoolsModel,
|
||||||
title="Default Pools")
|
title="Default Pools")
|
||||||
|
|
@ -332,9 +320,6 @@ class PublishPluginsModel(BaseSettingsModel):
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_DEADLINE_PLUGINS_SETTINGS = {
|
DEFAULT_DEADLINE_PLUGINS_SETTINGS = {
|
||||||
"CollectDefaultDeadlineServer": {
|
|
||||||
"pass_mongo_url": True
|
|
||||||
},
|
|
||||||
"CollectDeadlinePools": {
|
"CollectDeadlinePools": {
|
||||||
"primary_pool": "",
|
"primary_pool": "",
|
||||||
"secondary_pool": ""
|
"secondary_pool": ""
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,11 @@ def pytest_addoption(parser):
|
||||||
help="Provide url of the Mongo database."
|
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")
|
@pytest.fixture(scope="module")
|
||||||
def test_data_folder(request):
|
def test_data_folder(request):
|
||||||
|
|
@ -75,6 +80,11 @@ def mongo_url(request):
|
||||||
return request.config.getoption("--mongo_url")
|
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)
|
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
|
||||||
def pytest_runtest_makereport(item, call):
|
def pytest_runtest_makereport(item, call):
|
||||||
# execute all other hooks to obtain the report object
|
# execute all other hooks to obtain the report object
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ Command line arguments
|
||||||
- "--timeout" - "Provide specific timeout value for test case",
|
- "--timeout" - "Provide specific timeout value for test case",
|
||||||
- "--setup_only" - "Only create dbs, do not run tests",
|
- "--setup_only" - "Only create dbs, do not run tests",
|
||||||
- "--mongo_url" - "MongoDB for testing.",
|
- "--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
|
Run Tray for test
|
||||||
-----------------
|
-----------------
|
||||||
In case of failed test you might want to run it manually and visually debug what happened.
|
In case of failed test you might want to run it manually and visually debug what happened.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
Helper class for automatic testing, provides dump and restore via command
|
Helper class for automatic testing, provides dump and restore via command
|
||||||
line utilities.
|
line utilities.
|
||||||
|
|
||||||
Expect mongodump, mongoimport and mongorestore present at PATH
|
Expect mongodump, mongoexport, mongoimport and mongorestore present at PATH
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import pymongo
|
import pymongo
|
||||||
|
|
@ -148,7 +148,7 @@ class DBHandler:
|
||||||
self.client.drop_database(db_name)
|
self.client.drop_database(db_name)
|
||||||
|
|
||||||
def backup_to_dump(self, db_name, dump_dir, overwrite=False,
|
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'
|
Helper method for running mongodump for specific 'db_name'
|
||||||
"""
|
"""
|
||||||
|
|
@ -160,15 +160,24 @@ class DBHandler:
|
||||||
raise RuntimeError("Backup already exists, "
|
raise RuntimeError("Backup already exists, "
|
||||||
"run with overwrite=True")
|
"run with overwrite=True")
|
||||||
|
|
||||||
query = self._dump_query(self.uri, dump_dir,
|
collections = [collection]
|
||||||
db_name=db_name, collection=collection)
|
if format == "json" and collection is None:
|
||||||
print("Mongodump query:: {}".format(query))
|
collections = self.client[db_name].list_collection_names()
|
||||||
subprocess.run(query)
|
|
||||||
|
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):
|
def _db_exists(self, db_name):
|
||||||
return db_name in self.client.list_database_names()
|
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'."""
|
"""Prepares dump query based on 'db_name' or 'collection'."""
|
||||||
db_part = coll_part = ""
|
db_part = coll_part = ""
|
||||||
if db_name:
|
if db_name:
|
||||||
|
|
@ -177,11 +186,22 @@ class DBHandler:
|
||||||
if not db_name:
|
if not db_name:
|
||||||
raise ValueError("db_name must be present")
|
raise ValueError("db_name must be present")
|
||||||
coll_part = "--collection={}".format(collection)
|
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,
|
def _restore_query(self, uri, dump_dir,
|
||||||
db_name=None, db_name_out=None,
|
db_name=None, db_name_out=None,
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,9 @@ class ModuleUnitTest(BaseTest):
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@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
|
test_data_folder = test_data_folder or self.TEST_DATA_FOLDER
|
||||||
if test_data_folder:
|
if test_data_folder:
|
||||||
print("Using existing folder {}".format(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
|
if ext and ext.lstrip('.') in handler_class.IMPLEMENTED_ZIP_FORMATS: # noqa: E501
|
||||||
handler_class.unzip(os.path.join(tmpdir, file_name))
|
handler_class.unzip(os.path.join(tmpdir, file_name))
|
||||||
|
|
||||||
yield tmpdir
|
yield tmpdir
|
||||||
|
|
||||||
persist = (persist or self.PERSIST or
|
persist = (persist or self.PERSIST or
|
||||||
self.is_test_failed(request))
|
self.is_test_failed(request) or dump_databases)
|
||||||
if not persist:
|
if not persist:
|
||||||
print("Removing {}".format(tmpdir))
|
print("Removing {}".format(tmpdir))
|
||||||
shutil.rmtree(tmpdir)
|
shutil.rmtree(tmpdir)
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def output_folder_url(self, download_test_data):
|
def output_folder_url(self, download_test_data):
|
||||||
|
|
@ -163,7 +165,7 @@ class ModuleUnitTest(BaseTest):
|
||||||
|
|
||||||
@pytest.fixture(scope="module")
|
@pytest.fixture(scope="module")
|
||||||
def db_setup(self, download_test_data, env_var, monkeypatch_session,
|
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."""
|
"""Restore prepared MongoDB dumps into selected DB."""
|
||||||
backup_dir = os.path.join(download_test_data, "input", "dumps")
|
backup_dir = os.path.join(download_test_data, "input", "dumps")
|
||||||
uri = os.environ.get("OPENPYPE_MONGO")
|
uri = os.environ.get("OPENPYPE_MONGO")
|
||||||
|
|
@ -178,7 +180,17 @@ class ModuleUnitTest(BaseTest):
|
||||||
|
|
||||||
yield db_handler
|
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:
|
if not persist:
|
||||||
db_handler.teardown(self.TEST_DB_NAME)
|
db_handler.teardown(self.TEST_DB_NAME)
|
||||||
db_handler.teardown(self.TEST_OPENPYPE_NAME)
|
db_handler.teardown(self.TEST_OPENPYPE_NAME)
|
||||||
|
|
|
||||||