SyncServer - change settings to new format

Sites are configured in System
Schemas and defaults were modified
All providers carry dict of modifiable properties for Local Settings
This commit is contained in:
Petr Kalis 2021-05-11 14:49:13 +02:00
parent 49e44f0d70
commit 1aafb697e4
11 changed files with 212 additions and 127 deletions

View file

@ -27,37 +27,16 @@ class AbstractProvider:
(boolean)
"""
@classmethod
@abc.abstractmethod
def set_editable_properties(self):
def get_configurable_items(cls):
"""
Sets dictionary of editable properties with scopes.
Returns filtered dict of editable properties
Example:
{ 'credentials_url': {'scopes': [utils.EditableScopes.SYSTEM],
'type': 'text'}}
"""
@abc.abstractmethod
def get_editable_properties(self, scopes):
"""
Returns filtered list of editable properties
Args:
scopes (list) of utils.EditableScopes (optional - filter on)
Returns:
(dict)
"""
if not scopes:
return self._editable_properties
editable = {}
for scope in scopes:
for key, properties in self._editable_properties.items():
if scope in properties['scope']:
editable[key] = properties
return editable
@abc.abstractmethod
def upload_file(self, source_path, path,

View file

@ -1,22 +1,32 @@
from __future__ import print_function
import os.path
from googleapiclient.discovery import build
import google.oauth2.service_account as service_account
from googleapiclient import errors
from .abstract_provider import AbstractProvider
from googleapiclient.http import MediaFileUpload, MediaIoBaseDownload
import time
import sys
from setuptools.extern import six
from openpype.api import Logger
from openpype.api import get_system_settings
from .abstract_provider import AbstractProvider
from ..utils import time_function, ResumableError, EditableScopes
import time
log = Logger().get_logger("SyncServer")
try:
from googleapiclient.discovery import build
import google.oauth2.service_account as service_account
from googleapiclient import errors
from googleapiclient.http import MediaFileUpload, MediaIoBaseDownload
except (ImportError, SyntaxError):
if six.PY3:
six.reraise(*sys.exc_info())
# handle imports from Python 2 hosts - in those only basic methods are used
log.warning("Import failed, imported from Python 2, operations will fail.")
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.readonly'] # for write|delete
log = Logger().get_logger("SyncServer")
class GDriveHandler(AbstractProvider):
"""
@ -54,8 +64,7 @@ class GDriveHandler(AbstractProvider):
self.active = False
self.project_name = project_name
self.site_name = site_name
self._editable_properties = {}
self.service = None
self.presets = presets
if not self.presets:
@ -63,7 +72,7 @@ class GDriveHandler(AbstractProvider):
format(site_name))
return
if not os.path.exists(self.presets["credentials_url"]):
if not os.path.exists(self.presets.get("credentials_url", "")):
log.info("Sync Server: No credentials for Gdrive provider! ")
return
@ -78,7 +87,6 @@ class GDriveHandler(AbstractProvider):
self._tree = tree
self.active = True
self.set_editable_properties()
def is_active(self):
"""
@ -86,18 +94,29 @@ class GDriveHandler(AbstractProvider):
Returns:
(boolean)
"""
return self.active
return self.service is not None
def set_editable_properties(self):
@classmethod
def get_configurable_items(cls):
"""
Returns filtered dict of editable properties.
Returns:
(dict)
"""
editable = {
# credentials could be override on Project or User level
'credential_url': {'scope': [EditableScopes.PROJECT,
EditableScopes.LOCAL],
'label': "Credentials url",
'type': 'text'},
'roots': {'scope': [EditableScopes.PROJECT],
'type': 'dict'}
# roots could be override only on Project leve, User cannot
'root': {'scope': [EditableScopes.PROJECT],
'label': "Roots",
'type': 'dict'}
}
self._editable_properties = editable
return editable
def get_roots_config(self, anatomy=None):
"""

View file

@ -65,6 +65,17 @@ class ProviderFactory:
info = self._get_creator_info(provider)
return info[1]
def get_provider_configurable_items(self, provider):
"""
Returns dict of modifiable properties for 'provider'.
Provider contains information which its properties and on what
level could be override
"""
provider_info = self._get_creator_info(provider)
return provider_info[0].get_configurable_items()
def _get_creator_info(self, provider):
"""
Collect all necessary info for provider. Currently only creator

View file

