Giter Site home page Giter Site logo

ics-py's Introduction

ics.py 0.8.0.dev0 : iCalendar for Humans

Original repository (GitHub) -Bugtracker and issues (GitHub) -PyPi package (ics) -Documentation (Read The Docs).

Apache 2 License

Ics.py is a pythonic and easy iCalendar library. Its goals are to read and write ics data in a developer friendly way.

iCalendar is a widely-used and useful format but not user friendly. Ics.py is there to give you the ability of creating and reading this format without any knowledge of it.

It should be able to parse every calendar that respects the rfc5545 and maybe some more… It also outputs rfc compliant calendars.

iCalendar (file extension .ics) is used by Google Calendar, Apple Calendar, Android and many more.

Ics.py is available for Python 3.8, 3.9, 3.10, 3.11, 3.12 and is Apache2 Licensed.

Quickstart

$ pip install ics
from datetime import datetime
from ics import Calendar, Event

c = Calendar()
e = Event()
e.summary = "My cool event"
e.description = "A meaningful description"
e.begin = datetime.fromisoformat("2022-06-06T12:05:23+02:00")
e.end = datetime.fromisoformat("2022-06-06T13:05:23+02:00")
c.events.append(e)
c
# Calendar(extra=Container('VCALENDAR', []), extra_params={}, version='2.0', prodid='ics.py 0.8.0.dev0 - http://git.io/lLljaA', scale=None, method=None, events=[Event(extra=Container('VEVENT', []), extra_params={}, timespan=EventTimespan(begin_time=datetime.datetime(2022, 6, 6, 12, 5, 23, tzinfo=datetime.timezone(datetime.timedelta(seconds=7200))), end_time=None, duration=None, precision='second'), summary=None, uid='[email protected]', description=None, location=None, url=None, status=None, created=None, last_modified=None, dtstamp=datetime.datetime(2022, 6, 6, 19, 28, 14, 575558, tzinfo=Timezone.from_tzid('UTC')), alarms=[], attach=[], classification=None, transparent=None, organizer=None, geo=None, attendees=[], categories=[])], todos=[])
with open("my.ics", "w") as f:
    f.write(c.serialize())

More examples are available in the documentation.

Documentation

All the documentation is hosted on readthedocs.org and is updated automatically at every commit.

Contribute

Contribution are welcome of course! For more information and how to setup, see contributing.

Parse ALL the calendars!

ics-py's People

Contributors

allenporter avatar anoderay avatar astalaseven avatar c4ptaincrunch avatar davidjb avatar etnarek avatar introt avatar jammon avatar johnnoone avatar ludovic-gasc avatar make-github-pseudonymous-again avatar msabramo avatar n-coder avatar ousret avatar pascalbru avatar perette avatar philiptpp avatar prashnts avatar rkdarst avatar rkeilty avatar seants avatar strobeflash avatar t00n avatar tgamauf avatar timic3 avatar titouanc avatar tomschr avatar vyper0016 avatar whtsky avatar zuphilip avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ics-py's Issues

Event with no start should not try to export an end

e = Event()
print(e)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-18-1aca41ed74c4> in <module>()
----> 1 print(e)

/usr/lib/python2.7/site-packages/ics/event.py in __str__(self)
    127     def __str__(self):
    128         '''Returns the event as an iCalendar formatted string'''
--> 129         return super(Event, self).__str__()
    130 
    131     def __lt__(self, other):

/usr/lib/python2.7/site-packages/ics/component.py in __str__(self)
     77         container = self._unused.clone()
     78         for output in self._OUTPUTS:
---> 79             output(self, container)
     80         return str(container)

/usr/lib/python2.7/site-packages/ics/event.py in o_end(event, container)
    268 def o_end(event, container):
    269     if not event.begin:
--> 270         raise ValueError('An event with an end but no start cannot be exported')
    271     if event._end_time:
    272         container.append(ContentLine('DTEND', value=arrow_to_iso(event.end)))

ValueError: An event with an end but no start cannot be exported

`make_all_day` has no end

Event marked as during all day has no begin nor end.
Error on event.end.

    In [67]: f = Event()

    In [68]: f.name = "test"

    In [69]: f.make_all_day
    Out[69]: <bound method Event.make_all_day of <Event 'test'>>

    In [70]: f
    Out[70]: <Event 'test'>

    In [71]: f.end
    ---------------------------------------------------------------------------
    AttributeError                            Traceback (most recent call last)
    <ipython-input-71-02280bc918ac> in <module>()
    ----> 1 f.end

    /usr/lib/python2.7/site-packages/ics/event.py in end(self)
         89         else: # if end is not defined
         90             # return beginning + precision
    ---> 91             return self.begin.replace(**{self._begin_precision + 's': +1})
         92 
         93     @end.setter

    AttributeError: 'NoneType' object has no attribute 'replace'

    In [72]: f.begin

    In [73]: 

