[CalendarServer-changes] [11614] PyCalendar/branches/json-2/src/pycalendar
source_changes at macosforge.org
source_changes at macosforge.org
Sun Aug 18 18:31:02 PDT 2013
Revision: 11614
http://trac.calendarserver.org//changeset/11614
Author: cdaboo at apple.com
Date: 2013-08-18 18:31:02 -0700 (Sun, 18 Aug 2013)
Log Message:
-----------
Updated jcal support.
Modified Paths:
--------------
PyCalendar/branches/json-2/src/pycalendar/__init__.py
PyCalendar/branches/json-2/src/pycalendar/componentbase.py
PyCalendar/branches/json-2/src/pycalendar/datetime.py
PyCalendar/branches/json-2/src/pycalendar/datetimevalue.py
PyCalendar/branches/json-2/src/pycalendar/duration.py
PyCalendar/branches/json-2/src/pycalendar/durationvalue.py
PyCalendar/branches/json-2/src/pycalendar/exceptions.py
PyCalendar/branches/json-2/src/pycalendar/icalendar/calendar.py
PyCalendar/branches/json-2/src/pycalendar/icalendar/property.py
PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrence.py
PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrencevalue.py
PyCalendar/branches/json-2/src/pycalendar/icalendar/requeststatusvalue.py
PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_property.py
PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_recurrence.py
PyCalendar/branches/json-2/src/pycalendar/icalendar/xmldefinitions.py
PyCalendar/branches/json-2/src/pycalendar/integervalue.py
PyCalendar/branches/json-2/src/pycalendar/multivalue.py
PyCalendar/branches/json-2/src/pycalendar/parameter.py
PyCalendar/branches/json-2/src/pycalendar/period.py
PyCalendar/branches/json-2/src/pycalendar/periodvalue.py
PyCalendar/branches/json-2/src/pycalendar/plaintextvalue.py
PyCalendar/branches/json-2/src/pycalendar/property.py
PyCalendar/branches/json-2/src/pycalendar/utcoffsetvalue.py
PyCalendar/branches/json-2/src/pycalendar/value.py
PyCalendar/branches/json-2/src/pycalendar/valueutils.py
PyCalendar/branches/json-2/src/pycalendar/vcard/adr.py
PyCalendar/branches/json-2/src/pycalendar/vcard/adrvalue.py
PyCalendar/branches/json-2/src/pycalendar/vcard/card.py
PyCalendar/branches/json-2/src/pycalendar/vcard/n.py
PyCalendar/branches/json-2/src/pycalendar/vcard/nvalue.py
PyCalendar/branches/json-2/src/pycalendar/vcard/orgvalue.py
PyCalendar/branches/json-2/src/pycalendar/vcard/property.py
PyCalendar/branches/json-2/src/pycalendar/xmldefinitions.py
Added Paths:
-----------
PyCalendar/branches/json-2/src/pycalendar/floatvalue.py
PyCalendar/branches/json-2/src/pycalendar/geovalue.py
PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_json.py
Modified: PyCalendar/branches/json-2/src/pycalendar/__init__.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/__init__.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/__init__.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -20,6 +20,8 @@
import caladdressvalue
import datetimevalue
import durationvalue
+import floatvalue
+import geovalue
import icalendar.recurrencevalue
import icalendar.requeststatusvalue
import integervalue
Modified: PyCalendar/branches/json-2/src/pycalendar/componentbase.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/componentbase.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/componentbase.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -20,6 +20,7 @@
from pycalendar.periodvalue import PeriodValue
from pycalendar.value import Value
import xml.etree.cElementTree as XML
+from pycalendar.exceptions import InvalidComponent, ErrorBase
class ComponentBase(object):
@@ -359,6 +360,60 @@
self.writeComponentsFilteredXML(comp, namespace, filter)
+ @classmethod
+ def parseJSON(cls, jobject, parent, comp=None):
+ """
+ Parse the JSON object which has the form:
+
+ [name, properties, subcomponents]
+
+ @param jobject: a JSON array
+ @type jobject: C{list}
+ """
+ # [name, properties, subcomponents]
+
+ try:
+ if comp is None:
+ comp = cls.sComponentType.makeComponent(jobject[0].upper(), parent)
+ for prop in jobject[1]:
+ comp.addProperty(cls.sPropertyType.parseJSON(prop))
+ for subcomp in jobject[2]:
+ comp.addComponent(cls.sComponentType.parseJSON(subcomp, comp))
+ comp.finalise()
+ return comp
+ except ErrorBase:
+ raise
+ except Exception:
+ raise InvalidComponent("Invalid component", jobject)
+
+
+ def writeJSON(self, jobject):
+
+ # Component element
+ comp = [self.getType().lower(), [], []]
+
+ # Each property
+ self.writePropertiesJSON(comp[1])
+
+ # Each component
+ self.writeComponentsJSON(comp[2])
+
+ jobject.append(comp)
+
+
+ def writeJSONFiltered(self, jobject, filter):
+ # Component element
+ comp = [self.getType().lower(), [], []]
+
+ # Each property
+ self.writePropertiesFilteredJSON(comp[1], filter)
+
+ # Each component
+ self.writeComponentsFilteredJSON(comp[2], filter)
+
+ jobject.append(comp)
+
+
def sortedComponents(self):
components = self.mComponents[:]
@@ -431,6 +486,27 @@
subcomp.writeXMLFiltered(comps, namespace, subfilter)
+ def writeComponentsJSON(self, jobject):
+
+ if self.mComponents:
+ # Write out the remainder
+ for component in self.sortedComponents():
+ component.writeJSON(jobject)
+
+
+ def writeComponentsFilteredJSON(self, jobject, filter):
+
+ if self.mComponents:
+ # Shortcut for all sub-components
+ if filter.isAllSubComponents():
+ self.writeJSON(jobject)
+ elif filter.hasSubComponentFilters():
+ for subcomp in self.sortedcomponents():
+ subfilter = filter.getSubComponentFilter(subcomp.getType())
+ if subfilter is not None:
+ subcomp.writeJSONFiltered(jobject, subfilter)
+
+
def loadValue(self, value_name):
if self.hasProperty(value_name):
return self.findFirstProperty(value_name)
@@ -603,6 +679,32 @@
prop.writeXMLFiltered(props, namespace, filter)
+ def writePropertiesJSON(self, jobject):
+
+ # Sort properties by name
+ keys = self.sortedPropertyKeys()
+ for key in keys:
+ props = self.mProperties[key]
+ for prop in props:
+ prop.writeJSON(jobject)
+
+
+ def writePropertiesFilteredJSON(self, jobject, 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.writeJSON(jobject)
+ elif filter.hasPropertyFilters():
+ for key in keys:
+ for prop in self.getProperties(key):
+ prop.writeJSONFiltered(jobject, filter)
+
+
def loadPrivateValue(self, value_name):
# Read it in from properties list and then delete the property from the
# main list
Modified: PyCalendar/branches/json-2/src/pycalendar/datetime.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/datetime.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/datetime.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -934,6 +934,7 @@
else:
return "%04d-%02d-%02dT%02d:%02d:%02d" % (self.mYear, self.mMonth, self.mDay, self.mHours, self.mMinutes, self.mSeconds)
+ getJSONText = getXMLText
@classmethod
def parseText(cls, data, fullISO=False):
@@ -1030,7 +1031,7 @@
# iCalendar:
# parse format YYYYMMDD[THHMMSS[Z]]
- # vCard (fullISO)
+ # vCard (fullISO), jCal
# parse format YYYY[-]MM[-]DD[THH[:]MM[:]SS[(Z/(+/-)HHMM]]
try:
@@ -1092,6 +1093,14 @@
value.text = self.getXMLText()
+ def parseJSON(self, jobject):
+ self.parse(str(jobject), True)
+
+
+ def writeJSON(self, jobject):
+ jobject.append(self.getJSONText())
+
+
def normalise(self):
# Normalise seconds
normalised_secs = self.mSeconds % 60
Modified: PyCalendar/branches/json-2/src/pycalendar/datetimevalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/datetimevalue.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/datetimevalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -21,10 +21,9 @@
class DateTimeValue(WrapperValue, Value):
- def __init__(self, value=None):
- self.mValue = value if value is not None else DateTime()
+ _wrappedClass = DateTime
+ _wrappedType = None # Depends on actual value
-
def getType(self):
return (Value.VALUETYPE_DATETIME, Value.VALUETYPE_DATE)[self.mValue.isDateOnly()]
Modified: PyCalendar/branches/json-2/src/pycalendar/duration.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/duration.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/duration.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -270,3 +270,11 @@
def writeXML(self, node, namespace):
node.text = self.getText()
+
+
+ def parseJSON(self, jobject):
+ self.parse(str(jobject))
+
+
+ def writeJSON(self, jobject):
+ jobject.append(self.getText())
Modified: PyCalendar/branches/json-2/src/pycalendar/durationvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/durationvalue.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/durationvalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -21,11 +21,7 @@
class DurationValue(WrapperValue, Value):
- def __init__(self, value=None):
- self.mValue = value if value is not None else Duration()
+ _wrappedClass = Duration
+ _wrappedType = Value.VALUETYPE_DURATION
-
- def getType(self):
- return Value.VALUETYPE_DURATION
-
Value.registerType(Value.VALUETYPE_DURATION, DurationValue, xmldefinitions.value_duration)
Modified: PyCalendar/branches/json-2/src/pycalendar/exceptions.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/exceptions.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/exceptions.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -27,6 +27,11 @@
+class InvalidComponent(ErrorBase):
+ pass
+
+
+
class InvalidProperty(ErrorBase):
pass
Added: PyCalendar/branches/json-2/src/pycalendar/floatvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/floatvalue.py (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/floatvalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -0,0 +1,68 @@
+##
+# 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 float value
+
+from pycalendar import xmldefinitions
+from pycalendar.value import Value
+
+class FloatValue(Value):
+
+ def __init__(self, value=None):
+ self.mValue = value if value is not None else 0.0
+
+
+ def duplicate(self):
+ return FloatValue(self.mValue)
+
+
+ def getType(self):
+ return Value.VALUETYPE_FLOAT
+
+
+ def parse(self, data, variant):
+ self.mValue = float(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 parseJSONValue(self, jobject):
+ self.mValue = float(jobject)
+
+
+ def writeJSONValue(self, jobject):
+ jobject.append(self.mValue)
+
+
+ def getValue(self):
+ return self.mValue
+
+
+ def setValue(self, value):
+ self.mValue = value
+
+Value.registerType(Value.VALUETYPE_FLOAT, FloatValue, xmldefinitions.value_float)
Added: PyCalendar/branches/json-2/src/pycalendar/geovalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/geovalue.py (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/geovalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -0,0 +1,88 @@
+##
+# 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 xmlutils
+from pycalendar.exceptions import InvalidData
+from pycalendar.icalendar import xmldefinitions
+from pycalendar.value import Value
+from pycalendar import xmldefinitions as xmldefinitions_top
+import xml.etree.cElementTree as XML
+
+class GeoValue(Value):
+ """
+ The value is a list of 2 floats
+ """
+
+ def __init__(self, value=None):
+ self.mValue = value if value is not None else [0.0, 0.0]
+
+
+ def __hash__(self):
+ return hash(tuple(self.mValue))
+
+
+ def duplicate(self):
+ return GeoValue(self.mValue[:])
+
+
+ def getType(self):
+ return Value.VALUETYPE_GEO
+
+
+ def parse(self, data, variant="icalendar"):
+
+ splits = data.split(";")
+ if len(splits) != 2:
+ raise InvalidData("GEO value incorrect", data)
+ try:
+ self.mValue = [float(splits[0]), float(splits[1])]
+ except ValueError:
+ raise InvalidData("GEO value incorrect", data)
+
+
+ # os - StringIO object
+ def generate(self, os):
+ os.write("%s;%s" % (self.mValue[0], self.mValue[1],))
+
+
+ def writeXML(self, node, namespace):
+ value = self.getXMLNode(node, namespace)
+
+ latitude = XML.SubElement(value, xmlutils.makeTag(namespace, xmldefinitions.geo_latitude))
+ latitude.text = self.mValue[0]
+
+ longitude = XML.SubElement(value, xmlutils.makeTag(namespace, xmldefinitions.geo_longitude))
+ longitude.text = self.mValue[1]
+
+
+ def parseJSONValue(self, jobject):
+ self.mValue = jobject
+
+
+ def writeJSONValue(self, jobject):
+ jobject.append(list(self.mValue))
+
+
+ def getValue(self):
+ return self.mValue
+
+
+ def setValue(self, value):
+ self.mValue = value
+
+Value.registerType(Value.VALUETYPE_GEO, GeoValue, xmldefinitions.geo, xmldefinitions_top.value_float)
Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/calendar.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/calendar.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/calendar.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -30,6 +30,7 @@
from pycalendar.period import Period
from pycalendar.utils import readFoldedLine
import collections
+import json
import xml.etree.cElementTree as XML
class Calendar(ComponentBase):
@@ -499,6 +500,28 @@
return root
+ @staticmethod
+ def parseJSONText(data):
+
+ return Calendar.parseJSON(json.loads(data), None, Calendar(add_defaults=False))
+
+
+ def getTextJSON(self, includeTimezones=False):
+ jobject = self.writeJSON(includeTimezones)
+ return json.dumps(jobject, indent=2, separators=(',', ':'))
+
+
+ def writeJSON(self, includeTimezones=False):
+ # Make sure all required timezones are in this object
+ if includeTimezones:
+ self.includeTimezones()
+
+ # Root node structure
+ vcalendar = []
+ super(Calendar, self).writeJSON(vcalendar)
+ return vcalendar[0]
+
+
# Get expanded components
def getVEvents(self, period, list, all_day_at_top=True):
# Look at each VEvent
Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/property.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/property.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -46,7 +46,7 @@
definitions.cICalProperty_CLASS : Value.VALUETYPE_TEXT,
definitions.cICalProperty_COMMENT : Value.VALUETYPE_TEXT,
definitions.cICalProperty_DESCRIPTION : Value.VALUETYPE_TEXT,
- definitions.cICalProperty_GEO : Value.VALUETYPE_GEO,
+ definitions.cICalProperty_GEO : Value.VALUETYPE_FLOAT,
definitions.cICalProperty_LOCATION : Value.VALUETYPE_TEXT,
definitions.cICalProperty_PERCENT_COMPLETE : Value.VALUETYPE_INTEGER,
definitions.cICalProperty_PRIORITY : Value.VALUETYPE_INTEGER,
@@ -97,7 +97,7 @@
definitions.cICalProperty_SEQUENCE : Value.VALUETYPE_INTEGER,
# 5545 Section 3.8.8
- definitions.cICalProperty_REQUEST_STATUS : Value.VALUETYPE_REQUEST_STATUS,
+ definitions.cICalProperty_REQUEST_STATUS : Value.VALUETYPE_TEXT,
# Extensions: draft-daboo-valarm-extensions-03
definitions.cICalProperty_ACKNOWLEDGED : Value.VALUETYPE_DATETIME,
@@ -161,6 +161,11 @@
definitions.cICalProperty_RDATE,
))
+ sSpecialVariants = {
+ definitions.cICalProperty_GEO : Value.VALUETYPE_GEO,
+ definitions.cICalProperty_REQUEST_STATUS: Value.VALUETYPE_REQUEST_STATUS,
+ }
+
sVariant = "ical"
sValue = definitions.cICalParameter_VALUE
Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrence.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrence.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrence.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -720,6 +720,77 @@
child.text = str(item)
+ def parseJSON(self, jobject):
+ """
+ jCal splits the value into components. We need to convert that back to the
+ iCalendar string format, then parse it.
+ """
+
+ items = []
+ for name, value in jobject.items():
+ if name in (
+ "bysecond", "byminute", "byhour",
+ "bymonthday", "byyearday", "byweekno",
+ "bymonth", "bysetpos", "byday",
+ ):
+ if not isinstance(value, str) and not isinstance(value, unicode) and not isinstance(value, int):
+ value = ",".join(map(str, value))
+ items.append("%s=%s" % (name.upper(), value,))
+ self.parse(";".join(items))
+
+
+ def writeJSON(self, jobject):
+ """
+ jCal splits the value into components. We need to convert that back to the
+ iCalendar string format, then parse it.
+ """
+ jdict = {}
+
+ jdict[xmldefinitions.recur_freq] = self.cFreqToXMLMap[self.mFreq]
+
+ if self.mUseCount:
+ jdict[xmldefinitions.recur_count] = self.mCount
+ elif self.mUseUntil:
+ jdict[xmldefinitions.recur_until] = self.mUntil.getXMLText()
+
+ if self.mInterval > 1:
+ jdict[xmldefinitions.recur_interval] = self.mInterval
+
+ if self.mBySeconds:
+ jdict[xmldefinitions.recur_bysecond] = self.mBySeconds
+ if self.mByMinutes:
+ jdict[xmldefinitions.recur_byminute] = self.mByMinutes
+ if self.mByHours:
+ jdict[xmldefinitions.recur_byhour] = self.mByHours
+
+ if self.mByDay is not None and len(self.mByDay) != 0:
+ items = []
+ for iter in self.mByDay:
+ data = ""
+ if iter[0] != 0:
+ data = str(iter[0])
+ data += self.cWeekdayRecurMap.get(iter[1], "")
+ items.append(data)
+ jdict[xmldefinitions.recur_byday] = items
+
+ if self.mByMonthDay:
+ jdict[xmldefinitions.recur_bymonthday] = self.mByMonthDay
+ if self.mByYearDay:
+ jdict[xmldefinitions.recur_byyearday] = self.mByYearDay
+ if self.mByWeekNo:
+ jdict[xmldefinitions.recur_byweekno] = self.mByWeekNo
+ if self.mByMonth:
+ jdict[xmldefinitions.recur_bymonth] = self.mByMonth
+ if self.mBySetPos:
+ jdict[xmldefinitions.recur_bysetpos] = self.mBySetPos
+
+ # MO is the default so we do not need it
+ if self.mWeekstart != definitions.eRecurrence_WEEKDAY_MO:
+ jdict[xmldefinitions.recur_wkst] = self.cWeekdayRecurMap.get(self.mWeekstart, definitions.cICalValue_RECUR_WEEKDAY_MO)
+
+ jobject.append(jdict)
+
+
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) \
Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrencevalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrencevalue.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/recurrencevalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -21,14 +21,9 @@
class RecurrenceValue(WrapperValue, Value):
- def __init__(self, value=None):
- self.mValue = value if value is not None else Recurrence()
+ _wrappedClass = Recurrence
+ _wrappedType = Value.VALUETYPE_RECUR
-
- def getType(self):
- return Value.VALUETYPE_RECUR
-
-
def writeXML(self, node, namespace):
self.mValue.writeXML(node, namespace)
Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/requeststatusvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/requeststatusvalue.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/requeststatusvalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -82,17 +82,30 @@
def writeXML(self, node, namespace):
- code = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.req_status_code))
+ value = self.getXMLNode(node, namespace)
+
+ code = XML.SubElement(value, xmlutils.makeTag(namespace, xmldefinitions.req_status_code))
code.text = self.mValue[0]
- description = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.req_status_description))
+ description = XML.SubElement(value, xmlutils.makeTag(namespace, xmldefinitions.req_status_description))
description.text = self.mValue[1]
if len(self.mValue) == 3 and self.mValue[2]:
- data = XML.SubElement(node, xmlutils.makeTag(namespace, xmldefinitions.req_status_data))
- data.text = self.mValue[1]
+ data = XML.SubElement(value, xmlutils.makeTag(namespace, xmldefinitions.req_status_data))
+ data.text = self.mValue[2]
+ def parseJSONValue(self, jobject):
+ self.mValue = jobject
+
+
+ def writeJSONValue(self, jobject):
+ value = [self.mValue[0], self.mValue[1]]
+ if len(self.mValue) == 3:
+ value.append(self.mValue[2])
+ jobject.append(value)
+
+
def getValue(self):
return self.mValue
Added: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_json.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_json.py (rev 0)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_json.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -0,0 +1,629 @@
+##
+# 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 pycalendar.icalendar.calendar import Calendar
+import difflib
+import unittest
+
+class TestJSON(unittest.TestCase):
+
+ data = (
+ (
+"""BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTAMP:20080205T191224Z
+DTSTART;VALUE=DATE:20081006
+SUMMARY:Planning meeting
+UID:4088E990AD89CB3DBB484909
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""[
+ "vcalendar",
+ [
+ [
+ "version",
+ {},
+ "text",
+ "2.0"
+ ],
+ [
+ "calscale",
+ {},
+ "text",
+ "GREGORIAN"
+ ],
+ [
+ "prodid",
+ {},
+ "text",
+ "-//Example Inc.//Example Calendar//EN"
+ ]
+ ],
+ [
+ [
+ "vevent",
+ [
+ [
+ "uid",
+ {},
+ "text",
+ "4088E990AD89CB3DBB484909"
+ ],
+ [
+ "dtstart",
+ {},
+ "date",
+ "2008-10-06"
+ ],
+ [
+ "dtstamp",
+ {},
+ "date-time",
+ "2008-02-05T19:12:24Z"
+ ],
+ [
+ "summary",
+ {},
+ "text",
+ "Planning meeting"
+ ]
+ ],
+ []
+ ]
+ ]
+]""",
+ ),
+ (
+"""BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Example Corp.//Example Client//EN
+BEGIN:VTIMEZONE
+LAST-MODIFIED:20040110T032845Z
+TZID:US/Eastern
+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
+DTSTAMP:20060206T001121Z
+DTSTART;TZID=US/Eastern:20060102T120000
+DURATION:PT1H
+GEO:-123.45;67.89
+RRULE:FREQ=DAILY;COUNT=5
+RDATE;TZID=US/Eastern;VALUE=PERIOD:20060102T150000/PT2H
+SUMMARY:Event #2
+DESCRIPTION:We are having a meeting all this week at 12 pm fo
+ r one hour\\, with an additional meeting on the first day 2 h
+ ours long.\\nPlease bring your own lunch for the 12 pm meetin
+ gs.
+UID:00959BC664CA650E933C892C at example.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20060206T001121Z
+DTSTART;TZID=US/Eastern:20060104T140000
+DURATION:PT1H
+RECURRENCE-ID;TZID=US/Eastern:20060104T120000
+SUMMARY:Event #2 bis
+UID:00959BC664CA650E933C892C at example.com
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n"),
+
+"""[
+ "vcalendar",
+ [
+ [
+ "version",
+ {},
+ "text",
+ "2.0"
+ ],
+ [
+ "prodid",
+ {},
+ "text",
+ "-//Example Corp.//Example Client//EN"
+ ]
+ ],
+ [
+ [
+ "vtimezone",
+ [
+ [
+ "tzid",
+ {},
+ "text",
+ "US/Eastern"
+ ],
+ [
+ "last-modified",
+ {},
+ "date-time",
+ "2004-01-10T03:28:45Z"
+ ]
+ ],
+ [
+ [
+ "daylight",
+ [
+ [
+ "dtstart",
+ {},
+ "date-time",
+ "2000-04-04T02:00:00"
+ ],
+ [
+ "rrule",
+ {},
+ "recur",
+ {
+ "bymonth":[
+ 4
+ ],
+ "freq":"YEARLY",
+ "byday":[
+ "1SU"
+ ]
+ }
+ ],
+ [
+ "tzname",
+ {},
+ "text",
+ "EDT"
+ ],
+ [
+ "tzoffsetfrom",
+ {},
+ "utc-offset",
+ "-05:00"
+ ],
+ [
+ "tzoffsetto",
+ {},
+ "utc-offset",
+ "-04:00"
+ ]
+ ],
+ []
+ ],
+ [
+ "standard",
+ [
+ [
+ "dtstart",
+ {},
+ "date-time",
+ "2000-10-26T02:00:00"
+ ],
+ [
+ "rrule",
+ {},
+ "recur",
+ {
+ "bymonth":[
+ 10
+ ],
+ "freq":"YEARLY",
+ "byday":[
+ "-1SU"
+ ]
+ }
+ ],
+ [
+ "tzname",
+ {},
+ "text",
+ "EST"
+ ],
+ [
+ "tzoffsetfrom",
+ {},
+ "utc-offset",
+ "-04:00"
+ ],
+ [
+ "tzoffsetto",
+ {},
+ "utc-offset",
+ "-05:00"
+ ]
+ ],
+ []
+ ]
+ ]
+ ],
+ [
+ "vevent",
+ [
+ [
+ "uid",
+ {},
+ "text",
+ "00959BC664CA650E933C892C at example.com"
+ ],
+ [
+ "dtstart",
+ {
+ "tzid":"US/Eastern"
+ },
+ "date-time",
+ "2006-01-02T12:00:00"
+ ],
+ [
+ "duration",
+ {},
+ "duration",
+ "PT1H"
+ ],
+ [
+ "description",
+ {},
+ "text",
+ "We are having a meeting all this week at 12 pm for one hour, with an additional meeting on the first day 2 hours long.\\nPlease bring your own lunch for the 12 pm meetings."
+ ],
+ [
+ "dtstamp",
+ {},
+ "date-time",
+ "2006-02-06T00:11:21Z"
+ ],
+ [
+ "geo",
+ {},
+ "float",
+ [
+ -123.45,
+ 67.89
+ ]
+ ],
+ [
+ "rdate",
+ {
+ "tzid":"US/Eastern"
+ },
+ "period",
+ [
+ "2006-01-02T15:00:00",
+ "PT2H"
+ ]
+ ],
+ [
+ "rrule",
+ {},
+ "recur",
+ {
+ "count":5,
+ "freq":"DAILY"
+ }
+ ],
+ [
+ "summary",
+ {},
+ "text",
+ "Event #2"
+ ]
+ ],
+ []
+ ],
+ [
+ "vevent",
+ [
+ [
+ "uid",
+ {},
+ "text",
+ "00959BC664CA650E933C892C at example.com"
+ ],
+ [
+ "recurrence-id",
+ {
+ "tzid":"US/Eastern"
+ },
+ "date-time",
+ "2006-01-04T12:00:00"
+ ],
+ [
+ "dtstart",
+ {
+ "tzid":"US/Eastern"
+ },
+ "date-time",
+ "2006-01-04T14:00:00"
+ ],
+ [
+ "duration",
+ {},
+ "duration",
+ "PT1H"
+ ],
+ [
+ "dtstamp",
+ {},
+ "date-time",
+ "2006-02-06T00:11:21Z"
+ ],
+ [
+ "summary",
+ {},
+ "text",
+ "Event #2 bis"
+ ]
+ ],
+ []
+ ]
+ ]
+]""",
+ ),
+ )
+
+ def testGenerateJSON(self):
+
+ def _doRoundtrip(caldata, resultdata):
+ test1 = resultdata
+
+ cal = Calendar.parseText(caldata)
+
+ test2 = cal.getTextJSON()
+ self.assertEqual(
+ test1,
+ test2,
+ "\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
+ )
+
+ for item1, item2 in self.data:
+ _doRoundtrip(item1, item2)
+
+
+ def testParseJSON(self):
+
+ def _doRoundtrip(caldata, jcaldata):
+ cal1 = Calendar.parseText(caldata)
+ test1 = cal1.getText()
+
+ cal2 = Calendar.parseJSONText(jcaldata)
+ test2 = cal2.getText()
+
+ self.assertEqual(
+ test1,
+ test2,
+ "\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
+ )
+
+ for item1, item2 in self.data:
+ _doRoundtrip(item1, item2)
+
+
+ def testjCalExample1(self):
+
+ jcaldata = """["vcalendar",
+ [
+ ["calscale", {}, "text", "GREGORIAN"],
+ ["prodid", {}, "text", "-//Example Inc.//Example Calendar//EN"],
+ ["version", {}, "text", "2.0"]
+ ],
+ [
+ ["vevent",
+ [
+ ["dtstamp", {}, "date-time", "2008-02-05T19:12:24Z"],
+ ["dtstart", {}, "date", "2008-10-06"],
+ ["summary", {}, "text", "Planning meeting"],
+ ["uid", {}, "text", "4088E990AD89CB3DBB484909"]
+ ],
+ []
+ ]
+ ]
+]
+"""
+
+ icaldata = """BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VEVENT
+DTSTAMP:20080205T191224Z
+DTSTART;VALUE=DATE:20081006
+SUMMARY:Planning meeting
+UID:4088E990AD89CB3DBB484909
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+ cal1 = Calendar.parseText(icaldata)
+ test1 = cal1.getText()
+
+ cal2 = Calendar.parseJSONText(jcaldata)
+ test2 = cal2.getText()
+
+ self.assertEqual(
+ test1,
+ test2,
+ "\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
+ )
+
+
+ def testjCalExample2(self):
+
+ jcaldata = """["vcalendar",
+ [
+ ["prodid", {}, "text", "-//Example Corp.//Example Client//EN"],
+ ["version", {}, "text", "2.0"]
+ ],
+ [
+ ["vtimezone",
+ [
+ ["last-modified", {}, "date-time", "2004-01-10T03:28:45Z"],
+ ["tzid", {}, "text", "US/Eastern"]
+ ],
+ [
+ ["daylight",
+ [
+ ["dtstart", {}, "date-time", "2000-04-04T02:00:00"],
+ ["rrule",
+ {},
+ "recur",
+ {
+ "freq": "YEARLY",
+ "byday": "1SU",
+ "bymonth": 4
+ }
+ ],
+ ["tzname", {}, "text", "EDT"],
+ ["tzoffsetfrom", {}, "utc-offset", "-05:00"],
+ ["tzoffsetto", {}, "utc-offset", "-04:00"]
+ ],
+ []
+ ],
+ ["standard",
+ [
+ ["dtstart", {}, "date-time", "2000-10-26T02:00:00"],
+ ["rrule",
+ {},
+ "recur",
+ {
+ "freq": "YEARLY",
+ "byday": "-1SU",
+ "bymonth": 10
+ }
+ ],
+ ["tzname", {}, "text", "EST"],
+ ["tzoffsetfrom", {}, "utc-offset", "-04:00"],
+ ["tzoffsetto", {}, "utc-offset", "-05:00"]
+ ],
+ []
+ ]
+ ]
+ ],
+ ["vevent",
+ [
+ ["dtstamp", {}, "date-time", "2006-02-06T00:11:21Z"],
+ ["dtstart",
+ { "tzid": "US/Eastern" },
+ "date-time",
+ "2006-01-02T12:00:00"
+ ],
+ ["duration", {}, "duration", "PT1H"],
+ ["rrule", {}, "recur", { "freq": "DAILY", "count": 5 } ],
+ ["rdate",
+ { "tzid": "US/Eastern" },
+ "period",
+ ["2006-01-02T15:00:00", "PT2H"]
+ ],
+ ["summary", {}, "text", "Event #2"],
+ ["description",
+ {},
+ "text",
+ "We are having a meeting all this week at 12 pm for one hour, with an additional meeting on the first day 2 hours long.\\nPlease bring your own lunch for the 12 pm meetings."
+ ],
+ ["uid", {}, "text", "00959BC664CA650E933C892C at example.com"]
+ ],
+ []
+ ],
+ ["vevent",
+ [
+ ["dtstamp", {}, "date-time", "2006-02-06T00:11:21Z"],
+ ["dtstart",
+ { "tzid": "US/Eastern" },
+ "date-time",
+ "2006-01-04T14:00:00"
+ ],
+ ["duration", {}, "duration", "PT1H"],
+ ["recurrence-id",
+ { "tzid": "US/Eastern" },
+ "date-time",
+ "2006-01-04T12:00:00"
+ ],
+ ["summary", {}, "text", "Event #2 bis"],
+ ["uid", {}, "text", "00959BC664CA650E933C892C at example.com"]
+ ],
+ []
+ ]
+ ]
+]
+"""
+
+ icaldata = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Example Corp.//Example Client//EN
+BEGIN:VTIMEZONE
+LAST-MODIFIED:20040110T032845Z
+TZID:US/Eastern
+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
+DTSTAMP:20060206T001121Z
+DTSTART;TZID=US/Eastern:20060102T120000
+DURATION:PT1H
+RRULE:FREQ=DAILY;COUNT=5
+RDATE;TZID=US/Eastern;VALUE=PERIOD:20060102T150000/PT2H
+SUMMARY:Event #2
+DESCRIPTION:We are having a meeting all this week at 12 pm fo
+ r one hour\\, with an additional meeting on the first day 2 h
+ ours long.\\nPlease bring your own lunch for the 12 pm meetin
+ gs.
+UID:00959BC664CA650E933C892C at example.com
+END:VEVENT
+BEGIN:VEVENT
+DTSTAMP:20060206T001121Z
+DTSTART;TZID=US/Eastern:20060104T140000
+DURATION:PT1H
+RECURRENCE-ID;TZID=US/Eastern:20060104T120000
+SUMMARY:Event #2 bis
+UID:00959BC664CA650E933C892C at example.com
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n")
+
+ cal1 = Calendar.parseText(icaldata)
+ test1 = cal1.getText()
+
+ cal2 = Calendar.parseJSONText(jcaldata)
+ test2 = cal2.getText()
+
+ self.assertEqual(
+ test1,
+ test2,
+ "\n".join(difflib.unified_diff(str(test1).splitlines(), test2.splitlines()))
+ )
Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_property.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_property.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -40,8 +40,10 @@
"SUMMARY:Some \\ntext",
"RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=-1",
"REQUEST-STATUS:2.0;Success",
+ "GEO:-2.1;3.2",
"URI:http://www.example.com",
"TZOFFSETFROM:-0500",
+ "X-FOO;VALUE=FLOAT:-1.23",
"X-Test:Some\, text.",
"X-Test:Some:, text.",
"X-APPLE-STRUCTURED-LOCATION;VALUE=URI:geo:123.123,123.123",
@@ -130,7 +132,7 @@
def testGEOValueRoundtrip(self):
- data = "GEO:123.456,789.101"
+ data = "GEO:123.456;789.101"
prop = Property()
prop.parse(data)
self.assertEqual(str(prop), data + "\r\n")
Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_recurrence.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_recurrence.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/tests/test_recurrence.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -137,7 +137,6 @@
DateTime(2016, 1, 15, 0, 0, 0),
],
)
- print items
def testClearOnChange(self):
Modified: PyCalendar/branches/json-2/src/pycalendar/icalendar/xmldefinitions.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/icalendar/xmldefinitions.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/icalendar/xmldefinitions.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -46,6 +46,11 @@
recur_bysetpos = "bysetpos"
recur_wkst = "wkst"
+req_status = "request-status"
req_status_code = "code"
req_status_description = "description"
req_status_data = "data"
+
+geo = "geo"
+geo_longitude = "longitude"
+geo_latitude = "latitude"
Modified: PyCalendar/branches/json-2/src/pycalendar/integervalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/integervalue.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/integervalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -50,6 +50,14 @@
value.text = str(self.mValue)
+ def parseJSONValue(self, jobject):
+ self.mValue = int(jobject)
+
+
+ def writeJSONValue(self, jobject):
+ jobject.append(self.mValue)
+
+
def getValue(self):
return self.mValue
Modified: PyCalendar/branches/json-2/src/pycalendar/multivalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/multivalue.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/multivalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -93,4 +93,16 @@
for iter in self.mValues:
iter.writeXML(node, namespace)
+
+ def parseJSONValue(self, jobject):
+ for jvalue in jobject:
+ value = Value.createFromType(self.mType)
+ value.parseJSONValue(jvalue)
+ self.mValues.append(value)
+
+
+ def writeJSONValue(self, jobject):
+ for iter in self.mValues:
+ iter.writeJSONValue(jobject)
+
Value.registerType(Value.VALUETYPE_MULTIVALUE, MultiValue, None)
Modified: PyCalendar/branches/json-2/src/pycalendar/parameter.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/parameter.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/parameter.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -126,3 +126,7 @@
# TODO: need to figure out proper value types
text = XML.SubElement(param, xmlutils.makeTag(namespace, xmldefinitions.value_text))
text.text = value
+
+
+ def writeJSON(self, jobject):
+ jobject[self.getName().lower()] = self.mValues if len(self.mValues) != 1 else self.mValues[0]
Modified: PyCalendar/branches/json-2/src/pycalendar/period.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/period.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/period.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -78,19 +78,19 @@
return period
- def parse(self, data):
+ def parse(self, data, fullISO=False):
splits = data.split('/', 1)
if len(splits) == 2:
start = splits[0]
end = splits[1]
- self.mStart.parse(start)
+ self.mStart.parse(start, fullISO)
if end[0] == 'P':
self.mDuration.parse(end)
self.mUseDuration = True
self.mEnd = self.mStart + self.mDuration
else:
- self.mEnd.parse(end)
+ self.mEnd.parse(end, fullISO)
self.mUseDuration = False
self.mDuration = self.mEnd - self.mStart
else:
@@ -121,6 +121,26 @@
end.text = self.mEnd.getXMLText()
+ def parseJSON(self, jobject):
+ """
+ jCal encodes this as an array of two values. We convert back into a single "/"
+ separated string and parse as normal.
+ """
+ self.parse("%s/%s" % tuple(jobject), True)
+
+
+ def writeJSON(self, jobject):
+ """
+ jCal encodes this value as an array with two components.
+ """
+ value = [self.mStart.getXMLText(), ]
+ if self.mUseDuration:
+ value.append(self.mDuration.getText())
+ else:
+ value.append(self.mEnd.getXMLText())
+ jobject.append(value)
+
+
def getStart(self):
return self.mStart
Modified: PyCalendar/branches/json-2/src/pycalendar/periodvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/periodvalue.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/periodvalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -21,11 +21,7 @@
class PeriodValue(WrapperValue, Value):
- def __init__(self, value=None):
- self.mValue = value if value is not None else Period()
+ _wrappedClass = Period
+ _wrappedType = Value.VALUETYPE_PERIOD
-
- def getType(self):
- return Value.VALUETYPE_PERIOD
-
Value.registerType(Value.VALUETYPE_PERIOD, PeriodValue, xmldefinitions.value_period)
Modified: PyCalendar/branches/json-2/src/pycalendar/plaintextvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/plaintextvalue.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/plaintextvalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -47,6 +47,14 @@
value.text = self.mValue
+ def parseJSONValue(self, jobject):
+ self.mValue = str(jobject)
+
+
+ def writeJSONValue(self, jobject):
+ jobject.append(self.mValue)
+
+
def getValue(self):
return self.mValue
Modified: PyCalendar/branches/json-2/src/pycalendar/property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/property.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/property.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -40,7 +40,7 @@
sValueTypeMap = {}
sTypeValueMap = {}
sMultiValues = set()
- sTextVariants = set()
+ sSpecialVariants = {}
sUsesGroup = False
@@ -390,6 +390,93 @@
self.mValue.writeXML(prop, namespace)
+ @classmethod
+ def parseJSON(cls, jobject):
+ """
+ Parse a JSON property of the form:
+
+ [name, attrs, type, value1, value2, ...]
+
+ @param jobject: a JSON array
+ @type jobject: C{list}
+ """
+
+ try:
+ prop = cls()
+ prop.mName = jobject[0].upper()
+ if jobject[1]:
+ for name, value in jobject[1].items():
+ # Now add parameter value
+ name = name.upper()
+ attrvalue = Parameter(name=name, value=value)
+ prop.mParameters.setdefault(name, []).append(attrvalue)
+
+ value_type = cls.sValueTypeMap.get(jobject[2].upper(), Value.VALUETYPE_UNKNOWN)
+
+ # Get default value type from property name and insert a VALUE parameter if current value type is not default
+ default_type = cls.sDefaultValueTypeMap.get(prop.mName.upper(), Value.VALUETYPE_UNKNOWN)
+ if default_type != value_type:
+ attrvalue = Parameter(name=cls.sValue, value=value_type)
+ prop.mParameters.setdefault(cls.sValue, []).append(attrvalue)
+
+ # Check for specials
+ if prop.mName.upper() in cls.sSpecialVariants:
+ # Make sure we have the default value for the special
+ if value_type == cls.sDefaultValueTypeMap.get(prop.mName.upper(), Value.VALUETYPE_UNKNOWN):
+ value_type = cls.sSpecialVariants[prop.mName.upper()]
+
+ # Check for multivalued
+ values = jobject[3:]
+ if prop.mName.upper() in cls.sMultiValues:
+ prop.mValue = MultiValue(value_type)
+ prop.mValue.parseJSONValue(values)
+ else:
+ # Create the type
+ prop.mValue = Value.createFromType(value_type)
+ prop.mValue.parseJSONValue(values[0])
+
+ # Special post-create for some types
+ prop._postCreateValue(value_type)
+
+ return prop
+
+ except Exception:
+ raise InvalidProperty("Invalid property", jobject)
+
+
+ def writeJSON(self, jobject):
+
+ # Write it out always with value
+ self.generateValueJSON(jobject, False)
+
+
+ def writeJSONFiltered(self, jobject, filter):
+
+ # Check for property in filter and whether value is written out
+ test, novalue = filter.testPropertyValue(self.mName.upper())
+ if test:
+ self.generateValueJSON(jobject, novalue)
+
+
+ def generateValueJSON(self, jobject, novalue):
+
+ prop = [
+ self.getName().lower(),
+ {},
+ ]
+ jobject.append(prop)
+
+ # Write all parameters
+ for key in sorted(self.mParameters.keys()):
+ for attr in self.mParameters[key]:
+ if attr.getName().lower() != "value":
+ attr.writeJSON(prop[1])
+
+ # Write value
+ if self.mValue and not novalue:
+ self.mValue.writeJSON(prop)
+
+
def createValue(self, data):
# Tidy first
self.mValue = None
@@ -400,9 +487,15 @@
# Check whether custom value is set
if self.sValue in self.mParameters:
attr = self.getParameterValue(self.sValue)
- if attr != self.sText or self.mName.upper() not in self.sTextVariants:
+ if attr != self.sText:
value_type = self.sValueTypeMap.get(attr, value_type)
+ # Check for specials
+ if self.mName.upper() in self.sSpecialVariants:
+ # Make sure we have the default value for the special
+ if value_type == self.sDefaultValueTypeMap.get(self.mName.upper(), Value.VALUETYPE_UNKNOWN):
+ value_type = self.sSpecialVariants[self.mName.upper()]
+
# Check for multivalued
if self.mName.upper() in self.sMultiValues:
self.mValue = MultiValue(value_type)
@@ -464,7 +557,10 @@
# 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 self.mName.upper() in self.sSpecialVariants:
+ actual_type = default_type
+ else:
+ 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 != Value.VALUETYPE_TEXT):
Modified: PyCalendar/branches/json-2/src/pycalendar/utcoffsetvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/utcoffsetvalue.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/utcoffsetvalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -67,29 +67,24 @@
# os - StringIO object
- def generate(self, os):
+ def generate(self, os, fullISO=False):
try:
abs_value = self.mValue
- if self.mValue < 0 :
- os.write("-")
+ if abs_value < 0 :
+ sign = "-"
abs_value = -self.mValue
else:
- os.write("+")
+ sign = "+"
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))
+ s = ("%s%02d:%02d" if fullISO else "%s%02d%02d") % (sign, hours, mins,)
if (secs != 0):
- if (secs < 10):
- os.write("0")
- os.write(str(secs))
+ s = ("%s:%s" if fullISO else "%s%s") % (secs,)
+
+ os.write(s)
except:
pass
@@ -105,6 +100,18 @@
value.text = text
+ def parseJSONValue(self, jobject):
+ self.parse(str(jobject), variant="vcard")
+
+
+ def writeJSONValue(self, jobject):
+
+ os = StringIO()
+ self.generate(os, fullISO=True)
+ text = os.getvalue()
+ jobject.append(text)
+
+
def getValue(self):
return self.mValue
Modified: PyCalendar/branches/json-2/src/pycalendar/value.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/value.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/value.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -50,6 +50,7 @@
_typeMap = {}
_xmlMap = {}
+ _jsonMap = {}
def __hash__(self):
@@ -67,9 +68,10 @@
@classmethod
- def registerType(clz, type, cls, xmlNode):
+ def registerType(clz, type, cls, xmlNode, jsonNode=None):
clz._typeMap[type] = cls
clz._xmlMap[type] = xmlNode
+ clz._jsonMap[type] = xmlNode if jsonNode is None else jsonNode
@classmethod
@@ -106,5 +108,18 @@
raise NotImplementedError
+ def parseJSONValue(self, jobject):
+ raise NotImplementedError
+
+
+ def writeJSON(self, jobject):
+ jobject.append(self._jsonMap[self.getType()])
+ self.writeJSONValue(jobject)
+
+
+ def writeJSONValue(self, jobject):
+ raise NotImplementedError
+
+
def getXMLNode(self, node, namespace):
return XML.SubElement(node, xmlutils.makeTag(namespace, self._xmlMap[self.getType()]))
Modified: PyCalendar/branches/json-2/src/pycalendar/valueutils.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/valueutils.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/valueutils.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -52,20 +52,35 @@
raise NotImplementedError
+ def parseJSON(self, jobject):
+ raise NotImplementedError
+
+ def writeJSON(self, jobject):
+ raise NotImplementedError
+
+
+
class WrapperValue(object):
"""
Mix-in for Value derived classes that wrap a value-specific class.
"""
- def duplicate(self):
- return self.__class__(self.mValue.duplicate())
+ _wrappedClass = None
+ _wrappedType = None
+ def __init__(self, value=None):
+ self.mValue = value if value is not None else self._wrappedClass()
+
def getType(self):
- raise NotImplementedError
+ return self._wrappedType
+ def duplicate(self):
+ return self.__class__(self.mValue.duplicate())
+
+
def parse(self, data, variant):
self.mValue.parse(data)
@@ -79,6 +94,14 @@
value.text = self.mValue.writeXML()
+ def parseJSONValue(self, jobject):
+ self.mValue.parseJSON(jobject)
+
+
+ def writeJSONValue(self, jobject):
+ self.mValue.writeJSON(jobject)
+
+
def getValue(self):
return self.mValue
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/adr.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/adr.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/adr.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -119,6 +119,14 @@
utils.generateDoubleNestedList(os, self.mValue)
+ def parseJSON(self, jobject):
+ self.mValue = tuple(jobject)
+
+
+ def writeJSON(self, jobject):
+ jobject.append(list(self.mValue))
+
+
def getValue(self):
return self.mValue
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/adrvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/adrvalue.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/adrvalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -22,11 +22,7 @@
class AdrValue(WrapperValue, Value):
- def __init__(self, value=None):
- self.mValue = value if value is not None else Adr()
+ _wrappedClass = Adr
+ _wrappedType = Value.VALUETYPE_ADR
-
- def getType(self):
- return Value.VALUETYPE_ADR
-
Value.registerType(Value.VALUETYPE_ADR, AdrValue, None)
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/card.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/card.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/card.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -25,6 +25,7 @@
Property_PRODID, Property_UID
from pycalendar.vcard.property import Property
from pycalendar.vcard.validation import VCARD_VALUE_CHECKS
+import json
class Card(ComponentBase):
@@ -273,6 +274,20 @@
return result
+ @staticmethod
+ def parseJSONText(data):
+
+ try:
+ return Card.parseJSON(json.loads(data), None, Card(add_defaults=False))
+ except Exception:
+ return None
+
+
+ def getTextJSON(self, includeTimezones=False):
+ jobject = self.writeJSON(includeTimezones)
+ return json.dumps(jobject, indent=2, separators=(',', ':'))
+
+
def addDefaultProperties(self):
self.addProperty(Property(definitions.Property_PRODID, Card.sProdID))
self.addProperty(Property(definitions.Property_VERSION, "3.0"))
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/n.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/n.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/n.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -116,6 +116,14 @@
utils.generateDoubleNestedList(os, self.mValue)
+ def parseJSON(self, jobject):
+ self.mValue = tuple(jobject)
+
+
+ def writeJSON(self, jobject):
+ jobject.append(list(self.mValue))
+
+
def getValue(self):
return self.mValue
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/nvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/nvalue.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/nvalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -22,11 +22,7 @@
class NValue(WrapperValue, Value):
- def __init__(self, value=None):
- self.mValue = value if value is not None else N()
+ _wrappedClass = N
+ _wrappedType = Value.VALUETYPE_N
-
- def getType(self):
- return Value.VALUETYPE_N
-
Value.registerType(Value.VALUETYPE_N, NValue, None)
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/orgvalue.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/orgvalue.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/orgvalue.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -44,6 +44,14 @@
utils.generateTextList(os, self.mValue, ';')
+ def parseJSONValue(self, jobject):
+ self.mValue = tuple(jobject)
+
+
+ def writeJSONValue(self, jobject):
+ jobject.append(list(self.mValue))
+
+
def getValue(self):
return self.mValue
Modified: PyCalendar/branches/json-2/src/pycalendar/vcard/property.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/vcard/property.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/vcard/property.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -47,13 +47,13 @@
# 2426 Section 3.1
definitions.Property_FN : Value.VALUETYPE_TEXT,
- definitions.Property_N : Value.VALUETYPE_N,
+ definitions.Property_N : Value.VALUETYPE_TEXT,
definitions.Property_NICKNAME : Value.VALUETYPE_TEXT,
definitions.Property_PHOTO : Value.VALUETYPE_BINARY,
definitions.Property_BDAY : Value.VALUETYPE_DATE,
# 2426 Section 3.2
- definitions.Property_ADR : Value.VALUETYPE_ADR,
+ definitions.Property_ADR : Value.VALUETYPE_TEXT,
definitions.Property_LABEL : Value.VALUETYPE_TEXT,
# 2426 Section 3.3
@@ -63,14 +63,14 @@
# 2426 Section 3.4
definitions.Property_TZ : Value.VALUETYPE_UTC_OFFSET,
- definitions.Property_GEO : Value.VALUETYPE_GEO,
+ definitions.Property_GEO : Value.VALUETYPE_FLOAT,
# 2426 Section 3.5
definitions.Property_TITLE : Value.VALUETYPE_TEXT,
definitions.Property_ROLE : Value.VALUETYPE_TEXT,
definitions.Property_LOGO : Value.VALUETYPE_BINARY,
definitions.Property_AGENT : Value.VALUETYPE_VCARD,
- definitions.Property_ORG : Value.VALUETYPE_ORG,
+ definitions.Property_ORG : Value.VALUETYPE_TEXT,
# 2426 Section 3.6
definitions.Property_CATEGORIES : Value.VALUETYPE_TEXT,
@@ -125,11 +125,12 @@
definitions.Property_CATEGORIES,
))
- sTextVariants = set((
- definitions.Property_ADR,
- definitions.Property_N,
- definitions.Property_ORG,
- ))
+ sSpecialVariants = {
+ definitions.Property_ADR : Value.VALUETYPE_ADR,
+ definitions.Property_GEO : Value.VALUETYPE_GEO,
+ definitions.Property_N: Value.VALUETYPE_N,
+ definitions.Property_ORG: Value.VALUETYPE_ORG,
+ }
sUsesGroup = True
Modified: PyCalendar/branches/json-2/src/pycalendar/xmldefinitions.py
===================================================================
--- PyCalendar/branches/json-2/src/pycalendar/xmldefinitions.py 2013-08-16 18:14:50 UTC (rev 11613)
+++ PyCalendar/branches/json-2/src/pycalendar/xmldefinitions.py 2013-08-19 01:31:02 UTC (rev 11614)
@@ -28,6 +28,7 @@
value_date = "date"
value_date_time = "date-time"
value_duration = "duration"
+value_float = "float"
value_integer = "integer"
value_period = "period"
value_text = "text"
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130818/1471c9ee/attachment-0001.html>
More information about the calendarserver-changes
mailing list