From 4c2f79d929d4129a3490907221e667508b84619c Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 23 Dec 2011 10:30:06 +0100 Subject: [PATCH] add keytable stuff to grid-like field definitions --- composer/schema/README.txt | 57 +++++++++++++++++++- composer/schema/grid/field.py | 83 +++++++++++++++++++++++------- composer/schema/grid/interfaces.py | 13 +++-- 3 files changed, 130 insertions(+), 23 deletions(-) diff --git a/composer/schema/README.txt b/composer/schema/README.txt index 622f0bf..a5579c6 100644 --- a/composer/schema/README.txt +++ b/composer/schema/README.txt @@ -2,8 +2,6 @@ Schema and Field Management =========================== - ($Id$) - >>> from cybertools.composer.schema import Schema >>> from cybertools.composer.schema import Field @@ -193,3 +191,58 @@ Macros / renderers [u'field', u'field_spacer', u'fields', u'form', u'input_checkbox', u'input_date', u'input_dropdown', u'input_fileupload', u'input_html', u'input_list', u'input_password', u'input_textarea', u'input_textline'] + + +Special Field Types +=================== + +Grids, Records, Key Tables +-------------------------- + + >>> from cybertools.composer.schema.grid.field import KeyTableFieldInstance + + >>> ktfield = Field('data') + >>> ktfield.column_types = [zope.schema.Text(__name__='key', title=u'Key',), + ... zope.schema.Text(__name__='value', title=u'Value')] + + >>> ktfi = KeyTableFieldInstance(ktfield) + >>> ktfi.unmarshall([dict(key='0001', value='First row')]) + {u'0001': [u'First row']} + + >>> ktfi.marshall({u'0001': [u'First row']}) + [{'value': u'First row', 'key': u'0001'}] + +Now with some real stuff, using a field instance that takes the column types +from the context object of the edit form. + + >>> from cybertools.composer.schema.grid.interfaces import KeyTable + >>> from cybertools.composer.schema.grid.field import \ + ... ContextBasedKeyTableFieldInstance + >>> component.provideAdapter(ContextBasedKeyTableFieldInstance, name='keytable') + + >>> class IDataTable(Interface): + ... title = zope.schema.TextLine(title=u'Title', required=False) + ... columnNames = zope.schema.List(title=u'Column Names', required=False) + ... data = KeyTable(title=u'Data', required=False) + >>> IDataTable['columnNames'].nostore = True + + >>> class DataTable(object): + ... implements(IDataTable) + ... def __init__(self, title, columnNames): + ... self.title = title + ... self.columnNames = columnNames + + >>> dt = DataTable('Account Types', ['identifier', 'label', 'info']) + + >>> input = dict(title='Account Types', + ... columnNames=['identifier', 'label', 'info'], + ... data=[dict(identifier='0001', label='Standard', info='')], + ... action='update') + >>> form = Form(dt, TestRequest(form=input)) + >>> form.interface = IDataTable + >>> form.nextUrl = 'dummy_url' + >>> form.update() + False + + >>> dt.data + {u'0001': [u'Standard', u'']} diff --git a/composer/schema/grid/field.py b/composer/schema/grid/field.py index 83c57ea..d67f33a 100644 --- a/composer/schema/grid/field.py +++ b/composer/schema/grid/field.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2009 Helmut Merz helmutm@cy55.de +# Copyright (c) 2011 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 @@ -18,8 +18,6 @@ """ Field and field instance classes for grids. - -$Id$ """ from zope import component @@ -30,7 +28,7 @@ from zope.cachedescriptors.property import Lazy import zope.schema from cybertools.composer.schema.factory import createField -from cybertools.composer.schema.field import ListFieldInstance +from cybertools.composer.schema.field import Field, ListFieldInstance from cybertools.composer.schema.interfaces import IField, IFieldInstance from cybertools.composer.schema.interfaces import fieldTypes, undefined from cybertools.util.format import toStr, toUnicode @@ -91,6 +89,13 @@ class GridFieldInstance(ListFieldInstance): return [] result = [] rows = json.loads(value)['items'] + for row in rows: + item = self.unmarshallRow(row) + if item: + result.append(item) + return result + + def dummy(self): for row in rows: item = {} empty = True @@ -106,6 +111,18 @@ class GridFieldInstance(ListFieldInstance): result.append(item) return result + def unmarshallRow(self, row): + item = {} + for fi in self.columnFieldInstances: + value = fi.unmarshall(row[fi.name]) + if isinstance(value, basestring): + value = value.strip() + if fi.default is not None: + if value == fi.default: + continue + item[fi.name] = value + return item + class RecordsFieldInstance(GridFieldInstance): @@ -123,19 +140,49 @@ class RecordsFieldInstance(GridFieldInstance): value = [] result = [] for row in value: - item = {} - empty = True - for fi in self.columnFieldInstances: - value = row[fi.name] - if isinstance(value, basestring): - value = value.strip() - value = fi.unmarshall(value) - item[fi.name] = value - if fi.default is not None: - if value and value != fi.default: - empty = False - elif value: - empty = False - if not empty: + item = self.unmarshallRow(row) + if item: result.append(item) return result + + +class KeyTableFieldInstance(RecordsFieldInstance): + + @Lazy + def keyName(self): + return self.columnTypes[0].name + + @Lazy + def dataNames(self): + return [f.name for f in self.columnTypes[1:]] + + def marshall(self, value): + result = [] + if not value: + return result + for k, v in value.items(): + item = {self.keyName: k} + for idx, name in enumerate(self.dataNames): + item[name] = v[idx] + result.append(item) + return result + + def unmarshall(self, value): + if not value: + value = {} + result = {} + for row in value: + item = self.unmarshallRow(row) + if item: + result[item.pop(self.keyName)] = [item.get(name) + for name in self.dataNames] + return result + + +class ContextBasedKeyTableFieldInstance(KeyTableFieldInstance): + + @Lazy + def columnTypes(self): + obj = self.clientInstance.context + return [Field(name) for name in obj.columnNames] + diff --git a/composer/schema/grid/interfaces.py b/composer/schema/grid/interfaces.py index 2c9b9fb..f6ca57a 100644 --- a/composer/schema/grid/interfaces.py +++ b/composer/schema/grid/interfaces.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2009 Helmut Merz helmutm@cy55.de +# Copyright (c) 2011 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 @@ -18,8 +18,6 @@ """ Grid field definition. - -$Id$ """ from zope import schema @@ -50,3 +48,12 @@ class Records(Grid): u'A series of records or rows.', instanceName='records',)) + +class KeyTable(Grid): + + __typeInfo__ = ('keytable', + FieldType('keytable', 'keytable', + u'A dictionary of records or rows the first ' + u'column of which represents the key.', + instanceName='keytable',)) +