1
0
mirror of https://github.com/b4tman/sync_ics2gcal synced 2024-09-21 08:58:03 +00:00
sync_ics2gcal/gcal_sync/sync.py

135 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import dateutil.parser
import logging
import operator
from pytz import utc
class CalendarSync():
logger = logging.getLogger('CalendarSync')
def __init__(self, gcalendar, converter):
self.gcalendar = gcalendar
self.converter = converter
def _events_list_compare(self, items_src, items_dst, key='iCalUID'):
""" compare list of events by key
Arguments:
items_src {list of dict} -- source events
items_dst {list of dict} -- dest events
key {str} -- name of key to compare (default: {'iCalUID'})
Returns:
tuple -- (items_to_insert,
items_to_update,
items_to_delete)
"""
def get_key(item): return item[key]
keys_src = list(map(get_key, items_src))
keys_dst = list(map(get_key, items_dst))
keys_to_insert = set(keys_src) - set(keys_dst)
keys_to_update = set(keys_src) & set(keys_dst)
keys_to_delete = set(keys_dst) - set(keys_src)
def items_by_keys(items, key_name, keys):
return list(filter(lambda item: item[key_name] in keys, items))
items_to_insert = items_by_keys(items_src, key, keys_to_insert)
items_to_update = list(zip(items_by_keys(
items_src, key, keys_to_update), items_by_keys(items_dst, key, keys_to_update)))
items_to_delete = items_by_keys(items_dst, key, keys_to_delete)
return items_to_insert, items_to_update, items_to_delete
def _filter_events_to_update(self):
""" Отбор событий к обновлению, по дате обновления
Arguments:
events -- список кортежей к обновлению (новое, старое)
Returns:
список кортежей к обновлению (новое, старое)
"""
def filter_updated(event_tuple):
new, old = event_tuple
return dateutil.parser.parse(new['updated']) > dateutil.parser.parse(old['updated'])
self.to_update = list(filter(filter_updated, self.to_update))
def _filter_events_by_date(self, events, date, op):
""" Отбор событий по дате обновления
Arguments:
events -- список событий к обновлению
date {datetime} -- дата для сравнения
op {operator} -- оператор сравнения
Returns:
список событий
"""
def filter_by_date(event):
return op(dateutil.parser.parse(event['updated']), date)
return list(filter(filter_by_date, events))
@staticmethod
def _tz_aware_datetime(date):
if not isinstance(date, datetime.datetime):
date = datetime.datetime(date.year, date.month, date.day)
if date.tzinfo is None:
date = date.replace(tzinfo=utc)
return date
def prepare_sync(self, start_date):
start_date = _tz_aware_datetime(start_date)
events_src = self.converter.events_to_gcal()
events_dst = self.gcalendar.list_events_from(start_date)
# разбитие тестовых событий на будующие и прошлые
events_src_pending = self._filter_events_by_date(
events_src, start_date, operator.ge)
events_src_past = self._filter_events_by_date(
events_src, start_date, operator.lt)
events_src = None
# первоначальное сравнение списков
self.to_insert, self.to_update, self.to_delete = self._events_list_compare(
events_src_pending, events_dst)
events_src_pending, events_dst = None, None
# сравнение списка на удаление со списком прошлых событий, для определения доп событий к обновлению
_, add_to_update, self.to_delete = self._events_list_compare(
events_src_past, self.to_delete)
self.to_update.extend(add_to_update)
events_src_past = None
# проверка списка к вставке и перемещение доп. элементов в список к обновлению
add_to_update, self.to_insert = self.gcalendar.find_exists(
self.to_insert)
self.to_update.extend(add_to_update)
add_to_update = None
# отбор событий требующих обновления (по полю 'updated')
self._filter_events_to_update()
self.logger.info('prepared to sync: ( insert: %d, update: %d, delete: %d )',
len(self.to_insert), len(self.to_update), len(self.to_delete))
def apply(self):
self.gcalendar.insert_events(self.to_insert)
self.gcalendar.update_events(self.to_update)
self.gcalendar.delete_events(self.to_delete)
self.logger.info('sync done')
self.to_insert, self.to_update, self.to_delete = [], [], []