diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 2339ec878f..f402f4541d 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -35,6 +35,17 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
+ - 3.16.0
+ - 3.16.0-nightly.1
+ - 3.15.12
+ - 3.15.12-nightly.4
+ - 3.15.12-nightly.3
+ - 3.15.12-nightly.2
+ - 3.15.12-nightly.1
+ - 3.15.11
+ - 3.15.11-nightly.5
+ - 3.15.11-nightly.4
+ - 3.15.11-nightly.3
- 3.15.11-nightly.2
- 3.15.11-nightly.1
- 3.15.10
@@ -124,17 +135,6 @@ body:
- 3.14.5
- 3.14.5-nightly.3
- 3.14.5-nightly.2
- - 3.14.5-nightly.1
- - 3.14.4
- - 3.14.4-nightly.4
- - 3.14.4-nightly.3
- - 3.14.4-nightly.2
- - 3.14.4-nightly.1
- - 3.14.3
- - 3.14.3-nightly.7
- - 3.14.3-nightly.6
- - 3.14.3-nightly.5
- - 3.14.3-nightly.4
validations:
required: true
- type: dropdown
diff --git a/.gitignore b/.gitignore
index 50f52f65a3..e5019a4e74 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,6 +37,7 @@ Temporary Items
###########
/build
/dist/
+/server_addon/package/*
/vendor/bin/*
/vendor/python/*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 882620f26c..33dbdb14fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,1876 @@
# Changelog
+## [3.16.0](https://github.com/ynput/OpenPype/tree/3.16.0)
+
+
+[Full Changelog](https://github.com/ynput/OpenPype/compare/...3.16.0)
+
+### **🆕 New features**
+
+
+
+General: Reduce usage of legacy io #4723
+
+Replace usages of `legacy_io` with getter methods or reuse already available information. Create plugins using CreateContext are using context from CreateContext object. Loaders are usign getter function from context tools. Publish plugin are using information instance.data or context.data. In some cases were pieces of code refactored a little e.g. fps getter in maya.
+
+
+___
+
+
+
+
+
+Documentation: API docs reborn - yet again #4419
+
+## Feature
+
+Add functional base for API Documentation using Sphinx and AutoAPI.
+
+After unsuccessful #2512, #834 and #210 this is yet another try. But this time without ambition to solve the whole issue. This is making Shinx script to work and nothing else. Any changes and improvements in API docs should be made in subsequent PRs.
+
+## How to use it
+
+You can run:
+
+```sh
+cd .\docs
+make.bat html
+```
+
+or
+
+```sh
+cd ./docs
+make html
+```
+
+This will go over our code and generate **.rst** files in `/docs/source/autoapi` and from those it will generate full html documentation in `/docs/build/html`.
+
+During the build you'll see tons of red errors that are pointing to our issues:
+
+1) **Wrong imports**
+ Invalid import are usually wrong relative imports (too deep) or circular imports.
+
+2) **Invalid doc-strings**
+ Doc-strings to be processed into documentation needs to follow some syntax - this can be checked by running
+ `pydocstyle` that is already included with OpenPype
+3) **Invalid markdown/rst files**
+ md/rst files can be included inside rst files using `.. include::` directive. But they have to be properly formatted.
+
+
+## Editing rst templates
+
+Everything starts with `/docs/source/index.rst` - this file should be properly edited, Right now it just includes `readme.rst` that in turn include and parse main `README.md`. This is entrypoint to API documentation. All templates generated by AutoAPI are in `/docs/source/autoapi`. They should be eventually commited to repository and edited too.
+
+## Steps for enhancing API documentation
+
+1) Run `/docs/make.bat html`
+2) Read the red errors/warnings - fix it in the code
+3) Run `/docs/make.bat html` again until there are not red lines
+4) Edit rst files and add some meaningfull content there
+
+> **Note**
+> This can (should) be merged as is without doc-string fixes in the code or changes in templates. All additional improvements on API documentation should be made in new PRs.
+
+> **Warning**
+> You need to add new dependencies to use it. Run `create_venv`.
+
+Connected to #2490
+___
+
+
+
+
+
+Global: custom location for OP local versions #4673
+
+This provides configurable location to unzip Openpype version zips. By default, it was hardcoded to artist's app data folder, which might be problematic/slow with roaming profiles.Location must be accessible by user running OP Tray with write permissions (so `Program Files` might be problematic)
+
+
+___
+
+
+
+
+
+AYON: Update settings conversion #4837
+
+Updated conversion script of AYON settings to v3 settings. PR is related to changes in addons repository https://github.com/ynput/ayon-addons/pull/6 . Changed how the conversion happens -> conversion output does not start with openpype defaults but as empty dictionary.
+
+
+___
+
+
+
+
+
+AYON: Implement integrate links publish plugin #4842
+
+Implemented entity links get/create functions. Added new integrator which replaces v3 integrator for links.
+
+
+___
+
+
+
+
+
+General: Version attributes integration #4991
+
+Implemented unified integrate plugin to update version attributes after all integrations for AYON. The goal is to be able update attribute values in a unified way to a version when all addon integrators are done, so e.g. ftrack can add ftrack id to matching version in AYON server etc.The can be stored under `"versionAttributes"` key.
+
+
+___
+
+
+
+
+
+AYON: Staging versions can be used #4992
+
+Added ability to use staging versions in AYON mode.
+
+
+___
+
+
+
+
+
+AYON: Preparation for products #5038
+
+Prepare ayon settings conversion script for `product` settings conversion.
+
+
+___
+
+
+
+
+
+Loader: Hide inactive versions in UI #5101
+
+Added support for `active` argument to hide versions with active set to False in Loader UI when in AYON mode.
+
+
+___
+
+
+
+
+
+General: CLI addon command #5109
+
+Added `addon` alias for `module` in OpenPype cli commands.
+
+
+___
+
+
+
+
+
+AYON: OpenPype as server addon #5199
+
+OpenPype repository can be converted to AYON addon for distribution. Addon has defined dependencies that are required to use it and are not in base ayon-launcher (desktop application).
+
+
+___
+
+
+
+
+
+General: Runtime dependencies #5206
+
+Defined runtime dependencies in pyproject toml. Moved python ocio and otio modules there.
+
+
+___
+
+
+
+
+
+AYON: Bundle distribution #5209
+
+Since AYON server 0.3.0 are addon versions defined by bundles which affects how addons, dependency packages and installers are handled. Only source of truth, about any version of anything that should be used, is server bundle.
+
+
+___
+
+
+
+
+
+Feature/blender handle q application #5264
+
+This edit is to change the way the QApplication is run for Blender. It calls in the singleton (QApplication) during the register. This is made so that other Qt applications and addons are able to run on Blender. In its current implementation, if a QApplication is already running, all functionality of OpenPype becomes unavailable.
+
+
+___
+
+
+
+### **🚀 Enhancements**
+
+
+
+General: Connect to AYON server (base) #3924
+
+Initial implementation of being able use AYON server in current OpenPype client. Added ability to connect to AYON server and use base queries.
+
+AYON mode has it's own executable (and start script). To start in AYON mode just replace `start.py` with `ayon_start.py` (added tray start script to tools). Added constant `AYON_SERVER_ENABLED` to `openpype/__init__.py` to know if ayon mode is enabled. In that case Mongo is not used at all and any attempts will cause crashes.I had to modify `~/openpype/client` content to be able do this switch. Mongo implementation was moved to `mongo` subfolder and use "star imports" in files from where current imports are used. Logic of any tool or query in code was not changed at all. Since functions were based on mongo queries they don't use full potential of AYON server abilities.ATM implementation has login UI, distribution of files from server and replacement of mongo queries. For queries is used `ayon_api` module. Which is in live development so the versions may change from day to day.
+
+
+___
+
+
+
+
+
+Enhancement kitsu note with exceptions #4537
+
+Adding a setting to choose some exceptions to IntegrateKitsuNote task status changes.
+
+
+___
+
+
+
+
+
+General: Environment variable for default OCIO configs #4670
+
+Define environment variable which lead to root of builtin ocio configs to be able change the root without changing settings. For the path in settings was used `"{OPENPYPE_ROOT}/vendor/bin/ocioconfig/OpenColorIOConfig"` which disallow to change the root somewhere else. That will be needed in AYON where configs won't be part of desktop application but downloaded from server.
+
+
+___
+
+
+
+
+
+AYON: Editorial hierarchy creation #4699
+
+Implemented extract hierarchy to AYON plugin which created entities in AYON using ayon api.
+
+
+___
+
+
+
+
+
+AYON: Vendorize ayon api #4753
+
+Vendorize ayon api into openpype vendor directory. The reason is that `ayon-python-api` is in live development and will fix/add features often in next few weeks/months, and because update of dependency requires new release -> new build, we want to avoid the need of doing that as it would affect OpenPype development.
+
+
+___
+
+
+
+
+
+General: Update PySide 6 for MacOs #4764
+
+New version of PySide6 does not have issues with settings UI. It is still breaking UI stylesheets so it is not changed for other plaforms but it is enhancement from previous state.
+
+
+___
+
+
+
+
+
+General: Removed unused cli commands #4902
+
+Removed `texturecopy` and `launch` cli commands from cli commands.
+
+
+___
+
+
+
+
+
+AYON: Linux & MacOS launch script #4970
+
+Added shell script to launch tray in AYON mode.
+
+
+___
+
+
+
+
+
+General: Qt scale enhancement #5059
+
+Set ~~'QT_SCALE_FACTOR_ROUNDING_POLICY'~~ scale factor rounding policy of QApplication to `PassThrough` so the scaling can be 'float' number and not just 'int' (150% -> 1.5 scale).
+
+
+___
+
+
+
+
+
+CI: WPS linting instead of Hound (rebase) 2 #5115
+
+Because Hound currently used to lint the code on GH ships with really old flake8 support, it fails miserably on any newer Python syntax. This PR is adding WPS linter to GitHub workflows that should step in.
+
+
+___
+
+
+
+
+
+Max: OP parameters only displays what is attached to the container #5229
+
+The OP parameter in 3dsmax only displays what is currently attached to the container while deleting while you can see the items which is not added when you are adding to the container.
+
+
+___
+
+
+
+
+
+Testing: improving logging during testing #5271
+
+Unit testing logging was crashing on more then one nested layers of inherited loggers.
+
+
+___
+
+
+
+
+
+Nuke: removing deprecated settings in baking #5275
+
+Removing deprecated settings for baking with reformat. This option was only for single reformat node and it had been substituted with multiple reposition nodes.
+
+
+___
+
+
+
+### **🐛 Bug fixes**
+
+
+
+AYON: General fixes and updates #4975
+
+Few smaller fixes related to AYON connection. Some of fixes were taken from this PR.
+
+
+___
+
+
+
+
+
+Start script: Change returncode on validate or list versions #4515
+
+Change exit code from `1` to `0` when versions are printed or when version is validated.
+
+Return code `1` is indicating error but there didn't happen any error.
+
+
+___
+
+
+
+
+
+AYON: Change login UI works #4754
+
+Fixed change of login UI. Logic change UI did show up, new login was successful, but after restart was used the previous login. This change fix the issue.
+
+
+___
+
+
+
+
+
+AYON: General issues #4763
+
+Vendorized `ayon_api` from PR broke OpenPype launch, because `ayon_api` is not available. Moved `ayon_api` from ayon specific subforlder to `common` python vendor in OpenPype, and removed login in ayon start script (which was invalid anyway). Also made fixed compatibility with PySide6 by using `qtpy` instead of `Qt` and changing code which is not PySide6 compatible.
+
+
+___
+
+
+
+
+
+AYON: Small fixes #4841
+
+Bugsfixes and enhancements related to AYON logic. Define `BUILTIN_OCIO_ROOT` environment variable so OCIO configs are working. Use constants from ayon api instead of hardcoding them in codebase. Change process name from "openpype" to "ayon". Don't execute login dialog when application is not yet running but use `open` method instead. Fixed missing modules settings which were not taken from openpype defaults. Updated ayon api to `0.1.17`.
+
+
+___
+
+
+
+
+
+Bugfix - Update gazu to 0.9.3 #4845
+
+This updates Gazu to 0.9.3 to make sure Gazu works with Kitsu and Zou 0.16.x+
+
+
+___
+
+
+
+
+
+Igniter: fix error reports in silent mode #4909
+
+Some errors in silent mode commands in Igniter were suppressed and not visible for example in Deadline log.
+
+
+___
+
+
+
+
+
+General: Remove ayon api from poetry lock #4964
+
+Remove AYON python api from pyproject.toml and poetry.lock again.
+
+
+___
+
+
+
+
+
+Ftrack: Fix AYON settings conversion #4967
+
+Fix conversion of ftrack settings in AYON mode.
+
+
+___
+
+
+
+
+
+AYON: ISO date format conversion issues #4981
+
+Function `datetime.fromisoformat` was replaced with `arrow.get` to be used instead.
+
+
+___
+
+
+
+
+
+AYON: Missing files on representations #4989
+
+Fix integration of files into representation in server database.
+
+
+___
+
+
+
+
+
+General: Fix Python 2 vendor for arrow #4993
+
+Moved remaining dependencies for arrow from ftrack to python 2 vendor.
+
+
+___
+
+
+
+
+
+General: Fix new load plugins for next minor relase #5000
+
+Fix access to `fname` attribute which is not available on load plugin anymore.
+
+
+___
+
+
+
+
+
+General: Fix mongo secure connection #5031
+
+Fix `ssl` and `tls` keys checks in mongo uri query string.
+
+
+___
+
+
+
+
+
+AYON: Fix site sync settings #5069
+
+Fixed settings for AYON variant of sync server.
+
+
+___
+
+
+
+
+
+General: Replace deprecated keyword argument in PyMongo #5080
+
+Use argument `tlsCAFile` instead of `ssl_ca_certs` to avoid deprecation warnings.
+
+
+___
+
+
+
+
+
+Igniter: QApplication is created #5081
+
+Function `_get_qt_app` actually creates new `QApplication` if was not created yet.
+
+
+___
+
+
+
+
+
+General: Lower unidecode version #5090
+
+Use older version of Unidecode module to support Python 2.
+
+
+___
+
+
+
+
+
+General: Lower cryptography to 39.0.0 #5099
+
+Lower cryptography to 39.0.0 to avoid breaking of DCCs like Maya and Nuke.
+
+
+___
+
+
+
+
+
+AYON: Global environments key fix #5118
+
+Seems that when converting ayon settings to OP settings the `environments` setting is put under the `environments` key in `general` however when populating the environment the `environment` key gets picked up, which does not contain the environment variables from the `core/environments` setting
+
+
+___
+
+
+
+
+
+Add collector to tray publisher for getting frame range data #5152
+
+Add collector to tray publisher to get frame range data. User can choose to enable this collector if they need this in the publisher.Resolve #5136
+
+
+___
+
+
+
+
+
+Unreal: get current project settings not using unreal project name #5170
+
+There was a bug where Unreal project name was used to query project settings. But Unreal project name can differ from the "real" one because of naming convention rules set by Unreal. This is fixing it by asking for current project settings.
+
+
+___
+
+
+
+
+
+Substance Painter: Fix Collect Texture Set Images unable to copy.deepcopy due to QMenu #5238
+
+Fix `copy.deepcopy` of `instance.data`.
+
+
+___
+
+
+
+
+
+Ayon: server returns different key #5251
+
+Package returned from server has `filename` instead of `name`.
+
+
+___
+
+
+
+
+
+Substance Painter: Fix default color management settings #5259
+
+The default settings for color management for Substance Painter were invalid, it was set to override the global config by default but specified no valid config paths of its own - and thus errored that the paths were not correct.This sets the defaults correctly to match other hosts._I quickly checked - this seems to be the only host with the wrong default settings_
+
+
+___
+
+
+
+
+
+Nuke: fixing container data if windows path in value #5267
+
+Windows path in container data are reformatted. Previously it was reported that Nuke was rising `utf8 0xc0` error if backward slashes were in data values.
+
+
+___
+
+
+
+
+
+Houdini: fix typo error in collect arnold rop #5281
+
+Fixing a typo error in `collect_arnold_rop.py`Reference: #5280
+
+
+___
+
+
+
+
+
+Slack - enhanced logging and protection against failure #5287
+
+Covered issues found in production on customer site. SlackAPI exception doesn't need to have 'error', covered uncaught exception.
+
+
+___
+
+
+
+
+
+Maya: Removed unnecessary import of pyblish.cli #5292
+
+This import resulted in adding additional logging handler which lead to duplication of logs in hosts with plugins containing `is_in_tests` method. Import is unnecessary for testing functionality.
+
+
+___
+
+
+
+### **🔀 Refactored code**
+
+
+
+Loader: Remove `context` argument from Loader.__init__() #4602
+
+Remove the previously required `context` argument.
+
+
+___
+
+
+
+
+
+Global: Remove legacy integrator #4786
+
+Remove the legacy integrator.
+
+
+___
+
+
+
+### **📃 Documentation**
+
+
+
+Next Minor Release #5291
+
+
+___
+
+
+
+### **Merged pull requests**
+
+
+
+Maya: Refactor to new publisher #4388
+
+**Refactor Maya to use the new publisher with new creators.**
+
+
+- [x] Legacy instance can be converted in UI using `SubsetConvertorPlugin`
+- [x] Fix support for old style "render" and "vrayscene" instance to the new per layer format.
+- [x] Context data is stored with scene
+- [x] Workfile instance converted to AutoCreator
+- [x] Converted Creator classes
+- [x] Create animation
+- [x] Create ass
+- [x] Create assembly
+- [x] Create camera
+- [x] Create layout
+- [x] Create look
+- [x] Create mayascene
+- [x] Create model
+- [x] Create multiverse look
+- [x] Create multiverse usd
+- [x] Create multiverse usd comp
+- [x] Create multiverse usd over
+- [x] Create pointcache
+- [x] Create proxy abc
+- [x] Create redshift proxy
+- [x] Create render
+- [x] Create rendersetup
+- [x] Create review
+- [x] Create rig
+- [x] Create setdress
+- [x] Create unreal skeletalmesh
+- [x] Create unreal staticmesh
+- [x] Create vrayproxy
+- [x] Create vrayscene
+- [x] Create xgen
+- [x] Create yeti cache
+- [x] Create yeti rig
+- [ ] Tested new Creator publishes
+- [x] Publish animation
+- [x] Publish ass
+- [x] Publish assembly
+- [x] Publish camera
+- [x] Publish layout
+- [x] Publish look
+- [x] Publish mayascene
+- [x] Publish model
+- [ ] Publish multiverse look
+- [ ] Publish multiverse usd
+- [ ] Publish multiverse usd comp
+- [ ] Publish multiverse usd over
+- [x] Publish pointcache
+- [x] Publish proxy abc
+- [x] Publish redshift proxy
+- [x] Publish render
+- [x] Publish rendersetup
+- [x] Publish review
+- [x] Publish rig
+- [x] Publish setdress
+- [x] Publish unreal skeletalmesh
+- [x] Publish unreal staticmesh
+- [x] Publish vrayproxy
+- [x] Publish vrayscene
+- [x] Publish xgen
+- [x] Publish yeti cache
+- [x] Publish yeti rig
+- [x] Publish workfile
+- [x] Rig loader correctly generates a new style animation creator instance
+- [ ] Validations / Error messages for common validation failures look nice and usable as a report.
+- [ ] Make Create Animation hidden to the user (should not create manually?)
+- [x] Correctly detect difference between **'creator_attributes'** and **'instance_data'** since both are "flattened" to the top node.
+
+
+___
+
+
+
+
+
+Start script: Fix possible issues with destination drive path #4478
+
+Drive paths for windows are fixing possibly missing slash at the end of destination path.
+
+Windows `subst` command require to have destination path with slash if it's a drive (it should be `G:\` not `G:`).
+
+
+___
+
+
+
+
+
+Global: Move PyOpenColorIO to vendor/python #4946
+
+So that DCCs don't conflict with their own.
+
+See https://github.com/ynput/OpenPype/pull/4267#issuecomment-1537153263 for the issue with Gaffer.
+
+I'm not sure if this is the correct approach, but I assume PySide/Shiboken is under `vendor/python` for this reason as well...
+___
+
+
+
+
+
+RuntimeError with Click on deadline publish #5065
+
+I changed Click to version 8.0 instead of 7.1.2 to solve this error:
+```
+2023-05-30 16:16:51: 0: STDOUT: Traceback (most recent call last):
+2023-05-30 16:16:51: 0: STDOUT: File "start.py", line 1126, in boot
+2023-05-30 16:16:51: 0: STDOUT: File "/prod/softprod/apps/openpype/LINUX/3.15/dependencies/click/core.py", line 829, in __call__
+2023-05-30 16:16:51: 0: STDOUT: return self.main(*args, **kwargs)
+2023-05-30 16:16:51: 0: STDOUT: File "/prod/softprod/apps/openpype/LINUX/3.15/dependencies/click/core.py", line 760, in main
+2023-05-30 16:16:51: 0: STDOUT: _verify_python3_env()
+2023-05-30 16:16:51: 0: STDOUT: File "/prod/softprod/apps/openpype/LINUX/3.15/dependencies/click/_unicodefun.py", line 126, in _verify_python3_env
+2023-05-30 16:16:51: 0: STDOUT: raise RuntimeError(
+2023-05-30 16:16:51: 0: STDOUT: RuntimeError: Click will abort further execution because Python 3 was configured to use ASCII as encoding for the environment. Consult https://click.palletsprojects.com/python3/ for mitigation steps.
+```
+
+
+___
+
+
+
+
+
+
+## [3.15.12](https://github.com/ynput/OpenPype/tree/3.15.12)
+
+
+[Full Changelog](https://github.com/ynput/OpenPype/compare/3.15.11...3.15.12)
+
+### **🆕 New features**
+
+
+
+Tray Publisher: User can set colorspace per instance explicitly #4901
+
+With this feature a user can set/override the colorspace for the representations of an instance explicitly instead of relying on the File Rules from project settings or alike. This way you can ingest any file and explicitly say "this file is colorspace X".
+
+
+___
+
+
+
+
+
+Review Family in Max #5001
+
+Review Feature by creating preview animation in 3dsmax(The code is still cleaning up so there is going to be some updates until it is ready for review)
+
+
+___
+
+
+
+
+
+AfterEffects: support for workfile template builder #5163
+
+This PR add functionality of templated workfile builder. It allows someone to prepare AE workfile with placeholders as for automatically loading particular representation of particular subset of particular asset from context where workfile is opened.Selection from multiple prepared workfiles is provided with usage of templates, specific type of tasks could use particular workfile template etc.Artists then can build workfile from template when opening new workfile.
+
+
+___
+
+
+
+
+
+CreatePlugin: Get next version helper #5242
+
+Implemented helper functions to get next available versions for create instances.
+
+
+___
+
+
+
+### **🚀 Enhancements**
+
+
+
+Maya: Improve Templates #4854
+
+Use library method for fetching reference node and support parent in hierarchy.
+
+
+___
+
+
+
+
+
+Bug: Maya - xgen sidecar files arent moved when saving workfile as an new asset workfile changing context - OP-6222 #5215
+
+This PR manages the Xgen files when switching context in the Workfiles app.
+
+
+___
+
+
+
+
+
+node references to check for duplicates in Max #5192
+
+No duplicates for node references in Max when users trying to select nodes before publishing
+
+
+___
+
+
+
+
+
+Tweak profiles logging to debug level #5194
+
+Tweak profiles logging to debug level since they aren't artist facing logs.
+
+
+___
+
+
+
+
+
+Enhancement: Reduce more visual clutter for artists in new publisher reports #5208
+
+Got this from one of our artists' reports - figured some of these logs were definitely not for the artist, reduced those logs to debug level.
+
+
+___
+
+
+
+
+
+Cosmetics: Tweak pyblish repair actions (icon, logs, docstring) #5213
+
+- Add icon to RepairContextAction
+- logs to debug level
+- also add attempt repair for RepairAction for consistency
+- fix RepairContextAction docstring to mention correct argument name
+
+#### Additional info
+
+We should not forget to remove this ["deprecated" actions.py file](https://github.com/ynput/OpenPype/blob/3501d0d23a78fbaef106da2fffe946cb49bef855/openpype/action.py) in 3.16 (next-minor)
+
+## Testing notes:
+
+1. Run some fabulous repairs!
+
+___
+
+
+
+
+
+Maya: fix save file prompt on launch last workfile with color management enabled + restructure `set_colorspace` #5225
+
+- Only set `configFilePath` when OCIO env var is not set since it doesn't do anything if OCIO var is set anyway.
+- Set the Maya 2022+ default OCIO path using the resources path instead of "" to avoid Maya Save File on new file after launch
+- **Bugfix: This is what fixes the Save prompt on open last workfile feature with Global color management enabled**
+- Move all code related to applying the maya settings together after querying the settings
+- Swap around the `if use_workfile_settings` since the check was reversed
+- Use `get_current_project_name()` instead of environment vars
+
+
+___
+
+
+
+
+
+Enhancement: More descriptive error messages for Loaders #5227
+
+Tweak raised errors and error messages for loader errors.
+
+
+___
+
+
+
+
+
+Houdini: add select invalid action for ValidateSopOutputNode #5231
+
+This PR adds `SelectROPAction` action to `houdini\api\action.py`and it's used in `Validate Output Node``SelectROPAction` is used to select the associated ROPs with the errored instances.
+
+
+___
+
+
+
+
+
+Remove new lines from the delivery template string #5235
+
+If the delivery template has a new line symbol at the end, say it was copied from the text editor, the delivery process will fail with `OSError` due to incorrect destination path. To avoid that I added `rstrip()` to the `delivery_path` processing.
+
+
+___
+
+
+
+
+
+Houdini: better selection on pointcache creation #5250
+
+Houdini allows `ObjNode` path as `sop_path` in the `ROP` unlike OP/ Ayon require `sop_path` to be set to a sop node path explicitly In this code, better selection is used to filter out invalid selections from OP/ Ayon point of viewValid selections are
+- `SopNode` that has parent of type `geo` or `subnet`
+- `ObjNode` of type `geo` that has
+- `SopNode` of type `output`
+- `SopNode` with render flag `on` (if no `Sopnode` of type `output`)this effectively filter
+- empty `ObjNode`
+- `ObjNode`(s) of other types like `cam` and `dopnet`
+- `SopNode`(s) that thier parents of other types like `cam` and `sop solver`
+
+
+___
+
+
+
+
+
+Update scene inventory even if any errors occurred during update #5252
+
+When selecting many items in the scene inventory to update versions and one of the items would error out the updating stops. However, before this PR the scene inventory would also NOT refresh making you think it did nothing.Also implemented as method to allow some code deduplication.
+
+
+___
+
+
+
+### **🐛 Bug fixes**
+
+
+
+Maya: Convert frame values to integers #5188
+
+Convert frame values to integers.
+
+
+___
+
+
+
+
+
+Maya: fix the register_event_callback correctly collecting workfile save after #5214
+
+fixing the bug of register_event_callback not being able to collect action of "workfile_save_after" for lock file action
+
+
+___
+
+
+
+
+
+Maya: aligning default settings to distributed aces 1.2 config #5233
+
+Maya colorspace setttings defaults are set the way they align our distributed ACES 1.2 config file set in global colorspace configs.
+
+
+___
+
+
+
+
+
+RepairAction and SelectInvalidAction filter instances failed on the exact plugin #5240
+
+RepairAction and SelectInvalidAction actually filter to instances that failed on the exact plugin - not on "any failure"
+
+
+___
+
+
+
+
+
+Maya: Bugfix look update nodes by id with non-unique shape names (query with `fullPath`) #5257
+
+Fixes a bug where updating attributes on nodes with assigned shader if shape name existed more than once in the scene due to `cmds.listRelatives` call not being done with the `fullPath=True` flag.Original error:
+```python
+# Traceback (most recent call last):
+# File "E:\openpype\OpenPype\openpype\tools\sceneinventory\view.py", line 264, in
+# lambda: self._show_version_dialog(items))
+# File "E:\openpype\OpenPype\openpype\tools\sceneinventory\view.py", line 722, in _show_version_dialog
+# self._update_containers(items, version)
+# File "E:\openpype\OpenPype\openpype\tools\sceneinventory\view.py", line 849, in _update_containers
+# update_container(item, item_version)
+# File "E:\openpype\OpenPype\openpype\pipeline\load\utils.py", line 502, in update_container
+# return loader.update(container, new_representation)
+# File "E:\openpype\OpenPype\openpype\hosts\maya\plugins\load\load_look.py", line 119, in update
+# nodes_by_id[lib.get_id(n)].append(n)
+# File "E:\openpype\OpenPype\openpype\hosts\maya\api\lib.py", line 1420, in get_id
+# sel.add(node)
+```
+
+
+___
+
+
+
+
+
+Nuke: Create nodes with inpanel=False #5051
+
+This PR is meant to remove the annoyance of the UI changing focus to the properties window just for the property window of the newly created node to disappear. Instead of using node.hideControlPanel I'm implementing the concealment during the creation of the node which will not change the focus of the current window.
+___
+
+
+
+
+
+Fix the reset frame range not setting up the right timeline in Max #5187
+
+Resolve #5181
+
+
+___
+
+
+
+
+
+Resolve: after launch automatization fixes #5193
+
+Workfile is no correctly created and aligned witch actual project. Also the launching mechanism is now fixed so even no workfile had been saved yet it will open OpenPype menu automatically.
+
+
+___
+
+
+
+
+
+General: Revert backward incompatible change of path to template to multiplatform #5197
+
+Now platformity is still handed by usage of `work[root]` (or any other root that is accessible across platforms.)
+
+
+___
+
+
+
+
+
+Nuke: root set format updating in node graph #5198
+
+Nuke root node needs to be reset on some values so any knobs could be updated in node graph. This works the same way as an user would change frame number so expressions would update its values in knobs.
+
+
+___
+
+
+
+
+
+Hiero: fixing otio current project and cosmetics #5200
+
+Otio were not returning correct current project once additional Untitled project was open in project manager stack.
+
+
+___
+
+
+
+
+
+Max: Publisher instances dont hold its enabled disabled states when Publisher reopened again #5202
+
+Resolve #5183, general maxscript conversion issue to python (e.g. bool conversion, true in maxscript while True in Python)(Also resolve the ValueError when you change the subset to publish into list view menu)
+
+
+___
+
+
+
+
+
+Burnins: Filter script is defined only for video streams #5205
+
+Burnins are working for inputs with audio.
+
+
+___
+
+
+
+
+
+Colorspace lib fix compatible python version comparison #5212
+
+Fix python version comparison.
+
+
+___
+
+
+
+
+
+Houdini: Fix `get_color_management_preferences` #5217
+
+Fix the issue described here where the logic for retrieving the current OCIO display and view was incorrectly trying to apply a regex to it.
+
+
+___
+
+
+
+
+
+Houdini: Redshift ROP image format bug #5218
+
+Problem :
+"RS_outputFileFormat" parm value was missing
+and there were more "image_format" than redshift rop supports
+
+Fix:
+1) removed unnecessary formats from `image_format_enum`
+2) add the selected format value to `RS_outputFileFormat`
+___
+
+
+
+
+
+Colorspace: check PyOpenColorIO rather then python version #5223
+
+Fixing previously merged PR (https://github.com/ynput/OpenPype/pull/5212) And applying better way to check compatibility with PyOpenColorIO python api.
+
+
+___
+
+
+
+
+
+Validate delivery action representations status #5228
+
+- disable delivery button if no representations checked
+- fix macos combobox layout
+- add error message if no delivery templates found
+
+
+___
+
+
+
+
+
+ Houdini: Add geometry check for pointcache family #5230
+
+When `sop_path` on ABC ROP node points to a non `SopNode`, these validators `validate_abc_primitive_to_detail.py`, `validate_primitive_hierarchy_paths.py` will error and crash when this line is executed `geo = output_node.geometryAtFrame(frame)`
+
+
+___
+
+
+
+
+
+Houdini: Add geometry check for VDB family #5232
+
+When `sop_path` on Geometry ROP node points to a non SopNode, this validator `validate_vdb_output_node.py` will error and crash when this line is executed`sop_node.geometryAtFrame(frame)`
+
+
+___
+
+
+
+
+
+Substance Painter: Include the setting only in publish tab #5234
+
+Instead of having two settings in both create and publish tab, there is solely one setting in the publish tab for users to set up the parameters.Resolve #5172
+
+
+___
+
+
+
+
+
+Maya: Fix collecting arnold prefix when none #5243
+
+When no prefix is specified in render settings, the renderlayer collector would error.
+
+
+___
+
+
+
+
+
+Deadline: OPENPYPE_VERSION should only be added when running from build #5244
+
+When running from source the environment variable `OPENPYPE_VERSION` should not be added. This is a bugfix for the feature #4489
+
+
+___
+
+
+
+
+
+Fix no prompt for "unsaved changes" showing when opening workfile in Houdini #5246
+
+Fix no prompt for "unsaved changes" showing when opening workfile in Houdini.
+
+
+___
+
+
+
+
+
+Fix no prompt for "unsaved changes" showing when opening workfile in Substance Painter #5248
+
+Fix no prompt for "unsaved changes" showing when opening workfile in Substance Painter.
+
+
+___
+
+
+
+
+
+General: add the os library before os.environ.get #5249
+
+Adding os library into `creator_plugins.py` due to `os.environ.get` in line 667
+
+
+___
+
+
+
+
+
+Maya: Fix set_attribute for enum attributes #5261
+
+Fix for #5260
+
+
+___
+
+
+
+
+
+Unreal: Move Qt imports away from module init #5268
+
+Importing `Window` creates errors in headless mode.
+```
+*** WRN: >>> { ModulesLoader }: [ FAILED to import host folder unreal ]
+=============================
+No Qt bindings could be found
+=============================
+Traceback (most recent call last):
+ File "C:\Users\tokejepsen\OpenPype\.venv\lib\site-packages\qtpy\__init__.py", line 252, in
+ from PySide6 import __version__ as PYSIDE_VERSION # analysis:ignore
+ModuleNotFoundERROR: No module named 'PySide6'
+
+During handling of the above exception, another exception occurred:
+
+Traceback (most recent call last):
+ File "C:\Users\tokejepsen\OpenPype\openpype\modules\base.py", line 385, in _load_modules
+ default_module = __import__(
+ File "C:\Users\tokejepsen\OpenPype\openpype\hosts\unreal\__init__.py", line 1, in
+ from .addon import UnrealAddon
+ File "C:\Users\tokejepsen\OpenPype\openpype\hosts\unreal\addon.py", line 4, in
+ from openpype.widgets.message_window import Window
+ File "C:\Users\tokejepsen\OpenPype\openpype\widgets\__init__.py", line 1, in
+ from .password_dialog import PasswordDialog
+ File "C:\Users\tokejepsen\OpenPype\openpype\widgets\password_dialog.py", line 1, in
+ from qtpy import QtWidgets, QtCore, QtGui
+ File "C:\Users\tokejepsen\OpenPype\.venv\lib\site-packages\qtpy\__init__.py", line 259, in
+ raise QtBindingsNotFoundERROR()
+qtpy.QtBindingsNotFoundERROR: No Qt bindings could be found
+```
+
+
+___
+
+
+
+### **🔀 Refactored code**
+
+
+
+Maya: Minor refactoring and code cleanup #5226
+
+Some small cleanup and refactoring of logic. Removing old comments, unused imports and some minor optimization. Also removed the prints of the loader names of each container the scene in `fix_incompatible_containers` + optimizing by using `set` and defining only once. Moved some UI related code/tweaks to run `on_init` only if not in headless mode. Removed an empty `obj.py` file.Each commit message kind of describes why the change was made.
+
+
+___
+
+
+
+### **Merged pull requests**
+
+
+
+Bug: Template builder fails when loading data without outliner representation #5222
+
+I add an assertion management in case the container does not have a represention in outliner.
+
+
+___
+
+
+
+
+
+AfterEffects - add container check validator to AE settings #5203
+
+Adds check if scene contains only latest version of loaded containers.
+
+
+___
+
+
+
+
+
+
+## [3.15.11](https://github.com/ynput/OpenPype/tree/3.15.11)
+
+
+[Full Changelog](https://github.com/ynput/OpenPype/compare/3.15.10...3.15.11)
+
+### **🆕 New features**
+
+
+
+Ftrack: Task status during publishing #5123
+
+Added option to change task status during publishing for 3 possible cases: "sending to farm", "local integration" and "on farm integration".
+
+
+___
+
+
+
+
+
+Nuke: Allow for more complex temp rendering paths #5132
+
+When changing the temporary rendering template (i.e., add `{asset}` to the path) to something a bit more complex the formatting was erroring due to missing keys.
+
+
+___
+
+
+
+
+
+Blender: Add support for custom path for app templates #5137
+
+This PR adds support for a custom App Templates path in Blender by setting the `BLENDER_USER_SCRIPTS` environment variable to the path specified in `OPENPYPE_APP_TEMPLATES_PATH`. This allows users to use their own custom app templates in Blender.
+
+
+___
+
+
+
+
+
+TrayPublisher & StandalonePublisher: Specify version #5142
+
+Simple creators in TrayPublisher can affect which version will be integrated. Standalone publisher respects the version change from UI.
+
+
+___
+
+
+
+### **🚀 Enhancements**
+
+
+
+Workfile Builder UI: Workfile builder window is not modal #5131
+
+Workfile Templates Builder:
+- Create dialog is not a modal dialog
+- Create dialog remains open after create, so you can directly create a new placeholder with similar settings
+- In Maya allow to create root level placeholders (no selection during create) - **this felt more like a bugfix than anything else.**
+
+
+___
+
+
+
+
+
+3dsmax: Use custom modifiers to hold instance members #4931
+
+Moving logic to handle members of publishing instance from children/parent relationship on Container to tracking via custom attribute on modifier. This eliminates limitations where you couldn't have one node multiple times under one Container and because it stores those relationships as weak references, they are easily transferable even when original nodes are renamed.
+
+
+___
+
+
+
+
+
+Add height, width and fps setup to project manager #5075
+
+Add Width, Height, FPS, Pixel Aspect and Frame Start/End to the Project creation dialogue in the Project Manager.I understand that the Project manager will be replaced in the upcoming Ayon, but for the time being I believe setting new project with these options available would be more fun.
+
+
+___
+
+
+
+
+
+Nuke: connect custom write node script to the OP setting #5113
+
+Allows user to customize the values of knobs attribute in the OP setting and use it in custom write node
+
+
+___
+
+
+
+
+
+Keep `publisher.create_widget` variant when creating subsets #5119
+
+Whenever a person is creating a subset to publish, the "creator" widget resets (where you choose the variant, product, etc.) so if the person is publishing several images of the a variant which is not the default one, they have to keep selecting the correct one after every "create".
+
+This commit resets the original variant upon successful creation of a subset for publishing.
+
+Demo:
+[Screencast from 2023-06-08 10-46-40.webm](https://github.com/ynput/OpenPype/assets/1800151/ca1c91d4-b8f3-43d2-a7b7-35987f5b6a3f)
+
+## Testing notes:
+1. Launch AYON/OP
+2. Launch the publisher (select a project, shot, etc.)
+3. Crete a publish type (any works)
+4. Choose a variant for the publish that is not the default
+5. "Create >>"
+
+The Variant fields should still have the variant you choose.
+
+
+
+___
+
+
+
+
+
+Color Management- added color management support for simple expected files on Deadline #5122
+
+Running of `ExtractOIIOTranscode` during Deadline publish was previously implemented only on DCCs with AOVs (Maya, Max).This PR extends this for other DCCs with flat structure of expected files.
+
+
+___
+
+
+
+
+
+hide macos dock icon on build #5133
+
+Set `LSUIElement` to `1` in the `Info.plist` to hide OP icon from the macos dock by default.
+
+
+___
+
+
+
+
+
+Pack project: Raise exception with reasonable message #5145
+
+Pack project crashes with relevant message when destination directory is not set.
+
+
+___
+
+
+
+
+
+Allow "inventory" actions to be supplied by a Module/Addon. #5146
+
+Adds "inventory" as a possible key to the plugin paths to be returned from a module.
+
+
+___
+
+
+
+
+
+3dsmax: make code compatible with 3dsmax 2022 #5164
+
+Python 3.7 in 3dsmax 2022 is not supporting walrus operator. This is removing it from the code for the sake of compatibility
+
+
+___
+
+
+
+### **🐛 Bug fixes**
+
+
+
+Maya: Support same attribute names on different node types. #5054
+
+When validating render settings attributes, support same attribute names on different node types.
+
+
+___
+
+
+
+
+
+Maya: bug fix the standin being not loaded when they are first loaded #5143
+
+fix the bug of raising error when the first two standins are loaded through the loaderThe bug mentioned in the related issue: https://github.com/ynput/OpenPype/issues/5129For some reason, `defaultArnoldRenderOptions.operator` is not listed in the connection node attribute even if `cmds.loadPlugin("mtoa", quiet=True)` executed before loading the object as standins for the first time.But if you manually turn on mtoa through plugin preference and load the standins for the first time, it won't raise the related `defaultArnoldRenderOptions.operator` error.
+
+
+___
+
+
+
+
+
+Maya: bug fix arnoldExportAss unable to export selected set members #5150
+
+See #5108 fix the bug arnoldExportAss being not able to export and error out during extraction.
+
+
+___
+
+
+
+
+
+Maya: Xgen multiple descriptions on single shape - OP-6039 #5160
+
+When having multiple descriptions on the same geometry, the extraction would produce redundant duplicate geometries.
+
+
+___
+
+
+
+
+
+Maya: Xgen export of Abc's during Render Publishing - OP-6206 #5167
+
+Shading assignments was missing duplicating the setup for Xgen publishing and the exporting of patches was getting the end frame incorrectly.
+
+
+___
+
+
+
+
+
+Maya: Include handles - OP-6236 #5175
+
+Render range was missing the handles.
+
+
+___
+
+
+
+
+
+OCIO: Support working with single frame renders #5053
+
+When there is only 1 file, the datamember `files` on the representation should be a string.
+
+
+___
+
+
+
+
+
+Burnins: Refactored burnins script #5094
+
+Refactored list value for burnins and fixed command length limit by using temp file for filters string.
+
+
+___
+
+
+
+
+
+Nuke: open_file function can open autosave script #5107
+
+Fix the bug of the workfile dialog being unable to open autosave nuke script
+
+
+___
+
+
+
+
+
+ImageIO: Minor fixes #5147
+
+Resolve few minor fixes related to latest image io changes from PR.
+
+
+___
+
+
+
+
+
+Publisher: Fix save shortcut #5148
+
+Save shortcut should work for both PySide2 and PySide6.
+
+
+___
+
+
+
+
+
+Pack Project: Fix files packing #5154
+
+Packing of project with files does work again.
+
+
+___
+
+
+
+
+
+Maya: Xgen version mismatch after publish - OP-6204 #5161
+
+Xgen was not updating correctly when for example adding or removing descriptions. This resolve the issue by overwritting the workspace xgen file.
+
+
+___
+
+
+
+
+
+Publisher: Edge case fixes #5165
+
+Fix few edge case issues that may cause issues in Publisher UI.
+
+
+___
+
+
+
+
+
+Colorspace: host config path backward compatibility #5166
+
+Old project settings overrides are now fully backward compatible. The issue with host config paths overrides were solved and now once a project used to be set to ocio_config **enabled** with found filepaths - this is now considered as activated host ocio_config paths overrides.Nuke is having an popup dialogue which is letting know to a user that settings for config path were changed.
+
+
+___
+
+
+
+
+
+Maya: import workfile missing - OP-6233 #5174
+
+Missing `workfile` family to import.
+
+
+___
+
+
+
+
+
+Ftrack: Fix ignore sync filter #5176
+
+Ftrack ignore filter does not crash because of dictionary modifications during it's iteration.
+
+
+___
+
+
+
+
+
+Webpublisher - headless publish shouldn't be blocking operation #5177
+
+`subprocess.call` was blocking, which resulted in UI non responsiveness as it was waiting for publish to finish.
+
+
+___
+
+
+
+
+
+Publisher: Fix disappearing actions #5184
+
+Pyblish plugin actions are visible as expected.
+
+
+___
+
+
+
+### **Merged pull requests**
+
+
+
+Enhancement:animation family loaded as standing (abc) uses "use file sequence" #5110
+
+The changes are the following. We started by updating the the is_sequence(files) function allowing it to return True for a list of files which has only one file, since our animation in this provides just one alembic file. For the correct FPS number, we got the fps from the published ass/abc from the version data.
+
+
+___
+
+
+
+
+
+add label to matching family #5128
+
+I added the possibility to filter the `family smart select` with the label in addition to the family.
+
+
+___
+
+
+
+
+
+
## [3.15.10](https://github.com/ynput/OpenPype/tree/3.15.10)
diff --git a/README.md b/README.md
index 8757e3db92..6caed8061c 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[](#contributors-)
OpenPype
-====
+========
[](https://github.com/pypeclub/pype/actions/workflows/documentation.yml) 
@@ -47,7 +47,7 @@ It can be built and ran on all common platforms. We develop and test on the foll
For more details on requirements visit [requirements documentation](https://openpype.io/docs/dev_requirements)
Building OpenPype
--------------
+-----------------
To build OpenPype you currently need [Python 3.9](https://www.python.org/downloads/) as we are following
[vfx platform](https://vfxplatform.com). Because of some Linux distros comes with newer Python version
@@ -67,9 +67,9 @@ git clone --recurse-submodules git@github.com:Pypeclub/OpenPype.git
#### To build OpenPype:
-1) Run `.\tools\create_env.ps1` to create virtual environment in `.\venv`
+1) Run `.\tools\create_env.ps1` to create virtual environment in `.\venv`.
2) Run `.\tools\fetch_thirdparty_libs.ps1` to download third-party dependencies like ffmpeg and oiio. Those will be included in build.
-3) Run `.\tools\build.ps1` to build OpenPype executables in `.\build\`
+3) Run `.\tools\build.ps1` to build OpenPype executables in `.\build\`.
To create distributable OpenPype versions, run `./tools/create_zip.ps1` - that will
create zip file with name `openpype-vx.x.x.zip` parsed from current OpenPype repository and
@@ -88,38 +88,38 @@ some OpenPype dependencies like [CMake](https://cmake.org/) and **XCode Command
Easy way of installing everything necessary is to use [Homebrew](https://brew.sh):
1) Install **Homebrew**:
-```sh
-/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-```
+ ```sh
+ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
+ ```
2) Install **cmake**:
-```sh
-brew install cmake
-```
+ ```sh
+ brew install cmake
+ ```
3) Install [pyenv](https://github.com/pyenv/pyenv):
-```sh
-brew install pyenv
-echo 'eval "$(pyenv init -)"' >> ~/.zshrc
-pyenv init
-exec "$SHELL"
-PATH=$(pyenv root)/shims:$PATH
-```
+ ```sh
+ brew install pyenv
+ echo 'eval "$(pyenv init -)"' >> ~/.zshrc
+ pyenv init
+ exec "$SHELL"
+ PATH=$(pyenv root)/shims:$PATH
+ ```
-4) Pull in required Python version 3.9.x
-```sh
-# install Python build dependences
-brew install openssl readline sqlite3 xz zlib
+4) Pull in required Python version 3.9.x:
+ ```sh
+ # install Python build dependences
+ brew install openssl readline sqlite3 xz zlib
-# replace with up-to-date 3.9.x version
-pyenv install 3.9.6
-```
+ # replace with up-to-date 3.9.x version
+ pyenv install 3.9.6
+ ```
-5) Set local Python version
-```sh
-# switch to OpenPype source directory
-pyenv local 3.9.6
-```
+5) Set local Python version:
+ ```sh
+ # switch to OpenPype source directory
+ pyenv local 3.9.6
+ ```
#### To build OpenPype:
@@ -241,7 +241,7 @@ pyenv local 3.9.6
Running OpenPype
-------------
+----------------
OpenPype can by executed either from live sources (this repository) or from
*"frozen code"* - executables that can be build using steps described above.
@@ -289,7 +289,7 @@ To run tests, execute `.\tools\run_tests(.ps1|.sh)`.
Developer tools
--------------
+---------------
In case you wish to add your own tools to `.\tools` folder without git tracking, it is possible by adding it with `dev_*` suffix (example: `dev_clear_pyc(.ps1|.sh)`).
diff --git a/ayon_start.py b/ayon_start.py
new file mode 100644
index 0000000000..458c46bba6
--- /dev/null
+++ b/ayon_start.py
@@ -0,0 +1,483 @@
+# -*- coding: utf-8 -*-
+"""Main entry point for AYON command.
+
+Bootstrapping process of AYON.
+"""
+import os
+import sys
+import site
+import traceback
+import contextlib
+
+
+# Enabled logging debug mode when "--debug" is passed
+if "--verbose" in sys.argv:
+ expected_values = (
+ "Expected: notset, debug, info, warning, error, critical"
+ " or integer [0-50]."
+ )
+ idx = sys.argv.index("--verbose")
+ sys.argv.pop(idx)
+ if idx < len(sys.argv):
+ value = sys.argv.pop(idx)
+ else:
+ raise RuntimeError((
+ f"Expect value after \"--verbose\" argument. {expected_values}"
+ ))
+
+ log_level = None
+ low_value = value.lower()
+ if low_value.isdigit():
+ log_level = int(low_value)
+ elif low_value == "notset":
+ log_level = 0
+ elif low_value == "debug":
+ log_level = 10
+ elif low_value == "info":
+ log_level = 20
+ elif low_value == "warning":
+ log_level = 30
+ elif low_value == "error":
+ log_level = 40
+ elif low_value == "critical":
+ log_level = 50
+
+ if log_level is None:
+ raise ValueError((
+ "Unexpected value after \"--verbose\" "
+ f"argument \"{value}\". {expected_values}"
+ ))
+
+ os.environ["OPENPYPE_LOG_LEVEL"] = str(log_level)
+ os.environ["AYON_LOG_LEVEL"] = str(log_level)
+
+# Enable debug mode, may affect log level if log level is not defined
+if "--debug" in sys.argv:
+ sys.argv.remove("--debug")
+ os.environ["AYON_DEBUG"] = "1"
+ os.environ["OPENPYPE_DEBUG"] = "1"
+
+if "--automatic-tests" in sys.argv:
+ sys.argv.remove("--automatic-tests")
+ os.environ["IS_TEST"] = "1"
+
+SKIP_HEADERS = False
+if "--skip-headers" in sys.argv:
+ sys.argv.remove("--skip-headers")
+ SKIP_HEADERS = True
+
+SKIP_BOOTSTRAP = False
+if "--skip-bootstrap" in sys.argv:
+ sys.argv.remove("--skip-bootstrap")
+ SKIP_BOOTSTRAP = True
+
+if "--use-staging" in sys.argv:
+ sys.argv.remove("--use-staging")
+ os.environ["AYON_USE_STAGING"] = "1"
+ os.environ["OPENPYPE_USE_STAGING"] = "1"
+
+if "--headless" in sys.argv:
+ os.environ["AYON_HEADLESS_MODE"] = "1"
+ os.environ["OPENPYPE_HEADLESS_MODE"] = "1"
+ sys.argv.remove("--headless")
+
+elif (
+ os.getenv("AYON_HEADLESS_MODE") != "1"
+ or os.getenv("OPENPYPE_HEADLESS_MODE") != "1"
+):
+ os.environ.pop("AYON_HEADLESS_MODE", None)
+ os.environ.pop("OPENPYPE_HEADLESS_MODE", None)
+
+elif (
+ os.getenv("AYON_HEADLESS_MODE")
+ != os.getenv("OPENPYPE_HEADLESS_MODE")
+):
+ os.environ["OPENPYPE_HEADLESS_MODE"] = (
+ os.environ["AYON_HEADLESS_MODE"]
+ )
+
+IS_BUILT_APPLICATION = getattr(sys, "frozen", False)
+HEADLESS_MODE_ENABLED = os.getenv("AYON_HEADLESS_MODE") == "1"
+
+_pythonpath = os.getenv("PYTHONPATH", "")
+_python_paths = _pythonpath.split(os.pathsep)
+if not IS_BUILT_APPLICATION:
+ # Code root defined by `start.py` directory
+ AYON_ROOT = os.path.dirname(os.path.abspath(__file__))
+ _dependencies_path = site.getsitepackages()[-1]
+else:
+ AYON_ROOT = os.path.dirname(sys.executable)
+
+ # add dependencies folder to sys.pat for frozen code
+ _dependencies_path = os.path.normpath(
+ os.path.join(AYON_ROOT, "dependencies")
+ )
+# add stuff from `/dependencies` to PYTHONPATH.
+sys.path.append(_dependencies_path)
+_python_paths.append(_dependencies_path)
+
+# Vendored python modules that must not be in PYTHONPATH environment but
+# are required for OpenPype processes
+sys.path.insert(0, os.path.join(AYON_ROOT, "vendor", "python"))
+
+# Add common package to sys path
+# - common contains common code for bootstraping and OpenPype processes
+sys.path.insert(0, os.path.join(AYON_ROOT, "common"))
+
+# This is content of 'core' addon which is ATM part of build
+common_python_vendor = os.path.join(
+ AYON_ROOT,
+ "openpype",
+ "vendor",
+ "python",
+ "common"
+)
+# Add tools dir to sys path for pyblish UI discovery
+tools_dir = os.path.join(AYON_ROOT, "openpype", "tools")
+for path in (AYON_ROOT, common_python_vendor, tools_dir):
+ while path in _python_paths:
+ _python_paths.remove(path)
+
+ while path in sys.path:
+ sys.path.remove(path)
+
+ _python_paths.insert(0, path)
+ sys.path.insert(0, path)
+
+os.environ["PYTHONPATH"] = os.pathsep.join(_python_paths)
+
+# enabled AYON state
+os.environ["USE_AYON_SERVER"] = "1"
+# Set this to point either to `python` from venv in case of live code
+# or to `ayon` or `ayon_console` in case of frozen code
+os.environ["AYON_EXECUTABLE"] = sys.executable
+os.environ["OPENPYPE_EXECUTABLE"] = sys.executable
+os.environ["AYON_ROOT"] = AYON_ROOT
+os.environ["OPENPYPE_ROOT"] = AYON_ROOT
+os.environ["OPENPYPE_REPOS_ROOT"] = AYON_ROOT
+os.environ["AYON_MENU_LABEL"] = "AYON"
+os.environ["AVALON_LABEL"] = "AYON"
+# Set name of pyblish UI import
+os.environ["PYBLISH_GUI"] = "pyblish_pype"
+# Set builtin OCIO root
+os.environ["BUILTIN_OCIO_ROOT"] = os.path.join(
+ AYON_ROOT,
+ "vendor",
+ "bin",
+ "ocioconfig",
+ "OpenColorIOConfigs"
+)
+
+import blessed # noqa: E402
+import certifi # noqa: E402
+
+
+if sys.__stdout__:
+ term = blessed.Terminal()
+
+ def _print(message: str):
+ if message.startswith("!!! "):
+ print(f'{term.orangered2("!!! ")}{message[4:]}')
+ elif message.startswith(">>> "):
+ print(f'{term.aquamarine3(">>> ")}{message[4:]}')
+ elif message.startswith("--- "):
+ print(f'{term.darkolivegreen3("--- ")}{message[4:]}')
+ elif message.startswith("*** "):
+ print(f'{term.gold("*** ")}{message[4:]}')
+ elif message.startswith(" - "):
+ print(f'{term.wheat(" - ")}{message[4:]}')
+ elif message.startswith(" . "):
+ print(f'{term.tan(" . ")}{message[4:]}')
+ elif message.startswith(" - "):
+ print(f'{term.seagreen3(" - ")}{message[7:]}')
+ elif message.startswith(" ! "):
+ print(f'{term.goldenrod(" ! ")}{message[7:]}')
+ elif message.startswith(" * "):
+ print(f'{term.aquamarine1(" * ")}{message[7:]}')
+ elif message.startswith(" "):
+ print(f'{term.darkseagreen3(" ")}{message[4:]}')
+ else:
+ print(message)
+else:
+ def _print(message: str):
+ print(message)
+
+
+# if SSL_CERT_FILE is not set prior to OpenPype launch, we set it to point
+# to certifi bundle to make sure we have reasonably new CA certificates.
+if not os.getenv("SSL_CERT_FILE"):
+ os.environ["SSL_CERT_FILE"] = certifi.where()
+elif os.getenv("SSL_CERT_FILE") != certifi.where():
+ _print("--- your system is set to use custom CA certificate bundle.")
+
+from ayon_api import get_base_url
+from ayon_api.constants import SERVER_URL_ENV_KEY, SERVER_API_ENV_KEY
+from ayon_common import is_staging_enabled
+from ayon_common.connection.credentials import (
+ ask_to_login_ui,
+ add_server,
+ need_server_or_login,
+ load_environments,
+ set_environments,
+ create_global_connection,
+ confirm_server_login,
+)
+from ayon_common.distribution import (
+ AyonDistribution,
+ BundleNotFoundError,
+ show_missing_bundle_information,
+)
+
+
+def set_global_environments() -> None:
+ """Set global OpenPype's environments."""
+ import acre
+
+ from openpype.settings import get_general_environments
+
+ general_env = get_general_environments()
+
+ # first resolve general environment because merge doesn't expect
+ # values to be list.
+ # TODO: switch to OpenPype environment functions
+ merged_env = acre.merge(
+ acre.compute(acre.parse(general_env), cleanup=False),
+ dict(os.environ)
+ )
+ env = acre.compute(
+ merged_env,
+ cleanup=False
+ )
+ os.environ.clear()
+ os.environ.update(env)
+
+ # Hardcoded default values
+ os.environ["PYBLISH_GUI"] = "pyblish_pype"
+ # Change scale factor only if is not set
+ if "QT_AUTO_SCREEN_SCALE_FACTOR" not in os.environ:
+ os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
+
+
+def set_addons_environments():
+ """Set global environments for OpenPype modules.
+
+ This requires to have OpenPype in `sys.path`.
+ """
+
+ import acre
+ from openpype.modules import ModulesManager
+
+ modules_manager = ModulesManager()
+
+ # Merge environments with current environments and update values
+ if module_envs := modules_manager.collect_global_environments():
+ parsed_envs = acre.parse(module_envs)
+ env = acre.merge(parsed_envs, dict(os.environ))
+ os.environ.clear()
+ os.environ.update(env)
+
+
+def _connect_to_ayon_server():
+ load_environments()
+ if not need_server_or_login():
+ create_global_connection()
+ return
+
+ if HEADLESS_MODE_ENABLED:
+ _print("!!! Cannot open v4 Login dialog in headless mode.")
+ _print((
+ "!!! Please use `{}` to specify server address"
+ " and '{}' to specify user's token."
+ ).format(SERVER_URL_ENV_KEY, SERVER_API_ENV_KEY))
+ sys.exit(1)
+
+ current_url = os.environ.get(SERVER_URL_ENV_KEY)
+ url, token, username = ask_to_login_ui(current_url, always_on_top=True)
+ if url is not None and token is not None:
+ confirm_server_login(url, token, username)
+ return
+
+ if url is not None:
+ add_server(url, username)
+
+ _print("!!! Login was not successful.")
+ sys.exit(0)
+
+
+def _check_and_update_from_ayon_server():
+ """Gets addon info from v4, compares with local folder and updates it.
+
+ Raises:
+ RuntimeError
+ """
+
+ distribution = AyonDistribution()
+ bundle = None
+ bundle_name = None
+ try:
+ bundle = distribution.bundle_to_use
+ if bundle is not None:
+ bundle_name = bundle.name
+ except BundleNotFoundError as exc:
+ bundle_name = exc.bundle_name
+
+ if bundle is None:
+ url = get_base_url()
+ if not HEADLESS_MODE_ENABLED:
+ show_missing_bundle_information(url, bundle_name)
+
+ elif bundle_name:
+ _print((
+ f"!!! Requested release bundle '{bundle_name}'"
+ " is not available on server."
+ ))
+ _print(
+ "!!! Check if selected release bundle"
+ f" is available on the server '{url}'."
+ )
+
+ else:
+ mode = "staging" if is_staging_enabled() else "production"
+ _print(
+ f"!!! No release bundle is set as {mode} on the AYON server."
+ )
+ _print(
+ "!!! Make sure there is a release bundle set"
+ f" as \"{mode}\" on the AYON server '{url}'."
+ )
+ sys.exit(1)
+
+ distribution.distribute()
+ distribution.validate_distribution()
+ os.environ["AYON_BUNDLE_NAME"] = bundle_name
+
+ python_paths = [
+ path
+ for path in os.getenv("PYTHONPATH", "").split(os.pathsep)
+ if path
+ ]
+
+ for path in distribution.get_sys_paths():
+ sys.path.insert(0, path)
+ if path not in python_paths:
+ python_paths.append(path)
+ os.environ["PYTHONPATH"] = os.pathsep.join(python_paths)
+
+
+def boot():
+ """Bootstrap OpenPype."""
+
+ from openpype.version import __version__
+
+ # TODO load version
+ os.environ["OPENPYPE_VERSION"] = __version__
+ os.environ["AYON_VERSION"] = __version__
+
+ _connect_to_ayon_server()
+ _check_and_update_from_ayon_server()
+
+ # delete OpenPype module and it's submodules from cache so it is used from
+ # specific version
+ modules_to_del = [
+ sys.modules.pop(module_name)
+ for module_name in tuple(sys.modules)
+ if module_name == "openpype" or module_name.startswith("openpype.")
+ ]
+
+ for module_name in modules_to_del:
+ with contextlib.suppress(AttributeError, KeyError):
+ del sys.modules[module_name]
+
+
+def main_cli():
+ from openpype import cli
+ from openpype.version import __version__
+ from openpype.lib import terminal as t
+
+ _print(">>> loading environments ...")
+ _print(" - global AYON ...")
+ set_global_environments()
+ _print(" - for addons ...")
+ set_addons_environments()
+
+ # print info when not running scripts defined in 'silent commands'
+ if not SKIP_HEADERS:
+ info = get_info(is_staging_enabled())
+ info.insert(0, f">>> Using AYON from [ {AYON_ROOT} ]")
+
+ t_width = 20
+ with contextlib.suppress(ValueError, OSError):
+ t_width = os.get_terminal_size().columns - 2
+
+ _header = f"*** AYON [{__version__}] "
+ info.insert(0, _header + "-" * (t_width - len(_header)))
+
+ for i in info:
+ t.echo(i)
+
+ try:
+ cli.main(obj={}, prog_name="ayon")
+ except Exception: # noqa
+ exc_info = sys.exc_info()
+ _print("!!! AYON crashed:")
+ traceback.print_exception(*exc_info)
+ sys.exit(1)
+
+
+def script_cli():
+ """Run and execute script."""
+
+ filepath = os.path.abspath(sys.argv[1])
+
+ # Find '__main__.py' in directory
+ if os.path.isdir(filepath):
+ new_filepath = os.path.join(filepath, "__main__.py")
+ if not os.path.exists(new_filepath):
+ raise RuntimeError(
+ f"can't find '__main__' module in '{filepath}'")
+ filepath = new_filepath
+
+ # Add parent dir to sys path
+ sys.path.insert(0, os.path.dirname(filepath))
+
+ # Read content and execute
+ with open(filepath, "r") as stream:
+ content = stream.read()
+
+ exec(compile(content, filepath, "exec"), globals())
+
+
+def get_info(use_staging=None) -> list:
+ """Print additional information to console."""
+
+ inf = []
+ if use_staging:
+ inf.append(("AYON variant", "staging"))
+ else:
+ inf.append(("AYON variant", "production"))
+ inf.append(("AYON bundle", os.getenv("AYON_BUNDLE")))
+
+ # NOTE add addons information
+
+ maximum = max(len(i[0]) for i in inf)
+ formatted = []
+ for info in inf:
+ padding = (maximum - len(info[0])) + 1
+ formatted.append(f'... {info[0]}:{" " * padding}[ {info[1]} ]')
+ return formatted
+
+
+def main():
+ if not SKIP_BOOTSTRAP:
+ boot()
+
+ args = list(sys.argv)
+ args.pop(0)
+ if args and os.path.exists(args[0]):
+ script_cli()
+ else:
+ main_cli()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/common/ayon_common/__init__.py b/common/ayon_common/__init__.py
new file mode 100644
index 0000000000..ddabb7da2f
--- /dev/null
+++ b/common/ayon_common/__init__.py
@@ -0,0 +1,16 @@
+from .utils import (
+ IS_BUILT_APPLICATION,
+ is_staging_enabled,
+ get_local_site_id,
+ get_ayon_appdirs,
+ get_ayon_launch_args,
+)
+
+
+__all__ = (
+ "IS_BUILT_APPLICATION",
+ "is_staging_enabled",
+ "get_local_site_id",
+ "get_ayon_appdirs",
+ "get_ayon_launch_args",
+)
diff --git a/common/openpype_common/distribution/__init__.py b/common/ayon_common/connection/__init__.py
similarity index 100%
rename from common/openpype_common/distribution/__init__.py
rename to common/ayon_common/connection/__init__.py
diff --git a/common/ayon_common/connection/credentials.py b/common/ayon_common/connection/credentials.py
new file mode 100644
index 0000000000..7f70cb7992
--- /dev/null
+++ b/common/ayon_common/connection/credentials.py
@@ -0,0 +1,511 @@
+"""Handle credentials and connection to server for client application.
+
+Cache and store used server urls. Store/load API keys to/from keyring if
+needed. Store metadata about used urls, usernames for the urls and when was
+the connection with the username established.
+
+On bootstrap is created global connection with information about site and
+client version. The connection object lives in 'ayon_api'.
+"""
+
+import os
+import json
+import platform
+import datetime
+import contextlib
+import subprocess
+import tempfile
+from typing import Optional, Union, Any
+
+import ayon_api
+
+from ayon_api.constants import SERVER_URL_ENV_KEY, SERVER_API_ENV_KEY
+from ayon_api.exceptions import UrlError
+from ayon_api.utils import (
+ validate_url,
+ is_token_valid,
+ logout_from_server,
+)
+
+from ayon_common.utils import (
+ get_ayon_appdirs,
+ get_local_site_id,
+ get_ayon_launch_args,
+ is_staging_enabled,
+)
+
+
+class ChangeUserResult:
+ def __init__(
+ self, logged_out, old_url, old_token, old_username,
+ new_url, new_token, new_username
+ ):
+ shutdown = logged_out
+ restart = new_url is not None and new_url != old_url
+ token_changed = new_token is not None and new_token != old_token
+
+ self.logged_out = logged_out
+ self.old_url = old_url
+ self.old_token = old_token
+ self.old_username = old_username
+ self.new_url = new_url
+ self.new_token = new_token
+ self.new_username = new_username
+
+ self.shutdown = shutdown
+ self.restart = restart
+ self.token_changed = token_changed
+
+
+def _get_servers_path():
+ return get_ayon_appdirs("used_servers.json")
+
+
+def get_servers_info_data():
+ """Metadata about used server on this machine.
+
+ Store data about all used server urls, last used url and user username for
+ the url. Using this metadata we can remember which username was used per
+ url if token stored in keyring loose lifetime.
+
+ Returns:
+ dict[str, Any]: Information about servers.
+ """
+
+ data = {}
+ servers_info_path = _get_servers_path()
+ if not os.path.exists(servers_info_path):
+ dirpath = os.path.dirname(servers_info_path)
+ if not os.path.exists(dirpath):
+ os.makedirs(dirpath)
+
+ return data
+
+ with open(servers_info_path, "r") as stream:
+ with contextlib.suppress(BaseException):
+ data = json.load(stream)
+ return data
+
+
+def add_server(url: str, username: str):
+ """Add server to server info metadata.
+
+ This function will also mark the url as last used url on the machine so on
+ next launch will be used.
+
+ Args:
+ url (str): Server url.
+ username (str): Name of user used to log in.
+ """
+
+ servers_info_path = _get_servers_path()
+ data = get_servers_info_data()
+ data["last_server"] = url
+ if "urls" not in data:
+ data["urls"] = {}
+ data["urls"][url] = {
+ "updated_dt": datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S"),
+ "username": username,
+ }
+
+ with open(servers_info_path, "w") as stream:
+ json.dump(data, stream)
+
+
+def remove_server(url: str):
+ """Remove server url from servers information.
+
+ This should be used on logout to completelly loose information about server
+ on the machine.
+
+ Args:
+ url (str): Server url.
+ """
+
+ if not url:
+ return
+
+ servers_info_path = _get_servers_path()
+ data = get_servers_info_data()
+ if data.get("last_server") == url:
+ data["last_server"] = None
+
+ if "urls" in data:
+ data["urls"].pop(url, None)
+
+ with open(servers_info_path, "w") as stream:
+ json.dump(data, stream)
+
+
+def get_last_server(
+ data: Optional[dict[str, Any]] = None
+) -> Union[str, None]:
+ """Last server used to log in on this machine.
+
+ Args:
+ data (Optional[dict[str, Any]]): Prepared server information data.
+
+ Returns:
+ Union[str, None]: Last used server url.
+ """
+
+ if data is None:
+ data = get_servers_info_data()
+ return data.get("last_server")
+
+
+def get_last_username_by_url(
+ url: str,
+ data: Optional[dict[str, Any]] = None
+) -> Union[str, None]:
+ """Get last username which was used for passed url.
+
+ Args:
+ url (str): Server url.
+ data (Optional[dict[str, Any]]): Servers info.
+
+ Returns:
+ Union[str, None]: Username.
+ """
+
+ if not url:
+ return None
+
+ if data is None:
+ data = get_servers_info_data()
+
+ if urls := data.get("urls"):
+ if url_info := urls.get(url):
+ return url_info.get("username")
+ return None
+
+
+def get_last_server_with_username():
+ """Receive last server and username used in last connection.
+
+ Returns:
+ tuple[Union[str, None], Union[str, None]]: Url and username.
+ """
+
+ data = get_servers_info_data()
+ url = get_last_server(data)
+ username = get_last_username_by_url(url)
+ return url, username
+
+
+class TokenKeyring:
+ # Fake username with hardcoded username
+ username_key = "username"
+
+ def __init__(self, url):
+ try:
+ import keyring
+
+ except Exception as exc:
+ raise NotImplementedError(
+ "Python module `keyring` is not available."
+ ) from exc
+
+ # hack for cx_freeze and Windows keyring backend
+ if platform.system().lower() == "windows":
+ from keyring.backends import Windows
+
+ keyring.set_keyring(Windows.WinVaultKeyring())
+
+ self._url = url
+ self._keyring_key = f"AYON/{url}"
+
+ def get_value(self):
+ import keyring
+
+ return keyring.get_password(self._keyring_key, self.username_key)
+
+ def set_value(self, value):
+ import keyring
+
+ if value is not None:
+ keyring.set_password(self._keyring_key, self.username_key, value)
+ return
+
+ with contextlib.suppress(keyring.errors.PasswordDeleteError):
+ keyring.delete_password(self._keyring_key, self.username_key)
+
+
+def load_token(url: str) -> Union[str, None]:
+ """Get token for url from keyring.
+
+ Args:
+ url (str): Server url.
+
+ Returns:
+ Union[str, None]: Token for passed url available in keyring.
+ """
+
+ return TokenKeyring(url).get_value()
+
+
+def store_token(url: str, token: str):
+ """Store token by url to keyring.
+
+ Args:
+ url (str): Server url.
+ token (str): User token to server.
+ """
+
+ TokenKeyring(url).set_value(token)
+
+
+def ask_to_login_ui(
+ url: Optional[str] = None,
+ always_on_top: Optional[bool] = False
+) -> tuple[str, str, str]:
+ """Ask user to login using UI.
+
+ This should be used only when user is not yet logged in at all or available
+ credentials are invalid. To change credentials use 'change_user_ui'
+ function.
+
+ Use a subprocess to show UI.
+
+ Args:
+ url (Optional[str]): Server url that could be prefilled in UI.
+ always_on_top (Optional[bool]): Window will be drawn on top of
+ other windows.
+
+ Returns:
+ tuple[str, str, str]: Url, user's token and username.
+ """
+
+ current_dir = os.path.dirname(os.path.abspath(__file__))
+ ui_dir = os.path.join(current_dir, "ui")
+
+ if url is None:
+ url = get_last_server()
+ username = get_last_username_by_url(url)
+ data = {
+ "url": url,
+ "username": username,
+ "always_on_top": always_on_top,
+ }
+
+ with tempfile.NamedTemporaryFile(
+ mode="w", prefix="ayon_login", suffix=".json", delete=False
+ ) as tmp:
+ output = tmp.name
+ json.dump(data, tmp)
+
+ code = subprocess.call(
+ get_ayon_launch_args(ui_dir, "--skip-bootstrap", output))
+ if code != 0:
+ raise RuntimeError("Failed to show login UI")
+
+ with open(output, "r") as stream:
+ data = json.load(stream)
+ os.remove(output)
+ return data["output"]
+
+
+def change_user_ui() -> ChangeUserResult:
+ """Change user using UI.
+
+ Show UI to user where he can change credentials or url. Output will contain
+ all information about old/new values of url, username, api key. If user
+ confirmed or declined values.
+
+ Returns:
+ ChangeUserResult: Information about user change.
+ """
+
+ from .ui import change_user
+
+ url, username = get_last_server_with_username()
+ token = load_token(url)
+ result = change_user(url, username, token)
+ new_url, new_token, new_username, logged_out = result
+
+ output = ChangeUserResult(
+ logged_out, url, token, username,
+ new_url, new_token, new_username
+ )
+ if output.logged_out:
+ logout(url, token)
+
+ elif output.token_changed:
+ change_token(
+ output.new_url,
+ output.new_token,
+ output.new_username,
+ output.old_url
+ )
+ return output
+
+
+def change_token(
+ url: str,
+ token: str,
+ username: Optional[str] = None,
+ old_url: Optional[str] = None
+):
+ """Change url and token in currently running session.
+
+ Function can also change server url, in that case are previous credentials
+ NOT removed from cache.
+
+ Args:
+ url (str): Url to server.
+ token (str): New token to be used for url connection.
+ username (Optional[str]): Username of logged user.
+ old_url (Optional[str]): Previous url. Value from 'get_last_server'
+ is used if not entered.
+ """
+
+ if old_url is None:
+ old_url = get_last_server()
+ if old_url and old_url == url:
+ remove_url_cache(old_url)
+
+ # TODO check if ayon_api is already connected
+ add_server(url, username)
+ store_token(url, token)
+ ayon_api.change_token(url, token)
+
+
+def remove_url_cache(url: str):
+ """Clear cache for server url.
+
+ Args:
+ url (str): Server url which is removed from cache.
+ """
+
+ store_token(url, None)
+
+
+def remove_token_cache(url: str, token: str):
+ """Remove token from local cache of url.
+
+ Is skipped if cached token under the passed url is not the same
+ as passed token.
+
+ Args:
+ url (str): Url to server.
+ token (str): Token to be removed from url cache.
+ """
+
+ if load_token(url) == token:
+ remove_url_cache(url)
+
+
+def logout(url: str, token: str):
+ """Logout from server and throw token away.
+
+ Args:
+ url (str): Url from which should be logged out.
+ token (str): Token which should be used to log out.
+ """
+
+ remove_server(url)
+ ayon_api.close_connection()
+ ayon_api.set_environments(None, None)
+ remove_token_cache(url, token)
+ logout_from_server(url, token)
+
+
+def load_environments():
+ """Load environments on startup.
+
+ Handle environments needed for connection with server. Environments are
+ 'AYON_SERVER_URL' and 'AYON_API_KEY'.
+
+ Server is looked up from environment. Already set environent is not
+ changed. If environemnt is not filled then last server stored in appdirs
+ is used.
+
+ Token is skipped if url is not available. Otherwise, is also checked from
+ env and if is not available then uses 'load_token' to try to get token
+ based on server url.
+ """
+
+ server_url = os.environ.get(SERVER_URL_ENV_KEY)
+ if not server_url:
+ server_url = get_last_server()
+ if not server_url:
+ return
+ os.environ[SERVER_URL_ENV_KEY] = server_url
+
+ if not os.environ.get(SERVER_API_ENV_KEY):
+ if token := load_token(server_url):
+ os.environ[SERVER_API_ENV_KEY] = token
+
+
+def set_environments(url: str, token: str):
+ """Change url and token environemnts in currently running process.
+
+ Args:
+ url (str): New server url.
+ token (str): User's token.
+ """
+
+ ayon_api.set_environments(url, token)
+
+
+def create_global_connection():
+ """Create global connection with site id and client version.
+
+ Make sure the global connection in 'ayon_api' have entered site id and
+ client version.
+
+ Set default settings variant to use based on 'is_staging_enabled'.
+ """
+
+ ayon_api.create_connection(
+ get_local_site_id(), os.environ.get("AYON_VERSION")
+ )
+ ayon_api.set_default_settings_variant(
+ "staging" if is_staging_enabled() else "production"
+ )
+
+
+def need_server_or_login() -> bool:
+ """Check if server url or login to the server are needed.
+
+ It is recommended to call 'load_environments' on startup before this check.
+ But in some cases this function could be called after startup.
+
+ Returns:
+ bool: 'True' if server and token are available. Otherwise 'False'.
+ """
+
+ server_url = os.environ.get(SERVER_URL_ENV_KEY)
+ if not server_url:
+ return True
+
+ try:
+ server_url = validate_url(server_url)
+ except UrlError:
+ return True
+
+ token = os.environ.get(SERVER_API_ENV_KEY)
+ if token:
+ return not is_token_valid(server_url, token)
+
+ token = load_token(server_url)
+ if token:
+ return not is_token_valid(server_url, token)
+ return True
+
+
+def confirm_server_login(url, token, username):
+ """Confirm login of user and do necessary stepts to apply changes.
+
+ This should not be used on "change" of user but on first login.
+
+ Args:
+ url (str): Server url where user authenticated.
+ token (str): API token used for authentication to server.
+ username (Union[str, None]): Username related to API token.
+ """
+
+ add_server(url, username)
+ store_token(url, token)
+ set_environments(url, token)
+ create_global_connection()
diff --git a/common/ayon_common/connection/ui/__init__.py b/common/ayon_common/connection/ui/__init__.py
new file mode 100644
index 0000000000..96e573df0d
--- /dev/null
+++ b/common/ayon_common/connection/ui/__init__.py
@@ -0,0 +1,12 @@
+from .login_window import (
+ ServerLoginWindow,
+ ask_to_login,
+ change_user,
+)
+
+
+__all__ = (
+ "ServerLoginWindow",
+ "ask_to_login",
+ "change_user",
+)
diff --git a/common/ayon_common/connection/ui/__main__.py b/common/ayon_common/connection/ui/__main__.py
new file mode 100644
index 0000000000..719b2b8ef5
--- /dev/null
+++ b/common/ayon_common/connection/ui/__main__.py
@@ -0,0 +1,23 @@
+import sys
+import json
+
+from ayon_common.connection.ui.login_window import ask_to_login
+
+
+def main(output_path):
+ with open(output_path, "r") as stream:
+ data = json.load(stream)
+
+ url = data.get("url")
+ username = data.get("username")
+ always_on_top = data.get("always_on_top", False)
+ out_url, out_token, out_username = ask_to_login(
+ url, username, always_on_top=always_on_top)
+
+ data["output"] = [out_url, out_token, out_username]
+ with open(output_path, "w") as stream:
+ json.dump(data, stream)
+
+
+if __name__ == "__main__":
+ main(sys.argv[-1])
diff --git a/common/ayon_common/connection/ui/login_window.py b/common/ayon_common/connection/ui/login_window.py
new file mode 100644
index 0000000000..94c239852e
--- /dev/null
+++ b/common/ayon_common/connection/ui/login_window.py
@@ -0,0 +1,710 @@
+import traceback
+
+from qtpy import QtWidgets, QtCore, QtGui
+
+from ayon_api.exceptions import UrlError
+from ayon_api.utils import validate_url, login_to_server
+
+from ayon_common.resources import (
+ get_resource_path,
+ get_icon_path,
+ load_stylesheet,
+)
+from ayon_common.ui_utils import set_style_property, get_qt_app
+
+from .widgets import (
+ PressHoverButton,
+ PlaceholderLineEdit,
+)
+
+
+class LogoutConfirmDialog(QtWidgets.QDialog):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ self.setWindowTitle("Logout confirmation")
+
+ message_widget = QtWidgets.QWidget(self)
+
+ message_label = QtWidgets.QLabel(
+ (
+ "You are going to logout. This action will close this"
+ " application and will invalidate your login."
+ " All other applications launched with this login won't be"
+ " able to use it anymore.
"
+ "You can cancel logout and only change server and user login"
+ " in login dialog.
"
+ "Press OK to confirm logout."
+ ),
+ message_widget
+ )
+ message_label.setWordWrap(True)
+
+ message_layout = QtWidgets.QHBoxLayout(message_widget)
+ message_layout.setContentsMargins(0, 0, 0, 0)
+ message_layout.addWidget(message_label, 1)
+
+ sep_frame = QtWidgets.QFrame(self)
+ sep_frame.setObjectName("Separator")
+ sep_frame.setMinimumHeight(2)
+ sep_frame.setMaximumHeight(2)
+
+ footer_widget = QtWidgets.QWidget(self)
+
+ cancel_btn = QtWidgets.QPushButton("Cancel", footer_widget)
+ confirm_btn = QtWidgets.QPushButton("OK", footer_widget)
+
+ footer_layout = QtWidgets.QHBoxLayout(footer_widget)
+ footer_layout.setContentsMargins(0, 0, 0, 0)
+ footer_layout.addStretch(1)
+ footer_layout.addWidget(cancel_btn, 0)
+ footer_layout.addWidget(confirm_btn, 0)
+
+ main_layout = QtWidgets.QVBoxLayout(self)
+ main_layout.addWidget(message_widget, 0)
+ main_layout.addStretch(1)
+ main_layout.addWidget(sep_frame, 0)
+ main_layout.addWidget(footer_widget, 0)
+
+ cancel_btn.clicked.connect(self._on_cancel_click)
+ confirm_btn.clicked.connect(self._on_confirm_click)
+
+ self._cancel_btn = cancel_btn
+ self._confirm_btn = confirm_btn
+ self._result = False
+
+ def showEvent(self, event):
+ super().showEvent(event)
+ self._match_btns_sizes()
+
+ def resizeEvent(self, event):
+ super().resizeEvent(event)
+ self._match_btns_sizes()
+
+ def _match_btns_sizes(self):
+ width = max(
+ self._cancel_btn.sizeHint().width(),
+ self._confirm_btn.sizeHint().width()
+ )
+ self._cancel_btn.setMinimumWidth(width)
+ self._confirm_btn.setMinimumWidth(width)
+
+ def _on_cancel_click(self):
+ self._result = False
+ self.reject()
+
+ def _on_confirm_click(self):
+ self._result = True
+ self.accept()
+
+ def get_result(self):
+ return self._result
+
+
+class ServerLoginWindow(QtWidgets.QDialog):
+ default_width = 410
+ default_height = 170
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ icon_path = get_icon_path()
+ icon = QtGui.QIcon(icon_path)
+ self.setWindowIcon(icon)
+ self.setWindowTitle("Login to server")
+
+ edit_icon_path = get_resource_path("edit.png")
+ edit_icon = QtGui.QIcon(edit_icon_path)
+
+ # --- URL page ---
+ login_widget = QtWidgets.QWidget(self)
+
+ user_cred_widget = QtWidgets.QWidget(login_widget)
+
+ url_label = QtWidgets.QLabel("URL:", user_cred_widget)
+
+ url_widget = QtWidgets.QWidget(user_cred_widget)
+
+ url_input = PlaceholderLineEdit(url_widget)
+ url_input.setPlaceholderText("< https://ayon.server.com >")
+
+ url_preview = QtWidgets.QLineEdit(url_widget)
+ url_preview.setReadOnly(True)
+ url_preview.setObjectName("LikeDisabledInput")
+
+ url_edit_btn = PressHoverButton(user_cred_widget)
+ url_edit_btn.setIcon(edit_icon)
+ url_edit_btn.setObjectName("PasswordBtn")
+
+ url_layout = QtWidgets.QHBoxLayout(url_widget)
+ url_layout.setContentsMargins(0, 0, 0, 0)
+ url_layout.addWidget(url_input, 1)
+ url_layout.addWidget(url_preview, 1)
+
+ # --- URL separator ---
+ url_cred_sep = QtWidgets.QFrame(self)
+ url_cred_sep.setObjectName("Separator")
+ url_cred_sep.setMinimumHeight(2)
+ url_cred_sep.setMaximumHeight(2)
+
+ # --- Login page ---
+ username_label = QtWidgets.QLabel("Username:", user_cred_widget)
+
+ username_widget = QtWidgets.QWidget(user_cred_widget)
+
+ username_input = PlaceholderLineEdit(username_widget)
+ username_input.setPlaceholderText("< Artist >")
+
+ username_preview = QtWidgets.QLineEdit(username_widget)
+ username_preview.setReadOnly(True)
+ username_preview.setObjectName("LikeDisabledInput")
+
+ username_edit_btn = PressHoverButton(user_cred_widget)
+ username_edit_btn.setIcon(edit_icon)
+ username_edit_btn.setObjectName("PasswordBtn")
+
+ username_layout = QtWidgets.QHBoxLayout(username_widget)
+ username_layout.setContentsMargins(0, 0, 0, 0)
+ username_layout.addWidget(username_input, 1)
+ username_layout.addWidget(username_preview, 1)
+
+ password_label = QtWidgets.QLabel("Password:", user_cred_widget)
+ password_input = PlaceholderLineEdit(user_cred_widget)
+ password_input.setPlaceholderText("< *********** >")
+ password_input.setEchoMode(PlaceholderLineEdit.Password)
+
+ api_label = QtWidgets.QLabel("API key:", user_cred_widget)
+ api_preview = QtWidgets.QLineEdit(user_cred_widget)
+ api_preview.setReadOnly(True)
+ api_preview.setObjectName("LikeDisabledInput")
+
+ show_password_icon_path = get_resource_path("eye.png")
+ show_password_icon = QtGui.QIcon(show_password_icon_path)
+ show_password_btn = PressHoverButton(user_cred_widget)
+ show_password_btn.setObjectName("PasswordBtn")
+ show_password_btn.setIcon(show_password_icon)
+ show_password_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
+
+ cred_msg_sep = QtWidgets.QFrame(self)
+ cred_msg_sep.setObjectName("Separator")
+ cred_msg_sep.setMinimumHeight(2)
+ cred_msg_sep.setMaximumHeight(2)
+
+ # --- Credentials inputs ---
+ user_cred_layout = QtWidgets.QGridLayout(user_cred_widget)
+ user_cred_layout.setContentsMargins(0, 0, 0, 0)
+ row = 0
+
+ user_cred_layout.addWidget(url_label, row, 0, 1, 1)
+ user_cred_layout.addWidget(url_widget, row, 1, 1, 1)
+ user_cred_layout.addWidget(url_edit_btn, row, 2, 1, 1)
+ row += 1
+
+ user_cred_layout.addWidget(url_cred_sep, row, 0, 1, 3)
+ row += 1
+
+ user_cred_layout.addWidget(username_label, row, 0, 1, 1)
+ user_cred_layout.addWidget(username_widget, row, 1, 1, 1)
+ user_cred_layout.addWidget(username_edit_btn, row, 2, 2, 1)
+ row += 1
+
+ user_cred_layout.addWidget(api_label, row, 0, 1, 1)
+ user_cred_layout.addWidget(api_preview, row, 1, 1, 1)
+ row += 1
+
+ user_cred_layout.addWidget(password_label, row, 0, 1, 1)
+ user_cred_layout.addWidget(password_input, row, 1, 1, 1)
+ user_cred_layout.addWidget(show_password_btn, row, 2, 1, 1)
+ row += 1
+
+ user_cred_layout.addWidget(cred_msg_sep, row, 0, 1, 3)
+ row += 1
+
+ user_cred_layout.setColumnStretch(0, 0)
+ user_cred_layout.setColumnStretch(1, 1)
+ user_cred_layout.setColumnStretch(2, 0)
+
+ login_layout = QtWidgets.QVBoxLayout(login_widget)
+ login_layout.setContentsMargins(0, 0, 0, 0)
+ login_layout.addWidget(user_cred_widget, 1)
+
+ # --- Messages ---
+ # Messages for users (e.g. invalid url etc.)
+ message_label = QtWidgets.QLabel(self)
+ message_label.setWordWrap(True)
+ message_label.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
+
+ footer_widget = QtWidgets.QWidget(self)
+ logout_btn = QtWidgets.QPushButton("Logout", footer_widget)
+ user_message = QtWidgets.QLabel(footer_widget)
+ login_btn = QtWidgets.QPushButton("Login", footer_widget)
+ confirm_btn = QtWidgets.QPushButton("Confirm", footer_widget)
+
+ footer_layout = QtWidgets.QHBoxLayout(footer_widget)
+ footer_layout.setContentsMargins(0, 0, 0, 0)
+ footer_layout.addWidget(logout_btn, 0)
+ footer_layout.addWidget(user_message, 1)
+ footer_layout.addWidget(login_btn, 0)
+ footer_layout.addWidget(confirm_btn, 0)
+
+ main_layout = QtWidgets.QVBoxLayout(self)
+ main_layout.addWidget(login_widget, 0)
+ main_layout.addWidget(message_label, 0)
+ main_layout.addStretch(1)
+ main_layout.addWidget(footer_widget, 0)
+
+ url_input.textChanged.connect(self._on_url_change)
+ url_input.returnPressed.connect(self._on_url_enter_press)
+ username_input.textChanged.connect(self._on_user_change)
+ username_input.returnPressed.connect(self._on_username_enter_press)
+ password_input.returnPressed.connect(self._on_password_enter_press)
+ show_password_btn.change_state.connect(self._on_show_password)
+ url_edit_btn.clicked.connect(self._on_url_edit_click)
+ username_edit_btn.clicked.connect(self._on_username_edit_click)
+ logout_btn.clicked.connect(self._on_logout_click)
+ login_btn.clicked.connect(self._on_login_click)
+ confirm_btn.clicked.connect(self._on_login_click)
+
+ self._message_label = message_label
+
+ self._url_widget = url_widget
+ self._url_input = url_input
+ self._url_preview = url_preview
+ self._url_edit_btn = url_edit_btn
+
+ self._login_widget = login_widget
+
+ self._user_cred_widget = user_cred_widget
+ self._username_input = username_input
+ self._username_preview = username_preview
+ self._username_edit_btn = username_edit_btn
+
+ self._password_label = password_label
+ self._password_input = password_input
+ self._show_password_btn = show_password_btn
+ self._api_label = api_label
+ self._api_preview = api_preview
+
+ self._logout_btn = logout_btn
+ self._user_message = user_message
+ self._login_btn = login_btn
+ self._confirm_btn = confirm_btn
+
+ self._url_is_valid = None
+ self._credentials_are_valid = None
+ self._result = (None, None, None, False)
+ self._first_show = True
+
+ self._allow_logout = False
+ self._logged_in = False
+ self._url_edit_mode = False
+ self._username_edit_mode = False
+
+ def set_allow_logout(self, allow_logout):
+ if allow_logout is self._allow_logout:
+ return
+ self._allow_logout = allow_logout
+
+ self._update_states_by_edit_mode()
+
+ def _set_logged_in(self, logged_in):
+ if logged_in is self._logged_in:
+ return
+ self._logged_in = logged_in
+
+ self._update_states_by_edit_mode()
+
+ def _set_url_edit_mode(self, edit_mode):
+ if self._url_edit_mode is not edit_mode:
+ self._url_edit_mode = edit_mode
+ self._update_states_by_edit_mode()
+
+ def _set_username_edit_mode(self, edit_mode):
+ if self._username_edit_mode is not edit_mode:
+ self._username_edit_mode = edit_mode
+ self._update_states_by_edit_mode()
+
+ def _get_url_user_edit(self):
+ url_edit = True
+ if self._logged_in and not self._url_edit_mode:
+ url_edit = False
+ user_edit = url_edit
+ if not user_edit and self._logged_in and self._username_edit_mode:
+ user_edit = True
+ return url_edit, user_edit
+
+ def _update_states_by_edit_mode(self):
+ url_edit, user_edit = self._get_url_user_edit()
+
+ self._url_preview.setVisible(not url_edit)
+ self._url_input.setVisible(url_edit)
+ self._url_edit_btn.setVisible(self._allow_logout and not url_edit)
+
+ self._username_preview.setVisible(not user_edit)
+ self._username_input.setVisible(user_edit)
+ self._username_edit_btn.setVisible(
+ self._allow_logout and not user_edit
+ )
+
+ self._api_preview.setVisible(not user_edit)
+ self._api_label.setVisible(not user_edit)
+
+ self._password_label.setVisible(user_edit)
+ self._show_password_btn.setVisible(user_edit)
+ self._password_input.setVisible(user_edit)
+
+ self._logout_btn.setVisible(self._allow_logout and self._logged_in)
+ self._login_btn.setVisible(not self._allow_logout)
+ self._confirm_btn.setVisible(self._allow_logout)
+ self._update_login_btn_state(url_edit, user_edit)
+
+ def _update_login_btn_state(self, url_edit=None, user_edit=None, url=None):
+ if url_edit is None:
+ url_edit, user_edit = self._get_url_user_edit()
+
+ if url is None:
+ url = self._url_input.text()
+
+ enabled = bool(url) and (url_edit or user_edit)
+
+ self._login_btn.setEnabled(enabled)
+ self._confirm_btn.setEnabled(enabled)
+
+ def showEvent(self, event):
+ super().showEvent(event)
+ if self._first_show:
+ self._first_show = False
+ self._on_first_show()
+
+ def _on_first_show(self):
+ self.setStyleSheet(load_stylesheet())
+ self.resize(self.default_width, self.default_height)
+ self._center_window()
+ if self._allow_logout is None:
+ self.set_allow_logout(False)
+
+ self._update_states_by_edit_mode()
+ if not self._url_input.text():
+ widget = self._url_input
+ elif not self._username_input.text():
+ widget = self._username_input
+ else:
+ widget = self._password_input
+
+ self._set_input_focus(widget)
+
+ def result(self):
+ """Result url and token or login.
+
+ Returns:
+ Union[Tuple[str, str], Tuple[None, None]]: Url and token used for
+ login if was successful otherwise are both set to None.
+ """
+ return self._result
+
+ def _center_window(self):
+ """Move window to center of screen."""
+
+ if hasattr(QtWidgets.QApplication, "desktop"):
+ desktop = QtWidgets.QApplication.desktop()
+ screen_idx = desktop.screenNumber(self)
+ screen_geo = desktop.screenGeometry(screen_idx)
+ else:
+ screen = self.screen()
+ screen_geo = screen.geometry()
+
+ geo = self.frameGeometry()
+ geo.moveCenter(screen_geo.center())
+ if geo.y() < screen_geo.y():
+ geo.setY(screen_geo.y())
+ self.move(geo.topLeft())
+
+ def _on_url_change(self, text):
+ self._update_login_btn_state(url=text)
+ self._set_url_valid(None)
+ self._set_credentials_valid(None)
+ self._url_preview.setText(text)
+
+ def _set_url_valid(self, valid):
+ if valid is self._url_is_valid:
+ return
+
+ self._url_is_valid = valid
+ self._set_input_valid_state(self._url_input, valid)
+
+ def _set_credentials_valid(self, valid):
+ if self._credentials_are_valid is valid:
+ return
+
+ self._credentials_are_valid = valid
+ self._set_input_valid_state(self._username_input, valid)
+ self._set_input_valid_state(self._password_input, valid)
+
+ def _on_url_enter_press(self):
+ self._set_input_focus(self._username_input)
+
+ def _on_user_change(self, username):
+ self._username_preview.setText(username)
+
+ def _on_username_enter_press(self):
+ self._set_input_focus(self._password_input)
+
+ def _on_password_enter_press(self):
+ self._login()
+
+ def _on_show_password(self, show_password):
+ if show_password:
+ placeholder_text = "< MySecret124 >"
+ echo_mode = QtWidgets.QLineEdit.Normal
+ else:
+ placeholder_text = "< *********** >"
+ echo_mode = QtWidgets.QLineEdit.Password
+
+ self._password_input.setEchoMode(echo_mode)
+ self._password_input.setPlaceholderText(placeholder_text)
+
+ def _on_username_edit_click(self):
+ self._username_edit_mode = True
+ self._update_states_by_edit_mode()
+
+ def _on_url_edit_click(self):
+ self._url_edit_mode = True
+ self._update_states_by_edit_mode()
+
+ def _on_logout_click(self):
+ dialog = LogoutConfirmDialog(self)
+ dialog.exec_()
+ if dialog.get_result():
+ self._result = (None, None, None, True)
+ self.accept()
+
+ def _on_login_click(self):
+ self._login()
+
+ def _validate_url(self):
+ """Use url from input to connect and change window state on success.
+
+ Todos:
+ Threaded check.
+ """
+
+ url = self._url_input.text()
+ valid_url = None
+ try:
+ valid_url = validate_url(url)
+
+ except UrlError as exc:
+ parts = [f"{exc.title}"]
+ parts.extend(f"- {hint}" for hint in exc.hints)
+ self._set_message(" ".join(parts))
+
+ except KeyboardInterrupt:
+ # Reraise KeyboardInterrupt error
+ raise
+
+ except BaseException:
+ self._set_unexpected_error()
+ return
+
+ if valid_url is None:
+ return False
+
+ self._url_input.setText(valid_url)
+ return True
+
+ def _login(self):
+ if (
+ not self._login_btn.isEnabled()
+ and not self._confirm_btn.isEnabled()
+ ):
+ return
+
+ if not self._url_is_valid:
+ self._set_url_valid(self._validate_url())
+
+ if not self._url_is_valid:
+ self._set_input_focus(self._url_input)
+ self._set_credentials_valid(None)
+ return
+
+ self._clear_message()
+
+ url = self._url_input.text()
+ username = self._username_input.text()
+ password = self._password_input.text()
+ try:
+ token = login_to_server(url, username, password)
+ except BaseException:
+ self._set_unexpected_error()
+ return
+
+ if token is not None:
+ self._result = (url, token, username, False)
+ self.accept()
+ return
+
+ self._set_credentials_valid(False)
+ message_lines = ["Invalid credentials"]
+ if not username.strip():
+ message_lines.append("- Username is not filled")
+
+ if not password.strip():
+ message_lines.append("- Password is not filled")
+
+ if username and password:
+ message_lines.append("- Check your credentials")
+
+ self._set_message(" ".join(message_lines))
+ self._set_input_focus(self._username_input)
+
+ def _set_input_focus(self, widget):
+ widget.setFocus(QtCore.Qt.MouseFocusReason)
+
+ def _set_input_valid_state(self, widget, valid):
+ state = ""
+ if valid is True:
+ state = "valid"
+ elif valid is False:
+ state = "invalid"
+ set_style_property(widget, "state", state)
+
+ def _set_message(self, message):
+ self._message_label.setText(message)
+
+ def _clear_message(self):
+ self._message_label.setText("")
+
+ def _set_unexpected_error(self):
+ # TODO add traceback somewhere
+ # - maybe a button to show or copy?
+ traceback.print_exc()
+ lines = [
+ "Unexpected error happened",
+ "- Can be caused by wrong url (leading elsewhere)"
+ ]
+ self._set_message(" ".join(lines))
+
+ def set_url(self, url):
+ self._url_preview.setText(url)
+ self._url_input.setText(url)
+ self._validate_url()
+
+ def set_username(self, username):
+ self._username_preview.setText(username)
+ self._username_input.setText(username)
+
+ def _set_api_key(self, api_key):
+ if not api_key or len(api_key) < 3:
+ self._api_preview.setText(api_key or "")
+ return
+
+ api_key_len = len(api_key)
+ offset = 6
+ if api_key_len < offset:
+ offset = api_key_len // 2
+ api_key = api_key[:offset] + "." * (api_key_len - offset)
+
+ self._api_preview.setText(api_key)
+
+ def set_logged_in(
+ self,
+ logged_in,
+ url=None,
+ username=None,
+ api_key=None,
+ allow_logout=None
+ ):
+ if url is not None:
+ self.set_url(url)
+
+ if username is not None:
+ self.set_username(username)
+
+ if api_key:
+ self._set_api_key(api_key)
+
+ if logged_in and allow_logout is None:
+ allow_logout = True
+
+ self._set_logged_in(logged_in)
+
+ if allow_logout:
+ self.set_allow_logout(True)
+ elif allow_logout is False:
+ self.set_allow_logout(False)
+
+
+def ask_to_login(url=None, username=None, always_on_top=False):
+ """Ask user to login using Qt dialog.
+
+ Function creates new QApplication if is not created yet.
+
+ Args:
+ url (Optional[str]): Server url that will be prefilled in dialog.
+ username (Optional[str]): Username that will be prefilled in dialog.
+ always_on_top (Optional[bool]): Window will be drawn on top of
+ other windows.
+
+ Returns:
+ tuple[str, str, str]: Returns Url, user's token and username. Url can
+ be changed during dialog lifetime that's why the url is returned.
+ """
+
+ app_instance = get_qt_app()
+
+ window = ServerLoginWindow()
+ if always_on_top:
+ window.setWindowFlags(
+ window.windowFlags()
+ | QtCore.Qt.WindowStaysOnTopHint
+ )
+
+ if url:
+ window.set_url(url)
+
+ if username:
+ window.set_username(username)
+
+ if not app_instance.startingUp():
+ window.exec_()
+ else:
+ window.open()
+ app_instance.exec_()
+ result = window.result()
+ out_url, out_token, out_username, _ = result
+ return out_url, out_token, out_username
+
+
+def change_user(url, username, api_key, always_on_top=False):
+ """Ask user to login using Qt dialog.
+
+ Function creates new QApplication if is not created yet.
+
+ Args:
+ url (str): Server url that will be prefilled in dialog.
+ username (str): Username that will be prefilled in dialog.
+ api_key (str): API key that will be prefilled in dialog.
+ always_on_top (Optional[bool]): Window will be drawn on top of
+ other windows.
+
+ Returns:
+ Tuple[str, str]: Returns Url and user's token. Url can be changed
+ during dialog lifetime that's why the url is returned.
+ """
+
+ app_instance = get_qt_app()
+ window = ServerLoginWindow()
+ if always_on_top:
+ window.setWindowFlags(
+ window.windowFlags()
+ | QtCore.Qt.WindowStaysOnTopHint
+ )
+ window.set_logged_in(True, url, username, api_key)
+
+ if not app_instance.startingUp():
+ window.exec_()
+ else:
+ window.open()
+ # This can become main Qt loop. Maybe should live elsewhere
+ app_instance.exec_()
+ return window.result()
diff --git a/common/ayon_common/connection/ui/widgets.py b/common/ayon_common/connection/ui/widgets.py
new file mode 100644
index 0000000000..78b73e056d
--- /dev/null
+++ b/common/ayon_common/connection/ui/widgets.py
@@ -0,0 +1,47 @@
+from qtpy import QtWidgets, QtCore, QtGui
+
+
+class PressHoverButton(QtWidgets.QPushButton):
+ """Keep track about mouse press/release and enter/leave."""
+
+ _mouse_pressed = False
+ _mouse_hovered = False
+ change_state = QtCore.Signal(bool)
+
+ def mousePressEvent(self, event):
+ self._mouse_pressed = True
+ self._mouse_hovered = True
+ self.change_state.emit(self._mouse_hovered)
+ super(PressHoverButton, self).mousePressEvent(event)
+
+ def mouseReleaseEvent(self, event):
+ self._mouse_pressed = False
+ self._mouse_hovered = False
+ self.change_state.emit(self._mouse_hovered)
+ super(PressHoverButton, self).mouseReleaseEvent(event)
+
+ def mouseMoveEvent(self, event):
+ mouse_pos = self.mapFromGlobal(QtGui.QCursor.pos())
+ under_mouse = self.rect().contains(mouse_pos)
+ if under_mouse != self._mouse_hovered:
+ self._mouse_hovered = under_mouse
+ self.change_state.emit(self._mouse_hovered)
+
+ super(PressHoverButton, self).mouseMoveEvent(event)
+
+
+class PlaceholderLineEdit(QtWidgets.QLineEdit):
+ """Set placeholder color of QLineEdit in Qt 5.12 and higher."""
+
+ def __init__(self, *args, **kwargs):
+ super(PlaceholderLineEdit, self).__init__(*args, **kwargs)
+ # Change placeholder palette color
+ if hasattr(QtGui.QPalette, "PlaceholderText"):
+ filter_palette = self.palette()
+ color = QtGui.QColor("#D3D8DE")
+ color.setAlpha(67)
+ filter_palette.setColor(
+ QtGui.QPalette.PlaceholderText,
+ color
+ )
+ self.setPalette(filter_palette)
diff --git a/common/openpype_common/distribution/README.md b/common/ayon_common/distribution/README.md
similarity index 96%
rename from common/openpype_common/distribution/README.md
rename to common/ayon_common/distribution/README.md
index 212eb267b8..f1c34ba722 100644
--- a/common/openpype_common/distribution/README.md
+++ b/common/ayon_common/distribution/README.md
@@ -5,7 +5,7 @@ Code in this folder is backend portion of Addon distribution logic for v4 server
Each host, module will be separate Addon in the future. Each v4 server could run different set of Addons.
-Client (running on artist machine) will in the first step ask v4 for list of enabled addons.
+Client (running on artist machine) will in the first step ask v4 for list of enabled addons.
(It expects list of json documents matching to `addon_distribution.py:AddonInfo` object.)
Next it will compare presence of enabled addon version in local folder. In the case of missing version of
an addon, client will use information in the addon to download (from http/shared local disk/git) zip file
@@ -15,4 +15,4 @@ Required part of addon distribution will be sharing of dependencies (python libr
Location of this folder might change in the future as it will be required for a clint to add this folder to sys.path reliably.
-This code needs to be independent on Openpype code as much as possible!
\ No newline at end of file
+This code needs to be independent on Openpype code as much as possible!
diff --git a/common/ayon_common/distribution/__init__.py b/common/ayon_common/distribution/__init__.py
new file mode 100644
index 0000000000..e3c0f0e161
--- /dev/null
+++ b/common/ayon_common/distribution/__init__.py
@@ -0,0 +1,9 @@
+from .control import AyonDistribution, BundleNotFoundError
+from .utils import show_missing_bundle_information
+
+
+__all__ = (
+ "AyonDistribution",
+ "BundleNotFoundError",
+ "show_missing_bundle_information",
+)
diff --git a/common/ayon_common/distribution/control.py b/common/ayon_common/distribution/control.py
new file mode 100644
index 0000000000..95c221d753
--- /dev/null
+++ b/common/ayon_common/distribution/control.py
@@ -0,0 +1,1116 @@
+import os
+import sys
+import json
+import traceback
+import collections
+import datetime
+import logging
+import shutil
+import threading
+import platform
+import attr
+from enum import Enum
+
+import ayon_api
+
+from ayon_common.utils import is_staging_enabled
+
+from .utils import (
+ get_addons_dir,
+ get_dependencies_dir,
+)
+from .downloaders import get_default_download_factory
+from .data_structures import (
+ AddonInfo,
+ DependencyItem,
+ Bundle,
+)
+
+NOT_SET = type("UNKNOWN", (), {"__bool__": lambda: False})()
+
+
+class BundleNotFoundError(Exception):
+ """Bundle name is defined but is not available on server.
+
+ Args:
+ bundle_name (str): Name of bundle that was not found.
+ """
+
+ def __init__(self, bundle_name):
+ self.bundle_name = bundle_name
+ super().__init__(
+ f"Bundle '{bundle_name}' is not available on server"
+ )
+
+
+class UpdateState(Enum):
+ UNKNOWN = "unknown"
+ UPDATED = "udated"
+ OUTDATED = "outdated"
+ UPDATE_FAILED = "failed"
+ MISS_SOURCE_FILES = "miss_source_files"
+
+
+class DistributeTransferProgress:
+ """Progress of single source item in 'DistributionItem'.
+
+ The item is to keep track of single source item.
+ """
+
+ def __init__(self):
+ self._transfer_progress = ayon_api.TransferProgress()
+ self._started = False
+ self._failed = False
+ self._fail_reason = None
+ self._unzip_started = False
+ self._unzip_finished = False
+ self._hash_check_started = False
+ self._hash_check_finished = False
+
+ def set_started(self):
+ """Call when source distribution starts."""
+
+ self._started = True
+
+ def set_failed(self, reason):
+ """Set source distribution as failed.
+
+ Args:
+ reason (str): Error message why the transfer failed.
+ """
+
+ self._failed = True
+ self._fail_reason = reason
+
+ def set_hash_check_started(self):
+ """Call just before hash check starts."""
+
+ self._hash_check_started = True
+
+ def set_hash_check_finished(self):
+ """Call just after hash check finishes."""
+
+ self._hash_check_finished = True
+
+ def set_unzip_started(self):
+ """Call just before unzip starts."""
+
+ self._unzip_started = True
+
+ def set_unzip_finished(self):
+ """Call just after unzip finishes."""
+
+ self._unzip_finished = True
+
+ @property
+ def is_running(self):
+ """Source distribution is in progress.
+
+ Returns:
+ bool: Transfer is in progress.
+ """
+
+ return bool(
+ self._started
+ and not self._failed
+ and not self._hash_check_finished
+ )
+
+ @property
+ def transfer_progress(self):
+ """Source file 'download' progress tracker.
+
+ Returns:
+ ayon_api.TransferProgress.: Content download progress.
+ """
+
+ return self._transfer_progress
+
+ @property
+ def started(self):
+ return self._started
+
+ @property
+ def hash_check_started(self):
+ return self._hash_check_started
+
+ @property
+ def hash_check_finished(self):
+ return self._has_check_finished
+
+ @property
+ def unzip_started(self):
+ return self._unzip_started
+
+ @property
+ def unzip_finished(self):
+ return self._unzip_finished
+
+ @property
+ def failed(self):
+ return self._failed or self._transfer_progress.failed
+
+ @property
+ def fail_reason(self):
+ return self._fail_reason or self._transfer_progress.fail_reason
+
+
+class DistributionItem:
+ """Distribution item with sources and target directories.
+
+ Distribution item can be an addon or dependency package. Distribution item
+ can be already distributed and don't need any progression. The item keeps
+ track of the progress. The reason is to be able to use the distribution
+ items as source data for UI without implementing the same logic.
+
+ Distribution is "state" based. Distribution can be 'UPDATED' or 'OUTDATED'
+ at the initialization. If item is 'UPDATED' the distribution is skipped
+ and 'OUTDATED' will trigger the distribution process.
+
+ Because the distribution may have multiple sources each source has own
+ progress item.
+
+ Args:
+ state (UpdateState): Initial state (UpdateState.UPDATED or
+ UpdateState.OUTDATED).
+ unzip_dirpath (str): Path to directory where zip is downloaded.
+ download_dirpath (str): Path to directory where file is unzipped.
+ file_hash (str): Hash of file for validation.
+ factory (DownloadFactory): Downloaders factory object.
+ sources (List[SourceInfo]): Possible sources to receive the
+ distribution item.
+ downloader_data (Dict[str, Any]): More information for downloaders.
+ item_label (str): Label used in log outputs (and in UI).
+ logger (logging.Logger): Logger object.
+ """
+
+ def __init__(
+ self,
+ state,
+ unzip_dirpath,
+ download_dirpath,
+ file_hash,
+ factory,
+ sources,
+ downloader_data,
+ item_label,
+ logger=None,
+ ):
+ if logger is None:
+ logger = logging.getLogger(self.__class__.__name__)
+ self.log = logger
+ self.state = state
+ self.unzip_dirpath = unzip_dirpath
+ self.download_dirpath = download_dirpath
+ self.file_hash = file_hash
+ self.factory = factory
+ self.sources = [
+ (source, DistributeTransferProgress())
+ for source in sources
+ ]
+ self.downloader_data = downloader_data
+ self.item_label = item_label
+
+ self._need_distribution = state != UpdateState.UPDATED
+ self._current_source_progress = None
+ self._used_source_progress = None
+ self._used_source = None
+ self._dist_started = False
+ self._dist_finished = False
+
+ self._error_msg = None
+ self._error_detail = None
+
+ @property
+ def need_distribution(self):
+ """Need distribution based on initial state.
+
+ Returns:
+ bool: Need distribution.
+ """
+
+ return self._need_distribution
+
+ @property
+ def current_source_progress(self):
+ """Currently processed source progress object.
+
+ Returns:
+ Union[DistributeTransferProgress, None]: Transfer progress or None.
+ """
+
+ return self._current_source_progress
+
+ @property
+ def used_source_progress(self):
+ """Transfer progress that successfully distributed the item.
+
+ Returns:
+ Union[DistributeTransferProgress, None]: Transfer progress or None.
+ """
+
+ return self._used_source_progress
+
+ @property
+ def used_source(self):
+ """Data of source item.
+
+ Returns:
+ Union[Dict[str, Any], None]: SourceInfo data or None.
+ """
+
+ return self._used_source
+
+ @property
+ def error_message(self):
+ """Reason why distribution item failed.
+
+ Returns:
+ Union[str, None]: Error message.
+ """
+
+ return self._error_msg
+
+ @property
+ def error_detail(self):
+ """Detailed reason why distribution item failed.
+
+ Returns:
+ Union[str, None]: Detailed information (maybe traceback).
+ """
+
+ return self._error_detail
+
+ def _distribute(self):
+ if not self.sources:
+ message = (
+ f"{self.item_label}: Don't have"
+ " any sources to download from."
+ )
+ self.log.error(message)
+ self._error_msg = message
+ self.state = UpdateState.MISS_SOURCE_FILES
+ return
+
+ download_dirpath = self.download_dirpath
+ unzip_dirpath = self.unzip_dirpath
+ for source, source_progress in self.sources:
+ self._current_source_progress = source_progress
+ source_progress.set_started()
+
+ # Remove directory if exists
+ if os.path.isdir(unzip_dirpath):
+ self.log.debug(f"Cleaning {unzip_dirpath}")
+ shutil.rmtree(unzip_dirpath)
+
+ # Create directory
+ os.makedirs(unzip_dirpath)
+ if not os.path.isdir(download_dirpath):
+ os.makedirs(download_dirpath)
+
+ try:
+ downloader = self.factory.get_downloader(source.type)
+ except Exception:
+ message = f"Unknown downloader {source.type}"
+ source_progress.set_failed(message)
+ self.log.warning(message, exc_info=True)
+ continue
+
+ source_data = attr.asdict(source)
+ cleanup_args = (
+ source_data,
+ download_dirpath,
+ self.downloader_data
+ )
+
+ try:
+ zip_filepath = downloader.download(
+ source_data,
+ download_dirpath,
+ self.downloader_data,
+ source_progress.transfer_progress,
+ )
+ except Exception:
+ message = "Failed to download source"
+ source_progress.set_failed(message)
+ self.log.warning(
+ f"{self.item_label}: {message}",
+ exc_info=True
+ )
+ downloader.cleanup(*cleanup_args)
+ continue
+
+ source_progress.set_hash_check_started()
+ try:
+ downloader.check_hash(zip_filepath, self.file_hash)
+ except Exception:
+ message = "File hash does not match"
+ source_progress.set_failed(message)
+ self.log.warning(
+ f"{self.item_label}: {message}",
+ exc_info=True
+ )
+ downloader.cleanup(*cleanup_args)
+ continue
+
+ source_progress.set_hash_check_finished()
+ source_progress.set_unzip_started()
+ try:
+ downloader.unzip(zip_filepath, unzip_dirpath)
+ except Exception:
+ message = "Couldn't unzip source file"
+ source_progress.set_failed(message)
+ self.log.warning(
+ f"{self.item_label}: {message}",
+ exc_info=True
+ )
+ downloader.cleanup(*cleanup_args)
+ continue
+
+ source_progress.set_unzip_finished()
+ downloader.cleanup(*cleanup_args)
+ self.state = UpdateState.UPDATED
+ self._used_source = source_data
+ break
+
+ last_progress = self._current_source_progress
+ self._current_source_progress = None
+ if self.state == UpdateState.UPDATED:
+ self._used_source_progress = last_progress
+ self.log.info(f"{self.item_label}: Distributed")
+ return
+
+ self.log.error(f"{self.item_label}: Failed to distribute")
+ self._error_msg = "Failed to receive or install source files"
+
+ def distribute(self):
+ """Execute distribution logic."""
+
+ if not self.need_distribution or self._dist_started:
+ return
+
+ self._dist_started = True
+ try:
+ if self.state == UpdateState.OUTDATED:
+ self._distribute()
+
+ except Exception as exc:
+ self.state = UpdateState.UPDATE_FAILED
+ self._error_msg = str(exc)
+ self._error_detail = "".join(
+ traceback.format_exception(*sys.exc_info())
+ )
+ self.log.error(
+ f"{self.item_label}: Distibution filed",
+ exc_info=True
+ )
+
+ finally:
+ self._dist_finished = True
+ if self.state == UpdateState.OUTDATED:
+ self.state = UpdateState.UPDATE_FAILED
+ self._error_msg = "Distribution failed"
+
+ if (
+ self.state != UpdateState.UPDATED
+ and self.unzip_dirpath
+ and os.path.isdir(self.unzip_dirpath)
+ ):
+ self.log.debug(f"Cleaning {self.unzip_dirpath}")
+ shutil.rmtree(self.unzip_dirpath)
+
+
+class AyonDistribution:
+ """Distribution control.
+
+ Receive information from server what addons and dependency packages
+ should be available locally and prepare/validate their distribution.
+
+ Arguments are available for testing of the class.
+
+ Args:
+ addon_dirpath (Optional[str]): Where addons will be stored.
+ dependency_dirpath (Optional[str]): Where dependencies will be stored.
+ dist_factory (Optional[DownloadFactory]): Factory which cares about
+ downloading of items based on source type.
+ addons_info (Optional[list[dict[str, Any]]): List of prepared
+ addons' info.
+ dependency_packages_info (Optional[list[dict[str, Any]]): Info
+ about packages from server.
+ bundles_info (Optional[Dict[str, Any]]): Info about
+ bundles.
+ bundle_name (Optional[str]): Name of bundle to use. If not passed
+ an environment variable 'AYON_BUNDLE_NAME' is checked for value.
+ When both are not available the bundle is defined by 'use_staging'
+ value.
+ use_staging (Optional[bool]): Use staging versions of an addon.
+ If not passed, 'is_staging_enabled' is used as default value.
+ """
+
+ def __init__(
+ self,
+ addon_dirpath=None,
+ dependency_dirpath=None,
+ dist_factory=None,
+ addons_info=NOT_SET,
+ dependency_packages_info=NOT_SET,
+ bundles_info=NOT_SET,
+ bundle_name=NOT_SET,
+ use_staging=None
+ ):
+ self._log = None
+
+ self._dist_started = False
+ self._dist_finished = False
+
+ self._addons_dirpath = addon_dirpath or get_addons_dir()
+ self._dependency_dirpath = dependency_dirpath or get_dependencies_dir()
+ self._dist_factory = (
+ dist_factory or get_default_download_factory()
+ )
+
+ if bundle_name is NOT_SET:
+ bundle_name = os.environ.get("AYON_BUNDLE_NAME", NOT_SET)
+
+ # Raw addons data from server
+ self._addons_info = addons_info
+ # Prepared data as Addon objects
+ self._addon_items = NOT_SET
+ # Distrubtion items of addons
+ # - only those addons and versions that should be distributed
+ self._addon_dist_items = NOT_SET
+
+ # Raw dependency packages data from server
+ self._dependency_packages_info = dependency_packages_info
+ # Prepared dependency packages as objects
+ self._dependency_packages_items = NOT_SET
+ # Dependency package item that should be used
+ self._dependency_package_item = NOT_SET
+ # Distribution item of dependency package
+ self._dependency_dist_item = NOT_SET
+
+ # Raw bundles data from server
+ self._bundles_info = bundles_info
+ # Bundles as objects
+ self._bundle_items = NOT_SET
+
+ # Bundle that should be used in production
+ self._production_bundle = NOT_SET
+ # Bundle that should be used in staging
+ self._staging_bundle = NOT_SET
+ # Boolean that defines if staging bundle should be used
+ self._use_staging = use_staging
+
+ # Specific bundle name should be used
+ self._bundle_name = bundle_name
+ # Final bundle that will be used
+ self._bundle = NOT_SET
+
+ @property
+ def use_staging(self):
+ """Staging version of a bundle should be used.
+
+ This value is completely ignored if specific bundle name should
+ be used.
+
+ Returns:
+ bool: True if staging version should be used.
+ """
+
+ if self._use_staging is None:
+ self._use_staging = is_staging_enabled()
+ return self._use_staging
+
+ @property
+ def log(self):
+ """Helper to access logger.
+
+ Returns:
+ logging.Logger: Logger instance.
+ """
+ if self._log is None:
+ self._log = logging.getLogger(self.__class__.__name__)
+ return self._log
+
+ @property
+ def bundles_info(self):
+ """
+
+ Returns:
+ dict[str, dict[str, Any]]: Bundles information from server.
+ """
+
+ if self._bundles_info is NOT_SET:
+ self._bundles_info = ayon_api.get_bundles()
+ return self._bundles_info
+
+ @property
+ def bundle_items(self):
+ """
+
+ Returns:
+ list[Bundle]: List of bundles info.
+ """
+
+ if self._bundle_items is NOT_SET:
+ self._bundle_items = [
+ Bundle.from_dict(info)
+ for info in self.bundles_info["bundles"]
+ ]
+ return self._bundle_items
+
+ def _prepare_production_staging_bundles(self):
+ production_bundle = None
+ staging_bundle = None
+ for bundle in self.bundle_items:
+ if bundle.is_production:
+ production_bundle = bundle
+ if bundle.is_staging:
+ staging_bundle = bundle
+ self._production_bundle = production_bundle
+ self._staging_bundle = staging_bundle
+
+ @property
+ def production_bundle(self):
+ """
+ Returns:
+ Union[Bundle, None]: Bundle that should be used in production.
+ """
+
+ if self._production_bundle is NOT_SET:
+ self._prepare_production_staging_bundles()
+ return self._production_bundle
+
+ @property
+ def staging_bundle(self):
+ """
+ Returns:
+ Union[Bundle, None]: Bundle that should be used in staging.
+ """
+
+ if self._staging_bundle is NOT_SET:
+ self._prepare_production_staging_bundles()
+ return self._staging_bundle
+
+ @property
+ def bundle_to_use(self):
+ """Bundle that will be used for distribution.
+
+ Bundle that should be used can be affected by 'bundle_name'
+ or 'use_staging'.
+
+ Returns:
+ Union[Bundle, None]: Bundle that will be used for distribution
+ or None.
+
+ Raises:
+ BundleNotFoundError: When bundle name to use is defined
+ but is not available on server.
+ """
+
+ if self._bundle is NOT_SET:
+ if self._bundle_name is not NOT_SET:
+ bundle = next(
+ (
+ bundle
+ for bundle in self.bundle_items
+ if bundle.name == self._bundle_name
+ ),
+ None
+ )
+ if bundle is None:
+ raise BundleNotFoundError(self._bundle_name)
+
+ self._bundle = bundle
+ elif self.use_staging:
+ self._bundle = self.staging_bundle
+ else:
+ self._bundle = self.production_bundle
+ return self._bundle
+
+ @property
+ def bundle_name_to_use(self):
+ bundle = self.bundle_to_use
+ return None if bundle is None else bundle.name
+
+ @property
+ def addons_info(self):
+ """Server information about available addons.
+
+ Returns:
+ Dict[str, dict[str, Any]: Addon info by addon name.
+ """
+
+ if self._addons_info is NOT_SET:
+ server_info = ayon_api.get_addons_info(details=True)
+ self._addons_info = server_info["addons"]
+ return self._addons_info
+
+ @property
+ def addon_items(self):
+ """Information about available addons on server.
+
+ Addons may require distribution of files. For those addons will be
+ created 'DistributionItem' handling distribution itself.
+
+ Returns:
+ Dict[str, AddonInfo]: Addon info object by addon name.
+ """
+
+ if self._addon_items is NOT_SET:
+ addons_info = {}
+ for addon in self.addons_info:
+ addon_info = AddonInfo.from_dict(addon)
+ addons_info[addon_info.name] = addon_info
+ self._addon_items = addons_info
+ return self._addon_items
+
+ @property
+ def dependency_packages_info(self):
+ """Server information about available dependency packages.
+
+ Notes:
+ For testing purposes it is possible to pass dependency packages
+ information to '__init__'.
+
+ Returns:
+ list[dict[str, Any]]: Dependency packages information.
+ """
+
+ if self._dependency_packages_info is NOT_SET:
+ self._dependency_packages_info = (
+ ayon_api.get_dependency_packages())["packages"]
+ return self._dependency_packages_info
+
+ @property
+ def dependency_packages_items(self):
+ """Dependency packages as objects.
+
+ Returns:
+ dict[str, DependencyItem]: Dependency packages as objects by name.
+ """
+
+ if self._dependency_packages_items is NOT_SET:
+ dependenc_package_items = {}
+ for item in self.dependency_packages_info:
+ item = DependencyItem.from_dict(item)
+ dependenc_package_items[item.name] = item
+ self._dependency_packages_items = dependenc_package_items
+ return self._dependency_packages_items
+
+ @property
+ def dependency_package_item(self):
+ """Dependency package item that should be used by bundle.
+
+ Returns:
+ Union[None, Dict[str, Any]]: None if bundle does not have
+ specified dependency package.
+ """
+
+ if self._dependency_package_item is NOT_SET:
+ dependency_package_item = None
+ bundle = self.bundle_to_use
+ if bundle is not None:
+ package_name = bundle.dependency_packages.get(
+ platform.system().lower()
+ )
+ dependency_package_item = self.dependency_packages_items.get(
+ package_name)
+ self._dependency_package_item = dependency_package_item
+ return self._dependency_package_item
+
+ def _prepare_current_addon_dist_items(self):
+ addons_metadata = self.get_addons_metadata()
+ output = []
+ addon_versions = {}
+ bundle = self.bundle_to_use
+ if bundle is not None:
+ addon_versions = bundle.addon_versions
+ for addon_name, addon_item in self.addon_items.items():
+ addon_version = addon_versions.get(addon_name)
+ # Addon is not in bundle -> Skip
+ if addon_version is None:
+ continue
+
+ addon_version_item = addon_item.versions.get(addon_version)
+ # Addon version is not available in addons info
+ # - TODO handle this case (raise error, skip, store, report, ...)
+ if addon_version_item is None:
+ print(
+ f"Version '{addon_version}' of addon '{addon_name}'"
+ " is not available on server."
+ )
+ continue
+
+ if not addon_version_item.require_distribution:
+ continue
+ full_name = addon_version_item.full_name
+ addon_dest = os.path.join(self._addons_dirpath, full_name)
+ self.log.debug(f"Checking {full_name} in {addon_dest}")
+ addon_in_metadata = (
+ addon_name in addons_metadata
+ and addon_version_item.version in addons_metadata[addon_name]
+ )
+ if addon_in_metadata and os.path.isdir(addon_dest):
+ self.log.debug(
+ f"Addon version folder {addon_dest} already exists."
+ )
+ state = UpdateState.UPDATED
+
+ else:
+ state = UpdateState.OUTDATED
+
+ downloader_data = {
+ "type": "addon",
+ "name": addon_name,
+ "version": addon_version
+ }
+
+ dist_item = DistributionItem(
+ state,
+ addon_dest,
+ addon_dest,
+ addon_version_item.hash,
+ self._dist_factory,
+ list(addon_version_item.sources),
+ downloader_data,
+ full_name,
+ self.log
+ )
+ output.append({
+ "dist_item": dist_item,
+ "addon_name": addon_name,
+ "addon_version": addon_version,
+ "addon_item": addon_item,
+ "addon_version_item": addon_version_item,
+ })
+ return output
+
+ def _prepare_dependency_progress(self):
+ package = self.dependency_package_item
+ if package is None:
+ return None
+
+ metadata = self.get_dependency_metadata()
+ downloader_data = {
+ "type": "dependency_package",
+ "name": package.name,
+ "platform": package.platform_name
+ }
+ zip_dir = package_dir = os.path.join(
+ self._dependency_dirpath, package.name
+ )
+ self.log.debug(f"Checking {package.name} in {package_dir}")
+
+ if not os.path.isdir(package_dir) or package.name not in metadata:
+ state = UpdateState.OUTDATED
+ else:
+ state = UpdateState.UPDATED
+
+ return DistributionItem(
+ state,
+ zip_dir,
+ package_dir,
+ package.checksum,
+ self._dist_factory,
+ package.sources,
+ downloader_data,
+ package.name,
+ self.log,
+ )
+
+ def get_addon_dist_items(self):
+ """Addon distribution items.
+
+ These items describe source files required by addon to be available on
+ machine. Each item may have 0-n source information from where can be
+ obtained. If file is already available it's state will be 'UPDATED'.
+
+ Example output:
+ [
+ {
+ "dist_item": DistributionItem,
+ "addon_name": str,
+ "addon_version": str,
+ "addon_item": AddonInfo,
+ "addon_version_item": AddonVersionInfo
+ }, {
+ ...
+ }
+ ]
+
+ Returns:
+ list[dict[str, Any]]: Distribution items with addon version item.
+ """
+
+ if self._addon_dist_items is NOT_SET:
+ self._addon_dist_items = (
+ self._prepare_current_addon_dist_items())
+ return self._addon_dist_items
+
+ def get_dependency_dist_item(self):
+ """Dependency package distribution item.
+
+ Item describe source files required by server to be available on
+ machine. Item may have 0-n source information from where can be
+ obtained. If file is already available it's state will be 'UPDATED'.
+
+ 'None' is returned if server does not have defined any dependency
+ package.
+
+ Returns:
+ Union[None, DistributionItem]: Dependency item or None if server
+ does not have specified any dependency package.
+ """
+
+ if self._dependency_dist_item is NOT_SET:
+ self._dependency_dist_item = self._prepare_dependency_progress()
+ return self._dependency_dist_item
+
+ def get_dependency_metadata_filepath(self):
+ """Path to distribution metadata file.
+
+ Metadata contain information about distributed packages, used source,
+ expected file hash and time when file was distributed.
+
+ Returns:
+ str: Path to a file where dependency package metadata are stored.
+ """
+
+ return os.path.join(self._dependency_dirpath, "dependency.json")
+
+ def get_addons_metadata_filepath(self):
+ """Path to addons metadata file.
+
+ Metadata contain information about distributed addons, used sources,
+ expected file hashes and time when files were distributed.
+
+ Returns:
+ str: Path to a file where addons metadata are stored.
+ """
+
+ return os.path.join(self._addons_dirpath, "addons.json")
+
+ def read_metadata_file(self, filepath, default_value=None):
+ """Read json file from path.
+
+ Method creates the file when does not exist with default value.
+
+ Args:
+ filepath (str): Path to json file.
+ default_value (Union[Dict[str, Any], List[Any], None]): Default
+ value if the file is not available (or valid).
+
+ Returns:
+ Union[Dict[str, Any], List[Any]]: Value from file.
+ """
+
+ if default_value is None:
+ default_value = {}
+
+ if not os.path.exists(filepath):
+ return default_value
+
+ try:
+ with open(filepath, "r") as stream:
+ data = json.load(stream)
+ except ValueError:
+ data = default_value
+ return data
+
+ def save_metadata_file(self, filepath, data):
+ """Store data to json file.
+
+ Method creates the file when does not exist.
+
+ Args:
+ filepath (str): Path to json file.
+ data (Union[Dict[str, Any], List[Any]]): Data to store into file.
+ """
+
+ if not os.path.exists(filepath):
+ dirpath = os.path.dirname(filepath)
+ if not os.path.exists(dirpath):
+ os.makedirs(dirpath)
+ with open(filepath, "w") as stream:
+ json.dump(data, stream, indent=4)
+
+ def get_dependency_metadata(self):
+ filepath = self.get_dependency_metadata_filepath()
+ return self.read_metadata_file(filepath, {})
+
+ def update_dependency_metadata(self, package_name, data):
+ dependency_metadata = self.get_dependency_metadata()
+ dependency_metadata[package_name] = data
+ filepath = self.get_dependency_metadata_filepath()
+ self.save_metadata_file(filepath, dependency_metadata)
+
+ def get_addons_metadata(self):
+ filepath = self.get_addons_metadata_filepath()
+ return self.read_metadata_file(filepath, {})
+
+ def update_addons_metadata(self, addons_information):
+ if not addons_information:
+ return
+ addons_metadata = self.get_addons_metadata()
+ for addon_name, version_value in addons_information.items():
+ if addon_name not in addons_metadata:
+ addons_metadata[addon_name] = {}
+ for addon_version, version_data in version_value.items():
+ addons_metadata[addon_name][addon_version] = version_data
+
+ filepath = self.get_addons_metadata_filepath()
+ self.save_metadata_file(filepath, addons_metadata)
+
+ def finish_distribution(self):
+ """Store metadata about distributed items."""
+
+ self._dist_finished = True
+ stored_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+ dependency_dist_item = self.get_dependency_dist_item()
+ if (
+ dependency_dist_item is not None
+ and dependency_dist_item.need_distribution
+ and dependency_dist_item.state == UpdateState.UPDATED
+ ):
+ package = self.dependency_package
+ source = dependency_dist_item.used_source
+ if source is not None:
+ data = {
+ "source": source,
+ "file_hash": dependency_dist_item.file_hash,
+ "distributed_dt": stored_time
+ }
+ self.update_dependency_metadata(package.name, data)
+
+ addons_info = {}
+ for item in self.get_addon_dist_items():
+ dist_item = item["dist_item"]
+ if (
+ not dist_item.need_distribution
+ or dist_item.state != UpdateState.UPDATED
+ ):
+ continue
+
+ source_data = dist_item.used_source
+ if not source_data:
+ continue
+
+ addon_name = item["addon_name"]
+ addon_version = item["addon_version"]
+ addons_info.setdefault(addon_name, {})
+ addons_info[addon_name][addon_version] = {
+ "source": source_data,
+ "file_hash": dist_item.file_hash,
+ "distributed_dt": stored_time
+ }
+
+ self.update_addons_metadata(addons_info)
+
+ def get_all_distribution_items(self):
+ """Distribution items required by server.
+
+ Items contain dependency package item and all addons that are enabled
+ and have distribution requirements.
+
+ Items can be already available on machine.
+
+ Returns:
+ List[DistributionItem]: Distribution items required by server.
+ """
+
+ output = [
+ item["dist_item"]
+ for item in self.get_addon_dist_items()
+ ]
+ dependency_dist_item = self.get_dependency_dist_item()
+ if dependency_dist_item is not None:
+ output.insert(0, dependency_dist_item)
+
+ return output
+
+ def distribute(self, threaded=False):
+ """Distribute all missing items.
+
+ Method will try to distribute all items that are required by server.
+
+ This method does not handle failed items. To validate the result call
+ 'validate_distribution' when this method finishes.
+
+ Args:
+ threaded (bool): Distribute items in threads.
+ """
+
+ if self._dist_started:
+ raise RuntimeError("Distribution already started")
+ self._dist_started = True
+ threads = collections.deque()
+ for item in self.get_all_distribution_items():
+ if threaded:
+ threads.append(threading.Thread(target=item.distribute))
+ else:
+ item.distribute()
+
+ while threads:
+ thread = threads.popleft()
+ if thread.is_alive():
+ threads.append(thread)
+ else:
+ thread.join()
+
+ self.finish_distribution()
+
+ def validate_distribution(self):
+ """Check if all required distribution items are distributed.
+
+ Raises:
+ RuntimeError: Any of items is not available.
+ """
+
+ invalid = []
+ dependency_package = self.get_dependency_dist_item()
+ if (
+ dependency_package is not None
+ and dependency_package.state != UpdateState.UPDATED
+ ):
+ invalid.append("Dependency package")
+
+ for item in self.get_addon_dist_items():
+ dist_item = item["dist_item"]
+ if dist_item.state != UpdateState.UPDATED:
+ invalid.append(item["addon_name"])
+
+ if not invalid:
+ return
+
+ raise RuntimeError("Failed to distribute {}".format(
+ ", ".join([f'"{item}"' for item in invalid])
+ ))
+
+ def get_sys_paths(self):
+ """Get all paths to python packages that should be added to python.
+
+ These paths lead to addon directories and python dependencies in
+ dependency package.
+
+ Todos:
+ Add dependency package directory to output. ATM is not structure of
+ dependency package 100% defined.
+
+ Returns:
+ List[str]: Paths that should be added to 'sys.path' and
+ 'PYTHONPATH'.
+ """
+
+ output = []
+ for item in self.get_all_distribution_items():
+ if item.state != UpdateState.UPDATED:
+ continue
+ unzip_dirpath = item.unzip_dirpath
+ if unzip_dirpath and os.path.exists(unzip_dirpath):
+ output.append(unzip_dirpath)
+ return output
+
+
+def cli(*args):
+ raise NotImplementedError
diff --git a/common/ayon_common/distribution/data_structures.py b/common/ayon_common/distribution/data_structures.py
new file mode 100644
index 0000000000..aa93d4ed71
--- /dev/null
+++ b/common/ayon_common/distribution/data_structures.py
@@ -0,0 +1,265 @@
+import attr
+from enum import Enum
+
+
+class UrlType(Enum):
+ HTTP = "http"
+ GIT = "git"
+ FILESYSTEM = "filesystem"
+ SERVER = "server"
+
+
+@attr.s
+class MultiPlatformValue(object):
+ windows = attr.ib(default=None)
+ linux = attr.ib(default=None)
+ darwin = attr.ib(default=None)
+
+
+@attr.s
+class SourceInfo(object):
+ type = attr.ib()
+
+
+@attr.s
+class LocalSourceInfo(SourceInfo):
+ path = attr.ib(default=attr.Factory(MultiPlatformValue))
+
+
+@attr.s
+class WebSourceInfo(SourceInfo):
+ url = attr.ib(default=None)
+ headers = attr.ib(default=None)
+ filename = attr.ib(default=None)
+
+
+@attr.s
+class ServerSourceInfo(SourceInfo):
+ filename = attr.ib(default=None)
+ path = attr.ib(default=None)
+
+
+def convert_source(source):
+ """Create source object from data information.
+
+ Args:
+ source (Dict[str, any]): Information about source.
+
+ Returns:
+ Union[None, SourceInfo]: Object with source information if type is
+ known.
+ """
+
+ source_type = source.get("type")
+ if not source_type:
+ return None
+
+ if source_type == UrlType.FILESYSTEM.value:
+ return LocalSourceInfo(
+ type=source_type,
+ path=source["path"]
+ )
+
+ if source_type == UrlType.HTTP.value:
+ url = source["path"]
+ return WebSourceInfo(
+ type=source_type,
+ url=url,
+ headers=source.get("headers"),
+ filename=source.get("filename")
+ )
+
+ if source_type == UrlType.SERVER.value:
+ return ServerSourceInfo(
+ type=source_type,
+ filename=source.get("filename"),
+ path=source.get("path")
+ )
+
+
+def prepare_sources(src_sources):
+ sources = []
+ unknown_sources = []
+ for source in (src_sources or []):
+ dependency_source = convert_source(source)
+ if dependency_source is not None:
+ sources.append(dependency_source)
+ else:
+ print(f"Unknown source {source.get('type')}")
+ unknown_sources.append(source)
+ return sources, unknown_sources
+
+
+@attr.s
+class VersionData(object):
+ version_data = attr.ib(default=None)
+
+
+@attr.s
+class AddonVersionInfo(object):
+ version = attr.ib()
+ full_name = attr.ib()
+ title = attr.ib(default=None)
+ require_distribution = attr.ib(default=False)
+ sources = attr.ib(default=attr.Factory(list))
+ unknown_sources = attr.ib(default=attr.Factory(list))
+ hash = attr.ib(default=None)
+
+ @classmethod
+ def from_dict(
+ cls, addon_name, addon_title, addon_version, version_data
+ ):
+ """Addon version info.
+
+ Args:
+ addon_name (str): Name of addon.
+ addon_title (str): Title of addon.
+ addon_version (str): Version of addon.
+ version_data (dict[str, Any]): Addon version information from
+ server.
+
+ Returns:
+ AddonVersionInfo: Addon version info.
+ """
+
+ full_name = f"{addon_name}_{addon_version}"
+ title = f"{addon_title} {addon_version}"
+
+ source_info = version_data.get("clientSourceInfo")
+ require_distribution = source_info is not None
+ sources, unknown_sources = prepare_sources(source_info)
+
+ return cls(
+ version=addon_version,
+ full_name=full_name,
+ require_distribution=require_distribution,
+ sources=sources,
+ unknown_sources=unknown_sources,
+ hash=version_data.get("hash"),
+ title=title
+ )
+
+
+@attr.s
+class AddonInfo(object):
+ """Object matching json payload from Server"""
+ name = attr.ib()
+ versions = attr.ib(default=attr.Factory(dict))
+ title = attr.ib(default=None)
+ description = attr.ib(default=None)
+ license = attr.ib(default=None)
+ authors = attr.ib(default=None)
+
+ @classmethod
+ def from_dict(cls, data):
+ """Addon info by available versions.
+
+ Args:
+ data (dict[str, Any]): Addon information from server. Should
+ contain information about every version under 'versions'.
+
+ Returns:
+ AddonInfo: Addon info with available versions.
+ """
+
+ # server payload contains info about all versions
+ addon_name = data["name"]
+ title = data.get("title") or addon_name
+
+ src_versions = data.get("versions") or {}
+ dst_versions = {
+ addon_version: AddonVersionInfo.from_dict(
+ addon_name, title, addon_version, version_data
+ )
+ for addon_version, version_data in src_versions.items()
+ }
+ return cls(
+ name=addon_name,
+ versions=dst_versions,
+ description=data.get("description"),
+ title=data.get("title") or addon_name,
+ license=data.get("license"),
+ authors=data.get("authors")
+ )
+
+
+@attr.s
+class DependencyItem(object):
+ """Object matching payload from Server about single dependency package"""
+ name = attr.ib()
+ platform_name = attr.ib()
+ checksum = attr.ib()
+ sources = attr.ib(default=attr.Factory(list))
+ unknown_sources = attr.ib(default=attr.Factory(list))
+ source_addons = attr.ib(default=attr.Factory(dict))
+ python_modules = attr.ib(default=attr.Factory(dict))
+
+ @classmethod
+ def from_dict(cls, package):
+ src_sources = package.get("sources") or []
+ for source in src_sources:
+ if source.get("type") == "server" and not source.get("filename"):
+ source["filename"] = package["filename"]
+ sources, unknown_sources = prepare_sources(src_sources)
+ return cls(
+ name=package["filename"],
+ platform_name=package["platform"],
+ sources=sources,
+ unknown_sources=unknown_sources,
+ checksum=package["checksum"],
+ source_addons=package["sourceAddons"],
+ python_modules=package["pythonModules"]
+ )
+
+
+@attr.s
+class Installer:
+ version = attr.ib()
+ filename = attr.ib()
+ platform_name = attr.ib()
+ size = attr.ib()
+ checksum = attr.ib()
+ python_version = attr.ib()
+ python_modules = attr.ib()
+ sources = attr.ib(default=attr.Factory(list))
+ unknown_sources = attr.ib(default=attr.Factory(list))
+
+ @classmethod
+ def from_dict(cls, installer_info):
+ sources, unknown_sources = prepare_sources(
+ installer_info.get("sources"))
+
+ return cls(
+ version=installer_info["version"],
+ filename=installer_info["filename"],
+ platform_name=installer_info["platform"],
+ size=installer_info["size"],
+ sources=sources,
+ unknown_sources=unknown_sources,
+ checksum=installer_info["checksum"],
+ python_version=installer_info["pythonVersion"],
+ python_modules=installer_info["pythonModules"]
+ )
+
+
+@attr.s
+class Bundle:
+ """Class representing bundle information."""
+
+ name = attr.ib()
+ installer_version = attr.ib()
+ addon_versions = attr.ib(default=attr.Factory(dict))
+ dependency_packages = attr.ib(default=attr.Factory(dict))
+ is_production = attr.ib(default=False)
+ is_staging = attr.ib(default=False)
+
+ @classmethod
+ def from_dict(cls, data):
+ return cls(
+ name=data["name"],
+ installer_version=data.get("installerVersion"),
+ addon_versions=data.get("addons", {}),
+ dependency_packages=data.get("dependencyPackages", {}),
+ is_production=data["isProduction"],
+ is_staging=data["isStaging"],
+ )
diff --git a/common/ayon_common/distribution/downloaders.py b/common/ayon_common/distribution/downloaders.py
new file mode 100644
index 0000000000..23280176c3
--- /dev/null
+++ b/common/ayon_common/distribution/downloaders.py
@@ -0,0 +1,250 @@
+import os
+import logging
+import platform
+from abc import ABCMeta, abstractmethod
+
+import ayon_api
+
+from .file_handler import RemoteFileHandler
+from .data_structures import UrlType
+
+
+class SourceDownloader(metaclass=ABCMeta):
+ """Abstract class for source downloader."""
+
+ log = logging.getLogger(__name__)
+
+ @classmethod
+ @abstractmethod
+ def download(cls, source, destination_dir, data, transfer_progress):
+ """Returns url of downloaded addon zip file.
+
+ Tranfer progress can be ignored, in that case file transfer won't
+ be shown as 0-100% but as 'running'. First step should be to set
+ destination content size and then add transferred chunk sizes.
+
+ Args:
+ source (dict): {type:"http", "url":"https://} ...}
+ destination_dir (str): local folder to unzip
+ data (dict): More information about download content. Always have
+ 'type' key in.
+ transfer_progress (ayon_api.TransferProgress): Progress of
+ transferred (copy/download) content.
+
+ Returns:
+ (str) local path to addon zip file
+ """
+
+ pass
+
+ @classmethod
+ @abstractmethod
+ def cleanup(cls, source, destination_dir, data):
+ """Cleanup files when distribution finishes or crashes.
+
+ Cleanup e.g. temporary files (downloaded zip) or other related stuff
+ to downloader.
+ """
+
+ pass
+
+ @classmethod
+ def check_hash(cls, addon_path, addon_hash, hash_type="sha256"):
+ """Compares 'hash' of downloaded 'addon_url' file.
+
+ Args:
+ addon_path (str): Local path to addon file.
+ addon_hash (str): Hash of downloaded file.
+ hash_type (str): Type of hash.
+
+ Raises:
+ ValueError if hashes doesn't match
+ """
+
+ if not os.path.exists(addon_path):
+ raise ValueError(f"{addon_path} doesn't exist.")
+ if not RemoteFileHandler.check_integrity(
+ addon_path, addon_hash, hash_type=hash_type
+ ):
+ raise ValueError(f"{addon_path} doesn't match expected hash.")
+
+ @classmethod
+ def unzip(cls, addon_zip_path, destination_dir):
+ """Unzips local 'addon_zip_path' to 'destination'.
+
+ Args:
+ addon_zip_path (str): local path to addon zip file
+ destination_dir (str): local folder to unzip
+ """
+
+ RemoteFileHandler.unzip(addon_zip_path, destination_dir)
+ os.remove(addon_zip_path)
+
+
+class OSDownloader(SourceDownloader):
+ """Downloader using files from file drive."""
+
+ @classmethod
+ def download(cls, source, destination_dir, data, transfer_progress):
+ # OS doesn't need to download, unzip directly
+ addon_url = source["path"].get(platform.system().lower())
+ if not os.path.exists(addon_url):
+ raise ValueError(f"{addon_url} is not accessible")
+ return addon_url
+
+ @classmethod
+ def cleanup(cls, source, destination_dir, data):
+ # Nothing to do - download does not copy anything
+ pass
+
+
+class HTTPDownloader(SourceDownloader):
+ """Downloader using http or https protocol."""
+
+ CHUNK_SIZE = 100000
+
+ @staticmethod
+ def get_filename(source):
+ source_url = source["url"]
+ filename = source.get("filename")
+ if not filename:
+ filename = os.path.basename(source_url)
+ basename, ext = os.path.splitext(filename)
+ allowed_exts = set(RemoteFileHandler.IMPLEMENTED_ZIP_FORMATS)
+ if ext.lower().lstrip(".") not in allowed_exts:
+ filename = f"{basename}.zip"
+ return filename
+
+ @classmethod
+ def download(cls, source, destination_dir, data, transfer_progress):
+ source_url = source["url"]
+ cls.log.debug(f"Downloading {source_url} to {destination_dir}")
+ headers = source.get("headers")
+ filename = cls.get_filename(source)
+
+ # TODO use transfer progress
+ RemoteFileHandler.download_url(
+ source_url,
+ destination_dir,
+ filename,
+ headers=headers
+ )
+
+ return os.path.join(destination_dir, filename)
+
+ @classmethod
+ def cleanup(cls, source, destination_dir, data):
+ filename = cls.get_filename(source)
+ filepath = os.path.join(destination_dir, filename)
+ if os.path.exists(filepath) and os.path.isfile(filepath):
+ os.remove(filepath)
+
+
+class AyonServerDownloader(SourceDownloader):
+ """Downloads static resource file from AYON Server.
+
+ Expects filled env var AYON_SERVER_URL.
+ """
+
+ CHUNK_SIZE = 8192
+
+ @classmethod
+ def download(cls, source, destination_dir, data, transfer_progress):
+ path = source["path"]
+ filename = source["filename"]
+ if path and not filename:
+ filename = path.split("/")[-1]
+
+ cls.log.debug(f"Downloading {filename} to {destination_dir}")
+
+ _, ext = os.path.splitext(filename)
+ ext = ext.lower().lstrip(".")
+ valid_exts = set(RemoteFileHandler.IMPLEMENTED_ZIP_FORMATS)
+ if ext not in valid_exts:
+ raise ValueError((
+ f"Invalid file extension \"{ext}\"."
+ f" Expected {', '.join(valid_exts)}"
+ ))
+
+ if path:
+ filepath = os.path.join(destination_dir, filename)
+ return ayon_api.download_file(
+ path,
+ filepath,
+ chunk_size=cls.CHUNK_SIZE,
+ progress=transfer_progress
+ )
+
+ # dst_filepath = os.path.join(destination_dir, filename)
+ if data["type"] == "dependency_package":
+ return ayon_api.download_dependency_package(
+ data["name"],
+ destination_dir,
+ filename,
+ platform_name=data["platform"],
+ chunk_size=cls.CHUNK_SIZE,
+ progress=transfer_progress
+ )
+
+ if data["type"] == "addon":
+ return ayon_api.download_addon_private_file(
+ data["name"],
+ data["version"],
+ filename,
+ destination_dir,
+ chunk_size=cls.CHUNK_SIZE,
+ progress=transfer_progress
+ )
+
+ raise ValueError(f"Unknown type to download \"{data['type']}\"")
+
+ @classmethod
+ def cleanup(cls, source, destination_dir, data):
+ filename = source["filename"]
+ filepath = os.path.join(destination_dir, filename)
+ if os.path.exists(filepath) and os.path.isfile(filepath):
+ os.remove(filepath)
+
+
+class DownloadFactory:
+ """Factory for downloaders."""
+
+ def __init__(self):
+ self._downloaders = {}
+
+ def register_format(self, downloader_type, downloader):
+ """Register downloader for download type.
+
+ Args:
+ downloader_type (UrlType): Type of source.
+ downloader (SourceDownloader): Downloader which cares about
+ download, hash check and unzipping.
+ """
+
+ self._downloaders[downloader_type.value] = downloader
+
+ def get_downloader(self, downloader_type):
+ """Registered downloader for type.
+
+ Args:
+ downloader_type (UrlType): Type of source.
+
+ Returns:
+ SourceDownloader: Downloader object which should care about file
+ distribution.
+
+ Raises:
+ ValueError: If type does not have registered downloader.
+ """
+
+ if downloader := self._downloaders.get(downloader_type):
+ return downloader()
+ raise ValueError(f"{downloader_type} not implemented")
+
+
+def get_default_download_factory():
+ download_factory = DownloadFactory()
+ download_factory.register_format(UrlType.FILESYSTEM, OSDownloader)
+ download_factory.register_format(UrlType.HTTP, HTTPDownloader)
+ download_factory.register_format(UrlType.SERVER, AyonServerDownloader)
+ return download_factory
diff --git a/common/openpype_common/distribution/file_handler.py b/common/ayon_common/distribution/file_handler.py
similarity index 66%
rename from common/openpype_common/distribution/file_handler.py
rename to common/ayon_common/distribution/file_handler.py
index e649f143e9..07f6962c98 100644
--- a/common/openpype_common/distribution/file_handler.py
+++ b/common/ayon_common/distribution/file_handler.py
@@ -9,21 +9,23 @@ import hashlib
import tarfile
import zipfile
+import requests
-USER_AGENT = "openpype"
+USER_AGENT = "AYON-launcher"
class RemoteFileHandler:
"""Download file from url, might be GDrive shareable link"""
- IMPLEMENTED_ZIP_FORMATS = ['zip', 'tar', 'tgz',
- 'tar.gz', 'tar.xz', 'tar.bz2']
+ IMPLEMENTED_ZIP_FORMATS = {
+ "zip", "tar", "tgz", "tar.gz", "tar.xz", "tar.bz2"
+ }
@staticmethod
def calculate_md5(fpath, chunk_size=10000):
md5 = hashlib.md5()
- with open(fpath, 'rb') as f:
- for chunk in iter(lambda: f.read(chunk_size), b''):
+ with open(fpath, "rb") as f:
+ for chunk in iter(lambda: f.read(chunk_size), b""):
md5.update(chunk)
return md5.hexdigest()
@@ -45,7 +47,7 @@ class RemoteFileHandler:
h = hashlib.sha256()
b = bytearray(128 * 1024)
mv = memoryview(b)
- with open(fpath, 'rb', buffering=0) as f:
+ with open(fpath, "rb", buffering=0) as f:
for n in iter(lambda: f.readinto(mv), 0):
h.update(mv[:n])
return h.hexdigest()
@@ -62,27 +64,32 @@ class RemoteFileHandler:
return True
if not hash_type:
raise ValueError("Provide hash type, md5 or sha256")
- if hash_type == 'md5':
+ if hash_type == "md5":
return RemoteFileHandler.check_md5(fpath, hash_value)
if hash_type == "sha256":
return RemoteFileHandler.check_sha256(fpath, hash_value)
@staticmethod
def download_url(
- url, root, filename=None,
- sha256=None, max_redirect_hops=3
+ url,
+ root,
+ filename=None,
+ max_redirect_hops=3,
+ headers=None
):
- """Download a file from a url and place it in root.
+ """Download a file from url and place it in root.
+
Args:
url (str): URL to download file from
root (str): Directory to place downloaded file in
filename (str, optional): Name to save the file under.
If None, use the basename of the URL
- sha256 (str, optional): sha256 checksum of the download.
- If None, do not check
- max_redirect_hops (int, optional): Maximum number of redirect
+ max_redirect_hops (Optional[int]): Maximum number of redirect
hops allowed
+ headers (Optional[dict[str, str]]): Additional required headers
+ - Authentication etc..
"""
+
root = os.path.expanduser(root)
if not filename:
filename = os.path.basename(url)
@@ -90,55 +97,44 @@ class RemoteFileHandler:
os.makedirs(root, exist_ok=True)
- # check if file is already present locally
- if RemoteFileHandler.check_integrity(fpath,
- sha256, hash_type="sha256"):
- print('Using downloaded and verified file: ' + fpath)
- return
-
# expand redirect chain if needed
- url = RemoteFileHandler._get_redirect_url(url,
- max_hops=max_redirect_hops)
+ url = RemoteFileHandler._get_redirect_url(
+ url, max_hops=max_redirect_hops, headers=headers)
# check if file is located on Google Drive
file_id = RemoteFileHandler._get_google_drive_file_id(url)
if file_id is not None:
return RemoteFileHandler.download_file_from_google_drive(
- file_id, root, filename, sha256)
+ file_id, root, filename)
# download the file
try:
- print('Downloading ' + url + ' to ' + fpath)
- RemoteFileHandler._urlretrieve(url, fpath)
- except (urllib.error.URLError, IOError) as e:
- if url[:5] == 'https':
- url = url.replace('https:', 'http:')
- print('Failed download. Trying https -> http instead.'
- ' Downloading ' + url + ' to ' + fpath)
- RemoteFileHandler._urlretrieve(url, fpath)
- else:
- raise e
+ print(f"Downloading {url} to {fpath}")
+ RemoteFileHandler._urlretrieve(url, fpath, headers=headers)
+ except (urllib.error.URLError, IOError) as exc:
+ if url[:5] != "https":
+ raise exc
- # check integrity of downloaded file
- if not RemoteFileHandler.check_integrity(fpath,
- sha256, hash_type="sha256"):
- raise RuntimeError("File not found or corrupted.")
+ url = url.replace("https:", "http:")
+ print((
+ "Failed download. Trying https -> http instead."
+ f" Downloading {url} to {fpath}"
+ ))
+ RemoteFileHandler._urlretrieve(url, fpath, headers=headers)
@staticmethod
- def download_file_from_google_drive(file_id, root,
- filename=None,
- sha256=None):
+ def download_file_from_google_drive(
+ file_id, root, filename=None
+ ):
"""Download a Google Drive file from and place it in root.
Args:
file_id (str): id of file to be downloaded
root (str): Directory to place downloaded file in
filename (str, optional): Name to save the file under.
If None, use the id of the file.
- sha256 (str, optional): sha256 checksum of the download.
- If None, do not check
"""
# Based on https://stackoverflow.com/questions/38511444/python-download-files-from-google-drive-using-url # noqa
- import requests
+
url = "https://docs.google.com/uc?export=download"
root = os.path.expanduser(root)
@@ -148,17 +144,16 @@ class RemoteFileHandler:
os.makedirs(root, exist_ok=True)
- if os.path.isfile(fpath) and RemoteFileHandler.check_integrity(
- fpath, sha256, hash_type="sha256"):
- print('Using downloaded and verified file: ' + fpath)
+ if os.path.isfile(fpath) and RemoteFileHandler.check_integrity(fpath):
+ print(f"Using downloaded and verified file: {fpath}")
else:
session = requests.Session()
- response = session.get(url, params={'id': file_id}, stream=True)
+ response = session.get(url, params={"id": file_id}, stream=True)
token = RemoteFileHandler._get_confirm_token(response)
if token:
- params = {'id': file_id, 'confirm': token}
+ params = {"id": file_id, "confirm": token}
response = session.get(url, params=params, stream=True)
response_content_generator = response.iter_content(32768)
@@ -186,28 +181,28 @@ class RemoteFileHandler:
destination_path = os.path.dirname(path)
_, archive_type = os.path.splitext(path)
- archive_type = archive_type.lstrip('.')
+ archive_type = archive_type.lstrip(".")
- if archive_type in ['zip']:
- print("Unzipping {}->{}".format(path, destination_path))
+ if archive_type in ["zip"]:
+ print(f"Unzipping {path}->{destination_path}")
zip_file = zipfile.ZipFile(path)
zip_file.extractall(destination_path)
zip_file.close()
elif archive_type in [
- 'tar', 'tgz', 'tar.gz', 'tar.xz', 'tar.bz2'
+ "tar", "tgz", "tar.gz", "tar.xz", "tar.bz2"
]:
- print("Unzipping {}->{}".format(path, destination_path))
- if archive_type == 'tar':
- tar_type = 'r:'
- elif archive_type.endswith('xz'):
- tar_type = 'r:xz'
- elif archive_type.endswith('gz'):
- tar_type = 'r:gz'
- elif archive_type.endswith('bz2'):
- tar_type = 'r:bz2'
+ print(f"Unzipping {path}->{destination_path}")
+ if archive_type == "tar":
+ tar_type = "r:"
+ elif archive_type.endswith("xz"):
+ tar_type = "r:xz"
+ elif archive_type.endswith("gz"):
+ tar_type = "r:gz"
+ elif archive_type.endswith("bz2"):
+ tar_type = "r:bz2"
else:
- tar_type = 'r:*'
+ tar_type = "r:*"
try:
tar_file = tarfile.open(path, tar_type)
except tarfile.ReadError:
@@ -216,29 +211,35 @@ class RemoteFileHandler:
tar_file.close()
@staticmethod
- def _urlretrieve(url, filename, chunk_size):
+ def _urlretrieve(url, filename, chunk_size=None, headers=None):
+ final_headers = {"User-Agent": USER_AGENT}
+ if headers:
+ final_headers.update(headers)
+
+ chunk_size = chunk_size or 8192
with open(filename, "wb") as fh:
with urllib.request.urlopen(
- urllib.request.Request(url,
- headers={"User-Agent": USER_AGENT})) \
- as response:
+ urllib.request.Request(url, headers=final_headers)
+ ) as response:
for chunk in iter(lambda: response.read(chunk_size), ""):
if not chunk:
break
fh.write(chunk)
@staticmethod
- def _get_redirect_url(url, max_hops):
+ def _get_redirect_url(url, max_hops, headers=None):
initial_url = url
- headers = {"Method": "HEAD", "User-Agent": USER_AGENT}
-
+ final_headers = {"Method": "HEAD", "User-Agent": USER_AGENT}
+ if headers:
+ final_headers.update(headers)
for _ in range(max_hops + 1):
with urllib.request.urlopen(
- urllib.request.Request(url, headers=headers)) as response:
+ urllib.request.Request(url, headers=final_headers)
+ ) as response:
if response.url == url or response.url is None:
return url
- url = response.url
+ return response.url
else:
raise RecursionError(
f"Request to {initial_url} exceeded {max_hops} redirects. "
@@ -248,7 +249,7 @@ class RemoteFileHandler:
@staticmethod
def _get_confirm_token(response):
for key, value in response.cookies.items():
- if key.startswith('download_warning'):
+ if key.startswith("download_warning"):
return value
# handle antivirus warning for big zips
diff --git a/common/ayon_common/distribution/tests/test_addon_distributtion.py b/common/ayon_common/distribution/tests/test_addon_distributtion.py
new file mode 100644
index 0000000000..3e7bd1bc6a
--- /dev/null
+++ b/common/ayon_common/distribution/tests/test_addon_distributtion.py
@@ -0,0 +1,248 @@
+import os
+import sys
+import copy
+import tempfile
+
+
+import attr
+import pytest
+
+current_dir = os.path.dirname(os.path.abspath(__file__))
+root_dir = os.path.abspath(os.path.join(current_dir, "..", "..", "..", ".."))
+sys.path.append(root_dir)
+
+from common.ayon_common.distribution.downloaders import (
+ DownloadFactory,
+ OSDownloader,
+ HTTPDownloader,
+)
+from common.ayon_common.distribution.control import (
+ AyonDistribution,
+ UpdateState,
+)
+from common.ayon_common.distribution.data_structures import (
+ AddonInfo,
+ UrlType,
+)
+
+
+@pytest.fixture
+def download_factory():
+ addon_downloader = DownloadFactory()
+ addon_downloader.register_format(UrlType.FILESYSTEM, OSDownloader)
+ addon_downloader.register_format(UrlType.HTTP, HTTPDownloader)
+
+ yield addon_downloader
+
+
+@pytest.fixture
+def http_downloader(download_factory):
+ yield download_factory.get_downloader(UrlType.HTTP.value)
+
+
+@pytest.fixture
+def temp_folder():
+ yield tempfile.mkdtemp(prefix="ayon_test_")
+
+
+@pytest.fixture
+def sample_bundles():
+ yield {
+ "bundles": [
+ {
+ "name": "TestBundle",
+ "createdAt": "2023-06-29T00:00:00.0+00:00",
+ "installerVersion": None,
+ "addons": {
+ "slack": "1.0.0"
+ },
+ "dependencyPackages": {},
+ "isProduction": True,
+ "isStaging": False
+ }
+ ],
+ "productionBundle": "TestBundle",
+ "stagingBundle": None
+ }
+
+
+@pytest.fixture
+def sample_addon_info():
+ yield {
+ "name": "slack",
+ "title": "Slack addon",
+ "versions": {
+ "1.0.0": {
+ "hasSettings": True,
+ "hasSiteSettings": False,
+ "clientPyproject": {
+ "tool": {
+ "poetry": {
+ "dependencies": {
+ "nxtools": "^1.6",
+ "orjson": "^3.6.7",
+ "typer": "^0.4.1",
+ "email-validator": "^1.1.3",
+ "python": "^3.10",
+ "fastapi": "^0.73.0"
+ }
+ }
+ }
+ },
+ "clientSourceInfo": [
+ {
+ "type": "http",
+ "path": "https://drive.google.com/file/d/1TcuV8c2OV8CcbPeWi7lxOdqWsEqQNPYy/view?usp=sharing", # noqa
+ "filename": "dummy.zip"
+ },
+ {
+ "type": "filesystem",
+ "path": {
+ "windows": "P:/sources/some_file.zip",
+ "linux": "/mnt/srv/sources/some_file.zip",
+ "darwin": "/Volumes/srv/sources/some_file.zip"
+ }
+ }
+ ],
+ "frontendScopes": {
+ "project": {
+ "sidebar": "hierarchy",
+ }
+ },
+ "hash": "4be25eb6215e91e5894d3c5475aeb1e379d081d3f5b43b4ee15b0891cf5f5658" # noqa
+ }
+ },
+ "description": ""
+ }
+
+
+def test_register(printer):
+ download_factory = DownloadFactory()
+
+ assert len(download_factory._downloaders) == 0, "Contains registered"
+
+ download_factory.register_format(UrlType.FILESYSTEM, OSDownloader)
+ assert len(download_factory._downloaders) == 1, "Should contain one"
+
+
+def test_get_downloader(printer, download_factory):
+ assert download_factory.get_downloader(UrlType.FILESYSTEM.value), "Should find" # noqa
+
+ with pytest.raises(ValueError):
+ download_factory.get_downloader("unknown"), "Shouldn't find"
+
+
+def test_addon_info(printer, sample_addon_info):
+ """Tests parsing of expected payload from v4 server into AadonInfo."""
+ valid_minimum = {
+ "name": "slack",
+ "versions": {
+ "1.0.0": {
+ "clientSourceInfo": [
+ {
+ "type": "filesystem",
+ "path": {
+ "windows": "P:/sources/some_file.zip",
+ "linux": "/mnt/srv/sources/some_file.zip",
+ "darwin": "/Volumes/srv/sources/some_file.zip"
+ }
+ }
+ ]
+ }
+ }
+ }
+
+ assert AddonInfo.from_dict(valid_minimum), "Missing required fields"
+
+ addon = AddonInfo.from_dict(sample_addon_info)
+ assert addon, "Should be created"
+ assert addon.name == "slack", "Incorrect name"
+ assert "1.0.0" in addon.versions, "Version is not in versions"
+
+ with pytest.raises(TypeError):
+ assert addon["name"], "Dict approach not implemented"
+
+ addon_as_dict = attr.asdict(addon)
+ assert addon_as_dict["name"], "Dict approach should work"
+
+
+def _get_dist_item(dist_items, name, version):
+ final_dist_info = next(
+ (
+ dist_info
+ for dist_info in dist_items
+ if (
+ dist_info["addon_name"] == name
+ and dist_info["addon_version"] == version
+ )
+ ),
+ {}
+ )
+ return final_dist_info["dist_item"]
+
+
+def test_update_addon_state(
+ printer, sample_addon_info, temp_folder, download_factory, sample_bundles
+):
+ """Tests possible cases of addon update."""
+
+ addon_version = list(sample_addon_info["versions"])[0]
+ broken_addon_info = copy.deepcopy(sample_addon_info)
+
+ # Cause crash because of invalid hash
+ broken_addon_info["versions"][addon_version]["hash"] = "brokenhash"
+ distribution = AyonDistribution(
+ addon_dirpath=temp_folder,
+ dependency_dirpath=temp_folder,
+ dist_factory=download_factory,
+ addons_info=[broken_addon_info],
+ dependency_packages_info=[],
+ bundles_info=sample_bundles
+ )
+ distribution.distribute()
+ dist_items = distribution.get_addon_dist_items()
+ slack_dist_item = _get_dist_item(
+ dist_items,
+ sample_addon_info["name"],
+ addon_version
+ )
+ slack_state = slack_dist_item.state
+ assert slack_state == UpdateState.UPDATE_FAILED, (
+ "Update should have failed because of wrong hash")
+
+ # Fix cache and validate if was updated
+ distribution = AyonDistribution(
+ addon_dirpath=temp_folder,
+ dependency_dirpath=temp_folder,
+ dist_factory=download_factory,
+ addons_info=[sample_addon_info],
+ dependency_packages_info=[],
+ bundles_info=sample_bundles
+ )
+ distribution.distribute()
+ dist_items = distribution.get_addon_dist_items()
+ slack_dist_item = _get_dist_item(
+ dist_items,
+ sample_addon_info["name"],
+ addon_version
+ )
+ assert slack_dist_item.state == UpdateState.UPDATED, (
+ "Addon should have been updated")
+
+ # Is UPDATED without calling distribute
+ distribution = AyonDistribution(
+ addon_dirpath=temp_folder,
+ dependency_dirpath=temp_folder,
+ dist_factory=download_factory,
+ addons_info=[sample_addon_info],
+ dependency_packages_info=[],
+ bundles_info=sample_bundles
+ )
+ dist_items = distribution.get_addon_dist_items()
+ slack_dist_item = _get_dist_item(
+ dist_items,
+ sample_addon_info["name"],
+ addon_version
+ )
+ assert slack_dist_item.state == UpdateState.UPDATED, (
+ "Addon should already exist")
diff --git a/common/ayon_common/distribution/ui/missing_bundle_window.py b/common/ayon_common/distribution/ui/missing_bundle_window.py
new file mode 100644
index 0000000000..ae7a6a2976
--- /dev/null
+++ b/common/ayon_common/distribution/ui/missing_bundle_window.py
@@ -0,0 +1,146 @@
+import sys
+
+from qtpy import QtWidgets, QtGui
+
+from ayon_common import is_staging_enabled
+from ayon_common.resources import (
+ get_icon_path,
+ load_stylesheet,
+)
+from ayon_common.ui_utils import get_qt_app
+
+
+class MissingBundleWindow(QtWidgets.QDialog):
+ default_width = 410
+ default_height = 170
+
+ def __init__(
+ self, url=None, bundle_name=None, use_staging=None, parent=None
+ ):
+ super().__init__(parent)
+
+ icon_path = get_icon_path()
+ icon = QtGui.QIcon(icon_path)
+ self.setWindowIcon(icon)
+ self.setWindowTitle("Missing Bundle")
+
+ self._url = url
+ self._bundle_name = bundle_name
+ self._use_staging = use_staging
+ self._first_show = True
+
+ info_label = QtWidgets.QLabel("", self)
+ info_label.setWordWrap(True)
+
+ btns_widget = QtWidgets.QWidget(self)
+ confirm_btn = QtWidgets.QPushButton("Exit", btns_widget)
+
+ btns_layout = QtWidgets.QHBoxLayout(btns_widget)
+ btns_layout.setContentsMargins(0, 0, 0, 0)
+ btns_layout.addStretch(1)
+ btns_layout.addWidget(confirm_btn, 0)
+
+ main_layout = QtWidgets.QVBoxLayout(self)
+ main_layout.addWidget(info_label, 0)
+ main_layout.addStretch(1)
+ main_layout.addWidget(btns_widget, 0)
+
+ confirm_btn.clicked.connect(self._on_confirm_click)
+
+ self._info_label = info_label
+ self._confirm_btn = confirm_btn
+
+ self._update_label()
+
+ def set_url(self, url):
+ if url == self._url:
+ return
+ self._url = url
+ self._update_label()
+
+ def set_bundle_name(self, bundle_name):
+ if bundle_name == self._bundle_name:
+ return
+ self._bundle_name = bundle_name
+ self._update_label()
+
+ def set_use_staging(self, use_staging):
+ if self._use_staging == use_staging:
+ return
+ self._use_staging = use_staging
+ self._update_label()
+
+ def showEvent(self, event):
+ super().showEvent(event)
+ if self._first_show:
+ self._first_show = False
+ self._on_first_show()
+ self._recalculate_sizes()
+
+ def resizeEvent(self, event):
+ super().resizeEvent(event)
+ self._recalculate_sizes()
+
+ def _recalculate_sizes(self):
+ hint = self._confirm_btn.sizeHint()
+ new_width = max((hint.width(), hint.height() * 3))
+ self._confirm_btn.setMinimumWidth(new_width)
+
+ def _on_first_show(self):
+ self.setStyleSheet(load_stylesheet())
+ self.resize(self.default_width, self.default_height)
+
+ def _on_confirm_click(self):
+ self.accept()
+ self.close()
+
+ def _update_label(self):
+ self._info_label.setText(self._get_label())
+
+ def _get_label(self):
+ url_part = f" {self._url}" if self._url else ""
+
+ if self._bundle_name:
+ return (
+ f"Requested release bundle {self._bundle_name}"
+ f" is not available on server{url_part}."
+ "
Try to restart AYON desktop launcher. Please"
+ " contact your administrator if issue persist."
+ )
+ mode = "staging" if self._use_staging else "production"
+ return (
+ f"No release bundle is set as {mode} on the AYON"
+ f" server{url_part} so there is nothing to launch."
+ "
Please contact your administrator"
+ " to resolve the issue."
+ )
+
+
+def main():
+ """Show message that server does not have set bundle to use.
+
+ It is possible to pass url as argument to show it in the message. To use
+ this feature, pass `--url ` as argument to this script.
+ """
+
+ url = None
+ bundle_name = None
+ if "--url" in sys.argv:
+ url_index = sys.argv.index("--url") + 1
+ if url_index < len(sys.argv):
+ url = sys.argv[url_index]
+
+ if "--bundle" in sys.argv:
+ bundle_index = sys.argv.index("--bundle") + 1
+ if bundle_index < len(sys.argv):
+ bundle_name = sys.argv[bundle_index]
+
+ use_staging = is_staging_enabled()
+ app = get_qt_app()
+ window = MissingBundleWindow(url, bundle_name, use_staging)
+ window.show()
+ app.exec_()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/common/ayon_common/distribution/utils.py b/common/ayon_common/distribution/utils.py
new file mode 100644
index 0000000000..a8b755707a
--- /dev/null
+++ b/common/ayon_common/distribution/utils.py
@@ -0,0 +1,90 @@
+import os
+import subprocess
+
+from ayon_common.utils import get_ayon_appdirs, get_ayon_launch_args
+
+
+def get_local_dir(*subdirs):
+ """Get product directory in user's home directory.
+
+ Each user on machine have own local directory where are downloaded updates,
+ addons etc.
+
+ Returns:
+ str: Path to product local directory.
+ """
+
+ if not subdirs:
+ raise ValueError("Must fill dir_name if nothing else provided!")
+
+ local_dir = get_ayon_appdirs(*subdirs)
+ if not os.path.isdir(local_dir):
+ try:
+ os.makedirs(local_dir)
+ except Exception: # TODO fix exception
+ raise RuntimeError(f"Cannot create {local_dir}")
+
+ return local_dir
+
+
+def get_addons_dir():
+ """Directory where addon packages are stored.
+
+ Path to addons is defined using python module 'appdirs' which
+
+ The path is stored into environment variable 'AYON_ADDONS_DIR'.
+ Value of environment variable can be overriden, but we highly recommended
+ to use that option only for development purposes.
+
+ Returns:
+ str: Path to directory where addons should be downloaded.
+ """
+
+ addons_dir = os.environ.get("AYON_ADDONS_DIR")
+ if not addons_dir:
+ addons_dir = get_local_dir("addons")
+ os.environ["AYON_ADDONS_DIR"] = addons_dir
+ return addons_dir
+
+
+def get_dependencies_dir():
+ """Directory where dependency packages are stored.
+
+ Path to addons is defined using python module 'appdirs' which
+
+ The path is stored into environment variable 'AYON_DEPENDENCIES_DIR'.
+ Value of environment variable can be overriden, but we highly recommended
+ to use that option only for development purposes.
+
+ Returns:
+ str: Path to directory where dependency packages should be downloaded.
+ """
+
+ dependencies_dir = os.environ.get("AYON_DEPENDENCIES_DIR")
+ if not dependencies_dir:
+ dependencies_dir = get_local_dir("dependency_packages")
+ os.environ["AYON_DEPENDENCIES_DIR"] = dependencies_dir
+ return dependencies_dir
+
+
+def show_missing_bundle_information(url, bundle_name=None):
+ """Show missing bundle information window.
+
+ This function should be called when server does not have set bundle for
+ production or staging, or when bundle that should be used is not available
+ on server.
+
+ Using subprocess to show the dialog. Is blocking and is waiting until
+ dialog is closed.
+
+ Args:
+ url (str): Server url where bundle is not set.
+ bundle_name (Optional[str]): Name of bundle that was not found.
+ """
+
+ ui_dir = os.path.join(os.path.dirname(__file__), "ui")
+ script_path = os.path.join(ui_dir, "missing_bundle_window.py")
+ args = get_ayon_launch_args(script_path, "--skip-bootstrap", "--url", url)
+ if bundle_name:
+ args.extend(["--bundle", bundle_name])
+ subprocess.call(args)
diff --git a/common/ayon_common/resources/AYON.icns b/common/ayon_common/resources/AYON.icns
new file mode 100644
index 0000000000..2ec66cf3e0
Binary files /dev/null and b/common/ayon_common/resources/AYON.icns differ
diff --git a/common/ayon_common/resources/AYON.ico b/common/ayon_common/resources/AYON.ico
new file mode 100644
index 0000000000..e0ec3292f8
Binary files /dev/null and b/common/ayon_common/resources/AYON.ico differ
diff --git a/common/ayon_common/resources/AYON.png b/common/ayon_common/resources/AYON.png
new file mode 100644
index 0000000000..ed13aeea52
Binary files /dev/null and b/common/ayon_common/resources/AYON.png differ
diff --git a/common/ayon_common/resources/AYON_staging.png b/common/ayon_common/resources/AYON_staging.png
new file mode 100644
index 0000000000..75dadfd56c
Binary files /dev/null and b/common/ayon_common/resources/AYON_staging.png differ
diff --git a/common/ayon_common/resources/__init__.py b/common/ayon_common/resources/__init__.py
new file mode 100644
index 0000000000..2b516feff3
--- /dev/null
+++ b/common/ayon_common/resources/__init__.py
@@ -0,0 +1,25 @@
+import os
+
+from ayon_common.utils import is_staging_enabled
+
+RESOURCES_DIR = os.path.dirname(os.path.abspath(__file__))
+
+
+def get_resource_path(*args):
+ path_items = list(args)
+ path_items.insert(0, RESOURCES_DIR)
+ return os.path.sep.join(path_items)
+
+
+def get_icon_path():
+ if is_staging_enabled():
+ return get_resource_path("AYON_staging.png")
+ return get_resource_path("AYON.png")
+
+
+def load_stylesheet():
+ stylesheet_path = get_resource_path("stylesheet.css")
+
+ with open(stylesheet_path, "r") as stream:
+ content = stream.read()
+ return content
diff --git a/common/ayon_common/resources/edit.png b/common/ayon_common/resources/edit.png
new file mode 100644
index 0000000000..a5a07998a6
Binary files /dev/null and b/common/ayon_common/resources/edit.png differ
diff --git a/common/ayon_common/resources/eye.png b/common/ayon_common/resources/eye.png
new file mode 100644
index 0000000000..5a683e2974
Binary files /dev/null and b/common/ayon_common/resources/eye.png differ
diff --git a/common/ayon_common/resources/stylesheet.css b/common/ayon_common/resources/stylesheet.css
new file mode 100644
index 0000000000..01e664e9e8
--- /dev/null
+++ b/common/ayon_common/resources/stylesheet.css
@@ -0,0 +1,84 @@
+* {
+ font-size: 10pt;
+ font-family: "Noto Sans";
+ font-weight: 450;
+ outline: none;
+}
+
+QWidget {
+ color: #D3D8DE;
+ background: #2C313A;
+ border-radius: 0px;
+}
+
+QWidget:disabled {
+ color: #5b6779;
+}
+
+QLabel {
+ background: transparent;
+}
+
+QPushButton {
+ text-align:center center;
+ border: 0px solid transparent;
+ border-radius: 0.2em;
+ padding: 3px 5px 3px 5px;
+ background: #434a56;
+}
+
+QPushButton:hover {
+ background: rgba(168, 175, 189, 0.3);
+ color: #F0F2F5;
+}
+
+QPushButton:pressed {}
+
+QPushButton:disabled {
+ background: #434a56;
+}
+
+QLineEdit {
+ border: 1px solid #373D48;
+ border-radius: 0.3em;
+ background: #21252B;
+ padding: 0.1em;
+}
+
+QLineEdit:disabled {
+ background: #2C313A;
+}
+QLineEdit:hover {
+ border-color: rgba(168, 175, 189, .3);
+}
+QLineEdit:focus {
+ border-color: rgb(92, 173, 214);
+}
+
+QLineEdit[state="invalid"] {
+ border-color: #AA5050;
+}
+
+#Separator {
+ background: rgba(75, 83, 98, 127);
+}
+
+#PasswordBtn {
+ border: none;
+ padding: 0.1em;
+ background: transparent;
+}
+
+#PasswordBtn:hover {
+ background: #434a56;
+}
+
+#LikeDisabledInput {
+ background: #2C313A;
+}
+#LikeDisabledInput:hover {
+ border-color: #373D48;
+}
+#LikeDisabledInput:focus {
+ border-color: #373D48;
+}
diff --git a/common/ayon_common/ui_utils.py b/common/ayon_common/ui_utils.py
new file mode 100644
index 0000000000..a3894d0d9c
--- /dev/null
+++ b/common/ayon_common/ui_utils.py
@@ -0,0 +1,36 @@
+import sys
+from qtpy import QtWidgets, QtCore
+
+
+def set_style_property(widget, property_name, property_value):
+ """Set widget's property that may affect style.
+
+ Style of widget is polished if current property value is different.
+ """
+
+ cur_value = widget.property(property_name)
+ if cur_value == property_value:
+ return
+ widget.setProperty(property_name, property_value)
+ widget.style().polish(widget)
+
+
+def get_qt_app():
+ app = QtWidgets.QApplication.instance()
+ if app is not None:
+ return app
+
+ for attr_name in (
+ "AA_EnableHighDpiScaling",
+ "AA_UseHighDpiPixmaps",
+ ):
+ attr = getattr(QtCore.Qt, attr_name, None)
+ if attr is not None:
+ QtWidgets.QApplication.setAttribute(attr)
+
+ if hasattr(QtWidgets.QApplication, "setHighDpiScaleFactorRoundingPolicy"):
+ QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy(
+ QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
+ )
+
+ return QtWidgets.QApplication(sys.argv)
diff --git a/common/ayon_common/utils.py b/common/ayon_common/utils.py
new file mode 100644
index 0000000000..d0638e552f
--- /dev/null
+++ b/common/ayon_common/utils.py
@@ -0,0 +1,90 @@
+import os
+import sys
+import appdirs
+
+IS_BUILT_APPLICATION = getattr(sys, "frozen", False)
+
+
+def get_ayon_appdirs(*args):
+ """Local app data directory of AYON client.
+
+ Args:
+ *args (Iterable[str]): Subdirectories/files in local app data dir.
+
+ Returns:
+ str: Path to directory/file in local app data dir.
+ """
+
+ return os.path.join(
+ appdirs.user_data_dir("ayon", "ynput"),
+ *args
+ )
+
+
+def is_staging_enabled():
+ """Check if staging is enabled.
+
+ Returns:
+ bool: True if staging is enabled.
+ """
+
+ return os.getenv("AYON_USE_STAGING") == "1"
+
+
+def _create_local_site_id():
+ """Create a local site identifier.
+
+ Returns:
+ str: Randomly generated site id.
+ """
+
+ from coolname import generate_slug
+
+ new_id = generate_slug(3)
+
+ print("Created local site id \"{}\"".format(new_id))
+
+ return new_id
+
+
+def get_local_site_id():
+ """Get local site identifier.
+
+ Site id is created if does not exist yet.
+
+ Returns:
+ str: Site id.
+ """
+
+ # used for background syncing
+ site_id = os.environ.get("AYON_SITE_ID")
+ if site_id:
+ return site_id
+
+ site_id_path = get_ayon_appdirs("site_id")
+ if os.path.exists(site_id_path):
+ with open(site_id_path, "r") as stream:
+ site_id = stream.read()
+
+ if not site_id:
+ site_id = _create_local_site_id()
+ with open(site_id_path, "w") as stream:
+ stream.write(site_id)
+ return site_id
+
+
+def get_ayon_launch_args(*args):
+ """Launch arguments that can be used to launch ayon process.
+
+ Args:
+ *args (str): Additional arguments.
+
+ Returns:
+ list[str]: Launch arguments.
+ """
+
+ output = [sys.executable]
+ if not IS_BUILT_APPLICATION:
+ output.append(sys.argv[0])
+ output.extend(args)
+ return output
diff --git a/common/openpype_common/distribution/addon_distribution.py b/common/openpype_common/distribution/addon_distribution.py
deleted file mode 100644
index 5e48639dec..0000000000
--- a/common/openpype_common/distribution/addon_distribution.py
+++ /dev/null
@@ -1,208 +0,0 @@
-import os
-from enum import Enum
-from abc import abstractmethod
-import attr
-import logging
-import requests
-import platform
-import shutil
-
-from .file_handler import RemoteFileHandler
-from .addon_info import AddonInfo
-
-
-class UpdateState(Enum):
- EXISTS = "exists"
- UPDATED = "updated"
- FAILED = "failed"
-
-
-class AddonDownloader:
- log = logging.getLogger(__name__)
-
- def __init__(self):
- self._downloaders = {}
-
- def register_format(self, downloader_type, downloader):
- self._downloaders[downloader_type.value] = downloader
-
- def get_downloader(self, downloader_type):
- downloader = self._downloaders.get(downloader_type)
- if not downloader:
- raise ValueError(f"{downloader_type} not implemented")
- return downloader()
-
- @classmethod
- @abstractmethod
- def download(cls, source, destination):
- """Returns url to downloaded addon zip file.
-
- Args:
- source (dict): {type:"http", "url":"https://} ...}
- destination (str): local folder to unzip
- Returns:
- (str) local path to addon zip file
- """
- pass
-
- @classmethod
- def check_hash(cls, addon_path, addon_hash):
- """Compares 'hash' of downloaded 'addon_url' file.
-
- Args:
- addon_path (str): local path to addon zip file
- addon_hash (str): sha256 hash of zip file
- Raises:
- ValueError if hashes doesn't match
- """
- if not os.path.exists(addon_path):
- raise ValueError(f"{addon_path} doesn't exist.")
- if not RemoteFileHandler.check_integrity(addon_path,
- addon_hash,
- hash_type="sha256"):
- raise ValueError(f"{addon_path} doesn't match expected hash.")
-
- @classmethod
- def unzip(cls, addon_zip_path, destination):
- """Unzips local 'addon_zip_path' to 'destination'.
-
- Args:
- addon_zip_path (str): local path to addon zip file
- destination (str): local folder to unzip
- """
- RemoteFileHandler.unzip(addon_zip_path, destination)
- os.remove(addon_zip_path)
-
- @classmethod
- def remove(cls, addon_url):
- pass
-
-
-class OSAddonDownloader(AddonDownloader):
-
- @classmethod
- def download(cls, source, destination):
- # OS doesnt need to download, unzip directly
- addon_url = source["path"].get(platform.system().lower())
- if not os.path.exists(addon_url):
- raise ValueError("{} is not accessible".format(addon_url))
- return addon_url
-
-
-class HTTPAddonDownloader(AddonDownloader):
- CHUNK_SIZE = 100000
-
- @classmethod
- def download(cls, source, destination):
- source_url = source["url"]
- cls.log.debug(f"Downloading {source_url} to {destination}")
- file_name = os.path.basename(destination)
- _, ext = os.path.splitext(file_name)
- if (ext.replace(".", '') not
- in set(RemoteFileHandler.IMPLEMENTED_ZIP_FORMATS)):
- file_name += ".zip"
- RemoteFileHandler.download_url(source_url,
- destination,
- filename=file_name)
-
- return os.path.join(destination, file_name)
-
-
-def get_addons_info(server_endpoint):
- """Returns list of addon information from Server"""
- # TODO temp
- # addon_info = AddonInfo(
- # **{"name": "openpype_slack",
- # "version": "1.0.0",
- # "addon_url": "c:/projects/openpype_slack_1.0.0.zip",
- # "type": UrlType.FILESYSTEM,
- # "hash": "4be25eb6215e91e5894d3c5475aeb1e379d081d3f5b43b4ee15b0891cf5f5658"}) # noqa
- #
- # http_addon = AddonInfo(
- # **{"name": "openpype_slack",
- # "version": "1.0.0",
- # "addon_url": "https://drive.google.com/file/d/1TcuV8c2OV8CcbPeWi7lxOdqWsEqQNPYy/view?usp=sharing", # noqa
- # "type": UrlType.HTTP,
- # "hash": "4be25eb6215e91e5894d3c5475aeb1e379d081d3f5b43b4ee15b0891cf5f5658"}) # noqa
-
- response = requests.get(server_endpoint)
- if not response.ok:
- raise Exception(response.text)
-
- addons_info = []
- for addon in response.json():
- addons_info.append(AddonInfo(**addon))
- return addons_info
-
-
-def update_addon_state(addon_infos, destination_folder, factory,
- log=None):
- """Loops through all 'addon_infos', compares local version, unzips.
-
- Loops through server provided list of dictionaries with information about
- available addons. Looks if each addon is already present and deployed.
- If isn't, addon zip gets downloaded and unzipped into 'destination_folder'.
- Args:
- addon_infos (list of AddonInfo)
- destination_folder (str): local path
- factory (AddonDownloader): factory to get appropriate downloader per
- addon type
- log (logging.Logger)
- Returns:
- (dict): {"addon_full_name": UpdateState.value
- (eg. "exists"|"updated"|"failed")
- """
- if not log:
- log = logging.getLogger(__name__)
-
- download_states = {}
- for addon in addon_infos:
- full_name = "{}_{}".format(addon.name, addon.version)
- addon_dest = os.path.join(destination_folder, full_name)
-
- if os.path.isdir(addon_dest):
- log.debug(f"Addon version folder {addon_dest} already exists.")
- download_states[full_name] = UpdateState.EXISTS.value
- continue
-
- for source in addon.sources:
- download_states[full_name] = UpdateState.FAILED.value
- try:
- downloader = factory.get_downloader(source.type)
- zip_file_path = downloader.download(attr.asdict(source),
- addon_dest)
- downloader.check_hash(zip_file_path, addon.hash)
- downloader.unzip(zip_file_path, addon_dest)
- download_states[full_name] = UpdateState.UPDATED.value
- break
- except Exception:
- log.warning(f"Error happened during updating {addon.name}",
- exc_info=True)
- if os.path.isdir(addon_dest):
- log.debug(f"Cleaning {addon_dest}")
- shutil.rmtree(addon_dest)
-
- return download_states
-
-
-def check_addons(server_endpoint, addon_folder, downloaders):
- """Main entry point to compare existing addons with those on server.
-
- Args:
- server_endpoint (str): url to v4 server endpoint
- addon_folder (str): local dir path for addons
- downloaders (AddonDownloader): factory of downloaders
-
- Raises:
- (RuntimeError) if any addon failed update
- """
- addons_info = get_addons_info(server_endpoint)
- result = update_addon_state(addons_info,
- addon_folder,
- downloaders)
- if UpdateState.FAILED.value in result.values():
- raise RuntimeError(f"Unable to update some addons {result}")
-
-
-def cli(*args):
- raise NotImplementedError
diff --git a/common/openpype_common/distribution/addon_info.py b/common/openpype_common/distribution/addon_info.py
deleted file mode 100644
index 00ece11f3b..0000000000
--- a/common/openpype_common/distribution/addon_info.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import attr
-from enum import Enum
-
-
-class UrlType(Enum):
- HTTP = "http"
- GIT = "git"
- FILESYSTEM = "filesystem"
-
-
-@attr.s
-class MultiPlatformPath(object):
- windows = attr.ib(default=None)
- linux = attr.ib(default=None)
- darwin = attr.ib(default=None)
-
-
-@attr.s
-class AddonSource(object):
- type = attr.ib()
-
-
-@attr.s
-class LocalAddonSource(AddonSource):
- path = attr.ib(default=attr.Factory(MultiPlatformPath))
-
-
-@attr.s
-class WebAddonSource(AddonSource):
- url = attr.ib(default=None)
-
-
-@attr.s
-class VersionData(object):
- version_data = attr.ib(default=None)
-
-
-@attr.s
-class AddonInfo(object):
- """Object matching json payload from Server"""
- name = attr.ib()
- version = attr.ib()
- title = attr.ib(default=None)
- sources = attr.ib(default=attr.Factory(dict))
- hash = attr.ib(default=None)
- description = attr.ib(default=None)
- license = attr.ib(default=None)
- authors = attr.ib(default=None)
-
- @classmethod
- def from_dict(cls, data):
- sources = []
-
- production_version = data.get("productionVersion")
- if not production_version:
- return
-
- # server payload contains info about all versions
- # active addon must have 'productionVersion' and matching version info
- version_data = data.get("versions", {})[production_version]
-
- for source in version_data.get("clientSourceInfo", []):
- if source.get("type") == UrlType.FILESYSTEM.value:
- source_addon = LocalAddonSource(type=source["type"],
- path=source["path"])
- if source.get("type") == UrlType.HTTP.value:
- source_addon = WebAddonSource(type=source["type"],
- url=source["url"])
-
- sources.append(source_addon)
-
- return cls(name=data.get("name"),
- version=production_version,
- sources=sources,
- hash=data.get("hash"),
- description=data.get("description"),
- title=data.get("title"),
- license=data.get("license"),
- authors=data.get("authors"))
-
diff --git a/common/openpype_common/distribution/tests/test_addon_distributtion.py b/common/openpype_common/distribution/tests/test_addon_distributtion.py
deleted file mode 100644
index 765ea0596a..0000000000
--- a/common/openpype_common/distribution/tests/test_addon_distributtion.py
+++ /dev/null
@@ -1,167 +0,0 @@
-import pytest
-import attr
-import tempfile
-
-from common.openpype_common.distribution.addon_distribution import (
- AddonDownloader,
- OSAddonDownloader,
- HTTPAddonDownloader,
- AddonInfo,
- update_addon_state,
- UpdateState
-)
-from common.openpype_common.distribution.addon_info import UrlType
-
-
-@pytest.fixture
-def addon_downloader():
- addon_downloader = AddonDownloader()
- addon_downloader.register_format(UrlType.FILESYSTEM, OSAddonDownloader)
- addon_downloader.register_format(UrlType.HTTP, HTTPAddonDownloader)
-
- yield addon_downloader
-
-
-@pytest.fixture
-def http_downloader(addon_downloader):
- yield addon_downloader.get_downloader(UrlType.HTTP.value)
-
-
-@pytest.fixture
-def temp_folder():
- yield tempfile.mkdtemp()
-
-
-@pytest.fixture
-def sample_addon_info():
- addon_info = {
- "versions": {
- "1.0.0": {
- "clientPyproject": {
- "tool": {
- "poetry": {
- "dependencies": {
- "nxtools": "^1.6",
- "orjson": "^3.6.7",
- "typer": "^0.4.1",
- "email-validator": "^1.1.3",
- "python": "^3.10",
- "fastapi": "^0.73.0"
- }
- }
- }
- },
- "hasSettings": True,
- "clientSourceInfo": [
- {
- "type": "http",
- "url": "https://drive.google.com/file/d/1TcuV8c2OV8CcbPeWi7lxOdqWsEqQNPYy/view?usp=sharing" # noqa
- },
- {
- "type": "filesystem",
- "path": {
- "windows": ["P:/sources/some_file.zip",
- "W:/sources/some_file.zip"], # noqa
- "linux": ["/mnt/srv/sources/some_file.zip"],
- "darwin": ["/Volumes/srv/sources/some_file.zip"]
- }
- }
- ],
- "frontendScopes": {
- "project": {
- "sidebar": "hierarchy"
- }
- }
- }
- },
- "description": "",
- "title": "Slack addon",
- "name": "openpype_slack",
- "productionVersion": "1.0.0",
- "hash": "4be25eb6215e91e5894d3c5475aeb1e379d081d3f5b43b4ee15b0891cf5f5658" # noqa
- }
- yield addon_info
-
-
-def test_register(printer):
- addon_downloader = AddonDownloader()
-
- assert len(addon_downloader._downloaders) == 0, "Contains registered"
-
- addon_downloader.register_format(UrlType.FILESYSTEM, OSAddonDownloader)
- assert len(addon_downloader._downloaders) == 1, "Should contain one"
-
-
-def test_get_downloader(printer, addon_downloader):
- assert addon_downloader.get_downloader(UrlType.FILESYSTEM.value), "Should find" # noqa
-
- with pytest.raises(ValueError):
- addon_downloader.get_downloader("unknown"), "Shouldn't find"
-
-
-def test_addon_info(printer, sample_addon_info):
- """Tests parsing of expected payload from v4 server into AadonInfo."""
- valid_minimum = {
- "name": "openpype_slack",
- "productionVersion": "1.0.0",
- "versions": {
- "1.0.0": {
- "clientSourceInfo": [
- {
- "type": "filesystem",
- "path": {
- "windows": [
- "P:/sources/some_file.zip",
- "W:/sources/some_file.zip"],
- "linux": [
- "/mnt/srv/sources/some_file.zip"],
- "darwin": [
- "/Volumes/srv/sources/some_file.zip"] # noqa
- }
- }
- ]
- }
- }
- }
-
- assert AddonInfo.from_dict(valid_minimum), "Missing required fields"
-
- valid_minimum["versions"].pop("1.0.0")
- with pytest.raises(KeyError):
- assert not AddonInfo.from_dict(valid_minimum), "Must fail without version data" # noqa
-
- valid_minimum.pop("productionVersion")
- assert not AddonInfo.from_dict(
- valid_minimum), "none if not productionVersion" # noqa
-
- addon = AddonInfo.from_dict(sample_addon_info)
- assert addon, "Should be created"
- assert addon.name == "openpype_slack", "Incorrect name"
- assert addon.version == "1.0.0", "Incorrect version"
-
- with pytest.raises(TypeError):
- assert addon["name"], "Dict approach not implemented"
-
- addon_as_dict = attr.asdict(addon)
- assert addon_as_dict["name"], "Dict approach should work"
-
-
-def test_update_addon_state(printer, sample_addon_info,
- temp_folder, addon_downloader):
- """Tests possible cases of addon update."""
- addon_info = AddonInfo.from_dict(sample_addon_info)
- orig_hash = addon_info.hash
-
- addon_info.hash = "brokenhash"
- result = update_addon_state([addon_info], temp_folder, addon_downloader)
- assert result["openpype_slack_1.0.0"] == UpdateState.FAILED.value, \
- "Update should failed because of wrong hash"
-
- addon_info.hash = orig_hash
- result = update_addon_state([addon_info], temp_folder, addon_downloader)
- assert result["openpype_slack_1.0.0"] == UpdateState.UPDATED.value, \
- "Addon should have been updated"
-
- result = update_addon_state([addon_info], temp_folder, addon_downloader)
- assert result["openpype_slack_1.0.0"] == UpdateState.EXISTS.value, \
- "Addon should already exist"
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000000..102da990aa
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,74 @@
+API Documentation
+=================
+
+This documents the way how to build and modify API documentation using Sphinx and AutoAPI. Ground for documentation
+should be directly in sources - in docstrings and markdowns. Sphinx and AutoAPI will crawl over them and generate
+RST files that are in turn used to generate HTML documentation. For docstrings we prefer "Napoleon" or "Google" style
+docstrings, but RST is also acceptable mainly in cases where you need to use Sphinx directives.
+
+Using only docstrings is not really viable as some documentation should be done on higher level - like overview of
+some modules/functionality and so on. This should be done directly in RST files and committed to repository.
+
+Configuration
+-------------
+Configuration is done in `/docs/source/conf.py`. The most important settings are:
+
+- `autodoc_mock_imports`: add modules that can't be actually imported by Sphinx in running environment, like `nuke`, `maya`, etc.
+- `autoapi_ignore`: add directories that shouldn't be processed by **AutoAPI**, like vendor dirs, etc.
+- `html_theme_options`: you can use these options to influence how the html theme of the generated files will look.
+- `myst_gfm_only`: are Myst parser option for Markdown setting what flavour of Markdown should be used.
+
+How to build it
+---------------
+
+You can run:
+
+```sh
+cd .\docs
+make.bat html
+```
+
+on linux/macOS:
+
+```sh
+cd ./docs
+make html
+```
+
+This will go over our code and generate **.rst** files in `/docs/source/autoapi` and from those it will generate
+full html documentation in `/docs/build/html`.
+
+During the build you may see tons of red errors that are pointing to our issues:
+
+1) **Wrong imports** -
+Invalid import are usually wrong relative imports (too deep) or circular imports.
+2) **Invalid docstrings** -
+Docstrings to be processed into documentation needs to follow some syntax - this can be checked by running
+`pydocstyle` that is already included with OpenPype
+3) **Invalid markdown/rst files** -
+Markdown/RST files can be included inside RST files using `.. include::` directive. But they have to be properly
+formatted.
+
+Editing RST templates
+---------------------
+Everything starts with `/docs/source/index.rst` - this file should be properly edited, Right now it just
+includes `readme.rst` that in turn include and parse main `README.md`. This is entrypoint to API documentation.
+All templates generated by AutoAPI are in `/docs/source/autoapi`. They should be eventually committed to repository
+and edited too.
+
+Steps for enhancing API documentation
+-------------------------------------
+
+1) Run `/docs/make.bat html`
+2) Read the red errors/warnings - fix it in the code
+3) Run `/docs/make.bat html` - again until there are no red lines
+4) Edit RST files and add some meaningful content there
+
+Resources
+=========
+
+- [ReStructuredText on Wikipedia](https://en.wikipedia.org/wiki/ReStructuredText)
+- [RST Quick Reference](https://docutils.sourceforge.io/docs/user/rst/quickref.html)
+- [Sphinx AutoAPI Documentation](https://sphinx-autoapi.readthedocs.io/en/latest/)
+- [Example of Google Style Python Docstrings](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html)
+- [Sphinx Directives](https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html)
diff --git a/docs/make.bat b/docs/make.bat
index 4d9eb83d9f..1d261df277 100644
--- a/docs/make.bat
+++ b/docs/make.bat
@@ -5,7 +5,7 @@ pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
+ set SPHINXBUILD=..\.poetry\bin\poetry run sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
diff --git a/docs/source/_static/AYON_tight_G.svg b/docs/source/_static/AYON_tight_G.svg
new file mode 100644
index 0000000000..2c5b73deea
--- /dev/null
+++ b/docs/source/_static/AYON_tight_G.svg
@@ -0,0 +1,38 @@
+
+
+
diff --git a/openpype/hosts/maya/api/obj.py b/docs/source/_static/README.md
similarity index 100%
rename from openpype/hosts/maya/api/obj.py
rename to docs/source/_static/README.md
diff --git a/docs/source/_templates/autoapi/index.rst b/docs/source/_templates/autoapi/index.rst
new file mode 100644
index 0000000000..95d0ad8911
--- /dev/null
+++ b/docs/source/_templates/autoapi/index.rst
@@ -0,0 +1,15 @@
+API Reference
+=============
+
+This page contains auto-generated API reference documentation [#f1]_.
+
+.. toctree::
+ :titlesonly:
+
+ {% for page in pages %}
+ {% if page.top_level_object and page.display %}
+ {{ page.include_path }}
+ {% endif %}
+ {% endfor %}
+
+.. [#f1] Created with `sphinx-autoapi `_
diff --git a/docs/source/_templates/autoapi/python/attribute.rst b/docs/source/_templates/autoapi/python/attribute.rst
new file mode 100644
index 0000000000..ebaba555ad
--- /dev/null
+++ b/docs/source/_templates/autoapi/python/attribute.rst
@@ -0,0 +1 @@
+{% extends "python/data.rst" %}
diff --git a/docs/source/_templates/autoapi/python/class.rst b/docs/source/_templates/autoapi/python/class.rst
new file mode 100644
index 0000000000..df5edffb62
--- /dev/null
+++ b/docs/source/_templates/autoapi/python/class.rst
@@ -0,0 +1,58 @@
+{% if obj.display %}
+.. py:{{ obj.type }}:: {{ obj.short_name }}{% if obj.args %}({{ obj.args }}){% endif %}
+{% for (args, return_annotation) in obj.overloads %}
+ {{ " " * (obj.type | length) }} {{ obj.short_name }}{% if args %}({{ args }}){% endif %}
+{% endfor %}
+
+
+ {% if obj.bases %}
+ {% if "show-inheritance" in autoapi_options %}
+ Bases: {% for base in obj.bases %}{{ base|link_objs }}{% if not loop.last %}, {% endif %}{% endfor %}
+ {% endif %}
+
+
+ {% if "show-inheritance-diagram" in autoapi_options and obj.bases != ["object"] %}
+ .. autoapi-inheritance-diagram:: {{ obj.obj["full_name"] }}
+ :parts: 1
+ {% if "private-members" in autoapi_options %}
+ :private-bases:
+ {% endif %}
+
+ {% endif %}
+ {% endif %}
+ {% if obj.docstring %}
+ {{ obj.docstring|indent(3) }}
+ {% endif %}
+ {% if "inherited-members" in autoapi_options %}
+ {% set visible_classes = obj.classes|selectattr("display")|list %}
+ {% else %}
+ {% set visible_classes = obj.classes|rejectattr("inherited")|selectattr("display")|list %}
+ {% endif %}
+ {% for klass in visible_classes %}
+ {{ klass.render()|indent(3) }}
+ {% endfor %}
+ {% if "inherited-members" in autoapi_options %}
+ {% set visible_properties = obj.properties|selectattr("display")|list %}
+ {% else %}
+ {% set visible_properties = obj.properties|rejectattr("inherited")|selectattr("display")|list %}
+ {% endif %}
+ {% for property in visible_properties %}
+ {{ property.render()|indent(3) }}
+ {% endfor %}
+ {% if "inherited-members" in autoapi_options %}
+ {% set visible_attributes = obj.attributes|selectattr("display")|list %}
+ {% else %}
+ {% set visible_attributes = obj.attributes|rejectattr("inherited")|selectattr("display")|list %}
+ {% endif %}
+ {% for attribute in visible_attributes %}
+ {{ attribute.render()|indent(3) }}
+ {% endfor %}
+ {% if "inherited-members" in autoapi_options %}
+ {% set visible_methods = obj.methods|selectattr("display")|list %}
+ {% else %}
+ {% set visible_methods = obj.methods|rejectattr("inherited")|selectattr("display")|list %}
+ {% endif %}
+ {% for method in visible_methods %}
+ {{ method.render()|indent(3) }}
+ {% endfor %}
+{% endif %}
diff --git a/docs/source/_templates/autoapi/python/data.rst b/docs/source/_templates/autoapi/python/data.rst
new file mode 100644
index 0000000000..3d12b2d0c7
--- /dev/null
+++ b/docs/source/_templates/autoapi/python/data.rst
@@ -0,0 +1,37 @@
+{% if obj.display %}
+.. py:{{ obj.type }}:: {{ obj.name }}
+ {%- if obj.annotation is not none %}
+
+ :type: {%- if obj.annotation %} {{ obj.annotation }}{%- endif %}
+
+ {%- endif %}
+
+ {%- if obj.value is not none %}
+
+ :value: {% if obj.value is string and obj.value.splitlines()|count > 1 -%}
+ Multiline-String
+
+ .. raw:: html
+
+ Show Value
+
+ .. code-block:: python
+
+ """{{ obj.value|indent(width=8,blank=true) }}"""
+
+ .. raw:: html
+
+
+
+ {%- else -%}
+ {%- if obj.value is string -%}
+ {{ "%r" % obj.value|string|truncate(100) }}
+ {%- else -%}
+ {{ obj.value|string|truncate(100) }}
+ {%- endif -%}
+ {%- endif %}
+ {%- endif %}
+
+
+ {{ obj.docstring|indent(3) }}
+{% endif %}
diff --git a/docs/source/_templates/autoapi/python/exception.rst b/docs/source/_templates/autoapi/python/exception.rst
new file mode 100644
index 0000000000..92f3d38fd5
--- /dev/null
+++ b/docs/source/_templates/autoapi/python/exception.rst
@@ -0,0 +1 @@
+{% extends "python/class.rst" %}
diff --git a/docs/source/_templates/autoapi/python/function.rst b/docs/source/_templates/autoapi/python/function.rst
new file mode 100644
index 0000000000..b00d5c2445
--- /dev/null
+++ b/docs/source/_templates/autoapi/python/function.rst
@@ -0,0 +1,15 @@
+{% if obj.display %}
+.. py:function:: {{ obj.short_name }}({{ obj.args }}){% if obj.return_annotation is not none %} -> {{ obj.return_annotation }}{% endif %}
+
+{% for (args, return_annotation) in obj.overloads %}
+ {{ obj.short_name }}({{ args }}){% if return_annotation is not none %} -> {{ return_annotation }}{% endif %}
+
+{% endfor %}
+ {% for property in obj.properties %}
+ :{{ property }}:
+ {% endfor %}
+
+ {% if obj.docstring %}
+ {{ obj.docstring|indent(3) }}
+ {% endif %}
+{% endif %}
diff --git a/docs/source/_templates/autoapi/python/method.rst b/docs/source/_templates/autoapi/python/method.rst
new file mode 100644
index 0000000000..723cb7bbe5
--- /dev/null
+++ b/docs/source/_templates/autoapi/python/method.rst
@@ -0,0 +1,19 @@
+{%- if obj.display %}
+.. py:method:: {{ obj.short_name }}({{ obj.args }}){% if obj.return_annotation is not none %} -> {{ obj.return_annotation }}{% endif %}
+
+{% for (args, return_annotation) in obj.overloads %}
+ {{ obj.short_name }}({{ args }}){% if return_annotation is not none %} -> {{ return_annotation }}{% endif %}
+
+{% endfor %}
+ {% if obj.properties %}
+ {% for property in obj.properties %}
+ :{{ property }}:
+ {% endfor %}
+
+ {% else %}
+
+ {% endif %}
+ {% if obj.docstring %}
+ {{ obj.docstring|indent(3) }}
+ {% endif %}
+{% endif %}
diff --git a/docs/source/_templates/autoapi/python/module.rst b/docs/source/_templates/autoapi/python/module.rst
new file mode 100644
index 0000000000..d2714f6c9d
--- /dev/null
+++ b/docs/source/_templates/autoapi/python/module.rst
@@ -0,0 +1,114 @@
+{% if not obj.display %}
+:orphan:
+
+{% endif %}
+:py:mod:`{{ obj.name }}`
+=========={{ "=" * obj.name|length }}
+
+.. py:module:: {{ obj.name }}
+
+{% if obj.docstring %}
+.. autoapi-nested-parse::
+
+ {{ obj.docstring|indent(3) }}
+
+{% endif %}
+
+{% block subpackages %}
+{% set visible_subpackages = obj.subpackages|selectattr("display")|list %}
+{% if visible_subpackages %}
+Subpackages
+-----------
+.. toctree::
+ :titlesonly:
+ :maxdepth: 3
+
+{% for subpackage in visible_subpackages %}
+ {{ subpackage.short_name }}/index.rst
+{% endfor %}
+
+
+{% endif %}
+{% endblock %}
+{% block submodules %}
+{% set visible_submodules = obj.submodules|selectattr("display")|list %}
+{% if visible_submodules %}
+Submodules
+----------
+.. toctree::
+ :titlesonly:
+ :maxdepth: 1
+
+{% for submodule in visible_submodules %}
+ {{ submodule.short_name }}/index.rst
+{% endfor %}
+
+
+{% endif %}
+{% endblock %}
+{% block content %}
+{% if obj.all is not none %}
+{% set visible_children = obj.children|selectattr("short_name", "in", obj.all)|list %}
+{% elif obj.type is equalto("package") %}
+{% set visible_children = obj.children|selectattr("display")|list %}
+{% else %}
+{% set visible_children = obj.children|selectattr("display")|rejectattr("imported")|list %}
+{% endif %}
+{% if visible_children %}
+{{ obj.type|title }} Contents
+{{ "-" * obj.type|length }}---------
+
+{% set visible_classes = visible_children|selectattr("type", "equalto", "class")|list %}
+{% set visible_functions = visible_children|selectattr("type", "equalto", "function")|list %}
+{% set visible_attributes = visible_children|selectattr("type", "equalto", "data")|list %}
+{% if "show-module-summary" in autoapi_options and (visible_classes or visible_functions) %}
+{% block classes scoped %}
+{% if visible_classes %}
+Classes
+~~~~~~~
+
+.. autoapisummary::
+
+{% for klass in visible_classes %}
+ {{ klass.id }}
+{% endfor %}
+
+
+{% endif %}
+{% endblock %}
+
+{% block functions scoped %}
+{% if visible_functions %}
+Functions
+~~~~~~~~~
+
+.. autoapisummary::
+
+{% for function in visible_functions %}
+ {{ function.id }}
+{% endfor %}
+
+
+{% endif %}
+{% endblock %}
+
+{% block attributes scoped %}
+{% if visible_attributes %}
+Attributes
+~~~~~~~~~~
+
+.. autoapisummary::
+
+{% for attribute in visible_attributes %}
+ {{ attribute.id }}
+{% endfor %}
+
+
+{% endif %}
+{% endblock %}
+{% endif %}
+{% for obj_item in visible_children %}
+{{ obj_item.render()|indent(0) }}
+{% endfor %}
+{% endif %}
+{% endblock %}
diff --git a/docs/source/_templates/autoapi/python/package.rst b/docs/source/_templates/autoapi/python/package.rst
new file mode 100644
index 0000000000..fb9a64965e
--- /dev/null
+++ b/docs/source/_templates/autoapi/python/package.rst
@@ -0,0 +1 @@
+{% extends "python/module.rst" %}
diff --git a/docs/source/_templates/autoapi/python/property.rst b/docs/source/_templates/autoapi/python/property.rst
new file mode 100644
index 0000000000..70af24236f
--- /dev/null
+++ b/docs/source/_templates/autoapi/python/property.rst
@@ -0,0 +1,15 @@
+{%- if obj.display %}
+.. py:property:: {{ obj.short_name }}
+ {% if obj.annotation %}
+ :type: {{ obj.annotation }}
+ {% endif %}
+ {% if obj.properties %}
+ {% for property in obj.properties %}
+ :{{ property }}:
+ {% endfor %}
+ {% endif %}
+
+ {% if obj.docstring %}
+ {{ obj.docstring|indent(3) }}
+ {% endif %}
+{% endif %}
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 5b34ff8dc0..916a397e8e 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -17,18 +17,29 @@
import os
import sys
-pype_root = os.path.abspath('../..')
-sys.path.insert(0, pype_root)
+import revitron_sphinx_theme
+
+openpype_root = os.path.abspath('../..')
+sys.path.insert(0, openpype_root)
+# app = QApplication([])
+
+"""
repos = os.listdir(os.path.abspath("../../repos"))
-repos = [os.path.join(pype_root, "repos", repo) for repo in repos]
+repos = [os.path.join(openpype_root, "repos", repo) for repo in repos]
for repo in repos:
sys.path.append(repo)
+"""
+
+todo_include_todos = True
+autodoc_mock_imports = ["maya", "pymel", "nuke", "nukestudio", "nukescripts",
+ "hiero", "bpy", "fusion", "houdini", "hou", "unreal",
+ "__builtin__", "resolve", "pysync", "DaVinciResolveScript"]
# -- Project information -----------------------------------------------------
-project = 'pype'
-copyright = '2019, Orbi Tools'
-author = 'Orbi Tools'
+project = 'OpenPype'
+copyright = '2023 Ynput'
+author = 'Ynput'
# The short X.Y version
version = ''
@@ -52,11 +63,41 @@ extensions = [
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.mathjax',
- 'sphinx.ext.viewcode',
'sphinx.ext.autosummary',
- 'recommonmark'
+ 'revitron_sphinx_theme',
+ 'autoapi.extension',
+ 'myst_parser'
]
+##############################
+# Autoapi settings
+##############################
+
+autoapi_dirs = ['../../openpype', '../../igniter']
+
+# bypass modules with a lot of python2 content for now
+autoapi_ignore = [
+ "*vendor*",
+ "*schemas*",
+ "*startup/*",
+ "*/website*",
+ "*openpype/hooks*",
+ "*openpype/style*",
+ "openpype/tests*",
+ # to many levels of relative import:
+ "*/modules/sync_server/*"
+]
+autoapi_keep_files = True
+autoapi_options = [
+ 'members',
+ 'undoc-members',
+ 'show-inheritance',
+ 'show-module-summary'
+]
+autoapi_add_toctree_entry = True
+autoapi_template_dir = '_templates/autoapi'
+
+
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -64,7 +105,7 @@ templates_path = ['_templates']
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
+source_suffix = ['.rst', '.md']
# The master toctree document.
master_doc = 'index'
@@ -74,12 +115,15 @@ master_doc = 'index'
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
-language = None
+language = "English"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
-exclude_patterns = []
+exclude_patterns = [
+ "openpype.hosts.resolve.*",
+ "openpype.tools.*"
+ ]
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'friendly'
@@ -97,15 +141,22 @@ autosummary_generate = True
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
-html_theme = 'sphinx_rtd_theme'
+html_theme = 'revitron_sphinx_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {
- 'collapse_navigation': False
+ 'collapse_navigation': True,
+ 'sticky_navigation': True,
+ 'navigation_depth': 4,
+ 'includehidden': True,
+ 'titles_only': False,
+ 'github_url': '',
}
+html_logo = '_static/AYON_tight_G.svg'
+
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
@@ -153,8 +204,8 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- (master_doc, 'pype.tex', 'pype Documentation',
- 'OrbiTools', 'manual'),
+ (master_doc, 'openpype.tex', 'OpenPype Documentation',
+ 'Ynput', 'manual'),
]
@@ -163,7 +214,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
- (master_doc, 'pype', 'pype Documentation',
+ (master_doc, 'openpype', 'OpenPype Documentation',
[author], 1)
]
@@ -174,8 +225,8 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- (master_doc, 'pype', 'pype Documentation',
- author, 'pype', 'One line description of project.',
+ (master_doc, 'OpenPype', 'OpenPype Documentation',
+ author, 'OpenPype', 'Pipeline for studios',
'Miscellaneous'),
]
@@ -207,7 +258,4 @@ intersphinx_mapping = {
'https://docs.python.org/3/': None
}
-# -- Options for todo extension ----------------------------------------------
-
-# If true, `todo` and `todoList` produce output, else they produce nothing.
-todo_include_todos = True
+myst_gfm_only = True
diff --git a/docs/source/igniter.bootstrap_repos.rst b/docs/source/igniter.bootstrap_repos.rst
deleted file mode 100644
index 7c6e0a0757..0000000000
--- a/docs/source/igniter.bootstrap_repos.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-igniter.bootstrap\_repos module
-===============================
-
-.. automodule:: igniter.bootstrap_repos
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/igniter.install_dialog.rst b/docs/source/igniter.install_dialog.rst
deleted file mode 100644
index bf30ec270e..0000000000
--- a/docs/source/igniter.install_dialog.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-igniter.install\_dialog module
-==============================
-
-.. automodule:: igniter.install_dialog
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/igniter.install_thread.rst b/docs/source/igniter.install_thread.rst
deleted file mode 100644
index 6c19516219..0000000000
--- a/docs/source/igniter.install_thread.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-igniter.install\_thread module
-==============================
-
-.. automodule:: igniter.install_thread
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/igniter.rst b/docs/source/igniter.rst
deleted file mode 100644
index b4aebe88b0..0000000000
--- a/docs/source/igniter.rst
+++ /dev/null
@@ -1,42 +0,0 @@
-igniter package
-===============
-
-.. automodule:: igniter
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-igniter.bootstrap\_repos module
--------------------------------
-
-.. automodule:: igniter.bootstrap_repos
- :members:
- :undoc-members:
- :show-inheritance:
-
-igniter.install\_dialog module
-------------------------------
-
-.. automodule:: igniter.install_dialog
- :members:
- :undoc-members:
- :show-inheritance:
-
-igniter.install\_thread module
-------------------------------
-
-.. automodule:: igniter.install_thread
- :members:
- :undoc-members:
- :show-inheritance:
-
-igniter.tools module
---------------------
-
-.. automodule:: igniter.tools
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/igniter.tools.rst b/docs/source/igniter.tools.rst
deleted file mode 100644
index 4fdbdf9d29..0000000000
--- a/docs/source/igniter.tools.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-igniter.tools module
-====================
-
-.. automodule:: igniter.tools
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/index.rst b/docs/source/index.rst
index b54d153894..f703468fca 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -1,14 +1,15 @@
-.. pype documentation master file, created by
+.. openpype documentation master file, created by
sphinx-quickstart on Mon May 13 17:18:23 2019.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
-Welcome to pype's documentation!
-================================
+Welcome to OpenPype's API documentation!
+========================================
.. toctree::
- readme
- modules
+
+ Readme
+
Indices and tables
==================
diff --git a/docs/source/modules.rst b/docs/source/modules.rst
deleted file mode 100644
index 1956d9ed04..0000000000
--- a/docs/source/modules.rst
+++ /dev/null
@@ -1,8 +0,0 @@
-igniter
-=======
-
-.. toctree::
- :maxdepth: 6
-
- igniter
- pype
\ No newline at end of file
diff --git a/docs/source/pype.action.rst b/docs/source/pype.action.rst
deleted file mode 100644
index 62a32e08b5..0000000000
--- a/docs/source/pype.action.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.action module
-==================
-
-.. automodule:: pype.action
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.api.rst b/docs/source/pype.api.rst
deleted file mode 100644
index af3602a895..0000000000
--- a/docs/source/pype.api.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.api module
-===============
-
-.. automodule:: pype.api
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.cli.rst b/docs/source/pype.cli.rst
deleted file mode 100644
index 7e4a336fa9..0000000000
--- a/docs/source/pype.cli.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.cli module
-===============
-
-.. automodule:: pype.cli
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.aftereffects.rst b/docs/source/pype.hosts.aftereffects.rst
deleted file mode 100644
index 3c2b2dda41..0000000000
--- a/docs/source/pype.hosts.aftereffects.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.aftereffects package
-===============================
-
-.. automodule:: pype.hosts.aftereffects
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.blender.action.rst b/docs/source/pype.hosts.blender.action.rst
deleted file mode 100644
index a6444b1efc..0000000000
--- a/docs/source/pype.hosts.blender.action.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.blender.action module
-================================
-
-.. automodule:: pype.hosts.blender.action
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.blender.plugin.rst b/docs/source/pype.hosts.blender.plugin.rst
deleted file mode 100644
index cf6a8feec8..0000000000
--- a/docs/source/pype.hosts.blender.plugin.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.blender.plugin module
-================================
-
-.. automodule:: pype.hosts.blender.plugin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.blender.rst b/docs/source/pype.hosts.blender.rst
deleted file mode 100644
index 19cb85e5f3..0000000000
--- a/docs/source/pype.hosts.blender.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.hosts.blender package
-==========================
-
-.. automodule:: pype.hosts.blender
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.hosts.blender.action module
---------------------------------
-
-.. automodule:: pype.hosts.blender.action
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.blender.plugin module
---------------------------------
-
-.. automodule:: pype.hosts.blender.plugin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.celaction.cli.rst b/docs/source/pype.hosts.celaction.cli.rst
deleted file mode 100644
index c8843b90bd..0000000000
--- a/docs/source/pype.hosts.celaction.cli.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.celaction.cli module
-===============================
-
-.. automodule:: pype.hosts.celaction.cli
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.celaction.rst b/docs/source/pype.hosts.celaction.rst
deleted file mode 100644
index 1aa236397e..0000000000
--- a/docs/source/pype.hosts.celaction.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-pype.hosts.celaction package
-============================
-
-.. automodule:: pype.hosts.celaction
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.hosts.celaction.cli module
--------------------------------
-
-.. automodule:: pype.hosts.celaction.cli
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.fusion.lib.rst b/docs/source/pype.hosts.fusion.lib.rst
deleted file mode 100644
index 32b8f501f5..0000000000
--- a/docs/source/pype.hosts.fusion.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.fusion.lib module
-============================
-
-.. automodule:: pype.hosts.fusion.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.fusion.menu.rst b/docs/source/pype.hosts.fusion.menu.rst
deleted file mode 100644
index ec5bf76612..0000000000
--- a/docs/source/pype.hosts.fusion.menu.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.fusion.menu module
-=============================
-
-.. automodule:: pype.hosts.fusion.menu
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.fusion.pipeline.rst b/docs/source/pype.hosts.fusion.pipeline.rst
deleted file mode 100644
index ff2a6440a8..0000000000
--- a/docs/source/pype.hosts.fusion.pipeline.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.fusion.pipeline module
-=================================
-
-.. automodule:: pype.hosts.fusion.pipeline
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.fusion.rst b/docs/source/pype.hosts.fusion.rst
deleted file mode 100644
index 7c2fee827c..0000000000
--- a/docs/source/pype.hosts.fusion.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.hosts.fusion package
-=========================
-
-.. automodule:: pype.hosts.fusion
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.hosts.fusion.scripts
-
-Submodules
-----------
-
-pype.hosts.fusion.lib module
-----------------------------
-
-.. automodule:: pype.hosts.fusion.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.fusion.scripts.duplicate_with_inputs.rst b/docs/source/pype.hosts.fusion.scripts.duplicate_with_inputs.rst
deleted file mode 100644
index 2503c20f3b..0000000000
--- a/docs/source/pype.hosts.fusion.scripts.duplicate_with_inputs.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.fusion.scripts.duplicate\_with\_inputs module
-========================================================
-
-.. automodule:: pype.hosts.fusion.scripts.duplicate_with_inputs
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.fusion.scripts.fusion_switch_shot.rst b/docs/source/pype.hosts.fusion.scripts.fusion_switch_shot.rst
deleted file mode 100644
index 770300116f..0000000000
--- a/docs/source/pype.hosts.fusion.scripts.fusion_switch_shot.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.fusion.scripts.fusion\_switch\_shot module
-=====================================================
-
-.. automodule:: pype.hosts.fusion.scripts.fusion_switch_shot
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.fusion.scripts.rst b/docs/source/pype.hosts.fusion.scripts.rst
deleted file mode 100644
index 5de5f66652..0000000000
--- a/docs/source/pype.hosts.fusion.scripts.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.hosts.fusion.scripts package
-=================================
-
-.. automodule:: pype.hosts.fusion.scripts
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.hosts.fusion.scripts.fusion\_switch\_shot module
------------------------------------------------------
-
-.. automodule:: pype.hosts.fusion.scripts.fusion_switch_shot
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.fusion.scripts.publish\_filesequence module
-------------------------------------------------------
-
-.. automodule:: pype.hosts.fusion.scripts.publish_filesequence
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.fusion.scripts.set_rendermode.rst b/docs/source/pype.hosts.fusion.scripts.set_rendermode.rst
deleted file mode 100644
index 27bff63466..0000000000
--- a/docs/source/pype.hosts.fusion.scripts.set_rendermode.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.fusion.scripts.set\_rendermode module
-================================================
-
-.. automodule:: pype.hosts.fusion.scripts.set_rendermode
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.fusion.utils.rst b/docs/source/pype.hosts.fusion.utils.rst
deleted file mode 100644
index b6de3d0510..0000000000
--- a/docs/source/pype.hosts.fusion.utils.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.fusion.utils module
-==============================
-
-.. automodule:: pype.hosts.fusion.utils
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.harmony.rst b/docs/source/pype.hosts.harmony.rst
deleted file mode 100644
index 60e1fcdce6..0000000000
--- a/docs/source/pype.hosts.harmony.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.harmony package
-==========================
-
-.. automodule:: pype.hosts.harmony
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.hiero.events.rst b/docs/source/pype.hosts.hiero.events.rst
deleted file mode 100644
index 874abbffba..0000000000
--- a/docs/source/pype.hosts.hiero.events.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.hiero.events module
-==============================
-
-.. automodule:: pype.hosts.hiero.events
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.hiero.lib.rst b/docs/source/pype.hosts.hiero.lib.rst
deleted file mode 100644
index 8c0d33b03b..0000000000
--- a/docs/source/pype.hosts.hiero.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.hiero.lib module
-===========================
-
-.. automodule:: pype.hosts.hiero.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.hiero.menu.rst b/docs/source/pype.hosts.hiero.menu.rst
deleted file mode 100644
index baa1317e61..0000000000
--- a/docs/source/pype.hosts.hiero.menu.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.hiero.menu module
-============================
-
-.. automodule:: pype.hosts.hiero.menu
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.hiero.rst b/docs/source/pype.hosts.hiero.rst
deleted file mode 100644
index 9a7891b45e..0000000000
--- a/docs/source/pype.hosts.hiero.rst
+++ /dev/null
@@ -1,19 +0,0 @@
-pype.hosts.hiero package
-========================
-
-.. automodule:: pype.hosts.hiero
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-.. toctree::
- :maxdepth: 10
-
- pype.hosts.hiero.events
- pype.hosts.hiero.lib
- pype.hosts.hiero.menu
- pype.hosts.hiero.tags
- pype.hosts.hiero.workio
diff --git a/docs/source/pype.hosts.hiero.tags.rst b/docs/source/pype.hosts.hiero.tags.rst
deleted file mode 100644
index 0df33279d5..0000000000
--- a/docs/source/pype.hosts.hiero.tags.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.hiero.tags module
-============================
-
-.. automodule:: pype.hosts.hiero.tags
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.hiero.workio.rst b/docs/source/pype.hosts.hiero.workio.rst
deleted file mode 100644
index 11aae43212..0000000000
--- a/docs/source/pype.hosts.hiero.workio.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.hiero.workio module
-==============================
-
-.. automodule:: pype.hosts.hiero.workio
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.houdini.lib.rst b/docs/source/pype.hosts.houdini.lib.rst
deleted file mode 100644
index ba6e60d5f3..0000000000
--- a/docs/source/pype.hosts.houdini.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.houdini.lib module
-=============================
-
-.. automodule:: pype.hosts.houdini.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.houdini.rst b/docs/source/pype.hosts.houdini.rst
deleted file mode 100644
index 5db18ab3d4..0000000000
--- a/docs/source/pype.hosts.houdini.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-pype.hosts.houdini package
-==========================
-
-.. automodule:: pype.hosts.houdini
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.hosts.houdini.lib module
------------------------------
-
-.. automodule:: pype.hosts.houdini.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.maya.action.rst b/docs/source/pype.hosts.maya.action.rst
deleted file mode 100644
index e1ad7e5d43..0000000000
--- a/docs/source/pype.hosts.maya.action.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.maya.action module
-=============================
-
-.. automodule:: pype.hosts.maya.action
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.maya.customize.rst b/docs/source/pype.hosts.maya.customize.rst
deleted file mode 100644
index 335e75b0d4..0000000000
--- a/docs/source/pype.hosts.maya.customize.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.maya.customize module
-================================
-
-.. automodule:: pype.hosts.maya.customize
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.maya.expected_files.rst b/docs/source/pype.hosts.maya.expected_files.rst
deleted file mode 100644
index 0ecf22e502..0000000000
--- a/docs/source/pype.hosts.maya.expected_files.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.maya.expected\_files module
-======================================
-
-.. automodule:: pype.hosts.maya.expected_files
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.maya.lib.rst b/docs/source/pype.hosts.maya.lib.rst
deleted file mode 100644
index 7d7dbe4502..0000000000
--- a/docs/source/pype.hosts.maya.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.maya.lib module
-==========================
-
-.. automodule:: pype.hosts.maya.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.maya.menu.rst b/docs/source/pype.hosts.maya.menu.rst
deleted file mode 100644
index 614e113769..0000000000
--- a/docs/source/pype.hosts.maya.menu.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.maya.menu module
-===========================
-
-.. automodule:: pype.hosts.maya.menu
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.maya.plugin.rst b/docs/source/pype.hosts.maya.plugin.rst
deleted file mode 100644
index 5796b40c70..0000000000
--- a/docs/source/pype.hosts.maya.plugin.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.maya.plugin module
-=============================
-
-.. automodule:: pype.hosts.maya.plugin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.maya.rst b/docs/source/pype.hosts.maya.rst
deleted file mode 100644
index 0beab888fc..0000000000
--- a/docs/source/pype.hosts.maya.rst
+++ /dev/null
@@ -1,58 +0,0 @@
-pype.hosts.maya package
-=======================
-
-.. automodule:: pype.hosts.maya
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.hosts.maya.action module
------------------------------
-
-.. automodule:: pype.hosts.maya.action
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.maya.customize module
---------------------------------
-
-.. automodule:: pype.hosts.maya.customize
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.maya.expected\_files module
---------------------------------------
-
-.. automodule:: pype.hosts.maya.expected_files
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.maya.lib module
---------------------------
-
-.. automodule:: pype.hosts.maya.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.maya.menu module
----------------------------
-
-.. automodule:: pype.hosts.maya.menu
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.maya.plugin module
------------------------------
-
-.. automodule:: pype.hosts.maya.plugin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.nuke.actions.rst b/docs/source/pype.hosts.nuke.actions.rst
deleted file mode 100644
index d5e8849a38..0000000000
--- a/docs/source/pype.hosts.nuke.actions.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.nuke.actions module
-==============================
-
-.. automodule:: pype.hosts.nuke.actions
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.nuke.lib.rst b/docs/source/pype.hosts.nuke.lib.rst
deleted file mode 100644
index c177a27f2d..0000000000
--- a/docs/source/pype.hosts.nuke.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.nuke.lib module
-==========================
-
-.. automodule:: pype.hosts.nuke.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.nuke.menu.rst b/docs/source/pype.hosts.nuke.menu.rst
deleted file mode 100644
index 190e488b95..0000000000
--- a/docs/source/pype.hosts.nuke.menu.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.nuke.menu module
-===========================
-
-.. automodule:: pype.hosts.nuke.menu
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.nuke.plugin.rst b/docs/source/pype.hosts.nuke.plugin.rst
deleted file mode 100644
index ddd5f1db89..0000000000
--- a/docs/source/pype.hosts.nuke.plugin.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.nuke.plugin module
-=============================
-
-.. automodule:: pype.hosts.nuke.plugin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.nuke.presets.rst b/docs/source/pype.hosts.nuke.presets.rst
deleted file mode 100644
index a69aa8a367..0000000000
--- a/docs/source/pype.hosts.nuke.presets.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.nuke.presets module
-==============================
-
-.. automodule:: pype.hosts.nuke.presets
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.nuke.rst b/docs/source/pype.hosts.nuke.rst
deleted file mode 100644
index 559de65927..0000000000
--- a/docs/source/pype.hosts.nuke.rst
+++ /dev/null
@@ -1,58 +0,0 @@
-pype.hosts.nuke package
-=======================
-
-.. automodule:: pype.hosts.nuke
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.hosts.nuke.actions module
-------------------------------
-
-.. automodule:: pype.hosts.nuke.actions
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.nuke.lib module
---------------------------
-
-.. automodule:: pype.hosts.nuke.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.nuke.menu module
----------------------------
-
-.. automodule:: pype.hosts.nuke.menu
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.nuke.plugin module
------------------------------
-
-.. automodule:: pype.hosts.nuke.plugin
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.nuke.presets module
-------------------------------
-
-.. automodule:: pype.hosts.nuke.presets
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.nuke.utils module
-----------------------------
-
-.. automodule:: pype.hosts.nuke.utils
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.nuke.utils.rst b/docs/source/pype.hosts.nuke.utils.rst
deleted file mode 100644
index 66974dc707..0000000000
--- a/docs/source/pype.hosts.nuke.utils.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.nuke.utils module
-============================
-
-.. automodule:: pype.hosts.nuke.utils
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.nukestudio.rst b/docs/source/pype.hosts.nukestudio.rst
deleted file mode 100644
index c718d699fa..0000000000
--- a/docs/source/pype.hosts.nukestudio.rst
+++ /dev/null
@@ -1,50 +0,0 @@
-pype.hosts.nukestudio package
-=============================
-
-.. automodule:: pype.hosts.nukestudio
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.hosts.nukestudio.events module
------------------------------------
-
-.. automodule:: pype.hosts.nukestudio.events
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.nukestudio.lib module
---------------------------------
-
-.. automodule:: pype.hosts.nukestudio.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.nukestudio.menu module
----------------------------------
-
-.. automodule:: pype.hosts.nukestudio.menu
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.nukestudio.tags module
----------------------------------
-
-.. automodule:: pype.hosts.nukestudio.tags
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.nukestudio.workio module
------------------------------------
-
-.. automodule:: pype.hosts.nukestudio.workio
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.photoshop.rst b/docs/source/pype.hosts.photoshop.rst
deleted file mode 100644
index f77ea79874..0000000000
--- a/docs/source/pype.hosts.photoshop.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.photoshop package
-============================
-
-.. automodule:: pype.hosts.photoshop
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.premiere.lib.rst b/docs/source/pype.hosts.premiere.lib.rst
deleted file mode 100644
index e2c2723841..0000000000
--- a/docs/source/pype.hosts.premiere.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.premiere.lib module
-==============================
-
-.. automodule:: pype.hosts.premiere.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.premiere.rst b/docs/source/pype.hosts.premiere.rst
deleted file mode 100644
index 7c38d52c22..0000000000
--- a/docs/source/pype.hosts.premiere.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-pype.hosts.premiere package
-===========================
-
-.. automodule:: pype.hosts.premiere
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.hosts.premiere.lib module
-------------------------------
-
-.. automodule:: pype.hosts.premiere.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.action.rst b/docs/source/pype.hosts.resolve.action.rst
deleted file mode 100644
index 781694781f..0000000000
--- a/docs/source/pype.hosts.resolve.action.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.resolve.action module
-================================
-
-.. automodule:: pype.hosts.resolve.action
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.lib.rst b/docs/source/pype.hosts.resolve.lib.rst
deleted file mode 100644
index 5860f783cc..0000000000
--- a/docs/source/pype.hosts.resolve.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.resolve.lib module
-=============================
-
-.. automodule:: pype.hosts.resolve.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.menu.rst b/docs/source/pype.hosts.resolve.menu.rst
deleted file mode 100644
index df87dcde98..0000000000
--- a/docs/source/pype.hosts.resolve.menu.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.resolve.menu module
-==============================
-
-.. automodule:: pype.hosts.resolve.menu
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.otio.davinci_export.rst b/docs/source/pype.hosts.resolve.otio.davinci_export.rst
deleted file mode 100644
index 498f96a7ed..0000000000
--- a/docs/source/pype.hosts.resolve.otio.davinci_export.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.resolve.otio.davinci\_export module
-==============================================
-
-.. automodule:: pype.hosts.resolve.otio.davinci_export
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.otio.davinci_import.rst b/docs/source/pype.hosts.resolve.otio.davinci_import.rst
deleted file mode 100644
index 30f43cc9fe..0000000000
--- a/docs/source/pype.hosts.resolve.otio.davinci_import.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.resolve.otio.davinci\_import module
-==============================================
-
-.. automodule:: pype.hosts.resolve.otio.davinci_import
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.otio.rst b/docs/source/pype.hosts.resolve.otio.rst
deleted file mode 100644
index 523d8937ca..0000000000
--- a/docs/source/pype.hosts.resolve.otio.rst
+++ /dev/null
@@ -1,17 +0,0 @@
-pype.hosts.resolve.otio package
-===============================
-
-.. automodule:: pype.hosts.resolve.otio
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-.. toctree::
- :maxdepth: 10
-
- pype.hosts.resolve.otio.davinci_export
- pype.hosts.resolve.otio.davinci_import
- pype.hosts.resolve.otio.utils
diff --git a/docs/source/pype.hosts.resolve.otio.utils.rst b/docs/source/pype.hosts.resolve.otio.utils.rst
deleted file mode 100644
index 765f492732..0000000000
--- a/docs/source/pype.hosts.resolve.otio.utils.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.resolve.otio.utils module
-====================================
-
-.. automodule:: pype.hosts.resolve.otio.utils
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.pipeline.rst b/docs/source/pype.hosts.resolve.pipeline.rst
deleted file mode 100644
index 3efc24137b..0000000000
--- a/docs/source/pype.hosts.resolve.pipeline.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.resolve.pipeline module
-==================================
-
-.. automodule:: pype.hosts.resolve.pipeline
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.plugin.rst b/docs/source/pype.hosts.resolve.plugin.rst
deleted file mode 100644
index 26f6c56aef..0000000000
--- a/docs/source/pype.hosts.resolve.plugin.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.resolve.plugin module
-================================
-
-.. automodule:: pype.hosts.resolve.plugin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.preload_console.rst b/docs/source/pype.hosts.resolve.preload_console.rst
deleted file mode 100644
index 0d38ae14ea..0000000000
--- a/docs/source/pype.hosts.resolve.preload_console.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.resolve.preload\_console module
-==========================================
-
-.. automodule:: pype.hosts.resolve.preload_console
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.rst b/docs/source/pype.hosts.resolve.rst
deleted file mode 100644
index 368129e43e..0000000000
--- a/docs/source/pype.hosts.resolve.rst
+++ /dev/null
@@ -1,74 +0,0 @@
-pype.hosts.resolve package
-==========================
-
-.. automodule:: pype.hosts.resolve
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.hosts.resolve.action module
---------------------------------
-
-.. automodule:: pype.hosts.resolve.action
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.resolve.lib module
------------------------------
-
-.. automodule:: pype.hosts.resolve.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.resolve.menu module
-------------------------------
-
-.. automodule:: pype.hosts.resolve.menu
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.resolve.pipeline module
-----------------------------------
-
-.. automodule:: pype.hosts.resolve.pipeline
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.resolve.plugin module
---------------------------------
-
-.. automodule:: pype.hosts.resolve.plugin
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.resolve.preload\_console module
-------------------------------------------
-
-.. automodule:: pype.hosts.resolve.preload_console
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.resolve.utils module
--------------------------------
-
-.. automodule:: pype.hosts.resolve.utils
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.resolve.workio module
---------------------------------
-
-.. automodule:: pype.hosts.resolve.workio
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.todo-rendering.rst b/docs/source/pype.hosts.resolve.todo-rendering.rst
deleted file mode 100644
index 8ea80183ce..0000000000
--- a/docs/source/pype.hosts.resolve.todo-rendering.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.resolve.todo\-rendering module
-=========================================
-
-.. automodule:: pype.hosts.resolve.todo-rendering
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.utils.rst b/docs/source/pype.hosts.resolve.utils.rst
deleted file mode 100644
index e390a5d026..0000000000
--- a/docs/source/pype.hosts.resolve.utils.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.resolve.utils module
-===============================
-
-.. automodule:: pype.hosts.resolve.utils
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.resolve.workio.rst b/docs/source/pype.hosts.resolve.workio.rst
deleted file mode 100644
index 5dceb99d64..0000000000
--- a/docs/source/pype.hosts.resolve.workio.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.resolve.workio module
-================================
-
-.. automodule:: pype.hosts.resolve.workio
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.rst b/docs/source/pype.hosts.rst
deleted file mode 100644
index e2d9121501..0000000000
--- a/docs/source/pype.hosts.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.hosts package
-==================
-
-.. automodule:: pype.hosts
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.hosts.blender
- pype.hosts.celaction
- pype.hosts.fusion
- pype.hosts.harmony
- pype.hosts.houdini
- pype.hosts.maya
- pype.hosts.nuke
- pype.hosts.nukestudio
- pype.hosts.photoshop
- pype.hosts.premiere
- pype.hosts.resolve
- pype.hosts.unreal
diff --git a/docs/source/pype.hosts.tvpaint.api.rst b/docs/source/pype.hosts.tvpaint.api.rst
deleted file mode 100644
index 43273e8ec5..0000000000
--- a/docs/source/pype.hosts.tvpaint.api.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.tvpaint.api package
-==============================
-
-.. automodule:: pype.hosts.tvpaint.api
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.tvpaint.rst b/docs/source/pype.hosts.tvpaint.rst
deleted file mode 100644
index 561be3a9dc..0000000000
--- a/docs/source/pype.hosts.tvpaint.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-pype.hosts.tvpaint package
-==========================
-
-.. automodule:: pype.hosts.tvpaint
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 10
-
- pype.hosts.tvpaint.api
diff --git a/docs/source/pype.hosts.unreal.lib.rst b/docs/source/pype.hosts.unreal.lib.rst
deleted file mode 100644
index b891e71c47..0000000000
--- a/docs/source/pype.hosts.unreal.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.unreal.lib module
-============================
-
-.. automodule:: pype.hosts.unreal.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.unreal.plugin.rst b/docs/source/pype.hosts.unreal.plugin.rst
deleted file mode 100644
index e3ef81c7c7..0000000000
--- a/docs/source/pype.hosts.unreal.plugin.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.hosts.unreal.plugin module
-===============================
-
-.. automodule:: pype.hosts.unreal.plugin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.hosts.unreal.rst b/docs/source/pype.hosts.unreal.rst
deleted file mode 100644
index f46140298b..0000000000
--- a/docs/source/pype.hosts.unreal.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.hosts.unreal package
-=========================
-
-.. automodule:: pype.hosts.unreal
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.hosts.unreal.lib module
-----------------------------
-
-.. automodule:: pype.hosts.unreal.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.hosts.unreal.plugin module
--------------------------------
-
-.. automodule:: pype.hosts.unreal.plugin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.launcher_actions.rst b/docs/source/pype.launcher_actions.rst
deleted file mode 100644
index c7525acbd1..0000000000
--- a/docs/source/pype.launcher_actions.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.launcher\_actions module
-=============================
-
-.. automodule:: pype.launcher_actions
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.abstract_collect_render.rst b/docs/source/pype.lib.abstract_collect_render.rst
deleted file mode 100644
index d6adadc271..0000000000
--- a/docs/source/pype.lib.abstract_collect_render.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.abstract\_collect\_render module
-=========================================
-
-.. automodule:: pype.lib.abstract_collect_render
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.abstract_expected_files.rst b/docs/source/pype.lib.abstract_expected_files.rst
deleted file mode 100644
index 904aeb3375..0000000000
--- a/docs/source/pype.lib.abstract_expected_files.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.abstract\_expected\_files module
-=========================================
-
-.. automodule:: pype.lib.abstract_expected_files
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.abstract_metaplugins.rst b/docs/source/pype.lib.abstract_metaplugins.rst
deleted file mode 100644
index 9f2751b630..0000000000
--- a/docs/source/pype.lib.abstract_metaplugins.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.abstract\_metaplugins module
-=====================================
-
-.. automodule:: pype.lib.abstract_metaplugins
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.abstract_submit_deadline.rst b/docs/source/pype.lib.abstract_submit_deadline.rst
deleted file mode 100644
index a57222add3..0000000000
--- a/docs/source/pype.lib.abstract_submit_deadline.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.abstract\_submit\_deadline module
-==========================================
-
-.. automodule:: pype.lib.abstract_submit_deadline
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.anatomy.rst b/docs/source/pype.lib.anatomy.rst
deleted file mode 100644
index 7bddb37c8a..0000000000
--- a/docs/source/pype.lib.anatomy.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.anatomy module
-=======================
-
-.. automodule:: pype.lib.anatomy
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.applications.rst b/docs/source/pype.lib.applications.rst
deleted file mode 100644
index 8d1ff9b2c6..0000000000
--- a/docs/source/pype.lib.applications.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.applications module
-============================
-
-.. automodule:: pype.lib.applications
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.avalon_context.rst b/docs/source/pype.lib.avalon_context.rst
deleted file mode 100644
index 067ea3380f..0000000000
--- a/docs/source/pype.lib.avalon_context.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.avalon\_context module
-===============================
-
-.. automodule:: pype.lib.avalon_context
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.config.rst b/docs/source/pype.lib.config.rst
deleted file mode 100644
index ce4c13f4e7..0000000000
--- a/docs/source/pype.lib.config.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.config module
-======================
-
-.. automodule:: pype.lib.config
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.deprecated.rst b/docs/source/pype.lib.deprecated.rst
deleted file mode 100644
index ec5ee58d67..0000000000
--- a/docs/source/pype.lib.deprecated.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.deprecated module
-==========================
-
-.. automodule:: pype.lib.deprecated
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.editorial.rst b/docs/source/pype.lib.editorial.rst
deleted file mode 100644
index d32e495e51..0000000000
--- a/docs/source/pype.lib.editorial.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.editorial module
-=========================
-
-.. automodule:: pype.lib.editorial
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.env_tools.rst b/docs/source/pype.lib.env_tools.rst
deleted file mode 100644
index cb470207c8..0000000000
--- a/docs/source/pype.lib.env_tools.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.env\_tools module
-==========================
-
-.. automodule:: pype.lib.env_tools
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.execute.rst b/docs/source/pype.lib.execute.rst
deleted file mode 100644
index 82c4ef0ad8..0000000000
--- a/docs/source/pype.lib.execute.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.execute module
-=======================
-
-.. automodule:: pype.lib.execute
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.ffmpeg_utils.rst b/docs/source/pype.lib.ffmpeg_utils.rst
deleted file mode 100644
index 968a3f39c8..0000000000
--- a/docs/source/pype.lib.ffmpeg_utils.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.ffmpeg\_utils module
-=============================
-
-.. automodule:: pype.lib.ffmpeg_utils
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.git_progress.rst b/docs/source/pype.lib.git_progress.rst
deleted file mode 100644
index 017cf4c3c7..0000000000
--- a/docs/source/pype.lib.git_progress.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.git\_progress module
-=============================
-
-.. automodule:: pype.lib.git_progress
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.log.rst b/docs/source/pype.lib.log.rst
deleted file mode 100644
index 6282178850..0000000000
--- a/docs/source/pype.lib.log.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.log module
-===================
-
-.. automodule:: pype.lib.log
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.mongo.rst b/docs/source/pype.lib.mongo.rst
deleted file mode 100644
index 34fbc6af7f..0000000000
--- a/docs/source/pype.lib.mongo.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.mongo module
-=====================
-
-.. automodule:: pype.lib.mongo
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.path_tools.rst b/docs/source/pype.lib.path_tools.rst
deleted file mode 100644
index c19c41eea3..0000000000
--- a/docs/source/pype.lib.path_tools.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.path\_tools module
-===========================
-
-.. automodule:: pype.lib.path_tools
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.plugin_tools.rst b/docs/source/pype.lib.plugin_tools.rst
deleted file mode 100644
index 6eadc5d3be..0000000000
--- a/docs/source/pype.lib.plugin_tools.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.plugin\_tools module
-=============================
-
-.. automodule:: pype.lib.plugin_tools
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.profiling.rst b/docs/source/pype.lib.profiling.rst
deleted file mode 100644
index 1fded0c8fd..0000000000
--- a/docs/source/pype.lib.profiling.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.profiling module
-=========================
-
-.. automodule:: pype.lib.profiling
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.python_module_tools.rst b/docs/source/pype.lib.python_module_tools.rst
deleted file mode 100644
index c916080bce..0000000000
--- a/docs/source/pype.lib.python_module_tools.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.python\_module\_tools module
-=====================================
-
-.. automodule:: pype.lib.python_module_tools
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.rst b/docs/source/pype.lib.rst
deleted file mode 100644
index ea880eea3e..0000000000
--- a/docs/source/pype.lib.rst
+++ /dev/null
@@ -1,90 +0,0 @@
-pype.lib package
-================
-
-.. automodule:: pype.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.lib.anatomy module
------------------------
-
-.. automodule:: pype.lib.anatomy
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.lib.config module
-----------------------
-
-.. automodule:: pype.lib.config
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.lib.execute module
------------------------
-
-.. automodule:: pype.lib.execute
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.lib.git\_progress module
------------------------------
-
-.. automodule:: pype.lib.git_progress
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.lib.lib module
--------------------
-
-.. automodule:: pype.lib.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.lib.log module
--------------------
-
-.. automodule:: pype.lib.log
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.lib.mongo module
----------------------
-
-.. automodule:: pype.lib.mongo
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.lib.profiling module
--------------------------
-
-.. automodule:: pype.lib.profiling
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.lib.terminal module
-------------------------
-
-.. automodule:: pype.lib.terminal
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.lib.user\_settings module
-------------------------------
-
-.. automodule:: pype.lib.user_settings
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.terminal.rst b/docs/source/pype.lib.terminal.rst
deleted file mode 100644
index dafe1d8f69..0000000000
--- a/docs/source/pype.lib.terminal.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.terminal module
-========================
-
-.. automodule:: pype.lib.terminal
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.terminal_splash.rst b/docs/source/pype.lib.terminal_splash.rst
deleted file mode 100644
index 06038f0f09..0000000000
--- a/docs/source/pype.lib.terminal_splash.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.terminal\_splash module
-================================
-
-.. automodule:: pype.lib.terminal_splash
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.lib.user_settings.rst b/docs/source/pype.lib.user_settings.rst
deleted file mode 100644
index 7b4e8ced78..0000000000
--- a/docs/source/pype.lib.user_settings.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.lib.user\_settings module
-==============================
-
-.. automodule:: pype.lib.user_settings
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.adobe_communicator.adobe_comunicator.rst b/docs/source/pype.modules.adobe_communicator.adobe_comunicator.rst
deleted file mode 100644
index aadbaa0dc5..0000000000
--- a/docs/source/pype.modules.adobe_communicator.adobe_comunicator.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.adobe\_communicator.adobe\_comunicator module
-==========================================================
-
-.. automodule:: pype.modules.adobe_communicator.adobe_comunicator
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.adobe_communicator.lib.publish.rst b/docs/source/pype.modules.adobe_communicator.lib.publish.rst
deleted file mode 100644
index a16bf1dd0a..0000000000
--- a/docs/source/pype.modules.adobe_communicator.lib.publish.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.adobe\_communicator.lib.publish module
-===================================================
-
-.. automodule:: pype.modules.adobe_communicator.lib.publish
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.adobe_communicator.lib.rest_api.rst b/docs/source/pype.modules.adobe_communicator.lib.rest_api.rst
deleted file mode 100644
index 457bebef99..0000000000
--- a/docs/source/pype.modules.adobe_communicator.lib.rest_api.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.adobe\_communicator.lib.rest\_api module
-=====================================================
-
-.. automodule:: pype.modules.adobe_communicator.lib.rest_api
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.adobe_communicator.lib.rst b/docs/source/pype.modules.adobe_communicator.lib.rst
deleted file mode 100644
index cdec4ce80e..0000000000
--- a/docs/source/pype.modules.adobe_communicator.lib.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.modules.adobe\_communicator.lib package
-============================================
-
-.. automodule:: pype.modules.adobe_communicator.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.adobe\_communicator.lib.publish module
----------------------------------------------------
-
-.. automodule:: pype.modules.adobe_communicator.lib.publish
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.adobe\_communicator.lib.rest\_api module
------------------------------------------------------
-
-.. automodule:: pype.modules.adobe_communicator.lib.rest_api
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.adobe_communicator.rst b/docs/source/pype.modules.adobe_communicator.rst
deleted file mode 100644
index f2fa40ced4..0000000000
--- a/docs/source/pype.modules.adobe_communicator.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.modules.adobe\_communicator package
-========================================
-
-.. automodule:: pype.modules.adobe_communicator
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.modules.adobe_communicator.lib
-
-Submodules
-----------
-
-pype.modules.adobe\_communicator.adobe\_comunicator module
-----------------------------------------------------------
-
-.. automodule:: pype.modules.adobe_communicator.adobe_comunicator
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.avalon_apps.avalon_app.rst b/docs/source/pype.modules.avalon_apps.avalon_app.rst
deleted file mode 100644
index 43f467e748..0000000000
--- a/docs/source/pype.modules.avalon_apps.avalon_app.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.avalon\_apps.avalon\_app module
-============================================
-
-.. automodule:: pype.modules.avalon_apps.avalon_app
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.avalon_apps.rest_api.rst b/docs/source/pype.modules.avalon_apps.rest_api.rst
deleted file mode 100644
index d89c979311..0000000000
--- a/docs/source/pype.modules.avalon_apps.rest_api.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.avalon\_apps.rest\_api module
-==========================================
-
-.. automodule:: pype.modules.avalon_apps.rest_api
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.avalon_apps.rst b/docs/source/pype.modules.avalon_apps.rst
deleted file mode 100644
index 4755eddae6..0000000000
--- a/docs/source/pype.modules.avalon_apps.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.modules.avalon\_apps package
-=================================
-
-.. automodule:: pype.modules.avalon_apps
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.avalon\_apps.avalon\_app module
---------------------------------------------
-
-.. automodule:: pype.modules.avalon_apps.avalon_app
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.avalon\_apps.rest\_api module
-------------------------------------------
-
-.. automodule:: pype.modules.avalon_apps.rest_api
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.base.rst b/docs/source/pype.modules.base.rst
deleted file mode 100644
index 7cd3cfbd44..0000000000
--- a/docs/source/pype.modules.base.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.base module
-========================
-
-.. automodule:: pype.modules.base
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.clockify.clockify.rst b/docs/source/pype.modules.clockify.clockify.rst
deleted file mode 100644
index a3deaab81d..0000000000
--- a/docs/source/pype.modules.clockify.clockify.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.clockify.clockify module
-=====================================
-
-.. automodule:: pype.modules.clockify.clockify
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.clockify.clockify_api.rst b/docs/source/pype.modules.clockify.clockify_api.rst
deleted file mode 100644
index 2facc550c5..0000000000
--- a/docs/source/pype.modules.clockify.clockify_api.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.clockify.clockify\_api module
-==========================================
-
-.. automodule:: pype.modules.clockify.clockify_api
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.clockify.clockify_module.rst b/docs/source/pype.modules.clockify.clockify_module.rst
deleted file mode 100644
index 85f8e75ad1..0000000000
--- a/docs/source/pype.modules.clockify.clockify_module.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.clockify.clockify\_module module
-=============================================
-
-.. automodule:: pype.modules.clockify.clockify_module
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.clockify.constants.rst b/docs/source/pype.modules.clockify.constants.rst
deleted file mode 100644
index e30a073bfc..0000000000
--- a/docs/source/pype.modules.clockify.constants.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.clockify.constants module
-======================================
-
-.. automodule:: pype.modules.clockify.constants
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.clockify.rst b/docs/source/pype.modules.clockify.rst
deleted file mode 100644
index 550ba049c2..0000000000
--- a/docs/source/pype.modules.clockify.rst
+++ /dev/null
@@ -1,42 +0,0 @@
-pype.modules.clockify package
-=============================
-
-.. automodule:: pype.modules.clockify
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.clockify.clockify module
--------------------------------------
-
-.. automodule:: pype.modules.clockify.clockify
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.clockify.clockify\_api module
-------------------------------------------
-
-.. automodule:: pype.modules.clockify.clockify_api
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.clockify.constants module
---------------------------------------
-
-.. automodule:: pype.modules.clockify.constants
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.clockify.widgets module
-------------------------------------
-
-.. automodule:: pype.modules.clockify.widgets
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.clockify.widgets.rst b/docs/source/pype.modules.clockify.widgets.rst
deleted file mode 100644
index e9809fb048..0000000000
--- a/docs/source/pype.modules.clockify.widgets.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.clockify.widgets module
-====================================
-
-.. automodule:: pype.modules.clockify.widgets
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.deadline.deadline_module.rst b/docs/source/pype.modules.deadline.deadline_module.rst
deleted file mode 100644
index 43e7198a8b..0000000000
--- a/docs/source/pype.modules.deadline.deadline_module.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.deadline.deadline\_module module
-=============================================
-
-.. automodule:: pype.modules.deadline.deadline_module
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.deadline.rst b/docs/source/pype.modules.deadline.rst
deleted file mode 100644
index 7633b2b950..0000000000
--- a/docs/source/pype.modules.deadline.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-pype.modules.deadline package
-=============================
-
-.. automodule:: pype.modules.deadline
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-.. toctree::
- :maxdepth: 10
-
- pype.modules.deadline.deadline_module
diff --git a/docs/source/pype.modules.ftrack.ftrack_module.rst b/docs/source/pype.modules.ftrack.ftrack_module.rst
deleted file mode 100644
index 4188ffbed8..0000000000
--- a/docs/source/pype.modules.ftrack.ftrack_module.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.ftrack\_module module
-=========================================
-
-.. automodule:: pype.modules.ftrack.ftrack_module
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.ftrack_server.custom_db_connector.rst b/docs/source/pype.modules.ftrack.ftrack_server.custom_db_connector.rst
deleted file mode 100644
index b42c3e054d..0000000000
--- a/docs/source/pype.modules.ftrack.ftrack_server.custom_db_connector.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.ftrack\_server.custom\_db\_connector module
-===============================================================
-
-.. automodule:: pype.modules.ftrack.ftrack_server.custom_db_connector
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.ftrack_server.event_server_cli.rst b/docs/source/pype.modules.ftrack.ftrack_server.event_server_cli.rst
deleted file mode 100644
index d6404f965c..0000000000
--- a/docs/source/pype.modules.ftrack.ftrack_server.event_server_cli.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.ftrack\_server.event\_server\_cli module
-============================================================
-
-.. automodule:: pype.modules.ftrack.ftrack_server.event_server_cli
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.ftrack_server.ftrack_server.rst b/docs/source/pype.modules.ftrack.ftrack_server.ftrack_server.rst
deleted file mode 100644
index af2783c263..0000000000
--- a/docs/source/pype.modules.ftrack.ftrack_server.ftrack_server.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.ftrack\_server.ftrack\_server module
-========================================================
-
-.. automodule:: pype.modules.ftrack.ftrack_server.ftrack_server
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.ftrack_server.lib.rst b/docs/source/pype.modules.ftrack.ftrack_server.lib.rst
deleted file mode 100644
index 2ac4cef517..0000000000
--- a/docs/source/pype.modules.ftrack.ftrack_server.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.ftrack\_server.lib module
-=============================================
-
-.. automodule:: pype.modules.ftrack.ftrack_server.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.ftrack_server.rst b/docs/source/pype.modules.ftrack.ftrack_server.rst
deleted file mode 100644
index 417acc1a45..0000000000
--- a/docs/source/pype.modules.ftrack.ftrack_server.rst
+++ /dev/null
@@ -1,90 +0,0 @@
-pype.modules.ftrack.ftrack\_server package
-==========================================
-
-.. automodule:: pype.modules.ftrack.ftrack_server
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.ftrack.ftrack\_server.custom\_db\_connector module
----------------------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.ftrack_server.custom_db_connector
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.ftrack\_server.event\_server\_cli module
-------------------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.ftrack_server.event_server_cli
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.ftrack\_server.ftrack\_server module
---------------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.ftrack_server.ftrack_server
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.ftrack\_server.lib module
----------------------------------------------
-
-.. automodule:: pype.modules.ftrack.ftrack_server.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.ftrack\_server.socket\_thread module
---------------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.ftrack_server.socket_thread
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.ftrack\_server.sub\_event\_processor module
----------------------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.ftrack_server.sub_event_processor
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.ftrack\_server.sub\_event\_status module
-------------------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.ftrack_server.sub_event_status
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.ftrack\_server.sub\_event\_storer module
-------------------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.ftrack_server.sub_event_storer
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.ftrack\_server.sub\_legacy\_server module
--------------------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.ftrack_server.sub_legacy_server
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.ftrack\_server.sub\_user\_server module
------------------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.ftrack_server.sub_user_server
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.ftrack_server.socket_thread.rst b/docs/source/pype.modules.ftrack.ftrack_server.socket_thread.rst
deleted file mode 100644
index d8d24a8288..0000000000
--- a/docs/source/pype.modules.ftrack.ftrack_server.socket_thread.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.ftrack\_server.socket\_thread module
-========================================================
-
-.. automodule:: pype.modules.ftrack.ftrack_server.socket_thread
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.ftrack_server.sub_event_processor.rst b/docs/source/pype.modules.ftrack.ftrack_server.sub_event_processor.rst
deleted file mode 100644
index 04f863e347..0000000000
--- a/docs/source/pype.modules.ftrack.ftrack_server.sub_event_processor.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.ftrack\_server.sub\_event\_processor module
-===============================================================
-
-.. automodule:: pype.modules.ftrack.ftrack_server.sub_event_processor
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.ftrack_server.sub_event_status.rst b/docs/source/pype.modules.ftrack.ftrack_server.sub_event_status.rst
deleted file mode 100644
index 876b7313cf..0000000000
--- a/docs/source/pype.modules.ftrack.ftrack_server.sub_event_status.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.ftrack\_server.sub\_event\_status module
-============================================================
-
-.. automodule:: pype.modules.ftrack.ftrack_server.sub_event_status
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.ftrack_server.sub_event_storer.rst b/docs/source/pype.modules.ftrack.ftrack_server.sub_event_storer.rst
deleted file mode 100644
index 3d2d400d55..0000000000
--- a/docs/source/pype.modules.ftrack.ftrack_server.sub_event_storer.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.ftrack\_server.sub\_event\_storer module
-============================================================
-
-.. automodule:: pype.modules.ftrack.ftrack_server.sub_event_storer
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.ftrack_server.sub_legacy_server.rst b/docs/source/pype.modules.ftrack.ftrack_server.sub_legacy_server.rst
deleted file mode 100644
index d25cdfe8de..0000000000
--- a/docs/source/pype.modules.ftrack.ftrack_server.sub_legacy_server.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.ftrack\_server.sub\_legacy\_server module
-=============================================================
-
-.. automodule:: pype.modules.ftrack.ftrack_server.sub_legacy_server
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.ftrack_server.sub_user_server.rst b/docs/source/pype.modules.ftrack.ftrack_server.sub_user_server.rst
deleted file mode 100644
index c13095d5f1..0000000000
--- a/docs/source/pype.modules.ftrack.ftrack_server.sub_user_server.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.ftrack\_server.sub\_user\_server module
-===========================================================
-
-.. automodule:: pype.modules.ftrack.ftrack_server.sub_user_server
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.lib.avalon_sync.rst b/docs/source/pype.modules.ftrack.lib.avalon_sync.rst
deleted file mode 100644
index 954ec4d911..0000000000
--- a/docs/source/pype.modules.ftrack.lib.avalon_sync.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.lib.avalon\_sync module
-===========================================
-
-.. automodule:: pype.modules.ftrack.lib.avalon_sync
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.lib.credentials.rst b/docs/source/pype.modules.ftrack.lib.credentials.rst
deleted file mode 100644
index 3965dc406d..0000000000
--- a/docs/source/pype.modules.ftrack.lib.credentials.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.lib.credentials module
-==========================================
-
-.. automodule:: pype.modules.ftrack.lib.credentials
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.lib.ftrack_action_handler.rst b/docs/source/pype.modules.ftrack.lib.ftrack_action_handler.rst
deleted file mode 100644
index cec38f9b8a..0000000000
--- a/docs/source/pype.modules.ftrack.lib.ftrack_action_handler.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.lib.ftrack\_action\_handler module
-======================================================
-
-.. automodule:: pype.modules.ftrack.lib.ftrack_action_handler
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.lib.ftrack_app_handler.rst b/docs/source/pype.modules.ftrack.lib.ftrack_app_handler.rst
deleted file mode 100644
index 1f7395927d..0000000000
--- a/docs/source/pype.modules.ftrack.lib.ftrack_app_handler.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.lib.ftrack\_app\_handler module
-===================================================
-
-.. automodule:: pype.modules.ftrack.lib.ftrack_app_handler
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.lib.ftrack_base_handler.rst b/docs/source/pype.modules.ftrack.lib.ftrack_base_handler.rst
deleted file mode 100644
index 94fab7c940..0000000000
--- a/docs/source/pype.modules.ftrack.lib.ftrack_base_handler.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.lib.ftrack\_base\_handler module
-====================================================
-
-.. automodule:: pype.modules.ftrack.lib.ftrack_base_handler
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.lib.ftrack_event_handler.rst b/docs/source/pype.modules.ftrack.lib.ftrack_event_handler.rst
deleted file mode 100644
index 0b57219b50..0000000000
--- a/docs/source/pype.modules.ftrack.lib.ftrack_event_handler.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.lib.ftrack\_event\_handler module
-=====================================================
-
-.. automodule:: pype.modules.ftrack.lib.ftrack_event_handler
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.lib.rst b/docs/source/pype.modules.ftrack.lib.rst
deleted file mode 100644
index 32a219ab3a..0000000000
--- a/docs/source/pype.modules.ftrack.lib.rst
+++ /dev/null
@@ -1,58 +0,0 @@
-pype.modules.ftrack.lib package
-===============================
-
-.. automodule:: pype.modules.ftrack.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.ftrack.lib.avalon\_sync module
--------------------------------------------
-
-.. automodule:: pype.modules.ftrack.lib.avalon_sync
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.lib.credentials module
-------------------------------------------
-
-.. automodule:: pype.modules.ftrack.lib.credentials
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.lib.ftrack\_action\_handler module
-------------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.lib.ftrack_action_handler
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.lib.ftrack\_app\_handler module
----------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.lib.ftrack_app_handler
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.lib.ftrack\_base\_handler module
-----------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.lib.ftrack_base_handler
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.lib.ftrack\_event\_handler module
------------------------------------------------------
-
-.. automodule:: pype.modules.ftrack.lib.ftrack_event_handler
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.lib.settings.rst b/docs/source/pype.modules.ftrack.lib.settings.rst
deleted file mode 100644
index 255d52178a..0000000000
--- a/docs/source/pype.modules.ftrack.lib.settings.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.lib.settings module
-=======================================
-
-.. automodule:: pype.modules.ftrack.lib.settings
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.rst b/docs/source/pype.modules.ftrack.rst
deleted file mode 100644
index 13a92db808..0000000000
--- a/docs/source/pype.modules.ftrack.rst
+++ /dev/null
@@ -1,17 +0,0 @@
-pype.modules.ftrack package
-===========================
-
-.. automodule:: pype.modules.ftrack
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.modules.ftrack.ftrack_server
- pype.modules.ftrack.lib
- pype.modules.ftrack.tray
diff --git a/docs/source/pype.modules.ftrack.tray.ftrack_module.rst b/docs/source/pype.modules.ftrack.tray.ftrack_module.rst
deleted file mode 100644
index c4a370472c..0000000000
--- a/docs/source/pype.modules.ftrack.tray.ftrack_module.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.tray.ftrack\_module module
-==============================================
-
-.. automodule:: pype.modules.ftrack.tray.ftrack_module
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.tray.ftrack_tray.rst b/docs/source/pype.modules.ftrack.tray.ftrack_tray.rst
deleted file mode 100644
index 147647e9b4..0000000000
--- a/docs/source/pype.modules.ftrack.tray.ftrack_tray.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.tray.ftrack\_tray module
-============================================
-
-.. automodule:: pype.modules.ftrack.tray.ftrack_tray
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.tray.login_dialog.rst b/docs/source/pype.modules.ftrack.tray.login_dialog.rst
deleted file mode 100644
index dabc2e73a7..0000000000
--- a/docs/source/pype.modules.ftrack.tray.login_dialog.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.tray.login\_dialog module
-=============================================
-
-.. automodule:: pype.modules.ftrack.tray.login_dialog
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.tray.login_tools.rst b/docs/source/pype.modules.ftrack.tray.login_tools.rst
deleted file mode 100644
index 00ec690866..0000000000
--- a/docs/source/pype.modules.ftrack.tray.login_tools.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.ftrack.tray.login\_tools module
-============================================
-
-.. automodule:: pype.modules.ftrack.tray.login_tools
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.ftrack.tray.rst b/docs/source/pype.modules.ftrack.tray.rst
deleted file mode 100644
index 79772a9c3b..0000000000
--- a/docs/source/pype.modules.ftrack.tray.rst
+++ /dev/null
@@ -1,34 +0,0 @@
-pype.modules.ftrack.tray package
-================================
-
-.. automodule:: pype.modules.ftrack.tray
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.ftrack.tray.ftrack\_module module
-----------------------------------------------
-
-.. automodule:: pype.modules.ftrack.tray.ftrack_module
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.tray.login\_dialog module
----------------------------------------------
-
-.. automodule:: pype.modules.ftrack.tray.login_dialog
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.ftrack.tray.login\_tools module
---------------------------------------------
-
-.. automodule:: pype.modules.ftrack.tray.login_tools
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.idle_manager.idle_manager.rst b/docs/source/pype.modules.idle_manager.idle_manager.rst
deleted file mode 100644
index 8e93f97e6b..0000000000
--- a/docs/source/pype.modules.idle_manager.idle_manager.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.idle\_manager.idle\_manager module
-===============================================
-
-.. automodule:: pype.modules.idle_manager.idle_manager
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.idle_manager.rst b/docs/source/pype.modules.idle_manager.rst
deleted file mode 100644
index a3f7922999..0000000000
--- a/docs/source/pype.modules.idle_manager.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-pype.modules.idle\_manager package
-==================================
-
-.. automodule:: pype.modules.idle_manager
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.idle\_manager.idle\_manager module
------------------------------------------------
-
-.. automodule:: pype.modules.idle_manager.idle_manager
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.launcher_action.rst b/docs/source/pype.modules.launcher_action.rst
deleted file mode 100644
index a63408e747..0000000000
--- a/docs/source/pype.modules.launcher_action.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.launcher\_action module
-====================================
-
-.. automodule:: pype.modules.launcher_action
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.log_viewer.log_view_module.rst b/docs/source/pype.modules.log_viewer.log_view_module.rst
deleted file mode 100644
index 8d80170a9c..0000000000
--- a/docs/source/pype.modules.log_viewer.log_view_module.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.log\_viewer.log\_view\_module module
-=================================================
-
-.. automodule:: pype.modules.log_viewer.log_view_module
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.log_viewer.rst b/docs/source/pype.modules.log_viewer.rst
deleted file mode 100644
index e275d56086..0000000000
--- a/docs/source/pype.modules.log_viewer.rst
+++ /dev/null
@@ -1,23 +0,0 @@
-pype.modules.log\_viewer package
-================================
-
-.. automodule:: pype.modules.log_viewer
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 10
-
- pype.modules.log_viewer.tray
-
-Submodules
-----------
-
-.. toctree::
- :maxdepth: 10
-
- pype.modules.log_viewer.log_view_module
diff --git a/docs/source/pype.modules.log_viewer.tray.app.rst b/docs/source/pype.modules.log_viewer.tray.app.rst
deleted file mode 100644
index 0948a05594..0000000000
--- a/docs/source/pype.modules.log_viewer.tray.app.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.log\_viewer.tray.app module
-========================================
-
-.. automodule:: pype.modules.log_viewer.tray.app
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.log_viewer.tray.models.rst b/docs/source/pype.modules.log_viewer.tray.models.rst
deleted file mode 100644
index 4da3887600..0000000000
--- a/docs/source/pype.modules.log_viewer.tray.models.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.log\_viewer.tray.models module
-===========================================
-
-.. automodule:: pype.modules.log_viewer.tray.models
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.log_viewer.tray.rst b/docs/source/pype.modules.log_viewer.tray.rst
deleted file mode 100644
index 5f4b92f627..0000000000
--- a/docs/source/pype.modules.log_viewer.tray.rst
+++ /dev/null
@@ -1,17 +0,0 @@
-pype.modules.log\_viewer.tray package
-=====================================
-
-.. automodule:: pype.modules.log_viewer.tray
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-.. toctree::
- :maxdepth: 10
-
- pype.modules.log_viewer.tray.app
- pype.modules.log_viewer.tray.models
- pype.modules.log_viewer.tray.widgets
diff --git a/docs/source/pype.modules.log_viewer.tray.widgets.rst b/docs/source/pype.modules.log_viewer.tray.widgets.rst
deleted file mode 100644
index cb57c96559..0000000000
--- a/docs/source/pype.modules.log_viewer.tray.widgets.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.log\_viewer.tray.widgets module
-============================================
-
-.. automodule:: pype.modules.log_viewer.tray.widgets
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.muster.muster.rst b/docs/source/pype.modules.muster.muster.rst
deleted file mode 100644
index d3ba1e7052..0000000000
--- a/docs/source/pype.modules.muster.muster.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.muster.muster module
-=================================
-
-.. automodule:: pype.modules.muster.muster
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.muster.rst b/docs/source/pype.modules.muster.rst
deleted file mode 100644
index d8d0f762f4..0000000000
--- a/docs/source/pype.modules.muster.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.modules.muster package
-===========================
-
-.. automodule:: pype.modules.muster
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.muster.muster module
----------------------------------
-
-.. automodule:: pype.modules.muster.muster
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.muster.widget\_login module
-----------------------------------------
-
-.. automodule:: pype.modules.muster.widget_login
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.muster.widget_login.rst b/docs/source/pype.modules.muster.widget_login.rst
deleted file mode 100644
index 1c59cec820..0000000000
--- a/docs/source/pype.modules.muster.widget_login.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.muster.widget\_login module
-========================================
-
-.. automodule:: pype.modules.muster.widget_login
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.rest_api.base_class.rst b/docs/source/pype.modules.rest_api.base_class.rst
deleted file mode 100644
index c2a1030a78..0000000000
--- a/docs/source/pype.modules.rest_api.base_class.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.rest\_api.base\_class module
-=========================================
-
-.. automodule:: pype.modules.rest_api.base_class
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.rest_api.lib.exceptions.rst b/docs/source/pype.modules.rest_api.lib.exceptions.rst
deleted file mode 100644
index d755420ad0..0000000000
--- a/docs/source/pype.modules.rest_api.lib.exceptions.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.rest\_api.lib.exceptions module
-============================================
-
-.. automodule:: pype.modules.rest_api.lib.exceptions
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.rest_api.lib.factory.rst b/docs/source/pype.modules.rest_api.lib.factory.rst
deleted file mode 100644
index 2131d1b8da..0000000000
--- a/docs/source/pype.modules.rest_api.lib.factory.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.rest\_api.lib.factory module
-=========================================
-
-.. automodule:: pype.modules.rest_api.lib.factory
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.rest_api.lib.handler.rst b/docs/source/pype.modules.rest_api.lib.handler.rst
deleted file mode 100644
index 6e340daf9b..0000000000
--- a/docs/source/pype.modules.rest_api.lib.handler.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.rest\_api.lib.handler module
-=========================================
-
-.. automodule:: pype.modules.rest_api.lib.handler
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.rest_api.lib.lib.rst b/docs/source/pype.modules.rest_api.lib.lib.rst
deleted file mode 100644
index 19663788e0..0000000000
--- a/docs/source/pype.modules.rest_api.lib.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.rest\_api.lib.lib module
-=====================================
-
-.. automodule:: pype.modules.rest_api.lib.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.rest_api.lib.rst b/docs/source/pype.modules.rest_api.lib.rst
deleted file mode 100644
index ed8288ee73..0000000000
--- a/docs/source/pype.modules.rest_api.lib.rst
+++ /dev/null
@@ -1,42 +0,0 @@
-pype.modules.rest\_api.lib package
-==================================
-
-.. automodule:: pype.modules.rest_api.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.rest\_api.lib.exceptions module
---------------------------------------------
-
-.. automodule:: pype.modules.rest_api.lib.exceptions
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.rest\_api.lib.factory module
------------------------------------------
-
-.. automodule:: pype.modules.rest_api.lib.factory
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.rest\_api.lib.handler module
------------------------------------------
-
-.. automodule:: pype.modules.rest_api.lib.handler
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.rest\_api.lib.lib module
--------------------------------------
-
-.. automodule:: pype.modules.rest_api.lib.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.rest_api.rest_api.rst b/docs/source/pype.modules.rest_api.rest_api.rst
deleted file mode 100644
index e3d951ac9f..0000000000
--- a/docs/source/pype.modules.rest_api.rest_api.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.rest\_api.rest\_api module
-=======================================
-
-.. automodule:: pype.modules.rest_api.rest_api
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.rest_api.rst b/docs/source/pype.modules.rest_api.rst
deleted file mode 100644
index 09c58c84f8..0000000000
--- a/docs/source/pype.modules.rest_api.rst
+++ /dev/null
@@ -1,34 +0,0 @@
-pype.modules.rest\_api package
-==============================
-
-.. automodule:: pype.modules.rest_api
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.modules.rest_api.lib
-
-Submodules
-----------
-
-pype.modules.rest\_api.base\_class module
------------------------------------------
-
-.. automodule:: pype.modules.rest_api.base_class
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.rest\_api.rest\_api module
----------------------------------------
-
-.. automodule:: pype.modules.rest_api.rest_api
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.rst b/docs/source/pype.modules.rst
deleted file mode 100644
index 148c2084b4..0000000000
--- a/docs/source/pype.modules.rst
+++ /dev/null
@@ -1,36 +0,0 @@
-pype.modules package
-====================
-
-.. automodule:: pype.modules
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.modules.adobe_communicator
- pype.modules.avalon_apps
- pype.modules.clockify
- pype.modules.ftrack
- pype.modules.idle_manager
- pype.modules.muster
- pype.modules.rest_api
- pype.modules.standalonepublish
- pype.modules.timers_manager
- pype.modules.user
- pype.modules.websocket_server
-
-Submodules
-----------
-
-pype.modules.base module
-------------------------
-
-.. automodule:: pype.modules.base
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.settings_action.rst b/docs/source/pype.modules.settings_action.rst
deleted file mode 100644
index 10f0881ced..0000000000
--- a/docs/source/pype.modules.settings_action.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.settings\_action module
-====================================
-
-.. automodule:: pype.modules.settings_action
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.standalonepublish.rst b/docs/source/pype.modules.standalonepublish.rst
deleted file mode 100644
index 2ed366af5c..0000000000
--- a/docs/source/pype.modules.standalonepublish.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-pype.modules.standalonepublish package
-======================================
-
-.. automodule:: pype.modules.standalonepublish
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.standalonepublish.standalonepublish\_module module
----------------------------------------------------------------
-
-.. automodule:: pype.modules.standalonepublish.standalonepublish_module
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.standalonepublish.standalonepublish_module.rst b/docs/source/pype.modules.standalonepublish.standalonepublish_module.rst
deleted file mode 100644
index a78826a4b4..0000000000
--- a/docs/source/pype.modules.standalonepublish.standalonepublish_module.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.standalonepublish.standalonepublish\_module module
-===============================================================
-
-.. automodule:: pype.modules.standalonepublish.standalonepublish_module
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.standalonepublish_action.rst b/docs/source/pype.modules.standalonepublish_action.rst
deleted file mode 100644
index d51dbcefa0..0000000000
--- a/docs/source/pype.modules.standalonepublish_action.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.standalonepublish\_action module
-=============================================
-
-.. automodule:: pype.modules.standalonepublish_action
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.sync_server.rst b/docs/source/pype.modules.sync_server.rst
deleted file mode 100644
index a26dc7e212..0000000000
--- a/docs/source/pype.modules.sync_server.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-pype.modules.sync\_server package
-=================================
-
-.. automodule:: pype.modules.sync_server
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-.. toctree::
- :maxdepth: 10
-
- pype.modules.sync_server.sync_server
- pype.modules.sync_server.utils
diff --git a/docs/source/pype.modules.sync_server.sync_server.rst b/docs/source/pype.modules.sync_server.sync_server.rst
deleted file mode 100644
index 36d6aa68ed..0000000000
--- a/docs/source/pype.modules.sync_server.sync_server.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.sync\_server.sync\_server module
-=============================================
-
-.. automodule:: pype.modules.sync_server.sync_server
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.sync_server.utils.rst b/docs/source/pype.modules.sync_server.utils.rst
deleted file mode 100644
index 325d5e435d..0000000000
--- a/docs/source/pype.modules.sync_server.utils.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.sync\_server.utils module
-======================================
-
-.. automodule:: pype.modules.sync_server.utils
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.timers_manager.rst b/docs/source/pype.modules.timers_manager.rst
deleted file mode 100644
index 6c971e9dc1..0000000000
--- a/docs/source/pype.modules.timers_manager.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.modules.timers\_manager package
-====================================
-
-.. automodule:: pype.modules.timers_manager
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.timers\_manager.timers\_manager module
----------------------------------------------------
-
-.. automodule:: pype.modules.timers_manager.timers_manager
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.timers\_manager.widget\_user\_idle module
-------------------------------------------------------
-
-.. automodule:: pype.modules.timers_manager.widget_user_idle
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.timers_manager.timers_manager.rst b/docs/source/pype.modules.timers_manager.timers_manager.rst
deleted file mode 100644
index fe18e4d15c..0000000000
--- a/docs/source/pype.modules.timers_manager.timers_manager.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.timers\_manager.timers\_manager module
-===================================================
-
-.. automodule:: pype.modules.timers_manager.timers_manager
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.timers_manager.widget_user_idle.rst b/docs/source/pype.modules.timers_manager.widget_user_idle.rst
deleted file mode 100644
index b072879c7a..0000000000
--- a/docs/source/pype.modules.timers_manager.widget_user_idle.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.timers\_manager.widget\_user\_idle module
-======================================================
-
-.. automodule:: pype.modules.timers_manager.widget_user_idle
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.user.rst b/docs/source/pype.modules.user.rst
deleted file mode 100644
index d181b263e5..0000000000
--- a/docs/source/pype.modules.user.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.modules.user package
-=========================
-
-.. automodule:: pype.modules.user
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.user.user\_module module
--------------------------------------
-
-.. automodule:: pype.modules.user.user_module
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.user.widget\_user module
--------------------------------------
-
-.. automodule:: pype.modules.user.widget_user
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.user.user_module.rst b/docs/source/pype.modules.user.user_module.rst
deleted file mode 100644
index a8e0cd6bad..0000000000
--- a/docs/source/pype.modules.user.user_module.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.user.user\_module module
-=====================================
-
-.. automodule:: pype.modules.user.user_module
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.user.widget_user.rst b/docs/source/pype.modules.user.widget_user.rst
deleted file mode 100644
index 2979e5ead4..0000000000
--- a/docs/source/pype.modules.user.widget_user.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.user.widget\_user module
-=====================================
-
-.. automodule:: pype.modules.user.widget_user
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.websocket_server.hosts.aftereffects.rst b/docs/source/pype.modules.websocket_server.hosts.aftereffects.rst
deleted file mode 100644
index 9f4720ae14..0000000000
--- a/docs/source/pype.modules.websocket_server.hosts.aftereffects.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.websocket\_server.hosts.aftereffects module
-========================================================
-
-.. automodule:: pype.modules.websocket_server.hosts.aftereffects
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.websocket_server.hosts.external_app_1.rst b/docs/source/pype.modules.websocket_server.hosts.external_app_1.rst
deleted file mode 100644
index 4ac69d9015..0000000000
--- a/docs/source/pype.modules.websocket_server.hosts.external_app_1.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.websocket\_server.hosts.external\_app\_1 module
-============================================================
-
-.. automodule:: pype.modules.websocket_server.hosts.external_app_1
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.websocket_server.hosts.photoshop.rst b/docs/source/pype.modules.websocket_server.hosts.photoshop.rst
deleted file mode 100644
index cbda61275a..0000000000
--- a/docs/source/pype.modules.websocket_server.hosts.photoshop.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.websocket\_server.hosts.photoshop module
-=====================================================
-
-.. automodule:: pype.modules.websocket_server.hosts.photoshop
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.websocket_server.hosts.rst b/docs/source/pype.modules.websocket_server.hosts.rst
deleted file mode 100644
index d5ce7c3f8e..0000000000
--- a/docs/source/pype.modules.websocket_server.hosts.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.modules.websocket\_server.hosts package
-============================================
-
-.. automodule:: pype.modules.websocket_server.hosts
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.modules.websocket\_server.hosts.external\_app\_1 module
-------------------------------------------------------------
-
-.. automodule:: pype.modules.websocket_server.hosts.external_app_1
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules.websocket\_server.hosts.photoshop module
------------------------------------------------------
-
-.. automodule:: pype.modules.websocket_server.hosts.photoshop
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.websocket_server.rst b/docs/source/pype.modules.websocket_server.rst
deleted file mode 100644
index a83d371df1..0000000000
--- a/docs/source/pype.modules.websocket_server.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.modules.websocket\_server package
-======================================
-
-.. automodule:: pype.modules.websocket_server
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.modules.websocket_server.hosts
-
-Submodules
-----------
-
-pype.modules.websocket\_server.websocket\_server module
--------------------------------------------------------
-
-.. automodule:: pype.modules.websocket_server.websocket_server
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules.websocket_server.websocket_server.rst b/docs/source/pype.modules.websocket_server.websocket_server.rst
deleted file mode 100644
index 354c9e6cf9..0000000000
--- a/docs/source/pype.modules.websocket_server.websocket_server.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules.websocket\_server.websocket\_server module
-=======================================================
-
-.. automodule:: pype.modules.websocket_server.websocket_server
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.modules_manager.rst b/docs/source/pype.modules_manager.rst
deleted file mode 100644
index a5f2327d65..0000000000
--- a/docs/source/pype.modules_manager.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.modules\_manager module
-============================
-
-.. automodule:: pype.modules_manager
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugin.rst b/docs/source/pype.plugin.rst
deleted file mode 100644
index c20bb77b2b..0000000000
--- a/docs/source/pype.plugin.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugin module
-==================
-
-.. automodule:: pype.plugin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_animation.rst b/docs/source/pype.plugins.maya.publish.collect_animation.rst
deleted file mode 100644
index 497c497057..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_animation.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_animation module
-===================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_animation
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_ass.rst b/docs/source/pype.plugins.maya.publish.collect_ass.rst
deleted file mode 100644
index a44e61ce98..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_ass.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_ass module
-=============================================
-
-.. automodule:: pype.plugins.maya.publish.collect_ass
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_assembly.rst b/docs/source/pype.plugins.maya.publish.collect_assembly.rst
deleted file mode 100644
index 5baa91818b..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_assembly.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_assembly module
-==================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_assembly
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_file_dependencies.rst b/docs/source/pype.plugins.maya.publish.collect_file_dependencies.rst
deleted file mode 100644
index efe857140e..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_file_dependencies.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_file\_dependencies module
-============================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_file_dependencies
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_ftrack_family.rst b/docs/source/pype.plugins.maya.publish.collect_ftrack_family.rst
deleted file mode 100644
index 872bbc69a4..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_ftrack_family.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_ftrack\_family module
-========================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_ftrack_family
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_history.rst b/docs/source/pype.plugins.maya.publish.collect_history.rst
deleted file mode 100644
index 5a98778c24..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_history.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_history module
-=================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_history
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_instances.rst b/docs/source/pype.plugins.maya.publish.collect_instances.rst
deleted file mode 100644
index 33c8b97597..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_instances.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_instances module
-===================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_instances
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_look.rst b/docs/source/pype.plugins.maya.publish.collect_look.rst
deleted file mode 100644
index 234fcf20d1..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_look.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_look module
-==============================================
-
-.. automodule:: pype.plugins.maya.publish.collect_look
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_maya_units.rst b/docs/source/pype.plugins.maya.publish.collect_maya_units.rst
deleted file mode 100644
index 0cb01b0fa7..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_maya_units.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_maya\_units module
-=====================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_maya_units
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_maya_workspace.rst b/docs/source/pype.plugins.maya.publish.collect_maya_workspace.rst
deleted file mode 100644
index 7447052004..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_maya_workspace.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_maya\_workspace module
-=========================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_maya_workspace
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_mayaascii.rst b/docs/source/pype.plugins.maya.publish.collect_mayaascii.rst
deleted file mode 100644
index 14fe826229..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_mayaascii.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_mayaascii module
-===================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_mayaascii
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_model.rst b/docs/source/pype.plugins.maya.publish.collect_model.rst
deleted file mode 100644
index b30bf3fb22..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_model.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_model module
-===============================================
-
-.. automodule:: pype.plugins.maya.publish.collect_model
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_remove_marked.rst b/docs/source/pype.plugins.maya.publish.collect_remove_marked.rst
deleted file mode 100644
index a0bf9498d7..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_remove_marked.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_remove\_marked module
-========================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_remove_marked
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_render.rst b/docs/source/pype.plugins.maya.publish.collect_render.rst
deleted file mode 100644
index 6de8827119..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_render.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_render module
-================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_render
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_render_layer_aovs.rst b/docs/source/pype.plugins.maya.publish.collect_render_layer_aovs.rst
deleted file mode 100644
index ab511fc5dd..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_render_layer_aovs.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_render\_layer\_aovs module
-=============================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_render_layer_aovs
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_renderable_camera.rst b/docs/source/pype.plugins.maya.publish.collect_renderable_camera.rst
deleted file mode 100644
index c98e8000a1..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_renderable_camera.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_renderable\_camera module
-============================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_renderable_camera
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_review.rst b/docs/source/pype.plugins.maya.publish.collect_review.rst
deleted file mode 100644
index d73127aa85..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_review.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_review module
-================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_review
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_rig.rst b/docs/source/pype.plugins.maya.publish.collect_rig.rst
deleted file mode 100644
index e7c0528482..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_rig.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_rig module
-=============================================
-
-.. automodule:: pype.plugins.maya.publish.collect_rig
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_scene.rst b/docs/source/pype.plugins.maya.publish.collect_scene.rst
deleted file mode 100644
index c5c2fef222..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_scene.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_scene module
-===============================================
-
-.. automodule:: pype.plugins.maya.publish.collect_scene
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_unreal_staticmesh.rst b/docs/source/pype.plugins.maya.publish.collect_unreal_staticmesh.rst
deleted file mode 100644
index 673f0865fd..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_unreal_staticmesh.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_unreal\_staticmesh module
-============================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_unreal_staticmesh
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_workscene_fps.rst b/docs/source/pype.plugins.maya.publish.collect_workscene_fps.rst
deleted file mode 100644
index ed4386a7ba..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_workscene_fps.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_workscene\_fps module
-========================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_workscene_fps
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_yeti_cache.rst b/docs/source/pype.plugins.maya.publish.collect_yeti_cache.rst
deleted file mode 100644
index 32ab50baca..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_yeti_cache.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_yeti\_cache module
-=====================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_yeti_cache
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.collect_yeti_rig.rst b/docs/source/pype.plugins.maya.publish.collect_yeti_rig.rst
deleted file mode 100644
index 8cf968b7c5..0000000000
--- a/docs/source/pype.plugins.maya.publish.collect_yeti_rig.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.collect\_yeti\_rig module
-===================================================
-
-.. automodule:: pype.plugins.maya.publish.collect_yeti_rig
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.determine_future_version.rst b/docs/source/pype.plugins.maya.publish.determine_future_version.rst
deleted file mode 100644
index 55c6155680..0000000000
--- a/docs/source/pype.plugins.maya.publish.determine_future_version.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.determine\_future\_version module
-===========================================================
-
-.. automodule:: pype.plugins.maya.publish.determine_future_version
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_animation.rst b/docs/source/pype.plugins.maya.publish.extract_animation.rst
deleted file mode 100644
index 3649723042..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_animation.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_animation module
-===================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_animation
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_ass.rst b/docs/source/pype.plugins.maya.publish.extract_ass.rst
deleted file mode 100644
index be8123e5d7..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_ass.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_ass module
-=============================================
-
-.. automodule:: pype.plugins.maya.publish.extract_ass
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_assembly.rst b/docs/source/pype.plugins.maya.publish.extract_assembly.rst
deleted file mode 100644
index b36e8f6d30..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_assembly.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_assembly module
-==================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_assembly
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_assproxy.rst b/docs/source/pype.plugins.maya.publish.extract_assproxy.rst
deleted file mode 100644
index fc97a2ee46..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_assproxy.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_assproxy module
-==================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_assproxy
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_camera_alembic.rst b/docs/source/pype.plugins.maya.publish.extract_camera_alembic.rst
deleted file mode 100644
index a9df3da011..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_camera_alembic.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_camera\_alembic module
-=========================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_camera_alembic
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_camera_mayaScene.rst b/docs/source/pype.plugins.maya.publish.extract_camera_mayaScene.rst
deleted file mode 100644
index db1799f52f..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_camera_mayaScene.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_camera\_mayaScene module
-===========================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_camera_mayaScene
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_fbx.rst b/docs/source/pype.plugins.maya.publish.extract_fbx.rst
deleted file mode 100644
index fffd5a6394..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_fbx.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_fbx module
-=============================================
-
-.. automodule:: pype.plugins.maya.publish.extract_fbx
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_look.rst b/docs/source/pype.plugins.maya.publish.extract_look.rst
deleted file mode 100644
index f2708678ce..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_look.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_look module
-==============================================
-
-.. automodule:: pype.plugins.maya.publish.extract_look
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_maya_scene_raw.rst b/docs/source/pype.plugins.maya.publish.extract_maya_scene_raw.rst
deleted file mode 100644
index 1e080dd0eb..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_maya_scene_raw.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_maya\_scene\_raw module
-==========================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_maya_scene_raw
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_model.rst b/docs/source/pype.plugins.maya.publish.extract_model.rst
deleted file mode 100644
index c78b49c777..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_model.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_model module
-===============================================
-
-.. automodule:: pype.plugins.maya.publish.extract_model
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_playblast.rst b/docs/source/pype.plugins.maya.publish.extract_playblast.rst
deleted file mode 100644
index 1aa284b370..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_playblast.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_playblast module
-===================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_playblast
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_pointcache.rst b/docs/source/pype.plugins.maya.publish.extract_pointcache.rst
deleted file mode 100644
index 97ebde4933..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_pointcache.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_pointcache module
-====================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_pointcache
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_rendersetup.rst b/docs/source/pype.plugins.maya.publish.extract_rendersetup.rst
deleted file mode 100644
index 86cb178f42..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_rendersetup.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_rendersetup module
-=====================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_rendersetup
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_rig.rst b/docs/source/pype.plugins.maya.publish.extract_rig.rst
deleted file mode 100644
index f6419c9473..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_rig.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_rig module
-=============================================
-
-.. automodule:: pype.plugins.maya.publish.extract_rig
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_thumbnail.rst b/docs/source/pype.plugins.maya.publish.extract_thumbnail.rst
deleted file mode 100644
index 2d03e11d55..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_thumbnail.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_thumbnail module
-===================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_thumbnail
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_vrayproxy.rst b/docs/source/pype.plugins.maya.publish.extract_vrayproxy.rst
deleted file mode 100644
index 5439ff59ca..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_vrayproxy.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_vrayproxy module
-===================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_vrayproxy
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_yeti_cache.rst b/docs/source/pype.plugins.maya.publish.extract_yeti_cache.rst
deleted file mode 100644
index 7ad84dfc70..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_yeti_cache.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_yeti\_cache module
-=====================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_yeti_cache
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.extract_yeti_rig.rst b/docs/source/pype.plugins.maya.publish.extract_yeti_rig.rst
deleted file mode 100644
index 76d483d91b..0000000000
--- a/docs/source/pype.plugins.maya.publish.extract_yeti_rig.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.extract\_yeti\_rig module
-===================================================
-
-.. automodule:: pype.plugins.maya.publish.extract_yeti_rig
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.increment_current_file_deadline.rst b/docs/source/pype.plugins.maya.publish.increment_current_file_deadline.rst
deleted file mode 100644
index 97126a6c77..0000000000
--- a/docs/source/pype.plugins.maya.publish.increment_current_file_deadline.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.increment\_current\_file\_deadline module
-===================================================================
-
-.. automodule:: pype.plugins.maya.publish.increment_current_file_deadline
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.rst b/docs/source/pype.plugins.maya.publish.rst
deleted file mode 100644
index dba0a9118c..0000000000
--- a/docs/source/pype.plugins.maya.publish.rst
+++ /dev/null
@@ -1,146 +0,0 @@
-pype.plugins.maya.publish package
-=================================
-
-.. automodule:: pype.plugins.maya.publish
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-.. toctree::
- :maxdepth: 10
-
- pype.plugins.maya.publish.collect_animation
- pype.plugins.maya.publish.collect_ass
- pype.plugins.maya.publish.collect_assembly
- pype.plugins.maya.publish.collect_file_dependencies
- pype.plugins.maya.publish.collect_ftrack_family
- pype.plugins.maya.publish.collect_history
- pype.plugins.maya.publish.collect_instances
- pype.plugins.maya.publish.collect_look
- pype.plugins.maya.publish.collect_maya_units
- pype.plugins.maya.publish.collect_maya_workspace
- pype.plugins.maya.publish.collect_mayaascii
- pype.plugins.maya.publish.collect_model
- pype.plugins.maya.publish.collect_remove_marked
- pype.plugins.maya.publish.collect_render
- pype.plugins.maya.publish.collect_render_layer_aovs
- pype.plugins.maya.publish.collect_renderable_camera
- pype.plugins.maya.publish.collect_review
- pype.plugins.maya.publish.collect_rig
- pype.plugins.maya.publish.collect_scene
- pype.plugins.maya.publish.collect_unreal_staticmesh
- pype.plugins.maya.publish.collect_workscene_fps
- pype.plugins.maya.publish.collect_yeti_cache
- pype.plugins.maya.publish.collect_yeti_rig
- pype.plugins.maya.publish.determine_future_version
- pype.plugins.maya.publish.extract_animation
- pype.plugins.maya.publish.extract_ass
- pype.plugins.maya.publish.extract_assembly
- pype.plugins.maya.publish.extract_assproxy
- pype.plugins.maya.publish.extract_camera_alembic
- pype.plugins.maya.publish.extract_camera_mayaScene
- pype.plugins.maya.publish.extract_fbx
- pype.plugins.maya.publish.extract_look
- pype.plugins.maya.publish.extract_maya_scene_raw
- pype.plugins.maya.publish.extract_model
- pype.plugins.maya.publish.extract_playblast
- pype.plugins.maya.publish.extract_pointcache
- pype.plugins.maya.publish.extract_rendersetup
- pype.plugins.maya.publish.extract_rig
- pype.plugins.maya.publish.extract_thumbnail
- pype.plugins.maya.publish.extract_vrayproxy
- pype.plugins.maya.publish.extract_yeti_cache
- pype.plugins.maya.publish.extract_yeti_rig
- pype.plugins.maya.publish.increment_current_file_deadline
- pype.plugins.maya.publish.save_scene
- pype.plugins.maya.publish.submit_maya_deadline
- pype.plugins.maya.publish.submit_maya_muster
- pype.plugins.maya.publish.validate_animation_content
- pype.plugins.maya.publish.validate_animation_out_set_related_node_ids
- pype.plugins.maya.publish.validate_ass_relative_paths
- pype.plugins.maya.publish.validate_assembly_name
- pype.plugins.maya.publish.validate_assembly_namespaces
- pype.plugins.maya.publish.validate_assembly_transforms
- pype.plugins.maya.publish.validate_attributes
- pype.plugins.maya.publish.validate_camera_attributes
- pype.plugins.maya.publish.validate_camera_contents
- pype.plugins.maya.publish.validate_color_sets
- pype.plugins.maya.publish.validate_current_renderlayer_renderable
- pype.plugins.maya.publish.validate_deadline_connection
- pype.plugins.maya.publish.validate_frame_range
- pype.plugins.maya.publish.validate_instance_has_members
- pype.plugins.maya.publish.validate_instance_subset
- pype.plugins.maya.publish.validate_instancer_content
- pype.plugins.maya.publish.validate_instancer_frame_ranges
- pype.plugins.maya.publish.validate_joints_hidden
- pype.plugins.maya.publish.validate_look_contents
- pype.plugins.maya.publish.validate_look_default_shaders_connections
- pype.plugins.maya.publish.validate_look_id_reference_edits
- pype.plugins.maya.publish.validate_look_members_unique
- pype.plugins.maya.publish.validate_look_no_default_shaders
- pype.plugins.maya.publish.validate_look_sets
- pype.plugins.maya.publish.validate_look_shading_group
- pype.plugins.maya.publish.validate_look_single_shader
- pype.plugins.maya.publish.validate_maya_units
- pype.plugins.maya.publish.validate_mesh_arnold_attributes
- pype.plugins.maya.publish.validate_mesh_has_uv
- pype.plugins.maya.publish.validate_mesh_lamina_faces
- pype.plugins.maya.publish.validate_mesh_no_negative_scale
- pype.plugins.maya.publish.validate_mesh_non_manifold
- pype.plugins.maya.publish.validate_mesh_non_zero_edge
- pype.plugins.maya.publish.validate_mesh_normals_unlocked
- pype.plugins.maya.publish.validate_mesh_overlapping_uvs
- pype.plugins.maya.publish.validate_mesh_shader_connections
- pype.plugins.maya.publish.validate_mesh_single_uv_set
- pype.plugins.maya.publish.validate_mesh_uv_set_map1
- pype.plugins.maya.publish.validate_mesh_vertices_have_edges
- pype.plugins.maya.publish.validate_model_content
- pype.plugins.maya.publish.validate_model_name
- pype.plugins.maya.publish.validate_muster_connection
- pype.plugins.maya.publish.validate_no_animation
- pype.plugins.maya.publish.validate_no_default_camera
- pype.plugins.maya.publish.validate_no_namespace
- pype.plugins.maya.publish.validate_no_null_transforms
- pype.plugins.maya.publish.validate_no_unknown_nodes
- pype.plugins.maya.publish.validate_no_vraymesh
- pype.plugins.maya.publish.validate_node_ids
- pype.plugins.maya.publish.validate_node_ids_deformed_shapes
- pype.plugins.maya.publish.validate_node_ids_in_database
- pype.plugins.maya.publish.validate_node_ids_related
- pype.plugins.maya.publish.validate_node_ids_unique
- pype.plugins.maya.publish.validate_node_no_ghosting
- pype.plugins.maya.publish.validate_render_image_rule
- pype.plugins.maya.publish.validate_render_no_default_cameras
- pype.plugins.maya.publish.validate_render_single_camera
- pype.plugins.maya.publish.validate_renderlayer_aovs
- pype.plugins.maya.publish.validate_rendersettings
- pype.plugins.maya.publish.validate_resources
- pype.plugins.maya.publish.validate_rig_contents
- pype.plugins.maya.publish.validate_rig_controllers
- pype.plugins.maya.publish.validate_rig_controllers_arnold_attributes
- pype.plugins.maya.publish.validate_rig_out_set_node_ids
- pype.plugins.maya.publish.validate_rig_output_ids
- pype.plugins.maya.publish.validate_scene_set_workspace
- pype.plugins.maya.publish.validate_shader_name
- pype.plugins.maya.publish.validate_shape_default_names
- pype.plugins.maya.publish.validate_shape_render_stats
- pype.plugins.maya.publish.validate_single_assembly
- pype.plugins.maya.publish.validate_skinCluster_deformer_set
- pype.plugins.maya.publish.validate_step_size
- pype.plugins.maya.publish.validate_transform_naming_suffix
- pype.plugins.maya.publish.validate_transform_zero
- pype.plugins.maya.publish.validate_unicode_strings
- pype.plugins.maya.publish.validate_unreal_mesh_triangulated
- pype.plugins.maya.publish.validate_unreal_staticmesh_naming
- pype.plugins.maya.publish.validate_unreal_up_axis
- pype.plugins.maya.publish.validate_vray_distributed_rendering
- pype.plugins.maya.publish.validate_vray_translator_settings
- pype.plugins.maya.publish.validate_vrayproxy
- pype.plugins.maya.publish.validate_vrayproxy_members
- pype.plugins.maya.publish.validate_yeti_renderscript_callbacks
- pype.plugins.maya.publish.validate_yeti_rig_cache_state
- pype.plugins.maya.publish.validate_yeti_rig_input_in_instance
- pype.plugins.maya.publish.validate_yeti_rig_settings
diff --git a/docs/source/pype.plugins.maya.publish.save_scene.rst b/docs/source/pype.plugins.maya.publish.save_scene.rst
deleted file mode 100644
index 2537bca03d..0000000000
--- a/docs/source/pype.plugins.maya.publish.save_scene.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.save\_scene module
-============================================
-
-.. automodule:: pype.plugins.maya.publish.save_scene
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.submit_maya_deadline.rst b/docs/source/pype.plugins.maya.publish.submit_maya_deadline.rst
deleted file mode 100644
index 0e521cec4e..0000000000
--- a/docs/source/pype.plugins.maya.publish.submit_maya_deadline.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.submit\_maya\_deadline module
-=======================================================
-
-.. automodule:: pype.plugins.maya.publish.submit_maya_deadline
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.submit_maya_muster.rst b/docs/source/pype.plugins.maya.publish.submit_maya_muster.rst
deleted file mode 100644
index 4ae263e157..0000000000
--- a/docs/source/pype.plugins.maya.publish.submit_maya_muster.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.submit\_maya\_muster module
-=====================================================
-
-.. automodule:: pype.plugins.maya.publish.submit_maya_muster
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_animation_content.rst b/docs/source/pype.plugins.maya.publish.validate_animation_content.rst
deleted file mode 100644
index 65191bb957..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_animation_content.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_animation\_content module
-=============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_animation_content
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_animation_out_set_related_node_ids.rst b/docs/source/pype.plugins.maya.publish.validate_animation_out_set_related_node_ids.rst
deleted file mode 100644
index ea289e84ed..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_animation_out_set_related_node_ids.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_animation\_out\_set\_related\_node\_ids module
-==================================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_animation_out_set_related_node_ids
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_ass_relative_paths.rst b/docs/source/pype.plugins.maya.publish.validate_ass_relative_paths.rst
deleted file mode 100644
index f35ef916cc..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_ass_relative_paths.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_ass\_relative\_paths module
-===============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_ass_relative_paths
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_assembly_name.rst b/docs/source/pype.plugins.maya.publish.validate_assembly_name.rst
deleted file mode 100644
index c8178226b2..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_assembly_name.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_assembly\_name module
-=========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_assembly_name
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_assembly_namespaces.rst b/docs/source/pype.plugins.maya.publish.validate_assembly_namespaces.rst
deleted file mode 100644
index 847b90281e..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_assembly_namespaces.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_assembly\_namespaces module
-===============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_assembly_namespaces
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_assembly_transforms.rst b/docs/source/pype.plugins.maya.publish.validate_assembly_transforms.rst
deleted file mode 100644
index b4348a2908..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_assembly_transforms.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_assembly\_transforms module
-===============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_assembly_transforms
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_attributes.rst b/docs/source/pype.plugins.maya.publish.validate_attributes.rst
deleted file mode 100644
index 862820a7c0..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_attributes.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_attributes module
-=====================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_attributes
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_camera_attributes.rst b/docs/source/pype.plugins.maya.publish.validate_camera_attributes.rst
deleted file mode 100644
index 054198f812..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_camera_attributes.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_camera\_attributes module
-=============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_camera_attributes
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_camera_contents.rst b/docs/source/pype.plugins.maya.publish.validate_camera_contents.rst
deleted file mode 100644
index 9cf6604f7a..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_camera_contents.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_camera\_contents module
-===========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_camera_contents
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_color_sets.rst b/docs/source/pype.plugins.maya.publish.validate_color_sets.rst
deleted file mode 100644
index 59bb5607bf..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_color_sets.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_color\_sets module
-======================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_color_sets
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_current_renderlayer_renderable.rst b/docs/source/pype.plugins.maya.publish.validate_current_renderlayer_renderable.rst
deleted file mode 100644
index 31c52477aa..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_current_renderlayer_renderable.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_current\_renderlayer\_renderable module
-===========================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_current_renderlayer_renderable
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_deadline_connection.rst b/docs/source/pype.plugins.maya.publish.validate_deadline_connection.rst
deleted file mode 100644
index 3f8c4b6313..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_deadline_connection.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_deadline\_connection module
-===============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_deadline_connection
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_frame_range.rst b/docs/source/pype.plugins.maya.publish.validate_frame_range.rst
deleted file mode 100644
index 0ccc8ed1cd..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_frame_range.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_frame\_range module
-=======================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_frame_range
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_instance_has_members.rst b/docs/source/pype.plugins.maya.publish.validate_instance_has_members.rst
deleted file mode 100644
index 862d32f114..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_instance_has_members.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_instance\_has\_members module
-=================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_instance_has_members
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_instance_subset.rst b/docs/source/pype.plugins.maya.publish.validate_instance_subset.rst
deleted file mode 100644
index f71febb73c..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_instance_subset.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_instance\_subset module
-===========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_instance_subset
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_instancer_content.rst b/docs/source/pype.plugins.maya.publish.validate_instancer_content.rst
deleted file mode 100644
index 761889dd4d..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_instancer_content.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_instancer\_content module
-=============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_instancer_content
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_instancer_frame_ranges.rst b/docs/source/pype.plugins.maya.publish.validate_instancer_frame_ranges.rst
deleted file mode 100644
index 85338c3e2d..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_instancer_frame_ranges.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_instancer\_frame\_ranges module
-===================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_instancer_frame_ranges
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_joints_hidden.rst b/docs/source/pype.plugins.maya.publish.validate_joints_hidden.rst
deleted file mode 100644
index ede5af0c67..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_joints_hidden.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_joints\_hidden module
-=========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_joints_hidden
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_look_contents.rst b/docs/source/pype.plugins.maya.publish.validate_look_contents.rst
deleted file mode 100644
index 946f924fb3..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_look_contents.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_look\_contents module
-=========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_look_contents
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_look_default_shaders_connections.rst b/docs/source/pype.plugins.maya.publish.validate_look_default_shaders_connections.rst
deleted file mode 100644
index e293cfc0f1..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_look_default_shaders_connections.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_look\_default\_shaders\_connections module
-==============================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_look_default_shaders_connections
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_look_id_reference_edits.rst b/docs/source/pype.plugins.maya.publish.validate_look_id_reference_edits.rst
deleted file mode 100644
index 007f4e2d03..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_look_id_reference_edits.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_look\_id\_reference\_edits module
-=====================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_look_id_reference_edits
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_look_members_unique.rst b/docs/source/pype.plugins.maya.publish.validate_look_members_unique.rst
deleted file mode 100644
index 3378e8a0f6..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_look_members_unique.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_look\_members\_unique module
-================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_look_members_unique
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_look_no_default_shaders.rst b/docs/source/pype.plugins.maya.publish.validate_look_no_default_shaders.rst
deleted file mode 100644
index 662e2c7621..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_look_no_default_shaders.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_look\_no\_default\_shaders module
-=====================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_look_no_default_shaders
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_look_sets.rst b/docs/source/pype.plugins.maya.publish.validate_look_sets.rst
deleted file mode 100644
index 5427331568..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_look_sets.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_look\_sets module
-=====================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_look_sets
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_look_shading_group.rst b/docs/source/pype.plugins.maya.publish.validate_look_shading_group.rst
deleted file mode 100644
index 259f4952b7..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_look_shading_group.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_look\_shading\_group module
-===============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_look_shading_group
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_look_single_shader.rst b/docs/source/pype.plugins.maya.publish.validate_look_single_shader.rst
deleted file mode 100644
index fa43283416..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_look_single_shader.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_look\_single\_shader module
-===============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_look_single_shader
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_maya_units.rst b/docs/source/pype.plugins.maya.publish.validate_maya_units.rst
deleted file mode 100644
index 16af19f6d9..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_maya_units.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_maya\_units module
-======================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_maya_units
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_mesh_arnold_attributes.rst b/docs/source/pype.plugins.maya.publish.validate_mesh_arnold_attributes.rst
deleted file mode 100644
index ef18ad1457..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_mesh_arnold_attributes.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_mesh\_arnold\_attributes module
-===================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_mesh_arnold_attributes
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_mesh_has_uv.rst b/docs/source/pype.plugins.maya.publish.validate_mesh_has_uv.rst
deleted file mode 100644
index c6af7063c3..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_mesh_has_uv.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_mesh\_has\_uv module
-========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_mesh_has_uv
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_mesh_lamina_faces.rst b/docs/source/pype.plugins.maya.publish.validate_mesh_lamina_faces.rst
deleted file mode 100644
index 006488e77f..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_mesh_lamina_faces.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_mesh\_lamina\_faces module
-==============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_mesh_lamina_faces
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_mesh_no_negative_scale.rst b/docs/source/pype.plugins.maya.publish.validate_mesh_no_negative_scale.rst
deleted file mode 100644
index 8720f3d018..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_mesh_no_negative_scale.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_mesh\_no\_negative\_scale module
-====================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_mesh_no_negative_scale
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_mesh_non_manifold.rst b/docs/source/pype.plugins.maya.publish.validate_mesh_non_manifold.rst
deleted file mode 100644
index a69a4c6fc4..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_mesh_non_manifold.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_mesh\_non\_manifold module
-==============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_mesh_non_manifold
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_mesh_non_zero_edge.rst b/docs/source/pype.plugins.maya.publish.validate_mesh_non_zero_edge.rst
deleted file mode 100644
index 89ea60d1bc..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_mesh_non_zero_edge.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_mesh\_non\_zero\_edge module
-================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_mesh_non_zero_edge
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_mesh_normals_unlocked.rst b/docs/source/pype.plugins.maya.publish.validate_mesh_normals_unlocked.rst
deleted file mode 100644
index 7dfbd0717d..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_mesh_normals_unlocked.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_mesh\_normals\_unlocked module
-==================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_mesh_normals_unlocked
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_mesh_overlapping_uvs.rst b/docs/source/pype.plugins.maya.publish.validate_mesh_overlapping_uvs.rst
deleted file mode 100644
index f5df633124..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_mesh_overlapping_uvs.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_mesh\_overlapping\_uvs module
-=================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_mesh_overlapping_uvs
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_mesh_shader_connections.rst b/docs/source/pype.plugins.maya.publish.validate_mesh_shader_connections.rst
deleted file mode 100644
index b3cd77ab2a..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_mesh_shader_connections.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_mesh\_shader\_connections module
-====================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_mesh_shader_connections
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_mesh_single_uv_set.rst b/docs/source/pype.plugins.maya.publish.validate_mesh_single_uv_set.rst
deleted file mode 100644
index 29a1217437..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_mesh_single_uv_set.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_mesh\_single\_uv\_set module
-================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_mesh_single_uv_set
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_mesh_uv_set_map1.rst b/docs/source/pype.plugins.maya.publish.validate_mesh_uv_set_map1.rst
deleted file mode 100644
index 49d1b22497..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_mesh_uv_set_map1.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_mesh\_uv\_set\_map1 module
-==============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_mesh_uv_set_map1
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_mesh_vertices_have_edges.rst b/docs/source/pype.plugins.maya.publish.validate_mesh_vertices_have_edges.rst
deleted file mode 100644
index 99e3047e3d..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_mesh_vertices_have_edges.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_mesh\_vertices\_have\_edges module
-======================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_mesh_vertices_have_edges
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_model_content.rst b/docs/source/pype.plugins.maya.publish.validate_model_content.rst
deleted file mode 100644
index dc0a415718..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_model_content.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_model\_content module
-=========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_model_content
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_model_name.rst b/docs/source/pype.plugins.maya.publish.validate_model_name.rst
deleted file mode 100644
index ea78ceea70..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_model_name.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_model\_name module
-======================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_model_name
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_muster_connection.rst b/docs/source/pype.plugins.maya.publish.validate_muster_connection.rst
deleted file mode 100644
index 4a4a1e926b..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_muster_connection.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_muster\_connection module
-=============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_muster_connection
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_no_animation.rst b/docs/source/pype.plugins.maya.publish.validate_no_animation.rst
deleted file mode 100644
index b42021369d..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_no_animation.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_no\_animation module
-========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_no_animation
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_no_default_camera.rst b/docs/source/pype.plugins.maya.publish.validate_no_default_camera.rst
deleted file mode 100644
index 59544369f6..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_no_default_camera.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_no\_default\_camera module
-==============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_no_default_camera
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_no_namespace.rst b/docs/source/pype.plugins.maya.publish.validate_no_namespace.rst
deleted file mode 100644
index bdf4ceb324..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_no_namespace.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_no\_namespace module
-========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_no_namespace
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_no_null_transforms.rst b/docs/source/pype.plugins.maya.publish.validate_no_null_transforms.rst
deleted file mode 100644
index 12beed8c33..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_no_null_transforms.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_no\_null\_transforms module
-===============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_no_null_transforms
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_no_unknown_nodes.rst b/docs/source/pype.plugins.maya.publish.validate_no_unknown_nodes.rst
deleted file mode 100644
index 12c977dbb9..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_no_unknown_nodes.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_no\_unknown\_nodes module
-=============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_no_unknown_nodes
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_no_vraymesh.rst b/docs/source/pype.plugins.maya.publish.validate_no_vraymesh.rst
deleted file mode 100644
index a1a0b9ee64..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_no_vraymesh.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_no\_vraymesh module
-=======================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_no_vraymesh
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_node_ids.rst b/docs/source/pype.plugins.maya.publish.validate_node_ids.rst
deleted file mode 100644
index 7b1d79100f..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_node_ids.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_node\_ids module
-====================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_node_ids
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_node_ids_deformed_shapes.rst b/docs/source/pype.plugins.maya.publish.validate_node_ids_deformed_shapes.rst
deleted file mode 100644
index 90ef81c5b5..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_node_ids_deformed_shapes.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_node\_ids\_deformed\_shapes module
-======================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_node_ids_deformed_shapes
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_node_ids_in_database.rst b/docs/source/pype.plugins.maya.publish.validate_node_ids_in_database.rst
deleted file mode 100644
index 5eb0047d16..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_node_ids_in_database.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_node\_ids\_in\_database module
-==================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_node_ids_in_database
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_node_ids_related.rst b/docs/source/pype.plugins.maya.publish.validate_node_ids_related.rst
deleted file mode 100644
index 1f030462ae..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_node_ids_related.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_node\_ids\_related module
-=============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_node_ids_related
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_node_ids_unique.rst b/docs/source/pype.plugins.maya.publish.validate_node_ids_unique.rst
deleted file mode 100644
index 20ba3a3a6d..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_node_ids_unique.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_node\_ids\_unique module
-============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_node_ids_unique
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_node_no_ghosting.rst b/docs/source/pype.plugins.maya.publish.validate_node_no_ghosting.rst
deleted file mode 100644
index 8315888630..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_node_no_ghosting.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_node\_no\_ghosting module
-=============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_node_no_ghosting
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_render_image_rule.rst b/docs/source/pype.plugins.maya.publish.validate_render_image_rule.rst
deleted file mode 100644
index 88870a9ea8..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_render_image_rule.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_render\_image\_rule module
-==============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_render_image_rule
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_render_no_default_cameras.rst b/docs/source/pype.plugins.maya.publish.validate_render_no_default_cameras.rst
deleted file mode 100644
index b464dbeab6..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_render_no_default_cameras.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_render\_no\_default\_cameras module
-=======================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_render_no_default_cameras
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_render_single_camera.rst b/docs/source/pype.plugins.maya.publish.validate_render_single_camera.rst
deleted file mode 100644
index 60a0cbd6fb..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_render_single_camera.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_render\_single\_camera module
-=================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_render_single_camera
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_renderlayer_aovs.rst b/docs/source/pype.plugins.maya.publish.validate_renderlayer_aovs.rst
deleted file mode 100644
index 65d5181065..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_renderlayer_aovs.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_renderlayer\_aovs module
-============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_renderlayer_aovs
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_rendersettings.rst b/docs/source/pype.plugins.maya.publish.validate_rendersettings.rst
deleted file mode 100644
index fce7dba5b8..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_rendersettings.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_rendersettings module
-=========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_rendersettings
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_resources.rst b/docs/source/pype.plugins.maya.publish.validate_resources.rst
deleted file mode 100644
index 0a866acdbb..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_resources.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_resources module
-====================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_resources
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_rig_contents.rst b/docs/source/pype.plugins.maya.publish.validate_rig_contents.rst
deleted file mode 100644
index dbd7d84bed..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_rig_contents.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_rig\_contents module
-========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_rig_contents
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_rig_controllers.rst b/docs/source/pype.plugins.maya.publish.validate_rig_controllers.rst
deleted file mode 100644
index 3bf075e8ad..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_rig_controllers.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_rig\_controllers module
-===========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_rig_controllers
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_rig_controllers_arnold_attributes.rst b/docs/source/pype.plugins.maya.publish.validate_rig_controllers_arnold_attributes.rst
deleted file mode 100644
index 67e9256f3a..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_rig_controllers_arnold_attributes.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_rig\_controllers\_arnold\_attributes module
-===============================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_rig_controllers_arnold_attributes
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_rig_out_set_node_ids.rst b/docs/source/pype.plugins.maya.publish.validate_rig_out_set_node_ids.rst
deleted file mode 100644
index e4f1cfc428..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_rig_out_set_node_ids.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_rig\_out\_set\_node\_ids module
-===================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_rig_out_set_node_ids
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_rig_output_ids.rst b/docs/source/pype.plugins.maya.publish.validate_rig_output_ids.rst
deleted file mode 100644
index e1d3b1a659..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_rig_output_ids.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_rig\_output\_ids module
-===========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_rig_output_ids
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_scene_set_workspace.rst b/docs/source/pype.plugins.maya.publish.validate_scene_set_workspace.rst
deleted file mode 100644
index daf2f152d9..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_scene_set_workspace.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_scene\_set\_workspace module
-================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_scene_set_workspace
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_shader_name.rst b/docs/source/pype.plugins.maya.publish.validate_shader_name.rst
deleted file mode 100644
index ae5b196a1d..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_shader_name.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_shader\_name module
-=======================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_shader_name
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_shape_default_names.rst b/docs/source/pype.plugins.maya.publish.validate_shape_default_names.rst
deleted file mode 100644
index 49effc932d..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_shape_default_names.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_shape\_default\_names module
-================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_shape_default_names
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_shape_render_stats.rst b/docs/source/pype.plugins.maya.publish.validate_shape_render_stats.rst
deleted file mode 100644
index 359af50a0f..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_shape_render_stats.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_shape\_render\_stats module
-===============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_shape_render_stats
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_single_assembly.rst b/docs/source/pype.plugins.maya.publish.validate_single_assembly.rst
deleted file mode 100644
index 090f57b3ff..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_single_assembly.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_single\_assembly module
-===========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_single_assembly
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_skinCluster_deformer_set.rst b/docs/source/pype.plugins.maya.publish.validate_skinCluster_deformer_set.rst
deleted file mode 100644
index 607a610097..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_skinCluster_deformer_set.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_skinCluster\_deformer\_set module
-=====================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_skinCluster_deformer_set
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_step_size.rst b/docs/source/pype.plugins.maya.publish.validate_step_size.rst
deleted file mode 100644
index bb883ea7b5..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_step_size.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_step\_size module
-=====================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_step_size
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_transform_naming_suffix.rst b/docs/source/pype.plugins.maya.publish.validate_transform_naming_suffix.rst
deleted file mode 100644
index 4d7edda78d..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_transform_naming_suffix.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_transform\_naming\_suffix module
-====================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_transform_naming_suffix
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_transform_zero.rst b/docs/source/pype.plugins.maya.publish.validate_transform_zero.rst
deleted file mode 100644
index 6d5cacfe00..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_transform_zero.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_transform\_zero module
-==========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_transform_zero
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_unicode_strings.rst b/docs/source/pype.plugins.maya.publish.validate_unicode_strings.rst
deleted file mode 100644
index 9cc17d6810..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_unicode_strings.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_unicode\_strings module
-===========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_unicode_strings
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_unreal_mesh_triangulated.rst b/docs/source/pype.plugins.maya.publish.validate_unreal_mesh_triangulated.rst
deleted file mode 100644
index 4dcb518194..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_unreal_mesh_triangulated.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_unreal\_mesh\_triangulated module
-=====================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_unreal_mesh_triangulated
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_unreal_staticmesh_naming.rst b/docs/source/pype.plugins.maya.publish.validate_unreal_staticmesh_naming.rst
deleted file mode 100644
index f7225ab395..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_unreal_staticmesh_naming.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_unreal\_staticmesh\_naming module
-=====================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_unreal_staticmesh_naming
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_unreal_up_axis.rst b/docs/source/pype.plugins.maya.publish.validate_unreal_up_axis.rst
deleted file mode 100644
index ff688c493f..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_unreal_up_axis.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_unreal\_up\_axis module
-===========================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_unreal_up_axis
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_vray_distributed_rendering.rst b/docs/source/pype.plugins.maya.publish.validate_vray_distributed_rendering.rst
deleted file mode 100644
index f5d05e6d76..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_vray_distributed_rendering.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_vray\_distributed\_rendering module
-=======================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_vray_distributed_rendering
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_vray_referenced_aovs.rst b/docs/source/pype.plugins.maya.publish.validate_vray_referenced_aovs.rst
deleted file mode 100644
index 16ad9666aa..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_vray_referenced_aovs.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_vray\_referenced\_aovs module
-=================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_vray_referenced_aovs
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_vray_translator_settings.rst b/docs/source/pype.plugins.maya.publish.validate_vray_translator_settings.rst
deleted file mode 100644
index a06a9531dd..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_vray_translator_settings.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_vray\_translator\_settings module
-=====================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_vray_translator_settings
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_vrayproxy.rst b/docs/source/pype.plugins.maya.publish.validate_vrayproxy.rst
deleted file mode 100644
index 081f58924a..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_vrayproxy.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_vrayproxy module
-====================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_vrayproxy
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_vrayproxy_members.rst b/docs/source/pype.plugins.maya.publish.validate_vrayproxy_members.rst
deleted file mode 100644
index 7c587f39b0..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_vrayproxy_members.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_vrayproxy\_members module
-=============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_vrayproxy_members
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_yeti_renderscript_callbacks.rst b/docs/source/pype.plugins.maya.publish.validate_yeti_renderscript_callbacks.rst
deleted file mode 100644
index 889d469b2f..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_yeti_renderscript_callbacks.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_yeti\_renderscript\_callbacks module
-========================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_yeti_renderscript_callbacks
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_yeti_rig_cache_state.rst b/docs/source/pype.plugins.maya.publish.validate_yeti_rig_cache_state.rst
deleted file mode 100644
index 4138b1e8a4..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_yeti_rig_cache_state.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_yeti\_rig\_cache\_state module
-==================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_yeti_rig_cache_state
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_yeti_rig_input_in_instance.rst b/docs/source/pype.plugins.maya.publish.validate_yeti_rig_input_in_instance.rst
deleted file mode 100644
index 37b862926c..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_yeti_rig_input_in_instance.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_yeti\_rig\_input\_in\_instance module
-=========================================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_yeti_rig_input_in_instance
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.publish.validate_yeti_rig_settings.rst b/docs/source/pype.plugins.maya.publish.validate_yeti_rig_settings.rst
deleted file mode 100644
index 9fd54193dc..0000000000
--- a/docs/source/pype.plugins.maya.publish.validate_yeti_rig_settings.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.plugins.maya.publish.validate\_yeti\_rig\_settings module
-==============================================================
-
-.. automodule:: pype.plugins.maya.publish.validate_yeti_rig_settings
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.plugins.maya.rst b/docs/source/pype.plugins.maya.rst
deleted file mode 100644
index 129cf5fce9..0000000000
--- a/docs/source/pype.plugins.maya.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-pype.plugins.maya package
-=========================
-
-.. automodule:: pype.plugins.maya
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 10
-
- pype.plugins.maya.publish
diff --git a/docs/source/pype.plugins.rst b/docs/source/pype.plugins.rst
deleted file mode 100644
index 8e5e45ba5d..0000000000
--- a/docs/source/pype.plugins.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-pype.plugins package
-====================
-
-.. automodule:: pype.plugins
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 10
-
- pype.plugins.maya
diff --git a/docs/source/pype.pype_commands.rst b/docs/source/pype.pype_commands.rst
deleted file mode 100644
index b8a416df7b..0000000000
--- a/docs/source/pype.pype_commands.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.pype\_commands module
-==========================
-
-.. automodule:: pype.pype_commands
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.resources.rst b/docs/source/pype.resources.rst
deleted file mode 100644
index 2fb5b92dce..0000000000
--- a/docs/source/pype.resources.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.resources package
-======================
-
-.. automodule:: pype.resources
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.rst b/docs/source/pype.rst
deleted file mode 100644
index 3589d2f3fe..0000000000
--- a/docs/source/pype.rst
+++ /dev/null
@@ -1,99 +0,0 @@
-pype package
-============
-
-.. automodule:: pype
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.hosts
- pype.lib
- pype.modules
- pype.resources
- pype.scripts
- pype.settings
- pype.tests
- pype.tools
- pype.vendor
- pype.widgets
-
-Submodules
-----------
-
-pype.action module
-------------------
-
-.. automodule:: pype.action
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.api module
----------------
-
-.. automodule:: pype.api
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.cli module
----------------
-
-.. automodule:: pype.cli
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.launcher\_actions module
------------------------------
-
-.. automodule:: pype.launcher_actions
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.modules\_manager module
-----------------------------
-
-.. automodule:: pype.modules_manager
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.plugin module
-------------------
-
-.. automodule:: pype.plugin
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.pype\_commands module
---------------------------
-
-.. automodule:: pype.pype_commands
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.setdress\_api module
--------------------------
-
-.. automodule:: pype.setdress_api
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.version module
--------------------
-
-.. automodule:: pype.version
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.export_maya_ass_job.rst b/docs/source/pype.scripts.export_maya_ass_job.rst
deleted file mode 100644
index c35cc49ddd..0000000000
--- a/docs/source/pype.scripts.export_maya_ass_job.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.export\_maya\_ass\_job module
-==========================================
-
-.. automodule:: pype.scripts.export_maya_ass_job
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.fusion_switch_shot.rst b/docs/source/pype.scripts.fusion_switch_shot.rst
deleted file mode 100644
index 39d3473d16..0000000000
--- a/docs/source/pype.scripts.fusion_switch_shot.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.fusion\_switch\_shot module
-========================================
-
-.. automodule:: pype.scripts.fusion_switch_shot
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.otio_burnin.rst b/docs/source/pype.scripts.otio_burnin.rst
deleted file mode 100644
index e6a93017f5..0000000000
--- a/docs/source/pype.scripts.otio_burnin.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.otio\_burnin module
-================================
-
-.. automodule:: pype.scripts.otio_burnin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.publish_deadline.rst b/docs/source/pype.scripts.publish_deadline.rst
deleted file mode 100644
index d134e17244..0000000000
--- a/docs/source/pype.scripts.publish_deadline.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.publish\_deadline module
-=====================================
-
-.. automodule:: pype.scripts.publish_deadline
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.publish_filesequence.rst b/docs/source/pype.scripts.publish_filesequence.rst
deleted file mode 100644
index 440d52caad..0000000000
--- a/docs/source/pype.scripts.publish_filesequence.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.publish\_filesequence module
-=========================================
-
-.. automodule:: pype.scripts.publish_filesequence
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.rst b/docs/source/pype.scripts.rst
deleted file mode 100644
index 5985771b97..0000000000
--- a/docs/source/pype.scripts.rst
+++ /dev/null
@@ -1,58 +0,0 @@
-pype.scripts package
-====================
-
-.. automodule:: pype.scripts
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.scripts.slates
-
-Submodules
-----------
-
-pype.scripts.export\_maya\_ass\_job module
-------------------------------------------
-
-.. automodule:: pype.scripts.export_maya_ass_job
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.scripts.fusion\_switch\_shot module
-----------------------------------------
-
-.. automodule:: pype.scripts.fusion_switch_shot
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.scripts.otio\_burnin module
---------------------------------
-
-.. automodule:: pype.scripts.otio_burnin
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.scripts.publish\_deadline module
--------------------------------------
-
-.. automodule:: pype.scripts.publish_deadline
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.scripts.publish\_filesequence module
------------------------------------------
-
-.. automodule:: pype.scripts.publish_filesequence
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.slates.rst b/docs/source/pype.scripts.slates.rst
deleted file mode 100644
index 74b4cb4343..0000000000
--- a/docs/source/pype.scripts.slates.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-pype.scripts.slates package
-===========================
-
-.. automodule:: pype.scripts.slates
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.scripts.slates.slate_base
diff --git a/docs/source/pype.scripts.slates.slate_base.api.rst b/docs/source/pype.scripts.slates.slate_base.api.rst
deleted file mode 100644
index 0016a5c42a..0000000000
--- a/docs/source/pype.scripts.slates.slate_base.api.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.slates.slate\_base.api module
-==========================================
-
-.. automodule:: pype.scripts.slates.slate_base.api
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.slates.slate_base.base.rst b/docs/source/pype.scripts.slates.slate_base.base.rst
deleted file mode 100644
index 5e34d654b0..0000000000
--- a/docs/source/pype.scripts.slates.slate_base.base.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.slates.slate\_base.base module
-===========================================
-
-.. automodule:: pype.scripts.slates.slate_base.base
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.slates.slate_base.example.rst b/docs/source/pype.scripts.slates.slate_base.example.rst
deleted file mode 100644
index 95ebcc835a..0000000000
--- a/docs/source/pype.scripts.slates.slate_base.example.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.slates.slate\_base.example module
-==============================================
-
-.. automodule:: pype.scripts.slates.slate_base.example
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.slates.slate_base.font_factory.rst b/docs/source/pype.scripts.slates.slate_base.font_factory.rst
deleted file mode 100644
index c53efef554..0000000000
--- a/docs/source/pype.scripts.slates.slate_base.font_factory.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.slates.slate\_base.font\_factory module
-====================================================
-
-.. automodule:: pype.scripts.slates.slate_base.font_factory
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.slates.slate_base.items.rst b/docs/source/pype.scripts.slates.slate_base.items.rst
deleted file mode 100644
index 25abb11bb9..0000000000
--- a/docs/source/pype.scripts.slates.slate_base.items.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.slates.slate\_base.items module
-============================================
-
-.. automodule:: pype.scripts.slates.slate_base.items
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.slates.slate_base.layer.rst b/docs/source/pype.scripts.slates.slate_base.layer.rst
deleted file mode 100644
index 8681e3accf..0000000000
--- a/docs/source/pype.scripts.slates.slate_base.layer.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.slates.slate\_base.layer module
-============================================
-
-.. automodule:: pype.scripts.slates.slate_base.layer
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.slates.slate_base.lib.rst b/docs/source/pype.scripts.slates.slate_base.lib.rst
deleted file mode 100644
index c4ef2c912e..0000000000
--- a/docs/source/pype.scripts.slates.slate_base.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.slates.slate\_base.lib module
-==========================================
-
-.. automodule:: pype.scripts.slates.slate_base.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.slates.slate_base.main_frame.rst b/docs/source/pype.scripts.slates.slate_base.main_frame.rst
deleted file mode 100644
index 5093c28a74..0000000000
--- a/docs/source/pype.scripts.slates.slate_base.main_frame.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.scripts.slates.slate\_base.main\_frame module
-==================================================
-
-.. automodule:: pype.scripts.slates.slate_base.main_frame
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.scripts.slates.slate_base.rst b/docs/source/pype.scripts.slates.slate_base.rst
deleted file mode 100644
index 00726c04bf..0000000000
--- a/docs/source/pype.scripts.slates.slate_base.rst
+++ /dev/null
@@ -1,74 +0,0 @@
-pype.scripts.slates.slate\_base package
-=======================================
-
-.. automodule:: pype.scripts.slates.slate_base
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.scripts.slates.slate\_base.api module
-------------------------------------------
-
-.. automodule:: pype.scripts.slates.slate_base.api
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.scripts.slates.slate\_base.base module
--------------------------------------------
-
-.. automodule:: pype.scripts.slates.slate_base.base
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.scripts.slates.slate\_base.example module
-----------------------------------------------
-
-.. automodule:: pype.scripts.slates.slate_base.example
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.scripts.slates.slate\_base.font\_factory module
-----------------------------------------------------
-
-.. automodule:: pype.scripts.slates.slate_base.font_factory
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.scripts.slates.slate\_base.items module
---------------------------------------------
-
-.. automodule:: pype.scripts.slates.slate_base.items
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.scripts.slates.slate\_base.layer module
---------------------------------------------
-
-.. automodule:: pype.scripts.slates.slate_base.layer
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.scripts.slates.slate\_base.lib module
-------------------------------------------
-
-.. automodule:: pype.scripts.slates.slate_base.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.scripts.slates.slate\_base.main\_frame module
---------------------------------------------------
-
-.. automodule:: pype.scripts.slates.slate_base.main_frame
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.setdress_api.rst b/docs/source/pype.setdress_api.rst
deleted file mode 100644
index 95638ea64d..0000000000
--- a/docs/source/pype.setdress_api.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.setdress\_api module
-=========================
-
-.. automodule:: pype.setdress_api
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.settings.constants.rst b/docs/source/pype.settings.constants.rst
deleted file mode 100644
index ac652089c8..0000000000
--- a/docs/source/pype.settings.constants.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.settings.constants module
-==============================
-
-.. automodule:: pype.settings.constants
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.settings.handlers.rst b/docs/source/pype.settings.handlers.rst
deleted file mode 100644
index 60ea0ae952..0000000000
--- a/docs/source/pype.settings.handlers.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.settings.handlers module
-=============================
-
-.. automodule:: pype.settings.handlers
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.settings.lib.rst b/docs/source/pype.settings.lib.rst
deleted file mode 100644
index d6e3e8bd06..0000000000
--- a/docs/source/pype.settings.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.settings.lib module
-========================
-
-.. automodule:: pype.settings.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.settings.rst b/docs/source/pype.settings.rst
deleted file mode 100644
index 5bf131d555..0000000000
--- a/docs/source/pype.settings.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-pype.settings package
-=====================
-
-.. automodule:: pype.settings
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.settings.lib module
-------------------------
-
-.. automodule:: pype.settings.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tests.lib.rst b/docs/source/pype.tests.lib.rst
deleted file mode 100644
index 375ebd0258..0000000000
--- a/docs/source/pype.tests.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tests.lib module
-=====================
-
-.. automodule:: pype.tests.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tests.rst b/docs/source/pype.tests.rst
deleted file mode 100644
index 3f34cdcd77..0000000000
--- a/docs/source/pype.tests.rst
+++ /dev/null
@@ -1,42 +0,0 @@
-pype.tests package
-==================
-
-.. automodule:: pype.tests
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.tests.lib module
----------------------
-
-.. automodule:: pype.tests.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tests.test\_avalon\_plugin\_presets module
------------------------------------------------
-
-.. automodule:: pype.tests.test_avalon_plugin_presets
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tests.test\_mongo\_performance module
-------------------------------------------
-
-.. automodule:: pype.tests.test_mongo_performance
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tests.test\_pyblish\_filter module
----------------------------------------
-
-.. automodule:: pype.tests.test_pyblish_filter
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tests.test_avalon_plugin_presets.rst b/docs/source/pype.tests.test_avalon_plugin_presets.rst
deleted file mode 100644
index b4ff802256..0000000000
--- a/docs/source/pype.tests.test_avalon_plugin_presets.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tests.test\_avalon\_plugin\_presets module
-===============================================
-
-.. automodule:: pype.tests.test_avalon_plugin_presets
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tests.test_lib_restructuralization.rst b/docs/source/pype.tests.test_lib_restructuralization.rst
deleted file mode 100644
index 8d426fcb6b..0000000000
--- a/docs/source/pype.tests.test_lib_restructuralization.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tests.test\_lib\_restructuralization module
-================================================
-
-.. automodule:: pype.tests.test_lib_restructuralization
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tests.test_mongo_performance.rst b/docs/source/pype.tests.test_mongo_performance.rst
deleted file mode 100644
index 4686247e59..0000000000
--- a/docs/source/pype.tests.test_mongo_performance.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tests.test\_mongo\_performance module
-==========================================
-
-.. automodule:: pype.tests.test_mongo_performance
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tests.test_pyblish_filter.rst b/docs/source/pype.tests.test_pyblish_filter.rst
deleted file mode 100644
index 196ec02433..0000000000
--- a/docs/source/pype.tests.test_pyblish_filter.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tests.test\_pyblish\_filter module
-=======================================
-
-.. automodule:: pype.tests.test_pyblish_filter
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.assetcreator.app.rst b/docs/source/pype.tools.assetcreator.app.rst
deleted file mode 100644
index b46281b07a..0000000000
--- a/docs/source/pype.tools.assetcreator.app.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.assetcreator.app module
-==================================
-
-.. automodule:: pype.tools.assetcreator.app
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.assetcreator.model.rst b/docs/source/pype.tools.assetcreator.model.rst
deleted file mode 100644
index 752791d07c..0000000000
--- a/docs/source/pype.tools.assetcreator.model.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.assetcreator.model module
-====================================
-
-.. automodule:: pype.tools.assetcreator.model
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.assetcreator.rst b/docs/source/pype.tools.assetcreator.rst
deleted file mode 100644
index b95c3b3c60..0000000000
--- a/docs/source/pype.tools.assetcreator.rst
+++ /dev/null
@@ -1,34 +0,0 @@
-pype.tools.assetcreator package
-===============================
-
-.. automodule:: pype.tools.assetcreator
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.tools.assetcreator.app module
-----------------------------------
-
-.. automodule:: pype.tools.assetcreator.app
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.assetcreator.model module
-------------------------------------
-
-.. automodule:: pype.tools.assetcreator.model
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.assetcreator.widget module
--------------------------------------
-
-.. automodule:: pype.tools.assetcreator.widget
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.assetcreator.widget.rst b/docs/source/pype.tools.assetcreator.widget.rst
deleted file mode 100644
index 23ed335306..0000000000
--- a/docs/source/pype.tools.assetcreator.widget.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.assetcreator.widget module
-=====================================
-
-.. automodule:: pype.tools.assetcreator.widget
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.launcher.actions.rst b/docs/source/pype.tools.launcher.actions.rst
deleted file mode 100644
index e2ec217d4b..0000000000
--- a/docs/source/pype.tools.launcher.actions.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.launcher.actions module
-==================================
-
-.. automodule:: pype.tools.launcher.actions
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.launcher.delegates.rst b/docs/source/pype.tools.launcher.delegates.rst
deleted file mode 100644
index e8a7519cd5..0000000000
--- a/docs/source/pype.tools.launcher.delegates.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.launcher.delegates module
-====================================
-
-.. automodule:: pype.tools.launcher.delegates
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.launcher.flickcharm.rst b/docs/source/pype.tools.launcher.flickcharm.rst
deleted file mode 100644
index 5105d3235e..0000000000
--- a/docs/source/pype.tools.launcher.flickcharm.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.launcher.flickcharm module
-=====================================
-
-.. automodule:: pype.tools.launcher.flickcharm
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.launcher.lib.rst b/docs/source/pype.tools.launcher.lib.rst
deleted file mode 100644
index 28db8a6540..0000000000
--- a/docs/source/pype.tools.launcher.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.launcher.lib module
-==============================
-
-.. automodule:: pype.tools.launcher.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.launcher.models.rst b/docs/source/pype.tools.launcher.models.rst
deleted file mode 100644
index 701826284e..0000000000
--- a/docs/source/pype.tools.launcher.models.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.launcher.models module
-=================================
-
-.. automodule:: pype.tools.launcher.models
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.launcher.rst b/docs/source/pype.tools.launcher.rst
deleted file mode 100644
index c4782bf9bb..0000000000
--- a/docs/source/pype.tools.launcher.rst
+++ /dev/null
@@ -1,66 +0,0 @@
-pype.tools.launcher package
-===========================
-
-.. automodule:: pype.tools.launcher
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.tools.launcher.actions module
-----------------------------------
-
-.. automodule:: pype.tools.launcher.actions
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.launcher.delegates module
-------------------------------------
-
-.. automodule:: pype.tools.launcher.delegates
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.launcher.flickcharm module
--------------------------------------
-
-.. automodule:: pype.tools.launcher.flickcharm
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.launcher.lib module
-------------------------------
-
-.. automodule:: pype.tools.launcher.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.launcher.models module
----------------------------------
-
-.. automodule:: pype.tools.launcher.models
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.launcher.widgets module
-----------------------------------
-
-.. automodule:: pype.tools.launcher.widgets
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.launcher.window module
----------------------------------
-
-.. automodule:: pype.tools.launcher.window
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.launcher.widgets.rst b/docs/source/pype.tools.launcher.widgets.rst
deleted file mode 100644
index 400a5b7a2c..0000000000
--- a/docs/source/pype.tools.launcher.widgets.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.launcher.widgets module
-==================================
-
-.. automodule:: pype.tools.launcher.widgets
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.launcher.window.rst b/docs/source/pype.tools.launcher.window.rst
deleted file mode 100644
index ae92207795..0000000000
--- a/docs/source/pype.tools.launcher.window.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.launcher.window module
-=================================
-
-.. automodule:: pype.tools.launcher.window
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.app.rst b/docs/source/pype.tools.pyblish_pype.app.rst
deleted file mode 100644
index a70aada725..0000000000
--- a/docs/source/pype.tools.pyblish_pype.app.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.app module
-===================================
-
-.. automodule:: pype.tools.pyblish_pype.app
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.awesome.rst b/docs/source/pype.tools.pyblish_pype.awesome.rst
deleted file mode 100644
index 50a81ac5e8..0000000000
--- a/docs/source/pype.tools.pyblish_pype.awesome.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.awesome module
-=======================================
-
-.. automodule:: pype.tools.pyblish_pype.awesome
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.compat.rst b/docs/source/pype.tools.pyblish_pype.compat.rst
deleted file mode 100644
index 4beee41e00..0000000000
--- a/docs/source/pype.tools.pyblish_pype.compat.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.compat module
-======================================
-
-.. automodule:: pype.tools.pyblish_pype.compat
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.constants.rst b/docs/source/pype.tools.pyblish_pype.constants.rst
deleted file mode 100644
index bab67a2270..0000000000
--- a/docs/source/pype.tools.pyblish_pype.constants.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.constants module
-=========================================
-
-.. automodule:: pype.tools.pyblish_pype.constants
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.control.rst b/docs/source/pype.tools.pyblish_pype.control.rst
deleted file mode 100644
index c2f8c0031e..0000000000
--- a/docs/source/pype.tools.pyblish_pype.control.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.control module
-=======================================
-
-.. automodule:: pype.tools.pyblish_pype.control
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.delegate.rst b/docs/source/pype.tools.pyblish_pype.delegate.rst
deleted file mode 100644
index 8796c9830f..0000000000
--- a/docs/source/pype.tools.pyblish_pype.delegate.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.delegate module
-========================================
-
-.. automodule:: pype.tools.pyblish_pype.delegate
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.mock.rst b/docs/source/pype.tools.pyblish_pype.mock.rst
deleted file mode 100644
index 8c22e80856..0000000000
--- a/docs/source/pype.tools.pyblish_pype.mock.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.mock module
-====================================
-
-.. automodule:: pype.tools.pyblish_pype.mock
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.model.rst b/docs/source/pype.tools.pyblish_pype.model.rst
deleted file mode 100644
index 983b06cc8a..0000000000
--- a/docs/source/pype.tools.pyblish_pype.model.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.model module
-=====================================
-
-.. automodule:: pype.tools.pyblish_pype.model
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.rst b/docs/source/pype.tools.pyblish_pype.rst
deleted file mode 100644
index 9479b5399f..0000000000
--- a/docs/source/pype.tools.pyblish_pype.rst
+++ /dev/null
@@ -1,130 +0,0 @@
-pype.tools.pyblish\_pype package
-================================
-
-.. automodule:: pype.tools.pyblish_pype
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.tools.pyblish_pype.vendor
-
-Submodules
-----------
-
-pype.tools.pyblish\_pype.app module
------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.app
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.awesome module
----------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.awesome
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.compat module
---------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.compat
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.constants module
------------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.constants
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.control module
----------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.control
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.delegate module
-----------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.delegate
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.mock module
-------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.mock
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.model module
--------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.model
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.settings module
-----------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.settings
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.util module
-------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.util
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.version module
----------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.version
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.view module
-------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.view
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.widgets module
----------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.widgets
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.window module
---------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.window
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.settings.rst b/docs/source/pype.tools.pyblish_pype.settings.rst
deleted file mode 100644
index 2e4e95cca0..0000000000
--- a/docs/source/pype.tools.pyblish_pype.settings.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.settings module
-========================================
-
-.. automodule:: pype.tools.pyblish_pype.settings
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.util.rst b/docs/source/pype.tools.pyblish_pype.util.rst
deleted file mode 100644
index fa34295f12..0000000000
--- a/docs/source/pype.tools.pyblish_pype.util.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.util module
-====================================
-
-.. automodule:: pype.tools.pyblish_pype.util
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.vendor.qtawesome.animation.rst b/docs/source/pype.tools.pyblish_pype.vendor.qtawesome.animation.rst
deleted file mode 100644
index a892128308..0000000000
--- a/docs/source/pype.tools.pyblish_pype.vendor.qtawesome.animation.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.vendor.qtawesome.animation module
-==========================================================
-
-.. automodule:: pype.tools.pyblish_pype.vendor.qtawesome.animation
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.vendor.qtawesome.iconic_font.rst b/docs/source/pype.tools.pyblish_pype.vendor.qtawesome.iconic_font.rst
deleted file mode 100644
index 4f4337348f..0000000000
--- a/docs/source/pype.tools.pyblish_pype.vendor.qtawesome.iconic_font.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.vendor.qtawesome.iconic\_font module
-=============================================================
-
-.. automodule:: pype.tools.pyblish_pype.vendor.qtawesome.iconic_font
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.vendor.qtawesome.rst b/docs/source/pype.tools.pyblish_pype.vendor.qtawesome.rst
deleted file mode 100644
index 68b2ec4659..0000000000
--- a/docs/source/pype.tools.pyblish_pype.vendor.qtawesome.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.tools.pyblish\_pype.vendor.qtawesome package
-=================================================
-
-.. automodule:: pype.tools.pyblish_pype.vendor.qtawesome
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.tools.pyblish\_pype.vendor.qtawesome.animation module
-----------------------------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.vendor.qtawesome.animation
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.pyblish\_pype.vendor.qtawesome.iconic\_font module
--------------------------------------------------------------
-
-.. automodule:: pype.tools.pyblish_pype.vendor.qtawesome.iconic_font
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.vendor.rst b/docs/source/pype.tools.pyblish_pype.vendor.rst
deleted file mode 100644
index 69e6096053..0000000000
--- a/docs/source/pype.tools.pyblish_pype.vendor.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-pype.tools.pyblish\_pype.vendor package
-=======================================
-
-.. automodule:: pype.tools.pyblish_pype.vendor
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.tools.pyblish_pype.vendor.qtawesome
diff --git a/docs/source/pype.tools.pyblish_pype.version.rst b/docs/source/pype.tools.pyblish_pype.version.rst
deleted file mode 100644
index a6ddcd5ce8..0000000000
--- a/docs/source/pype.tools.pyblish_pype.version.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.version module
-=======================================
-
-.. automodule:: pype.tools.pyblish_pype.version
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.view.rst b/docs/source/pype.tools.pyblish_pype.view.rst
deleted file mode 100644
index 21d34d9daa..0000000000
--- a/docs/source/pype.tools.pyblish_pype.view.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.view module
-====================================
-
-.. automodule:: pype.tools.pyblish_pype.view
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.widgets.rst b/docs/source/pype.tools.pyblish_pype.widgets.rst
deleted file mode 100644
index 8a0d3c380a..0000000000
--- a/docs/source/pype.tools.pyblish_pype.widgets.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.widgets module
-=======================================
-
-.. automodule:: pype.tools.pyblish_pype.widgets
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.pyblish_pype.window.rst b/docs/source/pype.tools.pyblish_pype.window.rst
deleted file mode 100644
index 10f7b1a36e..0000000000
--- a/docs/source/pype.tools.pyblish_pype.window.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.pyblish\_pype.window module
-======================================
-
-.. automodule:: pype.tools.pyblish_pype.window
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.rst b/docs/source/pype.tools.rst
deleted file mode 100644
index d82ed3384a..0000000000
--- a/docs/source/pype.tools.rst
+++ /dev/null
@@ -1,19 +0,0 @@
-pype.tools package
-==================
-
-.. automodule:: pype.tools
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.tools.assetcreator
- pype.tools.launcher
- pype.tools.pyblish_pype
- pype.tools.settings
- pype.tools.standalonepublish
diff --git a/docs/source/pype.tools.settings.rst b/docs/source/pype.tools.settings.rst
deleted file mode 100644
index ef54851ab1..0000000000
--- a/docs/source/pype.tools.settings.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-pype.tools.settings package
-===========================
-
-.. automodule:: pype.tools.settings
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.tools.settings.settings
diff --git a/docs/source/pype.tools.settings.settings.rst b/docs/source/pype.tools.settings.settings.rst
deleted file mode 100644
index 793914e1a8..0000000000
--- a/docs/source/pype.tools.settings.settings.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-pype.tools.settings.settings package
-====================================
-
-.. automodule:: pype.tools.settings.settings
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.tools.settings.settings.style
- pype.tools.settings.settings.widgets
diff --git a/docs/source/pype.tools.settings.settings.style.rst b/docs/source/pype.tools.settings.settings.style.rst
deleted file mode 100644
index 228322245c..0000000000
--- a/docs/source/pype.tools.settings.settings.style.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.settings.settings.style package
-==========================================
-
-.. automodule:: pype.tools.settings.settings.style
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.settings.settings.widgets.anatomy_types.rst b/docs/source/pype.tools.settings.settings.widgets.anatomy_types.rst
deleted file mode 100644
index ca951c82f0..0000000000
--- a/docs/source/pype.tools.settings.settings.widgets.anatomy_types.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.settings.settings.widgets.anatomy\_types module
-==========================================================
-
-.. automodule:: pype.tools.settings.settings.widgets.anatomy_types
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.settings.settings.widgets.base.rst b/docs/source/pype.tools.settings.settings.widgets.base.rst
deleted file mode 100644
index 8964d6f628..0000000000
--- a/docs/source/pype.tools.settings.settings.widgets.base.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.settings.settings.widgets.base module
-================================================
-
-.. automodule:: pype.tools.settings.settings.widgets.base
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.settings.settings.widgets.item_types.rst b/docs/source/pype.tools.settings.settings.widgets.item_types.rst
deleted file mode 100644
index 5e505538a7..0000000000
--- a/docs/source/pype.tools.settings.settings.widgets.item_types.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.settings.settings.widgets.item\_types module
-=======================================================
-
-.. automodule:: pype.tools.settings.settings.widgets.item_types
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.settings.settings.widgets.lib.rst b/docs/source/pype.tools.settings.settings.widgets.lib.rst
deleted file mode 100644
index ae100c74b2..0000000000
--- a/docs/source/pype.tools.settings.settings.widgets.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.settings.settings.widgets.lib module
-===============================================
-
-.. automodule:: pype.tools.settings.settings.widgets.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.settings.settings.widgets.multiselection_combobox.rst b/docs/source/pype.tools.settings.settings.widgets.multiselection_combobox.rst
deleted file mode 100644
index 004f2ae21f..0000000000
--- a/docs/source/pype.tools.settings.settings.widgets.multiselection_combobox.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.settings.settings.widgets.multiselection\_combobox module
-====================================================================
-
-.. automodule:: pype.tools.settings.settings.widgets.multiselection_combobox
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.settings.settings.widgets.rst b/docs/source/pype.tools.settings.settings.widgets.rst
deleted file mode 100644
index 8734280a08..0000000000
--- a/docs/source/pype.tools.settings.settings.widgets.rst
+++ /dev/null
@@ -1,74 +0,0 @@
-pype.tools.settings.settings.widgets package
-============================================
-
-.. automodule:: pype.tools.settings.settings.widgets
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.tools.settings.settings.widgets.anatomy\_types module
-----------------------------------------------------------
-
-.. automodule:: pype.tools.settings.settings.widgets.anatomy_types
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.settings.settings.widgets.base module
-------------------------------------------------
-
-.. automodule:: pype.tools.settings.settings.widgets.base
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.settings.settings.widgets.item\_types module
--------------------------------------------------------
-
-.. automodule:: pype.tools.settings.settings.widgets.item_types
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.settings.settings.widgets.lib module
------------------------------------------------
-
-.. automodule:: pype.tools.settings.settings.widgets.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.settings.settings.widgets.multiselection\_combobox module
---------------------------------------------------------------------
-
-.. automodule:: pype.tools.settings.settings.widgets.multiselection_combobox
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.settings.settings.widgets.tests module
--------------------------------------------------
-
-.. automodule:: pype.tools.settings.settings.widgets.tests
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.settings.settings.widgets.widgets module
----------------------------------------------------
-
-.. automodule:: pype.tools.settings.settings.widgets.widgets
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.settings.settings.widgets.window module
---------------------------------------------------
-
-.. automodule:: pype.tools.settings.settings.widgets.window
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.settings.settings.widgets.tests.rst b/docs/source/pype.tools.settings.settings.widgets.tests.rst
deleted file mode 100644
index fe8d6dabef..0000000000
--- a/docs/source/pype.tools.settings.settings.widgets.tests.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.settings.settings.widgets.tests module
-=================================================
-
-.. automodule:: pype.tools.settings.settings.widgets.tests
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.settings.settings.widgets.widgets.rst b/docs/source/pype.tools.settings.settings.widgets.widgets.rst
deleted file mode 100644
index 238e584ac3..0000000000
--- a/docs/source/pype.tools.settings.settings.widgets.widgets.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.settings.settings.widgets.widgets module
-===================================================
-
-.. automodule:: pype.tools.settings.settings.widgets.widgets
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.settings.settings.widgets.window.rst b/docs/source/pype.tools.settings.settings.widgets.window.rst
deleted file mode 100644
index d67678012f..0000000000
--- a/docs/source/pype.tools.settings.settings.widgets.window.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.settings.settings.widgets.window module
-==================================================
-
-.. automodule:: pype.tools.settings.settings.widgets.window
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.app.rst b/docs/source/pype.tools.standalonepublish.app.rst
deleted file mode 100644
index 74776b80fe..0000000000
--- a/docs/source/pype.tools.standalonepublish.app.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.app module
-=======================================
-
-.. automodule:: pype.tools.standalonepublish.app
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.publish.rst b/docs/source/pype.tools.standalonepublish.publish.rst
deleted file mode 100644
index 47ad57e7fb..0000000000
--- a/docs/source/pype.tools.standalonepublish.publish.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.publish module
-===========================================
-
-.. automodule:: pype.tools.standalonepublish.publish
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.rst b/docs/source/pype.tools.standalonepublish.rst
deleted file mode 100644
index 5ca8194b61..0000000000
--- a/docs/source/pype.tools.standalonepublish.rst
+++ /dev/null
@@ -1,34 +0,0 @@
-pype.tools.standalonepublish package
-====================================
-
-.. automodule:: pype.tools.standalonepublish
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.tools.standalonepublish.widgets
-
-Submodules
-----------
-
-pype.tools.standalonepublish.app module
----------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.app
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.publish module
--------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.publish
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.model_asset.rst b/docs/source/pype.tools.standalonepublish.widgets.model_asset.rst
deleted file mode 100644
index 84d0ca2d93..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.model_asset.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.model\_asset module
-========================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_asset
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.model_filter_proxy_exact_match.rst b/docs/source/pype.tools.standalonepublish.widgets.model_filter_proxy_exact_match.rst
deleted file mode 100644
index 0c3ae79b99..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.model_filter_proxy_exact_match.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.model\_filter\_proxy\_exact\_match module
-==============================================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_filter_proxy_exact_match
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.model_filter_proxy_recursive_sort.rst b/docs/source/pype.tools.standalonepublish.widgets.model_filter_proxy_recursive_sort.rst
deleted file mode 100644
index b828b75030..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.model_filter_proxy_recursive_sort.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.model\_filter\_proxy\_recursive\_sort module
-=================================================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_filter_proxy_recursive_sort
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.model_node.rst b/docs/source/pype.tools.standalonepublish.widgets.model_node.rst
deleted file mode 100644
index 4789b14501..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.model_node.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.model\_node module
-=======================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_node
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.model_tasks_template.rst b/docs/source/pype.tools.standalonepublish.widgets.model_tasks_template.rst
deleted file mode 100644
index dbee838530..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.model_tasks_template.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.model\_tasks\_template module
-==================================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_tasks_template
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.model_tree.rst b/docs/source/pype.tools.standalonepublish.widgets.model_tree.rst
deleted file mode 100644
index 38eecb095a..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.model_tree.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.model\_tree module
-=======================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_tree
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.model_tree_view_deselectable.rst b/docs/source/pype.tools.standalonepublish.widgets.model_tree_view_deselectable.rst
deleted file mode 100644
index 9afb505113..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.model_tree_view_deselectable.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.model\_tree\_view\_deselectable module
-===========================================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_tree_view_deselectable
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.resources.rst b/docs/source/pype.tools.standalonepublish.widgets.resources.rst
deleted file mode 100644
index a0eddae63e..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.resources.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.resources package
-======================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.resources
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.rst b/docs/source/pype.tools.standalonepublish.widgets.rst
deleted file mode 100644
index 65bbcb62fc..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.rst
+++ /dev/null
@@ -1,146 +0,0 @@
-pype.tools.standalonepublish.widgets package
-============================================
-
-.. automodule:: pype.tools.standalonepublish.widgets
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.tools.standalonepublish.widgets.resources
-
-Submodules
-----------
-
-pype.tools.standalonepublish.widgets.model\_asset module
---------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_asset
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.model\_filter\_proxy\_exact\_match module
-------------------------------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_filter_proxy_exact_match
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.model\_filter\_proxy\_recursive\_sort module
----------------------------------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_filter_proxy_recursive_sort
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.model\_node module
--------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_node
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.model\_tasks\_template module
-------------------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_tasks_template
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.model\_tree module
--------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_tree
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.model\_tree\_view\_deselectable module
----------------------------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.model_tree_view_deselectable
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.widget\_asset module
----------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_asset
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.widget\_component\_item module
--------------------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_component_item
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.widget\_components module
---------------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_components
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.widget\_components\_list module
---------------------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_components_list
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.widget\_drop\_empty module
----------------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_drop_empty
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.widget\_drop\_frame module
----------------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_drop_frame
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.widget\_family module
-----------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_family
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.widget\_family\_desc module
-----------------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_family_desc
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.tools.standalonepublish.widgets.widget\_shadow module
-----------------------------------------------------------
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_shadow
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.widget_asset.rst b/docs/source/pype.tools.standalonepublish.widgets.widget_asset.rst
deleted file mode 100644
index 51a3763628..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.widget_asset.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.widget\_asset module
-=========================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_asset
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.widget_component_item.rst b/docs/source/pype.tools.standalonepublish.widgets.widget_component_item.rst
deleted file mode 100644
index 3495fdf192..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.widget_component_item.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.widget\_component\_item module
-===================================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_component_item
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.widget_components.rst b/docs/source/pype.tools.standalonepublish.widgets.widget_components.rst
deleted file mode 100644
index be7c19af9f..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.widget_components.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.widget\_components module
-==============================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_components
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.widget_components_list.rst b/docs/source/pype.tools.standalonepublish.widgets.widget_components_list.rst
deleted file mode 100644
index 051efe07fe..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.widget_components_list.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.widget\_components\_list module
-====================================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_components_list
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.widget_drop_empty.rst b/docs/source/pype.tools.standalonepublish.widgets.widget_drop_empty.rst
deleted file mode 100644
index b5b0a6acac..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.widget_drop_empty.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.widget\_drop\_empty module
-===============================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_drop_empty
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.widget_drop_frame.rst b/docs/source/pype.tools.standalonepublish.widgets.widget_drop_frame.rst
deleted file mode 100644
index 6b3e3690e0..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.widget_drop_frame.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.widget\_drop\_frame module
-===============================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_drop_frame
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.widget_family.rst b/docs/source/pype.tools.standalonepublish.widgets.widget_family.rst
deleted file mode 100644
index 24c9d5496f..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.widget_family.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.widget\_family module
-==========================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_family
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.widget_family_desc.rst b/docs/source/pype.tools.standalonepublish.widgets.widget_family_desc.rst
deleted file mode 100644
index 5a7f92177f..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.widget_family_desc.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.widget\_family\_desc module
-================================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_family_desc
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.standalonepublish.widgets.widget_shadow.rst b/docs/source/pype.tools.standalonepublish.widgets.widget_shadow.rst
deleted file mode 100644
index 19f5c22198..0000000000
--- a/docs/source/pype.tools.standalonepublish.widgets.widget_shadow.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.standalonepublish.widgets.widget\_shadow module
-==========================================================
-
-.. automodule:: pype.tools.standalonepublish.widgets.widget_shadow
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.tray.pype_tray.rst b/docs/source/pype.tools.tray.pype_tray.rst
deleted file mode 100644
index 9fc49c5763..0000000000
--- a/docs/source/pype.tools.tray.pype_tray.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.tray.pype\_tray module
-=================================
-
-.. automodule:: pype.tools.tray.pype_tray
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.tray.rst b/docs/source/pype.tools.tray.rst
deleted file mode 100644
index b28059d170..0000000000
--- a/docs/source/pype.tools.tray.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-pype.tools.tray package
-=======================
-
-.. automodule:: pype.tools.tray
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-.. toctree::
- :maxdepth: 10
-
- pype.tools.tray.pype_tray
diff --git a/docs/source/pype.tools.workfiles.app.rst b/docs/source/pype.tools.workfiles.app.rst
deleted file mode 100644
index a3a46b8a07..0000000000
--- a/docs/source/pype.tools.workfiles.app.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.workfiles.app module
-===============================
-
-.. automodule:: pype.tools.workfiles.app
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.workfiles.model.rst b/docs/source/pype.tools.workfiles.model.rst
deleted file mode 100644
index 44cea32b97..0000000000
--- a/docs/source/pype.tools.workfiles.model.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.workfiles.model module
-=================================
-
-.. automodule:: pype.tools.workfiles.model
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.tools.workfiles.rst b/docs/source/pype.tools.workfiles.rst
deleted file mode 100644
index 147c4cebbe..0000000000
--- a/docs/source/pype.tools.workfiles.rst
+++ /dev/null
@@ -1,17 +0,0 @@
-pype.tools.workfiles package
-============================
-
-.. automodule:: pype.tools.workfiles
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-.. toctree::
- :maxdepth: 10
-
- pype.tools.workfiles.app
- pype.tools.workfiles.model
- pype.tools.workfiles.view
diff --git a/docs/source/pype.tools.workfiles.view.rst b/docs/source/pype.tools.workfiles.view.rst
deleted file mode 100644
index acd32ed250..0000000000
--- a/docs/source/pype.tools.workfiles.view.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.tools.workfiles.view module
-================================
-
-.. automodule:: pype.tools.workfiles.view
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.backports.configparser.helpers.rst b/docs/source/pype.vendor.backports.configparser.helpers.rst
deleted file mode 100644
index 8d44d0a8c4..0000000000
--- a/docs/source/pype.vendor.backports.configparser.helpers.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.backports.configparser.helpers module
-=================================================
-
-.. automodule:: pype.vendor.backports.configparser.helpers
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.backports.configparser.rst b/docs/source/pype.vendor.backports.configparser.rst
deleted file mode 100644
index 4f778a4a87..0000000000
--- a/docs/source/pype.vendor.backports.configparser.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-pype.vendor.backports.configparser package
-==========================================
-
-.. automodule:: pype.vendor.backports.configparser
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.vendor.backports.configparser.helpers module
--------------------------------------------------
-
-.. automodule:: pype.vendor.backports.configparser.helpers
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.backports.functools_lru_cache.rst b/docs/source/pype.vendor.backports.functools_lru_cache.rst
deleted file mode 100644
index 26f2746cec..0000000000
--- a/docs/source/pype.vendor.backports.functools_lru_cache.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.backports.functools\_lru\_cache module
-==================================================
-
-.. automodule:: pype.vendor.backports.functools_lru_cache
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.backports.rst b/docs/source/pype.vendor.backports.rst
deleted file mode 100644
index ff9efc29c5..0000000000
--- a/docs/source/pype.vendor.backports.rst
+++ /dev/null
@@ -1,26 +0,0 @@
-pype.vendor.backports package
-=============================
-
-.. automodule:: pype.vendor.backports
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.vendor.backports.configparser
-
-Submodules
-----------
-
-pype.vendor.backports.functools\_lru\_cache module
---------------------------------------------------
-
-.. automodule:: pype.vendor.backports.functools_lru_cache
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.builtins.rst b/docs/source/pype.vendor.builtins.rst
deleted file mode 100644
index e21fb768ed..0000000000
--- a/docs/source/pype.vendor.builtins.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.builtins package
-============================
-
-.. automodule:: pype.vendor.builtins
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.capture.rst b/docs/source/pype.vendor.capture.rst
deleted file mode 100644
index d42e073fb5..0000000000
--- a/docs/source/pype.vendor.capture.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.capture module
-==========================
-
-.. automodule:: pype.vendor.capture
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.capture_gui.accordion.rst b/docs/source/pype.vendor.capture_gui.accordion.rst
deleted file mode 100644
index cca228f151..0000000000
--- a/docs/source/pype.vendor.capture_gui.accordion.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.capture\_gui.accordion module
-=========================================
-
-.. automodule:: pype.vendor.capture_gui.accordion
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.capture_gui.app.rst b/docs/source/pype.vendor.capture_gui.app.rst
deleted file mode 100644
index 291296834e..0000000000
--- a/docs/source/pype.vendor.capture_gui.app.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.capture\_gui.app module
-===================================
-
-.. automodule:: pype.vendor.capture_gui.app
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.capture_gui.colorpicker.rst b/docs/source/pype.vendor.capture_gui.colorpicker.rst
deleted file mode 100644
index c9e56500f2..0000000000
--- a/docs/source/pype.vendor.capture_gui.colorpicker.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.capture\_gui.colorpicker module
-===========================================
-
-.. automodule:: pype.vendor.capture_gui.colorpicker
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.capture_gui.lib.rst b/docs/source/pype.vendor.capture_gui.lib.rst
deleted file mode 100644
index e94a3bd196..0000000000
--- a/docs/source/pype.vendor.capture_gui.lib.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.capture\_gui.lib module
-===================================
-
-.. automodule:: pype.vendor.capture_gui.lib
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.capture_gui.plugin.rst b/docs/source/pype.vendor.capture_gui.plugin.rst
deleted file mode 100644
index 2e8f58c873..0000000000
--- a/docs/source/pype.vendor.capture_gui.plugin.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.capture\_gui.plugin module
-======================================
-
-.. automodule:: pype.vendor.capture_gui.plugin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.capture_gui.presets.rst b/docs/source/pype.vendor.capture_gui.presets.rst
deleted file mode 100644
index c81b4e1c23..0000000000
--- a/docs/source/pype.vendor.capture_gui.presets.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.capture\_gui.presets module
-=======================================
-
-.. automodule:: pype.vendor.capture_gui.presets
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.capture_gui.rst b/docs/source/pype.vendor.capture_gui.rst
deleted file mode 100644
index f7efce3501..0000000000
--- a/docs/source/pype.vendor.capture_gui.rst
+++ /dev/null
@@ -1,82 +0,0 @@
-pype.vendor.capture\_gui package
-================================
-
-.. automodule:: pype.vendor.capture_gui
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.vendor.capture_gui.vendor
-
-Submodules
-----------
-
-pype.vendor.capture\_gui.accordion module
------------------------------------------
-
-.. automodule:: pype.vendor.capture_gui.accordion
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.capture\_gui.app module
------------------------------------
-
-.. automodule:: pype.vendor.capture_gui.app
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.capture\_gui.colorpicker module
--------------------------------------------
-
-.. automodule:: pype.vendor.capture_gui.colorpicker
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.capture\_gui.lib module
------------------------------------
-
-.. automodule:: pype.vendor.capture_gui.lib
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.capture\_gui.plugin module
---------------------------------------
-
-.. automodule:: pype.vendor.capture_gui.plugin
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.capture\_gui.presets module
----------------------------------------
-
-.. automodule:: pype.vendor.capture_gui.presets
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.capture\_gui.tokens module
---------------------------------------
-
-.. automodule:: pype.vendor.capture_gui.tokens
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.capture\_gui.version module
----------------------------------------
-
-.. automodule:: pype.vendor.capture_gui.version
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.capture_gui.tokens.rst b/docs/source/pype.vendor.capture_gui.tokens.rst
deleted file mode 100644
index 9e144a4d37..0000000000
--- a/docs/source/pype.vendor.capture_gui.tokens.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.capture\_gui.tokens module
-======================================
-
-.. automodule:: pype.vendor.capture_gui.tokens
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.capture_gui.vendor.Qt.rst b/docs/source/pype.vendor.capture_gui.vendor.Qt.rst
deleted file mode 100644
index 447e6dd812..0000000000
--- a/docs/source/pype.vendor.capture_gui.vendor.Qt.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.capture\_gui.vendor.Qt module
-=========================================
-
-.. automodule:: pype.vendor.capture_gui.vendor.Qt
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.capture_gui.vendor.rst b/docs/source/pype.vendor.capture_gui.vendor.rst
deleted file mode 100644
index 0befc4bbb7..0000000000
--- a/docs/source/pype.vendor.capture_gui.vendor.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-pype.vendor.capture\_gui.vendor package
-=======================================
-
-.. automodule:: pype.vendor.capture_gui.vendor
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.vendor.capture\_gui.vendor.Qt module
------------------------------------------
-
-.. automodule:: pype.vendor.capture_gui.vendor.Qt
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.capture_gui.version.rst b/docs/source/pype.vendor.capture_gui.version.rst
deleted file mode 100644
index 3f0cfbabfd..0000000000
--- a/docs/source/pype.vendor.capture_gui.version.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.capture\_gui.version module
-=======================================
-
-.. automodule:: pype.vendor.capture_gui.version
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.accessor.base.rst b/docs/source/pype.vendor.ftrack_api_old.accessor.base.rst
deleted file mode 100644
index 5155df82aa..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.accessor.base.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.accessor.base module
-=================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.accessor.base
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.accessor.disk.rst b/docs/source/pype.vendor.ftrack_api_old.accessor.disk.rst
deleted file mode 100644
index 3040fe18fd..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.accessor.disk.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.accessor.disk module
-=================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.accessor.disk
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.accessor.rst b/docs/source/pype.vendor.ftrack_api_old.accessor.rst
deleted file mode 100644
index 1f7b522460..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.accessor.rst
+++ /dev/null
@@ -1,34 +0,0 @@
-pype.vendor.ftrack\_api\_old.accessor package
-=============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.accessor
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.vendor.ftrack\_api\_old.accessor.base module
--------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.accessor.base
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.accessor.disk module
--------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.accessor.disk
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.accessor.server module
----------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.accessor.server
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.accessor.server.rst b/docs/source/pype.vendor.ftrack_api_old.accessor.server.rst
deleted file mode 100644
index db835f99c4..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.accessor.server.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.accessor.server module
-===================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.accessor.server
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.attribute.rst b/docs/source/pype.vendor.ftrack_api_old.attribute.rst
deleted file mode 100644
index 54276ceb2a..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.attribute.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.attribute module
-=============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.attribute
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.cache.rst b/docs/source/pype.vendor.ftrack_api_old.cache.rst
deleted file mode 100644
index 396bc5a1cd..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.cache.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.cache module
-=========================================
-
-.. automodule:: pype.vendor.ftrack_api_old.cache
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.collection.rst b/docs/source/pype.vendor.ftrack_api_old.collection.rst
deleted file mode 100644
index de911619fc..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.collection.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.collection module
-==============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.collection
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.data.rst b/docs/source/pype.vendor.ftrack_api_old.data.rst
deleted file mode 100644
index 2f67185cee..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.data.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.data module
-========================================
-
-.. automodule:: pype.vendor.ftrack_api_old.data
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.entity.asset_version.rst b/docs/source/pype.vendor.ftrack_api_old.entity.asset_version.rst
deleted file mode 100644
index 7ad3d87fd9..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.entity.asset_version.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.entity.asset\_version module
-=========================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.asset_version
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.entity.base.rst b/docs/source/pype.vendor.ftrack_api_old.entity.base.rst
deleted file mode 100644
index b87428f817..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.entity.base.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.entity.base module
-===============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.base
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.entity.component.rst b/docs/source/pype.vendor.ftrack_api_old.entity.component.rst
deleted file mode 100644
index 27901ab786..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.entity.component.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.entity.component module
-====================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.component
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.entity.factory.rst b/docs/source/pype.vendor.ftrack_api_old.entity.factory.rst
deleted file mode 100644
index caada5c3c8..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.entity.factory.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.entity.factory module
-==================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.factory
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.entity.job.rst b/docs/source/pype.vendor.ftrack_api_old.entity.job.rst
deleted file mode 100644
index 6f4ca18323..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.entity.job.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.entity.job module
-==============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.job
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.entity.location.rst b/docs/source/pype.vendor.ftrack_api_old.entity.location.rst
deleted file mode 100644
index 2f0b380349..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.entity.location.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.entity.location module
-===================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.location
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.entity.note.rst b/docs/source/pype.vendor.ftrack_api_old.entity.note.rst
deleted file mode 100644
index c04e959402..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.entity.note.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.entity.note module
-===============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.note
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.entity.project_schema.rst b/docs/source/pype.vendor.ftrack_api_old.entity.project_schema.rst
deleted file mode 100644
index 6332a2e523..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.entity.project_schema.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.entity.project\_schema module
-==========================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.project_schema
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.entity.rst b/docs/source/pype.vendor.ftrack_api_old.entity.rst
deleted file mode 100644
index bb43a7621b..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.entity.rst
+++ /dev/null
@@ -1,82 +0,0 @@
-pype.vendor.ftrack\_api\_old.entity package
-===========================================
-
-.. automodule:: pype.vendor.ftrack_api_old.entity
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.vendor.ftrack\_api\_old.entity.asset\_version module
----------------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.asset_version
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.entity.base module
------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.base
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.entity.component module
-----------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.component
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.entity.factory module
---------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.factory
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.entity.job module
-----------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.job
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.entity.location module
----------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.location
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.entity.note module
------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.note
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.entity.project\_schema module
-----------------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.project_schema
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.entity.user module
------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.user
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.entity.user.rst b/docs/source/pype.vendor.ftrack_api_old.entity.user.rst
deleted file mode 100644
index c0fe6574a6..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.entity.user.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.entity.user module
-===============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.entity.user
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.event.base.rst b/docs/source/pype.vendor.ftrack_api_old.event.base.rst
deleted file mode 100644
index 74b86e3bb2..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.event.base.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.event.base module
-==============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.event.base
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.event.expression.rst b/docs/source/pype.vendor.ftrack_api_old.event.expression.rst
deleted file mode 100644
index 860678797b..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.event.expression.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.event.expression module
-====================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.event.expression
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.event.hub.rst b/docs/source/pype.vendor.ftrack_api_old.event.hub.rst
deleted file mode 100644
index d09d52eedf..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.event.hub.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.event.hub module
-=============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.event.hub
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.event.rst b/docs/source/pype.vendor.ftrack_api_old.event.rst
deleted file mode 100644
index 2db27bf7f8..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.event.rst
+++ /dev/null
@@ -1,50 +0,0 @@
-pype.vendor.ftrack\_api\_old.event package
-==========================================
-
-.. automodule:: pype.vendor.ftrack_api_old.event
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.vendor.ftrack\_api\_old.event.base module
-----------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.event.base
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.event.expression module
-----------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.event.expression
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.event.hub module
----------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.event.hub
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.event.subscriber module
-----------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.event.subscriber
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.event.subscription module
-------------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.event.subscription
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.event.subscriber.rst b/docs/source/pype.vendor.ftrack_api_old.event.subscriber.rst
deleted file mode 100644
index a9bd13aabc..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.event.subscriber.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.event.subscriber module
-====================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.event.subscriber
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.event.subscription.rst b/docs/source/pype.vendor.ftrack_api_old.event.subscription.rst
deleted file mode 100644
index 423fa9a688..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.event.subscription.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.event.subscription module
-======================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.event.subscription
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.exception.rst b/docs/source/pype.vendor.ftrack_api_old.exception.rst
deleted file mode 100644
index 54dbeeac36..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.exception.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.exception module
-=============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.exception
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.formatter.rst b/docs/source/pype.vendor.ftrack_api_old.formatter.rst
deleted file mode 100644
index 75a23eefca..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.formatter.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.formatter module
-=============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.formatter
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.inspection.rst b/docs/source/pype.vendor.ftrack_api_old.inspection.rst
deleted file mode 100644
index 2b8849b3d0..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.inspection.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.inspection module
-==============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.inspection
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.logging.rst b/docs/source/pype.vendor.ftrack_api_old.logging.rst
deleted file mode 100644
index a10fa10c26..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.logging.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.logging module
-===========================================
-
-.. automodule:: pype.vendor.ftrack_api_old.logging
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.operation.rst b/docs/source/pype.vendor.ftrack_api_old.operation.rst
deleted file mode 100644
index a1d9d606f8..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.operation.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.operation module
-=============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.operation
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.plugin.rst b/docs/source/pype.vendor.ftrack_api_old.plugin.rst
deleted file mode 100644
index 0f26c705d2..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.plugin.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.plugin module
-==========================================
-
-.. automodule:: pype.vendor.ftrack_api_old.plugin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.query.rst b/docs/source/pype.vendor.ftrack_api_old.query.rst
deleted file mode 100644
index 5cf5aba0e4..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.query.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.query module
-=========================================
-
-.. automodule:: pype.vendor.ftrack_api_old.query
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.resource_identifier_transformer.base.rst b/docs/source/pype.vendor.ftrack_api_old.resource_identifier_transformer.base.rst
deleted file mode 100644
index dccf51ea71..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.resource_identifier_transformer.base.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.resource\_identifier\_transformer.base module
-==========================================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.resource_identifier_transformer.base
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.resource_identifier_transformer.rst b/docs/source/pype.vendor.ftrack_api_old.resource_identifier_transformer.rst
deleted file mode 100644
index 342ecd9321..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.resource_identifier_transformer.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-pype.vendor.ftrack\_api\_old.resource\_identifier\_transformer package
-======================================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.resource_identifier_transformer
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.vendor.ftrack\_api\_old.resource\_identifier\_transformer.base module
---------------------------------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.resource_identifier_transformer.base
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.rst b/docs/source/pype.vendor.ftrack_api_old.rst
deleted file mode 100644
index 51d0a29357..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.rst
+++ /dev/null
@@ -1,126 +0,0 @@
-pype.vendor.ftrack\_api\_old package
-====================================
-
-.. automodule:: pype.vendor.ftrack_api_old
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.vendor.ftrack_api_old.accessor
- pype.vendor.ftrack_api_old.entity
- pype.vendor.ftrack_api_old.event
- pype.vendor.ftrack_api_old.resource_identifier_transformer
- pype.vendor.ftrack_api_old.structure
-
-Submodules
-----------
-
-pype.vendor.ftrack\_api\_old.attribute module
----------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.attribute
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.cache module
------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.cache
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.collection module
-----------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.collection
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.data module
-----------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.data
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.exception module
----------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.exception
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.formatter module
----------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.formatter
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.inspection module
-----------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.inspection
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.logging module
--------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.logging
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.operation module
----------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.operation
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.plugin module
-------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.plugin
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.query module
------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.query
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.session module
--------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.session
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.symbol module
-------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.symbol
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.session.rst b/docs/source/pype.vendor.ftrack_api_old.session.rst
deleted file mode 100644
index beecdeb6af..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.session.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.session module
-===========================================
-
-.. automodule:: pype.vendor.ftrack_api_old.session
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.structure.base.rst b/docs/source/pype.vendor.ftrack_api_old.structure.base.rst
deleted file mode 100644
index 617d8aaed7..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.structure.base.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.structure.base module
-==================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.structure.base
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.structure.entity_id.rst b/docs/source/pype.vendor.ftrack_api_old.structure.entity_id.rst
deleted file mode 100644
index ab6fd0997a..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.structure.entity_id.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.structure.entity\_id module
-========================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.structure.entity_id
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.structure.id.rst b/docs/source/pype.vendor.ftrack_api_old.structure.id.rst
deleted file mode 100644
index 6b887b7917..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.structure.id.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.structure.id module
-================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.structure.id
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.structure.origin.rst b/docs/source/pype.vendor.ftrack_api_old.structure.origin.rst
deleted file mode 100644
index 8ad5fbdc11..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.structure.origin.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.structure.origin module
-====================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.structure.origin
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.structure.rst b/docs/source/pype.vendor.ftrack_api_old.structure.rst
deleted file mode 100644
index 2402430589..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.structure.rst
+++ /dev/null
@@ -1,50 +0,0 @@
-pype.vendor.ftrack\_api\_old.structure package
-==============================================
-
-.. automodule:: pype.vendor.ftrack_api_old.structure
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.vendor.ftrack\_api\_old.structure.base module
---------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.structure.base
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.structure.entity\_id module
---------------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.structure.entity_id
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.structure.id module
-------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.structure.id
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.structure.origin module
-----------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.structure.origin
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.ftrack\_api\_old.structure.standard module
-------------------------------------------------------
-
-.. automodule:: pype.vendor.ftrack_api_old.structure.standard
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.structure.standard.rst b/docs/source/pype.vendor.ftrack_api_old.structure.standard.rst
deleted file mode 100644
index 800201084f..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.structure.standard.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.structure.standard module
-======================================================
-
-.. automodule:: pype.vendor.ftrack_api_old.structure.standard
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.ftrack_api_old.symbol.rst b/docs/source/pype.vendor.ftrack_api_old.symbol.rst
deleted file mode 100644
index bc358d374a..0000000000
--- a/docs/source/pype.vendor.ftrack_api_old.symbol.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.ftrack\_api\_old.symbol module
-==========================================
-
-.. automodule:: pype.vendor.ftrack_api_old.symbol
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.pysync.rst b/docs/source/pype.vendor.pysync.rst
deleted file mode 100644
index fbe5b33fb7..0000000000
--- a/docs/source/pype.vendor.pysync.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.vendor.pysync module
-=========================
-
-.. automodule:: pype.vendor.pysync
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.vendor.rst b/docs/source/pype.vendor.rst
deleted file mode 100644
index 23aa17f7ab..0000000000
--- a/docs/source/pype.vendor.rst
+++ /dev/null
@@ -1,37 +0,0 @@
-pype.vendor package
-===================
-
-.. automodule:: pype.vendor
- :members:
- :undoc-members:
- :show-inheritance:
-
-Subpackages
------------
-
-.. toctree::
- :maxdepth: 6
-
- pype.vendor.backports
- pype.vendor.builtins
- pype.vendor.capture_gui
- pype.vendor.ftrack_api_old
-
-Submodules
-----------
-
-pype.vendor.capture module
---------------------------
-
-.. automodule:: pype.vendor.capture
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.vendor.pysync module
--------------------------
-
-.. automodule:: pype.vendor.pysync
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.version.rst b/docs/source/pype.version.rst
deleted file mode 100644
index 7ec69dc423..0000000000
--- a/docs/source/pype.version.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.version module
-===================
-
-.. automodule:: pype.version
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.widgets.message_window.rst b/docs/source/pype.widgets.message_window.rst
deleted file mode 100644
index 60be203837..0000000000
--- a/docs/source/pype.widgets.message_window.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.widgets.message\_window module
-===================================
-
-.. automodule:: pype.widgets.message_window
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.widgets.popup.rst b/docs/source/pype.widgets.popup.rst
deleted file mode 100644
index 7186ff48de..0000000000
--- a/docs/source/pype.widgets.popup.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.widgets.popup module
-=========================
-
-.. automodule:: pype.widgets.popup
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.widgets.project_settings.rst b/docs/source/pype.widgets.project_settings.rst
deleted file mode 100644
index 9589cf5479..0000000000
--- a/docs/source/pype.widgets.project_settings.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-pype.widgets.project\_settings module
-=====================================
-
-.. automodule:: pype.widgets.project_settings
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/pype.widgets.rst b/docs/source/pype.widgets.rst
deleted file mode 100644
index 1f09318b67..0000000000
--- a/docs/source/pype.widgets.rst
+++ /dev/null
@@ -1,34 +0,0 @@
-pype.widgets package
-====================
-
-.. automodule:: pype.widgets
- :members:
- :undoc-members:
- :show-inheritance:
-
-Submodules
-----------
-
-pype.widgets.message\_window module
------------------------------------
-
-.. automodule:: pype.widgets.message_window
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.widgets.popup module
--------------------------
-
-.. automodule:: pype.widgets.popup
- :members:
- :undoc-members:
- :show-inheritance:
-
-pype.widgets.project\_settings module
--------------------------------------
-
-.. automodule:: pype.widgets.project_settings
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/docs/source/readme.rst b/docs/source/readme.rst
index 823c0df3c8..138b88bba8 100644
--- a/docs/source/readme.rst
+++ b/docs/source/readme.rst
@@ -1,2 +1,6 @@
-.. title:: Pype Readme
+===============
+OpenPype Readme
+===============
+
.. include:: ../../README.md
+ :parser: myst_parser.sphinx_
diff --git a/igniter/__init__.py b/igniter/__init__.py
index aa1b1d209e..16ffb940f6 100644
--- a/igniter/__init__.py
+++ b/igniter/__init__.py
@@ -19,21 +19,37 @@ if "OpenPypeVersion" not in sys.modules:
sys.modules["OpenPypeVersion"] = OpenPypeVersion
+def _get_qt_app():
+ from qtpy import QtWidgets, QtCore
+
+ app = QtWidgets.QApplication.instance()
+ if app is not None:
+ return app
+
+ for attr_name in (
+ "AA_EnableHighDpiScaling",
+ "AA_UseHighDpiPixmaps",
+ ):
+ attr = getattr(QtCore.Qt, attr_name, None)
+ if attr is not None:
+ QtWidgets.QApplication.setAttribute(attr)
+
+ if hasattr(QtWidgets.QApplication, "setHighDpiScaleFactorRoundingPolicy"):
+ QtWidgets.QApplication.setHighDpiScaleFactorRoundingPolicy(
+ QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
+ )
+
+ return QtWidgets.QApplication(sys.argv)
+
+
def open_dialog():
"""Show Igniter dialog."""
if os.getenv("OPENPYPE_HEADLESS_MODE"):
print("!!! Can't open dialog in headless mode. Exiting.")
sys.exit(1)
- from qtpy import QtWidgets, QtCore
from .install_dialog import InstallDialog
- scale_attr = getattr(QtCore.Qt, "AA_EnableHighDpiScaling", None)
- if scale_attr is not None:
- QtWidgets.QApplication.setAttribute(scale_attr)
-
- app = QtWidgets.QApplication.instance()
- if not app:
- app = QtWidgets.QApplication(sys.argv)
+ app = _get_qt_app()
d = InstallDialog()
d.open()
@@ -47,16 +63,10 @@ def open_update_window(openpype_version):
if os.getenv("OPENPYPE_HEADLESS_MODE"):
print("!!! Can't open dialog in headless mode. Exiting.")
sys.exit(1)
- from qtpy import QtWidgets, QtCore
+
from .update_window import UpdateWindow
- scale_attr = getattr(QtCore.Qt, "AA_EnableHighDpiScaling", None)
- if scale_attr is not None:
- QtWidgets.QApplication.setAttribute(scale_attr)
-
- app = QtWidgets.QApplication.instance()
- if not app:
- app = QtWidgets.QApplication(sys.argv)
+ app = _get_qt_app()
d = UpdateWindow(version=openpype_version)
d.open()
@@ -71,16 +81,10 @@ def show_message_dialog(title, message):
if os.getenv("OPENPYPE_HEADLESS_MODE"):
print("!!! Can't open dialog in headless mode. Exiting.")
sys.exit(1)
- from qtpy import QtWidgets, QtCore
+
from .message_dialog import MessageDialog
- scale_attr = getattr(QtCore.Qt, "AA_EnableHighDpiScaling", None)
- if scale_attr is not None:
- QtWidgets.QApplication.setAttribute(scale_attr)
-
- app = QtWidgets.QApplication.instance()
- if not app:
- app = QtWidgets.QApplication(sys.argv)
+ app = _get_qt_app()
dialog = MessageDialog(title, message)
dialog.open()
diff --git a/igniter/bootstrap_repos.py b/igniter/bootstrap_repos.py
index 6c7c834062..4cf00375bf 100644
--- a/igniter/bootstrap_repos.py
+++ b/igniter/bootstrap_repos.py
@@ -25,7 +25,8 @@ from .user_settings import (
from .tools import (
get_openpype_global_settings,
get_openpype_path_from_settings,
- get_expected_studio_version_str
+ get_expected_studio_version_str,
+ get_local_openpype_path_from_settings
)
@@ -61,6 +62,8 @@ class OpenPypeVersion(semver.VersionInfo):
"""
path = None
+
+ _local_openpype_path = None
# this should match any string complying with https://semver.org/
_VERSION_REGEX = re.compile(r"(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)\.(?P0|[1-9]\d*)(?:-(?P[a-zA-Z\d\-.]*))?(?:\+(?P[a-zA-Z\d\-.]*))?") # noqa: E501
_installed_version = None
@@ -289,6 +292,23 @@ class OpenPypeVersion(semver.VersionInfo):
"""
return os.getenv("OPENPYPE_PATH")
+ @classmethod
+ def get_local_openpype_path(cls):
+ """Path to unzipped versions.
+
+ By default it should be user appdata, but could be overridden by
+ settings.
+ """
+ if cls._local_openpype_path:
+ return cls._local_openpype_path
+
+ settings = get_openpype_global_settings(os.environ["OPENPYPE_MONGO"])
+ data_dir = get_local_openpype_path_from_settings(settings)
+ if not data_dir:
+ data_dir = Path(user_data_dir("openpype", "pypeclub"))
+ cls._local_openpype_path = data_dir
+ return data_dir
+
@classmethod
def openpype_path_is_set(cls):
"""Path to OpenPype zip directory is set."""
@@ -319,9 +339,8 @@ class OpenPypeVersion(semver.VersionInfo):
list: of compatible versions available on the machine.
"""
- # DEPRECATED: backwards compatible way to look for versions in root
- dir_to_search = Path(user_data_dir("openpype", "pypeclub"))
- versions = OpenPypeVersion.get_versions_from_directory(dir_to_search)
+ dir_to_search = cls.get_local_openpype_path()
+ versions = cls.get_versions_from_directory(dir_to_search)
return list(sorted(set(versions)))
@@ -533,17 +552,15 @@ class BootstrapRepos:
"""
# vendor and app used to construct user data dir
- self._vendor = "pypeclub"
- self._app = "openpype"
+ self._message = message
self._log = log.getLogger(str(__class__))
- self.data_dir = Path(user_data_dir(self._app, self._vendor))
+ self.set_data_dir(None)
self.secure_registry = OpenPypeSecureRegistry("mongodb")
self.registry = OpenPypeSettingsRegistry()
self.zip_filter = [".pyc", "__pycache__"]
self.openpype_filter = [
"openpype", "schema", "LICENSE"
]
- self._message = message
# dummy progress reporter
def empty_progress(x: int):
@@ -554,6 +571,13 @@ class BootstrapRepos:
progress_callback = empty_progress
self._progress_callback = progress_callback
+ def set_data_dir(self, data_dir):
+ if not data_dir:
+ self.data_dir = Path(user_data_dir("openpype", "pypeclub"))
+ else:
+ self._print(f"overriding local folder: {data_dir}")
+ self.data_dir = data_dir
+
@staticmethod
def get_version_path_from_list(
version: str, version_list: list) -> Union[Path, None]:
diff --git a/igniter/install_thread.py b/igniter/install_thread.py
index 4723e6adfb..1d55213de7 100644
--- a/igniter/install_thread.py
+++ b/igniter/install_thread.py
@@ -14,7 +14,11 @@ from .bootstrap_repos import (
OpenPypeVersion
)
-from .tools import validate_mongo_connection
+from .tools import (
+ get_openpype_global_settings,
+ get_local_openpype_path_from_settings,
+ validate_mongo_connection
+)
class InstallThread(QtCore.QThread):
@@ -80,6 +84,15 @@ class InstallThread(QtCore.QThread):
return
os.environ["OPENPYPE_MONGO"] = self._mongo
+ if not validate_mongo_connection(self._mongo):
+ self.message.emit(f"Cannot connect to {self._mongo}", True)
+ self._set_result(-1)
+ return
+
+ global_settings = get_openpype_global_settings(self._mongo)
+ data_dir = get_local_openpype_path_from_settings(global_settings)
+ bs.set_data_dir(data_dir)
+
self.message.emit(
f"Detecting installed OpenPype versions in {bs.data_dir}",
False)
diff --git a/igniter/tools.py b/igniter/tools.py
index 79235b2329..9dea203f0c 100644
--- a/igniter/tools.py
+++ b/igniter/tools.py
@@ -40,7 +40,7 @@ def should_add_certificate_path_to_mongo_url(mongo_url):
add_certificate = False
# Check if url 'ssl' or 'tls' are set to 'true'
for key in ("ssl", "tls"):
- if key in query and "true" in query["ssl"]:
+ if key in query and "true" in query[key]:
add_certificate = True
break
@@ -73,7 +73,7 @@ def validate_mongo_connection(cnx: str) -> (bool, str):
}
# Add certificate path if should be required
if should_add_certificate_path_to_mongo_url(cnx):
- kwargs["ssl_ca_certs"] = certifi.where()
+ kwargs["tlsCAFile"] = certifi.where()
try:
client = MongoClient(cnx, **kwargs)
@@ -147,7 +147,7 @@ def get_openpype_global_settings(url: str) -> dict:
"""
kwargs = {}
if should_add_certificate_path_to_mongo_url(url):
- kwargs["ssl_ca_certs"] = certifi.where()
+ kwargs["tlsCAFile"] = certifi.where()
try:
# Create mongo connection
@@ -188,6 +188,26 @@ def get_openpype_path_from_settings(settings: dict) -> Union[str, None]:
return next((path for path in paths if os.path.exists(path)), None)
+def get_local_openpype_path_from_settings(settings: dict) -> Union[str, None]:
+ """Get OpenPype local path from global settings.
+
+ Used to download and unzip OP versions.
+ Args:
+ settings (dict): settings from DB.
+
+ Returns:
+ path to OpenPype or None if not found
+ """
+ path = (
+ settings
+ .get("local_openpype_path", {})
+ .get(platform.system().lower())
+ )
+ if path:
+ return Path(path)
+ return None
+
+
def get_expected_studio_version_str(
staging=False, global_settings=None
) -> str:
diff --git a/igniter/update_thread.py b/igniter/update_thread.py
index e98c95f892..0223477d0a 100644
--- a/igniter/update_thread.py
+++ b/igniter/update_thread.py
@@ -48,6 +48,8 @@ class UpdateThread(QtCore.QThread):
"""
bs = BootstrapRepos(
progress_callback=self.set_progress, message=self.message)
+
+ bs.set_data_dir(OpenPypeVersion.get_local_openpype_path())
version_path = bs.install_version(self._openpype_version)
self._set_result(version_path)
diff --git a/openpype/__init__.py b/openpype/__init__.py
index 810664707a..e6b77b1853 100644
--- a/openpype/__init__.py
+++ b/openpype/__init__.py
@@ -3,3 +3,5 @@ import os
PACKAGE_DIR = os.path.dirname(os.path.abspath(__file__))
PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins")
+
+AYON_SERVER_ENABLED = os.environ.get("USE_AYON_SERVER") == "1"
diff --git a/openpype/action.py b/openpype/action.py
index 15c96404b6..6114c65fd4 100644
--- a/openpype/action.py
+++ b/openpype/action.py
@@ -49,7 +49,7 @@ def deprecated(new_destination):
@deprecated("openpype.pipeline.publish.get_errored_instances_from_context")
-def get_errored_instances_from_context(context):
+def get_errored_instances_from_context(context, plugin=None):
"""
Deprecated:
Since 3.14.* will be removed in 3.16.* or later.
@@ -57,7 +57,7 @@ def get_errored_instances_from_context(context):
from openpype.pipeline.publish import get_errored_instances_from_context
- return get_errored_instances_from_context(context)
+ return get_errored_instances_from_context(context, plugin=plugin)
@deprecated("openpype.pipeline.publish.get_errored_plugins_from_context")
@@ -97,11 +97,9 @@ class RepairAction(pyblish.api.Action):
# Get the errored instances
self.log.info("Finding failed instances..")
- errored_instances = get_errored_instances_from_context(context)
-
- # Apply pyblish.logic to get the instances for the plug-in
- instances = pyblish.api.instances_by_plugin(errored_instances, plugin)
- for instance in instances:
+ errored_instances = get_errored_instances_from_context(context,
+ plugin=plugin)
+ for instance in errored_instances:
plugin.repair(instance)
diff --git a/openpype/cli.py b/openpype/cli.py
index 54af42920d..bc837cdeba 100644
--- a/openpype/cli.py
+++ b/openpype/cli.py
@@ -5,11 +5,24 @@ import sys
import code
import click
-# import sys
from .pype_commands import PypeCommands
-@click.group(invoke_without_command=True)
+class AliasedGroup(click.Group):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._aliases = {}
+
+ def set_alias(self, src_name, dst_name):
+ self._aliases[dst_name] = src_name
+
+ def get_command(self, ctx, cmd_name):
+ if cmd_name in self._aliases:
+ cmd_name = self._aliases[cmd_name]
+ return super().get_command(ctx, cmd_name)
+
+
+@click.group(cls=AliasedGroup, invoke_without_command=True)
@click.pass_context
@click.option("--use-version",
expose_value=False, help="use specified version")
@@ -58,16 +71,20 @@ def tray():
@PypeCommands.add_modules
-@main.group(help="Run command line arguments of OpenPype modules")
+@main.group(help="Run command line arguments of OpenPype addons")
@click.pass_context
def module(ctx):
- """Module specific commands created dynamically.
+ """Addon specific commands created dynamically.
- These commands are generated dynamically by currently loaded addon/modules.
+ These commands are generated dynamically by currently loaded addons.
"""
pass
+# Add 'addon' as alias for module
+main.set_alias("module", "addon")
+
+
@main.command()
@click.option("--ftrack-url", envvar="FTRACK_SERVER",
help="Ftrack server url")
@@ -200,85 +217,6 @@ def remotepublish(project, path, user=None, targets=None):
PypeCommands.remotepublish(project, path, user, targets=targets)
-@main.command()
-@click.option("-p", "--project", required=True,
- help="name of project asset is under")
-@click.option("-a", "--asset", required=True,
- help="name of asset to which we want to copy textures")
-@click.option("--path", required=True,
- help="path where textures are found",
- type=click.Path(exists=True))
-def texturecopy(project, asset, path):
- """Copy specified textures to provided asset path.
-
- It validates if project and asset exists. Then it will use speedcopy to
- copy all textures found in all directories under --path to destination
- folder, determined by template texture in anatomy. I will use source
- filename and automatically rise version number on directory.
-
- Result will be copied without directory structure so it will be flat then.
- Nothing is written to database.
- """
-
- PypeCommands().texture_copy(project, asset, path)
-
-
-@main.command(context_settings={"ignore_unknown_options": True})
-@click.option("--app", help="Registered application name")
-@click.option("--project", help="Project name",
- default=lambda: os.environ.get('AVALON_PROJECT', ''))
-@click.option("--asset", help="Asset name",
- default=lambda: os.environ.get('AVALON_ASSET', ''))
-@click.option("--task", help="Task name",
- default=lambda: os.environ.get('AVALON_TASK', ''))
-@click.option("--tools", help="List of tools to add")
-@click.option("--user", help="Pype user name",
- default=lambda: os.environ.get('OPENPYPE_USERNAME', ''))
-@click.option("-fs",
- "--ftrack-server",
- help="Registered application name",
- default=lambda: os.environ.get('FTRACK_SERVER', ''))
-@click.option("-fu",
- "--ftrack-user",
- help="Registered application name",
- default=lambda: os.environ.get('FTRACK_API_USER', ''))
-@click.option("-fk",
- "--ftrack-key",
- help="Registered application name",
- default=lambda: os.environ.get('FTRACK_API_KEY', ''))
-@click.argument('arguments', nargs=-1)
-def launch(app, project, asset, task,
- ftrack_server, ftrack_user, ftrack_key, tools, arguments, user):
- """Launch registered application name in Pype context.
-
- You can define applications in pype-config toml files. Project, asset name
- and task name must be provided (even if they are not used by app itself).
- Optionally you can specify ftrack credentials if needed.
-
- ARGUMENTS are passed to launched application.
-
- """
- # TODO: this needs to switch for Settings
- if ftrack_server:
- os.environ["FTRACK_SERVER"] = ftrack_server
-
- if ftrack_server:
- os.environ["FTRACK_API_USER"] = ftrack_user
-
- if ftrack_server:
- os.environ["FTRACK_API_KEY"] = ftrack_key
-
- if user:
- os.environ["OPENPYPE_USERNAME"] = user
-
- # test required
- if not project or not asset or not task:
- print("!!! Missing required arguments")
- return
-
- PypeCommands().run_application(app, project, asset, task, tools, arguments)
-
-
@main.command(context_settings={"ignore_unknown_options": True})
def projectmanager():
PypeCommands().launch_project_manager()
diff --git a/openpype/client/entities.py b/openpype/client/entities.py
index adbdd7a47c..5d9654c611 100644
--- a/openpype/client/entities.py
+++ b/openpype/client/entities.py
@@ -1,1553 +1,6 @@
-"""Unclear if these will have public functions like these.
+from openpype import AYON_SERVER_ENABLED
-Goal is that most of functions here are called on (or with) an object
-that has project name as a context (e.g. on 'ProjectEntity'?).
-
-+ We will need more specific functions doing very specific queries really fast.
-"""
-
-import re
-import collections
-
-import six
-from bson.objectid import ObjectId
-
-from .mongo import get_project_database, get_project_connection
-
-PatternType = type(re.compile(""))
-
-
-def _prepare_fields(fields, required_fields=None):
- if not fields:
- return None
-
- output = {
- field: True
- for field in fields
- }
- if "_id" not in output:
- output["_id"] = True
-
- if required_fields:
- for key in required_fields:
- output[key] = True
- return output
-
-
-def convert_id(in_id):
- """Helper function for conversion of id from string to ObjectId.
-
- Args:
- in_id (Union[str, ObjectId, Any]): Entity id that should be converted
- to right type for queries.
-
- Returns:
- Union[ObjectId, Any]: Converted ids to ObjectId or in type.
- """
-
- if isinstance(in_id, six.string_types):
- return ObjectId(in_id)
- return in_id
-
-
-def convert_ids(in_ids):
- """Helper function for conversion of ids from string to ObjectId.
-
- Args:
- in_ids (Iterable[Union[str, ObjectId, Any]]): List of entity ids that
- should be converted to right type for queries.
-
- Returns:
- List[ObjectId]: Converted ids to ObjectId.
- """
-
- _output = set()
- for in_id in in_ids:
- if in_id is not None:
- _output.add(convert_id(in_id))
- return list(_output)
-
-
-def get_projects(active=True, inactive=False, fields=None):
- """Yield all project entity documents.
-
- Args:
- active (Optional[bool]): Include active projects. Defaults to True.
- inactive (Optional[bool]): Include inactive projects.
- Defaults to False.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Yields:
- dict: Project entity data which can be reduced to specified 'fields'.
- None is returned if project with specified filters was not found.
- """
- mongodb = get_project_database()
- for project_name in mongodb.collection_names():
- if project_name in ("system.indexes",):
- continue
- project_doc = get_project(
- project_name, active=active, inactive=inactive, fields=fields
- )
- if project_doc is not None:
- yield project_doc
-
-
-def get_project(project_name, active=True, inactive=True, fields=None):
- """Return project entity document by project name.
-
- Args:
- project_name (str): Name of project.
- active (Optional[bool]): Allow active project. Defaults to True.
- inactive (Optional[bool]): Allow inactive project. Defaults to True.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Project entity data which can be reduced to
- specified 'fields'. None is returned if project with specified
- filters was not found.
- """
- # Skip if both are disabled
- if not active and not inactive:
- return None
-
- query_filter = {"type": "project"}
- # Keep query untouched if both should be available
- if active and inactive:
- pass
-
- # Add filter to keep only active
- elif active:
- query_filter["$or"] = [
- {"data.active": {"$exists": False}},
- {"data.active": True},
- ]
-
- # Add filter to keep only inactive
- elif inactive:
- query_filter["$or"] = [
- {"data.active": {"$exists": False}},
- {"data.active": False},
- ]
-
- conn = get_project_connection(project_name)
- return conn.find_one(query_filter, _prepare_fields(fields))
-
-
-def get_whole_project(project_name):
- """Receive all documents from project.
-
- Helper that can be used to get all document from whole project. For example
- for backups etc.
-
- Returns:
- Cursor: Query cursor as iterable which returns all documents from
- project collection.
- """
-
- conn = get_project_connection(project_name)
- return conn.find({})
-
-
-def get_asset_by_id(project_name, asset_id, fields=None):
- """Receive asset data by its id.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- asset_id (Union[str, ObjectId]): Asset's id.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Asset entity data which can be reduced to
- specified 'fields'. None is returned if asset with specified
- filters was not found.
- """
-
- asset_id = convert_id(asset_id)
- if not asset_id:
- return None
-
- query_filter = {"type": "asset", "_id": asset_id}
- conn = get_project_connection(project_name)
- return conn.find_one(query_filter, _prepare_fields(fields))
-
-
-def get_asset_by_name(project_name, asset_name, fields=None):
- """Receive asset data by its name.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- asset_name (str): Asset's name.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Asset entity data which can be reduced to
- specified 'fields'. None is returned if asset with specified
- filters was not found.
- """
-
- if not asset_name:
- return None
-
- query_filter = {"type": "asset", "name": asset_name}
- conn = get_project_connection(project_name)
- return conn.find_one(query_filter, _prepare_fields(fields))
-
-
-# NOTE this could be just public function?
-# - any better variable name instead of 'standard'?
-# - same approach can be used for rest of types
-def _get_assets(
- project_name,
- asset_ids=None,
- asset_names=None,
- parent_ids=None,
- standard=True,
- archived=False,
- fields=None
-):
- """Assets for specified project by passed filters.
-
- Passed filters (ids and names) are always combined so all conditions must
- match.
-
- To receive all assets from project just keep filters empty.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- asset_ids (Iterable[Union[str, ObjectId]]): Asset ids that should
- be found.
- asset_names (Iterable[str]): Name assets that should be found.
- parent_ids (Iterable[Union[str, ObjectId]]): Parent asset ids.
- standard (bool): Query standard assets (type 'asset').
- archived (bool): Query archived assets (type 'archived_asset').
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Cursor: Query cursor as iterable which returns asset documents matching
- passed filters.
- """
-
- asset_types = []
- if standard:
- asset_types.append("asset")
- if archived:
- asset_types.append("archived_asset")
-
- if not asset_types:
- return []
-
- if len(asset_types) == 1:
- query_filter = {"type": asset_types[0]}
- else:
- query_filter = {"type": {"$in": asset_types}}
-
- if asset_ids is not None:
- asset_ids = convert_ids(asset_ids)
- if not asset_ids:
- return []
- query_filter["_id"] = {"$in": asset_ids}
-
- if asset_names is not None:
- if not asset_names:
- return []
- query_filter["name"] = {"$in": list(asset_names)}
-
- if parent_ids is not None:
- parent_ids = convert_ids(parent_ids)
- if not parent_ids:
- return []
- query_filter["data.visualParent"] = {"$in": parent_ids}
-
- conn = get_project_connection(project_name)
-
- return conn.find(query_filter, _prepare_fields(fields))
-
-
-def get_assets(
- project_name,
- asset_ids=None,
- asset_names=None,
- parent_ids=None,
- archived=False,
- fields=None
-):
- """Assets for specified project by passed filters.
-
- Passed filters (ids and names) are always combined so all conditions must
- match.
-
- To receive all assets from project just keep filters empty.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- asset_ids (Iterable[Union[str, ObjectId]]): Asset ids that should
- be found.
- asset_names (Iterable[str]): Name assets that should be found.
- parent_ids (Iterable[Union[str, ObjectId]]): Parent asset ids.
- archived (bool): Add also archived assets.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Cursor: Query cursor as iterable which returns asset documents matching
- passed filters.
- """
-
- return _get_assets(
- project_name,
- asset_ids,
- asset_names,
- parent_ids,
- True,
- archived,
- fields
- )
-
-
-def get_archived_assets(
- project_name,
- asset_ids=None,
- asset_names=None,
- parent_ids=None,
- fields=None
-):
- """Archived assets for specified project by passed filters.
-
- Passed filters (ids and names) are always combined so all conditions must
- match.
-
- To receive all archived assets from project just keep filters empty.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- asset_ids (Iterable[Union[str, ObjectId]]): Asset ids that should
- be found.
- asset_names (Iterable[str]): Name assets that should be found.
- parent_ids (Iterable[Union[str, ObjectId]]): Parent asset ids.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Cursor: Query cursor as iterable which returns asset documents matching
- passed filters.
- """
-
- return _get_assets(
- project_name, asset_ids, asset_names, parent_ids, False, True, fields
- )
-
-
-def get_asset_ids_with_subsets(project_name, asset_ids=None):
- """Find out which assets have existing subsets.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- asset_ids (Iterable[Union[str, ObjectId]]): Look only for entered
- asset ids.
-
- Returns:
- Iterable[ObjectId]: Asset ids that have existing subsets.
- """
-
- subset_query = {
- "type": "subset"
- }
- if asset_ids is not None:
- asset_ids = convert_ids(asset_ids)
- if not asset_ids:
- return []
- subset_query["parent"] = {"$in": asset_ids}
-
- conn = get_project_connection(project_name)
- result = conn.aggregate([
- {
- "$match": subset_query
- },
- {
- "$group": {
- "_id": "$parent",
- "count": {"$sum": 1}
- }
- }
- ])
- asset_ids_with_subsets = []
- for item in result:
- asset_id = item["_id"]
- count = item["count"]
- if count > 0:
- asset_ids_with_subsets.append(asset_id)
- return asset_ids_with_subsets
-
-
-def get_subset_by_id(project_name, subset_id, fields=None):
- """Single subset entity data by its id.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- subset_id (Union[str, ObjectId]): Id of subset which should be found.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Subset entity data which can be reduced to
- specified 'fields'. None is returned if subset with specified
- filters was not found.
- """
-
- subset_id = convert_id(subset_id)
- if not subset_id:
- return None
-
- query_filters = {"type": "subset", "_id": subset_id}
- conn = get_project_connection(project_name)
- return conn.find_one(query_filters, _prepare_fields(fields))
-
-
-def get_subset_by_name(project_name, subset_name, asset_id, fields=None):
- """Single subset entity data by its name and its version id.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- subset_name (str): Name of subset.
- asset_id (Union[str, ObjectId]): Id of parent asset.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Subset entity data which can be reduced to
- specified 'fields'. None is returned if subset with specified
- filters was not found.
- """
- if not subset_name:
- return None
-
- asset_id = convert_id(asset_id)
- if not asset_id:
- return None
-
- query_filters = {
- "type": "subset",
- "name": subset_name,
- "parent": asset_id
- }
- conn = get_project_connection(project_name)
- return conn.find_one(query_filters, _prepare_fields(fields))
-
-
-def get_subsets(
- project_name,
- subset_ids=None,
- subset_names=None,
- asset_ids=None,
- names_by_asset_ids=None,
- archived=False,
- fields=None
-):
- """Subset entities data from one project filtered by entered filters.
-
- Filters are additive (all conditions must pass to return subset).
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- subset_ids (Iterable[Union[str, ObjectId]]): Subset ids that should be
- queried. Filter ignored if 'None' is passed.
- subset_names (Iterable[str]): Subset names that should be queried.
- Filter ignored if 'None' is passed.
- asset_ids (Iterable[Union[str, ObjectId]]): Asset ids under which
- should look for the subsets. Filter ignored if 'None' is passed.
- names_by_asset_ids (dict[ObjectId, List[str]]): Complex filtering
- using asset ids and list of subset names under the asset.
- archived (bool): Look for archived subsets too.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Cursor: Iterable cursor yielding all matching subsets.
- """
-
- subset_types = ["subset"]
- if archived:
- subset_types.append("archived_subset")
-
- if len(subset_types) == 1:
- query_filter = {"type": subset_types[0]}
- else:
- query_filter = {"type": {"$in": subset_types}}
-
- if asset_ids is not None:
- asset_ids = convert_ids(asset_ids)
- if not asset_ids:
- return []
- query_filter["parent"] = {"$in": asset_ids}
-
- if subset_ids is not None:
- subset_ids = convert_ids(subset_ids)
- if not subset_ids:
- return []
- query_filter["_id"] = {"$in": subset_ids}
-
- if subset_names is not None:
- if not subset_names:
- return []
- query_filter["name"] = {"$in": list(subset_names)}
-
- if names_by_asset_ids is not None:
- or_query = []
- for asset_id, names in names_by_asset_ids.items():
- if asset_id and names:
- or_query.append({
- "parent": convert_id(asset_id),
- "name": {"$in": list(names)}
- })
- if not or_query:
- return []
- query_filter["$or"] = or_query
-
- conn = get_project_connection(project_name)
- return conn.find(query_filter, _prepare_fields(fields))
-
-
-def get_subset_families(project_name, subset_ids=None):
- """Set of main families of subsets.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- subset_ids (Iterable[Union[str, ObjectId]]): Subset ids that should
- be queried. All subsets from project are used if 'None' is passed.
-
- Returns:
- set[str]: Main families of matching subsets.
- """
-
- subset_filter = {
- "type": "subset"
- }
- if subset_ids is not None:
- if not subset_ids:
- return set()
- subset_filter["_id"] = {"$in": list(subset_ids)}
-
- conn = get_project_connection(project_name)
- result = list(conn.aggregate([
- {"$match": subset_filter},
- {"$project": {
- "family": {"$arrayElemAt": ["$data.families", 0]}
- }},
- {"$group": {
- "_id": "family_group",
- "families": {"$addToSet": "$family"}
- }}
- ]))
- if result:
- return set(result[0]["families"])
- return set()
-
-
-def get_version_by_id(project_name, version_id, fields=None):
- """Single version entity data by its id.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- version_id (Union[str, ObjectId]): Id of version which should be found.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Version entity data which can be reduced to
- specified 'fields'. None is returned if version with specified
- filters was not found.
- """
-
- version_id = convert_id(version_id)
- if not version_id:
- return None
-
- query_filter = {
- "type": {"$in": ["version", "hero_version"]},
- "_id": version_id
- }
- conn = get_project_connection(project_name)
- return conn.find_one(query_filter, _prepare_fields(fields))
-
-
-def get_version_by_name(project_name, version, subset_id, fields=None):
- """Single version entity data by its name and subset id.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- version (int): name of version entity (its version).
- subset_id (Union[str, ObjectId]): Id of version which should be found.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Version entity data which can be reduced to
- specified 'fields'. None is returned if version with specified
- filters was not found.
- """
-
- subset_id = convert_id(subset_id)
- if not subset_id:
- return None
-
- conn = get_project_connection(project_name)
- query_filter = {
- "type": "version",
- "parent": subset_id,
- "name": version
- }
- return conn.find_one(query_filter, _prepare_fields(fields))
-
-
-def version_is_latest(project_name, version_id):
- """Is version the latest from its subset.
-
- Note:
- Hero versions are considered as latest.
-
- Todo:
- Maybe raise exception when version was not found?
-
- Args:
- project_name (str):Name of project where to look for queried entities.
- version_id (Union[str, ObjectId]): Version id which is checked.
-
- Returns:
- bool: True if is latest version from subset else False.
- """
-
- version_id = convert_id(version_id)
- if not version_id:
- return False
- version_doc = get_version_by_id(
- project_name, version_id, fields=["_id", "type", "parent"]
- )
- # What to do when version is not found?
- if not version_doc:
- return False
-
- if version_doc["type"] == "hero_version":
- return True
-
- last_version = get_last_version_by_subset_id(
- project_name, version_doc["parent"], fields=["_id"]
- )
- return last_version["_id"] == version_id
-
-
-def _get_versions(
- project_name,
- subset_ids=None,
- version_ids=None,
- versions=None,
- standard=True,
- hero=False,
- fields=None
-):
- version_types = []
- if standard:
- version_types.append("version")
-
- if hero:
- version_types.append("hero_version")
-
- if not version_types:
- return []
- elif len(version_types) == 1:
- query_filter = {"type": version_types[0]}
- else:
- query_filter = {"type": {"$in": version_types}}
-
- if subset_ids is not None:
- subset_ids = convert_ids(subset_ids)
- if not subset_ids:
- return []
- query_filter["parent"] = {"$in": subset_ids}
-
- if version_ids is not None:
- version_ids = convert_ids(version_ids)
- if not version_ids:
- return []
- query_filter["_id"] = {"$in": version_ids}
-
- if versions is not None:
- versions = list(versions)
- if not versions:
- return []
-
- if len(versions) == 1:
- query_filter["name"] = versions[0]
- else:
- query_filter["name"] = {"$in": versions}
-
- conn = get_project_connection(project_name)
-
- return conn.find(query_filter, _prepare_fields(fields))
-
-
-def get_versions(
- project_name,
- version_ids=None,
- subset_ids=None,
- versions=None,
- hero=False,
- fields=None
-):
- """Version entities data from one project filtered by entered filters.
-
- Filters are additive (all conditions must pass to return subset).
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- version_ids (Iterable[Union[str, ObjectId]]): Version ids that will
- be queried. Filter ignored if 'None' is passed.
- subset_ids (Iterable[str]): Subset ids that will be queried.
- Filter ignored if 'None' is passed.
- versions (Iterable[int]): Version names (as integers).
- Filter ignored if 'None' is passed.
- hero (bool): Look also for hero versions.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Cursor: Iterable cursor yielding all matching versions.
- """
-
- return _get_versions(
- project_name,
- subset_ids,
- version_ids,
- versions,
- standard=True,
- hero=hero,
- fields=fields
- )
-
-
-def get_hero_version_by_subset_id(project_name, subset_id, fields=None):
- """Hero version by subset id.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- subset_id (Union[str, ObjectId]): Subset id under which
- is hero version.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Hero version entity data which can be reduced to
- specified 'fields'. None is returned if hero version with specified
- filters was not found.
- """
-
- subset_id = convert_id(subset_id)
- if not subset_id:
- return None
-
- versions = list(_get_versions(
- project_name,
- subset_ids=[subset_id],
- standard=False,
- hero=True,
- fields=fields
- ))
- if versions:
- return versions[0]
- return None
-
-
-def get_hero_version_by_id(project_name, version_id, fields=None):
- """Hero version by its id.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- version_id (Union[str, ObjectId]): Hero version id.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Hero version entity data which can be reduced to
- specified 'fields'. None is returned if hero version with specified
- filters was not found.
- """
-
- version_id = convert_id(version_id)
- if not version_id:
- return None
-
- versions = list(_get_versions(
- project_name,
- version_ids=[version_id],
- standard=False,
- hero=True,
- fields=fields
- ))
- if versions:
- return versions[0]
- return None
-
-
-def get_hero_versions(
- project_name,
- subset_ids=None,
- version_ids=None,
- fields=None
-):
- """Hero version entities data from one project filtered by entered filters.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- subset_ids (Iterable[Union[str, ObjectId]]): Subset ids for which
- should look for hero versions. Filter ignored if 'None' is passed.
- version_ids (Iterable[Union[str, ObjectId]]): Hero version ids. Filter
- ignored if 'None' is passed.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Cursor|list: Iterable yielding hero versions matching passed filters.
- """
-
- return _get_versions(
- project_name,
- subset_ids,
- version_ids,
- standard=False,
- hero=True,
- fields=fields
- )
-
-
-def get_output_link_versions(project_name, version_id, fields=None):
- """Versions where passed version was used as input.
-
- Question:
- Not 100% sure about the usage of the function so the name and docstring
- maybe does not match what it does?
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- version_id (Union[str, ObjectId]): Version id which can be used
- as input link for other versions.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Iterable: Iterable cursor yielding versions that are used as input
- links for passed version.
- """
-
- version_id = convert_id(version_id)
- if not version_id:
- return []
-
- conn = get_project_connection(project_name)
- # Does make sense to look for hero versions?
- query_filter = {
- "type": "version",
- "data.inputLinks.id": version_id
- }
- return conn.find(query_filter, _prepare_fields(fields))
-
-
-def get_last_versions(project_name, subset_ids, active=None, fields=None):
- """Latest versions for entered subset_ids.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- subset_ids (Iterable[Union[str, ObjectId]]): List of subset ids.
- active (Optional[bool]): If True only active versions are returned.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- dict[ObjectId, int]: Key is subset id and value is last version name.
- """
-
- subset_ids = convert_ids(subset_ids)
- if not subset_ids:
- return {}
-
- if fields is not None:
- fields = list(fields)
- if not fields:
- return {}
-
- # Avoid double query if only name and _id are requested
- name_needed = False
- limit_query = False
- if fields:
- fields_s = set(fields)
- if "name" in fields_s:
- name_needed = True
- fields_s.remove("name")
-
- for field in ("_id", "parent"):
- if field in fields_s:
- fields_s.remove(field)
- limit_query = len(fields_s) == 0
-
- group_item = {
- "_id": "$parent",
- "_version_id": {"$last": "$_id"}
- }
- # Add name if name is needed (only for limit query)
- if name_needed:
- group_item["name"] = {"$last": "$name"}
-
- aggregate_filter = {
- "type": "version",
- "parent": {"$in": subset_ids}
- }
- if active is False:
- aggregate_filter["data.active"] = active
- elif active is True:
- aggregate_filter["$or"] = [
- {"data.active": {"$exists": 0}},
- {"data.active": active},
- ]
-
- aggregation_pipeline = [
- # Find all versions of those subsets
- {"$match": aggregate_filter},
- # Sorting versions all together
- {"$sort": {"name": 1}},
- # Group them by "parent", but only take the last
- {"$group": group_item}
- ]
-
- conn = get_project_connection(project_name)
- aggregate_result = conn.aggregate(aggregation_pipeline)
- if limit_query:
- output = {}
- for item in aggregate_result:
- subset_id = item["_id"]
- item_data = {"_id": item["_version_id"], "parent": subset_id}
- if name_needed:
- item_data["name"] = item["name"]
- output[subset_id] = item_data
- return output
-
- version_ids = [
- doc["_version_id"]
- for doc in aggregate_result
- ]
-
- fields = _prepare_fields(fields, ["parent"])
-
- version_docs = get_versions(
- project_name, version_ids=version_ids, fields=fields
- )
-
- return {
- version_doc["parent"]: version_doc
- for version_doc in version_docs
- }
-
-
-def get_last_version_by_subset_id(project_name, subset_id, fields=None):
- """Last version for passed subset id.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- subset_id (Union[str, ObjectId]): Id of version which should be found.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Version entity data which can be reduced to
- specified 'fields'. None is returned if version with specified
- filters was not found.
- """
-
- subset_id = convert_id(subset_id)
- if not subset_id:
- return None
-
- last_versions = get_last_versions(
- project_name, subset_ids=[subset_id], fields=fields
- )
- return last_versions.get(subset_id)
-
-
-def get_last_version_by_subset_name(
- project_name, subset_name, asset_id=None, asset_name=None, fields=None
-):
- """Last version for passed subset name under asset id/name.
-
- It is required to pass 'asset_id' or 'asset_name'. Asset id is recommended
- if is available.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- subset_name (str): Name of subset.
- asset_id (Union[str, ObjectId]): Asset id which is parent of passed
- subset name.
- asset_name (str): Asset name which is parent of passed subset name.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Version entity data which can be reduced to
- specified 'fields'. None is returned if version with specified
- filters was not found.
- """
-
- if not asset_id and not asset_name:
- return None
-
- if not asset_id:
- asset_doc = get_asset_by_name(project_name, asset_name, fields=["_id"])
- if not asset_doc:
- return None
- asset_id = asset_doc["_id"]
- subset_doc = get_subset_by_name(
- project_name, subset_name, asset_id, fields=["_id"]
- )
- if not subset_doc:
- return None
- return get_last_version_by_subset_id(
- project_name, subset_doc["_id"], fields=fields
- )
-
-
-def get_representation_by_id(project_name, representation_id, fields=None):
- """Representation entity data by its id.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- representation_id (Union[str, ObjectId]): Representation id.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Representation entity data which can be reduced to
- specified 'fields'. None is returned if representation with
- specified filters was not found.
- """
-
- if not representation_id:
- return None
-
- repre_types = ["representation", "archived_representation"]
- query_filter = {
- "type": {"$in": repre_types}
- }
- if representation_id is not None:
- query_filter["_id"] = convert_id(representation_id)
-
- conn = get_project_connection(project_name)
-
- return conn.find_one(query_filter, _prepare_fields(fields))
-
-
-def get_representation_by_name(
- project_name, representation_name, version_id, fields=None
-):
- """Representation entity data by its name and its version id.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- representation_name (str): Representation name.
- version_id (Union[str, ObjectId]): Id of parent version entity.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[dict[str, Any], None]: Representation entity data which can be
- reduced to specified 'fields'. None is returned if representation
- with specified filters was not found.
- """
-
- version_id = convert_id(version_id)
- if not version_id or not representation_name:
- return None
- repre_types = ["representation", "archived_representations"]
- query_filter = {
- "type": {"$in": repre_types},
- "name": representation_name,
- "parent": version_id
- }
-
- conn = get_project_connection(project_name)
- return conn.find_one(query_filter, _prepare_fields(fields))
-
-
-def _flatten_dict(data):
- flatten_queue = collections.deque()
- flatten_queue.append(data)
- output = {}
- while flatten_queue:
- item = flatten_queue.popleft()
- for key, value in item.items():
- if not isinstance(value, dict):
- output[key] = value
- continue
-
- tmp = {}
- for subkey, subvalue in value.items():
- new_key = "{}.{}".format(key, subkey)
- tmp[new_key] = subvalue
- flatten_queue.append(tmp)
- return output
-
-
-def _regex_filters(filters):
- output = []
- for key, value in filters.items():
- regexes = []
- a_values = []
- if isinstance(value, PatternType):
- regexes.append(value)
- elif isinstance(value, (list, tuple, set)):
- for item in value:
- if isinstance(item, PatternType):
- regexes.append(item)
- else:
- a_values.append(item)
- else:
- a_values.append(value)
-
- key_filters = []
- if len(a_values) == 1:
- key_filters.append({key: a_values[0]})
- elif a_values:
- key_filters.append({key: {"$in": a_values}})
-
- for regex in regexes:
- key_filters.append({key: {"$regex": regex}})
-
- if len(key_filters) == 1:
- output.append(key_filters[0])
- else:
- output.append({"$or": key_filters})
-
- return output
-
-
-def _get_representations(
- project_name,
- representation_ids,
- representation_names,
- version_ids,
- context_filters,
- names_by_version_ids,
- standard,
- archived,
- fields
-):
- default_output = []
- repre_types = []
- if standard:
- repre_types.append("representation")
- if archived:
- repre_types.append("archived_representation")
-
- if not repre_types:
- return default_output
-
- if len(repre_types) == 1:
- query_filter = {"type": repre_types[0]}
- else:
- query_filter = {"type": {"$in": repre_types}}
-
- if representation_ids is not None:
- representation_ids = convert_ids(representation_ids)
- if not representation_ids:
- return default_output
- query_filter["_id"] = {"$in": representation_ids}
-
- if representation_names is not None:
- if not representation_names:
- return default_output
- query_filter["name"] = {"$in": list(representation_names)}
-
- if version_ids is not None:
- version_ids = convert_ids(version_ids)
- if not version_ids:
- return default_output
- query_filter["parent"] = {"$in": version_ids}
-
- or_queries = []
- if names_by_version_ids is not None:
- or_query = []
- for version_id, names in names_by_version_ids.items():
- if version_id and names:
- or_query.append({
- "parent": convert_id(version_id),
- "name": {"$in": list(names)}
- })
- if not or_query:
- return default_output
- or_queries.append(or_query)
-
- if context_filters is not None:
- if not context_filters:
- return []
- _flatten_filters = _flatten_dict(context_filters)
- flatten_filters = {}
- for key, value in _flatten_filters.items():
- if not key.startswith("context"):
- key = "context.{}".format(key)
- flatten_filters[key] = value
-
- for item in _regex_filters(flatten_filters):
- for key, value in item.items():
- if key != "$or":
- query_filter[key] = value
-
- elif value:
- or_queries.append(value)
-
- if len(or_queries) == 1:
- query_filter["$or"] = or_queries[0]
- elif or_queries:
- and_query = []
- for or_query in or_queries:
- if isinstance(or_query, list):
- or_query = {"$or": or_query}
- and_query.append(or_query)
- query_filter["$and"] = and_query
-
- conn = get_project_connection(project_name)
-
- return conn.find(query_filter, _prepare_fields(fields))
-
-
-def get_representations(
- project_name,
- representation_ids=None,
- representation_names=None,
- version_ids=None,
- context_filters=None,
- names_by_version_ids=None,
- archived=False,
- standard=True,
- fields=None
-):
- """Representation entities data from one project filtered by filters.
-
- Filters are additive (all conditions must pass to return subset).
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- representation_ids (Iterable[Union[str, ObjectId]]): Representation ids
- used as filter. Filter ignored if 'None' is passed.
- representation_names (Iterable[str]): Representations names used
- as filter. Filter ignored if 'None' is passed.
- version_ids (Iterable[str]): Subset ids used as parent filter. Filter
- ignored if 'None' is passed.
- context_filters (Dict[str, List[str, PatternType]]): Filter by
- representation context fields.
- names_by_version_ids (dict[ObjectId, list[str]]): Complex filtering
- using version ids and list of names under the version.
- archived (bool): Output will also contain archived representations.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Cursor: Iterable cursor yielding all matching representations.
- """
-
- return _get_representations(
- project_name=project_name,
- representation_ids=representation_ids,
- representation_names=representation_names,
- version_ids=version_ids,
- context_filters=context_filters,
- names_by_version_ids=names_by_version_ids,
- standard=standard,
- archived=archived,
- fields=fields
- )
-
-
-def get_archived_representations(
- project_name,
- representation_ids=None,
- representation_names=None,
- version_ids=None,
- context_filters=None,
- names_by_version_ids=None,
- fields=None
-):
- """Archived representation entities data from project with applied filters.
-
- Filters are additive (all conditions must pass to return subset).
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- representation_ids (Iterable[Union[str, ObjectId]]): Representation ids
- used as filter. Filter ignored if 'None' is passed.
- representation_names (Iterable[str]): Representations names used
- as filter. Filter ignored if 'None' is passed.
- version_ids (Iterable[str]): Subset ids used as parent filter. Filter
- ignored if 'None' is passed.
- context_filters (Dict[str, List[str, PatternType]]): Filter by
- representation context fields.
- names_by_version_ids (dict[ObjectId, List[str]]): Complex filtering
- using version ids and list of names under the version.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Cursor: Iterable cursor yielding all matching representations.
- """
-
- return _get_representations(
- project_name=project_name,
- representation_ids=representation_ids,
- representation_names=representation_names,
- version_ids=version_ids,
- context_filters=context_filters,
- names_by_version_ids=names_by_version_ids,
- standard=False,
- archived=True,
- fields=fields
- )
-
-
-def get_representations_parents(project_name, representations):
- """Prepare parents of representation entities.
-
- Each item of returned dictionary contains version, subset, asset
- and project in that order.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- representations (List[dict]): Representation entities with at least
- '_id' and 'parent' keys.
-
- Returns:
- dict[ObjectId, tuple]: Parents by representation id.
- """
-
- repre_docs_by_version_id = collections.defaultdict(list)
- version_docs_by_version_id = {}
- version_docs_by_subset_id = collections.defaultdict(list)
- subset_docs_by_subset_id = {}
- subset_docs_by_asset_id = collections.defaultdict(list)
- output = {}
- for repre_doc in representations:
- repre_id = repre_doc["_id"]
- version_id = repre_doc["parent"]
- output[repre_id] = (None, None, None, None)
- repre_docs_by_version_id[version_id].append(repre_doc)
-
- version_docs = get_versions(
- project_name,
- version_ids=repre_docs_by_version_id.keys(),
- hero=True
- )
- for version_doc in version_docs:
- version_id = version_doc["_id"]
- subset_id = version_doc["parent"]
- version_docs_by_version_id[version_id] = version_doc
- version_docs_by_subset_id[subset_id].append(version_doc)
-
- subset_docs = get_subsets(
- project_name, subset_ids=version_docs_by_subset_id.keys()
- )
- for subset_doc in subset_docs:
- subset_id = subset_doc["_id"]
- asset_id = subset_doc["parent"]
- subset_docs_by_subset_id[subset_id] = subset_doc
- subset_docs_by_asset_id[asset_id].append(subset_doc)
-
- asset_docs = get_assets(
- project_name, asset_ids=subset_docs_by_asset_id.keys()
- )
- asset_docs_by_id = {
- asset_doc["_id"]: asset_doc
- for asset_doc in asset_docs
- }
-
- project_doc = get_project(project_name)
-
- for version_id, repre_docs in repre_docs_by_version_id.items():
- asset_doc = None
- subset_doc = None
- version_doc = version_docs_by_version_id.get(version_id)
- if version_doc:
- subset_id = version_doc["parent"]
- subset_doc = subset_docs_by_subset_id.get(subset_id)
- if subset_doc:
- asset_id = subset_doc["parent"]
- asset_doc = asset_docs_by_id.get(asset_id)
-
- for repre_doc in repre_docs:
- repre_id = repre_doc["_id"]
- output[repre_id] = (
- version_doc, subset_doc, asset_doc, project_doc
- )
- return output
-
-
-def get_representation_parents(project_name, representation):
- """Prepare parents of representation entity.
-
- Each item of returned dictionary contains version, subset, asset
- and project in that order.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- representation (dict): Representation entities with at least
- '_id' and 'parent' keys.
-
- Returns:
- dict[ObjectId, tuple]: Parents by representation id.
- """
-
- if not representation:
- return None
-
- repre_id = representation["_id"]
- parents_by_repre_id = get_representations_parents(
- project_name, [representation]
- )
- return parents_by_repre_id[repre_id]
-
-
-def get_thumbnail_id_from_source(project_name, src_type, src_id):
- """Receive thumbnail id from source entity.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- src_type (str): Type of source entity ('asset', 'version').
- src_id (Union[str, ObjectId]): Id of source entity.
-
- Returns:
- Union[ObjectId, None]: Thumbnail id assigned to entity. If Source
- entity does not have any thumbnail id assigned.
- """
-
- if not src_type or not src_id:
- return None
-
- query_filter = {"_id": convert_id(src_id)}
-
- conn = get_project_connection(project_name)
- src_doc = conn.find_one(query_filter, {"data.thumbnail_id"})
- if src_doc:
- return src_doc.get("data", {}).get("thumbnail_id")
- return None
-
-
-def get_thumbnails(project_name, thumbnail_ids, fields=None):
- """Receive thumbnails entity data.
-
- Thumbnail entity can be used to receive binary content of thumbnail based
- on its content and ThumbnailResolvers.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- thumbnail_ids (Iterable[Union[str, ObjectId]]): Ids of thumbnail
- entities.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- cursor: Cursor of queried documents.
- """
-
- if thumbnail_ids:
- thumbnail_ids = convert_ids(thumbnail_ids)
-
- if not thumbnail_ids:
- return []
- query_filter = {
- "type": "thumbnail",
- "_id": {"$in": thumbnail_ids}
- }
- conn = get_project_connection(project_name)
- return conn.find(query_filter, _prepare_fields(fields))
-
-
-def get_thumbnail(project_name, thumbnail_id, fields=None):
- """Receive thumbnail entity data.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- thumbnail_id (Union[str, ObjectId]): Id of thumbnail entity.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Thumbnail entity data which can be reduced to
- specified 'fields'.None is returned if thumbnail with specified
- filters was not found.
- """
-
- if not thumbnail_id:
- return None
- query_filter = {"type": "thumbnail", "_id": convert_id(thumbnail_id)}
- conn = get_project_connection(project_name)
- return conn.find_one(query_filter, _prepare_fields(fields))
-
-
-def get_workfile_info(
- project_name, asset_id, task_name, filename, fields=None
-):
- """Document with workfile information.
-
- Warning:
- Query is based on filename and context which does not meant it will
- find always right and expected result. Information have limited usage
- and is not recommended to use it as source information about workfile.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- asset_id (Union[str, ObjectId]): Id of asset entity.
- task_name (str): Task name on asset.
- fields (Optional[Iterable[str]]): Fields that should be returned. All
- fields are returned if 'None' is passed.
-
- Returns:
- Union[Dict, None]: Workfile entity data which can be reduced to
- specified 'fields'.None is returned if workfile with specified
- filters was not found.
- """
-
- if not asset_id or not task_name or not filename:
- return None
-
- query_filter = {
- "type": "workfile",
- "parent": convert_id(asset_id),
- "task_name": task_name,
- "filename": filename
- }
- conn = get_project_connection(project_name)
- return conn.find_one(query_filter, _prepare_fields(fields))
-
-
-"""
-## Custom data storage:
-- Settings - OP settings overrides and local settings
-- Logging - logs from Logger
-- Webpublisher - jobs
-- Ftrack - events
-- Maya - Shaders
- - openpype/hosts/maya/api/shader_definition_editor.py
- - openpype/hosts/maya/plugins/publish/validate_model_name.py
-
-## Global publish plugins
-- openpype/plugins/publish/extract_hierarchy_avalon.py
- Create:
- - asset
- Update:
- - asset
-
-## Lib
-- openpype/lib/avalon_context.py
- Update:
- - workfile data
-- openpype/lib/project_backpack.py
- Update:
- - project
-"""
+if not AYON_SERVER_ENABLED:
+ from .mongo.entities import *
+else:
+ from .server.entities import *
diff --git a/openpype/client/entity_links.py b/openpype/client/entity_links.py
index b74b4ce7f6..e18970de90 100644
--- a/openpype/client/entity_links.py
+++ b/openpype/client/entity_links.py
@@ -1,243 +1,6 @@
-from .mongo import get_project_connection
-from .entities import (
- get_assets,
- get_asset_by_id,
- get_version_by_id,
- get_representation_by_id,
- convert_id,
-)
+from openpype import AYON_SERVER_ENABLED
-
-def get_linked_asset_ids(project_name, asset_doc=None, asset_id=None):
- """Extract linked asset ids from asset document.
-
- One of asset document or asset id must be passed.
-
- Note:
- Asset links now works only from asset to assets.
-
- Args:
- asset_doc (dict): Asset document from DB.
-
- Returns:
- List[Union[ObjectId, str]]: Asset ids of input links.
- """
-
- output = []
- if not asset_doc and not asset_id:
- return output
-
- if not asset_doc:
- asset_doc = get_asset_by_id(
- project_name, asset_id, fields=["data.inputLinks"]
- )
-
- input_links = asset_doc["data"].get("inputLinks")
- if not input_links:
- return output
-
- for item in input_links:
- # Backwards compatibility for "_id" key which was replaced with
- # "id"
- if "_id" in item:
- link_id = item["_id"]
- else:
- link_id = item["id"]
- output.append(link_id)
- return output
-
-
-def get_linked_assets(
- project_name, asset_doc=None, asset_id=None, fields=None
-):
- """Return linked assets based on passed asset document.
-
- One of asset document or asset id must be passed.
-
- Args:
- project_name (str): Name of project where to look for queried entities.
- asset_doc (Dict[str, Any]): Asset document from database.
- asset_id (Union[ObjectId, str]): Asset id. Can be used instead of
- asset document.
- fields (Iterable[str]): Fields that should be returned. All fields are
- returned if 'None' is passed.
-
- Returns:
- List[Dict[str, Any]]: Asset documents of input links for passed
- asset doc.
- """
-
- if not asset_doc:
- if not asset_id:
- return []
- asset_doc = get_asset_by_id(
- project_name,
- asset_id,
- fields=["data.inputLinks"]
- )
- if not asset_doc:
- return []
-
- link_ids = get_linked_asset_ids(project_name, asset_doc=asset_doc)
- if not link_ids:
- return []
-
- return list(get_assets(project_name, asset_ids=link_ids, fields=fields))
-
-
-def get_linked_representation_id(
- project_name, repre_doc=None, repre_id=None, link_type=None, max_depth=None
-):
- """Returns list of linked ids of particular type (if provided).
-
- One of representation document or representation id must be passed.
- Note:
- Representation links now works only from representation through version
- back to representations.
-
- Args:
- project_name (str): Name of project where look for links.
- repre_doc (Dict[str, Any]): Representation document.
- repre_id (Union[ObjectId, str]): Representation id.
- link_type (str): Type of link (e.g. 'reference', ...).
- max_depth (int): Limit recursion level. Default: 0
-
- Returns:
- List[ObjectId] Linked representation ids.
- """
-
- if repre_doc:
- repre_id = repre_doc["_id"]
-
- if repre_id:
- repre_id = convert_id(repre_id)
-
- if not repre_id and not repre_doc:
- return []
-
- version_id = None
- if repre_doc:
- version_id = repre_doc.get("parent")
-
- if not version_id:
- repre_doc = get_representation_by_id(
- project_name, repre_id, fields=["parent"]
- )
- version_id = repre_doc["parent"]
-
- if not version_id:
- return []
-
- version_doc = get_version_by_id(
- project_name, version_id, fields=["type", "version_id"]
- )
- if version_doc["type"] == "hero_version":
- version_id = version_doc["version_id"]
-
- if max_depth is None:
- max_depth = 0
-
- match = {
- "_id": version_id,
- # Links are not stored to hero versions at this moment so filter
- # is limited to just versions
- "type": "version"
- }
-
- graph_lookup = {
- "from": project_name,
- "startWith": "$data.inputLinks.id",
- "connectFromField": "data.inputLinks.id",
- "connectToField": "_id",
- "as": "outputs_recursive",
- "depthField": "depth"
- }
- if max_depth != 0:
- # We offset by -1 since 0 basically means no recursion
- # but the recursion only happens after the initial lookup
- # for outputs.
- graph_lookup["maxDepth"] = max_depth - 1
-
- query_pipeline = [
- # Match
- {"$match": match},
- # Recursive graph lookup for inputs
- {"$graphLookup": graph_lookup}
- ]
- conn = get_project_connection(project_name)
- result = conn.aggregate(query_pipeline)
- referenced_version_ids = _process_referenced_pipeline_result(
- result, link_type
- )
- if not referenced_version_ids:
- return []
-
- ref_ids = conn.distinct(
- "_id",
- filter={
- "parent": {"$in": list(referenced_version_ids)},
- "type": "representation"
- }
- )
-
- return list(ref_ids)
-
-
-def _process_referenced_pipeline_result(result, link_type):
- """Filters result from pipeline for particular link_type.
-
- Pipeline cannot use link_type directly in a query.
-
- Returns:
- (list)
- """
-
- referenced_version_ids = set()
- correctly_linked_ids = set()
- for item in result:
- input_links = item.get("data", {}).get("inputLinks")
- if not input_links:
- continue
-
- _filter_input_links(
- input_links,
- link_type,
- correctly_linked_ids
- )
-
- # outputs_recursive in random order, sort by depth
- outputs_recursive = item.get("outputs_recursive")
- if not outputs_recursive:
- continue
-
- for output in sorted(outputs_recursive, key=lambda o: o["depth"]):
- output_links = output.get("data", {}).get("inputLinks")
- if not output_links and output["type"] != "hero_version":
- continue
-
- # Leaf
- if output["_id"] not in correctly_linked_ids:
- continue
-
- _filter_input_links(
- output_links,
- link_type,
- correctly_linked_ids
- )
-
- referenced_version_ids.add(output["_id"])
-
- return referenced_version_ids
-
-
-def _filter_input_links(input_links, link_type, correctly_linked_ids):
- if not input_links: # to handle hero versions
- return
-
- for input_link in input_links:
- if link_type and input_link["type"] != link_type:
- continue
-
- link_id = input_link.get("id") or input_link.get("_id")
- if link_id is not None:
- correctly_linked_ids.add(link_id)
+if not AYON_SERVER_ENABLED:
+ from .mongo.entity_links import *
+else:
+ from .server.entity_links import *
diff --git a/openpype/client/mongo/__init__.py b/openpype/client/mongo/__init__.py
new file mode 100644
index 0000000000..5c5143a731
--- /dev/null
+++ b/openpype/client/mongo/__init__.py
@@ -0,0 +1,20 @@
+from .mongo import (
+ MongoEnvNotSet,
+ get_default_components,
+ should_add_certificate_path_to_mongo_url,
+ validate_mongo_connection,
+ OpenPypeMongoConnection,
+ get_project_database,
+ get_project_connection,
+)
+
+
+__all__ = (
+ "MongoEnvNotSet",
+ "get_default_components",
+ "should_add_certificate_path_to_mongo_url",
+ "validate_mongo_connection",
+ "OpenPypeMongoConnection",
+ "get_project_database",
+ "get_project_connection",
+)
diff --git a/openpype/client/mongo/entities.py b/openpype/client/mongo/entities.py
new file mode 100644
index 0000000000..260fde4594
--- /dev/null
+++ b/openpype/client/mongo/entities.py
@@ -0,0 +1,1555 @@
+"""Unclear if these will have public functions like these.
+
+Goal is that most of functions here are called on (or with) an object
+that has project name as a context (e.g. on 'ProjectEntity'?).
+
++ We will need more specific functions doing very specific queries really fast.
+"""
+
+import re
+import collections
+
+import six
+from bson.objectid import ObjectId
+
+from .mongo import get_project_database, get_project_connection
+
+PatternType = type(re.compile(""))
+
+
+def _prepare_fields(fields, required_fields=None):
+ if not fields:
+ return None
+
+ output = {
+ field: True
+ for field in fields
+ }
+ if "_id" not in output:
+ output["_id"] = True
+
+ if required_fields:
+ for key in required_fields:
+ output[key] = True
+ return output
+
+
+def convert_id(in_id):
+ """Helper function for conversion of id from string to ObjectId.
+
+ Args:
+ in_id (Union[str, ObjectId, Any]): Entity id that should be converted
+ to right type for queries.
+
+ Returns:
+ Union[ObjectId, Any]: Converted ids to ObjectId or in type.
+ """
+
+ if isinstance(in_id, six.string_types):
+ return ObjectId(in_id)
+ return in_id
+
+
+def convert_ids(in_ids):
+ """Helper function for conversion of ids from string to ObjectId.
+
+ Args:
+ in_ids (Iterable[Union[str, ObjectId, Any]]): List of entity ids that
+ should be converted to right type for queries.
+
+ Returns:
+ List[ObjectId]: Converted ids to ObjectId.
+ """
+
+ _output = set()
+ for in_id in in_ids:
+ if in_id is not None:
+ _output.add(convert_id(in_id))
+ return list(_output)
+
+
+def get_projects(active=True, inactive=False, fields=None):
+ """Yield all project entity documents.
+
+ Args:
+ active (Optional[bool]): Include active projects. Defaults to True.
+ inactive (Optional[bool]): Include inactive projects.
+ Defaults to False.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Yields:
+ dict: Project entity data which can be reduced to specified 'fields'.
+ None is returned if project with specified filters was not found.
+ """
+ mongodb = get_project_database()
+ for project_name in mongodb.collection_names():
+ if project_name in ("system.indexes",):
+ continue
+ project_doc = get_project(
+ project_name, active=active, inactive=inactive, fields=fields
+ )
+ if project_doc is not None:
+ yield project_doc
+
+
+def get_project(project_name, active=True, inactive=True, fields=None):
+ """Return project entity document by project name.
+
+ Args:
+ project_name (str): Name of project.
+ active (Optional[bool]): Allow active project. Defaults to True.
+ inactive (Optional[bool]): Allow inactive project. Defaults to True.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Project entity data which can be reduced to
+ specified 'fields'. None is returned if project with specified
+ filters was not found.
+ """
+ # Skip if both are disabled
+ if not active and not inactive:
+ return None
+
+ query_filter = {"type": "project"}
+ # Keep query untouched if both should be available
+ if active and inactive:
+ pass
+
+ # Add filter to keep only active
+ elif active:
+ query_filter["$or"] = [
+ {"data.active": {"$exists": False}},
+ {"data.active": True},
+ ]
+
+ # Add filter to keep only inactive
+ elif inactive:
+ query_filter["$or"] = [
+ {"data.active": {"$exists": False}},
+ {"data.active": False},
+ ]
+
+ conn = get_project_connection(project_name)
+ return conn.find_one(query_filter, _prepare_fields(fields))
+
+
+def get_whole_project(project_name):
+ """Receive all documents from project.
+
+ Helper that can be used to get all document from whole project. For example
+ for backups etc.
+
+ Returns:
+ Cursor: Query cursor as iterable which returns all documents from
+ project collection.
+ """
+
+ conn = get_project_connection(project_name)
+ return conn.find({})
+
+
+def get_asset_by_id(project_name, asset_id, fields=None):
+ """Receive asset data by its id.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ asset_id (Union[str, ObjectId]): Asset's id.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Asset entity data which can be reduced to
+ specified 'fields'. None is returned if asset with specified
+ filters was not found.
+ """
+
+ asset_id = convert_id(asset_id)
+ if not asset_id:
+ return None
+
+ query_filter = {"type": "asset", "_id": asset_id}
+ conn = get_project_connection(project_name)
+ return conn.find_one(query_filter, _prepare_fields(fields))
+
+
+def get_asset_by_name(project_name, asset_name, fields=None):
+ """Receive asset data by its name.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ asset_name (str): Asset's name.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Asset entity data which can be reduced to
+ specified 'fields'. None is returned if asset with specified
+ filters was not found.
+ """
+
+ if not asset_name:
+ return None
+
+ query_filter = {"type": "asset", "name": asset_name}
+ conn = get_project_connection(project_name)
+ return conn.find_one(query_filter, _prepare_fields(fields))
+
+
+# NOTE this could be just public function?
+# - any better variable name instead of 'standard'?
+# - same approach can be used for rest of types
+def _get_assets(
+ project_name,
+ asset_ids=None,
+ asset_names=None,
+ parent_ids=None,
+ standard=True,
+ archived=False,
+ fields=None
+):
+ """Assets for specified project by passed filters.
+
+ Passed filters (ids and names) are always combined so all conditions must
+ match.
+
+ To receive all assets from project just keep filters empty.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ asset_ids (Iterable[Union[str, ObjectId]]): Asset ids that should
+ be found.
+ asset_names (Iterable[str]): Name assets that should be found.
+ parent_ids (Iterable[Union[str, ObjectId]]): Parent asset ids.
+ standard (bool): Query standard assets (type 'asset').
+ archived (bool): Query archived assets (type 'archived_asset').
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Cursor: Query cursor as iterable which returns asset documents matching
+ passed filters.
+ """
+
+ asset_types = []
+ if standard:
+ asset_types.append("asset")
+ if archived:
+ asset_types.append("archived_asset")
+
+ if not asset_types:
+ return []
+
+ if len(asset_types) == 1:
+ query_filter = {"type": asset_types[0]}
+ else:
+ query_filter = {"type": {"$in": asset_types}}
+
+ if asset_ids is not None:
+ asset_ids = convert_ids(asset_ids)
+ if not asset_ids:
+ return []
+ query_filter["_id"] = {"$in": asset_ids}
+
+ if asset_names is not None:
+ if not asset_names:
+ return []
+ query_filter["name"] = {"$in": list(asset_names)}
+
+ if parent_ids is not None:
+ parent_ids = convert_ids(parent_ids)
+ if not parent_ids:
+ return []
+ query_filter["data.visualParent"] = {"$in": parent_ids}
+
+ conn = get_project_connection(project_name)
+
+ return conn.find(query_filter, _prepare_fields(fields))
+
+
+def get_assets(
+ project_name,
+ asset_ids=None,
+ asset_names=None,
+ parent_ids=None,
+ archived=False,
+ fields=None
+):
+ """Assets for specified project by passed filters.
+
+ Passed filters (ids and names) are always combined so all conditions must
+ match.
+
+ To receive all assets from project just keep filters empty.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ asset_ids (Iterable[Union[str, ObjectId]]): Asset ids that should
+ be found.
+ asset_names (Iterable[str]): Name assets that should be found.
+ parent_ids (Iterable[Union[str, ObjectId]]): Parent asset ids.
+ archived (bool): Add also archived assets.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Cursor: Query cursor as iterable which returns asset documents matching
+ passed filters.
+ """
+
+ return _get_assets(
+ project_name,
+ asset_ids,
+ asset_names,
+ parent_ids,
+ True,
+ archived,
+ fields
+ )
+
+
+def get_archived_assets(
+ project_name,
+ asset_ids=None,
+ asset_names=None,
+ parent_ids=None,
+ fields=None
+):
+ """Archived assets for specified project by passed filters.
+
+ Passed filters (ids and names) are always combined so all conditions must
+ match.
+
+ To receive all archived assets from project just keep filters empty.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ asset_ids (Iterable[Union[str, ObjectId]]): Asset ids that should
+ be found.
+ asset_names (Iterable[str]): Name assets that should be found.
+ parent_ids (Iterable[Union[str, ObjectId]]): Parent asset ids.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Cursor: Query cursor as iterable which returns asset documents matching
+ passed filters.
+ """
+
+ return _get_assets(
+ project_name, asset_ids, asset_names, parent_ids, False, True, fields
+ )
+
+
+def get_asset_ids_with_subsets(project_name, asset_ids=None):
+ """Find out which assets have existing subsets.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ asset_ids (Iterable[Union[str, ObjectId]]): Look only for entered
+ asset ids.
+
+ Returns:
+ Iterable[ObjectId]: Asset ids that have existing subsets.
+ """
+
+ subset_query = {
+ "type": "subset"
+ }
+ if asset_ids is not None:
+ asset_ids = convert_ids(asset_ids)
+ if not asset_ids:
+ return []
+ subset_query["parent"] = {"$in": asset_ids}
+
+ conn = get_project_connection(project_name)
+ result = conn.aggregate([
+ {
+ "$match": subset_query
+ },
+ {
+ "$group": {
+ "_id": "$parent",
+ "count": {"$sum": 1}
+ }
+ }
+ ])
+ asset_ids_with_subsets = []
+ for item in result:
+ asset_id = item["_id"]
+ count = item["count"]
+ if count > 0:
+ asset_ids_with_subsets.append(asset_id)
+ return asset_ids_with_subsets
+
+
+def get_subset_by_id(project_name, subset_id, fields=None):
+ """Single subset entity data by its id.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ subset_id (Union[str, ObjectId]): Id of subset which should be found.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Subset entity data which can be reduced to
+ specified 'fields'. None is returned if subset with specified
+ filters was not found.
+ """
+
+ subset_id = convert_id(subset_id)
+ if not subset_id:
+ return None
+
+ query_filters = {"type": "subset", "_id": subset_id}
+ conn = get_project_connection(project_name)
+ return conn.find_one(query_filters, _prepare_fields(fields))
+
+
+def get_subset_by_name(project_name, subset_name, asset_id, fields=None):
+ """Single subset entity data by its name and its version id.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ subset_name (str): Name of subset.
+ asset_id (Union[str, ObjectId]): Id of parent asset.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Subset entity data which can be reduced to
+ specified 'fields'. None is returned if subset with specified
+ filters was not found.
+ """
+ if not subset_name:
+ return None
+
+ asset_id = convert_id(asset_id)
+ if not asset_id:
+ return None
+
+ query_filters = {
+ "type": "subset",
+ "name": subset_name,
+ "parent": asset_id
+ }
+ conn = get_project_connection(project_name)
+ return conn.find_one(query_filters, _prepare_fields(fields))
+
+
+def get_subsets(
+ project_name,
+ subset_ids=None,
+ subset_names=None,
+ asset_ids=None,
+ names_by_asset_ids=None,
+ archived=False,
+ fields=None
+):
+ """Subset entities data from one project filtered by entered filters.
+
+ Filters are additive (all conditions must pass to return subset).
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ subset_ids (Iterable[Union[str, ObjectId]]): Subset ids that should be
+ queried. Filter ignored if 'None' is passed.
+ subset_names (Iterable[str]): Subset names that should be queried.
+ Filter ignored if 'None' is passed.
+ asset_ids (Iterable[Union[str, ObjectId]]): Asset ids under which
+ should look for the subsets. Filter ignored if 'None' is passed.
+ names_by_asset_ids (dict[ObjectId, List[str]]): Complex filtering
+ using asset ids and list of subset names under the asset.
+ archived (bool): Look for archived subsets too.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Cursor: Iterable cursor yielding all matching subsets.
+ """
+
+ subset_types = ["subset"]
+ if archived:
+ subset_types.append("archived_subset")
+
+ if len(subset_types) == 1:
+ query_filter = {"type": subset_types[0]}
+ else:
+ query_filter = {"type": {"$in": subset_types}}
+
+ if asset_ids is not None:
+ asset_ids = convert_ids(asset_ids)
+ if not asset_ids:
+ return []
+ query_filter["parent"] = {"$in": asset_ids}
+
+ if subset_ids is not None:
+ subset_ids = convert_ids(subset_ids)
+ if not subset_ids:
+ return []
+ query_filter["_id"] = {"$in": subset_ids}
+
+ if subset_names is not None:
+ if not subset_names:
+ return []
+ query_filter["name"] = {"$in": list(subset_names)}
+
+ if names_by_asset_ids is not None:
+ or_query = []
+ for asset_id, names in names_by_asset_ids.items():
+ if asset_id and names:
+ or_query.append({
+ "parent": convert_id(asset_id),
+ "name": {"$in": list(names)}
+ })
+ if not or_query:
+ return []
+ query_filter["$or"] = or_query
+
+ conn = get_project_connection(project_name)
+ return conn.find(query_filter, _prepare_fields(fields))
+
+
+def get_subset_families(project_name, subset_ids=None):
+ """Set of main families of subsets.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ subset_ids (Iterable[Union[str, ObjectId]]): Subset ids that should
+ be queried. All subsets from project are used if 'None' is passed.
+
+ Returns:
+ set[str]: Main families of matching subsets.
+ """
+
+ subset_filter = {
+ "type": "subset"
+ }
+ if subset_ids is not None:
+ if not subset_ids:
+ return set()
+ subset_filter["_id"] = {"$in": list(subset_ids)}
+
+ conn = get_project_connection(project_name)
+ result = list(conn.aggregate([
+ {"$match": subset_filter},
+ {"$project": {
+ "family": {"$arrayElemAt": ["$data.families", 0]}
+ }},
+ {"$group": {
+ "_id": "family_group",
+ "families": {"$addToSet": "$family"}
+ }}
+ ]))
+ if result:
+ return set(result[0]["families"])
+ return set()
+
+
+def get_version_by_id(project_name, version_id, fields=None):
+ """Single version entity data by its id.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ version_id (Union[str, ObjectId]): Id of version which should be found.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Version entity data which can be reduced to
+ specified 'fields'. None is returned if version with specified
+ filters was not found.
+ """
+
+ version_id = convert_id(version_id)
+ if not version_id:
+ return None
+
+ query_filter = {
+ "type": {"$in": ["version", "hero_version"]},
+ "_id": version_id
+ }
+ conn = get_project_connection(project_name)
+ return conn.find_one(query_filter, _prepare_fields(fields))
+
+
+def get_version_by_name(project_name, version, subset_id, fields=None):
+ """Single version entity data by its name and subset id.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ version (int): name of version entity (its version).
+ subset_id (Union[str, ObjectId]): Id of version which should be found.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Version entity data which can be reduced to
+ specified 'fields'. None is returned if version with specified
+ filters was not found.
+ """
+
+ subset_id = convert_id(subset_id)
+ if not subset_id:
+ return None
+
+ conn = get_project_connection(project_name)
+ query_filter = {
+ "type": "version",
+ "parent": subset_id,
+ "name": version
+ }
+ return conn.find_one(query_filter, _prepare_fields(fields))
+
+
+def version_is_latest(project_name, version_id):
+ """Is version the latest from its subset.
+
+ Note:
+ Hero versions are considered as latest.
+
+ Todo:
+ Maybe raise exception when version was not found?
+
+ Args:
+ project_name (str):Name of project where to look for queried entities.
+ version_id (Union[str, ObjectId]): Version id which is checked.
+
+ Returns:
+ bool: True if is latest version from subset else False.
+ """
+
+ version_id = convert_id(version_id)
+ if not version_id:
+ return False
+ version_doc = get_version_by_id(
+ project_name, version_id, fields=["_id", "type", "parent"]
+ )
+ # What to do when version is not found?
+ if not version_doc:
+ return False
+
+ if version_doc["type"] == "hero_version":
+ return True
+
+ last_version = get_last_version_by_subset_id(
+ project_name, version_doc["parent"], fields=["_id"]
+ )
+ return last_version["_id"] == version_id
+
+
+def _get_versions(
+ project_name,
+ subset_ids=None,
+ version_ids=None,
+ versions=None,
+ standard=True,
+ hero=False,
+ fields=None
+):
+ version_types = []
+ if standard:
+ version_types.append("version")
+
+ if hero:
+ version_types.append("hero_version")
+
+ if not version_types:
+ return []
+ elif len(version_types) == 1:
+ query_filter = {"type": version_types[0]}
+ else:
+ query_filter = {"type": {"$in": version_types}}
+
+ if subset_ids is not None:
+ subset_ids = convert_ids(subset_ids)
+ if not subset_ids:
+ return []
+ query_filter["parent"] = {"$in": subset_ids}
+
+ if version_ids is not None:
+ version_ids = convert_ids(version_ids)
+ if not version_ids:
+ return []
+ query_filter["_id"] = {"$in": version_ids}
+
+ if versions is not None:
+ versions = list(versions)
+ if not versions:
+ return []
+
+ if len(versions) == 1:
+ query_filter["name"] = versions[0]
+ else:
+ query_filter["name"] = {"$in": versions}
+
+ conn = get_project_connection(project_name)
+
+ return conn.find(query_filter, _prepare_fields(fields))
+
+
+def get_versions(
+ project_name,
+ version_ids=None,
+ subset_ids=None,
+ versions=None,
+ hero=False,
+ fields=None
+):
+ """Version entities data from one project filtered by entered filters.
+
+ Filters are additive (all conditions must pass to return subset).
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ version_ids (Iterable[Union[str, ObjectId]]): Version ids that will
+ be queried. Filter ignored if 'None' is passed.
+ subset_ids (Iterable[str]): Subset ids that will be queried.
+ Filter ignored if 'None' is passed.
+ versions (Iterable[int]): Version names (as integers).
+ Filter ignored if 'None' is passed.
+ hero (bool): Look also for hero versions.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Cursor: Iterable cursor yielding all matching versions.
+ """
+
+ return _get_versions(
+ project_name,
+ subset_ids,
+ version_ids,
+ versions,
+ standard=True,
+ hero=hero,
+ fields=fields
+ )
+
+
+def get_hero_version_by_subset_id(project_name, subset_id, fields=None):
+ """Hero version by subset id.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ subset_id (Union[str, ObjectId]): Subset id under which
+ is hero version.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Hero version entity data which can be reduced to
+ specified 'fields'. None is returned if hero version with specified
+ filters was not found.
+ """
+
+ subset_id = convert_id(subset_id)
+ if not subset_id:
+ return None
+
+ versions = list(_get_versions(
+ project_name,
+ subset_ids=[subset_id],
+ standard=False,
+ hero=True,
+ fields=fields
+ ))
+ if versions:
+ return versions[0]
+ return None
+
+
+def get_hero_version_by_id(project_name, version_id, fields=None):
+ """Hero version by its id.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ version_id (Union[str, ObjectId]): Hero version id.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Hero version entity data which can be reduced to
+ specified 'fields'. None is returned if hero version with specified
+ filters was not found.
+ """
+
+ version_id = convert_id(version_id)
+ if not version_id:
+ return None
+
+ versions = list(_get_versions(
+ project_name,
+ version_ids=[version_id],
+ standard=False,
+ hero=True,
+ fields=fields
+ ))
+ if versions:
+ return versions[0]
+ return None
+
+
+def get_hero_versions(
+ project_name,
+ subset_ids=None,
+ version_ids=None,
+ fields=None
+):
+ """Hero version entities data from one project filtered by entered filters.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ subset_ids (Iterable[Union[str, ObjectId]]): Subset ids for which
+ should look for hero versions. Filter ignored if 'None' is passed.
+ version_ids (Iterable[Union[str, ObjectId]]): Hero version ids. Filter
+ ignored if 'None' is passed.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Cursor|list: Iterable yielding hero versions matching passed filters.
+ """
+
+ return _get_versions(
+ project_name,
+ subset_ids,
+ version_ids,
+ standard=False,
+ hero=True,
+ fields=fields
+ )
+
+
+def get_output_link_versions(project_name, version_id, fields=None):
+ """Versions where passed version was used as input.
+
+ Question:
+ Not 100% sure about the usage of the function so the name and docstring
+ maybe does not match what it does?
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ version_id (Union[str, ObjectId]): Version id which can be used
+ as input link for other versions.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Iterable: Iterable cursor yielding versions that are used as input
+ links for passed version.
+ """
+
+ version_id = convert_id(version_id)
+ if not version_id:
+ return []
+
+ conn = get_project_connection(project_name)
+ # Does make sense to look for hero versions?
+ query_filter = {
+ "type": "version",
+ "data.inputLinks.id": version_id
+ }
+ return conn.find(query_filter, _prepare_fields(fields))
+
+
+def get_last_versions(project_name, subset_ids, active=None, fields=None):
+ """Latest versions for entered subset_ids.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ subset_ids (Iterable[Union[str, ObjectId]]): List of subset ids.
+ active (Optional[bool]): If True only active versions are returned.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ dict[ObjectId, int]: Key is subset id and value is last version name.
+ """
+
+ subset_ids = convert_ids(subset_ids)
+ if not subset_ids:
+ return {}
+
+ if fields is not None:
+ fields = list(fields)
+ if not fields:
+ return {}
+
+ # Avoid double query if only name and _id are requested
+ name_needed = False
+ limit_query = False
+ if fields:
+ fields_s = set(fields)
+ if "name" in fields_s:
+ name_needed = True
+ fields_s.remove("name")
+
+ for field in ("_id", "parent"):
+ if field in fields_s:
+ fields_s.remove(field)
+ limit_query = len(fields_s) == 0
+
+ group_item = {
+ "_id": "$parent",
+ "_version_id": {"$last": "$_id"}
+ }
+ # Add name if name is needed (only for limit query)
+ if name_needed:
+ group_item["name"] = {"$last": "$name"}
+
+ aggregate_filter = {
+ "type": "version",
+ "parent": {"$in": subset_ids}
+ }
+ if active is False:
+ aggregate_filter["data.active"] = active
+ elif active is True:
+ aggregate_filter["$or"] = [
+ {"data.active": {"$exists": 0}},
+ {"data.active": active},
+ ]
+
+ aggregation_pipeline = [
+ # Find all versions of those subsets
+ {"$match": aggregate_filter},
+ # Sorting versions all together
+ {"$sort": {"name": 1}},
+ # Group them by "parent", but only take the last
+ {"$group": group_item}
+ ]
+
+ conn = get_project_connection(project_name)
+ aggregate_result = conn.aggregate(aggregation_pipeline)
+ if limit_query:
+ output = {}
+ for item in aggregate_result:
+ subset_id = item["_id"]
+ item_data = {"_id": item["_version_id"], "parent": subset_id}
+ if name_needed:
+ item_data["name"] = item["name"]
+ output[subset_id] = item_data
+ return output
+
+ version_ids = [
+ doc["_version_id"]
+ for doc in aggregate_result
+ ]
+
+ fields = _prepare_fields(fields, ["parent"])
+
+ version_docs = get_versions(
+ project_name, version_ids=version_ids, fields=fields
+ )
+
+ return {
+ version_doc["parent"]: version_doc
+ for version_doc in version_docs
+ }
+
+
+def get_last_version_by_subset_id(project_name, subset_id, fields=None):
+ """Last version for passed subset id.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ subset_id (Union[str, ObjectId]): Id of version which should be found.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Version entity data which can be reduced to
+ specified 'fields'. None is returned if version with specified
+ filters was not found.
+ """
+
+ subset_id = convert_id(subset_id)
+ if not subset_id:
+ return None
+
+ last_versions = get_last_versions(
+ project_name, subset_ids=[subset_id], fields=fields
+ )
+ return last_versions.get(subset_id)
+
+
+def get_last_version_by_subset_name(
+ project_name, subset_name, asset_id=None, asset_name=None, fields=None
+):
+ """Last version for passed subset name under asset id/name.
+
+ It is required to pass 'asset_id' or 'asset_name'. Asset id is recommended
+ if is available.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ subset_name (str): Name of subset.
+ asset_id (Union[str, ObjectId]): Asset id which is parent of passed
+ subset name.
+ asset_name (str): Asset name which is parent of passed subset name.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Version entity data which can be reduced to
+ specified 'fields'. None is returned if version with specified
+ filters was not found.
+ """
+
+ if not asset_id and not asset_name:
+ return None
+
+ if not asset_id:
+ asset_doc = get_asset_by_name(project_name, asset_name, fields=["_id"])
+ if not asset_doc:
+ return None
+ asset_id = asset_doc["_id"]
+ subset_doc = get_subset_by_name(
+ project_name, subset_name, asset_id, fields=["_id"]
+ )
+ if not subset_doc:
+ return None
+ return get_last_version_by_subset_id(
+ project_name, subset_doc["_id"], fields=fields
+ )
+
+
+def get_representation_by_id(project_name, representation_id, fields=None):
+ """Representation entity data by its id.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ representation_id (Union[str, ObjectId]): Representation id.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Representation entity data which can be reduced to
+ specified 'fields'. None is returned if representation with
+ specified filters was not found.
+ """
+
+ if not representation_id:
+ return None
+
+ repre_types = ["representation", "archived_representation"]
+ query_filter = {
+ "type": {"$in": repre_types}
+ }
+ if representation_id is not None:
+ query_filter["_id"] = convert_id(representation_id)
+
+ conn = get_project_connection(project_name)
+
+ return conn.find_one(query_filter, _prepare_fields(fields))
+
+
+def get_representation_by_name(
+ project_name, representation_name, version_id, fields=None
+):
+ """Representation entity data by its name and its version id.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ representation_name (str): Representation name.
+ version_id (Union[str, ObjectId]): Id of parent version entity.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[dict[str, Any], None]: Representation entity data which can be
+ reduced to specified 'fields'. None is returned if representation
+ with specified filters was not found.
+ """
+
+ version_id = convert_id(version_id)
+ if not version_id or not representation_name:
+ return None
+ repre_types = ["representation", "archived_representations"]
+ query_filter = {
+ "type": {"$in": repre_types},
+ "name": representation_name,
+ "parent": version_id
+ }
+
+ conn = get_project_connection(project_name)
+ return conn.find_one(query_filter, _prepare_fields(fields))
+
+
+def _flatten_dict(data):
+ flatten_queue = collections.deque()
+ flatten_queue.append(data)
+ output = {}
+ while flatten_queue:
+ item = flatten_queue.popleft()
+ for key, value in item.items():
+ if not isinstance(value, dict):
+ output[key] = value
+ continue
+
+ tmp = {}
+ for subkey, subvalue in value.items():
+ new_key = "{}.{}".format(key, subkey)
+ tmp[new_key] = subvalue
+ flatten_queue.append(tmp)
+ return output
+
+
+def _regex_filters(filters):
+ output = []
+ for key, value in filters.items():
+ regexes = []
+ a_values = []
+ if isinstance(value, PatternType):
+ regexes.append(value)
+ elif isinstance(value, (list, tuple, set)):
+ for item in value:
+ if isinstance(item, PatternType):
+ regexes.append(item)
+ else:
+ a_values.append(item)
+ else:
+ a_values.append(value)
+
+ key_filters = []
+ if len(a_values) == 1:
+ key_filters.append({key: a_values[0]})
+ elif a_values:
+ key_filters.append({key: {"$in": a_values}})
+
+ for regex in regexes:
+ key_filters.append({key: {"$regex": regex}})
+
+ if len(key_filters) == 1:
+ output.append(key_filters[0])
+ else:
+ output.append({"$or": key_filters})
+
+ return output
+
+
+def _get_representations(
+ project_name,
+ representation_ids,
+ representation_names,
+ version_ids,
+ context_filters,
+ names_by_version_ids,
+ standard,
+ archived,
+ fields
+):
+ default_output = []
+ repre_types = []
+ if standard:
+ repre_types.append("representation")
+ if archived:
+ repre_types.append("archived_representation")
+
+ if not repre_types:
+ return default_output
+
+ if len(repre_types) == 1:
+ query_filter = {"type": repre_types[0]}
+ else:
+ query_filter = {"type": {"$in": repre_types}}
+
+ if representation_ids is not None:
+ representation_ids = convert_ids(representation_ids)
+ if not representation_ids:
+ return default_output
+ query_filter["_id"] = {"$in": representation_ids}
+
+ if representation_names is not None:
+ if not representation_names:
+ return default_output
+ query_filter["name"] = {"$in": list(representation_names)}
+
+ if version_ids is not None:
+ version_ids = convert_ids(version_ids)
+ if not version_ids:
+ return default_output
+ query_filter["parent"] = {"$in": version_ids}
+
+ or_queries = []
+ if names_by_version_ids is not None:
+ or_query = []
+ for version_id, names in names_by_version_ids.items():
+ if version_id and names:
+ or_query.append({
+ "parent": convert_id(version_id),
+ "name": {"$in": list(names)}
+ })
+ if not or_query:
+ return default_output
+ or_queries.append(or_query)
+
+ if context_filters is not None:
+ if not context_filters:
+ return []
+ _flatten_filters = _flatten_dict(context_filters)
+ flatten_filters = {}
+ for key, value in _flatten_filters.items():
+ if not key.startswith("context"):
+ key = "context.{}".format(key)
+ flatten_filters[key] = value
+
+ for item in _regex_filters(flatten_filters):
+ for key, value in item.items():
+ if key != "$or":
+ query_filter[key] = value
+
+ elif value:
+ or_queries.append(value)
+
+ if len(or_queries) == 1:
+ query_filter["$or"] = or_queries[0]
+ elif or_queries:
+ and_query = []
+ for or_query in or_queries:
+ if isinstance(or_query, list):
+ or_query = {"$or": or_query}
+ and_query.append(or_query)
+ query_filter["$and"] = and_query
+
+ conn = get_project_connection(project_name)
+
+ return conn.find(query_filter, _prepare_fields(fields))
+
+
+def get_representations(
+ project_name,
+ representation_ids=None,
+ representation_names=None,
+ version_ids=None,
+ context_filters=None,
+ names_by_version_ids=None,
+ archived=False,
+ standard=True,
+ fields=None
+):
+ """Representation entities data from one project filtered by filters.
+
+ Filters are additive (all conditions must pass to return subset).
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ representation_ids (Iterable[Union[str, ObjectId]]): Representation ids
+ used as filter. Filter ignored if 'None' is passed.
+ representation_names (Iterable[str]): Representations names used
+ as filter. Filter ignored if 'None' is passed.
+ version_ids (Iterable[str]): Subset ids used as parent filter. Filter
+ ignored if 'None' is passed.
+ context_filters (Dict[str, List[str, PatternType]]): Filter by
+ representation context fields.
+ names_by_version_ids (dict[ObjectId, list[str]]): Complex filtering
+ using version ids and list of names under the version.
+ archived (bool): Output will also contain archived representations.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Cursor: Iterable cursor yielding all matching representations.
+ """
+
+ return _get_representations(
+ project_name=project_name,
+ representation_ids=representation_ids,
+ representation_names=representation_names,
+ version_ids=version_ids,
+ context_filters=context_filters,
+ names_by_version_ids=names_by_version_ids,
+ standard=standard,
+ archived=archived,
+ fields=fields
+ )
+
+
+def get_archived_representations(
+ project_name,
+ representation_ids=None,
+ representation_names=None,
+ version_ids=None,
+ context_filters=None,
+ names_by_version_ids=None,
+ fields=None
+):
+ """Archived representation entities data from project with applied filters.
+
+ Filters are additive (all conditions must pass to return subset).
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ representation_ids (Iterable[Union[str, ObjectId]]): Representation ids
+ used as filter. Filter ignored if 'None' is passed.
+ representation_names (Iterable[str]): Representations names used
+ as filter. Filter ignored if 'None' is passed.
+ version_ids (Iterable[str]): Subset ids used as parent filter. Filter
+ ignored if 'None' is passed.
+ context_filters (Dict[str, List[str, PatternType]]): Filter by
+ representation context fields.
+ names_by_version_ids (dict[ObjectId, List[str]]): Complex filtering
+ using version ids and list of names under the version.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Cursor: Iterable cursor yielding all matching representations.
+ """
+
+ return _get_representations(
+ project_name=project_name,
+ representation_ids=representation_ids,
+ representation_names=representation_names,
+ version_ids=version_ids,
+ context_filters=context_filters,
+ names_by_version_ids=names_by_version_ids,
+ standard=False,
+ archived=True,
+ fields=fields
+ )
+
+
+def get_representations_parents(project_name, representations):
+ """Prepare parents of representation entities.
+
+ Each item of returned dictionary contains version, subset, asset
+ and project in that order.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ representations (List[dict]): Representation entities with at least
+ '_id' and 'parent' keys.
+
+ Returns:
+ dict[ObjectId, tuple]: Parents by representation id.
+ """
+
+ repre_docs_by_version_id = collections.defaultdict(list)
+ version_docs_by_version_id = {}
+ version_docs_by_subset_id = collections.defaultdict(list)
+ subset_docs_by_subset_id = {}
+ subset_docs_by_asset_id = collections.defaultdict(list)
+ output = {}
+ for repre_doc in representations:
+ repre_id = repre_doc["_id"]
+ version_id = repre_doc["parent"]
+ output[repre_id] = (None, None, None, None)
+ repre_docs_by_version_id[version_id].append(repre_doc)
+
+ version_docs = get_versions(
+ project_name,
+ version_ids=repre_docs_by_version_id.keys(),
+ hero=True
+ )
+ for version_doc in version_docs:
+ version_id = version_doc["_id"]
+ subset_id = version_doc["parent"]
+ version_docs_by_version_id[version_id] = version_doc
+ version_docs_by_subset_id[subset_id].append(version_doc)
+
+ subset_docs = get_subsets(
+ project_name, subset_ids=version_docs_by_subset_id.keys()
+ )
+ for subset_doc in subset_docs:
+ subset_id = subset_doc["_id"]
+ asset_id = subset_doc["parent"]
+ subset_docs_by_subset_id[subset_id] = subset_doc
+ subset_docs_by_asset_id[asset_id].append(subset_doc)
+
+ asset_docs = get_assets(
+ project_name, asset_ids=subset_docs_by_asset_id.keys()
+ )
+ asset_docs_by_id = {
+ asset_doc["_id"]: asset_doc
+ for asset_doc in asset_docs
+ }
+
+ project_doc = get_project(project_name)
+
+ for version_id, repre_docs in repre_docs_by_version_id.items():
+ asset_doc = None
+ subset_doc = None
+ version_doc = version_docs_by_version_id.get(version_id)
+ if version_doc:
+ subset_id = version_doc["parent"]
+ subset_doc = subset_docs_by_subset_id.get(subset_id)
+ if subset_doc:
+ asset_id = subset_doc["parent"]
+ asset_doc = asset_docs_by_id.get(asset_id)
+
+ for repre_doc in repre_docs:
+ repre_id = repre_doc["_id"]
+ output[repre_id] = (
+ version_doc, subset_doc, asset_doc, project_doc
+ )
+ return output
+
+
+def get_representation_parents(project_name, representation):
+ """Prepare parents of representation entity.
+
+ Each item of returned dictionary contains version, subset, asset
+ and project in that order.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ representation (dict): Representation entities with at least
+ '_id' and 'parent' keys.
+
+ Returns:
+ dict[ObjectId, tuple]: Parents by representation id.
+ """
+
+ if not representation:
+ return None
+
+ repre_id = representation["_id"]
+ parents_by_repre_id = get_representations_parents(
+ project_name, [representation]
+ )
+ return parents_by_repre_id[repre_id]
+
+
+def get_thumbnail_id_from_source(project_name, src_type, src_id):
+ """Receive thumbnail id from source entity.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ src_type (str): Type of source entity ('asset', 'version').
+ src_id (Union[str, ObjectId]): Id of source entity.
+
+ Returns:
+ Union[ObjectId, None]: Thumbnail id assigned to entity. If Source
+ entity does not have any thumbnail id assigned.
+ """
+
+ if not src_type or not src_id:
+ return None
+
+ query_filter = {"_id": convert_id(src_id)}
+
+ conn = get_project_connection(project_name)
+ src_doc = conn.find_one(query_filter, {"data.thumbnail_id"})
+ if src_doc:
+ return src_doc.get("data", {}).get("thumbnail_id")
+ return None
+
+
+def get_thumbnails(project_name, thumbnail_ids, fields=None):
+ """Receive thumbnails entity data.
+
+ Thumbnail entity can be used to receive binary content of thumbnail based
+ on its content and ThumbnailResolvers.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ thumbnail_ids (Iterable[Union[str, ObjectId]]): Ids of thumbnail
+ entities.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ cursor: Cursor of queried documents.
+ """
+
+ if thumbnail_ids:
+ thumbnail_ids = convert_ids(thumbnail_ids)
+
+ if not thumbnail_ids:
+ return []
+ query_filter = {
+ "type": "thumbnail",
+ "_id": {"$in": thumbnail_ids}
+ }
+ conn = get_project_connection(project_name)
+ return conn.find(query_filter, _prepare_fields(fields))
+
+
+def get_thumbnail(
+ project_name, thumbnail_id, entity_type, entity_id, fields=None
+):
+ """Receive thumbnail entity data.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ thumbnail_id (Union[str, ObjectId]): Id of thumbnail entity.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Thumbnail entity data which can be reduced to
+ specified 'fields'.None is returned if thumbnail with specified
+ filters was not found.
+ """
+
+ if not thumbnail_id:
+ return None
+ query_filter = {"type": "thumbnail", "_id": convert_id(thumbnail_id)}
+ conn = get_project_connection(project_name)
+ return conn.find_one(query_filter, _prepare_fields(fields))
+
+
+def get_workfile_info(
+ project_name, asset_id, task_name, filename, fields=None
+):
+ """Document with workfile information.
+
+ Warning:
+ Query is based on filename and context which does not meant it will
+ find always right and expected result. Information have limited usage
+ and is not recommended to use it as source information about workfile.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ asset_id (Union[str, ObjectId]): Id of asset entity.
+ task_name (str): Task name on asset.
+ fields (Optional[Iterable[str]]): Fields that should be returned. All
+ fields are returned if 'None' is passed.
+
+ Returns:
+ Union[Dict, None]: Workfile entity data which can be reduced to
+ specified 'fields'.None is returned if workfile with specified
+ filters was not found.
+ """
+
+ if not asset_id or not task_name or not filename:
+ return None
+
+ query_filter = {
+ "type": "workfile",
+ "parent": convert_id(asset_id),
+ "task_name": task_name,
+ "filename": filename
+ }
+ conn = get_project_connection(project_name)
+ return conn.find_one(query_filter, _prepare_fields(fields))
+
+
+"""
+## Custom data storage:
+- Settings - OP settings overrides and local settings
+- Logging - logs from Logger
+- Webpublisher - jobs
+- Ftrack - events
+- Maya - Shaders
+ - openpype/hosts/maya/api/shader_definition_editor.py
+ - openpype/hosts/maya/plugins/publish/validate_model_name.py
+
+## Global publish plugins
+- openpype/plugins/publish/extract_hierarchy_avalon.py
+ Create:
+ - asset
+ Update:
+ - asset
+
+## Lib
+- openpype/lib/avalon_context.py
+ Update:
+ - workfile data
+- openpype/lib/project_backpack.py
+ Update:
+ - project
+"""
diff --git a/openpype/client/mongo/entity_links.py b/openpype/client/mongo/entity_links.py
new file mode 100644
index 0000000000..c97a828118
--- /dev/null
+++ b/openpype/client/mongo/entity_links.py
@@ -0,0 +1,244 @@
+from .mongo import get_project_connection
+from .entities import (
+ get_assets,
+ get_asset_by_id,
+ get_version_by_id,
+ get_representation_by_id,
+ convert_id,
+)
+
+
+def get_linked_asset_ids(project_name, asset_doc=None, asset_id=None):
+ """Extract linked asset ids from asset document.
+
+ One of asset document or asset id must be passed.
+
+ Note:
+ Asset links now works only from asset to assets.
+
+ Args:
+ asset_doc (dict): Asset document from DB.
+
+ Returns:
+ List[Union[ObjectId, str]]: Asset ids of input links.
+ """
+
+ output = []
+ if not asset_doc and not asset_id:
+ return output
+
+ if not asset_doc:
+ asset_doc = get_asset_by_id(
+ project_name, asset_id, fields=["data.inputLinks"]
+ )
+
+ input_links = asset_doc["data"].get("inputLinks")
+ if not input_links:
+ return output
+
+ for item in input_links:
+ # Backwards compatibility for "_id" key which was replaced with
+ # "id"
+ if "_id" in item:
+ link_id = item["_id"]
+ else:
+ link_id = item["id"]
+ output.append(link_id)
+ return output
+
+
+def get_linked_assets(
+ project_name, asset_doc=None, asset_id=None, fields=None
+):
+ """Return linked assets based on passed asset document.
+
+ One of asset document or asset id must be passed.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ asset_doc (Dict[str, Any]): Asset document from database.
+ asset_id (Union[ObjectId, str]): Asset id. Can be used instead of
+ asset document.
+ fields (Iterable[str]): Fields that should be returned. All fields are
+ returned if 'None' is passed.
+
+ Returns:
+ List[Dict[str, Any]]: Asset documents of input links for passed
+ asset doc.
+ """
+
+ if not asset_doc:
+ if not asset_id:
+ return []
+ asset_doc = get_asset_by_id(
+ project_name,
+ asset_id,
+ fields=["data.inputLinks"]
+ )
+ if not asset_doc:
+ return []
+
+ link_ids = get_linked_asset_ids(project_name, asset_doc=asset_doc)
+ if not link_ids:
+ return []
+
+ return list(get_assets(project_name, asset_ids=link_ids, fields=fields))
+
+
+def get_linked_representation_id(
+ project_name, repre_doc=None, repre_id=None, link_type=None, max_depth=None
+):
+ """Returns list of linked ids of particular type (if provided).
+
+ One of representation document or representation id must be passed.
+ Note:
+ Representation links now works only from representation through version
+ back to representations.
+
+ Args:
+ project_name (str): Name of project where look for links.
+ repre_doc (Dict[str, Any]): Representation document.
+ repre_id (Union[ObjectId, str]): Representation id.
+ link_type (str): Type of link (e.g. 'reference', ...).
+ max_depth (int): Limit recursion level. Default: 0
+
+ Returns:
+ List[ObjectId] Linked representation ids.
+ """
+
+ if repre_doc:
+ repre_id = repre_doc["_id"]
+
+ if repre_id:
+ repre_id = convert_id(repre_id)
+
+ if not repre_id and not repre_doc:
+ return []
+
+ version_id = None
+ if repre_doc:
+ version_id = repre_doc.get("parent")
+
+ if not version_id:
+ repre_doc = get_representation_by_id(
+ project_name, repre_id, fields=["parent"]
+ )
+ version_id = repre_doc["parent"]
+
+ if not version_id:
+ return []
+
+ version_doc = get_version_by_id(
+ project_name, version_id, fields=["type", "version_id"]
+ )
+ if version_doc["type"] == "hero_version":
+ version_id = version_doc["version_id"]
+
+ if max_depth is None:
+ max_depth = 0
+
+ match = {
+ "_id": version_id,
+ # Links are not stored to hero versions at this moment so filter
+ # is limited to just versions
+ "type": "version"
+ }
+
+ graph_lookup = {
+ "from": project_name,
+ "startWith": "$data.inputLinks.id",
+ "connectFromField": "data.inputLinks.id",
+ "connectToField": "_id",
+ "as": "outputs_recursive",
+ "depthField": "depth"
+ }
+ if max_depth != 0:
+ # We offset by -1 since 0 basically means no recursion
+ # but the recursion only happens after the initial lookup
+ # for outputs.
+ graph_lookup["maxDepth"] = max_depth - 1
+
+ query_pipeline = [
+ # Match
+ {"$match": match},
+ # Recursive graph lookup for inputs
+ {"$graphLookup": graph_lookup}
+ ]
+
+ conn = get_project_connection(project_name)
+ result = conn.aggregate(query_pipeline)
+ referenced_version_ids = _process_referenced_pipeline_result(
+ result, link_type
+ )
+ if not referenced_version_ids:
+ return []
+
+ ref_ids = conn.distinct(
+ "_id",
+ filter={
+ "parent": {"$in": list(referenced_version_ids)},
+ "type": "representation"
+ }
+ )
+
+ return list(ref_ids)
+
+
+def _process_referenced_pipeline_result(result, link_type):
+ """Filters result from pipeline for particular link_type.
+
+ Pipeline cannot use link_type directly in a query.
+
+ Returns:
+ (list)
+ """
+
+ referenced_version_ids = set()
+ correctly_linked_ids = set()
+ for item in result:
+ input_links = item.get("data", {}).get("inputLinks")
+ if not input_links:
+ continue
+
+ _filter_input_links(
+ input_links,
+ link_type,
+ correctly_linked_ids
+ )
+
+ # outputs_recursive in random order, sort by depth
+ outputs_recursive = item.get("outputs_recursive")
+ if not outputs_recursive:
+ continue
+
+ for output in sorted(outputs_recursive, key=lambda o: o["depth"]):
+ output_links = output.get("data", {}).get("inputLinks")
+ if not output_links and output["type"] != "hero_version":
+ continue
+
+ # Leaf
+ if output["_id"] not in correctly_linked_ids:
+ continue
+
+ _filter_input_links(
+ output_links,
+ link_type,
+ correctly_linked_ids
+ )
+
+ referenced_version_ids.add(output["_id"])
+
+ return referenced_version_ids
+
+
+def _filter_input_links(input_links, link_type, correctly_linked_ids):
+ if not input_links: # to handle hero versions
+ return
+
+ for input_link in input_links:
+ if link_type and input_link["type"] != link_type:
+ continue
+
+ link_id = input_link.get("id") or input_link.get("_id")
+ if link_id is not None:
+ correctly_linked_ids.add(link_id)
diff --git a/openpype/client/mongo.py b/openpype/client/mongo/mongo.py
similarity index 98%
rename from openpype/client/mongo.py
rename to openpype/client/mongo/mongo.py
index 251041c028..2be426efeb 100644
--- a/openpype/client/mongo.py
+++ b/openpype/client/mongo/mongo.py
@@ -11,6 +11,7 @@ from bson.json_util import (
CANONICAL_JSON_OPTIONS
)
+from openpype import AYON_SERVER_ENABLED
if sys.version_info[0] == 2:
from urlparse import urlparse, parse_qs
else:
@@ -134,7 +135,7 @@ def should_add_certificate_path_to_mongo_url(mongo_url):
add_certificate = False
# Check if url 'ssl' or 'tls' are set to 'true'
for key in ("ssl", "tls"):
- if key in query and "true" in query["ssl"]:
+ if key in query and "true" in query[key]:
add_certificate = True
break
@@ -206,6 +207,8 @@ class OpenPypeMongoConnection:
@classmethod
def create_connection(cls, mongo_url, timeout=None, retry_attempts=None):
+ if AYON_SERVER_ENABLED:
+ raise RuntimeError("Created mongo connection in AYON mode")
parsed = urlparse(mongo_url)
# Force validation of scheme
if parsed.scheme not in ["mongodb", "mongodb+srv"]:
@@ -221,7 +224,7 @@ class OpenPypeMongoConnection:
"serverSelectionTimeoutMS": timeout
}
if should_add_certificate_path_to_mongo_url(mongo_url):
- kwargs["ssl_ca_certs"] = certifi.where()
+ kwargs["tlsCAFile"] = certifi.where()
mongo_client = pymongo.MongoClient(mongo_url, **kwargs)
diff --git a/openpype/client/mongo/operations.py b/openpype/client/mongo/operations.py
new file mode 100644
index 0000000000..3537aa4a3d
--- /dev/null
+++ b/openpype/client/mongo/operations.py
@@ -0,0 +1,632 @@
+import re
+import copy
+import collections
+
+from bson.objectid import ObjectId
+from pymongo import DeleteOne, InsertOne, UpdateOne
+
+from openpype.client.operations_base import (
+ REMOVED_VALUE,
+ CreateOperation,
+ UpdateOperation,
+ DeleteOperation,
+ BaseOperationsSession
+)
+from .mongo import get_project_connection
+from .entities import get_project
+
+
+PROJECT_NAME_ALLOWED_SYMBOLS = "a-zA-Z0-9_"
+PROJECT_NAME_REGEX = re.compile(
+ "^[{}]+$".format(PROJECT_NAME_ALLOWED_SYMBOLS)
+)
+
+CURRENT_PROJECT_SCHEMA = "openpype:project-3.0"
+CURRENT_PROJECT_CONFIG_SCHEMA = "openpype:config-2.0"
+CURRENT_ASSET_DOC_SCHEMA = "openpype:asset-3.0"
+CURRENT_SUBSET_SCHEMA = "openpype:subset-3.0"
+CURRENT_VERSION_SCHEMA = "openpype:version-3.0"
+CURRENT_HERO_VERSION_SCHEMA = "openpype:hero_version-1.0"
+CURRENT_REPRESENTATION_SCHEMA = "openpype:representation-2.0"
+CURRENT_WORKFILE_INFO_SCHEMA = "openpype:workfile-1.0"
+CURRENT_THUMBNAIL_SCHEMA = "openpype:thumbnail-1.0"
+
+
+def _create_or_convert_to_mongo_id(mongo_id):
+ if mongo_id is None:
+ return ObjectId()
+ return ObjectId(mongo_id)
+
+
+def new_project_document(
+ project_name, project_code, config, data=None, entity_id=None
+):
+ """Create skeleton data of project document.
+
+ Args:
+ project_name (str): Name of project. Used as identifier of a project.
+ project_code (str): Shorter version of projet without spaces and
+ special characters (in most of cases). Should be also considered
+ as unique name across projects.
+ config (Dic[str, Any]): Project config consist of roots, templates,
+ applications and other project Anatomy related data.
+ data (Dict[str, Any]): Project data with information about it's
+ attributes (e.g. 'fps' etc.) or integration specific keys.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of project document.
+ """
+
+ if data is None:
+ data = {}
+
+ data["code"] = project_code
+
+ return {
+ "_id": _create_or_convert_to_mongo_id(entity_id),
+ "name": project_name,
+ "type": CURRENT_PROJECT_SCHEMA,
+ "entity_data": data,
+ "config": config
+ }
+
+
+def new_asset_document(
+ name, project_id, parent_id, parents, data=None, entity_id=None
+):
+ """Create skeleton data of asset document.
+
+ Args:
+ name (str): Is considered as unique identifier of asset in project.
+ project_id (Union[str, ObjectId]): Id of project doument.
+ parent_id (Union[str, ObjectId]): Id of parent asset.
+ parents (List[str]): List of parent assets names.
+ data (Dict[str, Any]): Asset document data. Empty dictionary is used
+ if not passed. Value of 'parent_id' is used to fill 'visualParent'.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of asset document.
+ """
+
+ if data is None:
+ data = {}
+ if parent_id is not None:
+ parent_id = ObjectId(parent_id)
+ data["visualParent"] = parent_id
+ data["parents"] = parents
+
+ return {
+ "_id": _create_or_convert_to_mongo_id(entity_id),
+ "type": "asset",
+ "name": name,
+ "parent": ObjectId(project_id),
+ "data": data,
+ "schema": CURRENT_ASSET_DOC_SCHEMA
+ }
+
+
+def new_subset_document(name, family, asset_id, data=None, entity_id=None):
+ """Create skeleton data of subset document.
+
+ Args:
+ name (str): Is considered as unique identifier of subset under asset.
+ family (str): Subset's family.
+ asset_id (Union[str, ObjectId]): Id of parent asset.
+ data (Dict[str, Any]): Subset document data. Empty dictionary is used
+ if not passed. Value of 'family' is used to fill 'family'.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of subset document.
+ """
+
+ if data is None:
+ data = {}
+ data["family"] = family
+ return {
+ "_id": _create_or_convert_to_mongo_id(entity_id),
+ "schema": CURRENT_SUBSET_SCHEMA,
+ "type": "subset",
+ "name": name,
+ "data": data,
+ "parent": asset_id
+ }
+
+
+def new_version_doc(version, subset_id, data=None, entity_id=None):
+ """Create skeleton data of version document.
+
+ Args:
+ version (int): Is considered as unique identifier of version
+ under subset.
+ subset_id (Union[str, ObjectId]): Id of parent subset.
+ data (Dict[str, Any]): Version document data.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of version document.
+ """
+
+ if data is None:
+ data = {}
+
+ return {
+ "_id": _create_or_convert_to_mongo_id(entity_id),
+ "schema": CURRENT_VERSION_SCHEMA,
+ "type": "version",
+ "name": int(version),
+ "parent": subset_id,
+ "data": data
+ }
+
+
+def new_hero_version_doc(version_id, subset_id, data=None, entity_id=None):
+ """Create skeleton data of hero version document.
+
+ Args:
+ version_id (ObjectId): Is considered as unique identifier of version
+ under subset.
+ subset_id (Union[str, ObjectId]): Id of parent subset.
+ data (Dict[str, Any]): Version document data.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of version document.
+ """
+
+ if data is None:
+ data = {}
+
+ return {
+ "_id": _create_or_convert_to_mongo_id(entity_id),
+ "schema": CURRENT_HERO_VERSION_SCHEMA,
+ "type": "hero_version",
+ "version_id": version_id,
+ "parent": subset_id,
+ "data": data
+ }
+
+
+def new_representation_doc(
+ name, version_id, context, data=None, entity_id=None
+):
+ """Create skeleton data of asset document.
+
+ Args:
+ version (int): Is considered as unique identifier of version
+ under subset.
+ version_id (Union[str, ObjectId]): Id of parent version.
+ context (Dict[str, Any]): Representation context used for fill template
+ of to query.
+ data (Dict[str, Any]): Representation document data.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of version document.
+ """
+
+ if data is None:
+ data = {}
+
+ return {
+ "_id": _create_or_convert_to_mongo_id(entity_id),
+ "schema": CURRENT_REPRESENTATION_SCHEMA,
+ "type": "representation",
+ "parent": version_id,
+ "name": name,
+ "data": data,
+
+ # Imprint shortcut to context for performance reasons.
+ "context": context
+ }
+
+
+def new_thumbnail_doc(data=None, entity_id=None):
+ """Create skeleton data of thumbnail document.
+
+ Args:
+ data (Dict[str, Any]): Thumbnail document data.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of thumbnail document.
+ """
+
+ if data is None:
+ data = {}
+
+ return {
+ "_id": _create_or_convert_to_mongo_id(entity_id),
+ "type": "thumbnail",
+ "schema": CURRENT_THUMBNAIL_SCHEMA,
+ "data": data
+ }
+
+
+def new_workfile_info_doc(
+ filename, asset_id, task_name, files, data=None, entity_id=None
+):
+ """Create skeleton data of workfile info document.
+
+ Workfile document is at this moment used primarily for artist notes.
+
+ Args:
+ filename (str): Filename of workfile.
+ asset_id (Union[str, ObjectId]): Id of asset under which workfile live.
+ task_name (str): Task under which was workfile created.
+ files (List[str]): List of rootless filepaths related to workfile.
+ data (Dict[str, Any]): Additional metadata.
+
+ Returns:
+ Dict[str, Any]: Skeleton of workfile info document.
+ """
+
+ if not data:
+ data = {}
+
+ return {
+ "_id": _create_or_convert_to_mongo_id(entity_id),
+ "type": "workfile",
+ "parent": ObjectId(asset_id),
+ "task_name": task_name,
+ "filename": filename,
+ "data": data,
+ "files": files
+ }
+
+
+def _prepare_update_data(old_doc, new_doc, replace):
+ changes = {}
+ for key, value in new_doc.items():
+ if key not in old_doc or value != old_doc[key]:
+ changes[key] = value
+
+ if replace:
+ for key in old_doc.keys():
+ if key not in new_doc:
+ changes[key] = REMOVED_VALUE
+ return changes
+
+
+def prepare_subset_update_data(old_doc, new_doc, replace=True):
+ """Compare two subset documents and prepare update data.
+
+ Based on compared values will create update data for
+ 'MongoUpdateOperation'.
+
+ Empty output means that documents are identical.
+
+ Returns:
+ Dict[str, Any]: Changes between old and new document.
+ """
+
+ return _prepare_update_data(old_doc, new_doc, replace)
+
+
+def prepare_version_update_data(old_doc, new_doc, replace=True):
+ """Compare two version documents and prepare update data.
+
+ Based on compared values will create update data for
+ 'MongoUpdateOperation'.
+
+ Empty output means that documents are identical.
+
+ Returns:
+ Dict[str, Any]: Changes between old and new document.
+ """
+
+ return _prepare_update_data(old_doc, new_doc, replace)
+
+
+def prepare_hero_version_update_data(old_doc, new_doc, replace=True):
+ """Compare two hero version documents and prepare update data.
+
+ Based on compared values will create update data for 'UpdateOperation'.
+
+ Empty output means that documents are identical.
+
+ Returns:
+ Dict[str, Any]: Changes between old and new document.
+ """
+
+ return _prepare_update_data(old_doc, new_doc, replace)
+
+
+def prepare_representation_update_data(old_doc, new_doc, replace=True):
+ """Compare two representation documents and prepare update data.
+
+ Based on compared values will create update data for
+ 'MongoUpdateOperation'.
+
+ Empty output means that documents are identical.
+
+ Returns:
+ Dict[str, Any]: Changes between old and new document.
+ """
+
+ return _prepare_update_data(old_doc, new_doc, replace)
+
+
+def prepare_workfile_info_update_data(old_doc, new_doc, replace=True):
+ """Compare two workfile info documents and prepare update data.
+
+ Based on compared values will create update data for
+ 'MongoUpdateOperation'.
+
+ Empty output means that documents are identical.
+
+ Returns:
+ Dict[str, Any]: Changes between old and new document.
+ """
+
+ return _prepare_update_data(old_doc, new_doc, replace)
+
+
+class MongoCreateOperation(CreateOperation):
+ """Operation to create an entity.
+
+ Args:
+ project_name (str): On which project operation will happen.
+ entity_type (str): Type of entity on which change happens.
+ e.g. 'asset', 'representation' etc.
+ data (Dict[str, Any]): Data of entity that will be created.
+ """
+
+ operation_name = "create"
+
+ def __init__(self, project_name, entity_type, data):
+ super(MongoCreateOperation, self).__init__(
+ project_name, entity_type, data
+ )
+
+ if "_id" not in self._data:
+ self._data["_id"] = ObjectId()
+ else:
+ self._data["_id"] = ObjectId(self._data["_id"])
+
+ @property
+ def entity_id(self):
+ return self._data["_id"]
+
+ def to_mongo_operation(self):
+ return InsertOne(copy.deepcopy(self._data))
+
+
+class MongoUpdateOperation(UpdateOperation):
+ """Operation to update an entity.
+
+ Args:
+ project_name (str): On which project operation will happen.
+ entity_type (str): Type of entity on which change happens.
+ e.g. 'asset', 'representation' etc.
+ entity_id (Union[str, ObjectId]): Identifier of an entity.
+ update_data (Dict[str, Any]): Key -> value changes that will be set in
+ database. If value is set to 'REMOVED_VALUE' the key will be
+ removed. Only first level of dictionary is checked (on purpose).
+ """
+
+ operation_name = "update"
+
+ def __init__(self, project_name, entity_type, entity_id, update_data):
+ super(MongoUpdateOperation, self).__init__(
+ project_name, entity_type, entity_id, update_data
+ )
+
+ self._entity_id = ObjectId(self._entity_id)
+
+ def to_mongo_operation(self):
+ unset_data = {}
+ set_data = {}
+ for key, value in self._update_data.items():
+ if value is REMOVED_VALUE:
+ unset_data[key] = None
+ else:
+ set_data[key] = value
+
+ op_data = {}
+ if unset_data:
+ op_data["$unset"] = unset_data
+ if set_data:
+ op_data["$set"] = set_data
+
+ if not op_data:
+ return None
+
+ return UpdateOne(
+ {"_id": self.entity_id},
+ op_data
+ )
+
+
+class MongoDeleteOperation(DeleteOperation):
+ """Operation to delete an entity.
+
+ Args:
+ project_name (str): On which project operation will happen.
+ entity_type (str): Type of entity on which change happens.
+ e.g. 'asset', 'representation' etc.
+ entity_id (Union[str, ObjectId]): Entity id that will be removed.
+ """
+
+ operation_name = "delete"
+
+ def __init__(self, project_name, entity_type, entity_id):
+ super(MongoDeleteOperation, self).__init__(
+ project_name, entity_type, entity_id
+ )
+
+ self._entity_id = ObjectId(self._entity_id)
+
+ def to_mongo_operation(self):
+ return DeleteOne({"_id": self.entity_id})
+
+
+class MongoOperationsSession(BaseOperationsSession):
+ """Session storing operations that should happen in an order.
+
+ At this moment does not handle anything special can be sonsidered as
+ stupid list of operations that will happen after each other. If creation
+ of same entity is there multiple times it's handled in any way and document
+ values are not validated.
+
+ All operations must be related to single project.
+
+ Args:
+ project_name (str): Project name to which are operations related.
+ """
+
+ def commit(self):
+ """Commit session operations."""
+
+ operations, self._operations = self._operations, []
+ if not operations:
+ return
+
+ operations_by_project = collections.defaultdict(list)
+ for operation in operations:
+ operations_by_project[operation.project_name].append(operation)
+
+ for project_name, operations in operations_by_project.items():
+ bulk_writes = []
+ for operation in operations:
+ mongo_op = operation.to_mongo_operation()
+ if mongo_op is not None:
+ bulk_writes.append(mongo_op)
+
+ if bulk_writes:
+ collection = get_project_connection(project_name)
+ collection.bulk_write(bulk_writes)
+
+ def create_entity(self, project_name, entity_type, data):
+ """Fast access to 'MongoCreateOperation'.
+
+ Returns:
+ MongoCreateOperation: Object of update operation.
+ """
+
+ operation = MongoCreateOperation(project_name, entity_type, data)
+ self.add(operation)
+ return operation
+
+ def update_entity(self, project_name, entity_type, entity_id, update_data):
+ """Fast access to 'MongoUpdateOperation'.
+
+ Returns:
+ MongoUpdateOperation: Object of update operation.
+ """
+
+ operation = MongoUpdateOperation(
+ project_name, entity_type, entity_id, update_data
+ )
+ self.add(operation)
+ return operation
+
+ def delete_entity(self, project_name, entity_type, entity_id):
+ """Fast access to 'MongoDeleteOperation'.
+
+ Returns:
+ MongoDeleteOperation: Object of delete operation.
+ """
+
+ operation = MongoDeleteOperation(project_name, entity_type, entity_id)
+ self.add(operation)
+ return operation
+
+
+def create_project(
+ project_name,
+ project_code,
+ library_project=False,
+):
+ """Create project using OpenPype settings.
+
+ This project creation function is not validating project document on
+ creation. It is because project document is created blindly with only
+ minimum required information about project which is it's name, code, type
+ and schema.
+
+ Entered project name must be unique and project must not exist yet.
+
+ Note:
+ This function is here to be OP v4 ready but in v3 has more logic
+ to do. That's why inner imports are in the body.
+
+ Args:
+ project_name(str): New project name. Should be unique.
+ project_code(str): Project's code should be unique too.
+ library_project(bool): Project is library project.
+
+ Raises:
+ ValueError: When project name already exists in MongoDB.
+
+ Returns:
+ dict: Created project document.
+ """
+
+ from openpype.settings import ProjectSettings, SaveWarningExc
+ from openpype.pipeline.schema import validate
+
+ if get_project(project_name, fields=["name"]):
+ raise ValueError("Project with name \"{}\" already exists".format(
+ project_name
+ ))
+
+ if not PROJECT_NAME_REGEX.match(project_name):
+ raise ValueError((
+ "Project name \"{}\" contain invalid characters"
+ ).format(project_name))
+
+ project_doc = {
+ "type": "project",
+ "name": project_name,
+ "data": {
+ "code": project_code,
+ "library_project": library_project
+ },
+ "schema": CURRENT_PROJECT_SCHEMA
+ }
+
+ op_session = MongoOperationsSession()
+ # Insert document with basic data
+ create_op = op_session.create_entity(
+ project_name, project_doc["type"], project_doc
+ )
+ op_session.commit()
+
+ # Load ProjectSettings for the project and save it to store all attributes
+ # and Anatomy
+ try:
+ project_settings_entity = ProjectSettings(project_name)
+ project_settings_entity.save()
+ except SaveWarningExc as exc:
+ print(str(exc))
+ except Exception:
+ op_session.delete_entity(
+ project_name, project_doc["type"], create_op.entity_id
+ )
+ op_session.commit()
+ raise
+
+ project_doc = get_project(project_name)
+
+ try:
+ # Validate created project document
+ validate(project_doc)
+ except Exception:
+ # Remove project if is not valid
+ op_session.delete_entity(
+ project_name, project_doc["type"], create_op.entity_id
+ )
+ op_session.commit()
+ raise
+
+ return project_doc
diff --git a/openpype/client/operations.py b/openpype/client/operations.py
index ef48f2a1c4..8bc09dffd3 100644
--- a/openpype/client/operations.py
+++ b/openpype/client/operations.py
@@ -1,794 +1,24 @@
-import re
-import uuid
-import copy
-import collections
-from abc import ABCMeta, abstractmethod, abstractproperty
+from openpype import AYON_SERVER_ENABLED
-import six
-from bson.objectid import ObjectId
-from pymongo import DeleteOne, InsertOne, UpdateOne
+from .operations_base import REMOVED_VALUE
+if not AYON_SERVER_ENABLED:
+ from .mongo.operations import *
+ OperationsSession = MongoOperationsSession
-from .mongo import get_project_connection
-from .entities import get_project
-
-REMOVED_VALUE = object()
-
-PROJECT_NAME_ALLOWED_SYMBOLS = "a-zA-Z0-9_"
-PROJECT_NAME_REGEX = re.compile(
- "^[{}]+$".format(PROJECT_NAME_ALLOWED_SYMBOLS)
-)
-
-CURRENT_PROJECT_SCHEMA = "openpype:project-3.0"
-CURRENT_PROJECT_CONFIG_SCHEMA = "openpype:config-2.0"
-CURRENT_ASSET_DOC_SCHEMA = "openpype:asset-3.0"
-CURRENT_SUBSET_SCHEMA = "openpype:subset-3.0"
-CURRENT_VERSION_SCHEMA = "openpype:version-3.0"
-CURRENT_HERO_VERSION_SCHEMA = "openpype:hero_version-1.0"
-CURRENT_REPRESENTATION_SCHEMA = "openpype:representation-2.0"
-CURRENT_WORKFILE_INFO_SCHEMA = "openpype:workfile-1.0"
-CURRENT_THUMBNAIL_SCHEMA = "openpype:thumbnail-1.0"
-
-
-def _create_or_convert_to_mongo_id(mongo_id):
- if mongo_id is None:
- return ObjectId()
- return ObjectId(mongo_id)
-
-
-def new_project_document(
- project_name, project_code, config, data=None, entity_id=None
-):
- """Create skeleton data of project document.
-
- Args:
- project_name (str): Name of project. Used as identifier of a project.
- project_code (str): Shorter version of projet without spaces and
- special characters (in most of cases). Should be also considered
- as unique name across projects.
- config (Dic[str, Any]): Project config consist of roots, templates,
- applications and other project Anatomy related data.
- data (Dict[str, Any]): Project data with information about it's
- attributes (e.g. 'fps' etc.) or integration specific keys.
- entity_id (Union[str, ObjectId]): Predefined id of document. New id is
- created if not passed.
-
- Returns:
- Dict[str, Any]: Skeleton of project document.
- """
-
- if data is None:
- data = {}
-
- data["code"] = project_code
-
- return {
- "_id": _create_or_convert_to_mongo_id(entity_id),
- "name": project_name,
- "type": CURRENT_PROJECT_SCHEMA,
- "entity_data": data,
- "config": config
- }
-
-
-def new_asset_document(
- name, project_id, parent_id, parents, data=None, entity_id=None
-):
- """Create skeleton data of asset document.
-
- Args:
- name (str): Is considered as unique identifier of asset in project.
- project_id (Union[str, ObjectId]): Id of project doument.
- parent_id (Union[str, ObjectId]): Id of parent asset.
- parents (List[str]): List of parent assets names.
- data (Dict[str, Any]): Asset document data. Empty dictionary is used
- if not passed. Value of 'parent_id' is used to fill 'visualParent'.
- entity_id (Union[str, ObjectId]): Predefined id of document. New id is
- created if not passed.
-
- Returns:
- Dict[str, Any]: Skeleton of asset document.
- """
-
- if data is None:
- data = {}
- if parent_id is not None:
- parent_id = ObjectId(parent_id)
- data["visualParent"] = parent_id
- data["parents"] = parents
-
- return {
- "_id": _create_or_convert_to_mongo_id(entity_id),
- "type": "asset",
- "name": name,
- "parent": ObjectId(project_id),
- "data": data,
- "schema": CURRENT_ASSET_DOC_SCHEMA
- }
-
-
-def new_subset_document(name, family, asset_id, data=None, entity_id=None):
- """Create skeleton data of subset document.
-
- Args:
- name (str): Is considered as unique identifier of subset under asset.
- family (str): Subset's family.
- asset_id (Union[str, ObjectId]): Id of parent asset.
- data (Dict[str, Any]): Subset document data. Empty dictionary is used
- if not passed. Value of 'family' is used to fill 'family'.
- entity_id (Union[str, ObjectId]): Predefined id of document. New id is
- created if not passed.
-
- Returns:
- Dict[str, Any]: Skeleton of subset document.
- """
-
- if data is None:
- data = {}
- data["family"] = family
- return {
- "_id": _create_or_convert_to_mongo_id(entity_id),
- "schema": CURRENT_SUBSET_SCHEMA,
- "type": "subset",
- "name": name,
- "data": data,
- "parent": asset_id
- }
-
-
-def new_version_doc(version, subset_id, data=None, entity_id=None):
- """Create skeleton data of version document.
-
- Args:
- version (int): Is considered as unique identifier of version
- under subset.
- subset_id (Union[str, ObjectId]): Id of parent subset.
- data (Dict[str, Any]): Version document data.
- entity_id (Union[str, ObjectId]): Predefined id of document. New id is
- created if not passed.
-
- Returns:
- Dict[str, Any]: Skeleton of version document.
- """
-
- if data is None:
- data = {}
-
- return {
- "_id": _create_or_convert_to_mongo_id(entity_id),
- "schema": CURRENT_VERSION_SCHEMA,
- "type": "version",
- "name": int(version),
- "parent": subset_id,
- "data": data
- }
-
-
-def new_hero_version_doc(version_id, subset_id, data=None, entity_id=None):
- """Create skeleton data of hero version document.
-
- Args:
- version_id (ObjectId): Is considered as unique identifier of version
- under subset.
- subset_id (Union[str, ObjectId]): Id of parent subset.
- data (Dict[str, Any]): Version document data.
- entity_id (Union[str, ObjectId]): Predefined id of document. New id is
- created if not passed.
-
- Returns:
- Dict[str, Any]: Skeleton of version document.
- """
-
- if data is None:
- data = {}
-
- return {
- "_id": _create_or_convert_to_mongo_id(entity_id),
- "schema": CURRENT_HERO_VERSION_SCHEMA,
- "type": "hero_version",
- "version_id": version_id,
- "parent": subset_id,
- "data": data
- }
-
-
-def new_representation_doc(
- name, version_id, context, data=None, entity_id=None
-):
- """Create skeleton data of asset document.
-
- Args:
- version (int): Is considered as unique identifier of version
- under subset.
- version_id (Union[str, ObjectId]): Id of parent version.
- context (Dict[str, Any]): Representation context used for fill template
- of to query.
- data (Dict[str, Any]): Representation document data.
- entity_id (Union[str, ObjectId]): Predefined id of document. New id is
- created if not passed.
-
- Returns:
- Dict[str, Any]: Skeleton of version document.
- """
-
- if data is None:
- data = {}
-
- return {
- "_id": _create_or_convert_to_mongo_id(entity_id),
- "schema": CURRENT_REPRESENTATION_SCHEMA,
- "type": "representation",
- "parent": version_id,
- "name": name,
- "data": data,
-
- # Imprint shortcut to context for performance reasons.
- "context": context
- }
-
-
-def new_thumbnail_doc(data=None, entity_id=None):
- """Create skeleton data of thumbnail document.
-
- Args:
- data (Dict[str, Any]): Thumbnail document data.
- entity_id (Union[str, ObjectId]): Predefined id of document. New id is
- created if not passed.
-
- Returns:
- Dict[str, Any]: Skeleton of thumbnail document.
- """
-
- if data is None:
- data = {}
-
- return {
- "_id": _create_or_convert_to_mongo_id(entity_id),
- "type": "thumbnail",
- "schema": CURRENT_THUMBNAIL_SCHEMA,
- "data": data
- }
-
-
-def new_workfile_info_doc(
- filename, asset_id, task_name, files, data=None, entity_id=None
-):
- """Create skeleton data of workfile info document.
-
- Workfile document is at this moment used primarily for artist notes.
-
- Args:
- filename (str): Filename of workfile.
- asset_id (Union[str, ObjectId]): Id of asset under which workfile live.
- task_name (str): Task under which was workfile created.
- files (List[str]): List of rootless filepaths related to workfile.
- data (Dict[str, Any]): Additional metadata.
-
- Returns:
- Dict[str, Any]: Skeleton of workfile info document.
- """
-
- if not data:
- data = {}
-
- return {
- "_id": _create_or_convert_to_mongo_id(entity_id),
- "type": "workfile",
- "parent": ObjectId(asset_id),
- "task_name": task_name,
- "filename": filename,
- "data": data,
- "files": files
- }
-
-
-def _prepare_update_data(old_doc, new_doc, replace):
- changes = {}
- for key, value in new_doc.items():
- if key not in old_doc or value != old_doc[key]:
- changes[key] = value
-
- if replace:
- for key in old_doc.keys():
- if key not in new_doc:
- changes[key] = REMOVED_VALUE
- return changes
-
-
-def prepare_subset_update_data(old_doc, new_doc, replace=True):
- """Compare two subset documents and prepare update data.
-
- Based on compared values will create update data for 'UpdateOperation'.
-
- Empty output means that documents are identical.
-
- Returns:
- Dict[str, Any]: Changes between old and new document.
- """
-
- return _prepare_update_data(old_doc, new_doc, replace)
-
-
-def prepare_version_update_data(old_doc, new_doc, replace=True):
- """Compare two version documents and prepare update data.
-
- Based on compared values will create update data for 'UpdateOperation'.
-
- Empty output means that documents are identical.
-
- Returns:
- Dict[str, Any]: Changes between old and new document.
- """
-
- return _prepare_update_data(old_doc, new_doc, replace)
-
-
-def prepare_hero_version_update_data(old_doc, new_doc, replace=True):
- """Compare two hero version documents and prepare update data.
-
- Based on compared values will create update data for 'UpdateOperation'.
-
- Empty output means that documents are identical.
-
- Returns:
- Dict[str, Any]: Changes between old and new document.
- """
-
- return _prepare_update_data(old_doc, new_doc, replace)
-
-
-def prepare_representation_update_data(old_doc, new_doc, replace=True):
- """Compare two representation documents and prepare update data.
-
- Based on compared values will create update data for 'UpdateOperation'.
-
- Empty output means that documents are identical.
-
- Returns:
- Dict[str, Any]: Changes between old and new document.
- """
-
- return _prepare_update_data(old_doc, new_doc, replace)
-
-
-def prepare_workfile_info_update_data(old_doc, new_doc, replace=True):
- """Compare two workfile info documents and prepare update data.
-
- Based on compared values will create update data for 'UpdateOperation'.
-
- Empty output means that documents are identical.
-
- Returns:
- Dict[str, Any]: Changes between old and new document.
- """
-
- return _prepare_update_data(old_doc, new_doc, replace)
-
-
-@six.add_metaclass(ABCMeta)
-class AbstractOperation(object):
- """Base operation class.
-
- Operation represent a call into database. The call can create, change or
- remove data.
-
- Args:
- project_name (str): On which project operation will happen.
- entity_type (str): Type of entity on which change happens.
- e.g. 'asset', 'representation' etc.
- """
-
- def __init__(self, project_name, entity_type):
- self._project_name = project_name
- self._entity_type = entity_type
- self._id = str(uuid.uuid4())
-
- @property
- def project_name(self):
- return self._project_name
-
- @property
- def id(self):
- """Identifier of operation."""
-
- return self._id
-
- @property
- def entity_type(self):
- return self._entity_type
-
- @abstractproperty
- def operation_name(self):
- """Stringified type of operation."""
-
- pass
-
- @abstractmethod
- def to_mongo_operation(self):
- """Convert operation to Mongo batch operation."""
-
- pass
-
- def to_data(self):
- """Convert operation to data that can be converted to json or others.
-
- Warning:
- Current state returns ObjectId objects which cannot be parsed by
- json.
-
- Returns:
- Dict[str, Any]: Description of operation.
- """
-
- return {
- "id": self._id,
- "entity_type": self.entity_type,
- "project_name": self.project_name,
- "operation": self.operation_name
- }
-
-
-class CreateOperation(AbstractOperation):
- """Operation to create an entity.
-
- Args:
- project_name (str): On which project operation will happen.
- entity_type (str): Type of entity on which change happens.
- e.g. 'asset', 'representation' etc.
- data (Dict[str, Any]): Data of entity that will be created.
- """
-
- operation_name = "create"
-
- def __init__(self, project_name, entity_type, data):
- super(CreateOperation, self).__init__(project_name, entity_type)
-
- if not data:
- data = {}
- else:
- data = copy.deepcopy(dict(data))
-
- if "_id" not in data:
- data["_id"] = ObjectId()
- else:
- data["_id"] = ObjectId(data["_id"])
-
- self._entity_id = data["_id"]
- self._data = data
-
- def __setitem__(self, key, value):
- self.set_value(key, value)
-
- def __getitem__(self, key):
- return self.data[key]
-
- def set_value(self, key, value):
- self.data[key] = value
-
- def get(self, key, *args, **kwargs):
- return self.data.get(key, *args, **kwargs)
-
- @property
- def entity_id(self):
- return self._entity_id
-
- @property
- def data(self):
- return self._data
-
- def to_mongo_operation(self):
- return InsertOne(copy.deepcopy(self._data))
-
- def to_data(self):
- output = super(CreateOperation, self).to_data()
- output["data"] = copy.deepcopy(self.data)
- return output
-
-
-class UpdateOperation(AbstractOperation):
- """Operation to update an entity.
-
- Args:
- project_name (str): On which project operation will happen.
- entity_type (str): Type of entity on which change happens.
- e.g. 'asset', 'representation' etc.
- entity_id (Union[str, ObjectId]): Identifier of an entity.
- update_data (Dict[str, Any]): Key -> value changes that will be set in
- database. If value is set to 'REMOVED_VALUE' the key will be
- removed. Only first level of dictionary is checked (on purpose).
- """
-
- operation_name = "update"
-
- def __init__(self, project_name, entity_type, entity_id, update_data):
- super(UpdateOperation, self).__init__(project_name, entity_type)
-
- self._entity_id = ObjectId(entity_id)
- self._update_data = update_data
-
- @property
- def entity_id(self):
- return self._entity_id
-
- @property
- def update_data(self):
- return self._update_data
-
- def to_mongo_operation(self):
- unset_data = {}
- set_data = {}
- for key, value in self._update_data.items():
- if value is REMOVED_VALUE:
- unset_data[key] = None
- else:
- set_data[key] = value
-
- op_data = {}
- if unset_data:
- op_data["$unset"] = unset_data
- if set_data:
- op_data["$set"] = set_data
-
- if not op_data:
- return None
-
- return UpdateOne(
- {"_id": self.entity_id},
- op_data
- )
-
- def to_data(self):
- changes = {}
- for key, value in self._update_data.items():
- if value is REMOVED_VALUE:
- value = None
- changes[key] = value
-
- output = super(UpdateOperation, self).to_data()
- output.update({
- "entity_id": self.entity_id,
- "changes": changes
- })
- return output
-
-
-class DeleteOperation(AbstractOperation):
- """Operation to delete an entity.
-
- Args:
- project_name (str): On which project operation will happen.
- entity_type (str): Type of entity on which change happens.
- e.g. 'asset', 'representation' etc.
- entity_id (Union[str, ObjectId]): Entity id that will be removed.
- """
-
- operation_name = "delete"
-
- def __init__(self, project_name, entity_type, entity_id):
- super(DeleteOperation, self).__init__(project_name, entity_type)
-
- self._entity_id = ObjectId(entity_id)
-
- @property
- def entity_id(self):
- return self._entity_id
-
- def to_mongo_operation(self):
- return DeleteOne({"_id": self.entity_id})
-
- def to_data(self):
- output = super(DeleteOperation, self).to_data()
- output["entity_id"] = self.entity_id
- return output
-
-
-class OperationsSession(object):
- """Session storing operations that should happen in an order.
-
- At this moment does not handle anything special can be sonsidered as
- stupid list of operations that will happen after each other. If creation
- of same entity is there multiple times it's handled in any way and document
- values are not validated.
-
- All operations must be related to single project.
-
- Args:
- project_name (str): Project name to which are operations related.
- """
-
- def __init__(self):
- self._operations = []
-
- def add(self, operation):
- """Add operation to be processed.
-
- Args:
- operation (BaseOperation): Operation that should be processed.
- """
- if not isinstance(
- operation,
- (CreateOperation, UpdateOperation, DeleteOperation)
- ):
- raise TypeError("Expected Operation object got {}".format(
- str(type(operation))
- ))
-
- self._operations.append(operation)
-
- def append(self, operation):
- """Add operation to be processed.
-
- Args:
- operation (BaseOperation): Operation that should be processed.
- """
-
- self.add(operation)
-
- def extend(self, operations):
- """Add operations to be processed.
-
- Args:
- operations (List[BaseOperation]): Operations that should be
- processed.
- """
-
- for operation in operations:
- self.add(operation)
-
- def remove(self, operation):
- """Remove operation."""
-
- self._operations.remove(operation)
-
- def clear(self):
- """Clear all registered operations."""
-
- self._operations = []
-
- def to_data(self):
- return [
- operation.to_data()
- for operation in self._operations
- ]
-
- def commit(self):
- """Commit session operations."""
-
- operations, self._operations = self._operations, []
- if not operations:
- return
-
- operations_by_project = collections.defaultdict(list)
- for operation in operations:
- operations_by_project[operation.project_name].append(operation)
-
- for project_name, operations in operations_by_project.items():
- bulk_writes = []
- for operation in operations:
- mongo_op = operation.to_mongo_operation()
- if mongo_op is not None:
- bulk_writes.append(mongo_op)
-
- if bulk_writes:
- collection = get_project_connection(project_name)
- collection.bulk_write(bulk_writes)
-
- def create_entity(self, project_name, entity_type, data):
- """Fast access to 'CreateOperation'.
-
- Returns:
- CreateOperation: Object of update operation.
- """
-
- operation = CreateOperation(project_name, entity_type, data)
- self.add(operation)
- return operation
-
- def update_entity(self, project_name, entity_type, entity_id, update_data):
- """Fast access to 'UpdateOperation'.
-
- Returns:
- UpdateOperation: Object of update operation.
- """
-
- operation = UpdateOperation(
- project_name, entity_type, entity_id, update_data
- )
- self.add(operation)
- return operation
-
- def delete_entity(self, project_name, entity_type, entity_id):
- """Fast access to 'DeleteOperation'.
-
- Returns:
- DeleteOperation: Object of delete operation.
- """
-
- operation = DeleteOperation(project_name, entity_type, entity_id)
- self.add(operation)
- return operation
-
-
-def create_project(project_name, project_code, library_project=False):
- """Create project using OpenPype settings.
-
- This project creation function is not validating project document on
- creation. It is because project document is created blindly with only
- minimum required information about project which is it's name, code, type
- and schema.
-
- Entered project name must be unique and project must not exist yet.
-
- Note:
- This function is here to be OP v4 ready but in v3 has more logic
- to do. That's why inner imports are in the body.
-
- Args:
- project_name(str): New project name. Should be unique.
- project_code(str): Project's code should be unique too.
- library_project(bool): Project is library project.
-
- Raises:
- ValueError: When project name already exists in MongoDB.
-
- Returns:
- dict: Created project document.
- """
-
- from openpype.settings import ProjectSettings, SaveWarningExc
- from openpype.pipeline.schema import validate
-
- if get_project(project_name, fields=["name"]):
- raise ValueError("Project with name \"{}\" already exists".format(
- project_name
- ))
-
- if not PROJECT_NAME_REGEX.match(project_name):
- raise ValueError((
- "Project name \"{}\" contain invalid characters"
- ).format(project_name))
-
- project_doc = {
- "type": "project",
- "name": project_name,
- "data": {
- "code": project_code,
- "library_project": library_project
- },
- "schema": CURRENT_PROJECT_SCHEMA
- }
-
- op_session = OperationsSession()
- # Insert document with basic data
- create_op = op_session.create_entity(
- project_name, project_doc["type"], project_doc
+else:
+ from ayon_api.server_api import (
+ PROJECT_NAME_ALLOWED_SYMBOLS,
+ PROJECT_NAME_REGEX,
+ )
+ from .server.operations import *
+ from .mongo.operations import (
+ CURRENT_PROJECT_SCHEMA,
+ CURRENT_PROJECT_CONFIG_SCHEMA,
+ CURRENT_ASSET_DOC_SCHEMA,
+ CURRENT_SUBSET_SCHEMA,
+ CURRENT_VERSION_SCHEMA,
+ CURRENT_HERO_VERSION_SCHEMA,
+ CURRENT_REPRESENTATION_SCHEMA,
+ CURRENT_WORKFILE_INFO_SCHEMA,
+ CURRENT_THUMBNAIL_SCHEMA
)
- op_session.commit()
-
- # Load ProjectSettings for the project and save it to store all attributes
- # and Anatomy
- try:
- project_settings_entity = ProjectSettings(project_name)
- project_settings_entity.save()
- except SaveWarningExc as exc:
- print(str(exc))
- except Exception:
- op_session.delete_entity(
- project_name, project_doc["type"], create_op.entity_id
- )
- op_session.commit()
- raise
-
- project_doc = get_project(project_name)
-
- try:
- # Validate created project document
- validate(project_doc)
- except Exception:
- # Remove project if is not valid
- op_session.delete_entity(
- project_name, project_doc["type"], create_op.entity_id
- )
- op_session.commit()
- raise
-
- return project_doc
diff --git a/openpype/client/operations_base.py b/openpype/client/operations_base.py
new file mode 100644
index 0000000000..887b237b1c
--- /dev/null
+++ b/openpype/client/operations_base.py
@@ -0,0 +1,289 @@
+import uuid
+import copy
+from abc import ABCMeta, abstractmethod, abstractproperty
+import six
+
+REMOVED_VALUE = object()
+
+
+@six.add_metaclass(ABCMeta)
+class AbstractOperation(object):
+ """Base operation class.
+
+ Operation represent a call into database. The call can create, change or
+ remove data.
+
+ Args:
+ project_name (str): On which project operation will happen.
+ entity_type (str): Type of entity on which change happens.
+ e.g. 'asset', 'representation' etc.
+ """
+
+ def __init__(self, project_name, entity_type):
+ self._project_name = project_name
+ self._entity_type = entity_type
+ self._id = str(uuid.uuid4())
+
+ @property
+ def project_name(self):
+ return self._project_name
+
+ @property
+ def id(self):
+ """Identifier of operation."""
+
+ return self._id
+
+ @property
+ def entity_type(self):
+ return self._entity_type
+
+ @abstractproperty
+ def operation_name(self):
+ """Stringified type of operation."""
+
+ pass
+
+ def to_data(self):
+ """Convert operation to data that can be converted to json or others.
+
+ Warning:
+ Current state returns ObjectId objects which cannot be parsed by
+ json.
+
+ Returns:
+ Dict[str, Any]: Description of operation.
+ """
+
+ return {
+ "id": self._id,
+ "entity_type": self.entity_type,
+ "project_name": self.project_name,
+ "operation": self.operation_name
+ }
+
+
+class CreateOperation(AbstractOperation):
+ """Operation to create an entity.
+
+ Args:
+ project_name (str): On which project operation will happen.
+ entity_type (str): Type of entity on which change happens.
+ e.g. 'asset', 'representation' etc.
+ data (Dict[str, Any]): Data of entity that will be created.
+ """
+
+ operation_name = "create"
+
+ def __init__(self, project_name, entity_type, data):
+ super(CreateOperation, self).__init__(project_name, entity_type)
+
+ if not data:
+ data = {}
+ else:
+ data = copy.deepcopy(dict(data))
+ self._data = data
+
+ def __setitem__(self, key, value):
+ self.set_value(key, value)
+
+ def __getitem__(self, key):
+ return self.data[key]
+
+ def set_value(self, key, value):
+ self.data[key] = value
+
+ def get(self, key, *args, **kwargs):
+ return self.data.get(key, *args, **kwargs)
+
+ @abstractproperty
+ def entity_id(self):
+ pass
+
+ @property
+ def data(self):
+ return self._data
+
+ def to_data(self):
+ output = super(CreateOperation, self).to_data()
+ output["data"] = copy.deepcopy(self.data)
+ return output
+
+
+class UpdateOperation(AbstractOperation):
+ """Operation to update an entity.
+
+ Args:
+ project_name (str): On which project operation will happen.
+ entity_type (str): Type of entity on which change happens.
+ e.g. 'asset', 'representation' etc.
+ entity_id (Union[str, ObjectId]): Identifier of an entity.
+ update_data (Dict[str, Any]): Key -> value changes that will be set in
+ database. If value is set to 'REMOVED_VALUE' the key will be
+ removed. Only first level of dictionary is checked (on purpose).
+ """
+
+ operation_name = "update"
+
+ def __init__(self, project_name, entity_type, entity_id, update_data):
+ super(UpdateOperation, self).__init__(project_name, entity_type)
+
+ self._entity_id = entity_id
+ self._update_data = update_data
+
+ @property
+ def entity_id(self):
+ return self._entity_id
+
+ @property
+ def update_data(self):
+ return self._update_data
+
+ def to_data(self):
+ changes = {}
+ for key, value in self._update_data.items():
+ if value is REMOVED_VALUE:
+ value = None
+ changes[key] = value
+
+ output = super(UpdateOperation, self).to_data()
+ output.update({
+ "entity_id": self.entity_id,
+ "changes": changes
+ })
+ return output
+
+
+class DeleteOperation(AbstractOperation):
+ """Operation to delete an entity.
+
+ Args:
+ project_name (str): On which project operation will happen.
+ entity_type (str): Type of entity on which change happens.
+ e.g. 'asset', 'representation' etc.
+ entity_id (Union[str, ObjectId]): Entity id that will be removed.
+ """
+
+ operation_name = "delete"
+
+ def __init__(self, project_name, entity_type, entity_id):
+ super(DeleteOperation, self).__init__(project_name, entity_type)
+
+ self._entity_id = entity_id
+
+ @property
+ def entity_id(self):
+ return self._entity_id
+
+ def to_data(self):
+ output = super(DeleteOperation, self).to_data()
+ output["entity_id"] = self.entity_id
+ return output
+
+
+class BaseOperationsSession(object):
+ """Session storing operations that should happen in an order.
+
+ At this moment does not handle anything special can be considered as
+ stupid list of operations that will happen after each other. If creation
+ of same entity is there multiple times it's handled in any way and document
+ values are not validated.
+ """
+
+ def __init__(self):
+ self._operations = []
+
+ def __len__(self):
+ return len(self._operations)
+
+ def add(self, operation):
+ """Add operation to be processed.
+
+ Args:
+ operation (BaseOperation): Operation that should be processed.
+ """
+ if not isinstance(
+ operation,
+ (CreateOperation, UpdateOperation, DeleteOperation)
+ ):
+ raise TypeError("Expected Operation object got {}".format(
+ str(type(operation))
+ ))
+
+ self._operations.append(operation)
+
+ def append(self, operation):
+ """Add operation to be processed.
+
+ Args:
+ operation (BaseOperation): Operation that should be processed.
+ """
+
+ self.add(operation)
+
+ def extend(self, operations):
+ """Add operations to be processed.
+
+ Args:
+ operations (List[BaseOperation]): Operations that should be
+ processed.
+ """
+
+ for operation in operations:
+ self.add(operation)
+
+ def remove(self, operation):
+ """Remove operation."""
+
+ self._operations.remove(operation)
+
+ def clear(self):
+ """Clear all registered operations."""
+
+ self._operations = []
+
+ def to_data(self):
+ return [
+ operation.to_data()
+ for operation in self._operations
+ ]
+
+ @abstractmethod
+ def commit(self):
+ """Commit session operations."""
+ pass
+
+ def create_entity(self, project_name, entity_type, data):
+ """Fast access to 'CreateOperation'.
+
+ Returns:
+ CreateOperation: Object of update operation.
+ """
+
+ operation = CreateOperation(project_name, entity_type, data)
+ self.add(operation)
+ return operation
+
+ def update_entity(self, project_name, entity_type, entity_id, update_data):
+ """Fast access to 'UpdateOperation'.
+
+ Returns:
+ UpdateOperation: Object of update operation.
+ """
+
+ operation = UpdateOperation(
+ project_name, entity_type, entity_id, update_data
+ )
+ self.add(operation)
+ return operation
+
+ def delete_entity(self, project_name, entity_type, entity_id):
+ """Fast access to 'DeleteOperation'.
+
+ Returns:
+ DeleteOperation: Object of delete operation.
+ """
+
+ operation = DeleteOperation(project_name, entity_type, entity_id)
+ self.add(operation)
+ return operation
diff --git a/openpype/modules/ftrack/python2_vendor/arrow/tests/__init__.py b/openpype/client/server/__init__.py
similarity index 100%
rename from openpype/modules/ftrack/python2_vendor/arrow/tests/__init__.py
rename to openpype/client/server/__init__.py
diff --git a/openpype/client/server/constants.py b/openpype/client/server/constants.py
new file mode 100644
index 0000000000..1d3f94c702
--- /dev/null
+++ b/openpype/client/server/constants.py
@@ -0,0 +1,18 @@
+# --- Folders ---
+DEFAULT_FOLDER_FIELDS = {
+ "id",
+ "name",
+ "path",
+ "parentId",
+ "active",
+ "parents",
+ "thumbnailId"
+}
+
+REPRESENTATION_FILES_FIELDS = {
+ "files.name",
+ "files.hash",
+ "files.id",
+ "files.path",
+ "files.size",
+}
diff --git a/openpype/client/server/conversion_utils.py b/openpype/client/server/conversion_utils.py
new file mode 100644
index 0000000000..dc95bbeda5
--- /dev/null
+++ b/openpype/client/server/conversion_utils.py
@@ -0,0 +1,1328 @@
+import os
+import arrow
+import collections
+import json
+
+import six
+
+from openpype.client.operations_base import REMOVED_VALUE
+from openpype.client.mongo.operations import (
+ CURRENT_PROJECT_SCHEMA,
+ CURRENT_ASSET_DOC_SCHEMA,
+ CURRENT_SUBSET_SCHEMA,
+ CURRENT_VERSION_SCHEMA,
+ CURRENT_HERO_VERSION_SCHEMA,
+ CURRENT_REPRESENTATION_SCHEMA,
+ CURRENT_WORKFILE_INFO_SCHEMA,
+)
+from .constants import REPRESENTATION_FILES_FIELDS
+from .utils import create_entity_id, prepare_entity_changes
+
+# --- Project entity ---
+PROJECT_FIELDS_MAPPING_V3_V4 = {
+ "_id": {"name"},
+ "name": {"name"},
+ "data": {"data", "code"},
+ "data.library_project": {"library"},
+ "data.code": {"code"},
+ "data.active": {"active"},
+}
+
+# TODO this should not be hardcoded but received from server!!!
+# --- Folder entity ---
+FOLDER_FIELDS_MAPPING_V3_V4 = {
+ "_id": {"id"},
+ "name": {"name"},
+ "label": {"label"},
+ "data": {
+ "parentId", "parents", "active", "tasks", "thumbnailId"
+ },
+ "data.visualParent": {"parentId"},
+ "data.parents": {"parents"},
+ "data.active": {"active"},
+ "data.thumbnail_id": {"thumbnailId"},
+ "data.entityType": {"folderType"}
+}
+
+# --- Subset entity ---
+SUBSET_FIELDS_MAPPING_V3_V4 = {
+ "_id": {"id"},
+ "name": {"name"},
+ "data.active": {"active"},
+ "parent": {"folderId"}
+}
+
+# --- Version entity ---
+VERSION_FIELDS_MAPPING_V3_V4 = {
+ "_id": {"id"},
+ "name": {"version"},
+ "parent": {"productId"}
+}
+
+# --- Representation entity ---
+REPRESENTATION_FIELDS_MAPPING_V3_V4 = {
+ "_id": {"id"},
+ "name": {"name"},
+ "parent": {"versionId"},
+ "context": {"context"},
+ "files": {"files"},
+}
+
+
+def project_fields_v3_to_v4(fields, con):
+ """Convert project fields from v3 to v4 structure.
+
+ Args:
+ fields (Union[Iterable(str), None]): fields to be converted.
+
+ Returns:
+ Union[Set(str), None]: Converted fields to v4 fields.
+ """
+
+ # TODO config fields
+ # - config.apps
+ # - config.groups
+ if not fields:
+ return None
+
+ project_attribs = con.get_attributes_for_type("project")
+ output = set()
+ for field in fields:
+ # If config is needed the rest api call must be used
+ if field.startswith("config"):
+ return None
+
+ if field in PROJECT_FIELDS_MAPPING_V3_V4:
+ output |= PROJECT_FIELDS_MAPPING_V3_V4[field]
+ if field == "data":
+ output |= {
+ "attrib.{}".format(attr)
+ for attr in project_attribs
+ }
+
+ elif field.startswith("data"):
+ field_parts = field.split(".")
+ field_parts.pop(0)
+ data_key = ".".join(field_parts)
+ if data_key in project_attribs:
+ output.add("attrib.{}".format(data_key))
+ else:
+ output.add("data")
+ print("Requested specific key from data {}".format(data_key))
+
+ else:
+ raise ValueError("Unknown field mapping for {}".format(field))
+
+ if "name" not in output:
+ output.add("name")
+ return output
+
+
+def _get_default_template_name(templates):
+ default_template = None
+ for name, template in templates.items():
+ if name == "default":
+ return "default"
+
+ if default_template is None:
+ default_template = name
+
+ return default_template
+
+
+def _template_replacements_to_v3(template):
+ return (
+ template
+ .replace("{folder[name]}", "{asset}")
+ .replace("{product[name]}", "{subset}")
+ .replace("{product[type]}", "{family}")
+ )
+
+
+def _convert_template_item(template):
+ # Others won't have 'directory'
+ if "directory" not in template:
+ return
+ folder = _template_replacements_to_v3(template.pop("directory"))
+ template["folder"] = folder
+ template["file"] = _template_replacements_to_v3(template["file"])
+ template["path"] = "/".join(
+ (folder, template["file"])
+ )
+
+
+def _fill_template_category(templates, cat_templates, cat_key):
+ default_template_name = _get_default_template_name(cat_templates)
+ for template_name, cat_template in cat_templates.items():
+ _convert_template_item(cat_template)
+ if template_name == default_template_name:
+ templates[cat_key] = cat_template
+ else:
+ new_name = "{}_{}".format(cat_key, template_name)
+ templates["others"][new_name] = cat_template
+
+
+def convert_v4_project_to_v3(project):
+ """Convert Project entity data from v4 structure to v3 structure.
+
+ Args:
+ project (Dict[str, Any]): Project entity queried from v4 server.
+
+ Returns:
+ Dict[str, Any]: Project converted to v3 structure.
+ """
+
+ if not project:
+ return project
+
+ project_name = project["name"]
+ output = {
+ "_id": project_name,
+ "name": project_name,
+ "schema": CURRENT_PROJECT_SCHEMA,
+ "type": "project"
+ }
+
+ data = project.get("data") or {}
+ attribs = project.get("attrib") or {}
+ apps_attr = attribs.pop("applications", None) or []
+ applications = [
+ {"name": app_name}
+ for app_name in apps_attr
+ ]
+ data.update(attribs)
+ if "tools" in data:
+ data["tools_env"] = data.pop("tools")
+
+ data["entityType"] = "Project"
+
+ config = {}
+ project_config = project.get("config")
+
+ if project_config:
+ config["apps"] = applications
+ config["roots"] = project_config["roots"]
+
+ templates = project_config["templates"]
+ templates["defaults"] = templates.pop("common", None) or {}
+
+ others_templates = templates.pop("others", None) or {}
+ new_others_templates = {}
+ templates["others"] = new_others_templates
+ for name, template in others_templates.items():
+ _convert_template_item(template)
+ new_others_templates[name] = template
+
+ for key in (
+ "work",
+ "publish",
+ "hero"
+ ):
+ cat_templates = templates.pop(key)
+ _fill_template_category(templates, cat_templates, key)
+
+ delivery_templates = templates.pop("delivery", None) or {}
+ new_delivery_templates = {}
+ for name, delivery_template in delivery_templates.items():
+ new_delivery_templates[name] = "/".join(
+ (delivery_template["directory"], delivery_template["file"])
+ )
+ templates["delivery"] = new_delivery_templates
+
+ config["templates"] = templates
+
+ if "taskTypes" in project:
+ task_types = project["taskTypes"]
+ new_task_types = {}
+ for task_type in task_types:
+ name = task_type.pop("name")
+ new_task_types[name] = task_type
+
+ config["tasks"] = new_task_types
+
+ if config:
+ output["config"] = config
+
+ for data_key, key in (
+ ("library_project", "library"),
+ ("code", "code"),
+ ("active", "active")
+ ):
+ if key in project:
+ data[data_key] = project[key]
+
+ if "attrib" in project:
+ for key, value in project["attrib"].items():
+ data[key] = value
+
+ if data:
+ output["data"] = data
+ return output
+
+
+def folder_fields_v3_to_v4(fields, con):
+ """Convert folder fields from v3 to v4 structure.
+
+ Args:
+ fields (Union[Iterable(str), None]): fields to be converted.
+
+ Returns:
+ Union[Set(str), None]: Converted fields to v4 fields.
+ """
+
+ if not fields:
+ return None
+
+ folder_attributes = con.get_attributes_for_type("folder")
+ output = set()
+ for field in fields:
+ if field in ("schema", "type", "parent"):
+ continue
+
+ if field in FOLDER_FIELDS_MAPPING_V3_V4:
+ output |= FOLDER_FIELDS_MAPPING_V3_V4[field]
+ if field == "data":
+ output |= {
+ "attrib.{}".format(attr)
+ for attr in folder_attributes
+ }
+
+ elif field.startswith("data"):
+ field_parts = field.split(".")
+ field_parts.pop(0)
+ data_key = ".".join(field_parts)
+ if data_key == "label":
+ output.add("name")
+
+ elif data_key in ("icon", "color"):
+ continue
+
+ elif data_key.startswith("tasks"):
+ output.add("tasks")
+
+ elif data_key in folder_attributes:
+ output.add("attrib.{}".format(data_key))
+
+ else:
+ output.add("data")
+ print("Requested specific key from data {}".format(data_key))
+
+ else:
+ raise ValueError("Unknown field mapping for {}".format(field))
+
+ if "id" not in output:
+ output.add("id")
+ return output
+
+
+def convert_v4_tasks_to_v3(tasks):
+ """Convert v4 task item to v3 task.
+
+ Args:
+ tasks (List[Dict[str, Any]]): Task entites.
+
+ Returns:
+ Dict[str, Dict[str, Any]]: Tasks in v3 variant ready for v3 asset.
+ """
+
+ output = {}
+ for task in tasks:
+ task_name = task["name"]
+ new_task = {
+ "type": task["taskType"]
+ }
+ output[task_name] = new_task
+ return output
+
+
+def convert_v4_folder_to_v3(folder, project_name):
+ """Convert v4 folder to v3 asset.
+
+ Args:
+ folder (Dict[str, Any]): Folder entity data.
+ project_name (str): Project name from which folder was queried.
+
+ Returns:
+ Dict[str, Any]: Converted v4 folder to v3 asset.
+ """
+
+ output = {
+ "_id": folder["id"],
+ "parent": project_name,
+ "type": "asset",
+ "schema": CURRENT_ASSET_DOC_SCHEMA
+ }
+
+ output_data = folder.get("data") or {}
+
+ if "name" in folder:
+ output["name"] = folder["name"]
+ output_data["label"] = folder["name"]
+
+ if "folderType" in folder:
+ output_data["entityType"] = folder["folderType"]
+
+ for src_key, dst_key in (
+ ("parentId", "visualParent"),
+ ("active", "active"),
+ ("thumbnailId", "thumbnail_id"),
+ ("parents", "parents"),
+ ):
+ if src_key in folder:
+ output_data[dst_key] = folder[src_key]
+
+ if "attrib" in folder:
+ output_data.update(folder["attrib"])
+
+ if "tools" in output_data:
+ output_data["tools_env"] = output_data.pop("tools")
+
+ if "tasks" in folder:
+ output_data["tasks"] = convert_v4_tasks_to_v3(folder["tasks"])
+
+ output["data"] = output_data
+
+ return output
+
+
+def subset_fields_v3_to_v4(fields, con):
+ """Convert subset fields from v3 to v4 structure.
+
+ Args:
+ fields (Union[Iterable(str), None]): fields to be converted.
+
+ Returns:
+ Union[Set(str), None]: Converted fields to v4 fields.
+ """
+
+ if not fields:
+ return None
+
+ product_attributes = con.get_attributes_for_type("product")
+
+ output = set()
+ for field in fields:
+ if field in ("schema", "type"):
+ continue
+
+ if field in SUBSET_FIELDS_MAPPING_V3_V4:
+ output |= SUBSET_FIELDS_MAPPING_V3_V4[field]
+
+ elif field == "data":
+ output.add("productType")
+ output.add("active")
+ output |= {
+ "attrib.{}".format(attr)
+ for attr in product_attributes
+ }
+
+ elif field.startswith("data"):
+ field_parts = field.split(".")
+ field_parts.pop(0)
+ data_key = ".".join(field_parts)
+ if data_key in ("family", "families"):
+ output.add("productType")
+
+ elif data_key in product_attributes:
+ output.add("attrib.{}".format(data_key))
+
+ else:
+ output.add("data")
+ print("Requested specific key from data {}".format(data_key))
+
+ else:
+ raise ValueError("Unknown field mapping for {}".format(field))
+
+ if "id" not in output:
+ output.add("id")
+ return output
+
+
+def convert_v4_subset_to_v3(subset):
+ output = {
+ "_id": subset["id"],
+ "type": "subset",
+ "schema": CURRENT_SUBSET_SCHEMA
+ }
+ if "folderId" in subset:
+ output["parent"] = subset["folderId"]
+
+ output_data = subset.get("data") or {}
+
+ if "name" in subset:
+ output["name"] = subset["name"]
+
+ if "active" in subset:
+ output_data["active"] = subset["active"]
+
+ if "attrib" in subset:
+ attrib = subset["attrib"]
+ if "productGroup" in attrib:
+ attrib["subsetGroup"] = attrib.pop("productGroup")
+ output_data.update(attrib)
+
+ family = subset.get("productType")
+ if family:
+ output_data["family"] = family
+ output_data["families"] = [family]
+
+ output["data"] = output_data
+
+ return output
+
+
+def version_fields_v3_to_v4(fields, con):
+ """Convert version fields from v3 to v4 structure.
+
+ Args:
+ fields (Union[Iterable(str), None]): fields to be converted.
+
+ Returns:
+ Union[Set(str), None]: Converted fields to v4 fields.
+ """
+
+ if not fields:
+ return None
+
+ version_attributes = con.get_attributes_for_type("version")
+
+ output = set()
+ for field in fields:
+ if field in ("type", "schema", "version_id"):
+ continue
+
+ if field in VERSION_FIELDS_MAPPING_V3_V4:
+ output |= VERSION_FIELDS_MAPPING_V3_V4[field]
+
+ elif field == "data":
+ output |= {
+ "attrib.{}".format(attr)
+ for attr in version_attributes
+ }
+ output |= {
+ "author",
+ "createdAt",
+ "thumbnailId",
+ }
+
+ elif field.startswith("data"):
+ field_parts = field.split(".")
+ field_parts.pop(0)
+ data_key = ".".join(field_parts)
+ if data_key in version_attributes:
+ output.add("attrib.{}".format(data_key))
+
+ elif data_key == "thumbnail_id":
+ output.add("thumbnailId")
+
+ elif data_key == "time":
+ output.add("createdAt")
+
+ elif data_key == "author":
+ output.add("author")
+
+ elif data_key in ("tags", ):
+ continue
+
+ else:
+ output.add("data")
+ print("Requested specific key from data {}".format(data_key))
+
+ else:
+ raise ValueError("Unknown field mapping for {}".format(field))
+
+ if "id" not in output:
+ output.add("id")
+ return output
+
+
+def convert_v4_version_to_v3(version):
+ """Convert v4 version entity to v4 version.
+
+ Args:
+ version (Dict[str, Any]): Queried v4 version entity.
+
+ Returns:
+ Dict[str, Any]: Conveted version entity to v3 structure.
+ """
+
+ version_num = version["version"]
+ if version_num < 0:
+ output = {
+ "_id": version["id"],
+ "type": "hero_version",
+ "schema": CURRENT_HERO_VERSION_SCHEMA,
+ }
+ if "productId" in version:
+ output["parent"] = version["productId"]
+
+ if "data" in version:
+ output["data"] = version["data"]
+ return output
+
+ output = {
+ "_id": version["id"],
+ "type": "version",
+ "name": version_num,
+ "schema": CURRENT_VERSION_SCHEMA
+ }
+ if "productId" in version:
+ output["parent"] = version["productId"]
+
+ output_data = version.get("data") or {}
+ if "attrib" in version:
+ output_data.update(version["attrib"])
+
+ for src_key, dst_key in (
+ ("active", "active"),
+ ("thumbnailId", "thumbnail_id"),
+ ("author", "author")
+ ):
+ if src_key in version:
+ output_data[dst_key] = version[src_key]
+
+ if "createdAt" in version:
+ created_at = arrow.get(version["createdAt"])
+ output_data["time"] = created_at.strftime("%Y%m%dT%H%M%SZ")
+
+ output["data"] = output_data
+
+ return output
+
+
+def representation_fields_v3_to_v4(fields, con):
+ """Convert representation fields from v3 to v4 structure.
+
+ Args:
+ fields (Union[Iterable(str), None]): fields to be converted.
+
+ Returns:
+ Union[Set(str), None]: Converted fields to v4 fields.
+ """
+
+ if not fields:
+ return None
+
+ representation_attributes = con.get_attributes_for_type("representation")
+
+ output = set()
+ for field in fields:
+ if field in ("type", "schema"):
+ continue
+
+ if field in REPRESENTATION_FIELDS_MAPPING_V3_V4:
+ output |= REPRESENTATION_FIELDS_MAPPING_V3_V4[field]
+
+ elif field.startswith("context"):
+ output.add("context")
+
+ # TODO: 'files' can have specific attributes but the keys in v3 and v4
+ # are not the same (content is not the same)
+ elif field.startswith("files"):
+ output |= REPRESENTATION_FILES_FIELDS
+
+ elif field.startswith("data"):
+ output |= {
+ "attrib.{}".format(attr)
+ for attr in representation_attributes
+ }
+
+ else:
+ raise ValueError("Unknown field mapping for {}".format(field))
+
+ if "id" not in output:
+ output.add("id")
+ return output
+
+
+def convert_v4_representation_to_v3(representation):
+ """Convert v4 representation to v3 representation.
+
+ Args:
+ representation (Dict[str, Any]): Queried representation from v4 server.
+
+ Returns:
+ Dict[str, Any]: Converted representation to v3 structure.
+ """
+
+ output = {
+ "type": "representation",
+ "schema": CURRENT_REPRESENTATION_SCHEMA,
+ }
+ if "id" in representation:
+ output["_id"] = representation["id"]
+
+ for v3_key, v4_key in (
+ ("name", "name"),
+ ("parent", "versionId")
+ ):
+ if v4_key in representation:
+ output[v3_key] = representation[v4_key]
+
+ if "context" in representation:
+ context = representation["context"]
+ if isinstance(context, six.string_types):
+ context = json.loads(context)
+
+ if "folder" in context:
+ _c_folder = context.pop("folder")
+ context["asset"] = _c_folder["name"]
+
+ if "product" in context:
+ _c_product = context.pop("product")
+ context["family"] = _c_product["type"]
+ context["subset"] = _c_product["name"]
+
+ output["context"] = context
+
+ if "files" in representation:
+ files = representation["files"]
+ new_files = []
+ # From GraphQl is list
+ if isinstance(files, list):
+ for file_info in files:
+ file_info["_id"] = file_info["id"]
+ new_files.append(file_info)
+
+ # From RestPoint is dictionary
+ elif isinstance(files, dict):
+ for file_id, file_info in files:
+ file_info["_id"] = file_id
+ new_files.append(file_info)
+
+ for file_info in new_files:
+ if not file_info.get("sites"):
+ file_info["sites"] = [{
+ "name": "studio"
+ }]
+
+ output["files"] = new_files
+
+ if representation.get("active") is False:
+ output["type"] = "archived_representation"
+ output["old_id"] = output["_id"]
+
+ output_data = representation.get("data") or {}
+ if "attrib" in representation:
+ output_data.update(representation["attrib"])
+
+ for key, data_key in (
+ ("active", "active"),
+ ):
+ if key in representation:
+ output_data[data_key] = representation[key]
+
+ if "template" in output_data:
+ output_data["template"] = (
+ output_data["template"]
+ .replace("{folder[name]}", "{asset}")
+ .replace("{product[name]}", "{subset}")
+ .replace("{product[type]}", "{family}")
+ )
+
+ output["data"] = output_data
+
+ return output
+
+
+def workfile_info_fields_v3_to_v4(fields):
+ if not fields:
+ return None
+
+ new_fields = set()
+ fields = set(fields)
+ for v3_key, v4_key in (
+ ("_id", "id"),
+ ("files", "path"),
+ ("filename", "name"),
+ ("data", "data"),
+ ):
+ if v3_key in fields:
+ new_fields.add(v4_key)
+
+ if "parent" in fields or "task_name" in fields:
+ new_fields.add("taskId")
+
+ return new_fields
+
+
+def convert_v4_workfile_info_to_v3(workfile_info, task):
+ output = {
+ "type": "workfile",
+ "schema": CURRENT_WORKFILE_INFO_SCHEMA,
+ }
+ if "id" in workfile_info:
+ output["_id"] = workfile_info["id"]
+
+ if "path" in workfile_info:
+ output["files"] = [workfile_info["path"]]
+
+ if "name" in workfile_info:
+ output["filename"] = workfile_info["name"]
+
+ if "taskId" in workfile_info:
+ output["task_name"] = task["name"]
+ output["parent"] = task["folderId"]
+
+ return output
+
+
+def convert_create_asset_to_v4(asset, project, con):
+ folder_attributes = con.get_attributes_for_type("folder")
+
+ asset_data = asset["data"]
+ parent_id = asset_data["visualParent"]
+
+ folder = {
+ "name": asset["name"],
+ "parentId": parent_id,
+ }
+ entity_id = asset.get("_id")
+ if entity_id:
+ folder["id"] = entity_id
+
+ attribs = {}
+ data = {}
+ for key, value in asset_data.items():
+ if key in (
+ "visualParent",
+ "thumbnail_id",
+ "parents",
+ "inputLinks",
+ "avalon_mongo_id",
+ ):
+ continue
+
+ if key not in folder_attributes:
+ data[key] = value
+ elif value is not None:
+ attribs[key] = value
+
+ if attribs:
+ folder["attrib"] = attribs
+
+ if data:
+ folder["data"] = data
+ return folder
+
+
+def convert_create_task_to_v4(task, project, con):
+ if not project["taskTypes"]:
+ raise ValueError(
+ "Project \"{}\" does not have any task types".format(
+ project["name"]))
+
+ task_type = task["type"]
+ if task_type not in project["taskTypes"]:
+ task_type = tuple(project["taskTypes"].keys())[0]
+
+ return {
+ "name": task["name"],
+ "taskType": task_type,
+ "folderId": task["folderId"]
+ }
+
+
+def convert_create_subset_to_v4(subset, con):
+ product_attributes = con.get_attributes_for_type("product")
+
+ subset_data = subset["data"]
+ product_type = subset_data.get("family")
+ if not product_type:
+ product_type = subset_data["families"][0]
+
+ converted_product = {
+ "name": subset["name"],
+ "productType": product_type,
+ "folderId": subset["parent"],
+ }
+ entity_id = subset.get("_id")
+ if entity_id:
+ converted_product["id"] = entity_id
+
+ attribs = {}
+ data = {}
+ if "subsetGroup" in subset_data:
+ subset_data["productGroup"] = subset_data.pop("subsetGroup")
+ for key, value in subset_data.items():
+ if key not in product_attributes:
+ data[key] = value
+ elif value is not None:
+ attribs[key] = value
+
+ if attribs:
+ converted_product["attrib"] = attribs
+
+ if data:
+ converted_product["data"] = data
+
+ return converted_product
+
+
+def convert_create_version_to_v4(version, con):
+ version_attributes = con.get_attributes_for_type("version")
+ converted_version = {
+ "version": version["name"],
+ "productId": version["parent"],
+ }
+ entity_id = version.get("_id")
+ if entity_id:
+ converted_version["id"] = entity_id
+
+ version_data = version["data"]
+ attribs = {}
+ data = {}
+ for key, value in version_data.items():
+ if key not in version_attributes:
+ data[key] = value
+ elif value is not None:
+ attribs[key] = value
+
+ if attribs:
+ converted_version["attrib"] = attribs
+
+ if data:
+ converted_version["data"] = attribs
+
+ return converted_version
+
+
+def convert_create_hero_version_to_v4(hero_version, project_name, con):
+ if "version_id" in hero_version:
+ version_id = hero_version["version_id"]
+ version = con.get_version_by_id(project_name, version_id)
+ version["version"] = - version["version"]
+
+ for auto_key in (
+ "name",
+ "createdAt",
+ "updatedAt",
+ "author",
+ ):
+ version.pop(auto_key, None)
+
+ return version
+
+ version_attributes = con.get_attributes_for_type("version")
+ converted_version = {
+ "version": hero_version["version"],
+ "productId": hero_version["parent"],
+ }
+ entity_id = hero_version.get("_id")
+ if entity_id:
+ converted_version["id"] = entity_id
+
+ version_data = hero_version["data"]
+ attribs = {}
+ data = {}
+ for key, value in version_data.items():
+ if key not in version_attributes:
+ data[key] = value
+ elif value is not None:
+ attribs[key] = value
+
+ if attribs:
+ converted_version["attrib"] = attribs
+
+ if data:
+ converted_version["data"] = attribs
+
+ return converted_version
+
+
+def convert_create_representation_to_v4(representation, con):
+ representation_attributes = con.get_attributes_for_type("representation")
+
+ converted_representation = {
+ "name": representation["name"],
+ "versionId": representation["parent"],
+ }
+ entity_id = representation.get("_id")
+ if entity_id:
+ converted_representation["id"] = entity_id
+
+ if representation.get("type") == "archived_representation":
+ converted_representation["active"] = False
+
+ new_files = []
+ for file_item in representation["files"]:
+ new_file_item = {
+ key: value
+ for key, value in file_item.items()
+ if key in ("hash", "path", "size")
+ }
+ new_file_item.update({
+ "id": create_entity_id(),
+ "hash_type": "op3",
+ "name": os.path.basename(new_file_item["path"])
+ })
+ new_files.append(new_file_item)
+
+ converted_representation["files"] = new_files
+
+ context = representation["context"]
+ context["folder"] = {
+ "name": context.pop("asset", None)
+ }
+ context["product"] = {
+ "type": context.pop("family", None),
+ "name": context.pop("subset", None),
+ }
+
+ attribs = {}
+ data = {
+ "context": context,
+ }
+
+ representation_data = representation["data"]
+ representation_data["template"] = (
+ representation_data["template"]
+ .replace("{asset}", "{folder[name]}")
+ .replace("{subset}", "{product[name]}")
+ .replace("{family}", "{product[type]}")
+ )
+
+ for key, value in representation_data.items():
+ if key not in representation_attributes:
+ data[key] = value
+ elif value is not None:
+ attribs[key] = value
+
+ if attribs:
+ converted_representation["attrib"] = attribs
+
+ if data:
+ converted_representation["data"] = data
+
+ return converted_representation
+
+
+def convert_create_workfile_info_to_v4(data, project_name, con):
+ folder_id = data["parent"]
+ task_name = data["task_name"]
+ task = con.get_task_by_name(project_name, folder_id, task_name)
+ if not task:
+ return None
+
+ workfile_attributes = con.get_attributes_for_type("workfile")
+ filename = data["filename"]
+ possible_attribs = {
+ "extension": os.path.splitext(filename)[-1]
+ }
+ attribs = {}
+ for attr in workfile_attributes:
+ if attr in possible_attribs:
+ attribs[attr] = possible_attribs[attr]
+
+ output = {
+ "path": data["files"][0],
+ "name": filename,
+ "taskId": task["id"]
+ }
+ if "_id" in data:
+ output["id"] = data["_id"]
+
+ if attribs:
+ output["attrib"] = attribs
+
+ output_data = data.get("data")
+ if output_data:
+ output["data"] = output_data
+ return output
+
+
+def _from_flat_dict(data):
+ output = {}
+ for key, value in data.items():
+ output_value = output
+ subkeys = key.split(".")
+ last_key = subkeys.pop(-1)
+ for subkey in subkeys:
+ if subkey not in output_value:
+ output_value[subkey] = {}
+ output_value = output_value[subkey]
+
+ output_value[last_key] = value
+ return output
+
+
+def _to_flat_dict(data):
+ output = {}
+ flat_queue = collections.deque()
+ flat_queue.append(([], data))
+ while flat_queue:
+ item = flat_queue.popleft()
+ parent_keys, data = item
+ for key, value in data.items():
+ keys = list(parent_keys)
+ keys.append(key)
+ if isinstance(value, dict):
+ flat_queue.append((keys, value))
+ else:
+ full_key = ".".join(keys)
+ output[full_key] = value
+
+ return output
+
+
+def convert_update_folder_to_v4(project_name, asset_id, update_data, con):
+ new_update_data = {}
+
+ folder_attributes = con.get_attributes_for_type("folder")
+ full_update_data = _from_flat_dict(update_data)
+ data = full_update_data.get("data")
+
+ has_new_parent = False
+ has_task_changes = False
+ parent_id = None
+ tasks = None
+ new_data = {}
+ attribs = {}
+ if "type" in update_data:
+ new_update_data["active"] = update_data["type"] == "asset"
+
+ if data:
+ if "thumbnail_id" in data:
+ new_update_data["thumbnailId"] = data.pop("thumbnail_id")
+
+ if "tasks" in data:
+ tasks = data.pop("tasks")
+ has_task_changes = True
+
+ if "visualParent" in data:
+ has_new_parent = True
+ parent_id = data.pop("visualParent")
+
+ for key, value in data.items():
+ if key in folder_attributes:
+ attribs[key] = value
+ else:
+ new_data[key] = value
+
+ if "name" in update_data:
+ new_update_data["name"] = update_data["name"]
+
+ if "type" in update_data:
+ new_type = update_data["type"]
+ if new_type == "asset":
+ new_update_data["active"] = True
+ elif new_type == "archived_asset":
+ new_update_data["active"] = False
+
+ if has_new_parent:
+ new_update_data["parentId"] = parent_id
+
+ if new_data:
+ print("Folder has new data: {}".format(new_data))
+ new_update_data["data"] = new_data
+
+ if has_task_changes:
+ raise ValueError("Task changes of folder are not implemented")
+
+ return _to_flat_dict(new_update_data)
+
+
+def convert_update_subset_to_v4(project_name, subset_id, update_data, con):
+ new_update_data = {}
+
+ product_attributes = con.get_attributes_for_type("product")
+ full_update_data = _from_flat_dict(update_data)
+ data = full_update_data.get("data")
+ new_data = {}
+ attribs = {}
+ if data:
+ if "family" in data:
+ family = data.pop("family")
+ new_update_data["productType"] = family
+
+ if "families" in data:
+ families = data.pop("families")
+ if "productType" not in new_update_data:
+ new_update_data["productType"] = families[0]
+
+ if "subsetGroup" in data:
+ data["productGroup"] = data.pop("subsetGroup")
+ for key, value in data.items():
+ if key in product_attributes:
+ if value is REMOVED_VALUE:
+ value = None
+ attribs[key] = value
+
+ elif value is not REMOVED_VALUE:
+ new_data[key] = value
+
+ if attribs:
+ new_update_data["attribs"] = attribs
+
+ if "name" in update_data:
+ new_update_data["name"] = update_data["name"]
+
+ if "type" in update_data:
+ new_type = update_data["type"]
+ if new_type == "subset":
+ new_update_data["active"] = True
+ elif new_type == "archived_subset":
+ new_update_data["active"] = False
+
+ if "parent" in update_data:
+ new_update_data["folderId"] = update_data["parent"]
+
+ flat_data = _to_flat_dict(new_update_data)
+ if new_data:
+ print("Subset has new data: {}".format(new_data))
+ flat_data["data"] = new_data
+
+ return flat_data
+
+
+def convert_update_version_to_v4(project_name, version_id, update_data, con):
+ new_update_data = {}
+
+ version_attributes = con.get_attributes_for_type("version")
+ full_update_data = _from_flat_dict(update_data)
+ data = full_update_data.get("data")
+ new_data = {}
+ attribs = {}
+ if data:
+ if "author" in data:
+ new_update_data["author"] = data.pop("author")
+
+ if "thumbnail_id" in data:
+ new_update_data["thumbnailId"] = data.pop("thumbnail_id")
+
+ for key, value in data.items():
+ if key in version_attributes:
+ if value is REMOVED_VALUE:
+ value = None
+ attribs[key] = value
+
+ elif value is not REMOVED_VALUE:
+ new_data[key] = value
+
+ if attribs:
+ new_update_data["attribs"] = attribs
+
+ if "name" in update_data:
+ new_update_data["version"] = update_data["name"]
+
+ if "type" in update_data:
+ new_type = update_data["type"]
+ if new_type == "version":
+ new_update_data["active"] = True
+ elif new_type == "archived_version":
+ new_update_data["active"] = False
+
+ if "parent" in update_data:
+ new_update_data["productId"] = update_data["parent"]
+
+ flat_data = _to_flat_dict(new_update_data)
+ if new_data:
+ print("Version has new data: {}".format(new_data))
+ flat_data["data"] = new_data
+ return flat_data
+
+
+def convert_update_hero_version_to_v4(
+ project_name, hero_version_id, update_data, con
+):
+ if "version_id" not in update_data:
+ return None
+
+ version_id = update_data["version_id"]
+ hero_version = con.get_hero_version_by_id(project_name, hero_version_id)
+ version = con.get_version_by_id(project_name, version_id)
+ version["version"] = - version["version"]
+ version["id"] = hero_version_id
+
+ for auto_key in (
+ "name",
+ "createdAt",
+ "updatedAt",
+ "author",
+ ):
+ version.pop(auto_key, None)
+
+ return prepare_entity_changes(hero_version, version)
+
+
+def convert_update_representation_to_v4(
+ project_name, repre_id, update_data, con
+):
+ new_update_data = {}
+
+ folder_attributes = con.get_attributes_for_type("folder")
+ full_update_data = _from_flat_dict(update_data)
+ data = full_update_data.get("data")
+
+ new_data = {}
+ attribs = {}
+ if data:
+ for key, value in data.items():
+ if key in folder_attributes:
+ attribs[key] = value
+ else:
+ new_data[key] = value
+
+ if "template" in attribs:
+ attribs["template"] = (
+ attribs["template"]
+ .replace("{asset}", "{folder[name]}")
+ .replace("{family}", "{product[type]}")
+ .replace("{subset}", "{product[name]}")
+ )
+
+ if "name" in update_data:
+ new_update_data["name"] = update_data["name"]
+
+ if "type" in update_data:
+ new_type = update_data["type"]
+ if new_type == "representation":
+ new_update_data["active"] = True
+ elif new_type == "archived_representation":
+ new_update_data["active"] = False
+
+ if "parent" in update_data:
+ new_update_data["versionId"] = update_data["parent"]
+
+ if "context" in update_data:
+ context = update_data["context"]
+ if "asset" in context:
+ context["folder"] = {"name": context.pop("asset")}
+
+ if "family" in context or "subset" in context:
+ context["product"] = {
+ "name": context.pop("subset"),
+ "type": context.pop("family"),
+ }
+ new_data["context"] = context
+
+ if "files" in update_data:
+ new_files = update_data["files"]
+ if isinstance(new_files, dict):
+ new_files = list(new_files.values())
+
+ for item in new_files:
+ for key in tuple(item.keys()):
+ if key not in ("hash", "path", "size"):
+ item.pop(key)
+ item.update({
+ "id": create_entity_id(),
+ "name": os.path.basename(item["path"]),
+ "hash_type": "op3",
+ })
+ new_update_data["files"] = new_files
+
+ flat_data = _to_flat_dict(new_update_data)
+ if new_data:
+ print("Representation has new data: {}".format(new_data))
+ flat_data["data"] = new_data
+
+ return flat_data
+
+
+def convert_update_workfile_info_to_v4(update_data):
+ return {
+ key: value
+ for key, value in update_data.items()
+ if key.startswith("data")
+ }
diff --git a/openpype/client/server/entities.py b/openpype/client/server/entities.py
new file mode 100644
index 0000000000..9579f13add
--- /dev/null
+++ b/openpype/client/server/entities.py
@@ -0,0 +1,694 @@
+import collections
+
+from ayon_api import get_server_api_connection
+
+from openpype.client.mongo.operations import CURRENT_THUMBNAIL_SCHEMA
+
+from .openpype_comp import get_folders_with_tasks
+from .conversion_utils import (
+ project_fields_v3_to_v4,
+ convert_v4_project_to_v3,
+
+ folder_fields_v3_to_v4,
+ convert_v4_folder_to_v3,
+
+ subset_fields_v3_to_v4,
+ convert_v4_subset_to_v3,
+
+ version_fields_v3_to_v4,
+ convert_v4_version_to_v3,
+
+ representation_fields_v3_to_v4,
+ convert_v4_representation_to_v3,
+
+ workfile_info_fields_v3_to_v4,
+ convert_v4_workfile_info_to_v3,
+)
+
+
+def get_projects(active=True, inactive=False, library=None, fields=None):
+ if not active and not inactive:
+ return
+
+ if active and inactive:
+ active = None
+ elif active:
+ active = True
+ elif inactive:
+ active = False
+
+ con = get_server_api_connection()
+ fields = project_fields_v3_to_v4(fields, con)
+ for project in con.get_projects(active, library, fields=fields):
+ yield convert_v4_project_to_v3(project)
+
+
+def get_project(project_name, active=True, inactive=False, fields=None):
+ # Skip if both are disabled
+ con = get_server_api_connection()
+ fields = project_fields_v3_to_v4(fields, con)
+ return convert_v4_project_to_v3(
+ con.get_project(project_name, fields=fields)
+ )
+
+
+def get_whole_project(*args, **kwargs):
+ raise NotImplementedError("'get_whole_project' not implemented")
+
+
+def _get_subsets(
+ project_name,
+ subset_ids=None,
+ subset_names=None,
+ folder_ids=None,
+ names_by_folder_ids=None,
+ archived=False,
+ fields=None
+):
+ # Convert fields and add minimum required fields
+ con = get_server_api_connection()
+ fields = subset_fields_v3_to_v4(fields, con)
+ if fields is not None:
+ for key in (
+ "id",
+ "active"
+ ):
+ fields.add(key)
+
+ active = None
+ if archived:
+ active = False
+
+ for subset in con.get_products(
+ project_name,
+ subset_ids,
+ subset_names,
+ folder_ids,
+ names_by_folder_ids,
+ active,
+ fields
+ ):
+ yield convert_v4_subset_to_v3(subset)
+
+
+def _get_versions(
+ project_name,
+ version_ids=None,
+ subset_ids=None,
+ versions=None,
+ hero=True,
+ standard=True,
+ latest=None,
+ active=None,
+ fields=None
+):
+ con = get_server_api_connection()
+
+ fields = version_fields_v3_to_v4(fields, con)
+
+ # Make sure 'productId' and 'version' are available when hero versions
+ # are queried
+ if fields and hero:
+ fields = set(fields)
+ fields |= {"productId", "version"}
+
+ queried_versions = con.get_versions(
+ project_name,
+ version_ids,
+ subset_ids,
+ versions,
+ hero,
+ standard,
+ latest,
+ active=active,
+ fields=fields
+ )
+
+ versions = []
+ hero_versions = []
+ for version in queried_versions:
+ if version["version"] < 0:
+ hero_versions.append(version)
+ else:
+ versions.append(convert_v4_version_to_v3(version))
+
+ if hero_versions:
+ subset_ids = set()
+ versions_nums = set()
+ for hero_version in hero_versions:
+ versions_nums.add(abs(hero_version["version"]))
+ subset_ids.add(hero_version["productId"])
+
+ hero_eq_versions = con.get_versions(
+ project_name,
+ product_ids=subset_ids,
+ versions=versions_nums,
+ hero=False,
+ fields=["id", "version", "productId"]
+ )
+ hero_eq_by_subset_id = collections.defaultdict(list)
+ for version in hero_eq_versions:
+ hero_eq_by_subset_id[version["productId"]].append(version)
+
+ for hero_version in hero_versions:
+ abs_version = abs(hero_version["version"])
+ subset_id = hero_version["productId"]
+ version_id = None
+ for version in hero_eq_by_subset_id.get(subset_id, []):
+ if version["version"] == abs_version:
+ version_id = version["id"]
+ break
+ conv_hero = convert_v4_version_to_v3(hero_version)
+ conv_hero["version_id"] = version_id
+ versions.append(conv_hero)
+
+ return versions
+
+
+def get_asset_by_id(project_name, asset_id, fields=None):
+ assets = get_assets(
+ project_name, asset_ids=[asset_id], fields=fields
+ )
+ for asset in assets:
+ return asset
+ return None
+
+
+def get_asset_by_name(project_name, asset_name, fields=None):
+ assets = get_assets(
+ project_name, asset_names=[asset_name], fields=fields
+ )
+ for asset in assets:
+ return asset
+ return None
+
+
+def get_assets(
+ project_name,
+ asset_ids=None,
+ asset_names=None,
+ parent_ids=None,
+ archived=False,
+ fields=None
+):
+ if not project_name:
+ return
+
+ active = True
+ if archived:
+ active = False
+
+ con = get_server_api_connection()
+ fields = folder_fields_v3_to_v4(fields, con)
+ kwargs = dict(
+ folder_ids=asset_ids,
+ folder_names=asset_names,
+ parent_ids=parent_ids,
+ active=active,
+ fields=fields
+ )
+
+ if fields is None or "tasks" in fields:
+ folders = get_folders_with_tasks(con, project_name, **kwargs)
+
+ else:
+ folders = con.get_folders(project_name, **kwargs)
+
+ for folder in folders:
+ yield convert_v4_folder_to_v3(folder, project_name)
+
+
+def get_archived_assets(
+ project_name,
+ asset_ids=None,
+ asset_names=None,
+ parent_ids=None,
+ fields=None
+):
+ return get_assets(
+ project_name,
+ asset_ids,
+ asset_names,
+ parent_ids,
+ True,
+ fields
+ )
+
+
+def get_asset_ids_with_subsets(project_name, asset_ids=None):
+ con = get_server_api_connection()
+ return con.get_folder_ids_with_products(project_name, asset_ids)
+
+
+def get_subset_by_id(project_name, subset_id, fields=None):
+ subsets = get_subsets(
+ project_name, subset_ids=[subset_id], fields=fields
+ )
+ for subset in subsets:
+ return subset
+ return None
+
+
+def get_subset_by_name(project_name, subset_name, asset_id, fields=None):
+ subsets = get_subsets(
+ project_name,
+ subset_names=[subset_name],
+ asset_ids=[asset_id],
+ fields=fields
+ )
+ for subset in subsets:
+ return subset
+ return None
+
+
+def get_subsets(
+ project_name,
+ subset_ids=None,
+ subset_names=None,
+ asset_ids=None,
+ names_by_asset_ids=None,
+ archived=False,
+ fields=None
+):
+ return _get_subsets(
+ project_name,
+ subset_ids,
+ subset_names,
+ asset_ids,
+ names_by_asset_ids,
+ archived,
+ fields=fields
+ )
+
+
+def get_subset_families(project_name, subset_ids=None):
+ con = get_server_api_connection()
+ return con.get_product_type_names(project_name, subset_ids)
+
+
+def get_version_by_id(project_name, version_id, fields=None):
+ versions = get_versions(
+ project_name,
+ version_ids=[version_id],
+ fields=fields,
+ hero=True
+ )
+ for version in versions:
+ return version
+ return None
+
+
+def get_version_by_name(project_name, version, subset_id, fields=None):
+ versions = get_versions(
+ project_name,
+ subset_ids=[subset_id],
+ versions=[version],
+ fields=fields
+ )
+ for version in versions:
+ return version
+ return None
+
+
+def get_versions(
+ project_name,
+ version_ids=None,
+ subset_ids=None,
+ versions=None,
+ hero=False,
+ fields=None
+):
+ return _get_versions(
+ project_name,
+ version_ids,
+ subset_ids,
+ versions,
+ hero=hero,
+ standard=True,
+ fields=fields
+ )
+
+
+def get_hero_version_by_id(project_name, version_id, fields=None):
+ versions = get_hero_versions(
+ project_name,
+ version_ids=[version_id],
+ fields=fields
+ )
+ for version in versions:
+ return version
+ return None
+
+
+def get_hero_version_by_subset_id(
+ project_name, subset_id, fields=None
+):
+ versions = get_hero_versions(
+ project_name,
+ subset_ids=[subset_id],
+ fields=fields
+ )
+ for version in versions:
+ return version
+ return None
+
+
+def get_hero_versions(
+ project_name, subset_ids=None, version_ids=None, fields=None
+):
+ return _get_versions(
+ project_name,
+ version_ids=version_ids,
+ subset_ids=subset_ids,
+ hero=True,
+ standard=False,
+ fields=fields
+ )
+
+
+def get_last_versions(project_name, subset_ids, active=None, fields=None):
+ if fields:
+ fields = set(fields)
+ fields.add("parent")
+
+ versions = _get_versions(
+ project_name,
+ subset_ids=subset_ids,
+ latest=True,
+ hero=False,
+ active=active,
+ fields=fields
+ )
+ return {
+ version["parent"]: version
+ for version in versions
+ }
+
+
+def get_last_version_by_subset_id(project_name, subset_id, fields=None):
+ versions = _get_versions(
+ project_name,
+ subset_ids=[subset_id],
+ latest=True,
+ hero=False,
+ fields=fields
+ )
+ if not versions:
+ return None
+ return versions[0]
+
+
+def get_last_version_by_subset_name(
+ project_name,
+ subset_name,
+ asset_id=None,
+ asset_name=None,
+ fields=None
+):
+ if not asset_id and not asset_name:
+ return None
+
+ if not asset_id:
+ asset = get_asset_by_name(
+ project_name, asset_name, fields=["_id"]
+ )
+ if not asset:
+ return None
+ asset_id = asset["_id"]
+
+ subset = get_subset_by_name(
+ project_name, subset_name, asset_id, fields=["_id"]
+ )
+ if not subset:
+ return None
+ return get_last_version_by_subset_id(
+ project_name, subset["id"], fields=fields
+ )
+
+
+def get_output_link_versions(project_name, version_id, fields=None):
+ if not version_id:
+ return []
+
+ con = get_server_api_connection()
+ version_links = con.get_version_links(
+ project_name, version_id, link_direction="out")
+
+ version_ids = {
+ link["entityId"]
+ for link in version_links
+ if link["entityType"] == "version"
+ }
+ if not version_ids:
+ return []
+
+ return get_versions(project_name, version_ids=version_ids, fields=fields)
+
+
+def version_is_latest(project_name, version_id):
+ con = get_server_api_connection()
+ return con.version_is_latest(project_name, version_id)
+
+
+def get_representation_by_id(project_name, representation_id, fields=None):
+ representations = get_representations(
+ project_name,
+ representation_ids=[representation_id],
+ fields=fields
+ )
+ for representation in representations:
+ return representation
+ return None
+
+
+def get_representation_by_name(
+ project_name, representation_name, version_id, fields=None
+):
+ representations = get_representations(
+ project_name,
+ representation_names=[representation_name],
+ version_ids=[version_id],
+ fields=fields
+ )
+ for representation in representations:
+ return representation
+ return None
+
+
+def get_representations(
+ project_name,
+ representation_ids=None,
+ representation_names=None,
+ version_ids=None,
+ context_filters=None,
+ names_by_version_ids=None,
+ archived=False,
+ standard=True,
+ fields=None
+):
+ if context_filters is not None:
+ # TODO should we add the support?
+ # - there was ability to fitler using regex
+ raise ValueError("OP v4 can't filter by representation context.")
+
+ if not archived and not standard:
+ return
+
+ if archived and not standard:
+ active = False
+ elif not archived and standard:
+ active = True
+ else:
+ active = None
+
+ con = get_server_api_connection()
+ fields = representation_fields_v3_to_v4(fields, con)
+ if fields and active is not None:
+ fields.add("active")
+
+ representations = con.get_representations(
+ project_name,
+ representation_ids,
+ representation_names,
+ version_ids,
+ names_by_version_ids,
+ active,
+ fields=fields
+ )
+ for representation in representations:
+ yield convert_v4_representation_to_v3(representation)
+
+
+def get_representation_parents(project_name, representation):
+ if not representation:
+ return None
+
+ repre_id = representation["_id"]
+ parents_by_repre_id = get_representations_parents(
+ project_name, [representation]
+ )
+ return parents_by_repre_id[repre_id]
+
+
+def get_representations_parents(project_name, representations):
+ repre_ids = {
+ repre["_id"]
+ for repre in representations
+ }
+ con = get_server_api_connection()
+ parents_by_repre_id = con.get_representations_parents(project_name,
+ repre_ids)
+ folder_ids = set()
+ for parents in parents_by_repre_id .values():
+ folder_ids.add(parents[2]["id"])
+
+ tasks_by_folder_id = {}
+
+ new_parents = {}
+ for repre_id, parents in parents_by_repre_id .items():
+ version, subset, folder, project = parents
+ folder_tasks = tasks_by_folder_id.get(folder["id"]) or {}
+ folder["tasks"] = folder_tasks
+ new_parents[repre_id] = (
+ convert_v4_version_to_v3(version),
+ convert_v4_subset_to_v3(subset),
+ convert_v4_folder_to_v3(folder, project_name),
+ project
+ )
+ return new_parents
+
+
+def get_archived_representations(
+ project_name,
+ representation_ids=None,
+ representation_names=None,
+ version_ids=None,
+ context_filters=None,
+ names_by_version_ids=None,
+ fields=None
+):
+ return get_representations(
+ project_name,
+ representation_ids=representation_ids,
+ representation_names=representation_names,
+ version_ids=version_ids,
+ context_filters=context_filters,
+ names_by_version_ids=names_by_version_ids,
+ archived=True,
+ standard=False,
+ fields=fields
+ )
+
+
+def get_thumbnail(
+ project_name, thumbnail_id, entity_type, entity_id, fields=None
+):
+ """Receive thumbnail entity data.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ thumbnail_id (Union[str, ObjectId]): Id of thumbnail entity.
+ entity_type (str): Type of entity for which the thumbnail should be
+ received.
+ entity_id (str): Id of entity for which the thumbnail should be
+ received.
+ fields (Iterable[str]): Fields that should be returned. All fields are
+ returned if 'None' is passed.
+
+ Returns:
+ None: If thumbnail with specified id was not found.
+ Dict: Thumbnail entity data which can be reduced to specified 'fields'.
+ """
+
+ if not thumbnail_id or not entity_type or not entity_id:
+ return None
+
+ if entity_type == "asset":
+ entity_type = "folder"
+
+ elif entity_type == "hero_version":
+ entity_type = "version"
+
+ return {
+ "_id": thumbnail_id,
+ "type": "thumbnail",
+ "schema": CURRENT_THUMBNAIL_SCHEMA,
+ "data": {
+ "entity_type": entity_type,
+ "entity_id": entity_id
+ }
+ }
+
+
+def get_thumbnails(project_name, thumbnail_contexts, fields=None):
+ """Get thumbnail entities.
+
+ Warning:
+ This function is not OpenPype compatible. There is none usage of this
+ function in codebase so there is nothing to convert. The previous
+ implementation cannot be AYON compatible without entity types.
+ """
+
+ thumbnail_items = set()
+ for thumbnail_context in thumbnail_contexts:
+ thumbnail_id, entity_type, entity_id = thumbnail_context
+ thumbnail_item = get_thumbnail(
+ project_name, thumbnail_id, entity_type, entity_id
+ )
+ if thumbnail_item:
+ thumbnail_items.add(thumbnail_item)
+ return list(thumbnail_items)
+
+
+def get_thumbnail_id_from_source(project_name, src_type, src_id):
+ """Receive thumbnail id from source entity.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ src_type (str): Type of source entity ('asset', 'version').
+ src_id (Union[str, ObjectId]): Id of source entity.
+
+ Returns:
+ ObjectId: Thumbnail id assigned to entity.
+ None: If Source entity does not have any thumbnail id assigned.
+ """
+
+ if not src_type or not src_id:
+ return None
+
+ if src_type == "version":
+ version = get_version_by_id(
+ project_name, src_id, fields=["data.thumbnail_id"]
+ ) or {}
+ return version.get("data", {}).get("thumbnail_id")
+
+ if src_type == "asset":
+ asset = get_asset_by_id(
+ project_name, src_id, fields=["data.thumbnail_id"]
+ ) or {}
+ return asset.get("data", {}).get("thumbnail_id")
+
+ return None
+
+
+def get_workfile_info(
+ project_name, asset_id, task_name, filename, fields=None
+):
+ if not asset_id or not task_name or not filename:
+ return None
+
+ con = get_server_api_connection()
+ task = con.get_task_by_name(
+ project_name, asset_id, task_name, fields=["id", "name", "folderId"]
+ )
+ if not task:
+ return None
+
+ fields = workfile_info_fields_v3_to_v4(fields)
+
+ for workfile_info in con.get_workfiles_info(
+ project_name, task_ids=[task["id"]], fields=fields
+ ):
+ if workfile_info["name"] == filename:
+ return convert_v4_workfile_info_to_v3(workfile_info, task)
+ return None
diff --git a/openpype/client/server/entity_links.py b/openpype/client/server/entity_links.py
new file mode 100644
index 0000000000..d8395aabe7
--- /dev/null
+++ b/openpype/client/server/entity_links.py
@@ -0,0 +1,156 @@
+import ayon_api
+from ayon_api import get_folder_links, get_versions_links
+
+from .entities import get_assets, get_representation_by_id
+
+
+def get_linked_asset_ids(project_name, asset_doc=None, asset_id=None):
+ """Extract linked asset ids from asset document.
+
+ One of asset document or asset id must be passed.
+
+ Note:
+ Asset links now works only from asset to assets.
+
+ Args:
+ project_name (str): Project where to look for asset.
+ asset_doc (dict): Asset document from DB.
+ asset_id (str): Asset id to find its document.
+
+ Returns:
+ List[Union[ObjectId, str]]: Asset ids of input links.
+ """
+
+ output = []
+ if not asset_doc and not asset_id:
+ return output
+
+ if not asset_id:
+ asset_id = asset_doc["_id"]
+
+ links = get_folder_links(project_name, asset_id, link_direction="in")
+ return [
+ link["entityId"]
+ for link in links
+ if link["entityType"] == "folder"
+ ]
+
+
+def get_linked_assets(
+ project_name, asset_doc=None, asset_id=None, fields=None
+):
+ """Return linked assets based on passed asset document.
+
+ One of asset document or asset id must be passed.
+
+ Args:
+ project_name (str): Name of project where to look for queried entities.
+ asset_doc (Dict[str, Any]): Asset document from database.
+ asset_id (Union[ObjectId, str]): Asset id. Can be used instead of
+ asset document.
+ fields (Iterable[str]): Fields that should be returned. All fields are
+ returned if 'None' is passed.
+
+ Returns:
+ List[Dict[str, Any]]: Asset documents of input links for passed
+ asset doc.
+ """
+
+ link_ids = get_linked_asset_ids(project_name, asset_doc, asset_id)
+ if not link_ids:
+ return []
+ return list(get_assets(project_name, asset_ids=link_ids, fields=fields))
+
+
+
+def get_linked_representation_id(
+ project_name, repre_doc=None, repre_id=None, link_type=None, max_depth=None
+):
+ """Returns list of linked ids of particular type (if provided).
+
+ One of representation document or representation id must be passed.
+ Note:
+ Representation links now works only from representation through version
+ back to representations.
+
+ Todos:
+ Missing depth query. Not sure how it did find more representations in
+ depth, probably links to version?
+
+ Args:
+ project_name (str): Name of project where look for links.
+ repre_doc (Dict[str, Any]): Representation document.
+ repre_id (Union[ObjectId, str]): Representation id.
+ link_type (str): Type of link (e.g. 'reference', ...).
+ max_depth (int): Limit recursion level. Default: 0
+
+ Returns:
+ List[ObjectId] Linked representation ids.
+ """
+
+ if repre_doc:
+ repre_id = repre_doc["_id"]
+
+ if not repre_id and not repre_doc:
+ return []
+
+ version_id = None
+ if repre_doc:
+ version_id = repre_doc.get("parent")
+
+ if not version_id:
+ repre_doc = get_representation_by_id(
+ project_name, repre_id, fields=["parent"]
+ )
+ if repre_doc:
+ version_id = repre_doc["parent"]
+
+ if not version_id:
+ return []
+
+ if max_depth is None or max_depth == 0:
+ max_depth = 1
+
+ link_types = None
+ if link_type:
+ link_types = [link_type]
+
+ # Store already found version ids to avoid recursion, and also to store
+ # output -> Don't forget to remove 'version_id' at the end!!!
+ linked_version_ids = {version_id}
+ # Each loop of depth will reset this variable
+ versions_to_check = {version_id}
+ for _ in range(max_depth):
+ if not versions_to_check:
+ break
+
+ links = get_versions_links(
+ project_name,
+ versions_to_check,
+ link_types=link_types,
+ link_direction="out")
+
+ versions_to_check = set()
+ for link in links:
+ # Care only about version links
+ if link["entityType"] != "version":
+ continue
+ entity_id = link["entityId"]
+ # Skip already found linked version ids
+ if entity_id in linked_version_ids:
+ continue
+ linked_version_ids.add(entity_id)
+ versions_to_check.add(entity_id)
+
+ linked_version_ids.remove(version_id)
+ if not linked_version_ids:
+ return []
+
+ representations = ayon_api.get_representations(
+ project_name,
+ version_ids=linked_version_ids,
+ fields=["id"])
+ return [
+ repre["id"]
+ for repre in representations
+ ]
diff --git a/openpype/client/server/openpype_comp.py b/openpype/client/server/openpype_comp.py
new file mode 100644
index 0000000000..a123fe3167
--- /dev/null
+++ b/openpype/client/server/openpype_comp.py
@@ -0,0 +1,156 @@
+import collections
+from ayon_api.graphql import GraphQlQuery, FIELD_VALUE, fields_to_dict
+
+from .constants import DEFAULT_FOLDER_FIELDS
+
+
+def folders_tasks_graphql_query(fields):
+ query = GraphQlQuery("FoldersQuery")
+ project_name_var = query.add_variable("projectName", "String!")
+ folder_ids_var = query.add_variable("folderIds", "[String!]")
+ parent_folder_ids_var = query.add_variable("parentFolderIds", "[String!]")
+ folder_paths_var = query.add_variable("folderPaths", "[String!]")
+ folder_names_var = query.add_variable("folderNames", "[String!]")
+ has_products_var = query.add_variable("folderHasProducts", "Boolean!")
+
+ project_field = query.add_field("project")
+ project_field.set_filter("name", project_name_var)
+
+ folders_field = project_field.add_field_with_edges("folders")
+ folders_field.set_filter("ids", folder_ids_var)
+ folders_field.set_filter("parentIds", parent_folder_ids_var)
+ folders_field.set_filter("names", folder_names_var)
+ folders_field.set_filter("paths", folder_paths_var)
+ folders_field.set_filter("hasProducts", has_products_var)
+
+ fields = set(fields)
+ fields.discard("tasks")
+ tasks_field = folders_field.add_field_with_edges("tasks")
+ tasks_field.add_field("name")
+ tasks_field.add_field("taskType")
+
+ nested_fields = fields_to_dict(fields)
+
+ query_queue = collections.deque()
+ for key, value in nested_fields.items():
+ query_queue.append((key, value, folders_field))
+
+ while query_queue:
+ item = query_queue.popleft()
+ key, value, parent = item
+ field = parent.add_field(key)
+ if value is FIELD_VALUE:
+ continue
+
+ for k, v in value.items():
+ query_queue.append((k, v, field))
+ return query
+
+
+def get_folders_with_tasks(
+ con,
+ project_name,
+ folder_ids=None,
+ folder_paths=None,
+ folder_names=None,
+ parent_ids=None,
+ active=True,
+ fields=None
+):
+ """Query folders with tasks from server.
+
+ This is for v4 compatibility where tasks were stored on assets. This is
+ an inefficient way how folders and tasks are queried so it was added only
+ as compatibility function.
+
+ Todos:
+ Folder name won't be unique identifier, so we should add folder path
+ filtering.
+
+ Notes:
+ Filter 'active' don't have direct filter in GraphQl.
+
+ Args:
+ con (ServerAPI): Connection to server.
+ project_name (str): Name of project where folders are.
+ folder_ids (Iterable[str]): Folder ids to filter.
+ folder_paths (Iterable[str]): Folder paths used for filtering.
+ folder_names (Iterable[str]): Folder names used for filtering.
+ parent_ids (Iterable[str]): Ids of folder parents. Use 'None'
+ if folder is direct child of project.
+ active (Union[bool, None]): Filter active/inactive folders. Both
+ are returned if is set to None.
+ fields (Union[Iterable(str), None]): Fields to be queried
+ for folder. All possible folder fields are returned if 'None'
+ is passed.
+
+ Returns:
+ List[Dict[str, Any]]: Queried folder entities.
+ """
+
+ if not project_name:
+ return []
+
+ filters = {
+ "projectName": project_name
+ }
+ if folder_ids is not None:
+ folder_ids = set(folder_ids)
+ if not folder_ids:
+ return []
+ filters["folderIds"] = list(folder_ids)
+
+ if folder_paths is not None:
+ folder_paths = set(folder_paths)
+ if not folder_paths:
+ return []
+ filters["folderPaths"] = list(folder_paths)
+
+ if folder_names is not None:
+ folder_names = set(folder_names)
+ if not folder_names:
+ return []
+ filters["folderNames"] = list(folder_names)
+
+ if parent_ids is not None:
+ parent_ids = set(parent_ids)
+ if not parent_ids:
+ return []
+ if None in parent_ids:
+ # Replace 'None' with '"root"' which is used during GraphQl
+ # query for parent ids filter for folders without folder
+ # parent
+ parent_ids.remove(None)
+ parent_ids.add("root")
+
+ if project_name in parent_ids:
+ # Replace project name with '"root"' which is used during
+ # GraphQl query for parent ids filter for folders without
+ # folder parent
+ parent_ids.remove(project_name)
+ parent_ids.add("root")
+
+ filters["parentFolderIds"] = list(parent_ids)
+
+ if fields:
+ fields = set(fields)
+ else:
+ fields = con.get_default_fields_for_type("folder")
+ fields |= DEFAULT_FOLDER_FIELDS
+
+ if active is not None:
+ fields.add("active")
+
+ query = folders_tasks_graphql_query(fields)
+ for attr, filter_value in filters.items():
+ query.set_variable_value(attr, filter_value)
+
+ parsed_data = query.query(con)
+ folders = parsed_data["project"]["folders"]
+ if active is None:
+ return folders
+ return [
+ folder
+ for folder in folders
+ if folder["active"] is active
+ ]
diff --git a/openpype/client/server/operations.py b/openpype/client/server/operations.py
new file mode 100644
index 0000000000..eeb55784e1
--- /dev/null
+++ b/openpype/client/server/operations.py
@@ -0,0 +1,881 @@
+import copy
+import json
+import collections
+import uuid
+import datetime
+
+from bson.objectid import ObjectId
+from ayon_api import get_server_api_connection
+
+from openpype.client.operations_base import (
+ REMOVED_VALUE,
+ CreateOperation,
+ UpdateOperation,
+ DeleteOperation,
+ BaseOperationsSession
+)
+
+from openpype.client.mongo.operations import (
+ CURRENT_THUMBNAIL_SCHEMA,
+ CURRENT_REPRESENTATION_SCHEMA,
+ CURRENT_HERO_VERSION_SCHEMA,
+ CURRENT_VERSION_SCHEMA,
+ CURRENT_SUBSET_SCHEMA,
+ CURRENT_ASSET_DOC_SCHEMA,
+ CURRENT_PROJECT_SCHEMA,
+)
+
+from .conversion_utils import (
+ convert_create_asset_to_v4,
+ convert_create_task_to_v4,
+ convert_create_subset_to_v4,
+ convert_create_version_to_v4,
+ convert_create_hero_version_to_v4,
+ convert_create_representation_to_v4,
+ convert_create_workfile_info_to_v4,
+
+ convert_update_folder_to_v4,
+ convert_update_subset_to_v4,
+ convert_update_version_to_v4,
+ convert_update_hero_version_to_v4,
+ convert_update_representation_to_v4,
+ convert_update_workfile_info_to_v4,
+)
+from .utils import create_entity_id
+
+
+def _create_or_convert_to_id(entity_id=None):
+ if entity_id is None:
+ return create_entity_id()
+
+ if isinstance(entity_id, ObjectId):
+ raise TypeError("Type of 'ObjectId' is not supported anymore.")
+
+ # Validate if can be converted to uuid
+ uuid.UUID(entity_id)
+ return entity_id
+
+
+def new_project_document(
+ project_name, project_code, config, data=None, entity_id=None
+):
+ """Create skeleton data of project document.
+
+ Args:
+ project_name (str): Name of project. Used as identifier of a project.
+ project_code (str): Shorter version of projet without spaces and
+ special characters (in most of cases). Should be also considered
+ as unique name across projects.
+ config (Dic[str, Any]): Project config consist of roots, templates,
+ applications and other project Anatomy related data.
+ data (Dict[str, Any]): Project data with information about it's
+ attributes (e.g. 'fps' etc.) or integration specific keys.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of project document.
+ """
+
+ if data is None:
+ data = {}
+
+ data["code"] = project_code
+
+ return {
+ "_id": _create_or_convert_to_id(entity_id),
+ "name": project_name,
+ "type": CURRENT_PROJECT_SCHEMA,
+ "entity_data": data,
+ "config": config
+ }
+
+
+def new_asset_document(
+ name, project_id, parent_id, parents, data=None, entity_id=None
+):
+ """Create skeleton data of asset document.
+
+ Args:
+ name (str): Is considered as unique identifier of asset in project.
+ project_id (Union[str, ObjectId]): Id of project doument.
+ parent_id (Union[str, ObjectId]): Id of parent asset.
+ parents (List[str]): List of parent assets names.
+ data (Dict[str, Any]): Asset document data. Empty dictionary is used
+ if not passed. Value of 'parent_id' is used to fill 'visualParent'.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of asset document.
+ """
+
+ if data is None:
+ data = {}
+ if parent_id is not None:
+ parent_id = _create_or_convert_to_id(parent_id)
+ data["visualParent"] = parent_id
+ data["parents"] = parents
+
+ return {
+ "_id": _create_or_convert_to_id(entity_id),
+ "type": "asset",
+ "name": name,
+ # This will be ignored
+ "parent": project_id,
+ "data": data,
+ "schema": CURRENT_ASSET_DOC_SCHEMA
+ }
+
+
+def new_subset_document(name, family, asset_id, data=None, entity_id=None):
+ """Create skeleton data of subset document.
+
+ Args:
+ name (str): Is considered as unique identifier of subset under asset.
+ family (str): Subset's family.
+ asset_id (Union[str, ObjectId]): Id of parent asset.
+ data (Dict[str, Any]): Subset document data. Empty dictionary is used
+ if not passed. Value of 'family' is used to fill 'family'.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of subset document.
+ """
+
+ if data is None:
+ data = {}
+ data["family"] = family
+ return {
+ "_id": _create_or_convert_to_id(entity_id),
+ "schema": CURRENT_SUBSET_SCHEMA,
+ "type": "subset",
+ "name": name,
+ "data": data,
+ "parent": _create_or_convert_to_id(asset_id)
+ }
+
+
+def new_version_doc(version, subset_id, data=None, entity_id=None):
+ """Create skeleton data of version document.
+
+ Args:
+ version (int): Is considered as unique identifier of version
+ under subset.
+ subset_id (Union[str, ObjectId]): Id of parent subset.
+ data (Dict[str, Any]): Version document data.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of version document.
+ """
+
+ if data is None:
+ data = {}
+
+ return {
+ "_id": _create_or_convert_to_id(entity_id),
+ "schema": CURRENT_VERSION_SCHEMA,
+ "type": "version",
+ "name": int(version),
+ "parent": _create_or_convert_to_id(subset_id),
+ "data": data
+ }
+
+
+def new_hero_version_doc(subset_id, data, version=None, entity_id=None):
+ """Create skeleton data of hero version document.
+
+ Args:
+ subset_id (Union[str, ObjectId]): Id of parent subset.
+ data (Dict[str, Any]): Version document data.
+ version (int): Version of source version.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of version document.
+ """
+
+ if version is None:
+ version = -1
+ elif version > 0:
+ version = -version
+
+ return {
+ "_id": _create_or_convert_to_id(entity_id),
+ "schema": CURRENT_HERO_VERSION_SCHEMA,
+ "type": "hero_version",
+ "version": version,
+ "parent": _create_or_convert_to_id(subset_id),
+ "data": data
+ }
+
+
+def new_representation_doc(
+ name, version_id, context, data=None, entity_id=None
+):
+ """Create skeleton data of representation document.
+
+ Args:
+ name (str): Representation name considered as unique identifier
+ of representation under version.
+ version_id (Union[str, ObjectId]): Id of parent version.
+ context (Dict[str, Any]): Representation context used for fill template
+ of to query.
+ data (Dict[str, Any]): Representation document data.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of version document.
+ """
+
+ if data is None:
+ data = {}
+
+ return {
+ "_id": _create_or_convert_to_id(entity_id),
+ "schema": CURRENT_REPRESENTATION_SCHEMA,
+ "type": "representation",
+ "parent": _create_or_convert_to_id(version_id),
+ "name": name,
+ "data": data,
+
+ # Imprint shortcut to context for performance reasons.
+ "context": context
+ }
+
+
+def new_thumbnail_doc(data=None, entity_id=None):
+ """Create skeleton data of thumbnail document.
+
+ Args:
+ data (Dict[str, Any]): Thumbnail document data.
+ entity_id (Union[str, ObjectId]): Predefined id of document. New id is
+ created if not passed.
+
+ Returns:
+ Dict[str, Any]: Skeleton of thumbnail document.
+ """
+
+ if data is None:
+ data = {}
+
+ return {
+ "_id": _create_or_convert_to_id(entity_id),
+ "type": "thumbnail",
+ "schema": CURRENT_THUMBNAIL_SCHEMA,
+ "data": data
+ }
+
+
+def new_workfile_info_doc(
+ filename, asset_id, task_name, files, data=None, entity_id=None
+):
+ """Create skeleton data of workfile info document.
+
+ Workfile document is at this moment used primarily for artist notes.
+
+ Args:
+ filename (str): Filename of workfile.
+ asset_id (Union[str, ObjectId]): Id of asset under which workfile live.
+ task_name (str): Task under which was workfile created.
+ files (List[str]): List of rootless filepaths related to workfile.
+ data (Dict[str, Any]): Additional metadata.
+
+ Returns:
+ Dict[str, Any]: Skeleton of workfile info document.
+ """
+
+ if not data:
+ data = {}
+
+ return {
+ "_id": _create_or_convert_to_id(entity_id),
+ "type": "workfile",
+ "parent": _create_or_convert_to_id(asset_id),
+ "task_name": task_name,
+ "filename": filename,
+ "data": data,
+ "files": files
+ }
+
+
+def _prepare_update_data(old_doc, new_doc, replace):
+ changes = {}
+ for key, value in new_doc.items():
+ if key not in old_doc or value != old_doc[key]:
+ changes[key] = value
+
+ if replace:
+ for key in old_doc.keys():
+ if key not in new_doc:
+ changes[key] = REMOVED_VALUE
+ return changes
+
+
+def prepare_subset_update_data(old_doc, new_doc, replace=True):
+ """Compare two subset documents and prepare update data.
+
+ Based on compared values will create update data for
+ 'MongoUpdateOperation'.
+
+ Empty output means that documents are identical.
+
+ Returns:
+ Dict[str, Any]: Changes between old and new document.
+ """
+
+ return _prepare_update_data(old_doc, new_doc, replace)
+
+
+def prepare_version_update_data(old_doc, new_doc, replace=True):
+ """Compare two version documents and prepare update data.
+
+ Based on compared values will create update data for
+ 'MongoUpdateOperation'.
+
+ Empty output means that documents are identical.
+
+ Returns:
+ Dict[str, Any]: Changes between old and new document.
+ """
+
+ return _prepare_update_data(old_doc, new_doc, replace)
+
+
+def prepare_hero_version_update_data(old_doc, new_doc, replace=True):
+ """Compare two hero version documents and prepare update data.
+
+ Based on compared values will create update data for 'UpdateOperation'.
+
+ Empty output means that documents are identical.
+
+ Returns:
+ Dict[str, Any]: Changes between old and new document.
+ """
+
+ changes = _prepare_update_data(old_doc, new_doc, replace)
+ changes.pop("version_id", None)
+ return changes
+
+
+def prepare_representation_update_data(old_doc, new_doc, replace=True):
+ """Compare two representation documents and prepare update data.
+
+ Based on compared values will create update data for
+ 'MongoUpdateOperation'.
+
+ Empty output means that documents are identical.
+
+ Returns:
+ Dict[str, Any]: Changes between old and new document.
+ """
+
+ changes = _prepare_update_data(old_doc, new_doc, replace)
+ context = changes.get("data", {}).get("context")
+ # Make sure that both 'family' and 'subset' are in changes if
+ # one of them changed (they'll both become 'product').
+ if (
+ context
+ and ("family" in context or "subset" in context)
+ ):
+ context["family"] = new_doc["data"]["context"]["family"]
+ context["subset"] = new_doc["data"]["context"]["subset"]
+
+ return changes
+
+
+def prepare_workfile_info_update_data(old_doc, new_doc, replace=True):
+ """Compare two workfile info documents and prepare update data.
+
+ Based on compared values will create update data for
+ 'MongoUpdateOperation'.
+
+ Empty output means that documents are identical.
+
+ Returns:
+ Dict[str, Any]: Changes between old and new document.
+ """
+
+ return _prepare_update_data(old_doc, new_doc, replace)
+
+
+class FailedOperations(Exception):
+ pass
+
+
+def entity_data_json_default(value):
+ if isinstance(value, datetime.datetime):
+ return int(value.timestamp())
+
+ raise TypeError(
+ "Object of type {} is not JSON serializable".format(str(type(value)))
+ )
+
+
+def failed_json_default(value):
+ return "< Failed value {} > {}".format(type(value), str(value))
+
+
+class ServerCreateOperation(CreateOperation):
+ """Opeartion to create an entity.
+
+ Args:
+ project_name (str): On which project operation will happen.
+ entity_type (str): Type of entity on which change happens.
+ e.g. 'asset', 'representation' etc.
+ data (Dict[str, Any]): Data of entity that will be created.
+ """
+
+ def __init__(self, project_name, entity_type, data, session):
+ self._session = session
+
+ if not data:
+ data = {}
+ data = copy.deepcopy(data)
+ if entity_type == "project":
+ raise ValueError("Project cannot be created using operations")
+
+ tasks = None
+ if entity_type in "asset":
+ # TODO handle tasks
+ entity_type = "folder"
+ if "data" in data:
+ tasks = data["data"].get("tasks")
+
+ project = self._session.get_project(project_name)
+ new_data = convert_create_asset_to_v4(data, project, self.con)
+
+ elif entity_type == "task":
+ project = self._session.get_project(project_name)
+ new_data = convert_create_task_to_v4(data, project, self.con)
+
+ elif entity_type == "subset":
+ new_data = convert_create_subset_to_v4(data, self.con)
+ entity_type = "product"
+
+ elif entity_type == "version":
+ new_data = convert_create_version_to_v4(data, self.con)
+
+ elif entity_type == "hero_version":
+ new_data = convert_create_hero_version_to_v4(
+ data, project_name, self.con
+ )
+ entity_type = "version"
+
+ elif entity_type in ("representation", "archived_representation"):
+ new_data = convert_create_representation_to_v4(data, self.con)
+ entity_type = "representation"
+
+ elif entity_type == "workfile":
+ new_data = convert_create_workfile_info_to_v4(
+ data, project_name, self.con
+ )
+
+ else:
+ raise ValueError(
+ "Unhandled entity type \"{}\"".format(entity_type)
+ )
+
+ # Simple check if data can be dumped into json
+ # - should raise error on 'ObjectId' object
+ try:
+ new_data = json.loads(
+ json.dumps(new_data, default=entity_data_json_default)
+ )
+
+ except:
+ raise ValueError("Couldn't json parse body: {}".format(
+ json.dumps(new_data, default=failed_json_default)
+ ))
+
+ super(ServerCreateOperation, self).__init__(
+ project_name, entity_type, new_data
+ )
+
+ if "id" not in self._data:
+ self._data["id"] = create_entity_id()
+
+ if tasks:
+ copied_tasks = copy.deepcopy(tasks)
+ for task_name, task in copied_tasks.items():
+ task["name"] = task_name
+ task["folderId"] = self._data["id"]
+ self.session.create_entity(
+ project_name, "task", task, nested_id=self.id
+ )
+
+ @property
+ def con(self):
+ return self.session.con
+
+ @property
+ def session(self):
+ return self._session
+
+ @property
+ def entity_id(self):
+ return self._data["id"]
+
+ def to_server_operation(self):
+ return {
+ "id": self.id,
+ "type": "create",
+ "entityType": self.entity_type,
+ "entityId": self.entity_id,
+ "data": self._data
+ }
+
+
+class ServerUpdateOperation(UpdateOperation):
+ """Operation to update an entity.
+
+ Args:
+ project_name (str): On which project operation will happen.
+ entity_type (str): Type of entity on which change happens.
+ e.g. 'asset', 'representation' etc.
+ entity_id (Union[str, ObjectId]): Identifier of an entity.
+ update_data (Dict[str, Any]): Key -> value changes that will be set in
+ database. If value is set to 'REMOVED_VALUE' the key will be
+ removed. Only first level of dictionary is checked (on purpose).
+ """
+
+ def __init__(
+ self, project_name, entity_type, entity_id, update_data, session
+ ):
+ self._session = session
+
+ update_data = copy.deepcopy(update_data)
+ if entity_type == "project":
+ raise ValueError("Project cannot be created using operations")
+
+ if entity_type in ("asset", "archived_asset"):
+ new_update_data = convert_update_folder_to_v4(
+ project_name, entity_id, update_data, self.con
+ )
+ entity_type = "folder"
+
+ elif entity_type == "subset":
+ new_update_data = convert_update_subset_to_v4(
+ project_name, entity_id, update_data, self.con
+ )
+ entity_type = "product"
+
+ elif entity_type == "version":
+ new_update_data = convert_update_version_to_v4(
+ project_name, entity_id, update_data, self.con
+ )
+
+ elif entity_type == "hero_version":
+ new_update_data = convert_update_hero_version_to_v4(
+ project_name, entity_id, update_data, self.con
+ )
+ entity_type = "version"
+
+ elif entity_type in ("representation", "archived_representation"):
+ new_update_data = convert_update_representation_to_v4(
+ project_name, entity_id, update_data, self.con
+ )
+ entity_type = "representation"
+
+ elif entity_type == "workfile":
+ new_update_data = convert_update_workfile_info_to_v4(
+ project_name, entity_id, update_data, self.con
+ )
+
+ else:
+ raise ValueError(
+ "Unhandled entity type \"{}\"".format(entity_type)
+ )
+
+ try:
+ new_update_data = json.loads(
+ json.dumps(new_update_data, default=entity_data_json_default)
+ )
+
+ except:
+ raise ValueError("Couldn't json parse body: {}".format(
+ json.dumps(new_update_data, default=failed_json_default)
+ ))
+
+ super(ServerUpdateOperation, self).__init__(
+ project_name, entity_type, entity_id, new_update_data
+ )
+
+ @property
+ def con(self):
+ return self.session.con
+
+ @property
+ def session(self):
+ return self._session
+
+ def to_server_operation(self):
+ if not self._update_data:
+ return None
+
+ update_data = {}
+ for key, value in self._update_data.items():
+ if value is REMOVED_VALUE:
+ value = None
+ update_data[key] = value
+
+ return {
+ "id": self.id,
+ "type": "update",
+ "entityType": self.entity_type,
+ "entityId": self.entity_id,
+ "data": update_data
+ }
+
+
+class ServerDeleteOperation(DeleteOperation):
+ """Opeartion to delete an entity.
+
+ Args:
+ project_name (str): On which project operation will happen.
+ entity_type (str): Type of entity on which change happens.
+ e.g. 'asset', 'representation' etc.
+ entity_id (Union[str, ObjectId]): Entity id that will be removed.
+ """
+
+ def __init__(self, project_name, entity_type, entity_id, session):
+ self._session = session
+
+ if entity_type == "asset":
+ entity_type == "folder"
+
+ elif entity_type == "hero_version":
+ entity_type = "version"
+
+ elif entity_type == "subset":
+ entity_type = "product"
+
+ super(ServerDeleteOperation, self).__init__(
+ project_name, entity_type, entity_id
+ )
+
+ @property
+ def con(self):
+ return self.session.con
+
+ @property
+ def session(self):
+ return self._session
+
+ def to_server_operation(self):
+ return {
+ "id": self.id,
+ "type": self.operation_name,
+ "entityId": self.entity_id,
+ "entityType": self.entity_type,
+ }
+
+
+class OperationsSession(BaseOperationsSession):
+ def __init__(self, con=None, *args, **kwargs):
+ super(OperationsSession, self).__init__(*args, **kwargs)
+ if con is None:
+ con = get_server_api_connection()
+ self._con = con
+ self._project_cache = {}
+ self._nested_operations = collections.defaultdict(list)
+
+ @property
+ def con(self):
+ return self._con
+
+ def get_project(self, project_name):
+ if project_name not in self._project_cache:
+ self._project_cache[project_name] = self.con.get_project(
+ project_name)
+ return copy.deepcopy(self._project_cache[project_name])
+
+ def commit(self):
+ """Commit session operations."""
+
+ operations, self._operations = self._operations, []
+ if not operations:
+ return
+
+ operations_by_project = collections.defaultdict(list)
+ for operation in operations:
+ operations_by_project[operation.project_name].append(operation)
+
+ body_by_id = {}
+ results = []
+ for project_name, operations in operations_by_project.items():
+ operations_body = []
+ for operation in operations:
+ body = operation.to_server_operation()
+ if body is not None:
+ try:
+ json.dumps(body)
+ except:
+ raise ValueError("Couldn't json parse body: {}".format(
+ json.dumps(
+ body, indent=4, default=failed_json_default
+ )
+ ))
+
+ body_by_id[operation.id] = body
+ operations_body.append(body)
+
+ if operations_body:
+ result = self._con.post(
+ "projects/{}/operations".format(project_name),
+ operations=operations_body,
+ canFail=False
+ )
+ results.append(result.data)
+
+ for result in results:
+ if result.get("success"):
+ continue
+
+ if "operations" not in result:
+ raise FailedOperations(
+ "Operation failed. Content: {}".format(str(result))
+ )
+
+ for op_result in result["operations"]:
+ if not op_result["success"]:
+ operation_id = op_result["id"]
+ raise FailedOperations((
+ "Operation \"{}\" failed with data:\n{}\nError: {}."
+ ).format(
+ operation_id,
+ json.dumps(body_by_id[operation_id], indent=4),
+ op_result.get("error", "unknown"),
+ ))
+
+ def create_entity(self, project_name, entity_type, data, nested_id=None):
+ """Fast access to 'ServerCreateOperation'.
+
+ Args:
+ project_name (str): On which project the creation happens.
+ entity_type (str): Which entity type will be created.
+ data (Dicst[str, Any]): Entity data.
+ nested_id (str): Id of other operation from which is triggered
+ operation -> Operations can trigger suboperations but they
+ must be added to operations list after it's parent is added.
+
+ Returns:
+ ServerCreateOperation: Object of update operation.
+ """
+
+ operation = ServerCreateOperation(
+ project_name, entity_type, data, self
+ )
+
+ if nested_id:
+ self._nested_operations[nested_id].append(operation)
+ else:
+ self.add(operation)
+ if operation.id in self._nested_operations:
+ self.extend(self._nested_operations.pop(operation.id))
+
+ return operation
+
+ def update_entity(
+ self, project_name, entity_type, entity_id, update_data, nested_id=None
+ ):
+ """Fast access to 'ServerUpdateOperation'.
+
+ Returns:
+ ServerUpdateOperation: Object of update operation.
+ """
+
+ operation = ServerUpdateOperation(
+ project_name, entity_type, entity_id, update_data, self
+ )
+ if nested_id:
+ self._nested_operations[nested_id].append(operation)
+ else:
+ self.add(operation)
+ if operation.id in self._nested_operations:
+ self.extend(self._nested_operations.pop(operation.id))
+ return operation
+
+ def delete_entity(
+ self, project_name, entity_type, entity_id, nested_id=None
+ ):
+ """Fast access to 'ServerDeleteOperation'.
+
+ Returns:
+ ServerDeleteOperation: Object of delete operation.
+ """
+
+ operation = ServerDeleteOperation(
+ project_name, entity_type, entity_id, self
+ )
+ if nested_id:
+ self._nested_operations[nested_id].append(operation)
+ else:
+ self.add(operation)
+ if operation.id in self._nested_operations:
+ self.extend(self._nested_operations.pop(operation.id))
+ return operation
+
+
+def create_project(
+ project_name,
+ project_code,
+ library_project=False,
+ preset_name=None,
+ con=None
+):
+ """Create project using OpenPype settings.
+
+ This project creation function is not validating project document on
+ creation. It is because project document is created blindly with only
+ minimum required information about project which is it's name, code, type
+ and schema.
+
+ Entered project name must be unique and project must not exist yet.
+
+ Note:
+ This function is here to be OP v4 ready but in v3 has more logic
+ to do. That's why inner imports are in the body.
+
+ Args:
+ project_name (str): New project name. Should be unique.
+ project_code (str): Project's code should be unique too.
+ library_project (bool): Project is library project.
+ preset_name (str): Name of anatomy preset. Default is used if not
+ passed.
+ con (ServerAPI): Connection to server with logged user.
+
+ Raises:
+ ValueError: When project name already exists in MongoDB.
+
+ Returns:
+ dict: Created project document.
+ """
+
+ if con is None:
+ con = get_server_api_connection()
+
+ return con.create_project(
+ project_name,
+ project_code,
+ library_project,
+ preset_name
+ )
+
+
+def delete_project(project_name, con=None):
+ if con is None:
+ con = get_server_api_connection()
+
+ return con.delete_project(project_name)
+
+
+def create_thumbnail(project_name, src_filepath, thumbnail_id=None, con=None):
+ if con is None:
+ con = get_server_api_connection()
+ return con.create_thumbnail(project_name, src_filepath, thumbnail_id)
diff --git a/openpype/client/server/utils.py b/openpype/client/server/utils.py
new file mode 100644
index 0000000000..ed128cfad9
--- /dev/null
+++ b/openpype/client/server/utils.py
@@ -0,0 +1,109 @@
+import uuid
+
+from openpype.client.operations_base import REMOVED_VALUE
+
+
+def create_entity_id():
+ return uuid.uuid1().hex
+
+
+def prepare_attribute_changes(old_entity, new_entity, replace=False):
+ """Prepare changes of attributes on entities.
+
+ Compare 'attrib' of old and new entity data to prepare only changed
+ values that should be sent to server for update.
+
+ Example:
+ >>> # Limited entity data to 'attrib'
+ >>> old_entity = {
+ ... "attrib": {"attr_1": 1, "attr_2": "MyString", "attr_3": True}
+ ... }
+ >>> new_entity = {
+ ... "attrib": {"attr_1": 2, "attr_3": True, "attr_4": 3}
+ ... }
+ >>> # Changes if replacement should not happen
+ >>> expected_changes = {
+ ... "attr_1": 2,
+ ... "attr_4": 3
+ ... }
+ >>> changes = prepare_attribute_changes(old_entity, new_entity)
+ >>> changes == expected_changes
+ True
+
+ >>> # Changes if replacement should happen
+ >>> expected_changes_replace = {
+ ... "attr_1": 2,
+ ... "attr_2": REMOVED_VALUE,
+ ... "attr_4": 3
+ ... }
+ >>> changes_replace = prepare_attribute_changes(
+ ... old_entity, new_entity, True)
+ >>> changes_replace == expected_changes_replace
+ True
+
+ Args:
+ old_entity (dict[str, Any]): Data of entity queried from server.
+ new_entity (dict[str, Any]): Entity data with applied changes.
+ replace (bool): New entity should fully replace all old entity values.
+
+ Returns:
+ Dict[str, Any]: Values from new entity only if value has changed.
+ """
+
+ attrib_changes = {}
+ new_attrib = new_entity.get("attrib")
+ old_attrib = old_entity.get("attrib")
+ if new_attrib is None:
+ if not replace:
+ return attrib_changes
+ new_attrib = {}
+
+ if old_attrib is None:
+ return new_attrib
+
+ for attr, new_attr_value in new_attrib.items():
+ old_attr_value = old_attrib.get(attr)
+ if old_attr_value != new_attr_value:
+ attrib_changes[attr] = new_attr_value
+
+ if replace:
+ for attr in old_attrib:
+ if attr not in new_attrib:
+ attrib_changes[attr] = REMOVED_VALUE
+
+ return attrib_changes
+
+
+def prepare_entity_changes(old_entity, new_entity, replace=False):
+ """Prepare changes of AYON entities.
+
+ Compare old and new entity to filter values from new data that changed.
+
+ Args:
+ old_entity (dict[str, Any]): Data of entity queried from server.
+ new_entity (dict[str, Any]): Entity data with applied changes.
+ replace (bool): All attributes should be replaced by new values. So
+ all attribute values that are not on new entity will be removed.
+
+ Returns:
+ Dict[str, Any]: Only values from new entity that changed.
+ """
+
+ changes = {}
+ for key, new_value in new_entity.items():
+ if key == "attrib":
+ continue
+
+ old_value = old_entity.get(key)
+ if old_value != new_value:
+ changes[key] = new_value
+
+ if replace:
+ for key in old_entity:
+ if key not in new_entity:
+ changes[key] = REMOVED_VALUE
+
+ attr_changes = prepare_attribute_changes(old_entity, new_entity, replace)
+ if attr_changes:
+ changes["attrib"] = attr_changes
+ return changes
diff --git a/openpype/hooks/pre_global_host_data.py b/openpype/hooks/pre_global_host_data.py
index 8a178915fb..260e28a18b 100644
--- a/openpype/hooks/pre_global_host_data.py
+++ b/openpype/hooks/pre_global_host_data.py
@@ -5,7 +5,7 @@ from openpype.lib import (
prepare_app_environments,
prepare_context_environments
)
-from openpype.pipeline import AvalonMongoDB, Anatomy
+from openpype.pipeline import Anatomy
class GlobalHostDataHook(PreLaunchHook):
@@ -26,7 +26,6 @@ class GlobalHostDataHook(PreLaunchHook):
"app": app,
- "dbcon": self.data["dbcon"],
"project_doc": self.data["project_doc"],
"asset_doc": self.data["asset_doc"],
@@ -62,13 +61,6 @@ class GlobalHostDataHook(PreLaunchHook):
# Anatomy
self.data["anatomy"] = Anatomy(project_name)
- # Mongo connection
- dbcon = AvalonMongoDB()
- dbcon.Session["AVALON_PROJECT"] = project_name
- dbcon.install()
-
- self.data["dbcon"] = dbcon
-
# Project document
project_doc = get_project(project_name)
self.data["project_doc"] = project_doc
diff --git a/openpype/host/dirmap.py b/openpype/host/dirmap.py
index 42bf80ecec..e77f06e9d6 100644
--- a/openpype/host/dirmap.py
+++ b/openpype/host/dirmap.py
@@ -149,7 +149,7 @@ class HostDirmap(object):
Returns:
dict : { "source-path": [XXX], "destination-path": [YYYY]}
"""
- project_name = os.getenv("AVALON_PROJECT")
+ project_name = self.project_name
mapping = {}
if (not self.sync_module.enabled or
diff --git a/openpype/hosts/aftereffects/api/extension.zxp b/openpype/hosts/aftereffects/api/extension.zxp
index 50fda416f8..358e9740d3 100644
Binary files a/openpype/hosts/aftereffects/api/extension.zxp and b/openpype/hosts/aftereffects/api/extension.zxp differ
diff --git a/openpype/hosts/aftereffects/api/extension/CSXS/manifest.xml b/openpype/hosts/aftereffects/api/extension/CSXS/manifest.xml
index 9f65720ef0..0057758320 100644
--- a/openpype/hosts/aftereffects/api/extension/CSXS/manifest.xml
+++ b/openpype/hosts/aftereffects/api/extension/CSXS/manifest.xml
@@ -1,5 +1,5 @@
-
diff --git a/openpype/hosts/aftereffects/api/extension/index.html b/openpype/hosts/aftereffects/api/extension/index.html
index 291965559f..480b814a57 100644
--- a/openpype/hosts/aftereffects/api/extension/index.html
+++ b/openpype/hosts/aftereffects/api/extension/index.html
@@ -104,6 +104,39 @@
});
+
+
+
+
+
+