From fcb3884a8fcd0c8cef6f79b0083c49b45afb4ff9 Mon Sep 17 00:00:00 2001 From: I-Al-Istannen Date: Sat, 5 Dec 2020 13:47:53 +0100 Subject: [PATCH] Add --remote-first, --local-first and --no-delete flags --- PFERD/organizer.py | 40 ++++++++++++++++++++++++++++++---------- sync_url.py | 39 ++++++++++++++++++++++++++------------- 2 files changed, 56 insertions(+), 23 deletions(-) diff --git a/PFERD/organizer.py b/PFERD/organizer.py index f63e92a..a41d0d2 100644 --- a/PFERD/organizer.py +++ b/PFERD/organizer.py @@ -20,21 +20,37 @@ LOGGER = logging.getLogger(__name__) PRETTY = PrettyLogger(LOGGER) +class ConflictType(Enum): + """ + The type of the conflict. A file might not exist anymore and will be deleted + or it might be overwritten with a newer version. + """ + FILE_OVERWRITTEN = "overwritten" + FILE_DELETED = "deleted" + + class FileConflictResolution(Enum): """ - The reaction when confronted with a file conflict. + The reaction when confronted with a file conflict: """ - OVERWRITE_EXISTING = "overwrite" + DESTROY_EXISTING = "destroy" + """Delete/overwrite the current file""" + KEEP_EXISTING = "keep" + """Keep the current file""" + DEFAULT = "default" + """Do whatever the PFERD authors thought is sensible""" + PROMPT = "prompt" + """Interactively ask the user""" -FileConflictResolver = Callable[[PurePath], FileConflictResolution] +FileConflictResolver = Callable[[PurePath, ConflictType], FileConflictResolution] -def resolve_prompt_user(_path: PurePath) -> FileConflictResolution: +def resolve_prompt_user(_path: PurePath, _conflict: ConflictType) -> FileConflictResolution: """Resolves conflicts by always asking the user.""" return FileConflictResolution.PROMPT @@ -89,14 +105,16 @@ class Organizer(Location): if self._is_marked(dst): PRETTY.warning(f"File {str(dst_absolute)!r} was already written!") - if self._resolve_conflict(f"Overwrite file?", dst_absolute, default=False): + conflict = ConflictType.FILE_OVERWRITTEN + if self._resolve_conflict(f"Overwrite file?", dst_absolute, conflict, default=False): PRETTY.ignored_file(dst_absolute, "file was written previously") return None # Destination file is directory if dst_absolute.exists() and dst_absolute.is_dir(): prompt = f"Overwrite folder {dst_absolute} with file?" - if self._resolve_conflict(prompt, dst_absolute, default=False): + conflict = ConflictType.FILE_OVERWRITTEN + if self._resolve_conflict(prompt, dst_absolute, conflict, default=False): shutil.rmtree(dst_absolute) else: PRETTY.warning(f"Could not add file {str(dst_absolute)!r}") @@ -167,20 +185,22 @@ class Organizer(Location): def _delete_file_if_confirmed(self, path: Path) -> None: prompt = f"Do you want to delete {path}" - if self._resolve_conflict(prompt, path, default=False): + if self._resolve_conflict(prompt, path, ConflictType.FILE_DELETED, default=False): self.download_summary.add_deleted_file(path) path.unlink() - def _resolve_conflict(self, prompt: str, path: Path, default: bool) -> bool: + def _resolve_conflict( + self, prompt: str, path: Path, conflict: ConflictType, default: bool + ) -> bool: if not self.conflict_resolver: return prompt_yes_no(prompt, default=default) - result = self.conflict_resolver(path) + result = self.conflict_resolver(path, conflict) if result == FileConflictResolution.DEFAULT: return default if result == FileConflictResolution.KEEP_EXISTING: return False - if result == FileConflictResolution.OVERWRITE_EXISTING: + if result == FileConflictResolution.DESTROY_EXISTING: return True return prompt_yes_no(prompt, default=default) diff --git a/sync_url.py b/sync_url.py index e06deb6..91a7521 100755 --- a/sync_url.py +++ b/sync_url.py @@ -12,17 +12,26 @@ from PFERD import Pferd from PFERD.cookie_jar import CookieJar from PFERD.ilias import (IliasCrawler, IliasElementType, KitShibbolethAuthenticator) -from PFERD.organizer import FileConflictResolution, resolve_prompt_user +from PFERD.organizer import (ConflictType, FileConflictResolution, + FileConflictResolver, resolve_prompt_user) from PFERD.transform import sanitize_windows_path from PFERD.utils import to_path -def _resolve_overwrite(_path: PurePath) -> FileConflictResolution: - return FileConflictResolution.OVERWRITE_EXISTING +def _resolve_remote_first(_path: PurePath, _conflict: ConflictType) -> FileConflictResolution: + return FileConflictResolution.DESTROY_EXISTING -def _resolve_default(_path: PurePath) -> FileConflictResolution: - return FileConflictResolution.DEFAULT +def _resolve_local_first(_path: PurePath, _conflict: ConflictType) -> FileConflictResolution: + return FileConflictResolution.KEEP_EXISTING + + +def _resolve_no_delete(_path: PurePath, conflict: ConflictType) -> FileConflictResolution: + # Update files + if conflict == ConflictType.FILE_OVERWRITTEN: + return FileConflictResolution.DESTROY_EXISTING + # But do not delete them + return FileConflictResolution.KEEP_EXISTING def main() -> None: @@ -30,10 +39,12 @@ def main() -> None: parser.add_argument("--test-run", action="store_true") parser.add_argument('-c', '--cookies', nargs='?', default=None, help="File to store cookies in") parser.add_argument('--no-videos', nargs='?', default=None, help="Don't download videos") - parser.add_argument('-d', '--default', action="store_true", - help="Don't prompt for confirmations and use sane defaults") - parser.add_argument('-r', '--remove', action="store_true", - help="Remove and overwrite files without prompting for confirmation") + parser.add_argument('--local-first', action="store_true", + help="Don't prompt for confirmation, keep existing files") + parser.add_argument('--remote-first', action="store_true", + help="Don't prompt for confirmation, delete and overwrite local files") + parser.add_argument('--no-delete', action="store_true", + help="Don't prompt for confirmation, overwrite local files, don't delete") parser.add_argument('url', help="URL to the course page") parser.add_argument('folder', nargs='?', default=None, help="Folder to put stuff into") args = parser.parse_args() @@ -68,10 +79,12 @@ def main() -> None: return element not in [IliasElementType.VIDEO_FILE, IliasElementType.VIDEO_FOLDER] return True - if args.default: - file_confilict_resolver = _resolve_default - elif args.remove: - file_confilict_resolver = _resolve_overwrite + if args.remote_first: + file_confilict_resolver: FileConflictResolver = _resolve_remote_first + elif args.local_first: + file_confilict_resolver = _resolve_local_first + elif args.no_delete: + file_confilict_resolver = _resolve_no_delete else: file_confilict_resolver = resolve_prompt_user