@ -25,18 +25,25 @@ class LocalDriveHandler(AbstractProvider):
self._editable_properties = {}
self.active = self.is_active()
self.set_editable_properties()
def is_active(self):
return True
def set_editable_properties(self):
@classmethod
def get_configurable_items(cls):
"""
Returns filtered dict of editable properties
Returns:
(dict)
"""
editable = {
'roots': {'scope': [EditableScopes.PROJECT,
EditableScopes.LOCAL],
'type': 'dict'}
'root': {'scope': [EditableScopes.PROJECT,
EditableScopes.LOCAL],
'label': "Roots",
'type': 'dict'}
}
self._editable_properties = editable
return editable
def upload_file(self, source_path, target_path,
server, collection, file, representation, site,

View file

@ -9,10 +9,12 @@ from .. import PypeModule, ITrayModule
from openpype.api import (
Anatomy,
get_project_settings,
get_system_settings,
get_local_site_id)
from openpype.lib import PypeLogger
from .providers.local_drive import LocalDriveHandler
from .providers import lib
from .utils import time_function, SyncStatus
@ -390,31 +392,99 @@ class SyncServerModule(PypeModule, ITrayModule):
return remote_site
def get_configurable_items(self):
pass
def get_configurable_items_for_site(self, project_name, site_name):
def get_configurable_items(self, scope=None):
"""
Returns list of items that should be configurable by User
Returns list of items that could be configurable for all projects.
Could be filtered by 'scope' argument (list)
Args:
scope (list of utils.EditableScope) (optional)
Returns:
(list of dict)
[{key:"root", label:"root", value:"valueFromSettings"}]
(dict of dict)
{projectA: {
siteA : {
key:"root", label:"root", value:"valueFromSettings"
}
}
"""
# sites = set(self.get_active_sites(project_name),
# self.get_remote_sites(project_name))
# for site in sites:
# if site_name
def _get_configurable_items_for_project(self, project_name):
from .providers import lib
sites = set(self.get_active_sites(project_name),
self.get_remote_sites(project_name))
editable = {}
for site in sites:
provider_name = self.get_provider_for_site(project_name, site)
for project in self.connection.projects():
project_name = project["name"]
items = self.get_configurable_items_for_project(project_name,
scope)
editable.update(items)
return editable
def get_configurable_items_for_project(self, project_name, scope=None):
"""
Returns list of items that could be configurable for specific
'project_name'
Args:
project_name (str)
scope (list of utils.EditableScope) (optional)
Returns:
(dict of dict)
{projectA: {
siteA : {
key:"root", label:"root", value:"valueFromSettings"
}
}
"""
sites = set(self.get_active_sites(project_name)) | \
set(self.get_remote_sites(project_name))
editable = {}
for site_name in sites:
items = self.get_configurable_items_for_site(project_name,
site_name,
scope)
editable.update(items)
return editable
def get_configurable_items_for_site(self, project_name, site_name,
scope=None):
"""
Returns list of items that could be configurable.
Args:
project_name (str)
site_name (str)
scope (list of utils.EditableScope) (optional)
Returns:
(dict of dict)
{projectA: {
siteA : {
key:"root", label:"root", value:"valueFromSettings"
}
}
"""
provider_name = self.get_provider_for_site(site=site_name)
items = lib.factory.get_provider_configurable_items(provider_name,
scope)
if not scope:
return {project_name: {site_name: items}}
editable = {}
sync_s = self.get_sync_project_setting(project_name, True)
for scope in set([scope]):
for key, properties in items.items():
if scope in properties['scope']:
val = sync_s.get("sites", {}).get(site_name, {}).get(key)
editable = {
"key": key,
"value": val,
"label": properties.get("label"),
"type": properties.get("type"),
}
return {project_name: {site_name: editable}}
def reset_timer(self):
"""
@ -432,7 +502,7 @@ class SyncServerModule(PypeModule, ITrayModule):
for project in self.connection.projects():
project_name = project["name"]
project_settings = self.get_sync_project_setting(project_name)
if project_settings:
if project_settings and project_settings.get("enabled"):
enabled_projects.append(project_name)
return enabled_projects
@ -584,55 +654,60 @@ class SyncServerModule(PypeModule, ITrayModule):
return self._sync_project_settings
def set_sync_project_settings(self):
def set_sync_project_settings(self, exclude_locals=False):
"""
Set sync_project_settings for all projects (caching)
Args:
exclude_locals (bool): ignore overrides from Local Settings
For performance
"""
sync_project_settings = {}
# sites are now configured system wide
sys_sett = get_system_settings()
sync_sett = sys_sett["modules"].get("sync_server")
system_sites = {}
for site, detail in sync_sett.get("sites", {}).items():
system_sites[site] = detail
for collection in self.connection.database.collection_names(False):
sync_settings = self._parse_sync_settings_from_settings(
get_project_settings(collection))
if sync_settings:
default_sites = self._get_default_site_configs()
sync_settings['sites'].update(default_sites)
sync_project_settings[collection] = sync_settings
get_project_settings(collection,
exclude_locals=exclude_locals))
default_sites = self._get_default_site_configs()
sync_settings['sites'].update(default_sites)
sync_settings['sites'].update(system_sites)
sync_project_settings[collection] = sync_settings
if not sync_project_settings:
log.info("No enabled and configured projects for sync.")
self._sync_project_settings = sync_project_settings
def get_sync_project_setting(self, project_name):
def get_sync_project_setting(self, project_name, exclude_locals=False):
""" Handles pulling sync_server's settings for enabled 'project_name'
Args:
project_name (str): used in project settings
exclude_locals (bool): ignore overrides from Local Settings
Returns:
(dict): settings dictionary for the enabled project,
empty if no settings or sync is disabled
"""
# presets set already, do not call again and again
# self.log.debug("project preset {}".format(self.presets))
if self.sync_project_settings and \
self.sync_project_settings.get(project_name):
return self.sync_project_settings.get(project_name)
if not self.sync_project_settings or \
not self.sync_project_settings.get(project_name):
self.set_sync_project_settings(project_name, exclude_locals)
settings = get_project_settings(project_name)
return self._parse_sync_settings_from_settings(settings)
return self.sync_project_settings.get(project_name)
def _parse_sync_settings_from_settings(self, settings):
""" settings from api.get_project_settings, TOOD rename """
sync_settings = settings.get("global").get("sync_server")
if not sync_settings:
log.info("No project setting not syncing.")
return {}
if sync_settings.get("enabled"):
return sync_settings
return {}
return sync_settings
def _get_default_site_configs(self):
"""
@ -643,16 +718,29 @@ class SyncServerModule(PypeModule, ITrayModule):
get_local_site_id(): default_config}
return all_sites
def get_provider_for_site(self, project_name, site):
def get_provider_for_site(self, project_name=None, site=None):
"""
Return provider name for site.
Return provider name for site (unique name across all projects.
"""
site_preset = self.get_sync_project_setting(project_name)["sites"].\
get(site)
if site_preset:
return site_preset["provider"]
sites = {self.DEFAULT_SITE: "local_drive",
self.LOCAL_SITE: "local_drive",
get_local_site_id(): "local_drive"}
return "NA"
if site in sites.keys():
return sites[site]
if project_name: # backward compatibility
proj_settings = self.get_sync_project_setting(project_name)
provider = proj_settings.get("sites", {}).get(site, {}).\
get("provider")
return provider
sys_sett = get_system_settings()
sync_sett = sys_sett["modules"].get("sync_server")
for site, detail in sync_sett.get("sites", {}).items():
sites[site] = detail.get("provider")
return sites.get(site, 'N/A')
@time_function
def get_sync_representations(self, collection, active_site, remote_site):
@ -1130,7 +1218,7 @@ class SyncServerModule(PypeModule, ITrayModule):
format(site_name))
return
provider_name = self.get_provider_for_site(collection, site_name)
provider_name = self.get_provider_for_site(site=site_name)
if provider_name == 'local_drive':
query = {

View file

@ -158,7 +158,7 @@ def translate_provider_for_icon(sync_server, project, site):
"""
if site == sync_server.DEFAULT_SITE:
return sync_server.DEFAULT_SITE
return sync_server.get_provider_for_site(project, site)
return sync_server.get_provider_for_site(site=site)
def get_item_by_id(model, object_id):

View file

@ -236,7 +236,7 @@ class _SyncRepresentationWidget(QtWidgets.QWidget):
for site, progress in {active_site: local_progress,
remote_site: remote_progress}.items():
provider = self.sync_server.get_provider_for_site(project, site)
provider = self.sync_server.get_provider_for_site(site=site)
if provider == 'local_drive':
if 'studio' in site:
txt = " studio version"

View file

@ -267,13 +267,6 @@
"remote_site": "studio"
},
"sites": {
"gdrive": {
"provider": "gdrive",
"credentials_url": "",
"root": {
"work": ""
}
}
}
},
"project_plugins": {

View file

@ -135,7 +135,8 @@
"workspace_name": ""
},
"sync_server": {
"enabled": false
"enabled": false,
"sites": {}
},
"deadline": {
"enabled": true,

View file

@ -231,21 +231,17 @@ class ProvidersEnum(BaseEnumEntity):
self.placeholder = None
def _get_enum_values(self):
# from openpype.modules.sync_server.providers import lib as lib_providers
#
# providers = lib_providers.factory.providers
#
# valid_keys = set()
# enum_items = []
# for provider_code, provider_info in providers.items():
# provider, _ = provider_info
# enum_items.append({provider_code: provider.LABEL})
# valid_keys.add(provider_code)
from openpype.modules.sync_server.providers import lib as lib_providers
providers = lib_providers.factory.providers
valid_keys = set()
enum_items = []
if not valid_keys:
enum_items.append({'': 'N/A'})
valid_keys.add('')
valid_keys.add('')
enum_items = [{'': 'Choose Provider'}]
for provider_code, provider_info in providers.items():
provider, _ = provider_info
enum_items.append({provider_code: provider.LABEL})
valid_keys.add(provider_code)
return enum_items, valid_keys
@ -259,7 +255,3 @@ class ProvidersEnum(BaseEnumEntity):
self._current_value = value_on_not_set
self.value_on_not_set = value_on_not_set
# class ActiveSiteEnum
# class RemoteSiteEnum

View file

@ -49,11 +49,6 @@
{
"type": "dict",
"children": [
{
"type": "text",
"key": "provider",
"label": "Provider"
},
{
"type": "text",
"key": "credentials_url",