From e66e661c38ba68bbd95a261a51ca7dae626d7d76 Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Wed, 3 Feb 2021 16:19:09 +0100 Subject: [PATCH] SyncServer GUI - added reset site for whole representation Updated saving to DB with better approach --- pype/modules/sync_server/sync_server.py | 105 +++++++++++------------- pype/modules/sync_server/tray/app.py | 82 ++++++++++++++++-- 2 files changed, 122 insertions(+), 65 deletions(-) diff --git a/pype/modules/sync_server/sync_server.py b/pype/modules/sync_server/sync_server.py index 8fb0dfe955..db5180f0b1 100644 --- a/pype/modules/sync_server/sync_server.py +++ b/pype/modules/sync_server/sync_server.py @@ -557,34 +557,32 @@ class SyncServer(PypeModule, ITrayModule): representation_id = representation.get("_id") file_id = file.get("_id") query = { - "_id": representation_id, - "files._id": file_id + "_id": representation_id } - file_index, _ = self._get_file_info(representation.get('files', []), - file_id) - site_index, _ = self._get_provider_rec(file.get('sites', []), - site) + update = {} if new_file_id: - update["$set"] = self._get_success_dict(file_index, site_index, - new_file_id) + update["$set"] = self._get_success_dict(new_file_id) # reset previous errors if any - update["$unset"] = self._get_error_dict(file_index, site_index, - "", "", "") + update["$unset"] = self._get_error_dict("", "", "") elif progress is not None: - update["$set"] = self._get_progress_dict(file_index, site_index, - progress) + update["$set"] = self._get_progress_dict(progress) else: tries = self._get_tries_count(file, site) tries += 1 - update["$set"] = self._get_error_dict(file_index, site_index, - error, tries) + update["$set"] = self._get_error_dict(error, tries) - self.connection.Session["AVALON_PROJECT"] = collection - self.connection.update_one( + arr_filter = [ + {'s.name': site}, + {'f._id': ObjectId(file_id)} + ] + + self.connection.database[collection].update_one( query, - update + update, + upsert=True, + array_filters=arr_filter ) if progress is not None: @@ -642,7 +640,7 @@ class SyncServer(PypeModule, ITrayModule): return -1, None def reset_provider_for_file(self, collection, representation_id, - file_id, side): + side, file_id=None): """ Reset information about synchronization for particular 'file_id' and provider. @@ -671,24 +669,32 @@ class SyncServer(PypeModule, ITrayModule): else: site_name = remote_site - files = representation[0].get('files', []) - file_index, _ = self._get_file_info(files, - file_id) - site_index, _ = self._get_provider_rec(files[file_index]. - get('sites', []), - site_name) - if file_index >= 0 and site_index >= 0: - elem = {"name": site_name} + elem = {"name": site_name} + + if file_id: update = { - "$set": {"files.{}.sites.{}".format(file_index, site_index): - elem - } + "$set": {"files.$[f].sites.$[s]": elem} } - self.connection.database[collection].update_one( - query, - update - ) + arr_filter = [ + {'s.name': site_name}, + {'f._id': ObjectId(file_id)} + ] + else: + update = { + "$set": {"files.$[].sites.$[s]": elem} + } + + arr_filter = [ + {'s.name': site_name} + ] + + self.connection.database[collection].update_one( + query, + update, + upsert=True, + array_filters=arr_filter + ) def get_loop_delay(self, project_name): """ @@ -703,44 +709,35 @@ class SyncServer(PypeModule, ITrayModule): """Show dialog to enter credentials""" self.widget.show() - def _get_success_dict(self, file_index, site_index, new_file_id): + def _get_success_dict(self, new_file_id): """ Provide success metadata ("id", "created_dt") to be stored in Db. Used in $set: "DICT" part of query. Sites are array inside of array(file), so real indexes for both file and site are needed for upgrade in DB. Args: - file_index: (int) - index of modified file - site_index: (int) - index of modified site of modified file new_file_id: id of created file Returns: (dictionary) """ - val = {"files.{}.sites.{}.id".format(file_index, site_index): - new_file_id, - "files.{}.sites.{}.created_dt".format(file_index, site_index): - datetime.utcnow()} + val = {"files.$[f].sites.$[s].id": new_file_id, + "files.$[f].sites.$[s].created_dt": datetime.utcnow()} return val - def _get_error_dict(self, file_index, site_index, - error="", tries="", progress=""): + def _get_error_dict(self, error="", tries="", progress=""): """ Provide error metadata to be stored in Db. Used for set (error and tries provided) or unset mode. Args: - file_index: (int) - index of modified file - site_index: (int) - index of modified site of modified file error: (string) - message tries: how many times failed Returns: (dictionary) """ - val = {"files.{}.sites.{}.last_failed_dt". - format(file_index, site_index): datetime.utcnow(), - "files.{}.sites.{}.error".format(file_index, site_index): error, - "files.{}.sites.{}.tries".format(file_index, site_index): tries, - "files.{}.sites.{}.progress".format(file_index, site_index): - progress + val = {"files.$[f].sites.$[s].last_failed_dt": datetime.utcnow(), + "files.$[f].sites.$[s].error": error, + "files.$[f].sites.$[s].tries": tries, + "files.$[f].sites.$[s].progress": progress } return val @@ -768,20 +765,16 @@ class SyncServer(PypeModule, ITrayModule): _, rec = self._get_provider_rec(file.get("sites", []), provider) return rec.get("tries", 0) - def _get_progress_dict(self, file_index, site_index, progress): + def _get_progress_dict(self, progress): """ Provide progress metadata to be stored in Db. Used during upload/download for GUI to show. Args: - file_index: (int) - index of modified file - site_index: (int) - index of modified site of modified file progress: (float) - 0-1 progress of upload/download Returns: (dictionary) """ - val = {"files.{}.sites.{}.progress". - format(file_index, site_index): progress - } + val = {"files.$[f].sites.$[s].progress": progress} return val def _get_local_file_path(self, file, local_root): diff --git a/pype/modules/sync_server/tray/app.py b/pype/modules/sync_server/tray/app.py index 48c93fc7e5..d0ff28ba22 100644 --- a/pype/modules/sync_server/tray/app.py +++ b/pype/modules/sync_server/tray/app.py @@ -139,6 +139,7 @@ class SyncRepresentationWidget(QtWidgets.QWidget): self.sync_server = sync_server self._selected_id = None # keep last selected _id + self.representation_id = None self.filter = QtWidgets.QLineEdit() self.filter.setPlaceholderText("Filter representations..") @@ -202,7 +203,8 @@ class SyncRepresentationWidget(QtWidgets.QWidget): def _selection_changed(self, new_selection): index = self.selection_model.currentIndex() - self._selected_id = self.table_view.model().data(index, Qt.UserRole) + self.representation_id = \ + self.table_view.model().data(index, Qt.UserRole) def _set_selection(self): """ @@ -236,6 +238,66 @@ class SyncRepresentationWidget(QtWidgets.QWidget): if not point_index.isValid(): return + self.item = self.table_view.model()._data[point_index.row()] + self.representation_id = self.item._id + log.debug("menu representation _id:: {}". + format(self.representation_id)) + + menu = QtWidgets.QMenu() + actions_mapping = {} + + if self.item.state == STATUS[1]: + action = QtWidgets.QAction("Open error detail") + actions_mapping[action] = self._show_detail + menu.addAction(action) + + remote_site, remote_progress = self.item.remote_site.split() + if float(remote_progress) == 1.0: + action = QtWidgets.QAction("Reset local site") + actions_mapping[action] = self._reset_local_site + menu.addAction(action) + + local_site, local_progress = self.item.local_site.split() + if float(local_progress) == 1.0: + action = QtWidgets.QAction("Reset remote site") + actions_mapping[action] = self._reset_remote_site + menu.addAction(action) + + if not actions_mapping: + action = QtWidgets.QAction("< No action >") + actions_mapping[action] = None + menu.addAction(action) + + result = menu.exec_(QtGui.QCursor.pos()) + if result: + to_run = actions_mapping[result] + if to_run: + to_run() + + def _reset_local_site(self): + """ + Removes errors or success metadata for particular file >> forces + redo of upload/download + """ + self.sync_server.reset_provider_for_file( + self.table_view.model()._project, + self.representation_id, + 'local' + ) + self.table_view.model().refresh() + + def _reset_remote_site(self): + """ + Removes errors or success metadata for particular file >> forces + redo of upload/download + """ + self.sync_server.reset_provider_for_file( + self.table_view.model()._project, + self.representation_id, + 'remote' + ) + self.table_view.model().refresh() + class SyncRepresentationModel(QtCore.QAbstractTableModel): PAGE_SIZE = 20 @@ -517,7 +579,7 @@ class SyncRepresentationModel(QtCore.QAbstractTableModel): value = self.data(index, Qt.UserRole) if value == id: return index - return index + return None def get_default_query(self, limit=0): """ @@ -916,13 +978,13 @@ class SyncRepresentationDetailWidget(QtWidgets.QWidget): menu.addAction(action) remote_site, remote_progress = self.item.remote_site.split() - if remote_progress == '1': + if float(remote_progress) == 1.0: action = QtWidgets.QAction("Reset local site") actions_mapping[action] = self._reset_local_site menu.addAction(action) local_site, local_progress = self.item.local_site.split() - if local_progress == '1': + if float(local_progress) == 1.0: action = QtWidgets.QAction("Reset remote site") actions_mapping[action] = self._reset_remote_site menu.addAction(action) @@ -946,8 +1008,9 @@ class SyncRepresentationDetailWidget(QtWidgets.QWidget): self.sync_server.reset_provider_for_file( self.table_view.model()._project, self.representation_id, - self.item._id, - 'local') + 'local', + self.item._id) + self.table_view.model().refresh() def _reset_remote_site(self): """ @@ -957,8 +1020,9 @@ class SyncRepresentationDetailWidget(QtWidgets.QWidget): self.sync_server.reset_provider_for_file( self.table_view.model()._project, self.representation_id, - self.item._id, - 'remote') + 'remote', + self.item._id) + self.table_view.model().refresh() class SyncRepresentationDetailModel(QtCore.QAbstractTableModel): @@ -1208,7 +1272,7 @@ class SyncRepresentationDetailModel(QtCore.QAbstractTableModel): value = self.data(index, Qt.UserRole) if value == id: return index - return index + return None def get_default_query(self, limit=0): """