diff --git a/contact/__init__.py b/contact/__init__.py
index 026f644..33c188a 100644
--- a/contact/__init__.py
+++ b/contact/__init__.py
@@ -18,20 +18,59 @@
"""
A set of simple application classes for contact management to be used
-as an example for the cybertools.reporter package.
+as an example for some of the cybertools packages.
$Id$
"""
from zope.component import adapts
from zope.interface import implements
+from cybertools.contact.interfaces import IPerson
+from datetime import date
class Person(object):
+
+ implements(IPerson)
def __init__(self, firstName, lastName, birthDate):
self.firstName = firstName
self.lastName = lastName
self.birthDate = birthDate
+ self.moreFirstNames = []
+ self.personalAddress = 'mrs' # or 'mr', 'ms', None (unknown)
+ self.academicTitle = None
+ self.communicationInfos = []
+ self.addresses = {} # keys: 'standard', ...?
+ self.affiliations = {} # keys: 'employed', ...?
+
+ @property
+ def age(self):
+ return (date.today() - self.birthDate).days/365.25
+
+
+class Address(object):
+
+ def __init__(self, title, lines, street, zipcode, city, country):
+ self.title = title
+ self.lines = lines # a sequence of address lines
+ self.street = street
+ self.zipcode = zipcode
+ self.city = city
+ self.country = country # 'de', 'at', 'us', ...
+
+
+class Institution(object):
+
+ def __init__(self, title):
+ self.title = title
+ self.addresses = {}
+
+
+class CommunicationInfo(object):
+
+ def __init__(self, commType, qualifier, address):
+ self.commType = commType # e.g. 'email', 'phone', ...
+ self.qualifier = qualifier # e.g. 'private', or institution
+ self.address = address # the real address or number
-
\ No newline at end of file
diff --git a/contact/interfaces.py b/contact/interfaces.py
index 12f21d2..62daf2b 100644
--- a/contact/interfaces.py
+++ b/contact/interfaces.py
@@ -23,4 +23,10 @@ as an example for some of the cybertools packages.
$Id$
"""
-from zope.interface import Interface
+from zope.interface import Interface, Attribute
+
+class IPerson(Interface):
+ """ Just a person...
+ """
+
+ age = Attribute('A float representing the age in years. Read-only.')
diff --git a/reporter/README.txt b/reporter/README.txt
index ba9e910..ea5d659 100644
--- a/reporter/README.txt
+++ b/reporter/README.txt
@@ -20,18 +20,20 @@ then provide a listing of persons...
>>> from cybertools.reporter.example.interfaces import IContactsDataSource
>>> from cybertools.reporter.example.contact import Contacts
- >>> import time
- >>> format = '%Y-%m-%d'
+ >>> from datetime import date
>>> pdata = ((u'John', u'Smith', '1956-08-01'),
... (u'David', u'Waters', '1972-12-24'),
... (u'Carla', u'Myers', '1981-10-11'))
- >>> persons = DataSource([Person(f, s, time.strptime(b, format))
+ >>> persons = DataSource([Person(f, s, date(*[int(d) for d in b.split('-')]))
... for f, s, b in pdata])
>>> directlyProvides(persons, IContactsDataSource)
>>> ztapi.provideAdapter(IContactsDataSource, IResultSet, Contacts)
>>> rset = IResultSet(persons)
+ >>> len(rset)
+ 3
+
For the browser presentation we can also use a browser view providing
the result set with extended attributes:
diff --git a/reporter/example/contact.py b/reporter/example/contact.py
index bcba199..963c8fe 100644
--- a/reporter/example/contact.py
+++ b/reporter/example/contact.py
@@ -23,12 +23,52 @@ cybertools.contact package
$Id$
"""
+# TODO: move the generic stuff to cybertools.reporter.result
+
from zope.component import adapts
from zope.interface import implements
-from cybertools.reporter.interfaces import IResultSet
+from cybertools.reporter.interfaces import IResultSet, IRow, ICell
from cybertools.reporter.example.interfaces import IContactsDataSource
+class Cell(object):
+
+ implements(ICell)
+
+ def __init__(self, field, value, row):
+ self.field = field
+ self.value = value
+ self.row = row
+
+ @property
+ def text(self):
+ return value
+
+ @property
+ def token(self):
+ return value
+
+ def sortKey(self):
+ return value
+
+
+class Row(object):
+
+ implements(IRow)
+
+ def __init__(self, context, resultSet):
+ self.context = context # a single object (in this case)
+ self.resultSet = resultSet
+
+ @property
+ def cells(self):
+ schema = self.resultSet.schema
+ if schema is None:
+ return {}
+ return dict([(f.__name__, getattr(self.context, f.__name__))
+ for f in schema.fields])
+
+
class Contacts(object):
implements(IResultSet)
@@ -37,4 +77,14 @@ class Contacts(object):
def __init__(self, context):
self.context = context
-
\ No newline at end of file
+ _schema = None
+ def setSchema(self, schema): self._schema = schema
+ def getSchema(self): return self._schema
+ schema = property(getSchema, setSchema)
+
+ @property
+ def rows(self):
+ return [Row(o, self) for o in iter(self.context)]
+
+ def __len__(self):
+ return len(self.rows)
diff --git a/typology/README.txt b/typology/README.txt
new file mode 100644
index 0000000..19c44aa
--- /dev/null
+++ b/typology/README.txt
@@ -0,0 +1,57 @@
+Quickstart Instructions
+=======================
+
+ ($Id$)
+
+ >>> from zope.app import zapi
+ >>> from zope.app.testing import ztapi
+ >>> from zope.interface import directlyProvides
+
+A Basic API for Dynamic Typing
+==============================
+
+ >>> from cybertools.typology.interfaces import IType, ITypeManager
+
+Let's start with the Person class from the cybertools.contact package -
+we will then apply dynamic typing to Person objects:
+
+ >>> from cybertools.contact.interfaces import IPerson
+ >>> from cybertools.contact import Person
+
+ >>> from datetime import date
+ >>> pdata = ((u'John', u'Smith', '1956-08-01'),
+ ... (u'David', u'Waters', '1972-12-24'),
+ ... (u'Carla', u'Myers', '1999-10-11'))
+ >>> persons = [Person(f, s, date(*[int(d) for d in b.split('-')]))
+ ... for f, s, b in pdata]
+
+Now we want to express that any person younger than 18 years is a
+child, and from 18 years on a person is an adult. (Note that this test
+will only work until November 2017 ;-))
+
+The example package gives us a class for this type that we use as an
+adapter to IPerson:
+
+ >>> from cybertools.typology.example.person import BasicAgeGroup
+ >>> ztapi.provideAdapter(IPerson, IType, BasicAgeGroup)
+ >>> john_type = IType(persons[0])
+ >>> david_type = IType(persons[1])
+ >>> carla_type = IType(persons[2])
+
+We can now look what the type is telling us about the persons:
+
+ >>> john_type.title
+ u'Adult'
+ >>> john_type.token
+ 'contact.person.agetype.adult'
+ >>> david_type.token
+ 'contact.person.agetype.adult'
+ >>> carla_type.token
+ 'contact.person.agetype.child'
+
+Usually types are equal if they have the same token:
+
+ >>> john_type == david_type
+ True
+ >>> john_type == carla_type
+ False
diff --git a/typology/__init__.py b/typology/__init__.py
new file mode 100644
index 0000000..4bc90fb
--- /dev/null
+++ b/typology/__init__.py
@@ -0,0 +1,4 @@
+"""
+$Id$
+"""
+
diff --git a/typology/browser/__init__.py b/typology/browser/__init__.py
new file mode 100644
index 0000000..4bc90fb
--- /dev/null
+++ b/typology/browser/__init__.py
@@ -0,0 +1,4 @@
+"""
+$Id$
+"""
+
diff --git a/typology/configure.zcml b/typology/configure.zcml
new file mode 100644
index 0000000..db5642b
--- /dev/null
+++ b/typology/configure.zcml
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/typology/cybertools.typology-configure.zcml b/typology/cybertools.typology-configure.zcml
new file mode 100644
index 0000000..baaf9be
--- /dev/null
+++ b/typology/cybertools.typology-configure.zcml
@@ -0,0 +1 @@
+
diff --git a/typology/example/__init__.py b/typology/example/__init__.py
new file mode 100644
index 0000000..4bc90fb
--- /dev/null
+++ b/typology/example/__init__.py
@@ -0,0 +1,4 @@
+"""
+$Id$
+"""
+
diff --git a/typology/example/person.py b/typology/example/person.py
new file mode 100644
index 0000000..b96c409
--- /dev/null
+++ b/typology/example/person.py
@@ -0,0 +1,60 @@
+#
+# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+Example classes for the cybertools.reporter package. These use the
+cybertools.contact package
+
+$Id$
+"""
+
+# TODO: move generic stuff to type.Type class
+
+from zope.component import adapts
+from zope.interface import implements
+from cybertools.contact.interfaces import IPerson
+from cybertools.typology.interfaces import IType
+
+class BasicAgeGroup(object):
+
+ implements(IType)
+ adapts(IPerson)
+
+ def __init__(self, context):
+ self.context = context
+
+ def __eq__(self, other):
+ return self.token == other.token
+
+ # IType attributes
+
+ @property
+ def title(self):
+ return self.isChild() and u'Child' or u'Adult'
+
+ @property
+ def token(self): return 'contact.person.agetype.' + str(self.title.lower())
+
+ @property
+ def tokenForSearch(self): return self.token
+
+ # helper methods
+
+ def isChild(self):
+ return self.context.age < 18.0
+
diff --git a/typology/interfaces.py b/typology/interfaces.py
new file mode 100644
index 0000000..d2fe8b6
--- /dev/null
+++ b/typology/interfaces.py
@@ -0,0 +1,66 @@
+#
+# Copyright (c) 2006 Helmut Merz helmutm@cy55.de
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+interface definitions for the typology package.
+
+$Id$
+"""
+
+from zope.app.container.interfaces import IContainer
+from zope import schema
+from zope.configuration.fields import GlobalObject
+from zope.interface import Interface, Attribute
+
+
+class IType(Interface):
+ """ Associated with an object (typically as an adapter) giving
+ information about the object's type.
+ """
+
+ title = schema.TextLine(title=u'Title',
+ description=u'A readable representation',
+ required=True)
+ token = schema.ASCIILine(title=u'Token',
+ description=u'A representation used for identifying a type '
+ 'temporarily, e.g. on forms',
+ required=True)
+ tokenForSearch = schema.ASCIILine(title=u'Token for Search',
+ description=u'A fairly unique token that may be used '
+ 'e.g. for identifying types via a catalog index')
+ interfaceToProvide = GlobalObject(title=u'Interface to Provide',
+ description=u'An (optional) interface that objects of this '
+ 'type will provide')
+ factory = GlobalObject(title=u'Factory',
+ description=u'A factory (or class) that may be used for '
+ 'creating an object of this type')
+ defaultContainer = schema.Object(IContainer,
+ title=u'Default Container',
+ description=u'Where objects of this type will be created in '
+ 'when no explicit container is given')
+ typeProvider = schema.Object(Interface,
+ title=u'Type Provider',
+ description=u'A usually long-living object that corresponds '
+ 'to the type. Note that this object need not '
+ 'provide the IType interface itself')
+
+
+class ITypeManager(Interface):
+ """ A utility or utility-like object (e.g. a container) that may
+ manage (store, retrieve, assign) types.
+ """
diff --git a/typology/tests.py b/typology/tests.py
new file mode 100755
index 0000000..df3faca
--- /dev/null
+++ b/typology/tests.py
@@ -0,0 +1,27 @@
+# $Id$
+
+import unittest
+from zope.testing.doctestunit import DocFileSuite
+from zope.app.testing import ztapi
+from zope.interface.verify import verifyClass
+from zope.interface import implements
+from zope.app import zapi
+
+from cybertools.typology.interfaces import IType, ITypeManager
+
+
+class TestTypology(unittest.TestCase):
+ "Basic tests for the typology package."
+
+ def testInterfaces(self):
+ pass
+
+
+def test_suite():
+ return unittest.TestSuite((
+ unittest.makeSuite(TestTypology),
+ DocFileSuite('README.txt'),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')