Remove - and : from example DTSTAMPs
[pycalendar.git] / pycalendar / aggregator.py
1 # Copyright (C) 2013 W. Trevor King <wking@tremily.us>
2 #
3 # This file is part of pycalender.
4 #
5 # pycalender is free software: you can redistribute it and/or modify it under
6 # the terms of the GNU General Public License as published by the Free Software
7 # Foundation, either version 3 of the License, or (at your option) any later
8 # version.
9 #
10 # pycalender is distributed in the hope that it will be useful, but WITHOUT ANY
11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License along with
15 # pycalender.  If not, see <http://www.gnu.org/licenses/>.
16
17 from . import text as _text
18
19
20 class Aggregator (list):
21     r"""An iCalendar feed aggregator
22
23     Figure out where the example feeds are located, relative to the
24     directory from which you run this doctest (i.e., the project's
25     root directory).
26
27     >>> import os
28     >>> root_dir = os.curdir
29     >>> data_dir = os.path.abspath(os.path.join(root_dir, 'test', 'data'))
30     >>> base_url = 'file://{}'.format(data_dir.replace(os.sep, '/'))
31
32     >>> from .feed import Feed
33
34     You can set processing hooks to analyze and manipulate feeds as
35     they come in.
36
37     >>> processors = [lambda feed: print("I'm processing {!r}".format(feed))]
38
39     >>> a = Aggregator(
40     ...     prodid='-//pycalendar//NONSGML testing//EN',
41     ...     feeds=[
42     ...         Feed(url='{}/{}'.format(base_url, name))
43     ...         for name in ['geohash.ics',]],
44     ...     processors=processors,
45     ...     )
46     >>> a  # doctest: +ELLIPSIS
47     [<Feed url:file://.../test/data/geohash.ics>]
48     >>> a.fetch()  # doctest: +ELLIPSIS
49     I'm processing <Feed url:file://.../test/data/geohash.ics>
50
51     Generate aggregate calendars with the ``.write`` method.
52
53     >>> import io
54     >>> stream = io.StringIO()
55     >>> a.write(stream=stream)
56     >>> value = stream.getvalue()
57     >>> value  # doctest: +ELLIPSIS
58     'BEGIN:VCALENDAR\r\nVERSION:2.0\r\n...END:VCALENDAR\r\n'
59     >>> print(value.replace('\r\n', '\n'))
60     BEGIN:VCALENDAR
61     VERSION:2.0
62     PRODID:-//pycalendar//NONSGML testing//EN
63     BEGIN:VEVENT
64     UID:2013-06-30@geohash.invalid
65     DTSTAMP:20130630T000000Z
66     DTSTART;VALUE=DATE:20130630
67     DTEND;VALUE=DATE:20130701
68     SUMMARY:XKCD geohashing\, Boston graticule
69     URL:http://xkcd.com/426/
70     LOCATION:Snow Hill\, Dover\, Massachusetts
71     GEO:42.226663;-71.28676
72     END:VEVENT
73     END:VCALENDAR
74     <BLANKLINE>
75     """
76     def __init__(self, prodid, version='2.0', feeds=None, processors=None):
77         super(Aggregator, self).__init__()
78         self.prodid = prodid
79         self.version = version
80         if feeds:
81             self.extend(feeds)
82         if not processors:
83             processors = []
84         self.processors = processors
85
86     def fetch(self):
87         for feed in self:
88             feed.fetch()
89             for processor in self.processors:
90                 processor(feed)
91
92     def write(self, stream):
93         stream.write('BEGIN:VCALENDAR\r\n')
94         stream.write('VERSION:{}\r\n'.format(_text.escape(self.version)))
95         stream.write('PRODID:{}\r\n'.format(_text.escape(self.prodid)))
96         for feed in self:
97             for key in [
98                     'VEVENT',
99                     'VFREEBUSY',
100                     'VJOURNAL',
101                     'VTODO',
102                     ]:
103                 for entry in feed.get(key, []):
104                     entry.write(stream=stream)
105         stream.write('END:VCALENDAR\r\n')