From d600bfb113e647e6f13dfbb9da9b32d43501a497 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 15 Sep 2021 11:19:51 +0200 Subject: [PATCH 1/6] removed interface of timers manager --- .../timers_manager/interfaces.py | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 openpype/modules/default_modules/timers_manager/interfaces.py diff --git a/openpype/modules/default_modules/timers_manager/interfaces.py b/openpype/modules/default_modules/timers_manager/interfaces.py deleted file mode 100644 index 179013cffe6..00000000000 --- a/openpype/modules/default_modules/timers_manager/interfaces.py +++ /dev/null @@ -1,26 +0,0 @@ -from abc import abstractmethod -from openpype.modules import OpenPypeInterface - - -class ITimersManager(OpenPypeInterface): - timer_manager_module = None - - @abstractmethod - def stop_timer(self): - pass - - @abstractmethod - def start_timer(self, data): - pass - - def timer_started(self, data): - if not self.timer_manager_module: - return - - self.timer_manager_module.timer_started(self.id, data) - - def timer_stopped(self): - if not self.timer_manager_module: - return - - self.timer_manager_module.timer_stopped(self.id) From 292655e1d64f2acc32a58bda428b5461c0b34c41 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 15 Sep 2021 11:30:48 +0200 Subject: [PATCH 2/6] TimersManager has new way of connection definition to it's logic --- .../timers_manager/timers_manager.py | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index 80f448095f6..7fb52eaef72 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -22,6 +22,11 @@ class TimersManager( name = "timers_manager" label = "Timers Service" + _required_methods = ( + "stop_timer", + "start_timer" + ) + def initialize(self, modules_settings): timers_settings = modules_settings[self.name] @@ -44,7 +49,8 @@ def initialize(self, modules_settings): self.widget_user_idle = None self.signal_handler = None - self.modules = [] + self._connectors_by_module_id = {} + self._modules_by_id = {} def tray_init(self): from .widget_user_idle import WidgetUserIdle, SignalHandler @@ -135,10 +141,36 @@ def stop_timers(self): def connect_with_modules(self, enabled_modules): for module in enabled_modules: - if not isinstance(module, ITimersManager): + connector = getattr(module, "timers_manager_connector", None) + if connector is None: + continue + + missing_methods = set() + for method_name in self._required_methods: + if not hasattr(connector, method_name): + missing_methods.add(method_name) + + if missing_methods: + joined = ", ".join( + ['"{}"'.format(name for name in missing_methods)] + ) + self.log.info(( + "Module \"{}\" has missing required methods {}." + ).format(module.name, joined)) continue - module.timer_manager_module = self - self.modules.append(module) + + self._connectors_by_module_id[module.id] = connector + self._modules_by_id[module.id] = module + + # Optional method + if hasattr(connector, "register_timers_manager"): + try: + connector.register_timers_manager(self) + except Exception: + self.log.info(( + "Failed to register timers manager" + " for connector of module \"{}\"." + ).format(module.name)) def callbacks_by_idle_time(self): """Implementation of IIdleManager interface.""" From 412d429f6d210f986f653ba053bcd93feab92ec3 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 15 Sep 2021 11:32:13 +0200 Subject: [PATCH 3/6] modified timer stopped/started methods --- .../timers_manager/timers_manager.py | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index 7fb52eaef72..b66dfaef940 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -31,6 +31,7 @@ def initialize(self, modules_settings): timers_settings = modules_settings[self.name] self.enabled = timers_settings["enabled"] + auto_stop = timers_settings["auto_stop"] # When timer will stop if idle manager is running (minutes) full_time = int(timers_settings["full_time"] * 60) @@ -68,8 +69,9 @@ def webserver_initialization(self, server_manager): """Implementation of IWebServerRoutes interface.""" if self.tray_initialized: from .rest_api import TimersManagerModuleRestApi - self.rest_api_obj = TimersManagerModuleRestApi(self, - server_manager) + self.rest_api_obj = TimersManagerModuleRestApi( + self, server_manager + ) def start_timer(self, project_name, asset_name, task_name, hierarchy): """ @@ -112,17 +114,35 @@ def start_timer(self, project_name, asset_name, task_name, hierarchy): self.timer_started(None, data) def timer_started(self, source_id, data): - for module in self.modules: - if module.id != source_id: - module.start_timer(data) + for module_id, connector in self._connectors_by_module_id.items(): + if module_id == source_id: + continue + + try: + connector.start_timer(data) + except Exception: + self.log.info( + "Failed to start timer on connector {}".format( + str(connector) + ) + ) self.last_task = data self.is_running = True def timer_stopped(self, source_id): - for module in self.modules: - if module.id != source_id: - module.stop_timer() + for module_id, connector in self._connectors_by_module_id.items(): + if module_id == source_id: + continue + + try: + connector.stop_timer() + except Exception: + self.log.info( + "Failed to stop timer on connector {}".format( + str(connector) + ) + ) def restart_timers(self): if self.last_task is not None: @@ -136,8 +156,7 @@ def stop_timers(self): self.widget_user_idle.refresh_context() self.is_running = False - for module in self.modules: - module.stop_timer() + self.timer_stopper(None) def connect_with_modules(self, enabled_modules): for module in enabled_modules: From 2647b3fb1fcf351d7e748889cd4618c8edf763b1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 15 Sep 2021 11:32:34 +0200 Subject: [PATCH 4/6] added example of connector to timers manager --- .../timers_manager/timers_manager.py | 68 ++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/openpype/modules/default_modules/timers_manager/timers_manager.py b/openpype/modules/default_modules/timers_manager/timers_manager.py index b66dfaef940..7d83cf0349f 100644 --- a/openpype/modules/default_modules/timers_manager/timers_manager.py +++ b/openpype/modules/default_modules/timers_manager/timers_manager.py @@ -10,14 +10,78 @@ from avalon.api import AvalonMongoDB +class ExampleTimersManagerConnector: + """Timers manager can handle timers of multiple modules/addons. + + Module must have object under `timers_manager_connector` attribute with + few methods. This is example class of the object that could be stored under + module. + + Required methods are 'stop_timer' and 'start_timer'. + + # TODO pass asset document instead of `hierarchy` + Example of `data` that are passed during changing timer: + ``` + data = { + "project_name": project_name, + "task_name": task_name, + "task_type": task_type, + "hierarchy": hierarchy + } + ``` + """ + # Not needed at all + def __init__(self, module): + # Store timer manager module to be able call it's methods when needed + self._timers_manager_module = None + + # Store module which want to use timers manager to have access + self._module = module + + # Required + def stop_timer(self): + """Called by timers manager when module should stop timer.""" + self._module.stop_timer() + + # Required + def start_timer(self, data): + """Method called by timers manager when should start timer.""" + self._module.start_timer(data) + + # Optional + def register_timers_manager(self, timer_manager_module): + """Method called by timers manager where it's object is passed. + + This is moment when timers manager module can be store to be able + call it's callbacks (e.g. timer started). + """ + self._timers_manager_module = timer_manager_module + + # Custom implementation + def timer_started(self, data): + """This is example of possibility to trigger callbacks on manager.""" + if self._timers_manager_module is not None: + self._timers_manager_module.timer_started(self._module.id, data) + + # Custom implementation + def timer_stopped(self): + if self._timers_manager_module is not None: + self._timers_manager_module.timer_stopped(self._module.id) + + class TimersManager( OpenPypeModule, ITrayService, IIdleManager, IWebServerRoutes ): """ Handles about Timers. Should be able to start/stop all timers at once. - If IdleManager is imported then is able to handle about stop timers - when user idles for a long time (set in presets). + + To be able use this advantage module has to have attribute with name + `timers_manager_connector` which has two methods 'stop_timer' + and 'start_timer'. Optionally may have `register_timers_manager` where + object of TimersManager module is passed to be able call it's callbacks. + + See `ExampleTimersManagerConnector`. """ name = "timers_manager" label = "Timers Service" From 15a8c477d79fa311e1f258835e6a59531579effa Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 15 Sep 2021 11:34:39 +0200 Subject: [PATCH 5/6] modified clickify to not use ITimersManager but defined attribute with methods --- .../clockify/clockify_module.py | 105 +++++++++++------- 1 file changed, 63 insertions(+), 42 deletions(-) diff --git a/openpype/modules/default_modules/clockify/clockify_module.py b/openpype/modules/default_modules/clockify/clockify_module.py index a9e989f4ec3..5136b9cbc3a 100644 --- a/openpype/modules/default_modules/clockify/clockify_module.py +++ b/openpype/modules/default_modules/clockify/clockify_module.py @@ -11,8 +11,7 @@ from openpype_interfaces import ( ITrayModule, IPluginPaths, - IFtrackEventHandlerPaths, - ITimersManager + IFtrackEventHandlerPaths ) @@ -20,8 +19,7 @@ class ClockifyModule( OpenPypeModule, ITrayModule, IPluginPaths, - IFtrackEventHandlerPaths, - ITimersManager + IFtrackEventHandlerPaths ): name = "clockify" @@ -39,6 +37,11 @@ def initialize(self, modules_settings): self.clockapi = ClockifyAPI(master_parent=self) + # TimersManager attributes + # - set `timers_manager_connector` only in `tray_init` + self.timers_manager_connector = None + self._timers_manager_module = None + def get_global_environments(self): return { "CLOCKIFY_WORKSPACE": self.workspace_name @@ -61,6 +64,9 @@ def tray_init(self): self.bool_timer_run = False self.bool_api_key_set = self.clockapi.set_api() + # Define itself as TimersManager connector + self.timers_manager_connector = self + def tray_start(self): if self.bool_api_key_set is False: self.show_settings() @@ -165,10 +171,6 @@ def check_running(self): self.set_menu_visibility() time.sleep(5) - def stop_timer(self): - """Implementation of ITimersManager.""" - self.clockapi.finish_time_entry() - def signed_in(self): if not self.timer_manager: return @@ -179,8 +181,60 @@ def signed_in(self): if self.timer_manager.is_running: self.start_timer_manager(self.timer_manager.last_task) + def on_message_widget_close(self): + self.message_widget = None + + # Definition of Tray menu + def tray_menu(self, parent_menu): + # Menu for Tray App + from Qt import QtWidgets + menu = QtWidgets.QMenu("Clockify", parent_menu) + menu.setProperty("submenu", "on") + + # Actions + action_show_settings = QtWidgets.QAction("Settings", menu) + action_stop_timer = QtWidgets.QAction("Stop timer", menu) + + menu.addAction(action_show_settings) + menu.addAction(action_stop_timer) + + action_show_settings.triggered.connect(self.show_settings) + action_stop_timer.triggered.connect(self.stop_timer) + + self.action_stop_timer = action_stop_timer + + self.set_menu_visibility() + + parent_menu.addMenu(menu) + + def show_settings(self): + self.widget_settings.input_api_key.setText(self.clockapi.get_api_key()) + self.widget_settings.show() + + def set_menu_visibility(self): + self.action_stop_timer.setVisible(self.bool_timer_run) + + # --- TimersManager connection methods --- + def register_timers_manager(self, timer_manager_module): + """Store TimersManager for future use.""" + self._timers_manager_module = timer_manager_module + + def timer_started(self, data): + """Tell TimersManager that timer started.""" + if self._timers_manager_module is not None: + self._timers_manager_module.timer_started(self._module.id, data) + + def timer_stopped(self): + """Tell TimersManager that timer stopped.""" + if self._timers_manager_module is not None: + self._timers_manager_module.timer_stopped(self._module.id) + + def stop_timer(self): + """Called from TimersManager to stop timer.""" + self.clockapi.finish_time_entry() + def start_timer(self, input_data): - """Implementation of ITimersManager.""" + """Called from TimersManager to start timer.""" # If not api key is not entered then skip if not self.clockapi.get_api_key(): return @@ -237,36 +291,3 @@ def start_timer(self, input_data): self.clockapi.start_time_entry( description, project_id, tag_ids=tag_ids ) - - def on_message_widget_close(self): - self.message_widget = None - - # Definition of Tray menu - def tray_menu(self, parent_menu): - # Menu for Tray App - from Qt import QtWidgets - menu = QtWidgets.QMenu("Clockify", parent_menu) - menu.setProperty("submenu", "on") - - # Actions - action_show_settings = QtWidgets.QAction("Settings", menu) - action_stop_timer = QtWidgets.QAction("Stop timer", menu) - - menu.addAction(action_show_settings) - menu.addAction(action_stop_timer) - - action_show_settings.triggered.connect(self.show_settings) - action_stop_timer.triggered.connect(self.stop_timer) - - self.action_stop_timer = action_stop_timer - - self.set_menu_visibility() - - parent_menu.addMenu(menu) - - def show_settings(self): - self.widget_settings.input_api_key.setText(self.clockapi.get_api_key()) - self.widget_settings.show() - - def set_menu_visibility(self): - self.action_stop_timer.setVisible(self.bool_timer_run) From 3704b0c4cf022a33d0582c5242f396f1e5de9ff8 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 15 Sep 2021 11:35:11 +0200 Subject: [PATCH 6/6] modified ftrack to not use ITimersManager but defined attributes with predefined methods --- .../default_modules/ftrack/ftrack_module.py | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/openpype/modules/default_modules/ftrack/ftrack_module.py b/openpype/modules/default_modules/ftrack/ftrack_module.py index 1de152535cd..3732e762b46 100644 --- a/openpype/modules/default_modules/ftrack/ftrack_module.py +++ b/openpype/modules/default_modules/ftrack/ftrack_module.py @@ -7,7 +7,6 @@ from openpype_interfaces import ( ITrayModule, IPluginPaths, - ITimersManager, ILaunchHookPaths, ISettingsChangeListener, IFtrackEventHandlerPaths @@ -21,7 +20,6 @@ class FtrackModule( OpenPypeModule, ITrayModule, IPluginPaths, - ITimersManager, ILaunchHookPaths, ISettingsChangeListener ): @@ -61,6 +59,10 @@ def initialize(self, settings): self.user_event_handlers_paths = user_event_handlers_paths self.tray_module = None + # TimersManager connection + self.timers_manager_connector = None + self._timers_manager_module = None + def get_global_environments(self): """Ftrack's global environments.""" return { @@ -102,16 +104,6 @@ def connect_with_modules(self, enabled_modules): elif key == "user": self.user_event_handlers_paths.extend(value) - def start_timer(self, data): - """Implementation of ITimersManager interface.""" - if self.tray_module: - self.tray_module.start_timer_manager(data) - - def stop_timer(self): - """Implementation of ITimersManager interface.""" - if self.tray_module: - self.tray_module.stop_timer_manager() - def on_system_settings_save( self, old_value, new_value, changes, new_value_metadata ): @@ -343,7 +335,10 @@ def create_ftrack_session(self, **session_kwargs): def tray_init(self): from .tray import FtrackTrayWrapper + self.tray_module = FtrackTrayWrapper(self) + # Module is it's own connector to TimersManager + self.timers_manager_connector = self def tray_menu(self, parent_menu): return self.tray_module.tray_menu(parent_menu) @@ -357,3 +352,23 @@ def tray_exit(self): def set_credentials_to_env(self, username, api_key): os.environ["FTRACK_API_USER"] = username or "" os.environ["FTRACK_API_KEY"] = api_key or "" + + # --- TimersManager connection methods --- + def start_timer(self, data): + if self.tray_module: + self.tray_module.start_timer_manager(data) + + def stop_timer(self): + if self.tray_module: + self.tray_module.stop_timer_manager() + + def register_timers_manager(self, timer_manager_module): + self._timers_manager_module = timer_manager_module + + def timer_started(self, data): + if self._timers_manager_module is not None: + self._timers_manager_module.timer_started(self.id, data) + + def timer_stopped(self): + if self._timers_manager_module is not None: + self._timers_manager_module.timer_stopped(self.id)