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')