From 95bf6837719533b5621faf57753bf99778995e5f Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Fri, 11 Jun 2021 16:50:00 +0200 Subject: [PATCH 1/3] Add contrib script to resolve conflicts Asks the user when two iCalender objects conflict during a sync, which one to take. --- .../resolve_interactively.py | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100755 contrib/conflict_resolution/resolve_interactively.py diff --git a/contrib/conflict_resolution/resolve_interactively.py b/contrib/conflict_resolution/resolve_interactively.py new file mode 100755 index 0000000..09e06ff --- /dev/null +++ b/contrib/conflict_resolution/resolve_interactively.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +"""Ask user to resolve a vdirsyncer sync conflict interactively. + +Needs a way to ask the user. +The use of https://apps.kde.org/kdialog/ for GNU/Linix is hardcoded. + +Depends on python>3.5 and KDialog. + +Usage: + Ensure the file executable and use it in the vdirsyncer.conf file, e.g. + + conflict_resolution = ["command", "/home/bern/vdirsyncer/resolve_interactively.py"] + +This file is Free Software under the following license: +SPDX-License-Identifier: Apache-2.0 +SPDX-FileCopyrightText: 2021 Intevation GmbH +Author: +""" +from pathlib import Path +import re +import subprocess +import sys + +KDIALOG = "/usr/bin/kdialog" + +SUMMARY_PATTERN = re.compile("^(SUMMARY:.*)$", re.MULTILINE) + +def get_summary(icalendar_text:str): + """Get the first SUMMARY: line from an iCalendar text. + + Do not care about the line being continued. + """ + match = re.search(SUMMARY_PATTERN, icalendar_text) + return match[1] + + +def main(ical1_filename, ical2_filename): + ical1 = ical1_filename.read_text() + ical2 = ical2_filename.read_text() + + additional_args = ["--yes-label", "take first"] # return code == 0 + additional_args += ["--no-label", "take second"] # return code == 1 + additional_args += ["--cancel-label", "do not resolve"] # return code == 2 + + r = subprocess.run(args = [ + KDIALOG, + "--warningyesnocancel", + "There was a sync conflict, do you prefer the first entry: \n" + + get_summary(ical1) + "...\n(full contents: " + str(ical1_filename) + + " )\n\nor the second entry: \n" + + get_summary(ical2) + "...\n(full contents: " + str(ical2_filename) + + " )?" + ] + additional_args) + + if r.returncode == 2: + # cancel was pressed + return # shall lead to items not changed, because not copied + + if r.returncode == 0: + # we want to take the first item, so overwrite the second + ical2_filename.write_text(ical1) + else: # r.returncode == 1, we want the second item, so overwrite the first + ical1_filename.write_text(ical2) + +if len(sys.argv) != 3: + sys.stdout.write(__doc__) +else: + main(Path(sys.argv[1]), Path(sys.argv[2])) From b3bee77c177d5f5b51147deedb75cca621ce01ff Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Mon, 28 Jun 2021 15:22:56 +0200 Subject: [PATCH 2/3] Change license from Apache-2.0 to BSD-3-Clause to match the license of vdirsyncer --- contrib/conflict_resolution/resolve_interactively.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/conflict_resolution/resolve_interactively.py b/contrib/conflict_resolution/resolve_interactively.py index 09e06ff..7d0e1c3 100755 --- a/contrib/conflict_resolution/resolve_interactively.py +++ b/contrib/conflict_resolution/resolve_interactively.py @@ -12,7 +12,7 @@ Usage: conflict_resolution = ["command", "/home/bern/vdirsyncer/resolve_interactively.py"] This file is Free Software under the following license: -SPDX-License-Identifier: Apache-2.0 +SPDX-License-Identifier: BSD-3-Clause SPDX-FileCopyrightText: 2021 Intevation GmbH Author: """ From 61edfc090e06a822b0e63a7a6d28580781ad3ce2 Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Mon, 28 Jun 2021 16:01:21 +0200 Subject: [PATCH 3/3] Adjust codestyle running flake8, black and reorder-python-imports. --- .../resolve_interactively.py | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/contrib/conflict_resolution/resolve_interactively.py b/contrib/conflict_resolution/resolve_interactively.py index 7d0e1c3..bd5b645 100755 --- a/contrib/conflict_resolution/resolve_interactively.py +++ b/contrib/conflict_resolution/resolve_interactively.py @@ -16,16 +16,17 @@ SPDX-License-Identifier: BSD-3-Clause SPDX-FileCopyrightText: 2021 Intevation GmbH Author: """ -from pathlib import Path import re import subprocess import sys +from pathlib import Path KDIALOG = "/usr/bin/kdialog" SUMMARY_PATTERN = re.compile("^(SUMMARY:.*)$", re.MULTILINE) -def get_summary(icalendar_text:str): + +def get_summary(icalendar_text: str): """Get the first SUMMARY: line from an iCalendar text. Do not care about the line being continued. @@ -38,30 +39,38 @@ def main(ical1_filename, ical2_filename): ical1 = ical1_filename.read_text() ical2 = ical2_filename.read_text() - additional_args = ["--yes-label", "take first"] # return code == 0 - additional_args += ["--no-label", "take second"] # return code == 1 - additional_args += ["--cancel-label", "do not resolve"] # return code == 2 + additional_args = ["--yes-label", "take first"] # return code == 0 + additional_args += ["--no-label", "take second"] # return code == 1 + additional_args += ["--cancel-label", "do not resolve"] # return code == 2 - r = subprocess.run(args = [ - KDIALOG, - "--warningyesnocancel", - "There was a sync conflict, do you prefer the first entry: \n" + - get_summary(ical1) + "...\n(full contents: " + str(ical1_filename) + - " )\n\nor the second entry: \n" + - get_summary(ical2) + "...\n(full contents: " + str(ical2_filename) + - " )?" - ] + additional_args) + r = subprocess.run( + args=[ + KDIALOG, + "--warningyesnocancel", + "There was a sync conflict, do you prefer the first entry: \n" + + get_summary(ical1) + + "...\n(full contents: " + + str(ical1_filename) + + " )\n\nor the second entry: \n" + + get_summary(ical2) + + "...\n(full contents: " + + str(ical2_filename) + + " )?", + ] + + additional_args + ) if r.returncode == 2: # cancel was pressed - return # shall lead to items not changed, because not copied + return # shall lead to items not changed, because not copied if r.returncode == 0: # we want to take the first item, so overwrite the second ical2_filename.write_text(ical1) - else: # r.returncode == 1, we want the second item, so overwrite the first + else: # r.returncode == 1, we want the second item, so overwrite the first ical1_filename.write_text(ical2) + if len(sys.argv) != 3: sys.stdout.write(__doc__) else: