# Item Synchronizer
<img src="https://github.com/bergercookie/item_synchronizer/raw/master/res/logo.png" alt="logo" style="zoom:50%;" />
<a href="https://github.com/bergercookie/item_synchronizer/actions" alt="CI">
<img src="https://github.com/bergercookie/item_synchronizer/actions/workflows/ci.yml/badge.svg"/></a>
<a href="https://github.com/pre-commit/pre-commit">
<img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white" alt="pre-commit"></a>
<a href='https://coveralls.io/github/bergercookie/item_synchronizer'>
<img src='https://coveralls.io/repos/github/bergercookie/item_synchronizer/badge.svg' alt='Coverage Status' /></a>
<a href="https://github.com/bergercookie/item_synchronizer/blob/master/LICENSE.md" alt="LICENCE">
<img src="https://img.shields.io/github/license/bergercookie/item_synchronizer.svg" /></a>
<a href="https://pypi.org/project/item_synchronizer/" alt="pypi">
<img src="https://img.shields.io/pypi/pyversions/item-synchronizer.svg" /></a>
<a href="https://badge.fury.io/py/item-synchronizer">
<img src="https://badge.fury.io/py/item-synchronizer.svg" alt="PyPI version" height="18"></a>
<a href="https://pepy.tech/project/item-synchronizer">
<img alt="Downloads" src="https://pepy.tech/badge/item-synchronizer"></a>
<a href="https://github.com/psf/black">
<img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>
## Description
Synchronize items from two different sources in a bidirectional manner.
This library aims to offer an abstract and versatile way to _create_, _update_
and/or _delete_ items to keep two "sources" in sync.
These "items" may range from Calendar entries, TODO task lists, or whatever else
you want as long as the user registers the appropriate functions/methods to
convert from one said item to another.
## Usage
The `Synchronizer` class requires the following `Callable`s to be given, for each
one of the sides. See the most up-to-date python types
[here](https://github.com/bergercookie/item_synchronizer/blob/master/item_synchronizer/types.py)
- Insertion callable: when called with the contents of an item it should create
and return the ID of the newly added item on the other source
- Update callable: update an item given by the item ID, using the (possibly
partial) new contents specified by Item
- Deletion callable: Delete the item given by the specified ID
- Conversion callable: convert an item from the format of one source to the
format of another.
- `Item Getter` callable: Given the ID of an Item of one source return the
corresponding item on the other source.
- `A_to_B` [bidict](https://github.com/jab/bidict)
- This should be a bidict mapping IDs of A to the corresponding IDs of B and
vice-versa. Given this the `item_synchronizer` is responsible for keeping
it up to date on insertion, update and deletion events. The contents of this
bidict should be persistent across the various runs, thus, consider
pickle-ing and unpickling its contents to disk
Additionally `item_synchronizer` needs to know what items (their IDs) were
inserted, updated and deleted during the call to its main method, `sync()`. This
is dependent on your application at hand. You could either cache the items and
their content after each run and compare them with the latest state in the
current run. Or, the API of the calendar/task manager, etc. that you are using
may allow you to query the items that were modified/inserted/deleted since the
last run.
## Examples
Let's say you want to bi-directionally synchronize your calendar events with
your TODO Task Manager tasks. This way, when you remove calendar event, the
correspoding task will be deleted, when you add a new task in your task manager,
a new calendar event will be created and when you update the task (e.g., change
its description or start time) the changes will reflect in the corresponding
calendar entry.
Thus, you have the following and you want to sync the `A` and `B` items.
![sync0](res/drawio/sync0.drawio.svg)
As described in the previous section, `item_synchronizer` requires a set of
functions which it will call when it needs to insert, update or delete an item
from the corresponding side. It also requires an `A_to_B` bidict persistent
across its runs with the item mappings between the two sides before the very
latest changes. Notice that, `item_synchonizer` will be responsible for updating
entries in this `A_to_B` bidict, you do not need to do that manually. You only
need to make it persist across the different runs of the `Synchronizer.sync()`
call (e.g., by pickle-ing and unpickle-ing it every time your application exits
and starts again.)
Thus, this is the situation that `item_synchronizer` expects at its first
run.
![sync1](res/drawio/sync1.drawio.svg)
After the first call to `sync()` here's the expected results. After this call
each event of one side will have a counterpart on the other side and that's also
going to be reflected in the provided `A_to_B` bidict.
![sync2](res/drawio/sync2.drawio.svg)
Subsequent calls to `sync()` will pick up the changes and will insert any new
items from each side to the other side accordingly.
Now let's say that item 2 was modified from side A, item 3 from side B and
item 21 was deleted from side A.
![update-n-delete0](res/drawio/update-n-delete0.drawio.svg)
In the subsequent call to `sync()`, `item_synchronizer` will forward these
changes to the other side appropriately.
![update-n-delete1](res/drawio/update-n-delete1.drawio.svg)
If there was a conflict, e.g., an item removed from one side and updated from
the other, then `item_synchronizer` supports a series of resolution strategies
for handling such conflicts.
![resolution-strategy](res/drawio/resolution-strategy.drawio.svg)
## Installation
Add it as a dependency to either your `requirements.txt` or to `pyproject.toml`
```console
[tool.poetry.dependencies]
...
item_synchronizer = "^1.0"
...
```
Or simply install it with `pip` if you want to use it locally:
```sh
pip3 install item_synchronizer
```
## Projects using it
Projects using this:
- [Taskwarrior <-> Google Calendar Bidirectonal Synchronisation](https://github.com/bergercookie/taskw_gcal_sync/blob/master/taskw_gcal_sync/TWGCalAggregator.py)
## Notes
- Currently IDs of items on either side should be of `str` type.
Raw data
{
"_id": null,
"home_page": "https://github.com/bergercookie/item_synchronizer",
"name": "item-synchronizer",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.8,<4.0",
"maintainer_email": "",
"keywords": "",
"author": "Nikos Koukis",
"author_email": "nickkouk@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/dc/61/2f69a1087d8f00df0bb68d4d3ca7eec6ec87481485dc0dde4775bae12644/item_synchronizer-1.1.4.tar.gz",
"platform": null,
"description": "# Item Synchronizer\n\n<img src=\"https://github.com/bergercookie/item_synchronizer/raw/master/res/logo.png\" alt=\"logo\" style=\"zoom:50%;\" />\n\n<a href=\"https://github.com/bergercookie/item_synchronizer/actions\" alt=\"CI\">\n<img src=\"https://github.com/bergercookie/item_synchronizer/actions/workflows/ci.yml/badge.svg\"/></a>\n<a href=\"https://github.com/pre-commit/pre-commit\">\n<img src=\"https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white\" alt=\"pre-commit\"></a>\n\n<a href='https://coveralls.io/github/bergercookie/item_synchronizer'>\n<img src='https://coveralls.io/repos/github/bergercookie/item_synchronizer/badge.svg' alt='Coverage Status' /></a>\n<a href=\"https://github.com/bergercookie/item_synchronizer/blob/master/LICENSE.md\" alt=\"LICENCE\">\n<img src=\"https://img.shields.io/github/license/bergercookie/item_synchronizer.svg\" /></a>\n<a href=\"https://pypi.org/project/item_synchronizer/\" alt=\"pypi\">\n<img src=\"https://img.shields.io/pypi/pyversions/item-synchronizer.svg\" /></a>\n<a href=\"https://badge.fury.io/py/item-synchronizer\">\n<img src=\"https://badge.fury.io/py/item-synchronizer.svg\" alt=\"PyPI version\" height=\"18\"></a>\n<a href=\"https://pepy.tech/project/item-synchronizer\">\n<img alt=\"Downloads\" src=\"https://pepy.tech/badge/item-synchronizer\"></a>\n<a href=\"https://github.com/psf/black\">\n<img alt=\"Code style: black\" src=\"https://img.shields.io/badge/code%20style-black-000000.svg\"></a>\n\n## Description\n\nSynchronize items from two different sources in a bidirectional manner.\n\nThis library aims to offer an abstract and versatile way to _create_, _update_\nand/or _delete_ items to keep two \"sources\" in sync.\n\nThese \"items\" may range from Calendar entries, TODO task lists, or whatever else\nyou want as long as the user registers the appropriate functions/methods to\nconvert from one said item to another.\n\n## Usage\n\nThe `Synchronizer` class requires the following `Callable`s to be given, for each\none of the sides. See the most up-to-date python types\n[here](https://github.com/bergercookie/item_synchronizer/blob/master/item_synchronizer/types.py)\n\n- Insertion callable: when called with the contents of an item it should create\n and return the ID of the newly added item on the other source\n- Update callable: update an item given by the item ID, using the (possibly\n partial) new contents specified by Item\n- Deletion callable: Delete the item given by the specified ID\n- Conversion callable: convert an item from the format of one source to the\n format of another.\n- `Item Getter` callable: Given the ID of an Item of one source return the\n corresponding item on the other source.\n- `A_to_B` [bidict](https://github.com/jab/bidict)\n\n - This should be a bidict mapping IDs of A to the corresponding IDs of B and\n vice-versa. Given this the `item_synchronizer` is responsible for keeping\n it up to date on insertion, update and deletion events. The contents of this\n bidict should be persistent across the various runs, thus, consider\n pickle-ing and unpickling its contents to disk\n\nAdditionally `item_synchronizer` needs to know what items (their IDs) were\ninserted, updated and deleted during the call to its main method, `sync()`. This\nis dependent on your application at hand. You could either cache the items and\ntheir content after each run and compare them with the latest state in the\ncurrent run. Or, the API of the calendar/task manager, etc. that you are using\nmay allow you to query the items that were modified/inserted/deleted since the\nlast run.\n\n## Examples\n\nLet's say you want to bi-directionally synchronize your calendar events with\nyour TODO Task Manager tasks. This way, when you remove calendar event, the\ncorrespoding task will be deleted, when you add a new task in your task manager,\na new calendar event will be created and when you update the task (e.g., change\nits description or start time) the changes will reflect in the corresponding\ncalendar entry.\n\nThus, you have the following and you want to sync the `A` and `B` items.\n\n![sync0](res/drawio/sync0.drawio.svg)\n\nAs described in the previous section, `item_synchronizer` requires a set of\nfunctions which it will call when it needs to insert, update or delete an item\nfrom the corresponding side. It also requires an `A_to_B` bidict persistent\nacross its runs with the item mappings between the two sides before the very\nlatest changes. Notice that, `item_synchonizer` will be responsible for updating\nentries in this `A_to_B` bidict, you do not need to do that manually. You only\nneed to make it persist across the different runs of the `Synchronizer.sync()`\ncall (e.g., by pickle-ing and unpickle-ing it every time your application exits\nand starts again.)\n\nThus, this is the situation that `item_synchronizer` expects at its first\nrun.\n\n![sync1](res/drawio/sync1.drawio.svg)\n\nAfter the first call to `sync()` here's the expected results. After this call\neach event of one side will have a counterpart on the other side and that's also\ngoing to be reflected in the provided `A_to_B` bidict.\n\n![sync2](res/drawio/sync2.drawio.svg)\n\nSubsequent calls to `sync()` will pick up the changes and will insert any new\nitems from each side to the other side accordingly.\n\nNow let's say that item 2 was modified from side A, item 3 from side B and\nitem 21 was deleted from side A.\n\n![update-n-delete0](res/drawio/update-n-delete0.drawio.svg)\n\nIn the subsequent call to `sync()`, `item_synchronizer` will forward these\nchanges to the other side appropriately.\n\n![update-n-delete1](res/drawio/update-n-delete1.drawio.svg)\n\nIf there was a conflict, e.g., an item removed from one side and updated from\nthe other, then `item_synchronizer` supports a series of resolution strategies\nfor handling such conflicts.\n\n![resolution-strategy](res/drawio/resolution-strategy.drawio.svg)\n\n## Installation\n\nAdd it as a dependency to either your `requirements.txt` or to `pyproject.toml`\n\n```console\n[tool.poetry.dependencies]\n...\nitem_synchronizer = \"^1.0\"\n...\n```\n\nOr simply install it with `pip` if you want to use it locally:\n\n```sh\npip3 install item_synchronizer\n```\n\n## Projects using it\n\nProjects using this:\n\n- [Taskwarrior <-> Google Calendar Bidirectonal Synchronisation](https://github.com/bergercookie/taskw_gcal_sync/blob/master/taskw_gcal_sync/TWGCalAggregator.py)\n\n## Notes\n\n- Currently IDs of items on either side should be of `str` type.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Synchronize items bi-directionally between two different sources",
"version": "1.1.4",
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"md5": "d6a156d05cc3bec2836514cee3aa4e99",
"sha256": "9e47d793bf2453ee5efd6a41c7aa3b1b6cdb3d9717c2be0c050b2e6baf5fa72b"
},
"downloads": -1,
"filename": "item_synchronizer-1.1.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "d6a156d05cc3bec2836514cee3aa4e99",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8,<4.0",
"size": 11125,
"upload_time": "2022-12-05T21:53:39",
"upload_time_iso_8601": "2022-12-05T21:53:39.645157Z",
"url": "https://files.pythonhosted.org/packages/23/1a/5c691430da02b09aa061e17c409b5f46d32b69d15b6294b6c1cfd43706cc/item_synchronizer-1.1.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "9f0e6584331da01c2a431703294a5953",
"sha256": "0c5b84c79ab743571d7b2171483835901933789e8deecce57f6395bf4fbe7df2"
},
"downloads": -1,
"filename": "item_synchronizer-1.1.4.tar.gz",
"has_sig": false,
"md5_digest": "9f0e6584331da01c2a431703294a5953",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8,<4.0",
"size": 12612,
"upload_time": "2022-12-05T21:53:40",
"upload_time_iso_8601": "2022-12-05T21:53:40.941358Z",
"url": "https://files.pythonhosted.org/packages/dc/61/2f69a1087d8f00df0bb68d4d3ca7eec6ec87481485dc0dde4775bae12644/item_synchronizer-1.1.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2022-12-05 21:53:40",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "bergercookie",
"github_project": "item_synchronizer",
"travis_ci": false,
"coveralls": true,
"github_actions": true,
"lcname": "item-synchronizer"
}