diff --git a/composer/schema/field.py b/composer/schema/field.py index 379e51c..86d0dee 100644 --- a/composer/schema/field.py +++ b/composer/schema/field.py @@ -33,7 +33,7 @@ from zope.i18n.locales import locales from cybertools.composer.base import Component from cybertools.composer.schema.interfaces import IField, IFieldInstance -from cybertools.composer.schema.interfaces import fieldTypes +from cybertools.composer.schema.interfaces import fieldTypes, undefined from cybertools.composer.schema.schema import formErrors from cybertools.util.format import toStr, toUnicode @@ -61,7 +61,8 @@ class Field(Component): def __init__(self, name, title=None, fieldType='textline', **kw): assert name self.__name__ = name - title = title or u'' + #title = title or u'' + title = title or name self.fieldType = fieldType super(Field, self).__init__(title, __name__=name, **kw) self.title = title @@ -127,6 +128,7 @@ class FieldInstance(object): adapts(IField) clientInstance = None + value = undefined def __init__(self, context): self.context = context diff --git a/composer/schema/instance.py b/composer/schema/instance.py index 8caf2cd..812aa34 100644 --- a/composer/schema/instance.py +++ b/composer/schema/instance.py @@ -39,6 +39,7 @@ class Instance(BaseInstance): aspect = 'schema.editor.default' template = None + view = None def applyTemplate(self, *args, **kw): #result = dict(__name__=self.context.__name__) diff --git a/composer/schema/interfaces.py b/composer/schema/interfaces.py index 9fd64c5..24f3499 100644 --- a/composer/schema/interfaces.py +++ b/composer/schema/interfaces.py @@ -192,6 +192,8 @@ class IField(IComponent): 'field instance.') +undefined = object() # A marker for a field instance value not set. + class IFieldInstance(Interface): """ An adapter for checking and converting data values coming from or being displayed on an external system (like a browser form). @@ -201,18 +203,28 @@ class IFieldInstance(Interface): name = Attribute('Field name.') change = Attribute('A tuple ``(oldValue, newValue)`` or None.') errors = Attribute('A sequence of error infos.') - severity = Attribute("An integer giving a state or error " - "code, 0 meaning 'OK'.") + severity = Attribute('An integer giving a state or error ' + 'code, 0 meaning OK.') + clientInstance = Attribute('An optional adapter to a client object that ' + 'provides or receives data processed by this field instance.') + value = Attribute ('May contain the current value of the field ' + 'for later reuse. Default is ``undefined``. ' + 'If the ``change`` attribute is set ``value`` should ' + 'be equal to ``change[1]``.') def getRawValue(data, key, default=None): """ Extract a raw value for the field from the data given using the key; if no corresponding value is found return - the default. + the default. The value returned may then be turned + byt self.unmarshall() to the real (internal) value. """ def marshall(value): """ Return a string (possibly unicode) representation of the - value given that may be used for editing. + value given that may be used for editing. In case of complex + or structured fields (list, mapping, object fields) the return + value may also be a structured object (typically a list or + mapping) built up form string (unicode) values. """ def display(value): @@ -220,9 +232,9 @@ class IFieldInstance(Interface): value given that may be used for presentation. """ - def unmarshall(rawValue): + def unmarshall(inputValue): """ Return the internal (real) value corresponding to the - raw value given. + input (external, raw) value given. """ def validate(value, data=None): diff --git a/reporter/README.txt b/reporter/README.txt index 5f5bc12..f63074e 100644 --- a/reporter/README.txt +++ b/reporter/README.txt @@ -7,15 +7,12 @@ A Basic API for Reports and Listings TO DO... >>> from zope import component - >>> from zope.interface import directlyProvides Listings ======== >>> from cybertools.reporter.data import DataSource - >>> from cybertools.reporter.resultset import ResultSet - >>> from cybertools.reporter.interfaces import IResultSet Let's start with the Person class from the cybertools.organize package - we will then provide a listing of persons... @@ -29,38 +26,42 @@ then provide a listing of persons... >>> persons = DataSource([Person(f, s, date(*[int(d) for d in b.split('-')])) ... for f, s, b in pdata]) + >>> from cybertools.reporter.resultset import ResultSet, ContentRow + >>> from cybertools.reporter.interfaces import IResultSet, IRow >>> component.provideAdapter(ResultSet) - >>> rset = IResultSet(persons) - - >>> len(list(rset.rows)) - 3 - -As we have not yet provided a schema for the result set the rows are -empty. - - >>> r1 = rset.rows.next() - >>> list(r1.cells) - [] - -So let's assign a schema to the result set. + >>> component.provideAdapter(ContentRow, provides=IRow) >>> from cybertools.composer.schema.schema import Schema >>> from cybertools.composer.schema.field import Field - >>> rset.schema = Schema(Field(u'firstName'), Field(u'lastName'), Field(u'birthDate')) - >>> r1 = rset.rows.next() - >>> [c.text for c in r1.cells] - [u'Smith', u'John', u'1956-08-01'] + >>> from cybertools.composer.schema.field import FieldInstance, DateFieldInstance + >>> component.provideAdapter(FieldInstance) + >>> component.provideAdapter(DateFieldInstance, name='date') + + >>> rset = IResultSet(persons) + >>> rset.schema = Schema(Field(u'firstName'), Field(u'lastName'), + ... Field(u'birthDate', fieldType='date')) + + >>> rows = list(rset.getRows()) + >>> len(rows) + 3 + + >>> for r in rows: + ... print r.applyTemplate() + {u'lastName': u'John', u'birthDate': '1956-08-01', u'firstName': u'Smith'} + {u'lastName': u'David', u'birthDate': '1972-12-24', u'firstName': u'Waters'} + {u'lastName': u'Carla', u'birthDate': '1981-10-11', u'firstName': u'Myers'} For the browser presentation we can also use a browser view providing the result set with extended attributes: - >>> #rsView = component.getMultiAdapter((context, TestRequest()), IBrowserView) + >>> #rsView = component.getMultiAdapter((rset, TestRequest()), IBrowserView) + The reporter package also includes facilities for sorting the rows in a result set and splitting a result into batches. Sorting -------- +======= Batching diff --git a/reporter/configure.zcml b/reporter/configure.zcml index f820e5f..a6f9a1a 100644 --- a/reporter/configure.zcml +++ b/reporter/configure.zcml @@ -5,6 +5,11 @@ xmlns:browser="http://namespaces.zope.org/browser" i18n_domain="zope"> + + diff --git a/reporter/interfaces.py b/reporter/interfaces.py index 7c9db16..e20a647 100644 --- a/reporter/interfaces.py +++ b/reporter/interfaces.py @@ -55,6 +55,7 @@ class IRow(Interface): class ICell(Interface): """ A single cell of a listing or table. """ + # TODO: replace Cell by FieldInstance field = Attribute(u'The schema field the cell belongs to.') diff --git a/reporter/resultset.py b/reporter/resultset.py index 573ee6c..4e10586 100644 --- a/reporter/resultset.py +++ b/reporter/resultset.py @@ -25,15 +25,18 @@ $Id$ # TODO: move the generic stuff to cybertools.reporter.result +from zope.cachedescriptors.property import Lazy from zope.component import adapts -from zope.interface import implements +from zope.interface import Interface, implements -from cybertools.composer.schema.schema import Schema +from cybertools.composer.schema import Schema +from cybertools.composer.schema.instance import Instance from cybertools.reporter.interfaces import IDataSource from cybertools.reporter.interfaces import IResultSet, IRow, ICell class Cell(object): + # TODO: replace Cell by FieldInstance implements(ICell) @@ -61,7 +64,7 @@ class Cell(object): url = urlTitle = u'' -class Row(object): +class Row(Instance): implements(IRow) @@ -69,17 +72,33 @@ class Row(object): self.context = context self.resultSet = resultSet - @property + @Lazy def schema(self): return self.resultSet.schema + @Lazy + def fields(self): + return self.schema.fields + @property def cells(self): - for f in self.resultSet.schema.fields: + for f in self.schema.fields: rf = f.renderFactory or Cell yield rf(f, getattr(self.context, f.name), self) +class ContentRow(Instance): + """ A row adapter for standard content objects. + """ + + implements(IRow) + adapts(Interface) + + @Lazy + def fields(self): + return self.template.fields + + class ResultSet(object): implements(IResultSet) @@ -96,3 +115,9 @@ class ResultSet(object): for o in iter(self.context): yield Row(o, self) + def getRows(self): + for o in iter(self.context): + row = IRow(o) + row.resultSet = self + row.template = self.schema + yield row