3 commits - pykolab/xml tests/unit

Thomas Brüderli bruederli at kolabsys.com
Thu Mar 26 10:36:29 CET 2015


 pykolab/xml/__init__.py     |    9 ++
 pykolab/xml/event.py        |   16 -----
 pykolab/xml/note.py         |  136 ++++++++++++++++++++++++++++++++++++++++++++
 pykolab/xml/utils.py        |   18 +++++
 tests/unit/test-018-note.py |   95 ++++++++++++++++++++++++++++++
 5 files changed, 260 insertions(+), 14 deletions(-)

New commits:
commit 85974d137dd2b37a7d78bffc1d8980cb775f1386
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Mar 26 10:35:51 2015 +0100

    Add wrapper class for kolabformat.Note (#4908)

diff --git a/pykolab/xml/__init__.py b/pykolab/xml/__init__.py
index 00edf06..20e4763 100644
--- a/pykolab/xml/__init__.py
+++ b/pykolab/xml/__init__.py
@@ -20,6 +20,11 @@ from todo import todo_from_ical
 from todo import todo_from_string
 from todo import todo_from_message
 
+from note import Note
+from note import NoteIntegrityError
+from note import note_from_string
+from note import note_from_message
+
 from utils import property_label
 from utils import property_to_string
 from utils import compute_diff
@@ -31,6 +36,7 @@ __all__ = [
         "ContactReference",
         "Event",
         "Todo",
+        "Note",
         "RecurrenceRule",
         "event_from_ical",
         "event_from_string",
@@ -38,6 +44,8 @@ __all__ = [
         "todo_from_ical",
         "todo_from_string",
         "todo_from_message",
+        "note_from_string",
+        "note_from_message",
         "property_label",
         "property_to_string",
         "compute_diff",
@@ -49,6 +57,7 @@ errors = [
         "InvalidEventDateError",
         "InvalidAttendeeParticipantStatusError",
         "TodoIntegrityError",
+        "NoteIntegrityError",
     ]
 
 __all__.extend(errors)
diff --git a/pykolab/xml/note.py b/pykolab/xml/note.py
new file mode 100644
index 0000000..e46c41c
--- /dev/null
+++ b/pykolab/xml/note.py
@@ -0,0 +1,136 @@
+import pytz
+import datetime
+import kolabformat
+from pykolab.xml import utils as xmlutils
+from pykolab.xml.utils import ustr
+
+def note_from_string(string):
+    _xml = kolabformat.readNote(string, False)
+    return Note(_xml)
+
+def note_from_message(message):
+    note = None
+    if message.is_multipart():
+        for part in message.walk():
+            if part.get_content_type() == "application/vnd.kolab+xml":
+                payload = part.get_payload(decode=True)
+                note = note_from_string(payload)
+
+            # append attachment parts to Note object
+            elif note and part.has_key('Content-ID'):
+                note._attachment_parts.append(part)
+
+    return todo
+
+
+class Note(kolabformat.Note):
+    type = 'note'
+
+    classification_map = {
+        'PUBLIC': kolabformat.ClassPublic,
+        'PRIVATE': kolabformat.ClassPrivate,
+        'CONFIDENTIAL': kolabformat.ClassConfidential,
+    }
+
+    properties_map = {
+        'uid':               'get_uid',
+        'summary':           'summary',
+        'description':       'description',
+        'created':           'get_created',
+        'lastmodified-date': 'get_lastmodified',
+        'classification':    'get_classification',
+        'categories':        'categories',
+        'color':             'color',
+    }
+
+    def __init__(self, *args, **kw):
+        self._attachment_parts = []
+        kolabformat.Note.__init__(self, *args, **kw)
+
+    def get_uid(self):
+        uid = self.uid()
+        if not uid == '':
+            return uid
+        else:
+            self.__str__()
+            return kolabformat.getSerializedUID()
+
+    def get_created(self):
+        try:
+            return xmlutils.from_cdatetime(self.created(), True)
+        except ValueError:
+            return datetime.datetime.now()
+
+    def get_lastmodified(self):
+        try:
+            _datetime = self.lastModified()
+            if _datetime == None or not _datetime.isValid():
+                self.__str__()
+        except:
+            return datetime.datetime.now(pytz.utc)
+
+        return xmlutils.from_cdatetime(self.lastModified(), True)
+
+    def set_summary(self, summary):
+        self.setSummary(ustr(summary))
+
+    def set_description(self, description):
+        self.setDescription(ustr(description))
+
+    def get_classification(self, translated=True):
+        _class = self.classification()
+        if translated:
+            return self._translate_value(_class, self.classification_map)
+        return _class
+
+    def set_classification(self, classification):
+        if classification in self.classification_map.keys():
+            self.setClassification(self.classification_map[classification])
+        elif classification in self.classification_map.values():
+            self.setClassification(status)
+        else:
+            raise ValueError, _("Invalid classification %r") % (classification)
+
+    def add_category(self, category):
+        _categories = self.categories()
+        _categories.append(ustr(category))
+        self.setCategories(_categories)
+
+    def _translate_value(self, val, map):
+        name_map = dict([(v, k) for (k, v) in map.iteritems()])
+        return name_map[val] if name_map.has_key(val) else 'UNKNOWN'
+
+    def to_dict(self):
+        if not self.isValid():
+            return None
+
+        data = dict()
+
+        for p, getter in self.properties_map.iteritems():
+            val = None
+            if hasattr(self, getter):
+                val = getattr(self, getter)()
+            if isinstance(val, kolabformat.cDateTime):
+                val = xmlutils.from_cdatetime(val, True)
+            elif isinstance(val, kolabformat.vectori):
+                val = [int(x) for x in val]
+            elif isinstance(val, kolabformat.vectors):
+                val = [str(x) for x in val]
+
+            if val is not None:
+                data[p] = val
+
+        return data
+
+    def __str__(self):
+        xml = kolabformat.writeNote(self)
+        error = kolabformat.error()
+
+        if error == None or not error:
+            return xml
+        else:
+            raise NoteIntegrityError, kolabformat.errorMessage()
+
+class NoteIntegrityError(Exception):
+    def __init__(self, message):
+        Exception.__init__(self, message)
diff --git a/tests/unit/test-018-note.py b/tests/unit/test-018-note.py
new file mode 100644
index 0000000..c4ba764
--- /dev/null
+++ b/tests/unit/test-018-note.py
@@ -0,0 +1,95 @@
+import datetime
+import pytz
+import unittest
+import kolabformat
+
+from pykolab.xml import Note
+from pykolab.xml import NoteIntegrityError
+from pykolab.xml import note_from_string
+
+xml_note = """
+<note xmlns="http://kolab.org" version="3.0">
+  <uid>d407f007-cb52-42cb-8e06-67f6132d718f</uid>
+  <prodid>Roundcube-libkolab-1.1 Libkolabxml-1.1</prodid>
+  <creation-date>2015-03-26T08:12:37Z</creation-date>
+  <last-modification-date>2015-03-26T08:12:37Z</last-modification-date>
+  <categories>One</categories>
+  <categories>Two</categories>
+  <classification>PUBLIC</classification>
+  <summary>Kolab Note</summary>
+  <description><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<html><head><meta name="qrichtext" content="1" /><meta http-equiv="Content-Type" /></head><body>
+<p>This is a HTML note</p>
+</body></html></description>
+  <color/>
+</note>
+"""
+
+class TestNoteXML(unittest.TestCase):
+    def assertIsInstance(self, _value, _type):
+        if hasattr(unittest.TestCase, 'assertIsInstance'):
+            return unittest.TestCase.assertIsInstance(self, _value, _type)
+        else:
+            if (type(_value)) == _type:
+                return True
+            else:
+                raise AssertionError, "%s != %s" % (type(_value), _type)
+
+    def test_001_minimal(self):
+        note = Note()
+        note.set_summary("test")
+        self.assertEqual(note.summary(), "test")
+        self.assertIsInstance(note.__str__(), str)
+
+    def test_002_full(self):
+        note = Note()
+        note.set_summary("test")
+        note.set_description("Description")
+        note.set_classification("CONFIDENTIAL")
+        note.add_category("Foo")
+        note.add_category("Bar")
+        # print str(note)
+
+        self.assertEqual(len(note.get_uid()), 36)
+        self.assertEqual(note.summary(), "test")
+        self.assertEqual(note.description(), "Description")
+        self.assertEqual(note.get_classification(), "CONFIDENTIAL")
+        self.assertEqual(note.get_classification(False), kolabformat.ClassConfidential)
+        self.assertEqual(len(note.categories()), 2)
+
+    def test_010_load_from_xml(self):
+        note = note_from_string(xml_note)
+        self.assertEqual(note.get_uid(), "d407f007-cb52-42cb-8e06-67f6132d718f")
+        self.assertEqual(note.summary(), "Kolab Note")
+        self.assertIsInstance(note.get_created(), datetime.datetime)
+        self.assertEqual(note.get_created().tzinfo, pytz.utc)
+        self.assertIsInstance(note.get_lastmodified(), datetime.datetime)
+        self.assertEqual(note.get_lastmodified().tzinfo, pytz.utc)
+
+    def test_011_to_xml(self):
+        note = Note()
+        note.setClassification(-1)
+        self.assertRaises(NoteIntegrityError, note.__str__)
+
+        # minimal
+        note = Note()
+        xml = str(note)
+        self.assertTrue('<summary/>' in xml)
+        self.assertTrue('<description/>' in xml)
+
+    def test_020_to_dict(self):
+        data = note_from_string(xml_note).to_dict()
+
+        self.assertIsInstance(data, dict)
+        self.assertTrue(data.has_key('uid'))
+        self.assertIsInstance(data.get('created', None), datetime.datetime)
+        self.assertIsInstance(data.get('lastmodified-date', None), datetime.datetime)
+        self.assertEqual(data.get('summary', None), "Kolab Note")
+        self.assertEqual(data.get('classification', None), 'PUBLIC')
+        self.assertIsInstance(data.get('categories', None), list)
+        self.assertEqual(len(data.get('categories', None)), 2)
+        self.assertTrue('<p>This is a HTML note</p>' in data.get('description', None))
+
+
+if __name__ == '__main__':
+    unittest.main()
\ No newline at end of file


commit 035978ff20b1336ec2db06e3f73a4449ea9ddb72
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Mar 26 10:22:43 2015 +0100

    Move ustr() to xml utilities module

diff --git a/pykolab/xml/utils.py b/pykolab/xml/utils.py
index 261e33c..9d80f9c 100644
--- a/pykolab/xml/utils.py
+++ b/pykolab/xml/utils.py
@@ -127,6 +127,24 @@ def dates_equal(a, b):
     return type(a) == type(b) and a.strftime(date_format) == b.strftime(date_format)
 
 
+def ustr(s):
+    """
+        Force the given (unicode) string into UTF-8 encoding
+    """
+    if not isinstance(s, unicode):
+        for cs in ['utf-8','latin-1']:
+            try:
+                s = unicode(s, cs)
+                break
+            except:
+                pass
+
+    if isinstance(s, unicode):
+        return s.encode('utf-8')
+
+    return s
+
+
 property_labels = {
     "name":        N_("Name"),
     "summary":     N_("Summary"),


commit 7566d1439506543bdf885a3350e4363dde109fdd
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Mar 26 10:21:50 2015 +0100

    Fix varname and avoid throwing an execption on every call

diff --git a/pykolab/xml/event.py b/pykolab/xml/event.py
index 5080e43..e2e5440 100644
--- a/pykolab/xml/event.py
+++ b/pykolab/xml/event.py
@@ -12,6 +12,7 @@ from pykolab import constants
 from pykolab import utils
 from pykolab.xml import utils as xmlutils
 from pykolab.xml import participant_status_label
+from pykolab.xml.utils import ustr
 from pykolab.translate import _
 
 from os import path
@@ -21,19 +22,6 @@ from recurrence_rule import RecurrenceRule
 
 log = pykolab.getLogger('pykolab.xml_event')
 
-def ustr(s):
-    if not isinstance(s, unicode):
-        for cs in ['utf-8','latin-1']:
-            try:
-                s = unicode(s, cs)
-                break
-            except:
-                pass
-
-    if isinstance(s, unicode):
-        return s.encode('utf-8')
-
-    return s
 
 def event_from_ical(ical, string=None):
     return Event(from_ical=ical, from_string=string)
@@ -680,7 +668,7 @@ class Event(object):
     def get_lastmodified(self):
         try:
             _datetime = self.event.lastModified()
-            if retval == None or retval == "":
+            if _datetime == None or not _datetime.isValid():
                 self.__str__()
         except:
             self.__str__()




More information about the commits mailing list