-
-
Notifications
You must be signed in to change notification settings - Fork 175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add missing mandatory fields to user generated components #390
Closed
Closed
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,3 +61,72 @@ Related projects | |
* `icalevents <https://github.com/irgangla/icalevents>`_. It is built on top of icalendar and allows you to query iCal files and get the events happening on specific dates. It manages recurrent events as well. | ||
* `recurring-ical-events <https://pypi.org/project/recurring-ical-events/>`_. Library to query an ``ICalendar`` object for events happening at a certain date or within a certain time. | ||
* `x-wr-timezone <https://pypi.org/project/x-wr-timezone/>`_. Library to make ``ICalendar`` objects and files using the non-standard ``X-WR-TIMEZONE`` compliant with the standard (RFC 5545). | ||
|
||
|
||
Examples | ||
================ | ||
Create a calendear and add an event to it (master branch) | ||
|
||
.. code-block:: python | ||
|
||
#!/usr/bin/env python3 | ||
from dateutil import tz | ||
from datetime import datetime | ||
|
||
from icalendar import ComponentWithRequiredFieldsFactory, vDatetime | ||
|
||
def main(): | ||
component_factory = ComponentWithRequiredFieldsFactory(tzid='Europe/Warsaw') | ||
|
||
calendar = component_factory['VCALENDAR']() | ||
event = component_factory['VEVENT']( | ||
DTSTART=vDDDTypes(datetime.now()), | ||
DTEND=vDDDTypes(datetime(year=2050, month=7, day=22, hour=12)), | ||
SUMMARY='A sentence succinctly describing the event', | ||
LOCATION='Where the event will take place', | ||
ORGANIZER='[email protected]', | ||
DESCRIPTION='Longer and more detailed version of the summary\nIt can also be multi-line', | ||
COMMENT='A comment') | ||
event.add('attendee', '[email protected]') | ||
event.add('attendee', '[email protected]') | ||
timezone = component_factory['VTIMEZONE']() | ||
calendar.add_component(timezone) | ||
calendar.add_component(event) | ||
print(calendar.to_ical().decode('utf-8')) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() | ||
|
||
|
||
You can view it with the handy CLI tool by: | ||
|
||
.. code-block:: text | ||
|
||
python SCRIPT-NAME.py > sample.ics && pythom -m icalendar.cli sample.ics | ||
|
||
Create a ics file with 5 alarms, each 10 minutes apart | ||
|
||
.. code-block:: python | ||
|
||
#!/usr/bin/env python3 | ||
from datetime import datetime, timedelta | ||
|
||
from icalendar import ComponentWithRequiredFieldsFactory, vDatetime | ||
|
||
def main(): | ||
now = datetime.now() | ||
alarms_triggers = [now + timedelta(minutes=10 * i) for i in range(5)] | ||
component_factory = ComponentWithRequiredFieldsFactory(tzid='Europe/Warsaw', alarm_trigger_supplier=lambda: alarms_triggers.pop()) | ||
|
||
calendar = component_factory['VCALENDAR']() | ||
calendar.add_component(component_factory['VTIMEZONE']()) | ||
|
||
for _ in alarms_triggers: | ||
calendar.add_component(component_factory['VALARM']()) | ||
|
||
print(calendar.to_ical().decode('utf-8')) | ||
|
||
if __name__ == '__main__': | ||
main() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,8 @@ | |
These are the defined components. | ||
""" | ||
from datetime import datetime, timedelta | ||
|
||
from icalendar import __version__ | ||
from icalendar.caselessdict import CaselessDict | ||
from icalendar.parser import Contentline | ||
from icalendar.parser import Contentlines | ||
|
@@ -12,8 +14,9 @@ | |
from icalendar.parser import q_split | ||
from icalendar.parser_tools import DEFAULT_ENCODING | ||
from icalendar.prop import TypesFactory | ||
from icalendar.prop import vText, vDDDLists | ||
from icalendar.prop import vText, vDDDLists, vDDDTypes | ||
from icalendar.timezone_cache import _timezone_cache | ||
from icalendar.tools import UIDGenerator | ||
|
||
import pytz | ||
import dateutil.rrule, dateutil.tz | ||
|
@@ -24,11 +27,60 @@ | |
###################################### | ||
# The component factory | ||
|
||
class ComponentWithRequiredFieldsFactory(CaselessDict): | ||
"""All components defined in rfc 5545 in the section 3.6 are registered in this factory class. | ||
All components returned by this factory should have all the required fields | ||
as per each component's definition. | ||
""" | ||
|
||
def __init__(self, host_name='example.com', tzid='Europe/London', daylight_saving=False, alarm_action='AUDIO', alarm_trigger_supplier=None, *args, **kwargs): | ||
"""Set keys to upper for initial dict. | ||
:param host_name is the suffix to be appended after components uid, e.g | ||
host_name='example.com' would result in [email protected] | ||
|
||
:param tzid is the the timezone id you'd like your event to be in, e.g Europe/Warsaw | ||
|
||
:param alarm_trigger_supplier is a callable that gives you the the date | ||
when the alarm is to go off, e.g lambda: datetime.datime(year=2002, month=7, day=22). | ||
Bother with it, if and only if, you plan on creating alarms with this factory | ||
""" | ||
super().__init__(*args, **kwargs,) | ||
self['VEVENT'] = lambda **kwargs: Event(**kwargs, UID=UIDGenerator.uid(host_name), DTSTAMP=vDDDTypes(datetime.now(pytz.timezone(tzid)))) | ||
self['VTODO'] = lambda **kwargs: Todo(**kwargs, UID=UIDGenerator.uid(host_name), DTSTAMP=vDDDTypes(datetime.now(pytz.timezone(tzid)))) | ||
self['VJOURNAL'] = lambda **kwargs: Journal(**kwargs, UID=UIDGenerator.uid(host_name), DTSTAMP=vDDDTypes(datetime.now(pytz.timezone(tzid)))) | ||
self['VFREEBUSY'] = lambda **kwargs: FreeBusy(**kwargs, UID=UIDGenerator.uid(host_name), DTSTAMP=vDDDTypes(datetime.now(pytz.timezone(tzid)))) | ||
self['VTIMEZONE'] = lambda **kwargs: self._create_timezone(tzid, daylight_saving, **kwargs) | ||
self['VALARM'] = lambda **kwargs: self.create_alarm(alarm_action, alarm_trigger_supplier(), **kwargs) | ||
self['VCALENDAR'] = lambda **kwargs: Calendar(**kwargs, PRODID='icalendar', VERSION='2') | ||
self._host_name = host_name | ||
|
||
def _create_timezone(self, tzid, daylight_saving, **kwargs): | ||
now = datetime.now(dateutil.tz.gettz(tzid)) | ||
offset = now.strftime('%z') | ||
timezone = Timezone(**kwargs, tzid=now.tzname()) | ||
timezone_type = TimezoneDaylight if daylight_saving else TimezoneStandard | ||
# DTSTART is probably very wrong. I have no idea what it should be set to | ||
timezone_type = timezone_type(DTSTART=vDDDTypes(now), TZOFFSETTO=offset, TZOFFSETFROM=offset) | ||
timezone.add_component(timezone_type) | ||
return timezone | ||
|
||
|
||
|
||
def create_alarm(self, alarm_action, alarm_trigger, **kwargs): | ||
"""Create a VALARM | ||
:param alarm_action: what is supposed to happen on alarm trigger, e.g use AUDIO to play a sound | ||
on trigger | ||
:type alarm_action: string | ||
|
||
:param alarm_trigger: at what time is the alarm supposed to trigger | ||
:type alarm_trigger: :class `datetime.datetime` | ||
""" | ||
return Alarm(**kwargs, action=alarm_action, trigger=vDDDTypes(alarm_trigger)) | ||
|
||
class ComponentFactory(CaselessDict): | ||
"""All components defined in rfc 2445 are registered in this factory class. | ||
To get a component you can use it like this. | ||
""" | ||
|
||
def __init__(self, *args, **kwargs): | ||
"""Set keys to upper for initial dict. | ||
""" | ||
|
@@ -43,7 +95,6 @@ def __init__(self, *args, **kwargs): | |
self['VALARM'] = Alarm | ||
self['VCALENDAR'] = Calendar | ||
|
||
|
||
# These Properties have multiple property values inlined in one propertyline | ||
# seperated by comma. Use CaselessDict as simple caseless set. | ||
INLINE = CaselessDict({ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are already using pytz and hopefully soon using tzinfo, let's not start using dateutil's tz implementation as well.
Also, at least in this snippet, we don't really need it, or am I missing something?