1
0
mirror of https://github.com/b4tman/sync_ics2gcal synced 2025-02-01 12:28:29 +00:00

try pydantic for converter

---
x2 slower:
before: best: _82001700 ns,     avg: _85379966.2 ns,    median: _84408700.0 ns
after__: best: 162860900 ns,     avg: 175015097.0 ns,    median: 171212750.0 ns
This commit is contained in:
Dmitry Belyaev 2021-10-17 17:10:44 +03:00
parent d03e5691ee
commit 15951ba200
Signed by: b4tman
GPG Key ID: 41A00BF15EA7E5F3

View File

@ -5,11 +5,56 @@ from typing import Union, Dict, Callable, Optional
from icalendar import Calendar, Event from icalendar import Calendar, Event
from pytz import utc from pytz import utc
import pydantic
from .gcal import EventData, EventList from .gcal import EventData, EventList
DateDateTime = Union[datetime.date, datetime.datetime] DateDateTime = Union[datetime.date, datetime.datetime]
class GCal_DateDateTime(pydantic.BaseModel):
date: Optional[str] = pydantic.Field(default=None)
date_time: Optional[str] = pydantic.Field(alias='dateTime', default=None)
timezone: Optional[str] = pydantic.Field(alias='timeZone', default=None)
@pydantic.root_validator(allow_reuse=True)
def check_only_date_or_datetime(cls, values):
date = values.get('date', None)
date_time = values.get('date_time', None)
assert (date is None) != (date_time is None), \
'only date or date_time must be provided'
return values
@classmethod
def create_from(cls, value: DateDateTime) -> 'GCal_DateDateTime':
key: str = 'date'
str_value: str = ''
if type(value) is datetime.datetime:
key = 'date_time'
str_value = format_datetime_utc(value)
else:
str_value = value.isoformat()
return cls(**{key: str_value})
class Config:
allow_population_by_field_name = True
class GCal_Event(pydantic.BaseModel):
created: Optional[str] = None
updated: Optional[str] = None
summary: Optional[str] = None
description: Optional[str] = None
location: Optional[str] = None
start: GCal_DateDateTime
end: GCal_DateDateTime
transparency: Optional[str] = None
ical_uid: str = pydantic.Field(alias='iCalUID')
class Config:
allow_population_by_field_name = True
def format_datetime_utc(value: DateDateTime) -> str: def format_datetime_utc(value: DateDateTime) -> str:
"""utc datetime as string from date or datetime value """utc datetime as string from date or datetime value
@ -19,7 +64,7 @@ def format_datetime_utc(value: DateDateTime) -> str:
Returns: Returns:
utc datetime value as string in iso format utc datetime value as string in iso format
""" """
if not isinstance(value, datetime.datetime): if type(value) is datetime.date:
value = datetime.datetime( value = datetime.datetime(
value.year, value.month, value.day, tzinfo=utc) value.year, value.month, value.day, tzinfo=utc)
value = value.replace(microsecond=1) value = value.replace(microsecond=1)
@ -85,7 +130,7 @@ class EventConverter(Event):
return format_datetime_utc(self.decoded(prop)) return format_datetime_utc(self.decoded(prop))
def _gcal_start(self) -> Dict[str, str]: def _gcal_start(self) -> GCal_DateDateTime:
""" event start dict from icalendar event """ event start dict from icalendar event
Raises: Raises:
@ -96,9 +141,9 @@ class EventConverter(Event):
""" """
value = self.decoded('DTSTART') value = self.decoded('DTSTART')
return gcal_date_or_dateTime(value) return GCal_DateDateTime.create_from(value)
def _gcal_end(self) -> Dict[str, str]: def _gcal_end(self) -> GCal_DateDateTime:
"""event end dict from icalendar event """event end dict from icalendar event
Raises: Raises:
@ -110,13 +155,17 @@ class EventConverter(Event):
result = None result = None
if 'DTEND' in self: if 'DTEND' in self:
value = self.decoded('DTEND') value = self.decoded('DTEND')
result = gcal_date_or_dateTime(value) result = GCal_DateDateTime.create_from(value)
elif 'DURATION' in self: elif 'DURATION' in self:
start_val = self.decoded('DTSTART') start_val = self.decoded('DTSTART')
duration = self.decoded('DURATION') duration = self.decoded('DURATION')
end_val = start_val + duration end_val = start_val + duration
if type(start_val) is datetime.date:
if type(end_val) is datetime.datetime:
end_val = datetime.date(
end_val.year, end_val.month, end_val.day)
result = gcal_date_or_dateTime(end_val, check_value=start_val) result = GCal_DateDateTime.create_from(end_val)
else: else:
raise ValueError('no DTEND or DURATION') raise ValueError('no DTEND or DURATION')
return result return result
@ -138,6 +187,18 @@ class EventConverter(Event):
if ics_prop in self: if ics_prop in self:
gcal_event[prop] = func(ics_prop) gcal_event[prop] = func(ics_prop)
def _get_prop(self, prop: str, func: Callable[[str], str]):
"""get property from ical event if exist else None
Arguments:
prop -- property name
func -- function to convert
"""
if prop not in self:
return None
return func(prop)
def to_gcal(self) -> EventData: def to_gcal(self) -> EventData:
"""Convert """Convert
@ -145,24 +206,19 @@ class EventConverter(Event):
dict - google calendar#event resource dict - google calendar#event resource
""" """
event = { kwargs = {
'iCalUID': self._str_prop('UID'), 'ical_uid': self._str_prop('UID'),
'start': self._gcal_start(), 'start': self._gcal_start(),
'end': self._gcal_end() 'end': self._gcal_end(),
'summary': self._get_prop('SUMMARY', self._str_prop),
'description': self._get_prop('DESCRIPTION', self._str_prop),
'location': self._get_prop('LOCATION', self._str_prop),
'created': self._get_prop('CREATED', self._datetime_str_prop),
'updated': self._get_prop('LAST-MODIFIED', self._datetime_str_prop),
'transparency': self._get_prop('TRANSP', lambda prop: self._str_prop(prop).lower()),
} }
self._put_to_gcal(event, 'summary', self._str_prop) return GCal_Event(**kwargs).dict(by_alias=True, exclude_defaults=True)
self._put_to_gcal(event, 'description', self._str_prop)
self._put_to_gcal(event, 'location', self._str_prop)
self._put_to_gcal(event, 'created', self._datetime_str_prop)
self._put_to_gcal(
event, 'updated', self._datetime_str_prop, 'LAST-MODIFIED')
self._put_to_gcal(
event,
'transparency',
lambda prop: self._str_prop(prop).lower(), 'TRANSP')
return event
class CalendarConverter: class CalendarConverter: