diff --git a/docs/changes/5.5.1.md b/docs/changes/5.5.1.md index b1151875bb..a2885c66c7 100644 --- a/docs/changes/5.5.1.md +++ b/docs/changes/5.5.1.md @@ -12,7 +12,7 @@ Release date: `2024-xx-xx` ### Direct Transfer -- [NXDRIVE-2](https://jira.nuxeo.com/browse/NXDRIVE-2): +- [NXDRIVE-2942](https://jira.nuxeo.com/browse/NXDRIVE-2942): Improve folder selection on Direct Transfer screen ### Task Management - [NXDRIVE-2](https://jira.nuxeo.com/browse/NXDRIVE-2): diff --git a/nxdrive/__main__.py b/nxdrive/__main__.py index 0609ba2198..2c26909aa5 100644 --- a/nxdrive/__main__.py +++ b/nxdrive/__main__.py @@ -2,6 +2,7 @@ In this file we cannot use a relative import here, else Drive will not start when packaged. See https://github.com/pyinstaller/pyinstaller/issues/2560 """ + import locale import platform import signal diff --git a/nxdrive/behavior.py b/nxdrive/behavior.py index 487941eba4..1c6f0a84df 100644 --- a/nxdrive/behavior.py +++ b/nxdrive/behavior.py @@ -11,6 +11,7 @@ Allow or disallow server deletions. """ + from types import SimpleNamespace Behavior = SimpleNamespace(server_deletion=True) diff --git a/nxdrive/client/local/__init__.py b/nxdrive/client/local/__init__.py index 912d966266..86a4014bd9 100644 --- a/nxdrive/client/local/__init__.py +++ b/nxdrive/client/local/__init__.py @@ -1,4 +1,5 @@ """ API to access local resources for synchronization. """ + from .base import FileInfo, get # Get the local client related to the current OS diff --git a/nxdrive/client/uploader/__init__.py b/nxdrive/client/uploader/__init__.py index 601d90db8c..533ff59674 100644 --- a/nxdrive/client/uploader/__init__.py +++ b/nxdrive/client/uploader/__init__.py @@ -1,6 +1,7 @@ """ Uploader used by the Remote client for all upload stuff. """ + import json from abc import abstractmethod from logging import getLogger diff --git a/nxdrive/client/uploader/direct_transfer.py b/nxdrive/client/uploader/direct_transfer.py index 95143edfe5..8a17f9722a 100644 --- a/nxdrive/client/uploader/direct_transfer.py +++ b/nxdrive/client/uploader/direct_transfer.py @@ -1,6 +1,7 @@ """ Uploader used by the Direct Transfer feature. """ + import json from logging import getLogger from pathlib import Path diff --git a/nxdrive/client/uploader/sync.py b/nxdrive/client/uploader/sync.py index 0d1804f6c1..8a06b1b48e 100644 --- a/nxdrive/client/uploader/sync.py +++ b/nxdrive/client/uploader/sync.py @@ -1,6 +1,7 @@ """ Uploader used by the synchronization engine. """ + from pathlib import Path from typing import Any, Dict, Optional diff --git a/nxdrive/constants.py b/nxdrive/constants.py index 0e0c5fd90b..b1cd4dd79c 100644 --- a/nxdrive/constants.py +++ b/nxdrive/constants.py @@ -70,6 +70,8 @@ errno.ERANGE, # Result too large } +QUERY_ENDPOINT = "/api/v1/search/lang/NXQL/execute?query=" + # OSError indicating the incapacity to do anything because of too long file name or deep tree LONG_FILE_ERRORS = {errno.ENAMETOOLONG} if WINDOWS: diff --git a/nxdrive/dao/base.py b/nxdrive/dao/base.py index 24bce57600..ab3462ddb4 100644 --- a/nxdrive/dao/base.py +++ b/nxdrive/dao/base.py @@ -1,6 +1,7 @@ """ Query formatting in this file is based on http://www.sqlstyle.guide/ """ + import sys from contextlib import suppress from logging import getLogger diff --git a/nxdrive/dao/engine.py b/nxdrive/dao/engine.py index 9f52a18d90..97b0ee2781 100644 --- a/nxdrive/dao/engine.py +++ b/nxdrive/dao/engine.py @@ -1,6 +1,7 @@ """ Query formatting in this file is based on http://www.sqlstyle.guide/ """ + import json import os import shutil diff --git a/nxdrive/dao/manager.py b/nxdrive/dao/manager.py index 0095b71da2..0425ff50e5 100644 --- a/nxdrive/dao/manager.py +++ b/nxdrive/dao/manager.py @@ -1,6 +1,7 @@ """ Query formatting in this file is based on http://www.sqlstyle.guide/ """ + from logging import getLogger from pathlib import Path from sqlite3 import Cursor, IntegrityError, Row diff --git a/nxdrive/engine/engine.py b/nxdrive/engine/engine.py index 4fc2478f12..8fd58da890 100644 --- a/nxdrive/engine/engine.py +++ b/nxdrive/engine/engine.py @@ -838,9 +838,7 @@ def resume_transfer( meth = ( self.dao.get_download if nature == "download" - else self.dao.get_dt_upload - if is_direct_transfer - else self.dao.get_upload + else self.dao.get_dt_upload if is_direct_transfer else self.dao.get_upload ) func = partial(meth, uid=uid) # type: ignore self._resume_transfers(nature, func, is_direct_transfer=is_direct_transfer) diff --git a/nxdrive/fatal_error.py b/nxdrive/fatal_error.py index b5771dded2..9c0d7f100b 100644 --- a/nxdrive/fatal_error.py +++ b/nxdrive/fatal_error.py @@ -1,6 +1,7 @@ """ Fatal error screen management using either Qt or OS-specific dialogs. """ + import sys from contextlib import suppress from pathlib import Path diff --git a/nxdrive/feature.py b/nxdrive/feature.py index 52ce7124b2..18ec07fe30 100644 --- a/nxdrive/feature.py +++ b/nxdrive/feature.py @@ -22,6 +22,7 @@ Enable or disable the synchronization features. """ + from types import SimpleNamespace from typing import List diff --git a/nxdrive/gui/application.py b/nxdrive/gui/application.py index eef2ac55c0..ab4d1a7c55 100644 --- a/nxdrive/gui/application.py +++ b/nxdrive/gui/application.py @@ -1,4 +1,5 @@ """ Main Qt application handling OS events and system tray UI. """ + import os import webbrowser from contextlib import suppress diff --git a/nxdrive/gui/folders_model.py b/nxdrive/gui/folders_model.py index 336c122e76..0c56899f30 100644 --- a/nxdrive/gui/folders_model.py +++ b/nxdrive/gui/folders_model.py @@ -1,5 +1,5 @@ from logging import getLogger -from typing import Iterator, List, Union +from typing import Any, Iterator, List, Union from nuxeo.models import Document @@ -213,10 +213,12 @@ class FoldersOnly: def __init__(self, remote: Remote, /) -> None: self.remote = remote + self.personal_space_uid = "" def get_personal_space(self) -> "Documents": """Retrieve the "Personal space" special folder.""" personal_space = self.remote.personal_space() + self.personal_space_uid = personal_space.uid # Alter the title to use "Personal space" instead of "Firstname Lastname" personal_space.title = Translator.get("PERSONAL_SPACE") @@ -247,18 +249,37 @@ def _get_personal_space(self) -> "Document": ) ) + def get_roots(self) -> List[Any]: + from ..constants import QUERY_ENDPOINT + + url = ( + f"{QUERY_ENDPOINT}select * from Document WHERE ecm:mixinType = 'Folderish'" + ) + return self.remote.client.request("GET", url).json()["entries"] + def _get_root_folders(self) -> List["Documents"]: """Get root folders. Use a try...except block to prevent loading error on the root, else it will also show a loading error for the personal space. """ + root_details = [] + returned_folders = [] try: - root = self.remote.documents.get(path="/") - return [Doc(doc) for doc in self._get_children(root.uid)] + roots = self.get_roots() + for root in roots: + if ( + root["type"] == "Workspace" + and root["uid"] != self.personal_space_uid + ): + for doc in self._get_children(root["parentRef"]): + if doc.title not in returned_folders: + returned_folders.append(doc.title) + yield Doc(doc) except Exception: log.warning("Error while retrieving documents on '/'", exc_info=True) context = {"permissions": [], "hasFolderishChild": False} - return [Doc(Document(title="/", contextParameters=context))] + root_details.append([Doc(Document(title="/", contextParameters=context))]) + return root_details def get_top_documents(self) -> Iterator["Documents"]: """Fetch all documents at the root.""" diff --git a/nxdrive/osi/darwin/pyNotificationCenter.py b/nxdrive/osi/darwin/pyNotificationCenter.py index 171632ed9b..8b0043acd3 100644 --- a/nxdrive/osi/darwin/pyNotificationCenter.py +++ b/nxdrive/osi/darwin/pyNotificationCenter.py @@ -1,4 +1,5 @@ """ Python integration macOS notification center. """ + from typing import TYPE_CHECKING, Dict from CoreServices import ( diff --git a/nxdrive/qt/constants.py b/nxdrive/qt/constants.py index 3aac258c2e..d81e5f42be 100644 --- a/nxdrive/qt/constants.py +++ b/nxdrive/qt/constants.py @@ -1,6 +1,7 @@ """ Put here all PyQt constants used across the project. """ + from .imports import ( QAbstractSocket, QDialogButtonBox, diff --git a/nxdrive/qt/imports.py b/nxdrive/qt/imports.py index 9187f27799..86af2e3cb8 100644 --- a/nxdrive/qt/imports.py +++ b/nxdrive/qt/imports.py @@ -1,6 +1,7 @@ """ Put here all PyQt imports used across the project. """ + from PyQt5.QtCore import ( QT_VERSION_STR, QAbstractListModel, diff --git a/nxdrive/state.py b/nxdrive/state.py index 8ea37ed349..d15ac06444 100644 --- a/nxdrive/state.py +++ b/nxdrive/state.py @@ -11,6 +11,7 @@ This state is set at the start of the application to know if it has crashed at the previous run. """ + from types import SimpleNamespace State = SimpleNamespace(about_to_quit=False, crash_details="", has_crashed=False) diff --git a/nxdrive/utils.py b/nxdrive/utils.py index 83635b8304..1894d87b10 100644 --- a/nxdrive/utils.py +++ b/nxdrive/utils.py @@ -5,6 +5,7 @@ Most of functions are pure enough to be decorated with a LRU cache. Each *maxsize* is adjusted depending of the heavy use of the decorated function. """ + import os import os.path import re diff --git a/tests/benchmarks/test_safe_filename.py b/tests/benchmarks/test_safe_filename.py index f0c10d9cf5..6ad8662573 100644 --- a/tests/benchmarks/test_safe_filename.py +++ b/tests/benchmarks/test_safe_filename.py @@ -3,6 +3,7 @@ If is not the most efficient for small ASCII-only filenames, but it is the best when there are non-ASCII characters. """ + import pytest FILENAMES = [ diff --git a/tests/cleanup.py b/tests/cleanup.py index 1e3cf95cad..087832f0b1 100644 --- a/tests/cleanup.py +++ b/tests/cleanup.py @@ -1,4 +1,5 @@ """Cleanup old test users and workspaces.""" + import env from nuxeo.client import Nuxeo diff --git a/tests/functional/test_folders_model.py b/tests/functional/test_folders_model.py new file mode 100644 index 0000000000..fa994a04d3 --- /dev/null +++ b/tests/functional/test_folders_model.py @@ -0,0 +1,31 @@ +from unittest.mock import Mock, patch + +from nxdrive.gui.folders_model import FoldersOnly + + +def test_folders_only(): + def request_(*args, **kwargs): + return { + "entries": [ + { + "entity-type": "document", + "uid": "11be49d0-875e-4054-a353-eff47b7358b3", + "path": "/default-domain/workspaces/Shared_WSP", + "type": "Workspace", + "parentRef": "e537253c-c59a-411a-a96a-25f972b4c22a", + "title": "Shared_WSP", + "facets": ["Folderish", "NXTag", "SuperSpace"], + } + ] + } + + def request_with_error(*args, **kwargs): + return 1 / 0 + + folders_only = FoldersOnly(Mock()) + + with patch.object(folders_only.remote.client, "request", new=request_): + assert folders_only._get_root_folders() + + with patch.object(folders_only.remote.client, "request", new=request_with_error): + assert folders_only._get_root_folders() diff --git a/tests/integration/windows/test_cli.py b/tests/integration/windows/test_cli.py index 5ebecfa80c..8c76e80e1b 100644 --- a/tests/integration/windows/test_cli.py +++ b/tests/integration/windows/test_cli.py @@ -104,11 +104,12 @@ def test_argument_log_filename(exe, tmp, file): with exe(args=arg) as app: assert not fatal_error_dlg(app) share_metrics_dlg(app) - assert log.is_file() -@pytest.mark.parametrize("folder", ["azerty", "$alice", "léa", "mi Kaël", "こん ツリ ^^"]) +@pytest.mark.parametrize( + "folder", ["azerty", "$alice", "léa", "mi Kaël", "こん ツリ ^^"] +) def test_argument_nxdrive_home(exe, tmp, folder): path = tmp() path.mkdir(parents=True, exist_ok=True) @@ -119,7 +120,6 @@ def test_argument_nxdrive_home(exe, tmp, folder): with exe(args=arg) as app: assert not fatal_error_dlg(app) share_metrics_dlg(app) - assert home.is_dir() diff --git a/tests/markers.py b/tests/markers.py index d618ed386a..efa7ad8047 100644 --- a/tests/markers.py +++ b/tests/markers.py @@ -1,4 +1,5 @@ """Collection of pytest markers to ease test filtering.""" + import os import pytest diff --git a/tests/old_functional/common.py b/tests/old_functional/common.py index 2d6be8b238..16b2053a06 100644 --- a/tests/old_functional/common.py +++ b/tests/old_functional/common.py @@ -1,4 +1,5 @@ """ Common test utilities. """ + import os import sys import tempfile diff --git a/tests/old_functional/test_behavior.py b/tests/old_functional/test_behavior.py index 9592ffb135..8c7ab640f9 100644 --- a/tests/old_functional/test_behavior.py +++ b/tests/old_functional/test_behavior.py @@ -1,6 +1,7 @@ """ Test application Behavior. """ + from nxdrive.behavior import Behavior from .. import ensure_no_exception diff --git a/tests/old_functional/test_direct_transfer.py b/tests/old_functional/test_direct_transfer.py index 3956342358..55bb49dd20 100644 --- a/tests/old_functional/test_direct_transfer.py +++ b/tests/old_functional/test_direct_transfer.py @@ -1,6 +1,7 @@ """ Test the Direct Transfer feature in different scenarii. """ + import logging import re from pathlib import Path diff --git a/tests/old_functional/test_local_changes_when_offline.py b/tests/old_functional/test_local_changes_when_offline.py index 1b3923834f..e26c7938b7 100644 --- a/tests/old_functional/test_local_changes_when_offline.py +++ b/tests/old_functional/test_local_changes_when_offline.py @@ -2,6 +2,7 @@ Test if changes made to local file system when Drive is offline sync's back later when Drive becomes online. """ + import pytest from nxdrive.constants import WINDOWS diff --git a/tests/old_functional/test_local_client.py b/tests/old_functional/test_local_client.py index 175c4103df..98f474a906 100644 --- a/tests/old_functional/test_local_client.py +++ b/tests/old_functional/test_local_client.py @@ -4,6 +4,7 @@ See NXDRIVE-742. """ + import hashlib import os from pathlib import Path diff --git a/tests/old_functional/test_synchronization_dedup.py b/tests/old_functional/test_synchronization_dedup.py index d4c2979e8b..6cdff02a54 100644 --- a/tests/old_functional/test_synchronization_dedup.py +++ b/tests/old_functional/test_synchronization_dedup.py @@ -1,6 +1,7 @@ """ Test behaviors when the server allows duplicates and not the client. """ + from pathlib import Path import pytest diff --git a/tests/old_functional/test_transfer.py b/tests/old_functional/test_transfer.py index 6ac830a9b7..72e85b20eb 100644 --- a/tests/old_functional/test_transfer.py +++ b/tests/old_functional/test_transfer.py @@ -1,6 +1,7 @@ """ Test pause/resume transfers in different scenarii. """ + import re from unittest.mock import patch diff --git a/tests/unit/test_autolock.py b/tests/unit/test_autolock.py index 3e9d240403..b2a8975876 100644 --- a/tests/unit/test_autolock.py +++ b/tests/unit/test_autolock.py @@ -1,6 +1,7 @@ """ Test the Auto-Lock feature used heavily by Direct Edit. """ + from pathlib import Path from typing import List, Tuple from unittest.mock import Mock, patch diff --git a/tests/unit/test_pytest_random.py b/tests/unit/test_pytest_random.py index 6112825756..1ee6010938 100644 --- a/tests/unit/test_pytest_random.py +++ b/tests/unit/test_pytest_random.py @@ -2,6 +2,7 @@ Tests for pytests_random: a pytest plugin to mitigate random failures. Adapted from github.com/pytest-dev/pytest-rerunfailures """ + import pytest pytest_plugins = "pytester" diff --git a/tools/cleanup_application_tree.py b/tools/cleanup_application_tree.py index 66e2fd0c08..8945094b19 100644 --- a/tools/cleanup_application_tree.py +++ b/tools/cleanup_application_tree.py @@ -2,6 +2,7 @@ Remove files from the package that are not needed and too big. This script can be launched after PyInstaller and before installers creation. """ + import os import shutil import sys diff --git a/tools/jenkins/junit/merge.py b/tools/jenkins/junit/merge.py index 6680a43ee5..066d21e022 100644 --- a/tools/jenkins/junit/merge.py +++ b/tools/jenkins/junit/merge.py @@ -19,6 +19,7 @@ Léa Klein Mickaël Schoentgen """ + import os import sys from pathlib import Path diff --git a/tools/scripts/csv_to_log.py b/tools/scripts/csv_to_log.py index 9ed52b29f6..c358a4aa83 100644 --- a/tools/scripts/csv_to_log.py +++ b/tools/scripts/csv_to_log.py @@ -2,6 +2,7 @@ Convert a CSV "log" file to a real log file. Such files are ones attached to NCO tickets. """ + import csv import sys from pathlib import Path