pycalendar-2.0~svn13177/ 0000755 0001750 0001750 00000000000 12322631216 014033 5 ustar rahul rahul pycalendar-2.0~svn13177/LICENSE 0000644 0001750 0001750 00000026136 10613006211 015040 0 ustar rahul rahul
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
pycalendar-2.0~svn13177/setup.py 0000644 0001750 0001750 00000001734 12101017573 015551 0 ustar rahul rahul ##
# Copyright (c) 2007-2011 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from distutils.core import setup, Extension
setup (
name = "pycalendar",
version = "2.0",
description = "iCalendar/vCard Library",
license = "Apache 2.0",
platforms = ["any"],
package_dir={'': 'src'},
packages = [
'pycalendar',
'pycalendar.icalendar',
'pycalendar.vcard',
]
)
pycalendar-2.0~svn13177/.project 0000644 0001750 0001750 00000000752 10777427303 015522 0 ustar rahul rahul
PyCalendar
org.python.pydev.PyDevBuilder
com.ibm.etools.validation.validationbuilder
org.python.pydev.pythonNature
pycalendar-2.0~svn13177/src/ 0000755 0001750 0001750 00000000000 12322631216 014622 5 ustar rahul rahul pycalendar-2.0~svn13177/src/zonal/ 0000755 0001750 0001750 00000000000 12322631216 015745 5 ustar rahul rahul pycalendar-2.0~svn13177/src/zonal/zone.py 0000644 0001750 0001750 00000044627 12101017573 017306 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.vtimezone import PyCalendarVTimezone
from pycalendar.property import PyCalendarProperty
from pycalendar import definitions
from pycalendar.vtimezonestandard import PyCalendarVTimezoneStandard
from pycalendar.utcoffsetvalue import PyCalendarUTCOffsetValue
import utils
import rule
"""
Class that maintains a TZ data Zone.
"""
__all__ = (
"Zone",
"ZoneRule",
)
class Zone(object):
"""
A tzdata Zone object containing a set of ZoneRules
"""
def __init__(self):
self.name = ""
self.rules = []
def __str__(self):
return self.generate()
def __eq__(self, other):
return other and (
self.name == other.name and
self.rules == other.rules
)
def __ne__(self, other):
return not self.__eq__(other)
def parse(self, lines):
"""
Parse the Zone lines from tzdata.
@param lines: the lines to parse.
@type lines: C{str}
"""
# Parse one line at a time
splitlines = lines.split("\n")
# First line is special
line = splitlines[0]
splits = [x for x in line.expandtabs(1).split(" ") if len(x) > 0]
self.name = splits[1]
rule = ZoneRule(self)
rule.parse(line, 0)
self.rules.append(rule)
for line in splitlines[1:]:
if len(line) == 0:
continue
rule = ZoneRule(self)
rule.parse(line, 2)
if rule.gmtoff != "#":
self.rules.append(rule)
def generate(self):
"""
Generate a partial Zone line.
@return: a C{str} with the Rule.
"""
lines = []
for count, rule in enumerate(self.rules):
if count == 0:
items = (
"Zone " + self.name,
rule.generate(),
)
else:
items = (
"",
"",
"",
rule.generate(),
)
lines.append("\t".join(items))
return "\n".join(lines)
def expand(self, rules, minYear, maxYear):
"""
Expand this zone into a set of transitions.
@param rules: parsed Rules for the tzdb
@type rules: C{dict}
@param minYear: starting year
@type minYear: C{int}
@param maxYear: ending year
@type maxYear: C{int}
@return: C{list} of C{tuple} for (
transition date-time,
offset to,
offset from,
associated rule,
)
"""
# Start at 1/1/1800 with the offset from the initial zone rule
start = PyCalendarDateTime(year=1800, month=1, day=1, hours=0, minutes=0, seconds=0)
start_offset = self.rules[0].getUTCOffset()
start_stdoffset = self.rules[0].getUTCOffset()
startdt = start.duplicate()
# Now add each zone rules dates
transitions = []
lastUntilDateUTC = start.duplicate()
last_offset = start_offset
last_stdoffset = start_stdoffset
first = True
for zonerule in self.rules:
last_offset, last_stdoffset = zonerule.expand(rules, transitions, lastUntilDateUTC, last_offset, last_stdoffset, maxYear)
lastUntilDate = zonerule.getUntilDate()
lastUntilDateUTC = lastUntilDate.getUTC(last_offset, last_stdoffset)
# We typically don't care about the initial one
if first and len(self.rules) > 1:
transitions = []
first = False
# Sort the results by date
transitions.sort(cmp=lambda x, y: x[0].compareDateTime(y[0]))
# Now scan transitions looking for real changes and note those
results = []
last_transition = (startdt, start_offset, start_offset)
for transition in transitions:
dtutc, to_offset, zonerule, rule = transition
dt = dtutc.duplicate()
dt.offsetSeconds(last_transition[1])
if dtutc.getYear() >= minYear:
if dt > last_transition[0]:
results.append((dt, last_transition[1], to_offset, zonerule, rule))
elif dt <= last_transition[0]:
if len(results):
results[-1] = ((results[-1][0], results[-1][1], to_offset, zonerule, None))
else:
results.append((last_transition[0], last_transition[1], last_transition[2], zonerule, None))
last_transition = (dt, to_offset, last_transition[2], rule)
return results
def vtimezone(self, calendar, rules, minYear, maxYear):
"""
Generate a VTIMEZONE for this Zone.
@param calendar: the L{PyCalendar} object for the VCALENDAR in which the VTIMEZONE
will be created.
@param rules: the C{dict} containing the set of Rules currently defined.
@param startYear: a C{int} containing the first year that should be present
in the VTIMEZONE.
@return: C{vtimezone} component.
"""
# Get a VTIMEZONE component
vtz = PyCalendarVTimezone(parent=calendar)
# Add TZID property
vtz.addProperty(PyCalendarProperty(definitions.cICalProperty_TZID, self.name))
vtz.addProperty(PyCalendarProperty("X-LIC-LOCATION", self.name))
transitions = self.expand(rules, minYear, maxYear)
# Group rules
lastZoneRule = None
ruleorder = []
rulemap = {}
def _generateRuleData():
# Generate VTIMEZONE component for last set of rules
for rule in ruleorder:
if rule:
# Accumulate rule portions with the same offset pairs
lastOffsetPair = (rulemap[rule][0][1], rulemap[rule][0][2],)
startIndex = 0
for index in xrange(len(rulemap[rule])):
offsetPair = (rulemap[rule][index][1], rulemap[rule][index][2],)
if offsetPair != lastOffsetPair:
rule.vtimezone(
vtz,
lastZoneRule,
rulemap[rule][startIndex][0],
rulemap[rule][index - 1][0],
rulemap[rule][startIndex][1],
rulemap[rule][startIndex][2],
index - startIndex,
)
lastOffsetPair = (rulemap[rule][index][1], rulemap[rule][index][2],)
startIndex = index
rule.vtimezone(
vtz,
lastZoneRule,
rulemap[rule][startIndex][0],
rulemap[rule][index][0],
rulemap[rule][startIndex][1],
rulemap[rule][startIndex][2],
len(rulemap[rule]),
)
else:
lastZoneRule.vtimezone(
vtz,
lastZoneRule,
rulemap[rule][0][0],
rulemap[rule][-1][0],
rulemap[rule][0][1],
rulemap[rule][0][2],
)
del ruleorder[:]
rulemap.clear()
for dt, offsetfrom, offsetto, zonerule, rule in transitions:
# Check for change of rule - we ignore LMT's
if zonerule.format != "LMT":
if lastZoneRule and lastZoneRule != zonerule:
_generateRuleData()
if rule not in ruleorder:
ruleorder.append(rule)
rulemap.setdefault(rule, []).append((dt, offsetfrom, offsetto,))
lastZoneRule = zonerule
# Do left overs
_generateRuleData()
self._compressRDateComponents(vtz)
vtz.finalise()
return vtz
def _compressRDateComponents(self, vtz):
"""
Compress sub-components with RDATEs into a single component with multiple
RDATEs assuming all other properties are the same.
@param vtz: the VTIMEZONE object to compress
@type vtz: L{PyCalendarVTimezone}
"""
# Map the similar sub-components together
similarMap = {}
for item in vtz.mComponents:
item.finalise()
key = (
item.getType(),
item.getTZName(),
item.getUTCOffset(),
item.getUTCOffsetFrom(),
)
if item.hasProperty(definitions.cICalProperty_RDATE):
similarMap.setdefault(key, []).append(item)
# Merge similar
for values in similarMap.itervalues():
if len(values) > 1:
mergeTo = values[0]
for mergeFrom in values[1:]:
# Copy RDATE from to and remove from actual timezone
prop = mergeFrom.getProperties()[definitions.cICalProperty_RDATE][0]
mergeTo.addProperty(prop)
vtz.mComponents.remove(mergeFrom)
class ZoneRule(object):
"""
A specific rule for a portion of a Zone
"""
def __init__(self, zone):
self.zone = zone
self.gmtoff = 0
self.rule = ""
self.format = ""
self.until = None
def __str__(self):
return self.generate()
def __eq__(self, other):
return other and (
self.gmtoff == other.gmtoff and
self.rule == other.rule and
self.format == other.format and
self.until == other.until
)
def __ne__(self, other):
return not self.__eq__(other)
def parse(self, line, offset):
"""
Parse the Zone line from tzdata.
@param line: a C{str} containing the line to parse.
"""
splits = [x for x in line.expandtabs(1).split(" ") if len(x) > 0]
assert len(splits) + offset >= 5, "Help: %s" % (line,)
self.gmtoff = splits[2 - offset]
self.rule = splits[3 - offset]
self.format = splits[4 - offset]
if len(splits) >= 6 - offset:
self.until = " ".join(splits[5 - offset:])
def generate(self):
"""
Generate a partial Zone line.
@return: a C{str} with the Rule.
"""
items = (
self.gmtoff,
self.rule,
self.format,
)
if self.until:
items = items + (self.until,)
return "\t".join(items)
def getUntilDate(self):
if hasattr(self, "_cached_until"):
return self._cached_until
year = 9999
month = 12
day = 1
hours = 0
minutes = 0
seconds = 0
mode = None
if self.until and not self.until.startswith("#"):
splits = self.until.split(" ")
year = int(splits[0])
month = 1
day = 1
hours = 0
minutes = 0
seconds = 0
mode = None
if len(splits) > 1 and not splits[1].startswith("#"):
month = int(rule.Rule.MONTH_NAME_TO_POS[splits[1]])
if len(splits) > 2 and not splits[2].startswith("#"):
if splits[2] == "lastSun":
dt = PyCalendarDateTime(year=year, month=month, day=1)
dt.setDayOfWeekInMonth(-1, PyCalendarDateTime.SUNDAY)
splits[2] = dt.getDay()
elif splits[2] == "lastSat":
dt = PyCalendarDateTime(year=year, month=month, day=1)
dt.setDayOfWeekInMonth(-1, PyCalendarDateTime.SATURDAY)
splits[2] = dt.getDay()
elif splits[2] == "Sun>=1":
dt = PyCalendarDateTime(year=year, month=month, day=1)
dt.setDayOfWeekInMonth(1, PyCalendarDateTime.SUNDAY)
splits[2] = dt.getDay()
day = int(splits[2])
if len(splits) > 3 and not splits[3].startswith("#"):
splits = splits[3].split(":")
hours = int(splits[0])
minutes = int(splits[1][:2])
if len(splits[1]) > 2:
mode = splits[1][2:]
else:
mode = None
if len(splits) > 2:
seconds = int(splits[2])
dt = PyCalendarDateTime(year=year, month=month, day=day, hours=hours, minutes=minutes, seconds=seconds)
self._cached_until = utils.DateTime(dt, mode)
return self._cached_until
def getUTCOffset(self):
if hasattr(self, "_cached_utc_offset"):
return self._cached_uutc_offset
splits = self.gmtoff.split(":")
hours = int(splits[0] if splits[0][0] != "-" else splits[0][1:])
minutes = int(splits[1]) if len(splits) > 1 else 0
seconds = int(splits[2]) if len(splits) > 2 else 0
negative = splits[0][0] == "-"
self._cached_uutc_offset = ((hours * 60) + minutes) * 60 + seconds
if negative:
self._cached_uutc_offset = -self._cached_uutc_offset
return self._cached_uutc_offset
def expand(self, rules, results, lastUntilUTC, lastOffset, lastStdOffset, maxYear):
# Expand the rule
assert self.rule == "-" or self.rule[0].isdigit() or self.rule in rules, "No rule '%s' found in cache. %s for %s" % (self.rule, self, self.zone,)
if self.rule == "-" or self.rule[0].isdigit():
return self.expand_norule(results, lastUntilUTC, maxYear)
else:
tempresults = []
ruleset = rules[self.rule]
ruleset.expand(tempresults, self, maxYear)
# Sort the results by date
tempresults.sort(cmp=lambda x, y: x[0].compareDateTime(y[0]))
found_one = False
found_start = False
last_offset = lastOffset
last_stdoffset = lastStdOffset
finalUntil = self.getUntilDate()
for dt, to_offset, rule in tempresults:
dtutc = dt.getUTC(last_offset, last_stdoffset)
if dtutc >= lastUntilUTC:
if not found_start and dtutc != lastUntilUTC:
# Insert a start item
if not found_one:
last_offset = self.getUTCOffset()
last_stdoffset = self.getUTCOffset()
dtutc = dt.getUTC(last_offset, last_stdoffset)
results.append((lastUntilUTC, last_offset, self, None))
found_start = True
if dtutc >= finalUntil.getUTC(last_offset, last_stdoffset):
break
results.append((dtutc, to_offset, self, rule))
last_offset = to_offset
last_stdoffset = self.getUTCOffset()
found_one = True
if found_start == 0:
results.append((lastUntilUTC, last_offset, self, None))
return last_offset, last_stdoffset
def expand_norule(self, results, lastUntil, maxYear):
to_offset = 0
if self.rule[0].isdigit():
splits = self.rule.split(":")
to_offset = 60 * 60 * int(splits[0])
if len(splits) > 1:
to_offset += 60 * int(splits[1])
# Always add a transition for the start of this rule
results.append((lastUntil, self.getUTCOffset() + to_offset, self, None))
return (self.getUTCOffset() + to_offset, self.getUTCOffset())
def vtimezone(self, vtz, zonerule, start, end, offsetfrom, offsetto):
# Determine type of component based on offset
comp = PyCalendarVTimezoneStandard(parent=vtz)
# Do offsets
tzoffsetfrom = PyCalendarUTCOffsetValue(offsetfrom)
tzoffsetto = PyCalendarUTCOffsetValue(offsetto)
comp.addProperty(PyCalendarProperty(definitions.cICalProperty_TZOFFSETFROM, tzoffsetfrom))
comp.addProperty(PyCalendarProperty(definitions.cICalProperty_TZOFFSETTO, tzoffsetto))
# Do TZNAME
if self.format.find("%") != -1:
tzname = self.format % ("S",)
else:
tzname = self.format
comp.addProperty(PyCalendarProperty(definitions.cICalProperty_TZNAME, tzname))
# Do DTSTART
comp.addProperty(PyCalendarProperty(definitions.cICalProperty_DTSTART, start))
# Recurrence
comp.addProperty(PyCalendarProperty(definitions.cICalProperty_RDATE, start))
comp.finalise()
vtz.addComponent(comp)
if __name__ == '__main__':
rulesdef = """Rule\tUS\t1918\t1919\t-\tMar\tlastSun\t2:00\t1:00\tD
Rule\tUS\t1918\t1919\t-\tOct\tlastSun\t2:00\t0\tS
Rule\tUS\t1942\tonly\t-\tFeb\t9\t2:00\t1:00\tW # War
Rule\tUS\t1945\tonly\t-\tAug\t14\t23:00u\t1:00\tP # Peace
Rule\tUS\t1945\tonly\t-\tSep\t30\t2:00\t0\tS
Rule\tUS\t1967\t2006\t-\tOct\tlastSun\t2:00\t0\tS
Rule\tUS\t1967\t1973\t-\tApr\tlastSun\t2:00\t1:00\tD
Rule\tUS\t1974\tonly\t-\tJan\t6\t2:00\t1:00\tD
Rule\tUS\t1975\tonly\t-\tFeb\t23\t2:00\t1:00\tD
Rule\tUS\t1976\t1986\t-\tApr\tlastSun\t2:00\t1:00\tD
Rule\tUS\t1987\t2006\t-\tApr\tSun>=1\t2:00\t1:00\tD
Rule\tUS\t2007\tmax\t-\tMar\tSun>=8\t2:00\t1:00\tD
Rule\tUS\t2007\tmax\t-\tNov\tSun>=1\t2:00\t0\tS"""
rules = {}
import rule
ruleset = rule.RuleSet()
ruleset.parse(rulesdef)
rules[ruleset.name] = ruleset
zonedef = """Zone America/New_York -4:56:02\t-\tLMT\t1883 Nov 18 12:03:58
\t\t\t-5:00\tUS\tE%sT\t1920
\t\t\t-5:00\tNYC\tE%sT\t1942
\t\t\t-5:00\tUS\tE%sT\t1946
\t\t\t-5:00\tNYC\tE%sT\t1967
\t\t\t-5:00\tUS\tE%sT"""
zone = Zone()
zone.parse(zonedef)
pycalendar-2.0~svn13177/src/zonal/tzconvert.py 0000755 0001750 0001750 00000022005 12101017573 020356 0 ustar rahul rahul #!/usr/bin/env python
##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from __future__ import with_statement
from difflib import unified_diff
from pycalendar.calendar import PyCalendar
import cStringIO as StringIO
import getopt
import os
import rule
import sys
import zone
"""
Classes to parse a tzdata files and generate VTIMEZONE data.
"""
__all__ = (
"tzconvert",
)
class tzconvert(object):
def __init__(self, verbose=False):
self.rules = {}
self.zones = {}
self.links = {}
self.verbose = verbose
def getZoneNames(self):
return set(self.zones.keys())
def parse(self, file):
try:
f = open(file, "r")
ctr = 0
for line in f:
ctr += 1
line = line[:-1]
while True:
if line.startswith("#") or len(line) == 0:
break
elif line.startswith("Rule"):
self.parseRule(line)
break
elif line.startswith("Zone"):
line = self.parseZone(line, f)
if line is None:
break
elif line.startswith("Link"):
self.parseLink(line)
break
elif len(line.strip()) != 0:
assert False, "Could not parse line %d from tzconvert file: '%s'" % (ctr, line,)
else:
break
except:
print "Failed to parse file %s" % (file,)
raise
def parseRule(self, line):
ruleitem = rule.Rule()
ruleitem.parse(line)
self.rules.setdefault(ruleitem.name, rule.RuleSet()).rules.append(ruleitem)
def parseZone(self, line, f):
os = StringIO.StringIO()
os.write(line)
last_line = None
for nextline in f:
nextline = nextline[:-1]
if nextline.startswith("\t"):
os.write("\n")
os.write(nextline)
elif nextline.startswith("#") or len(nextline) == 0:
continue
else:
last_line = nextline
break
zoneitem = zone.Zone()
zoneitem.parse(os.getvalue())
self.zones[zoneitem.name] = zoneitem
return last_line
def parseLink(self, line):
splits = line.split()
linkFrom = splits[1]
linkTo = splits[2]
self.links[linkTo] = linkFrom
def expandZone(self, zonename, minYear, maxYear=2018):
"""
Expand a zones transition dates up to the specified year.
"""
zone = self.zones[zonename]
expanded = zone.expand(self.rules, minYear, maxYear)
return [(item[0], item[1], item[2],) for item in expanded]
def vtimezones(self, minYear, maxYear=2018, filterzones=None):
"""
Generate iCalendar data for all VTIMEZONEs or just those specified
"""
cal = PyCalendar()
for zone in self.zones.itervalues():
if filterzones and zone.name not in filterzones:
continue
vtz = zone.vtimezone(cal, self.rules, minYear, maxYear)
cal.addComponent(vtz)
return cal.getText()
def generateZoneinfoFiles(self, outputdir, minYear, maxYear=2018, links=True, filterzones=None):
# Empty current directory
try:
for root, dirs, files in os.walk(outputdir, topdown=False):
for name in files:
os.remove(os.path.join(root, name))
for name in dirs:
os.rmdir(os.path.join(root, name))
except OSError:
pass
for zone in self.zones.itervalues():
if filterzones and zone.name not in filterzones:
continue
cal = PyCalendar()
vtz = zone.vtimezone(cal, self.rules, minYear, maxYear)
cal.addComponent(vtz)
icsdata = cal.getText()
fpath = os.path.join(outputdir, zone.name + ".ics")
if not os.path.exists(os.path.dirname(fpath)):
os.makedirs(os.path.dirname(fpath))
with open(fpath, "w") as f:
f.write(icsdata)
if self.verbose:
print "Write path: %s" % (fpath,)
if links:
link_list = []
for linkTo, linkFrom in self.links.iteritems():
# Check for existing output file
fromPath = os.path.join(outputdir, linkFrom + ".ics")
if not os.path.exists(fromPath):
print "Missing link from: %s to %s" % (linkFrom, linkTo,)
continue
with open(fromPath) as f:
icsdata = f.read()
icsdata = icsdata.replace(linkFrom, linkTo)
toPath = os.path.join(outputdir, linkTo + ".ics")
if not os.path.exists(os.path.dirname(toPath)):
os.makedirs(os.path.dirname(toPath))
with open(toPath, "w") as f:
f.write(icsdata)
if self.verbose:
print "Write link: %s" % (linkTo,)
link_list.append("%s\t%s" % (linkTo, linkFrom,))
# Generate link mapping file
linkPath = os.path.join(outputdir, "links.txt")
open(linkPath, "w").write("\n".join(link_list))
def usage(error_msg=None):
if error_msg:
print error_msg
print """Usage: tzconvert [options] [DIR]
Options:
-h Print this help and exit
--prodid PROD-ID string to use
--start Start year
--end End year
Arguments:
DIR Directory containing an Olson tzdata directory to read, also
where zoneinfo data will be written
Description:
This utility convert Olson-style timezone data in iCalendar.
VTIMEZONE objects, one .ics file per-timezone.
"""
if error_msg:
raise ValueError(error_msg)
else:
sys.exit(0)
if __name__ == '__main__':
# Set the PRODID value used in generated iCalendar data
prodid = "-//mulberrymail.com//Zonal//EN"
rootdir = "../../stuff/temp"
startYear = 1800
endYear = 2018
options, args = getopt.getopt(sys.argv[1:], "h", ["prodid=", "root=", "start=", "end=", ])
for option, value in options:
if option == "-h":
usage()
elif option == "--prodid":
prodid = value
elif option == "--root":
rootdir = value
elif option == "--start":
startYear = int(value)
elif option == "--end":
endYear = int(value)
else:
usage("Unrecognized option: %s" % (option,))
# Process arguments
if len(args) > 1:
usage("Must have only one argument")
if len(args) == 1:
rootdir = os.path.expanduser(args[0])
PyCalendar.sProdID = prodid
zonedir = os.path.join(rootdir, "tzdata")
zonefiles = (
"northamerica",
"southamerica",
"europe",
"africa",
"asia",
"australasia",
"antarctica",
"etcetera",
"backward",
)
parser = tzconvert(verbose=True)
for file in zonefiles:
parser.parse(os.path.join(zonedir, file))
if 1:
parser.generateZoneinfoFiles(os.path.join(rootdir, "zoneinfo"), startYear, endYear, filterzones=(
#"America/Montevideo",
#"Europe/Paris",
#"Africa/Cairo",
))
if 0:
checkName = "EST"
parsed = parser.vtimezones(1800, 2018, filterzones=(
checkName,
))
icsdir = "../2008i/zoneinfo"
cal = PyCalendar()
for file in (checkName,):
fin = open(os.path.join(icsdir, file + ".ics"), "r")
cal.parse(fin)
for vtz in cal.getVTimezoneDB():
#from pycalendar.vtimezoneelement import PyCalendarVTimezoneElement
#vtz.mEmbedded.sort(PyCalendarVTimezoneElement.sort_dtstart)
for embedded in vtz.mEmbedded:
embedded.finalise()
vtz.finalise()
os = StringIO.StringIO()
cal.generate(os, False)
actual = os.getvalue()
print "-- ACTUAL --"
print actual
print
print "-- PARSED --"
print parsed
print
print "-- DIFF --"
print "\n".join([line for line in unified_diff(actual.split("\n"), parsed.split("\n"))])
pycalendar-2.0~svn13177/src/zonal/tzverify.py 0000755 0001750 0001750 00000017370 12101017573 020213 0 ustar rahul rahul #!/usr/bin/env python
##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.calendar import PyCalendar
from pycalendar.datetime import PyCalendarDateTime
from tzconvert import tzconvert
import os
import sys
import getopt
from pycalendar.exceptions import PyCalendarInvalidData
def loadCalendarFromZoneinfo(zoneinfopath, skips=(), verbose=False, quiet=False):
if not quiet:
print "Scanning for calendar data in: %s" % (zoneinfopath,)
paths = []
def scanForICS(dirpath):
for fname in os.listdir(dirpath):
fpath = os.path.join(dirpath, fname)
if os.path.isdir(fpath):
scanForICS(fpath)
elif fname.endswith(".ics"):
for skip in skips:
if skip in fpath:
break
else:
if verbose:
print "Found calendar data: %s" % (fpath,)
paths.append(fpath)
scanForICS(zoneinfopath)
if not quiet:
print "Parsing calendar data in: %s" % (zoneinfopath,)
return loadCalendar(paths, verbose)
def loadCalendar(files, verbose):
cal = PyCalendar()
for file in files:
if verbose:
print "Parsing calendar data: %s" % (file,)
fin = open(file, "r")
try:
cal.parse(fin)
except PyCalendarInvalidData, e:
print "Failed to parse bad data: %s" % (e.mData,)
raise
return CalendarZonesWrapper(calendar=cal)
def parseTZData(zonedir, zonefiles):
parser = tzconvert()
for file in zonefiles:
zonefile = os.path.join(zonedir, file)
if not os.path.exists(zonefile):
print "Zone file '%s' does not exist." % (zonefile,)
parser.parse(zonefile)
return CalendarZonesWrapper(zones=parser)
class CalendarZonesWrapper(object):
def __init__(self, calendar=None, zones=None):
self.calendar = calendar
self.zones = zones
assert self.calendar is not None or self.zones is not None
def getTZIDs(self):
if self.calendar:
return getTZIDs(self.calendar)
elif self.zones:
return self.zones.getZoneNames()
def expandTransitions(self, tzid, start, end):
if self.calendar:
return getExpandedDates(self.calendar, tzid, start, end)
elif self.zones:
return self.zones.expandZone(tzid, start.getYear(), end.getYear())
def compareCalendars(calendar1, calendar2, start, end, filterTzids=(), verbose=False, quiet=False):
# Get all TZIDs from the calendar
tzids1 = calendar1.getTZIDs()
tzids2 = calendar2.getTZIDs()
# Find TZIDs that do not have a corresponding zone
missing = tzids1.difference(tzids2)
if missing:
print """TZIDs in calendar 1 not in calendar 2 files: %s
These cannot be checked.""" % (", ".join(missing),)
for tzid in tzids1.intersection(tzids2):
if filterTzids and tzid not in filterTzids:
continue
if not quiet:
print "\nChecking TZID: %s" % (tzid,)
calendardates1 = calendar1.expandTransitions(tzid, start, end)
calendardates2 = calendar2.expandTransitions(tzid, start, end)
if verbose:
print "Calendar 1 dates: %s" % (formattedExpandedDates(calendardates1),)
print "Calendar 2 dates: %s" % (formattedExpandedDates(calendardates2),)
set1 = set(calendardates1)
set2 = set(calendardates2)
d1 = set1.difference(set2)
for i in set(d1):
if i[0] == start:
d1.discard(i)
break
if i[1] == i[2]:
d1.discard(i)
d2 = set2.difference(set1)
for i in set(d2):
if i[1] == i[2]:
d2.discard(i)
if d1:
print "In calendar 1 but not in calendar 2 tzid=%s: %s" % (tzid, formattedExpandedDates(d1),)
if d2:
print "In calendar 2 but not in calendar 1 tzid=%s: %s" % (tzid, formattedExpandedDates(d2),)
if not d1 and not d2 and not quiet:
print "Matched: %s" % (tzid,)
def getTZIDs(cal):
results = set()
db = cal.getVTimezoneDB()
for vtz in db:
tzid = vtz.getID()
results.add(tzid)
return results
def getExpandedDates(cal, tzid, start, end):
db = cal.getVTimezoneDB()
return db[tzid].expandAll(start, end)
def sortedList(setdata):
l = list(setdata)
l.sort(cmp=lambda x, y: PyCalendarDateTime.sort(x[0], y[0]))
return l
def formattedExpandedDates(expanded):
items = sortedList([(item[0], secondsToTime(item[1]), secondsToTime(item[2]),) for item in expanded])
return ", ".join(["(%s, %s, %s)" % item for item in items])
def secondsToTime(seconds):
if seconds < 0:
seconds = -seconds
negative = "-"
else:
negative = ""
secs = divmod(seconds, 60)[1]
mins = divmod(seconds / 60, 60)[1]
hours = divmod(seconds / (60 * 60), 60)[1]
if secs:
return "%s%02d:%02d:%02d" % (negative, hours, mins, secs,)
else:
return "%s%02d:%02d" % (negative, hours, mins,)
def usage(error_msg=None):
if error_msg:
print error_msg
print """Usage: tzverify [options] DIR1 DIR2
Options:
-h Print this help and exit
-v Be verbose
-q Be quiet
--start Start year
--end End year
Arguments:
DIR1 Directories containing two sets of zoneinfo data
DIR2 to be compared
Description:
This utility will compare iCalendar zoneinfo hierarchies by expanding
timezone transitions and comparing.
"""
if error_msg:
raise ValueError(error_msg)
else:
sys.exit(0)
if __name__ == '__main__':
verbose = False
quiet = False
startYear = 1933
endYear = 2018
zonedir1 = None
zonedir2 = None
options, args = getopt.getopt(sys.argv[1:], "hvq", ["start=", "end=", ])
for option, value in options:
if option == "-h":
usage()
elif option == "-v":
verbose = True
elif option == "-q":
quiet = True
elif option == "--start":
startYear = int(value)
elif option == "--end":
endYear = int(value)
else:
usage("Unrecognized option: %s" % (option,))
# Process arguments
if len(args) != 2:
usage("Must have two arguments")
zonedir1 = os.path.expanduser(args[0])
zonedir2 = os.path.expanduser(args[1])
start = PyCalendarDateTime(year=startYear, month=1, day=1)
end = PyCalendarDateTime(year=endYear, month=1, day=1)
zonefiles = (
"northamerica",
"southamerica",
"europe",
"africa",
"asia",
"australasia",
"antarctica",
)
skips = (
#"Europe/Sofia",
#"Africa/Cairo",
)
checkcalendar1 = loadCalendarFromZoneinfo(zonedir1, skips, verbose, quiet)
checkcalendar2 = loadCalendarFromZoneinfo(zonedir2, skips, verbose, quiet)
compareCalendars(
checkcalendar1,
checkcalendar2,
start,
end,
filterTzids=(
#"America/Goose_Bay",
),
verbose=verbose,
quiet=quiet,
)
pycalendar-2.0~svn13177/src/zonal/tests/ 0000755 0001750 0001750 00000000000 12322631216 017107 5 ustar rahul rahul pycalendar-2.0~svn13177/src/zonal/tests/test_zone.py 0000644 0001750 0001750 00000005471 12101017573 021501 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import unittest
from zonal.zone import Zone
from zonal.rule import RuleSet
from pycalendar.calendar import PyCalendar
class TestZone(unittest.TestCase):
def test_parse(self):
zonedef = """Zone America/New_York\t-4:56:02\t-\tLMT\t1883 Nov 18 12:03:58
\t\t\t-5:00\tUS\tE%sT\t1920
\t\t\t-5:00\tNYC\tE%sT\t1942
\t\t\t-5:00\tUS\tE%sT\t1946
\t\t\t-5:00\tNYC\tE%sT\t1967
\t\t\t-5:00\tUS\tE%sT"""
zone = Zone()
zone.parse(zonedef)
self.assertEqual(str(zone), zonedef)
def test_vtimezone(self):
zonedef = """Zone America/New_York\t-4:56:02\t-\tLMT\t1883 Nov 18 12:03:58
\t\t\t-5:00\tUS\tE%sT"""
rules = """Rule\tUS\t1918\t1919\t-\tMar\tlastSun\t2:00\t1:00\tD
Rule\tUS\t1918\t1919\t-\tOct\tlastSun\t2:00\t0\tS
Rule\tUS\t1942\tonly\t-\tFeb\t9\t2:00\t1:00\tW
Rule\tUS\t1945\tonly\t-\tAug\t14\t23:00u\t1:00\tP
Rule\tUS\t1945\tonly\t-\tSep\t30\t2:00\t0\tS
Rule\tUS\t1967\t2006\t-\tOct\tlastSun\t2:00\t0\tS
Rule\tUS\t1967\t1973\t-\tApr\tlastSun\t2:00\t1:00\tD
Rule\tUS\t1974\tonly\t-\tJan\t6\t2:00\t1:00\tD
Rule\tUS\t1975\tonly\t-\tFeb\t23\t2:00\t1:00\tD
Rule\tUS\t1976\t1986\t-\tApr\tlastSun\t2:00\t1:00\tD
Rule\tUS\t1987\t2006\t-\tApr\tSun>=1\t2:00\t1:00\tD
Rule\tUS\t2007\tmax\t-\tMar\tSun>=8\t2:00\t1:00\tD
Rule\tUS\t2007\tmax\t-\tNov\tSun>=1\t2:00\t0\tS"""
result = """BEGIN:VTIMEZONE
TZID:America/New_York
X-LIC-LOCATION:America/New_York
BEGIN:DAYLIGHT
DTSTART:20060402T020000
RDATE:20060402T020000
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20061029T020000
RDATE:20061029T020000
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
""".replace("\n", "\r\n")
zone = Zone()
zone.parse(zonedef)
ruleset = RuleSet()
ruleset.parse(rules)
rules = {ruleset.name: ruleset}
cal = PyCalendar()
vtz = zone.vtimezone(cal, rules, 2006, 2011)
self.assertEqual(str(vtz), result)
pycalendar-2.0~svn13177/src/zonal/tests/test_rule.py 0000644 0001750 0001750 00000006134 12101017573 021472 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import unittest
from zonal.rule import Rule, RuleSet
from pycalendar.datetime import PyCalendarDateTime
class TestRule(unittest.TestCase):
def test_parse(self):
data = (
"Rule\tGuat\t2006\tonly\t-\tOct\t1\t0:00\t0\tS",
"Rule\tAlgeria\t1916\t1919\t-\tOct\tSun>=1\t23:00s\t0\t-",
"Rule\tEgypt\t1945\tonly\t-\tApr\t16\t0:00\t1:00\tS",
"Rule\tGhana\t1936\t1942\t-\tSep\t1\t0:00\t0:20\tGHST",
)
for ruletext in data:
ruleitem = Rule()
ruleitem.parse(ruletext)
self.assertEqual(str(ruleitem), ruletext)
def test_datetimeforyear(self):
data = (
("Rule\tGuat\t2006\tonly\t-\tOct\t1\t0:00\t0\tS", 2006, PyCalendarDateTime(2006, 10, 1, 0, 0, 0), ""),
("Rule\tAlgeria\t1916\t1919\t-\tOct\tSun>=1\t23:00s\t0\t-", 1916, PyCalendarDateTime(1916, 10, 1, 23, 0, 0), "s"),
("Rule\tGhana\t1936\t1942\t-\tSep\t1\t0:00\t0:20\tGHST", 1937, PyCalendarDateTime(1937, 9, 1, 0, 0, 0), ""),
)
for ruletext, year, dt, special in data:
ruleitem = Rule()
ruleitem.parse(ruletext)
self.assertEqual(ruleitem.datetimeForYear(year), (dt, special))
def test_getoffset(self):
data = (
("Rule\tGuat\t2006\tonly\t-\tOct\t1\t0:00\t0\tS", 0),
("Rule\tEgypt\t1945\tonly\t-\tApr\t16\t0:00\t1:00\tS", 60 * 60),
("Rule\tGhana\t1936\t1942\t-\tSep\t1\t0:00\t0:20\tGHST", 20 * 60),
)
for ruletext, offset in data:
ruleitem = Rule()
ruleitem.parse(ruletext)
self.assertEqual(ruleitem.getOffset(), offset)
class TestRuleSet(unittest.TestCase):
def test_parse(self):
data = """Rule\tUS\t1918\t1919\t-\tMar\tlastSun\t2:00\t1:00\tD
Rule\tUS\t1918\t1919\t-\tOct\tlastSun\t2:00\t0\tS
Rule\tUS\t1942\tonly\t-\tFeb\t9\t2:00\t1:00\tW
Rule\tUS\t1945\tonly\t-\tAug\t14\t23:00u\t1:00\tP
Rule\tUS\t1945\tonly\t-\tSep\t30\t2:00\t0\tS
Rule\tUS\t1967\t2006\t-\tOct\tlastSun\t2:00\t0\tS
Rule\tUS\t1967\t1973\t-\tApr\tlastSun\t2:00\t1:00\tD
Rule\tUS\t1974\tonly\t-\tJan\t6\t2:00\t1:00\tD
Rule\tUS\t1975\tonly\t-\tFeb\t23\t2:00\t1:00\tD
Rule\tUS\t1976\t1986\t-\tApr\tlastSun\t2:00\t1:00\tD
Rule\tUS\t1987\t2006\t-\tApr\tSun>=1\t2:00\t1:00\tD
Rule\tUS\t2007\tmax\t-\tMar\tSun>=8\t2:00\t1:00\tD
Rule\tUS\t2007\tmax\t-\tNov\tSun>=1\t2:00\t0\tS"""
ruleset = RuleSet()
ruleset.parse(data)
self.assertEqual(str(ruleset), data)
pycalendar-2.0~svn13177/src/zonal/tests/__init__.py 0000644 0001750 0001750 00000001202 12101017573 021212 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
pycalendar-2.0~svn13177/src/zonal/utils.py 0000644 0001750 0001750 00000003133 12101017573 017456 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
class DateTime(object):
"""
A date-time object that wraps the tzdb wall-clock/utc style date-time information
and that can generate appropriate localtime or UTC offsets based on Zone/Rule offsets.
"""
def __init__(self, dt, mode):
self.dt = dt
self.mode = mode
def __repr__(self):
return str(self.dt)
def compareDateTime(self, other):
return self.dt.compareDateTime(other.dt)
def getLocaltime(self, offset, stdoffset):
new_dt = self.dt.duplicate()
if self.mode == "u":
new_dt.offsetSeconds(offset)
elif self.mode == "s":
new_dt.offsetSeconds(-stdoffset + offset)
return new_dt
def getUTC(self, offset, stdoffset):
new_dt = self.dt.duplicate()
if self.mode == "u":
pass
elif self.mode == "s":
new_dt.offsetSeconds(-stdoffset)
else:
new_dt.offsetSeconds(-offset)
return new_dt
pycalendar-2.0~svn13177/src/zonal/tzdump.py 0000755 0001750 0001750 00000006713 12101017573 017653 0 ustar rahul rahul #!/usr/bin/env python
##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.calendar import PyCalendar
from pycalendar.datetime import PyCalendarDateTime
import os
import sys
import getopt
from pycalendar.exceptions import PyCalendarInvalidData
def loadCalendar(file, verbose):
cal = PyCalendar()
if verbose:
print "Parsing calendar data: %s" % (file,)
fin = open(file, "r")
try:
cal.parse(fin)
except PyCalendarInvalidData, e:
print "Failed to parse bad data: %s" % (e.mData,)
raise
return cal
def getExpandedDates(cal, start, end):
vtz = cal.getComponents()[0]
expanded = vtz.expandAll(start, end)
expanded.sort(cmp=lambda x, y: PyCalendarDateTime.sort(x[0], y[0]))
return expanded
def formattedExpandedDates(expanded):
items = []
for item in expanded:
utc = item[0].duplicate()
utc.setTimezoneUTC(True)
utc.offsetSeconds(-item[1])
items.append((item[0], utc, secondsToTime(item[1]), secondsToTime(item[2]),))
return "\n".join(["(%s, %s, %s, %s)" % item for item in items])
def secondsToTime(seconds):
if seconds < 0:
seconds = -seconds
negative = "-"
else:
negative = ""
secs = divmod(seconds, 60)[1]
mins = divmod(seconds / 60, 60)[1]
hours = divmod(seconds / (60 * 60), 60)[1]
if secs:
return "%s%02d:%02d:%02d" % (negative, hours, mins, secs,)
else:
return "%s%02d:%02d" % (negative, hours, mins,)
def usage(error_msg=None):
if error_msg:
print error_msg
print """Usage: tzdump [options] FILE
Options:
-h Print this help and exit
-v Be verbose
--start Start year
--end End year
Arguments:
FILE iCalendar file containing a single VTIMEZONE
Description:
This utility will dump the transitions in a VTIMEZONE over
the request time range.
"""
if error_msg:
raise ValueError(error_msg)
else:
sys.exit(0)
if __name__ == '__main__':
verbose = False
startYear = 1918
endYear = 2018
fpath = None
options, args = getopt.getopt(sys.argv[1:], "hv", ["start=", "end=", ])
for option, value in options:
if option == "-h":
usage()
elif option == "-v":
verbose = True
elif option == "--start":
startYear = int(value)
elif option == "--end":
endYear = int(value)
else:
usage("Unrecognized option: %s" % (option,))
# Process arguments
if len(args) != 1:
usage("Must have one argument")
fpath = os.path.expanduser(args[0])
start = PyCalendarDateTime(year=startYear, month=1, day=1)
end = PyCalendarDateTime(year=endYear, month=1, day=1)
cal = loadCalendar(fpath, verbose)
dates = getExpandedDates(cal, start, end)
print formattedExpandedDates(dates)
pycalendar-2.0~svn13177/src/zonal/__init__.py 0000644 0001750 0001750 00000001202 12101017573 020050 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
pycalendar-2.0~svn13177/src/zonal/rule.py 0000644 0001750 0001750 00000046247 12101017573 017302 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.utils import daysInMonth
from pycalendar.vtimezonestandard import PyCalendarVTimezoneStandard
from pycalendar.utcoffsetvalue import PyCalendarUTCOffsetValue
from pycalendar.vtimezonedaylight import PyCalendarVTimezoneDaylight
from pycalendar.property import PyCalendarProperty
from pycalendar.recurrence import PyCalendarRecurrence
import utils
"""
Class that maintains a TZ data Rule.
"""
__all__ = (
"Rule",
"RuleSet",
)
class RuleSet(object):
"""
A set of tzdata rules tied to a specific Rule name
"""
def __init__(self):
self.name = ""
self.rules = []
def __str__(self):
return self.generate()
def __eq__(self, other):
return other and (
self.name == other.name and
self.rules == other.rules
)
def __ne__(self, other):
return not self.__eq__(other)
def parse(self, lines):
"""
Parse the set of Rule lines from tzdata.
@param lines: the lines to parse.
@type lines: C{str}
"""
splitlines = lines.split("\n")
for line in splitlines:
splits = line.expandtabs(1).split(" ")
name = splits[1]
assert name, "Must have a zone name: '%s'" % (lines,)
if not self.name:
self.name = name
assert self.name == name, "Different zone names %s and %s: %s" % (self.name, name,)
rule = Rule()
rule.parse(line)
self.rules.append(rule)
def generate(self):
"""
Generate a Rule line.
@return: a C{str} with the Rule.
"""
items = [rule.generate() for rule in self.rules]
return "\n".join(items)
def expand(self, results, zoneinfo, maxYear):
"""
Expand the set of rules into transition/offset pairs for the entire RuleSet
starting at the beginning and going up to maxYear at most.
@param results: list to append results to
@type results: C{list}
@param zoneinfo: the Zone in which this RuleSet is being used
@type zoneinfo: L{ZoneRule}
@param maxYear: the maximum year to expand out to
@type maxYear: C{int}
"""
for rule in self.rules:
rule.expand(results, zoneinfo, maxYear)
class Rule(object):
"""
A tzdata Rule
"""
# Some useful mapping tables
LASTDAY_NAME_TO_DAY = {
"lastSun": PyCalendarDateTime.SUNDAY,
"lastMon": PyCalendarDateTime.MONDAY,
"lastTue": PyCalendarDateTime.TUESDAY,
"lastWed": PyCalendarDateTime.WEDNESDAY,
"lastThu": PyCalendarDateTime.THURSDAY,
"lastFri": PyCalendarDateTime.FRIDAY,
"lastSat": PyCalendarDateTime.SATURDAY,
}
DAY_NAME_TO_DAY = {
"Sun": PyCalendarDateTime.SUNDAY,
"Mon": PyCalendarDateTime.MONDAY,
"Tue": PyCalendarDateTime.TUESDAY,
"Wed": PyCalendarDateTime.WEDNESDAY,
"Thu": PyCalendarDateTime.THURSDAY,
"Fri": PyCalendarDateTime.FRIDAY,
"Sat": PyCalendarDateTime.SATURDAY,
}
LASTDAY_NAME_TO_RDAY = {
"lastSun": definitions.eRecurrence_WEEKDAY_SU,
"lastMon": definitions.eRecurrence_WEEKDAY_MO,
"lastTue": definitions.eRecurrence_WEEKDAY_TU,
"lastWed": definitions.eRecurrence_WEEKDAY_WE,
"lastThu": definitions.eRecurrence_WEEKDAY_TH,
"lastFri": definitions.eRecurrence_WEEKDAY_FR,
"lastSat": definitions.eRecurrence_WEEKDAY_SA,
}
DAY_NAME_TO_RDAY = {
PyCalendarDateTime.SUNDAY: definitions.eRecurrence_WEEKDAY_SU,
PyCalendarDateTime.MONDAY: definitions.eRecurrence_WEEKDAY_MO,
PyCalendarDateTime.TUESDAY: definitions.eRecurrence_WEEKDAY_TU,
PyCalendarDateTime.WEDNESDAY: definitions.eRecurrence_WEEKDAY_WE,
PyCalendarDateTime.THURSDAY: definitions.eRecurrence_WEEKDAY_TH,
PyCalendarDateTime.FRIDAY: definitions.eRecurrence_WEEKDAY_FR,
PyCalendarDateTime.SATURDAY: definitions.eRecurrence_WEEKDAY_SA,
}
MONTH_NAME_TO_POS = {
"Jan": 1,
"Feb": 2,
"Mar": 3,
"Apr": 4,
"May": 5,
"Jun": 6,
"Jul": 7,
"Aug": 8,
"Sep": 9,
"Oct": 10,
"Nov": 11,
"Dec": 12,
}
MONTH_POS_TO_NAME = ("", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
def __init__(self):
self.name = ""
self.fromYear = ""
self.toYear = ""
self.type = "-"
self.inMonth = 0
self.onDay = ""
self.atTime = 0
self.saveTime = 0
self.letter = ""
def __str__(self):
return self.generate()
def __eq__(self, other):
return other and (
self.name == other.name and
self.fromYear == other.fromYear and
self.toYear == other.toYear and
self.type == other.type and
self.inMonth == other.inMonth and
self.onDay == other.onDay and
self.atTime == other.atTime and
self.saveTime == other.saveTime and
self.letter == other.letter
)
def __ne__(self, other):
return not self.__eq__(other)
def parse(self, line):
"""
Parse the Rule line from tzdata.
@param line: the line to parse.
@type line: C{str}
"""
# Simply split the bits up and store them in various properties
splits = [x for x in line.expandtabs(1).split(" ") if len(x) > 0]
assert len(splits) >= 10, "Wrong number of fields in Rule: '%s'" % (line,)
self.name = splits[1]
self.fromYear = splits[2]
self.toYear = splits[3]
self.type = splits[4]
self.inMonth = splits[5]
self.onDay = splits[6]
self.atTime = splits[7]
self.saveTime = splits[8]
self.letter = splits[9]
def generate(self):
"""
Generate a Rule line.
@return: a C{str} with the Rule.
"""
items = (
"Rule",
self.name,
self.fromYear,
self.toYear,
self.type,
self.inMonth,
self.onDay,
self.atTime,
self.saveTime,
self.letter,
)
return "\t".join(items)
def getOffset(self):
"""
Return the specified rule offset in seconds.
@return: C{int}
"""
splits = self.saveTime.split(":")
hours = int(splits[0])
if len(splits) == 2:
minutes = int(splits[1])
else:
minutes = 0
negative = hours < 0
if negative:
return -((-hours * 60) + minutes) * 60
else:
return ((hours * 60) + minutes) * 60
def startYear(self):
return int(self.fromYear)
def endYear(self):
if self.toYear == "only":
return self.startYear()
elif self.toYear == "max":
return 9999
else:
return int(self.toYear)
def datetimeForYear(self, year):
"""
Given a specific year, determine the actual date/time of the transition
@param year: the year to determine the transition for
@type year: C{int}
@return: C{tuple} of L{PyCalendarDateTime} and C{str} (which is the special
tzdata mode character
"""
# Create a floating date-time
dt = PyCalendarDateTime()
# Setup base year/month/day
dt.setYear(year)
dt.setMonth(Rule.MONTH_NAME_TO_POS[self.inMonth])
dt.setDay(1)
# Setup base hours/minutes
splits = self.atTime.split(":")
if len(splits) == 1:
splits.append("0")
assert len(splits) == 2, "atTime format is wrong: %s, %s" % (self.atTime, self,)
hours = int(splits[0])
if len(splits[1]) > 2:
minutes = int(splits[1][:2])
special = splits[1][2:]
else:
minutes = int(splits[1])
special = ""
# Special case for 24:00
if hours == 24 and minutes == 0:
dt.setHours(23)
dt.setMinutes(59)
dt.setSeconds(59)
else:
dt.setHours(hours)
dt.setMinutes(minutes)
# Now determine the actual start day
if self.onDay in Rule.LASTDAY_NAME_TO_DAY:
dt.setDayOfWeekInMonth(-1, Rule.LASTDAY_NAME_TO_DAY[self.onDay])
elif self.onDay.find(">=") != -1:
splits = self.onDay.split(">=")
dt.setNextDayOfWeek(int(splits[1]), Rule.DAY_NAME_TO_DAY[splits[0]])
else:
try:
day = int(self.onDay)
dt.setDay(day)
except:
assert False, "onDay value is not recognized: %s" % (self.onDay,)
return dt, special
def getOnDayDetails(self, start, indicatedDay, indicatedOffset):
"""
Get RRULE BYxxx part items from the Rule data.
@param start: start date-time for the recurrence set
@type start: L{PyCalendarDateTime}
@param indicatedDay: the day that the Rule indicates for recurrence
@type indicatedDay: C{int}
@param indicatedOffset: the offset that the Rule indicates for recurrence
@type indicatedOffset: C{int}
"""
month = start.getMonth()
year = start.getYear()
dayOfWeek = start.getDayOfWeek()
# Need to check whether day has changed due to time shifting
# e.g. if "u" mode is specified, the actual localtime may be
# shifted to the previous day if the offset is negative
if indicatedDay != dayOfWeek:
difference = dayOfWeek - indicatedDay
if difference in (1, -6,):
indicatedOffset += 1
# Adjust the month down too if needed
if start.getDay() == 1:
month -= 1
if month < 1:
month = 12
elif difference in (-1, 6,):
assert indicatedOffset != 1, "Bad RRULE adjustment"
indicatedOffset -= 1
else:
assert False, "Unknown RRULE adjustment"
try:
# Form the appropriate RRULE bits
day = Rule.DAY_NAME_TO_RDAY[dayOfWeek]
offset = indicatedOffset
bymday = None
if offset == 1:
offset = 1
elif offset == 8:
offset = 2
elif offset == 15:
offset = 3
elif offset == 22:
offset = 4
else:
days_in_month = daysInMonth(month, year)
if days_in_month - offset == 6:
offset = -1
elif days_in_month - offset == 13:
offset = -2
elif days_in_month - offset == 20:
offset = -3
else:
bymday = [offset + i for i in range(7) if (offset + i) <= days_in_month]
offset = 0
except:
assert False, "onDay value is not recognized: %s" % (self.onDay,)
return offset, day, bymday
def expand(self, results, zoneinfo, maxYear):
"""
Expand the Rule into a set of transition date/offset pairs
@param results: list to append results to
@type results: C{list}
@param zoneinfo: the Zone in which this RuleSet is being used
@type zoneinfo: L{ZoneRule}
@param maxYear: the maximum year to expand out to
@type maxYear: C{int}
"""
if self.startYear() >= maxYear:
return
self.fullExpand(maxYear)
zoneoffset = zoneinfo.getUTCOffset()
offset = self.getOffset()
for dt in self.dt_cache:
results.append((dt, zoneoffset + offset, self))
def fullExpand(self, maxYear):
"""
Do a full recurrence expansion for each year in the Rule's range, upto
a specified maximum.
@param maxYear: maximum year to expand to
@type maxYear: C{int}
"""
if hasattr(self, "dt_cache"):
return self.dt_cache
start = self.startYear()
end = self.endYear()
if end > maxYear:
end = maxYear - 1
self.dt_cache = []
for year in xrange(start, end + 1):
dt = utils.DateTime(*self.datetimeForYear(year))
self.dt_cache.append(dt)
def vtimezone(self, vtz, zonerule, start, end, offsetfrom, offsetto, instanceCount):
"""
Generate a VTIMEZONE sub-component for this Rule.
@param vtz: VTIMEZONE to add to
@type vtz: L{PyCalendarVTimezone}
@param zonerule: the Zone rule line being used
@type zonerule: L{ZoneRule}
@param start: the start time for the first instance
@type start: L{PyCalendarDateTime}
@param end: the start time for the last instance
@type end: L{PyCalendarDateTime}
@param offsetfrom: the UTC offset-from
@type offsetfrom: C{int}
@param offsetto: the UTC offset-to
@type offsetto: C{int}
@param instanceCount: the number of instances in the set
@type instanceCount: C{int}
"""
# Determine type of component based on offset
dstoffset = self.getOffset()
if dstoffset == 0:
comp = PyCalendarVTimezoneStandard(parent=vtz)
else:
comp = PyCalendarVTimezoneDaylight(parent=vtz)
# Do offsets
tzoffsetfrom = PyCalendarUTCOffsetValue(offsetfrom)
tzoffsetto = PyCalendarUTCOffsetValue(offsetto)
comp.addProperty(PyCalendarProperty(definitions.cICalProperty_TZOFFSETFROM, tzoffsetfrom))
comp.addProperty(PyCalendarProperty(definitions.cICalProperty_TZOFFSETTO, tzoffsetto))
# Do TZNAME
if zonerule.format.find("%") != -1:
tzname = zonerule.format % (self.letter if self.letter != "-" else "",)
else:
tzname = zonerule.format
comp.addProperty(PyCalendarProperty(definitions.cICalProperty_TZNAME, tzname))
# Do DTSTART
comp.addProperty(PyCalendarProperty(definitions.cICalProperty_DTSTART, start))
# Now determine the recurrences (use RDATE if only one year or
# number of instances is one)
if self.toYear != "only" and instanceCount != 1:
rrule = PyCalendarRecurrence()
rrule.setFreq(definitions.eRecurrence_YEARLY)
rrule.setByMonth((Rule.MONTH_NAME_TO_POS[self.inMonth],))
if self.onDay in Rule.LASTDAY_NAME_TO_RDAY:
# Need to check whether day has changed due to time shifting
dayOfWeek = start.getDayOfWeek()
indicatedDay = Rule.LASTDAY_NAME_TO_DAY[self.onDay]
if dayOfWeek == indicatedDay:
rrule.setByDay(((-1, Rule.LASTDAY_NAME_TO_RDAY[self.onDay]),))
elif dayOfWeek < indicatedDay or dayOfWeek == 6 and indicatedDay == 0:
# This is OK as we have moved back a day and thus no month transition
# could have occurred
fakeOffset = daysInMonth(start.getMonth(), start.getYear()) - 6
offset, rday, bymday = self.getOnDayDetails(start, indicatedDay, fakeOffset)
if bymday:
rrule.setByMonthDay(bymday)
rrule.setByDay(((offset, rday),))
else:
# This is bad news as we have moved forward a day possibly into the next month
# What we do is switch to using a BYYEARDAY rule with offset from the end of the year
rrule.setByMonth(())
daysBackStartOfMonth = (
365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0 # Does not account for leap year
)
rrule.setByYearDay([-(daysBackStartOfMonth[Rule.MONTH_NAME_TO_POS[self.inMonth]] + i) for i in range(7)])
rrule.setByDay(
((0, divmod(Rule.LASTDAY_NAME_TO_DAY[self.onDay] + 1, 7)[1]),),
)
elif self.onDay.find(">=") != -1:
indicatedDay, dayoffset = self.onDay.split(">=")
# Need to check whether day has changed due to time shifting
dayOfWeek = start.getDayOfWeek()
indicatedDay = Rule.DAY_NAME_TO_DAY[indicatedDay]
if dayOfWeek == indicatedDay:
offset, rday, bymday = self.getOnDayDetails(start, indicatedDay, int(dayoffset))
if bymday:
rrule.setByMonthDay(bymday)
rrule.setByDay(((offset, rday),))
elif dayoffset == 1 and divmod(dayoffset - indicatedDay, 7)[1] == 6:
# This is bad news as we have moved backward a day possibly into the next month
# What we do is switch to using a BYYEARDAY rule with offset from the end of the year
rrule.setByMonth(())
daysBackStartOfMonth = (
365, 334, 306, 275, 245, 214, 184, 153, 122, 92, 61, 31, 0 # Does not account for leap year
)
rrule.setByYearDay([-(daysBackStartOfMonth[Rule.MONTH_NAME_TO_POS[self.inMonth]] + i) for i in range(7)])
rrule.setByDay(
((0, divmod(indicatedDay + 1, 7)[1]),),
)
else:
# This is OK as we have moved forward a day and thus no month transition
# could have occurred
offset, rday, bymday = self.getOnDayDetails(start, indicatedDay, int(dayoffset))
if bymday:
rrule.setByMonthDay(bymday)
rrule.setByDay(((offset, rday),))
else:
try:
_ignore_day = int(self.onDay)
except:
assert False, "onDay value is not recognized: %s" % (self.onDay,)
# Add any UNTIL
if zonerule.getUntilDate().dt.getYear() < 9999 or self.endYear() < 9999:
until = end.duplicate()
until.offsetSeconds(-offsetfrom)
until.setTimezoneUTC(True)
rrule.setUseUntil(True)
rrule.setUntil(until)
comp.addProperty(PyCalendarProperty(definitions.cICalProperty_RRULE, rrule))
else:
comp.addProperty(PyCalendarProperty(definitions.cICalProperty_RDATE, start))
comp.finalise()
vtz.addComponent(comp)
pycalendar-2.0~svn13177/src/pycalendar/ 0000755 0001750 0001750 00000000000 12322631216 016744 5 ustar rahul rahul pycalendar-2.0~svn13177/src/pycalendar/datetime.py 0000644 0001750 0001750 00000110470 12320545600 021114 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar import locale
from pycalendar import utils
from pycalendar import xmldefs
from pycalendar.duration import PyCalendarDuration
from pycalendar.parser import ParserContext
from pycalendar.timezone import PyCalendarTimezone
from pycalendar.valueutils import ValueMixin
import cStringIO as StringIO
import time
import xml.etree.cElementTree as XML
class PyCalendarDateTime(ValueMixin):
SUNDAY = 0
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
THURSDAY = 4
FRIDAY = 5
SATURDAY = 6
FULLDATE = 0
ABBREVDATE = 1
NUMERICDATE = 2
FULLDATENOYEAR = 3
ABBREVDATENOYEAR = 4
NUMERICDATENOYEAR = 5
@staticmethod
def sort(e1, e2):
return e1.compareDateTime(e2)
def __init__(self, year=None, month=None, day=None, hours=None, minutes=None, seconds=None, tzid=None, utcoffset=None):
self.mYear = 1970
self.mMonth = 1
self.mDay = 1
self.mHours = 0
self.mMinutes = 0
self.mSeconds = 0
self.mDateOnly = False
self.mTZUTC = False
self.mTZID = None
self.mTZOffset = None
self.mPosixTimeCached = False
self.mPosixTime = 0
if (year is not None) and (month is not None) and (day is not None):
self.mYear = year
self.mMonth = month
self.mDay = day
if (hours is not None) and (minutes is not None) and (seconds is not None):
self.mHours = hours
self.mMinutes = minutes
self.mSeconds = seconds
else:
self.mDateOnly = True
if tzid:
self.mTZUTC = tzid.getUTC()
self.mTZID = tzid.getTimezoneID()
def duplicate(self):
other = PyCalendarDateTime(self.mYear, self.mMonth, self.mDay, self.mHours, self.mMinutes, self.mSeconds)
other.mDateOnly = self.mDateOnly
other.mTZUTC = self.mTZUTC
other.mTZID = self.mTZID
other.mTZOffset = self.mTZOffset
other.mPosixTimeCached = self.mPosixTimeCached
other.mPosixTime = self.mPosixTime
return other
def duplicateAsUTC(self):
other = self.duplicate()
other.adjustToUTC()
return other
def __repr__(self):
return "PyCalendarDateTime: %s" % (self.getText(),)
def __hash__(self):
return hash(self.getPosixTime())
# Operators
def __add__(self, duration):
# Add duration seconds to temp object and normalise it
result = self.duplicate()
result.mSeconds += duration.getTotalSeconds()
result.normalise()
return result
def __sub__(self, dateorduration):
if isinstance(dateorduration, PyCalendarDateTime):
date = dateorduration
# Look for floating
if self.floating() or date.floating():
# Adjust the floating ones to fixed
copy1 = self.duplicate()
copy2 = date.duplicate()
if copy1.floating() and copy2.floating():
# Set both to UTC and do comparison
copy1.setTimezoneUTC(True)
copy2.setTimezoneUTC(True)
return copy1 - copy2
elif copy1.floating():
# Set to be the same
copy1.setTimezoneID(copy2.getTimezoneID())
copy1.setTimezoneUTC(copy2.getTimezoneUTC())
return copy1 - copy2
else:
# Set to be the same
copy2.setTimezoneID(copy1.getTimezoneID())
copy2.setTimezoneUTC(copy1.getTimezoneUTC())
return copy1 - copy2
else:
# Do diff of date-time in seconds
diff = self.getPosixTime() - date.getPosixTime()
return PyCalendarDuration(duration=diff)
elif isinstance(dateorduration, PyCalendarDuration):
duration = dateorduration
result = self.duplicate()
result.mSeconds -= duration.getTotalSeconds()
result.normalise()
return result
raise ValueError
# Comparators
def __eq__(self, comp):
return self.compareDateTime(comp) == 0
def __ne__(self, comp):
return self.compareDateTime(comp) != 0
def __ge__(self, comp):
return self.compareDateTime(comp) >= 0
def __le__(self, comp):
return self.compareDateTime(comp) <= 0
def __gt__(self, comp):
return self.compareDateTime(comp) > 0
def __lt__(self, comp):
return self.compareDateTime(comp) < 0
def compareDateTime(self, comp):
if comp is None:
return 1
# If either are date only, then just do date compare
if self.mDateOnly or comp.mDateOnly:
if self.mYear == comp.mYear:
if self.mMonth == comp.mMonth:
if self.mDay == comp.mDay:
return 0
else:
if self.mDay < comp.mDay:
return -1
else:
return 1
else:
if self.mMonth < comp.mMonth:
return -1
else:
return 1
else:
if self.mYear < comp.mYear:
return -1
else:
return 1
# If they have the same timezone do simple compare - no posix calc
# needed
elif (PyCalendarTimezone.same(self.mTZUTC, self.mTZID, comp.mTZUTC, comp.mTZID)):
if self.mYear == comp.mYear:
if self.mMonth == comp.mMonth:
if self.mDay == comp.mDay:
if self.mHours == comp.mHours:
if self.mMinutes == comp.mMinutes:
if self.mSeconds == comp.mSeconds:
return 0
else:
if self.mSeconds < comp.mSeconds:
return -1
else:
return 1
else:
if self.mMinutes < comp.mMinutes:
return -1
else:
return 1
else:
if self.mHours < comp.mHours:
return -1
else:
return 1
else:
if self.mDay < comp.mDay:
return -1
else:
return 1
else:
if self.mMonth < comp.mMonth:
return -1
else:
return 1
else:
if self.mYear < comp.mYear:
return -1
else:
return 1
else:
posix1 = self.getPosixTime()
posix2 = comp.getPosixTime()
if posix1 == posix2:
return 0
else:
if posix1 < posix2:
return -1
else:
return 1
def compareDate(self, comp):
return (self.mYear == comp.mYear) and (self.mMonth == comp.mMonth) and (self.mDay == comp.mDay)
def getPosixTime(self):
# Look for cached value (or floating time which has to be calculated
# each time)
if (not self.mPosixTimeCached) or self.floating():
result = 0L
# Add hour/mins/secs
result = (self.mHours * 60L + self.mMinutes) * 60L + self.mSeconds
# Number of days since 1970
result += self.daysSince1970() * 24L * 60L * 60L
# Adjust for timezone offset
result -= self.timeZoneSecondsOffset()
# Now indicate cache state
self.mPosixTimeCached = True
self.mPosixTime = result
return self.mPosixTime
def isDateOnly(self):
return self.mDateOnly
def setDateOnly(self, date_only):
self.mDateOnly = date_only
self.changed()
def getYear(self):
return self.mYear
def setYear(self, year):
if self.mYear != year:
self.mYear = year
self.changed()
def offsetYear(self, diff_year):
self.mYear += diff_year
self.normalise()
def getMonth(self):
return self.mMonth
def setMonth(self, month):
if self.mMonth != month:
self.mMonth = month
self.changed()
def offsetMonth(self, diff_month):
self.mMonth += diff_month
self.normalise()
def getDay(self):
return self.mDay
def setDay(self, day):
if self.mDay != day:
self.mDay = day
self.changed()
def offsetDay(self, diff_day):
self.mDay += diff_day
self.normalise()
def setYearDay(self, day, allow_invalid=False):
# 1 .. 366 offset from start, or
# -1 .. -366 offset from end
if day == 366:
self.mMonth = 12
self.mDay = 31 if utils.isLeapYear(self.mYear) else 32
elif day == -366:
self.mMonth = 1 if utils.isLeapYear(self.mYear) else 1
self.mDay = 1 if utils.isLeapYear(self.mYear) else 0
elif day > 0:
# Offset current date to 1st January of current year
self.mMonth = 1
self.mDay = 1
# Increment day
self.mDay += day - 1
elif day < 0:
# Offset current date to 1st January of next year
self.mMonth = 12
self.mDay = 31
# Decrement day
self.mDay += day + 1
if not allow_invalid:
# Normalise to get proper year/month/day values
self.normalise()
else:
self.changed()
def getYearDay(self):
return self.mDay + utils.daysUptoMonth(self.mMonth, self.mYear)
def setMonthDay(self, day, allow_invalid=False):
# 1 .. 31 offset from start, or
# -1 .. -31 offset from end
if day > 0:
# Offset current date to 1st of current month
self.mDay = 1
# Increment day
self.mDay += day - 1
elif day < 0:
# Offset current date to last of month
self.mDay = utils.daysInMonth(self.mMonth, self.mYear)
# Decrement day
self.mDay += day + 1
if not allow_invalid:
# Normalise to get proper year/month/day values
self.normalise()
else:
self.changed()
def isMonthDay(self, day):
if day > 0:
return self.mDay == day
elif day < 0:
return self.mDay - 1 - utils.daysInMonth(self.mMonth, self.mYear) == day
else:
return False
def setWeekNo(self, weekno):
"""
Set the current date to one with the same day of the week in the current year with the
specified week number. Note this might cause the year to shift backwards or forwards
if the date is at the boundary between two years.
@param weekno: the week number to set (currently must be positive)
@type weekno: C{int}
"""
# Don't both if already correct
if self.getWeekNo() == weekno:
return
# What day does the current year start on, and diff that with the current day
temp = PyCalendarDateTime(year=self.mYear, month=1, day=1)
first_day = temp.getDayOfWeek()
current_day = self.getDayOfWeek()
# Calculate and set yearday for start of week. The first week is the one that contains at least
# four days (with week start defaulting to MONDAY), so that means the 1st of January would fall
# on MO, TU, WE, TH.
if first_day in (PyCalendarDateTime.MONDAY, PyCalendarDateTime.TUESDAY, PyCalendarDateTime.WEDNESDAY, PyCalendarDateTime.THURSDAY):
year_day = (weekno - 1) * 7 + current_day - first_day
else:
year_day = weekno * 7 + current_day - first_day
# It is possible we have a negative offset which means go back to the prior year as part of
# week #1 exists at the end of that year.
if year_day < 0:
self.offsetYear(-1)
else:
year_day += 1
self.setYearDay(year_day)
def getWeekNo(self):
"""
Return the ISO week number for the current date.
"""
# What day does the current year start on
temp = PyCalendarDateTime(year=self.mYear, month=1, day=1)
first_day = temp.getDayOfWeek()
if first_day == 0:
first_day = 7
current_day = self.getDayOfWeek()
if current_day == 0:
current_day = 7
# This arithmetic uses the ISO day of week (1-7) and the year day to get the week number
week_no = (self.getYearDay() - current_day + 10) / 7
# Might need to adjust forward/backwards based on year boundaries
if week_no == 0:
# Last week of previous year
temp = PyCalendarDateTime(year=self.mYear - 1, month=12, day=31)
week_no = temp.getWeekNo()
elif week_no == 53:
# Might be first week of next year
temp = PyCalendarDateTime(year=self.mYear + 1, month=1, day=1)
first_day = temp.getDayOfWeek()
if first_day in (PyCalendarDateTime.MONDAY, PyCalendarDateTime.TUESDAY, PyCalendarDateTime.WEDNESDAY, PyCalendarDateTime.THURSDAY):
week_no = 1
return week_no
def isWeekNo(self, weekno):
# This is the iso 8601 week number definition
if weekno > 0:
return self.getWeekNo() == weekno
else:
# This needs to calculate the negative offset from the last week in
# the current year
return False
def setDayOfWeekInYear(self, offset, day):
# Set to first day in year
self.mMonth = 1
self.mDay = 1
# Determine first weekday in year
first_day = self.getDayOfWeek()
if offset > 0:
cycle = (offset - 1) * 7 + day
cycle -= first_day
if first_day > day:
cycle += 7
self.mDay = cycle + 1
elif offset < 0:
first_day += 365 + [1, 0][not utils.isLeapYear(self.mYear)] - 1
first_day %= 7
cycle = (-offset - 1) * 7 - day
cycle += first_day
if day > first_day:
cycle += 7
self.mDay = 365 + [1, 0][not utils.isLeapYear(self.mYear)] - cycle
self.normalise()
def setDayOfWeekInMonth(self, offset, day, allow_invalid=False):
# Set to first day in month
self.mDay = 1
# Determine first weekday in month
first_day = self.getDayOfWeek()
if offset > 0:
cycle = (offset - 1) * 7 + day
cycle -= first_day
if first_day > day:
cycle += 7
self.mDay = cycle + 1
elif offset < 0:
days_in_month = utils.daysInMonth(self.mMonth, self.mYear)
first_day += days_in_month - 1
first_day %= 7
cycle = (-offset - 1) * 7 - day
cycle += first_day
if day > first_day:
cycle += 7
self.mDay = days_in_month - cycle
if not allow_invalid:
self.normalise()
else:
self.changed()
def setNextDayOfWeek(self, start, day):
# Set to first day in month
self.mDay = start
# Determine first weekday in month
first_day = self.getDayOfWeek()
if first_day > day:
self.mDay += 7
self.mDay += day - first_day
self.normalise()
def isDayOfWeekInMonth(self, offset, day):
# First of the actual day must match
if self.getDayOfWeek() != day:
return False
# If there is no count the we match any of this day in the month
if offset == 0:
return True
# Create temp date-time with the appropriate parameters and then
# compare
temp = self.duplicate()
temp.setDayOfWeekInMonth(offset, day)
# Now compare dates
return self.compareDate(temp)
def getDayOfWeek(self):
# Count days since 01-Jan-1970 which was a Thursday
result = PyCalendarDateTime.THURSDAY + self.daysSince1970()
result %= 7
if result < 0:
result += 7
return result
def getMonthText(self, short_txt):
# Make sure range is valid
if (self.mMonth < 1) or (self.mMonth > 12):
return ""
else:
return locale.getMonth(self.mMonth,
[locale.SHORT, locale.LONG][not short_txt])
def getDayOfWeekText(self, day):
return locale.getDay(day, locale.SHORT)
def setHHMMSS(self, hours, minutes, seconds):
if (self.mHours != hours) or (self.mMinutes != minutes) or (self.mSeconds != seconds):
self.mHours = hours
self.mMinutes = minutes
self.mSeconds = seconds
self.changed()
def getHours(self):
return self.mHours
def setHours(self, hours):
if self.mHours != hours:
self.mHours = hours
self.changed()
def offsetHours(self, diff_hour):
self.mHours += diff_hour
self.normalise()
def getMinutes(self):
return self.mMinutes
def setMinutes(self, minutes):
if self.mMinutes != minutes:
self.mMinutes = minutes
self.changed()
def offsetMinutes(self, diff_minutes):
self.mMinutes += diff_minutes
self.normalise()
def getSeconds(self):
return self.mSeconds
def setSeconds(self, seconds):
if self.mSeconds != seconds:
self.mSeconds = seconds
self.changed()
def offsetSeconds(self, diff_seconds):
self.mSeconds += diff_seconds
self.normalise()
def getTimezoneUTC(self):
return self.mTZUTC
def setTimezoneUTC(self, utc):
if self.mTZUTC != utc:
self.mTZUTC = utc
self.changed()
def getTimezoneID(self):
return self.mTZID
def setTimezoneID(self, tzid):
self.mTZUTC = False
self.mTZID = tzid
self.changed()
def utc(self):
return self.mTZUTC
def local(self):
return (not self.mTZUTC) and self.mTZID
def floating(self):
return (not self.mTZUTC) and not self.mTZID
def getTimezone(self):
return PyCalendarTimezone(utc=self.mTZUTC, tzid=self.mTZID)
def setTimezone(self, tzid):
self.mTZUTC = tzid.getUTC()
self.mTZID = tzid.getTimezoneID()
self.changed()
def adjustTimezone(self, tzid):
# Only if different
s1 = tzid.getTimezoneID()
if (tzid.getUTC() != self.mTZUTC) or (s1 != self.mTZID):
if not self.mTZUTC:
self.adjustToUTC()
self.setTimezone(tzid)
offset_to = self.timeZoneSecondsOffset(relative_to_utc=True)
self.offsetSeconds(offset_to)
return self
def adjustToUTC(self):
if self.local() and not self.mDateOnly:
# Cache and restore and adjust the posix value to avoid a recalc since it won't change during this adjust
tempPosix = self.mPosixTime if self.mPosixTimeCached else None
utc = PyCalendarTimezone(utc=True)
offset_from = self.timeZoneSecondsOffset()
self.setTimezone(utc)
self.offsetSeconds(-offset_from)
if tempPosix is not None:
self.mPosixTimeCached = True
self.mPosixTime = tempPosix
self.mTZOffset = 0
return self
def getAdjustedTime(self, tzid=None):
# Copy this and adjust to input timezone
adjusted = self.duplicate()
adjusted.adjustTimezone(tzid)
return adjusted
def setToday(self):
tz = PyCalendarTimezone(utc=self.mTZUTC, tzid=self.mTZID)
self.copy_ICalendarDateTime(self.getToday(tz))
@staticmethod
def getToday(tzid=None):
# Get from posix time
now = time.time()
now_tm = time.localtime(now)
temp = PyCalendarDateTime(year=now_tm.tm_year, month=now_tm.tm_mon, day=now_tm.tm_mday, tzid=tzid)
return temp
def setNow(self):
tz = PyCalendarTimezone(utc=self.mTZUTC, tzid=self.mTZID)
self.copy_ICalendarDateTime(self.getNow(tz))
@staticmethod
def getNow(tzid):
utc = PyCalendarDateTime.getNowUTC()
utc.adjustTimezone(tzid if tzid is not None else PyCalendarTimezone())
return utc
def setNowUTC(self):
self.copy_PyCalendarDateTime(self.getNowUTC())
@staticmethod
def getNowUTC():
# Get from posix time
now = time.time()
now_tm = time.gmtime(now)
tzid = PyCalendarTimezone(utc=True)
return PyCalendarDateTime(year=now_tm.tm_year, month=now_tm.tm_mon, day=now_tm.tm_mday, hours=now_tm.tm_hour, minutes=now_tm.tm_min, seconds=now_tm.tm_sec, tzid=tzid)
def recur(self, freq, interval, allow_invalid=False):
# Add appropriate interval
normalize = True
if freq == definitions.eRecurrence_SECONDLY:
self.mSeconds += interval
elif freq == definitions.eRecurrence_MINUTELY:
self.mMinutes += interval
elif freq == definitions.eRecurrence_HOURLY:
self.mHours += interval
elif freq == definitions.eRecurrence_DAILY:
self.mDay += interval
elif freq == definitions.eRecurrence_WEEKLY:
self.mDay += 7 * interval
elif freq == definitions.eRecurrence_MONTHLY:
# Iterate until a valid day in the next month is found.
# This can happen if adding one month to e.g. 31 January.
# That is an undefined operation - does it mean 28/29 February
# or 1/2 May, or 31 March or what? We choose to find the next month with
# the same day number as the current one.
self.mMonth += interval
# Normalise month
normalised_month = ((self.mMonth - 1) % 12) + 1
adjustment_year = (self.mMonth - 1) / 12
if (normalised_month - 1) < 0:
normalised_month += 12
adjustment_year -= 1
self.mMonth = normalised_month
self.mYear += adjustment_year
if not allow_invalid:
self.normalise()
while self.mDay > utils.daysInMonth(self.mMonth, self.mYear):
self.mMonth += interval
self.normalise()
normalize = False
elif freq == definitions.eRecurrence_YEARLY:
self.mYear += interval
if allow_invalid:
normalize = False
if normalize:
# Normalise to standard date-time ranges
self.normalise()
else:
self.changed()
def getLocaleDate(self, locale):
buf = StringIO.StringIO()
if locale == PyCalendarDateTime.FULLDATE:
buf.write(locale.getDay(self.getDayOfWeek(), locale.LONG))
buf.write(", ")
buf.write(locale.getMonth(self.mMonth, locale.LONG))
buf.write(" ")
buf.write(str(self.mDay))
buf.write(", ")
buf.write(str(self.mYear))
elif locale == PyCalendarDateTime.ABBREVDATE:
buf.write(locale.getDay(self.getDayOfWeek(), locale.SHORT))
buf.write(", ")
buf.write(locale.getMonth(self.mMonth, locale.SHORT))
buf.write(" ")
buf.write(str(self.mDay))
buf.write(", ")
buf.write(str(self.mYear))
elif locale == PyCalendarDateTime.NUMERICDATE:
buf.write(str(self.mMonth))
buf.write("/")
buf.write(str(self.mDay))
buf.write("/")
buf.write(str(self.mYear))
elif locale == PyCalendarDateTime.FULLDATENOYEAR:
buf.write(locale.getDay(self.getDayOfWeek(), locale.LONG))
buf.write(", ")
buf.write(locale.getMonth(self.mMonth, locale.LONG))
buf.write(" ")
buf.write(str(self.mDay))
elif locale == PyCalendarDateTime.ABBREVDATENOYEAR:
buf.write(locale.getDay(self. getDayOfWeek(), locale.SHORT))
buf.write(", ")
buf.write(locale.getMonth(self.mMonth, locale.SHORT))
buf.write(" ")
buf.write(str(self.mDay))
elif locale == PyCalendarDateTime.NUMERICDATENOYEAR:
buf.write(str(self.mMonth))
buf.write("/")
buf.write(str(self.mDay))
return buf.getvalue()
def getTime(self, with_seconds, am_pm, tzid):
buf = StringIO.StringIO()
adjusted_hour = self.mHours
if am_pm:
am = True
if adjusted_hour >= 12:
adjusted_hour -= 12
am = False
if adjusted_hour == 0:
adjusted_hour = 12
buf.write(str(adjusted_hour))
buf.write(":")
if self.mMinutes < 10:
buf.write("0")
buf.write(str(self.mMinutes))
if with_seconds:
buf.write(":")
if self.mSeconds < 10:
buf.write("0")
buf.write(str(self.mSeconds))
buf.write([" AM", " PM"][not am])
else:
if self.mHours < 10:
buf.write("0")
buf.write(str(self.mHours))
buf.write(":")
if self.mMinutes < 10:
buf.write("0")
buf.write(str(self.mMinutes))
if with_seconds:
buf.write(":")
if self.mSeconds < 10:
buf.write("0")
buf.write(str(self.mSeconds))
if tzid:
desc = self.timeZoneDescriptor()
if len(desc) > 0:
buf.write(" ")
buf.write(desc)
return buf.getvalue()
def getLocaleDateTime(self, locale, with_seconds, am_pm, tzid):
return self.getLocaleDate(locale) + " " + self.getTime(with_seconds, am_pm, tzid)
def getText(self):
if self.mDateOnly:
return "%04d%02d%02d" % (self.mYear, self.mMonth, self.mDay)
else:
if self.mTZUTC:
return "%04d%02d%02dT%02d%02d%02dZ" % (self.mYear, self.mMonth, self.mDay, self.mHours, self.mMinutes, self.mSeconds)
elif isinstance(self.mTZID, int):
sign = "-" if self.mTZID < 0 else "+"
hours = abs(self.mTZID) / 3600
minutes = divmod(abs(self.mTZID) / 60, 60)[1]
return "%04d%02d%02dT%02d%02d%02d%s%02d%02d" % (self.mYear, self.mMonth, self.mDay, self.mHours, self.mMinutes, self.mSeconds, sign, hours, minutes)
else:
return "%04d%02d%02dT%02d%02d%02d" % (self.mYear, self.mMonth, self.mDay, self.mHours, self.mMinutes, self.mSeconds)
def getXMLText(self):
if self.mDateOnly:
return "%04d-%02d-%02d" % (self.mYear, self.mMonth, self.mDay)
else:
if self.mTZUTC:
return "%04d-%02d-%02dT%02d:%02d:%02dZ" % (self.mYear, self.mMonth, self.mDay, self.mHours, self.mMinutes, self.mSeconds)
elif isinstance(self.mTZID, int):
sign = "-" if self.mTZID < 0 else "+"
hours = abs(self.mTZID) / 3600
minutes = divmod(abs(self.mTZID) / 60, 60)[1]
return "%04d-%02d-%02dT%02d:%02d:%02d%s%02d:%02d" % (self.mYear, self.mMonth, self.mDay, self.mHours, self.mMinutes, self.mSeconds, sign, hours, minutes)
else:
return "%04d-%02d-%02dT%02d:%02d:%02d" % (self.mYear, self.mMonth, self.mDay, self.mHours, self.mMinutes, self.mSeconds)
@classmethod
def parseText(cls, data, fullISO=False):
dt = cls()
dt.parse(data, fullISO)
return dt
def parseDate(self, data, fullISO):
# Get year
self.mYear = int(data[0:4])
index = 4
if fullISO and data[index] == "-":
index += 1
# Get month
self.mMonth = int(data[index:index + 2])
index += 2
if fullISO and data[index] == "-":
index += 1
# Get day
self.mDay = int(data[index:index + 2])
index += 2
if ' ' in data[:index] and ParserContext.INVALID_DATETIME_LEADINGSPACE == ParserContext.PARSER_RAISE:
raise ValueError
if self.mYear < 0 or self.mMonth < 0 or self.mDay < 0:
raise ValueError
return index
def parseTime(self, data, index, fullISO):
# Get hours
self.mHours = int(data[index:index + 2])
index += 2
if fullISO and data[index] == ":":
index += 1
# Get minutes
self.mMinutes = int(data[index:index + 2])
index += 2
if fullISO and data[index] == ":":
index += 1
# Get seconds
self.mSeconds = int(data[index:index + 2])
index += 2
return index
def parseFractionalAndUTCOffset(self, data, index, dlen):
# Optional fraction
if data[index] == ",":
index += 1
if not data[index].isdigit():
raise ValueError
while index < dlen and data[index].isdigit():
index += 1
# Optional timezone descriptor
if index < dlen:
if data[index] == "Z":
index += 1
self.mTZUTC = True
else:
if data[index] == "+":
sign = 1
elif data[index] == "-":
sign = -1
else:
raise ValueError
index += 1
# Get hours
hours_offset = int(data[index:index + 2])
index += 2
if data[index] == ":":
index += 1
# Get minutes
minutes_offset = int(data[index:index + 2])
index += 2
self.mTZID = sign * (hours_offset * 60 + minutes_offset) * 60
if index < dlen:
raise ValueError
return index
def parse(self, data, fullISO=False):
# iCalendar:
# parse format YYYYMMDD[THHMMSS[Z]]
# vCard (fullISO)
# parse format YYYY[-]MM[-]DD[THH[:]MM[:]SS[(Z/(+/-)HHMM]]
try:
# Parse out the date
index = self.parseDate(data, fullISO)
# Now look for more
dlen = len(data)
if index < dlen:
if data[index] != 'T':
raise ValueError
index += 1
# Parse out the time
index = self.parseTime(data, index, fullISO)
self.mDateOnly = False
if index < dlen:
if fullISO:
index = self.parseFractionalAndUTCOffset(data, index, dlen)
else:
if index < dlen:
if data[index] != 'Z':
raise ValueError
index += 1
self.mTZUTC = True
if index < dlen:
raise ValueError
else:
self.mTZUTC = False
else:
self.mDateOnly = True
except IndexError:
raise ValueError
# Always uncache posix time
self.changed()
def generate(self, os):
try:
os.write(self.getText())
except:
pass
def generateRFC2822(self, os):
pass
def writeXML(self, node, namespace):
value = XML.SubElement(
node,
xmldefs.makeTag(
namespace,
xmldefs.value_date if self.isDateOnly() else xmldefs.value_date_time
))
value.text = self.getXMLText()
def invalid(self):
"""
Are any of the current fields invalid.
"""
# Right now we only care about invalid days of the month (e.g. February 30th). In the
# future we may also want to look for invalid times during a DST transition.
if self.mDay <= 0 or self.mDay > utils.daysInMonth(self.mMonth, self.mYear):
return True
return False
def normalise(self):
# Normalise seconds
normalised_secs = self.mSeconds % 60
adjustment_mins = self.mSeconds / 60
if normalised_secs < 0:
normalised_secs += 60
adjustment_mins -= 1
self.mSeconds = normalised_secs
self.mMinutes += adjustment_mins
# Normalise minutes
normalised_mins = self.mMinutes % 60
adjustment_hours = self.mMinutes / 60
if normalised_mins < 0:
normalised_mins += 60
adjustment_hours -= 1
self.mMinutes = normalised_mins
self.mHours += adjustment_hours
# Normalise hours
normalised_hours = self.mHours % 24
adjustment_days = self.mHours / 24
if normalised_hours < 0:
normalised_hours += 24
adjustment_days -= 1
self.mHours = normalised_hours
self.mDay += adjustment_days
# Wipe the time if date only
if self.mDateOnly:
self.mSeconds = self.mMinutes = self.mHours = 0
# Adjust the month first, since the day adjustment is month dependent
# Normalise month
normalised_month = ((self.mMonth - 1) % 12) + 1
adjustment_year = (self.mMonth - 1) / 12
if (normalised_month - 1) < 0:
normalised_month += 12
adjustment_year -= 1
self.mMonth = normalised_month
self.mYear += adjustment_year
# Now do days
if self.mDay > 0:
while self.mDay > utils.daysInMonth(self.mMonth, self.mYear):
self.mDay -= utils.daysInMonth(self.mMonth, self.mYear)
self.mMonth += 1
if self.mMonth > 12:
self.mMonth = 1
self.mYear += 1
else:
while self.mDay <= 0:
self.mMonth -= 1
if self.mMonth < 1:
self.mMonth = 12
self.mYear -= 1
self.mDay += utils.daysInMonth(self.mMonth, self.mYear)
# Always invalidate posix time cache
self.changed()
def timeZoneSecondsOffset(self, relative_to_utc=False):
if self.mTZUTC:
return 0
if self.mTZOffset is None:
tz = PyCalendarTimezone(utc=self.mTZUTC, tzid=self.mTZID)
self.mTZOffset = tz.timeZoneSecondsOffset(self, relative_to_utc)
return self.mTZOffset
def timeZoneDescriptor(self):
tz = PyCalendarTimezone(utc=self.mTZUTC, tzid=self.mTZID)
return tz.timeZoneDescriptor(self)
def changed(self):
self.mPosixTimeCached = False
self.mTZOffset = None
def daysSince1970(self):
# Add days between 1970 and current year (ignoring leap days)
result = (self.mYear - 1970) * 365
# Add leap days between years
result += utils.leapDaysSince1970(self.mYear - 1970)
# Add days in current year up to current month (includes leap day for
# current year as needed)
result += utils.daysUptoMonth(self.mMonth, self.mYear)
# Add days in month
result += self.mDay - 1
return result
pycalendar-2.0~svn13177/src/pycalendar/componentrecur.py 0000644 0001750 0001750 00000061501 12101017573 022363 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar.component import PyCalendarComponent
from pycalendar.componentexpanded import PyCalendarComponentExpanded
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.property import PyCalendarProperty
from pycalendar.recurrenceset import PyCalendarRecurrenceSet
from pycalendar.timezone import PyCalendarTimezone
from pycalendar.utils import set_difference
import uuid
class PyCalendarComponentRecur(PyCalendarComponent):
propertyCardinality_STATUS_Fix = (
definitions.cICalProperty_STATUS,
)
@staticmethod
def mapKey(uid, rid=None):
if uid:
result = "u:" + uid
if rid is not None:
result += rid
return result
else:
return None
@staticmethod
def sort_by_dtstart_allday(e1, e2):
if e1.self.mStart.isDateOnly() and e2.self.mStart.isDateOnly():
return e1.self.mStart < e2.self.mStart
elif e1.self.mStart.isDateOnly():
return True
elif (e2.self.mStart.isDateOnly()):
return False
elif e1.self.mStart == e2.self.mStart:
if e1.self.mEnd == e2.self.mEnd:
# Put ones created earlier in earlier columns in day view
return e1.self.mStamp < e2.self.mStamp
else:
# Put ones that end later in earlier columns in day view
return e1.self.mEnd > e2.self.mEnd
else:
return e1.self.mStart < e2.self.mStart
@staticmethod
def sort_by_dtstart(e1, e2):
if e1.self.mStart == e2.self.mStart:
if (e1.self.mStart.isDateOnly() and e2.self.mStart.isDateOnly() or
not e1.self.mStart.isDateOnly() and not e2.self.mStart.isDateOnly()):
return False
else:
return e1.self.mStart.isDateOnly()
else:
return e1.self.mStart < e2.self.mStart
def __init__(self, parent=None):
super(PyCalendarComponentRecur, self).__init__(parent=parent)
self.mMaster = self
self.mMapKey = None
self.mSummary = None
self.mStamp = PyCalendarDateTime()
self.mHasStamp = False
self.mStart = PyCalendarDateTime()
self.mHasStart = False
self.mEnd = PyCalendarDateTime()
self.mHasEnd = False
self.mDuration = False
self.mHasRecurrenceID = False
self.mAdjustFuture = False
self.mAdjustPrior = False
self.mRecurrenceID = None
self.mRecurrences = None
# This is a special check we do only for STATUS due to a calendarserver bug
self.cardinalityChecks += (
self.check_cardinality_STATUS_Fix,
)
def duplicate(self, parent=None):
other = super(PyCalendarComponentRecur, self).duplicate(parent=parent)
# Special determination of master
other.mMaster = self.mMaster if self.recurring() else self
other.mMapKey = self.mMapKey
other.mSummary = self.mSummary
if (self.mStamp is not None):
other.mStamp = self.mStamp.duplicate()
other.mHasStamp = self.mHasStamp
other.mStart = self.mStart.duplicate()
other.mHasStart = self.mHasStart
other.mEnd = self.mEnd.duplicate()
other.mHasEnd = self.mHasEnd
other.mDuration = self.mDuration
other.mHasRecurrenceID = self.mHasRecurrenceID
other.mAdjustFuture = self.mAdjustFuture
other.mAdjustPrior = self.mAdjustPrior
if self.mRecurrenceID is not None:
other.mRecurrenceID = self.mRecurrenceID.duplicate()
other._resetRecurrenceSet()
return other
def canGenerateInstance(self):
return not self.mHasRecurrenceID
def recurring(self):
return (self.mMaster is not None) and (self.mMaster is not self)
def setMaster(self, master):
self.mMaster = master
self.initFromMaster()
def getMaster(self):
return self.mMaster
def getMapKey(self):
if self.mMapKey is None:
self.mMapKey = str(uuid.uuid4())
return self.mMapKey
def getMasterKey(self):
return PyCalendarComponentRecur.mapKey(self.mUID)
def initDTSTAMP(self):
# Save new one
super(PyCalendarComponentRecur, self).initDTSTAMP()
# Get the new one
temp = self.loadValueDateTime(definitions.cICalProperty_DTSTAMP)
self.mHasStamp = temp is not None
if self.mHasStamp:
self.mStamp = temp
def getStamp(self):
return self.mStamp
def hasStamp(self):
return self.mHasStamp
def getStart(self):
return self.mStart
def hasStart(self):
return self.mHasStart
def getEnd(self):
return self.mEnd
def hasEnd(self):
return self.mHasEnd
def useDuration(self):
return self.mDuration
def isRecurrenceInstance(self):
return self.mHasRecurrenceID
def isAdjustFuture(self):
return self.mAdjustFuture
def isAdjustPrior(self):
return self.mAdjustPrior
def getRecurrenceID(self):
return self.mRecurrenceID
def isRecurring(self):
return (self.mRecurrences is not None) and self.mRecurrences.hasRecurrence()
def getRecurrenceSet(self):
return self.mRecurrences
def setUID(self, uid):
super(PyCalendarComponentRecur, self).setUID(uid)
# Update the map key
if self.mHasRecurrenceID:
self.mMapKey = self.mapKey(self.mUID, self.mRecurrenceID.getText())
else:
self.mMapKey = self.mapKey(self.mUID)
def getSummary(self):
return self.mSummary
def setSummary(self, summary):
self.mSummary = summary
def getDescription(self):
# Get DESCRIPTION
txt = self.loadValueString(definitions.cICalProperty_DESCRIPTION)
if txt is not None:
return txt
else:
return ""
def getLocation(self):
# Get LOCATION
txt = self.loadValueString(definitions.cICalProperty_LOCATION)
if txt is not None:
return txt
else:
return ""
def finalise(self):
super(PyCalendarComponentRecur, self).finalise()
# Get DTSTAMP
temp = self.loadValueDateTime(definitions.cICalProperty_DTSTAMP)
self.mHasStamp = temp is not None
if self.mHasStamp:
self.mStamp = temp
# Get DTSTART
temp = self.loadValueDateTime(definitions.cICalProperty_DTSTART)
self.mHasStart = temp is not None
if self.mHasStart:
self.mStart = temp
# Get DTEND
temp = self.loadValueDateTime(definitions.cICalProperty_DTEND)
if temp is None:
# Try DURATION instead
temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
if temp is not None:
self.mHasEnd = False
self.mEnd = self.mStart + temp
self.mDuration = True
else:
# If no end or duration then use the start
self.mHasEnd = False
self.mEnd = self.mStart.duplicate()
self.mDuration = False
else:
self.mHasEnd = True
self.mEnd = temp
self.mDuration = False
# Get SUMMARY
temp = self.loadValueString(definitions.cICalProperty_SUMMARY)
if temp is not None:
self.mSummary = temp
# Get RECURRENCE-ID
self.mHasRecurrenceID = (self.countProperty(definitions.cICalProperty_RECURRENCE_ID) != 0)
if self.mHasRecurrenceID:
self.mRecurrenceID = self.loadValueDateTime(definitions.cICalProperty_RECURRENCE_ID)
# Update the map key
if self.mHasRecurrenceID:
self.mMapKey = self.mapKey(self.mUID, self.mRecurrenceID.getText())
# Also get the RANGE attribute
attrs = self.findFirstProperty(definitions.cICalProperty_RECURRENCE_ID).getAttributes()
if definitions.cICalAttribute_RANGE in attrs:
self.mAdjustFuture = (attrs[definitions.cICalAttribute_RANGE][0].getFirstValue() == definitions.cICalAttribute_RANGE_THISANDFUTURE)
self.mAdjustPrior = (attrs[definitions.cICalAttribute_RANGE][0].getFirstValue() == definitions.cICalAttribute_RANGE_THISANDPRIOR)
else:
self.mAdjustFuture = False
self.mAdjustPrior = False
else:
self.mMapKey = self.mapKey(self.mUID)
self._resetRecurrenceSet()
def validate(self, doFix=False):
"""
Validate the data in this component and optionally fix any problems. Return
a tuple containing two lists: the first describes problems that were fixed, the
second problems that were not fixed. Caller can then decide what to do with unfixed
issues.
"""
# Do normal checks
fixed, unfixed = super(PyCalendarComponentRecur, self).validate(doFix)
# Check that any UNTIL value matches that for DTSTART
if self.mHasStart and self.mRecurrences:
dtutc = self.mStart.duplicateAsUTC()
for rrule in self.mRecurrences.getRules():
if rrule.getUseUntil():
if rrule.getUntil().isDateOnly() ^ self.mStart.isDateOnly():
logProblem = "[%s] Value types must match: %s, %s" % (
self.getType(),
definitions.cICalProperty_DTSTART,
definitions.cICalValue_RECUR_UNTIL,
)
if doFix:
rrule.getUntil().setDateOnly(self.mStart.isDateOnly())
if not self.mStart.isDateOnly():
rrule.getUntil().setHHMMSS(dtutc.getHours(), dtutc.getMinutes(), dtutc.getSeconds())
rrule.getUntil().setTimezone(PyCalendarTimezone(utc=True))
self.mRecurrences.changed()
fixed.append(logProblem)
else:
unfixed.append(logProblem)
return fixed, unfixed
def check_cardinality_STATUS_Fix(self, fixed, unfixed, doFix):
"""
Special for bug with STATUS where STATUS:CANCELLED is added alongside
another STATUS. In this case we want STATUS:CANCELLED to win.
"""
for propname in self.propertyCardinality_STATUS_Fix:
if self.countProperty(propname) > 1:
logProblem = "[%s] Too many properties: %s" % (self.getType(), propname)
if doFix:
# Check that one of them is STATUS:CANCELLED
for prop in self.getProperties(propname):
if prop.getTextValue().getValue().upper() == definitions.cICalProperty_STATUS_CANCELLED:
self.removeProperties(propname)
self.addProperty(PyCalendarProperty(propname, definitions.cICalProperty_STATUS_CANCELLED))
fixed.append(logProblem)
break
else:
unfixed.append(logProblem)
else:
unfixed.append(logProblem)
def _resetRecurrenceSet(self):
# May need to create items
self.mRecurrences = None
if ((self.countProperty(definitions.cICalProperty_RRULE) != 0) or
(self.countProperty(definitions.cICalProperty_RDATE) != 0) or
(self.countProperty(definitions.cICalProperty_EXRULE) != 0) or
(self.countProperty(definitions.cICalProperty_EXDATE) != 0)):
self.mRecurrences = PyCalendarRecurrenceSet()
# Get RRULEs
self.loadValueRRULE(definitions.cICalProperty_RRULE, self.mRecurrences, True)
# Get RDATEs
self.loadValueRDATE(definitions.cICalProperty_RDATE, self.mRecurrences, True)
# Get EXRULEs
self.loadValueRRULE(definitions.cICalProperty_EXRULE, self.mRecurrences, False)
# Get EXDATEs
self.loadValueRDATE(definitions.cICalProperty_EXDATE, self.mRecurrences, False)
def FixStartEnd(self):
# End is always greater than start if start exists
if self.mHasStart and self.mEnd <= self.mStart:
# Use the start
self.mEnd = self.mStart.duplicate()
self.mDuration = False
# Adjust to approriate non-inclusive end point
if self.mStart.isDateOnly():
self.mEnd.offsetDay(1)
# For all day events it makes sense to use duration
self.mDuration = True
else:
# Use end of current day
self.mEnd.offsetDay(1)
self.mEnd.setHHMMSS(0, 0, 0)
def expandPeriod(self, period, results):
# Check for recurrence and True master
if ((self.mRecurrences is not None) and self.mRecurrences.hasRecurrence()
and not self.isRecurrenceInstance()):
# Expand recurrences within the range
items = []
self.mRecurrences.expand(self.mStart, period, items)
# Look for overridden recurrence items
cal = self.mParentComponent
if cal is not None:
# Remove recurrence instances from the list of items
recurs = []
cal.getRecurrenceInstancesIds(definitions.cICalComponent_VEVENT, self.getUID(), recurs)
recurs.sort()
if len(recurs) != 0:
temp = []
temp = set_difference(items, recurs)
items = temp
# Now get actual instances
instances = []
cal.getRecurrenceInstancesItems(definitions.cICalComponent_VEVENT, self.getUID(), instances)
# Get list of each ones with RANGE
prior = []
future = []
for iter in instances:
if iter.isAdjustPrior():
prior.append(iter)
if iter.isAdjustFuture():
future.append(iter)
# Check for special behaviour
if len(prior) + len(future) == 0:
# Add each expanded item
for iter in items:
results.append(self.createExpanded(self, iter))
else:
# Sort each list first
prior.sort(self.sort_by_dtstart)
future.sort(self.sort_by_dtstart)
# Add each expanded item
for iter1 in items:
# Now step through each using the slave item
# instead of the master as appropriate
slave = None
# Find most appropriate THISANDPRIOR item
for i in range(len(prior) - 1, 0, -1):
riter2 = prior[i]
if riter2.getStart() > iter1:
slave = riter2
break
# Find most appropriate THISANDFUTURE item
for i in range(len(future) - 1, 0, -1):
riter2 = future.elementAt(i)
if riter2.getStart() < iter1:
slave = riter2
break
if slave is None:
slave = self
results.append(self.createExpanded(slave, iter1))
else:
# Add each expanded item
for iter in items:
results.append(self.createExpanded(self, iter))
elif self.withinPeriod(period):
if self.isRecurrenceInstance():
rid = self.mRecurrenceID
else:
rid = None
results.append(PyCalendarComponentExpanded(self, rid))
def withinPeriod(self, period):
# Check for recurrence
if ((self.mRecurrences is not None) and self.mRecurrences.hasRecurrence()):
items = []
self.mRecurrences.expand(self.mStart, period, items)
return len(items) != 0
else:
# Does event span the period (assume self.mEnd > self.mStart)
# Check start (inclusive) and end (exclusive)
if self.mEnd <= period.getStart() or self.mStart >= period.getEnd():
return False
else:
return True
def changedRecurrence(self):
# Clear cached values
if self.mRecurrences is not None:
self.mRecurrences.changed()
# Editing
def editSummary(self, summary):
# Updated cached value
self.mSummary = summary
# Remove existing items
self.editProperty(definitions.cICalProperty_SUMMARY, summary)
def editDetails(self, description, location):
# Edit existing items
self.editProperty(definitions.cICalProperty_DESCRIPTION, description)
self.editProperty(definitions.cICalProperty_LOCATION, location)
def editTiming(self):
# Updated cached values
self.mHasStart = False
self.mHasEnd = False
self.mDuration = False
self.mStart.setToday()
self.mEnd.setToday()
# Remove existing DTSTART & DTEND & DURATION & DUE items
self.removeProperties(definitions.cICalProperty_DTSTART)
self.removeProperties(definitions.cICalProperty_DTEND)
self.removeProperties(definitions.cICalProperty_DURATION)
self.removeProperties(definitions.cICalProperty_DUE)
def editTimingDue(self, due):
# Updated cached values
self.mHasStart = False
self.mHasEnd = True
self.mDuration = False
self.mStart = due
self.mEnd = due
# Remove existing DUE & DTSTART & DTEND & DURATION items
self.removeProperties(definitions.cICalProperty_DUE)
self.removeProperties(definitions.cICalProperty_DTSTART)
self.removeProperties(definitions.cICalProperty_DTEND)
self.removeProperties(definitions.cICalProperty_DURATION)
# Now create properties
prop = PyCalendarProperty(definitions.cICalProperty_DUE, due)
self.addProperty(prop)
def editTimingStartEnd(self, start, end):
# Updated cached values
self.mHasStart = self.mHasEnd = True
self.mStart = start
self.mEnd = end
self.mDuration = False
self.FixStartEnd()
# Remove existing DTSTART & DTEND & DURATION & DUE items
self.removeProperties(definitions.cICalProperty_DTSTART)
self.removeProperties(definitions.cICalProperty_DTEND)
self.removeProperties(definitions.cICalProperty_DURATION)
self.removeProperties(definitions.cICalProperty_DUE)
# Now create properties
prop = PyCalendarProperty(definitions.cICalProperty_DTSTART, start)
self.addProperty(prop)
# If its an all day event and the end one day after the start, ignore it
temp = start.duplicate()
temp.offsetDay(1)
if not start.isDateOnly() or end != temp:
prop = PyCalendarProperty(definitions.cICalProperty_DTEND, end)
self.addProperty(prop)
def editTimingStartDuration(self, start, duration):
# Updated cached values
self.mHasStart = True
self.mHasEnd = False
self.mStart = start
self.mEnd = start + duration
self.mDuration = True
# Remove existing DTSTART & DTEND & DURATION & DUE items
self.removeProperties(definitions.cICalProperty_DTSTART)
self.removeProperties(definitions.cICalProperty_DTEND)
self.removeProperties(definitions.cICalProperty_DURATION)
self.removeProperties(definitions.cICalProperty_DUE)
# Now create properties
prop = PyCalendarProperty(definitions.cICalProperty_DTSTART, start)
self.addProperty(prop)
# If its an all day event and the duration is one day, ignore it
if (not start.isDateOnly() or (duration.getWeeks() != 0)
or (duration.getDays() > 1)):
prop = PyCalendarProperty(definitions.cICalProperty_DURATION, duration)
self.addProperty(prop)
def editRecurrenceSet(self, recurs):
# Must have items
if self.mRecurrences is None:
self.mRecurrences = PyCalendarRecurrenceSet()
# Updated cached values
self.mRecurrences = recurs
# Remove existing RRULE, EXRULE, RDATE & EXDATE
self.removeProperties(definitions.cICalProperty_RRULE)
self.removeProperties(definitions.cICalProperty_EXRULE)
self.removeProperties(definitions.cICalProperty_RDATE)
self.removeProperties(definitions.cICalProperty_EXDATE)
# Now create properties
for iter in self.mRecurrences.getRules():
prop = PyCalendarProperty(definitions.cICalProperty_RRULE, iter)
self.addProperty(prop)
for iter in self.getExrules():
prop = PyCalendarProperty(definitions.cICalProperty_EXRULE, iter)
self.addProperty(prop)
for iter in self.mRecurrences.getDates():
prop = PyCalendarProperty(definitions.cICalProperty_RDATE, iter)
self.addProperty(prop)
for iter in self.mRecurrences.getExdates():
prop = PyCalendarProperty(definitions.cICalProperty_EXDATE, iter)
self.addProperty(prop)
def excludeRecurrence(self, start):
# Must have items
if self.mRecurrences is None:
return
# Add to recurrence set and clear cache
self.mRecurrences.subtract(start)
# Add property
prop = PyCalendarProperty(definitions.cICalProperty_EXDATE, start)
self.addProperty(prop)
def excludeFutureRecurrence(self, start):
# Must have items
if self.mRecurrences is None:
return
# Adjust RRULES to end before start
self.mRecurrences.excludeFutureRecurrence(start)
# Remove existing RRULE & RDATE
self.removeProperties(definitions.cICalProperty_RRULE)
self.removeProperties(definitions.cICalProperty_RDATE)
# Now create properties
for iter in self.mRecurrences.getRules():
prop = PyCalendarProperty(definitions.cICalProperty_RRULE, iter)
self.addProperty(prop)
for iter in self.mRecurrences.getDates():
prop = PyCalendarProperty(definitions.cICalProperty_RDATE, iter)
self.addProperty(prop)
def initFromMaster(self):
# Only if not master
if self.recurring():
# Redo this to get cached values from master
self.finalise()
# If this component does not have its own start property, use the
# recurrence id
# i.e. the start time of this instance has not changed - something
# else has
if not self.hasProperty(definitions.cICalProperty_DTSTART):
self.mStart = self.mRecurrenceID
# If this component does not have its own end/duration property,
# the determine
# the end from the master duration
if (not self.hasProperty(definitions.cICalProperty_DTEND) and
not self.hasProperty(definitions.cICalProperty_DURATION)):
# End is based on original events settings
self.mEnd = self.mStart + (self.mMaster.getEnd() - self.mMaster.getStart())
# If this instance has a duration, but no start of its own, then we
# need to readjust the end
# to account for the start being changed to the recurrence id
elif (self.hasProperty(definitions.cICalProperty_DURATION) and
not self.hasProperty(definitions.cICalProperty_DTSTART)):
temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
self.mEnd = self.mStart + temp
def createExpanded(self, master, recurid):
return PyCalendarComponentExpanded(master, recurid)
pycalendar-2.0~svn13177/src/pycalendar/unknownvalue.py 0000644 0001750 0001750 00000002117 12101017573 022052 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# iCalendar Unknown value - one whose default type we don't know about
from pycalendar import xmldefs
from pycalendar.plaintextvalue import PyCalendarPlainTextValue
from pycalendar.value import PyCalendarValue
class PyCalendarUnknownValue(PyCalendarPlainTextValue):
def getType(self):
return PyCalendarUnknownValue.VALUETYPE_UNKNOWN
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_UNKNOWN, PyCalendarUnknownValue, xmldefs.value_unknown)
pycalendar-2.0~svn13177/src/pycalendar/validation.py 0000644 0001750 0001750 00000004014 12101017573 021446 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.plaintextvalue import PyCalendarPlainTextValue
# Grabbed from http://docs.python.org/library/functools.html since we need to support Python 2.5
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc
class PropertyValueChecks(object):
@staticmethod
def stringValue(text, property):
value = property.getValue()
if value and isinstance(value, PyCalendarPlainTextValue):
value = value.getValue()
return value.lower() == text.lower()
return False
@staticmethod
def alwaysUTC(property):
value = property.getDateTimeValue()
if value:
value = value.getValue()
return value.utc()
return False
@staticmethod
def numericRange(low, high, property):
value = property.getIntegerValue()
if value:
value = value.getValue()
return value >= low and value <= high
return False
@staticmethod
def positiveIntegerOrZero(property):
value = property.getIntegerValue()
if value:
value = value.getValue()
return value >= 0
return False
pycalendar-2.0~svn13177/src/pycalendar/timezonedb.py 0000644 0001750 0001750 00000011072 12317143440 021460 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.exceptions import PyCalendarNoTimezoneInDatabase, \
PyCalendarInvalidData
import os
class PyCalendarTimezoneDatabase(object):
"""
On demand timezone database cache. This scans a TZdb directory for .ics files matching a
TZID and caches the component data in a calendar from whence the actual component is returned.
"""
sTimezoneDatabase = None
@staticmethod
def createTimezoneDatabase(dbpath):
PyCalendarTimezoneDatabase.sTimezoneDatabase = PyCalendarTimezoneDatabase()
PyCalendarTimezoneDatabase.sTimezoneDatabase.setPath(dbpath)
@staticmethod
def clearTimezoneDatabase():
if PyCalendarTimezoneDatabase.sTimezoneDatabase is not None:
PyCalendarTimezoneDatabase.sTimezoneDatabase.clear()
def __init__(self):
from pycalendar.calendar import PyCalendar
self.dbpath = None
self.calendar = PyCalendar()
def setPath(self, dbpath):
self.dbpath = dbpath
def clear(self):
from pycalendar.calendar import PyCalendar
self.calendar = PyCalendar()
@staticmethod
def getTimezoneDatabase():
if PyCalendarTimezoneDatabase.sTimezoneDatabase is None:
PyCalendarTimezoneDatabase.sTimezoneDatabase = PyCalendarTimezoneDatabase()
return PyCalendarTimezoneDatabase.sTimezoneDatabase
@staticmethod
def getTimezone(tzid):
# Check whether current cached
tzdb = PyCalendarTimezoneDatabase.getTimezoneDatabase()
tz = tzdb.calendar.getTimezone(tzid)
if tz is None:
try:
tzdb.cacheTimezone(tzid)
except PyCalendarNoTimezoneInDatabase:
pass
tz = tzdb.calendar.getTimezone(tzid)
return tz
@staticmethod
def getTimezoneInCalendar(tzid):
"""
Return a VTIMEZONE inside a valid VCALENDAR
"""
tz = PyCalendarTimezoneDatabase.getTimezone(tzid)
if tz is not None:
from pycalendar.calendar import PyCalendar
cal = PyCalendar()
cal.addComponent(tz.duplicate(cal))
return cal
else:
return None
@staticmethod
def getTimezoneOffsetSeconds(tzid, dt, relative_to_utc=False):
# Cache it first
tz = PyCalendarTimezoneDatabase.getTimezone(tzid)
if tz is not None:
return tz.getTimezoneOffsetSeconds(dt, relative_to_utc)
else:
return 0
@staticmethod
def getTimezoneDescriptor(tzid, dt):
# Cache it first
tz = PyCalendarTimezoneDatabase.getTimezone(tzid)
if tz is not None:
return tz.getTimezoneDescriptor(dt)
else:
return ""
def cacheTimezone(self, tzid):
if self.dbpath is None:
return
tzpath = os.path.join(self.dbpath, "%s.ics" % (tzid,))
tzpath = os.path.normpath(tzpath)
if tzpath.startswith(self.dbpath) and os.path.isfile(tzpath):
try:
self.calendar.parseComponent(open(tzpath))
except (IOError, PyCalendarInvalidData):
raise PyCalendarNoTimezoneInDatabase(self.dbpath, tzid)
else:
raise PyCalendarNoTimezoneInDatabase(self.dbpath, tzid)
def addTimezone(self, tz):
copy = tz.duplicate(self.calendar)
self.calendar.addComponent(copy)
@staticmethod
def mergeTimezones(cal, tzs):
"""
Merge each timezone from other calendar.
"""
tzdb = PyCalendarTimezoneDatabase.getTimezoneDatabase()
# Not if our own calendar
if cal is tzdb.calendar:
return
# Merge each timezone from other calendar
for tz in tzs:
tzdb.mergeTimezone(tz)
def mergeTimezone(self, tz):
"""
If the supplied VTIMEZONE is not in our cache then store it in memory.
"""
if self.getTimezone(tz.getID()) is None:
self.addTimezone(tz)
pycalendar-2.0~svn13177/src/pycalendar/utcoffsetvalue.py 0000644 0001750 0001750 00000005515 12101017573 022362 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# iCalendar UTC Offset value
from cStringIO import StringIO
from pycalendar import xmldefs
from pycalendar.value import PyCalendarValue
class PyCalendarUTCOffsetValue(PyCalendarValue):
def __init__(self, value=0):
self.mValue = value
def duplicate(self):
return PyCalendarUTCOffsetValue(self.mValue)
def getType(self):
return PyCalendarValue.VALUETYPE_UTC_OFFSET
def parse(self, data):
# Must be of specific lengths
datalen = len(data)
if datalen not in (5, 7):
self.mValue = 0
raise ValueError
# Get sign
if data[0] not in ('+', '-'):
raise ValueError
plus = (data[0] == '+')
# Get hours
hours = int(data[1:3])
# Get minutes
mins = int(data[3:5])
# Get seconds if present
secs = 0
if datalen == 7 :
secs = int(data[5:])
self.mValue = ((hours * 60) + mins) * 60 + secs
if not plus:
self.mValue = -self.mValue
# os - StringIO object
def generate(self, os):
try:
abs_value = self.mValue
if self.mValue < 0 :
os.write("-")
abs_value = -self.mValue
else:
os.write("+")
secs = abs_value % 60
mins = (abs_value / 60) % 60
hours = abs_value / (60 * 60)
if (hours < 10):
os.write("0")
os.write(str(hours))
if (mins < 10):
os.write("0")
os.write(str(mins))
if (secs != 0):
if (secs < 10):
os.write("0")
os.write(str(secs))
except:
pass
def writeXML(self, node, namespace):
os = StringIO.StringIO()
self.generate(os)
text = os.getvalue()
text = text[:-2] + ":" + text[-2:]
value = self.getXMLNode(node, namespace)
value.text = text
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_UTC_OFFSET, PyCalendarUTCOffsetValue, xmldefs.value_utc_offset)
pycalendar-2.0~svn13177/src/pycalendar/vtimezonedaylight.py 0000644 0001750 0001750 00000002122 12101017573 023060 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from vtimezoneelement import PyCalendarVTimezoneElement
class PyCalendarVTimezoneDaylight(PyCalendarVTimezoneElement):
def __init__(self, parent=None):
super(PyCalendarVTimezoneDaylight, self).__init__(parent=parent)
def duplicate(self, parent=None):
return super(PyCalendarVTimezoneDaylight, self).duplicate(parent=parent)
def getType(self):
return definitions.cICalComponent_DAYLIGHT
pycalendar-2.0~svn13177/src/pycalendar/multivalue.py 0000644 0001750 0001750 00000004707 12101017573 021514 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.value import PyCalendarValue
class PyCalendarMultiValue(PyCalendarValue):
def __init__(self, type):
self.mType = type
self.mValues = []
def __hash__(self):
return hash(tuple(self.mValues))
def duplicate(self):
other = PyCalendarMultiValue(self.mType)
other.mValues = [i.duplicate() for i in self.mValues]
return other
def getType(self):
return self.mType
def getRealType(self):
return PyCalendarValue.VALUETYPE_MULTIVALUE
def getValue(self):
return self.mValues
def getValues(self):
return self.mValues
def addValue(self, value):
self.mValues.append(value)
def setValue(self, value):
newValues = []
for v in value:
pyCalendarValue = PyCalendarValue.createFromType(self.mType)
pyCalendarValue.setValue(v)
newValues.append(pyCalendarValue)
self.mValues = newValues
def parse(self, data):
# Tokenize on comma
if "," in data:
tokens = data.split(",")
else:
tokens = (data,)
for token in tokens:
# Create single value, and parse data
value = PyCalendarValue.createFromType(self.mType)
value.parse(token)
self.mValues.append(value)
def generate(self, os):
try:
first = True
for iter in self.mValues:
if first:
first = False
else:
os.write(",")
iter.generate(os)
except:
pass
def writeXML(self, node, namespace):
for iter in self.mValues:
iter.writeXML(node, namespace)
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_MULTIVALUE, PyCalendarMultiValue, None)
pycalendar-2.0~svn13177/src/pycalendar/componentbase.py 0000644 0001750 0001750 00000047106 12101017573 022162 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from cStringIO import StringIO
from pycalendar import xmldefs
from pycalendar.datetimevalue import PyCalendarDateTimeValue
from pycalendar.periodvalue import PyCalendarPeriodValue
from pycalendar.property import PyCalendarProperty
from pycalendar.value import PyCalendarValue
import xml.etree.cElementTree as XML
class PyCalendarComponentBase(object):
# These are class attributes for sets of properties for testing cardinality constraints. The sets
# must contain property names.
propertyCardinality_1 = () # Must be present
propertyCardinality_1_Fix_Empty = () # Must be present but can be fixed by adding an empty value
propertyCardinality_0_1 = () # 0 or 1 only
propertyCardinality_1_More = () # 1 or more
propertyValueChecks = None # Either iCalendar or vCard validation
sortSubComponents = True
def __init__(self, parent=None):
self.mParentComponent = parent
self.mComponents = []
self.mProperties = {}
# This is the set of checks we do by default for components
self.cardinalityChecks = (
self.check_cardinality_1,
self.check_cardinality_1_Fix_Empty,
self.check_cardinality_0_1,
self.check_cardinality_1_More,
)
def duplicate(self, **args):
other = self.__class__(**args)
for component in self.mComponents:
other.addComponent(component.duplicate(parent=other))
other.mProperties = {}
for propname, props in self.mProperties.iteritems():
other.mProperties[propname] = [i.duplicate() for i in props]
return other
def __str__(self):
return self.getText()
def __ne__(self, other):
return not self.__eq__(other)
def __eq__(self, other):
if not isinstance(other, PyCalendarComponentBase):
return False
return self.getType() == other.getType() and self.compareProperties(other) and self.compareComponents(other)
def getType(self):
raise NotImplementedError
def getBeginDelimiter(self):
return "BEGIN:" + self.getType()
def getEndDelimiter(self):
return "END:" + self.getType()
def getSortKey(self):
return ""
def getParentComponent(self):
return self.mParentComponent
def setParentComponent(self, parent):
self.mParentComponent = parent
def compareComponents(self, other):
mine = set(self.mComponents)
theirs = set(other.mComponents)
for item in mine:
for another in theirs:
if item == another:
theirs.remove(another)
break
else:
return False
return len(theirs) == 0
def getComponents(self, compname=None):
compname = compname.upper() if compname else None
return [component for component in self.mComponents if compname is None or component.getType().upper() == compname]
def getComponentByKey(self, key):
for component in self.mComponents:
if component.getMapKey() == key:
return component
else:
return None
def removeComponentByKey(self, key):
for component in self.mComponents:
if component.getMapKey() == key:
self.removeComponent(component)
return
def addComponent(self, component):
self.mComponents.append(component)
def hasComponent(self, compname):
return self.countComponents(compname) != 0
def countComponents(self, compname):
return len(self.getComponents(compname))
def removeComponent(self, component):
self.mComponents.remove(component)
def removeAllComponent(self, compname=None):
if compname:
compname = compname.upper()
for component in tuple(self.mComponents):
if component.getType().upper() == compname:
self.removeComponent(component)
else:
self.mComponents = []
def sortedComponentNames(self):
return ()
def compareProperties(self, other):
mine = set()
for props in self.mProperties.values():
mine.update(props)
theirs = set()
for props in other.mProperties.values():
theirs.update(props)
return mine == theirs
def getProperties(self, propname=None):
return self.mProperties.get(propname.upper(), []) if propname else self.mProperties
def setProperties(self, props):
self.mProperties = props
def addProperty(self, prop):
self.mProperties.setdefault(prop.getName().upper(), []).append(prop)
def hasProperty(self, propname):
return propname.upper() in self.mProperties
def countProperty(self, propname):
return len(self.mProperties.get(propname.upper(), []))
def findFirstProperty(self, propname):
return self.mProperties.get(propname.upper(), [None])[0]
def removeProperty(self, prop):
if prop.getName().upper() in self.mProperties:
self.mProperties[prop.getName().upper()].remove(prop)
if len(self.mProperties[prop.getName().upper()]) == 0:
del self.mProperties[prop.getName().upper()]
def removeProperties(self, propname):
if propname.upper() in self.mProperties:
del self.mProperties[propname.upper()]
def getPropertyInteger(self, prop, type=None):
return self.loadValueInteger(prop, type)
def getPropertyString(self, prop):
return self.loadValueString(prop)
def getProperty(self, prop, value):
return self.loadValue(prop, value)
def finalise(self):
raise NotImplemented
def validate(self, doFix=False):
"""
Validate the data in this component and optionally fix any problems. Return
a tuple containing two lists: the first describes problems that were fixed, the
second problems that were not fixed. Caller can then decide what to do with unfixed
issues.
"""
fixed = []
unfixed = []
# Cardinality tests
for check in self.cardinalityChecks:
check(fixed, unfixed, doFix)
# Value constraints - these tests come from class specific attributes
if self.propertyValueChecks is not None:
for properties in self.mProperties.values():
for property in properties:
propname = property.getName().upper()
if propname in self.propertyValueChecks:
if not self.propertyValueChecks[propname](property):
# Cannot fix a bad property value
logProblem = "[%s] Property value incorrect: %s" % (self.getType(), propname,)
unfixed.append(logProblem)
# Validate all subcomponents
for component in self.mComponents:
morefixed, moreunfixed = component.validate(doFix)
fixed.extend(morefixed)
unfixed.extend(moreunfixed)
return fixed, unfixed
def check_cardinality_1(self, fixed, unfixed, doFix):
for propname in self.propertyCardinality_1:
if self.countProperty(propname) != 1: # Cannot fix a missing required property
logProblem = "[%s] Missing or too many required property: %s" % (self.getType(), propname)
unfixed.append(logProblem)
def check_cardinality_1_Fix_Empty(self, fixed, unfixed, doFix):
for propname in self.propertyCardinality_1_Fix_Empty:
if self.countProperty(propname) > 1: # Cannot fix too many required property
logProblem = "[%s] Too many required property: %s" % (self.getType(), propname)
unfixed.append(logProblem)
elif self.countProperty(propname) == 0: # Possibly fix by adding empty property
logProblem = "[%s] Missing required property: %s" % (self.getType(), propname)
if doFix:
self.addProperty(PyCalendarProperty(propname, ""))
fixed.append(logProblem)
else:
unfixed.append(logProblem)
def check_cardinality_0_1(self, fixed, unfixed, doFix):
for propname in self.propertyCardinality_0_1:
if self.countProperty(propname) > 1: # Cannot be fixed - no idea which one to delete
logProblem = "[%s] Too many properties present: %s" % (self.getType(), propname)
unfixed.append(logProblem)
def check_cardinality_1_More(self, fixed, unfixed, doFix):
for propname in self.propertyCardinality_1_More:
if not self.countProperty(propname) > 0: # Cannot fix a missing required property
logProblem = "[%s] Missing required property: %s" % (self.getType(), propname)
unfixed.append(logProblem)
def getText(self):
s = StringIO()
self.generate(s)
return s.getvalue()
def generate(self, os):
# Header
os.write(self.getBeginDelimiter())
os.write("\r\n")
# Write each property
self.writeProperties(os)
# Write each embedded component based on specific order
self.writeComponents(os)
# Footer
os.write(self.getEndDelimiter())
os.write("\r\n")
def generateFiltered(self, os, filter):
# Header
os.write(self.getBeginDelimiter())
os.write("\r\n")
# Write each property
self.writePropertiesFiltered(os, filter)
# Write each embedded component based on specific order
self.writeComponentsFiltered(os, filter)
# Footer
os.write(self.getEndDelimiter())
os.write("\r\n")
def writeXML(self, node, namespace):
# Component element
comp = XML.SubElement(node, xmldefs.makeTag(namespace, self.getType()))
# Each property
self.writePropertiesXML(comp, namespace)
# Each component
self.writeComponentsXML(comp, namespace)
def writeXMLFiltered(self, node, namespace, filter):
# Component element
comp = XML.SubElement(node, xmldefs.makeTag(namespace, self.getType()))
# Each property
self.writePropertiesFilteredXML(comp, namespace, filter)
# Each component
self.writeComponentsFilteredXML(comp, namespace, filter)
def sortedComponents(self):
components = self.mComponents[:]
sortedcomponents = []
# Write each component based on specific order
orderedNames = self.sortedComponentNames()
for name in orderedNames:
# Group by name then sort by map key (UID/R-ID)
namedcomponents = []
for component in tuple(components):
if component.getType().upper() == name:
namedcomponents.append(component)
components.remove(component)
for component in sorted(namedcomponents, key=lambda x: x.getSortKey()):
sortedcomponents.append(component)
# Write out the remainder sorted by name, sortKey
if self.sortSubComponents:
remainder = sorted(components, key=lambda x: (x.getType().upper(), x.getSortKey(),))
else:
remainder = components
for component in remainder:
sortedcomponents.append(component)
return sortedcomponents
def writeComponents(self, os):
# Write out the remainder
for component in self.sortedComponents():
component.generate(os)
def writeComponentsFiltered(self, os, filter):
# Shortcut for all sub-components
if filter.isAllSubComponents():
self.writeComponents(os)
elif filter.hasSubComponentFilters():
for subcomp in self.sortedcomponents():
subfilter = filter.getSubComponentFilter(subcomp.getType())
if subfilter is not None:
subcomp.generateFiltered(os, subfilter)
def writeComponentsXML(self, node, namespace):
if self.mComponents:
comps = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.components))
# Write out the remainder
for component in self.sortedComponents():
component.writeXML(comps, namespace)
def writeComponentsFilteredXML(self, node, namespace, filter):
if self.mComponents:
comps = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.components))
# Shortcut for all sub-components
if filter.isAllSubComponents():
self.writeXML(comps, namespace)
elif filter.hasSubComponentFilters():
for subcomp in self.sortedcomponents():
subfilter = filter.getSubComponentFilter(subcomp.getType())
if subfilter is not None:
subcomp.writeFilteredXML(comps, namespace, subfilter)
def loadValue(self, value_name):
if self.hasProperty(value_name):
return self.findFirstProperty(value_name)
return None
def loadValueInteger(self, value_name, type=None):
if type:
if self.hasProperty(value_name):
if type == PyCalendarValue.VALUETYPE_INTEGER:
ivalue = self.findFirstProperty(value_name).getIntegerValue()
if ivalue is not None:
return ivalue.getValue()
elif type == PyCalendarValue.VALUETYPE_UTC_OFFSET:
uvalue = self.findFirstProperty(value_name).getUTCOffsetValue()
if (uvalue is not None):
return uvalue.getValue()
return None
else:
return self.loadValueInteger(value_name, PyCalendarValue.VALUETYPE_INTEGER)
def loadValueString(self, value_name):
if self.hasProperty(value_name):
tvalue = self.findFirstProperty(value_name).getTextValue()
if (tvalue is not None):
return tvalue.getValue()
return None
def loadValueDateTime(self, value_name):
if self.hasProperty(value_name):
dtvalue = self.findFirstProperty(value_name).getDateTimeValue()
if dtvalue is not None:
return dtvalue.getValue()
return None
def loadValueDuration(self, value_name):
if self.hasProperty(value_name):
dvalue = self.findFirstProperty(value_name).getDurationValue()
if (dvalue is not None):
return dvalue.getValue()
return None
def loadValuePeriod(self, value_name):
if self.hasProperty(value_name):
pvalue = self.findFirstProperty(value_name).getPeriodValue()
if (pvalue is not None):
return pvalue.getValue()
return None
def loadValueRRULE(self, value_name, value, add):
# Get RRULEs
if self.hasProperty(value_name):
items = self.getProperties()[value_name]
for iter in items:
rvalue = iter.getRecurrenceValue()
if (rvalue is not None):
if add:
value.addRule(rvalue.getValue())
else:
value.subtractRule(rvalue.getValue())
return True
else:
return False
def loadValueRDATE(self, value_name, value, add):
# Get RDATEs
if self.hasProperty(value_name):
for iter in self.getProperties(value_name):
mvalue = iter.getMultiValue()
if (mvalue is not None):
for obj in mvalue.getValues():
# cast to date-time
if isinstance(obj, PyCalendarDateTimeValue):
if add:
value.addDT(obj.getValue())
else:
value.subtractDT(obj.getValue())
elif isinstance(obj, PyCalendarPeriodValue):
if add:
value.addPeriod(obj.getValue().getStart())
else:
value.subtractPeriod(obj.getValue().getStart())
return True
else:
return False
def sortedPropertyKeys(self):
keys = self.mProperties.keys()
keys.sort()
results = []
for skey in self.sortedPropertyKeyOrder():
if skey in keys:
results.append(skey)
keys.remove(skey)
results.extend(keys)
return results
def sortedPropertyKeyOrder(self):
return ()
def writeProperties(self, os):
# Sort properties by name
keys = self.sortedPropertyKeys()
for key in keys:
props = self.mProperties[key]
for prop in props:
prop.generate(os)
def writePropertiesFiltered(self, os, filter):
# Sort properties by name
keys = self.sortedPropertyKeys()
# Shortcut for all properties
if filter.isAllProperties():
for key in keys:
for prop in self.getProperties(key):
prop.generate(os)
elif filter.hasPropertyFilters():
for key in keys:
for prop in self.getProperties(key):
prop.generateFiltered(os, filter)
def writePropertiesXML(self, node, namespace):
properties = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.properties))
# Sort properties by name
keys = self.sortedPropertyKeys()
for key in keys:
props = self.mProperties[key]
for prop in props:
prop.writeXML(properties, namespace)
def writePropertiesFilteredXML(self, node, namespace, filter):
props = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.properties))
# Sort properties by name
keys = self.sortedPropertyKeys()
# Shortcut for all properties
if filter.isAllProperties():
for key in keys:
for prop in self.getProperties(key):
prop.writeXML(props, namespace)
elif filter.hasPropertyFilters():
for key in keys:
for prop in self.getProperties(key):
prop.writeFilteredXML(props, namespace, filter)
def loadPrivateValue(self, value_name):
# Read it in from properties list and then delete the property from the
# main list
result = self.loadValueString(value_name)
if (result is not None):
self.removeProperties(value_name)
return result
def writePrivateProperty(self, os, key, value):
prop = PyCalendarProperty(name=key, value=value)
prop.generate(os)
def editProperty(self, propname, propvalue):
# Remove existing items
self.removeProperties(propname)
# Now create properties
if propvalue:
self.addProperty(PyCalendarProperty(name=propname, value=propvalue))
pycalendar-2.0~svn13177/src/pycalendar/vavailability.py 0000644 0001750 0001750 00000007154 12101017573 022164 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar import itipdefinitions
from pycalendar.component import PyCalendarComponent
from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
class PyCalendarVAvailability(PyCalendarComponent):
propertyCardinality_1 = (
definitions.cICalProperty_DTSTAMP,
definitions.cICalProperty_UID,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_BUSYTYPE,
definitions.cICalProperty_CLASS,
definitions.cICalProperty_CREATED,
definitions.cICalProperty_DESCRIPTION,
definitions.cICalProperty_DTSTART,
definitions.cICalProperty_LAST_MODIFIED,
definitions.cICalProperty_ORGANIZER,
definitions.cICalProperty_SEQUENCE,
definitions.cICalProperty_SUMMARY,
definitions.cICalProperty_URL,
definitions.cICalProperty_RECURRENCE_ID,
definitions.cICalProperty_DTEND,
definitions.cICalProperty_DURATION,
)
propertyValueChecks = ICALENDAR_VALUE_CHECKS
def __init__(self, parent=None):
super(PyCalendarVAvailability, self).__init__(parent=parent)
def duplicate(self, parent=None):
return super(PyCalendarVAvailability, self).duplicate(parent=parent)
def getType(self):
return definitions.cICalComponent_VAVAILABILITY
def getMimeComponentName(self):
return itipdefinitions.cICalMIMEComponent_VAVAILABILITY
def finalise(self):
super(PyCalendarVAvailability, self).finalise()
def validate(self, doFix=False):
"""
Validate the data in this component and optionally fix any problems, else raise. If
loggedProblems is not None it must be a C{list} and problem descriptions are appended
to that.
"""
fixed, unfixed = super(PyCalendarVAvailability, self).validate(doFix)
# Extra constraint: only one of DTEND or DURATION
if self.hasProperty(definitions.cICalProperty_DTEND) and self.hasProperty(definitions.cICalProperty_DURATION):
# Fix by removing the DTEND
logProblem = "[%s] Properties must not both be present: %s, %s" % (
self.getType(),
definitions.cICalProperty_DTEND,
definitions.cICalProperty_DURATION,
)
if doFix:
self.removeProperties(definitions.cICalProperty_DTEND)
fixed.append(logProblem)
else:
unfixed.append(logProblem)
return fixed, unfixed
def addComponent(self, comp):
# We can embed the available components only
if comp.getType() == definitions.cICalComponent_AVAILABLE:
super(PyCalendarVAvailability, self).addComponent(comp)
else:
raise ValueError
def sortedPropertyKeyOrder(self):
return (
definitions.cICalProperty_UID,
definitions.cICalProperty_DTSTART,
definitions.cICalProperty_DURATION,
definitions.cICalProperty_DTEND,
)
pycalendar-2.0~svn13177/src/pycalendar/property.py 0000644 0001750 0001750 00000067337 12101017573 021221 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions, xmldefs
from pycalendar import stringutils
from pycalendar.attribute import PyCalendarAttribute
from pycalendar.binaryvalue import PyCalendarBinaryValue
from pycalendar.caladdressvalue import PyCalendarCalAddressValue
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.datetimevalue import PyCalendarDateTimeValue
from pycalendar.duration import PyCalendarDuration
from pycalendar.durationvalue import PyCalendarDurationValue
from pycalendar.exceptions import PyCalendarInvalidProperty
from pycalendar.integervalue import PyCalendarIntegerValue
from pycalendar.multivalue import PyCalendarMultiValue
from pycalendar.period import PyCalendarPeriod
from pycalendar.periodvalue import PyCalendarPeriodValue
from pycalendar.plaintextvalue import PyCalendarPlainTextValue
from pycalendar.recurrence import PyCalendarRecurrence
from pycalendar.recurrencevalue import PyCalendarRecurrenceValue
from pycalendar.requeststatusvalue import PyCalendarRequestStatusValue
from pycalendar.unknownvalue import PyCalendarUnknownValue
from pycalendar.urivalue import PyCalendarURIValue
from pycalendar.utcoffsetvalue import PyCalendarUTCOffsetValue
from pycalendar.utils import decodeParameterValue
from pycalendar.value import PyCalendarValue
import cStringIO as StringIO
import xml.etree.cElementTree as XML
class PyCalendarProperty(object):
sDefaultValueTypeMap = {
# 5545 Section 3.7
definitions.cICalProperty_CALSCALE : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_METHOD : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_PRODID : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_VERSION : PyCalendarValue.VALUETYPE_TEXT,
# 5545 Section 3.8.1
definitions.cICalProperty_ATTACH : PyCalendarValue.VALUETYPE_URI,
definitions.cICalProperty_CATEGORIES : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_CLASS : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_COMMENT : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_DESCRIPTION : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_GEO : PyCalendarValue.VALUETYPE_GEO,
definitions.cICalProperty_LOCATION : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_PERCENT_COMPLETE : PyCalendarValue.VALUETYPE_INTEGER,
definitions.cICalProperty_PRIORITY : PyCalendarValue.VALUETYPE_INTEGER,
definitions.cICalProperty_RESOURCES : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_STATUS : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_SUMMARY : PyCalendarValue.VALUETYPE_TEXT,
# 5545 Section 3.8.2
definitions.cICalProperty_COMPLETED : PyCalendarValue.VALUETYPE_DATETIME,
definitions.cICalProperty_DTEND : PyCalendarValue.VALUETYPE_DATETIME,
definitions.cICalProperty_DUE : PyCalendarValue.VALUETYPE_DATETIME,
definitions.cICalProperty_DTSTART : PyCalendarValue.VALUETYPE_DATETIME,
definitions.cICalProperty_DURATION : PyCalendarValue.VALUETYPE_DURATION,
definitions.cICalProperty_FREEBUSY : PyCalendarValue.VALUETYPE_PERIOD,
definitions.cICalProperty_TRANSP : PyCalendarValue.VALUETYPE_TEXT,
# 5545 Section 3.8.3
definitions.cICalProperty_TZID : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_TZNAME : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_TZOFFSETFROM : PyCalendarValue.VALUETYPE_UTC_OFFSET,
definitions.cICalProperty_TZOFFSETTO : PyCalendarValue.VALUETYPE_UTC_OFFSET,
definitions.cICalProperty_TZURL : PyCalendarValue.VALUETYPE_URI,
# 5545 Section 3.8.4
definitions.cICalProperty_ATTENDEE : PyCalendarValue.VALUETYPE_CALADDRESS,
definitions.cICalProperty_CONTACT : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_ORGANIZER : PyCalendarValue.VALUETYPE_CALADDRESS,
definitions.cICalProperty_RECURRENCE_ID : PyCalendarValue.VALUETYPE_DATETIME,
definitions.cICalProperty_RELATED_TO : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_URL : PyCalendarValue.VALUETYPE_URI,
definitions.cICalProperty_UID : PyCalendarValue.VALUETYPE_TEXT,
# 5545 Section 3.8.5
definitions.cICalProperty_EXDATE : PyCalendarValue.VALUETYPE_DATETIME,
definitions.cICalProperty_EXRULE : PyCalendarValue.VALUETYPE_RECUR, # 2445 only
definitions.cICalProperty_RDATE : PyCalendarValue.VALUETYPE_DATETIME,
definitions.cICalProperty_RRULE : PyCalendarValue.VALUETYPE_RECUR,
# 5545 Section 3.8.6
definitions.cICalProperty_ACTION : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_REPEAT : PyCalendarValue.VALUETYPE_INTEGER,
definitions.cICalProperty_TRIGGER : PyCalendarValue.VALUETYPE_DURATION,
# 5545 Section 3.8.7
definitions.cICalProperty_CREATED : PyCalendarValue.VALUETYPE_DATETIME,
definitions.cICalProperty_DTSTAMP : PyCalendarValue.VALUETYPE_DATETIME,
definitions.cICalProperty_LAST_MODIFIED : PyCalendarValue.VALUETYPE_DATETIME,
definitions.cICalProperty_SEQUENCE : PyCalendarValue.VALUETYPE_INTEGER,
# 5545 Section 3.8.8
definitions.cICalProperty_REQUEST_STATUS : PyCalendarValue.VALUETYPE_REQUEST_STATUS,
# Extensions: draft-daboo-valarm-extensions-03
definitions.cICalProperty_ACKNOWLEDGED : PyCalendarValue.VALUETYPE_DATETIME,
# Apple Extensions
definitions.cICalProperty_XWRCALNAME : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_XWRCALDESC : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_XWRALARMUID : PyCalendarValue.VALUETYPE_TEXT,
# Mulberry extensions
definitions.cICalProperty_ACTION_X_SPEAKTEXT : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalProperty_ALARM_X_LASTTRIGGER : PyCalendarValue.VALUETYPE_DATETIME,
definitions.cICalProperty_ALARM_X_ALARMSTATUS : PyCalendarValue.VALUETYPE_TEXT,
}
sValueTypeMap = {
# 5545 Section 3.3
definitions.cICalValue_BINARY : PyCalendarValue.VALUETYPE_BINARY,
definitions.cICalValue_BOOLEAN : PyCalendarValue.VALUETYPE_BOOLEAN,
definitions.cICalValue_CAL_ADDRESS : PyCalendarValue.VALUETYPE_CALADDRESS,
definitions.cICalValue_DATE : PyCalendarValue.VALUETYPE_DATE,
definitions.cICalValue_DATE_TIME : PyCalendarValue.VALUETYPE_DATETIME,
definitions.cICalValue_DURATION : PyCalendarValue.VALUETYPE_DURATION,
definitions.cICalValue_FLOAT : PyCalendarValue.VALUETYPE_FLOAT,
definitions.cICalValue_INTEGER : PyCalendarValue.VALUETYPE_INTEGER,
definitions.cICalValue_PERIOD : PyCalendarValue.VALUETYPE_PERIOD,
definitions.cICalValue_RECUR : PyCalendarValue.VALUETYPE_RECUR,
definitions.cICalValue_TEXT : PyCalendarValue.VALUETYPE_TEXT,
definitions.cICalValue_TIME : PyCalendarValue.VALUETYPE_TIME,
definitions.cICalValue_URI : PyCalendarValue.VALUETYPE_URI,
definitions.cICalValue_UTC_OFFSET : PyCalendarValue.VALUETYPE_UTC_OFFSET,
}
sTypeValueMap = {
# 5545 Section 3.3
PyCalendarValue.VALUETYPE_BINARY : definitions.cICalValue_BINARY,
PyCalendarValue.VALUETYPE_BOOLEAN : definitions.cICalValue_BOOLEAN,
PyCalendarValue.VALUETYPE_CALADDRESS : definitions.cICalValue_CAL_ADDRESS,
PyCalendarValue.VALUETYPE_DATE : definitions.cICalValue_DATE,
PyCalendarValue.VALUETYPE_DATETIME : definitions.cICalValue_DATE_TIME,
PyCalendarValue.VALUETYPE_DURATION : definitions.cICalValue_DURATION,
PyCalendarValue.VALUETYPE_FLOAT : definitions.cICalValue_FLOAT,
PyCalendarValue.VALUETYPE_GEO : definitions.cICalValue_FLOAT,
PyCalendarValue.VALUETYPE_INTEGER : definitions.cICalValue_INTEGER,
PyCalendarValue.VALUETYPE_PERIOD : definitions.cICalValue_PERIOD,
PyCalendarValue.VALUETYPE_RECUR : definitions.cICalValue_RECUR,
PyCalendarValue.VALUETYPE_TEXT : definitions.cICalValue_TEXT,
PyCalendarValue.VALUETYPE_REQUEST_STATUS : definitions.cICalValue_TEXT,
PyCalendarValue.VALUETYPE_TIME : definitions.cICalValue_TIME,
PyCalendarValue.VALUETYPE_URI : definitions.cICalValue_URI,
PyCalendarValue.VALUETYPE_UTC_OFFSET : definitions.cICalValue_UTC_OFFSET,
}
sMultiValues = set((
definitions.cICalProperty_CATEGORIES,
definitions.cICalProperty_RESOURCES,
definitions.cICalProperty_FREEBUSY,
definitions.cICalProperty_EXDATE,
definitions.cICalProperty_RDATE,
))
@staticmethod
def registerDefaultValue(propname, valuetype):
if propname not in PyCalendarProperty.sDefaultValueTypeMap:
PyCalendarProperty.sDefaultValueTypeMap[propname] = valuetype
def __init__(self, name=None, value=None, valuetype=None):
self._init_PyCalendarProperty()
self.mName = name if name is not None else ""
# The None check speeds up .duplicate()
if value is None:
pass
# Order these based on most likely occurrence to speed up this method
elif isinstance(value, str):
self._init_attr_value_text(value, valuetype if valuetype else PyCalendarProperty.sDefaultValueTypeMap.get(self.mName.upper(), PyCalendarValue.VALUETYPE_UNKNOWN))
elif isinstance(value, PyCalendarDateTime):
self._init_attr_value_datetime(value)
elif isinstance(value, PyCalendarDuration):
self._init_attr_value_duration(value)
elif isinstance(value, PyCalendarRecurrence):
self._init_attr_value_recur(value)
elif isinstance(value, PyCalendarPeriod):
self._init_attr_value_period(value)
elif isinstance(value, int):
self._init_attr_value_int(value)
elif isinstance(value, list):
if name.upper() == definitions.cICalProperty_REQUEST_STATUS:
self._init_attr_value_requeststatus(value)
else:
period_list = False
if len(value) != 0:
period_list = isinstance(value[0], PyCalendarPeriod)
if period_list:
self._init_attr_value_periodlist(value)
else:
self._init_attr_value_datetimelist(value)
elif isinstance(value, PyCalendarUTCOffsetValue):
self._init_attr_value_utcoffset(value)
def duplicate(self):
other = PyCalendarProperty(self.mName)
for attrname, attrs in self.mAttributes.items():
other.mAttributes[attrname] = [i.duplicate() for i in attrs]
other.mValue = self.mValue.duplicate()
return other
def __hash__(self):
return hash((
self.mName,
tuple([tuple(self.mAttributes[attrname]) for attrname in sorted(self.mAttributes.keys())]),
self.mValue,
))
def __ne__(self, other):
return not self.__eq__(other)
def __eq__(self, other):
if not isinstance(other, PyCalendarProperty):
return False
return self.mName == other.mName and self.mValue == other.mValue and self.mAttributes == other.mAttributes
def __repr__(self):
return "PyCalendarProperty: %s" % (self.getText(),)
def __str__(self):
return self.getText()
def getName(self):
return self.mName
def setName(self, name):
self.mName = name
def getAttributes(self):
return self.mAttributes
def setAttributes(self, attributes):
self.mAttributes = dict([(k.upper(), v) for k, v in attributes.iteritems()])
def hasAttribute(self, attr):
return attr.upper() in self.mAttributes
def getAttributeValue(self, attr):
return self.mAttributes[attr.upper()][0].getFirstValue()
def addAttribute(self, attr):
self.mAttributes.setdefault(attr.getName().upper(), []).append(attr)
def replaceAttribute(self, attr):
self.mAttributes[attr.getName().upper()] = [attr]
def removeAttributes(self, attr):
if attr.upper() in self.mAttributes:
del self.mAttributes[attr.upper()]
def getValue(self):
return self.mValue
def getBinaryValue(self):
if isinstance(self.mValue, PyCalendarBinaryValue):
return self.mValue
else:
return None
def getCalAddressValue(self):
if isinstance(self.mValue, PyCalendarCalAddressValue):
return self.mValue
else:
return None
def getDateTimeValue(self):
if isinstance(self.mValue, PyCalendarDateTimeValue):
return self.mValue
else:
return None
def getDurationValue(self):
if isinstance(self.mValue, PyCalendarDurationValue):
return self.mValue
else:
return None
def getIntegerValue(self):
if isinstance(self.mValue, PyCalendarIntegerValue):
return self.mValue
else:
return None
def getMultiValue(self):
if isinstance(self.mValue, PyCalendarMultiValue):
return self.mValue
else:
return None
def getPeriodValue(self):
if isinstance(self.mValue, PyCalendarPeriodValue):
return self.mValue
else:
return None
def getRecurrenceValue(self):
if isinstance(self.mValue, PyCalendarRecurrenceValue):
return self.mValue
else:
return None
def getTextValue(self):
if isinstance(self.mValue, PyCalendarPlainTextValue):
return self.mValue
else:
return None
def getURIValue(self):
if isinstance(self.mValue, PyCalendarURIValue):
return self.mValue
else:
return None
def getUTCOffsetValue(self):
if isinstance(self.mValue, PyCalendarUTCOffsetValue):
return self.mValue
else:
return None
def parse(self, data):
# Look for attribute or value delimiter
prop_name, txt = stringutils.strduptokenstr(data, ";:")
if not prop_name:
raise PyCalendarInvalidProperty("Invalid property", data)
# We have the name
self.mName = prop_name
# TODO: Try to use static string for the name
# Now loop getting data
try:
while txt:
if txt[0] == ';':
# Parse attribute
# Move past delimiter
txt = txt[1:]
# Get quoted string or token
attribute_name, txt = stringutils.strduptokenstr(txt, "=")
if attribute_name is None:
raise PyCalendarInvalidProperty("Invalid property", data)
txt = txt[1:]
attribute_value, txt = stringutils.strduptokenstr(txt, ":;,")
if attribute_value is None:
raise PyCalendarInvalidProperty("Invalid property", data)
# Now add attribute value (decode ^-escpaing)
attrvalue = PyCalendarAttribute(name=attribute_name, value=decodeParameterValue(attribute_value))
self.mAttributes.setdefault(attribute_name.upper(), []).append(attrvalue)
# Look for additional values
while txt[0] == ',':
txt = txt[1:]
attribute_value2, txt = stringutils.strduptokenstr(txt, ":;,")
if attribute_value2 is None:
raise PyCalendarInvalidProperty("Invalid property", data)
attrvalue.addValue(decodeParameterValue(attribute_value2))
elif txt[0] == ':':
txt = txt[1:]
self.createValue(txt)
txt = None
else:
# We should never get here but if we do we need to terminate the loop
raise PyCalendarInvalidProperty("Invalid property", data)
except IndexError:
raise PyCalendarInvalidProperty("Invalid property", data)
# We must have a value of some kind
if self.mValue is None:
raise PyCalendarInvalidProperty("Invalid property", data)
return True
def getText(self):
os = StringIO.StringIO()
self.generate(os)
return os.getvalue()
def generate(self, os):
# Write it out always with value
self.generateValue(os, False)
def generateFiltered(self, os, filter):
# Check for property in filter and whether value is written out
test, novalue = filter.testPropertyValue(self.mName.upper())
if test:
self.generateValue(os, novalue)
# Write out the actual property, possibly skipping the value
def generateValue(self, os, novalue):
self.setupValueAttribute()
# Must write to temp buffer and then wrap
sout = StringIO.StringIO()
sout.write(self.mName)
# Write all attributes
for key in sorted(self.mAttributes.keys()):
for attr in self.mAttributes[key]:
sout.write(";")
attr.generate(sout)
# Write value
sout.write(":")
if self.mValue and not novalue:
self.mValue.generate(sout)
# Get string text
temp = sout.getvalue()
sout.close()
# Look for line length exceed
if len(temp) < 75:
os.write(temp)
else:
# Look for valid utf8 range and write that out
start = 0
written = 0
while written < len(temp):
# Start 74 chars on from where we are
offset = start + 74
if offset >= len(temp):
line = temp[start:]
os.write(line)
written = len(temp)
else:
# Check whether next char is valid utf8 lead byte
while (temp[offset] > 0x7F) and ((ord(temp[offset]) & 0xC0) == 0x80):
# Step back until we have a valid char
offset -= 1
line = temp[start:offset]
os.write(line)
os.write("\r\n ")
written += offset - start
start = offset
os.write("\r\n")
def writeXML(self, node, namespace):
# Write it out always with value
self.generateValueXML(node, namespace, False)
def generateFilteredXML(self, node, namespace, filter):
# Check for property in filter and whether value is written out
test, novalue = filter.testPropertyValue(self.mName.upper())
if test:
self.generateValueXML(node, namespace, novalue)
# Write out the actual property, possibly skipping the value
def generateValueXML(self, node, namespace, novalue):
prop = XML.SubElement(node, xmldefs.makeTag(namespace, self.getName()))
# Write all attributes
if len(self.mAttributes):
params = XML.SubElement(prop, xmldefs.makeTag(namespace, xmldefs.parameters))
for key in sorted(self.mAttributes.keys()):
for attr in self.mAttributes[key]:
if attr.getName() != "VALUE":
attr.writeXML(params, namespace)
# Write value
if self.mValue and not novalue:
self.mValue.writeXML(prop, namespace)
def _init_PyCalendarProperty(self):
self.mName = ""
self.mAttributes = {}
self.mValue = None
def createValue(self, data):
# Tidy first
self.mValue = None
# Get value type from property name
type = PyCalendarProperty.sDefaultValueTypeMap.get(self.mName.upper(), PyCalendarValue.VALUETYPE_UNKNOWN)
# Check whether custom value is set
if definitions.cICalAttribute_VALUE in self.mAttributes:
type = PyCalendarProperty.sValueTypeMap.get(self.getAttributeValue(definitions.cICalAttribute_VALUE), type)
# Check for multivalued
if self.mName.upper() in PyCalendarProperty.sMultiValues:
self.mValue = PyCalendarMultiValue(type)
else:
# Create the type
self.mValue = PyCalendarValue.createFromType(type)
# Now parse the data
try:
self.mValue.parse(data)
except ValueError:
raise PyCalendarInvalidProperty("Invalid property value", data)
# Special post-create for some types
if type in (PyCalendarValue.VALUETYPE_TIME, PyCalendarValue.VALUETYPE_DATETIME):
# Look for TZID attribute
tzid = None
if (self.hasAttribute(definitions.cICalAttribute_TZID)):
tzid = self.getAttributeValue(definitions.cICalAttribute_TZID)
if isinstance(self.mValue, PyCalendarDateTimeValue):
self.mValue.getValue().setTimezoneID(tzid)
elif isinstance(self.mValue, PyCalendarMultiValue):
for item in self.mValue.getValues():
if isinstance(item, PyCalendarDateTimeValue):
item.getValue().setTimezoneID(tzid)
def setValue(self, value):
# Tidy first
self.mValue = None
# Get value type from property name
type = PyCalendarProperty.sDefaultValueTypeMap.get(self.mName.upper(), PyCalendarValue.VALUETYPE_TEXT)
# Check whether custom value is set
if definitions.cICalAttribute_VALUE in self.mAttributes:
type = PyCalendarProperty.sValueTypeMap.get(self.getAttributeValue(definitions.cICalAttribute_VALUE), type)
# Check for multivalued
if self.mName.upper() in PyCalendarProperty.sMultiValues:
self.mValue = PyCalendarMultiValue(type)
else:
# Create the type
self.mValue = PyCalendarValue.createFromType(type)
self.mValue.setValue(value)
# Special post-create for some types
if type in (PyCalendarValue.VALUETYPE_TIME, PyCalendarValue.VALUETYPE_DATETIME):
# Look for TZID attribute
tzid = None
if (self.hasAttribute(definitions.cICalAttribute_TZID)):
tzid = self.getAttributeValue(definitions.cICalAttribute_TZID)
if isinstance(self.mValue, PyCalendarDateTimeValue):
self.mValue.getValue().setTimezoneID(tzid)
elif isinstance(self.mValue, PyCalendarMultiValue):
for item in self.mValue.getValues():
if isinstance(item, PyCalendarDateTimeValue):
item.getValue().setTimezoneID(tzid)
def setupValueAttribute(self):
if definitions.cICalAttribute_VALUE in self.mAttributes:
del self.mAttributes[definitions.cICalAttribute_VALUE]
# Only if we have a value right now
if self.mValue is None:
return
# See if current type is default for this property. If there is no mapping available,
# then always add VALUE if it is not TEXT.
default_type = self.sDefaultValueTypeMap.get(self.mName.upper())
actual_type = self.mValue.getType()
if default_type is None or default_type != actual_type:
actual_value = self.sTypeValueMap.get(actual_type)
if actual_value is not None and (default_type is not None or actual_type != PyCalendarValue.VALUETYPE_TEXT):
self.mAttributes.setdefault(definitions.cICalAttribute_VALUE, []).append(PyCalendarAttribute(name=definitions.cICalAttribute_VALUE, value=actual_value))
# Creation
def _init_attr_value_int(self, ival):
# Value
self.mValue = PyCalendarIntegerValue(value=ival)
# Attributes
self.setupValueAttribute()
def _init_attr_value_text(self, txt, value_type):
# Value
self.mValue = PyCalendarValue.createFromType(value_type)
if isinstance(self.mValue, PyCalendarPlainTextValue) or isinstance(self.mValue, PyCalendarUnknownValue):
self.mValue.setValue(txt)
# Attributes
self.setupValueAttribute()
def _init_attr_value_requeststatus(self, reqstatus):
# Value
self.mValue = PyCalendarRequestStatusValue(reqstatus)
# Attributes
self.setupValueAttribute()
def _init_attr_value_datetime(self, dt):
# Value
self.mValue = PyCalendarDateTimeValue(value=dt)
# Attributes
self.setupValueAttribute()
# Look for timezone
if not dt.isDateOnly() and dt.local():
if definitions.cICalAttribute_TZID in self.mAttributes:
del self.mAttributes[definitions.cICalAttribute_TZID]
self.mAttributes.setdefault(definitions.cICalAttribute_TZID, []).append(
PyCalendarAttribute(name=definitions.cICalAttribute_TZID, value=dt.getTimezoneID()))
def _init_attr_value_datetimelist(self, dtl):
# Value
date_only = (len(dtl) > 0) and dtl[0].isDateOnly()
if date_only:
self.mValue = PyCalendarMultiValue(PyCalendarValue.VALUETYPE_DATE)
else:
self.mValue = PyCalendarMultiValue(PyCalendarValue.VALUETYPE_DATETIME)
for dt in dtl:
self.mValue.addValue(PyCalendarDateTimeValue(dt))
# Attributes
self.setupValueAttribute()
# Look for timezone
if ((len(dtl) > 0)
and not dtl[0].isDateOnly()
and dtl[0].local()):
if definitions.cICalAttribute_TZID in self.mAttributes:
del self.mAttributes[definitions.cICalAttribute_TZID]
self.mAttributes.setdefault(definitions.cICalAttribute_TZID, []).append(
PyCalendarAttribute(name=definitions.cICalAttribute_TZID, value=dtl[0].getTimezoneID()))
def _init_attr_value_periodlist(self, periodlist):
# Value
self.mValue = PyCalendarMultiValue(PyCalendarValue.VALUETYPE_PERIOD)
for period in periodlist:
self.mValue.addValue(PyCalendarPeriodValue(period))
# Attributes
self.setupValueAttribute()
def _init_attr_value_duration(self, du):
# Value
self.mValue = PyCalendarDurationValue(value=du)
# Attributes
self.setupValueAttribute()
def _init_attr_value_period(self, pe):
# Value
self.mValue = PyCalendarPeriodValue(value=pe)
# Attributes
self.setupValueAttribute()
def _init_attr_value_recur(self, recur):
# Value
self.mValue = PyCalendarRecurrenceValue(value=recur)
# Attributes
self.setupValueAttribute()
def _init_attr_value_utcoffset(self, utcoffset):
# Value
self.mValue = PyCalendarUTCOffsetValue()
self.mValue.setValue(utcoffset.getValue())
# Attributes
self.setupValueAttribute()
pycalendar-2.0~svn13177/src/pycalendar/vcard/ 0000755 0001750 0001750 00000000000 12322631215 020042 5 ustar rahul rahul pycalendar-2.0~svn13177/src/pycalendar/vcard/validation.py 0000644 0001750 0001750 00000001655 12101017573 022555 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.validation import partial, PropertyValueChecks
from pycalendar.vcard import definitions
VCARD_VALUE_CHECKS = {
definitions.Property_VERSION: partial(PropertyValueChecks.stringValue, "3.0"),
definitions.Property_PROFILE: partial(PropertyValueChecks.stringValue, "VCARD"),
}
pycalendar-2.0~svn13177/src/pycalendar/vcard/property.py 0000644 0001750 0001750 00000051270 12101017573 022305 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import stringutils
from pycalendar.adr import Adr
from pycalendar.adrvalue import AdrValue
from pycalendar.attribute import PyCalendarAttribute
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.datetimevalue import PyCalendarDateTimeValue
from pycalendar.exceptions import PyCalendarInvalidProperty
from pycalendar.integervalue import PyCalendarIntegerValue
from pycalendar.multivalue import PyCalendarMultiValue
from pycalendar.n import N
from pycalendar.nvalue import NValue
from pycalendar.orgvalue import OrgValue
from pycalendar.parser import ParserContext
from pycalendar.plaintextvalue import PyCalendarPlainTextValue
from pycalendar.unknownvalue import PyCalendarUnknownValue
from pycalendar.utcoffsetvalue import PyCalendarUTCOffsetValue
from pycalendar.utils import decodeParameterValue
from pycalendar.value import PyCalendarValue
from pycalendar.vcard import definitions
import cStringIO as StringIO
handleOptions = ("allow", "ignore", "fix", "raise")
missingParameterValues = "fix"
class Property(object):
sDefaultValueTypeMap = {
# 2425 Properties
definitions.Property_SOURCE : PyCalendarValue.VALUETYPE_URI,
definitions.Property_NAME : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_PROFILE : PyCalendarValue.VALUETYPE_TEXT,
# 2426 vCard Properties
# 2426 Section 3.1
definitions.Property_FN : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_N : PyCalendarValue.VALUETYPE_N,
definitions.Property_NICKNAME : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_PHOTO : PyCalendarValue.VALUETYPE_BINARY,
definitions.Property_BDAY : PyCalendarValue.VALUETYPE_DATE,
# 2426 Section 3.2
definitions.Property_ADR : PyCalendarValue.VALUETYPE_ADR,
definitions.Property_LABEL : PyCalendarValue.VALUETYPE_TEXT,
# 2426 Section 3.3
definitions.Property_TEL : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_EMAIL : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_MAILER : PyCalendarValue.VALUETYPE_TEXT,
# 2426 Section 3.4
definitions.Property_TZ : PyCalendarValue.VALUETYPE_UTC_OFFSET,
definitions.Property_GEO : PyCalendarValue.VALUETYPE_GEO,
# 2426 Section 3.5
definitions.Property_TITLE : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_ROLE : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_LOGO : PyCalendarValue.VALUETYPE_BINARY,
definitions.Property_AGENT : PyCalendarValue.VALUETYPE_VCARD,
definitions.Property_ORG : PyCalendarValue.VALUETYPE_ORG,
# 2426 Section 3.6
definitions.Property_CATEGORIES : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_NOTE : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_PRODID : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_REV : PyCalendarValue.VALUETYPE_DATETIME,
definitions.Property_SORT_STRING : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_SOUND : PyCalendarValue.VALUETYPE_BINARY,
definitions.Property_UID : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_URL : PyCalendarValue.VALUETYPE_URI,
definitions.Property_VERSION : PyCalendarValue.VALUETYPE_TEXT,
# 2426 Section 3.7
definitions.Property_CLASS : PyCalendarValue.VALUETYPE_TEXT,
definitions.Property_KEY : PyCalendarValue.VALUETYPE_BINARY,
}
sValueTypeMap = {
definitions.Value_BINARY : PyCalendarValue.VALUETYPE_BINARY,
definitions.Value_BOOLEAN : PyCalendarValue.VALUETYPE_BOOLEAN,
definitions.Value_DATE : PyCalendarValue.VALUETYPE_DATE,
definitions.Value_DATE_TIME : PyCalendarValue.VALUETYPE_DATETIME,
definitions.Value_FLOAT : PyCalendarValue.VALUETYPE_FLOAT,
definitions.Value_INTEGER : PyCalendarValue.VALUETYPE_INTEGER,
definitions.Value_TEXT : PyCalendarValue.VALUETYPE_TEXT,
definitions.Value_TIME : PyCalendarValue.VALUETYPE_TIME,
definitions.Value_URI : PyCalendarValue.VALUETYPE_URI,
definitions.Value_UTCOFFSET : PyCalendarValue.VALUETYPE_UTC_OFFSET,
definitions.Value_VCARD : PyCalendarValue.VALUETYPE_VCARD,
}
sTypeValueMap = {
PyCalendarValue.VALUETYPE_ADR : definitions.Value_TEXT,
PyCalendarValue.VALUETYPE_BINARY : definitions.Value_BINARY,
PyCalendarValue.VALUETYPE_BOOLEAN : definitions.Value_BOOLEAN,
PyCalendarValue.VALUETYPE_DATE : definitions.Value_DATE,
PyCalendarValue.VALUETYPE_DATETIME : definitions.Value_DATE_TIME,
PyCalendarValue.VALUETYPE_FLOAT : definitions.Value_FLOAT,
PyCalendarValue.VALUETYPE_GEO : definitions.Value_FLOAT,
PyCalendarValue.VALUETYPE_INTEGER : definitions.Value_INTEGER,
PyCalendarValue.VALUETYPE_N : definitions.Value_TEXT,
PyCalendarValue.VALUETYPE_ORG : definitions.Value_TEXT,
PyCalendarValue.VALUETYPE_TEXT : definitions.Value_TEXT,
PyCalendarValue.VALUETYPE_TIME : definitions.Value_TIME,
PyCalendarValue.VALUETYPE_URI : definitions.Value_URI,
PyCalendarValue.VALUETYPE_UTC_OFFSET : definitions.Value_UTCOFFSET,
PyCalendarValue.VALUETYPE_VCARD : definitions.Value_VCARD,
}
sMultiValues = set((
definitions.Property_NICKNAME,
definitions.Property_CATEGORIES,
))
sTextVariants = set((
definitions.Property_ADR,
definitions.Property_N,
definitions.Property_ORG,
))
def __init__(self, group=None, name=None, value=None, valuetype=None):
self._init_PyCalendarProperty()
self.mGroup = group
self.mName = name if name is not None else ""
if isinstance(value, int):
self._init_attr_value_int(value)
elif isinstance(value, str):
self._init_attr_value_text(value, valuetype if valuetype else Property.sDefaultValueTypeMap.get(self.mName.upper(), PyCalendarValue.VALUETYPE_TEXT))
elif isinstance(value, PyCalendarDateTime):
self._init_attr_value_datetime(value)
elif isinstance(value, Adr):
self._init_attr_value_adr(value)
elif isinstance(value, N):
self._init_attr_value_n(value)
elif isinstance(value, list) or isinstance(value, tuple):
if name.upper() == definitions.Property_ORG:
self._init_attr_value_org(value)
elif name.upper() == definitions.Property_GEO:
self._init_attr_value_geo(value)
else:
# Assume everything else is a text list
self._init_attr_value_text_list(value)
elif isinstance(value, PyCalendarUTCOffsetValue):
self._init_attr_value_utcoffset(value)
def duplicate(self):
other = Property(self.mGroup, self.mName)
for attrname, attrs in self.mAttributes.items():
other.mAttributes[attrname] = [i.duplicate() for i in attrs]
other.mValue = self.mValue.duplicate()
return other
def __hash__(self):
return hash((
self.mName,
tuple([tuple(self.mAttributes[attrname]) for attrname in sorted(self.mAttributes.keys())]),
self.mValue,
))
def __ne__(self, other):
return not self.__eq__(other)
def __eq__(self, other):
if not isinstance(other, Property):
return False
return (
self.mGroup == self.mGroup and
self.mName == other.mName and
self.mValue == other.mValue and
self.mAttributes == other.mAttributes
)
def __repr__(self):
return "vCard Property: %s" % (self.getText(),)
def __str__(self):
return self.getText()
def getGroup(self):
return self.mGroup
def setGroup(self, group):
self.mGroup = group
def getName(self):
return self.mName
def setName(self, name):
self.mName = name
def getAttributes(self):
return self.mAttributes
def setAttributes(self, attributes):
self.mAttributes = dict([(k.upper(), v) for k, v in attributes.iteritems()])
def hasAttribute(self, attr):
return attr.upper() in self.mAttributes
def getAttributeValue(self, attr):
return self.mAttributes[attr.upper()][0].getFirstValue()
def addAttribute(self, attr):
self.mAttributes.setdefault(attr.getName().upper(), []).append(attr)
def replaceAttribute(self, attr):
self.mAttributes[attr.getName().upper()] = [attr]
def removeAttributes(self, attr):
if attr.upper() in self.mAttributes:
del self.mAttributes[attr.upper()]
def getValue(self):
return self.mValue
def parse(self, data):
# Look for attribute or value delimiter
prop_name, txt = stringutils.strduptokenstr(data, ";:")
if not prop_name:
raise PyCalendarInvalidProperty("Invalid property", data)
# Check for group prefix
splits = prop_name.split(".", 1)
if len(splits) == 2:
# We have both group and name
self.mGroup = splits[0]
self.mName = splits[1]
else:
# We have the name
self.mName = prop_name
# Now loop getting data
try:
stripValueSpaces = False # Fix for AB.app base PHOTO properties that use two spaces at start of line
while txt:
if txt[0] == ';':
# Parse attribute
# Move past delimiter
txt = txt[1:]
# Get quoted string or token - in iCalendar we only look for "=" here
# but for "broken" vCard BASE64 property we need to also terminate on
# ":;"
attribute_name, txt = stringutils.strduptokenstr(txt, "=:;")
if attribute_name is None:
raise PyCalendarInvalidProperty("Invalid property", data)
if txt[0] != "=":
# Deal with parameters without values
if ParserContext.VCARD_2_NO_PARAMETER_VALUES == ParserContext.PARSER_RAISE:
raise PyCalendarInvalidProperty("Invalid property parameter", data)
elif ParserContext.VCARD_2_NO_PARAMETER_VALUES == ParserContext.PARSER_ALLOW:
attribute_value = None
else: # PARSER_IGNORE and PARSER_FIX
attribute_name = None
if attribute_name.upper() == "BASE64" and ParserContext.VCARD_2_BASE64 == ParserContext.PARSER_FIX:
attribute_name = definitions.Parameter_ENCODING
attribute_value = definitions.Parameter_Value_ENCODING_B
stripValueSpaces = True
else:
txt = txt[1:]
attribute_value, txt = stringutils.strduptokenstr(txt, ":;,")
if attribute_value is None:
raise PyCalendarInvalidProperty("Invalid property", data)
# Now add attribute value (decode ^-escaping)
if attribute_name is not None:
attrvalue = PyCalendarAttribute(name=attribute_name, value=decodeParameterValue(attribute_value))
self.mAttributes.setdefault(attribute_name.upper(), []).append(attrvalue)
# Look for additional values
while txt[0] == ',':
txt = txt[1:]
attribute_value2, txt = stringutils.strduptokenstr(txt, ":;,")
if attribute_value2 is None:
raise PyCalendarInvalidProperty("Invalid property", data)
attrvalue.addValue(decodeParameterValue(attribute_value2))
elif txt[0] == ':':
txt = txt[1:]
if stripValueSpaces:
txt = txt.replace(" ", "")
self.createValue(txt)
txt = None
except IndexError:
raise PyCalendarInvalidProperty("Invalid property", data)
# We must have a value of some kind
if self.mValue is None:
raise PyCalendarInvalidProperty("Invalid property", data)
return True
def getText(self):
os = StringIO.StringIO()
self.generate(os)
return os.getvalue()
def generate(self, os):
# Write it out always with value
self.generateValue(os, False)
def generateFiltered(self, os, filter):
# Check for property in filter and whether value is written out
test, novalue = filter.testPropertyValue(self.mName.upper())
if test:
self.generateValue(os, novalue)
# Write out the actual property, possibly skipping the value
def generateValue(self, os, novalue):
self.setupValueAttribute()
# Must write to temp buffer and then wrap
sout = StringIO.StringIO()
if self.mGroup:
sout.write(self.mGroup + ".")
sout.write(self.mName)
# Write all attributes
for key in sorted(self.mAttributes.keys()):
for attr in self.mAttributes[key]:
sout.write(";")
attr.generate(sout)
# Write value
sout.write(":")
if self.mName.upper() == "PHOTO" and self.mValue.getType() == PyCalendarValue.VALUETYPE_BINARY:
# Handle AB.app PHOTO values
sout.write("\r\n")
value = self.mValue.getText()
value_len = len(value)
offset = 0
while(value_len > 72):
sout.write(" ")
sout.write(value[offset:offset + 72])
sout.write("\r\n")
value_len -= 72
offset += 72
sout.write(" ")
sout.write(value[offset:])
os.write(sout.getvalue())
else:
if self.mValue and not novalue:
self.mValue.generate(sout)
# Get string text
temp = sout.getvalue()
sout.close()
# Look for line length exceed
if len(temp) < 75:
os.write(temp)
else:
# Look for valid utf8 range and write that out
start = 0
written = 0
lineWrap = 74
while written < len(temp):
# Start 74 chars on from where we are
offset = start + lineWrap
if offset >= len(temp):
line = temp[start:]
os.write(line)
written = len(temp)
else:
# Check whether next char is valid utf8 lead byte
while (temp[offset] > 0x7F) and ((ord(temp[offset]) & 0xC0) == 0x80):
# Step back until we have a valid char
offset -= 1
line = temp[start:offset]
os.write(line)
os.write("\r\n ")
lineWrap = 73 # We are now adding a space at the start
written += offset - start
start = offset
os.write("\r\n")
def _init_PyCalendarProperty(self):
self.mGroup = None
self.mName = ""
self.mAttributes = {}
self.mValue = None
def createValue(self, data):
# Tidy first
self.mValue = None
# Get value type from property name
valueType = Property.sDefaultValueTypeMap.get(self.mName.upper(), PyCalendarValue.VALUETYPE_TEXT)
# Check whether custom value is set
if definitions.Parameter_VALUE in self.mAttributes:
attr = self.getAttributeValue(definitions.Parameter_VALUE)
if attr != definitions.Value_TEXT or self.mName.upper() not in Property.sTextVariants:
valueType = Property.sValueTypeMap.get(attr, valueType)
# Check for multivalued
if self.mName.upper() in Property.sMultiValues:
self.mValue = PyCalendarMultiValue(valueType)
else:
# Create the type
self.mValue = PyCalendarValue.createFromType(valueType)
# Now parse the data
try:
if valueType in (PyCalendarValue.VALUETYPE_DATE, PyCalendarValue.VALUETYPE_DATETIME):
# vCard supports a slightly different, expanded form, of date
self.mValue.parse(data, fullISO=True)
else:
self.mValue.parse(data)
except ValueError:
raise PyCalendarInvalidProperty("Invalid property value", data)
def setValue(self, value):
# Tidy first
self.mValue = None
# Get value type from property name
valueType = Property.sDefaultValueTypeMap.get(self.mName.upper(), PyCalendarUnknownValue)
# Check whether custom value is set
if definitions.Parameter_VALUE in self.mAttributes:
attr = self.getAttributeValue(definitions.Parameter_VALUE)
if attr != definitions.Value_TEXT or self.mName.upper() not in Property.sTextVariants:
valueType = Property.sValueTypeMap.get(attr, valueType)
# Check for multivalued
if self.mName.upper() in Property.sMultiValues:
self.mValue = PyCalendarMultiValue(valueType)
else:
# Create the type
self.mValue = PyCalendarValue.createFromType(valueType)
self.mValue.setValue(value)
def setupValueAttribute(self):
if definitions.Parameter_VALUE in self.mAttributes:
del self.mAttributes[definitions.Parameter_VALUE]
# Only if we have a value right now
if self.mValue is None:
return
# See if current type is default for this property. If there is no mapping available,
# then always add VALUE if it is not TEXT.
default_type = Property.sDefaultValueTypeMap.get(self.mName.upper())
actual_type = self.mValue.getType()
if default_type is None or default_type != actual_type:
actual_value = self.sTypeValueMap.get(actual_type)
if actual_value is not None and (default_type is not None or actual_type != PyCalendarValue.VALUETYPE_TEXT):
self.mAttributes.setdefault(definitions.Parameter_VALUE, []).append(PyCalendarAttribute(name=definitions.Parameter_VALUE, value=actual_value))
# Creation
def _init_attr_value_int(self, ival):
# Value
self.mValue = PyCalendarIntegerValue(value=ival)
# Attributes
self.setupValueAttribute()
def _init_attr_value_text(self, txt, value_type):
# Value
self.mValue = PyCalendarValue.createFromType(value_type)
if isinstance(self.mValue, PyCalendarPlainTextValue) or isinstance(self.mValue, PyCalendarUnknownValue):
self.mValue.setValue(txt)
# Attributes
self.setupValueAttribute()
def _init_attr_value_adr(self, reqstatus):
# Value
self.mValue = AdrValue(reqstatus)
# Attributes
self.setupValueAttribute()
def _init_attr_value_n(self, reqstatus):
# Value
self.mValue = NValue(reqstatus)
# Attributes
self.setupValueAttribute()
def _init_attr_value_org(self, reqstatus):
# Value
self.mValue = OrgValue(reqstatus)
# Attributes
self.setupValueAttribute()
def _init_attr_value_datetime(self, dt):
# Value
self.mValue = PyCalendarDateTimeValue(value=dt)
# Attributes
self.setupValueAttribute()
def _init_attr_value_utcoffset(self, utcoffset):
# Value
self.mValue = PyCalendarUTCOffsetValue()
self.mValue.setValue(utcoffset.getValue())
# Attributes
self.setupValueAttribute()
pycalendar-2.0~svn13177/src/pycalendar/vcard/tests/ 0000755 0001750 0001750 00000000000 12322631215 021204 5 ustar rahul rahul pycalendar-2.0~svn13177/src/pycalendar/vcard/tests/test_property.py 0000644 0001750 0001750 00000010115 12146674036 024513 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.attribute import PyCalendarAttribute
from pycalendar.exceptions import PyCalendarInvalidProperty
from pycalendar.parser import ParserContext
from pycalendar.vcard.property import Property
import unittest
class TestProperty(unittest.TestCase):
test_data = (
# Different value types
"PHOTO;VALUE=URI:http://example.com/photo.jpg",
"photo;VALUE=URI:http://example.com/photo.jpg",
"TEL;type=WORK;type=pref:1-555-555-5555",
"REV:20060226T120000Z",
"X-FOO:BAR",
"NOTE:Some \\ntext",
"note:Some \\ntext",
"item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;CA;11111;USA",
"X-Test:Some\, text.",
"X-Test;VALUE=URI:geio:123.123,123.123",
)
def testParseGenerate(self):
for data in TestProperty.test_data:
prop = Property()
prop.parse(data)
propstr = str(prop)
self.assertEqual(propstr[:-2], data, "Failed parse/generate: %s to %s" % (data, propstr,))
def testEquality(self):
for data in TestProperty.test_data:
prop1 = Property()
prop1.parse(data)
prop2 = Property()
prop2.parse(data)
self.assertEqual(prop1, prop2, "Failed equality: %s" % (data,))
def testParseBad(self):
test_bad_data = (
"REV:20060226T120",
"NOTE:Some \\atext",
)
save = ParserContext.INVALID_ESCAPE_SEQUENCES
for data in test_bad_data:
ParserContext.INVALID_ESCAPE_SEQUENCES = ParserContext.PARSER_RAISE
prop = Property()
self.assertRaises(PyCalendarInvalidProperty, prop.parse, data)
ParserContext.INVALID_ESCAPE_SEQUENCES = save
def testHash(self):
hashes = []
for item in TestProperty.test_data:
prop = Property()
prop.parse(item)
hashes.append(hash(prop))
hashes.sort()
for i in range(1, len(hashes)):
self.assertNotEqual(hashes[i - 1], hashes[i])
def testDefaultValueCreate(self):
test_data = (
("SOURCE", "http://example.com/source", "SOURCE:http://example.com/source\r\n"),
("souRCE", "http://example.com/source", "souRCE:http://example.com/source\r\n"),
("PHOTO", "YWJj", "PHOTO:\r\n YWJj\r\n"),
("photo", "YWJj", "photo:\r\n YWJj\r\n"),
("URL", "http://example.com/tz1", "URL:http://example.com/tz1\r\n"),
)
for propname, propvalue, result in test_data:
prop = Property(name=propname, value=propvalue)
self.assertEqual(str(prop), result)
def testParameterEncodingDecoding(self):
prop = Property(name="X-FOO", value="Test")
prop.addAttribute(PyCalendarAttribute("X-BAR", "\"Check\""))
self.assertEqual(str(prop), "X-FOO;X-BAR=^'Check^':Test\r\n")
prop.addAttribute(PyCalendarAttribute("X-BAR2", "Check\nThis\tOut\n"))
self.assertEqual(str(prop), "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test\r\n")
data = "X-FOO;X-BAR=^'Check^':Test"
prop = Property()
prop.parse(data)
self.assertEqual(prop.getAttributeValue("X-BAR"), "\"Check\"")
data = "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test"
prop = Property()
prop.parse(data)
self.assertEqual(prop.getAttributeValue("X-BAR"), "\"Check\"")
self.assertEqual(prop.getAttributeValue("X-BAR2"), "Check\nThis\tOut\n")
pycalendar-2.0~svn13177/src/pycalendar/vcard/tests/test_validation.py 0000644 0001750 0001750 00000016212 12101017573 024751 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.exceptions import PyCalendarValidationError
from pycalendar.vcard.card import Card
import unittest
class TestValidation(unittest.TestCase):
def test_basic(self):
data = (
(
"No problems",
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"No VERSION",
"""BEGIN:VCARD
VERSION:3.0
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
set(),
set((
"[VCARD] Missing or too many required property: N",
)),
),
)
for title, item, test_fixed, test_unfixed in data:
card = Card.parseText(item)
fixed, unfixed = card.validate(doFix=False)
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_mode_no_raise(self):
data = (
(
"OK",
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Unfixable only",
"""BEGIN:VCARD
VERSION:3.0
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
set(),
set((
"[VCARD] Missing or too many required property: N",
)),
),
)
for title, test_old, test_new, test_fixed, test_unfixed in data:
card = Card.parseText(test_old)
fixed, unfixed = card.validate(doFix=False, doRaise=False)
self.assertEqual(str(card), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_mode_raise(self):
data = (
(
"OK",
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
set(),
set(),
False,
),
(
"Unfixable only",
"""BEGIN:VCARD
VERSION:3.0
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
set(),
set((
"[VCARD] Missing or too many required property: N",
)),
True,
),
)
for title, test_old, test_new, test_fixed, test_unfixed, test_raises in data:
card = Card.parseText(test_old)
if test_raises:
self.assertRaises(PyCalendarValidationError, card.validate, doFix=False, doRaise=True)
else:
try:
fixed, unfixed = card.validate(doFix=False, doRaise=False)
except:
self.fail(msg="Failed test: %s" % (title,))
self.assertEqual(str(card), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
pycalendar-2.0~svn13177/src/pycalendar/vcard/tests/test_card.py 0000644 0001750 0001750 00000040343 12101017573 023532 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.exceptions import PyCalendarInvalidData
from pycalendar.parser import ParserContext
from pycalendar.vcard.card import Card
from pycalendar.vcard.property import Property
import cStringIO as StringIO
import difflib
import unittest
class TestCard(unittest.TestCase):
data = (
(
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
),
(
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
BDAY:2001-01-02
REV:2011-01-02T12:34:56-0500
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
BDAY:20010102
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
REV:20110102T123456-0500
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
),
(
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
BDAY:2001-01-02
REV:2011-01-02T12:34:56-0500
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
BDAY:20010102
EMAIL;type=INTERNET;type=pref;WORK:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
REV:20110102T123456-0500
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
),
(
"""BEGIN:VCARD
VERSION:3.0
N:Picture;With;;;
FN:With Picture
EMAIL;type=INTERNET;type=WORK;type=pref:withpicture@example.com
TEL;type=WORK;type=pref:777-777-7777
TEL;type=CELL:8888888888
item1.ADR;type=WORK;type=pref:;;1234 Golly Street;Sunnyside;CA;99999;USA
item1.X-ABADR:us
PHOTO;Encoding=b:QkVHSU46VkNBUkQKVkVSU0lPTjozLjAKTjpQaWN0dXJlO1dpdGg7Ozs
KRk46V2l0aCBQaWN0dXJlCkVNQUlMO3R5cGU9SU5URVJORVQ7dHlwZT1XT1JLO3R5cGU9cH
JlZjp3aXRocGljdHVyZUBleGFtcGxlLmNvbQpURUw7dHlwZT1XT1JLO3R5cGU9cHJlZjo3N
zctNzc3LTc3NzcKVEVMO3R5cGU9Q0VMTDo4ODg4ODg4ODg4Cml0ZW0xLkFEUjt0eXBlPVdP
Uks7dHlwZT1wcmVmOjs7MTIzNCBHb2xseSBTdHJlZXQ7U3VubnlzaWRlO0NBOzk5OTk5O1V
TQQppdGVtMS5YLUFCQURSOnVzClBIT1RPO0JBU0U2NDoKVUlEOjkzNDczMUM2LTFDOTUtNE
M0MC1CRTFGLUZBNDIxNUIyMzA3QjpBQlBlcnNvbgpFTkQ6VkNBUkQK
UID:934731C6-1C95-4C40-BE1F-FA4215B2307B:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:934731C6-1C95-4C40-BE1F-FA4215B2307B:ABPerson
item1.ADR;type=WORK;type=pref:;;1234 Golly Street;Sunnyside;CA;99999;USA
EMAIL;type=INTERNET;type=WORK;type=pref:withpicture@example.com
FN:With Picture
N:Picture;With;;;
PHOTO;Encoding=b:
QkVHSU46VkNBUkQKVkVSU0lPTjozLjAKTjpQaWN0dXJlO1dpdGg7OzsKRk46V2l0aCBQaWN0
dXJlCkVNQUlMO3R5cGU9SU5URVJORVQ7dHlwZT1XT1JLO3R5cGU9cHJlZjp3aXRocGljdHVy
ZUBleGFtcGxlLmNvbQpURUw7dHlwZT1XT1JLO3R5cGU9cHJlZjo3NzctNzc3LTc3NzcKVEVM
O3R5cGU9Q0VMTDo4ODg4ODg4ODg4Cml0ZW0xLkFEUjt0eXBlPVdPUks7dHlwZT1wcmVmOjs7
MTIzNCBHb2xseSBTdHJlZXQ7U3VubnlzaWRlO0NBOzk5OTk5O1VTQQppdGVtMS5YLUFCQURS
OnVzClBIT1RPO0JBU0U2NDoKVUlEOjkzNDczMUM2LTFDOTUtNEM0MC1CRTFGLUZBNDIxNUIy
MzA3QjpBQlBlcnNvbgpFTkQ6VkNBUkQK
TEL;type=WORK;type=pref:777-777-7777
TEL;type=CELL:8888888888
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
),
(
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
X-APPLE-STRUCTURED-LOCATION;VALUE=URI:geo:123.123,123.123
X-Test:Some\, text.
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
X-APPLE-STRUCTURED-LOCATION;VALUE=URI:geo:123.123,123.123
X-Test:Some\, text.
END:VCARD
""".replace("\n", "\r\n"),
),
)
def testRoundtrip(self):
def _doRoundtrip(caldata, resultdata=None):
test1 = resultdata if resultdata is not None else caldata
card = Card()
card.parse(StringIO.StringIO(caldata))
s = StringIO.StringIO()
card.generate(s)
test2 = s.getvalue()
self.assertEqual(
test1,
test2,
"\n".join(difflib.unified_diff(test1.splitlines(), test2.splitlines())),
)
for item, result in self.data:
_doRoundtrip(item, result)
def testRoundtripDuplicate(self):
def _doDuplicateRoundtrip(caldata, result):
card = Card()
card.parse(StringIO.StringIO(caldata))
card = card.duplicate()
s = StringIO.StringIO()
card.generate(s)
test = s.getvalue()
self.assertEqual(test, result, "\n".join(difflib.unified_diff(test.splitlines(), result.splitlines())))
for item, result in self.data:
_doDuplicateRoundtrip(item, result)
def testEquality(self):
def _doEquality(caldata):
card1 = Card()
card1.parse(StringIO.StringIO(caldata))
card2 = Card()
card2.parse(StringIO.StringIO(caldata))
self.assertEqual(card1, card2, "\n".join(difflib.unified_diff(str(card1).splitlines(), str(card2).splitlines())))
def _doNonEquality(caldata):
card1 = Card()
card1.parse(StringIO.StringIO(caldata))
card2 = Card()
card2.parse(StringIO.StringIO(caldata))
card2.addProperty(Property("X-FOO", "BAR"))
self.assertNotEqual(card1, card2)
for item, _ignore in self.data:
_doEquality(item)
_doNonEquality(item)
def testMultiple(self):
data = (
(
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"), (
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
)),
(
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E2:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:athompson@example.com
FN:Another Thompson
N:Thompson;Another;;;
TEL;type=WORK;type=pref:1-555-555-5556
TEL;type=CELL:1-444-444-4445
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"), (
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E2:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:athompson@example.com
FN:Another Thompson
N:Thompson;Another;;;
TEL;type=WORK;type=pref:1-555-555-5556
TEL;type=CELL:1-444-444-4445
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
)),
)
for item, results in data:
cards = Card.parseMultiple(StringIO.StringIO(item))
self.assertEqual(len(cards), len(results))
for card, result in zip(cards, results):
self.assertEqual(str(card), result, "\n".join(difflib.unified_diff(str(card).splitlines(), result.splitlines())))
def testABapp(self):
data = """BEGIN:VCARD
VERSION:3.0
N:Card;Test;;;
FN:Test Card
EMAIL;type=INTERNET;type=WORK;type=pref:sagen@apple.com
TEL;type=WORK;type=pref:555-1212
TEL;type=HOME:532-1234
PHOTO;BASE64:
TU0AKgAAMAj////////////////////////////////////////////////////////////+///+
SW1hZ2VNYWdpY2sgNS4zLjkgMTAvMDEvMDEgUToxNiBodHRwOi8vd3d3LmltYWdlbWFnaWNrLm9y
ZwA=
CATEGORIES:My Contacts
X-ABUID:5B77BC10-E9DB-48C4-8BE1-BAB5E38E1E43\:ABPerson
UID:128ad7ee-a656-4773-95ce-f07f77e8cc23
REV:2011-03-23T20:20:04Z
END:VCARD
""".replace("\n", "\r\n")
result = """BEGIN:VCARD
VERSION:3.0
UID:128ad7ee-a656-4773-95ce-f07f77e8cc23
CATEGORIES:My Contacts
EMAIL;type=INTERNET;type=WORK;type=pref:sagen@apple.com
FN:Test Card
N:Card;Test;;;
PHOTO;ENCODING=B:
TU0AKgAAMAj////////////////////////////////////////////////////////////+
///+SW1hZ2VNYWdpY2sgNS4zLjkgMTAvMDEvMDEgUToxNiBodHRwOi8vd3d3LmltYWdlbWFn
aWNrLm9yZwA=
REV:20110323T202004Z
TEL;type=WORK;type=pref:555-1212
TEL;type=HOME:532-1234
X-ABUID:5B77BC10-E9DB-48C4-8BE1-BAB5E38E1E43:ABPerson
END:VCARD
""".replace("\n", "\r\n")
card = Card.parseText(data)
self.assertEqual(str(card), result)
def testParseFail(self):
data = (
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
VERSION:2.0
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BOGUS
BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
"""BOGUS
BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
BOGUS
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default;;;
FN:Default Thompson
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;USA
item1.X-ABADR:us
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
END:VCARD
BOGUS
""".replace("\n", "\r\n"),
)
for item in data:
self.assertRaises(PyCalendarInvalidData, Card.parseText, item)
def testParseBlank(self):
data = (
"""
BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
"""
BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
VERSION:3.0
UID:ED7A5AEC-AB19-4CE0-AD6A-2923A3E5C4E1:ABPerson
item1.ADR;type=WORK;type=pref:;;1245 Test;Sesame Street;California;11111;U
SA
EMAIL;type=INTERNET;type=WORK;type=pref:lthompson@example.com
FN:Default Thompson
N:Thompson;Default;;;
TEL;type=WORK;type=pref:1-555-555-5555
TEL;type=CELL:1-444-444-4444
item1.X-ABADR:us
END:VCARD
""".replace("\n", "\r\n"),
)
save = ParserContext.BLANK_LINES_IN_DATA
for item in data:
ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_RAISE
self.assertRaises(PyCalendarInvalidData, Card.parseText, item)
ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_IGNORE
lines = item.split("\r\n")
result = "\r\n".join([line for line in lines if line]) + "\r\n"
self.assertEqual(str(Card.parseText(item)), result)
ParserContext.BLANK_LINES_IN_DATA = save
pycalendar-2.0~svn13177/src/pycalendar/vcard/tests/__init__.py 0000644 0001750 0001750 00000001201 12101017573 023307 0 ustar rahul rahul #
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
pycalendar-2.0~svn13177/src/pycalendar/vcard/definitions.py 0000644 0001750 0001750 00000004047 12101017573 022734 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# 2426 Component
VCARD = "VCARD"
# 2425 Properties
Property_SOURCE = "SOURCE"
Property_NAME = "NAME"
Property_PROFILE = "PROFILE"
# 2426 vCard Properties
# 2426 Section 3.1
Property_FN = "FN"
Property_N = "N"
Property_NICKNAME = "NICKNAME"
Property_PHOTO = "PHOTO"
Property_BDAY = "BDAY"
# 2426 Section 3.2
Property_ADR = "ADR"
Property_LABEL = "LABEL"
# 2426 Section 3.3
Property_TEL = "TEL"
Property_EMAIL = "EMAIL"
Property_MAILER = "MAILER"
# 2426 Section 3.4
Property_TZ = "TZ"
Property_GEO = "GEO"
# 2426 Section 3.5
Property_TITLE = "TITLE"
Property_ROLE = "ROLE"
Property_LOGO = "LOGO"
Property_AGENT = "AGENT"
Property_ORG = "ORG"
# 2426 Section 3.6
Property_CATEGORIES = "CATEGORIES"
Property_NOTE = "NOTE"
Property_PRODID = "PRODID"
Property_REV = "REV"
Property_SORT_STRING = "SORT-STRING"
Property_SOUND = "SOUND"
Property_UID = "UID"
Property_URL = "URL"
Property_VERSION = "VERSION"
# 2426 Section 3.7
Property_CLASS = "CLASS"
Property_KEY = "KEY"
# 2426 Value Types
Value_BINARY = "BINARY"
Value_BOOLEAN = "BOOLEAN"
Value_DATE = "DATE"
Value_DATE_TIME = "DATE-TIME"
Value_FLOAT = "FLOAT"
Value_INTEGER = "INTEGER"
Value_TEXT = "TEXT"
Value_TIME = "TIME"
Value_URI = "URI"
Value_UTCOFFSET = "UTCOFFSET"
Value_VCARD = "VCARD"
Parameter_ENCODING = "ENCODING"
Parameter_LANGUAGE = "LANGUAGE"
Parameter_TYPE = "TYPE"
Parameter_VALUE = "VALUE"
Parameter_Value_ENCODING_B = "B"
pycalendar-2.0~svn13177/src/pycalendar/vcard/card.py 0000644 0001750 0001750 00000020666 12101017573 021337 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from cStringIO import StringIO
from pycalendar.componentbase import PyCalendarComponentBase
from pycalendar.exceptions import PyCalendarInvalidData, \
PyCalendarValidationError
from pycalendar.parser import ParserContext
from pycalendar.utils import readFoldedLine
from pycalendar.vcard import definitions
from pycalendar.vcard.definitions import VCARD, Property_VERSION, \
Property_PRODID, Property_UID
from pycalendar.vcard.property import Property
from pycalendar.vcard.validation import VCARD_VALUE_CHECKS
class Card(PyCalendarComponentBase):
sProdID = "-//mulberrymail.com//Mulberry v4.0//EN"
sDomain = "mulberrymail.com"
@staticmethod
def setPRODID(prodid):
Card.sProdID = prodid
@staticmethod
def setDomain(domain):
Card.sDomain = domain
propertyCardinality_1 = (
definitions.Property_VERSION,
definitions.Property_N,
)
propertyCardinality_0_1 = (
definitions.Property_BDAY,
definitions.Property_PRODID,
definitions.Property_REV,
definitions.Property_UID,
)
propertyCardinality_1_More = (
definitions.Property_FN,
)
propertyValueChecks = VCARD_VALUE_CHECKS
def __init__(self, add_defaults=True):
super(Card, self).__init__()
if add_defaults:
self.addDefaultProperties()
def duplicate(self):
return super(Card, self).duplicate()
def getType(self):
return VCARD
def finalise(self):
pass
def validate(self, doFix=False, doRaise=False):
"""
Validate the data in this component and optionally fix any problems. Return
a tuple containing two lists: the first describes problems that were fixed, the
second problems that were not fixed. Caller can then decide what to do with unfixed
issues.
"""
# Optional raise behavior
fixed, unfixed = super(Card, self).validate(doFix)
if doRaise and unfixed:
raise PyCalendarValidationError(";".join(unfixed))
return fixed, unfixed
def sortedPropertyKeyOrder(self):
return (
Property_VERSION,
Property_PRODID,
Property_UID,
)
@staticmethod
def parseMultiple(ins):
results = []
card = Card(add_defaults=False)
LOOK_FOR_VCARD = 0
GET_PROPERTY = 1
state = LOOK_FOR_VCARD
# Get lines looking for start of calendar
lines = [None, None]
while readFoldedLine(ins, lines):
line = lines[0]
if state == LOOK_FOR_VCARD:
# Look for start
if line == card.getBeginDelimiter():
# Next state
state = GET_PROPERTY
# Handle blank line
elif len(line) == 0:
# Raise if requested, otherwise just ignore
if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
raise PyCalendarInvalidData("vCard data has blank lines")
# Unrecognized data
else:
raise PyCalendarInvalidData("vCard data not recognized", line)
elif state == GET_PROPERTY:
# Look for end of object
if line == card.getEndDelimiter():
# Finalise the current calendar
card.finalise()
# Validate some things
if not card.hasProperty("VERSION"):
raise PyCalendarInvalidData("vCard missing VERSION", "")
results.append(card)
# Change state
card = Card(add_defaults=False)
state = LOOK_FOR_VCARD
# Blank line
elif len(line) == 0:
# Raise if requested, otherwise just ignore
if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
raise PyCalendarInvalidData("vCard data has blank lines")
# Must be a property
else:
# Parse attribute/value for top-level calendar item
prop = Property()
if prop.parse(line):
# Check for valid property
if not card.validProperty(prop):
raise PyCalendarInvalidData("Invalid property", str(prop))
else:
card.addProperty(prop)
# Check for truncated data
if state != LOOK_FOR_VCARD:
raise PyCalendarInvalidData("vCard data not complete")
return results
@staticmethod
def parseText(data):
cal = Card(add_defaults=False)
if cal.parse(StringIO(data)):
return cal
else:
return None
def parse(self, ins):
result = False
self.setProperties({})
LOOK_FOR_VCARD = 0
GET_PROPERTY = 1
state = LOOK_FOR_VCARD
# Get lines looking for start of calendar
lines = [None, None]
while readFoldedLine(ins, lines):
line = lines[0]
if state == LOOK_FOR_VCARD:
# Look for start
if line == self.getBeginDelimiter():
# Next state
state = GET_PROPERTY
# Indicate success at this point
result = True
# Handle blank line
elif len(line) == 0:
# Raise if requested, otherwise just ignore
if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
raise PyCalendarInvalidData("vCard data has blank lines")
# Unrecognized data
else:
raise PyCalendarInvalidData("vCard data not recognized", line)
elif state == GET_PROPERTY:
# Look for end of object
if line == self.getEndDelimiter():
# Finalise the current calendar
self.finalise()
# Change state
state = LOOK_FOR_VCARD
# Blank line
elif len(line) == 0:
# Raise if requested, otherwise just ignore
if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
raise PyCalendarInvalidData("vCard data has blank lines")
# Must be a property
else:
# Parse attribute/value for top-level calendar item
prop = Property()
try:
if prop.parse(line):
# Check for valid property
if not self.validProperty(prop):
raise PyCalendarInvalidData("Invalid property", str(prop))
else:
self.addProperty(prop)
except IndexError:
print line
# Check for truncated data
if state != LOOK_FOR_VCARD:
raise PyCalendarInvalidData("vCard data not complete", "")
# Validate some things
if result and not self.hasProperty("VERSION"):
raise PyCalendarInvalidData("vCard missing VERSION", "")
return result
def addDefaultProperties(self):
self.addProperty(Property(definitions.Property_PRODID, Card.sProdID))
self.addProperty(Property(definitions.Property_VERSION, "3.0"))
def validProperty(self, prop):
if prop.getName() == definitions.Property_VERSION:
tvalue = prop.getValue()
if ((tvalue is None) or (tvalue.getValue() != "3.0")):
return False
return True
pycalendar-2.0~svn13177/src/pycalendar/vcard/__init__.py 0000644 0001750 0001750 00000001201 12101017573 022145 0 ustar rahul rahul #
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
pycalendar-2.0~svn13177/src/pycalendar/duration.py 0000644 0001750 0001750 00000016762 12101017573 021156 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.parser import ParserContext
from pycalendar.stringutils import strtoul
from pycalendar.valueutils import ValueMixin
class PyCalendarDuration(ValueMixin):
def __init__(self, duration=None, weeks=0, days=0, hours=0, minutes=0, seconds=0):
self.mForward = True
self.mWeeks = 0
self.mDays = 0
self.mHours = 0
self.mMinutes = 0
self.mSeconds = 0
if duration is None:
duration = (((weeks * 7 + days) * 24 + hours) * 60 + minutes) * 60 + seconds
self.setDuration(duration)
def duplicate(self):
other = PyCalendarDuration(None)
other.mForward = self.mForward
other.mWeeks = self.mWeeks
other.mDays = self.mDays
other.mHours = self.mHours
other.mMinutes = self.mMinutes
other.mSeconds = self.mSeconds
return other
def __hash__(self):
return hash(self.getTotalSeconds())
def __eq__(self, comp):
return self.getTotalSeconds() == comp.getTotalSeconds()
def __gt__(self, comp):
return self.getTotalSeconds() > comp.getTotalSeconds()
def __lt__(self, comp):
return self.getTotalSeconds() < comp.getTotalSeconds()
def getTotalSeconds(self):
return [1, -1][not self.mForward] \
* (self.mSeconds + (self.mMinutes + (self.mHours + (self.mDays + (self.mWeeks * 7)) * 24) * 60) * 60)
def setDuration(self, seconds):
self.mForward = seconds >= 0
remainder = seconds
if remainder < 0:
remainder = -remainder
# Is it an exact number of weeks - if so use the weeks value, otherwise
# days, hours, minutes, seconds
if remainder % (7 * 24 * 60 * 60) == 0:
self.mWeeks = remainder / (7 * 24 * 60 * 60)
self.mDays = 0
self.mHours = 0
self.mMinutes = 0
self.mSeconds = 0
else:
self.mSeconds = remainder % 60
remainder -= self.mSeconds
remainder /= 60
self.mMinutes = remainder % 60
remainder -= self.mMinutes
remainder /= 60
self.mHours = remainder % 24
remainder -= self.mHours
self.mDays = remainder / 24
self.mWeeks = 0
def getForward(self):
return self.mForward
def getWeeks(self):
return self.mWeeks
def getDays(self):
return self.mDays
def getHours(self):
return self.mHours
def getMinutes(self):
return self.mMinutes
def getSeconds(self):
return self.mSeconds
@classmethod
def parseText(cls, data):
dur = cls()
dur.parse(data)
return dur
def parse(self, data):
# parse format ([+]/-) "P" (dur-date / dur-time / dur-week)
try:
offset = 0
maxoffset = len(data)
# Look for +/-
self.mForward = True
if data[offset] in ('-', '+'):
self.mForward = data[offset] == '+'
offset += 1
# Must have a 'P'
if data[offset] != "P":
raise ValueError
offset += 1
# Look for time
if data[offset] != "T":
# Must have a number
num, offset = strtoul(data, offset)
# Now look at character
if data[offset] == "W":
# Have a number of weeks
self.mWeeks = num
offset += 1
# There cannot be anything else after this so just exit
if offset != maxoffset:
if ParserContext.INVALID_DURATION_VALUE != ParserContext.PARSER_RAISE:
return
raise ValueError
return
elif data[offset] == "D":
# Have a number of days
self.mDays = num
offset += 1
# Look for more data - exit if none
if offset == maxoffset:
return
# Look for time - exit if none
if data[offset] != "T":
raise ValueError
else:
# Error in format
raise ValueError
# Have time
offset += 1
# Strictly speaking T must always be followed by time values, but some clients
# send T with no additional text
if offset == maxoffset:
if ParserContext.INVALID_DURATION_VALUE == ParserContext.PARSER_RAISE:
raise ValueError
else:
return
num, offset = strtoul(data, offset)
# Look for hour
if data[offset] == "H":
# Get hours
self.mHours = num
offset += 1
# Look for more data - exit if none
if offset == maxoffset:
return
# Parse the next number
num, offset = strtoul(data, offset)
# Look for minute
if data[offset] == "M":
# Get hours
self.mMinutes = num
offset += 1
# Look for more data - exit if none
if offset == maxoffset:
return
# Parse the next number
num, offset = strtoul(data, offset)
# Look for seconds
if data[offset] == "S":
# Get hours
self.mSeconds = num
offset += 1
# No more data - exit
if offset == maxoffset:
return
raise ValueError
except IndexError:
raise ValueError
def generate(self, os):
try:
if not self.mForward and (self.mWeeks or self.mDays or self.mHours or self.mMinutes or self.mSeconds):
os.write("-")
os.write("P")
if self.mWeeks != 0:
os.write("%dW" % (self.mWeeks,))
else:
if self.mDays != 0:
os.write("%dD" % (self.mDays,))
if (self.mHours != 0) or (self.mMinutes != 0) or (self.mSeconds != 0):
os.write("T")
if self.mHours != 0:
os.write("%dH" % (self.mHours,))
if (self.mMinutes != 0) or ((self.mHours != 0) and (self.mSeconds != 0)):
os.write("%dM" % (self.mMinutes,))
if self.mSeconds != 0:
os.write("%dS" % (self.mSeconds,))
elif self.mDays == 0:
os.write("T0S")
except:
pass
def writeXML(self, node, namespace):
node.text = self.getText()
pycalendar-2.0~svn13177/src/pycalendar/adrvalue.py 0000644 0001750 0001750 00000002462 12101017573 021124 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# vCard ADR value
from pycalendar.adr import Adr
from pycalendar.value import PyCalendarValue
class AdrValue(PyCalendarValue):
def __init__(self, value=None):
self.mValue = value if value is not None else Adr()
def duplicate(self):
return AdrValue(self.mValue.duplicate())
def getType(self):
return PyCalendarValue.VALUETYPE_ADR
def parse(self, data):
self.mValue.parse(data)
def generate(self, os):
self.mValue.generate(os)
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_ADR, AdrValue, None)
pycalendar-2.0~svn13177/src/pycalendar/timezone.py 0000644 0001750 0001750 00000010210 12317143440 021143 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import stringutils
from pycalendar.timezonedb import PyCalendarTimezoneDatabase
class PyCalendarTimezone(object):
"""
Wrapper around a timezone specification. There are three options:
UTC - when mUTC is True
TZID - when mUTC is False and tzid is a str
UTCOFFSET - when mUTC is False and tzid is an int
"""
sDefaultTimezone = None
def __init__(self, utc=None, tzid=None):
if utc is not None:
self.mUTC = utc
self.mTimezone = tzid
elif tzid is not None:
self.mUTC = tzid.lower() == 'utc'
self.mTimezone = None if tzid.lower() == 'utc' else tzid
else:
self.mUTC = True
self.mTimezone = None
# Copy default timezone if it exists
if PyCalendarTimezone.sDefaultTimezone is not None:
self.mUTC = PyCalendarTimezone.sDefaultTimezone.mUTC
self.mTimezone = PyCalendarTimezone.sDefaultTimezone.mTimezone
def duplicate(self):
return PyCalendarTimezone(self.mUTC, self.mTimezone)
def equals(self, comp):
# Always match if any one of them is 'floating'
if self.floating() or comp.floating():
return True
elif self.mUTC != comp.mUTC:
return False
else:
return self.mUTC or stringutils.compareStringsSafe(self.mTimezone, comp.mTimezone)
@staticmethod
def same(utc1, tzid1, utc2, tzid2):
# Always match if any one of them is 'floating'
if PyCalendarTimezone.is_float(utc1, tzid1) or PyCalendarTimezone.is_float(utc2, tzid2):
return True
elif utc1 != utc2:
return False
else:
return utc1 or stringutils.compareStringsSafe(tzid1, tzid2)
@staticmethod
def is_float(utc, tzid):
return not utc and not tzid
def getUTC(self):
return self.mUTC
def setUTC(self, utc):
self.mUTC = utc
def getTimezoneID(self):
return self.mTimezone
def setTimezoneID(self, tzid):
self.mTimezone = tzid
def floating(self):
return not self.mUTC and self.mTimezone is None
def hasTZID(self):
return not self.mUTC and self.mTimezone is not None
def timeZoneSecondsOffset(self, dt, relative_to_utc=False):
if self.mUTC:
return 0
elif self.mTimezone is None:
return PyCalendarTimezoneDatabase.getTimezoneOffsetSeconds(PyCalendarTimezone.sDefaultTimezone.getTimezoneID(), dt, relative_to_utc)
elif isinstance(self.mTimezone, int):
return self.mTimezone
else:
# Look up timezone and resolve date using default timezones
return PyCalendarTimezoneDatabase.getTimezoneOffsetSeconds(self.mTimezone, dt, relative_to_utc)
def timeZoneDescriptor(self, dt):
if self.mUTC:
return "(UTC)"
elif self.mTimezone is None:
return PyCalendarTimezoneDatabase.getTimezoneDescriptor(PyCalendarTimezone.sDefaultTimezone.getTimezoneID(), dt)
elif isinstance(self.mTimezone, int):
sign = "-" if self.mTimezone < 0 else "+"
hours = abs(self.mTimezone) / 3600
minutes = divmod(abs(self.mTimezone) / 60, 60)[1]
return "%s%02d%02d" % (sign, hours, minutes,)
else:
# Look up timezone and resolve date using default timezones
return PyCalendarTimezoneDatabase.getTimezoneDescriptor(self.mTimezone, dt)
PyCalendarTimezone.sDefaultTimezone = PyCalendarTimezone()
pycalendar-2.0~svn13177/src/pycalendar/componentexpanded.py 0000644 0001750 0001750 00000012224 12101017573 023031 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.datetime import PyCalendarDateTime
class PyCalendarComponentExpanded(object):
@staticmethod
def sort_by_dtstart_allday(e1, e2):
if e1.mInstanceStart.isDateOnly() and e2.mInstanceStart.isDateOnly():
return e1.mInstanceStart < e2.mInstanceStart
elif e1.mInstanceStart.isDateOnly():
return True
elif e2.mInstanceStart.isDateOnly():
return False
elif e1.mInstanceStart == e2.mInstanceStart:
if e1.mInstanceEnd == e2.mInstanceEnd:
# Put ones created earlier in earlier columns in day view
return e1.getOwner().getStamp() < e2.getOwner().getStamp()
else:
# Put ones that end later in earlier columns in day view
return e1.mInstanceEnd > e2.mInstanceEnd
else:
return e1.mInstanceStart < e2.mInstanceStart
@staticmethod
def sort_by_dtstart(e1, e2):
if e1.mInstanceStart == e2.mInstanceStart:
if (e1.mInstanceStart.isDateOnly() and not e2.mInstanceStart.isDateOnly() or
not e1.mInstanceStart.isDateOnly() and e2.mInstanceStart.isDateOnly()):
return e1.mInstanceStart.isDateOnly()
else:
return False
else:
return e1.mInstanceStart < e2.mInstanceStart
def __init__(self, owner, rid):
self.mOwner = owner
self.initFromOwner(rid)
def duplicate(self):
other = PyCalendarComponentExpanded(self.mOwner, None)
other.mInstanceStart = self.mInstanceStart.duplicate()
other.mInstanceEnd = self.mInstanceEnd.duplicate()
other.mRecurring = self.mRecurring
return other
def close(self):
# Clean-up
self.mOwner = None
def getOwner(self):
return self.mOwner
def getMaster(self):
return self.mOwner
def getTrueMaster(self):
return self.mOwner.getMaster()
def getInstanceStart(self):
return self.mInstanceStart
def getInstanceEnd(self):
return self.mInstanceEnd
def recurring(self):
return self.mRecurring
def isNow(self):
# Check instance start/end against current date-time
now = PyCalendarDateTime.getNowUTC()
return self.mInstanceStart <= now and self.mInstanceEnd > now
def initFromOwner(self, rid):
# There are four possibilities here:
#
# 1: this instance is the instance for the master component
#
# 2: this instance is an expanded instance derived directly from the
# master component
#
# 3: This instance is the instance for a slave (overridden recurrence
# instance)
#
# 4: This instance is the expanded instance for a slave with a RANGE
# parameter
#
# rid is not set if the owner is the master (case 1)
if rid is None:
# Just get start/end from owner
self.mInstanceStart = self.mOwner.getStart()
self.mInstanceEnd = self.mOwner.getEnd()
self.mRecurring = False
# If the owner is not a recurrence instance then it is case 2
elif not self.mOwner.isRecurrenceInstance():
# Derive start/end from rid and duration of master
# Start of the recurrence instance is the recurrence id
self.mInstanceStart = rid
# End is based on original events settings
if self.mOwner.hasEnd():
self.mInstanceEnd = self.mInstanceStart + (self.mOwner.getEnd() - self.mOwner.getStart())
else:
self.mInstanceEnd = self.mInstanceStart.duplicate()
self.mRecurring = True
# If the owner is a recurrence item and the passed in rid is the same
# as the component rid we have case 3
elif rid == self.mOwner.getRecurrenceID():
# Derive start/end directly from the owner
self.mInstanceStart = self.mOwner.getStart()
self.mInstanceEnd = self.mOwner.getEnd()
self.mRecurring = True
# case 4 - the complicated one!
else:
# We need to use the rid as the starting point, but adjust it by
# the offset between the slave's
# rid and its start
self.mInstanceStart = rid + (self.mOwner.getStart() - self.mOwner.getRecurrenceID())
# End is based on duration of owner
self.mInstanceEnd = self.mInstanceStart + (self.mOwner.getEnd() - self.mOwner.getStart())
self.mRecurring = True
pycalendar-2.0~svn13177/src/pycalendar/value.py 0000644 0001750 0001750 00000005175 12101017573 020441 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# ICalendar Value class
from pycalendar.valueutils import ValueMixin
from pycalendar import xmldefs
import xml.etree.cElementTree as XML
class PyCalendarValue(ValueMixin):
(
VALUETYPE_ADR,
VALUETYPE_BINARY,
VALUETYPE_BOOLEAN,
VALUETYPE_CALADDRESS,
VALUETYPE_DATE,
VALUETYPE_DATETIME,
VALUETYPE_DURATION,
VALUETYPE_FLOAT,
VALUETYPE_GEO,
VALUETYPE_INTEGER,
VALUETYPE_N,
VALUETYPE_ORG,
VALUETYPE_PERIOD,
VALUETYPE_RECUR,
VALUETYPE_REQUEST_STATUS,
VALUETYPE_TEXT,
VALUETYPE_TIME,
VALUETYPE_UNKNOWN,
VALUETYPE_URI,
VALUETYPE_UTC_OFFSET,
VALUETYPE_VCARD,
VALUETYPE_MULTIVALUE,
VALUETYPE_XNAME,
) = range(23)
_typeMap = {}
_xmlMap = {}
def __hash__(self):
return hash((self.getType(), self.getValue()))
def __ne__(self, other):
return not self.__eq__(other)
def __eq__(self, other):
if not isinstance(other, PyCalendarValue):
return False
return self.getType() == other.getType() and self.getValue() == other.getValue()
@classmethod
def registerType(clz, type, cls, xmlNode):
clz._typeMap[type] = cls
clz._xmlMap[type] = xmlNode
@classmethod
def createFromType(clz, type):
# Create the type
created = clz._typeMap.get(type, None)
if created:
return created()
else:
return clz._typeMap.get(PyCalendarValue.VALUETYPE_UNKNOWN)(type)
def getType(self):
raise NotImplementedError
def getRealType(self):
return self.getType()
def getValue(self):
raise NotImplementedError
def setValue(self, value):
raise NotImplementedError
def writeXML(self, node, namespace):
raise NotImplementedError
def getXMLNode(self, node, namespace):
return XML.SubElement(node, xmldefs.makeTag(namespace, self._xmlMap[self.getType()]))
pycalendar-2.0~svn13177/src/pycalendar/n.py 0000644 0001750 0001750 00000005166 12101017573 017562 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# vCard ADR value
from pycalendar import utils
from pycalendar.valueutils import ValueMixin
class N(ValueMixin):
"""
mValue is a tuple of seven str or tuples of str
"""
(
LAST,
FIRST,
MIDDLE,
PREFIX,
SUFFIX,
MAXITEMS
) = range(6)
def __init__(self, last="", first="", middle="", prefix="", suffix=""):
self.mValue = (last, first, middle, prefix, suffix)
def duplicate(self):
return N(*self.mValue)
def __hash__(self):
return hash(self.mValue)
def __repr__(self):
return "N %s" % (self.getText(),)
def __eq__(self, comp):
return self.mValue == comp.mValue
def getFirst(self):
return self.mValue[N.FIRST]
def setFirst(self, value):
self.mValue[N.FIRST] = value
def getLast(self):
return self.mValue[N.LAST]
def setLast(self, value):
self.mValue[N.LAST] = value
def getMiddle(self):
return self.mValue[N.MIDDLE]
def setMiddle(self, value):
self.mValue[N.MIDDLE] = value
def getPrefix(self):
return self.mValue[N.PREFIX]
def setPrefix(self, value):
self.mValue[N.PREFIX] = value
def getSuffix(self):
return self.mValue[N.SUFFIX]
def setSuffix(self, value):
self.mValue[N.SUFFIX] = value
def getFullName(self):
def _stringOrList(item):
return item if isinstance(item, basestring) else " ".join(item)
results = []
for i in (N.PREFIX, N.FIRST, N.MIDDLE, N.LAST, N.SUFFIX):
result = _stringOrList(self.mValue[i])
if result:
results.append(result)
return " ".join(results)
def parse(self, data):
self.mValue = utils.parseDoubleNestedList(data, N.MAXITEMS)
def generate(self, os):
utils.generateDoubleNestedList(os, self.mValue)
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
pycalendar-2.0~svn13177/src/pycalendar/attribute.py 0000644 0001750 0001750 00000006353 12101017573 021327 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
"""
ICalendar attribute.
The attribute can consist of one or more values, all string.
"""
from pycalendar import xmldefs
from pycalendar.utils import encodeParameterValue
import xml.etree.cElementTree as XML
class PyCalendarAttribute(object):
def __init__(self, name, value=None):
self.mName = name
if value is None:
self.mValues = []
elif isinstance(value, basestring):
self.mValues = [value]
else:
self.mValues = value
def duplicate(self):
other = PyCalendarAttribute(self.mName)
other.mValues = self.mValues[:]
return other
def __hash__(self):
return hash((self.mName.upper(), tuple(self.mValues)))
def __ne__(self, other):
return not self.__eq__(other)
def __eq__(self, other):
if not isinstance(other, PyCalendarAttribute):
return False
return self.mName.upper() == other.mName.upper() and self.mValues == other.mValues
def getName(self):
return self.mName
def setName(self, name):
self.mName = name
def getFirstValue(self):
return self.mValues[0]
def getValues(self):
return self.mValues
def setValues(self, values):
self.mValues = values
def addValue(self, value):
self.mValues.append(value)
def removeValue(self, value):
self.mValues.remove(value)
return len(self.mValues)
def generate(self, os):
try:
os.write(self.mName)
# To support vCard 2.1 syntax we allow parameters without values
if self.mValues:
os.write("=")
first = True
for s in self.mValues:
if first:
first = False
else:
os.write(",")
# Write with quotation if required
self.generateValue(os, s)
except:
# We ignore errors
pass
def generateValue(self, os, str):
# ^-escaping
str = encodeParameterValue(str)
# Look for quoting
if str.find(":") != -1 or str.find(";") != -1 or str.find(",") != -1:
os.write("\"%s\"" % (str,))
else:
os.write(str)
def writeXML(self, node, namespace):
param = XML.SubElement(node, xmldefs.makeTag(namespace, self.getName()))
for value in self.getValues():
# TODO: need to figure out proper value types
text = XML.SubElement(param, xmldefs.makeTag(namespace, xmldefs.value_text))
text.text = value
pycalendar-2.0~svn13177/src/pycalendar/requeststatusvalue.py 0000644 0001750 0001750 00000006532 12127044213 023313 0 ustar rahul rahul ##
# Copyright (c) 2011-2013 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# iCalendar REQUEST-STATUS value
from pycalendar import utils, xmldefs
from pycalendar.parser import ParserContext
from pycalendar.value import PyCalendarValue
import xml.etree.cElementTree as XML
class PyCalendarRequestStatusValue(PyCalendarValue):
"""
The value is a list of strings (either 2 or 3 items)
"""
def __init__(self, value=None):
self.mValue = value if value is not None else ["2.0", "Success"]
def __hash__(self):
return hash(tuple(self.mValue))
def duplicate(self):
return PyCalendarRequestStatusValue(self.mValue[:])
def getType(self):
return PyCalendarValue.VALUETYPE_REQUEST_STATUS
def parse(self, data):
result = utils.parseTextList(data, always_list=True)
if len(result) == 1:
if ParserContext.INVALID_REQUEST_STATUS_VALUE != ParserContext.PARSER_RAISE:
if ";" in result[0]:
code, desc = result[0].split(";", 1)
else:
code = result[0]
desc = ""
rest = None
else:
raise ValueError
elif len(result) == 2:
code, desc = result
rest = None
elif len(result) == 3:
code, desc, rest = result
else:
if ParserContext.INVALID_REQUEST_STATUS_VALUE != ParserContext.PARSER_RAISE:
code, desc, rest = result[:3]
else:
raise ValueError
if "\\" in code and ParserContext.INVALID_REQUEST_STATUS_VALUE in (ParserContext.PARSER_IGNORE, ParserContext.PARSER_FIX):
code = code.replace("\\", "")
elif ParserContext.INVALID_REQUEST_STATUS_VALUE == ParserContext.PARSER_RAISE:
raise ValueError
# Decoding required
self.mValue = [code, desc, rest, ] if rest else [code, desc, ]
# os - StringIO object
def generate(self, os):
utils.generateTextList(os, self.mValue if len(self.mValue) < 3 or self.mValue[2] else self.mValue[:2])
def writeXML(self, node, namespace):
code = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.req_status_code))
code.text = self.mValue[0]
description = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.req_status_description))
description.text = self.mValue[1]
if len(self.mValue) == 3 and self.mValue[2]:
data = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.req_status_data))
data.text = self.mValue[1]
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_REQUEST_STATUS, PyCalendarRequestStatusValue, None)
pycalendar-2.0~svn13177/src/pycalendar/calendar.py 0000644 0001750 0001750 00000063756 12317143440 021111 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from cStringIO import StringIO
from pycalendar import definitions, xmldefs
from pycalendar.available import PyCalendarAvailable
from pycalendar.componentbase import PyCalendarComponentBase
from pycalendar.componentexpanded import PyCalendarComponentExpanded
from pycalendar.componentrecur import PyCalendarComponentRecur
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.exceptions import PyCalendarInvalidData, \
PyCalendarValidationError
from pycalendar.freebusy import PyCalendarFreeBusy
from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
from pycalendar.parser import ParserContext
from pycalendar.period import PyCalendarPeriod
from pycalendar.property import PyCalendarProperty
from pycalendar.utils import readFoldedLine
from pycalendar.valarm import PyCalendarVAlarm
from pycalendar.vavailability import PyCalendarVAvailability
from pycalendar.vevent import PyCalendarVEvent
from pycalendar.vfreebusy import PyCalendarVFreeBusy
from pycalendar.vjournal import PyCalendarVJournal
from pycalendar.vtimezone import PyCalendarVTimezone
from pycalendar.vtimezonedaylight import PyCalendarVTimezoneDaylight
from pycalendar.vtimezonestandard import PyCalendarVTimezoneStandard
from pycalendar.vtodo import PyCalendarVToDo
from pycalendar.vunknown import PyCalendarUnknownComponent
import collections
import xml.etree.cElementTree as XML
class PyCalendar(PyCalendarComponentBase):
REMOVE_ALL = 0
REMOVE_ONLY_THIS = 1
REMOVE_THIS_AND_FUTURE = 2
FIND_EXACT = 0
FIND_MASTER = 1
sProdID = "-//mulberrymail.com//Mulberry v4.0//EN"
sDomain = "mulberrymail.com"
@staticmethod
def setPRODID(prodid):
PyCalendar.sProdID = prodid
@staticmethod
def setDomain(domain):
PyCalendar.sDomain = domain
propertyCardinality_1 = (
definitions.cICalProperty_PRODID,
definitions.cICalProperty_VERSION,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_CALSCALE,
definitions.cICalProperty_METHOD,
)
propertyValueChecks = ICALENDAR_VALUE_CHECKS
def __init__(self, parent=None, add_defaults=True):
super(PyCalendar, self).__init__(None)
self.mName = ""
self.mDescription = ""
self.mMasterComponentsByTypeAndUID = collections.defaultdict(lambda: collections.defaultdict(list))
self.mOverriddenComponentsByUID = collections.defaultdict(list)
if add_defaults:
self.addDefaultProperties()
def duplicate(self):
other = super(PyCalendar, self).duplicate()
other.mName = self.mName
other.mDescription = self.mDescription
return other
def getType(self):
return definitions.cICalComponent_VCALENDAR
def getName(self):
return self.mName
def setName(self, name):
self.mName = name
def editName(self, name):
if self.mName != name:
# Updated cached value
self.mName = name
# Remove existing items
self.removeProperties(definitions.cICalProperty_XWRCALNAME)
# Now create properties
if len(name):
self.ddProperty(PyCalendarProperty(definitions.cICalProperty_XWRCALNAME, name))
def getDescription(self):
return self.mDescription
def setDescription(self, description):
self.mDescription = description
def editDescription(self, description):
if self.mDescription != description:
# Updated cached value
self.mDescription = description
# Remove existing items
self.removeProperties(definitions.cICalProperty_XWRCALDESC)
# Now create properties
if len(description):
self.addProperty(PyCalendarProperty(definitions.cICalProperty_XWRCALDESC, description))
def getMethod(self):
result = ""
if self.hasProperty(definitions.cICalProperty_METHOD):
result = self.loadValueString(definitions.cICalProperty_METHOD)
return result
def changeUID(self, oldUID, newUID):
"""
Change the UID of all components with a matching UID to a new value. We need to
do this at the calendar level because this object maintains mappings based on UID
which need to be updated whenever the UID changes.
@param oldUID: the old value to match
@type oldUID: C{str}
@param newUID: the new value to match
@type newUID: C{str}
"""
# Each component
for component in self.mComponents:
if component.getUID() == oldUID:
component.setUID(newUID)
# Maps
if oldUID in self.mOverriddenComponentsByUID:
self.mOverriddenComponentsByUID[newUID] = self.mOverriddenComponentsByUID[oldUID]
del self.mOverriddenComponentsByUID[oldUID]
for ctype in self.mMasterComponentsByTypeAndUID:
if oldUID in self.mMasterComponentsByTypeAndUID[ctype]:
self.mMasterComponentsByTypeAndUID[ctype][newUID] = self.mMasterComponentsByTypeAndUID[ctype][oldUID]
del self.mMasterComponentsByTypeAndUID[ctype][oldUID]
def finalise(self):
# Get calendar name if present
# Get X-WR-CALNAME
temps = self.loadValueString(definitions.cICalProperty_XWRCALNAME)
if temps is not None:
self.mName = temps
# Get X-WR-CALDESC
temps = self.loadValueString(definitions.cICalProperty_XWRCALDESC)
if temps is not None:
self.mDescription = temps
def validate(self, doFix=False, doRaise=False):
"""
Validate the data in this component and optionally fix any problems. Return
a tuple containing two lists: the first describes problems that were fixed, the
second problems that were not fixed. Caller can then decide what to do with unfixed
issues.
"""
# Optional raise behavior
fixed, unfixed = super(PyCalendar, self).validate(doFix)
if doRaise and unfixed:
raise PyCalendarValidationError(";".join(unfixed))
return fixed, unfixed
def sortedComponentNames(self):
return (
definitions.cICalComponent_VTIMEZONE,
definitions.cICalComponent_VEVENT,
definitions.cICalComponent_VTODO,
definitions.cICalComponent_VJOURNAL,
definitions.cICalComponent_VFREEBUSY,
definitions.cICalComponent_VAVAILABILITY,
)
def sortedPropertyKeyOrder(self):
return (
definitions.cICalProperty_VERSION,
definitions.cICalProperty_CALSCALE,
definitions.cICalProperty_METHOD,
definitions.cICalProperty_PRODID,
)
@staticmethod
def parseText(data):
cal = PyCalendar(add_defaults=False)
if cal.parse(StringIO(data)):
return cal
else:
return None
def parse(self, ins):
result = False
self.setProperties({})
LOOK_FOR_VCALENDAR = 0
GET_PROPERTY_OR_COMPONENT = 1
state = LOOK_FOR_VCALENDAR
# Get lines looking for start of calendar
lines = [None, None]
comp = self
compend = None
componentstack = []
while readFoldedLine(ins, lines):
line = lines[0]
if state == LOOK_FOR_VCALENDAR:
# Look for start
if line == self.getBeginDelimiter():
# Next state
state = GET_PROPERTY_OR_COMPONENT
# Indicate success at this point
result = True
# Handle blank line
elif len(line) == 0:
# Raise if requested, otherwise just ignore
if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
raise PyCalendarInvalidData("iCalendar data has blank lines")
# Unrecognized data
else:
raise PyCalendarInvalidData("iCalendar data not recognized", line)
elif state == GET_PROPERTY_OR_COMPONENT:
# Parse property or look for start of component
if line.startswith("BEGIN:"):
# Push previous details to stack
componentstack.append((comp, compend,))
# Start a new component
comp = PyCalendar.makeComponent(line[6:], comp)
compend = comp.getEndDelimiter()
# Look for end of object
elif line == self.getEndDelimiter():
# Finalise the current calendar
self.finalise()
# Change state
state = LOOK_FOR_VCALENDAR
# Look for end of current component
elif line == compend:
# Finalise the component (this caches data from the properties)
comp.finalise()
# Embed component in parent and reset to use parent
componentstack[-1][0].addComponent(comp)
comp, compend = componentstack.pop()
# Blank line
elif len(line) == 0:
# Raise if requested, otherwise just ignore
if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
raise PyCalendarInvalidData("iCalendar data has blank lines")
# Must be a property
else:
# Parse attribute/value for top-level calendar item
prop = PyCalendarProperty()
if prop.parse(line):
# Check for valid property
if comp is self:
if not comp.validProperty(prop):
raise PyCalendarInvalidData("Invalid property", str(prop))
elif not comp.ignoreProperty(prop):
comp.addProperty(prop)
else:
comp.addProperty(prop)
# Check for truncated data
if state != LOOK_FOR_VCALENDAR:
raise PyCalendarInvalidData("iCalendar data not complete")
# We need to store all timezones in the static object so they can be accessed by any date object
from timezonedb import PyCalendarTimezoneDatabase
PyCalendarTimezoneDatabase.mergeTimezones(self, self.getComponents(definitions.cICalComponent_VTIMEZONE))
# Validate some things
if result and not self.hasProperty("VERSION"):
raise PyCalendarInvalidData("iCalendar missing VERSION")
return result
def parseComponent(self, ins):
result = None
LOOK_FOR_VCALENDAR = 0
GET_PROPERTY_OR_COMPONENT = 1
GOT_VCALENDAR = 4
state = LOOK_FOR_VCALENDAR
# Get lines looking for start of calendar
lines = [None, None]
comp = self
compend = None
componentstack = []
got_timezone = False
while readFoldedLine(ins, lines):
if state == LOOK_FOR_VCALENDAR:
# Look for start
if lines[0] == self.getBeginDelimiter():
# Next state
state = GET_PROPERTY_OR_COMPONENT
# Handle blank line
elif len(lines[0]) == 0:
# Raise if requested, otherwise just ignore
if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
raise PyCalendarInvalidData("iCalendar data has blank lines")
# Unrecognized data
else:
raise PyCalendarInvalidData("iCalendar data not recognized", lines[0])
elif state == GET_PROPERTY_OR_COMPONENT:
# Parse property or look for start of component
if lines[0].startswith("BEGIN:"):
# Push previous details to stack
componentstack.append((comp, compend,))
# Start a new component
comp = PyCalendar.makeComponent(lines[0][6:], comp)
compend = comp.getEndDelimiter()
# Cache as result - but only the first one, we ignore the rest
if result is None:
result = comp
# Look for timezone component to trigger timezone merge only if one is present
if comp.getType() == definitions.cICalComponent_VTIMEZONE:
got_timezone = True
elif lines[0] == self.getEndDelimiter():
# Change state
state = GOT_VCALENDAR
# Look for end of current component
elif lines[0] == compend:
# Finalise the component (this caches data from the properties)
comp.finalise()
# Embed component in parent and reset to use parent
componentstack[-1][0].addComponent(comp)
comp, compend = componentstack.pop()
# Blank line
elif len(lines[0]) == 0:
# Raise if requested, otherwise just ignore
if ParserContext.BLANK_LINES_IN_DATA == ParserContext.PARSER_RAISE:
raise PyCalendarInvalidData("iCalendar data has blank lines")
# Ignore top-level items
elif comp is self:
pass
# Must be a property
else:
# Parse attribute/value for top-level calendar item
prop = PyCalendarProperty()
if prop.parse(lines[0]):
# Check for valid property
if comp is not self:
comp.addProperty(prop)
# Exit if we have one - ignore all the rest
if state == GOT_VCALENDAR:
break
# We need to store all timezones in the static object so they can be accessed by any date object
# Only do this if we read in a timezone
if got_timezone:
from timezonedb import PyCalendarTimezoneDatabase
PyCalendarTimezoneDatabase.mergeTimezones(self, self.getComponents(definitions.cICalComponent_VTIMEZONE))
return result
def addComponent(self, component):
"""
Override to track components by UID.
"""
super(PyCalendar, self).addComponent(component)
if isinstance(component, PyCalendarComponentRecur):
uid = component.getUID()
rid = component.getRecurrenceID()
if rid:
self.mOverriddenComponentsByUID[uid].append(component)
else:
self.mMasterComponentsByTypeAndUID[component.getType()][uid] = component
def removeComponent(self, component):
"""
Override to track components by UID.
"""
super(PyCalendar, self).removeComponent(component)
if isinstance(component, PyCalendarComponentRecur):
uid = component.getUID()
rid = component.getRecurrenceID()
if rid:
self.mOverriddenComponentsByUID[uid].remove(component)
else:
del self.mMasterComponentsByTypeAndUID[component.getType()][uid]
def getText(self, includeTimezones=False):
s = StringIO()
self.generate(s, includeTimezones=includeTimezones)
return s.getvalue()
def generate(self, os, includeTimezones=False):
# Make sure all required timezones are in this object
if includeTimezones:
self.includeTimezones()
super(PyCalendar, self).generate(os)
def getTextXML(self, includeTimezones=False):
node = self.writeXML(includeTimezones)
return xmldefs.toString(node)
def writeXML(self, includeTimezones=False):
# Make sure all required timezones are in this object
if includeTimezones:
self.includeTimezones()
# Root node structure
root = XML.Element(xmldefs.makeTag(xmldefs.iCalendar20_namespace, xmldefs.icalendar))
super(PyCalendar, self).writeXML(root, xmldefs.iCalendar20_namespace)
return root
# Get expanded components
def getVEvents(self, period, list, all_day_at_top=True):
# Look at each VEvent
for vevent in self.getComponents(definitions.cICalComponent_VEVENT):
vevent.expandPeriod(period, list)
if (all_day_at_top):
list.sort(PyCalendarComponentExpanded.sort_by_dtstart_allday)
else:
list.sort(PyCalendarComponentExpanded.sort_by_dtstart)
def getVToDos(self, only_due, all_dates, upto_due_date, list):
# Get current date-time less one day to test for completed events during the last day
minusoneday = PyCalendarDateTime()
minusoneday.setNowUTC()
minusoneday.offsetDay(-1)
today = PyCalendarDateTime()
today.setToday()
# Look at each VToDo
for vtodo in self.getComponents(definitions.cICalComponent_VTODO):
# Filter out done (that were complted more than a day ago) or cancelled to dos if required
if only_due:
if vtodo.getStatus() == definitions.eStatus_VToDo_Cancelled:
continue
elif ((vtodo.getStatus() == definitions.eStatus_VToDo_Completed) and
(not vtodo.hasCompleted() or (vtodo.getCompleted() < minusoneday))):
continue
# Filter out those with end after chosen date if required
if not all_dates:
if vtodo.hasEnd() and (vtodo.getEnd() > upto_due_date):
continue
elif not vtodo.hasEnd() and (today > upto_due_date):
continue
# TODO: fix this
#list.append(PyCalendarComponentExpandedShared(PyCalendarComponentExpanded(vtodo, None)))
def getRecurrenceInstancesItems(self, type, uid, items):
# Get instances from list
items.extend(self.mOverriddenComponentsByUID.get(uid, ()))
def getRecurrenceInstancesIds(self, type, uid, ids):
# Get instances from list
ids.extend([comp.getRecurrenceID() for comp in self.mOverriddenComponentsByUID.get(uid, ())])
# Freebusy generation
def getVFreeBusyList(self, period, list):
# Look at each VFreeBusy
for vfreebusy in self.getComponents(definitions.cICalComponent_VFREEBUSY):
vfreebusy.expandPeriod(period, list)
def getVFreeBusyFB(self, period, fb):
# First create expanded set
# TODO: fix this
#list = PyCalendarExpandedComponents()
self.getVEvents(period, list)
if len(list) == 0:
return
# Get start/end list for each non-all-day expanded components
dtstart = []
dtend = []
for dt in list:
# Ignore if all-day
if dt.getInstanceStart().isDateOnly():
continue
# Ignore if transparent to free-busy
transp = ""
if dt.getOwner().getProperty(definitions.cICalProperty_TRANSP, transp) and (transp == definitions.cICalProperty_TRANSPARENT):
continue
# Add start/end to list
dtstart.append(dt.getInstanceStart())
dtend.append(dt.getInstanceEnd())
# No longer need the expanded items
list.clear()
# Create non-overlapping periods as properties in the freebusy component
temp = PyCalendarPeriod(dtstart.front(), dtend.front())
dtstart_iter = dtstart.iter()
dtstart_iter.next()
dtend_iter = dtend.iter()
dtend_iter.next()
for i in i:
# Check for non-overlap
if dtstart_iter > temp.getEnd():
# Current period is complete
fb.addProperty(PyCalendarProperty(definitions.cICalProperty_FREEBUSY, temp))
# Reset period to new range
temp = PyCalendarPeriod(dtstart_iter, dtend_iter)
# They overlap - check for extended end
if dtend_iter > temp.getEnd():
# Extend the end
temp = PyCalendarPeriod(temp.getStart(), dtend_iter)
# Add remaining period as property
fb.addProperty(PyCalendarProperty(definitions.cICalProperty_FREEBUSY, temp))
def getFreeBusy(self, period, fb):
# First create expanded set
list = []
self.getVEvents(period, list)
# Get start/end list for each non-all-day expanded components
for comp in list:
# Ignore if all-day
if comp.getInstanceStart().isDateOnly():
continue
# Ignore if transparent to free-busy
transp = ""
if comp.getOwner().getProperty(definitions.cICalProperty_TRANSP, transp) and (transp == definitions.cICalProperty_TRANSPARENT):
continue
# Add free busy item to list
status = comp.getMaster().getStatus()
if status in (definitions.eStatus_VEvent_None, definitions.eStatus_VEvent_Confirmed):
fb.append(PyCalendarFreeBusy(PyCalendarFreeBusy.BUSY, PyCalendarPeriod(comp.getInstanceStart(), comp.getInstanceEnd())))
elif status == definitions.eStatus_VEvent_Tentative:
fb.append(PyCalendarFreeBusy(PyCalendarFreeBusy.BUSYTENTATIVE, PyCalendarPeriod(comp.getInstanceStart(), comp.getInstanceEnd())))
break
elif status == definitions.eStatus_VEvent_Cancelled:
# Cancelled => does not contribute to busy time
pass
# Now get the VFREEBUSY info
list2 = []
self.getVFreeBusy(period, list2)
# Get start/end list for each free-busy
for comp in list2:
# Expand component and add free busy info to list
comp.expandPeriod(period, fb)
# Add remaining period as property
PyCalendarFreeBusy.resolveOverlaps(fb)
def getTimezoneOffsetSeconds(self, tzid, dt, relative_to_utc=False):
# Find timezone that matches the name (which is the same as the map key)
timezone = self.getTimezone(tzid)
return timezone.getTimezoneOffsetSeconds(dt, relative_to_utc) if timezone else 0
def getTimezoneDescriptor(self, tzid, dt):
# Find timezone that matches the name (which is the same as the map key)
timezone = self.getTimezone(tzid)
return timezone.getTimezoneDescriptor(dt) if timezone else ""
def getTimezone(self, tzid):
# Find timezone that matches the name (which is the same as the map key)
for timezone in self.getComponents(definitions.cICalComponent_VTIMEZONE):
if timezone.getID() == tzid:
return timezone
else:
return None
def addDefaultProperties(self):
self.addProperty(PyCalendarProperty(definitions.cICalProperty_PRODID, PyCalendar.sProdID))
self.addProperty(PyCalendarProperty(definitions.cICalProperty_VERSION, "2.0"))
self.addProperty(PyCalendarProperty(definitions.cICalProperty_CALSCALE, "GREGORIAN"))
def validProperty(self, prop):
if prop.getName() == definitions.cICalProperty_VERSION:
tvalue = prop.getTextValue()
if ((tvalue is None) or (tvalue.getValue() != "2.0")):
return False
elif prop.getName() == definitions.cICalProperty_CALSCALE:
tvalue = prop.getTextValue()
if ((tvalue is None) or (tvalue.getValue() != "GREGORIAN")):
return False
return True
def ignoreProperty(self, prop):
return False #prop.getName() in (definitions.cICalProperty_VERSION, definitions.cICalProperty_CALSCALE, definitions.cICalProperty_PRODID)
def includeTimezones(self):
# Get timezone names from each component
tzids = set()
for component in self.mComponents:
if component.getType() != definitions.cICalComponent_VTIMEZONE:
component.getTimezones(tzids)
# Make sure each timezone is in current calendar
from timezonedb import PyCalendarTimezoneDatabase
for tzid in tzids:
tz = self.getTimezone(tzid)
if tz is None:
# Find it in the static object
tz = PyCalendarTimezoneDatabase.getTimezone(tzid)
if tz is not None:
dup = tz.duplicate()
self.addComponent(dup)
@staticmethod
def makeComponent(compname, parent):
mapper = {
definitions.cICalComponent_VEVENT: PyCalendarVEvent,
definitions.cICalComponent_VTODO: PyCalendarVToDo,
definitions.cICalComponent_VJOURNAL: PyCalendarVJournal,
definitions.cICalComponent_VFREEBUSY: PyCalendarVFreeBusy,
definitions.cICalComponent_VTIMEZONE: PyCalendarVTimezone,
definitions.cICalComponent_VAVAILABILITY: PyCalendarVAvailability,
definitions.cICalComponent_VALARM: PyCalendarVAlarm,
definitions.cICalComponent_AVAILABLE: PyCalendarAvailable,
definitions.cICalComponent_STANDARD: PyCalendarVTimezoneStandard,
definitions.cICalComponent_DAYLIGHT: PyCalendarVTimezoneDaylight,
}
try:
cls = mapper[compname]
return cls(parent=parent)
except KeyError:
return PyCalendarUnknownComponent(parent=parent, comptype=compname)
pycalendar-2.0~svn13177/src/pycalendar/urivalue.py 0000644 0001750 0001750 00000003524 12126720253 021160 0 ustar rahul rahul ##
# Copyright (c) 2007-2013 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# iCalendar URI value
from pycalendar import xmldefs, utils
from pycalendar.plaintextvalue import PyCalendarPlainTextValue
from pycalendar.value import PyCalendarValue
from pycalendar.parser import ParserContext
class PyCalendarURIValue(PyCalendarPlainTextValue):
def getType(self):
return PyCalendarURIValue.VALUETYPE_URI
def parse(self, data):
if ParserContext.BACKSLASH_IN_URI_VALUE == ParserContext.PARSER_FIX:
# Decoding required
self.mValue = utils.decodeTextValue(data)
else:
# No decoding required
self.mValue = data
# os - StringIO object
def generate(self, os):
"""
Handle a client bug where it sometimes includes a \n in the value and we need
to make sure that gets encoded rather than included literally which would break syntax.
"""
if '\n' in self.mValue:
try:
# No encoding required
os.write(self.mValue.replace("\n", "\\n"))
except:
pass
else:
super(PyCalendarURIValue, self).generate(os)
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_URI, PyCalendarURIValue, xmldefs.value_uri)
pycalendar-2.0~svn13177/src/pycalendar/caladdressvalue.py 0000644 0001750 0001750 00000002056 12101017573 022462 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# iCalendar UTC Offset value
from pycalendar import xmldefs
from pycalendar.plaintextvalue import PyCalendarPlainTextValue
from pycalendar.value import PyCalendarValue
class PyCalendarCalAddressValue(PyCalendarPlainTextValue):
def getType(self):
return PyCalendarValue.VALUETYPE_CALADDRESS
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_CALADDRESS, PyCalendarCalAddressValue, xmldefs.value_cal_address)
pycalendar-2.0~svn13177/src/pycalendar/valarm.py 0000644 0001750 0001750 00000061117 12101017573 020605 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar.attribute import PyCalendarAttribute
from pycalendar.component import PyCalendarComponent
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.duration import PyCalendarDuration
from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
from pycalendar.property import PyCalendarProperty
from pycalendar.value import PyCalendarValue
class PyCalendarVAlarm(PyCalendarComponent):
sActionMap = {
definitions.cICalProperty_ACTION_AUDIO: definitions.eAction_VAlarm_Audio,
definitions.cICalProperty_ACTION_DISPLAY: definitions.eAction_VAlarm_Display,
definitions.cICalProperty_ACTION_EMAIL: definitions.eAction_VAlarm_Email,
definitions.cICalProperty_ACTION_PROCEDURE: definitions.eAction_VAlarm_Procedure,
definitions.cICalProperty_ACTION_URI: definitions.eAction_VAlarm_URI,
definitions.cICalProperty_ACTION_NONE: definitions.eAction_VAlarm_None,
}
sActionValueMap = {
definitions.eAction_VAlarm_Audio: definitions.cICalProperty_ACTION_AUDIO,
definitions.eAction_VAlarm_Display: definitions.cICalProperty_ACTION_DISPLAY,
definitions.eAction_VAlarm_Email: definitions.cICalProperty_ACTION_EMAIL,
definitions.eAction_VAlarm_Procedure: definitions.cICalProperty_ACTION_PROCEDURE,
definitions.eAction_VAlarm_URI: definitions.cICalProperty_ACTION_URI,
definitions.eAction_VAlarm_None: definitions.cICalProperty_ACTION_NONE,
}
# Classes for each action encapsulating action-specific data
class PyCalendarVAlarmAction(object):
propertyCardinality_1 = ()
propertyCardinality_1_Fix_Empty = ()
propertyCardinality_0_1 = ()
propertyCardinality_1_More = ()
def __init__(self, type):
self.mType = type
def duplicate(self):
return PyCalendarVAlarm.PyCalendarVAlarmAction(self.mType)
def load(self, valarm):
pass
def add(self, valarm):
pass
def remove(self, valarm):
pass
def getType(self):
return self.mType
class PyCalendarVAlarmAudio(PyCalendarVAlarmAction):
propertyCardinality_1 = (
definitions.cICalProperty_ACTION,
definitions.cICalProperty_TRIGGER,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_DURATION,
definitions.cICalProperty_REPEAT,
definitions.cICalProperty_ATTACH,
definitions.cICalProperty_ACKNOWLEDGED,
)
def __init__(self, speak=None):
super(PyCalendarVAlarm.PyCalendarVAlarmAudio, self).__init__(type=definitions.eAction_VAlarm_Audio)
self.mSpeakText = speak
def duplicate(self):
return PyCalendarVAlarm.PyCalendarVAlarmAudio(self.mSpeakText)
def load(self, valarm):
# Get properties
self.mSpeakText = valarm.loadValueString(definitions.cICalProperty_ACTION_X_SPEAKTEXT)
def add(self, valarm):
# Delete existing then add
self.remove(valarm)
prop = PyCalendarProperty(definitions.cICalProperty_ACTION_X_SPEAKTEXT, self.mSpeakText)
valarm.addProperty(prop)
def remove(self, valarm):
valarm.removeProperties(definitions.cICalProperty_ACTION_X_SPEAKTEXT)
def isSpeakText(self):
return len(self.mSpeakText) != 0
def getSpeakText(self):
return self.mSpeakText
class PyCalendarVAlarmDisplay(PyCalendarVAlarmAction):
propertyCardinality_1 = (
definitions.cICalProperty_ACTION,
definitions.cICalProperty_TRIGGER,
)
propertyCardinality_1_Fix_Empty = (
definitions.cICalProperty_DESCRIPTION,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_DURATION,
definitions.cICalProperty_REPEAT,
definitions.cICalProperty_ACKNOWLEDGED,
)
def __init__(self, description=None):
super(PyCalendarVAlarm.PyCalendarVAlarmDisplay, self).__init__(type=definitions.eAction_VAlarm_Display)
self.mDescription = description
def duplicate(self):
return PyCalendarVAlarm.PyCalendarVAlarmDisplay(self.mDescription)
def load(self, valarm):
# Get properties
self.mDescription = valarm.loadValueString(definitions.cICalProperty_DESCRIPTION)
def add(self, valarm):
# Delete existing then add
self.remove(valarm)
prop = PyCalendarProperty(definitions.cICalProperty_DESCRIPTION, self.mDescription)
valarm.addProperty(prop)
def remove(self, valarm):
valarm.removeProperties(definitions.cICalProperty_DESCRIPTION)
def getDescription(self):
return self.mDescription
class PyCalendarVAlarmEmail(PyCalendarVAlarmAction):
propertyCardinality_1 = (
definitions.cICalProperty_ACTION,
definitions.cICalProperty_TRIGGER,
)
propertyCardinality_1_Fix_Empty = (
definitions.cICalProperty_DESCRIPTION,
definitions.cICalProperty_SUMMARY,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_DURATION,
definitions.cICalProperty_REPEAT,
definitions.cICalProperty_ACKNOWLEDGED,
)
propertyCardinality_1_More = (
definitions.cICalProperty_ATTENDEE,
)
def __init__(self, description=None, summary=None, attendees=None):
super(PyCalendarVAlarm.PyCalendarVAlarmEmail, self).__init__(type=definitions.eAction_VAlarm_Email)
self.mDescription = description
self.mSummary = summary
self.mAttendees = attendees
def duplicate(self):
return PyCalendarVAlarm.PyCalendarVAlarmEmail(self.mDescription, self.mSummary, self.mAttendees)
def load(self, valarm):
# Get properties
self.mDescription = valarm.loadValueString(definitions.cICalProperty_DESCRIPTION)
self.mSummary = valarm.loadValueString(definitions.cICalProperty_SUMMARY)
self.mAttendees = []
if valarm.hasProperty(definitions.cICalProperty_ATTENDEE):
# Get each attendee
range = valarm.getProperties().get(definitions.cICalProperty_ATTENDEE, ())
for iter in range:
# Get the attendee value
attendee = iter.getCalAddressValue()
if attendee is not None:
self.mAttendees.append(attendee.getValue())
def add(self, valarm):
# Delete existing then add
self.remove(valarm)
prop = PyCalendarProperty(definitions.cICalProperty_DESCRIPTION, self.mDescription)
valarm.addProperty(prop)
prop = PyCalendarProperty(definitions.cICalProperty_SUMMARY, self.mSummary)
valarm.addProperty(prop)
for iter in self.mAttendees:
prop = PyCalendarProperty(definitions.cICalProperty_ATTENDEE, iter, PyCalendarValue.VALUETYPE_CALADDRESS)
valarm.addProperty(prop)
def remove(self, valarm):
valarm.removeProperties(definitions.cICalProperty_DESCRIPTION)
valarm.removeProperties(definitions.cICalProperty_SUMMARY)
valarm.removeProperties(definitions.cICalProperty_ATTENDEE)
def getDescription(self):
return self.mDescription
def getSummary(self):
return self.mSummary
def getAttendees(self):
return self.mAttendees
class PyCalendarVAlarmUnknown(PyCalendarVAlarmAction):
propertyCardinality_1 = (
definitions.cICalProperty_ACTION,
definitions.cICalProperty_TRIGGER,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_DURATION,
definitions.cICalProperty_REPEAT,
definitions.cICalProperty_ACKNOWLEDGED,
)
def __init__(self):
super(PyCalendarVAlarm.PyCalendarVAlarmUnknown, self).__init__(type=definitions.eAction_VAlarm_Unknown)
def duplicate(self):
return PyCalendarVAlarm.PyCalendarVAlarmUnknown()
class PyCalendarVAlarmURI(PyCalendarVAlarmAction):
propertyCardinality_1 = (
definitions.cICalProperty_ACTION,
definitions.cICalProperty_TRIGGER,
definitions.cICalProperty_URL,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_DURATION,
definitions.cICalProperty_REPEAT,
definitions.cICalProperty_ACKNOWLEDGED,
)
def __init__(self, uri=None):
super(PyCalendarVAlarm.PyCalendarVAlarmURI, self).__init__(type=definitions.eAction_VAlarm_URI)
self.mURI = uri
def duplicate(self):
return PyCalendarVAlarm.PyCalendarVAlarmURI(self.mURI)
def load(self, valarm):
# Get properties
self.mURI = valarm.loadValueString(definitions.cICalProperty_URL)
def add(self, valarm):
# Delete existing then add
self.remove(valarm)
prop = PyCalendarProperty(definitions.cICalProperty_URL, self.mURI)
valarm.addProperty(prop)
def remove(self, valarm):
valarm.removeProperties(definitions.cICalProperty_URL)
def getURI(self):
return self.mURI
class PyCalendarVAlarmNone(PyCalendarVAlarmAction):
propertyCardinality_1 = (
definitions.cICalProperty_ACTION,
)
def __init__(self):
super(PyCalendarVAlarm.PyCalendarVAlarmNone, self).__init__(type=definitions.eAction_VAlarm_None)
def duplicate(self):
return PyCalendarVAlarm.PyCalendarVAlarmNone()
def getMimeComponentName(self):
# Cannot be sent as a separate MIME object
return None
sActionToAlarmMap = {
definitions.eAction_VAlarm_Audio: PyCalendarVAlarmAudio,
definitions.eAction_VAlarm_Display: PyCalendarVAlarmDisplay,
definitions.eAction_VAlarm_Email: PyCalendarVAlarmEmail,
definitions.eAction_VAlarm_URI: PyCalendarVAlarmURI,
definitions.eAction_VAlarm_None: PyCalendarVAlarmNone,
}
propertyValueChecks = ICALENDAR_VALUE_CHECKS
def __init__(self, parent=None):
super(PyCalendarVAlarm, self).__init__(parent=parent)
self.mAction = definitions.eAction_VAlarm_Display
self.mTriggerAbsolute = False
self.mTriggerOnStart = True
self.mTriggerOn = PyCalendarDateTime()
# Set duration default to 1 hour
self.mTriggerBy = PyCalendarDuration()
self.mTriggerBy.setDuration(60 * 60)
# Does not repeat by default
self.mRepeats = 0
self.mRepeatInterval = PyCalendarDuration()
self.mRepeatInterval.setDuration(5 * 60) # Five minutes
# Status
self.mStatusInit = False
self.mAlarmStatus = definitions.eAlarm_Status_Pending
self.mLastTrigger = PyCalendarDateTime()
self.mNextTrigger = PyCalendarDateTime()
self.mDoneCount = 0
# Create action data
self.mActionData = PyCalendarVAlarm.PyCalendarVAlarmDisplay("")
def duplicate(self, parent=None):
other = super(PyCalendarVAlarm, self).duplicate(parent=parent)
other.mAction = self.mAction
other.mTriggerAbsolute = self.mTriggerAbsolute
other.mTriggerOn = self.mTriggerOn.duplicate()
other.mTriggerBy = self.mTriggerBy.duplicate()
other.mTriggerOnStart = self.mTriggerOnStart
other.mRepeats = self.mRepeats
other.mRepeatInterval = self.mRepeatInterval.duplicate()
other.mAlarmStatus = self.mAlarmStatus
if self.mLastTrigger is not None:
other.mLastTrigger = self.mLastTrigger.duplicate()
if self.mNextTrigger is not None:
other.mNextTrigger = self.mNextTrigger.duplicate()
other.mDoneCount = self.mDoneCount
other.mActionData = self.mActionData.duplicate()
return other
def getType(self):
return definitions.cICalComponent_VALARM
def getAction(self):
return self.mAction
def getActionData(self):
return self.mActionData
def isTriggerAbsolute(self):
return self.mTriggerAbsolute
def getTriggerOn(self):
return self.mTriggerOn
def getTriggerDuration(self):
return self.mTriggerBy
def isTriggerOnStart(self):
return self.mTriggerOnStart
def getRepeats(self):
return self.mRepeats
def getInterval(self):
return self.mRepeatInterval
def added(self):
# Added to calendar so add to calendar notifier
# calstore::CCalendarNotifier::sCalendarNotifier.AddAlarm(this)
# Do inherited
super(PyCalendarVAlarm, self).added()
def removed(self):
# Removed from calendar so add to calendar notifier
# calstore::CCalendarNotifier::sCalendarNotifier.RemoveAlarm(this)
# Do inherited
super(PyCalendarVAlarm, self).removed()
def changed(self):
# Always force recalc of trigger status
self.mStatusInit = False
# Changed in calendar so change in calendar notifier
# calstore::CCalendarNotifier::sCalendarNotifier.ChangedAlarm(this)
# Do not do inherited as this is always a sub-component and we do not
# do top-level component changes
# super.changed()
def finalise(self):
# Do inherited
super(PyCalendarVAlarm, self).finalise()
# Get the ACTION
temp = self.loadValueString(definitions.cICalProperty_ACTION)
if temp is not None:
self.mAction = PyCalendarVAlarm.sActionMap.get(temp, definitions.eAction_VAlarm_Unknown)
self.loadAction()
# Get the trigger
if self.hasProperty(definitions.cICalProperty_TRIGGER):
# Determine the type of the value
temp1 = self.loadValueDateTime(definitions.cICalProperty_TRIGGER)
temp2 = self.loadValueDuration(definitions.cICalProperty_TRIGGER)
if temp1 is not None:
self.mTriggerAbsolute = True
self.mTriggerOn = temp1
elif temp2 is not None:
self.mTriggerAbsolute = False
self.mTriggerBy = temp2
# Get the property
prop = self.findFirstProperty(definitions.cICalProperty_TRIGGER)
# Look for RELATED attribute
if prop.hasAttribute(definitions.cICalAttribute_RELATED):
temp = prop.getAttributeValue(definitions.cICalAttribute_RELATED)
if temp == definitions.cICalAttribute_RELATED_START:
self.mTriggerOnStart = True
elif temp == definitions.cICalAttribute_RELATED_END:
self.mTriggerOnStart = False
else:
self.mTriggerOnStart = True
# Get repeat & interval
temp = self.loadValueInteger(definitions.cICalProperty_REPEAT)
if temp is not None:
self.mRepeats = temp
temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
if temp is not None:
self.mRepeatInterval = temp
# Set a map key for sorting
self.mMapKey = "%s:%s" % (self.mAction, self.mTriggerOn if self.mTriggerAbsolute else self.mTriggerBy,)
# Alarm status - private to Mulberry
status = self.loadValueString(definitions.cICalProperty_ALARM_X_ALARMSTATUS)
if status is not None:
if status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING:
self.mAlarmStatus = definitions.eAlarm_Status_Pending
elif status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED:
self.mAlarmStatus = definitions.eAlarm_Status_Completed
elif status == definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED:
self.mAlarmStatus = definitions.eAlarm_Status_Disabled
else:
self.mAlarmStatus = definitions.eAlarm_Status_Pending
# Last trigger time - private to Mulberry
temp = self.loadValueDateTime(definitions.cICalProperty_ALARM_X_LASTTRIGGER)
if temp is not None:
self.mLastTrigger = temp
def validate(self, doFix=False):
"""
Validate the data in this component and optionally fix any problems, else raise. If
loggedProblems is not None it must be a C{list} and problem descriptions are appended
to that.
"""
# Validate using action specific constraints
self.propertyCardinality_1 = self.mActionData.propertyCardinality_1
self.propertyCardinality_1_Fix_Empty = self.mActionData.propertyCardinality_1_Fix_Empty
self.propertyCardinality_0_1 = self.mActionData.propertyCardinality_0_1
self.propertyCardinality_1_More = self.mActionData.propertyCardinality_1_More
fixed, unfixed = super(PyCalendarVAlarm, self).validate(doFix)
# Extra constraint: both DURATION and REPEAT must be present togethe
if self.hasProperty(definitions.cICalProperty_DURATION) ^ self.hasProperty(definitions.cICalProperty_REPEAT):
# Cannot fix this
logProblem = "[%s] Properties must be present together: %s, %s" % (
self.getType(),
definitions.cICalProperty_DURATION,
definitions.cICalProperty_REPEAT,
)
unfixed.append(logProblem)
return fixed, unfixed
def editStatus(self, status):
# Remove existing
self.removeProperties(definitions.cICalProperty_ALARM_X_ALARMSTATUS)
# Updated cached values
self.mAlarmStatus = status
# Add new
status_txt = ""
if self.mAlarmStatus == definitions.eAlarm_Status_Pending:
status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING
elif self.mAlarmStatus == definitions.eAlarm_Status_Completed:
status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED
elif self.mAlarmStatus == definitions.eAlarm_Status_Disabled:
status_txt = definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED
self.addProperty(PyCalendarProperty(definitions.cICalProperty_ALARM_X_ALARMSTATUS, status_txt))
def editAction(self, action, data):
# Remove existing
self.removeProperties(definitions.cICalProperty_ACTION)
self.mActionData.remove(self)
self.mActionData = None
# Updated cached values
self.mAction = action
self.mActionData = data
# Add new properties to alarm
action_txt = PyCalendarVAlarm.sActionValueMap.get(self.mAction, definitions.cICalProperty_ACTION_PROCEDURE)
prop = PyCalendarProperty(definitions.cICalProperty_ACTION, action_txt)
self.addProperty(prop)
self.mActionData.add(self)
def editTriggerOn(self, dt):
# Remove existing
self.removeProperties(definitions.cICalProperty_TRIGGER)
# Updated cached values
self.mTriggerAbsolute = True
self.mTriggerOn = dt
# Add new
prop = PyCalendarProperty(definitions.cICalProperty_TRIGGER, dt)
self.addProperty(prop)
def editTriggerBy(self, duration, trigger_start):
# Remove existing
self.removeProperties(definitions.cICalProperty_TRIGGER)
# Updated cached values
self.mTriggerAbsolute = False
self.mTriggerBy = duration
self.mTriggerOnStart = trigger_start
# Add new (with attribute)
prop = PyCalendarProperty(definitions.cICalProperty_TRIGGER, duration)
attr = PyCalendarAttribute(definitions.cICalAttribute_RELATED,
(definitions.cICalAttribute_RELATED_START,
definitions.cICalAttribute_RELATED_END)[not trigger_start])
prop.addAttribute(attr)
self.addProperty(prop)
def editRepeats(self, repeat, interval):
# Remove existing
self.removeProperties(definitions.cICalProperty_REPEAT)
self.removeProperties(definitions.cICalProperty_DURATION)
# Updated cached values
self.mRepeats = repeat
self.mRepeatInterval = interval
# Add new
if self.mRepeats > 0:
self.addProperty(PyCalendarProperty(definitions.cICalProperty_REPEAT, repeat))
self.addProperty(PyCalendarProperty(definitions.cICalProperty_DURATION, interval))
def getAlarmStatus(self):
return self.mAlarmStatus
def getNextTrigger(self, dt):
if not self.mStatusInit:
self.initNextTrigger()
dt.copy(self.mNextTrigger)
def alarmTriggered(self, dt):
# Remove existing
self.removeProperties(definitions.cICalProperty_ALARM_X_LASTTRIGGER)
self.removeProperties(definitions.cICalProperty_ALARM_X_ALARMSTATUS)
# Updated cached values
self.mLastTrigger.copy(dt)
if self.mDoneCount < self.mRepeats:
self.mNextTrigger = self.mLastTrigger + self.mRepeatInterval
dt.copy(self.mNextTrigger)
self.mDoneCount += 1
self.mAlarmStatus = definitions.eAlarm_Status_Pending
else:
self.mAlarmStatus = definitions.eAlarm_Status_Completed
# Add new
self.addProperty(PyCalendarProperty(definitions.cICalProperty_ALARM_X_LASTTRIGGER, dt))
status = ""
if self.mAlarmStatus == definitions.eAlarm_Status_Pending:
status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_PENDING
elif self.mAlarmStatus == definitions.eAlarm_Status_Completed:
status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED
elif self.mAlarmStatus == definitions.eAlarm_Status_Disabled:
status = definitions.cICalProperty_ALARM_X_ALARMSTATUS_DISABLED
self.addProperty(PyCalendarProperty(definitions.cICalProperty_ALARM_X_ALARMSTATUS, status))
# Now update dt to the next alarm time
return self.mAlarmStatus == definitions.eAlarm_Status_Pending
def loadAction(self):
# Delete current one
self.mActionData = None
self.mActionData = PyCalendarVAlarm.sActionToAlarmMap.get(self.mAction, PyCalendarVAlarm.PyCalendarVAlarmUnknown)()
self.mActionData.load(self)
def initNextTrigger(self):
# Do not bother if its completed
if self.mAlarmStatus == definitions.eAlarm_Status_Completed:
return
self.mStatusInit = True
# Look for trigger immediately preceeding or equal to utc now
nowutc = PyCalendarDateTime.getNowUTC()
# Init done counter
self.mDoneCount = 0
# Determine the first trigger
trigger = PyCalendarDateTime()
self.getFirstTrigger(trigger)
while self.mDoneCount < self.mRepeats:
# See if next trigger is later than now
next_trigger = trigger + self.mRepeatInterval
if next_trigger > nowutc:
break
self.mDoneCount += 1
trigger = next_trigger
# Check for completion
if trigger == self.mLastTrigger or (nowutc - trigger).getTotalSeconds() > 24 * 60 * 60:
if self.mDoneCount == self.mRepeats:
self.mAlarmStatus = definitions.eAlarm_Status_Completed
return
else:
trigger = trigger + self.mRepeatInterval
self.mDoneCount += 1
self.mNextTrigger = trigger
def getFirstTrigger(self, dt):
# If absolute trigger, use that
if self.isTriggerAbsolute():
# Get the trigger on
dt.copy(self.getTriggerOn())
else:
# Get the parent embedder class (must be CICalendarComponentRecur type)
owner = self.getEmbedder()
if owner is not None:
# Determine time at which alarm will trigger
trigger = (owner.getStart(), owner.getEnd())[not self.isTriggerOnStart()]
# Offset by duration
dt.copy(trigger + self.getTriggerDuration())
pycalendar-2.0~svn13177/src/pycalendar/manager.py 0000644 0001750 0001750 00000003533 12101017573 020733 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.timezone import PyCalendarTimezone
class PyCalendarManager(object):
sICalendarManager = None
def __init__(self):
PyCalendarTimezone.sDefaultTimezone = PyCalendarTimezone()
def initManager(self):
# TODO: - read in timezones from vtimezones.ics file
# Eventually we need to read these from prefs - for now they are
# hard-coded to my personal prefs!
self.setDefaultTimezone(PyCalendarTimezone(utc=False, tzid="US/Eastern"))
def setDefaultTimezoneID(self, tzid):
# Check for UTC
if tzid == "UTC":
temp = PyCalendarTimezone(utc=True)
self.setDefaultTimezone(temp)
else:
temp = PyCalendarTimezone(utc=False, tzid=tzid)
self.setDefaultTimezone(temp)
def setDefaultTimezone(self, tzid):
PyCalendarTimezone.sDefaultTimezone = tzid
def getDefaultTimezoneID(self):
if PyCalendarTimezone.sDefaultTimezone.getUTC():
return "UTC"
else:
return PyCalendarTimezone.sDefaultTimezone.getTimezoneID()
def getDefaultTimezone(self):
return PyCalendarTimezone.sDefaultTimezone
PyCalendarManager.sICalendarManager = PyCalendarManager()
pycalendar-2.0~svn13177/src/pycalendar/durationvalue.py 0000644 0001750 0001750 00000003062 12101017573 022200 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import xmldefs
from pycalendar.duration import PyCalendarDuration
from pycalendar.value import PyCalendarValue
class PyCalendarDurationValue(PyCalendarValue):
def __init__(self, value=None):
self.mValue = value if value is not None else PyCalendarDuration()
def duplicate(self):
return PyCalendarDurationValue(self.mValue.duplicate())
def getType(self):
return PyCalendarValue.VALUETYPE_DURATION
def parse(self, data):
self.mValue.parse(data)
def generate(self, os):
self.mValue.generate(os)
def writeXML(self, node, namespace):
value = self.getXMLNode(node, namespace)
value.text = self.mValue.writeXML()
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_DURATION, PyCalendarDurationValue, xmldefs.value_duration)
pycalendar-2.0~svn13177/src/pycalendar/integervalue.py 0000644 0001750 0001750 00000003101 12101017573 022002 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# iCalendar UTC Offset value
from pycalendar import xmldefs
from pycalendar.value import PyCalendarValue
class PyCalendarIntegerValue(PyCalendarValue):
def __init__(self, value=None):
self.mValue = value if value is not None else 0
def duplicate(self):
return PyCalendarIntegerValue(self.mValue)
def getType(self):
return PyCalendarValue.VALUETYPE_INTEGER
def parse(self, data):
self.mValue = int(data)
# os - StringIO object
def generate(self, os):
try:
os.write(str(self.mValue))
except:
pass
def writeXML(self, node, namespace):
value = self.getXMLNode(node, namespace)
value.text = str(self.mValue)
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_INTEGER, PyCalendarIntegerValue, xmldefs.value_integer)
pycalendar-2.0~svn13177/src/pycalendar/parser.py 0000644 0001750 0001750 00000006203 12301206170 020605 0 ustar rahul rahul ##
# Copyright (c) 2011-2013 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
class ParserContext(object):
"""
Ultimately want to have these states as per-object so we can pass a context down
through the entire parse call chain so that we can use different error handling for
different situations. For now though it is a module static.
"""
(
PARSER_ALLOW, # Pass the "suspect" data through to the object model
PARSER_IGNORE, # Ignore the "suspect" data
PARSER_FIX, # Fix (or if not possible ignore) the "suspect" data
PARSER_RAISE, # Raise an exception
) = range(4)
# Some clients escape ":" - fix
INVALID_COLON_ESCAPE_SEQUENCE = PARSER_FIX
# Other escape sequences - raise
INVALID_ESCAPE_SEQUENCES = PARSER_RAISE
# Some client generate empty lines in the body of the data
BLANK_LINES_IN_DATA = PARSER_FIX
# Some clients still generate vCard 2 parameter syntax
VCARD_2_NO_PARAMETER_VALUES = PARSER_ALLOW
# Use this to fix v2 BASE64 to v3 ENCODING=b - only PARSER_FIX or PARSER_ALLOW
VCARD_2_BASE64 = PARSER_FIX
# Allow DATE values when DATETIME specified (and vice versa)
INVALID_DATETIME_VALUE = PARSER_FIX
# Allow leading space instead of leading zeros for year in DATE or DATE-TIME values
INVALID_DATETIME_LEADINGSPACE = PARSER_ALLOW
# Allow slightly invalid DURATION values
INVALID_DURATION_VALUE = PARSER_FIX
# Truncate over long ADR and N values
INVALID_ADR_N_VALUES = PARSER_FIX
# REQUEST-STATUS values with \; as the first separator or single element
INVALID_REQUEST_STATUS_VALUE = PARSER_FIX
# Remove \-escaping in URI values when parsing - only PARSER_FIX or PARSER_ALLOW
BACKSLASH_IN_URI_VALUE = PARSER_FIX
@staticmethod
def allRaise():
"""
Make all tests raise an error - never fix
"""
ParserContext.INVALID_COLON_ESCAPE_SEQUENCE = ParserContext.PARSER_RAISE
ParserContext.INVALID_ESCAPE_SEQUENCES = ParserContext.PARSER_RAISE
ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_RAISE
ParserContext.VCARD_2_NO_PARAMETER_VALUES = ParserContext.PARSER_RAISE
ParserContext.VCARD_2_BASE64 = ParserContext.PARSER_RAISE
ParserContext.INVALID_DURATION_VALUE = ParserContext.PARSER_RAISE
ParserContext.INVALID_ADR_N_VALUES = ParserContext.PARSER_RAISE
ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_RAISE
ParserContext.BACKSLASH_IN_URI_VALUE = ParserContext.PARSER_RAISE
ParserContext.INVALID_REQUEST_STATUS = ParserContext.PARSER_RAISE
pycalendar-2.0~svn13177/src/pycalendar/icalendar/ 0000755 0001750 0001750 00000000000 12322631215 020665 5 ustar rahul rahul pycalendar-2.0~svn13177/src/pycalendar/icalendar/validation.py 0000644 0001750 0001750 00000003212 12101017573 023367 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar.validation import partial, PropertyValueChecks
ICALENDAR_VALUE_CHECKS = {
definitions.cICalProperty_CALSCALE: partial(PropertyValueChecks.stringValue, "GREGORIAN"),
definitions.cICalProperty_VERSION: partial(PropertyValueChecks.stringValue, "2.0"),
definitions.cICalProperty_PERCENT_COMPLETE: partial(PropertyValueChecks.numericRange, 0, 100),
definitions.cICalProperty_PRIORITY: partial(PropertyValueChecks.numericRange, 0, 9),
definitions.cICalProperty_COMPLETED: PropertyValueChecks.alwaysUTC,
definitions.cICalProperty_REPEAT: PropertyValueChecks.positiveIntegerOrZero,
definitions.cICalProperty_CREATED: PropertyValueChecks.alwaysUTC,
definitions.cICalProperty_DTSTAMP: PropertyValueChecks.alwaysUTC,
definitions.cICalProperty_LAST_MODIFIED: PropertyValueChecks.alwaysUTC,
definitions.cICalProperty_SEQUENCE: PropertyValueChecks.positiveIntegerOrZero,
definitions.cICalProperty_ACKNOWLEDGED: PropertyValueChecks.alwaysUTC,
}
pycalendar-2.0~svn13177/src/pycalendar/icalendar/tests/ 0000755 0001750 0001750 00000000000 12322631215 022027 5 ustar rahul rahul pycalendar-2.0~svn13177/src/pycalendar/icalendar/tests/test_validation.py 0000644 0001750 0001750 00000247077 12101017573 025613 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.calendar import PyCalendar
from pycalendar.exceptions import PyCalendarValidationError
import unittest
class TestValidation(unittest.TestCase):
def test_basic(self):
data = (
(
"No problems",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"No PRODID",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VCALENDAR] Missing or too many required property: PRODID",
)),
),
)
for title, item, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(item)
fixed, unfixed = cal.validate(doFix=False)
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_mode_no_fix_no_raise(self):
data = (
(
"OK",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Unfixable only",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VCALENDAR] Missing or too many required property: PRODID",
)),
),
(
"Fixable only",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VEVENT] Properties must not both be present: DTEND, DURATION",
)),
),
(
"Fixable and unfixable",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VCALENDAR] Missing or too many required property: PRODID",
"[VEVENT] Properties must not both be present: DTEND, DURATION",
)),
),
)
for title, test_old, test_new, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(test_old)
fixed, unfixed = cal.validate(doFix=False, doRaise=False)
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_mode_fix_no_raise(self):
data = (
(
"OK",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Unfixable only",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VCALENDAR] Missing or too many required property: PRODID",
)),
),
(
"Fixable only",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set((
"[VEVENT] Properties must not both be present: DTEND, DURATION",
)),
set(),
),
(
"Fixable and unfixable",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set((
"[VEVENT] Properties must not both be present: DTEND, DURATION",
)),
set((
"[VCALENDAR] Missing or too many required property: PRODID",
)),
),
)
for title, test_old, test_new, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(test_old)
fixed, unfixed = cal.validate(doFix=True, doRaise=False)
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_mode_no_fix_raise(self):
data = (
(
"OK",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
False,
),
(
"Unfixable only",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VCALENDAR] Missing or too many required property: PRODID",
)),
True,
),
(
"Fixable only",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VEVENT] Properties must not both be present: DTEND, DURATION",
)),
True,
),
(
"Fixable and unfixable",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VCALENDAR] Missing or too many required property: PRODID",
"[VEVENT] Properties must not both be present: DTEND, DURATION",
)),
True,
),
)
for title, test_old, test_new, test_fixed, test_unfixed, test_raises in data:
cal = PyCalendar.parseText(test_old)
if test_raises:
self.assertRaises(PyCalendarValidationError, cal.validate, doFix=False, doRaise=True)
else:
try:
fixed, unfixed = cal.validate(doFix=False, doRaise=False)
except:
self.fail(msg="Failed test: %s" % (title,))
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_mode_fix_raise(self):
data = (
(
"OK",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
False,
),
(
"Unfixable only",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VCALENDAR] Missing or too many required property: PRODID",
)),
True,
),
(
"Fixable only",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set((
"[VEVENT] Properties must not both be present: DTEND, DURATION",
)),
set(),
False,
),
(
"Fixable and unfixable",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set((
"[VEVENT] Properties must not both be present: DTEND, DURATION",
)),
set((
"[VCALENDAR] Missing or too many required property: PRODID",
)),
True,
),
)
for title, test_old, test_new, test_fixed, test_unfixed, test_raises in data:
cal = PyCalendar.parseText(test_old)
if test_raises:
self.assertRaises(PyCalendarValidationError, cal.validate, doFix=False, doRaise=True)
else:
try:
fixed, unfixed = cal.validate(doFix=True, doRaise=False)
except:
self.fail(msg="Failed test: %s" % (title,))
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_vevent(self):
data = (
(
"No problem",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Missing required",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VEVENT] Missing or too many required property: DTSTAMP",
)),
),
(
"Too many",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
UID:C3184A66-1ED0-11D9-A5E0-000A958A3253
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
SUMMARY:New Year's Eve
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
UID:C3184A66-1ED0-11D9-A5E0-000A958A3253
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
SUMMARY:New Year's Eve
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VEVENT] Missing or too many required property: UID",
"[VEVENT] Too many properties present: SUMMARY",
)),
),
(
"PROP value",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VEVENT] Property value incorrect: DTSTAMP",
)),
),
(
"No DTSTART without METHOD",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:CANCEL
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTAMP:20020101T000000Z
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:CANCEL
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTAMP:20020101T000000Z
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Combo fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set((
"[VEVENT] Properties must not both be present: DTEND, DURATION",
)),
set(),
),
(
"Mismatch UNTIL as DATE-TIME",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231T120000;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set((
"[VEVENT] Value types must match: DTSTART, UNTIL",
)),
set(),
),
(
"Mismatch UNTIL as DATE",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART:20020101T121212Z
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART:20020101T121212Z
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231T121212Z;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set((
"[VEVENT] Value types must match: DTSTART, UNTIL",
)),
set(),
),
)
for title, test_old, test_new, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(test_old)
fixed, unfixed = cal.validate(doFix=True)
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_vfreebusy(self):
data = (
(
"No problem",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VFREEBUSY
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
END:VFREEBUSY
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VFREEBUSY
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
END:VFREEBUSY
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Missing required",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VFREEBUSY
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
END:VFREEBUSY
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VFREEBUSY
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
END:VFREEBUSY
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VFREEBUSY] Missing or too many required property: DTSTAMP",
)),
),
)
for title, test_old, test_new, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(test_old)
fixed, unfixed = cal.validate(doFix=True)
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_vjournal(self):
data = (
(
"No problem",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VJOURNAL
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTSTAMP:20020101T000000Z
END:VJOURNAL
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VJOURNAL
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTSTAMP:20020101T000000Z
END:VJOURNAL
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Missing required",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VJOURNAL
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
END:VJOURNAL
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VJOURNAL
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
END:VJOURNAL
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VJOURNAL] Missing or too many required property: DTSTAMP",
)),
),
)
for title, test_old, test_new, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(test_old)
fixed, unfixed = cal.validate(doFix=True)
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_vtimezone(self):
data = (
(
"No problem",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTIMEZONE
TZID:America/New_York
LAST-MODIFIED:20050809T050000Z
BEGIN:DAYLIGHT
DTSTART:19670430T020000
RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTIMEZONE
TZID:America/New_York
LAST-MODIFIED:20050809T050000Z
BEGIN:DAYLIGHT
DTSTART:19670430T020000
RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Missing required",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTIMEZONE
LAST-MODIFIED:20050809T050000Z
BEGIN:DAYLIGHT
DTSTART:19670430T020000
RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTIMEZONE
LAST-MODIFIED:20050809T050000Z
BEGIN:DAYLIGHT
DTSTART:19670430T020000
RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VTIMEZONE] Missing or too many required property: TZID",
)),
),
(
"Missing components",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTIMEZONE
TZID:America/New_York
LAST-MODIFIED:20050809T050000Z
END:VTIMEZONE
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTIMEZONE
TZID:America/New_York
LAST-MODIFIED:20050809T050000Z
END:VTIMEZONE
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VTIMEZONE] At least one component must be present: STANDARD or DAYLIGHT",
)),
),
)
for title, test_old, test_new, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(test_old)
fixed, unfixed = cal.validate(doFix=True)
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_vtodo(self):
data = (
(
"No problem",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Missing required",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VTODO] Missing or too many required property: DTSTAMP",
)),
),
(
"Too many",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
UID:C3184A66-1ED0-11D9-A5E0-000A958A3253
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
SUMMARY:New Year's Eve
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
UID:C3184A66-1ED0-11D9-A5E0-000A958A3253
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
SUMMARY:New Year's Eve
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VTODO] Missing or too many required property: UID",
"[VTODO] Too many properties present: SUMMARY",
)),
),
(
"PROP value",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VTODO] Property value incorrect: DTSTAMP",
)),
),
(
"DURATION without DTSTART",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VTODO] Property must be present: DTSTART with DURATION",
)),
),
(
"Combo fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
set((
"[VTODO] Properties must not both be present: DUE, DURATION",
)),
set(),
),
)
for title, test_old, test_new, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(test_old)
fixed, unfixed = cal.validate(doFix=True)
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_vavailability(self):
data = (
(
"No problem",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Missing required",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VAVAILABILITY] Missing or too many required property: DTSTAMP",
)),
),
(
"Too many",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
UID:20111005T133225Z-00001@example.com
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
UID:20111005T133225Z-00001@example.com
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VAVAILABILITY] Missing or too many required property: UID",
"[VAVAILABILITY] Too many properties present: ORGANIZER",
)),
),
(
"Combo fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DURATION:P1D
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DURATION:P1D
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
set((
"[VAVAILABILITY] Properties must not both be present: DTEND, DURATION",
)),
set(),
),
)
for title, test_old, test_new, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(test_old)
fixed, unfixed = cal.validate(doFix=True)
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_available(self):
data = (
(
"No problem",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Missing required",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[AVAILABLE] Missing or too many required property: DTSTAMP",
)),
),
(
"Too many",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
SUMMARY:Monday to Friday from 9 am to 5 pm
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
SUMMARY:Monday to Friday from 9 am to 5 pm
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[AVAILABLE] Missing or too many required property: UID",
"[AVAILABLE] Too many properties present: SUMMARY",
)),
),
(
"Combo fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DURATION:P1D
DTEND;TZID=America/Montreal:20111002T170000
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VAVAILABILITY
UID:20111005T133225Z-00001@example.com
DTSTAMP:20111005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20111005T133225Z-00001-A@example.com
DTSTART;TZID=America/Montreal:20111002T090000
DURATION:P1D
DTSTAMP:20111005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR
SUMMARY:Monday to Friday from 9:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
set((
"[AVAILABLE] Properties must not both be present: DTEND, DURATION",
)),
set(),
),
)
for title, test_old, test_new, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(test_old)
fixed, unfixed = cal.validate(doFix=True)
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_valarm(self):
data = (
(
"No problem",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Reminder
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Reminder
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Missing required",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VALARM] Missing or too many required property: TRIGGER",
)),
),
(
"Missing required with fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:DISPLAY
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set((
"[VALARM] Missing required property: DESCRIPTION",
)),
set((
"[VALARM] Missing or too many required property: TRIGGER",
)),
),
(
"Too many",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:AUDIO
ATTACH:http://example.com/audio/boink
ATTACH:http://example.com/audio/quack
TRIGGER:-PT15M
TRIGGER:-PT30M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:AUDIO
ATTACH:http://example.com/audio/boink
ATTACH:http://example.com/audio/quack
TRIGGER:-PT15M
TRIGGER:-PT30M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VALARM] Missing or too many required property: TRIGGER",
"[VALARM] Too many properties present: ATTACH",
)),
),
(
"Too few",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:EMAIL
DESCRIPTION:Reminder
SUMMARY:Reminder
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:EMAIL
DESCRIPTION:Reminder
SUMMARY:Reminder
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VALARM] Missing required property: ATTENDEE",
)),
),
(
"PROP value",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Reminder
DURATION:-P1D
REPEAT:-1
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Reminder
DURATION:-P1D
REPEAT:-1
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VALARM] Property value incorrect: REPEAT",
)),
),
(
"DUARTION and REPEAT together",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:CANCEL
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Reminder
REPEAT:2
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:CANCEL
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Reminder
REPEAT:2
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VALARM] Properties must be present together: DURATION, REPEAT",
)),
),
(
"Combo fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set((
"[VEVENT] Properties must not both be present: DTEND, DURATION",
)),
set((
"[VALARM] Missing or too many required property: TRIGGER",
)),
),
(
"ACKNOWLEDGED OK",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACKNOWLEDGED:20020101T000000Z
ACTION:DISPLAY
DESCRIPTION:Reminder
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACKNOWLEDGED:20020101T000000Z
ACTION:DISPLAY
DESCRIPTION:Reminder
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"ACKNOWLEDGED Bad value",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACKNOWLEDGED:20020101T000000
ACTION:DISPLAY
DESCRIPTION:Reminder
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACKNOWLEDGED:20020101T000000
ACTION:DISPLAY
DESCRIPTION:Reminder
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VALARM] Property value incorrect: ACKNOWLEDGED",
)),
),
(
"ACKNOWLEDGED too many",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACKNOWLEDGED:20020101T000000Z
ACKNOWLEDGED:20020102T000000Z
ACTION:DISPLAY
DESCRIPTION:Reminder
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACKNOWLEDGED:20020101T000000Z
ACKNOWLEDGED:20020102T000000Z
ACTION:DISPLAY
DESCRIPTION:Reminder
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VALARM] Too many properties present: ACKNOWLEDGED",
)),
),
(
"No problem ACTION=URI",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:URI
TRIGGER:-PT15M
URL:http://test.example.com
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:URI
TRIGGER:-PT15M
URL:http://test.example.com
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"ACTION=URI missing URL",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:URI
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:URI
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[VALARM] Missing or too many required property: URL",
)),
),
(
"No problem ACTION=NONE",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:NONE
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:NONE
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"No problem ACTION=NONE with TRIGGER",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:NONE
TRIGGER;VALUE=DATE-TIME:19760401T005545Z
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
BEGIN:VALARM
ACTION:NONE
TRIGGER;VALUE=DATE-TIME:19760401T005545Z
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
)
for title, test_old, test_new, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(test_old)
fixed, unfixed = cal.validate(doFix=True)
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_xcomponents(self):
data = (
(
"No problem #1",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
BEGIN:X-COMPONENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
SUMMARY:New Year's Day
END:X-COMPONENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
BEGIN:X-COMPONENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
SUMMARY:New Year's Day
END:X-COMPONENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"No problem #2",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
BEGIN:X-COMPONENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTEND;VALUE=DATE:20020102
DTSTART;VALUE=DATE:20020101
DURATION:P1D
SUMMARY:New Year's Day
END:X-COMPONENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
BEGIN:X-COMPONENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTEND;VALUE=DATE:20020102
DTSTART;VALUE=DATE:20020101
DURATION:P1D
SUMMARY:New Year's Day
END:X-COMPONENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set(),
),
(
"Prop value",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
BEGIN:X-COMPONENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000
DTSTART;VALUE=DATE:20020101
SUMMARY:New Year's Day
END:X-COMPONENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
BEGIN:X-COMPONENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000
DTSTART;VALUE=DATE:20020101
SUMMARY:New Year's Day
END:X-COMPONENT
END:VCALENDAR
""".replace("\n", "\r\n"),
set(),
set((
"[X-COMPONENT] Property value incorrect: DTSTAMP",
)),
),
)
for title, test_old, test_new, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(test_old)
fixed, unfixed = cal.validate(doFix=True)
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
def test_STATUS_fix(self):
"""
Test calendarserver issue where multiple STATUS properties can be present in components.
Want to make sure we report those as an error or fix them.
"""
data = (
(
"1.1 Two STATUS in VEVENT - fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
STATUS:CONFIRMED
STATUS:CANCELLED
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
STATUS:CANCELLED
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
True,
set((
"[VEVENT] Too many properties: STATUS",
)),
set(),
),
(
"1.2 Two STATUS in VEVENT - no fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
STATUS:CONFIRMED
STATUS:CANCELLED
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
STATUS:CONFIRMED
STATUS:CANCELLED
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
False,
set(),
set((
"[VEVENT] Too many properties: STATUS",
)),
),
(
"1.3 Two STATUS in VEVENT, none CANCELLED - fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
STATUS:CONFIRMED
STATUS:TENTATIVE
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
STATUS:CONFIRMED
STATUS:TENTATIVE
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
True,
set(),
set((
"[VEVENT] Too many properties: STATUS",
)),
),
(
"1.4 Two STATUS in VEVENT, none CANCELLED - no fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
STATUS:CONFIRMED
STATUS:TENTATIVE
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
STATUS:CONFIRMED
STATUS:TENTATIVE
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
False,
set(),
set((
"[VEVENT] Too many properties: STATUS",
)),
),
(
"2.1 Two STATUS in VTODO - fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
STATUS:NEEDS-ACTION
STATUS:CANCELLED
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
STATUS:CANCELLED
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
True,
set((
"[VTODO] Too many properties: STATUS",
)),
set(),
),
(
"2.2 Two STATUS in VTODO - no fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
STATUS:NEEDS-ACTION
STATUS:CANCELLED
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
STATUS:NEEDS-ACTION
STATUS:CANCELLED
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
False,
set(),
set((
"[VTODO] Too many properties: STATUS",
)),
),
(
"2.3 Two STATUS in VTODO, none CANCELLED - fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
STATUS:NEEDS-ACTION
STATUS:COMPLETED
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
STATUS:NEEDS-ACTION
STATUS:COMPLETED
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
True,
set(),
set((
"[VTODO] Too many properties: STATUS",
)),
),
(
"2.4 Two STATUS in VTODO, none CANCELLED - no fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
STATUS:NEEDS-ACTION
STATUS:COMPLETED
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTODO
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DUE;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
STATUS:NEEDS-ACTION
STATUS:COMPLETED
SUMMARY:New Year's Day
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
False,
set(),
set((
"[VTODO] Too many properties: STATUS",
)),
),
(
"3.1 Two STATUS in VJOURNAL - fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VJOURNAL
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTSTAMP:20020101T000000Z
STATUS:DRAFT
STATUS:CANCELLED
END:VJOURNAL
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VJOURNAL
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTSTAMP:20020101T000000Z
STATUS:CANCELLED
END:VJOURNAL
END:VCALENDAR
""".replace("\n", "\r\n"),
True,
set((
"[VJOURNAL] Too many properties: STATUS",
)),
set(),
),
(
"3.2 Two STATUS in VJOURNAL - no fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VJOURNAL
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTSTAMP:20020101T000000Z
STATUS:DRAFT
STATUS:CANCELLED
END:VJOURNAL
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VJOURNAL
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTSTAMP:20020101T000000Z
STATUS:DRAFT
STATUS:CANCELLED
END:VJOURNAL
END:VCALENDAR
""".replace("\n", "\r\n"),
False,
set(),
set((
"[VJOURNAL] Too many properties: STATUS",
)),
),
(
"3.3 Two STATUS in VJOURNAL, none CANCELLED - fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VJOURNAL
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTSTAMP:20020101T000000Z
STATUS:DRAFT
STATUS:FINAL
END:VJOURNAL
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VJOURNAL
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTSTAMP:20020101T000000Z
STATUS:DRAFT
STATUS:FINAL
END:VJOURNAL
END:VCALENDAR
""".replace("\n", "\r\n"),
True,
set(),
set((
"[VJOURNAL] Too many properties: STATUS",
)),
),
(
"3.4 Two STATUS in VJOURNAL, none CANCELLED - no fix",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VJOURNAL
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTSTAMP:20020101T000000Z
STATUS:DRAFT
STATUS:FINAL
END:VJOURNAL
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VJOURNAL
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTSTAMP:20020101T000000Z
STATUS:DRAFT
STATUS:FINAL
END:VJOURNAL
END:VCALENDAR
""".replace("\n", "\r\n"),
False,
set(),
set((
"[VJOURNAL] Too many properties: STATUS",
)),
),
)
for title, test_old, test_new, test_doFix, test_fixed, test_unfixed in data:
cal = PyCalendar.parseText(test_old)
fixed, unfixed = cal.validate(doFix=test_doFix)
self.assertEqual(str(cal), test_new, msg="Failed test: %s" % (title,))
self.assertEqual(set(fixed), test_fixed, msg="Failed test: %s" % (title,))
self.assertEqual(set(unfixed), test_unfixed, msg="Failed test: %s" % (title,))
pycalendar-2.0~svn13177/src/pycalendar/icalendar/tests/__init__.py 0000644 0001750 0001750 00000001201 12101017573 024132 0 ustar rahul rahul #
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
pycalendar-2.0~svn13177/src/pycalendar/icalendar/__init__.py 0000644 0001750 0001750 00000001201 12101017573 022770 0 ustar rahul rahul #
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
pycalendar-2.0~svn13177/src/pycalendar/plaintextvalue.py 0000644 0001750 0001750 00000002567 12101017573 022374 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# iCalendar UTC Offset value
from pycalendar.value import PyCalendarValue
class PyCalendarPlainTextValue(PyCalendarValue):
def __init__(self, value=''):
self.mValue = value
def duplicate(self):
return self.__class__(self.mValue)
def parse(self, data):
# No decoding required
self.mValue = data
# os - StringIO object
def generate(self, os):
try:
# No encoding required
os.write(self.mValue)
except:
pass
def writeXML(self, node, namespace):
value = self.getXMLNode(node, namespace)
value.text = self.mValue
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
pycalendar-2.0~svn13177/src/pycalendar/recurrence.py 0000644 0001750 0001750 00000163512 12320545600 021462 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar import xmldefs
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.period import PyCalendarPeriod
from pycalendar.valueutils import ValueMixin
import cStringIO as StringIO
import xml.etree.cElementTree as XML
def WeekDayNumCompare_compare(w1, w2):
if w1[0] < w2[0]:
return -1
elif w1[0] > w2[0]:
return 1
elif w1[1] < w2[1]:
return -1
elif w1[1] > w2[1]:
return 1
else:
return 0
def WeekDayNumSort_less_than(w1, w2):
return (w1[0] < w2[0]) or (w1[0] == w2[0]) and (w1[1] < w2[1])
class PyCalendarRecurrence(ValueMixin):
cFreqMap = {
definitions.cICalValue_RECUR_SECONDLY : definitions.eRecurrence_SECONDLY,
definitions.cICalValue_RECUR_MINUTELY : definitions.eRecurrence_MINUTELY,
definitions.cICalValue_RECUR_HOURLY : definitions.eRecurrence_HOURLY,
definitions.cICalValue_RECUR_DAILY : definitions.eRecurrence_DAILY,
definitions.cICalValue_RECUR_WEEKLY : definitions.eRecurrence_WEEKLY,
definitions.cICalValue_RECUR_MONTHLY : definitions.eRecurrence_MONTHLY,
definitions.cICalValue_RECUR_YEARLY : definitions.eRecurrence_YEARLY,
}
cFreqToXMLMap = {
definitions.eRecurrence_SECONDLY: xmldefs.recur_freq_secondly,
definitions.eRecurrence_MINUTELY: xmldefs.recur_freq_minutely,
definitions.eRecurrence_HOURLY: xmldefs.recur_freq_hourly,
definitions.eRecurrence_DAILY: xmldefs.recur_freq_daily,
definitions.eRecurrence_WEEKLY: xmldefs.recur_freq_weekly,
definitions.eRecurrence_MONTHLY: xmldefs.recur_freq_monthly,
definitions.eRecurrence_YEARLY: xmldefs.recur_freq_yearly,
}
cRecurMap = {
definitions.cICalValue_RECUR_FREQ : definitions.eRecurrence_FREQ,
definitions.cICalValue_RECUR_UNTIL : definitions.eRecurrence_UNTIL,
definitions.cICalValue_RECUR_COUNT : definitions.eRecurrence_COUNT,
definitions.cICalValue_RECUR_INTERVAL : definitions.eRecurrence_INTERVAL,
definitions.cICalValue_RECUR_BYSECOND : definitions.eRecurrence_BYSECOND,
definitions.cICalValue_RECUR_BYMINUTE : definitions.eRecurrence_BYMINUTE,
definitions.cICalValue_RECUR_BYHOUR : definitions.eRecurrence_BYHOUR,
definitions.cICalValue_RECUR_BYDAY : definitions.eRecurrence_BYDAY,
definitions.cICalValue_RECUR_BYMONTHDAY : definitions.eRecurrence_BYMONTHDAY,
definitions.cICalValue_RECUR_BYYEARDAY : definitions.eRecurrence_BYYEARDAY,
definitions.cICalValue_RECUR_BYWEEKNO : definitions.eRecurrence_BYWEEKNO,
definitions.cICalValue_RECUR_BYMONTH : definitions.eRecurrence_BYMONTH,
definitions.cICalValue_RECUR_BYSETPOS : definitions.eRecurrence_BYSETPOS,
definitions.cICalValue_RECUR_WKST : definitions.eRecurrence_WKST,
}
cWeekdayMap = {
definitions.cICalValue_RECUR_WEEKDAY_SU : definitions.eRecurrence_WEEKDAY_SU,
definitions.cICalValue_RECUR_WEEKDAY_MO : definitions.eRecurrence_WEEKDAY_MO,
definitions.cICalValue_RECUR_WEEKDAY_TU : definitions.eRecurrence_WEEKDAY_TU,
definitions.cICalValue_RECUR_WEEKDAY_WE : definitions.eRecurrence_WEEKDAY_WE,
definitions.cICalValue_RECUR_WEEKDAY_TH : definitions.eRecurrence_WEEKDAY_TH,
definitions.cICalValue_RECUR_WEEKDAY_FR : definitions.eRecurrence_WEEKDAY_FR,
definitions.cICalValue_RECUR_WEEKDAY_SA : definitions.eRecurrence_WEEKDAY_SA,
}
cWeekdayRecurMap = dict([(v, k) for k, v in cWeekdayMap.items()])
cUnknownIndex = -1
def __init__(self):
self.init_PyCalendarRecurrence()
def duplicate(self):
other = PyCalendarRecurrence()
other.mFreq = self.mFreq
other.mUseCount = self.mUseCount
other.mCount = self.mCount
other.mUseUntil = self.mUseUntil
if other.mUseUntil:
other.mUntil = self.mUntil.duplicate()
other.mInterval = self.mInterval
if self.mBySeconds is not None:
other.mBySeconds = self.mBySeconds[:]
if self.mByMinutes is not None:
other.mByMinutes = self.mByMinutes[:]
if self.mByHours is not None:
other.mByHours = self.mByHours[:]
if self.mByDay is not None:
other.mByDay = self.mByDay[:]
if self.mByMonthDay is not None:
other.mByMonthDay = self.mByMonthDay[:]
if self.mByYearDay is not None:
other.mByYearDay = self.mByYearDay[:]
if self.mByWeekNo is not None:
other.mByWeekNo = self.mByWeekNo[:]
if self.mByMonth is not None:
other.mByMonth = self.mByMonth[:]
if self.mBySetPos is not None:
other.mBySetPos = self.mBySetPos[:]
other.mWeekstart = self.mWeekstart
other.mCached = self.mCached
if self.mCacheStart:
other.mCacheStart = self.mCacheStart.duplicate()
if self.mCacheUpto:
other.mCacheUpto = self.mCacheUpto.duplicate()
other.mFullyCached = self.mFullyCached
if self.mRecurrences:
other.mRecurrences = self.mRecurrences[:]
return other
def init_PyCalendarRecurrence(self):
self.mFreq = definitions.eRecurrence_YEARLY
self.mUseCount = False
self.mCount = 0
self.mUseUntil = False
self.mUntil = None
self.mInterval = 1
self.mBySeconds = None
self.mByMinutes = None
self.mByHours = None
self.mByDay = None
self.mByMonthDay = None
self.mByYearDay = None
self.mByWeekNo = None
self.mByMonth = None
self.mBySetPos = None
self.mWeekstart = definitions.eRecurrence_WEEKDAY_MO
self.mCached = False
self.mCacheStart = None
self.mCacheUpto = None
self.mFullyCached = False
self.mRecurrences = None
def __hash__(self):
return hash((
self.mFreq,
self.mUseCount,
self.mCount,
self.mUseUntil,
self.mUntil,
self.mInterval,
tuple(self.mBySeconds) if self.mBySeconds else None,
tuple(self.mByMinutes) if self.mByMinutes else None,
tuple(self.mByHours) if self.mByHours else None,
tuple(self.mByDay) if self.mByDay else None,
tuple(self.mByMonthDay) if self.mByMonthDay else None,
tuple(self.mByYearDay) if self.mByYearDay else None,
tuple(self.mByWeekNo) if self.mByWeekNo else None,
tuple(self.mByMonth) if self.mByMonth else None,
tuple(self.mBySetPos) if self.mBySetPos else None,
self.mWeekstart,
))
def __ne__(self, other):
return not self.__eq__(other)
def __eq__(self, other):
if not isinstance(other, PyCalendarRecurrence):
return False
return self.equals(other)
def equals(self, comp):
return (self.mFreq == comp.mFreq) \
and (self.mUseCount == comp.mUseCount) and (self.mCount == comp.mCount) \
and (self.mUseUntil == comp.mUseUntil) and (self.mUntil == comp.mUntil) \
and (self.mInterval == comp.mInterval) \
and self.equalsNum(self.mBySeconds, comp.mBySeconds) \
and self.equalsNum(self.mByMinutes, comp.mByMinutes) \
and self.equalsNum(self.mByHours, comp.mByHours) \
and self.equalsDayNum(self.mByDay, comp.mByDay) \
and self.equalsNum(self.mByMonthDay, comp.mByMonthDay) \
and self.equalsNum(self.mByYearDay, comp.mByYearDay) \
and self.equalsNum(self.mByWeekNo, comp.mByWeekNo) \
and self.equalsNum(self.mByMonth, comp.mByMonth) \
and self.equalsNum(self.mBySetPos, comp.mBySetPos) \
and (self.mWeekstart == comp.mWeekstart)
def equalsNum(self, items1, items2):
# Check sizes first
if items1 is None:
items1 = []
if items2 is None:
items2 = []
if len(items1) != len(items2):
return False
elif len(items1) == 0:
return True
# Copy and sort each one for comparison
temp1 = items1[:]
temp2 = items2[:]
temp1.sort()
temp2.sort()
for i in range(0, len(temp1)):
if temp1[i] != temp2[i]:
return False
return True
def equalsDayNum(self, items1, items2):
# Check sizes first
if items1 is None:
items1 = []
if items2 is None:
items2 = []
if len(items1) != len(items2):
return False
elif len(items1) == 0:
return True
# Copy and sort each one for comparison
temp1 = items1[:]
temp2 = items2[:]
temp1.sort()
temp2.sort()
for i in range(0, len(temp1)):
if temp1[i] != temp2[i]:
return False
return True
def _setAndclearIfChanged(self, attr, value):
if getattr(self, attr) != value:
self.clear()
setattr(self, attr, value)
def getFreq(self):
return self.mFreq
def setFreq(self, freq):
self._setAndclearIfChanged("mFreq", freq)
def getUseUntil(self):
return self.mUseUntil
def setUseUntil(self, use_until):
self._setAndclearIfChanged("mUseUntil", use_until)
def getUntil(self):
return self.mUntil
def setUntil(self, until):
self._setAndclearIfChanged("mUntil", until)
def getUseCount(self):
return self.mUseCount
def setUseCount(self, use_count):
self._setAndclearIfChanged("mUseCount", use_count)
def getCount(self):
return self.mCount
def setCount(self, count):
self._setAndclearIfChanged("mCount", count)
def getInterval(self):
return self.mInterval
def setInterval(self, interval):
self._setAndclearIfChanged("mInterval", interval)
def getByMonth(self):
return self.mByMonth
def setByMonth(self, by):
self._setAndclearIfChanged("mByMonth", by[:])
def getByMonthDay(self):
return self.mByMonthDay
def setByMonthDay(self, by):
self._setAndclearIfChanged("mByMonthDay", by[:])
def getByYearDay(self):
return self.mByYearDay
def setByYearDay(self, by):
self._setAndclearIfChanged("mByYearDay", by[:])
def getByDay(self):
return self.mByDay
def setByDay(self, by):
self._setAndclearIfChanged("mByDay", by[:])
def getBySetPos(self):
return self.mBySetPos
def setBySetPos(self, by):
self._setAndclearIfChanged("mBySetPos", by[:])
def parse(self, data):
self.init_PyCalendarRecurrence()
# Tokenise using ''
tokens = data.split(";")
tokens.reverse()
if len(tokens) == 0:
raise ValueError("PyCalendarRecurrence: Invalid recurrence rule value")
while len(tokens) != 0:
# Get next token
token = tokens.pop()
try:
tname, tvalue = token.split("=")
except ValueError:
raise ValueError("PyCalendarRecurrence: Invalid token '%s'" % (token,))
# Determine token type
index = PyCalendarRecurrence.cRecurMap.get(tname, PyCalendarRecurrence.cUnknownIndex)
if index == PyCalendarRecurrence.cUnknownIndex:
raise ValueError("PyCalendarRecurrence: Invalid token '%s'" % (tname,))
# Parse remainder based on index
if index == definitions.eRecurrence_FREQ:
# Get the FREQ value
index = PyCalendarRecurrence.cFreqMap.get(tvalue, PyCalendarRecurrence.cUnknownIndex)
if index == PyCalendarRecurrence.cUnknownIndex:
raise ValueError("PyCalendarRecurrence: Invalid FREQ value")
self.mFreq = index
elif index == definitions.eRecurrence_UNTIL:
if self.mUseCount:
raise ValueError("PyCalendarRecurrence: Can't have both UNTIL and COUNT")
self.mUseUntil = True
if self.mUntil is None:
self.mUntil = PyCalendarDateTime()
try:
self.mUntil.parse(tvalue)
except ValueError:
raise ValueError("PyCalendarRecurrence: Invalid UNTIL value")
elif index == definitions.eRecurrence_COUNT:
if self.mUseUntil:
raise ValueError("PyCalendarRecurrence: Can't have both UNTIL and COUNT")
self.mUseCount = True
try:
self.mCount = int(tvalue)
except ValueError:
raise ValueError("PyCalendarRecurrence: Invalid COUNT value")
# Must not be less than one
if self.mCount < 1:
raise ValueError("PyCalendarRecurrence: Invalid COUNT value")
elif index == definitions.eRecurrence_INTERVAL:
try:
self.mInterval = int(tvalue)
except ValueError:
raise ValueError("PyCalendarRecurrence: Invalid INTERVAL value")
# Must NOT be less than one
if self.mInterval < 1:
raise ValueError("PyCalendarRecurrence: Invalid INTERVAL value")
elif index == definitions.eRecurrence_BYSECOND:
if self.mBySeconds is not None:
raise ValueError("PyCalendarRecurrence: Only one BYSECOND allowed")
self.mBySeconds = []
self.parseList(tvalue, self.mBySeconds, 0, 60, errmsg="PyCalendarRecurrence: Invalid BYSECOND value")
elif index == definitions.eRecurrence_BYMINUTE:
if self.mByMinutes is not None:
raise ValueError("PyCalendarRecurrence: Only one BYMINUTE allowed")
self.mByMinutes = []
self.parseList(tvalue, self.mByMinutes, 0, 59, errmsg="PyCalendarRecurrence: Invalid BYMINUTE value")
elif index == definitions.eRecurrence_BYHOUR:
if self.mByHours is not None:
raise ValueError("PyCalendarRecurrence: Only one BYHOUR allowed")
self.mByHours = []
self.parseList(tvalue, self.mByHours, 0, 23, errmsg="PyCalendarRecurrence: Invalid BYHOUR value")
elif index == definitions.eRecurrence_BYDAY:
if self.mByDay is not None:
raise ValueError("PyCalendarRecurrence: Only one BYDAY allowed")
self.mByDay = []
self.parseListDW(tvalue, self.mByDay, errmsg="PyCalendarRecurrence: Invalid BYDAY value")
elif index == definitions.eRecurrence_BYMONTHDAY:
if self.mByMonthDay is not None:
raise ValueError("PyCalendarRecurrence: Only one BYMONTHDAY allowed")
self.mByMonthDay = []
self.parseList(tvalue, self.mByMonthDay, 1, 31, True, errmsg="PyCalendarRecurrence: Invalid BYMONTHDAY value")
elif index == definitions.eRecurrence_BYYEARDAY:
if self.mByYearDay is not None:
raise ValueError("PyCalendarRecurrence: Only one BYYEARDAY allowed")
self.mByYearDay = []
self.parseList(tvalue, self.mByYearDay, 1, 366, True, errmsg="PyCalendarRecurrence: Invalid BYYEARDAY value")
elif index == definitions.eRecurrence_BYWEEKNO:
if self.mByWeekNo is not None:
raise ValueError("PyCalendarRecurrence: Only one BYWEEKNO allowed")
self.mByWeekNo = []
self.parseList(tvalue, self.mByWeekNo, 1, 53, True, errmsg="PyCalendarRecurrence: Invalid BYWEEKNO value")
elif index == definitions.eRecurrence_BYMONTH:
if self.mByMonth is not None:
raise ValueError("PyCalendarRecurrence: Only one BYMONTH allowed")
self.mByMonth = []
self.parseList(tvalue, self.mByMonth, 1, 12, errmsg="PyCalendarRecurrence: Invalid BYMONTH value")
elif index == definitions.eRecurrence_BYSETPOS:
if self.mBySetPos is not None:
raise ValueError("PyCalendarRecurrence: Only one BYSETPOS allowed")
self.mBySetPos = []
self.parseList(tvalue, self.mBySetPos, allowNegative=True, errmsg="PyCalendarRecurrence: Invalid BYSETPOS value")
elif index == definitions.eRecurrence_WKST:
index = PyCalendarRecurrence.cWeekdayMap.get(tvalue, PyCalendarRecurrence.cUnknownIndex)
if (index == PyCalendarRecurrence.cUnknownIndex):
raise ValueError("PyCalendarRecurrence: Invalid WKST value")
self.mWeekstart = index
def parseList(self, txt, list, min=None, max=None, allowNegative=False, errmsg=""):
if "," in txt:
tokens = txt.split(",")
else:
tokens = (txt,)
for token in tokens:
value = int(token)
if not allowNegative and value < 0:
raise ValueError(errmsg)
avalue = abs(value)
if min is not None and avalue < min:
raise ValueError(errmsg)
if max is not None and avalue > max:
raise ValueError(errmsg)
list.append(value)
def parseListDW(self, txt, list, errmsg=""):
if "," in txt:
tokens = txt.split(",")
else:
tokens = (txt,)
for token in tokens:
# Get number if present
num = 0
if (len(token) > 0) and token[0] in "+-1234567890":
offset = 0
while (offset < len(token)) and token[offset] in "+-1234567890":
offset += 1
num = int(token[0:offset])
token = token[offset:]
anum = abs(num)
if anum < 1:
raise ValueError(errmsg)
if anum > 53:
raise ValueError(errmsg)
# Get day
index = PyCalendarRecurrence.cWeekdayMap.get(token, PyCalendarRecurrence.cUnknownIndex)
if (index == PyCalendarRecurrence.cUnknownIndex):
raise ValueError(errmsg)
wday = index
list.append((num, wday))
def generate(self, os):
try:
os.write(definitions.cICalValue_RECUR_FREQ)
os.write("=")
if self.mFreq == definitions.eRecurrence_SECONDLY:
os.write(definitions.cICalValue_RECUR_SECONDLY)
elif self.mFreq == definitions.eRecurrence_MINUTELY:
os.write(definitions.cICalValue_RECUR_MINUTELY)
elif self.mFreq == definitions.eRecurrence_HOURLY:
os.write(definitions.cICalValue_RECUR_HOURLY)
elif self.mFreq == definitions.eRecurrence_DAILY:
os.write(definitions.cICalValue_RECUR_DAILY)
elif self.mFreq == definitions.eRecurrence_WEEKLY:
os.write(definitions.cICalValue_RECUR_WEEKLY)
elif self.mFreq == definitions.eRecurrence_MONTHLY:
os.write(definitions.cICalValue_RECUR_MONTHLY)
elif self.mFreq == definitions.eRecurrence_YEARLY:
os.write(definitions.cICalValue_RECUR_YEARLY)
if self.mUseCount:
os.write(";")
os.write(definitions.cICalValue_RECUR_COUNT)
os.write("=")
os.write(str(self.mCount))
elif self.mUseUntil:
os.write(";")
os.write(definitions.cICalValue_RECUR_UNTIL)
os.write("=")
self.mUntil.generate(os)
if self.mInterval > 1:
os.write(";")
os.write(definitions.cICalValue_RECUR_INTERVAL)
os.write("=")
os.write(str(self.mInterval))
self.generateList(os, definitions.cICalValue_RECUR_BYSECOND, self.mBySeconds)
self.generateList(os, definitions.cICalValue_RECUR_BYMINUTE, self.mByMinutes)
self.generateList(os, definitions.cICalValue_RECUR_BYHOUR, self.mByHours)
if (self.mByDay is not None) and (len(self.mByDay) != 0):
os.write(";")
os.write(definitions.cICalValue_RECUR_BYDAY)
os.write("=")
comma = False
for iter in self.mByDay:
if comma:
os.write(",")
comma = True
if iter[0] != 0:
os.write(str(iter[0]))
if iter[1] == definitions.eRecurrence_WEEKDAY_SU:
os.write(definitions.cICalValue_RECUR_WEEKDAY_SU)
elif iter[1] == definitions.eRecurrence_WEEKDAY_MO:
os.write(definitions.cICalValue_RECUR_WEEKDAY_MO)
elif iter[1] == definitions.eRecurrence_WEEKDAY_TU:
os.write(definitions.cICalValue_RECUR_WEEKDAY_TU)
elif iter[1] == definitions.eRecurrence_WEEKDAY_WE:
os.write(definitions.cICalValue_RECUR_WEEKDAY_WE)
elif iter[1] == definitions.eRecurrence_WEEKDAY_TH:
os.write(definitions.cICalValue_RECUR_WEEKDAY_TH)
elif iter[1] == definitions.eRecurrence_WEEKDAY_FR:
os.write(definitions.cICalValue_RECUR_WEEKDAY_FR)
elif iter[1] == definitions.eRecurrence_WEEKDAY_SA:
os.write(definitions.cICalValue_RECUR_WEEKDAY_SA)
self.generateList(os, definitions.cICalValue_RECUR_BYMONTHDAY, self.mByMonthDay)
self.generateList(os, definitions.cICalValue_RECUR_BYYEARDAY, self.mByYearDay)
self.generateList(os, definitions.cICalValue_RECUR_BYWEEKNO, self.mByWeekNo)
self.generateList(os, definitions.cICalValue_RECUR_BYMONTH, self.mByMonth)
self.generateList(os, definitions.cICalValue_RECUR_BYSETPOS, self.mBySetPos)
# MO is the default so we do not need it
if self.mWeekstart != definitions.eRecurrence_WEEKDAY_MO:
os.write(";")
os.write(definitions.cICalValue_RECUR_WKST)
os.write("=")
if self.mWeekstart == definitions.eRecurrence_WEEKDAY_SU:
os.write(definitions.cICalValue_RECUR_WEEKDAY_SU)
elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_MO:
os.write(definitions.cICalValue_RECUR_WEEKDAY_MO)
elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_TU:
os.write(definitions.cICalValue_RECUR_WEEKDAY_TU)
elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_WE:
os.write(definitions.cICalValue_RECUR_WEEKDAY_WE)
elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_TH:
os.write(definitions.cICalValue_RECUR_WEEKDAY_TH)
elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_FR:
os.write(definitions.cICalValue_RECUR_WEEKDAY_FR)
elif self.mWeekstart == definitions.eRecurrence_WEEKDAY_SA:
os.write(definitions.cICalValue_RECUR_WEEKDAY_SA)
except:
pass
def generateList(self, os, title, items):
if (items is not None) and (len(items) != 0):
os.write(";")
os.write(title)
os.write("=")
comma = False
for e in items:
if comma:
os.write(",")
comma = True
os.write(str(e))
def writeXML(self, node, namespace):
recur = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.value_recur))
freq = XML.SubElement(recur, xmldefs.makeTag(namespace, xmldefs.recur_freq))
freq.text = self.cFreqToXMLMap[self.mFreq]
if self.mUseCount:
count = XML.SubElement(recur, xmldefs.makeTag(namespace, xmldefs.recur_count))
count.text = str(self.mCount)
elif self.mUseUntil:
until = XML.SubElement(recur, xmldefs.makeTag(namespace, xmldefs.recur_until))
self.mUntil.writeXML(until, namespace)
if self.mInterval > 1:
interval = XML.SubElement(recur, xmldefs.makeTag(namespace, xmldefs.recur_interval))
interval.text = str(self.mInterval)
self.writeXMLList(recur, namespace, xmldefs.recur_bysecond, self.mBySeconds)
self.writeXMLList(recur, namespace, xmldefs.recur_byminute, self.mByMinutes)
self.writeXMLList(recur, namespace, xmldefs.recur_byhour, self.mByHours)
if self.mByDay is not None and len(self.mByDay) != 0:
for iter in self.mByDay:
byday = XML.SubElement(recur, xmldefs.makeTag(namespace, xmldefs.recur_byday))
data = ""
if iter[0] != 0:
data = str(iter[0])
data += self.cWeekdayRecurMap.get(iter[1], "")
byday.text = data
self.writeXMLList(recur, namespace, xmldefs.recur_bymonthday, self.mByMonthDay)
self.writeXMLList(recur, namespace, xmldefs.recur_byyearday, self.mByYearDay)
self.writeXMLList(recur, namespace, xmldefs.recur_byweekno, self.mByWeekNo)
self.writeXMLList(recur, namespace, xmldefs.recur_bymonth, self.mByMonth)
self.writeXMLList(recur, namespace, xmldefs.recur_bysetpos, self.mBySetPos)
# MO is the default so we do not need it
if self.mWeekstart != definitions.eRecurrence_WEEKDAY_MO:
wkst = XML.SubElement(recur, xmldefs.makeTag(namespace, xmldefs.recur_wkst))
wkst.text = self.cWeekdayRecurMap.get(self.mWeekstart, definitions.cICalValue_RECUR_WEEKDAY_MO)
def writeXMLList(self, node, namespace, name, items):
if items is not None and len(items) != 0:
for item in items:
child = XML.SubElement(node, xmldefs.makeTag(namespace, name))
child.text = str(item)
def hasBy(self):
return (self.mBySeconds is not None) and (len(self.mBySeconds) != 0) \
or (self.mByMinutes is not None) and (len(self.mByMinutes) != 0) \
or (self.mByHours is not None) and (len(self.mByHours) != 0) \
or (self.mByDay is not None) and (len(self.mByDay) != 0) \
or (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0) \
or (self.mByYearDay is not None) and (len(self.mByYearDay) != 0) \
or (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0) \
or (self.mByMonth is not None) and (len(self.mByMonth) != 0) \
or (self.mBySetPos is not None) and (len(self.mBySetPos) != 0)
def isSimpleRule(self):
# One that has no BYxxx rules
return not self.hasBy()
def isAdvancedRule(self):
# One that has BYMONTH,
# BYMONTHDAY (with no negative value),
# BYDAY (with multiple unnumbered, or numbered with all the same number
# (1..4, -2, -1)
# BYSETPOS with +1, or -1 only
# no others
# First checks the ones we do not handle at all
if ((self.mBySeconds is not None) and (len(self.mBySeconds) != 0) \
or (self.mByMinutes is not None) and (len(self.mByMinutes) != 0) \
or (self.mByHours is not None) and (len(self.mByHours) != 0) \
or (self.mByYearDay is not None) and (len(self.mByYearDay) != 0) \
or (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0)):
return False
# Check BYMONTHDAY numbers (we can handle -7...-1, 1..31)
if self.mByMonthDay is not None:
for iter in self.mByMonthDay:
if (iter < -7) or (iter > 31) or (iter == 0):
return False
# Check BYDAY numbers
if self.mByDay is not None:
number = 0
first = True
for iter in self.mByDay:
# Get the first number
if (first):
number = iter[0]
first = False
# Check number range
if (number > 4) or (number < -2):
return False
# If current differs from last, then we have an error
elif number != iter[0]:
return False
# Check BYSETPOS numbers
if self.mBySetPos is not None:
if len(self.mBySetPos) > 1:
return False
if (len(self.mBySetPos) == 1) and (self.mBySetPos[0] != -1) and (self.mBySetPos[0] != 1):
return False
# If we get here it must be OK
return True
def getUIDescription(self):
try:
# For now just use iCal item
sout = StringIO()
self.generate(sout)
result = sout.getvalue()
except:
result = ""
return result
def expand(self, start, range, items, float_offset=0):
# Have to normalize this to be very sure we are starting with a valid date, as otherwise
# we could end up looping forever when doing recurrence.
start.normalise()
# Must have recurrence list at this point
if self.mRecurrences is None:
self.mRecurrences = []
# Wipe cache if start is different
if self.mCached and (start != self.mCacheStart):
self.mCached = False
self.mFullyCached = False
self.mRecurrences = []
# Is the current cache complete or does it extend past the requested
# range end
if not self.mCached or not self.mFullyCached \
and (self.mCacheUpto is None or self.mCacheUpto < range.getEnd()):
cache_range = range.duplicate()
# If partially cached just cache from previous cache end up to new
# end
if self.mCached:
cache_range = PyCalendarPeriod(self.mCacheUpto, range.getEnd())
# Simple expansion is one where there is no BYXXX rule part
if not self.hasBy():
self.mFullyCached = self.simpleExpand(start, cache_range, self.mRecurrences, float_offset)
else:
self.mFullyCached = self.complexExpand(start, cache_range, self.mRecurrences, float_offset)
# Set cache values
self.mCached = True
self.mCacheStart = start
self.mCacheUpto = range.getEnd()
# Just return the cached items in the requested range
limited = not self.mFullyCached
for iter in self.mRecurrences:
if range.isDateWithinPeriod(iter):
items.append(iter)
else:
limited = True
return limited
def simpleExpand(self, start, range, items, float_offset):
start_iter = start.duplicate()
ctr = 0
if self.mUseUntil:
float_until = self.mUntil.duplicate()
if start.floating():
float_until.setTimezoneID(0)
float_until.offsetSeconds(float_offset)
while True:
# Exit if after period we want
if range.isDateAfterPeriod(start_iter):
return False
# Add current one to list
items.append(start_iter.duplicate())
# Get next item
start_iter.recur(self.mFreq, self.mInterval, allow_invalid=True)
while start_iter.invalid():
start_iter.recur(self.mFreq, self.mInterval, allow_invalid=True)
# Check limits
if self.mUseCount:
# Bump counter and exit if over
ctr += 1
if ctr >= self.mCount:
return True
elif self.mUseUntil:
# Exit if next item is after until (its OK if its the same as
# UNTIL as UNTIL is inclusive)
if start_iter > float_until:
return True
def complexExpand(self, start, range, items, float_offset):
start_iter = start.duplicate()
ctr = 0
if self.mUseUntil:
float_until = self.mUntil.duplicate()
if start.floating():
float_until.setTimezoneID(None)
float_until.offsetSeconds(float_offset)
# Always add the initial instance DTSTART
if self.mUseCount:
# Bump counter and exit if over
ctr += 1
if ctr >= self.mCount:
return True
# Need to re-initialise start based on BYxxx rules
while True:
# Behaviour is based on frequency
set_items = []
if self.mFreq == definitions.eRecurrence_SECONDLY:
self.generateSecondlySet(start_iter, set_items)
elif self.mFreq == definitions.eRecurrence_MINUTELY:
self.generateMinutelySet(start_iter, set_items)
elif self.mFreq == definitions.eRecurrence_HOURLY:
self.generateHourlySet(start_iter, set_items)
elif self.mFreq == definitions.eRecurrence_DAILY:
self.generateDailySet(start_iter, set_items)
elif self.mFreq == definitions.eRecurrence_WEEKLY:
self.generateWeeklySet(start_iter, set_items)
elif self.mFreq == definitions.eRecurrence_MONTHLY:
self.generateMonthlySet(start_iter, set_items)
elif self.mFreq == definitions.eRecurrence_YEARLY:
self.generateYearlySet(start_iter, set_items)
# Ignore if it is invalid
set_items = filter(lambda x: not x.invalid(), set_items)
# Always sort the set as BYxxx rules may not be sorted
#set_items.sort(cmp=PyCalendarDateTime.sort)
set_items.sort(key=lambda x: x.getPosixTime())
# Process each one in the generated set
for iter in set_items:
# Ignore if it is before the actual start - we need this
# because the expansion
# can go back in time from the real start, but we must exclude
# those when counting
# even if they are not within the requested range
if iter < start:
continue
# Exit if after period we want
if range.isDateAfterPeriod(iter):
return False
# Exit if beyond the UNTIL limit
if self.mUseUntil:
# Exit if next item is after until (its OK if its the same
# as UNTIL as UNTIL is inclusive)
if iter > float_until:
return True
# Special for start instance
if (ctr == 1) and (start == iter):
continue
# Add current one to list
items.append(iter)
# Check limits
if self.mUseCount:
# Bump counter and exit if over
ctr += 1
if ctr >= self.mCount:
return True
# Exit if after period we want
if range.isDateAfterPeriod(start_iter):
return False
# Get next item
start_iter.recur(self.mFreq, self.mInterval, allow_invalid=True)
def clear(self):
self.mCached = False
self.mFullyCached = False
if self.mRecurrences is not None:
self.mRecurrences = []
# IMPORTANT ExcludeFutureRecurrence assumes mCacheStart is setup with the
# owning VEVENT's DTSTART
# Currently this method is only called when a recurrence is being removed
# so
# the recurrence data should be cached
# Exclude dates on or after the chosen one
def excludeFutureRecurrence(self, exclude):
# Expand the rule up to the exclude date
items = []
period = PyCalendarPeriod()
period.init(self.mCacheStart, exclude)
self.expand(self.mCacheStart, period, items)
# Adjust UNTIL or add one if no COUNT
if self.getUseUntil() or not self.getUseCount():
# The last one is just less than the exclude date
if len(items) != 0:
# Now use the data as the UNTIL
self.mUseUntil = True
self.mUntil = items[-1]
# Adjust COUNT
elif self.getUseCount():
# The last one is just less than the exclude date
self.mUseCount = True
self.mCount = len(items)
# Now clear out the cached set after making changes
self.clear()
def generateYearlySet(self, start, items):
# All possible BYxxx are valid, though some combinations are not
# Start with initial date-time
items.append(start.duplicate())
if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
items[:] = self.byMonthExpand(items)
if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
items[:] = self.byWeekNoExpand(items)
if (self.mByYearDay is not None) and (len(self.mByYearDay) != 0):
items[:] = self.byYearDayExpand(items)
if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
items[:] = self.byMonthDayExpand(items)
if (self.mByDay is not None) and (len(self.mByDay) != 0):
# BYDAY is complicated:
# if BYDAY is included with BYYEARDAY or BYMONTHDAY then it
# contracts the recurrence set
# else it expands it, but the expansion depends on the frequency
# and other BYxxx periodicities
if ((self.mByYearDay is not None) and (len(self.mByYearDay) != 0)) \
or ((self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0)):
items[:] = self.byDayLimit(items)
elif (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
items[:] = self.byDayExpandWeekly(items)
elif (self.mByMonth is not None) and (len(self.mByMonth) != 0):
items[:] = self.byDayExpandMonthly(items)
else:
items[:] = self.byDayExpandYearly(items)
if (self.mByHours is not None) and (len(self.mByHours) != 0):
items[:] = self.byHourExpand(items)
if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
items[:] = self.byMinuteExpand(items)
if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
items[:] = self.bySecondExpand(items)
if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
items[:] = self.bySetPosLimit(items)
def generateMonthlySet(self, start, items):
# Cannot have BYYEARDAY and BYWEEKNO
# Start with initial date-time
items.append(start.duplicate())
if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
# BYMONTH limits the range of possible values
items[:] = self.byMonthLimit(items)
if (len(items) == 0):
return
# No BYWEEKNO
# No BYYEARDAY
if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
items[:] = self.byMonthDayExpand(items)
if (self.mByDay is not None) and (len(self.mByDay) != 0):
# BYDAY is complicated:
# if BYDAY is included with BYYEARDAY or BYMONTHDAY then it
# contracts the recurrence set
# else it expands it, but the expansion depends on the frequency
# and other BYxxx periodicities
if ((self.mByYearDay is not None) and (len(self.mByYearDay) != 0)) \
or ((self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0)):
items[:] = self.byDayLimit(items)
else:
items[:] = self.byDayExpandMonthly(items)
if ((self.mByHours is not None) and (len(self.mByHours) != 0)):
items[:] = self.byHourExpand(items)
if ((self.mByMinutes is not None) and (len(self.mByMinutes) != 0)):
items[:] = self.byMinuteExpand(items)
if ((self.mBySeconds is not None) and (len(self.mBySeconds) != 0)):
items[:] = self.bySecondExpand(items)
if ((self.mBySetPos is not None) and (len(self.mBySetPos) != 0)):
items[:] = self.bySetPosLimit(items)
def generateWeeklySet(self, start, items):
# Cannot have BYYEARDAY and BYMONTHDAY
# Start with initial date-time
items.append(start.duplicate())
if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
# BYMONTH limits the range of possible values
items[:] = self.byMonthLimit(items)
if (len(items) == 0):
return
if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
items[:] = self.byWeekNoLimit(items)
if (len(items) == 0):
return
# No BYYEARDAY
# No BYMONTHDAY
if (self.mByDay is not None) and (len(self.mByDay) != 0):
items[:] = self.byDayExpandWeekly(items)
if (self.mByHours is not None) and (len(self.mByHours) != 0):
items[:] = self.byHourExpand(items)
if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
items[:] = self.byMinuteExpand(items)
if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
items[:] = self.bySecondExpand(items)
if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
items[:] = self.bySetPosLimit(items)
def generateDailySet(self, start, items):
# Cannot have BYYEARDAY
# Start with initial date-time
items.append(start.duplicate())
if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
# BYMONTH limits the range of possible values
items[:] = self.byMonthLimit(items)
if (len(items) == 0):
return
if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
items[:] = self.byWeekNoLimit(items)
if (len(items) == 0):
return
# No BYYEARDAY
if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
items[:] = self.byMonthDayLimit(items)
if (len(items) == 0):
return
if (self.mByDay is not None) and (len(self.mByDay) != 0):
items[:] = self.byDayLimit(items)
if (len(items) == 0):
return
if (self.mByHours is not None) and (len(self.mByHours) != 0):
items[:] = self.byHourExpand(items)
if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
items[:] = self.byMinuteExpand(items)
if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
items[:] = self.bySecondExpand(items)
if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
items[:] = self.bySetPosLimit(items)
def generateHourlySet(self, start, items):
# Cannot have BYYEARDAY
# Start with initial date-time
items.append(start.duplicate())
if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
# BYMONTH limits the range of possible values
items[:] = self.byMonthLimit(items)
if (len(items) == 0):
return
if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
items[:] = self.byWeekNoLimit(items)
if (len(items) == 0):
return
# No BYYEARDAY
if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
items[:] = self.byMonthDayLimit(items)
if (len(items) == 0):
return
if (self.mByDay is not None) and (len(self.mByDay) != 0):
items[:] = self.byDayLimit(items)
if (len(items) == 0):
return
if (self.mByHours is not None) and (len(self.mByHours) != 0):
items[:] = self.byHourLimit(items)
if (len(items) == 0):
return
if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
items[:] = self.byMinuteExpand(items)
if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
items[:] = self.bySecondExpand(items)
if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
items[:] = self.bySetPosLimit(items)
def generateMinutelySet(self, start, items):
# Cannot have BYYEARDAY
# Start with initial date-time
items.append(start.duplicate())
if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
# BYMONTH limits the range of possible values
items[:] = self.byMonthLimit(items)
if (len(items) == 0):
return
if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
items[:] = self.byWeekNoLimit(items)
if (len(items) == 0):
return
# No BYYEARDAY
if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
items[:] = self.byMonthDayLimit(items)
if (len(items) == 0):
return
if (self.mByDay is not None) and (len(self.mByDay) != 0):
items[:] = self.byDayLimit(items)
if (len(items) == 0):
return
if (self.mByHours is not None) and (len(self.mByHours) != 0):
items[:] = self.byHourLimit(items)
if (len(items) == 0):
return
if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
items[:] = self.byMinuteLimit(items)
if (len(items) == 0):
return
if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
items[:] = self.bySecondExpand(items)
if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
items[:] = self.bySetPosLimit(items)
def generateSecondlySet(self, start, items):
# Cannot have BYYEARDAY
# Start with initial date-time
items.append(start.duplicate())
if (self.mByMonth is not None) and (len(self.mByMonth) != 0):
# BYMONTH limits the range of possible values
items[:] = self.byMonthLimit(items)
if (len(items) == 0):
return
if (self.mByWeekNo is not None) and (len(self.mByWeekNo) != 0):
items[:] = self.byWeekNoLimit(items)
if (len(items) == 0):
return
# No BYYEARDAY
if (self.mByMonthDay is not None) and (len(self.mByMonthDay) != 0):
items[:] = self.byMonthDayLimit(items)
if (len(items) == 0):
return
if (self.mByDay is not None) and (len(self.mByDay) != 0):
items[:] = self.byDayLimit(items)
if (len(items) == 0):
return
if (self.mByHours is not None) and (len(self.mByHours) != 0):
items[:] = self.byHourLimit(items)
if (len(items) == 0):
return
if (self.mByMinutes is not None) and (len(self.mByMinutes) != 0):
items[:] = self.byMinuteLimit(items)
if (len(items) == 0):
return
if (self.mBySeconds is not None) and (len(self.mBySeconds) != 0):
items[:] = self.bySecondLimit(items)
if (len(items) == 0):
return
if (self.mBySetPos is not None) and (len(self.mBySetPos) != 0):
items[:] = self.bySetPosLimit(items)
def byMonthExpand(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYMONTH and generating a new date-time for it and
# insert into output
for iter2 in self.mByMonth:
temp = iter1.duplicate()
temp.setMonth(iter2)
output.append(temp)
return output
def byWeekNoExpand(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYWEEKNO and generating a new date-time for it and
# insert into output
for iter2 in self.mByWeekNo:
temp = iter1.duplicate()
temp.setWeekNo(iter2)
output.append(temp)
return output
def byYearDayExpand(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYYEARDAY and generating a new date-time for it
# and insert into output
for iter2 in self.mByYearDay:
temp = iter1.duplicate()
temp.setYearDay(iter2, allow_invalid=True)
output.append(temp)
return output
def byMonthDayExpand(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYMONTHDAY and generating a new date-time for it
# and insert into output
for iter2 in self.mByMonthDay:
temp = iter1.duplicate()
temp.setMonthDay(iter2, allow_invalid=True)
output.append(temp)
return output
def byDayExpandYearly(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYDAY and generating a new date-time for it and
# insert into output
for iter2 in self.mByDay:
# Numeric value means specific instance
if iter2[0] != 0:
temp = iter1.duplicate()
temp.setDayOfWeekInYear(iter2[0], iter2[1])
output.append(temp)
else:
# Every matching day in the year
for i in range(1, 54):
temp = iter1.duplicate()
temp.setDayOfWeekInYear(i, iter2[1])
if temp.getYear() == (iter1).getYear():
output.append(temp)
return output
def byDayExpandMonthly(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYDAY and generating a new date-time for it and
# insert into output
for iter2 in self.mByDay:
# Numeric value means specific instance
if iter2[0] != 0:
temp = iter1.duplicate()
temp.setDayOfWeekInMonth(iter2[0], iter2[1], allow_invalid=True)
output.append(temp)
else:
# Every matching day in the month
for i in range(1, 7):
temp = iter1.duplicate()
temp.setDayOfWeekInMonth(i, iter2[1], allow_invalid=True)
if temp.getMonth() == iter1.getMonth():
output.append(temp)
return output
def byDayExpandWeekly(self, dates):
# Must take into account the WKST value
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYDAY and generating a new date-time for it and
# insert into output
for iter2 in self.mByDay:
# Numeric values are meaningless so ignore them
if iter2[0] == 0:
temp = iter1.duplicate()
# Determine amount of offset to apply to temp to shift it
# to the start of the week (backwards)
week_start_offset = self.mWeekstart - temp.getDayOfWeek()
if week_start_offset > 0:
week_start_offset -= 7
# Determine amount of offset from the start of the week to
# the day we want (forwards)
day_in_week_offset = iter2[1] - self.mWeekstart
if day_in_week_offset < 0:
day_in_week_offset += 7
# Apply offsets
temp.offsetDay(week_start_offset + day_in_week_offset)
output.append(temp)
return output
def byHourExpand(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYHOUR and generating a new date-time for it and
# insert into output
for iter2 in self.mByHours:
temp = iter1.duplicate()
temp.setHours(iter2)
output.append(temp)
return output
def byMinuteExpand(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYMINUTE and generating a new date-time for it and
# insert into output
for iter2 in self.mByMinutes:
temp = iter1.duplicate()
temp.setMinutes(iter2)
output.append(temp)
return output
def bySecondExpand(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYSECOND and generating a new date-time for it and
# insert into output
for iter2 in self.mBySeconds:
temp = iter1.duplicate()
temp.setSeconds(iter2)
output.append(temp)
return output
def byMonthLimit(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYMONTH and indicate keep if input month matches
keep = False
for iter2 in self.mByMonth:
keep = (iter1.getMonth() == iter2)
if keep:
break
if keep:
output.append(iter1)
return output
def byWeekNoLimit(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYWEEKNO and indicate keep if input month matches
keep = False
for iter2 in self.mByWeekNo:
keep = iter1.isWeekNo(iter2)
if keep:
break
if keep:
output.append(iter1)
return output
def byMonthDayLimit(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYMONTHDAY and indicate keep if input month
# matches
keep = False
for iter2 in self.mByMonthDay:
keep = iter1.isMonthDay(iter2)
if keep:
break
if keep:
output.append(iter1)
return output
def byDayLimit(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYDAY and indicate keep if input month matches
keep = False
for iter2 in self.mByDay:
keep = iter1.isDayOfWeekInMonth(iter2[0], iter2[1])
if keep:
break
if keep:
output.append(iter1)
return output
def byHourLimit(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYHOUR and indicate keep if input hour matches
keep = False
for iter2 in self.mByHours:
keep = (iter1.getHours() == iter2)
if keep:
break
if keep:
output.append(iter1)
return output
def byMinuteLimit(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYMINUTE and indicate keep if input minute matches
keep = False
for iter2 in self.mByMinutes:
keep = (iter1.getMinutes() == iter2)
if keep:
break
if keep:
output.append(iter1)
return output
def bySecondLimit(self, dates):
# Loop over all input items
output = []
for iter1 in dates:
# Loop over each BYSECOND and indicate keep if input second matches
keep = False
for iter2 in self.mBySeconds:
keep = (iter1.getSeconds() == iter2)
if keep:
break
if keep:
output.append(iter1)
return output
def bySetPosLimit(self, dates):
# The input dates MUST be sorted in order for this to work properly
#dates.sort(cmp=PyCalendarDateTime.sort)
dates.sort(key=lambda x: x.getPosixTime())
# Loop over each BYSETPOS and extract the relevant component from the
# input array and add to the output
output = []
input_size = len(dates)
for iter in self.mBySetPos:
if iter > 0:
# Positive values are offset from the start
if iter <= input_size:
output.append(dates[iter - 1])
elif iter < 0:
# Negative values are offset from the end
if -iter <= input_size:
output.append(dates[input_size + iter])
return output
pycalendar-2.0~svn13177/src/pycalendar/available.py 0000644 0001750 0001750 00000006205 12101017573 021240 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar.componentrecur import PyCalendarComponentRecur
from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
class PyCalendarAvailable(PyCalendarComponentRecur):
propertyCardinality_1 = (
definitions.cICalProperty_DTSTAMP,
definitions.cICalProperty_DTSTART,
definitions.cICalProperty_UID,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_CREATED,
definitions.cICalProperty_DESCRIPTION,
definitions.cICalProperty_GEO,
definitions.cICalProperty_LAST_MODIFIED,
definitions.cICalProperty_LOCATION,
definitions.cICalProperty_RECURRENCE_ID,
definitions.cICalProperty_RRULE,
definitions.cICalProperty_SEQUENCE,
definitions.cICalProperty_SUMMARY,
definitions.cICalProperty_DTEND,
definitions.cICalProperty_DURATION,
)
propertyValueChecks = ICALENDAR_VALUE_CHECKS
def __init__(self, parent=None):
super(PyCalendarAvailable, self).__init__(parent=parent)
def duplicate(self, parent=None):
return super(PyCalendarAvailable, self).duplicate(parent=parent)
def getType(self):
return definitions.cICalComponent_AVAILABLE
def validate(self, doFix=False):
"""
Validate the data in this component and optionally fix any problems, else raise. If
loggedProblems is not None it must be a C{list} and problem descriptions are appended
to that.
"""
fixed, unfixed = super(PyCalendarAvailable, self).validate(doFix)
# Extra constraint: only one of DTEND or DURATION
if self.hasProperty(definitions.cICalProperty_DTEND) and self.hasProperty(definitions.cICalProperty_DURATION):
# Fix by removing the DTEND
logProblem = "[%s] Properties must not both be present: %s, %s" % (
self.getType(),
definitions.cICalProperty_DTEND,
definitions.cICalProperty_DURATION,
)
if doFix:
self.removeProperties(definitions.cICalProperty_DTEND)
fixed.append(logProblem)
else:
unfixed.append(logProblem)
return fixed, unfixed
def sortedPropertyKeyOrder(self):
return (
definitions.cICalProperty_UID,
definitions.cICalProperty_RECURRENCE_ID,
definitions.cICalProperty_DTSTART,
definitions.cICalProperty_DURATION,
definitions.cICalProperty_DTEND,
)
pycalendar-2.0~svn13177/src/pycalendar/binaryvalue.py 0000644 0001750 0001750 00000002025 12101017573 021635 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# iCalendar Binary value
from pycalendar import xmldefs
from pycalendar.plaintextvalue import PyCalendarPlainTextValue
from pycalendar.value import PyCalendarValue
class PyCalendarBinaryValue(PyCalendarPlainTextValue):
def getType(self):
return PyCalendarValue.VALUETYPE_BINARY
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_BINARY, PyCalendarBinaryValue, xmldefs.value_binary)
pycalendar-2.0~svn13177/src/pycalendar/vtimezoneelement.py 0000644 0001750 0001750 00000017614 12101017573 022720 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from bisect import bisect_right
from pycalendar import definitions
from pycalendar.component import PyCalendarComponent
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
from pycalendar.period import PyCalendarPeriod
from pycalendar.recurrenceset import PyCalendarRecurrenceSet
from pycalendar.value import PyCalendarValue
class PyCalendarVTimezoneElement(PyCalendarComponent):
propertyCardinality_1 = (
definitions.cICalProperty_DTSTART,
definitions.cICalProperty_TZOFFSETTO,
definitions.cICalProperty_TZOFFSETFROM,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_RRULE,
)
propertyValueChecks = ICALENDAR_VALUE_CHECKS
def __init__(self, parent=None, dt=None, offset=None):
super(PyCalendarVTimezoneElement, self).__init__(parent=parent)
self.mStart = dt if dt is not None else PyCalendarDateTime()
self.mTZName = ""
self.mUTCOffset = offset if offset is not None else 0
self.mUTCOffsetFrom = 0
self.mRecurrences = PyCalendarRecurrenceSet()
self.mCachedExpandBelow = None
self.mCachedExpandBelowItems = None
def duplicate(self, parent=None):
other = super(PyCalendarVTimezoneElement, self).duplicate(parent=parent)
other.mStart = self.mStart.duplicate()
other.mTZName = self.mTZName
other.mUTCOffset = self.mUTCOffset
other.mUTCOffsetFrom = self.mUTCOffsetFrom
other.mRecurrences = self.mRecurrences.duplicate()
other.mCachedExpandBelow = None
other.mCachedExpandBelowItems = None
return other
def finalise(self):
# Get DTSTART
temp = self.loadValueDateTime(definitions.cICalProperty_DTSTART)
if temp is not None:
self.mStart = temp
# Get TZOFFSETTO
temp = self.loadValueInteger(definitions.cICalProperty_TZOFFSETTO, PyCalendarValue.VALUETYPE_UTC_OFFSET)
if temp is not None:
self.mUTCOffset = temp
# Get TZOFFSETFROM
temp = self.loadValueInteger(definitions.cICalProperty_TZOFFSETFROM, PyCalendarValue.VALUETYPE_UTC_OFFSET)
if temp is not None:
self.mUTCOffsetFrom = temp
# Get TZNAME
temps = self.loadValueString(definitions.cICalProperty_TZNAME)
if temps is not None:
self.mTZName = temps
# Get RRULEs
self.loadValueRRULE(definitions.cICalProperty_RRULE, self.mRecurrences, True)
# Get RDATEs
self.loadValueRDATE(definitions.cICalProperty_RDATE, self.mRecurrences, True)
# Do inherited
super(PyCalendarVTimezoneElement, self).finalise()
def getSortKey(self):
"""
We do not want these components sorted.
"""
return ""
def getStart(self):
return self.mStart
def getUTCOffset(self):
return self.mUTCOffset
def getUTCOffsetFrom(self):
return self.mUTCOffsetFrom
def getTZName(self):
return self.mTZName
def expandBelow(self, below):
# Look for recurrences
if not self.mRecurrences.hasRecurrence() or self.mStart > below:
# Return DTSTART even if it is newer
return self.mStart
else:
# We want to allow recurrence calculation caching to help us here
# as this method
# gets called a lot - most likely for ever increasing dt values
# (which will therefore
# invalidate the recurrence cache).
#
# What we will do is round up the date-time to the next year so
# that the recurrence
# cache is invalidated less frequently
temp = PyCalendarDateTime(below.getYear(), 1, 1, 0, 0, 0)
# Use cache of expansion
if self.mCachedExpandBelowItems is None:
self.mCachedExpandBelowItems = []
if self.mCachedExpandBelow is None:
self.mCachedExpandBelow = self.mStart.duplicate()
if temp > self.mCachedExpandBelow:
self.mCachedExpandBelowItems = []
period = PyCalendarPeriod(self.mStart, temp)
self.mRecurrences.expand(self.mStart, period, self.mCachedExpandBelowItems, float_offset=self.mUTCOffsetFrom)
self.mCachedExpandBelow = temp
if len(self.mCachedExpandBelowItems) != 0:
# List comes back sorted so we pick the element just less than
# the dt value we want
i = bisect_right(self.mCachedExpandBelowItems, below)
if i != 0:
return self.mCachedExpandBelowItems[i - 1]
# The first one in the list is the one we want
return self.mCachedExpandBelowItems[0]
return self.mStart
def expandAll(self, start, end, with_name):
if start is None:
start = self.mStart
# Ignore if there is no change in offset
offsetto = self.loadValueInteger(definitions.cICalProperty_TZOFFSETTO, PyCalendarValue.VALUETYPE_UTC_OFFSET)
offsetfrom = self.loadValueInteger(definitions.cICalProperty_TZOFFSETFROM, PyCalendarValue.VALUETYPE_UTC_OFFSET)
# if offsetto == offsetfrom:
# return ()
# Look for recurrences
if self.mStart > end:
# Return nothing
return ()
elif not self.mRecurrences.hasRecurrence():
# Return DTSTART even if it is newer
if self.mStart >= start:
result = (self.mStart, offsetfrom, offsetto,)
if with_name:
result += (self.getTZName(),)
return (result,)
else:
return ()
else:
# We want to allow recurrence calculation caching to help us here
# as this method
# gets called a lot - most likely for ever increasing dt values
# (which will therefore
# invalidate the recurrence cache).
#
# What we will do is round up the date-time to the next year so
# that the recurrence
# cache is invalidated less frequently
temp = PyCalendarDateTime(end.getYear(), 1, 1, 0, 0, 0)
# Use cache of expansion
if self.mCachedExpandBelowItems is None:
self.mCachedExpandBelowItems = []
if self.mCachedExpandBelow is None:
self.mCachedExpandBelow = self.mStart.duplicate()
if temp > self.mCachedExpandBelow:
self.mCachedExpandBelowItems = []
period = PyCalendarPeriod(self.mStart, end)
self.mRecurrences.expand(self.mStart, period, self.mCachedExpandBelowItems, float_offset=self.mUTCOffsetFrom)
self.mCachedExpandBelow = temp
if len(self.mCachedExpandBelowItems) != 0:
# Return them all within the range
results = []
for dt in self.mCachedExpandBelowItems:
if dt >= start and dt < end:
result = (dt, offsetfrom, offsetto,)
if with_name:
result += (self.getTZName(),)
results.append(result)
return results
return ()
pycalendar-2.0~svn13177/src/pycalendar/vevent.py 0000644 0001750 0001750 00000014121 12101017573 020623 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar import itipdefinitions
from pycalendar.componentrecur import PyCalendarComponentRecur
from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
from pycalendar.property import PyCalendarProperty
class PyCalendarVEvent(PyCalendarComponentRecur):
propertyCardinality_1 = (
definitions.cICalProperty_DTSTAMP,
definitions.cICalProperty_UID,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_CLASS,
definitions.cICalProperty_CREATED,
definitions.cICalProperty_DESCRIPTION,
definitions.cICalProperty_GEO,
definitions.cICalProperty_LAST_MODIFIED,
definitions.cICalProperty_LOCATION,
definitions.cICalProperty_ORGANIZER,
definitions.cICalProperty_PRIORITY,
definitions.cICalProperty_SEQUENCE,
# definitions.cICalProperty_STATUS, # Special fix done for multiple STATUS
definitions.cICalProperty_SUMMARY,
definitions.cICalProperty_TRANSP,
definitions.cICalProperty_URL,
definitions.cICalProperty_RECURRENCE_ID,
definitions.cICalProperty_RRULE,
definitions.cICalProperty_DTEND,
definitions.cICalProperty_DURATION,
)
propertyValueChecks = ICALENDAR_VALUE_CHECKS
def __init__(self, parent=None):
super(PyCalendarVEvent, self).__init__(parent=parent)
self.mStatus = definitions.eStatus_VEvent_None
def duplicate(self, parent=None):
other = super(PyCalendarVEvent, self).duplicate(parent=parent)
other.mStatus = self.mStatus
return other
def getType(self):
return definitions.cICalComponent_VEVENT
def getMimeComponentName(self):
return itipdefinitions.cICalMIMEComponent_VEVENT
def addComponent(self, comp):
# We can embed the alarm components only
if comp.getType() == definitions.cICalComponent_VALARM:
super(PyCalendarVEvent, self).addComponent(comp)
else:
raise ValueError
def getStatus(self):
return self.mStatus
def setStatus(self, status):
self.mStatus = status
def finalise(self):
# Do inherited
super(PyCalendarVEvent, self).finalise()
temp = self.loadValueString(definitions.cICalProperty_STATUS)
if temp is not None:
if temp == definitions.cICalProperty_STATUS_TENTATIVE:
self.mStatus = definitions.eStatus_VEvent_Tentative
elif temp == definitions.cICalProperty_STATUS_CONFIRMED:
self.mStatus = definitions.eStatus_VEvent_Confirmed
elif temp == definitions.cICalProperty_STATUS_CANCELLED:
self.mStatus = definitions.eStatus_VEvent_Cancelled
def validate(self, doFix=False):
"""
Validate the data in this component and optionally fix any problems, else raise. If
loggedProblems is not None it must be a C{list} and problem descriptions are appended
to that.
"""
fixed, unfixed = super(PyCalendarVEvent, self).validate(doFix)
# Extra constraint: if METHOD not present, DTSTART must be
if self.mParentComponent and not self.mParentComponent.hasProperty(definitions.cICalProperty_METHOD):
if not self.hasProperty(definitions.cICalProperty_DTSTART):
# Cannot fix a missing required property
logProblem = "[%s] Missing required property: %s" % (self.getType(), definitions.cICalProperty_DTSTART,)
unfixed.append(logProblem)
# Extra constraint: only one of DTEND or DURATION
if self.hasProperty(definitions.cICalProperty_DTEND) and self.hasProperty(definitions.cICalProperty_DURATION):
# Fix by removing the DTEND
logProblem = "[%s] Properties must not both be present: %s, %s" % (
self.getType(),
definitions.cICalProperty_DTEND,
definitions.cICalProperty_DURATION,
)
if doFix:
self.removeProperties(definitions.cICalProperty_DTEND)
fixed.append(logProblem)
else:
unfixed.append(logProblem)
return fixed, unfixed
# Editing
def editStatus(self, status):
# Only if it is different
if self.mStatus != status:
# Updated cached values
self.mStatus = status
# Remove existing STATUS items
self.removeProperties(definitions.cICalProperty_STATUS)
# Now create properties
value = None
if status == definitions.eStatus_VEvent_None:
pass
elif status == definitions.eStatus_VEvent_Tentative:
value = definitions.cICalProperty_STATUS_TENTATIVE
elif status == definitions.eStatus_VEvent_Confirmed:
value = definitions.cICalProperty_STATUS_CONFIRMED
elif status == definitions.eStatus_VEvent_Cancelled:
value = definitions.cICalProperty_STATUS_CANCELLED
else:
pass
if value is not None:
prop = PyCalendarProperty(definitions.cICalProperty_STATUS, value)
self.addProperty(prop)
def sortedPropertyKeyOrder(self):
return (
definitions.cICalProperty_UID,
definitions.cICalProperty_RECURRENCE_ID,
definitions.cICalProperty_DTSTART,
definitions.cICalProperty_DURATION,
definitions.cICalProperty_DTEND,
)
pycalendar-2.0~svn13177/src/pycalendar/vfreebusy.py 0000644 0001750 0001750 00000026034 12101017573 021334 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar import itipdefinitions
from pycalendar.component import PyCalendarComponent
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.freebusy import PyCalendarFreeBusy
from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
from pycalendar.period import PyCalendarPeriod
from pycalendar.periodvalue import PyCalendarPeriodValue
from pycalendar.property import PyCalendarProperty
from pycalendar.value import PyCalendarValue
class PyCalendarVFreeBusy(PyCalendarComponent):
propertyCardinality_1 = (
definitions.cICalProperty_DTSTAMP,
definitions.cICalProperty_UID,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_CONTACT,
definitions.cICalProperty_DTSTART,
definitions.cICalProperty_DTEND,
definitions.cICalProperty_ORGANIZER,
definitions.cICalProperty_URL,
)
propertyValueChecks = ICALENDAR_VALUE_CHECKS
def __init__(self, parent=None):
super(PyCalendarVFreeBusy, self).__init__(parent=parent)
self.mStart = PyCalendarDateTime()
self.mHasStart = False
self.mEnd = PyCalendarDateTime()
self.mHasEnd = False
self.mDuration = False
self.mCachedBusyTime = False
self.mSpanPeriod = None
self.mBusyTime = None
def duplicate(self, parent=None):
other = super(PyCalendarVFreeBusy, self).duplicate(parent=parent)
other.mStart = self.mStart.duplicate()
other.mHasStart = self.mHasStart
other.mEnd = self.mEnd.duplicate()
other.mHasEnd = self.mHasEnd
other.mDuration = self.mDuration
other.mCachedBusyTime = False
other.mBusyTime = None
return other
def getType(self):
return definitions.cICalComponent_VFREEBUSY
def getMimeComponentName(self):
return itipdefinitions.cICalMIMEComponent_VFREEBUSY
def finalise(self):
# Do inherited
super(PyCalendarVFreeBusy, self).finalise()
# Get DTSTART
temp = self.loadValueDateTime(definitions.cICalProperty_DTSTART)
self.mHasStart = temp is not None
if self.mHasStart:
self.mStart = temp
# Get DTEND
temp = self.loadValueDateTime(definitions.cICalProperty_DTEND)
if temp is None:
# Try DURATION instead
temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
if temp is not None:
self.mEnd = self.mStart + temp
self.mDuration = True
else:
# Force end to start, which will then be fixed to sensible
# value later
self.mEnd = self.mStart
else:
self.mHasEnd = True
self.mDuration = False
self.mEnd = temp
def fixStartEnd(self):
# End is always greater than start if start exists
if self.mHasStart and self.mEnd <= self.mStart:
# Use the start
self.mEnd = self.mStart.duplicate()
self.mDuration = False
# Adjust to appropiate non-inclusive end point
if self.mStart.isDateOnly():
self.mEnd.offsetDay(1)
# For all day events it makes sense to use duration
self.mDuration = True
else:
# Use end of current day
self.mEnd.offsetDay(1)
self.mEnd.setHHMMSS(0, 0, 0)
def getStart(self):
return self.mStart
def hasStart(self):
return self.mHasStart
def getEnd(self):
return self.mEnd
def hasEnd(self):
return self.mHasEnd
def useDuration(self):
return self.mDuration
def getSpanPeriod(self):
return self.mSpanPeriod
def getBusyTime(self):
return self.mBusyTime
def editTiming(self):
# Updated cached values
self.mHasStart = False
self.mHasEnd = False
self.mDuration = False
self.mStart.setToday()
self.mEnd.setToday()
# Remove existing DTSTART & DTEND & DURATION & DUE items
self.removeProperties(definitions.cICalProperty_DTSTART)
self.removeProperties(definitions.cICalProperty_DTEND)
self.removeProperties(definitions.cICalProperty_DURATION)
def editTimingStartEnd(self, start, end):
# Updated cached values
self.mHasStart = self.mHasEnd = True
self.mStart = start
self.mEnd = end
self.mDuration = False
self.fixStartEnd()
# Remove existing DTSTART & DTEND & DURATION & DUE items
self.removeProperties(definitions.cICalProperty_DTSTART)
self.removeProperties(definitions.cICalProperty_DTEND)
self.removeProperties(definitions.cICalProperty_DURATION)
# Now create properties
prop = PyCalendarProperty(definitions.cICalProperty_DTSTART, start)
self.addProperty(prop)
# If its an all day event and the end one day after the start, ignore it
temp = start.duplicate()
temp.offsetDay(1)
if not start.isDateOnly() or end != temp:
prop = PyCalendarProperty(definitions.cICalProperty_DTEND, end)
self.addProperty(prop)
def editTimingStartDuration(self, start, duration):
# Updated cached values
self.mHasStart = True
self.mHasEnd = False
self.mStart = start
self.mEnd = start + duration
self.mDuration = True
# Remove existing DTSTART & DTEND & DURATION & DUE items
self.removeProperties(definitions.cICalProperty_DTSTART)
self.removeProperties(definitions.cICalProperty_DTEND)
self.removeProperties(definitions.cICalProperty_DURATION)
self.removeProperties(definitions.cICalProperty_DUE)
# Now create properties
prop = PyCalendarProperty(definitions.cICalProperty_DTSTART, start)
self.addProperty(prop)
# If its an all day event and the duration is one day, ignore it
if (not start.isDateOnly() or (duration.getWeeks() != 0)
or (duration.getDays() > 1)):
prop = PyCalendarProperty(definitions.cICalProperty_DURATION, duration)
self.addProperty(prop)
# Generating info
def expandPeriodComp(self, period, list):
# Cache the busy-time details if not done already
if not self.mCachedBusyTime:
self.cacheBusyTime()
# See if period intersects the busy time span range
if (self.mBusyTime is not None) and period.isPeriodOverlap(self.mSpanPeriod):
list.append(self)
def expandPeriodFB(self, period, list):
# Cache the busy-time details if not done already
if not self.mCachedBusyTime:
self.cacheBusyTime()
# See if period intersects the busy time span range
if (self.mBusyTime is not None) and period.isPeriodOverlap(self.mSpanPeriod):
for fb in self.mBusyTime:
list.append(PyCalendarFreeBusy(fb))
def cacheBusyTime(self):
# Clear out any existing cache
self.mBusyTime = []
# Get all FREEBUSY items and add those that are BUSY
min_start = PyCalendarDateTime()
max_end = PyCalendarDateTime()
props = self.getProperties()
result = props.get(definitions.cICalProperty_FREEBUSY, ())
for iter in result:
# Check the properties FBTYPE attribute
type = 0
is_busy = False
if iter.hasAttribute(definitions.cICalAttribute_FBTYPE):
fbyype = iter.getAttributeValue(definitions.cICalAttribute_FBTYPE)
if fbyype.upper() == definitions.cICalAttribute_FBTYPE_BUSY:
is_busy = True
type = PyCalendarFreeBusy.BUSY
elif fbyype.upper() == definitions.cICalAttribute_FBTYPE_BUSYUNAVAILABLE:
is_busy = True
type = PyCalendarFreeBusy.BUSYUNAVAILABLE
elif fbyype.upper() == definitions.cICalAttribute_FBTYPE_BUSYTENTATIVE:
is_busy = True
type = PyCalendarFreeBusy.BUSYTENTATIVE
else:
is_busy = False
type = PyCalendarFreeBusy.FREE
else:
# Default is busy when no attribute
is_busy = True
type = PyCalendarFreeBusy.BUSY
# Add this period
if is_busy:
multi = iter.getMultiValue()
if (multi is not None) and (multi.getType() == PyCalendarValue.VALUETYPE_PERIOD):
for o in multi.getValues():
# Double-check type
period = None
if isinstance(o, PyCalendarPeriodValue):
period = o
# Double-check type
if period is not None:
self.mBusyTime.append(PyCalendarFreeBusy(type, period.getValue()))
if len(self.mBusyTime) == 1:
min_start = period.getValue().getStart()
max_end = period.getValue().getEnd()
else:
if min_start > period.getValue().getStart():
min_start = period.getValue().getStart()
if max_end < period.getValue().getEnd():
max_end = period.getValue().getEnd()
# If nothing present, empty the list
if len(self.mBusyTime) == 0:
self.mBusyTime = None
else:
# Sort the list by period
self.mBusyTime.sort(cmp=lambda x, y: x.getPeriod().getStart().compareDateTime(y.getPeriod().getStart()))
# Determine range
start = PyCalendarDateTime()
end = PyCalendarDateTime()
if self.mHasStart:
start = self.mStart
else:
start = min_start
if self.mHasEnd:
end = self.mEnd
else:
end = max_end
self.mSpanPeriod = PyCalendarPeriod(start, end)
self.mCachedBusyTime = True
def sortedPropertyKeyOrder(self):
return (
definitions.cICalProperty_UID,
definitions.cICalProperty_DTSTART,
definitions.cICalProperty_DURATION,
definitions.cICalProperty_DTEND,
)
pycalendar-2.0~svn13177/src/pycalendar/tests/ 0000755 0001750 0001750 00000000000 12322631215 020105 5 ustar rahul rahul pycalendar-2.0~svn13177/src/pycalendar/tests/test_orgvalue.py 0000644 0001750 0001750 00000003462 12101017573 023347 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.orgvalue import OrgValue
from pycalendar.vcard.property import Property
import unittest
class TestNValue(unittest.TestCase):
def testParseValue(self):
items = (
("", ""),
("Example", "Example"),
("Example\, Inc.", "Example\, Inc."),
("Example\; Inc;Dept. of Silly Walks", "Example\; Inc;Dept. of Silly Walks"),
)
for item, result in items:
req = OrgValue()
req.parse(item)
test = req.getText()
self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
def testParseProperty(self):
items = (
("ORG:", "ORG:"),
("ORG:Example", "ORG:Example"),
("ORG:Example\, Inc.", "ORG:Example\, Inc."),
("ORG:Example\; Inc;Dept. of Silly Walks", "ORG:Example\; Inc;Dept. of Silly Walks"),
("ORG;VALUE=TEXT:Example", "ORG:Example"),
)
for item, result in items:
prop = Property()
prop.parse(item)
test = prop.getText()
self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))
pycalendar-2.0~svn13177/src/pycalendar/tests/test_requeststatus.py 0000644 0001750 0001750 00000006447 12127044213 024464 0 ustar rahul rahul ##
# Copyright (c) 2011-2013 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.parser import ParserContext
from pycalendar.property import PyCalendarProperty
from pycalendar.requeststatusvalue import PyCalendarRequestStatusValue
import unittest
class TestRequestStatus(unittest.TestCase):
def testParseValue(self):
items = (
"2.0;Success",
"2.0;Success\;here",
"2.0;Success;Extra",
"2.0;Success\;here;Extra",
"2.0;Success;Extra\;here",
"2.0;Success\;here;Extra\;here too",
)
for item in items:
req = PyCalendarRequestStatusValue()
req.parse(item)
self.assertEqual(req.getText(), item, "Failed to parse and re-generate '%s'" % (item,))
def testBadValue(self):
bad_value = "2.0\;Success"
ok_value = "2.0;Success"
# Fix the value
oldContext = ParserContext.INVALID_REQUEST_STATUS_VALUE
ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_FIX
req = PyCalendarRequestStatusValue()
req.parse(bad_value)
self.assertEqual(req.getText(), ok_value, "Failed to parse and re-generate '%s'" % (bad_value,))
# Raise the value
ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_RAISE
req = PyCalendarRequestStatusValue()
self.assertRaises(ValueError, req.parse, bad_value)
ParserContext.INVALID_REQUEST_STATUS_VALUE = oldContext
def testTruncatedValue(self):
bad_value = "2.0"
ok_value = "2.0;"
# Fix the value
oldContext = ParserContext.INVALID_REQUEST_STATUS_VALUE
ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_FIX
req = PyCalendarRequestStatusValue()
req.parse(bad_value)
self.assertEqual(req.getText(), ok_value, "Failed to parse and re-generate '%s'" % (bad_value,))
# Raise the value
ParserContext.INVALID_REQUEST_STATUS_VALUE = ParserContext.PARSER_RAISE
req = PyCalendarRequestStatusValue()
self.assertRaises(ValueError, req.parse, bad_value)
ParserContext.INVALID_REQUEST_STATUS_VALUE = oldContext
def testParseProperty(self):
items = (
"REQUEST-STATUS:2.0;Success",
"REQUEST-STATUS:2.0;Success\;here",
"REQUEST-STATUS:2.0;Success;Extra",
"REQUEST-STATUS:2.0;Success\;here;Extra",
"REQUEST-STATUS:2.0;Success;Extra\;here",
"REQUEST-STATUS:2.0;Success\;here;Extra\;here too",
)
for item in items:
req = PyCalendarProperty()
req.parse(item)
self.assertEqual(req.getText(), item + "\r\n", "Failed to parse and re-generate '%s'" % (item,))
pycalendar-2.0~svn13177/src/pycalendar/tests/test_adr.py 0000644 0001750 0001750 00000003031 12101017573 022261 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.adr import Adr
import unittest
class TestAdrValue(unittest.TestCase):
def testInit(self):
data = (
(
("pobox", "extended", "street", "locality", "region", "postalcode", "country"),
"pobox;extended;street;locality;region;postalcode;country",
),
(
(("pobox",), ("extended",), ("street1", "street2",), "locality", "region", (), "country"),
"pobox;extended;street1,street2;locality;region;;country",
),
)
for args, result in data:
a = Adr(*args)
self.assertEqual(
a.getValue(),
args,
)
self.assertEqual(
a.getText(),
result,
)
self.assertEqual(
a.duplicate().getText(),
result,
)
pycalendar-2.0~svn13177/src/pycalendar/tests/test_nvalue.py 0000644 0001750 0001750 00000003635 12101017573 023017 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.nvalue import NValue
from pycalendar.vcard.property import Property
import unittest
class TestNValue(unittest.TestCase):
def testParseValue(self):
items = (
("", ";;;;"),
(";", ";;;;"),
(";;;;", ";;;;"),
("Cyrus;Daboo;;Dr.", "Cyrus;Daboo;;Dr.;"),
(";;;;PhD.", ";;;;PhD."),
("Cyrus", "Cyrus;;;;"),
(";Daboo", ";Daboo;;;"),
)
for item, result in items:
req = NValue()
req.parse(item)
test = req.getText()
self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
def testParseProperty(self):
items = (
("N:", "N:;;;;"),
("N:;", "N:;;;;"),
("N:;;;;", "N:;;;;"),
("N:Cyrus;Daboo;;Dr.", "N:Cyrus;Daboo;;Dr.;"),
("N:;;;;PhD.", "N:;;;;PhD."),
("N:Cyrus", "N:Cyrus;;;;"),
("N:;Daboo", "N:;Daboo;;;"),
("N;VALUE=TEXT:Cyrus;Daboo;;Dr.", "N:Cyrus;Daboo;;Dr.;"),
)
for item, result in items:
prop = Property()
prop.parse(item)
test = prop.getText()
self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))
pycalendar-2.0~svn13177/src/pycalendar/tests/test_duration.py 0000644 0001750 0001750 00000006345 12101017573 023353 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from cStringIO import StringIO
from pycalendar.duration import PyCalendarDuration
from pycalendar.parser import ParserContext
import unittest
class TestDuration(unittest.TestCase):
test_data = (
(0, "PT0S"),
(1, "PT1S"),
(1, "+PT1S"),
(-1, "-PT1S"),
(60, "PT1M"),
(60 + 2, "PT1M2S"),
(1 * 60 * 60, "PT1H"),
(1 * 60 * 60 + 2 * 60, "PT1H2M"),
(1 * 60 * 60 + 1, "PT1H0M1S"),
(1 * 60 * 60 + 2 * 60 + 1, "PT1H2M1S"),
(24 * 60 * 60, "P1D"),
(24 * 60 * 60 + 3 * 60 * 60, "P1DT3H"),
(24 * 60 * 60 + 2 * 60, "P1DT2M"),
(24 * 60 * 60 + 3 * 60 * 60 + 2 * 60, "P1DT3H2M"),
(24 * 60 * 60 + 1, "P1DT1S"),
(24 * 60 * 60 + 2 * 60 + 1, "P1DT2M1S"),
(24 * 60 * 60 + 3 * 60 * 60 + 1, "P1DT3H0M1S"),
(24 * 60 * 60 + 3 * 60 * 60 + 2 * 60 + 1, "P1DT3H2M1S"),
(14 * 24 * 60 * 60, "P2W"),
(15 * 24 * 60 * 60, "P15D"),
(14 * 24 * 60 * 60 + 1, "P14DT1S"),
)
def testGenerate(self):
def _doTest(d, result):
if result[0] == "+":
result = result[1:]
os = StringIO()
d.generate(os)
self.assertEqual(os.getvalue(), result)
for seconds, result in TestDuration.test_data:
_doTest(PyCalendarDuration(duration=seconds), result)
def testParse(self):
for seconds, result in TestDuration.test_data:
duration = PyCalendarDuration().parseText(result)
self.assertEqual(duration.getTotalSeconds(), seconds)
def testParseBad(self):
test_bad_data = (
"",
"ABC",
"P",
"PABC",
"P12",
"P12D1",
"P12DTAB",
"P12DT1HA",
"P12DT1MA",
"P12DT1SA",
)
for data in test_bad_data:
self.assertRaises(ValueError, PyCalendarDuration.parseText, data)
def testRelaxedBad(self):
test_relaxed_data = (
("P12DT", 12 * 24 * 60 * 60, "P12D"),
("-P1WT", -7 * 24 * 60 * 60, "-P1W"),
("-P1W1D", -7 * 24 * 60 * 60, "-P1W"),
)
for text, seconds, result in test_relaxed_data:
ParserContext.INVALID_DURATION_VALUE = ParserContext.PARSER_FIX
self.assertEqual(PyCalendarDuration.parseText(text).getTotalSeconds(), seconds)
self.assertEqual(PyCalendarDuration.parseText(text).getText(), result)
ParserContext.INVALID_DURATION_VALUE = ParserContext.PARSER_RAISE
self.assertRaises(ValueError, PyCalendarDuration.parseText, text)
pycalendar-2.0~svn13177/src/pycalendar/tests/test_componentrecur.py 0000644 0001750 0001750 00000003570 12101017573 024566 0 ustar rahul rahul # Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.calendar import PyCalendar
import cStringIO as StringIO
import unittest
class TestCalendar(unittest.TestCase):
def testDuplicateWithRecurrenceChange(self):
data = (
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;COUNT=400
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
)
cal1 = PyCalendar()
cal1.parse(StringIO.StringIO(data[0]))
cal2 = cal1.duplicate()
vevent = cal2.getComponents()[0]
rrules = vevent.getRecurrenceSet()
for rrule in rrules.getRules():
rrule.setUseCount(True)
rrule.setCount(400)
rrules.changed()
self.assertEqual(data[0], str(cal1))
self.assertEqual(data[1], str(cal2))
pycalendar-2.0~svn13177/src/pycalendar/tests/test_xml.py 0000644 0001750 0001750 00000005656 12101017573 022332 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.calendar import PyCalendar
import cStringIO as StringIO
import difflib
import unittest
class TestCalendar(unittest.TestCase):
data = (
(
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:12345-67890-3
DTSTART:20071114T000000Z
ATTENDEE:mailto:user2@example.com
EXDATE:20081114T000000Z
ORGANIZER:mailto:user1@example.com
RRULE:FREQ=YEARLY
END:VEVENT
X-TEST:Testing
END:VCALENDAR
""".replace("\n", "\r\n"),
"""
2.0
-//mulberrymail.com//Mulberry v4.0//EN
Testing
12345-67890-3
2007-11-14T00:00:00Z
mailto:user2@example.com
2008-11-14T00:00:00Z
mailto:user1@example.com
YEARLY
""",
),
)
def testGenerateXML(self):
def _doRoundtrip(caldata, resultdata=None):
test1 = resultdata if resultdata is not None else caldata
cal = PyCalendar()
cal.parse(StringIO.StringIO(caldata))
test2 = cal.getTextXML()
self.assertEqual(
test1,
test2,
"\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
)
for item1, item2 in self.data:
_doRoundtrip(item1, item2)
pycalendar-2.0~svn13177/src/pycalendar/tests/test_datetime.py 0000644 0001750 0001750 00000030145 12301206170 023310 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.calendar import PyCalendar
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.parser import ParserContext
from pycalendar.timezone import PyCalendarTimezone
import unittest
class TestDateTime(unittest.TestCase):
def _patch(self, obj, attr, value):
oldvalue = getattr(obj, attr)
setattr(obj, attr, value)
def _restore():
setattr(obj, attr, oldvalue)
self.addCleanup(_restore)
def testDuplicateASUTC(self):
items = (
(
PyCalendarDateTime(2011, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
PyCalendarDateTime(2011, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
),
(
PyCalendarDateTime(2011, 1, 1, 0, 0, 0),
PyCalendarDateTime(2011, 1, 1, 0, 0, 0),
),
(
PyCalendarDateTime(2011, 1, 1),
PyCalendarDateTime(2011, 1, 1),
)
)
for item, result in items:
dup = item.duplicateAsUTC()
self.assertEqual(str(dup), str(result), "Failed to convert '%s'" % (item,))
def testDuplicateInSet(self):
s = set(
(
PyCalendarDateTime(2011, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
PyCalendarDateTime(2011, 1, 2, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)),
)
)
self.assertTrue(PyCalendarDateTime(2011, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)) in s)
self.assertFalse(PyCalendarDateTime(2011, 1, 3, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)) in s)
def testRoundtrip(self):
data1 = (
"20110102",
"20110103T121212",
"20110103T121212Z",
"00010102",
"00010103T121212",
"00010103T121212Z",
)
data2 = (
("20110102", "20110102"),
("2011-01-02", "20110102"),
("20110103T121212", "20110103T121212"),
("2011-01-03T12:12:12", "20110103T121212"),
("20110103T121212Z", "20110103T121212Z"),
("2011-01-03T12:12:12Z", "20110103T121212Z"),
("20110103T121212+0100", "20110103T121212+0100"),
("2011-01-03T12:12:12-0500", "20110103T121212-0500"),
("20110103T121212,123", "20110103T121212"),
("2011-01-03T12:12:12,123", "20110103T121212"),
("20110103T121212,123Z", "20110103T121212Z"),
("2011-01-03T12:12:12,123Z", "20110103T121212Z"),
("20110103T121212,123+0100", "20110103T121212+0100"),
("2011-01-03T12:12:12,123-0500", "20110103T121212-0500"),
)
for item in data1:
dt = PyCalendarDateTime.parseText(item, False)
self.assertEqual(dt.getText(), item, "Failed on: %s" % (item,))
for item, result in data2:
dt = PyCalendarDateTime.parseText(item, True)
self.assertEqual(dt.getText(), result, "Failed on: %s" % (item,))
def testBadParse(self):
self._patch(ParserContext, "INVALID_DATETIME_LEADINGSPACE", ParserContext.PARSER_RAISE)
data1 = (
"2011",
"201101023",
"20110103t121212",
"20110103T1212",
"20110103T1212123",
"20110103T121212A",
"2011-01-03T121212Z",
"20110103T12:12:12Z",
"20110103T121212+0500",
" 10102",
" 10102T010101",
"2011 102",
"201101 3T121212",
"-1110102",
"2011-102",
"201101-3T121212",
)
data2 = (
"2011-01+02",
"20110103T12-12-12",
"2011-01-03T12:12:12,",
"2011-01-03T12:12:12,ABC",
"20110103T12:12:12-1",
)
for item in data1:
self.assertRaises(ValueError, PyCalendarDateTime.parseText, item, False)
for item in data2:
self.assertRaises(ValueError, PyCalendarDateTime.parseText, item, False)
def testBadParseFixed(self):
self._patch(ParserContext, "INVALID_DATETIME_LEADINGSPACE", ParserContext.PARSER_ALLOW)
data = (
(" 10102", "00010102"),
("2001 102", "20010102"),
("200101 2", "20010102"),
(" 10102T010101", "00010102T010101"),
("2001 102T010101", "20010102T010101"),
("200101 2T010101", "20010102T010101"),
)
for item, result in data:
dt = PyCalendarDateTime.parseText(item)
self.assertEqual(str(dt), result)
def testCachePreserveOnAdjustment(self):
# UTC first
dt = PyCalendarDateTime(2012, 6, 7, 12, 0, 0, PyCalendarTimezone(tzid="utc"))
dt.getPosixTime()
# check existing cache is complete
self.assertTrue(dt.mPosixTimeCached)
self.assertNotEqual(dt.mPosixTime, 0)
self.assertEqual(dt.mTZOffset, None)
# duplicate preserves cache details
dt2 = dt.duplicate()
self.assertTrue(dt2.mPosixTimeCached)
self.assertEqual(dt2.mPosixTime, dt.mPosixTime)
self.assertEqual(dt2.mTZOffset, dt.mTZOffset)
# adjust preserves cache details
dt2.adjustToUTC()
self.assertTrue(dt2.mPosixTimeCached)
self.assertEqual(dt2.mPosixTime, dt.mPosixTime)
self.assertEqual(dt2.mTZOffset, dt.mTZOffset)
# Now timezone
tzdata = """BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:America/Pittsburgh
BEGIN:STANDARD
DTSTART:18831118T120358
RDATE:18831118T120358
TZNAME:EST
TZOFFSETFROM:-045602
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19180331T020000
RRULE:FREQ=YEARLY;UNTIL=19190330T070000Z;BYDAY=-1SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19181027T020000
RRULE:FREQ=YEARLY;UNTIL=19191026T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:STANDARD
DTSTART:19200101T000000
RDATE:19200101T000000
RDATE:19420101T000000
RDATE:19460101T000000
RDATE:19670101T000000
TZNAME:EST
TZOFFSETFROM:-0500
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19200328T020000
RDATE:19200328T020000
RDATE:19740106T020000
RDATE:19750223T020000
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19201031T020000
RDATE:19201031T020000
RDATE:19450930T020000
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19210424T020000
RRULE:FREQ=YEARLY;UNTIL=19410427T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19210925T020000
RRULE:FREQ=YEARLY;UNTIL=19410928T060000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19420209T020000
RDATE:19420209T020000
TZNAME:EWT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19450814T190000
RDATE:19450814T190000
TZNAME:EPT
TZOFFSETFROM:-0400
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19460428T020000
RRULE:FREQ=YEARLY;UNTIL=19660424T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19460929T020000
RRULE:FREQ=YEARLY;UNTIL=19540926T060000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:STANDARD
DTSTART:19551030T020000
RRULE:FREQ=YEARLY;UNTIL=19661030T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19670430T020000
RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19760425T020000
RRULE:FREQ=YEARLY;UNTIL=19860427T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19870405T020000
RRULE:FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""".replace("\n", "\r\n")
PyCalendar.parseText(tzdata)
dt = PyCalendarDateTime(2012, 6, 7, 12, 0, 0, PyCalendarTimezone(tzid="America/Pittsburgh"))
dt.getPosixTime()
# check existing cache is complete
self.assertTrue(dt.mPosixTimeCached)
self.assertNotEqual(dt.mPosixTime, 0)
self.assertEqual(dt.mTZOffset, -14400)
# duplicate preserves cache details
dt2 = dt.duplicate()
self.assertTrue(dt2.mPosixTimeCached)
self.assertEqual(dt2.mPosixTime, dt.mPosixTime)
self.assertEqual(dt2.mTZOffset, dt.mTZOffset)
# adjust preserves cache details
dt2.adjustToUTC()
self.assertTrue(dt2.mPosixTimeCached)
self.assertEqual(dt2.mPosixTime, dt.mPosixTime)
self.assertEqual(dt2.mTZOffset, 0)
def testSetWeekNo(self):
dt = PyCalendarDateTime(2013, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
self.assertEqual(dt.getWeekNo(), 1)
dt.setWeekNo(1)
self.assertEqual(dt, PyCalendarDateTime(2013, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
self.assertEqual(dt.getWeekNo(), 1)
dt = PyCalendarDateTime(2013, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
self.assertEqual(dt.getWeekNo(), 1)
dt.setWeekNo(2)
self.assertEqual(dt, PyCalendarDateTime(2013, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
self.assertEqual(dt.getWeekNo(), 2)
dt = PyCalendarDateTime(2013, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
self.assertEqual(dt.getWeekNo(), 2)
dt.setWeekNo(1)
self.assertEqual(dt, PyCalendarDateTime(2013, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
self.assertEqual(dt.getWeekNo(), 1)
dt = PyCalendarDateTime(2014, 1, 7, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
self.assertEqual(dt.getWeekNo(), 2)
dt.setWeekNo(1)
self.assertEqual(dt, PyCalendarDateTime(2013, 12, 31, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
self.assertEqual(dt.getWeekNo(), 1)
dt = PyCalendarDateTime(2012, 12, 31, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
self.assertEqual(dt.getWeekNo(), 1)
dt.setWeekNo(1)
self.assertEqual(dt, PyCalendarDateTime(2012, 12, 31, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
self.assertEqual(dt.getWeekNo(), 1)
dt = PyCalendarDateTime(2016, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
self.assertEqual(dt.getWeekNo(), 53)
dt.setWeekNo(1)
self.assertEqual(dt, PyCalendarDateTime(2016, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
self.assertEqual(dt.getWeekNo(), 1)
dt.setWeekNo(2)
self.assertEqual(dt, PyCalendarDateTime(2016, 1, 15, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
self.assertEqual(dt.getWeekNo(), 2)
dt = PyCalendarDateTime(2016, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
self.assertEqual(dt.getWeekNo(), 1)
dt.setWeekNo(1)
self.assertEqual(dt, PyCalendarDateTime(2016, 1, 8, 0, 0, 0, tzid=PyCalendarTimezone(utc=True)))
self.assertEqual(dt.getWeekNo(), 1)
pycalendar-2.0~svn13177/src/pycalendar/tests/test_n.py 0000644 0001750 0001750 00000005105 12101017573 021754 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.n import N
import unittest
class TestAdrValue(unittest.TestCase):
def testInit(self):
data = (
(
("last", "first", "middle", "prefix", "suffix"),
"last;first;middle;prefix;suffix",
"prefix first middle last suffix",
),
(
("last", ("first",), ("middle1", "middle2",), (), ("suffix",)),
"last;first;middle1,middle2;;suffix",
"first middle1 middle2 last suffix",
),
)
for args, result, fullName in data:
n = N(*args)
self.assertEqual(
n.getValue(),
args,
)
self.assertEqual(
n.getText(),
result,
)
self.assertEqual(
n.getFullName(),
fullName,
)
self.assertEqual(
n.duplicate().getText(),
result,
)
def testInitWithKeywords(self):
data = (
(
{"first": "first", "last": "last", "middle": "middle", "prefix": "prefix", "suffix": "suffix"},
"last;first;middle;prefix;suffix",
"prefix first middle last suffix",
),
(
{"first": ("first",), "last": "last", "middle": ("middle1", "middle2",), "prefix": (), "suffix": ("suffix",)},
"last;first;middle1,middle2;;suffix",
"first middle1 middle2 last suffix",
),
)
for kwargs, result, fullName in data:
n = N(**kwargs)
self.assertEqual(
n.getText(),
result,
)
self.assertEqual(
n.getFullName(),
fullName,
)
self.assertEqual(
n.duplicate().getText(),
result,
)
pycalendar-2.0~svn13177/src/pycalendar/tests/test_property.py 0000644 0001750 0001750 00000016006 12146674036 023421 0 ustar rahul rahul ##
# Copyright (c) 2007-2013 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.attribute import PyCalendarAttribute
from pycalendar.exceptions import PyCalendarInvalidProperty
from pycalendar.parser import ParserContext
from pycalendar.property import PyCalendarProperty
from pycalendar.value import PyCalendarValue
import unittest
class TestProperty(unittest.TestCase):
test_data = (
# Different value types
"ATTACH;VALUE=BINARY:VGVzdA==",
"attach;VALUE=BINARY:VGVzdA==",
"ORGANIZER:mailto:jdoe@example.com",
"DTSTART;TZID=US/Eastern:20060226T120000",
"DTSTART;VALUE=DATE:20060226",
"DTSTART:20060226T130000Z",
"X-FOO:BAR",
"DURATION:PT10M",
"duraTION:PT10M",
"SEQUENCE:1",
"RDATE:20060226T120000Z,20060227T120000Z",
"FREEBUSY:20060226T120000Z/20060227T120000Z",
"SUMMARY:Some \\ntext",
"RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=-1",
"REQUEST-STATUS:2.0;Success",
"URI:http://www.example.com",
"TZOFFSETFROM:-0500",
"X-Test:Some\, text.",
"X-Test:Some:, text.",
"X-APPLE-STRUCTURED-LOCATION;VALUE=URI:geo:123.123,123.123",
"X-CALENDARSERVER-PRIVATE-COMMENT:This\\ntest\\nis\\, here.\\n",
# Various parameters
"DTSTART;TZID=\"Somewhere, else\":20060226T120000",
"ATTENDEE;PARTSTAT=ACCEPTED;ROLE=CHAIR:mailto:jdoe@example.com",
"X-APPLE-STRUCTURED-LOCATION;VALUE=URI;X-APPLE-ABUID=ab\\://Work;X-TITLE=\"10\\n XX S. XXX Dr.\\nSuite XXX\\nXX XX XXXXX\\nUnited States\":\"geo:11.111111,-11.111111\"",
# Parameter escaping
"ATTENDEE;CN=My ^'Test^' Name;ROLE=CHAIR:mailto:jdoe@example.com",
)
def testParseGenerate(self):
for data in TestProperty.test_data:
prop = PyCalendarProperty()
prop.parse(data)
propstr = str(prop).replace("\r\n ", "")
self.assertEqual(propstr[:-2], data, "Failed parse/generate: %s to %s" % (data, propstr,))
def testEquality(self):
for data in TestProperty.test_data:
prop1 = PyCalendarProperty()
prop1.parse(data)
prop2 = PyCalendarProperty()
prop2.parse(data)
self.assertEqual(prop1, prop2, "Failed equality: %s" % (data,))
def testParseBad(self):
test_bad_data = (
"DTSTART;TZID=US/Eastern:abc",
"DTSTART;VALUE=DATE:20060226T",
"DTSTART:20060226T120000A",
"X-FOO;:BAR",
"DURATION:A",
"SEQUENCE:b",
"RDATE:20060226T120000Z;20060227T120000Z",
"FREEBUSY:20060226T120000Z/ABC",
"SUMMARY:Some \\qtext",
"RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,VE;BYSETPOS=-1",
"TZOFFSETFROM:-050",
"""ATTENDEE;CN="\\";CUTYPE=INDIVIDUAL;PARTSTAT=X-UNDELIVERABLE:invalid:nomai
l""",
)
save = ParserContext.INVALID_ESCAPE_SEQUENCES
for data in test_bad_data:
ParserContext.INVALID_ESCAPE_SEQUENCES = ParserContext.PARSER_RAISE
prop = PyCalendarProperty()
self.assertRaises(PyCalendarInvalidProperty, prop.parse, data)
ParserContext.INVALID_ESCAPE_SEQUENCES = save
def testHash(self):
hashes = []
for item in TestProperty.test_data:
prop = PyCalendarProperty()
prop.parse(item)
hashes.append(hash(prop))
hashes.sort()
for i in range(1, len(hashes)):
self.assertNotEqual(hashes[i - 1], hashes[i])
def testDefaultValueCreate(self):
test_data = (
("ATTENDEE", "mailto:attendee@example.com", "ATTENDEE:mailto:attendee@example.com\r\n"),
("attendee", "mailto:attendee@example.com", "attendee:mailto:attendee@example.com\r\n"),
("ORGANIZER", "mailto:organizer@example.com", "ORGANIZER:mailto:organizer@example.com\r\n"),
("ORGANizer", "mailto:organizer@example.com", "ORGANizer:mailto:organizer@example.com\r\n"),
("URL", "http://example.com/tz1", "URL:http://example.com/tz1\r\n"),
("TZURL", "http://example.com/tz2", "TZURL:http://example.com/tz2\r\n"),
)
for propname, propvalue, result in test_data:
prop = PyCalendarProperty(name=propname, value=propvalue)
self.assertEqual(str(prop), result)
def testGEOValueRoundtrip(self):
data = "GEO:123.456,789.101"
prop = PyCalendarProperty()
prop.parse(data)
self.assertEqual(str(prop), data + "\r\n")
def testUnknownValueRoundtrip(self):
data = "X-FOO:Text, not escaped"
prop = PyCalendarProperty()
prop.parse(data)
self.assertEqual(str(prop), data + "\r\n")
prop = PyCalendarProperty("X-FOO", "Text, not escaped")
self.assertEqual(str(prop), data + "\r\n")
data = "X-FOO:Text\\, escaped\\n"
prop = PyCalendarProperty()
prop.parse(data)
self.assertEqual(str(prop), data + "\r\n")
prop = PyCalendarProperty("X-FOO", "Text\\, escaped\\n")
self.assertEqual(str(prop), data + "\r\n")
def testNewRegistrationValueRoundtrip(self):
PyCalendarProperty.registerDefaultValue("X-SPECIAL-REGISTRATION", PyCalendarValue.VALUETYPE_TEXT)
data = "X-SPECIAL-REGISTRATION:Text\\, escaped\\n"
prop = PyCalendarProperty()
prop.parse(data)
self.assertEqual(str(prop), "X-SPECIAL-REGISTRATION:Text\\, escaped\\n\r\n")
prop = PyCalendarProperty("X-SPECIAL-REGISTRATION", "Text, escaped\n")
self.assertEqual(str(prop), "X-SPECIAL-REGISTRATION:Text\\, escaped\\n\r\n")
def testParameterEncodingDecoding(self):
prop = PyCalendarProperty("X-FOO", "Test")
prop.addAttribute(PyCalendarAttribute("X-BAR", "\"Check\""))
self.assertEqual(str(prop), "X-FOO;X-BAR=^'Check^':Test\r\n")
prop.addAttribute(PyCalendarAttribute("X-BAR2", "Check\nThis\tOut\n"))
self.assertEqual(str(prop), "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test\r\n")
data = "X-FOO;X-BAR=^'Check^':Test"
prop = PyCalendarProperty()
prop.parse(data)
self.assertEqual(prop.getAttributeValue("X-BAR"), "\"Check\"")
data = "X-FOO;X-BAR=^'Check^';X-BAR2=Check^nThis\tOut^n:Test"
prop = PyCalendarProperty()
prop.parse(data)
self.assertEqual(prop.getAttributeValue("X-BAR"), "\"Check\"")
self.assertEqual(prop.getAttributeValue("X-BAR2"), "Check\nThis\tOut\n")
pycalendar-2.0~svn13177/src/pycalendar/tests/test_utils.py 0000644 0001750 0001750 00000003450 12146674036 022674 0 ustar rahul rahul ##
# Copyright (c) 2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import unittest
from pycalendar.utils import encodeParameterValue, decodeParameterValue
class TestUtils(unittest.TestCase):
def test_encodeParameterValue(self):
"""
Round trip encodeParameterValue and decodeParameterValue.
"""
data = (
("abc", "abc", None),
("\"abc\"", "^'abc^'", None),
("abc\ndef", "abc^ndef", None),
("abc\rdef", "abc^ndef", "abc\ndef"),
("abc\r\ndef", "abc^ndef", "abc\ndef"),
("abc\n\tdef", "abc^n\tdef", None),
("abc^2", "abc^^2", None),
("^abc^", "^^abc^^", None),
)
for value, encoded, decoded in data:
if decoded is None:
decoded = value
self.assertEqual(encodeParameterValue(value), encoded)
self.assertEqual(decodeParameterValue(encoded), decoded)
def test_decodeParameterValue(self):
"""
Special cases for decodeParameterValue.
"""
data = (
("^a^bc^", "^a^bc^"),
("^^^abc", "^^abc"),
)
for value, decoded in data:
self.assertEqual(decodeParameterValue(value), decoded)
pycalendar-2.0~svn13177/src/pycalendar/tests/test_i18n.py 0000644 0001750 0001750 00000003777 12101017573 022313 0 ustar rahul rahul # coding: utf-8
##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.attribute import PyCalendarAttribute
from pycalendar.calendar import PyCalendar
import cStringIO as StringIO
import unittest
class TestCalendar(unittest.TestCase):
def testAddCN(self):
data = (
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
ORGANIZER:user01@example.com
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"まだ",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
ORGANIZER;CN=まだ:user01@example.com
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
)
cal1 = PyCalendar()
cal1.parse(StringIO.StringIO(data[0]))
vevent = cal1.getComponents("VEVENT")[0]
organizer = vevent.getProperties("ORGANIZER")[0]
organizer.addAttribute(PyCalendarAttribute("CN", data[1]))
cal2 = PyCalendar()
cal2.parse(StringIO.StringIO(data[2]))
self.assertEqual(str(cal1), str(cal2))
pycalendar-2.0~svn13177/src/pycalendar/tests/test_validation.py 0000644 0001750 0001750 00000005606 12101017573 023657 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.property import PyCalendarProperty
from pycalendar.validation import partial, PropertyValueChecks
import unittest
class TestValidation(unittest.TestCase):
def test_partial(self):
def _test(a, b):
return (a, b)
self.assertEqual(partial(_test, "a", "b")(), ("a", "b",))
self.assertEqual(partial(_test, "a")("b"), ("a", "b",))
self.assertEqual(partial(_test)("a", "b"), ("a", "b",))
def test_stringValue(self):
props = (
("SUMMARY:Test", "Test", True,),
("SUMMARY:Test", "TEST", True,),
("DTSTART:20110623T174806", "Test", False),
)
for prop, test, result in props:
property = PyCalendarProperty()
property.parse(prop)
self.assertEqual(PropertyValueChecks.stringValue(test, property), result)
def test_alwaysUTC(self):
props = (
("SUMMARY:Test", False,),
("DTSTART:20110623T174806", False),
("DTSTART;VALUE=DATE:20110623", False),
("DTSTART:20110623T174806Z", True),
)
for prop, result in props:
property = PyCalendarProperty()
property.parse(prop)
self.assertEqual(PropertyValueChecks.alwaysUTC(property), result)
def test_numericRange(self):
props = (
("SUMMARY:Test", 0, 100, False,),
("PERCENT-COMPLETE:0", 0, 100, True,),
("PERCENT-COMPLETE:100", 0, 100, True,),
("PERCENT-COMPLETE:50", 0, 100, True,),
("PERCENT-COMPLETE:200", 0, 100, False,),
("PERCENT-COMPLETE:-1", 0, 100, False,),
)
for prop, low, high, result in props:
property = PyCalendarProperty()
property.parse(prop)
self.assertEqual(PropertyValueChecks.numericRange(low, high, property), result)
def test_positiveIntegerOrZero(self):
props = (
("SUMMARY:Test", False,),
("REPEAT:0", True,),
("REPEAT:100", True,),
("REPEAT:-1", False,),
)
for prop, result in props:
property = PyCalendarProperty()
property.parse(prop)
self.assertEqual(PropertyValueChecks.positiveIntegerOrZero(property), result)
pycalendar-2.0~svn13177/src/pycalendar/tests/test_calendar.py 0000644 0001750 0001750 00000047652 12101017573 023305 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.calendar import PyCalendar
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.exceptions import PyCalendarInvalidData
from pycalendar.parser import ParserContext
from pycalendar.period import PyCalendarPeriod
from pycalendar.property import PyCalendarProperty
import cStringIO as StringIO
import difflib
import unittest
class TestCalendar(unittest.TestCase):
data = (
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
X-WR-CALNAME:PayDay
BEGIN:VTIMEZONE
TZID:US/Eastern
LAST-MODIFIED:20040110T032845Z
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:DC3D0301C7790B38631F1FBB@ninevah.local
DTSTART;VALUE=DATE:20040227
DTSTAMP:20050211T173501Z
RRULE:FREQ=MONTHLY;BYDAY=-1MO,-1TU,-1WE,-1TH,-1FR;BYSETPOS=-1
SUMMARY:PAY DAY
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Alarm for Organizer!
TRIGGER;RELATED=START:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:12345-67890-3
DTSTART:20071114T000000Z
ATTENDEE:mailto:user2@example.com
EXDATE:20081114T000000Z
ORGANIZER:mailto:user1@example.com
RRULE:FREQ=YEARLY
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
ATTACH:http://example.com/test.jpg
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
ATTACH;ENCODING=BASE64;VALUE=BINARY:dGVzdA==
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//Example Inc.//Example Calendar//EN
BEGIN:VTIMEZONE
TZID:America/Montreal
LAST-MODIFIED:20040110T032845Z
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VAVAILABILITY
UID:20061005T133225Z-00001-availability@example.com
DTSTART;TZID=America/Montreal:20060101T000000
DTEND;TZID=America/Montreal:20060108T000000
DTSTAMP:20061005T133225Z
ORGANIZER:mailto:bernard@example.com
BEGIN:AVAILABLE
UID:20061005T133225Z-00001-A-availability@example.com
DTSTART;TZID=America/Montreal:20060102T090000
DTEND;TZID=America/Montreal:20060102T120000
DTSTAMP:20061005T133225Z
RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR
SUMMARY:Monday\\, Wednesday and Friday from 9:00 to 12:00
END:AVAILABLE
BEGIN:AVAILABLE
UID:20061005T133225Z-00001-A-availability@example.com
RECURRENCE-ID;TZID=America/Montreal:20060106T090000
DTSTART;TZID=America/Montreal:20060106T120000
DTEND;TZID=America/Montreal:20060106T170000
DTSTAMP:20061005T133225Z
SUMMARY:Friday override from 12:00 to 17:00
END:AVAILABLE
END:VAVAILABILITY
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//Example Inc.//Example Calendar//EN
BEGIN:VTODO
UID:event1@ninevah.local
CREATED:20060101T150000Z
DTSTAMP:20051222T205953Z
SUMMARY:event 1
END:VTODO
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
BEGIN:X-COMPONENT
UID:1234
END:X-COMPONENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//Apple Inc.//iCal 4.0.1//EN
BEGIN:VTIMEZONE
TZID:US/Pacific
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:uid4
DTSTART;TZID=US/Pacific:20100207T170000
DTEND;TZID=US/Pacific:20100207T173000
CREATED:20100203T013849Z
DTSTAMP:20100203T013909Z
SEQUENCE:3
SUMMARY:New Event
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:AUDIO
ATTACH:Basso
TRIGGER:-PT20M
X-WR-ALARMUID:1377CCC7-F85C-4610-8583-9513D4B364E1
END:VALARM
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
ATTACH:http://example.com/test.jpg
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
X-APPLE-STRUCTURED-LOCATION:geo:123.123,123.123
X-Test:Some\, text.
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
ATTACH:http://example.com/test.jpg
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
X-APPLE-STRUCTURED-LOCATION;VALUE=URI:geo:123.123,123.123
X-Test:Some\, text.
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
)
data2 = (
(
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:12345-67890-3
DTSTART:20071114T000000Z
ATTENDEE:mailto:user2@example.com
EXDATE:20081114T000000Z
ORGANIZER:mailto:user1@example.com
RRULE:FREQ=YEARLY
END:VEVENT
X-TEST:Testing
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
X-TEST:Testing
BEGIN:VEVENT
UID:12345-67890-3
DTSTART:20071114T000000Z
ATTENDEE:mailto:user2@example.com
EXDATE:20081114T000000Z
ORGANIZER:mailto:user1@example.com
RRULE:FREQ=YEARLY
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
),
)
def testRoundtrip(self):
def _doRoundtrip(caldata, resultdata=None):
test1 = resultdata if resultdata is not None else caldata
cal = PyCalendar()
cal.parse(StringIO.StringIO(caldata))
s = StringIO.StringIO()
cal.generate(s)
test2 = s.getvalue()
self.assertEqual(
test1,
test2,
"\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
)
for item in self.data:
_doRoundtrip(item)
for item1, item2 in self.data2:
_doRoundtrip(item1, item2)
def testRoundtripDuplicate(self):
def _doDuplicateRoundtrip(caldata):
cal = PyCalendar()
cal.parse(StringIO.StringIO(caldata))
cal = cal.duplicate()
s = StringIO.StringIO()
cal.generate(s)
self.assertEqual(caldata, s.getvalue())
for item in self.data:
_doDuplicateRoundtrip(item)
def testEquality(self):
def _doEquality(caldata):
cal1 = PyCalendar()
cal1.parse(StringIO.StringIO(caldata))
cal2 = PyCalendar()
cal2.parse(StringIO.StringIO(caldata))
self.assertEqual(cal1, cal2, "%s\n\n%s" % (cal1, cal2,))
def _doNonEquality(caldata):
cal1 = PyCalendar()
cal1.parse(StringIO.StringIO(caldata))
cal2 = PyCalendar()
cal2.parse(StringIO.StringIO(caldata))
cal2.addProperty(PyCalendarProperty("X-FOO", "BAR"))
self.assertNotEqual(cal1, cal2)
for item in self.data:
_doEquality(item)
_doNonEquality(item)
def testParseComponent(self):
data1 = """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
data2 = """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//Example Inc.//Example Calendar//EN
BEGIN:VTIMEZONE
TZID:America/Montreal
LAST-MODIFIED:20040110T032845Z
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""".replace("\n", "\r\n")
result = """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VTIMEZONE
TZID:America/Montreal
LAST-MODIFIED:20040110T032845Z
BEGIN:DAYLIGHT
DTSTART:20000404T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20001026T020000
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n")
cal = PyCalendar()
cal.parse(StringIO.StringIO(data1))
cal.parseComponent(StringIO.StringIO(data2))
self.assertEqual(str(cal), result)
def testParseFail(self):
data = (
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
""".replace("\n", "\r\n"),
"""BEGIN:VCARD
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
VERSION:2.0
END:VCARD
""".replace("\n", "\r\n"),
"""BOGUS
BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BOGUS
BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
BOGUS
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
BOGUS
""".replace("\n", "\r\n"),
)
for item in data:
self.assertRaises(PyCalendarInvalidData, PyCalendar.parseText, item)
def testParseBlank(self):
data = (
"""
BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""
BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20020101
DTEND;VALUE=DATE:20020102
DTSTAMP:20020101T000000Z
RRULE:FREQ=YEARLY;UNTIL=20031231;BYMONTH=1
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
)
save = ParserContext.BLANK_LINES_IN_DATA
for item in data:
ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_RAISE
self.assertRaises(PyCalendarInvalidData, PyCalendar.parseText, item)
ParserContext.BLANK_LINES_IN_DATA = ParserContext.PARSER_IGNORE
lines = item.split("\r\n")
result = "\r\n".join([line for line in lines if line]) + "\r\n"
self.assertEqual(str(PyCalendar.parseText(item)), result)
ParserContext.BLANK_LINES_IN_DATA = save
def testGetVEvents(self):
data = (
(
"Non-recurring match",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110601
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
(PyCalendarDateTime(2011, 6, 1),),
),
(
"Non-recurring no-match",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110501
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
(),
),
(
"Recurring match",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110601
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY;COUNT=2
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
(
PyCalendarDateTime(2011, 6, 1),
PyCalendarDateTime(2011, 6, 2),
),
),
(
"Recurring no match",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110501
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY;COUNT=2
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
(),
),
(
"Recurring with override match",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART:20110601T120000
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY;COUNT=2
SUMMARY:New Year's Day
END:VEVENT
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
RECURRENCE-ID;VALUE=DATE:20110602T120000
DTSTART;VALUE=DATE:20110602T130000
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
(
PyCalendarDateTime(2011, 6, 1, 12, 0, 0),
PyCalendarDateTime(2011, 6, 2, 13, 0, 0),
),
),
(
"Recurring with override no match",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART:20110501T120000
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY;COUNT=2
SUMMARY:New Year's Day
END:VEVENT
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
RECURRENCE-ID;VALUE=DATE:20110502T120000
DTSTART;VALUE=DATE:20110502T130000
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
(),
),
(
"Recurring partial match",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART;VALUE=DATE:20110531
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY;COUNT=2
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
(
PyCalendarDateTime(2011, 6, 1),
),
),
(
"Recurring with override partial match",
"""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//mulberrymail.com//Mulberry v4.0//EN
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
DTSTART:20110531T120000
DURATION:P1D
DTSTAMP:20020101T000000Z
RRULE:FREQ=DAILY;COUNT=2
SUMMARY:New Year's Day
END:VEVENT
BEGIN:VEVENT
UID:C3184A66-1ED0-11D9-A5E0-000A958A3252
RECURRENCE-ID;VALUE=DATE:20110601T120000
DTSTART;VALUE=DATE:20110601T130000
DURATION:P1D
DTSTAMP:20020101T000000Z
SUMMARY:New Year's Day
END:VEVENT
END:VCALENDAR
""".replace("\n", "\r\n"),
(
PyCalendarDateTime(2011, 6, 1, 13, 0, 0),
),
),
)
for title, caldata, result in data:
calendar = PyCalendar.parseText(caldata)
instances = []
calendar.getVEvents(
PyCalendarPeriod(
start=PyCalendarDateTime(2011, 6, 1),
end=PyCalendarDateTime(2011, 7, 1),
),
instances
)
instances = tuple([instance.getInstanceStart() for instance in instances])
self.assertEqual(instances, result, "Failed in %s: got %s, expected %s" % (title, instances, result))
pycalendar-2.0~svn13177/src/pycalendar/tests/test_urivalue.py 0000644 0001750 0001750 00000006532 12101017573 023360 0 ustar rahul rahul ##
# Copyright (c) 2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.parser import ParserContext
from pycalendar.urivalue import PyCalendarURIValue
from pycalendar.vcard.property import Property
import unittest
class TestNValue(unittest.TestCase):
def testParseValue(self):
# Restore BACKSLASH_IN_URI_VALUE after test
old_state = ParserContext.BACKSLASH_IN_URI_VALUE
self.addCleanup(setattr, ParserContext, "BACKSLASH_IN_URI_VALUE", old_state)
# Test with BACKSLASH_IN_URI_VALUE = PARSER_FIX
ParserContext.BACKSLASH_IN_URI_VALUE = ParserContext.PARSER_FIX
items = (
("http://example.com", "http://example.com"),
("http://example.com&abd\\,def", "http://example.com&abd,def"),
)
for item, result in items:
req = PyCalendarURIValue()
req.parse(item)
test = req.getText()
self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
# Test with BACKSLASH_IN_URI_VALUE = PARSER_ALLOW
ParserContext.BACKSLASH_IN_URI_VALUE = ParserContext.PARSER_ALLOW
items = (
("http://example.com", "http://example.com"),
("http://example.com&abd\\,def", "http://example.com&abd\\,def"),
)
for item, result in items:
req = PyCalendarURIValue()
req.parse(item)
test = req.getText()
self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
def testParseProperty(self):
# Restore BACKSLASH_IN_URI_VALUE after test
old_state = ParserContext.BACKSLASH_IN_URI_VALUE
self.addCleanup(setattr, ParserContext, "BACKSLASH_IN_URI_VALUE", old_state)
# Test with BACKSLASH_IN_URI_VALUE = PARSER_FIX
ParserContext.BACKSLASH_IN_URI_VALUE = ParserContext.PARSER_FIX
items = (
("URL:http://example.com", "URL:http://example.com"),
("URL:http://example.com&abd\\,def", "URL:http://example.com&abd,def"),
)
for item, result in items:
prop = Property()
prop.parse(item)
test = prop.getText()
self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))
# Test with BACKSLASH_IN_URI_VALUE = PARSER_ALLOW
ParserContext.BACKSLASH_IN_URI_VALUE = ParserContext.PARSER_ALLOW
items = (
("URL:http://example.com", "URL:http://example.com"),
("URL:http://example.com&abd\\,def", "URL:http://example.com&abd\\,def"),
)
for item, result in items:
prop = Property()
prop.parse(item)
test = prop.getText()
self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))
pycalendar-2.0~svn13177/src/pycalendar/tests/test_recurrence.py 0000644 0001750 0001750 00000032716 12320545600 023664 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.period import PyCalendarPeriod
from pycalendar.recurrence import PyCalendarRecurrence
from pycalendar.timezone import PyCalendarTimezone
import unittest
class TestRecurrence(unittest.TestCase):
items = (
"FREQ=DAILY",
"FREQ=YEARLY;COUNT=400",
"FREQ=MONTHLY;UNTIL=20110102",
"FREQ=MONTHLY;UNTIL=20110102T090000",
"FREQ=MONTHLY;UNTIL=20110102T100000Z",
"FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=-1",
# These are from RFC5545 examples
"FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1",
"FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4",
"FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4",
"FREQ=YEARLY;BYDAY=2SU;BYMONTH=3",
"FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10",
"FREQ=DAILY;INTERVAL=2",
"FREQ=DAILY;COUNT=5;INTERVAL=10",
"FREQ=DAILY;UNTIL=20000131T140000Z;BYMONTH=1",
"FREQ=WEEKLY;INTERVAL=2;WKST=SU",
"FREQ=WEEKLY;UNTIL=19971007T000000Z;BYDAY=TU,TH;WKST=SU",
"FREQ=WEEKLY;COUNT=10;BYDAY=TU,TH;WKST=SU",
"FREQ=WEEKLY;UNTIL=19971224T000000Z;INTERVAL=2;BYDAY=MO,WE,FR;WKST=SU",
"FREQ=WEEKLY;COUNT=8;INTERVAL=2;BYDAY=TU,TH;WKST=SU",
"FREQ=MONTHLY;BYMONTHDAY=-3",
"FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1",
"FREQ=MONTHLY;COUNT=10;INTERVAL=18;BYMONTHDAY=10,11,12,13,14,15",
"FREQ=YEARLY;COUNT=10;INTERVAL=3;BYYEARDAY=1,100,200",
"FREQ=YEARLY;BYDAY=MO;BYWEEKNO=20",
"FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3",
"FREQ=DAILY;BYMINUTE=0,20,40;BYHOUR=9,10,11,12,13,14,15,16",
)
def testParse(self):
for item in TestRecurrence.items:
recur = PyCalendarRecurrence()
recur.parse(item)
self.assertEqual(recur.getText(), item, "Failed to parse and re-generate '%s'" % (item,))
def testParseInvalid(self):
items = (
"",
"FREQ=",
"FREQ=MICROSECONDLY",
"FREQ=YEARLY;COUNT=ABC",
"FREQ=YEARLY;COUNT=123;UNTIL=20110102",
"FREQ=MONTHLY;UNTIL=20110102T",
"FREQ=MONTHLY;UNTIL=20110102t090000",
"FREQ=MONTHLY;UNTIL=20110102T100000z",
"FREQ=MONTHLY;UNTIL=20110102TAABBCCz",
"FREQ=MONTHLY;BYDAY=A",
"FREQ=MONTHLY;BYDAY=+1,3MO",
"FREQ=MONTHLY;BYHOUR=A",
"FREQ=MONTHLY;BYHOUR=54",
)
for item in items:
self.assertRaises(ValueError, PyCalendarRecurrence().parse, item)
def testEquality(self):
recur1 = PyCalendarRecurrence()
recur1.parse("FREQ=YEARLY;COUNT=400")
recur2 = PyCalendarRecurrence()
recur2.parse("COUNT=400;FREQ=YEARLY")
self.assertEqual(recur1, recur2)
def testInequality(self):
recur1 = PyCalendarRecurrence()
recur1.parse("FREQ=YEARLY;COUNT=400")
recur2 = PyCalendarRecurrence()
recur2.parse("COUNT=400;FREQ=YEARLY;BYMONTH=1")
self.assertNotEqual(recur1, recur2)
def testHash(self):
hashes = []
for item in TestRecurrence.items:
recur = PyCalendarRecurrence()
recur.parse(item)
hashes.append(hash(recur))
hashes.sort()
for i in range(1, len(hashes)):
self.assertNotEqual(hashes[i - 1], hashes[i])
def testByWeekNoExpand(self):
recur = PyCalendarRecurrence()
recur.parse("FREQ=YEARLY;BYWEEKNO=1,2")
start = PyCalendarDateTime(2013, 1, 1, 0, 0, 0)
end = PyCalendarDateTime(2017, 1, 1, 0, 0, 0)
items = []
range = PyCalendarPeriod(start, end)
recur.expand(start, range, items)
self.assertEqual(
items,
[
PyCalendarDateTime(2013, 1, 1, 0, 0, 0),
PyCalendarDateTime(2013, 1, 8, 0, 0, 0),
PyCalendarDateTime(2014, 1, 1, 0, 0, 0),
PyCalendarDateTime(2014, 1, 8, 0, 0, 0),
PyCalendarDateTime(2015, 1, 1, 0, 0, 0),
PyCalendarDateTime(2015, 1, 8, 0, 0, 0),
PyCalendarDateTime(2016, 1, 8, 0, 0, 0),
PyCalendarDateTime(2016, 1, 15, 0, 0, 0),
],
)
def testMonthlyInvalidStart(self):
recur = PyCalendarRecurrence()
recur.parse("FREQ=MONTHLY")
start = PyCalendarDateTime(2014, 1, 40, 12, 0, 0)
end = PyCalendarDateTime(2015, 1, 1, 0, 0, 0)
items = []
range = PyCalendarPeriod(start, end)
recur.expand(start, range, items)
self.assertEqual(
items,
[
PyCalendarDateTime(2014, 2, 9, 12, 0, 0),
PyCalendarDateTime(2014, 3, 9, 12, 0, 0),
PyCalendarDateTime(2014, 4, 9, 12, 0, 0),
PyCalendarDateTime(2014, 5, 9, 12, 0, 0),
PyCalendarDateTime(2014, 6, 9, 12, 0, 0),
PyCalendarDateTime(2014, 7, 9, 12, 0, 0),
PyCalendarDateTime(2014, 8, 9, 12, 0, 0),
PyCalendarDateTime(2014, 9, 9, 12, 0, 0),
PyCalendarDateTime(2014, 10, 9, 12, 0, 0),
PyCalendarDateTime(2014, 11, 9, 12, 0, 0),
PyCalendarDateTime(2014, 12, 9, 12, 0, 0),
],
)
def testMonthlyInUTC(self):
recur = PyCalendarRecurrence()
recur.parse("FREQ=MONTHLY")
start = PyCalendarDateTime(2014, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(utc=True))
end = PyCalendarDateTime(2015, 1, 1, 0, 0, 0, tzid=PyCalendarTimezone(utc=True))
items = []
range = PyCalendarPeriod(start, end)
recur.expand(PyCalendarDateTime(2014, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")), range, items)
self.assertEqual(
items,
[
PyCalendarDateTime(2014, 1, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")),
PyCalendarDateTime(2014, 2, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")),
PyCalendarDateTime(2014, 3, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")),
PyCalendarDateTime(2014, 4, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")),
PyCalendarDateTime(2014, 5, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")),
PyCalendarDateTime(2014, 6, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")),
PyCalendarDateTime(2014, 7, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")),
PyCalendarDateTime(2014, 8, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")),
PyCalendarDateTime(2014, 9, 1, 12, 0, 0, tzid=PyCalendarTimezone(tzid="America/New_York")),
PyCalendarDateTime(2014, 10, 1, 12, 0, 0),
PyCalendarDateTime(2014, 11, 1, 12, 0, 0),
PyCalendarDateTime(2014, 12, 1, 12, 0, 0),
],
)
def testMonthlyStart31st(self):
recur = PyCalendarRecurrence()
recur.parse("FREQ=MONTHLY")
start = PyCalendarDateTime(2014, 1, 31, 12, 0, 0)
end = PyCalendarDateTime(2015, 1, 1, 0, 0, 0)
items = []
range = PyCalendarPeriod(start, end)
recur.expand(start, range, items)
self.assertEqual(
items,
[
PyCalendarDateTime(2014, 1, 31, 12, 0, 0),
PyCalendarDateTime(2014, 3, 31, 12, 0, 0),
PyCalendarDateTime(2014, 5, 31, 12, 0, 0),
PyCalendarDateTime(2014, 7, 31, 12, 0, 0),
PyCalendarDateTime(2014, 8, 31, 12, 0, 0),
PyCalendarDateTime(2014, 10, 31, 12, 0, 0),
PyCalendarDateTime(2014, 12, 31, 12, 0, 0),
],
)
def testMonthlyByMonthDay31(self):
recur = PyCalendarRecurrence()
recur.parse("FREQ=MONTHLY;BYMONTHDAY=31")
start = PyCalendarDateTime(2014, 1, 31, 12, 0, 0)
end = PyCalendarDateTime(2015, 1, 1, 0, 0, 0)
items = []
range = PyCalendarPeriod(start, end)
recur.expand(start, range, items)
self.assertEqual(
items,
[
PyCalendarDateTime(2014, 1, 31, 12, 0, 0),
PyCalendarDateTime(2014, 3, 31, 12, 0, 0),
PyCalendarDateTime(2014, 5, 31, 12, 0, 0),
PyCalendarDateTime(2014, 7, 31, 12, 0, 0),
PyCalendarDateTime(2014, 8, 31, 12, 0, 0),
PyCalendarDateTime(2014, 10, 31, 12, 0, 0),
PyCalendarDateTime(2014, 12, 31, 12, 0, 0),
],
)
def testMonthlyByMonthDayMinus31(self):
recur = PyCalendarRecurrence()
recur.parse("FREQ=MONTHLY;BYMONTHDAY=-31")
start = PyCalendarDateTime(2014, 1, 1, 12, 0, 0)
end = PyCalendarDateTime(2015, 1, 1, 0, 0, 0)
items = []
range = PyCalendarPeriod(start, end)
recur.expand(start, range, items)
self.assertEqual(
items,
[
PyCalendarDateTime(2014, 1, 1, 12, 0, 0),
PyCalendarDateTime(2014, 3, 1, 12, 0, 0),
PyCalendarDateTime(2014, 5, 1, 12, 0, 0),
PyCalendarDateTime(2014, 7, 1, 12, 0, 0),
PyCalendarDateTime(2014, 8, 1, 12, 0, 0),
PyCalendarDateTime(2014, 10, 1, 12, 0, 0),
PyCalendarDateTime(2014, 12, 1, 12, 0, 0),
],
)
def testMonthlyByLastFridayExpand(self):
recur = PyCalendarRecurrence()
recur.parse("FREQ=MONTHLY;BYDAY=-1FR")
start = PyCalendarDateTime(2014, 1, 31, 12, 0, 0)
end = PyCalendarDateTime(2015, 1, 1, 0, 0, 0)
items = []
range = PyCalendarPeriod(start, end)
recur.expand(start, range, items)
self.assertEqual(
items,
[
PyCalendarDateTime(2014, 1, 31, 12, 0, 0),
PyCalendarDateTime(2014, 2, 28, 12, 0, 0),
PyCalendarDateTime(2014, 3, 28, 12, 0, 0),
PyCalendarDateTime(2014, 4, 25, 12, 0, 0),
PyCalendarDateTime(2014, 5, 30, 12, 0, 0),
PyCalendarDateTime(2014, 6, 27, 12, 0, 0),
PyCalendarDateTime(2014, 7, 25, 12, 0, 0),
PyCalendarDateTime(2014, 8, 29, 12, 0, 0),
PyCalendarDateTime(2014, 9, 26, 12, 0, 0),
PyCalendarDateTime(2014, 10, 31, 12, 0, 0),
PyCalendarDateTime(2014, 11, 28, 12, 0, 0),
PyCalendarDateTime(2014, 12, 26, 12, 0, 0),
],
)
def testMonthlyByFifthFridayExpand(self):
recur = PyCalendarRecurrence()
recur.parse("FREQ=MONTHLY;BYDAY=5FR")
start = PyCalendarDateTime(2014, 1, 31, 12, 0, 0)
end = PyCalendarDateTime(2015, 1, 1, 0, 0, 0)
items = []
range = PyCalendarPeriod(start, end)
recur.expand(start, range, items)
self.assertEqual(
items,
[
PyCalendarDateTime(2014, 1, 31, 12, 0, 0),
PyCalendarDateTime(2014, 5, 30, 12, 0, 0),
PyCalendarDateTime(2014, 8, 29, 12, 0, 0),
PyCalendarDateTime(2014, 10, 31, 12, 0, 0),
],
)
def testYearlyLeapDay(self):
recur = PyCalendarRecurrence()
recur.parse("FREQ=YEARLY")
start = PyCalendarDateTime(2012, 2, 29, 12, 0, 0)
end = PyCalendarDateTime(2020, 1, 1, 0, 0, 0)
items = []
range = PyCalendarPeriod(start, end)
recur.expand(start, range, items)
self.assertEqual(
items,
[
PyCalendarDateTime(2012, 2, 29, 12, 0, 0),
PyCalendarDateTime(2016, 2, 29, 12, 0, 0),
],
)
def testYearlyYearDay(self):
recur = PyCalendarRecurrence()
recur.parse("FREQ=YEARLY;BYYEARDAY=366")
start = PyCalendarDateTime(2012, 12, 31, 12, 0, 0)
end = PyCalendarDateTime(2020, 1, 1, 0, 0, 0)
items = []
range = PyCalendarPeriod(start, end)
recur.expand(start, range, items)
self.assertEqual(
items,
[
PyCalendarDateTime(2012, 12, 31, 12, 0, 0),
PyCalendarDateTime(2016, 12, 31, 12, 0, 0),
],
)
def testClearOnChange(self):
recur = PyCalendarRecurrence()
recur.parse("FREQ=DAILY")
start = PyCalendarDateTime(2013, 1, 1, 0, 0, 0)
end = PyCalendarDateTime(2017, 1, 1, 0, 0, 0)
range = PyCalendarPeriod(start, end)
items = []
recur.expand(start, range, items)
self.assertTrue(recur.mCached)
self.assertTrue(len(items) > 100)
recur.setUseCount(True)
recur.setCount(10)
self.assertFalse(recur.mCached)
items = []
recur.expand(start, range, items)
self.assertEqual(len(items), 10)
pycalendar-2.0~svn13177/src/pycalendar/tests/test_adrvalue.py 0000644 0001750 0001750 00000004742 12101017573 023330 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.adrvalue import AdrValue
from pycalendar.vcard.property import Property
import unittest
class TestAdrValue(unittest.TestCase):
def testParseValue(self):
items = (
("", ";;;;;;"),
(";", ";;;;;;"),
(";;;;;;", ";;;;;;"),
(";;123 Main Street;Any Town;CA;91921-1234", ";;123 Main Street;Any Town;CA;91921-1234;"),
(";;;;;;USA", ";;;;;;USA"),
("POB1", "POB1;;;;;;"),
(";EXT", ";EXT;;;;;"),
(";;123 Main Street,The Cards;Any Town;CA;91921-1234", ";;123 Main Street,The Cards;Any Town;CA;91921-1234;"),
(";;123 Main\, Street,The Cards;Any Town;CA;91921-1234", ";;123 Main\, Street,The Cards;Any Town;CA;91921-1234;"),
(";;123 Main\, Street,The\, Cards;Any Town;CA;91921-1234", ";;123 Main\, Street,The\, Cards;Any Town;CA;91921-1234;"),
)
for item, result in items:
req = AdrValue()
req.parse(item)
test = req.getText()
self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
def testParseProperty(self):
items = (
("ADR:", "ADR:;;;;;;"),
("ADR:;", "ADR:;;;;;;"),
("ADR:;;;;;;", "ADR:;;;;;;"),
("ADR:;;123 Main Street;Any Town;CA;91921-1234", "ADR:;;123 Main Street;Any Town;CA;91921-1234;"),
("ADR:;;;;;;USA", "ADR:;;;;;;USA"),
("ADR:POB1", "ADR:POB1;;;;;;"),
("ADR:;EXT", "ADR:;EXT;;;;;"),
("ADR;VALUE=TEXT:;;123 Main Street;Any Town;CA;91921-1234", "ADR:;;123 Main Street;Any Town;CA;91921-1234;"),
)
for item, result in items:
prop = Property()
prop.parse(item)
test = prop.getText()
self.assertEqual(test, result + "\r\n", "Failed to parse and re-generate '%s'" % (item,))
pycalendar-2.0~svn13177/src/pycalendar/tests/test_timezone.py 0000644 0001750 0001750 00000035036 12317143440 023361 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.calendar import PyCalendar
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.timezone import PyCalendarTimezone
import unittest
class TestCalendar(unittest.TestCase):
def testOffsets(self):
data = (
("""BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
BEGIN:VTIMEZONE
TZID:America/New_York
X-LIC-LOCATION:America/New_York
BEGIN:STANDARD
DTSTART:18831118T120358
RDATE:18831118T120358
TZNAME:EST
TZOFFSETFROM:-045602
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19180331T020000
RRULE:FREQ=YEARLY;UNTIL=19190330T070000Z;BYDAY=-1SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19181027T020000
RRULE:FREQ=YEARLY;UNTIL=19191026T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:STANDARD
DTSTART:19200101T000000
RDATE:19200101T000000
RDATE:19420101T000000
RDATE:19460101T000000
RDATE:19670101T000000
TZNAME:EST
TZOFFSETFROM:-0500
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19200328T020000
RDATE:19200328T020000
RDATE:19740106T020000
RDATE:19750223T020000
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19201031T020000
RDATE:19201031T020000
RDATE:19450930T020000
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19210424T020000
RRULE:FREQ=YEARLY;UNTIL=19410427T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19210925T020000
RRULE:FREQ=YEARLY;UNTIL=19410928T060000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19420209T020000
RDATE:19420209T020000
TZNAME:EWT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19450814T190000
RDATE:19450814T190000
TZNAME:EPT
TZOFFSETFROM:-0400
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19460428T020000
RRULE:FREQ=YEARLY;UNTIL=19660424T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19460929T020000
RRULE:FREQ=YEARLY;UNTIL=19540926T060000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:STANDARD
DTSTART:19551030T020000
RRULE:FREQ=YEARLY;UNTIL=19661030T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19670430T020000
RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19760425T020000
RRULE:FREQ=YEARLY;UNTIL=19860427T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19870405T020000
RRULE:FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""",
(
(PyCalendarDateTime(1942, 2, 8), False, -5),
(PyCalendarDateTime(1942, 2, 10), False, -4),
(PyCalendarDateTime(2011, 1, 1), False, -5),
(PyCalendarDateTime(2011, 4, 1), False, -4),
(PyCalendarDateTime(2011, 10, 24), False, -4),
(PyCalendarDateTime(2011, 11, 8), False, -5),
(PyCalendarDateTime(2006, 1, 1), False, -5),
(PyCalendarDateTime(2006, 4, 1), False, -5),
(PyCalendarDateTime(2006, 5, 1), False, -4),
(PyCalendarDateTime(2006, 10, 1), False, -4),
(PyCalendarDateTime(2006, 10, 24), False, -4),
(PyCalendarDateTime(2006, 11, 8), False, -5),
(PyCalendarDateTime(2014, 3, 8, 23, 0, 0), False, -5),
(PyCalendarDateTime(2014, 3, 9, 0, 0, 0), False, -5),
(PyCalendarDateTime(2014, 3, 9, 3, 0, 0), False, -4),
(PyCalendarDateTime(2014, 3, 9, 8, 0, 0), False, -4),
(PyCalendarDateTime(2014, 3, 8, 23, 0, 0), True, -5),
(PyCalendarDateTime(2014, 3, 9, 0, 0, 0), True, -5),
(PyCalendarDateTime(2014, 3, 9, 3, 0, 0), True, -5),
(PyCalendarDateTime(2014, 3, 9, 8, 0, 0), True, -4),
(PyCalendarDateTime(2014, 11, 1, 23, 0, 0), False, -4),
(PyCalendarDateTime(2014, 11, 2, 0, 0, 0), False, -4),
(PyCalendarDateTime(2014, 11, 2, 3, 0, 0), False, -5),
(PyCalendarDateTime(2014, 11, 2, 8, 0, 0), False, -5),
(PyCalendarDateTime(2014, 11, 1, 23, 0, 0), True, -4),
(PyCalendarDateTime(2014, 11, 2, 0, 0, 0), True, -4),
(PyCalendarDateTime(2014, 11, 2, 3, 0, 0), True, -4),
(PyCalendarDateTime(2014, 11, 2, 8, 0, 0), True, -5),
)
),
("""BEGIN:VCALENDAR
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Etc/GMT+8
X-LIC-LOCATION:Etc/GMT+8
BEGIN:STANDARD
DTSTART:18000101T000000
RDATE:18000101T000000
TZNAME:GMT+8
TZOFFSETFROM:-0800
TZOFFSETTO:-0800
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
""",
(
(PyCalendarDateTime(1942, 2, 8), False, -8),
(PyCalendarDateTime(1942, 2, 10), False, -8),
(PyCalendarDateTime(2011, 1, 1), False, -8),
(PyCalendarDateTime(2011, 4, 1), False, -8),
)
),
)
for tzdata, offsets in data:
cal = PyCalendar.parseText(tzdata.replace("\n", "\r\n"))
tz = cal.getComponents()[0]
for dt, relative_to_utc, offset in offsets:
tzoffset = tz.getTimezoneOffsetSeconds(dt, relative_to_utc)
self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s with caching" % (tz.getID(), dt,))
for dt, relative_to_utc, offset in reversed(offsets):
tzoffset = tz.getTimezoneOffsetSeconds(dt, relative_to_utc)
self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s with caching, reversed" % (tz.getID(), dt,))
for dt, relative_to_utc, offset in offsets:
tz.mCachedExpandAllMax = None
tzoffset = tz.getTimezoneOffsetSeconds(dt, relative_to_utc)
self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s without caching" % (tz.getID(), dt,))
for dt, relative_to_utc, offset in reversed(offsets):
tz.mCachedExpandAllMax = None
tzoffset = tz.getTimezoneOffsetSeconds(dt, relative_to_utc)
self.assertEqual(tzoffset, offset * 60 * 60, "Failed to match offset for %s at %s without caching, reversed" % (tz.getID(), dt,))
def testConversions(self):
tzdata = """BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
PRODID:-//calendarserver.org//Zonal//EN
BEGIN:VTIMEZONE
TZID:America/New_York
X-LIC-LOCATION:America/New_York
BEGIN:STANDARD
DTSTART:18831118T120358
RDATE:18831118T120358
TZNAME:EST
TZOFFSETFROM:-045602
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19180331T020000
RRULE:FREQ=YEARLY;UNTIL=19190330T070000Z;BYDAY=-1SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19181027T020000
RRULE:FREQ=YEARLY;UNTIL=19191026T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:STANDARD
DTSTART:19200101T000000
RDATE:19200101T000000
RDATE:19420101T000000
RDATE:19460101T000000
RDATE:19670101T000000
TZNAME:EST
TZOFFSETFROM:-0500
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19200328T020000
RDATE:19200328T020000
RDATE:19740106T020000
RDATE:19750223T020000
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19201031T020000
RDATE:19201031T020000
RDATE:19450930T020000
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19210424T020000
RRULE:FREQ=YEARLY;UNTIL=19410427T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19210925T020000
RRULE:FREQ=YEARLY;UNTIL=19410928T060000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19420209T020000
RDATE:19420209T020000
TZNAME:EWT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19450814T190000
RDATE:19450814T190000
TZNAME:EPT
TZOFFSETFROM:-0400
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19460428T020000
RRULE:FREQ=YEARLY;UNTIL=19660424T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19460929T020000
RRULE:FREQ=YEARLY;UNTIL=19540926T060000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:STANDARD
DTSTART:19551030T020000
RRULE:FREQ=YEARLY;UNTIL=19661030T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19670430T020000
RRULE:FREQ=YEARLY;UNTIL=19730429T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;UNTIL=20061029T060000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19760425T020000
RRULE:FREQ=YEARLY;UNTIL=19860427T070000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19870405T020000
RRULE:FREQ=YEARLY;UNTIL=20060402T070000Z;BYDAY=1SU;BYMONTH=4
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE
BEGIN:VTIMEZONE
TZID:America/Los_Angeles
X-LIC-LOCATION:America/Los_Angeles
BEGIN:STANDARD
DTSTART:18831118T120702
RDATE:18831118T120702
TZNAME:PST
TZOFFSETFROM:-075258
TZOFFSETTO:-0800
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19180331T020000
RRULE:FREQ=YEARLY;UNTIL=19190330T100000Z;BYDAY=-1SU;BYMONTH=3
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19181027T020000
RRULE:FREQ=YEARLY;UNTIL=19191026T090000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19420209T020000
RDATE:19420209T020000
TZNAME:PWT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19450814T160000
RDATE:19450814T160000
TZNAME:PPT
TZOFFSETFROM:-0700
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19450930T020000
RDATE:19450930T020000
RDATE:19490101T020000
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
BEGIN:STANDARD
DTSTART:19460101T000000
RDATE:19460101T000000
RDATE:19670101T000000
TZNAME:PST
TZOFFSETFROM:-0800
TZOFFSETTO:-0800
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19480314T020000
RDATE:19480314T020000
RDATE:19740106T020000
RDATE:19750223T020000
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19500430T020000
RRULE:FREQ=YEARLY;UNTIL=19660424T100000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19500924T020000
RRULE:FREQ=YEARLY;UNTIL=19610924T090000Z;BYDAY=-1SU;BYMONTH=9
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
BEGIN:STANDARD
DTSTART:19621028T020000
RRULE:FREQ=YEARLY;UNTIL=19661030T090000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19670430T020000
RRULE:FREQ=YEARLY;UNTIL=19730429T100000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:19671029T020000
RRULE:FREQ=YEARLY;UNTIL=20061029T090000Z;BYDAY=-1SU;BYMONTH=10
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:19760425T020000
RRULE:FREQ=YEARLY;UNTIL=19860427T100000Z;BYDAY=-1SU;BYMONTH=4
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:19870405T020000
RRULE:FREQ=YEARLY;UNTIL=20060402T100000Z;BYDAY=1SU;BYMONTH=4
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:DAYLIGHT
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZNAME:PDT
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
END:DAYLIGHT
BEGIN:STANDARD
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZNAME:PST
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
END:STANDARD
END:VTIMEZONE
END:VCALENDAR
"""
data = (
(
PyCalendarDateTime(2014, 3, 8, 23, 0, 0, PyCalendarTimezone(tzid="America/New_York")),
PyCalendarDateTime(2014, 3, 8, 20, 0, 0, PyCalendarTimezone(tzid="America/Los_Angeles")),
),
(
PyCalendarDateTime(2014, 3, 9, 3, 0, 0, PyCalendarTimezone(utc=True)),
PyCalendarDateTime(2014, 3, 8, 19, 0, 0, PyCalendarTimezone(tzid="America/Los_Angeles")),
),
(
PyCalendarDateTime(2014, 3, 9, 13, 0, 0, PyCalendarTimezone(utc=True)),
PyCalendarDateTime(2014, 3, 9, 6, 0, 0, PyCalendarTimezone(tzid="America/Los_Angeles")),
),
)
PyCalendar.parseText(tzdata.replace("\n", "\r\n"))
for dtfrom, dtto in data:
self.assertEqual(dtfrom, dtto)
newdtfrom = dtfrom.duplicate()
newdtfrom.adjustTimezone(dtto.getTimezone())
self.assertEqual(newdtfrom, dtto)
self.assertEqual(newdtfrom.getHours(), dtto.getHours())
pycalendar-2.0~svn13177/src/pycalendar/tests/__init__.py 0000644 0001750 0001750 00000001202 12101017573 022211 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
pycalendar-2.0~svn13177/src/pycalendar/tests/test_multivalue.py 0000644 0001750 0001750 00000003127 12101017573 023710 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import unittest
from pycalendar.multivalue import PyCalendarMultiValue
from pycalendar.value import PyCalendarValue
class TestMultiValue(unittest.TestCase):
def testParseValue(self):
items = (
("", "", 1),
("Example", "Example", 1),
("Example1,Example2", "Example1,Example2", 2),
)
for item, result, count in items:
req = PyCalendarMultiValue(PyCalendarValue.VALUETYPE_TEXT)
req.parse(item)
test = req.getText()
self.assertEqual(test, result, "Failed to parse and re-generate '%s'" % (item,))
self.assertEqual(len(req.mValues), count, "Failed to parse and re-generate '%s'" % (item,))
def testSetValue(self):
req = PyCalendarMultiValue(PyCalendarValue.VALUETYPE_TEXT)
req.parse("Example1, Example2")
req.setValue(("Example3", "Example4",))
test = req.getText()
self.assertEqual(test, "Example3,Example4")
pycalendar-2.0~svn13177/src/pycalendar/utils.py 0000644 0001750 0001750 00000033377 12127624310 020473 0 ustar rahul rahul ##
# Copyright (c) 2007-2013 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.parser import ParserContext
import cStringIO as StringIO
def readFoldedLine(ins, lines):
# If line2 already has data, transfer that into line1
if lines[1] is not None:
lines[0] = lines[1]
else:
# Fill first line
try:
myline = ins.readline()
if len(myline) == 0:
raise ValueError
if myline[-1] == "\n":
if myline[-2] == "\r":
lines[0] = myline[:-2]
else:
lines[0] = myline[:-1]
elif myline[-1] == "\r":
lines[0] = myline[:-1]
else:
lines[0] = myline
except IndexError:
lines[0] = ""
except:
lines[0] = None
return False
# Now loop looking ahead at the next line to see if it is folded
while True:
# Get next line
try:
myline = ins.readline()
if len(myline) == 0:
raise ValueError
if myline[-1] == "\n":
if myline[-2] == "\r":
lines[1] = myline[:-2]
else:
lines[1] = myline[:-1]
elif myline[-1] == "\r":
lines[1] = myline[:-1]
else:
lines[1] = myline
except IndexError:
lines[1] = ""
except:
lines[1] = None
return True
if not lines[1]:
return True
# Does it start with a space => folded
if lines[1][0].isspace():
# Copy folded line (without space) to current line and cycle
# for more
lines[0] = lines[0] + lines[1][1:]
else:
# Not folded - just exit loop
break
return True
def find_first_of(text, tokens, offset):
for ctr, c in enumerate(text[offset:]):
if c in tokens:
return offset + ctr
return -1
def escapeTextValue(value):
os = StringIO.StringIO()
writeTextValue(os, value)
return os.getvalue()
def writeTextValue(os, value):
try:
start_pos = 0
end_pos = find_first_of(value, "\r\n;\\,", start_pos)
if end_pos != -1:
while True:
# Write current segment
os.write(value[start_pos:end_pos])
# Write escape
os.write("\\")
c = value[end_pos]
if c == '\r':
os.write("r")
elif c == '\n':
os.write("n")
elif c == ';':
os.write(";")
elif c == '\\':
os.write("\\")
elif c == ',':
os.write(",")
# Bump past escapee and look for next segment
start_pos = end_pos + 1
end_pos = find_first_of(value, "\r\n;\\,", start_pos)
if end_pos == -1:
os.write(value[start_pos:])
break
else:
os.write(value)
except:
pass
def decodeTextValue(value):
os = StringIO.StringIO()
start_pos = 0
end_pos = find_first_of(value, "\\", start_pos)
size_pos = len(value)
if end_pos != -1:
while True:
# Write current segment upto but not including the escape char
os.write(value[start_pos:end_pos])
# Bump to escapee char but not past the end
end_pos += 1
if end_pos >= size_pos:
break
# Unescape
c = value[end_pos]
if c == 'r':
os.write('\r')
elif c == 'n':
os.write('\n')
elif c == 'N':
os.write('\n')
elif c == '':
os.write('')
elif c == '\\':
os.write('\\')
elif c == ',':
os.write(',')
elif c == ';':
os.write(';')
elif c == ':':
# ":" escape normally invalid
if ParserContext.INVALID_COLON_ESCAPE_SEQUENCE == ParserContext.PARSER_RAISE:
raise ValueError
elif ParserContext.INVALID_COLON_ESCAPE_SEQUENCE == ParserContext.PARSER_FIX:
os.write(':')
# Other escaped chars normally not allowed
elif ParserContext.INVALID_ESCAPE_SEQUENCES == ParserContext.PARSER_RAISE:
raise ValueError
elif ParserContext.INVALID_ESCAPE_SEQUENCES == ParserContext.PARSER_FIX:
os.write(c)
# Bump past escapee and look for next segment (not past the end)
start_pos = end_pos + 1
if start_pos >= size_pos:
break
end_pos = find_first_of(value, "\\", start_pos)
if end_pos == -1:
os.write(value[start_pos:])
break
else:
os.write(value)
return os.getvalue()
def encodeParameterValue(value):
"""
RFC6868 parameter encoding.
"""
encoded = []
last = ''
for c in value:
if c == '\r':
encoded.append('^')
encoded.append('n')
elif c == '\n':
if last != '\r':
encoded.append('^')
encoded.append('n')
elif c == '"':
encoded.append('^')
encoded.append('\'')
elif c == '^':
encoded.append('^')
encoded.append('^')
else:
encoded.append(c)
last = c
return "".join(encoded)
def decodeParameterValue(value):
"""
RFC6868 parameter decoding.
"""
if value is None:
return None
decoded = []
last = ''
for c in value:
if last == '^':
if c == 'n':
decoded.append('\n')
elif c == '\'':
decoded.append('"')
elif c == '^':
decoded.append('^')
c = ''
else:
decoded.append('^')
decoded.append(c)
elif c != '^':
decoded.append(c)
last = c
if last == '^':
decoded.append('^')
return "".join(decoded)
# vCard text list parsing/generation
def parseTextList(data, sep=';', always_list=False):
"""
Each element of the list has to be separately un-escaped
"""
results = []
item = []
pre_s = ''
for s in data:
if s == sep and pre_s != '\\':
results.append(decodeTextValue("".join(item)))
item = []
else:
item.append(s)
pre_s = s
results.append(decodeTextValue("".join(item)))
return tuple(results) if len(results) > 1 or always_list else (results[0] if len(results) else "")
def generateTextList(os, data, sep=';'):
"""
Each element of the list must be separately escaped
"""
try:
if isinstance(data, basestring):
data = (data,)
results = [escapeTextValue(value) for value in data]
os.write(sep.join(results))
except:
pass
# vCard double-nested list parsing/generation
def parseDoubleNestedList(data, maxsize):
results = []
items = [""]
pre_s = ''
for s in data:
if s == ';' and pre_s != '\\':
if len(items) > 1:
results.append(tuple([decodeTextValue(item) for item in items]))
elif len(items) == 1:
results.append(decodeTextValue(items[0]))
else:
results.append("")
items = [""]
elif s == ',' and pre_s != '\\':
items.append("")
else:
items[-1] += s
pre_s = s
if len(items) > 1:
results.append(tuple([decodeTextValue(item) for item in items]))
elif len(items) == 1:
results.append(decodeTextValue(items[0]))
else:
results.append("")
for _ignore in range(maxsize - len(results)):
results.append("")
if len(results) > maxsize:
if ParserContext.INVALID_ADR_N_VALUES == ParserContext.PARSER_FIX:
results = results[:maxsize]
elif ParserContext.INVALID_ADR_N_VALUES == ParserContext.PARSER_RAISE:
raise ValueError
return tuple(results)
def generateDoubleNestedList(os, data):
try:
def _writeElement(item):
if isinstance(item, basestring):
writeTextValue(os, item)
else:
if item:
writeTextValue(os, item[0])
for bit in item[1:]:
os.write(",")
writeTextValue(os, bit)
for item in data[:-1]:
_writeElement(item)
os.write(";")
_writeElement(data[-1])
except:
pass
# Date/time calcs
days_in_month = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
days_in_month_leap = (0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
def daysInMonth(month, year):
# NB month is 1..12 so use dummy value at start of array to avoid index
# adjustment
if isLeapYear(year):
return days_in_month_leap[month]
else:
return days_in_month[month]
days_upto_month = (0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)
days_upto_month_leap = (0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)
def daysUptoMonth(month, year):
# NB month is 1..12 so use dummy value at start of array to avoid index
# adjustment
if isLeapYear(year):
return days_upto_month_leap[month]
else:
return days_upto_month[month]
cachedLeapYears = {}
def isLeapYear(year):
try:
return cachedLeapYears[year]
except KeyError:
if year <= 1752:
result = (year % 4 == 0)
else:
result = ((year % 4 == 0) and (year % 100 != 0)) or (year % 400 == 0)
cachedLeapYears[year] = result
return result
cachedLeapDaysSince1970 = {}
def leapDaysSince1970(year_offset):
try:
return cachedLeapDaysSince1970[year_offset]
except KeyError:
if year_offset > 2:
result = (year_offset + 1) / 4
elif year_offset < -1:
# Python will round down negative numbers (i.e. -5/4 = -2, but we want -1), so
# what is (year_offset - 2) in C code is actually (year_offset - 2 + 3) in Python.
result = (year_offset + 1) / 4
else:
result = 0
cachedLeapDaysSince1970[year_offset] = result
return result
# Packed date
def packDate(year, month, day):
return (year << 16) | (month << 8) | (day + 128)
def unpackDate(data, unpacked):
unpacked[0] = (data & 0xFFFF0000) >> 16
unpacked[1] = (data & 0x0000FF00) >> 8
unpacked[2] = (data & 0xFF) - 128
def unpackDateYear(data):
return (data & 0xFFFF0000) >> 16
def unpackDateMonth(data):
return (data & 0x0000FF00) >> 8
def unpackDateDay(data):
return (data & 0xFF) - 128
# Display elements
def getMonthTable(month, year, weekstart, table, today_index):
from pycalendar.datetime import PyCalendarDateTime
# Get today
today = PyCalendarDateTime.getToday(None)
today_index = [-1, -1]
# Start with empty table
table = []
# Determine first weekday in month
temp = PyCalendarDateTime(year, month, 1, 0)
row = -1
initial_col = temp.getDayOfWeek() - weekstart
if initial_col < 0:
initial_col += 7
col = initial_col
# Counters
max_day = daysInMonth(month, year)
# Fill up each row
for day in range(1, max_day + 1):
# Insert new row if we are at the start of a row
if (col == 0) or (day == 1):
table.extend([0] * 7)
row += 1
# Set the table item to the current day
table[row][col] = packDate(temp.getYear(), temp.getMonth(), day)
# Check on today
if (temp.getYear() == today.getYear()) and (temp.getMonth() == today.getMonth()) and (day == today.getDay()):
today_index = [row, col]
# Bump column (modulo 7)
col += 1
if (col > 6):
col = 0
# Add next month to remainder
temp.offsetMonth(1)
if col != 0:
day = 1
while col < 7:
table[row][col] = packDate(temp.getYear(), temp.getMonth(), -day)
# Check on today
if (temp.getYear() == today.getYear()) and (temp.getMonth() == today.getMonth()) and (day == today.getDay()):
today_index = [row, col]
day += 1
col += 1
# Add previous month to start
temp.offsetMonth(-2)
if (initial_col != 0):
day = daysInMonth(temp.getMonth(), temp.getYear())
back_col = initial_col - 1
while(back_col >= 0):
table[row][back_col] = packDate(temp.getYear(), temp.getMonth(), -day)
# Check on today
if (temp.getYear() == today.getYear()) and (temp.getMonth() == today.getMonth()) and (day == today.getDay()):
today_index = [0, back_col]
back_col -= 1
day -= 1
return table, today_index
def set_difference(v1, v2):
if len(v1) == 0 or len(v2) == 0:
return v1
s1 = set(v1)
s2 = set(v2)
s3 = s1.difference(s2)
return list(s3)
pycalendar-2.0~svn13177/src/pycalendar/locale.py 0000644 0001750 0001750 00000003520 12101017573 020554 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
LONG = 0
SHORT = 1
ABBREVIATED = 2
cLongDays = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
cShortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
cAbbrevDays = ["S", "M", "T", "W", "T", "F", "S"]
cLongMonths = ["", "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"]
cShortMonths = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
cAbbrevMonths = ["", "J", "F", "M", "A", "M", "J",
"J", "A", "S", "O", "N", "D"]
s24HourTime = False
sDDMMDate = False
# 0..6 - Sunday - Saturday
def getDay(day, strl):
return {LONG: cLongDays[day], SHORT: cShortDays[day], ABBREVIATED: cAbbrevDays[day]}[strl]
# 1..12 - January - December
def getMonth(month, strl):
return {LONG: cLongMonths[month], SHORT: cShortMonths[month], ABBREVIATED: cAbbrevMonths[month]}[strl]
# Use 24 hour time display
def use24HourTime():
# TODO: get 24 hour option from system prefs
return s24HourTime
# Use DD/MM date display
def useDDMMDate():
# TODO: get 24 hour option from system prefs
return sDDMMDate
pycalendar-2.0~svn13177/src/pycalendar/periodvalue.py 0000644 0001750 0001750 00000003040 12101017573 021631 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import xmldefs
from pycalendar.period import PyCalendarPeriod
from pycalendar.value import PyCalendarValue
class PyCalendarPeriodValue(PyCalendarValue):
def __init__(self, value=None):
self.mValue = value if value is not None else PyCalendarPeriod()
def duplicate(self):
return PyCalendarPeriodValue(self.mValue.duplicate())
def getType(self):
return PyCalendarValue.VALUETYPE_PERIOD
def parse(self, data):
self.mValue.parse(data)
def generate(self, os):
self.mValue.generate(os)
def writeXML(self, node, namespace):
value = self.getXMLNode(node, namespace)
value.text = self.mValue.writeXML()
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_PERIOD, PyCalendarPeriodValue, xmldefs.value_period)
pycalendar-2.0~svn13177/src/pycalendar/exceptions.py 0000644 0001750 0001750 00000002122 12101017573 021473 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
class PyCalendarError(Exception):
def __init__(self, reason, data=""):
self.mReason = reason
self.mData = data
class PyCalendarInvalidData(PyCalendarError):
pass
class PyCalendarInvalidProperty(PyCalendarError):
pass
class PyCalendarValidationError(PyCalendarError):
pass
class PyCalendarNoTimezoneInDatabase(Exception):
def __init__(self, dbpath, tzid):
self.mTZDBpath = dbpath
self.mTZID = tzid
pycalendar-2.0~svn13177/src/pycalendar/vtodo.py 0000644 0001750 0001750 00000032205 12101017573 020452 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar import itipdefinitions
from pycalendar.componentrecur import PyCalendarComponentRecur
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
from pycalendar.property import PyCalendarProperty
import cStringIO as StringIO
class PyCalendarVToDo(PyCalendarComponentRecur):
OVERDUE = 0
DUE_NOW = 1
DUE_LATER = 2
DONE = 3
CANCELLED = 4
@staticmethod
def sort_for_display(e1, e2):
s1 = e1.getMaster()
s2 = e2.getMaster()
# Check status first (convert None -> Needs action for tests)
status1 = s1.self.mStatus
status2 = s2.self.mStatus
if status1 == definitions.eStatus_VToDo_None:
status1 = definitions.eStatus_VToDo_NeedsAction
if status2 == definitions.eStatus_VToDo_None:
status2 = definitions.eStatus_VToDo_NeedsAction
if status1 != status2:
# More important ones at the top
return status1 < status2
# At this point the status of each is the same
# If status is cancelled sort by start time
if s1.self.mStatus == definitions.eStatus_VToDo_Cancelled:
# Older ones at the bottom
return s1.mStart > s2.mStart
# If status is completed sort by completion time
if s1.self.mStatus == definitions.eStatus_VToDo_Completed:
# Older ones at the bottom
return s1.self.mCompleted > s2.self.mCompleted
# Check due date exists
if s1.mHasEnd != s2.mHasEnd:
now = PyCalendarDateTime()
now.setToday()
# Ones with due dates after today below ones without due dates
if s1.hasEnd():
return s1.mEnd <= now
elif s2.hasEnd():
return now < s2.mEnd
# Check due dates if present
if s1.mHasEnd:
if s1.mEnd != s2.mEnd:
# Soonest dues dates above later ones
return s1.mEnd < s2.mEnd
# Check priority next
if s1.self.mPriority != s2.self.mPriority:
# Higher priority above lower ones
return s1.self.mPriority < s2.self.mPriority
# Just use start time - older ones at the top
return s1.mStart < s2.mStart
propertyCardinality_1 = (
definitions.cICalProperty_DTSTAMP,
definitions.cICalProperty_UID,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_CLASS,
definitions.cICalProperty_COMPLETED,
definitions.cICalProperty_CREATED,
definitions.cICalProperty_DESCRIPTION,
definitions.cICalProperty_DTSTART,
definitions.cICalProperty_GEO,
definitions.cICalProperty_LAST_MODIFIED,
definitions.cICalProperty_LOCATION,
definitions.cICalProperty_ORGANIZER,
definitions.cICalProperty_PERCENT_COMPLETE,
definitions.cICalProperty_PRIORITY,
definitions.cICalProperty_RECURRENCE_ID,
definitions.cICalProperty_SEQUENCE,
# definitions.cICalProperty_STATUS, # Special fix done for multiple STATUS
definitions.cICalProperty_SUMMARY,
definitions.cICalProperty_URL,
definitions.cICalProperty_RRULE,
definitions.cICalProperty_DUE,
definitions.cICalProperty_DURATION,
)
propertyValueChecks = ICALENDAR_VALUE_CHECKS
def __init__(self, parent=None):
super(PyCalendarVToDo, self).__init__(parent=parent)
self.mPriority = 0
self.mStatus = definitions.eStatus_VToDo_None
self.mPercentComplete = 0
self.mCompleted = PyCalendarDateTime()
self.mHasCompleted = False
def duplicate(self, parent=None):
other = super(PyCalendarVToDo, self).duplicate(parent=parent)
other.mPriority = self.mPriority
other.mStatus = self.mStatus
other.mPercentComplete = self.mPercentComplete
other.mCompleted = self.mCompleted.duplicate()
other.mHasCompleted = self.mHasCompleted
return other
def getType(self):
return definitions.cICalComponent_VTODO
def getMimeComponentName(self):
return itipdefinitions.cICalMIMEComponent_VTODO
def addComponent(self, comp):
# We can embed the alarm components only
if comp.getType() == definitions.cICalComponent_VALARM:
super(PyCalendarVToDo, self).addComponent(comp)
else:
raise ValueError
def getStatus(self):
return self.mStatus
def setStatus(self, status):
self.mStatus = status
def getStatusText(self):
sout = StringIO()
if self.mStatus in (definitions.eStatus_VToDo_NeedsAction, definitions.eStatus_VToDo_InProcess):
if self.hasEnd():
# Check due date
today = PyCalendarDateTime()
today.setToday()
if self.getEnd() > today:
sout.append("Due: ")
whendue = self.getEnd() - today
if (whendue.getDays() > 0) and (whendue.getDays() <= 7):
sout.write(whendue.getDays())
sout.write(" days")
else:
sout.write(self.getEnd().getLocaleDate(PyCalendarDateTime.NUMERICDATE))
elif self.getEnd() == today:
sout.write("Due today")
else:
sout.write("Overdue: ")
overdue = today - self.getEnd()
if overdue.getWeeks() != 0:
sout.write(overdue.getWeeks())
sout.write(" weeks")
else:
sout.write(overdue.getDays() + 1)
sout.write(" days")
else:
sout.write("Not Completed")
elif self.mStatus == definitions.eStatus_VToDo_Completed:
if self.hasCompleted():
sout.write("Completed: ")
sout.write(self.getCompleted().getLocaleDate(PyCalendarDateTime.NUMERICDATE))
else:
sout.write("Completed")
elif definitions.eStatus_VToDo_Cancelled:
sout.write("Cancelled")
return sout.toString()
def getCompletionState(self):
if self.mStatus in (definitions.eStatus_VToDo_NeedsAction, definitions.eStatus_VToDo_InProcess):
if self.hasEnd():
# Check due date
today = PyCalendarDateTime()
today.setToday()
if self.getEnd() > today:
return PyCalendarVToDo.DUE_LATER
elif self.getEnd() == today:
return PyCalendarVToDo.DUE_NOW
else:
return PyCalendarVToDo.OVERDUE
else:
return PyCalendarVToDo.DUE_NOW
elif self.mStatus == definitions.eStatus_VToDo_Completed:
return PyCalendarVToDo.DONE
elif self.mStatus == definitions.eStatus_VToDo_Cancelled:
return PyCalendarVToDo.CANCELLED
def getPriority(self):
return self.mPriority
def setPriority(self, priority):
self.mPriority = priority
def getCompleted(self):
return self.mCompleted
def hasCompleted(self):
return self.mHasCompleted
def finalise(self):
# Do inherited
super(PyCalendarVToDo, self).finalise()
# Get DUE
temp = self.loadValueDateTime(definitions.cICalProperty_DUE)
if temp is None:
# Try DURATION instead
temp = self.loadValueDuration(definitions.cICalProperty_DURATION)
if temp is not None:
self.mEnd = self.mStart + temp
self.mHasEnd = True
else:
self.mHasEnd = False
else:
self.mHasEnd = True
self.mEnd = temp
# Get PRIORITY
self.mPriority = self.loadValueInteger(definitions.cICalProperty_PRIORITY)
# Get STATUS
temp = self.loadValueString(definitions.cICalProperty_STATUS)
if temp is not None:
if temp == definitions.cICalProperty_STATUS_NEEDS_ACTION:
self.mStatus = definitions.eStatus_VToDo_NeedsAction
elif temp == definitions.cICalProperty_STATUS_COMPLETED:
self.mStatus = definitions.eStatus_VToDo_Completed
elif temp == definitions.cICalProperty_STATUS_IN_PROCESS:
self.mStatus = definitions.eStatus_VToDo_InProcess
elif temp == definitions.cICalProperty_STATUS_CANCELLED:
self.mStatus = definitions.eStatus_VToDo_Cancelled
# Get PERCENT-COMPLETE
self.mPercentComplete = self.loadValueInteger(definitions.cICalProperty_PERCENT_COMPLETE)
# Get COMPLETED
temp = self.loadValueDateTime(definitions.cICalProperty_COMPLETED)
self.mHasCompleted = temp is not None
if self.mHasCompleted:
self.mCompleted = temp
def validate(self, doFix=False):
"""
Validate the data in this component and optionally fix any problems, else raise. If
loggedProblems is not None it must be a C{list} and problem descriptions are appended
to that.
"""
fixed, unfixed = super(PyCalendarVToDo, self).validate(doFix)
# Extra constraint: only one of DUE or DURATION
if self.hasProperty(definitions.cICalProperty_DUE) and self.hasProperty(definitions.cICalProperty_DURATION):
# Fix by removing the DURATION
logProblem = "[%s] Properties must not both be present: %s, %s" % (
self.getType(),
definitions.cICalProperty_DUE,
definitions.cICalProperty_DURATION,
)
if doFix:
self.removeProperties(definitions.cICalProperty_DURATION)
fixed.append(logProblem)
else:
unfixed.append(logProblem)
# Extra constraint: DTSTART must be present if DURATION is present
if self.hasProperty(definitions.cICalProperty_DURATION) and not self.hasProperty(definitions.cICalProperty_DTSTART):
# Cannot fix this one
logProblem = "[%s] Property must be present: %s with %s" % (
self.getType(),
definitions.cICalProperty_DTSTART,
definitions.cICalProperty_DURATION,
)
unfixed.append(logProblem)
return fixed, unfixed
# Editing
def editStatus(self, status):
# Only if it is different
if self.mStatus != status:
# Updated cached values
self.mStatus = status
# Remove existing STATUS & COMPLETED items
self.removeProperties(definitions.cICalProperty_STATUS)
self.removeProperties(definitions.cICalProperty_COMPLETED)
self.mHasCompleted = False
# Now create properties
value = None
if status == definitions.eStatus_VToDo_NeedsAction:
value = definitions.cICalProperty_STATUS_NEEDS_ACTION
if status == definitions.eStatus_VToDo_Completed:
value = definitions.cICalProperty_STATUS_COMPLETED
# Add the completed item
self.mCompleted.setNowUTC()
self.mHasCompleted = True
prop = PyCalendarProperty(definitions.cICalProperty_STATUS_COMPLETED, self.mCompleted)
self.addProperty(prop)
elif status == definitions.eStatus_VToDo_InProcess:
value = definitions.cICalProperty_STATUS_IN_PROCESS
elif status == definitions.eStatus_VToDo_Cancelled:
value = definitions.cICalProperty_STATUS_CANCELLED
prop = PyCalendarProperty(definitions.cICalProperty_STATUS, value)
self.addProperty(prop)
def editCompleted(self, completed):
# Remove existing COMPLETED item
self.removeProperties(definitions.cICalProperty_COMPLETED)
self.mHasCompleted = False
# Always UTC
self.mCompleted = completed.duplicate()
self.mCompleted.adjustToUTC()
self.mHasCompleted = True
prop = PyCalendarProperty(definitions.cICalProperty_STATUS_COMPLETED, self.mCompleted)
self.addProperty(prop)
def sortedPropertyKeyOrder(self):
return (
definitions.cICalProperty_UID,
definitions.cICalProperty_RECURRENCE_ID,
definitions.cICalProperty_DTSTART,
definitions.cICalProperty_DURATION,
definitions.cICalProperty_DUE,
definitions.cICalProperty_COMPLETED,
)
pycalendar-2.0~svn13177/src/pycalendar/recurrencevalue.py 0000644 0001750 0001750 00000003006 12101017573 022506 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import xmldefs
from pycalendar.recurrence import PyCalendarRecurrence
from pycalendar.value import PyCalendarValue
class PyCalendarRecurrenceValue(PyCalendarValue):
def __init__(self, value=None):
self.mValue = value if value is not None else PyCalendarRecurrence()
def duplicate(self):
return PyCalendarRecurrenceValue(self.mValue.duplicate())
def getType(self):
return PyCalendarValue.VALUETYPE_RECUR
def parse(self, data):
self.mValue.parse(data)
def generate(self, os):
self.mValue.generate(os)
def writeXML(self, node, namespace):
self.mValue.writeXML(node, namespace)
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_RECUR, PyCalendarRecurrenceValue, xmldefs.value_recur)
pycalendar-2.0~svn13177/src/pycalendar/outputfilter.py 0000644 0001750 0001750 00000005205 12101017573 022065 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
class PyCalendarOutputFilter(object):
def __init__(self, type):
self.mType = type
self.mAllSubComponents = False
self.mSubComponents = None
self.mAllProperties = False
self.mProperties = None
def getType(self):
return self.mType
# Test to see if component type can be written out
def testComponent(self, oftype):
return self.mType == oftype
def isAllSubComponents(self):
return self.mAllSubComponents
def setAllSubComponents(self):
self.mAllSubComponents = True
self.mSubComponents = None
def addSubComponent(self, comp):
if self.mSubComponents == None:
self.mSubComponents = {}
self.mSubComponents[comp.getType()] = comp
# Test to see if sub-component type can be written out
def testSubComponent(self, oftype):
return self.mAllSubComponents or (self.mSubComponents is not None) \
and oftype in self.mSubComponents
def hasSubComponentFilters(self):
return self.mSubComponents is not None
def getSubComponentFilter(self, type):
if self.mSubComponents is not None:
return self.mSubComponents.get(type, None)
else:
return None
def isAllProperties(self):
return self.mAllProperties
def setAllProperties(self):
self.mAllProperties = True
self.mProperties = None
def addProperty(self, name, no_value):
if self.mProperties is None:
self.mProperties = {}
self.mProperties[name] = no_value
def hasPropertyFilters(self):
return self.mProperties is not None
# Test to see if property can be written out and also return whether
# the property value is used
def testPropertyValue(self, name):
if self.mAllProperties:
return True, False
if self.mProperties is None:
return False, False
result = self.mProperties.get(name, None)
return result is not None, result
pycalendar-2.0~svn13177/src/pycalendar/nvalue.py 0000644 0001750 0001750 00000002442 12101017573 020611 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# vCard ADR value
from pycalendar.n import N
from pycalendar.value import PyCalendarValue
class NValue(PyCalendarValue):
def __init__(self, value=None):
self.mValue = value if value is not None else N()
def duplicate(self):
return NValue(self.mValue.duplicate())
def getType(self):
return PyCalendarValue.VALUETYPE_N
def parse(self, data):
self.mValue.parse(data)
def generate(self, os):
self.mValue.generate(os)
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_N, NValue, None)
pycalendar-2.0~svn13177/src/pycalendar/recurrenceset.py 0000644 0001750 0001750 00000021205 12101017573 022166 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.utils import set_difference
class PyCalendarRecurrenceSet(object):
def __init__(self):
self.mRrules = []
self.mExrules = []
self.mRdates = []
self.mExdates = []
self.mRperiods = []
self.mExperiods = []
def duplicate(self):
other = PyCalendarRecurrenceSet()
other.mRrules = [i.duplicate() for i in self.mRrules]
other.mExrules = [i.duplicate() for i in self.mExrules]
other.mRdates = [i.duplicate() for i in self.mRdates]
other.mExdates = [i.duplicate() for i in self.mExdates]
other.mRperiods = [i.duplicate() for i in self.mRperiods]
other.mExperiods = [i.duplicate() for i in self.mExperiods]
return other
def hasRecurrence(self):
return ((len(self.mRrules) != 0) or (len(self.mRdates) != 0) or (len(self.mRperiods) != 0)
or (len(self.mExrules) != 0) or (len(self.mExdates) != 0)
or (len(self.mExperiods) != 0))
def equals(self, comp):
# Look at RRULEs
if not self.equalsRules(self.mRrules, comp.self.mRrules):
return False
# Look at EXRULEs
if not self.equalsRules(self.mExrules, comp.self.mExrules):
return False
# Look at RDATEs
if not self.equalsDates(self.mRdates, comp.self.mRdates):
return False
if not self.equalsPeriods(self.mRperiods, comp.self.mRperiods):
return False
# Look at EXDATEs
if not self.equalsDates(self.mExdates, comp.self.mExdates):
return False
if not self.equalsPeriods(self.mExperiods, comp.self.mExperiods):
return False
# If we get here they match
return True
def equalsRules(self, rules1, rules2):
# Check sizes first
if len(rules1) != len(rules2):
return False
elif len(rules1) == 0:
return True
# Do sledge hammer O(n^2) approach as its not easy to sort these things
# for a smarter test.
# In most cases there will only be one rule anyway, so this should not
# be too painful.
temp2 = rules2[:]
for r1 in rules1:
found = False
for r2 in temp2:
if r1.equals(r2):
# Remove the one found so it is not tested again
temp2.remove(r2)
found = True
break
if not found:
return False
return True
def equalsDates(self, dates1, dates2):
# Check sizes first
if len(dates1) != len(dates2):
return False
elif len(dates1) == 0:
return True
# Copy each and sort for comparison
dt1 = dates1[:]
dt2 = dates2[:]
dt1.sort(key=lambda x: x.getPosixTime())
dt2.sort(key=lambda x: x.getPosixTime())
return dt1.equal(dt2)
def equalsPeriods(self, periods1, periods2):
# Check sizes first
if len(periods1) != len(periods2):
return False
elif len(periods1) == 0:
return True
# Copy each and sort for comparison
p1 = periods1[:]
p2 = periods2[:]
p1.sort()
p2.sort()
return p1.equal(p2)
def addRule(self, rule):
self.mRrules.append(rule)
def subtractRule(self, rule):
self.mExrules.append(rule)
def addDT(self, dt):
self.mRdates.append(dt)
def subtractDT(self, dt):
self.mExdates.append(dt)
def addPeriod(self, p):
self.mRperiods.append(p)
def subtractPeriod(self, p):
self.mExperiods.append(p)
def getRules(self):
return self.mRrules
def getExrules(self):
return self.mExrules
def getDates(self):
return self.mRdates
def getExdates(self):
return self.mExdates
def getPeriods(self):
return self.mRperiods
def getExperiods(self):
return self.mExperiods
def expand(self, start, range, items, float_offset=0):
# Need to return whether the limit was applied or not
limited = False
# Now create list of items to include
include = []
# Always include the initial DTSTART if within the range
if range.isDateWithinPeriod(start):
include.append(start)
else:
limited = True
# RRULES
for iter in self.mRrules:
if iter.expand(start, range, include, float_offset=float_offset):
limited = True
# RDATES
for iter in self.mRdates:
if range.isDateWithinPeriod(iter):
include.append(iter)
else:
limited = True
for iter in self.mRperiods:
if range.isPeriodOverlap(iter):
include.append(iter.getStart())
else:
limited = True
# Make sure the list is unique
include = [x for x in set(include)]
include.sort(key=lambda x: x.getPosixTime())
# Now create list of items to exclude
exclude = []
# EXRULES
for iter in self.mExrules:
iter.expand(start, range, exclude, float_offset=float_offset)
# EXDATES
for iter in self.mExdates:
if range.isDateWithinPeriod(iter):
exclude.append(iter)
for iter in self.mExperiods:
if range.isPeriodOverlap(iter):
exclude.append(iter.getStart())
# Make sure the list is unique
exclude = [x for x in set(exclude)]
exclude.sort(key=lambda x: x.getPosixTime())
# Add difference between to the two sets (include - exclude) to the
# results
items.extend(set_difference(include, exclude))
return limited
def changed(self):
# RRULES
for iter in self.mRrules:
iter.clear()
# EXRULES
for iter in self.mExrules:
iter.clear()
def excludeFutureRecurrence(self, exclude):
# Adjust RRULES to end before start
for iter in self.mRrules:
iter.excludeFutureRecurrence(exclude)
# Remove RDATES on or after start
self.mRdates.removeOnOrAfter(exclude)
for iter in self.mRperiods:
if iter > exclude:
self.mRperiods.remove(iter)
# UI operations
def isSimpleUI(self):
# Right now the Event dialog only handles a single RRULE (but we allow
# any number of EXDATES as deleted
# instances will appear as EXDATES)
if ((len(self.mRrules) > 1) or (len(self.mExrules) > 0)
or (len(self.mRdates) > 0) or (len(self.mRperiods) > 0)):
return False
# Also, check the rule iteself
elif len(self.mRrules) == 1:
return self.mRrules.firstElement().isSimpleRule()
else:
return True
def isAdvancedUI(self):
# Right now the Event dialog only handles a single RRULE
if ((len(self.mRrules) > 1) or (len(self.mExrules) > 0)
or (len(self.mRdates) > 0) or (len(self.mRperiods) > 0)):
return False
# Also, check the rule iteself
elif len(self.mRrules) == 1:
return self.mRrules.firstElement().isAdvancedRule()
else:
return True
def getUIRecurrence(self):
if len(self.mRrules) == 1:
return self.mRrules[0]
else:
return None
def getUIDescription(self):
# Check for anything
if not self.hasRecurrence():
return "No Recurrence"
# Look for a single RRULE and return its descriptor
if ((len(self.mRrules) == 1) and (len(self.mExrules) == 0) and (len(self.mRdates) == 0)
and (len(self.mExdates) == 0) and (len(self.mRperiods) == 0)
and (len(self.mExperiods) == 0)):
return self.mRrules.firstElement().getUIDescription()
# Indicate some form of complex recurrence
return "Multiple recurrence rules, dates or exclusions"
pycalendar-2.0~svn13177/src/pycalendar/component.py 0000644 0001750 0001750 00000012374 12101017573 021326 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar import stringutils
from pycalendar.componentbase import PyCalendarComponentBase
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.property import PyCalendarProperty
import os
import time
import uuid
class PyCalendarComponent(PyCalendarComponentBase):
uid_ctr = 1
def __init__(self, parent=None):
super(PyCalendarComponent, self).__init__(parent)
self.mUID = ""
self.mSeq = 0
self.mOriginalSeq = 0
self.mChanged = False
def duplicate(self, parent=None, **args):
other = super(PyCalendarComponent, self).duplicate(parent=parent, **args)
other.mUID = self.mUID
other.mSeq = self.mSeq
other.mOriginalSeq = self.mOriginalSeq
other.mChanged = self.mChanged
return other
def __repr__(self):
return "%s: UID: %s" % (self.getType(), self.getMapKey(),)
def getMimeComponentName(self):
raise NotImplementedError
def getMapKey(self):
if hasattr(self, "mMapKey"):
return self.mMapKey
elif self.mUID:
return self.mUID
else:
self.mMapKey = str(uuid.uuid4())
return self.mMapKey
def getSortKey(self):
return self.getMapKey()
def getMasterKey(self):
return self.mUID
def getUID(self):
return self.mUID
def setUID(self, uid):
if uid:
self.mUID = uid
else:
# Get left-side of UID (first 24 chars of MD5 digest of time, pid
# and ctr)
lhs_txt = ""
lhs_txt += str(time.time())
lhs_txt += "."
lhs_txt += str(os.getpid())
lhs_txt += "."
lhs_txt += str(PyCalendarComponent.uid_ctr)
PyCalendarComponent.uid_ctr += 1
lhs = stringutils.md5digest(lhs_txt)
# Get right side (domain) of message-id
rhs = None
# Use app name
from pycalendar.calendar import PyCalendar
domain = PyCalendar.sDomain
domain += str(PyCalendarComponent.uid_ctr)
# Use first 24 chars of MD5 digest of the domain as the
# right-side of message-id
rhs = stringutils.md5digest(domain)
# Generate the UID string
new_uid = lhs
new_uid += "@"
new_uid += rhs
self.mUID = new_uid
self.removeProperties(definitions.cICalProperty_UID)
prop = PyCalendarProperty(definitions.cICalProperty_UID, self.mUID)
self.addProperty(prop)
def getSeq(self):
return self.mSeq
def setSeq(self, seq):
self.mSeq = seq
self.removeProperties(definitions.cICalProperty_SEQUENCE)
prop = PyCalendarProperty(definitions.cICalProperty_SEQUENCE, self.mSeq)
self.addProperty(prop)
def getOriginalSeq(self):
return self.mOriginalSeq
def getChanged(self):
return self.mChanged
def setChanged(self, changed):
self.mChanged = changed
def initDTSTAMP(self):
self.removeProperties(definitions.cICalProperty_DTSTAMP)
prop = PyCalendarProperty(definitions.cICalProperty_DTSTAMP,
PyCalendarDateTime.getNowUTC())
self.addProperty(prop)
def updateLastModified(self):
self.removeProperties(definitions.cICalProperty_LAST_MODIFIED)
prop = PyCalendarProperty(definitions.cICalProperty_LAST_MODIFIED,
PyCalendarDateTime.getNowUTC())
self.addProperty(prop)
def finalise(self):
# Get UID
temps = self.loadValueString(definitions.cICalProperty_UID)
if temps is not None:
self.mUID = temps
# Get SEQ
temp = self.loadValueInteger(definitions.cICalProperty_SEQUENCE)
if temp is not None:
self.mSeq = temp
# Cache the original sequence when the component is read in.
# This will be used to synchronise changes between two instances of the
# same calendar
self.mOriginalSeq = self.mSeq
def canGenerateInstance(self):
return True
def getTimezones(self, tzids):
# Look for all date-time properties
for props in self.mProperties.itervalues():
for prop in props:
# Try to get a date-time value from the property
dtv = prop.getDateTimeValue()
if dtv is not None:
# Add timezone id if appropriate
if dtv.getValue().getTimezoneID():
tzids.add(dtv.getValue().getTimezoneID())
pycalendar-2.0~svn13177/src/pycalendar/validator.py 0000755 0001750 0001750 00000004466 12120354250 021314 0 ustar rahul rahul #!/usr/bin/env python
##
# Copyright (c) 2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar.calendar import PyCalendar
from pycalendar.exceptions import PyCalendarError
from pycalendar.parser import ParserContext
from pycalendar.vcard.card import Card
import os
import sys
def validate(fname):
"""
Check whether the contents of the specified file is valid iCalendar or vCard data.
"""
data = open(fname).read()
ParserContext.allRaise()
if data.find("BEGIN:VCALENDAR") != -1:
try:
cal = PyCalendar.parseText(data)
except PyCalendarError, e:
print "Failed to parse iCalendar: %r" % (e,)
sys.exit(1)
elif data.find("BEGIN:VCARD") != -1:
try:
cal = Card.parseText(data)
except PyCalendarError, e:
print "Failed to parse vCard: %r" % (e,)
sys.exit(1)
else:
print "Failed to find valid iCalendar or vCard data"
sys.exit(1)
_ignore_fixed, unfixed = cal.validate(doFix=False, doRaise=False)
if unfixed:
print "List of problems: %s" % (unfixed,)
else:
print "No problems"
# Control character check - only HTAB, CR, LF allowed for characters in the range 0x00-0x1F
s = str(data)
if len(s.translate(None, "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F")) != len(s):
for ctr, i in enumerate(data):
if i in "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0B\x0C\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F":
print "Control character %d at position %d" % (ord(i), ctr,)
if __name__ == '__main__':
fname = os.path.expanduser(sys.argv[1])
validate(fname)
pycalendar-2.0~svn13177/src/pycalendar/itipdefinitions.py 0000644 0001750 0001750 00000002641 12101017573 022521 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# 2446 Section 3
cICalMethod_PUBLISH = "PUBLISH"
cICalMethod_REQUEST = "REQUEST"
cICalMethod_REFRESH = "REFRESH"
cICalMethod_CANCEL = "CANCEL"
cICalMethod_ADD = "ADD"
cICalMethod_REPLY = "REPLY"
cICalMethod_COUNTER = "COUNTER"
cICalMethod_DECLINECOUNTER = "DECLINECOUNTER"
# 2447 Section 2.4
cICalMIMEMethod_PUBLISH = "publish"
cICalMIMEMethod_REQUEST = "request"
cICalMIMEMethod_REFRESH = "refresh"
cICalMIMEMethod_CANCEL = "cancel"
cICalMIMEMethod_ADD = "add"
cICalMIMEMethod_REPLY = "reply"
cICalMIMEMethod_COUNTER = "counter"
cICalMIMEMethod_DECLINECOUNTER = "declinecounter"
cICalMIMEComponent_VEVENT = "vevent"
cICalMIMEComponent_VTODO = "vtodo"
cICalMIMEComponent_VJOURNAL = "vjournal"
cICalMIMEComponent_VFREEBUSY = "vfreebusy"
cICalMIMEComponent_VAVAILABILITY = "vavailability"
pycalendar-2.0~svn13177/src/pycalendar/valueutils.py 0000644 0001750 0001750 00000002272 12101017573 021515 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# Helpers for value classes
from cStringIO import StringIO
class ValueMixin(object):
def __str__(self):
return self.getText()
@classmethod
def parseText(cls, data):
value = cls()
value.parse(data)
return value
def parse(self, data):
raise NotImplementedError
def generate(self, os):
raise NotImplementedError
def getText(self):
os = StringIO()
self.generate(os)
return os.getvalue()
def writeXML(self, node, namespace):
raise NotImplementedError
pycalendar-2.0~svn13177/src/pycalendar/freebusy.py 0000644 0001750 0001750 00000002617 12101017573 021147 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
class PyCalendarFreeBusy(object):
FREE = 0
BUSYTENTATIVE = 1
BUSYUNAVAILABLE = 2
BUSY = 3
def __init__(self, type=None, period=None):
self.mType = type if type else PyCalendarFreeBusy.FREE
self.mPeriod = period.duplicate() if period is not None else None
def duplicate(self):
return PyCalendarFreeBusy(self.mType, self.mPeriod)
def setType(self, type):
self.mType = type
def getType(self):
return self.mType
def setPeriod(self, period):
self.mPeriod = period.duplicate()
def getPeriod(self):
return self.mPeriod
def isPeriodOverlap(self, period):
return self.mPeriod.isPeriodOverlap(period)
def resolveOverlaps(self, fb):
# TODO:
pass
pycalendar-2.0~svn13177/src/pycalendar/definitions.py 0000644 0001750 0001750 00000023441 12101017573 021634 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# 5545 Components
cICalComponent_VCALENDAR = "VCALENDAR"
cICalComponent_VEVENT = "VEVENT"
cICalComponent_VTODO = "VTODO"
cICalComponent_VJOURNAL = "VJOURNAL"
cICalComponent_VFREEBUSY = "VFREEBUSY"
cICalComponent_VTIMEZONE = "VTIMEZONE"
cICalComponent_VALARM = "VALARM"
cICalComponent_STANDARD = "STANDARD"
cICalComponent_DAYLIGHT = "DAYLIGHT"
# 5545 Calendar Property Attributes
# 5545 Section 3.2
cICalAttribute_ALTREP = "ALTREP"
cICalAttribute_CN = "CN"
cICalAttribute_CUTYPE = "CUTYPE"
cICalAttribute_DELEGATED_FROM = "DELEGATED-FROM"
cICalAttribute_DELEGATED_TO = "DELEGATED-TO"
cICalAttribute_DIR = "DIR"
cICalAttribute_ENCODING = "ENCODING"
cICalAttribute_FMTTYPE = "FMTTYPE"
cICalAttribute_FBTYPE = "FBTYPE"
cICalAttribute_LANGUAGE = "LANGUAGE"
cICalAttribute_MEMBER = "MEMBER"
cICalAttribute_PARTSTAT = "PARTSTAT"
cICalAttribute_RANGE = "RANGE"
cICalAttribute_RELATED = "RELATED"
cICalAttribute_RELTYPE = "RELTYPE"
cICalAttribute_ROLE = "ROLE"
cICalAttribute_RSVP = "RSVP"
cICalAttribute_RSVP_TRUE = "TRUE"
cICalAttribute_RSVP_FALSE = "FALSE"
cICalAttribute_SENT_BY = "SENT-BY"
cICalAttribute_TZID = "TZID"
cICalAttribute_VALUE = "VALUE"
# 5545 Section 3.2.9
cICalAttribute_FBTYPE_FREE = "FREE"
cICalAttribute_FBTYPE_BUSY = "BUSY"
cICalAttribute_FBTYPE_BUSYUNAVAILABLE = "BUSY-UNAVAILABLE"
cICalAttribute_FBTYPE_BUSYTENTATIVE = "BUSY-TENTATIVE"
# 5545 Section 3.2.12
ePartStat_NeedsAction = 0
ePartStat_Accepted = 1
ePartStat_Declined = 2
ePartStat_Tentative = 3
ePartStat_Delegated = 4
ePartStat_Completed = 5
ePartStat_InProcess = 6
cICalAttribute_PARTSTAT_NEEDSACTION = "NEEDS-ACTION"
cICalAttribute_PARTSTAT_ACCEPTED = "ACCEPTED"
cICalAttribute_PARTSTAT_DECLINED = "DECLINED"
cICalAttribute_PARTSTAT_TENTATIVE = "TENTATIVE"
cICalAttribute_PARTSTAT_DELEGATED = "DELEGATED"
cICalAttribute_PARTSTAT_COMPLETED = "COMPLETE"
cICalAttribute_PARTSTAT_INPROCESS = "IN-PROCESS"
# 5545 Section 3.2.13
cICalAttribute_RANGE_THISANDFUTURE = "THISANDFUTURE"
cICalAttribute_RANGE_THISANDPRIOR = "THISANDPRIOR" # 2445 only
# 5545 Section 3.2.14
cICalAttribute_RELATED_START = "START"
cICalAttribute_RELATED_END = "END"
# 5545 Section 3.2.16
ePartRole_Chair = 0
ePartRole_Required = 1
ePartRole_Optional = 2
ePartRole_Non = 3
cICalAttribute_ROLE_CHAIR = "CHAIR"
cICalAttribute_ROLE_REQ_PART = "REQ-PARTICIPANT"
cICalAttribute_ROLE_OPT_PART = "OPT-PARTICIPANT"
cICalAttribute_ROLE_NON_PART = "NON-PARTICIPANT"
# 5545 Section 3.2.3
eCutype_Individual = 0
eCutype_Group = 1
eCutype_Resource = 2
eCutype_Room = 3
eCutype_Unknown = 4
cICalAttribute_CUTYPE_INDIVIDUAL = "INDIVIDUAL"
cICalAttribute_CUTYPE_GROUP = "GROUP"
cICalAttribute_CUTYPE_RESOURCE = "RESOURCE"
cICalAttribute_CUTYPE_ROOM = "ROOM"
cICalAttribute_CUTYPE_UNKNOWN = "UNKNOWN"
# 5545 Value types
# 5545 Section 3.3
cICalValue_BINARY = "BINARY"
cICalValue_BOOLEAN = "BOOLEAN"
cICalValue_CAL_ADDRESS = "CAL-ADDRESS"
cICalValue_DATE = "DATE"
cICalValue_DATE_TIME = "DATE-TIME"
cICalValue_DURATION = "DURATION"
cICalValue_FLOAT = "FLOAT"
cICalValue_INTEGER = "INTEGER"
cICalValue_PERIOD = "PERIOD"
cICalValue_RECUR = "RECUR"
cICalValue_TEXT = "TEXT"
cICalValue_TIME = "TIME"
cICalValue_URI = "URI"
cICalValue_UTC_OFFSET = "UTC-OFFSET"
# 5545 Calendar Properties
# 5545 Section 3.7
cICalProperty_CALSCALE = "CALSCALE"
cICalProperty_METHOD = "METHOD"
cICalProperty_PRODID = "PRODID"
cICalProperty_VERSION = "VERSION"
# Apple Extensions
cICalProperty_XWRCALNAME = "X-WR-CALNAME"
cICalProperty_XWRCALDESC = "X-WR-CALDESC"
cICalProperty_XWRALARMUID = "X-WR-ALARMUID"
# 5545 Component Property names
# 5545 Section 3.8.1
cICalProperty_ATTACH = "ATTACH"
cICalProperty_CATEGORIES = "CATEGORIES"
cICalProperty_CLASS = "CLASS"
cICalProperty_COMMENT = "COMMENT"
cICalProperty_DESCRIPTION = "DESCRIPTION"
cICalProperty_GEO = "GEO"
cICalProperty_LOCATION = "LOCATION"
cICalProperty_PERCENT_COMPLETE = "PERCENT-COMPLETE"
cICalProperty_PRIORITY = "PRIORITY"
cICalProperty_RESOURCES = "RESOURCES"
cICalProperty_STATUS = "STATUS"
cICalProperty_SUMMARY = "SUMMARY"
# 5545 Section 3.8.2
cICalProperty_COMPLETED = "COMPLETED"
cICalProperty_DTEND = "DTEND"
cICalProperty_DUE = "DUE"
cICalProperty_DTSTART = "DTSTART"
cICalProperty_DURATION = "DURATION"
cICalProperty_FREEBUSY = "FREEBUSY"
cICalProperty_TRANSP = "TRANSP"
cICalProperty_OPAQUE = "OPAQUE"
cICalProperty_TRANSPARENT = "TRANSPARENT"
# 5545 Section 3.8.3
cICalProperty_TZID = "TZID"
cICalProperty_TZNAME = "TZNAME"
cICalProperty_TZOFFSETFROM = "TZOFFSETFROM"
cICalProperty_TZOFFSETTO = "TZOFFSETTO"
cICalProperty_TZURL = "TZURL"
# 5545 Section 3.8.4
cICalProperty_ATTENDEE = "ATTENDEE"
cICalProperty_CONTACT = "CONTACT"
cICalProperty_ORGANIZER = "ORGANIZER"
cICalProperty_RECURRENCE_ID = "RECURRENCE-ID"
cICalProperty_RELATED_TO = "RELATED-TO"
cICalProperty_URL = "URL"
cICalProperty_UID = "UID"
# 5545 Section 3.8.5
cICalProperty_EXDATE = "EXDATE"
cICalProperty_EXRULE = "EXRULE" # 2445 only
cICalProperty_RDATE = "RDATE"
cICalProperty_RRULE = "RRULE"
# 5545 Section 3.8.6
cICalProperty_ACTION = "ACTION"
cICalProperty_REPEAT = "REPEAT"
cICalProperty_TRIGGER = "TRIGGER"
# 5545 Section 3.8.7
cICalProperty_CREATED = "CREATED"
cICalProperty_DTSTAMP = "DTSTAMP"
cICalProperty_LAST_MODIFIED = "LAST-MODIFIED"
cICalProperty_SEQUENCE = "SEQUENCE"
# 5545 Section 3.8.8.3
cICalProperty_REQUEST_STATUS = "REQUEST-STATUS"
# Enums
# Use ascending order for sensible sorting
# 5545 Section 3.3.10
eRecurrence_SECONDLY = 0
eRecurrence_MINUTELY = 1
eRecurrence_HOURLY = 2
eRecurrence_DAILY = 3
eRecurrence_WEEKLY = 4
eRecurrence_MONTHLY = 5
eRecurrence_YEARLY = 6
eRecurrence_FREQ = 0
eRecurrence_UNTIL = 1
eRecurrence_COUNT = 2
eRecurrence_INTERVAL = 3
eRecurrence_BYSECOND = 4
eRecurrence_BYMINUTE = 5
eRecurrence_BYHOUR = 6
eRecurrence_BYDAY = 7
eRecurrence_BYMONTHDAY = 8
eRecurrence_BYYEARDAY = 9
eRecurrence_BYWEEKNO = 10
eRecurrence_BYMONTH = 11
eRecurrence_BYSETPOS = 12
eRecurrence_WKST = 13
cICalValue_RECUR_FREQ = "FREQ"
cICalValue_RECUR_FREQ_LEN = 5
cICalValue_RECUR_SECONDLY = "SECONDLY"
cICalValue_RECUR_MINUTELY = "MINUTELY"
cICalValue_RECUR_HOURLY = "HOURLY"
cICalValue_RECUR_DAILY = "DAILY"
cICalValue_RECUR_WEEKLY = "WEEKLY"
cICalValue_RECUR_MONTHLY = "MONTHLY"
cICalValue_RECUR_YEARLY = "YEARLY"
cICalValue_RECUR_UNTIL = "UNTIL"
cICalValue_RECUR_COUNT = "COUNT"
cICalValue_RECUR_INTERVAL = "INTERVAL"
cICalValue_RECUR_BYSECOND = "BYSECOND"
cICalValue_RECUR_BYMINUTE = "BYMINUTE"
cICalValue_RECUR_BYHOUR = "BYHOUR"
cICalValue_RECUR_BYDAY = "BYDAY"
cICalValue_RECUR_BYMONTHDAY = "BYMONTHDAY"
cICalValue_RECUR_BYYEARDAY = "BYYEARDAY"
cICalValue_RECUR_BYWEEKNO = "BYWEEKNO"
cICalValue_RECUR_BYMONTH = "BYMONTH"
cICalValue_RECUR_BYSETPOS = "BYSETPOS"
cICalValue_RECUR_WKST = "WKST"
eRecurrence_WEEKDAY_SU = 0
eRecurrence_WEEKDAY_MO = 1
eRecurrence_WEEKDAY_TU = 2
eRecurrence_WEEKDAY_WE = 3
eRecurrence_WEEKDAY_TH = 4
eRecurrence_WEEKDAY_FR = 5
eRecurrence_WEEKDAY_SA = 6
cICalValue_RECUR_WEEKDAY_SU = "SU"
cICalValue_RECUR_WEEKDAY_MO = "MO"
cICalValue_RECUR_WEEKDAY_TU = "TU"
cICalValue_RECUR_WEEKDAY_WE = "WE"
cICalValue_RECUR_WEEKDAY_TH = "TH"
cICalValue_RECUR_WEEKDAY_FR = "FR"
cICalValue_RECUR_WEEKDAY_SA = "SA"
# 5545 Section 3.8.1.11
eStatus_VEvent_None = 0
eStatus_VEvent_Confirmed = 1
eStatus_VEvent_Tentative = 2
eStatus_VEvent_Cancelled = 3
eStatus_VToDo_None = 0
eStatus_VToDo_NeedsAction = 1
eStatus_VToDo_InProcess = 2
eStatus_VToDo_Completed = 3
eStatus_VToDo_Cancelled = 4
eStatus_VJournal_None = 0
eStatus_VJournal_Final = 1
eStatus_VJournal_Draft = 2
eStatus_VJournal_Cancelled = 3
cICalProperty_STATUS_TENTATIVE = "TENTATIVE"
cICalProperty_STATUS_CONFIRMED = "CONFIRMED"
cICalProperty_STATUS_CANCELLED = "CANCELLED"
cICalProperty_STATUS_NEEDS_ACTION = "NEEDS-ACTION"
cICalProperty_STATUS_COMPLETED = "COMPLETED"
cICalProperty_STATUS_IN_PROCESS = "IN-PROCESS"
cICalProperty_STATUS_DRAFT = "DRAFT"
cICalProperty_STATUS_FINAL = "FINAL"
# 5545 Section 3.8.6.1
eAction_VAlarm_Audio = 0
eAction_VAlarm_Display = 1
eAction_VAlarm_Email = 2
eAction_VAlarm_Procedure = 3
eAction_VAlarm_Unknown = 4
cICalProperty_ACTION_AUDIO = "AUDIO"
cICalProperty_ACTION_DISPLAY = "DISPLAY"
cICalProperty_ACTION_EMAIL = "EMAIL"
cICalProperty_ACTION_PROCEDURE = "PROCEDURE"
# Extensions: draft-daboo-calendar-availability-02
# Section 3.1
cICalComponent_VAVAILABILITY = "VAVAILABILITY"
cICalComponent_AVAILABLE = "AVAILABLE"
# Section 3.2
cICalProperty_BUSYTYPE = "BUSYTYPE"
# Extensions: draft-daboo-valarm-extensions-03
# Section 5
eAction_VAlarm_URI = 5
cICalProperty_ACTION_URI = "URI"
# Section 7.1
cICalProperty_ACKNOWLEDGED = "ACKNOWLEDGED"
eAction_VAlarm_None = 6
cICalProperty_ACTION_NONE = "NONE"
# Mulberry extensions
cICalProperty_ACTION_X_SPEAKTEXT = "X-MULBERRY-SPEAK-TEXT"
cICalProperty_ALARM_X_LASTTRIGGER = "X-MULBERRY-LAST-TRIGGER"
cICalProperty_ALARM_X_ALARMSTATUS = "X-MULBERRY-ALARM-STATUS"
eAlarm_Status_Pending = 0
eAlarm_Status_Completed = 1
eAlarm_Status_Disabled = 2
cICalProperty_ALARM_X_ALARMSTATUS_PENDING = "PENDING"
cICalProperty_ALARM_X_ALARMSTATUS_COMPLETED = "COMPLETED"
cICalProperty_ALARM_X_ALARMSTATUS_DISABLED = "DISABLED"
cICalAttribute_ORGANIZER_X_IDENTITY = "X-MULBERRY-IDENTITY"
cICalAttribute_ATTENDEE_X_NEEDS_ITIP = "X-MULBERRY-NEEDS-ITIP"
pycalendar-2.0~svn13177/src/pycalendar/xmldefs.py 0000644 0001750 0001750 00000005317 12101017573 020765 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import xml.etree.cElementTree as XML
# iCalendar/vCard XML definitions
iCalendar20_namespace = "urn:ietf:params:xml:ns:icalendar-2.0"
icalendar = "icalendar"
components = "components"
properties = "properties"
parameters = "parameters"
value_binary = "binary"
value_boolean = "boolean"
value_cal_address = "cal-address"
value_date = "date"
value_date_time = "date-time"
value_duration = "duration"
value_integer = "integer"
value_period = "period"
value_recur = "recur"
value_text = "text"
value_unknown = "unknown"
value_uri = "uri"
value_utc_offset = "utc-offset"
period_start = "start"
period_end = "end"
period_duration = "duration"
recur_freq = "freq"
recur_freq_secondly = "SECONDLY"
recur_freq_minutely = "MINUTELY"
recur_freq_hourly = "HOURLY"
recur_freq_daily = "DAILY"
recur_freq_weekly = "WEEKLY"
recur_freq_monthly = "MONTHLY"
recur_freq_yearly = "YEARLY"
recur_count = "count"
recur_until = "until"
recur_interval = "interval"
recur_bysecond = "bysecond"
recur_byminute = "byminute"
recur_byhour = "byhour"
recur_byday = "byday"
recur_bymonthday = "bymonthday"
recur_byyearday = "byyearday"
recur_byweekno = "byweekno"
recur_bymonth = "bymonth"
recur_bysetpos = "bysetpos"
recur_wkst = "wkst"
req_status_code = "code"
req_status_description = "description"
req_status_data = "data"
vCard40_namespace = "urn:ietf:params:xml:ns:vcard-4.0"
def makeTag(namespace, name):
return "{%s}%s" % (namespace, name.lower(),)
def toString(root):
data = """\n"""
INDENT = 2
# Generate indentation
def _indentNode(node, level=0):
if node.text is not None and node.text.strip():
return
elif len(node.getchildren()):
indent = "\n" + " " * (level + 1) * INDENT
node.text = indent
for child in node.getchildren():
child.tail = indent
_indentNode(child, level + 1)
if len(node.getchildren()):
node.getchildren()[-1].tail = "\n" + " " * level * INDENT
_indentNode(root, 0)
data += XML.tostring(root) + "\n"
return data
pycalendar-2.0~svn13177/src/pycalendar/stringutils.py 0000644 0001750 0001750 00000005551 12101017573 021712 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from hashlib import md5
def strduptokenstr(txt, tokens):
result = None
start = 0
# First punt over any leading space
for s in txt:
if s == " ":
start += 1
else:
break
else:
return None, ""
# Handle quoted string
if txt[start] == '\"':
maxlen = len(txt)
# Punt leading quote
start += 1
end = start
done = False
while not done:
if end == maxlen:
return None, txt
if txt[end] == '\"':
done = True
elif txt[end] == '\\':
# Punt past quote
end += 2
else:
end += 1
if end >= maxlen:
return None, txt
return txt[start:end], txt[end + 1:]
else:
for relend, s in enumerate(txt[start:]):
if s in tokens:
if relend:
result = txt[start:start + relend]
else:
result = ""
return result, txt[start + relend:]
return txt[start:], ""
def strtoul(s, offset=0):
max = len(s)
startoffset = offset
while offset < max:
if s[offset] in "0123456789":
offset += 1
continue
elif offset == 0:
raise ValueError
else:
return int(s[startoffset:offset]), offset
else:
if offset == 0:
raise ValueError
else:
return int(s[startoffset:]), offset
def strindexfind(s, ss, default_index):
if s and ss:
i = 0
s = s.upper()
while ss[i]:
if s == ss[i]:
return i
i += 1
return default_index
def strnindexfind(s, ss, default_index):
if s and ss:
i = 0
s = s.upper()
while ss[i]:
if s.startswith(ss[i]):
return i
i += 1
return default_index
def compareStringsSafe(s1, s2):
if s1 is None and s2 is None:
return True
elif (s1 is None and s2 is not None) or (s1 is not None and s2 is None):
return False
else:
return s1 == s2
def md5digest(txt):
return md5.new(txt).hexdigest()
pycalendar-2.0~svn13177/src/pycalendar/__init__.py 0000644 0001750 0001750 00000003277 12101017573 021065 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
__all__ = [
"attribute",
"available",
"binaryvalue",
"caladdressvalue",
"calendar",
"datetime",
"datetimevalue",
"definitions",
"duration",
"durationvalue",
"exceptions",
"freebusy",
"integervalue",
"locale",
"manager",
"multivalue",
"period",
"periodvalue",
"plaintextvalue",
"property",
"recurrence",
"recurrencevalue",
"requeststatusvalue",
"textvalue",
"timezone",
"timezonedb",
"unknownvalue",
"urivalue",
"utcoffsetvalue",
"valarm",
"value",
"vevent",
"vfreebusy",
"vjournal",
"vtimezone",
"vtimezonedaylight",
"vtimezonestandard",
"vtodo",
"vunknown",
]
# Import these to register the values
import binaryvalue
import caladdressvalue
import datetimevalue
import durationvalue
import integervalue
import multivalue
import periodvalue
import recurrencevalue
import requeststatusvalue
import textvalue
import unknownvalue
import urivalue
import utcoffsetvalue
# Import these to force static initialisation
import property
pycalendar-2.0~svn13177/src/pycalendar/vtimezonestandard.py 0000644 0001750 0001750 00000002135 12101017573 023057 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar.vtimezoneelement import PyCalendarVTimezoneElement
class PyCalendarVTimezoneStandard(PyCalendarVTimezoneElement):
def __init__(self, parent=None):
super(PyCalendarVTimezoneStandard, self).__init__(parent=parent)
def duplicate(self, parent=None):
return super(PyCalendarVTimezoneStandard, self).duplicate(parent=parent)
def getType(self):
return definitions.cICalComponent_STANDARD
pycalendar-2.0~svn13177/src/pycalendar/period.py 0000644 0001750 0001750 00000010732 12146673726 020623 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import xmldefs
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.duration import PyCalendarDuration
from pycalendar.valueutils import ValueMixin
import xml.etree.cElementTree as XML
class PyCalendarPeriod(ValueMixin):
def __init__(self, start=None, end=None, duration=None):
self.mStart = start if start is not None else PyCalendarDateTime()
if end is not None:
self.mEnd = end
self.mDuration = self.mEnd - self.mStart
self.mUseDuration = False
elif duration is not None:
self.mDuration = duration
self.mEnd = self.mStart + self.mDuration
self.mUseDuration = True
else:
self.mEnd = self.mStart.duplicate()
self.mDuration = PyCalendarDuration()
self.mUseDuration = False
def duplicate(self):
other = PyCalendarPeriod(start=self.mStart.duplicate(), end=self.mEnd.duplicate())
other.mUseDuration = self.mUseDuration
return other
def __hash__(self):
return hash((self.mStart, self.mEnd,))
def __repr__(self):
return "PyCalendarPeriod %s" % (self.getText(),)
def __str__(self):
return self.getText()
def __eq__(self, comp):
return self.mStart == comp.mStart and self.mEnd == comp.mEnd
def __gt__(self, comp):
return self.mStart > comp
def __lt__(self, comp):
return self.mStart < comp.mStart \
or (self.mStart == comp.mStart) and self.mEnd < comp.mEnd
@classmethod
def parseText(cls, data):
period = cls()
period.parse(data)
return period
def parse(self, data):
splits = data.split('/', 1)
if len(splits) == 2:
start = splits[0]
end = splits[1]
self.mStart.parse(start)
if end[0] == 'P':
self.mDuration.parse(end)
self.mUseDuration = True
self.mEnd = self.mStart + self.mDuration
else:
self.mEnd.parse(end)
self.mUseDuration = False
self.mDuration = self.mEnd - self.mStart
else:
raise ValueError
def generate(self, os):
try:
self.mStart.generate(os)
os.write("/")
if self.mUseDuration:
self.mDuration.generate(os)
else:
self.mEnd.generate(os)
except:
pass
def writeXML(self, node, namespace):
start = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.period_start))
start.text = self.mStart.getXMLText()
if self.mUseDuration:
duration = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.period_duration))
duration.text = self.mDuration.getText()
else:
end = XML.SubElement(node, xmldefs.makeTag(namespace, xmldefs.period_end))
end.text = self.mEnd.getXMLText()
def getStart(self):
return self.mStart
def getEnd(self):
return self.mEnd
def getDuration(self):
return self.mDuration
def getUseDuration(self):
return self.mUseDuration
def setUseDuration(self, use):
self.mUseDuration = use
def isDateWithinPeriod(self, dt):
# Inclusive start, exclusive end
return dt >= self.mStart and dt < self.mEnd
def isDateBeforePeriod(self, dt):
# Inclusive start
return dt < self.mStart
def isDateAfterPeriod(self, dt):
# Exclusive end
return dt >= self.mEnd
def isPeriodOverlap(self, p):
# Inclusive start, exclusive end
return not (self.mStart >= p.mEnd or self.mEnd <= p.mStart)
def adjustToUTC(self):
self.mStart.adjustToUTC()
self.mEnd.adjustToUTC()
def describeDuration(self):
return ""
pycalendar-2.0~svn13177/src/pycalendar/vunknown.py 0000644 0001750 0001750 00000003376 12101017573 021213 0 ustar rahul rahul ##
# Copyright (c) 2011-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar.component import PyCalendarComponent
from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
import uuid
class PyCalendarUnknownComponent(PyCalendarComponent):
propertyValueChecks = ICALENDAR_VALUE_CHECKS
def __init__(self, parent=None, comptype=""):
super(PyCalendarUnknownComponent, self).__init__(parent=parent)
self.mType = comptype
self.mMapKey = str(uuid.uuid4())
def duplicate(self, parent=None):
return super(PyCalendarUnknownComponent, self).duplicate(parent=parent, comptype=self.mType)
def getType(self):
return self.mType
def getBeginDelimiter(self):
return "BEGIN:" + self.mType
def getEndDelimiter(self):
return "END:" + self.mType
def getMimeComponentName(self):
return "unknown"
def getMapKey(self):
return self.mMapKey
def getSortKey(self):
"""
We do not want unknown components sorted.
"""
return ""
def sortedPropertyKeyOrder(self):
return (
definitions.cICalProperty_UID,
)
pycalendar-2.0~svn13177/src/pycalendar/vjournal.py 0000644 0001750 0001750 00000004376 12101017573 021167 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar import itipdefinitions
from pycalendar.componentrecur import PyCalendarComponentRecur
from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
class PyCalendarVJournal(PyCalendarComponentRecur):
propertyCardinality_1 = (
definitions.cICalProperty_DTSTAMP,
definitions.cICalProperty_UID,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_CLASS,
definitions.cICalProperty_CREATED,
definitions.cICalProperty_DTSTART,
definitions.cICalProperty_LAST_MODIFIED,
definitions.cICalProperty_ORGANIZER,
definitions.cICalProperty_RECURRENCE_ID,
definitions.cICalProperty_SEQUENCE,
# definitions.cICalProperty_STATUS, # Special fix done for multiple STATUS
definitions.cICalProperty_SUMMARY,
definitions.cICalProperty_URL,
definitions.cICalProperty_RRULE,
)
propertyValueChecks = ICALENDAR_VALUE_CHECKS
def __init__(self, parent=None):
super(PyCalendarVJournal, self).__init__(parent=parent)
def duplicate(self, parent=None):
return super(PyCalendarVJournal, self).duplicate(parent=parent)
def getType(self):
return definitions.cICalComponent_VJOURNAL
def getMimeComponentName(self):
return itipdefinitions.cICalMIMEComponent_VJOURNAL
def finalise(self):
super(PyCalendarVJournal, self).finalise()
def sortedPropertyKeyOrder(self):
return (
definitions.cICalProperty_UID,
definitions.cICalProperty_RECURRENCE_ID,
definitions.cICalProperty_DTSTART,
)
pycalendar-2.0~svn13177/src/pycalendar/datetimevalue.py 0000644 0001750 0001750 00000003303 12101017573 022145 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import xmldefs
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.value import PyCalendarValue
class PyCalendarDateTimeValue(PyCalendarValue):
def __init__(self, value=None):
self.mValue = value if value is not None else PyCalendarDateTime()
def duplicate(self):
return PyCalendarDateTimeValue(self.mValue.duplicate())
def getType(self):
return (PyCalendarValue.VALUETYPE_DATETIME, PyCalendarValue.VALUETYPE_DATE)[self.mValue.isDateOnly()]
def parse(self, data, fullISO=False):
self.mValue.parse(data, fullISO)
def generate(self, os):
self.mValue.generate(os)
def writeXML(self, node, namespace):
self.mValue.writeXML(node, namespace)
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_DATE, PyCalendarDateTimeValue, xmldefs.value_date)
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_DATETIME, PyCalendarDateTimeValue, xmldefs.value_date_time)
pycalendar-2.0~svn13177/src/pycalendar/textvalue.py 0000644 0001750 0001750 00000002472 12101017573 021343 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# iCalendar UTC Offset value
from pycalendar import utils, xmldefs
from pycalendar.plaintextvalue import PyCalendarPlainTextValue
from pycalendar.value import PyCalendarValue
class PyCalendarTextValue(PyCalendarPlainTextValue):
def getType(self):
return PyCalendarValue.VALUETYPE_TEXT
def parse(self, data):
# Decoding required
self.mValue = utils.decodeTextValue(data)
# os - StringIO object
def generate(self, os):
try:
# Encoding required
utils.writeTextValue(os, self.mValue)
except:
pass
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_TEXT, PyCalendarTextValue, xmldefs.value_text)
pycalendar-2.0~svn13177/src/pycalendar/adr.py 0000644 0001750 0001750 00000005306 12101017573 020067 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# vCard ADR value
from pycalendar import utils
from pycalendar.valueutils import ValueMixin
class Adr(ValueMixin):
"""
mValue is a tuple of seven str or tuples of str
"""
(
POBOX,
EXTENDED,
STREET,
LOCALITY,
REGION,
POSTALCODE,
COUNTRY,
MAXITEMS
) = range(8)
def __init__(self, pobox="", extended="", street="", locality="", region="", postalcode="", country=""):
self.mValue = (pobox, extended, street, locality, region, postalcode, country)
def duplicate(self):
return Adr(*self.mValue)
def __hash__(self):
return hash(self.mValue)
def __repr__(self):
return "ADR %s" % (self.getText(),)
def __eq__(self, comp):
return self.mValue == comp.mValue
def getPobox(self):
return self.mValue[Adr.POBOX]
def setPobox(self, value):
self.mValue[Adr.POBOX] = value
def getExtended(self):
return self.mValue[Adr.EXTENDED]
def setExtended(self, value):
self.mValue[Adr.EXTENDED] = value
def getStreet(self):
return self.mValue[Adr.STREET]
def setStreet(self, value):
self.mValue[Adr.STREET] = value
def getLocality(self):
return self.mValue[Adr.LOCALITY]
def setLocality(self, value):
self.mValue[Adr.LOCALITY] = value
def getRegion(self):
return self.mValue[Adr.REGION]
def setRegion(self, value):
self.mValue[Adr.REGION] = value
def getPostalCode(self):
return self.mValue[Adr.POSTALCODE]
def setPostalCode(self, value):
self.mValue[Adr.POSTALCODE] = value
def getCountry(self):
return self.mValue[Adr.COUNTRY]
def setCountry(self, value):
self.mValue[Adr.COUNTRY] = value
def parse(self, data):
self.mValue = utils.parseDoubleNestedList(data, Adr.MAXITEMS)
def generate(self, os):
utils.generateDoubleNestedList(os, self.mValue)
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
pycalendar-2.0~svn13177/src/pycalendar/orgvalue.py 0000644 0001750 0001750 00000002541 12101017573 021143 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# vCard ORG value
from pycalendar import utils
from pycalendar.value import PyCalendarValue
class OrgValue(PyCalendarValue):
"""
mValue is a str or tuple of str
"""
def __init__(self, value=None):
self.mValue = value
def duplicate(self):
return OrgValue(self.mValue)
def getType(self):
return PyCalendarValue.VALUETYPE_ORG
def parse(self, data):
self.mValue = utils.parseTextList(data, ';')
def generate(self, os):
utils.generateTextList(os, self.mValue, ';')
def getValue(self):
return self.mValue
def setValue(self, value):
self.mValue = value
PyCalendarValue.registerType(PyCalendarValue.VALUETYPE_ORG, OrgValue, None)
pycalendar-2.0~svn13177/src/pycalendar/vtimezone.py 0000644 0001750 0001750 00000024100 12317143440 021334 0 ustar rahul rahul ##
# Copyright (c) 2007-2012 Cyrus Daboo. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from pycalendar import definitions
from pycalendar.component import PyCalendarComponent
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.icalendar.validation import ICALENDAR_VALUE_CHECKS
class PyCalendarVTimezone(PyCalendarComponent):
propertyCardinality_1 = (
definitions.cICalProperty_TZID,
)
propertyCardinality_0_1 = (
definitions.cICalProperty_LAST_MODIFIED,
definitions.cICalProperty_TZURL,
)
propertyValueChecks = ICALENDAR_VALUE_CHECKS
UTCOFFSET_CACHE_MAX_ENTRIES = 100000
sortSubComponents = False
def __init__(self, parent=None):
super(PyCalendarVTimezone, self).__init__(parent=parent)
self.mID = ""
self.mUTCOffsetSortKey = None
self.mCachedExpandAllMaxYear = None
self.mCachedOffsets = None
def duplicate(self, parent=None):
other = super(PyCalendarVTimezone, self).duplicate(parent=parent)
other.mID = self.mID
other.mUTCOffsetSortKey = self.mUTCOffsetSortKey
return other
def getType(self):
return definitions.cICalComponent_VTIMEZONE
def getMimeComponentName(self):
# Cannot be sent as a separate MIME object
return None
def addComponent(self, comp):
# We can embed the timezone components only
if ((comp.getType() == definitions.cICalComponent_STANDARD)
or (comp.getType() == definitions.cICalComponent_DAYLIGHT)):
super(PyCalendarVTimezone, self).addComponent(comp)
else:
raise ValueError
def getMapKey(self):
return self.mID
def finalise(self):
# Get TZID
temp = self.loadValueString(definitions.cICalProperty_TZID)
if temp is not None:
self.mID = temp
# Sort sub-components by DTSTART
self.mComponents.sort(key=lambda x: x.getStart())
# Do inherited
super(PyCalendarVTimezone, self).finalise()
def validate(self, doFix=False):
"""
Validate the data in this component and optionally fix any problems, else raise. If
loggedProblems is not None it must be a C{list} and problem descriptions are appended
to that.
"""
fixed, unfixed = super(PyCalendarVTimezone, self).validate(doFix)
# Must have at least one STANDARD or DAYLIGHT sub-component
for component in self.mComponents:
if component.getType() in (definitions.cICalComponent_STANDARD, definitions.cICalComponent_DAYLIGHT):
break
else:
# Cannot fix a missing required component
logProblem = "[%s] At least one component must be present: %s or %s" % (
self.getType(),
definitions.cICalComponent_STANDARD,
definitions.cICalComponent_DAYLIGHT,
)
unfixed.append(logProblem)
return fixed, unfixed
def getID(self):
return self.mID
def getUTCOffsetSortKey(self):
if self.mUTCOffsetSortKey is None:
# Take time from first element
if len(self.mComponents) > 0:
# Initial offset provides the primary key
utc_offset1 = self.mComponents[0].getUTCOffset()
# Presence of secondary is the next key
utc_offset2 = utc_offset1
if len(self.mComponents) > 1:
utc_offset2 = self.mComponents[1].getUTCOffset()
# Create key
self.mUTCOffsetSortKey = (utc_offset1 + utc_offset2) / 2
else:
self.mUTCOffsetSortKey = 0
return self.mUTCOffsetSortKey
def getTimezoneOffsetSeconds(self, dt, relative_to_utc=False):
"""
Caching implementation of expansion. We cache the entire set of transitions up to one year ahead
of the requested time.
We need to handle calculating the offset based on both a local time and a UTC time. The later
is needed when converting from one timezone offset to another which is best done by determining
the UTC time as an intermediate value.
@param dt: a date-time to determine the offset for
@type dt: L{DateTime}
@param relative_to_utc: if L{False}, then the L{dt} value is the local time for which an
offset is desired, if L{True}, then the L{dt} value is a UTC time for which an
offset is desired.
@type relative_to_utc: L{bool}
"""
# Need to make the incoming date-time relative to the DTSTART in the
# timezone component for proper comparison.
# This means making the incoming date-time a floating (no timezone)
# item
temp = dt.duplicate()
temp.setTimezoneID(None)
# Check whether we need to recache
if self.mCachedExpandAllMaxYear is None or temp.mYear >= self.mCachedExpandAllMaxYear:
cacheMax = temp.duplicate()
cacheMax.setHHMMSS(0, 0, 0)
cacheMax.offsetYear(2)
cacheMax.setMonth(1)
cacheMax.setDay(1)
self.mCachedExpandAll = self.expandAll(None, cacheMax)
self.mCachedExpandAllMaxYear = cacheMax.mYear
self.mCachedOffsets = {}
# Now search for the transition just below the time we want
if len(self.mCachedExpandAll):
cacheKey = (temp.mYear, temp.mMonth, temp.mDay, temp.mHours, temp.mMinutes, relative_to_utc)
i = self.mCachedOffsets.get(cacheKey)
if i is None:
i = PyCalendarVTimezone.tuple_bisect_right(self.mCachedExpandAll, temp, relative_to_utc)
if len(self.mCachedOffsets) >= self.UTCOFFSET_CACHE_MAX_ENTRIES:
self.mCachedOffsets = {}
self.mCachedOffsets[cacheKey] = i
if i != 0:
return self.mCachedExpandAll[i - 1][3]
return 0
def getTimezoneDescriptor(self, dt):
result = ""
# Get the closet matching element to the time
found = self.findTimezoneElement(dt)
# Get it
if found is not None:
if len(found.getTZName()) == 0:
tzoffset = found.getUTCOffset()
negative = False
if tzoffset < 0:
tzoffset = -tzoffset
negative = True
result = ("+", "-")[negative]
hours_offset = tzoffset / (60 * 60)
if hours_offset < 10:
result += "0"
result += str(hours_offset)
mins_offset = (tzoffset / 60) % 60
if mins_offset < 10:
result += "0"
result += str(mins_offset)
else:
result = "("
result += found.getTZName()
result += ")"
return result
def mergeTimezone(self, tz):
pass
@staticmethod
def tuple_bisect_right(a, x, relative_to_utc=False):
"""
Same as bisect_right except that the values being compared are the first elements
of a tuple.
"""
lo = 0
hi = len(a)
while lo < hi:
mid = (lo + hi) // 2
if x < a[mid][1 if relative_to_utc else 0]:
hi = mid
else:
lo = mid + 1
return lo
def findTimezoneElement(self, dt):
# Need to make the incoming date-time relative to the DTSTART in the
# timezone component for proper comparison.
# This means making the incoming date-time a floating (no timezone)
# item
temp = dt.duplicate()
temp.setTimezoneID(None)
# Had to rework this because some VTIMEZONEs have sub-components where the DST instances are interleaved. That
# means we have to evaluate each and every sub-component to find the instance immediately less than the time we are checking.
# Now do the expansion for each one found and pick the lowest
found = None
dt_found = PyCalendarDateTime()
for item in self.mComponents:
dt_item = item.expandBelow(temp)
if temp >= dt_item:
if found is not None:
# Compare with the one previously cached and switch to this
# one if newer
if dt_item > dt_found:
found = item
dt_found = dt_item
else:
found = item
dt_found = dt_item
return found
def expandAll(self, start, end, with_name=False):
results = []
for item in self.mComponents:
results.extend(item.expandAll(start, end, with_name))
utc_results = []
for items in set(results):
items = list(items)
utcdt = items[0].duplicate()
utcdt.offsetSeconds(-items[1])
items.insert(1, utcdt)
utc_results.append(tuple(items))
utc_results.sort(key=lambda x: x[0].getPosixTime())
return utc_results
def sortedPropertyKeyOrder(self):
return (
definitions.cICalProperty_TZID,
definitions.cICalProperty_LAST_MODIFIED,
definitions.cICalProperty_TZURL,
)
@staticmethod
def sortByUTCOffsetComparator(tz1, tz2):
sort1 = tz1.getUTCOffsetSortKey()
sort2 = tz2.getUTCOffsetSortKey()
if sort1 == sort2:
return tz1.getID().compareToIgnoreCase(tz2.getID())
else:
return (1, -1)[sort1 < sort2]
pycalendar-2.0~svn13177/.pydevproject 0000644 0001750 0001750 00000000653 12301206170 016550 0 ustar rahul rahul
python 2.7
/${PROJECT_DIR_NAME}/src
Default