Skip to content
Browse files

Merge pull request #15 from alvesjnr/master

CompositeTextProperty
  • Loading branch information...
2 parents 9bc1521 + 89494c2 commit ff267d202619d6a2147b264071f76675979eb1ba @gustavofonseca gustavofonseca committed May 31, 2011
View
54 examples/bibitex/bibitex.py
@@ -0,0 +1,54 @@
+from pyramid.config import Configurator
+from pyramid.response import Response
+from pyramid.view import view_config
+from pyramid.renderers import render_to_response
+
+from paste.httpserver import serve
+from models import Bibitex
+from forms import BibitexForm
+
+import deform
+import couchdbkit
+
+
+
+def hello_world(request):
+ return Response('Hello world!')
+
+def goodbye_world(request):
+ return Response('Goodbye world!')
+
+
+def new_entry(request):
+ bibitex_form = BibitexForm.get_form()
+ if 'submit' in request.POST:
+ return Response('OK')
+ else:
+ return render_to_response('bibitex:template.pt',
+ {'content': bibitex_form.render()},
+ )
+
+def list_all(request):
+ pass
+
+def view_entry(request):
+ pass
+
+
+if __name__ == '__main__':
+ config = Configurator()
+
+ """Configuring couchdb"""
+ server = couchdbkit.Server()
+ db = server.get_or_create_db('bibitex')
+
+ """Adding static views"""
+ config.add_static_view('deform_static', 'deform:static')
+
+ """Adding views"""
+ config.add_view(list_all)
+ config.add_view(new_entry, name="new_entry")
+ config.add_view(view_entry, name="view_entry")
+
+ app = config.make_wsgi_app()
+ serve(app, host='0.0.0.0')
View
12 examples/bibitex/forms.py
@@ -0,0 +1,12 @@
+from models import Bibitex
+
+import deform
+import colander
+
+class BibitexForm():
+
+ base_schema = Bibitex.get_schema()
+ base_schema['review'].widget = deform.widget.TextAreaWidget(cols=80, rows=15)
+ @classmethod
+ def get_form(cls):
+ return deform.Form(cls.base_schema, buttons=('submit',))
View
17 examples/bibitex/models.py
@@ -0,0 +1,17 @@
+from isis import model
+import deform
+
+choices = ['article', 'book', 'booklet', 'conference', 'inbook', 'incollection', 'inproceedings',
+ 'manual', 'mastersthesis', 'misc', 'phdthesis', 'proceedings', 'techreport', 'unpublished', ]
+
+class Bibitex(model.CouchdbDocument):
+ entry_type = model.TextProperty(choices=[(entry,entry) for entry in choices],)
+ reference_name = model.TextProperty(required=True)
+ title = model.TextProperty(required=True)
+ authors = model.MultiCompositeTextProperty(required=True, subkeys=['name', 'lastname'])
+ publisher = model.TextProperty()
+ year = model.TextProperty()
+ address = model.TextProperty()
+ review = model.TextProperty()
+
+
View
15 examples/bibitex/template.pt
@@ -0,0 +1,15 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <script type="text/javascript" src="/deform_static/scripts/deform.js"></script>
+ <script type="text/javascript">
+ deform.load()
+ </script>
+ </head>
+ <body>
+ <div>
+ <span tal:replace="structure content"></span>
+ </div>
+ </body>
+</html>
View
3 isis/model/__init__.py
@@ -1,6 +1,7 @@
# package
from .mapper import Document
from .mapper import TextProperty, MultiTextProperty
-from .mapper import CompositeTextProperty, MultiCompositeTextProperty
+from .mapper import CompositeTextProperty, IsisCompositeTextProperty
+from .mapper import MultiIsisCompositeTextProperty, MultiCompositeTextProperty
from .mapper import ReferenceProperty, FileProperty, BooleanProperty
from .couchdb import CouchdbDocument
View
96 isis/model/mapper.py
@@ -19,7 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from .ordered import OrderedProperty, OrderedModel
-from .subfield import CompositeString
+from .subfield import CompositeString, CompositeField
import json
import colander
import deform
@@ -230,18 +230,18 @@ def _colander_schema(self, instance, value):
return schema
-class CompositeTextProperty(CheckedProperty):
+class IsisCompositeTextProperty(CheckedProperty):
def __init__(self, subkeys=None, **kwargs):
- super(CompositeTextProperty, self).__init__(**kwargs)
+ super(IsisCompositeTextProperty, self).__init__(**kwargs)
self.subkeys = subkeys
def __set__(self, instance, value):
if not isinstance(value, basestring):
raise TypeError('%r value must be unicode or str instance' % self.name)
composite_text = CompositeString(value, self.subkeys)
- super(CompositeTextProperty, self).__set__(instance, composite_text)
+ super(IsisCompositeTextProperty, self).__set__(instance, composite_text)
def _pystruct(self, instance, value):
'''
@@ -258,18 +258,58 @@ def _colander_schema(self, instance, value):
subfield,
name=self.name)
-class MultiCompositeTextProperty(CheckedProperty):
+
+class CompositeTextProperty(CheckedProperty):
+
+ def __init__(self, subkeys, **kwargs):
+ super(CompositeTextProperty, self).__init__(**kwargs)
+ if not isinstance(subkeys,tuple) and not isinstance(subkeys, list):
+ raise TypeError('subkeys argument must be tuple or list')
+ self.subkeys = subkeys
+
+ def __set__(self, instance, value):
+ try:
+ value_as_dict = dict(value)
+ except ValueError:
+ raise TypeError('%r value must be a key-value structure' % self.name)
+
+ try:
+ value = CompositeField(value_as_dict, self.subkeys)
+ except TypeError:
+ raise TypeError('%r got an unexpected keyword' % self.name)
+
+ super(CompositeTextProperty, self).__set__(instance, value)
+
+ def _pystruct(self, instance, value):
+ '''
+ python representation for this property
+ '''
+ return value.items()
+
+ def _colander_schema(self, instance, value):
+ subfield = colander.SchemaNode(colander.Mapping(), name=self.name,)
+
+ kwargs = {}
+ if not self.required:
+ kwargs.update({'missing':None})
+
+ for subkey in self.subkeys:
+ subfield.add(colander.SchemaNode(colander.String(), name=subkey, **kwargs))
+
+ return subfield
+
+class MultiIsisCompositeTextProperty(CheckedProperty):
def __init__(self, subkeys=None, **kwargs):
- super(MultiCompositeTextProperty, self).__init__(**kwargs)
+ super(MultiIsisCompositeTextProperty, self).__init__(**kwargs)
self.subkeys = subkeys
def __set__(self, instance, value):
if not isinstance(value, tuple):
- raise TypeError('MultiCompositeText value must be tuple')
+ raise TypeError('MultiIsisCompositeText value must be tuple')
composite_texts = tuple(CompositeString(raw_composite_text, self.subkeys) for raw_composite_text in value)
- super(MultiCompositeTextProperty, self).__set__(instance, composite_texts)
+ super(MultiIsisCompositeTextProperty, self).__set__(instance, composite_texts)
def _pystruct(self, instance, value):
'''
@@ -286,6 +326,46 @@ def _colander_schema(self, instance, value):
schema,
name=self.name)
+class MultiCompositeTextProperty(CheckedProperty):
+
+ def __init__(self, subkeys, **kwargs):
+ super(MultiCompositeTextProperty, self).__init__(**kwargs)
+ if not isinstance(subkeys,tuple) and not isinstance(subkeys, list):
+ raise TypeError('subkeys argument must be tuple or list')
+ self.subkeys = subkeys
+
+ def __set__(self, instance, value):
+ if not isinstance(value, tuple) and not isinstance(value, list):
+ raise TypeError('%r value must be tuple or list')
+
+ try:
+ composite_texts = tuple(CompositeTuple(dict(composite_text), self.subkeys) for composite_text in value)
+ except ValueError:
+ raise TypeError('%r value must be a list or tuple of key-value structures' % self.name)
+
+ super(MultiCompositeTextProperty, self).__set__(instance, composite_texts)
+
+ def _pystruct(self, instance, value):
+ '''
+ python representation for this property
+ '''
+ return tuple(composite_text.items() for composite_text in value)
+
+ def _colander_schema(self, instance, value):
+ schema = colander.SchemaNode(colander.Mapping(), name=self.name)
+
+ kwargs = {}
+ if not self.required:
+ kwargs.update({'missing':None})
+
+ for subkey in self.subkeys:
+ schema.add(colander.SchemaNode(colander.String(), name=subkey, **kwargs))
+
+ return colander.SchemaNode(colander.Sequence(),
+ schema,
+ name=self.name,
+ **kwargs)
+
class ReferenceProperty(CheckedProperty):
def __set__(self, instance, value):
View
57 isis/model/subfield.py
@@ -18,6 +18,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+from collections import namedtuple
import re
@@ -54,15 +55,14 @@ def expand(content, subkeys=None):
class CompositeString(object):
''' Represent an Isis field, with subfields, using
- Python native datastructures
-
- >>> author = CompositeString('John Tenniel^xillustrator',
- ... subkeys='x')
- >>> unicode(author)
- u'John Tenniel^xillustrator'
+ Python native datastructures
+ >>> author = CompositeString('John Tenniel^xillustrator',
+ ... subkeys='x')
+ >>> unicode(author)
+ u'John Tenniel^xillustrator'
'''
-
+
def __init__(self, isis_raw, subkeys=None, encoding=DEFAULT_ENCODING):
if not isinstance(isis_raw, basestring):
raise TypeError('%r value must be unicode or str instance' % isis_raw)
@@ -90,6 +90,49 @@ def __str__(self):
return str(self.__isis_raw)
+class CompositeField(object):
+ ''' Represent an Isis field, with subfields, using
+ Python native datastructures
+
+ >>> author = CompositeField( [('name','Braz, Marcelo'),('role','writer')] )
+ >>> print author['name']
+ Braz, Marcelo
+ >>> print author['role']
+ writer
+ >>> author
+ CompositeField((('name', 'Braz, Marcelo'), ('role', 'writer')))
+
+ '''
+
+ def __init__(self, value, subkeys=None):
+ if subkeys is None:
+ subkeys = [item[0] for item in value]
+ try:
+ value_as_dict = dict(value)
+ except TypeError:
+ raise TypeError('%r value must be a key-value structure' % self)
+
+ for key in value_as_dict:
+ if key not in subkeys:
+ raise TypeError('Unexpected keyword %r' % key)
+
+ self.value = tuple([(key, value_as_dict.get(key,None)) for key in subkeys])
+
+ def __getitem__(self, key):
+ return dict(self.value)[key]
+
+ def __repr__(self):
+ return "CompositeField(%s)" % str(self.items())
+
+ def items(self):
+ return self.value
+
+ def __unicode__(self):
+ unicode(self.items())
+
+ def __str__(self):
+ str(self.items())
+
def test():
import doctest
View
46 isis/model/tests/test_mapper.py
@@ -27,12 +27,12 @@
...
>>> class Article(Document):
... title = TextProperty(required=True, validator=text_validator)
- ... authors = CompositeTextProperty(required=False, subkeys='fl')
+ ... authors = IsisCompositeTextProperty(required=False, subkeys='fl')
... publisher = TextProperty(required=True, validator=text_validator)
...
>>> class Magazine(Document):
... title = TextProperty(required=True, validator=text_validator)
- ... authors = MultiCompositeTextProperty(required=False, subkeys='fl')
+ ... authors = MultiIsisCompositeTextProperty(required=False, subkeys='fl')
... pages = TextProperty(validator=is_number)
...
@@ -44,7 +44,7 @@
...
>>> class BookWithinCollection(Document):
... title = TextProperty(required=True, validator=text_validator)
- ... authors = MultiCompositeTextProperty(required=False, subkeys='fl')
+ ... authors = MultiIsisCompositeTextProperty(required=False, subkeys='fl')
... collection = ReferenceProperty()
...
@@ -63,7 +63,7 @@
>>> class BookWithAttachment(Document):
... title = TextProperty(required=True, validator=text_validator)
- ... authors = MultiCompositeTextProperty(required=False, subkeys='fl')
+ ... authors = MultiIsisCompositeTextProperty(required=False, subkeys='fl')
... cover = FileProperty()
...
@@ -178,6 +178,9 @@
>>> ' '.join('%s:%s' % (c.name, type(c.typ).__name__) for c in sc.children)
'title:String authors:Sequence pages:String'
+ >>> sc.serialize({'title':'My home', 'authors':['Edward','John'], 'pages':'22'}) == {'authors': [u'Edward', u'John'], 'pages': u'22', 'title': u'My home'}
+ True
+
>>> book_with_bool_schema = BookWithBoolean.get_schema()
>>> ' '.join('%s:%s' % (c.name, type(c.typ).__name__) for c in book_with_bool_schema.children)
'title:String is_published:Boolean'
@@ -187,10 +190,43 @@
>>> book_with_meta_schema = BookWithinMeta.get_schema()
>>> ' '.join('%s:%s' % (c.name, type(c.typ).__name__) for c in book_with_meta_schema.children)
'title:String authors:Sequence pages:String'
+
+New CompositeText test
+
+ >>> class OtherBook(Document):
+ ... author = CompositeTextProperty(subkeys=['name','role'])
+ ...
+
+ >>> other = OtherBook(author=[['name','Braz, Marcelo'],['role','writer']])
+ >>> other.author
+ CompositeTuple((('name', 'Braz, Marcelo'), ('role', 'writer')))
+ >>> print other.author['name']
+ Braz, Marcelo
+ >>> other.to_python()
+ {'TYPE': 'OtherBook', 'author': (('name', 'Braz, Marcelo'), ('role', 'writer'))}
+
+ >>> other_schema = OtherBook.get_schema()
+ >>> other_schema.serialize({'author':{'name':'john', 'role':'writer'}}) == {'author': {'role': u'writer', 'name': u'john'}}
+ True
+
+MultiCompositeTextProperty test
+
+ >>> class MultiCompositeOtherBook(Document):
+ ... author = MultiCompositeTextProperty(subkeys=['name','role'])
+
+ >>> multi_other = MultiCompositeOtherBook(author=[[['name','Braz, Marcelo'],['role','writer']], [['name','Foo, Tony'],['role','editor']]])
+ >>> print multi_other.author[0]['name']
+ Braz, Marcelo
+ >>> multi_other.to_python() == {'TYPE': 'MultiCompositeOtherBook', 'author': ((('name', 'Braz, Marcelo'), ('role', 'writer')), (('name', 'Foo, Tony'), ('role', 'editor')))}
+ True
+ >>> multi_other_schema = MultiCompositeOtherBook.get_schema()
+ >>> multi_other_schema.serialize({'author':({'name':'john', 'role':'writer'}, {'name':'foo', 'role':'editor'})}) == {'author': [{'role': u'writer', 'name': u'john'}, {'role': u'editor', 'name': u'foo'}]}
+ True
"""
from isis.model import Document
from isis.model import TextProperty, MultiTextProperty
-from isis.model import CompositeTextProperty, MultiCompositeTextProperty
+from isis.model import CompositeTextProperty, IsisCompositeTextProperty
+from isis.model import MultiCompositeTextProperty, MultiIsisCompositeTextProperty
from isis.model import ReferenceProperty, FileProperty, BooleanProperty
def test():

0 comments on commit ff267d2

Please sign in to comment.
Something went wrong with that request. Please try again.