Add-on: cache manager info in a JSON file
Instead of storing the cached manager info in the Blender preferences, store the info in a JSON file. The file is located in the user prefs folder (`~/.config/blender/{version}/config` on Linux). This also reduces the number of 'refresh' operators to a single one, which then fetches all necessary info from the Manager. This fixes an issue (reported via chat) where worker tags were sometimes not retained across file saves.
This commit is contained in:
parent
a4e5eef83e
commit
3b4da656c9
@ -27,6 +27,7 @@ if __is_first_load:
|
|||||||
preferences,
|
preferences,
|
||||||
projects,
|
projects,
|
||||||
worker_tags,
|
worker_tags,
|
||||||
|
manager_info,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
import importlib
|
import importlib
|
||||||
@ -38,6 +39,7 @@ else:
|
|||||||
preferences = importlib.reload(preferences)
|
preferences = importlib.reload(preferences)
|
||||||
projects = importlib.reload(projects)
|
projects = importlib.reload(projects)
|
||||||
worker_tags = importlib.reload(worker_tags)
|
worker_tags = importlib.reload(worker_tags)
|
||||||
|
manager_info = importlib.reload(manager_info)
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
@ -160,6 +162,9 @@ def register() -> None:
|
|||||||
gui.register()
|
gui.register()
|
||||||
job_types.register()
|
job_types.register()
|
||||||
|
|
||||||
|
# Once everything is registered, load the cached manager info from JSON.
|
||||||
|
manager_info.load_into_cache()
|
||||||
|
|
||||||
|
|
||||||
def unregister() -> None:
|
def unregister() -> None:
|
||||||
discard_global_flamenco_data(None)
|
discard_global_flamenco_data(None)
|
||||||
|
@ -3,13 +3,12 @@
|
|||||||
# <pep8 compliant>
|
# <pep8 compliant>
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import dataclasses
|
from typing import TYPE_CHECKING
|
||||||
import platform
|
|
||||||
from typing import TYPE_CHECKING, Optional
|
|
||||||
|
|
||||||
from urllib3.exceptions import HTTPError, MaxRetryError
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
|
from flamenco import manager_info, job_types
|
||||||
|
|
||||||
_flamenco_client = None
|
_flamenco_client = None
|
||||||
_log = logging.getLogger(__name__)
|
_log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -27,23 +26,6 @@ else:
|
|||||||
_SharedStorageLocation = object
|
_SharedStorageLocation = object
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(frozen=True)
|
|
||||||
class ManagerInfo:
|
|
||||||
version: Optional[_FlamencoVersion] = None
|
|
||||||
storage: Optional[_SharedStorageLocation] = None
|
|
||||||
error: str = ""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def with_error(cls, error: str) -> "ManagerInfo":
|
|
||||||
return cls(error=error)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def with_info(
|
|
||||||
cls, version: _FlamencoVersion, storage: _SharedStorageLocation
|
|
||||||
) -> "ManagerInfo":
|
|
||||||
return cls(version=version, storage=storage)
|
|
||||||
|
|
||||||
|
|
||||||
def flamenco_api_client(manager_url: str) -> _ApiClient:
|
def flamenco_api_client(manager_url: str) -> _ApiClient:
|
||||||
"""Returns an API client for communicating with a Manager."""
|
"""Returns an API client for communicating with a Manager."""
|
||||||
global _flamenco_client
|
global _flamenco_client
|
||||||
@ -87,12 +69,12 @@ def discard_flamenco_data():
|
|||||||
_flamenco_client = None
|
_flamenco_client = None
|
||||||
|
|
||||||
|
|
||||||
def ping_manager_with_report(
|
def ping_manager(
|
||||||
window_manager: bpy.types.WindowManager,
|
window_manager: bpy.types.WindowManager,
|
||||||
|
scene: bpy.types.Scene,
|
||||||
api_client: _ApiClient,
|
api_client: _ApiClient,
|
||||||
prefs: _FlamencoPreferences,
|
|
||||||
) -> tuple[str, str]:
|
) -> tuple[str, str]:
|
||||||
"""Ping the Manager, update preferences, and return a report as string.
|
"""Fetch Manager info, and update the scene for it.
|
||||||
|
|
||||||
:returns: tuple (report, level). The report will be something like "<name>
|
:returns: tuple (report, level). The report will be something like "<name>
|
||||||
version <version> found", or an error message. The level will be
|
version <version> found", or an error message. The level will be
|
||||||
@ -100,55 +82,49 @@ def ping_manager_with_report(
|
|||||||
`Operator.report()`.
|
`Operator.report()`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
info = ping_manager(window_manager, api_client, prefs)
|
|
||||||
if info.error:
|
|
||||||
return info.error, "ERROR"
|
|
||||||
|
|
||||||
assert info.version is not None
|
|
||||||
report = "%s version %s found" % (info.version.name, info.version.version)
|
|
||||||
return report, "INFO"
|
|
||||||
|
|
||||||
|
|
||||||
def ping_manager(
|
|
||||||
window_manager: bpy.types.WindowManager,
|
|
||||||
api_client: _ApiClient,
|
|
||||||
prefs: _FlamencoPreferences,
|
|
||||||
) -> ManagerInfo:
|
|
||||||
"""Fetch Manager config & version, and update cached preferences."""
|
|
||||||
|
|
||||||
window_manager.flamenco_status_ping = "..."
|
window_manager.flamenco_status_ping = "..."
|
||||||
|
|
||||||
# Do a late import, so that the API is only imported when actually used.
|
# Remember the old values, as they may have disappeared from the Manager.
|
||||||
from flamenco.manager import ApiException
|
old_job_type_name = getattr(scene, "flamenco_job_type", "")
|
||||||
from flamenco.manager.apis import MetaApi
|
old_tag_name = getattr(scene, "flamenco_worker_tag", "")
|
||||||
from flamenco.manager.models import FlamencoVersion, SharedStorageLocation
|
|
||||||
|
|
||||||
meta_api = MetaApi(api_client)
|
|
||||||
error = ""
|
|
||||||
try:
|
try:
|
||||||
version: FlamencoVersion = meta_api.get_version()
|
info = manager_info.fetch(api_client)
|
||||||
storage: SharedStorageLocation = meta_api.get_shared_storage(
|
except manager_info.FetchError as ex:
|
||||||
"users", platform.system().lower()
|
report = str(ex)
|
||||||
)
|
window_manager.flamenco_status_ping = report
|
||||||
except ApiException as ex:
|
return report, "ERROR"
|
||||||
error = "Manager cannot be reached: %s" % ex
|
|
||||||
except MaxRetryError as ex:
|
|
||||||
# This is the common error, when for example the port number is
|
|
||||||
# incorrect and nothing is listening. The exception text is not included
|
|
||||||
# because it's very long and confusing.
|
|
||||||
error = "Manager cannot be reached"
|
|
||||||
except HTTPError as ex:
|
|
||||||
error = "Manager cannot be reached: %s" % ex
|
|
||||||
|
|
||||||
if error:
|
manager_info.save(info)
|
||||||
window_manager.flamenco_status_ping = error
|
|
||||||
return ManagerInfo.with_error(error)
|
|
||||||
|
|
||||||
# Store whether this Manager supports the Shaman API.
|
report = "%s version %s found" % (
|
||||||
prefs.is_shaman_enabled = storage.shaman_enabled
|
info.flamenco_version.name,
|
||||||
prefs.job_storage = storage.location
|
info.flamenco_version.version,
|
||||||
|
)
|
||||||
|
report_level = "INFO"
|
||||||
|
|
||||||
|
job_types.refresh_scene_properties(scene, info.job_types)
|
||||||
|
|
||||||
|
# Try to restore the old values.
|
||||||
|
#
|
||||||
|
# Since you cannot un-set an enum property, and 'empty string' is not a
|
||||||
|
# valid value either, when the old choice is no longer available we remove
|
||||||
|
# the underlying ID property.
|
||||||
|
if old_job_type_name:
|
||||||
|
try:
|
||||||
|
scene.flamenco_job_type = old_job_type_name
|
||||||
|
except TypeError: # Thrown when the old enum value no longer exists.
|
||||||
|
del scene["flamenco_job_type"]
|
||||||
|
report = f"Job type {old_job_type_name!r} no longer available, choose another one"
|
||||||
|
report_level = "WARNING"
|
||||||
|
|
||||||
|
if old_tag_name:
|
||||||
|
try:
|
||||||
|
scene.flamenco_worker_tag = old_tag_name
|
||||||
|
except TypeError: # Thrown when the old enum value no longer exists.
|
||||||
|
del scene["flamenco_worker_tag"]
|
||||||
|
report = f"Tag {old_tag_name!r} no longer available, choose another one"
|
||||||
|
report_level = "WARNING"
|
||||||
|
|
||||||
report = "%s version %s found" % (version.name, version.version)
|
|
||||||
window_manager.flamenco_status_ping = report
|
window_manager.flamenco_status_ping = report
|
||||||
|
return report, report_level
|
||||||
return ManagerInfo.with_info(version, storage)
|
|
||||||
|
@ -43,23 +43,19 @@ class FLAMENCO_PT_job_submission(bpy.types.Panel):
|
|||||||
col.prop(context.scene, "flamenco_job_name", text="Job Name")
|
col.prop(context.scene, "flamenco_job_name", text="Job Name")
|
||||||
col.prop(context.scene, "flamenco_job_priority", text="Priority")
|
col.prop(context.scene, "flamenco_job_priority", text="Priority")
|
||||||
|
|
||||||
# Worker tag:
|
# Refreshables:
|
||||||
row = col.row(align=True)
|
col = layout.column(align=True)
|
||||||
row.prop(context.scene, "flamenco_worker_tag", text="Tag")
|
col.operator(
|
||||||
row.operator("flamenco.fetch_worker_tags", text="", icon="FILE_REFRESH")
|
"flamenco.ping_manager", text="Refresh from Manager", icon="FILE_REFRESH"
|
||||||
|
)
|
||||||
layout.separator()
|
|
||||||
|
|
||||||
col = layout.column()
|
|
||||||
if not job_types.are_job_types_available():
|
if not job_types.are_job_types_available():
|
||||||
col.operator("flamenco.fetch_job_types", icon="FILE_REFRESH")
|
|
||||||
return
|
return
|
||||||
|
col.prop(context.scene, "flamenco_worker_tag", text="Tag")
|
||||||
|
|
||||||
row = col.row(align=True)
|
# Job properties:
|
||||||
row.prop(context.scene, "flamenco_job_type", text="")
|
job_col = layout.column(align=True)
|
||||||
row.operator("flamenco.fetch_job_types", text="", icon="FILE_REFRESH")
|
job_col.prop(context.scene, "flamenco_job_type", text="Job Type")
|
||||||
|
self.draw_job_settings(context, job_col)
|
||||||
self.draw_job_settings(context, layout.column(align=True))
|
|
||||||
|
|
||||||
layout.separator()
|
layout.separator()
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import bpy
|
|||||||
|
|
||||||
from .job_types_propgroup import JobTypePropertyGroup
|
from .job_types_propgroup import JobTypePropertyGroup
|
||||||
from .bat.submodules import bpathlib
|
from .bat.submodules import bpathlib
|
||||||
from . import preferences
|
from . import manager_info
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .manager import ApiClient as _ApiClient
|
from .manager import ApiClient as _ApiClient
|
||||||
@ -133,8 +133,11 @@ def is_file_inside_job_storage(context: bpy.types.Context, blendfile: Path) -> b
|
|||||||
|
|
||||||
blendfile = bpathlib.make_absolute(blendfile)
|
blendfile = bpathlib.make_absolute(blendfile)
|
||||||
|
|
||||||
prefs = preferences.get(context)
|
info = manager_info.load_cached()
|
||||||
job_storage = bpathlib.make_absolute(Path(prefs.job_storage))
|
if not info:
|
||||||
|
raise RuntimeError("Flamenco Manager info unknown, please refresh.")
|
||||||
|
|
||||||
|
job_storage = bpathlib.make_absolute(Path(info.shared_storage.location))
|
||||||
|
|
||||||
log.info("Checking whether the file is already inside the job storage")
|
log.info("Checking whether the file is already inside the job storage")
|
||||||
log.info(" file : %s", blendfile)
|
log.info(" file : %s", blendfile)
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
from typing import TYPE_CHECKING, Optional, Union
|
from typing import TYPE_CHECKING, Optional, Union
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
from . import job_types_propgroup
|
from . import job_types_propgroup, manager_info
|
||||||
|
|
||||||
_log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from flamenco.manager import ApiClient as _ApiClient
|
from flamenco.manager import ApiClient as _ApiClient
|
||||||
@ -39,24 +35,12 @@ _selected_job_type_propgroup: Optional[
|
|||||||
] = None
|
] = None
|
||||||
|
|
||||||
|
|
||||||
def fetch_available_job_types(api_client: _ApiClient, scene: bpy.types.Scene) -> None:
|
def refresh_scene_properties(
|
||||||
from flamenco.manager import ApiClient
|
scene: bpy.types.Scene, available_job_types: _AvailableJobTypes
|
||||||
from flamenco.manager.api import jobs_api
|
) -> None:
|
||||||
from flamenco.manager.model.available_job_types import AvailableJobTypes
|
|
||||||
|
|
||||||
assert isinstance(api_client, ApiClient)
|
|
||||||
|
|
||||||
job_api_instance = jobs_api.JobsApi(api_client)
|
|
||||||
response: AvailableJobTypes = job_api_instance.get_job_types()
|
|
||||||
|
|
||||||
_clear_available_job_types(scene)
|
_clear_available_job_types(scene)
|
||||||
|
_store_available_job_types(available_job_types)
|
||||||
# Store the response JSON on the scene. This is used when the blend file is
|
update_job_type_properties(scene)
|
||||||
# loaded (and thus the _available_job_types global variable is still empty)
|
|
||||||
# to generate the PropertyGroup of the selected job type.
|
|
||||||
scene.flamenco_available_job_types_json = json.dumps(response.to_dict())
|
|
||||||
|
|
||||||
_store_available_job_types(response)
|
|
||||||
|
|
||||||
|
|
||||||
def setting_is_visible(setting: _AvailableJobSetting) -> bool:
|
def setting_is_visible(setting: _AvailableJobSetting) -> bool:
|
||||||
@ -125,33 +109,6 @@ def _store_available_job_types(available_job_types: _AvailableJobTypes) -> None:
|
|||||||
_job_type_enum_items.insert(0, ("", "Select a Job Type", "", 0, 0))
|
_job_type_enum_items.insert(0, ("", "Select a Job Type", "", 0, 0))
|
||||||
|
|
||||||
|
|
||||||
def _available_job_types_from_json(job_types_json: str) -> None:
|
|
||||||
"""Convert JSON to AvailableJobTypes object, and update global variables for it."""
|
|
||||||
from flamenco.manager.models import AvailableJobTypes
|
|
||||||
from flamenco.manager.configuration import Configuration
|
|
||||||
from flamenco.manager.model_utils import validate_and_convert_types
|
|
||||||
|
|
||||||
json_dict = json.loads(job_types_json)
|
|
||||||
|
|
||||||
dummy_cfg = Configuration()
|
|
||||||
|
|
||||||
try:
|
|
||||||
job_types = validate_and_convert_types(
|
|
||||||
json_dict, (AvailableJobTypes,), ["job_types"], True, True, dummy_cfg
|
|
||||||
)
|
|
||||||
except TypeError:
|
|
||||||
_log.warn(
|
|
||||||
"Flamenco: could not restore cached job types, refresh them from Flamenco Manager"
|
|
||||||
)
|
|
||||||
_store_available_job_types(AvailableJobTypes(job_types=[]))
|
|
||||||
return
|
|
||||||
|
|
||||||
assert isinstance(
|
|
||||||
job_types, AvailableJobTypes
|
|
||||||
), "expected AvailableJobTypes, got %s" % type(job_types)
|
|
||||||
_store_available_job_types(job_types)
|
|
||||||
|
|
||||||
|
|
||||||
def are_job_types_available() -> bool:
|
def are_job_types_available() -> bool:
|
||||||
"""Returns whether job types have been fetched and are available."""
|
"""Returns whether job types have been fetched and are available."""
|
||||||
return bool(_job_type_enum_items)
|
return bool(_job_type_enum_items)
|
||||||
@ -199,7 +156,7 @@ def _clear_available_job_types(scene: bpy.types.Scene) -> None:
|
|||||||
_clear_job_type_propgroup()
|
_clear_job_type_propgroup()
|
||||||
|
|
||||||
_available_job_types = None
|
_available_job_types = None
|
||||||
_job_type_enum_items.clear()
|
_job_type_enum_items = []
|
||||||
scene.flamenco_available_job_types_json = ""
|
scene.flamenco_available_job_types_json = ""
|
||||||
|
|
||||||
|
|
||||||
@ -238,21 +195,21 @@ def _get_job_types_enum_items(dummy1, dummy2):
|
|||||||
|
|
||||||
|
|
||||||
@bpy.app.handlers.persistent
|
@bpy.app.handlers.persistent
|
||||||
def restore_available_job_types(dummy1, dummy2):
|
def restore_available_job_types(_filepath, _none):
|
||||||
scene = bpy.context.scene
|
scene = bpy.context.scene
|
||||||
job_types_json = getattr(scene, "flamenco_available_job_types_json", "")
|
info = manager_info.load_cached()
|
||||||
if not job_types_json:
|
if info is None:
|
||||||
_clear_available_job_types(scene)
|
_clear_available_job_types(scene)
|
||||||
return
|
return
|
||||||
_available_job_types_from_json(job_types_json)
|
refresh_scene_properties(scene, info.job_types)
|
||||||
update_job_type_properties(scene)
|
|
||||||
|
|
||||||
|
|
||||||
def discard_flamenco_data():
|
def discard_flamenco_data():
|
||||||
if _available_job_types:
|
global _available_job_types
|
||||||
_available_job_types.clear()
|
global _job_type_enum_items
|
||||||
if _job_type_enum_items:
|
|
||||||
_job_type_enum_items.clear()
|
_available_job_types = None
|
||||||
|
_job_type_enum_items = []
|
||||||
|
|
||||||
|
|
||||||
def register() -> None:
|
def register() -> None:
|
||||||
|
210
addon/flamenco/manager_info.py
Normal file
210
addon/flamenco/manager_info.py
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
# <pep8 compliant>
|
||||||
|
|
||||||
|
import dataclasses
|
||||||
|
import json
|
||||||
|
import platform
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
|
from urllib3.exceptions import HTTPError, MaxRetryError
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from flamenco.manager import ApiClient as _ApiClient
|
||||||
|
from flamenco.manager.models import (
|
||||||
|
AvailableJobTypes as _AvailableJobTypes,
|
||||||
|
FlamencoVersion as _FlamencoVersion,
|
||||||
|
SharedStorageLocation as _SharedStorageLocation,
|
||||||
|
WorkerTagList as _WorkerTagList,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_ApiClient = object
|
||||||
|
_AvailableJobTypes = object
|
||||||
|
_FlamencoVersion = object
|
||||||
|
_SharedStorageLocation = object
|
||||||
|
_WorkerTagList = object
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class ManagerInfo:
|
||||||
|
"""Cached information obtained from a Flamenco Manager.
|
||||||
|
|
||||||
|
This is the root object of what is stored on disk, every time someone
|
||||||
|
presses a 'refresh' button to update worker tags, job types, etc.
|
||||||
|
"""
|
||||||
|
|
||||||
|
flamenco_version: _FlamencoVersion
|
||||||
|
shared_storage: _SharedStorageLocation
|
||||||
|
job_types: _AvailableJobTypes
|
||||||
|
worker_tags: _WorkerTagList
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def type_info() -> dict[str, type]:
|
||||||
|
# Do a late import, so that the API is only imported when actually used.
|
||||||
|
from flamenco.manager.models import (
|
||||||
|
AvailableJobTypes,
|
||||||
|
FlamencoVersion,
|
||||||
|
SharedStorageLocation,
|
||||||
|
WorkerTagList,
|
||||||
|
)
|
||||||
|
|
||||||
|
# These types cannot be obtained by introspecting the ManagerInfo class, as
|
||||||
|
# at runtime that doesn't use real type annotations.
|
||||||
|
return {
|
||||||
|
"flamenco_version": FlamencoVersion,
|
||||||
|
"shared_storage": SharedStorageLocation,
|
||||||
|
"job_types": AvailableJobTypes,
|
||||||
|
"worker_tags": WorkerTagList,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FetchError(RuntimeError):
|
||||||
|
"""Raised when the manager info could not be fetched from the Manager."""
|
||||||
|
|
||||||
|
|
||||||
|
class LoadError(RuntimeError):
|
||||||
|
"""Raised when the manager info could not be loaded from disk cache."""
|
||||||
|
|
||||||
|
|
||||||
|
_cached_manager_info: Optional[ManagerInfo] = None
|
||||||
|
|
||||||
|
|
||||||
|
def fetch(api_client: _ApiClient) -> ManagerInfo:
|
||||||
|
global _cached_manager_info
|
||||||
|
|
||||||
|
# Do a late import, so that the API is only imported when actually used.
|
||||||
|
from flamenco.manager import ApiException
|
||||||
|
from flamenco.manager.apis import MetaApi, JobsApi, WorkerMgtApi
|
||||||
|
from flamenco.manager.models import (
|
||||||
|
AvailableJobTypes,
|
||||||
|
FlamencoVersion,
|
||||||
|
SharedStorageLocation,
|
||||||
|
WorkerTagList,
|
||||||
|
)
|
||||||
|
|
||||||
|
meta_api = MetaApi(api_client)
|
||||||
|
jobs_api = JobsApi(api_client)
|
||||||
|
worker_mgt_api = WorkerMgtApi(api_client)
|
||||||
|
|
||||||
|
try:
|
||||||
|
flamenco_version: FlamencoVersion = meta_api.get_version()
|
||||||
|
shared_storage: SharedStorageLocation = meta_api.get_shared_storage(
|
||||||
|
"users", platform.system().lower()
|
||||||
|
)
|
||||||
|
job_types: AvailableJobTypes = jobs_api.get_job_types()
|
||||||
|
worker_tags: WorkerTagList = worker_mgt_api.fetch_worker_tags()
|
||||||
|
except ApiException as ex:
|
||||||
|
raise FetchError("Manager cannot be reached: %s" % ex) from ex
|
||||||
|
except MaxRetryError as ex:
|
||||||
|
# This is the common error, when for example the port number is
|
||||||
|
# incorrect and nothing is listening. The exception text is not included
|
||||||
|
# because it's very long and confusing.
|
||||||
|
raise FetchError("Manager cannot be reached") from ex
|
||||||
|
except HTTPError as ex:
|
||||||
|
raise FetchError("Manager cannot be reached: %s" % ex) from ex
|
||||||
|
|
||||||
|
_cached_manager_info = ManagerInfo(
|
||||||
|
flamenco_version=flamenco_version,
|
||||||
|
shared_storage=shared_storage,
|
||||||
|
job_types=job_types,
|
||||||
|
worker_tags=worker_tags,
|
||||||
|
)
|
||||||
|
return _cached_manager_info
|
||||||
|
|
||||||
|
|
||||||
|
class Encoder(json.JSONEncoder):
|
||||||
|
def default(self, o):
|
||||||
|
from flamenco.manager.model_utils import OpenApiModel
|
||||||
|
|
||||||
|
if isinstance(o, OpenApiModel):
|
||||||
|
return o.to_dict()
|
||||||
|
|
||||||
|
if isinstance(o, ManagerInfo):
|
||||||
|
# dataclasses.asdict() creates a copy of the OpenAPI models,
|
||||||
|
# in a way that just doesn't work, hence this workaround.
|
||||||
|
return {f.name: getattr(o, f.name) for f in dataclasses.fields(o)}
|
||||||
|
|
||||||
|
return super().default(o)
|
||||||
|
|
||||||
|
|
||||||
|
def _to_json(info: ManagerInfo) -> str:
|
||||||
|
return json.dumps(info, indent=" ", cls=Encoder)
|
||||||
|
|
||||||
|
|
||||||
|
def _from_json(contents: str | bytes) -> ManagerInfo:
|
||||||
|
# Do a late import, so that the API is only imported when actually used.
|
||||||
|
from flamenco.manager.configuration import Configuration
|
||||||
|
from flamenco.manager.model_utils import validate_and_convert_types
|
||||||
|
|
||||||
|
json_dict = json.loads(contents)
|
||||||
|
dummy_cfg = Configuration()
|
||||||
|
api_models = {}
|
||||||
|
|
||||||
|
for name, api_type in ManagerInfo.type_info().items():
|
||||||
|
api_model = validate_and_convert_types(
|
||||||
|
json_dict[name],
|
||||||
|
(api_type,),
|
||||||
|
[name],
|
||||||
|
True,
|
||||||
|
True,
|
||||||
|
dummy_cfg,
|
||||||
|
)
|
||||||
|
api_models[name] = api_model
|
||||||
|
|
||||||
|
return ManagerInfo(**api_models)
|
||||||
|
|
||||||
|
|
||||||
|
def _json_filepath() -> Path:
|
||||||
|
# This is the '~/.config/blender/{version}' path.
|
||||||
|
user_path = Path(bpy.utils.resource_path(type="USER"))
|
||||||
|
return user_path / "config" / "flamenco-manager-info.json"
|
||||||
|
|
||||||
|
|
||||||
|
def save(info: ManagerInfo) -> None:
|
||||||
|
json_path = _json_filepath()
|
||||||
|
json_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
as_json = _to_json(info)
|
||||||
|
json_path.write_text(as_json, encoding="utf8")
|
||||||
|
|
||||||
|
|
||||||
|
def load() -> ManagerInfo:
|
||||||
|
json_path = _json_filepath()
|
||||||
|
if not json_path.exists():
|
||||||
|
raise FileNotFoundError(f"{json_path.name} not found in {json_path.parent}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
as_json = json_path.read_text(encoding="utf8")
|
||||||
|
except OSError as ex:
|
||||||
|
raise LoadError(f"Could not read {json_path}: {ex}") from ex
|
||||||
|
|
||||||
|
try:
|
||||||
|
return _from_json(as_json)
|
||||||
|
except json.JSONDecodeError as ex:
|
||||||
|
raise LoadError(f"Could not decode JSON in {json_path}") from ex
|
||||||
|
|
||||||
|
|
||||||
|
def load_into_cache() -> Optional[ManagerInfo]:
|
||||||
|
global _cached_manager_info
|
||||||
|
|
||||||
|
_cached_manager_info = None
|
||||||
|
try:
|
||||||
|
_cached_manager_info = load()
|
||||||
|
except FileNotFoundError:
|
||||||
|
return None
|
||||||
|
except LoadError as ex:
|
||||||
|
print(f"Could not load Flamenco Manager info from disk: {ex}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return _cached_manager_info
|
||||||
|
|
||||||
|
|
||||||
|
def load_cached() -> Optional[ManagerInfo]:
|
||||||
|
global _cached_manager_info
|
||||||
|
|
||||||
|
if _cached_manager_info is not None:
|
||||||
|
return _cached_manager_info
|
||||||
|
|
||||||
|
return load_into_cache()
|
@ -10,7 +10,7 @@ from urllib3.exceptions import HTTPError, MaxRetryError
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
from . import job_types, job_submission, preferences, worker_tags
|
from . import job_types, job_submission, preferences, manager_info
|
||||||
from .job_types_propgroup import JobTypePropertyGroup
|
from .job_types_propgroup import JobTypePropertyGroup
|
||||||
from .bat.submodules import bpathlib
|
from .bat.submodules import bpathlib
|
||||||
|
|
||||||
@ -51,80 +51,6 @@ class FlamencoOpMixin:
|
|||||||
return api_client
|
return api_client
|
||||||
|
|
||||||
|
|
||||||
class FLAMENCO_OT_fetch_job_types(FlamencoOpMixin, bpy.types.Operator):
|
|
||||||
bl_idname = "flamenco.fetch_job_types"
|
|
||||||
bl_label = "Fetch Job Types"
|
|
||||||
bl_description = "Query Flamenco Manager to obtain the available job types"
|
|
||||||
|
|
||||||
def execute(self, context: bpy.types.Context) -> set[str]:
|
|
||||||
api_client = self.get_api_client(context)
|
|
||||||
|
|
||||||
from flamenco.manager import ApiException
|
|
||||||
|
|
||||||
scene = context.scene
|
|
||||||
old_job_type_name = getattr(scene, "flamenco_job_type", "")
|
|
||||||
|
|
||||||
try:
|
|
||||||
job_types.fetch_available_job_types(api_client, scene)
|
|
||||||
except ApiException as ex:
|
|
||||||
self.report({"ERROR"}, "Error getting job types: %s" % ex)
|
|
||||||
return {"CANCELLED"}
|
|
||||||
except MaxRetryError as ex:
|
|
||||||
# This is the common error, when for example the port number is
|
|
||||||
# incorrect and nothing is listening.
|
|
||||||
self.report({"ERROR"}, "Unable to reach Manager")
|
|
||||||
return {"CANCELLED"}
|
|
||||||
|
|
||||||
if old_job_type_name:
|
|
||||||
try:
|
|
||||||
scene.flamenco_job_type = old_job_type_name
|
|
||||||
except TypeError: # Thrown when the old job type no longer exists.
|
|
||||||
# You cannot un-set an enum property, and 'empty string' is not
|
|
||||||
# a valid value either, so better to just remove the underlying
|
|
||||||
# ID property.
|
|
||||||
del scene["flamenco_job_type"]
|
|
||||||
|
|
||||||
self.report(
|
|
||||||
{"WARNING"},
|
|
||||||
"Job type %r no longer available, choose another one"
|
|
||||||
% old_job_type_name,
|
|
||||||
)
|
|
||||||
|
|
||||||
job_types.update_job_type_properties(scene)
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class FLAMENCO_OT_fetch_worker_tags(FlamencoOpMixin, bpy.types.Operator):
|
|
||||||
bl_idname = "flamenco.fetch_worker_tags"
|
|
||||||
bl_label = "Fetch Worker Tags"
|
|
||||||
bl_description = "Query Flamenco Manager to obtain the available worker tags"
|
|
||||||
|
|
||||||
def execute(self, context: bpy.types.Context) -> set[str]:
|
|
||||||
api_client = self.get_api_client(context)
|
|
||||||
|
|
||||||
from flamenco.manager import ApiException
|
|
||||||
|
|
||||||
scene = context.scene
|
|
||||||
old_tag = getattr(scene, "flamenco_worker_tag", "")
|
|
||||||
|
|
||||||
try:
|
|
||||||
worker_tags.refresh(context, api_client)
|
|
||||||
except ApiException as ex:
|
|
||||||
self.report({"ERROR"}, "Error getting job types: %s" % ex)
|
|
||||||
return {"CANCELLED"}
|
|
||||||
except MaxRetryError as ex:
|
|
||||||
# This is the common error, when for example the port number is
|
|
||||||
# incorrect and nothing is listening.
|
|
||||||
self.report({"ERROR"}, "Unable to reach Manager")
|
|
||||||
return {"CANCELLED"}
|
|
||||||
|
|
||||||
if old_tag:
|
|
||||||
# TODO: handle cases where the old tag no longer exists.
|
|
||||||
scene.flamenco_worker_tag = old_tag
|
|
||||||
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class FLAMENCO_OT_ping_manager(FlamencoOpMixin, bpy.types.Operator):
|
class FLAMENCO_OT_ping_manager(FlamencoOpMixin, bpy.types.Operator):
|
||||||
bl_idname = "flamenco.ping_manager"
|
bl_idname = "flamenco.ping_manager"
|
||||||
bl_label = "Flamenco: Ping Manager"
|
bl_label = "Flamenco: Ping Manager"
|
||||||
@ -132,13 +58,13 @@ class FLAMENCO_OT_ping_manager(FlamencoOpMixin, bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER"} # No UNDO.
|
bl_options = {"REGISTER"} # No UNDO.
|
||||||
|
|
||||||
def execute(self, context: bpy.types.Context) -> set[str]:
|
def execute(self, context: bpy.types.Context) -> set[str]:
|
||||||
from . import comms, preferences
|
from . import comms
|
||||||
|
|
||||||
api_client = self.get_api_client(context)
|
api_client = self.get_api_client(context)
|
||||||
prefs = preferences.get(context)
|
report, level = comms.ping_manager(
|
||||||
|
context.window_manager,
|
||||||
report, level = comms.ping_manager_with_report(
|
context.scene,
|
||||||
context.window_manager, api_client, prefs
|
api_client,
|
||||||
)
|
)
|
||||||
self.report({level}, report)
|
self.report({level}, report)
|
||||||
|
|
||||||
@ -259,29 +185,31 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):
|
|||||||
|
|
||||||
:return: an error string when something went wrong.
|
:return: an error string when something went wrong.
|
||||||
"""
|
"""
|
||||||
from . import comms, preferences
|
from . import comms
|
||||||
|
|
||||||
# Get the manager's info. This is cached in the preferences, so
|
# Get the manager's info. This is cached to disk, so regardless of
|
||||||
# regardless of whether this function actually responds to version
|
# whether this function actually responds to version mismatches, it has
|
||||||
# mismatches, it has to be called to also refresh the shared storage
|
# to be called to also refresh the shared storage location.
|
||||||
# location.
|
|
||||||
api_client = self.get_api_client(context)
|
api_client = self.get_api_client(context)
|
||||||
prefs = preferences.get(context)
|
|
||||||
mgrinfo = comms.ping_manager(context.window_manager, api_client, prefs)
|
report, report_level = comms.ping_manager(
|
||||||
if mgrinfo.error:
|
context.window_manager,
|
||||||
return mgrinfo.error
|
context.scene,
|
||||||
|
api_client,
|
||||||
|
)
|
||||||
|
if report_level != "INFO":
|
||||||
|
return report
|
||||||
|
|
||||||
# Check the Manager's version.
|
# Check the Manager's version.
|
||||||
if not self.ignore_version_mismatch:
|
if not self.ignore_version_mismatch:
|
||||||
my_version = comms.flamenco_client_version()
|
mgrinfo = manager_info.load_cached()
|
||||||
assert mgrinfo.version is not None
|
|
||||||
|
# Safe to assume, as otherwise the ping_manager() call would not have succeeded.
|
||||||
|
assert mgrinfo is not None
|
||||||
|
|
||||||
|
my_version = comms.flamenco_client_version()
|
||||||
|
mgrversion = mgrinfo.flamenco_version.shortversion
|
||||||
|
|
||||||
try:
|
|
||||||
mgrversion = mgrinfo.version.shortversion
|
|
||||||
except AttributeError:
|
|
||||||
# shortversion was introduced in Manager version 3.0-beta2, which
|
|
||||||
# may not be running here yet.
|
|
||||||
mgrversion = mgrinfo.version.version
|
|
||||||
if mgrversion != my_version:
|
if mgrversion != my_version:
|
||||||
context.window_manager.flamenco_version_mismatch = True
|
context.window_manager.flamenco_version_mismatch = True
|
||||||
return (
|
return (
|
||||||
@ -299,6 +227,23 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):
|
|||||||
# Empty error message indicates 'ok'.
|
# Empty error message indicates 'ok'.
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def _manager_info(
|
||||||
|
self, context: bpy.types.Context
|
||||||
|
) -> Optional[manager_info.ManagerInfo]:
|
||||||
|
"""Load the manager info.
|
||||||
|
|
||||||
|
If it cannot be loaded, returns None after emitting an error message and
|
||||||
|
calling self._quit(context).
|
||||||
|
"""
|
||||||
|
manager = manager_info.load_cached()
|
||||||
|
if not manager:
|
||||||
|
self.report(
|
||||||
|
{"ERROR"}, "No information known about Flamenco Manager, refresh first."
|
||||||
|
)
|
||||||
|
self._quit(context)
|
||||||
|
return None
|
||||||
|
return manager
|
||||||
|
|
||||||
def _save_blendfile(self, context):
|
def _save_blendfile(self, context):
|
||||||
"""Save to a different file, specifically for Flamenco.
|
"""Save to a different file, specifically for Flamenco.
|
||||||
|
|
||||||
@ -368,8 +313,11 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):
|
|||||||
self._quit(context)
|
self._quit(context)
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
prefs = preferences.get(context)
|
manager = self._manager_info(context)
|
||||||
if prefs.is_shaman_enabled:
|
if not manager:
|
||||||
|
return {"CANCELLED"}
|
||||||
|
|
||||||
|
if manager.shared_storage.shaman_enabled:
|
||||||
# self.blendfile_on_farm will be set when BAT created the checkout,
|
# self.blendfile_on_farm will be set when BAT created the checkout,
|
||||||
# see _on_bat_pack_msg() below.
|
# see _on_bat_pack_msg() below.
|
||||||
self.blendfile_on_farm = None
|
self.blendfile_on_farm = None
|
||||||
@ -414,11 +362,14 @@ class FLAMENCO_OT_submit_job(FlamencoOpMixin, bpy.types.Operator):
|
|||||||
raise FileNotFoundError()
|
raise FileNotFoundError()
|
||||||
|
|
||||||
# Determine where the blend file will be stored.
|
# Determine where the blend file will be stored.
|
||||||
|
manager = self._manager_info(context)
|
||||||
|
if not manager:
|
||||||
|
raise FileNotFoundError("Manager info not known")
|
||||||
unique_dir = "%s-%s" % (
|
unique_dir = "%s-%s" % (
|
||||||
datetime.datetime.now().isoformat("-").replace(":", ""),
|
datetime.datetime.now().isoformat("-").replace(":", ""),
|
||||||
self.job_name,
|
self.job_name,
|
||||||
)
|
)
|
||||||
pack_target_dir = Path(prefs.job_storage) / unique_dir
|
pack_target_dir = Path(manager.shared_storage.location) / unique_dir
|
||||||
|
|
||||||
# TODO: this should take the blendfile location relative to the project path into account.
|
# TODO: this should take the blendfile location relative to the project path into account.
|
||||||
pack_target_file = pack_target_dir / blendfile.name
|
pack_target_file = pack_target_dir / blendfile.name
|
||||||
@ -690,8 +641,6 @@ class FLAMENCO3_OT_explore_file_path(bpy.types.Operator):
|
|||||||
|
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
FLAMENCO_OT_fetch_job_types,
|
|
||||||
FLAMENCO_OT_fetch_worker_tags,
|
|
||||||
FLAMENCO_OT_ping_manager,
|
FLAMENCO_OT_ping_manager,
|
||||||
FLAMENCO_OT_eval_setting,
|
FLAMENCO_OT_eval_setting,
|
||||||
FLAMENCO_OT_submit_job,
|
FLAMENCO_OT_submit_job,
|
||||||
|
@ -5,7 +5,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
from . import projects
|
from . import projects, manager_info
|
||||||
|
|
||||||
|
|
||||||
def discard_flamenco_client(context):
|
def discard_flamenco_client(context):
|
||||||
@ -16,9 +16,7 @@ def discard_flamenco_client(context):
|
|||||||
context.window_manager.flamenco_status_ping = ""
|
context.window_manager.flamenco_status_ping = ""
|
||||||
|
|
||||||
|
|
||||||
def _refresh_the_planet(
|
def _refresh_the_planet(context: bpy.types.Context) -> None:
|
||||||
prefs: "FlamencoPreferences", context: bpy.types.Context
|
|
||||||
) -> None:
|
|
||||||
"""Refresh all GUI areas."""
|
"""Refresh all GUI areas."""
|
||||||
for win in context.window_manager.windows:
|
for win in context.window_manager.windows:
|
||||||
for area in win.screen.areas:
|
for area in win.screen.areas:
|
||||||
@ -35,7 +33,8 @@ def _manager_url_updated(prefs, context):
|
|||||||
|
|
||||||
# Warning, be careful what of the context to access here. Accessing /
|
# Warning, be careful what of the context to access here. Accessing /
|
||||||
# changing too much can cause crashes, infinite loops, etc.
|
# changing too much can cause crashes, infinite loops, etc.
|
||||||
comms.ping_manager_with_report(context.window_manager, api_client, prefs)
|
comms.ping_manager(context.window_manager, context.scene, api_client)
|
||||||
|
_refresh_the_planet(context)
|
||||||
|
|
||||||
|
|
||||||
_project_finder_enum_items = [
|
_project_finder_enum_items = [
|
||||||
@ -66,22 +65,6 @@ class FlamencoPreferences(bpy.types.AddonPreferences):
|
|||||||
items=_project_finder_enum_items,
|
items=_project_finder_enum_items,
|
||||||
)
|
)
|
||||||
|
|
||||||
is_shaman_enabled: bpy.props.BoolProperty( # type: ignore
|
|
||||||
name="Shaman Enabled",
|
|
||||||
description="Whether this Manager has the Shaman protocol enabled",
|
|
||||||
default=False,
|
|
||||||
update=_refresh_the_planet,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Property that should be editable from Python. It's not exposed to the GUI.
|
|
||||||
job_storage: bpy.props.StringProperty( # type: ignore
|
|
||||||
name="Job Storage Directory",
|
|
||||||
subtype="DIR_PATH",
|
|
||||||
default="",
|
|
||||||
options={"HIDDEN"},
|
|
||||||
description="Directory where blend files are stored when submitting them to Flamenco. This value is determined by Flamenco Manager",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Property that gets its value from the above _job_storage, and cannot be
|
# Property that gets its value from the above _job_storage, and cannot be
|
||||||
# set. This makes it read-only in the GUI.
|
# set. This makes it read-only in the GUI.
|
||||||
job_storage_for_gui: bpy.props.StringProperty( # type: ignore
|
job_storage_for_gui: bpy.props.StringProperty( # type: ignore
|
||||||
@ -90,14 +73,7 @@ class FlamencoPreferences(bpy.types.AddonPreferences):
|
|||||||
default="",
|
default="",
|
||||||
options={"SKIP_SAVE"},
|
options={"SKIP_SAVE"},
|
||||||
description="Directory where blend files are stored when submitting them to Flamenco. This value is determined by Flamenco Manager",
|
description="Directory where blend files are stored when submitting them to Flamenco. This value is determined by Flamenco Manager",
|
||||||
get=lambda prefs: prefs.job_storage,
|
get=lambda prefs: prefs._job_storage(),
|
||||||
)
|
|
||||||
|
|
||||||
worker_tags: bpy.props.CollectionProperty( # type: ignore
|
|
||||||
type=WorkerTag,
|
|
||||||
name="Worker Tags",
|
|
||||||
description="Cache for the worker tags available on the configured Manager",
|
|
||||||
options={"HIDDEN"},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def draw(self, context: bpy.types.Context) -> None:
|
def draw(self, context: bpy.types.Context) -> None:
|
||||||
@ -116,7 +92,9 @@ class FlamencoPreferences(bpy.types.AddonPreferences):
|
|||||||
split.label(text="")
|
split.label(text="")
|
||||||
split.label(text=label)
|
split.label(text=label)
|
||||||
|
|
||||||
if not self.job_storage:
|
manager = manager_info.load_cached()
|
||||||
|
|
||||||
|
if not manager:
|
||||||
text_row(col, "Press the refresh button before using Flamenco")
|
text_row(col, "Press the refresh button before using Flamenco")
|
||||||
|
|
||||||
if context.window_manager.flamenco_status_ping:
|
if context.window_manager.flamenco_status_ping:
|
||||||
@ -126,7 +104,7 @@ class FlamencoPreferences(bpy.types.AddonPreferences):
|
|||||||
text_row(aligned, "Press the refresh button to check the connection")
|
text_row(aligned, "Press the refresh button to check the connection")
|
||||||
text_row(aligned, "and update the job storage location")
|
text_row(aligned, "and update the job storage location")
|
||||||
|
|
||||||
if self.is_shaman_enabled:
|
if manager and manager.shared_storage.shaman_enabled:
|
||||||
text_row(col, "Shaman enabled")
|
text_row(col, "Shaman enabled")
|
||||||
col.prop(self, "job_storage_for_gui", text="Job Storage")
|
col.prop(self, "job_storage_for_gui", text="Job Storage")
|
||||||
|
|
||||||
@ -152,6 +130,12 @@ class FlamencoPreferences(bpy.types.AddonPreferences):
|
|||||||
blendfile = Path(bpy.data.filepath)
|
blendfile = Path(bpy.data.filepath)
|
||||||
return projects.for_blendfile(blendfile, self.project_finder)
|
return projects.for_blendfile(blendfile, self.project_finder)
|
||||||
|
|
||||||
|
def _job_storage(self) -> str:
|
||||||
|
info = manager_info.load_cached()
|
||||||
|
if not info:
|
||||||
|
return "Unknown, refresh first."
|
||||||
|
return str(info.shared_storage.location)
|
||||||
|
|
||||||
|
|
||||||
def get(context: bpy.types.Context) -> FlamencoPreferences:
|
def get(context: bpy.types.Context) -> FlamencoPreferences:
|
||||||
"""Return the add-on preferences."""
|
"""Return the add-on preferences."""
|
||||||
|
@ -1,57 +1,35 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Union
|
from typing import Union
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
from . import preferences
|
from . import manager_info
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from flamenco.manager import ApiClient as _ApiClient
|
|
||||||
else:
|
|
||||||
_ApiClient = object
|
|
||||||
|
|
||||||
|
|
||||||
_enum_items: list[Union[tuple[str, str, str], tuple[str, str, str, int, int]]] = []
|
_enum_items: list[Union[tuple[str, str, str], tuple[str, str, str, int, int]]] = []
|
||||||
|
|
||||||
|
|
||||||
def refresh(context: bpy.types.Context, api_client: _ApiClient) -> None:
|
|
||||||
"""Fetch the available worker tags from the Manager."""
|
|
||||||
from flamenco.manager import ApiClient
|
|
||||||
from flamenco.manager.api import worker_mgt_api
|
|
||||||
from flamenco.manager.model.worker_tag_list import WorkerTagList
|
|
||||||
|
|
||||||
assert isinstance(api_client, ApiClient)
|
|
||||||
|
|
||||||
api = worker_mgt_api.WorkerMgtApi(api_client)
|
|
||||||
response: WorkerTagList = api.fetch_worker_tags()
|
|
||||||
|
|
||||||
# Store on the preferences, so a cached version persists until the next refresh.
|
|
||||||
prefs = preferences.get(context)
|
|
||||||
prefs.worker_tags.clear()
|
|
||||||
|
|
||||||
for tag in response.tags:
|
|
||||||
rna_tag = prefs.worker_tags.add()
|
|
||||||
rna_tag.id = tag.id
|
|
||||||
rna_tag.name = tag.name
|
|
||||||
rna_tag.description = getattr(tag, "description", "")
|
|
||||||
|
|
||||||
# Preferences have changed, so make sure that Blender saves them (assuming
|
|
||||||
# auto-save here).
|
|
||||||
context.preferences.is_dirty = True
|
|
||||||
|
|
||||||
|
|
||||||
def _get_enum_items(self, context):
|
def _get_enum_items(self, context):
|
||||||
global _enum_items
|
global _enum_items
|
||||||
prefs = preferences.get(context)
|
|
||||||
|
manager = manager_info.load_cached()
|
||||||
|
if manager is None:
|
||||||
|
_enum_items = [
|
||||||
|
(
|
||||||
|
"-",
|
||||||
|
"-tags unknown-",
|
||||||
|
"Refresh to load the available Worker tags from the Manager",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
return _enum_items
|
||||||
|
|
||||||
_enum_items = [
|
_enum_items = [
|
||||||
("-", "All", "No specific tag assigned, any worker can handle this job"),
|
("-", "All", "No specific tag assigned, any worker can handle this job"),
|
||||||
]
|
]
|
||||||
_enum_items.extend(
|
for tag in manager.worker_tags.tags:
|
||||||
(tag.id, tag.name, tag.description)
|
_enum_items.append((tag.id, tag.name, getattr(tag, "description", "")))
|
||||||
for tag in prefs.worker_tags
|
|
||||||
)
|
|
||||||
return _enum_items
|
return _enum_items
|
||||||
|
|
||||||
|
|
||||||
@ -70,9 +48,3 @@ def unregister() -> None:
|
|||||||
delattr(ob, attr)
|
delattr(ob, attr)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import doctest
|
|
||||||
|
|
||||||
print(doctest.testmod())
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user