Event with begin doesn't change when marked as during all day.

    In [73]: f.begin = "2013-11-04T08:15:00+00:00"

    In [74]: f.make_all_day
    Out[74]: <bound method Event.make_all_day of <Event 'test' begin:2013-11-04T08:15:00+00:00 end:2013-11-04T08:15:01+00:00>>

    In [75]: f.begin
    Out[75]: <Arrow [2013-11-04T08:15:00+00:00]>

    In [76]: f.end
    Out[76]: <Arrow [2013-11-04T08:15:01+00:00]>

Support of extra attributes in event

Hi,

Unfortunately to me, if Google Calendar supports TRANSP attribute, with Office 365, they use extra attributes to do that:

X-MICROSOFT-CDO-APPT-SEQUENCE:0
X-MICROSOFT-CDO-BUSYSTATUS:FREE
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
X-MICROSOFT-CDO-IMPORTANCE:1

However, they provide more information.

Do you have an suggestion to implement that ?
I thought to add an attribute "extra", with a dict of extra attributes.

Thanks for your time.

Clean stacktrace for users

If an user gives something wrong to the lib, the stacktrace should stop at the first level of the lib and not go deeper.

Use descriptors

Hi Nikita,
thanks for this great piece of code, I like the pythonic way of dealing with the ical format.
For one of my projects I needed to have an URL in an event. I added url support and discovered, that it is even in the RFC. And there are many more possible properties for events. But adding them all the same way seemed a bit complicated. So I came up with this idea: Why not define the properties as descriptors on the components? Something like this:

class ICalProperty(object):
    """ Property for an ical object
    """
    def __init__(self, ical_name, validation=None):
        """ 
        ical_name: the name of this property in the rfc 5545
        validation: a function used to validate (and process) the input
        """
        self.ical_name = ical_name
        self.python_name = '_' + ical_name
        self.validation = validation

    def __get__(self, instance, owner):
        return getattr(instance, self.python_name, None)

    def __set__(self, instance, value):
        value = self.toPython(value)
        if self.validation:
            value = getattr(instance, self.validation)(value)
        setattr(instance, self.python_name, value)

    def toPython(self, value):
        """ Translate the input to a python value
        """
        return value


class DateTimeProperty(ICalProperty):
    """
    A Property that represents a datetime
    Some properties also accept a date (DTSTART, DTEND, DUE)
    """

    def __init__(self, *args, **kwargs):
        """
        Expects a parameter 'format_str' that can be used for datetime.strptime
        when reading the input in toPython
        Default is '%Y%m%d'
        """
        format_str = kwargs.pop('format_str', '%Y%m%d')
        super(DateProperty, self).__init__(*args, *kwargs)
        self.format_str = format_str

    def toPython(self, value):
        if isinstance(value, datetime) or isinstance(value, date):
            return value
        if isinstance(value, six.string_types):
            return datetime.strptime(value, self.format_str)
        raise ValueError


class Event(object):
    """Event"""
    begin = DateTimeProperty('DTSTART')
    end = DateTimeProperty('DTEND', 'validate_end')

    def validate_end(self, value):
        if value < self.begin:
            raise ValueError
        return value

What do you think of it? I'm tempted to try this, but 'im not sure, if it's a good idea.

Support badly formatted linesplits

Edit : the file shown below is broken (it does not follow the line wrap specified in the rfc). I keep this issue open just to remember to someday add a flag to allow those illegal line-wraps and be less strict.
@C4ptainCrunch


Hi C4,

I haven't really dug into the specificities of the .ics format, so I apologize if I'm saying something wrong :).

Using this as the source (which comes from this URL) I've been getting errors from ics.py. The reason is that the line 649 doesn't include a ':' character.

interrogations

def parse(cls, line):
    if ':' not in line:
        raise ParseError("No ':' in line '{}'".format(line))

I'm not sure if this is in fact allowed & your code is a bit too strict or if my source is at fault... but Google Calendar doesn't seem to have any problem with this feed.

My (quick & dirty) fix was to surround this parse() command with a try/except like this:

for line in unfolded_lines:
    try:
        yield ContentLine.parse(line)
    except ParseError:
        pass

I'm sure you can come up with something better :). This is pretty dirty so I didn't even bother making a pull request, especially since the SUMMARY field will be missing a word.

This is also one of the reasons why your gehol-tools isn't working for me atm.

Hope this helps!

How to set Timezone for Event?

Hi,

thanks for this great work so far. There is only one thing I haven't figured out yet. How do I set the Timezone for DTSTART and DTEND?

event equality

Event equality currently only checks for uid equality. This leads to strange situations:

e2 = e1.clone()
e2.begin=#something different than in e1
if e1 == e2:
    print("this will be printed")

I think event equality should check if all the members are the same.

Parse error

Impossible to parse a str(Calendar):

$ ipython
Python 2.7.5 (default, Oct 27 2013, 23:39:27) 
Type "copyright", "credits" or "license" for more information.

IPython 1.1.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: from ics import *

In [2]: from tests.fixture import *

In [3]: Calendar(cal1)
Out[3]: <Calendar with 1 events>

In [4]: Calendar(str(Calendar(cal1)))
---------------------------------------------------------------------------
ParseError                                Traceback (most recent call last)
<ipython-input-4-6ad9ad1c5089> in <module>()
----> 1 Calendar(str(Calendar(cal1)))

/home/titou/UrLab/ics.py/ics/icalendar.pyc in __init__(self, imports, events, creator)
     63                 container = string_to_container(imports)
     64             else:
---> 65                 container = lines_to_container(imports)
     66 
     67             # TODO : make a better API for multiple calendars

/home/titou/UrLab/ics.py/ics/parse.pyc in lines_to_container(lines)
    157 
    158 def lines_to_container(lines):
--> 159     return parse(tokenize_line(unfold_lines(lines)))
    160 
    161 

/home/titou/UrLab/ics.py/ics/parse.pyc in parse(tokenized_lines, block_name)
    148 def parse(tokenized_lines, block_name=None):
    149     res = []
--> 150     for line in tokenized_lines:
    151         if line.name == 'BEGIN':
    152             res.append(Container.parse(line.value, tokenized_lines))

/home/titou/UrLab/ics.py/ics/parse.pyc in tokenize_line(unfolded_lines)
    143 def tokenize_line(unfolded_lines):
    144     for line in unfolded_lines:
--> 145         yield ContentLine.parse(line)
    146 
    147 

/home/titou/UrLab/ics.py/ics/parse.pyc in parse(cls, line)
     51     def parse(cls, line):
     52         if ':' not in line:
---> 53             raise ParseError("No ':' in line '{}'".format(line))
     54 
     55         # Separe key and value

ParseError: No ':' in line 'B'

Cannot instanciate new Calendar

Events seem to be shared across all Calendar objects

In [50]: from ics import Calendar, Event

In [51]: c = Calendar()

In [52]: e = Event()

In [53]: e.name = "test"

In [54]: e.begin = "2013-11-04T08:15:00+00:00"

In [55]: c.events.append(e)

In [56]: c.events
Out[56]: [<Event 'test' begin:2013-11-04T08:15:00+00:00 end:2013-11-04T08:15:01+00:00>]

In [57]: c = Calendar()

In [58]: c.events
Out[58]: [<Event 'test' begin:2013-11-04T08:15:00+00:00 end:2013-11-04T08:15:01+00:00>]

In [59]: d = Calendar()

In [60]: d.events
Out[60]: [<Event 'test' begin:2013-11-04T08:15:00+00:00 end:2013-11-04T08:15:01+00:00>]

http://stackoverflow.com/questions/3161827/what-am-i-doing-wrong-python-object-instantiation-keeping-data-from-previous-in
http://stackoverflow.com/questions/2313075/python-default-value-for-a-function
http://stackoverflow.com/questions/1132941/least-astonishment-in-python-the-mutable-default-argument

ImportError: cannot import name PY2

readthedocs.org fails and it also appeared to some users

File "/var/build/user_builds/icspy/checkouts/latest/ics/icalendar.py", line 6, in <module>
    from six import PY2, PY3, StringIO, string_types, text_type, integer_types
ImportError: cannot import name PY2

Precisions wanted :)

The `Calendar.events` setter should not accept all iterables

Calendar.events setter should not accept Calendar().events = "42"
The Calendar.events setter should not accept all iterables

c.events = "42"
c.events
>> ['4', '2']

If value for events is a string, must first check string is a calendar.

(this if the other half of #44 )

Outputting DTSTART with VALUE=DATE

iso_to_arrow() supports parsing lines like DTSTART;VALUE=DATE:20151217 but arrow_to_iso() always forces events to be datetime including the Z timezone specifier. I am running into issues supporting all day events where the appended Z means that I have to generate Events using the user's timezone or else it ends up being on the previous day for all timezones with negative offsets.

DTSTART:20151215T000000Z ends up being an all day event on Dec 14th at 10pm for America/New_York

Recurrance support

Hey

I like your plugin and api. Sadly it does not support recurring events (like weekly events, birthdays). It will only find the first one.

Cheers, Kattalunikes

Error when parsing times

I have an event with the following details from a Google Calendar ICS file:

BEGIN:VEVENT
DTSTART;VALUE=DATE:20110519
DTEND;VALUE=DATE:20110520
RRULE:FREQ=YEARLY
DTSTAMP:20150223T150857Z
UID:someuid
CREATED:20130514T081129Z
DESCRIPTION:
LAST-MODIFIED:20130514T081155Z
LOCATION:
SEQUENCE:1
STATUS:CONFIRMED
SUMMARY:Event Details
TRANSP:TRANSPARENT
END:VEVENT

and when trying to parse this, I am getting the following error:

ParserError: Could not match input to any of ['YYYY-MM-DDTHH:mm'] on '20150223T150857Z'

Enhence Event order

If 2 events begin at the same time, the shortest event should be considered smaller than the other

ICS does not support timezone aware datetime objects

I banged my head against a wall for a while on this one as I was getting very strange timezones back.

There appears ot be a bug in Arrow where if you pass it a timezone aware object it misbehaves.

I submitted a bug there with more details, but logging this issue here in case anyone else runs into something similar: arrow-py/arrow#304

My solution was to strip the timezone from the datetime and then apply it to a non-timezone aware arrow object before assigning to begin.

timezone information is dropped

test case:

from ics.parse import ContentLine
from ics.utils import iso_to_arrow
line = ContentLine.parse("DTSTART;TZID=Europe/Berlin:20151104T190000")
print(line)
arrow = iso_to_arrow(line)
print(arrow)
print(arrow.__dict__)`

It acts as if the given time were in UTC and completely ignores that it is in fact another time zone.

I'm digging through the code right now.

Upgrade Arrow to 0.10.0

I see you are using an old version of Arrow (0.4.2). Now that datetime.date instances can be used for all day events, the version of Arrow you pin to doesn't support arrow.get(datetime.date) so the setter for Event.begin raises an exception. Is there a reason why it hasn't been upgraded yet? I can take a look if not.

  File "/Users/Josh/Developer/Github/training-cal/src/lib/ics/event.py", line 81, in __init__
    self.begin = begin
  File "/Users/Josh/Developer/Github/training-cal/src/lib/ics/event.py", line 112, in begin
    value = get_arrow(value)
  File "/Users/Josh/Developer/Github/training-cal/src/lib/ics/utils.py", line 157, in get_arrow
    return arrow.get(value)
  File "/Users/Josh/Developer/Github/training-cal/src/lib/arrow/api.py", line 23, in get
    return _factory.get(*args, **kwargs)
  File "/Users/Josh/Developer/Github/training-cal/src/lib/arrow/factory.py", line 146, in get
    raise TypeError('Can\'t parse single argument type of \'{0}\''.format(type(arg)))
TypeError: Can't parse single argument type of '<type 'datetime.date'>'

`EventList.append()` should only accept appending events.

EventList.append() should only accept appending events.

from ics import Calendar, Event

c = Calendar()
e = Event()
c.events.append(e)
c.events.append("not_an_event")

c
>> <Calendar with 2 events>

c.events
>> [<Event>, 'not_an_event']

event.end crashes if event.begin is None

Same problem as #28

f = Event()
f.begin # is None
f.end
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-71-02280bc918ac> in <module>()
----> 1 f.end

/usr/lib/python2.7/site-packages/ics/event.py in end(self)
     89         else: # if end is not defined
     90             # return beginning + precision
---> 91             return self.begin.replace(**{self._begin_precision + 's': +1})
     92 
     93     @end.setter

AttributeError: 'NoneType' object has no attribute 'replace'

Import from string broken

Import a calendar from a file in python3 is broken

In [18]: c = ics.Calendar(text)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-18-1e8f072ea87d> in <module>()
----> 1 c = ics.Calendar(text)

/usr/local/lib/python3.3/site-packages/ics/icalendar.py in __init__(self, imports, events, creator)
     40         if imports is not None:
     41             # TODO : Check python3 types
---> 42             if isinstance(imports, (str, unicode)):
     43                 container = string_to_container(imports)
     44             else:

NameError: global name 'unicode' is not defined

repr for events with unset time fails

repr(Event(name='bug'))

gives

/usr/local/lib/python3.3/site-packages/ics/event.py in __unicode__(self)
    114             return "<all-day Event {} :{}>".format(name, self.begin.strftime("%F"))
    115         else:
--> 116             return "<Event {}begin:{} end:{}>".format(name, self.begin.strftime("%F %X"), self.end.strftime("%F %X"))
    117
    118     def __str__(self):

AttributeError: 'NoneType' object has no attribute 'strftime'

Parse error with public google calendars

ics.py is unable to parse public google calendars, most likely due to an unexpected datetime format:

BEGIN:VEVENT
DTSTART:20141203T184500Z
DTEND:20141203T204500Z
DTSTAMP:20141106T083430Z

Sample calendar here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.