From 706fd31e7abc84073f60bfeb47222ab1d5fa8ea7 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Oct 2021 14:08:30 +0200 Subject: [PATCH 01/12] PYPE-1905 - renamed and fixed script for creating dummy records for performance testing --- ...go_performance.py => mongo_performance.py} | 45 ++++++++----------- 1 file changed, 18 insertions(+), 27 deletions(-) rename openpype/tests/{test_mongo_performance.py => mongo_performance.py} (82%) diff --git a/openpype/tests/test_mongo_performance.py b/openpype/tests/mongo_performance.py similarity index 82% rename from openpype/tests/test_mongo_performance.py rename to openpype/tests/mongo_performance.py index cd606d6483..1090cc5e82 100644 --- a/openpype/tests/test_mongo_performance.py +++ b/openpype/tests/mongo_performance.py @@ -80,7 +80,7 @@ class TestPerformance(): file_id3 = bson.objectid.ObjectId() self.inserted_ids.extend([file_id, file_id2, file_id3]) - version_str = "v{0:03}".format(i + 1) + version_str = "v{:03d}".format(i + 1) file_name = "test_Cylinder_workfileLookdev_{}.mb".\ format(version_str) @@ -95,7 +95,7 @@ class TestPerformance(): "family": "workfile", "hierarchy": "Assets", "project": {"code": "test", "name": "Test"}, - "version": 1, + "version": i + 1, "asset": "Cylinder", "representation": "mb", "root": self.ROOT_DIR @@ -104,8 +104,8 @@ class TestPerformance(): "name": "mb", "parent": {"oid": '{}'.format(id)}, "data": { - "path": "C:\\projects\\Test\\Assets\\Cylinder\\publish\\workfile\\workfileLookdev\\{}\\{}".format(version_str, file_name), - "template": "{root}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{representation}" + "path": "C:\\projects\\test_performance\\Assets\\Cylinder\\publish\\workfile\\workfileLookdev\\{}\\{}".format(version_str, file_name), # noqa + "template": "{root[work]}\\{project[name]}\\{hierarchy}\\{asset}\\publish\\{family}\\{subset}\\v{version:0>3}\\{project[code]}_{asset}_{subset}_v{version:0>3}<_{output}><.{frame:0>4}>.{representation}" # noqa }, "type": "representation", "schema": "openpype:representation-2.0" @@ -188,30 +188,21 @@ class TestPerformance(): create_files=False): ret = [ { - "path": "{root}" + "/Test/Assets/Cylinder/publish/workfile/" + - "workfileLookdev/v{0:03}/" + - "test_Cylinder_A_workfileLookdev_v{0:03}.dat" - .format(i, i), + "path": "{root[work]}" + "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/v{:03d}/test_Cylinder_A_workfileLookdev_v{:03d}.dat".format(i, i), #noqa "_id": '{}'.format(file_id), "hash": "temphash", "sites": self.get_sites(self.MAX_NUMBER_OF_SITES), "size": random.randint(0, self.MAX_FILE_SIZE_B) }, { - "path": "{root}" + "/Test/Assets/Cylinder/publish/workfile/" + - "workfileLookdev/v{0:03}/" + - "test_Cylinder_B_workfileLookdev_v{0:03}.dat" - .format(i, i), + "path": "{root[work]}" + "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/v{:03d}/test_Cylinder_B_workfileLookdev_v{:03d}.dat".format(i, i), #noqa "_id": '{}'.format(file_id2), "hash": "temphash", "sites": self.get_sites(self.MAX_NUMBER_OF_SITES), "size": random.randint(0, self.MAX_FILE_SIZE_B) }, { - "path": "{root}" + "/Test/Assets/Cylinder/publish/workfile/" + - "workfileLookdev/v{0:03}/" + - "test_Cylinder_C_workfileLookdev_v{0:03}.dat" - .format(i, i), + "path": "{root[work]}" + "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/v{:03d}/test_Cylinder_C_workfileLookdev_v{:03d}.dat".format(i, i), #noqa "_id": '{}'.format(file_id3), "hash": "temphash", "sites": self.get_sites(self.MAX_NUMBER_OF_SITES), @@ -221,7 +212,7 @@ class TestPerformance(): ] if create_files: for f in ret: - path = f.get("path").replace("{root}", self.ROOT_DIR) + path = f.get("path").replace("{root[work]}", self.ROOT_DIR) os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, 'wb') as fp: fp.write(os.urandom(f.get("size"))) @@ -231,26 +222,26 @@ class TestPerformance(): def get_files_doc(self, i, file_id, file_id2, file_id3): ret = {} ret['{}'.format(file_id)] = { - "path": "{root}" + - "/Test/Assets/Cylinder/publish/workfile/workfileLookdev/" - "v001/test_CylinderA_workfileLookdev_v{0:03}.mb".format(i), + "path": "{root[work]}" + + "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/" + "v{:03d}/test_CylinderA_workfileLookdev_v{:03d}.mb".format(i, i), # noqa "hash": "temphash", "sites": ["studio"], "size": 87236 } ret['{}'.format(file_id2)] = { - "path": "{root}" + - "/Test/Assets/Cylinder/publish/workfile/workfileLookdev/" - "v001/test_CylinderB_workfileLookdev_v{0:03}.mb".format(i), + "path": "{root[work]}" + + "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/" + "v{:03d}/test_CylinderB_workfileLookdev_v{:03d}.mb".format(i, i), # noqa "hash": "temphash", "sites": ["studio"], "size": 87236 } ret['{}'.format(file_id3)] = { - "path": "{root}" + - "/Test/Assets/Cylinder/publish/workfile/workfileLookdev/" - "v001/test_CylinderC_workfileLookdev_v{0:03}.mb".format(i), + "path": "{root[work]}" + + "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/" + "v{:03d}/test_CylinderC_workfileLookdev_v{:03d}.mb".format(i, i), # noqa "hash": "temphash", "sites": ["studio"], "size": 87236 @@ -287,7 +278,7 @@ class TestPerformance(): if __name__ == '__main__': tp = TestPerformance('array') - tp.prepare(no_of_records=10, create_files=True) # enable to prepare data + tp.prepare(no_of_records=10000, create_files=True) # enable to prepare data # tp.run(10, 3) # print('-'*50) From a4f710b221f7065835246c04a4594e5dacddf825 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Thu, 21 Oct 2021 14:09:07 +0200 Subject: [PATCH 02/12] PYPE-1905 - added better logging for broken path values --- .../sync_server/providers/abstract_provider.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/modules/default_modules/sync_server/providers/abstract_provider.py b/openpype/modules/default_modules/sync_server/providers/abstract_provider.py index 8ae0ceed79..688a17f14f 100644 --- a/openpype/modules/default_modules/sync_server/providers/abstract_provider.py +++ b/openpype/modules/default_modules/sync_server/providers/abstract_provider.py @@ -201,5 +201,9 @@ class AbstractProvider: msg = "Error in resolving local root from anatomy" log.error(msg) raise ValueError(msg) + except IndexError: + msg = "Path {} contains unfillable placeholder" + log.error(msg) + raise ValueError(msg) return path From 2481972af1bc5b6330d1201799120f5fa5daa8b8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 25 Oct 2021 12:54:06 +0200 Subject: [PATCH 03/12] OP-1905 - add allowDiskUse flag for bigger sets --- .../default_modules/sync_server/tray/models.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/openpype/modules/default_modules/sync_server/tray/models.py b/openpype/modules/default_modules/sync_server/tray/models.py index 5642c5b34a..1c179dcc3a 100644 --- a/openpype/modules/default_modules/sync_server/tray/models.py +++ b/openpype/modules/default_modules/sync_server/tray/models.py @@ -124,7 +124,8 @@ class _SyncRepresentationModel(QtCore.QAbstractTableModel): if not representations: self.query = self.get_query(load_records) - representations = self.dbcon.aggregate(self.query) + representations = self.dbcon.aggregate(pipeline=self.query, + allowDiskUse=True) self.add_page_records(self.active_site, self.remote_site, representations) @@ -159,7 +160,8 @@ class _SyncRepresentationModel(QtCore.QAbstractTableModel): items_to_fetch = min(self._total_records - self._rec_loaded, self.PAGE_SIZE) self.query = self.get_query(self._rec_loaded) - representations = self.dbcon.aggregate(self.query) + representations = self.dbcon.aggregate(pipeline=self.query, + allowDiskUse=True) self.beginInsertRows(index, self._rec_loaded, self._rec_loaded + items_to_fetch - 1) @@ -209,7 +211,8 @@ class _SyncRepresentationModel(QtCore.QAbstractTableModel): # replace('False', 'false').\ # replace('True', 'true').replace('None', 'null')) - representations = self.dbcon.aggregate(self.query) + representations = self.dbcon.aggregate(pipeline=self.query, + allowDiskUse=True) self.refresh(representations) def set_word_filter(self, word_filter): @@ -445,7 +448,8 @@ class SyncRepresentationSummaryModel(_SyncRepresentationModel): self.query = self.get_query() self.default_query = list(self.get_query()) - representations = self.dbcon.aggregate(self.query) + representations = self.dbcon.aggregate(pipeline=self.query, + allowDiskUse=True) self.refresh(representations) self.timer = QtCore.QTimer() @@ -973,7 +977,8 @@ class SyncRepresentationDetailModel(_SyncRepresentationModel): self.sort = self.DEFAULT_SORT self.query = self.get_query() - representations = self.dbcon.aggregate(self.query) + representations = self.dbcon.aggregate(pipeline=self.query, + allowDiskUse=True) self.refresh(representations) self.timer = QtCore.QTimer() From 700c3b30584678117cfada65e0ab80cdf103d897 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Mon, 25 Oct 2021 12:55:18 +0200 Subject: [PATCH 04/12] OP-1905 - create validate project functionality, not yet working in asyncio --- .../sync_server/sync_server.py | 83 +++++++++++++++++++ .../sync_server/sync_server_module.py | 42 ++++++++-- .../sync_server/tray/widgets.py | 15 ++++ 3 files changed, 134 insertions(+), 6 deletions(-) diff --git a/openpype/modules/default_modules/sync_server/sync_server.py b/openpype/modules/default_modules/sync_server/sync_server.py index 2227ec9366..54ed577278 100644 --- a/openpype/modules/default_modules/sync_server/sync_server.py +++ b/openpype/modules/default_modules/sync_server/sync_server.py @@ -3,6 +3,8 @@ import os import asyncio import threading import concurrent.futures +from datetime import datetime +from bson.objectid import ObjectId from concurrent.futures._base import CancelledError from .providers import lib @@ -162,6 +164,80 @@ def resolve_paths(module, file_path, collection, return local_file_path, remote_file_path +def validate_project(module, collection, site_name, both_way=False): + """ + Validate 'collection' of 'site_name' and its local files + + If file present and not marked with a 'site_name' in DB, DB is + updated with site name and file modified date. + """ + module.log.debug("Validation of {} for {} started".format(collection, + site_name)) + query = { + "type": "representation" + } + + representations = list( + module.connection.database[collection].find(query)) + if not representations: + module.log.debug("No repre found") + return + + sites_added = 0 + sites_removed = 0 + for repre in representations: + repre_id = repre["_id"] + # self.remove_site(collection, str(repre_id), site_name) + for repre_file in repre.get("files", []): + try: + has_site = site_name in [site["name"] + for site in repre_file["sites"]] + except TypeError: + module.log.debug("Structure error in {}".format(repre_id)) + continue + + if has_site and not both_way: + continue + + file_path = repre_file.get("path", "") + local_file_path = module.get_local_file_path(collection, + site_name, + file_path) + + if local_file_path and os.path.exists(local_file_path): + module.log.debug("Adding site {} for {}".format(site_name, + repre_id)) + if not has_site: + query = { + "_id": ObjectId(repre_id) + } + created_dt = datetime.fromtimestamp( + os.path.getmtime(local_file_path)) + elem = {"name": site_name, + "created_dt": created_dt} + module._add_site(collection, query, [repre], elem, + site_name=site_name, + file_id=repre_file["_id"]) + sites_added += 1 + else: + if has_site and both_way: + module.log.debug("Removing site {} for {}".\ + format(site_name, repre["_id"])) + module.reset_provider_for_file(collection, + repre["_id"], + file_id=repre_file["_id"], + remove=True) + sites_removed += 1 + + if sites_added % 100 == 0: + module.log.debug("Sites added {}".format(sites_added)) + + module.log.debug("Validation of {} for {} ended".format(collection, + site_name)) + module.log.info("Sites added {}, sites removed {}".format( + sites_added, sites_removed)) + + def site_is_working(module, project_name, site_name): """ Confirm that 'site_name' is configured correctly for 'project_name'. @@ -421,6 +497,13 @@ class SyncServerThread(threading.Thread): periodically. """ while self.is_running: + if self.module.long_running_tasks: + task = self.module.long_running_tasks.pop() + self.module.projects_processed.add(task["project_name"]) + log.info("starting long running") + await self.loop.run_in_executor(None, task["func"]) + log.info("finished long running") + self.module.projects_processed.remove(task["project_name"]) await asyncio.sleep(0.5) tasks = [task for task in asyncio.all_tasks() if task is not asyncio.current_task()] diff --git a/openpype/modules/default_modules/sync_server/sync_server_module.py b/openpype/modules/default_modules/sync_server/sync_server_module.py index f2e9237542..749a065cb3 100644 --- a/openpype/modules/default_modules/sync_server/sync_server_module.py +++ b/openpype/modules/default_modules/sync_server/sync_server_module.py @@ -4,6 +4,7 @@ from datetime import datetime import threading import platform import copy +from collections import deque from avalon.api import AvalonMongoDB @@ -120,6 +121,11 @@ class SyncServerModule(OpenPypeModule, ITrayModule): self._connection = None + # list of long blocking tasks + self.long_running_tasks = deque() + # projects that long tasks are running on + self.projects_processed = set() + """ Start of Public API """ def add_site(self, collection, representation_id, site_name=None, force=False): @@ -197,6 +203,15 @@ class SyncServerModule(OpenPypeModule, ITrayModule): for repre in representations: self.remove_site(collection, repre.get("_id"), site_name, True) + def create_validate_project_task(self, collection, site_name): + from .sync_server import validate_project + task = { + "type": "validate", + "project_name": collection, + "func": lambda: validate_project(self, collection, site_name) + } + self.long_running_tasks.append(task) + def pause_representation(self, collection, representation_id, site_name): """ Sets 'representation_id' as paused, eg. no syncing should be @@ -1347,7 +1362,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule): found = False for repre_file in representation.pop().get("files"): for site in repre_file.get("sites"): - if site["name"] == site_name: + if site.get("name") == site_name: found = True break if not found: @@ -1398,13 +1413,20 @@ class SyncServerModule(OpenPypeModule, ITrayModule): self._update_site(collection, query, update, arr_filter) def _add_site(self, collection, query, representation, elem, site_name, - force=False): + force=False, file_id=None): """ Adds 'site_name' to 'representation' on 'collection' + Args: + representation (list of 1 dict) + file_id (ObjectId) + Use 'force' to remove existing or raises ValueError """ for repre_file in representation.pop().get("files"): + if file_id and file_id != repre_file["_id"]: + continue + for site in repre_file.get("sites"): if site["name"] == site_name: if force: @@ -1417,11 +1439,19 @@ class SyncServerModule(OpenPypeModule, ITrayModule): log.info(msg) raise ValueError(msg) - update = { - "$push": {"files.$[].sites": elem} - } + if not file_id: + update = { + "$push": {"files.$[].sites": elem} + } - arr_filter = [] + arr_filter = [] + else: + update = { + "$push": {"files.$[f].sites": elem} + } + arr_filter = [ + {'f._id': file_id} + ] self._update_site(collection, query, update, arr_filter) diff --git a/openpype/modules/default_modules/sync_server/tray/widgets.py b/openpype/modules/default_modules/sync_server/tray/widgets.py index 45537c1c2e..c660e1aff2 100644 --- a/openpype/modules/default_modules/sync_server/tray/widgets.py +++ b/openpype/modules/default_modules/sync_server/tray/widgets.py @@ -88,6 +88,9 @@ class SyncProjectListWidget(QtWidgets.QWidget): else: icon = self._get_icon("synced") + if project_name in self.sync_server.projects_processed: + icon = self._get_icon("paused") # change + model.appendRow(QtGui.QStandardItem(icon, project_name)) if len(self.sync_server.sync_project_settings.keys()) == 0: @@ -143,6 +146,11 @@ class SyncProjectListWidget(QtWidgets.QWidget): actions_mapping[action] = self._clear_project menu.addAction(action) + if self.project_name not in self.sync_server.projects_processed: + action = QtWidgets.QAction("Validate files on active site") + actions_mapping[action] = self._validate_site + menu.addAction(action) + result = menu.exec_(QtGui.QCursor.pos()) if result: to_run = actions_mapping[result] @@ -167,6 +175,13 @@ class SyncProjectListWidget(QtWidgets.QWidget): self.project_name = None self.refresh() + def _validate_site(self): + if self.project_name: + self.sync_server.create_validate_project_task(self.project_name, + self.local_site) + self.project_name = None + self.refresh() + class _SyncRepresentationWidget(QtWidgets.QWidget): """ From 11f2043fa3d95c5bf54529a23f35eb35c8313d8c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 26 Oct 2021 13:41:37 +0200 Subject: [PATCH 05/12] OP-1905 - added documentation for Validate files on active site --- website/docs/artist_tools.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/website/docs/artist_tools.md b/website/docs/artist_tools.md index f099b48a9a..ae7fbbca49 100644 --- a/website/docs/artist_tools.md +++ b/website/docs/artist_tools.md @@ -474,5 +474,13 @@ Actions accessible by context menu on single (or multiple representations): Double click on any of the representation open Detail dialog with information about all files for particular representation. In this dialog error details could be accessed in the context menu. +#### Context menu on project name Artists can also Pause whole server or specific project for synchronization. In that state no download/upload is being run. -This might be helpful if the artist is not interested in a particular project for a while or wants to save bandwidth data limit for a bit. \ No newline at end of file +This might be helpful if the artist is not interested in a particular project for a while or wants to save bandwidth data limit for a bit. + +Another option is `Validate files on active site`. This option triggers process where all representation of the selected project are looped through, file paths are resolved for active site and +if paths point to local system, paths are physically checked if files are existing. If file exists and representation is not marked to be present on 'active_site' in DB, DB is updated +to follow that. + +This might be useful if artist has representation files that Site Sync doesn't know about (newly attached external drive with representations from studio). +This project might take a while! \ No newline at end of file From 412c730843c65219c9cd869e30a1c90e047ae28b Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 26 Oct 2021 14:03:52 +0200 Subject: [PATCH 06/12] OP-1905 - added Validate files on active site --- .../sync_server/resources/refresh.png | Bin 0 -> 3304 bytes .../sync_server/sync_server.py | 23 +++++++++++------- .../sync_server/sync_server_module.py | 14 +++++++++++ .../sync_server/tray/widgets.py | 15 +++++++++++- 4 files changed, 42 insertions(+), 10 deletions(-) create mode 100644 openpype/modules/default_modules/sync_server/resources/refresh.png diff --git a/openpype/modules/default_modules/sync_server/resources/refresh.png b/openpype/modules/default_modules/sync_server/resources/refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..5ddd181fe67f5ea5810064e65cf1c81802196b99 GIT binary patch literal 3304 zcmbVP2{_d0A0IlAChJI2jj>WPCuSITGm0|f4qbN4j4_$Z%rGnYxTkBR)j!2v2h}!*)c;vGr573DSLK_HN# zoh`u`m?6@!dL{7Y2L(+6lN{I9lMe!|ZIlj~lpSj~fSQw;SXXe1hiKpG=ZSQyFxhcd<)U?GbS6p+WG`rw=iyB4JZU*=F> zfq;ucAcR68TxbC2@aPDXiHQjUiAJE&FaQDLi`fFQ2*&1Xe`6rf_!J(4D`0Tg5Gf?U0AFo5qyD7AYjI#L;e=ccNKGK2xl6f6TqX; zYytr_wWZc@ad;k$Ea325IUMGn)B8~+6gBn_1;ApawnO2PSXcq}qtFoQ*= z(-2%X9ST`;BM#4Da(IAXKs$p!@7v+=jy#SJg9#k)op`x8H0tPjj<*u6K?}7ki*~Z z6F8IrX$pM1PyIjdJMb7l5t5nznnzli(%iw>GWbAL#fzomLi1l-F&U7Bbit7+(lRiI zQl#;xQK5_5jDPKcue3s68bJC#^5QFu&+!om$vm1h9q`sa<{bi19w9B*uXZ5*)5L|d zKNa^kIM6Yq%cW)od@Qv)8XM?wJfN8c_zPx0pjCl(1Z!81n5(J5u5JNpcP)o{R=M{< zeW6jg1KCQh$W3=9l&D8=1<7E7ul@TNu)gA<2+zUb=#a$ORwt#Dyar8Ln(sqH=OlRs zG>cJS8x4h&JgA)>n;nRp?JwOoULGdw4H$nH9QOP}@AKMebIEE4Z(;|=b6=iLg5->( zg{kE4CqEvq35N>eLrLnAHHT0KC-xp|PLNPsxOxuTiqv)}?;_ebxppN^b}46+i4G)3 zIM~WihQz%(K|^LrN}XKanzmK(9;P-Lcivp6cUqsW(${eJl``8=QCr3Bv-bI?9pTPa zE^bP&p`pma4qSo9OibHt1Ui3|DBCw~H>6OT<5$Ce>paM=`c!!VyQ^NyJDBis{go+~ zeIe`Dyzu4WnE`Rj&dXWhs9Q#q)v4jUFl$cj&ZW7JMeJ^HEHQKn z&WgSTtxa8i@8y_a7Ir--b*qnENyiR_gt+?$nwC*-iLXi|^Ws4(#)db#Uw%2v3JdQd z_{#mCl)sR|1pEo@8y|x8O{-O= z&pMP+-VEM-_P!Q*04Az79D4EliGHKvP3fBt6HcUW_{nF&)XONkhTc#UliTxSwaufN z+K%DcP1d(B6C9ieH|N!=bhep;xR~msbJfQiLpO?qdu|3eb!fWR_o$Co=y4-n&!v`K z%$<@gFbFbHKTcAej#9CCd`$B3i{EU;F)q3aNjW&V<5t&Xo+L7_f;Tb2@IKqr+uh!Qe4F|8P4oMN}hJu6Xx(mmGDT8`PG$xn$kb+>LWAzn>&&kk3|{lh)fv#l)?d%i*id zE(*8DUAw@4AD*WizY3e_vPLmdZJcCp9yFtj7G4^X-0b2$+8Jq`D^H*CGb$YlY0k}`;RkQy&4(< zN99I3GV+yB+Y;8=xV9bXj>>=Pp>IE88Q&fi>-<_AHN5w7{yyb){r31_^`AR?3ePD_ zSQ|w@HywRBqE=G`<%%z+0RdU)TzGb@+9a>^?22b0UKpn5`0{D9<2O0 z?M9uxVTQHw~~J_f|KzGer+{ z%LQ8o%Ag(tw~{dKpVKnRI>tkYF*xmnNGR@ppqe`Vr_NpX!1Qddh#!6 z8rK^2FPNCFuXK98Ay8@K$Vm2C2Nl`V;N%+#XC#emPp!IvD2+=ipilM_dcvoV+3jil zm8}qp(;<|ktAxz-_|JC*Ry{%~?ouUoc1G*UshrMkdYx+&N*<_pm&Ge#o% z!d?OL$`tc;-Fw3=<`bK0eoambF5WIL`eE8*`;;)^Zu9RlBj5;#ZcFi)^k2lzhD0dd H`9tI%FQ8(R literal 0 HcmV?d00001 diff --git a/openpype/modules/default_modules/sync_server/sync_server.py b/openpype/modules/default_modules/sync_server/sync_server.py index 54ed577278..7839758975 100644 --- a/openpype/modules/default_modules/sync_server/sync_server.py +++ b/openpype/modules/default_modules/sync_server/sync_server.py @@ -164,15 +164,22 @@ def resolve_paths(module, file_path, collection, return local_file_path, remote_file_path -def validate_project(module, collection, site_name, both_way=False): +def validate_project(module, collection, site_name, remove_missing=False): """ Validate 'collection' of 'site_name' and its local files If file present and not marked with a 'site_name' in DB, DB is updated with site name and file modified date. + + Args: + module (SyncServerModule) + collection (string): project name + site_name (string): active site name + remove_missing (bool): if True remove sites in DB if missing + physically """ module.log.debug("Validation of {} for {} started".format(collection, - site_name)) + site_name)) query = { "type": "representation" } @@ -187,7 +194,6 @@ def validate_project(module, collection, site_name, both_way=False): sites_removed = 0 for repre in representations: repre_id = repre["_id"] - # self.remove_site(collection, str(repre_id), site_name) for repre_file in repre.get("files", []): try: has_site = site_name in [site["name"] @@ -196,17 +202,17 @@ def validate_project(module, collection, site_name, both_way=False): module.log.debug("Structure error in {}".format(repre_id)) continue - if has_site and not both_way: + if has_site and not remove_missing: continue file_path = repre_file.get("path", "") local_file_path = module.get_local_file_path(collection, - site_name, - file_path) + site_name, + file_path) if local_file_path and os.path.exists(local_file_path): module.log.debug("Adding site {} for {}".format(site_name, - repre_id)) + repre_id)) if not has_site: query = { "_id": ObjectId(repre_id) @@ -220,7 +226,7 @@ def validate_project(module, collection, site_name, both_way=False): file_id=repre_file["_id"]) sites_added += 1 else: - if has_site and both_way: + if has_site and remove_missing: module.log.debug("Removing site {} for {}".\ format(site_name, repre["_id"])) module.reset_provider_for_file(collection, @@ -499,7 +505,6 @@ class SyncServerThread(threading.Thread): while self.is_running: if self.module.long_running_tasks: task = self.module.long_running_tasks.pop() - self.module.projects_processed.add(task["project_name"]) log.info("starting long running") await self.loop.run_in_executor(None, task["func"]) log.info("finished long running") diff --git a/openpype/modules/default_modules/sync_server/sync_server_module.py b/openpype/modules/default_modules/sync_server/sync_server_module.py index 749a065cb3..ce6ef6ef7d 100644 --- a/openpype/modules/default_modules/sync_server/sync_server_module.py +++ b/openpype/modules/default_modules/sync_server/sync_server_module.py @@ -204,12 +204,24 @@ class SyncServerModule(OpenPypeModule, ITrayModule): self.remove_site(collection, repre.get("_id"), site_name, True) def create_validate_project_task(self, collection, site_name): + """Adds metadata about project files validation on a queue. + + This process will loop through all representation and check if + their files actually exist on an active site. + + This might be useful for edge cases when artists is switching + between sites, remote site is actually physically mounted and + active site has same file urls etc. + + Task will run on a asyncio loop, shouldn't be blocking. + """ from .sync_server import validate_project task = { "type": "validate", "project_name": collection, "func": lambda: validate_project(self, collection, site_name) } + self.projects_processed.add(collection) self.long_running_tasks.append(task) def pause_representation(self, collection, representation_id, site_name): @@ -742,6 +754,8 @@ class SyncServerModule(OpenPypeModule, ITrayModule): "no syncing possible"). format(str(self.sync_project_settings)), exc_info=True) self.enabled = False + except: + log.info("fck", exc_info=True) def tray_start(self): """ diff --git a/openpype/modules/default_modules/sync_server/tray/widgets.py b/openpype/modules/default_modules/sync_server/tray/widgets.py index c660e1aff2..4ecdd131a5 100644 --- a/openpype/modules/default_modules/sync_server/tray/widgets.py +++ b/openpype/modules/default_modules/sync_server/tray/widgets.py @@ -32,6 +32,8 @@ class SyncProjectListWidget(QtWidgets.QWidget): project_changed = QtCore.Signal() message_generated = QtCore.Signal(str) + REFRESH_SEC = 10000 + def __init__(self, sync_server, parent): super(SyncProjectListWidget, self).__init__(parent) self.setObjectName("ProjectListWidget") @@ -69,6 +71,17 @@ class SyncProjectListWidget(QtWidgets.QWidget): self.remote_site = None self.icons = {} + self.timer = QtCore.QTimer() + self.timer.timeout.connect(self.tick) + self.timer.start(self.REFRESH_SEC) + + def tick(self): + """ + Triggers refresh of model. + """ + self.refresh() + self.timer.start(self.REFRESH_SEC) + def _on_index_change(self, new_idx, _old_idx): project_name = new_idx.data(QtCore.Qt.DisplayRole) @@ -89,7 +102,7 @@ class SyncProjectListWidget(QtWidgets.QWidget): icon = self._get_icon("synced") if project_name in self.sync_server.projects_processed: - icon = self._get_icon("paused") # change + icon = self._get_icon("refresh") model.appendRow(QtGui.QStandardItem(icon, project_name)) From 8595146fe25f3d7be1ce17efe62bf627d6a066b3 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Tue, 26 Oct 2021 16:20:07 +0200 Subject: [PATCH 07/12] Hound --- .../modules/default_modules/sync_server/sync_server.py | 8 ++++---- openpype/tests/mongo_performance.py | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/openpype/modules/default_modules/sync_server/sync_server.py b/openpype/modules/default_modules/sync_server/sync_server.py index 7839758975..c09916898f 100644 --- a/openpype/modules/default_modules/sync_server/sync_server.py +++ b/openpype/modules/default_modules/sync_server/sync_server.py @@ -224,10 +224,10 @@ def validate_project(module, collection, site_name, remove_missing=False): module._add_site(collection, query, [repre], elem, site_name=site_name, file_id=repre_file["_id"]) - sites_added += 1 + sites_added += 1 else: if has_site and remove_missing: - module.log.debug("Removing site {} for {}".\ + module.log.debug("Removing site {} for {}". format(site_name, repre["_id"])) module.reset_provider_for_file(collection, repre["_id"], @@ -240,8 +240,8 @@ def validate_project(module, collection, site_name, remove_missing=False): module.log.debug("Validation of {} for {} ended".format(collection, site_name)) - module.log.info("Sites added {}, sites removed {}".format( - sites_added, sites_removed)) + module.log.info("Sites added {}, sites removed {}".format(sites_added, + sites_removed)) def site_is_working(module, project_name, site_name): diff --git a/openpype/tests/mongo_performance.py b/openpype/tests/mongo_performance.py index 1090cc5e82..9220c6c730 100644 --- a/openpype/tests/mongo_performance.py +++ b/openpype/tests/mongo_performance.py @@ -188,7 +188,7 @@ class TestPerformance(): create_files=False): ret = [ { - "path": "{root[work]}" + "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/v{:03d}/test_Cylinder_A_workfileLookdev_v{:03d}.dat".format(i, i), #noqa + "path": "{root[work]}" + "{root[work]}/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/v{:03d}/test_Cylinder_A_workfileLookdev_v{:03d}.dat".format(i, i), #noqa "_id": '{}'.format(file_id), "hash": "temphash", "sites": self.get_sites(self.MAX_NUMBER_OF_SITES), @@ -223,7 +223,7 @@ class TestPerformance(): ret = {} ret['{}'.format(file_id)] = { "path": "{root[work]}" + - "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/" + "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/" #noqa "v{:03d}/test_CylinderA_workfileLookdev_v{:03d}.mb".format(i, i), # noqa "hash": "temphash", "sites": ["studio"], @@ -232,7 +232,7 @@ class TestPerformance(): ret['{}'.format(file_id2)] = { "path": "{root[work]}" + - "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/" + "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/" #noqa "v{:03d}/test_CylinderB_workfileLookdev_v{:03d}.mb".format(i, i), # noqa "hash": "temphash", "sites": ["studio"], @@ -240,7 +240,7 @@ class TestPerformance(): } ret['{}'.format(file_id3)] = { "path": "{root[work]}" + - "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/" + "/test_performance/Assets/Cylinder/publish/workfile/workfileLookdev/" #noqa "v{:03d}/test_CylinderC_workfileLookdev_v{:03d}.mb".format(i, i), # noqa "hash": "temphash", "sites": ["studio"], @@ -278,7 +278,7 @@ class TestPerformance(): if __name__ == '__main__': tp = TestPerformance('array') - tp.prepare(no_of_records=10000, create_files=True) # enable to prepare data + tp.prepare(no_of_records=10000, create_files=True) # tp.run(10, 3) # print('-'*50) From 2025470663b6ef3f945dc835a434959c9760c517 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 27 Oct 2021 13:53:07 +0200 Subject: [PATCH 08/12] OPPYPE-1905 - fix wrong name of variable --- .../default_modules/sync_server/tray/models.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/openpype/modules/default_modules/sync_server/tray/models.py b/openpype/modules/default_modules/sync_server/tray/models.py index 1c179dcc3a..713e167a6a 100644 --- a/openpype/modules/default_modules/sync_server/tray/models.py +++ b/openpype/modules/default_modules/sync_server/tray/models.py @@ -194,16 +194,16 @@ class _SyncRepresentationModel(QtCore.QAbstractTableModel): else: order = -1 - backup_sort = dict(self.sort) + backup_sort = dict(self.sort_criteria) - self.sort = {self.SORT_BY_COLUMN[index]: order} # reset + self.sort_criteria = {self.SORT_BY_COLUMN[index]: order} # reset # add last one for key, val in backup_sort.items(): if key != '_id' and key != self.SORT_BY_COLUMN[index]: - self.sort[key] = val + self.sort_criteria[key] = val break # add default one - self.sort['_id'] = 1 + self.sort_criteria['_id'] = 1 self.query = self.get_query() # import json @@ -443,7 +443,7 @@ class SyncRepresentationSummaryModel(_SyncRepresentationModel): self.active_site = self.sync_server.get_active_site(self.project) self.remote_site = self.sync_server.get_remote_site(self.project) - self.sort = self.DEFAULT_SORT + self.sort_criteria = self.DEFAULT_SORT self.query = self.get_query() self.default_query = list(self.get_query()) @@ -736,7 +736,7 @@ class SyncRepresentationSummaryModel(_SyncRepresentationModel): ) aggr.extend( - [{"$sort": self.sort}, + [{"$sort": self.sort_criteria}, { '$facet': { 'paginatedResults': [{'$skip': self._rec_loaded}, @@ -974,7 +974,7 @@ class SyncRepresentationDetailModel(_SyncRepresentationModel): self.active_site = self.sync_server.get_active_site(self.project) self.remote_site = self.sync_server.get_remote_site(self.project) - self.sort = self.DEFAULT_SORT + self.sort_criteria = self.DEFAULT_SORT self.query = self.get_query() representations = self.dbcon.aggregate(pipeline=self.query, @@ -1240,7 +1240,7 @@ class SyncRepresentationDetailModel(_SyncRepresentationModel): print(self.column_filtering) aggr.extend([ - {"$sort": self.sort}, + {"$sort": self.sort_criteria}, { '$facet': { 'paginatedResults': [{'$skip': self._rec_loaded}, From cb248a013d47ac5c08f095fa9bfa714e32a40e15 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 29 Oct 2021 10:57:20 +0200 Subject: [PATCH 09/12] OPPYPE-1905 - fixes after review --- .../sync_server/sync_server.py | 80 ----------------- .../sync_server/sync_server_module.py | 89 +++++++++++++++++-- .../sync_server/tray/widgets.py | 73 ++++++++++----- 3 files changed, 135 insertions(+), 107 deletions(-) diff --git a/openpype/modules/default_modules/sync_server/sync_server.py b/openpype/modules/default_modules/sync_server/sync_server.py index c09916898f..9f16c4b945 100644 --- a/openpype/modules/default_modules/sync_server/sync_server.py +++ b/openpype/modules/default_modules/sync_server/sync_server.py @@ -164,86 +164,6 @@ def resolve_paths(module, file_path, collection, return local_file_path, remote_file_path -def validate_project(module, collection, site_name, remove_missing=False): - """ - Validate 'collection' of 'site_name' and its local files - - If file present and not marked with a 'site_name' in DB, DB is - updated with site name and file modified date. - - Args: - module (SyncServerModule) - collection (string): project name - site_name (string): active site name - remove_missing (bool): if True remove sites in DB if missing - physically - """ - module.log.debug("Validation of {} for {} started".format(collection, - site_name)) - query = { - "type": "representation" - } - - representations = list( - module.connection.database[collection].find(query)) - if not representations: - module.log.debug("No repre found") - return - - sites_added = 0 - sites_removed = 0 - for repre in representations: - repre_id = repre["_id"] - for repre_file in repre.get("files", []): - try: - has_site = site_name in [site["name"] - for site in repre_file["sites"]] - except TypeError: - module.log.debug("Structure error in {}".format(repre_id)) - continue - - if has_site and not remove_missing: - continue - - file_path = repre_file.get("path", "") - local_file_path = module.get_local_file_path(collection, - site_name, - file_path) - - if local_file_path and os.path.exists(local_file_path): - module.log.debug("Adding site {} for {}".format(site_name, - repre_id)) - if not has_site: - query = { - "_id": ObjectId(repre_id) - } - created_dt = datetime.fromtimestamp( - os.path.getmtime(local_file_path)) - elem = {"name": site_name, - "created_dt": created_dt} - module._add_site(collection, query, [repre], elem, - site_name=site_name, - file_id=repre_file["_id"]) - sites_added += 1 - else: - if has_site and remove_missing: - module.log.debug("Removing site {} for {}". - format(site_name, repre["_id"])) - module.reset_provider_for_file(collection, - repre["_id"], - file_id=repre_file["_id"], - remove=True) - sites_removed += 1 - - if sites_added % 100 == 0: - module.log.debug("Sites added {}".format(sites_added)) - - module.log.debug("Validation of {} for {} ended".format(collection, - site_name)) - module.log.info("Sites added {}, sites removed {}".format(sites_added, - sites_removed)) - - def site_is_working(module, project_name, site_name): """ Confirm that 'site_name' is configured correctly for 'project_name'. diff --git a/openpype/modules/default_modules/sync_server/sync_server_module.py b/openpype/modules/default_modules/sync_server/sync_server_module.py index ce6ef6ef7d..0484864e8e 100644 --- a/openpype/modules/default_modules/sync_server/sync_server_module.py +++ b/openpype/modules/default_modules/sync_server/sync_server_module.py @@ -215,15 +215,93 @@ class SyncServerModule(OpenPypeModule, ITrayModule): Task will run on a asyncio loop, shouldn't be blocking. """ - from .sync_server import validate_project task = { "type": "validate", "project_name": collection, - "func": lambda: validate_project(self, collection, site_name) + "func": lambda: self.validate_project(collection, site_name) } self.projects_processed.add(collection) self.long_running_tasks.append(task) + def validate_project(self, collection, site_name, remove_missing=False): + """ + Validate 'collection' of 'site_name' and its local files + + If file present and not marked with a 'site_name' in DB, DB is + updated with site name and file modified date. + + Args: + module (SyncServerModule) + collection (string): project name + site_name (string): active site name + remove_missing (bool): if True remove sites in DB if missing + physically + """ + self.log.debug("Validation of {} for {} started".format(collection, + site_name)) + query = { + "type": "representation" + } + + representations = list( + self.connection.database[collection].find(query)) + if not representations: + self.log.debug("No repre found") + return + + sites_added = 0 + sites_removed = 0 + for repre in representations: + repre_id = repre["_id"] + for repre_file in repre.get("files", []): + try: + has_site = site_name in [site["name"] + for site in repre_file["sites"]] + except TypeError: + self.log.debug("Structure error in {}".format(repre_id)) + continue + + if has_site and not remove_missing: + continue + + file_path = repre_file.get("path", "") + local_file_path = self.get_local_file_path(collection, + site_name, + file_path) + + if local_file_path and os.path.exists(local_file_path): + self.log.debug("Adding site {} for {}".format(site_name, + repre_id)) + if not has_site: + query = { + "_id": repre_id + } + created_dt = datetime.fromtimestamp( + os.path.getmtime(local_file_path)) + elem = {"name": site_name, + "created_dt": created_dt} + self._add_site(collection, query, [repre], elem, + site_name=site_name, + file_id=repre_file["_id"]) + sites_added += 1 + else: + if has_site and remove_missing: + self.log.debug("Removing site {} for {}". + format(site_name, repre_id)) + self.reset_provider_for_file(collection, + repre_id, + file_id=repre_file["_id"], + remove=True) + sites_removed += 1 + + if sites_added % 100 == 0: + self.log.debug("Sites added {}".format(sites_added)) + + self.log.debug("Validation of {} for {} ended".format(collection, + site_name)) + self.log.info("Sites added {}, sites removed {}".format(sites_added, + sites_removed)) + def pause_representation(self, collection, representation_id, site_name): """ Sets 'representation_id' as paused, eg. no syncing should be @@ -740,22 +818,23 @@ class SyncServerModule(OpenPypeModule, ITrayModule): self.lock = threading.Lock() try: + self.enabled = False self.sync_server_thread = SyncServerThread(self) from .tray.app import SyncServerWindow self.widget = SyncServerWindow(self) + self.enabled = True except ValueError: log.info("No system setting for sync. Not syncing.", exc_info=True) - self.enabled = False except KeyError: log.info(( "There are not set presets for SyncServer OR " "Credentials provided are invalid, " "no syncing possible"). format(str(self.sync_project_settings)), exc_info=True) - self.enabled = False except: - log.info("fck", exc_info=True) + log.error("Uncaught exception durin start of SyncServer", + exc_info=True) def tray_start(self): """ diff --git a/openpype/modules/default_modules/sync_server/tray/widgets.py b/openpype/modules/default_modules/sync_server/tray/widgets.py index 4ecdd131a5..ce3867beae 100644 --- a/openpype/modules/default_modules/sync_server/tray/widgets.py +++ b/openpype/modules/default_modules/sync_server/tray/widgets.py @@ -32,7 +32,7 @@ class SyncProjectListWidget(QtWidgets.QWidget): project_changed = QtCore.Signal() message_generated = QtCore.Signal(str) - REFRESH_SEC = 10000 + refresh_msec = 10000 def __init__(self, sync_server, parent): super(SyncProjectListWidget, self).__init__(parent) @@ -58,8 +58,8 @@ class SyncProjectListWidget(QtWidgets.QWidget): layout.addWidget(project_list, 1) project_list.customContextMenuRequested.connect(self._on_context_menu) - project_list.selectionModel().currentChanged.connect( - self._on_index_change + project_list.selectionModel().selectionChanged.connect( + self._on_selection_changed ) self.project_model = project_model @@ -71,28 +71,43 @@ class SyncProjectListWidget(QtWidgets.QWidget): self.remote_site = None self.icons = {} - self.timer = QtCore.QTimer() - self.timer.timeout.connect(self.tick) - self.timer.start(self.REFRESH_SEC) + self._selection_changed = False + self._model_reset = False - def tick(self): - """ - Triggers refresh of model. - """ - self.refresh() - self.timer.start(self.REFRESH_SEC) + timer = QtCore.QTimer() + timer.setInterval(self.refresh_msec) + timer.timeout.connect(self.refresh) + timer.start() - def _on_index_change(self, new_idx, _old_idx): - project_name = new_idx.data(QtCore.Qt.DisplayRole) + self.timer = timer + def _on_selection_changed(self, new_selection, _old_selection): + # block involuntary selection changes + if self._selection_changed or self._model_reset: + return + + indexes = new_selection.indexes() + if not indexes: + return + + project_name = indexes[0].data(QtCore.Qt.DisplayRole) + + if self.current_project == project_name: + return + self._selection_changed = True self.current_project = project_name self.project_changed.emit() + self.refresh() + self._selection_changed = False def refresh(self): + selected_index = None model = self.project_model + self._model_reset = True model.clear() + self._model_reset = False - project_name = None + selected_item = None for project_name in self.sync_server.sync_project_settings.\ keys(): if self.sync_server.is_paused() or \ @@ -104,20 +119,34 @@ class SyncProjectListWidget(QtWidgets.QWidget): if project_name in self.sync_server.projects_processed: icon = self._get_icon("refresh") - model.appendRow(QtGui.QStandardItem(icon, project_name)) + item = QtGui.QStandardItem(icon, project_name) + model.appendRow(item) + + if self.current_project == project_name: + selected_item = item + + if selected_item: + selected_index = model.indexFromItem(selected_item) if len(self.sync_server.sync_project_settings.keys()) == 0: model.appendRow(QtGui.QStandardItem(lib.DUMMY_PROJECT)) - self.current_project = self.project_list.currentIndex().data( - QtCore.Qt.DisplayRole - ) if not self.current_project: self.current_project = model.item(0).data(QtCore.Qt.DisplayRole) - if project_name: - self.local_site = self.sync_server.get_active_site(project_name) - self.remote_site = self.sync_server.get_remote_site(project_name) + self.project_model = model + + if selected_index and selected_index.isValid() and \ + not self._selection_changed: + mode = QtCore.QItemSelectionModel.Select | \ + QtCore.QItemSelectionModel.Rows + self.project_list.selectionModel().select(selected_index, mode) + + if self.current_project: + self.local_site = self.sync_server.get_active_site( + self.current_project) + self.remote_site = self.sync_server.get_remote_site( + self.current_project) def _can_edit(self): """Returns true if some site is user local site, eg. could edit""" From 2d9cd8b019f9730faf5bb55a3493818a50d73964 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 29 Oct 2021 11:04:10 +0200 Subject: [PATCH 10/12] OPPYPE-1905 - move initialization of Sync Queue only when needed Unnecessary refreshes were triggered --- .../sync_server/sync_server_module.py | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/openpype/modules/default_modules/sync_server/sync_server_module.py b/openpype/modules/default_modules/sync_server/sync_server_module.py index 0484864e8e..3c23436245 100644 --- a/openpype/modules/default_modules/sync_server/sync_server_module.py +++ b/openpype/modules/default_modules/sync_server/sync_server_module.py @@ -816,25 +816,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule): return self.lock = threading.Lock() - - try: - self.enabled = False - self.sync_server_thread = SyncServerThread(self) - - from .tray.app import SyncServerWindow - self.widget = SyncServerWindow(self) - self.enabled = True - except ValueError: - log.info("No system setting for sync. Not syncing.", exc_info=True) - except KeyError: - log.info(( - "There are not set presets for SyncServer OR " - "Credentials provided are invalid, " - "no syncing possible"). - format(str(self.sync_project_settings)), exc_info=True) - except: - log.error("Uncaught exception durin start of SyncServer", - exc_info=True) + self.sync_server_thread = SyncServerThread(self) def tray_start(self): """ @@ -1619,7 +1601,24 @@ class SyncServerModule(OpenPypeModule, ITrayModule): return int(ld) def show_widget(self): - """Show dialog to enter credentials""" + """Show dialog for Sync Queue""" + try: + self.enabled = False + from .tray.app import SyncServerWindow + self.widget = SyncServerWindow(self) + self.enabled = True + except ValueError: + log.info("No system setting for sync. Not syncing.", exc_info=True) + except KeyError: + log.info(( + "There are not set presets for SyncServer OR " + "Credentials provided are invalid, " + "no syncing possible"). + format(str(self.sync_project_settings)), exc_info=True) + except: + log.error("Uncaught exception durin start of SyncServer", + exc_info=True) + self.widget.show() def _get_success_dict(self, new_file_id): From 289b4c76e0eeac381872932c454e0eb443df6d6a Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 29 Oct 2021 11:10:50 +0200 Subject: [PATCH 11/12] Hound --- openpype/modules/default_modules/sync_server/sync_server.py | 2 -- openpype/modules/default_modules/sync_server/tray/widgets.py | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/modules/default_modules/sync_server/sync_server.py b/openpype/modules/default_modules/sync_server/sync_server.py index 9f16c4b945..6aca2460e3 100644 --- a/openpype/modules/default_modules/sync_server/sync_server.py +++ b/openpype/modules/default_modules/sync_server/sync_server.py @@ -3,8 +3,6 @@ import os import asyncio import threading import concurrent.futures -from datetime import datetime -from bson.objectid import ObjectId from concurrent.futures._base import CancelledError from .providers import lib diff --git a/openpype/modules/default_modules/sync_server/tray/widgets.py b/openpype/modules/default_modules/sync_server/tray/widgets.py index ce3867beae..5e368f9e0b 100644 --- a/openpype/modules/default_modules/sync_server/tray/widgets.py +++ b/openpype/modules/default_modules/sync_server/tray/widgets.py @@ -136,8 +136,9 @@ class SyncProjectListWidget(QtWidgets.QWidget): self.project_model = model - if selected_index and selected_index.isValid() and \ - not self._selection_changed: + if selected_index and \ + selected_index.isValid() and \ + not self._selection_changed: mode = QtCore.QItemSelectionModel.Select | \ QtCore.QItemSelectionModel.Rows self.project_list.selectionModel().select(selected_index, mode) From e983f95c81ef711df23b441ad73b49e7e5122bb8 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 29 Oct 2021 16:12:25 +0200 Subject: [PATCH 12/12] OP-1905 - refactore enabled flag --- .../default_modules/sync_server/sync_server_module.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/modules/default_modules/sync_server/sync_server_module.py b/openpype/modules/default_modules/sync_server/sync_server_module.py index 3c23436245..d60147a989 100644 --- a/openpype/modules/default_modules/sync_server/sync_server_module.py +++ b/openpype/modules/default_modules/sync_server/sync_server_module.py @@ -1602,11 +1602,11 @@ class SyncServerModule(OpenPypeModule, ITrayModule): def show_widget(self): """Show dialog for Sync Queue""" + no_errors = False try: - self.enabled = False from .tray.app import SyncServerWindow self.widget = SyncServerWindow(self) - self.enabled = True + no_errors = True except ValueError: log.info("No system setting for sync. Not syncing.", exc_info=True) except KeyError: @@ -1618,7 +1618,7 @@ class SyncServerModule(OpenPypeModule, ITrayModule): except: log.error("Uncaught exception durin start of SyncServer", exc_info=True) - + self.enabled = no_errors self.widget.show() def _get_success_dict(self, new_file_id):