Sync branch magefile with main #104308
@ -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…
Reference in New Issue
Block a user