mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 21:32:15 +01:00
Merge remote-tracking branch 'origin/develop' into bugfix/super-very-important-bugfix
This commit is contained in:
commit
2bd0856168
29 changed files with 837 additions and 256 deletions
20
pype.py
20
pype.py
|
|
@ -48,8 +48,11 @@ from igniter.tools import load_environments, add_acre_to_sys_path
|
|||
|
||||
from igniter import BootstrapRepos
|
||||
|
||||
add_acre_to_sys_path()
|
||||
import acre
|
||||
try:
|
||||
import acre
|
||||
except ImportError:
|
||||
add_acre_to_sys_path()
|
||||
import acre
|
||||
|
||||
|
||||
def set_environments() -> None:
|
||||
|
|
@ -101,20 +104,10 @@ def set_modules_environments():
|
|||
|
||||
def boot():
|
||||
"""Bootstrap Pype."""
|
||||
art = r"""
|
||||
____________
|
||||
/\ ___ \
|
||||
\ \ \/_\ \
|
||||
\ \ _____/ ___ ___ ___
|
||||
\ \ \___/ ---- \ \\ \\ \
|
||||
\ \____\ / \____\ \__\\__\\__\
|
||||
\/____/ \/____/ . PYPE Club .
|
||||
|
||||
"""
|
||||
|
||||
from pype.lib.terminal_splash import play_animation
|
||||
play_animation()
|
||||
set_environments()
|
||||
|
||||
# find pype versions
|
||||
bootstrap = BootstrapRepos()
|
||||
pype_versions = bootstrap.find_pype()
|
||||
|
|
@ -140,6 +133,7 @@ def boot():
|
|||
else:
|
||||
os.environ["PYPE_MONGO"] = pype_mongo
|
||||
|
||||
set_environments()
|
||||
if getattr(sys, 'frozen', False):
|
||||
if not pype_versions:
|
||||
import igniter
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ from .log import PypeLogger, timeit
|
|||
from .mongo import (
|
||||
decompose_url,
|
||||
compose_url,
|
||||
get_default_components
|
||||
get_default_components,
|
||||
PypeMongoConnection
|
||||
)
|
||||
from .anatomy import Anatomy
|
||||
|
||||
|
|
@ -130,6 +131,8 @@ __all__ = [
|
|||
"decompose_url",
|
||||
"compose_url",
|
||||
"get_default_components",
|
||||
"PypeMongoConnection",
|
||||
|
||||
"IniSettingRegistry",
|
||||
"JSONSettingRegistry",
|
||||
"PypeSettingsRegistry",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
import os
|
||||
import sys
|
||||
import time
|
||||
import logging
|
||||
import pymongo
|
||||
|
||||
try:
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
except ImportError:
|
||||
if sys.version_info[0] == 2:
|
||||
from urlparse import urlparse, parse_qs
|
||||
else:
|
||||
from urllib.parse import urlparse, parse_qs
|
||||
|
||||
|
||||
class MongoEnvNotSet(Exception):
|
||||
|
|
@ -79,3 +83,79 @@ def get_default_components():
|
|||
"URL for Mongo logging connection is not set."
|
||||
)
|
||||
return decompose_url(mongo_url)
|
||||
|
||||
|
||||
def extract_port_from_url(url):
|
||||
parsed_url = urlparse(url)
|
||||
if parsed_url.scheme is None:
|
||||
_url = "mongodb://{}".format(url)
|
||||
parsed_url = urlparse(_url)
|
||||
return parsed_url.port
|
||||
|
||||
|
||||
class PypeMongoConnection:
|
||||
"""Singleton MongoDB connection.
|
||||
|
||||
Keeps MongoDB connections by url.
|
||||
"""
|
||||
mongo_clients = {}
|
||||
log = logging.getLogger("PypeMongoConnection")
|
||||
|
||||
@classmethod
|
||||
def get_mongo_client(cls, mongo_url=None):
|
||||
if mongo_url is None:
|
||||
mongo_url = os.environ["PYPE_MONGO"]
|
||||
|
||||
connection = cls.mongo_clients.get(mongo_url)
|
||||
if connection:
|
||||
# Naive validation of existing connection
|
||||
try:
|
||||
connection.server_info()
|
||||
except Exception:
|
||||
connection = None
|
||||
|
||||
if not connection:
|
||||
cls.log.debug("Creating mongo connection to {}".format(mongo_url))
|
||||
connection = cls.create_connection(mongo_url)
|
||||
cls.mongo_clients[mongo_url] = connection
|
||||
|
||||
return connection
|
||||
|
||||
@classmethod
|
||||
def create_connection(cls, mongo_url, timeout=None):
|
||||
if timeout is None:
|
||||
timeout = int(os.environ.get("AVALON_TIMEOUT") or 1000)
|
||||
|
||||
kwargs = {
|
||||
"host": mongo_url,
|
||||
"serverSelectionTimeoutMS": timeout
|
||||
}
|
||||
|
||||
port = extract_port_from_url(mongo_url)
|
||||
if port is not None:
|
||||
kwargs["port"] = int(port)
|
||||
|
||||
mongo_client = pymongo.MongoClient(**kwargs)
|
||||
|
||||
for _retry in range(3):
|
||||
try:
|
||||
t1 = time.time()
|
||||
mongo_client.server_info()
|
||||
|
||||
except Exception:
|
||||
cls.log.warning("Retrying...")
|
||||
time.sleep(1)
|
||||
timeout *= 1.5
|
||||
|
||||
else:
|
||||
break
|
||||
|
||||
else:
|
||||
raise IOError((
|
||||
"ERROR: Couldn't connect to {} in less than {:.3f}ms"
|
||||
).format(mongo_url, timeout))
|
||||
|
||||
cls.log.info("Connected to {}, delay {:.3f}s".format(
|
||||
mongo_url, time.time() - t1
|
||||
))
|
||||
return mongo_client
|
||||
|
|
|
|||
|
|
@ -29,6 +29,6 @@ def play_animation():
|
|||
# term.aquamarine3_bold(frame)
|
||||
print(f"{term.bold}{term.aquamarine3}{frame}{term.normal}")
|
||||
|
||||
sleep(0.05)
|
||||
sleep(0.035)
|
||||
current_frame += frame_size
|
||||
print(term.move_y(7))
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from .base import (
|
|||
ModulesManager,
|
||||
TrayModulesManager
|
||||
)
|
||||
|
||||
from .settings_module import SettingsModule
|
||||
from .rest_api import (
|
||||
RestApiModule,
|
||||
IRestApi
|
||||
|
|
@ -44,6 +44,8 @@ __all__ = (
|
|||
"ModulesManager",
|
||||
"TrayModulesManager",
|
||||
|
||||
"SettingsModule",
|
||||
|
||||
"UserModule",
|
||||
"IUserModule",
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from .. import (
|
|||
|
||||
|
||||
class AvalonModule(PypeModule, ITrayModule, IRestApi):
|
||||
name = "Avalon"
|
||||
name = "avalon"
|
||||
|
||||
def initialize(self, modules_settings):
|
||||
# This module is always enabled
|
||||
|
|
|
|||
|
|
@ -150,15 +150,15 @@ class ITrayService(ITrayModule):
|
|||
if ITrayService._services_submenu is None:
|
||||
from Qt import QtWidgets
|
||||
services_submenu = QtWidgets.QMenu("Services", tray_menu)
|
||||
services_submenu.setVisible(False)
|
||||
services_submenu.menuAction().setVisible(False)
|
||||
ITrayService._services_submenu = services_submenu
|
||||
return ITrayService._services_submenu
|
||||
|
||||
@staticmethod
|
||||
def add_service_action(action):
|
||||
ITrayService._services_submenu.addAction(action)
|
||||
if not ITrayService._services_submenu.isVisible():
|
||||
ITrayService._services_submenu.setVisible(True)
|
||||
if not ITrayService._services_submenu.menuAction().isVisible():
|
||||
ITrayService._services_submenu.menuAction().setVisible(True)
|
||||
|
||||
@staticmethod
|
||||
def _load_service_icons():
|
||||
|
|
@ -384,13 +384,14 @@ class ModulesManager:
|
|||
class TrayModulesManager(ModulesManager):
|
||||
# Define order of modules in menu
|
||||
modules_menu_order = (
|
||||
"User setting",
|
||||
"Ftrack",
|
||||
"user",
|
||||
"ftrack",
|
||||
"muster",
|
||||
"Avalon",
|
||||
"Clockify",
|
||||
"Standalone Publish",
|
||||
"Logging"
|
||||
"avalon",
|
||||
"clockify",
|
||||
"standalonepublish_tool",
|
||||
"log_viewer",
|
||||
"settings"
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class ClockifyModule(
|
|||
IFtrackEventHandlerPaths,
|
||||
ITimersManager
|
||||
):
|
||||
name = "Clockify"
|
||||
name = "clockify"
|
||||
|
||||
def initialize(self, modules_settings):
|
||||
clockify_settings = modules_settings[self.name]
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class IFtrackEventHandlerPaths:
|
|||
class FtrackModule(
|
||||
PypeModule, ITrayModule, IPluginPaths, ITimersManager, IUserModule
|
||||
):
|
||||
name = "Ftrack"
|
||||
name = "ftrack"
|
||||
|
||||
def initialize(self, settings):
|
||||
ftrack_settings = settings[self.name]
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class IdleManager(PypeModule, ITrayService):
|
|||
Is able to emit signals at specific time idle.
|
||||
"""
|
||||
label = "Idle Service"
|
||||
name = "Idle Manager"
|
||||
name = "idle_manager"
|
||||
|
||||
def initialize(self, module_settings):
|
||||
idle_man_settings = module_settings[self.name]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from .. import PypeModule, ITrayModule
|
|||
|
||||
|
||||
class LoggingModule(PypeModule, ITrayModule):
|
||||
name = "Logging"
|
||||
name = "log_viewer"
|
||||
|
||||
def initialize(self, modules_settings):
|
||||
logging_settings = modules_settings[self.name]
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ class RestApiModule(PypeModule, ITrayService):
|
|||
`_handle_callback_result` defined in handler.
|
||||
"""
|
||||
label = "Rest API Service"
|
||||
name = "Rest Api"
|
||||
name = "rest_api"
|
||||
|
||||
def initialize(self, modules_settings):
|
||||
rest_api_settings = modules_settings[self.name]
|
||||
|
|
|
|||
53
pype/modules/settings_module.py
Normal file
53
pype/modules/settings_module.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
from . import PypeModule, ITrayModule
|
||||
|
||||
|
||||
class SettingsModule(PypeModule, ITrayModule):
|
||||
name = "settings"
|
||||
|
||||
def initialize(self, _modules_settings):
|
||||
# This module is always enabled
|
||||
self.enabled = True
|
||||
|
||||
# User role
|
||||
# TODO should be changeable
|
||||
self.user_role = "developer"
|
||||
|
||||
# Tray attributes
|
||||
self.settings_window = None
|
||||
|
||||
def connect_with_modules(self, *_a, **_kw):
|
||||
return
|
||||
|
||||
def create_settings_window(self):
|
||||
if self.settings_window:
|
||||
return
|
||||
from pype.tools.settings import MainWidget
|
||||
self.settings_window = MainWidget(self.user_role)
|
||||
|
||||
def show_settings_window(self):
|
||||
if not self.settings_window:
|
||||
raise AssertionError("Window is not initialized.")
|
||||
|
||||
self.settings_window.show()
|
||||
|
||||
# Pull window to the front.
|
||||
self.settings_window.raise_()
|
||||
self.settings_window.activateWindow()
|
||||
|
||||
def tray_init(self):
|
||||
self.create_settings_window()
|
||||
|
||||
def tray_menu(self, tray_menu):
|
||||
"""Add **change credentials** option to tray menu."""
|
||||
from Qt import QtWidgets
|
||||
|
||||
# Actions
|
||||
action = QtWidgets.QAction("Settings", tray_menu)
|
||||
action.triggered.connect(self.show_settings_window)
|
||||
tray_menu.addAction(action)
|
||||
|
||||
def tray_start(self):
|
||||
return
|
||||
|
||||
def tray_exit(self):
|
||||
return
|
||||
|
|
@ -7,7 +7,7 @@ from .. import PypeModule, ITrayModule
|
|||
|
||||
class StandAlonePublishModule(PypeModule, ITrayModule):
|
||||
menu_label = "Publish"
|
||||
name = "Standalone Publish"
|
||||
name = "standalonepublish_tool"
|
||||
|
||||
def initialize(self, modules_settings):
|
||||
self.enabled = modules_settings[self.name]["enabled"]
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class TimersManager(PypeModule, ITrayService, IIdleManager):
|
|||
If IdleManager is imported then is able to handle about stop timers
|
||||
when user idles for a long time (set in presets).
|
||||
"""
|
||||
name = "Timers Manager"
|
||||
name = "timers_manager"
|
||||
label = "Timers Service"
|
||||
|
||||
def initialize(self, modules_settings):
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class UserModule(PypeModule, ITrayModule, IRestApi):
|
|||
cred_filename = 'user_info.json'
|
||||
env_name = "PYPE_USERNAME"
|
||||
|
||||
name = "User setting"
|
||||
name = "user"
|
||||
|
||||
def initialize(self, modules_settings):
|
||||
user_settings = modules_settings[self.name]
|
||||
|
|
|
|||
34
pype/settings/constants.py
Normal file
34
pype/settings/constants.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Metadata keys for work with studio and project overrides
|
||||
M_OVERRIDEN_KEY = "__overriden_keys__"
|
||||
# Metadata key for storing information about environments
|
||||
M_ENVIRONMENT_KEY = "__environment_keys__"
|
||||
# Metadata key for storing dynamic created labels
|
||||
M_DYNAMIC_KEY_LABEL = "__dynamic_keys_labels__"
|
||||
# NOTE key popping not implemented yet
|
||||
M_POP_KEY = "__pop_key__"
|
||||
|
||||
METADATA_KEYS = (
|
||||
M_OVERRIDEN_KEY,
|
||||
M_ENVIRONMENT_KEY,
|
||||
M_DYNAMIC_KEY_LABEL,
|
||||
M_POP_KEY
|
||||
)
|
||||
|
||||
# File where studio's system overrides are stored
|
||||
SYSTEM_SETTINGS_KEY = "system_settings"
|
||||
PROJECT_SETTINGS_KEY = "project_settings"
|
||||
PROJECT_ANATOMY_KEY = "project_anatomy"
|
||||
|
||||
|
||||
__all__ = (
|
||||
"M_OVERRIDEN_KEY",
|
||||
"M_ENVIRONMENT_KEY",
|
||||
"M_DYNAMIC_KEY_LABEL",
|
||||
"M_POP_KEY",
|
||||
|
||||
"METADATA_KEYS",
|
||||
|
||||
"SYSTEM_SETTINGS_KEY",
|
||||
"PROJECT_SETTINGS_KEY",
|
||||
"PROJECT_ANATOMY_KEY"
|
||||
)
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"Avalon": {
|
||||
"avalon": {
|
||||
"AVALON_MONGO": "",
|
||||
"AVALON_TIMEOUT": 1000,
|
||||
"AVALON_THUMBNAIL_ROOT": {
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
},
|
||||
"AVALON_DB_DATA": "{PYPE_SETUP_PATH}/../mongo_db_data"
|
||||
},
|
||||
"Ftrack": {
|
||||
"ftrack": {
|
||||
"enabled": true,
|
||||
"ftrack_server": "https://pype.ftrackapp.com",
|
||||
"ftrack_actions_path": [],
|
||||
|
|
@ -104,16 +104,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"Rest Api": {
|
||||
"rest_api": {
|
||||
"default_port": 8021,
|
||||
"exclude_ports": []
|
||||
},
|
||||
"Timers Manager": {
|
||||
"timers_manager": {
|
||||
"enabled": true,
|
||||
"full_time": 15.0,
|
||||
"message_time": 0.5
|
||||
},
|
||||
"Clockify": {
|
||||
"clockify": {
|
||||
"enabled": false,
|
||||
"workspace_name": "studio name"
|
||||
},
|
||||
|
|
@ -138,16 +138,16 @@
|
|||
"ffmpeg": 48
|
||||
}
|
||||
},
|
||||
"Logging": {
|
||||
"log_viewer": {
|
||||
"enabled": true
|
||||
},
|
||||
"User setting": {
|
||||
"user": {
|
||||
"enabled": true
|
||||
},
|
||||
"Standalone Publish": {
|
||||
"standalonepublish_tool": {
|
||||
"enabled": true
|
||||
},
|
||||
"Idle Manager": {
|
||||
"idle_manager": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
497
pype/settings/handlers.py
Normal file
497
pype/settings/handlers.py
Normal file
|
|
@ -0,0 +1,497 @@
|
|||
import os
|
||||
import json
|
||||
import copy
|
||||
import logging
|
||||
import collections
|
||||
import datetime
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import six
|
||||
import pype
|
||||
from .constants import (
|
||||
SYSTEM_SETTINGS_KEY,
|
||||
PROJECT_SETTINGS_KEY,
|
||||
PROJECT_ANATOMY_KEY
|
||||
)
|
||||
from .lib import load_json_file
|
||||
|
||||
JSON_EXC = getattr(json.decoder, "JSONDecodeError", ValueError)
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class SettingsHandler:
|
||||
@abstractmethod
|
||||
def save_studio_settings(self, data):
|
||||
"""Save studio overrides of system settings.
|
||||
|
||||
Do not use to store whole system settings data with defaults but only
|
||||
it's overrides with metadata defining how overrides should be applied
|
||||
in load function. For loading should be used function
|
||||
`studio_system_settings`.
|
||||
|
||||
Args:
|
||||
data(dict): Data of studio overrides with override metadata.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def save_project_settings(self, project_name, overrides):
|
||||
"""Save studio overrides of project settings.
|
||||
|
||||
Data are saved for specific project or as defaults for all projects.
|
||||
|
||||
Do not use to store whole project settings data with defaults but only
|
||||
it's overrides with metadata defining how overrides should be applied
|
||||
in load function. For loading should be used function
|
||||
`get_studio_project_settings_overrides` for global project settings
|
||||
and `get_project_settings_overrides` for project specific settings.
|
||||
|
||||
Args:
|
||||
project_name(str, null): Project name for which overrides are
|
||||
or None for global settings.
|
||||
data(dict): Data of project overrides with override metadata.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def save_project_anatomy(self, project_name, anatomy_data):
|
||||
"""Save studio overrides of project anatomy data.
|
||||
|
||||
Args:
|
||||
project_name(str, null): Project name for which overrides are
|
||||
or None for global settings.
|
||||
data(dict): Data of project overrides with override metadata.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_studio_system_settings_overrides(self):
|
||||
"""Studio overrides of system settings."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_studio_project_settings_overrides(self):
|
||||
"""Studio overrides of default project settings."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_studio_project_anatomy_overrides(self):
|
||||
"""Studio overrides of default project anatomy data."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_project_settings_overrides(self, project_name):
|
||||
"""Studio overrides of project settings for specific project.
|
||||
|
||||
Args:
|
||||
project_name(str): Name of project for which data should be loaded.
|
||||
|
||||
Returns:
|
||||
dict: Only overrides for entered project, may be empty dictionary.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_project_anatomy_overrides(self, project_name):
|
||||
"""Studio overrides of project anatomy for specific project.
|
||||
|
||||
Args:
|
||||
project_name(str): Name of project for which data should be loaded.
|
||||
|
||||
Returns:
|
||||
dict: Only overrides for entered project, may be empty dictionary.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class SettingsFileHandler(SettingsHandler):
|
||||
def __init__(self):
|
||||
self.log = logging.getLogger("SettingsFileHandler")
|
||||
|
||||
# Folder where studio overrides are stored
|
||||
studio_overrides_dir = os.getenv("PYPE_PROJECT_CONFIGS")
|
||||
if not studio_overrides_dir:
|
||||
studio_overrides_dir = os.path.dirname(os.path.dirname(
|
||||
os.path.abspath(pype.__file__)
|
||||
))
|
||||
system_settings_path = os.path.join(
|
||||
studio_overrides_dir, SYSTEM_SETTINGS_KEY + ".json"
|
||||
)
|
||||
|
||||
# File where studio's default project overrides are stored
|
||||
project_settings_filename = PROJECT_SETTINGS_KEY + ".json"
|
||||
project_settings_path = os.path.join(
|
||||
studio_overrides_dir, project_settings_filename
|
||||
)
|
||||
|
||||
project_anatomy_filename = PROJECT_ANATOMY_KEY + ".json"
|
||||
project_anatomy_path = os.path.join(
|
||||
studio_overrides_dir, project_anatomy_filename
|
||||
)
|
||||
|
||||
self.studio_overrides_dir = studio_overrides_dir
|
||||
self.system_settings_path = system_settings_path
|
||||
|
||||
self.project_settings_filename = project_settings_filename
|
||||
self.project_anatomy_filename = project_anatomy_filename
|
||||
|
||||
self.project_settings_path = project_settings_path
|
||||
self.project_anatomy_path = project_anatomy_path
|
||||
|
||||
def path_to_project_settings(self, project_name):
|
||||
if not project_name:
|
||||
return self.project_settings_path
|
||||
return os.path.join(
|
||||
self.studio_overrides_dir,
|
||||
project_name,
|
||||
self.project_settings_filename
|
||||
)
|
||||
|
||||
def path_to_project_anatomy(self, project_name):
|
||||
if not project_name:
|
||||
return self.project_anatomy_path
|
||||
return os.path.join(
|
||||
self.studio_overrides_dir,
|
||||
project_name,
|
||||
self.project_anatomy_filename
|
||||
)
|
||||
|
||||
def save_studio_settings(self, data):
|
||||
"""Save studio overrides of system settings.
|
||||
|
||||
Do not use to store whole system settings data with defaults but only
|
||||
it's overrides with metadata defining how overrides should be applied
|
||||
in load function. For loading should be used function
|
||||
`studio_system_settings`.
|
||||
|
||||
Args:
|
||||
data(dict): Data of studio overrides with override metadata.
|
||||
"""
|
||||
dirpath = os.path.dirname(self.system_settings_path)
|
||||
if not os.path.exists(dirpath):
|
||||
os.makedirs(dirpath)
|
||||
|
||||
self.log.debug(
|
||||
"Saving studio overrides. Output path: {}".format(
|
||||
self.system_settings_path
|
||||
)
|
||||
)
|
||||
with open(self.system_settings_path, "w") as file_stream:
|
||||
json.dump(data, file_stream, indent=4)
|
||||
|
||||
def save_project_settings(self, project_name, overrides):
|
||||
"""Save studio overrides of project settings.
|
||||
|
||||
Data are saved for specific project or as defaults for all projects.
|
||||
|
||||
Do not use to store whole project settings data with defaults but only
|
||||
it's overrides with metadata defining how overrides should be applied
|
||||
in load function. For loading should be used function
|
||||
`get_studio_project_settings_overrides` for global project settings
|
||||
and `get_project_settings_overrides` for project specific settings.
|
||||
|
||||
Args:
|
||||
project_name(str, null): Project name for which overrides are
|
||||
or None for global settings.
|
||||
data(dict): Data of project overrides with override metadata.
|
||||
"""
|
||||
project_overrides_json_path = self.path_to_project_settings(
|
||||
project_name
|
||||
)
|
||||
dirpath = os.path.dirname(project_overrides_json_path)
|
||||
if not os.path.exists(dirpath):
|
||||
os.makedirs(dirpath)
|
||||
|
||||
self.log.debug(
|
||||
"Saving overrides of project \"{}\". Output path: {}".format(
|
||||
project_name, project_overrides_json_path
|
||||
)
|
||||
)
|
||||
with open(project_overrides_json_path, "w") as file_stream:
|
||||
json.dump(overrides, file_stream, indent=4)
|
||||
|
||||
def save_project_anatomy(self, project_name, anatomy_data):
|
||||
"""Save studio overrides of project anatomy data.
|
||||
|
||||
Args:
|
||||
project_name(str, null): Project name for which overrides are
|
||||
or None for global settings.
|
||||
data(dict): Data of project overrides with override metadata.
|
||||
"""
|
||||
project_anatomy_json_path = self.path_to_project_anatomy(project_name)
|
||||
dirpath = os.path.dirname(project_anatomy_json_path)
|
||||
if not os.path.exists(dirpath):
|
||||
os.makedirs(dirpath)
|
||||
|
||||
self.log.debug(
|
||||
"Saving anatomy of project \"{}\". Output path: {}".format(
|
||||
project_name, project_anatomy_json_path
|
||||
)
|
||||
)
|
||||
with open(project_anatomy_json_path, "w") as file_stream:
|
||||
json.dump(anatomy_data, file_stream, indent=4)
|
||||
|
||||
def get_studio_system_settings_overrides(self):
|
||||
"""Studio overrides of system settings."""
|
||||
if os.path.exists(self.system_settings_path):
|
||||
return load_json_file(self.system_settings_path)
|
||||
return {}
|
||||
|
||||
def get_studio_project_settings_overrides(self):
|
||||
"""Studio overrides of default project settings."""
|
||||
if os.path.exists(self.project_settings_path):
|
||||
return load_json_file(self.project_settings_path)
|
||||
return {}
|
||||
|
||||
def get_studio_project_anatomy_overrides(self):
|
||||
"""Studio overrides of default project anatomy data."""
|
||||
if os.path.exists(self.project_anatomy_path):
|
||||
return load_json_file(self.project_anatomy_path)
|
||||
return {}
|
||||
|
||||
def get_project_settings_overrides(self, project_name):
|
||||
"""Studio overrides of project settings for specific project.
|
||||
|
||||
Args:
|
||||
project_name(str): Name of project for which data should be loaded.
|
||||
|
||||
Returns:
|
||||
dict: Only overrides for entered project, may be empty dictionary.
|
||||
"""
|
||||
path_to_json = self.path_to_project_settings(project_name)
|
||||
if not os.path.exists(path_to_json):
|
||||
return {}
|
||||
return load_json_file(path_to_json)
|
||||
|
||||
def get_project_anatomy_overrides(self, project_name):
|
||||
"""Studio overrides of project anatomy for specific project.
|
||||
|
||||
Args:
|
||||
project_name(str): Name of project for which data should be loaded.
|
||||
|
||||
Returns:
|
||||
dict: Only overrides for entered project, may be empty dictionary.
|
||||
"""
|
||||
if not project_name:
|
||||
return {}
|
||||
|
||||
path_to_json = self.path_to_project_anatomy(project_name)
|
||||
if not os.path.exists(path_to_json):
|
||||
return {}
|
||||
return load_json_file(path_to_json)
|
||||
|
||||
|
||||
class CacheValues:
|
||||
cache_lifetime = 10
|
||||
|
||||
def __init__(self):
|
||||
self.data = None
|
||||
self.creation_time = None
|
||||
|
||||
def data_copy(self):
|
||||
if not self.data:
|
||||
return {}
|
||||
return copy.deepcopy(self.data)
|
||||
|
||||
def update_data(self, data):
|
||||
self.data = data
|
||||
self.creation_time = datetime.datetime.now()
|
||||
|
||||
def update_from_document(self, document):
|
||||
value = "{}"
|
||||
if document:
|
||||
value = document.get("value") or value
|
||||
self.data = json.loads(value)
|
||||
|
||||
def to_json_string(self):
|
||||
return json.dumps(self.data or {})
|
||||
|
||||
@property
|
||||
def is_outdated(self):
|
||||
if self.creation_time is None:
|
||||
return True
|
||||
delta = (datetime.datetime.now() - self.creation_time).seconds
|
||||
return delta > self.cache_lifetime
|
||||
|
||||
|
||||
class MongoSettingsHandler(SettingsHandler):
|
||||
"""Settings handler that use mongo for storing and loading of settings."""
|
||||
|
||||
def __init__(self):
|
||||
# Get mongo connection
|
||||
from pype.lib import PypeMongoConnection
|
||||
settings_collection = PypeMongoConnection.get_mongo_client()
|
||||
|
||||
# TODO prepare version of pype
|
||||
# - pype version should define how are settings saved and loaded
|
||||
|
||||
# TODO modify to not use hardcoded keys
|
||||
database_name = "pype"
|
||||
collection_name = "settings"
|
||||
|
||||
self.settings_collection = settings_collection
|
||||
|
||||
self.database_name = database_name
|
||||
self.collection_name = collection_name
|
||||
|
||||
self.collection = settings_collection[database_name][collection_name]
|
||||
|
||||
self.system_settings_cache = CacheValues()
|
||||
self.project_settings_cache = collections.defaultdict(CacheValues)
|
||||
self.project_anatomy_cache = collections.defaultdict(CacheValues)
|
||||
|
||||
def save_studio_settings(self, data):
|
||||
"""Save studio overrides of system settings.
|
||||
|
||||
Do not use to store whole system settings data with defaults but only
|
||||
it's overrides with metadata defining how overrides should be applied
|
||||
in load function. For loading should be used function
|
||||
`studio_system_settings`.
|
||||
|
||||
Args:
|
||||
data(dict): Data of studio overrides with override metadata.
|
||||
"""
|
||||
self.system_settings_cache.update_data(data)
|
||||
|
||||
self.collection.replace_one(
|
||||
{
|
||||
"type": SYSTEM_SETTINGS_KEY
|
||||
},
|
||||
{
|
||||
"type": SYSTEM_SETTINGS_KEY,
|
||||
"value": self.system_settings_cache.to_json_string()
|
||||
},
|
||||
upsert=True
|
||||
)
|
||||
|
||||
def save_project_settings(self, project_name, overrides):
|
||||
"""Save studio overrides of project settings.
|
||||
|
||||
Data are saved for specific project or as defaults for all projects.
|
||||
|
||||
Do not use to store whole project settings data with defaults but only
|
||||
it's overrides with metadata defining how overrides should be applied
|
||||
in load function. For loading should be used function
|
||||
`get_studio_project_settings_overrides` for global project settings
|
||||
and `get_project_settings_overrides` for project specific settings.
|
||||
|
||||
Args:
|
||||
project_name(str, null): Project name for which overrides are
|
||||
or None for global settings.
|
||||
data(dict): Data of project overrides with override metadata.
|
||||
"""
|
||||
data_cache = self.project_settings_cache[project_name]
|
||||
data_cache.update_data(overrides)
|
||||
|
||||
self._save_project_data(
|
||||
project_name, PROJECT_SETTINGS_KEY, data_cache
|
||||
)
|
||||
|
||||
def save_project_anatomy(self, project_name, anatomy_data):
|
||||
"""Save studio overrides of project anatomy data.
|
||||
|
||||
Args:
|
||||
project_name(str, null): Project name for which overrides are
|
||||
or None for global settings.
|
||||
data(dict): Data of project overrides with override metadata.
|
||||
"""
|
||||
data_cache = self.project_anatomy_cache[project_name]
|
||||
data_cache.update_data(anatomy_data)
|
||||
|
||||
self._save_project_data(
|
||||
project_name, PROJECT_ANATOMY_KEY, data_cache
|
||||
)
|
||||
|
||||
def _save_project_data(self, project_name, doc_type, data_cache):
|
||||
is_default = bool(project_name is None)
|
||||
replace_filter = {
|
||||
"type": doc_type,
|
||||
"is_default": is_default
|
||||
}
|
||||
replace_data = {
|
||||
"type": doc_type,
|
||||
"value": data_cache.to_json_string(),
|
||||
"is_default": is_default
|
||||
}
|
||||
if not is_default:
|
||||
replace_filter["project_name"] = project_name
|
||||
replace_data["project_name"] = project_name
|
||||
|
||||
self.collection.replace_one(
|
||||
replace_filter,
|
||||
replace_data,
|
||||
upsert=True
|
||||
)
|
||||
|
||||
def get_studio_system_settings_overrides(self):
|
||||
"""Studio overrides of system settings."""
|
||||
if self.system_settings_cache.is_outdated:
|
||||
document = self.collection.find_one({
|
||||
"type": SYSTEM_SETTINGS_KEY
|
||||
})
|
||||
|
||||
self.system_settings_cache.update_from_document(document)
|
||||
return self.system_settings_cache.data_copy()
|
||||
|
||||
def _get_project_settings_overrides(self, project_name):
|
||||
if self.project_settings_cache[project_name].is_outdated:
|
||||
document_filter = {
|
||||
"type": PROJECT_SETTINGS_KEY,
|
||||
}
|
||||
if project_name is None:
|
||||
document_filter["is_default"] = True
|
||||
else:
|
||||
document_filter["project_name"] = project_name
|
||||
document = self.collection.find_one(document_filter)
|
||||
self.project_settings_cache[project_name].update_from_document(
|
||||
document
|
||||
)
|
||||
return self.project_settings_cache[project_name].data_copy()
|
||||
|
||||
def get_studio_project_settings_overrides(self):
|
||||
"""Studio overrides of default project settings."""
|
||||
return self._get_project_settings_overrides(None)
|
||||
|
||||
def get_project_settings_overrides(self, project_name):
|
||||
"""Studio overrides of project settings for specific project.
|
||||
|
||||
Args:
|
||||
project_name(str): Name of project for which data should be loaded.
|
||||
|
||||
Returns:
|
||||
dict: Only overrides for entered project, may be empty dictionary.
|
||||
"""
|
||||
if not project_name:
|
||||
return {}
|
||||
return self._get_project_settings_overrides(project_name)
|
||||
|
||||
def _get_project_anatomy_overrides(self, project_name):
|
||||
if self.project_anatomy_cache[project_name].is_outdated:
|
||||
document_filter = {
|
||||
"type": PROJECT_ANATOMY_KEY,
|
||||
}
|
||||
if project_name is None:
|
||||
document_filter["is_default"] = True
|
||||
else:
|
||||
document_filter["project_name"] = project_name
|
||||
document = self.collection.find_one(document_filter)
|
||||
self.project_anatomy_cache[project_name].update_from_document(
|
||||
document
|
||||
)
|
||||
return self.project_anatomy_cache[project_name].data_copy()
|
||||
|
||||
def get_studio_project_anatomy_overrides(self):
|
||||
"""Studio overrides of default project anatomy data."""
|
||||
return self._get_project_anatomy_overrides(None)
|
||||
|
||||
def get_project_anatomy_overrides(self, project_name):
|
||||
"""Studio overrides of project anatomy for specific project.
|
||||
|
||||
Args:
|
||||
project_name(str): Name of project for which data should be loaded.
|
||||
|
||||
Returns:
|
||||
dict: Only overrides for entered project, may be empty dictionary.
|
||||
"""
|
||||
if not project_name:
|
||||
return {}
|
||||
return self._get_project_anatomy_overrides(project_name)
|
||||
|
|
@ -1,60 +1,95 @@
|
|||
import os
|
||||
import json
|
||||
import functools
|
||||
import logging
|
||||
import copy
|
||||
from .constants import (
|
||||
M_OVERRIDEN_KEY,
|
||||
M_ENVIRONMENT_KEY,
|
||||
M_POP_KEY,
|
||||
|
||||
METADATA_KEYS,
|
||||
|
||||
SYSTEM_SETTINGS_KEY,
|
||||
PROJECT_SETTINGS_KEY,
|
||||
PROJECT_ANATOMY_KEY
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Py2 + Py3 json decode exception
|
||||
JSON_EXC = getattr(json.decoder, "JSONDecodeError", ValueError)
|
||||
|
||||
# Metadata keys for work with studio and project overrides
|
||||
M_OVERRIDEN_KEY = "__overriden_keys__"
|
||||
# Metadata key for storing information about environments
|
||||
M_ENVIRONMENT_KEY = "__environment_keys__"
|
||||
# Metadata key for storing dynamic created labels
|
||||
M_DYNAMIC_KEY_LABEL = "__dynamic_keys_labels__"
|
||||
# NOTE key popping not implemented yet
|
||||
M_POP_KEY = "__pop_key__"
|
||||
|
||||
METADATA_KEYS = (
|
||||
M_OVERRIDEN_KEY,
|
||||
M_ENVIRONMENT_KEY,
|
||||
M_DYNAMIC_KEY_LABEL,
|
||||
M_POP_KEY
|
||||
)
|
||||
|
||||
# Folder where studio overrides are stored
|
||||
STUDIO_OVERRIDES_PATH = os.getenv("PYPE_PROJECT_CONFIGS") or ""
|
||||
|
||||
# File where studio's system overrides are stored
|
||||
SYSTEM_SETTINGS_KEY = "system_settings"
|
||||
SYSTEM_SETTINGS_PATH = os.path.join(
|
||||
STUDIO_OVERRIDES_PATH, SYSTEM_SETTINGS_KEY + ".json"
|
||||
)
|
||||
|
||||
# File where studio's environment overrides are stored
|
||||
ENVIRONMENTS_KEY = "environments"
|
||||
|
||||
# File where studio's default project overrides are stored
|
||||
PROJECT_SETTINGS_KEY = "project_settings"
|
||||
PROJECT_SETTINGS_FILENAME = PROJECT_SETTINGS_KEY + ".json"
|
||||
PROJECT_SETTINGS_PATH = os.path.join(
|
||||
STUDIO_OVERRIDES_PATH, PROJECT_SETTINGS_FILENAME
|
||||
)
|
||||
|
||||
PROJECT_ANATOMY_KEY = "project_anatomy"
|
||||
PROJECT_ANATOMY_FILENAME = PROJECT_ANATOMY_KEY + ".json"
|
||||
PROJECT_ANATOMY_PATH = os.path.join(
|
||||
STUDIO_OVERRIDES_PATH, PROJECT_ANATOMY_FILENAME
|
||||
)
|
||||
|
||||
# Path to default settings
|
||||
DEFAULTS_DIR = os.path.join(os.path.dirname(__file__), "defaults")
|
||||
DEFAULTS_DIR = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"defaults"
|
||||
)
|
||||
|
||||
# Variable where cache of default settings are stored
|
||||
_DEFAULT_SETTINGS = None
|
||||
|
||||
# Handler of studio overrides
|
||||
_SETTINGS_HANDLER = None
|
||||
|
||||
|
||||
def require_handler(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
global _SETTINGS_HANDLER
|
||||
if _SETTINGS_HANDLER is None:
|
||||
_SETTINGS_HANDLER = create_settings_handler()
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
def create_settings_handler():
|
||||
from .handlers import MongoSettingsHandler
|
||||
# Handler can't be created in global space on initialization but only when
|
||||
# needed. Plus here may be logic: Which handler is used (in future).
|
||||
return MongoSettingsHandler()
|
||||
|
||||
|
||||
@require_handler
|
||||
def save_studio_settings(data):
|
||||
return _SETTINGS_HANDLER.save_studio_settings(data)
|
||||
|
||||
|
||||
@require_handler
|
||||
def save_project_settings(project_name, overrides):
|
||||
return _SETTINGS_HANDLER.save_project_settings(project_name, overrides)
|
||||
|
||||
|
||||
@require_handler
|
||||
def save_project_anatomy(project_name, anatomy_data):
|
||||
return _SETTINGS_HANDLER.save_project_anatomy(project_name, anatomy_data)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_studio_system_settings_overrides():
|
||||
return _SETTINGS_HANDLER.get_studio_system_settings_overrides()
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_studio_project_settings_overrides():
|
||||
return _SETTINGS_HANDLER.get_studio_project_settings_overrides()
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_studio_project_anatomy_overrides():
|
||||
return _SETTINGS_HANDLER.get_studio_project_anatomy_overrides()
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_project_settings_overrides(project_name):
|
||||
return _SETTINGS_HANDLER.get_project_settings_overrides(project_name)
|
||||
|
||||
|
||||
@require_handler
|
||||
def get_project_anatomy_overrides(project_name):
|
||||
return _SETTINGS_HANDLER.get_project_anatomy_overrides(project_name)
|
||||
|
||||
|
||||
class DuplicatedEnvGroups(Exception):
|
||||
def __init__(self, duplicated):
|
||||
|
|
@ -80,10 +115,12 @@ def reset_default_settings():
|
|||
|
||||
|
||||
def get_default_settings():
|
||||
global _DEFAULT_SETTINGS
|
||||
if _DEFAULT_SETTINGS is None:
|
||||
_DEFAULT_SETTINGS = load_jsons_from_dir(DEFAULTS_DIR)
|
||||
return copy.deepcopy(_DEFAULT_SETTINGS)
|
||||
# TODO add cacher
|
||||
return load_jsons_from_dir(DEFAULTS_DIR)
|
||||
# global _DEFAULT_SETTINGS
|
||||
# if _DEFAULT_SETTINGS is None:
|
||||
# _DEFAULT_SETTINGS = load_jsons_from_dir(DEFAULTS_DIR)
|
||||
# return copy.deepcopy(_DEFAULT_SETTINGS)
|
||||
|
||||
|
||||
def load_json_file(fpath):
|
||||
|
|
@ -246,150 +283,6 @@ def subkey_merge(_dict, value, keys):
|
|||
return _dict
|
||||
|
||||
|
||||
def path_to_project_settings(project_name):
|
||||
if not project_name:
|
||||
return PROJECT_SETTINGS_PATH
|
||||
return os.path.join(
|
||||
STUDIO_OVERRIDES_PATH,
|
||||
project_name,
|
||||
PROJECT_SETTINGS_FILENAME
|
||||
)
|
||||
|
||||
|
||||
def path_to_project_anatomy(project_name):
|
||||
if not project_name:
|
||||
return PROJECT_ANATOMY_PATH
|
||||
return os.path.join(
|
||||
STUDIO_OVERRIDES_PATH,
|
||||
project_name,
|
||||
PROJECT_ANATOMY_FILENAME
|
||||
)
|
||||
|
||||
|
||||
def save_studio_settings(data):
|
||||
"""Save studio overrides of system settings.
|
||||
|
||||
Do not use to store whole system settings data with defaults but only it's
|
||||
overrides with metadata defining how overrides should be applied in load
|
||||
function. For loading should be used function `studio_system_settings`.
|
||||
|
||||
Args:
|
||||
data(dict): Data of studio overrides with override metadata.
|
||||
"""
|
||||
dirpath = os.path.dirname(SYSTEM_SETTINGS_PATH)
|
||||
if not os.path.exists(dirpath):
|
||||
os.makedirs(dirpath)
|
||||
|
||||
print("Saving studio overrides. Output path: {}".format(
|
||||
SYSTEM_SETTINGS_PATH
|
||||
))
|
||||
with open(SYSTEM_SETTINGS_PATH, "w") as file_stream:
|
||||
json.dump(data, file_stream, indent=4)
|
||||
|
||||
|
||||
def save_project_settings(project_name, overrides):
|
||||
"""Save studio overrides of project settings.
|
||||
|
||||
Data are saved for specific project or as defaults for all projects.
|
||||
|
||||
Do not use to store whole project settings data with defaults but only it's
|
||||
overrides with metadata defining how overrides should be applied in load
|
||||
function. For loading should be used function
|
||||
`get_studio_project_settings_overrides` for global project settings
|
||||
and `get_project_settings_overrides` for project specific settings.
|
||||
|
||||
Args:
|
||||
project_name(str, null): Project name for which overrides are
|
||||
or None for global settings.
|
||||
data(dict): Data of project overrides with override metadata.
|
||||
"""
|
||||
project_overrides_json_path = path_to_project_settings(project_name)
|
||||
dirpath = os.path.dirname(project_overrides_json_path)
|
||||
if not os.path.exists(dirpath):
|
||||
os.makedirs(dirpath)
|
||||
|
||||
print("Saving overrides of project \"{}\". Output path: {}".format(
|
||||
project_name, project_overrides_json_path
|
||||
))
|
||||
with open(project_overrides_json_path, "w") as file_stream:
|
||||
json.dump(overrides, file_stream, indent=4)
|
||||
|
||||
|
||||
def save_project_anatomy(project_name, anatomy_data):
|
||||
"""Save studio overrides of project anatomy data.
|
||||
|
||||
Args:
|
||||
project_name(str, null): Project name for which overrides are
|
||||
or None for global settings.
|
||||
data(dict): Data of project overrides with override metadata.
|
||||
"""
|
||||
project_anatomy_json_path = path_to_project_anatomy(project_name)
|
||||
dirpath = os.path.dirname(project_anatomy_json_path)
|
||||
if not os.path.exists(dirpath):
|
||||
os.makedirs(dirpath)
|
||||
|
||||
print("Saving anatomy of project \"{}\". Output path: {}".format(
|
||||
project_name, project_anatomy_json_path
|
||||
))
|
||||
with open(project_anatomy_json_path, "w") as file_stream:
|
||||
json.dump(anatomy_data, file_stream, indent=4)
|
||||
|
||||
|
||||
def get_studio_system_settings_overrides():
|
||||
"""Studio overrides of system settings."""
|
||||
if os.path.exists(SYSTEM_SETTINGS_PATH):
|
||||
return load_json_file(SYSTEM_SETTINGS_PATH)
|
||||
return {}
|
||||
|
||||
|
||||
def get_studio_project_settings_overrides():
|
||||
"""Studio overrides of default project settings."""
|
||||
if os.path.exists(PROJECT_SETTINGS_PATH):
|
||||
return load_json_file(PROJECT_SETTINGS_PATH)
|
||||
return {}
|
||||
|
||||
|
||||
def get_studio_project_anatomy_overrides():
|
||||
"""Studio overrides of default project anatomy data."""
|
||||
if os.path.exists(PROJECT_ANATOMY_PATH):
|
||||
return load_json_file(PROJECT_ANATOMY_PATH)
|
||||
return {}
|
||||
|
||||
|
||||
def get_project_settings_overrides(project_name):
|
||||
"""Studio overrides of project settings for specific project.
|
||||
|
||||
Args:
|
||||
project_name(str): Name of project for which data should be loaded.
|
||||
|
||||
Returns:
|
||||
dict: Only overrides for entered project, may be empty dictionary.
|
||||
"""
|
||||
|
||||
path_to_json = path_to_project_settings(project_name)
|
||||
if not os.path.exists(path_to_json):
|
||||
return {}
|
||||
return load_json_file(path_to_json)
|
||||
|
||||
|
||||
def get_project_anatomy_overrides(project_name):
|
||||
"""Studio overrides of project anatomy for specific project.
|
||||
|
||||
Args:
|
||||
project_name(str): Name of project for which data should be loaded.
|
||||
|
||||
Returns:
|
||||
dict: Only overrides for entered project, may be empty dictionary.
|
||||
"""
|
||||
if not project_name:
|
||||
return {}
|
||||
|
||||
path_to_json = path_to_project_anatomy(project_name)
|
||||
if not os.path.exists(path_to_json):
|
||||
return {}
|
||||
return load_json_file(path_to_json)
|
||||
|
||||
|
||||
def merge_overrides(source_dict, override_dict):
|
||||
"""Merge data from override_dict to source_dict."""
|
||||
|
||||
|
|
@ -459,7 +352,9 @@ def get_anatomy_settings(project_name, clear_metadata=True):
|
|||
)
|
||||
|
||||
studio_overrides = get_default_anatomy_settings(False)
|
||||
project_overrides = get_project_anatomy_overrides(project_name)
|
||||
project_overrides = get_project_anatomy_overrides(
|
||||
project_name
|
||||
)
|
||||
|
||||
result = apply_overrides(studio_overrides, project_overrides)
|
||||
if clear_metadata:
|
||||
|
|
@ -476,7 +371,9 @@ def get_project_settings(project_name, clear_metadata=True):
|
|||
)
|
||||
|
||||
studio_overrides = get_default_project_settings(False)
|
||||
project_overrides = get_project_settings_overrides(project_name)
|
||||
project_overrides = get_project_settings_overrides(
|
||||
project_name
|
||||
)
|
||||
|
||||
result = apply_overrides(studio_overrides, project_overrides)
|
||||
if clear_metadata:
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@ from Qt import QtWidgets, QtGui
|
|||
|
||||
if __name__ == "__main__":
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
|
||||
stylesheet = settings.style.load_stylesheet()
|
||||
app.setStyleSheet(stylesheet)
|
||||
app.setWindowIcon(QtGui.QIcon(settings.style.app_icon_path()))
|
||||
|
||||
_develop = "-d" in sys.argv or "--develop" in sys.argv
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"type": "dict",
|
||||
"key": "Ftrack",
|
||||
"key": "ftrack",
|
||||
"label": "Ftrack",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
"is_file": true,
|
||||
"children": [{
|
||||
"type": "dict",
|
||||
"key": "Avalon",
|
||||
"key": "avalon",
|
||||
"label": "Avalon",
|
||||
"collapsable": true,
|
||||
"children": [{
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"key": "Rest Api",
|
||||
"key": "rest_api",
|
||||
"label": "Rest Api",
|
||||
"collapsable": true,
|
||||
"children": [{
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Timers Manager",
|
||||
"key": "timers_manager",
|
||||
"label": "Timers Manager",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
|
|
@ -86,7 +86,7 @@
|
|||
]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Clockify",
|
||||
"key": "clockify",
|
||||
"label": "Clockify",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
|
|
@ -144,7 +144,7 @@
|
|||
}]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Logging",
|
||||
"key": "log_viewer",
|
||||
"label": "Logging",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
|
|
@ -155,7 +155,7 @@
|
|||
}]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "User setting",
|
||||
"key": "user",
|
||||
"label": "User setting",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
|
|
@ -166,7 +166,7 @@
|
|||
}]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Standalone Publish",
|
||||
"key": "standalonepublish_tool",
|
||||
"label": "Standalone Publish",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
|
|
@ -177,7 +177,7 @@
|
|||
}]
|
||||
}, {
|
||||
"type": "dict",
|
||||
"key": "Idle Manager",
|
||||
"key": "idle_manager",
|
||||
"label": "Idle Manager",
|
||||
"collapsable": true,
|
||||
"checkbox_key": "enabled",
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@ import os
|
|||
import copy
|
||||
import json
|
||||
from Qt import QtWidgets, QtCore, QtGui
|
||||
from pype.settings.lib import (
|
||||
from pype.settings.constants import (
|
||||
SYSTEM_SETTINGS_KEY,
|
||||
PROJECT_SETTINGS_KEY,
|
||||
PROJECT_ANATOMY_KEY,
|
||||
PROJECT_ANATOMY_KEY
|
||||
)
|
||||
|
||||
from pype.settings.lib import (
|
||||
DEFAULTS_DIR,
|
||||
|
||||
reset_default_settings,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import os
|
|||
import re
|
||||
import json
|
||||
import copy
|
||||
from pype.settings.lib import (
|
||||
from pype.settings.constants import (
|
||||
M_OVERRIDEN_KEY,
|
||||
M_ENVIRONMENT_KEY,
|
||||
M_DYNAMIC_KEY_LABEL
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from Qt import QtWidgets
|
||||
from Qt import QtWidgets, QtGui
|
||||
from .base import SystemWidget, ProjectWidget
|
||||
from .. import style
|
||||
|
||||
|
||||
class MainWidget(QtWidgets.QWidget):
|
||||
|
|
@ -13,6 +14,10 @@ class MainWidget(QtWidgets.QWidget):
|
|||
|
||||
self.resize(self.widget_width, self.widget_height)
|
||||
|
||||
stylesheet = style.load_stylesheet()
|
||||
self.setStyleSheet(stylesheet)
|
||||
self.setWindowIcon(QtGui.QIcon(style.app_icon_path()))
|
||||
|
||||
header_tab_widget = QtWidgets.QTabWidget(parent=self)
|
||||
|
||||
studio_widget = SystemWidget(user_role, header_tab_widget)
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ class Window(QtWidgets.QDialog):
|
|||
|
||||
# signals
|
||||
widget_assets.selection_changed.connect(self.on_asset_changed)
|
||||
widget_assets.project_changed.connect(self.on_project_change)
|
||||
widget_family.stateChanged.connect(self.set_valid_family)
|
||||
|
||||
self.widget_assets = widget_assets
|
||||
|
|
@ -116,6 +117,9 @@ class Window(QtWidgets.QDialog):
|
|||
parents.append(parent['name'])
|
||||
return parents
|
||||
|
||||
def on_project_change(self, project_name):
|
||||
self.widget_family.refresh()
|
||||
|
||||
def on_asset_changed(self):
|
||||
'''Callback on asset selection changed
|
||||
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
|
||||
"""
|
||||
|
||||
project_changed = QtCore.Signal(str)
|
||||
assets_refreshed = QtCore.Signal() # on model refresh
|
||||
selection_changed = QtCore.Signal() # on view selection change
|
||||
current_changed = QtCore.Signal() # on view current index change
|
||||
|
|
@ -249,6 +250,9 @@ class AssetWidget(QtWidgets.QWidget):
|
|||
project_name = self.combo_projects.currentText()
|
||||
if project_name in projects:
|
||||
self.dbcon.Session["AVALON_PROJECT"] = project_name
|
||||
|
||||
self.project_changed.emit(project_name)
|
||||
|
||||
self.refresh()
|
||||
|
||||
def _refresh_model(self):
|
||||
|
|
|
|||
|
|
@ -285,7 +285,10 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
self.schedule(self._on_data_changed, 500, channel="gui")
|
||||
|
||||
def on_selection_changed(self, *args):
|
||||
plugin = self.list_families.currentItem().data(PluginRole)
|
||||
item = self.list_families.currentItem()
|
||||
if not item:
|
||||
return
|
||||
plugin = item.data(PluginRole)
|
||||
if plugin is None:
|
||||
return
|
||||
|
||||
|
|
@ -309,10 +312,15 @@ class FamilyWidget(QtWidgets.QWidget):
|
|||
"""
|
||||
|
||||
def refresh(self):
|
||||
self.list_families.clear()
|
||||
|
||||
has_families = False
|
||||
settings = get_project_settings(os.environ['AVALON_PROJECT'])
|
||||
project_name = self.dbcon.Session.get("AVALON_PROJECT")
|
||||
if not project_name:
|
||||
return
|
||||
|
||||
settings = get_project_settings(project_name)
|
||||
sp_settings = settings.get('standalonepublisher', {})
|
||||
print(sp_settings)
|
||||
|
||||
for key, creator in sp_settings.get("create", {}).items():
|
||||
if key == "__dynamic_keys_labels__":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue