From 0fc014e12f151f326f8a3e6b0cf71a3ab4b23996 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 16:49:48 +0200 Subject: [PATCH 01/28] add contants to clockify module --- pype/modules/clockify/constants.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 pype/modules/clockify/constants.py diff --git a/pype/modules/clockify/constants.py b/pype/modules/clockify/constants.py new file mode 100644 index 0000000000..5603bbc641 --- /dev/null +++ b/pype/modules/clockify/constants.py @@ -0,0 +1,17 @@ +import os +import appdirs + + +CLOCKIFY_FTRACK_SERVER_PATH = os.path.join( + os.path.dirname(__file__), "ftrack", "server" +) +CLOCKIFY_FTRACK_USER_PATH = os.path.join( + os.path.dirname(__file__), "ftrack", "user" +) +CREDENTIALS_PATH = os.path.normpath(os.path.join( + appdirs.user_data_dir("pype-app", "pype"), + "clockify.json" +)) + +ADMIN_PERMISSION_NAMES = ["WORKSPACE_OWN", "WORKSPACE_ADMIN"] +CLOCKIFY_ENDPOINT = "https://api.clockify.me/api/" From 7635c680170cf1b162a7314771021ffb07bfa126 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 16:50:37 +0200 Subject: [PATCH 02/28] moved clockify action to ftrack/user/ structure --- .../{ftrack_actions => ftrack/user}/action_clockify_sync.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pype/modules/clockify/{ftrack_actions => ftrack/user}/action_clockify_sync.py (100%) diff --git a/pype/modules/clockify/ftrack_actions/action_clockify_sync.py b/pype/modules/clockify/ftrack/user/action_clockify_sync.py similarity index 100% rename from pype/modules/clockify/ftrack_actions/action_clockify_sync.py rename to pype/modules/clockify/ftrack/user/action_clockify_sync.py From a021deb5635106ec64921d6d1191292ad8ba6856 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 16:50:56 +0200 Subject: [PATCH 03/28] created copy of clockify sync action to server folder --- .../ftrack/server/action_clockify_sync.py | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 pype/modules/clockify/ftrack/server/action_clockify_sync.py diff --git a/pype/modules/clockify/ftrack/server/action_clockify_sync.py b/pype/modules/clockify/ftrack/server/action_clockify_sync.py new file mode 100644 index 0000000000..0ba4c3a265 --- /dev/null +++ b/pype/modules/clockify/ftrack/server/action_clockify_sync.py @@ -0,0 +1,122 @@ +import json +from pype.modules.ftrack.lib import BaseAction, statics_icon +from pype.modules.clockify import ClockifyAPI + + +class SyncClocify(BaseAction): + '''Synchronise project names and task types.''' + + #: Action identifier. + identifier = 'clockify.sync' + #: Action label. + label = 'Sync To Clockify' + #: Action description. + description = 'Synchronise data to Clockify workspace' + #: roles that are allowed to register this action + role_list = ["Pypeclub", "Administrator", "project Manager"] + #: icon + icon = statics_icon("app_icons", "clockify-white.png") + + #: CLockifyApi + clockapi = ClockifyAPI() + + def discover(self, session, entities, event): + if ( + len(entities) == 1 + and entities[0].entity_type.lower() == "project" + ): + return True + return False + + def launch(self, session, entities, event): + self.clockapi.set_api() + if self.clockapi.workspace_id is None: + return { + "success": False, + "message": "Clockify Workspace or API key are not set!" + } + + if self.clockapi.validate_workspace_perm() is False: + return { + "success": False, + "message": "Missing permissions for this action!" + } + + # JOB SETTINGS + userId = event['source']['user']['id'] + user = session.query('User where id is ' + userId).one() + + job = session.create('Job', { + 'user': user, + 'status': 'running', + 'data': json.dumps({ + 'description': 'Sync Ftrack to Clockify' + }) + }) + session.commit() + + project_entity = entities[0] + if project_entity.entity_type.lower() != "project": + project_entity = self.get_project_from_entity(project_entity) + + project_name = project_entity["full_name"] + self.log.info( + "Synchronization of project \"{}\" to clockify begins.".format( + project_name + ) + ) + task_types = ( + project_entity["project_schema"]["_task_type_schema"]["types"] + ) + task_type_names = [ + task_type["name"] for task_type in task_types + ] + try: + clockify_projects = self.clockapi.get_projects() + if project_name not in clockify_projects: + response = self.clockapi.add_project(project_name) + if "id" not in response: + self.log.warning( + "Project \"{}\" can't be created. Response: {}".format( + project_name, response + ) + ) + return { + "success": False, + "message": ( + "Can't create clockify project \"{}\"." + " Unexpected error." + ).format(project_name) + } + + clockify_workspace_tags = self.clockapi.get_tags() + for task_type_name in task_type_names: + if task_type_name in clockify_workspace_tags: + self.log.debug( + "Task \"{}\" already exist".format(task_type_name) + ) + continue + + response = self.clockapi.add_tag(task_type_name) + if "id" not in response: + self.log.warning( + "Task \"{}\" can't be created. Response: {}".format( + task_type_name, response + ) + ) + + job["status"] = "done" + + except Exception: + pass + + finally: + if job["status"] != "done": + job["status"] = "failed" + session.commit() + + return True + + +def register(session, **kw): + SyncClocify(session).register() From 433a67b2dacf4e1d9c29e90cebc698af6c14ae65 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 16:52:56 +0200 Subject: [PATCH 04/28] ClockifyApi is not singleton anymore --- pype/modules/clockify/clockify_api.py | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/pype/modules/clockify/clockify_api.py b/pype/modules/clockify/clockify_api.py index 86365a9352..d8df2d8990 100644 --- a/pype/modules/clockify/clockify_api.py +++ b/pype/modules/clockify/clockify_api.py @@ -6,29 +6,7 @@ import datetime import appdirs -class Singleton(type): - _instances = {} - - def __call__(cls, *args, **kwargs): - if cls not in cls._instances: - cls._instances[cls] = super( - Singleton, cls - ).__call__(*args, **kwargs) - return cls._instances[cls] - - -class ClockifyAPI(metaclass=Singleton): - endpoint = "https://api.clockify.me/api/" - headers = {"X-Api-Key": None} - app_dir = os.path.normpath(appdirs.user_data_dir('pype-app', 'pype')) - file_name = 'clockify.json' - fpath = os.path.join(app_dir, file_name) - admin_permission_names = ['WORKSPACE_OWN', 'WORKSPACE_ADMIN'] - master_parent = None - workspace = None - workspace_id = None - - def set_master(self, master_parent): +class ClockifyAPI: self.master_parent = master_parent def verify_api(self): From b793e8bd79ce4d58031d45987c32cfcde9f65a58 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 16:55:57 +0200 Subject: [PATCH 05/28] using endpoint constant in clockify api --- pype/modules/clockify/clockify_api.py | 38 +++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/pype/modules/clockify/clockify_api.py b/pype/modules/clockify/clockify_api.py index d8df2d8990..eafe95e3bd 100644 --- a/pype/modules/clockify/clockify_api.py +++ b/pype/modules/clockify/clockify_api.py @@ -3,7 +3,7 @@ import re import requests import json import datetime -import appdirs +from . import CLOCKIFY_ENDPOINT class ClockifyAPI: @@ -31,7 +31,7 @@ class ClockifyAPI: test_headers = {'X-Api-Key': api_key} action_url = 'workspaces/' response = requests.get( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=test_headers ) if response.status_code != 200: @@ -48,7 +48,7 @@ class ClockifyAPI: workspace_id, user_id ) response = requests.get( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers ) user_permissions = response.json() @@ -60,7 +60,7 @@ class ClockifyAPI: def get_user_id(self): action_url = 'v1/user/' response = requests.get( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers ) # this regex is neccessary: UNICODE strings are crashing @@ -120,7 +120,7 @@ class ClockifyAPI: def get_workspaces(self): action_url = 'workspaces/' response = requests.get( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers ) return { @@ -132,7 +132,7 @@ class ClockifyAPI: workspace_id = self.workspace_id action_url = 'workspaces/{}/projects/'.format(workspace_id) response = requests.get( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers ) @@ -147,7 +147,7 @@ class ClockifyAPI: workspace_id, project_id ) response = requests.get( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers ) @@ -158,7 +158,7 @@ class ClockifyAPI: workspace_id = self.workspace_id action_url = 'workspaces/{}/tags/'.format(workspace_id) response = requests.get( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers ) @@ -173,7 +173,7 @@ class ClockifyAPI: workspace_id, project_id ) response = requests.get( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers ) @@ -255,7 +255,7 @@ class ClockifyAPI: "tagIds": tag_ids } response = requests.post( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers, json=body ) @@ -272,7 +272,7 @@ class ClockifyAPI: workspace_id ) response = requests.get( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers ) try: @@ -302,7 +302,7 @@ class ClockifyAPI: "end": self.get_current_time() } response = requests.put( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers, json=body ) @@ -315,7 +315,7 @@ class ClockifyAPI: workspace_id = self.workspace_id action_url = 'workspaces/{}/timeEntries/'.format(workspace_id) response = requests.get( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers ) return response.json()[:quantity] @@ -327,7 +327,7 @@ class ClockifyAPI: workspace_id, tid ) response = requests.delete( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers ) return response.json() @@ -348,7 +348,7 @@ class ClockifyAPI: "billable": "true" } response = requests.post( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers, json=body ) @@ -358,7 +358,7 @@ class ClockifyAPI: action_url = 'workspaces/' body = {"name": name} response = requests.post( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers, json=body ) @@ -377,7 +377,7 @@ class ClockifyAPI: "projectId": project_id } response = requests.post( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers, json=body ) @@ -391,7 +391,7 @@ class ClockifyAPI: "name": name } response = requests.post( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers, json=body ) @@ -406,7 +406,7 @@ class ClockifyAPI: workspace_id, project_id ) response = requests.delete( - self.endpoint + action_url, + CLOCKIFY_ENDPOINT + action_url, headers=self.headers, ) return response.json() From a0eba1199bbb27e46b4efe26903a44f3c3b70342 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 16:58:10 +0200 Subject: [PATCH 06/28] added more constants in usage --- pype/modules/clockify/clockify_api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pype/modules/clockify/clockify_api.py b/pype/modules/clockify/clockify_api.py index eafe95e3bd..c4eb3bbba0 100644 --- a/pype/modules/clockify/clockify_api.py +++ b/pype/modules/clockify/clockify_api.py @@ -53,7 +53,7 @@ class ClockifyAPI: ) user_permissions = response.json() for perm in user_permissions: - if perm['name'] in self.admin_permission_names: + if perm['name'] in ADMIN_PERMISSION_NAMES: return True return False @@ -102,18 +102,18 @@ class ClockifyAPI: def get_api_key(self): api_key = None try: - file = open(self.fpath, 'r') + file = open(CREDENTIALS_JSON_PATH, 'r') api_key = json.load(file).get('api_key', None) if api_key == '': api_key = None except Exception: - file = open(self.fpath, 'w') + file = open(CREDENTIALS_JSON_PATH, 'w') file.close() return api_key def save_api_key(self, api_key): data = {'api_key': api_key} - file = open(self.fpath, 'w') + file = open(CREDENTIALS_JSON_PATH, 'w') file.write(json.dumps(data)) file.close() From 50a1545d0f8cb6a811c5f2e9c895e895b1ebc31c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 16:58:29 +0200 Subject: [PATCH 07/28] removed attributes are in init now --- pype/modules/clockify/clockify_api.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pype/modules/clockify/clockify_api.py b/pype/modules/clockify/clockify_api.py index c4eb3bbba0..8ebed302fe 100644 --- a/pype/modules/clockify/clockify_api.py +++ b/pype/modules/clockify/clockify_api.py @@ -3,11 +3,15 @@ import re import requests import json import datetime -from . import CLOCKIFY_ENDPOINT +from . import CLOCKIFY_ENDPOINT, ADMIN_PERMISSION_NAMES, CREDENTIALS_JSON_PATH class ClockifyAPI: + def __init__(self, workspace_name=None, api_key=None, master_parent=None): + self.workspace_name = workspace_name self.master_parent = master_parent + self.workspace_id = None + self.headers = {"X-Api-Key": api_key} def verify_api(self): for key, value in self.headers.items(): @@ -76,9 +80,9 @@ class ClockifyAPI: def set_workspace(self, name=None): if name is None: name = os.environ.get('CLOCKIFY_WORKSPACE', None) - self.workspace = name + self.workspace_name = name self.workspace_id = None - if self.workspace is None: + if self.workspace_name is None: return try: result = self.validate_workspace() @@ -93,7 +97,7 @@ class ClockifyAPI: def validate_workspace(self, name=None): if name is None: - name = self.workspace + name = self.workspace_name all_workspaces = self.get_workspaces() if name in all_workspaces: return all_workspaces[name] From 562fabe94df095379104bd67a4b48127b71b038d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 16:58:53 +0200 Subject: [PATCH 08/28] added constants to clockify init --- pype/modules/clockify/__init__.py | 7 +++++++ pype/modules/clockify/constants.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pype/modules/clockify/__init__.py b/pype/modules/clockify/__init__.py index 0ee2189fa5..0ea58b5b00 100644 --- a/pype/modules/clockify/__init__.py +++ b/pype/modules/clockify/__init__.py @@ -1,3 +1,10 @@ +from .constants import ( + CLOCKIFY_ENDPOINT, + ADMIN_PERMISSION_NAMES, + CREDENTIALS_JSON_PATH, + CLOCKIFY_FTRACK_USER_PATH, + CLOCKIFY_FTRACK_SERVER_PATH +) from .clockify_api import ClockifyAPI from .widget_settings import ClockifySettings from .widget_message import MessageWidget diff --git a/pype/modules/clockify/constants.py b/pype/modules/clockify/constants.py index 5603bbc641..38ad4b64cf 100644 --- a/pype/modules/clockify/constants.py +++ b/pype/modules/clockify/constants.py @@ -8,7 +8,7 @@ CLOCKIFY_FTRACK_SERVER_PATH = os.path.join( CLOCKIFY_FTRACK_USER_PATH = os.path.join( os.path.dirname(__file__), "ftrack", "user" ) -CREDENTIALS_PATH = os.path.normpath(os.path.join( +CREDENTIALS_JSON_PATH = os.path.normpath(os.path.join( appdirs.user_data_dir("pype-app", "pype"), "clockify.json" )) From 0de373f94fd19bcc0421edad3918862a1377d0ba Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 16:59:46 +0200 Subject: [PATCH 09/28] formatting fix --- pype/modules/clockify/clockify.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pype/modules/clockify/clockify.py b/pype/modules/clockify/clockify.py index 02b322c1c6..247bdaf0b1 100644 --- a/pype/modules/clockify/clockify.py +++ b/pype/modules/clockify/clockify.py @@ -195,9 +195,10 @@ class ClockifyModule: ).format(project_name)) msg = ( - "Project \"{}\" is not in Clockify Workspace \"{}\"." + "Project \"{}\" is not" + " in Clockify Workspace \"{}\"." "

Please inform your Project Manager." - ).format(project_name, str(self.clockapi.workspace)) + ).format(project_name, str(self.clockapi.workspace_name)) self.message_widget = MessageWidget( self.main_parent, msg, "Clockify - Info Message" From eeb5b54cef05c91cc610e68a0406d6f022fd3410 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 17:00:35 +0200 Subject: [PATCH 10/28] using constant in clockify module --- pype/modules/clockify/clockify.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/pype/modules/clockify/clockify.py b/pype/modules/clockify/clockify.py index 247bdaf0b1..5191727b33 100644 --- a/pype/modules/clockify/clockify.py +++ b/pype/modules/clockify/clockify.py @@ -3,11 +3,12 @@ import threading from pype.api import Logger from avalon import style from Qt import QtWidgets -from . import ClockifySettings, ClockifyAPI, MessageWidget +from . import ( + ClockifySettings, ClockifyAPI, MessageWidget, CLOCKIFY_FTRACK_USER_PATH +) class ClockifyModule: - workspace_name = None def __init__(self, main_parent=None, parent=None): @@ -50,14 +51,12 @@ class ClockifyModule: def process_modules(self, modules): if 'FtrackModule' in modules: - actions_path = os.path.sep.join([ - os.path.dirname(__file__), - 'ftrack_actions' - ]) current = os.environ.get('FTRACK_ACTIONS_PATH', '') if current: current += os.pathsep - os.environ['FTRACK_ACTIONS_PATH'] = current + actions_path + os.environ['FTRACK_ACTIONS_PATH'] = ( + current + CLOCKIFY_FTRACK_USER_PATH + ) if 'AvalonApps' in modules: from launcher import lib From 774f7953dd9b0e488f738b005370ba4334bc6b38 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 17:00:57 +0200 Subject: [PATCH 11/28] clockify module can handle new clockify api --- pype/modules/clockify/clockify.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pype/modules/clockify/clockify.py b/pype/modules/clockify/clockify.py index 5191727b33..4c99fb8a15 100644 --- a/pype/modules/clockify/clockify.py +++ b/pype/modules/clockify/clockify.py @@ -21,7 +21,7 @@ class ClockifyModule: self.main_parent = main_parent self.parent = parent - self.clockapi = ClockifyAPI() + self.clockapi = ClockifyAPI(master_parent=self) self.message_widget = None self.widget_settings = ClockifySettings(main_parent, self) self.widget_settings_required = None @@ -32,8 +32,6 @@ class ClockifyModule: self.bool_api_key_set = False self.bool_workspace_set = False self.bool_timer_run = False - - self.clockapi.set_master(self) self.bool_api_key_set = self.clockapi.set_api() def tray_start(self): From 489bed82a6b83bbc9ea7c6bf4631d974e894ca44 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 17:02:18 +0200 Subject: [PATCH 12/28] sync to clockify changed label --- pype/modules/clockify/ftrack/user/action_clockify_sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/clockify/ftrack/user/action_clockify_sync.py b/pype/modules/clockify/ftrack/user/action_clockify_sync.py index 0ba4c3a265..50ec455b13 100644 --- a/pype/modules/clockify/ftrack/user/action_clockify_sync.py +++ b/pype/modules/clockify/ftrack/user/action_clockify_sync.py @@ -9,7 +9,7 @@ class SyncClocify(BaseAction): #: Action identifier. identifier = 'clockify.sync' #: Action label. - label = 'Sync To Clockify' + label = 'Sync To Clockify (local)' #: Action description. description = 'Synchronise data to Clockify workspace' #: roles that are allowed to register this action From c840f31b598fbca6f0a6aa66499bb99316b3afa2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 17:06:12 +0200 Subject: [PATCH 13/28] renamed clockify action file name --- .../{action_clockify_sync.py => action_clockify_sync_local.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename pype/modules/clockify/ftrack/user/{action_clockify_sync.py => action_clockify_sync_local.py} (99%) diff --git a/pype/modules/clockify/ftrack/user/action_clockify_sync.py b/pype/modules/clockify/ftrack/user/action_clockify_sync_local.py similarity index 99% rename from pype/modules/clockify/ftrack/user/action_clockify_sync.py rename to pype/modules/clockify/ftrack/user/action_clockify_sync_local.py index 50ec455b13..a7385c4774 100644 --- a/pype/modules/clockify/ftrack/user/action_clockify_sync.py +++ b/pype/modules/clockify/ftrack/user/action_clockify_sync_local.py @@ -7,7 +7,7 @@ class SyncClocify(BaseAction): '''Synchronise project names and task types.''' #: Action identifier. - identifier = 'clockify.sync' + identifier = 'clockify.sync.local' #: Action label. label = 'Sync To Clockify (local)' #: Action description. From e97000b26d3a7a8c7c2fac022168b5cf03b87aa1 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 17:06:39 +0200 Subject: [PATCH 14/28] renamed server action --- .../{action_clockify_sync.py => action_clockify_sync_server.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename pype/modules/clockify/ftrack/server/{action_clockify_sync.py => action_clockify_sync_server.py} (100%) diff --git a/pype/modules/clockify/ftrack/server/action_clockify_sync.py b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py similarity index 100% rename from pype/modules/clockify/ftrack/server/action_clockify_sync.py rename to pype/modules/clockify/ftrack/server/action_clockify_sync_server.py From 789f965defd60fffa012b55d30ff73420a165de5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 17:07:07 +0200 Subject: [PATCH 15/28] initial modifications of clockify server action --- .../server/action_clockify_sync_server.py | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py index 0ba4c3a265..f09b1cc746 100644 --- a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py +++ b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py @@ -1,23 +1,16 @@ import json -from pype.modules.ftrack.lib import BaseAction, statics_icon +from pype.modules.ftrack.lib import BaseAction from pype.modules.clockify import ClockifyAPI -class SyncClocify(BaseAction): +class SyncClocifyServer(BaseAction): '''Synchronise project names and task types.''' - #: Action identifier. - identifier = 'clockify.sync' - #: Action label. - label = 'Sync To Clockify' - #: Action description. - description = 'Synchronise data to Clockify workspace' - #: roles that are allowed to register this action + identifier = "clockify.sync.server" + label = "Sync To Clockify (server)" + description = "Synchronise data to Clockify workspace" role_list = ["Pypeclub", "Administrator", "project Manager"] - #: icon - icon = statics_icon("app_icons", "clockify-white.png") - #: CLockifyApi clockapi = ClockifyAPI() def discover(self, session, entities, event): @@ -28,6 +21,18 @@ class SyncClocify(BaseAction): return True return False + def register(self): + self.session.event_hub.subscribe( + "topic=ftrack.action.discover", + self._discover, + priority=self.priority + ) + + launch_subscription = ( + "topic=ftrack.action.launch and data.actionIdentifier={}" + ).format(self.identifier) + self.session.event_hub.subscribe(launch_subscription, self._launch) + def launch(self, session, entities, event): self.clockapi.set_api() if self.clockapi.workspace_id is None: From e38de6ed581d77f89b4242c0a5b30e63a1bb943e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 17:09:24 +0200 Subject: [PATCH 16/28] allow discovering only for discover roles --- .../server/action_clockify_sync_server.py | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py index f09b1cc746..19deb05e6d 100644 --- a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py +++ b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py @@ -9,16 +9,30 @@ class SyncClocifyServer(BaseAction): identifier = "clockify.sync.server" label = "Sync To Clockify (server)" description = "Synchronise data to Clockify workspace" - role_list = ["Pypeclub", "Administrator", "project Manager"] + + discover_role_list = ["Pypeclub", "Administrator", "project Manager"] clockapi = ClockifyAPI() def discover(self, session, entities, event): if ( - len(entities) == 1 - and entities[0].entity_type.lower() == "project" + len(entities) != 1 + or entities[0].entity_type.lower() == "project" ): - return True + return False + + # Get user and check his roles + user_id = event.get("source", {}).get("user", {}).get("id") + if not user_id: + return False + + user = session.query("User where id is \"{}\"".format(user_id)).first() + if not user: + return False + + for role in user["user_security_roles"]: + if role["security_role"]["name"] in self.discover_role_list: + return True return False def register(self): @@ -124,4 +138,4 @@ class SyncClocifyServer(BaseAction): def register(session, **kw): - SyncClocify(session).register() + SyncClocifyServer(session).register() From de9a075f04b24afea517daeaf4486a68e855fc5c Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 17:25:59 +0200 Subject: [PATCH 17/28] modified headers access in clockify --- pype/modules/clockify/clockify_api.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pype/modules/clockify/clockify_api.py b/pype/modules/clockify/clockify_api.py index 8ebed302fe..a8eefe13a4 100644 --- a/pype/modules/clockify/clockify_api.py +++ b/pype/modules/clockify/clockify_api.py @@ -11,7 +11,11 @@ class ClockifyAPI: self.workspace_name = workspace_name self.master_parent = master_parent self.workspace_id = None - self.headers = {"X-Api-Key": api_key} + self.api_key = api_key + + @property + def headers(self): + return {"X-Api-Key": self.api_key} def verify_api(self): for key, value in self.headers.items(): @@ -24,7 +28,7 @@ class ClockifyAPI: api_key = self.get_api_key() if api_key is not None and self.validate_api_key(api_key) is True: - self.headers["X-Api-Key"] = api_key + self.api_key = api_key self.set_workspace() if self.master_parent: self.master_parent.signed_in() From 8dfca2378c9a0653c76f4713f50b18943822647d Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 17:26:13 +0200 Subject: [PATCH 18/28] minor tweaks --- .../server/action_clockify_sync_server.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py index 19deb05e6d..7e3c266b72 100644 --- a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py +++ b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py @@ -62,15 +62,13 @@ class SyncClocifyServer(BaseAction): } # JOB SETTINGS - userId = event['source']['user']['id'] - user = session.query('User where id is ' + userId).one() + user_id = event["source"]["user"]["id"] + user = session.query("User where id is " + user_id).one() - job = session.create('Job', { - 'user': user, - 'status': 'running', - 'data': json.dumps({ - 'description': 'Sync Ftrack to Clockify' - }) + job = session.create("Job", { + "user": user, + "status": "running", + "data": json.dumps({"description": "Sync Ftrack to Clockify"}) }) session.commit() @@ -127,7 +125,10 @@ class SyncClocifyServer(BaseAction): job["status"] = "done" except Exception: - pass + self.log.warning( + "Synchronization to clockify failed.", + exc_info=True + ) finally: if job["status"] != "done": From 4f244d187b75677435161aa6790ef4542a4a7df0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 17:48:34 +0200 Subject: [PATCH 19/28] clockify api is set in initialization part of action on server --- .../server/action_clockify_sync_server.py | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py index 7e3c266b72..0a844c9ef5 100644 --- a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py +++ b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py @@ -1,3 +1,4 @@ +import os import json from pype.modules.ftrack.lib import BaseAction from pype.modules.clockify import ClockifyAPI @@ -12,7 +13,32 @@ class SyncClocifyServer(BaseAction): discover_role_list = ["Pypeclub", "Administrator", "project Manager"] - clockapi = ClockifyAPI() + def __init__(self, *args, **kwargs): + super(SyncClocifyServer, self).__init__(*args, **kwargs) + + self.workspace_name = os.environ.get("CLOCKIFY_WORKSPACE") + api_key = os.environ.get("CLOCKIFY_API_KEY") + self.clockapi = ClockifyAPI(self.workspace_name, api_key) + self.api_key = api_key + + if api_key is None: + modified_key = "None" + else: + str_len = int(len(api_key) / 2) + start_replace = int(len(api_key) / 4) + modified_key = "" + for idx in range(len(api_key)): + if idx >= start_replace and idx < start_replace + str_len: + replacement = "X" + else: + replacement = api_key[idx] + modified_key += replacement + + self.log.info( + "Clockify info. Workspace: \"{}\" API key: \"{}\"".format( + str(self.workspace_name), str(modified_key) + ) + ) def discover(self, session, entities, event): if ( @@ -48,7 +74,6 @@ class SyncClocifyServer(BaseAction): self.session.event_hub.subscribe(launch_subscription, self._launch) def launch(self, session, entities, event): - self.clockapi.set_api() if self.clockapi.workspace_id is None: return { "success": False, From eb30a53af8c6327a9f3d5d2f642a74032527147a Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 17:49:12 +0200 Subject: [PATCH 20/28] changed class name --- .../clockify/ftrack/user/action_clockify_sync_local.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pype/modules/clockify/ftrack/user/action_clockify_sync_local.py b/pype/modules/clockify/ftrack/user/action_clockify_sync_local.py index a7385c4774..528614eeba 100644 --- a/pype/modules/clockify/ftrack/user/action_clockify_sync_local.py +++ b/pype/modules/clockify/ftrack/user/action_clockify_sync_local.py @@ -3,7 +3,7 @@ from pype.modules.ftrack.lib import BaseAction, statics_icon from pype.modules.clockify import ClockifyAPI -class SyncClocify(BaseAction): +class SyncClocifyLocal(BaseAction): '''Synchronise project names and task types.''' #: Action identifier. @@ -119,4 +119,4 @@ class SyncClocify(BaseAction): def register(session, **kw): - SyncClocify(session).register() + SyncClocifyLocal(session).register() From 98c1587da7ad6f26d517526e7c8f534d9f35f77e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 18:02:50 +0200 Subject: [PATCH 21/28] added clockify registration to ftrack event processor --- .../ftrack_server/sub_event_processor.py | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/pype/modules/ftrack/ftrack_server/sub_event_processor.py b/pype/modules/ftrack/ftrack_server/sub_event_processor.py index d7bb7a53b3..de0f0459a9 100644 --- a/pype/modules/ftrack/ftrack_server/sub_event_processor.py +++ b/pype/modules/ftrack/ftrack_server/sub_event_processor.py @@ -9,7 +9,7 @@ from pype.modules.ftrack.ftrack_server.lib import ( SocketSession, ProcessEventHub, TOPIC_STATUS_SERVER ) import ftrack_api -from pype.api import Logger +from pype.api import Logger, config log = Logger().get_logger("Event processor") @@ -55,6 +55,42 @@ def register(session): ) +def clockify_module_registration(): + module_name = "Clockify" + + menu_items = config.get_presets()["tray"]["menu_items"] + if not menu_items["item_usage"][module_name]: + return + + api_key = os.environ.get("CLOCKIFY_API_KEY") + if not api_key: + log.warning("Clockify API key is not set.") + return + + workspace_name = os.environ.get("CLOCKIFY_WORKSPACE") + if not workspace_name: + workspace_name = ( + menu_items + .get("attributes", {}) + .get(module_name, {}) + .get("workspace_name", {}) + ) + + if not workspace_name: + log.warning("Clockify Workspace is not set.") + return + + os.environ["CLOCKIFY_WORKSPACE"] = workspace_name + + from pype.modules.clockify import CLOCKIFY_FTRACK_SERVER_PATH + + current = os.environ.get("FTRACK_EVENTS_PATH") or "" + if current: + current += os.pathsep + os.environ["FTRACK_EVENTS_PATH"] = current + CLOCKIFY_FTRACK_SERVER_PATH + return True + + def main(args): port = int(args[-1]) # Create a TCP/IP socket @@ -66,6 +102,11 @@ def main(args): sock.connect(server_address) sock.sendall(b"CreatedProcess") + try: + clockify_result = clockify_module_registration() + except Exception: + log.info("Clockify registration failed.", exc_info=True) + try: session = SocketSession( auto_connect_event_hub=True, sock=sock, Eventhub=ProcessEventHub From eb4153a7865a7ebe941c2a3f076cde680e05c9fc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 18:08:03 +0200 Subject: [PATCH 22/28] added clockifyapikey and clockifyworkspace arguments to ftrack event server script --- .../ftrack/ftrack_server/event_server_cli.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pype/modules/ftrack/ftrack_server/event_server_cli.py b/pype/modules/ftrack/ftrack_server/event_server_cli.py index 73c7abfc5d..bf51c37290 100644 --- a/pype/modules/ftrack/ftrack_server/event_server_cli.py +++ b/pype/modules/ftrack/ftrack_server/event_server_cli.py @@ -522,6 +522,21 @@ def main(argv): help="Load creadentials from apps dir", action="store_true" ) + parser.add_argument( + "-clockifyapikey", type=str, + help=( + "Enter API key for Clockify actions." + " (default from environment: $CLOCKIFY_API_KEY)" + ) + ) + parser.add_argument( + "-clockifyworkspace", type=str, + help=( + "Enter workspace for Clockify." + " (default from module presets or " + "environment: $CLOCKIFY_WORKSPACE)" + ) + ) ftrack_url = os.environ.get('FTRACK_SERVER') username = os.environ.get('FTRACK_API_USER') api_key = os.environ.get('FTRACK_API_KEY') @@ -546,6 +561,12 @@ def main(argv): if kwargs.ftrackapikey: api_key = kwargs.ftrackapikey + if kwargs.clockifyworkspace: + os.environ["CLOCKIFY_WORKSPACE"] = kwargs.clockifyworkspace + + if kwargs.clockifyapikey: + os.environ["CLOCKIFY_API_KEY"] = kwargs.clockifyapikey + legacy = kwargs.legacy # Check url regex and accessibility ftrack_url = check_ftrack_url(ftrack_url) From ec9ff173379cf6601414445bac41b621272a4a16 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 18:32:45 +0200 Subject: [PATCH 23/28] removed workspace_name from clockify api init --- pype/modules/clockify/clockify_api.py | 8 ++++---- .../ftrack/server/action_clockify_sync_server.py | 9 ++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pype/modules/clockify/clockify_api.py b/pype/modules/clockify/clockify_api.py index a8eefe13a4..ca642a1c21 100644 --- a/pype/modules/clockify/clockify_api.py +++ b/pype/modules/clockify/clockify_api.py @@ -7,10 +7,10 @@ from . import CLOCKIFY_ENDPOINT, ADMIN_PERMISSION_NAMES, CREDENTIALS_JSON_PATH class ClockifyAPI: - def __init__(self, workspace_name=None, api_key=None, master_parent=None): - self.workspace_name = workspace_name - self.master_parent = master_parent + def __init__(self, api_key=None, master_parent=None): + self.workspace_name = None self.workspace_id = None + self.master_parent = master_parent self.api_key = api_key @property @@ -73,7 +73,7 @@ class ClockifyAPI: ) # this regex is neccessary: UNICODE strings are crashing # during json serialization - id_regex ='\"{1}id\"{1}\:{1}\"{1}\w+\"{1}' + id_regex = '\"{1}id\"{1}\:{1}\"{1}\w+\"{1}' result = re.findall(id_regex, str(response.content)) if len(result) != 1: # replace with log and better message? diff --git a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py index 0a844c9ef5..442d1f92df 100644 --- a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py +++ b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py @@ -16,11 +16,10 @@ class SyncClocifyServer(BaseAction): def __init__(self, *args, **kwargs): super(SyncClocifyServer, self).__init__(*args, **kwargs) - self.workspace_name = os.environ.get("CLOCKIFY_WORKSPACE") + workspace_name = os.environ.get("CLOCKIFY_WORKSPACE") api_key = os.environ.get("CLOCKIFY_API_KEY") - self.clockapi = ClockifyAPI(self.workspace_name, api_key) - self.api_key = api_key - + self.clockapi = ClockifyAPI(api_key) + self.clockapi.set_workspace(workspace_name) if api_key is None: modified_key = "None" else: @@ -36,7 +35,7 @@ class SyncClocifyServer(BaseAction): self.log.info( "Clockify info. Workspace: \"{}\" API key: \"{}\"".format( - str(self.workspace_name), str(modified_key) + str(workspace_name), str(modified_key) ) ) From 2cfe2e139307236af803428ae33cffe87d6f5b0e Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 18:32:58 +0200 Subject: [PATCH 24/28] fix discover condition --- .../clockify/ftrack/server/action_clockify_sync_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py index 442d1f92df..94b2f21da4 100644 --- a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py +++ b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py @@ -42,7 +42,7 @@ class SyncClocifyServer(BaseAction): def discover(self, session, entities, event): if ( len(entities) != 1 - or entities[0].entity_type.lower() == "project" + or entities[0].entity_type.lower() != "project" ): return False From 0318a5c35cf4829843dbfee5acd13428fab51aba Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 18:44:18 +0200 Subject: [PATCH 25/28] removed unused variable --- pype/modules/ftrack/ftrack_server/sub_event_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pype/modules/ftrack/ftrack_server/sub_event_processor.py b/pype/modules/ftrack/ftrack_server/sub_event_processor.py index de0f0459a9..baef2ec5f6 100644 --- a/pype/modules/ftrack/ftrack_server/sub_event_processor.py +++ b/pype/modules/ftrack/ftrack_server/sub_event_processor.py @@ -103,7 +103,7 @@ def main(args): sock.sendall(b"CreatedProcess") try: - clockify_result = clockify_module_registration() + clockify_module_registration() except Exception: log.info("Clockify registration failed.", exc_info=True) From b6b3ea0f35836d364ed186cf8faf82f08247d692 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 19:56:09 +0200 Subject: [PATCH 26/28] cleaned clockify imports --- pype/modules/clockify/__init__.py | 10 ---------- pype/modules/clockify/clockify.py | 6 +++--- pype/modules/clockify/clockify_api.py | 4 +++- .../ftrack/server/action_clockify_sync_server.py | 2 +- .../clockify/ftrack/user/action_clockify_sync_local.py | 2 +- .../modules/clockify/launcher_actions/ClockifyStart.py | 2 +- pype/modules/clockify/launcher_actions/ClockifySync.py | 2 +- .../ftrack/ftrack_server/sub_event_processor.py | 2 +- 8 files changed, 11 insertions(+), 19 deletions(-) diff --git a/pype/modules/clockify/__init__.py b/pype/modules/clockify/__init__.py index 0ea58b5b00..8e11d2f5f4 100644 --- a/pype/modules/clockify/__init__.py +++ b/pype/modules/clockify/__init__.py @@ -1,13 +1,3 @@ -from .constants import ( - CLOCKIFY_ENDPOINT, - ADMIN_PERMISSION_NAMES, - CREDENTIALS_JSON_PATH, - CLOCKIFY_FTRACK_USER_PATH, - CLOCKIFY_FTRACK_SERVER_PATH -) -from .clockify_api import ClockifyAPI -from .widget_settings import ClockifySettings -from .widget_message import MessageWidget from .clockify import ClockifyModule CLASS_DEFINIION = ClockifyModule diff --git a/pype/modules/clockify/clockify.py b/pype/modules/clockify/clockify.py index 4c99fb8a15..fea15a1bea 100644 --- a/pype/modules/clockify/clockify.py +++ b/pype/modules/clockify/clockify.py @@ -3,9 +3,9 @@ import threading from pype.api import Logger from avalon import style from Qt import QtWidgets -from . import ( - ClockifySettings, ClockifyAPI, MessageWidget, CLOCKIFY_FTRACK_USER_PATH -) +from .widgets import ClockifySettings, MessageWidget +from .clockify_api import ClockifyAPI +from .constants import CLOCKIFY_FTRACK_USER_PATH class ClockifyModule: diff --git a/pype/modules/clockify/clockify_api.py b/pype/modules/clockify/clockify_api.py index ca642a1c21..0a09c65628 100644 --- a/pype/modules/clockify/clockify_api.py +++ b/pype/modules/clockify/clockify_api.py @@ -3,7 +3,9 @@ import re import requests import json import datetime -from . import CLOCKIFY_ENDPOINT, ADMIN_PERMISSION_NAMES, CREDENTIALS_JSON_PATH +from .constants import ( + CLOCKIFY_ENDPOINT, ADMIN_PERMISSION_NAMES, CREDENTIALS_JSON_PATH +) class ClockifyAPI: diff --git a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py index 94b2f21da4..ae911f6258 100644 --- a/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py +++ b/pype/modules/clockify/ftrack/server/action_clockify_sync_server.py @@ -1,7 +1,7 @@ import os import json from pype.modules.ftrack.lib import BaseAction -from pype.modules.clockify import ClockifyAPI +from pype.modules.clockify.clockify_api import ClockifyAPI class SyncClocifyServer(BaseAction): diff --git a/pype/modules/clockify/ftrack/user/action_clockify_sync_local.py b/pype/modules/clockify/ftrack/user/action_clockify_sync_local.py index 528614eeba..e74bf3dbb3 100644 --- a/pype/modules/clockify/ftrack/user/action_clockify_sync_local.py +++ b/pype/modules/clockify/ftrack/user/action_clockify_sync_local.py @@ -1,6 +1,6 @@ import json from pype.modules.ftrack.lib import BaseAction, statics_icon -from pype.modules.clockify import ClockifyAPI +from pype.modules.clockify.clockify_api import ClockifyAPI class SyncClocifyLocal(BaseAction): diff --git a/pype/modules/clockify/launcher_actions/ClockifyStart.py b/pype/modules/clockify/launcher_actions/ClockifyStart.py index d5e9164977..f97360662f 100644 --- a/pype/modules/clockify/launcher_actions/ClockifyStart.py +++ b/pype/modules/clockify/launcher_actions/ClockifyStart.py @@ -1,6 +1,6 @@ from avalon import api, io from pype.api import Logger -from pype.modules.clockify import ClockifyAPI +from pype.modules.clockify.clockify_api import ClockifyAPI log = Logger().get_logger(__name__, "clockify_start") diff --git a/pype/modules/clockify/launcher_actions/ClockifySync.py b/pype/modules/clockify/launcher_actions/ClockifySync.py index 0f20d1dce1..a77c038076 100644 --- a/pype/modules/clockify/launcher_actions/ClockifySync.py +++ b/pype/modules/clockify/launcher_actions/ClockifySync.py @@ -1,5 +1,5 @@ from avalon import api, io -from pype.modules.clockify import ClockifyAPI +from pype.modules.clockify.clockify_api import ClockifyAPI from pype.api import Logger log = Logger().get_logger(__name__, "clockify_sync") diff --git a/pype/modules/ftrack/ftrack_server/sub_event_processor.py b/pype/modules/ftrack/ftrack_server/sub_event_processor.py index baef2ec5f6..4a3241dd4f 100644 --- a/pype/modules/ftrack/ftrack_server/sub_event_processor.py +++ b/pype/modules/ftrack/ftrack_server/sub_event_processor.py @@ -82,7 +82,7 @@ def clockify_module_registration(): os.environ["CLOCKIFY_WORKSPACE"] = workspace_name - from pype.modules.clockify import CLOCKIFY_FTRACK_SERVER_PATH + from pype.modules.clockify.constants import CLOCKIFY_FTRACK_SERVER_PATH current = os.environ.get("FTRACK_EVENTS_PATH") or "" if current: From 3b0fae9fa25055e73d99086d4348d6f9d5e42b86 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Wed, 22 Jul 2020 19:56:21 +0200 Subject: [PATCH 27/28] clockify widgets are in one file --- pype/modules/clockify/widget_message.py | 92 ------------------- .../{widget_settings.py => widgets.py} | 90 +++++++++++++++++- 2 files changed, 89 insertions(+), 93 deletions(-) delete mode 100644 pype/modules/clockify/widget_message.py rename pype/modules/clockify/{widget_settings.py => widgets.py} (66%) diff --git a/pype/modules/clockify/widget_message.py b/pype/modules/clockify/widget_message.py deleted file mode 100644 index 9e4fad1df1..0000000000 --- a/pype/modules/clockify/widget_message.py +++ /dev/null @@ -1,92 +0,0 @@ -from Qt import QtCore, QtGui, QtWidgets -from avalon import style -from pype.api import resources - - -class MessageWidget(QtWidgets.QWidget): - - SIZE_W = 300 - SIZE_H = 130 - - closed = QtCore.Signal() - - def __init__(self, parent=None, messages=[], title="Message"): - - super(MessageWidget, self).__init__() - - self._parent = parent - - # Icon - if parent and hasattr(parent, 'icon'): - self.setWindowIcon(parent.icon) - else: - icon = QtGui.QIcon(resources.pype_icon_filepath()) - self.setWindowIcon(icon) - - self.setWindowFlags( - QtCore.Qt.WindowCloseButtonHint | - QtCore.Qt.WindowMinimizeButtonHint - ) - - # Font - self.font = QtGui.QFont() - self.font.setFamily("DejaVu Sans Condensed") - self.font.setPointSize(9) - self.font.setBold(True) - self.font.setWeight(50) - self.font.setKerning(True) - - # Size setting - self.resize(self.SIZE_W, self.SIZE_H) - self.setMinimumSize(QtCore.QSize(self.SIZE_W, self.SIZE_H)) - self.setMaximumSize(QtCore.QSize(self.SIZE_W+100, self.SIZE_H+100)) - - # Style - self.setStyleSheet(style.load_stylesheet()) - - self.setLayout(self._ui_layout(messages)) - self.setWindowTitle(title) - - def _ui_layout(self, messages): - if not messages: - messages = ["*Misssing messages (This is a bug)*", ] - - elif not isinstance(messages, (tuple, list)): - messages = [messages, ] - - main_layout = QtWidgets.QVBoxLayout(self) - - labels = [] - for message in messages: - label = QtWidgets.QLabel(message) - label.setFont(self.font) - label.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) - label.setTextFormat(QtCore.Qt.RichText) - label.setWordWrap(True) - - labels.append(label) - main_layout.addWidget(label) - - btn_close = QtWidgets.QPushButton("Close") - btn_close.setToolTip('Close this window') - btn_close.clicked.connect(self.on_close_clicked) - - btn_group = QtWidgets.QHBoxLayout() - btn_group.addStretch(1) - btn_group.addWidget(btn_close) - - main_layout.addLayout(btn_group) - - self.labels = labels - self.btn_group = btn_group - self.btn_close = btn_close - self.main_layout = main_layout - - return main_layout - - def on_close_clicked(self): - self.close() - - def close(self, *args, **kwargs): - self.closed.emit() - super(MessageWidget, self).close(*args, **kwargs) diff --git a/pype/modules/clockify/widget_settings.py b/pype/modules/clockify/widgets.py similarity index 66% rename from pype/modules/clockify/widget_settings.py rename to pype/modules/clockify/widgets.py index 7e5ee300bb..dc57a48ecb 100644 --- a/pype/modules/clockify/widget_settings.py +++ b/pype/modules/clockify/widgets.py @@ -1,9 +1,97 @@ -import os from Qt import QtCore, QtGui, QtWidgets from avalon import style from pype.api import resources +class MessageWidget(QtWidgets.QWidget): + + SIZE_W = 300 + SIZE_H = 130 + + closed = QtCore.Signal() + + def __init__(self, parent=None, messages=[], title="Message"): + + super(MessageWidget, self).__init__() + + self._parent = parent + + # Icon + if parent and hasattr(parent, 'icon'): + self.setWindowIcon(parent.icon) + else: + icon = QtGui.QIcon(resources.pype_icon_filepath()) + self.setWindowIcon(icon) + + self.setWindowFlags( + QtCore.Qt.WindowCloseButtonHint | + QtCore.Qt.WindowMinimizeButtonHint + ) + + # Font + self.font = QtGui.QFont() + self.font.setFamily("DejaVu Sans Condensed") + self.font.setPointSize(9) + self.font.setBold(True) + self.font.setWeight(50) + self.font.setKerning(True) + + # Size setting + self.resize(self.SIZE_W, self.SIZE_H) + self.setMinimumSize(QtCore.QSize(self.SIZE_W, self.SIZE_H)) + self.setMaximumSize(QtCore.QSize(self.SIZE_W+100, self.SIZE_H+100)) + + # Style + self.setStyleSheet(style.load_stylesheet()) + + self.setLayout(self._ui_layout(messages)) + self.setWindowTitle(title) + + def _ui_layout(self, messages): + if not messages: + messages = ["*Misssing messages (This is a bug)*", ] + + elif not isinstance(messages, (tuple, list)): + messages = [messages, ] + + main_layout = QtWidgets.QVBoxLayout(self) + + labels = [] + for message in messages: + label = QtWidgets.QLabel(message) + label.setFont(self.font) + label.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) + label.setTextFormat(QtCore.Qt.RichText) + label.setWordWrap(True) + + labels.append(label) + main_layout.addWidget(label) + + btn_close = QtWidgets.QPushButton("Close") + btn_close.setToolTip('Close this window') + btn_close.clicked.connect(self.on_close_clicked) + + btn_group = QtWidgets.QHBoxLayout() + btn_group.addStretch(1) + btn_group.addWidget(btn_close) + + main_layout.addLayout(btn_group) + + self.labels = labels + self.btn_group = btn_group + self.btn_close = btn_close + self.main_layout = main_layout + + return main_layout + + def on_close_clicked(self): + self.close() + + def close(self, *args, **kwargs): + self.closed.emit() + super(MessageWidget, self).close(*args, **kwargs) + + class ClockifySettings(QtWidgets.QWidget): SIZE_W = 300 From 60501b17632297ddd77d07c437c31cfc59da03ae Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 24 Jul 2020 12:30:28 +0200 Subject: [PATCH 28/28] added time check for limit of 10 request in 1 second --- pype/modules/clockify/clockify_api.py | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pype/modules/clockify/clockify_api.py b/pype/modules/clockify/clockify_api.py index 0a09c65628..d88b2ef8df 100644 --- a/pype/modules/clockify/clockify_api.py +++ b/pype/modules/clockify/clockify_api.py @@ -1,5 +1,6 @@ import os import re +import time import requests import json import datetime @@ -8,12 +9,27 @@ from .constants import ( ) +def time_check(obj): + if obj.request_counter < 10: + obj.request_counter += 1 + return + + wait_time = 1 - (time.time() - obj.request_time) + if wait_time > 0: + time.sleep(wait_time) + + obj.request_time = time.time() + obj.request_counter = 0 + + class ClockifyAPI: def __init__(self, api_key=None, master_parent=None): self.workspace_name = None self.workspace_id = None self.master_parent = master_parent self.api_key = api_key + self.request_counter = 0 + self.request_time = time.time() @property def headers(self): @@ -40,6 +56,7 @@ class ClockifyAPI: def validate_api_key(self, api_key): test_headers = {'X-Api-Key': api_key} action_url = 'workspaces/' + time_check(self) response = requests.get( CLOCKIFY_ENDPOINT + action_url, headers=test_headers @@ -57,6 +74,7 @@ class ClockifyAPI: action_url = "/workspaces/{}/users/{}/permissions".format( workspace_id, user_id ) + time_check(self) response = requests.get( CLOCKIFY_ENDPOINT + action_url, headers=self.headers @@ -69,6 +87,7 @@ class ClockifyAPI: def get_user_id(self): action_url = 'v1/user/' + time_check(self) response = requests.get( CLOCKIFY_ENDPOINT + action_url, headers=self.headers @@ -129,6 +148,7 @@ class ClockifyAPI: def get_workspaces(self): action_url = 'workspaces/' + time_check(self) response = requests.get( CLOCKIFY_ENDPOINT + action_url, headers=self.headers @@ -141,6 +161,7 @@ class ClockifyAPI: if workspace_id is None: workspace_id = self.workspace_id action_url = 'workspaces/{}/projects/'.format(workspace_id) + time_check(self) response = requests.get( CLOCKIFY_ENDPOINT + action_url, headers=self.headers @@ -156,6 +177,7 @@ class ClockifyAPI: action_url = 'workspaces/{}/projects/{}/'.format( workspace_id, project_id ) + time_check(self) response = requests.get( CLOCKIFY_ENDPOINT + action_url, headers=self.headers @@ -167,6 +189,7 @@ class ClockifyAPI: if workspace_id is None: workspace_id = self.workspace_id action_url = 'workspaces/{}/tags/'.format(workspace_id) + time_check(self) response = requests.get( CLOCKIFY_ENDPOINT + action_url, headers=self.headers @@ -182,6 +205,7 @@ class ClockifyAPI: action_url = 'workspaces/{}/projects/{}/tasks/'.format( workspace_id, project_id ) + time_check(self) response = requests.get( CLOCKIFY_ENDPOINT + action_url, headers=self.headers @@ -264,6 +288,7 @@ class ClockifyAPI: "taskId": task_id, "tagIds": tag_ids } + time_check(self) response = requests.post( CLOCKIFY_ENDPOINT + action_url, headers=self.headers, @@ -281,6 +306,7 @@ class ClockifyAPI: action_url = 'workspaces/{}/timeEntries/inProgress'.format( workspace_id ) + time_check(self) response = requests.get( CLOCKIFY_ENDPOINT + action_url, headers=self.headers @@ -311,6 +337,7 @@ class ClockifyAPI: "tagIds": current["tagIds"], "end": self.get_current_time() } + time_check(self) response = requests.put( CLOCKIFY_ENDPOINT + action_url, headers=self.headers, @@ -324,6 +351,7 @@ class ClockifyAPI: if workspace_id is None: workspace_id = self.workspace_id action_url = 'workspaces/{}/timeEntries/'.format(workspace_id) + time_check(self) response = requests.get( CLOCKIFY_ENDPOINT + action_url, headers=self.headers @@ -336,6 +364,7 @@ class ClockifyAPI: action_url = 'workspaces/{}/timeEntries/{}'.format( workspace_id, tid ) + time_check(self) response = requests.delete( CLOCKIFY_ENDPOINT + action_url, headers=self.headers @@ -357,6 +386,7 @@ class ClockifyAPI: "color": "#f44336", "billable": "true" } + time_check(self) response = requests.post( CLOCKIFY_ENDPOINT + action_url, headers=self.headers, @@ -367,6 +397,7 @@ class ClockifyAPI: def add_workspace(self, name): action_url = 'workspaces/' body = {"name": name} + time_check(self) response = requests.post( CLOCKIFY_ENDPOINT + action_url, headers=self.headers, @@ -386,6 +417,7 @@ class ClockifyAPI: "name": name, "projectId": project_id } + time_check(self) response = requests.post( CLOCKIFY_ENDPOINT + action_url, headers=self.headers, @@ -400,6 +432,7 @@ class ClockifyAPI: body = { "name": name } + time_check(self) response = requests.post( CLOCKIFY_ENDPOINT + action_url, headers=self.headers, @@ -415,6 +448,7 @@ class ClockifyAPI: action_url = '/workspaces/{}/projects/{}'.format( workspace_id, project_id ) + time_check(self) response = requests.delete( CLOCKIFY_ENDPOINT + action_url, headers=self.headers,