vdirsyncer
vdirsyncer copied to clipboard
add conflict_resolution possibility to "add two events" depending on the resolution script
Users using calendar application frontends only, want to see if there was a conflict within their applications by seeing both versions of the event.
vdirsyncer version 0.18.0a2.dev16+g81895c2.d20210520 only offers the conflict resolution options
null
, a wins
, b wins
and ["command..]
.
An option take both
would save both events (by using a difference uid for the second event), this way users can resolve their conflicts within their calendar applications and vdirsyncer can run non-interactively, periodically in the background.
This seems a bit trickier that it might sound.
Say we have an event ABC
which is in conflict. We'd then create events "ABC1" and "ABC2", right?
How does the user figure out that a duplication has happened? I can image a conflict for some event in 30 days, and then they day before that, I notice duplicate events. How do I figure out where each copy came from?
I sound that you could use conflict resolution command
, and just write a script that returns two events... Though I guess vdirsyncer
would also need to be able to handle that :thinking:
Thanks for thinking along. We had a feature like this in the Kolab groupware, but the default was to shown an interactive conflict resolution dialog. The idea is that you see both events next to each other, because they are at the same time (and they have the same title). Then you open both and manually look at them and in case that you cannot decide, you'll have to externally ask for the details again. However, this is a problem if the time and title has changed. So maybe you are right that it is not a good idea. 
(I've also been thinking about using command, but if it cannot get some gui stuff up, it will stay a problem. And from the description, it seemed that vdirsyncer did not take both if they are different.)
vdirsyncer
expects both to be identical. Them being different is an indicator of "resolution command failed".
I guess the command
logic could be extended to have some fancier form of communication (e.g.: reading the stdout of the command to determine more complex outcomes).
As an alternate approach, I think you could just use command
and use write a short script to send yourself an email or show a desktop notification saying "conflict in vdirsyncer". The alert should suffice, and you can then re-run vdirsyncer manually on a terminal and do the conflict resolution yourself.
Writing a script for interactive use is probably a good idea to solve my use case. Still I believe the idea to be able to return two events that are both taken is a good one. (Or even to be able to delete both events). It would make the command more flexible. Here is my script (v1), tested on Debian Buster with current master:
#!/usr/bin/env python3
"""Ask to resolve a vdirsyncer sync conflict interatively.
Currently kdialog is hardcoded, but can be easily be replaced.
Needs at least python3.5.
conflict_resolution = ["command", "/home/bern/vdirsyncer/conflictscript.py"]
This file is Free Software under the following license:
SPDX-License-Identifier: Apache-2.0
SPDX-FileCopyrightText: 2021 Intevation GmbH <https://intevation.de>
Author: <[email protected]>
"""
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."""
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",
"The 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)
main(Path(sys.argv[1]), Path(sys.argv[2]))