From 3bddfef76fa23a83163ed4c99b52d03ffae85cfe Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Wed, 25 May 2011 02:25:26 +0800 Subject: [PATCH 01/24] fixing bugs in couchdb.py --- .gitignore | 1 + isis/model/couchdb.py | 34 ++++++++++++++++++++-------------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 2a26fe8..23d559b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.pyc *.komodoproject +*.orig dist/ *.egg-info/ diff --git a/isis/model/couchdb.py b/isis/model/couchdb.py index 4ae7606..83db4cf 100644 --- a/isis/model/couchdb.py +++ b/isis/model/couchdb.py @@ -60,7 +60,10 @@ def __clean_before_save(self, doc): def to_python(self): pstruct = super(CouchdbDocument, self).to_python() - pstruct['_id'] = self._id + try: + pstruct['_id'] = self._id + except AttributeError: + pass try: pstruct['_rev'] = self._rev @@ -71,9 +74,10 @@ def to_python(self): @classmethod def from_python(cls, pystruct): - if pystruct['_id'] == 'None': + if pystruct.get('_id',None) == 'None': pystruct['_id'] = None - if pystruct['_rev'] == 'None': + + if pystruct.get('_rev',None) == 'None': pystruct['_rev'] = None return super(CouchdbDocument, cls).from_python(pystruct) @@ -120,18 +124,20 @@ def get(cls, db, doc_id): return cls.from_python(doc) @classmethod - def get_schema(cls): + def get_schema(cls, controls=True): schema = super(CouchdbDocument, cls).get_schema() - rev_definition = colander.SchemaNode(colander.String(), - widget = deform.widget.HiddenWidget(), - default=None, - name='_rev') - id_definition = colander.SchemaNode(colander.String(), - widget = deform.widget.HiddenWidget(), - default=None, - name='_id') - schema.add(rev_definition) - schema.add(id_definition) + + if controls: + rev_definition = colander.SchemaNode(colander.String(), + widget = deform.widget.HiddenWidget(), + default=None, + name='_rev') + id_definition = colander.SchemaNode(colander.String(), + widget = deform.widget.HiddenWidget(), + default=None, + name='_id') + schema.add(rev_definition) + schema.add(id_definition) return schema From 91b5efb1c833a8af4906c8dc85013ffe21a70294 Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Wed, 25 May 2011 02:41:52 +0800 Subject: [PATCH 02/24] improving an option to remove controls field when getting a document from couchdb --- isis/model/couchdb.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/isis/model/couchdb.py b/isis/model/couchdb.py index 83db4cf..7edb34d 100644 --- a/isis/model/couchdb.py +++ b/isis/model/couchdb.py @@ -119,9 +119,16 @@ def save(self, db): self._id, self._rev = new_doc['_id'], new_doc['_rev'] @classmethod - def get(cls, db, doc_id): + def get(cls, db, doc_id, controls=True): doc = db.get(doc_id) - return cls.from_python(doc) + couchdocument = cls.from_python(doc) + + if not controls: + del(couchdocument._id) + del(couchdocument._rev) + + return couchdocument + @classmethod def get_schema(cls, controls=True): From 4266bf6897c9b5ae73525a07f6aba7e27be356bc Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Wed, 25 May 2011 17:36:29 -0300 Subject: [PATCH 03/24] one more step to create an composite text field --- isis/model/mapper.py | 21 +++++++++++++++------ isis/model/tests/test_mapper.py | 11 +++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/isis/model/mapper.py b/isis/model/mapper.py index af96220..ed4d471 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -234,20 +234,29 @@ class CompositeTextProperty(CheckedProperty): def __init__(self, subkeys=None, **kwargs): super(CompositeTextProperty, self).__init__(**kwargs) - self.subkeys = subkeys + if isinstance(subkeys,basestring): + self.subkeys = list(subkeys) + else: + 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) + if isinstance(value, basestring): + subkeys = ''.join(self.subkeys) + composite_text = CompositeString(value, subkeys) + super(CompositeTextProperty, self).__set__(instance, composite_text) + else: + try: + dict(value) #TODO/FIXME: change this value to an which accept __getittem__ + super(CompositeTextProperty, self).__set__(instance, value) + except ValueError: + raise TypeError('%r value must be unicode or str instance or associative list' % self.name) - composite_text = CompositeString(value, self.subkeys) - super(CompositeTextProperty, self).__set__(instance, composite_text) def _pystruct(self, instance, value): ''' python representation for this property ''' - return value.items() + return value def _colander_schema(self, instance, value): subfield = colander.SchemaNode(colander.Tuple()) diff --git a/isis/model/tests/test_mapper.py b/isis/model/tests/test_mapper.py index 6a314aa..054b741 100644 --- a/isis/model/tests/test_mapper.py +++ b/isis/model/tests/test_mapper.py @@ -187,6 +187,17 @@ >>> 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=['role']) + ... + >>> other = OtherBook(author=[['_','Braz, Marcelo'],['role','writer']]) + >>> print other.author + [['_', 'Braz, Marcelo'], ['role', 'writer']] + + """ from isis.model import Document from isis.model import TextProperty, MultiTextProperty From 4947362691b37a07f8d7b51e05fb5bfe918769d0 Mon Sep 17 00:00:00 2001 From: gustavofonseca Date: Wed, 25 May 2011 17:47:18 -0300 Subject: [PATCH 04/24] removed the md5 generation for FileProperty --- isis/model/mapper.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/isis/model/mapper.py b/isis/model/mapper.py index 3189c58..ae9571d 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -169,19 +169,13 @@ class FileProperty(CheckedProperty): def __set__(self, instance, value): if not isinstance(value, dict): raise TypeError('%r must be a dictionary' % self.name) - - # if 'fp' not in value: - # raise TypeError('fp value must exists') - + if 'filename' not in value: try: value['filename'] = value['fp'].name except AttributeError: raise TypeError('%r must be a file' % self.name) - - if 'fp' in value: - value['md5'] = hashlib.md5(value['fp'].read()).hexdigest() - + super(FileProperty, self).__set__(instance, value) def _pystruct(self, instance, value): @@ -190,8 +184,7 @@ def _pystruct(self, instance, value): ''' if isinstance(value, dict): serializable_value = {'uid':value['uid'], - 'filename':value['filename'], - 'md5':value['md5'],} + 'filename':value['filename'],} return serializable_value return value From 5ff051067fc4fb438759e6da1094dd20089d8fcc Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Thu, 26 May 2011 17:09:05 -0300 Subject: [PATCH 05/24] implementing CompositeTextProperty --- isis/model/__init__.py | 2 +- isis/model/mapper.py | 64 +++++++++++++++++++++++---------- isis/model/subfield.py | 57 +++++++++++++++++++++++++---- isis/model/tests/test_mapper.py | 26 ++++++++++---- 4 files changed, 116 insertions(+), 33 deletions(-) diff --git a/isis/model/__init__.py b/isis/model/__init__.py index 0a045f9..4acaa6d 100644 --- a/isis/model/__init__.py +++ b/isis/model/__init__.py @@ -1,6 +1,6 @@ # package from .mapper import Document from .mapper import TextProperty, MultiTextProperty -from .mapper import CompositeTextProperty, MultiCompositeTextProperty +from .mapper import CompositeTextProperty, IsisCompositeTextProperty, MultiCompositeTextProperty from .mapper import ReferenceProperty, FileProperty, BooleanProperty from .couchdb import CouchdbDocument \ No newline at end of file diff --git a/isis/model/mapper.py b/isis/model/mapper.py index ed4d471..bde9799 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -19,7 +19,7 @@ # along with this program. If not, see . from .ordered import OrderedProperty, OrderedModel -from .subfield import CompositeString +from .subfield import CompositeString, CompositeTuple import json import colander import deform @@ -230,33 +230,24 @@ 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) - if isinstance(subkeys,basestring): - self.subkeys = list(subkeys) - else: - self.subkeys = subkeys + super(IsisCompositeTextProperty, self).__init__(**kwargs) + self.subkeys = subkeys def __set__(self, instance, value): - if isinstance(value, basestring): - subkeys = ''.join(self.subkeys) - composite_text = CompositeString(value, subkeys) - super(CompositeTextProperty, self).__set__(instance, composite_text) - else: - try: - dict(value) #TODO/FIXME: change this value to an which accept __getittem__ - super(CompositeTextProperty, self).__set__(instance, value) - except ValueError: - raise TypeError('%r value must be unicode or str instance or associative list' % self.name) + if not isinstance(value, basestring): + raise TypeError('%r value must be unicode or str instance' % self.name) + composite_text = CompositeString(value, self.subkeys) + super(IsisCompositeTextProperty, self).__set__(instance, composite_text) def _pystruct(self, instance, value): ''' python representation for this property ''' - return value + return value.items() def _colander_schema(self, instance, value): subfield = colander.SchemaNode(colander.Tuple()) @@ -267,6 +258,43 @@ def _colander_schema(self, instance, value): subfield, name=self.name) + +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('subkey attribute must be tuple') + 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 = CompositeTuple(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) + + for subkey in self.subkeys: + subfield.add(colander.SchemaNode(colander.String(), name=subkey)) + + return subfield + + class MultiCompositeTextProperty(CheckedProperty): def __init__(self, subkeys=None, **kwargs): diff --git a/isis/model/subfield.py b/isis/model/subfield.py index cc5ebe4..318df7d 100644 --- a/isis/model/subfield.py +++ b/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 . +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 CompositeTuple(object): + ''' Represent an Isis field, with subfields, using + Python native datastructures + + >>> author = CompositeTuple( [('name','Braz, Marcelo'),('role','writer')] ) + >>> print author['name'] + Braz, Marcelo + >>> print author['role'] + writer + >>> author + CompositeTuple((('name', 'Braz, Marcelo'), ('role', 'writer'))) + + ''' + + def __init__(self, value, subkeys=None): + if subkeys is None: + subkeys = [item[0] for item in value] + composite = namedtuple('Composite', subkeys) + try: + value_as_dict = dict(value) + except TypeError: + raise TypeError('%r value must be a key-value structure' % self) + + try: + self.value = composite(**value_as_dict) + except TypeError: + raise TypeError('%r got an unexpected keyword' % self) + + def __getitem__(self, key): + return getattr(self.value,key) + + def __repr__(self): + return "CompositeTuple(%s)" % str(self.items()) + + def items(self): + return tuple(zip(self.value._fields, self.value)) + + def __unicode__(self): + unicode(self.items()) + + def __str__(self): + str(self.items()) + def test(): import doctest diff --git a/isis/model/tests/test_mapper.py b/isis/model/tests/test_mapper.py index 054b741..fc56be5 100644 --- a/isis/model/tests/test_mapper.py +++ b/isis/model/tests/test_mapper.py @@ -27,7 +27,7 @@ ... >>> 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): @@ -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' @@ -191,17 +194,26 @@ New CompositeText test >>> class OtherBook(Document): - ... author = CompositeTextProperty(subkeys=['role']) - ... - >>> other = OtherBook(author=[['_','Braz, Marcelo'],['role','writer']]) - >>> print other.author - [['_', 'Braz, Marcelo'], ['role', 'writer']] + ... 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 """ from isis.model import Document from isis.model import TextProperty, MultiTextProperty -from isis.model import CompositeTextProperty, MultiCompositeTextProperty +from isis.model import CompositeTextProperty, IsisCompositeTextProperty, MultiCompositeTextProperty from isis.model import ReferenceProperty, FileProperty, BooleanProperty def test(): From c5dba9ca9df4314e775f8169bbe14c661cc3d118 Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Thu, 26 May 2011 04:36:29 +0800 Subject: [PATCH 06/24] one more step to create an composite text field --- isis/model/mapper.py | 21 +++++++++++++++------ isis/model/tests/test_mapper.py | 11 +++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/isis/model/mapper.py b/isis/model/mapper.py index ae9571d..f0e4fb1 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -230,20 +230,29 @@ class CompositeTextProperty(CheckedProperty): def __init__(self, subkeys=None, **kwargs): super(CompositeTextProperty, self).__init__(**kwargs) - self.subkeys = subkeys + if isinstance(subkeys,basestring): + self.subkeys = list(subkeys) + else: + 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) + if isinstance(value, basestring): + subkeys = ''.join(self.subkeys) + composite_text = CompositeString(value, subkeys) + super(CompositeTextProperty, self).__set__(instance, composite_text) + else: + try: + dict(value) #TODO/FIXME: change this value to an which accept __getittem__ + super(CompositeTextProperty, self).__set__(instance, value) + except ValueError: + raise TypeError('%r value must be unicode or str instance or associative list' % self.name) - composite_text = CompositeString(value, self.subkeys) - super(CompositeTextProperty, self).__set__(instance, composite_text) def _pystruct(self, instance, value): ''' python representation for this property ''' - return value.items() + return value def _colander_schema(self, instance, value): subfield = colander.SchemaNode(colander.Tuple()) diff --git a/isis/model/tests/test_mapper.py b/isis/model/tests/test_mapper.py index 6a314aa..054b741 100644 --- a/isis/model/tests/test_mapper.py +++ b/isis/model/tests/test_mapper.py @@ -187,6 +187,17 @@ >>> 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=['role']) + ... + >>> other = OtherBook(author=[['_','Braz, Marcelo'],['role','writer']]) + >>> print other.author + [['_', 'Braz, Marcelo'], ['role', 'writer']] + + """ from isis.model import Document from isis.model import TextProperty, MultiTextProperty From 64f57074cb060511e077b062fc05a9204b12efbc Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Fri, 27 May 2011 04:09:05 +0800 Subject: [PATCH 07/24] implementing CompositeTextProperty --- isis/model/__init__.py | 2 +- isis/model/mapper.py | 64 +++++++++++++++++++++++---------- isis/model/subfield.py | 57 +++++++++++++++++++++++++---- isis/model/tests/test_mapper.py | 26 ++++++++++---- 4 files changed, 116 insertions(+), 33 deletions(-) diff --git a/isis/model/__init__.py b/isis/model/__init__.py index 0a045f9..4acaa6d 100644 --- a/isis/model/__init__.py +++ b/isis/model/__init__.py @@ -1,6 +1,6 @@ # package from .mapper import Document from .mapper import TextProperty, MultiTextProperty -from .mapper import CompositeTextProperty, MultiCompositeTextProperty +from .mapper import CompositeTextProperty, IsisCompositeTextProperty, MultiCompositeTextProperty from .mapper import ReferenceProperty, FileProperty, BooleanProperty from .couchdb import CouchdbDocument \ No newline at end of file diff --git a/isis/model/mapper.py b/isis/model/mapper.py index f0e4fb1..120faf2 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -19,7 +19,7 @@ # along with this program. If not, see . from .ordered import OrderedProperty, OrderedModel -from .subfield import CompositeString +from .subfield import CompositeString, CompositeTuple import json import colander import deform @@ -226,33 +226,24 @@ 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) - if isinstance(subkeys,basestring): - self.subkeys = list(subkeys) - else: - self.subkeys = subkeys + super(IsisCompositeTextProperty, self).__init__(**kwargs) + self.subkeys = subkeys def __set__(self, instance, value): - if isinstance(value, basestring): - subkeys = ''.join(self.subkeys) - composite_text = CompositeString(value, subkeys) - super(CompositeTextProperty, self).__set__(instance, composite_text) - else: - try: - dict(value) #TODO/FIXME: change this value to an which accept __getittem__ - super(CompositeTextProperty, self).__set__(instance, value) - except ValueError: - raise TypeError('%r value must be unicode or str instance or associative list' % self.name) + if not isinstance(value, basestring): + raise TypeError('%r value must be unicode or str instance' % self.name) + composite_text = CompositeString(value, self.subkeys) + super(IsisCompositeTextProperty, self).__set__(instance, composite_text) def _pystruct(self, instance, value): ''' python representation for this property ''' - return value + return value.items() def _colander_schema(self, instance, value): subfield = colander.SchemaNode(colander.Tuple()) @@ -263,6 +254,43 @@ def _colander_schema(self, instance, value): subfield, name=self.name) + +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('subkey attribute must be tuple') + 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 = CompositeTuple(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) + + for subkey in self.subkeys: + subfield.add(colander.SchemaNode(colander.String(), name=subkey)) + + return subfield + + class MultiCompositeTextProperty(CheckedProperty): def __init__(self, subkeys=None, **kwargs): diff --git a/isis/model/subfield.py b/isis/model/subfield.py index cc5ebe4..318df7d 100644 --- a/isis/model/subfield.py +++ b/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 . +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 CompositeTuple(object): + ''' Represent an Isis field, with subfields, using + Python native datastructures + + >>> author = CompositeTuple( [('name','Braz, Marcelo'),('role','writer')] ) + >>> print author['name'] + Braz, Marcelo + >>> print author['role'] + writer + >>> author + CompositeTuple((('name', 'Braz, Marcelo'), ('role', 'writer'))) + + ''' + + def __init__(self, value, subkeys=None): + if subkeys is None: + subkeys = [item[0] for item in value] + composite = namedtuple('Composite', subkeys) + try: + value_as_dict = dict(value) + except TypeError: + raise TypeError('%r value must be a key-value structure' % self) + + try: + self.value = composite(**value_as_dict) + except TypeError: + raise TypeError('%r got an unexpected keyword' % self) + + def __getitem__(self, key): + return getattr(self.value,key) + + def __repr__(self): + return "CompositeTuple(%s)" % str(self.items()) + + def items(self): + return tuple(zip(self.value._fields, self.value)) + + def __unicode__(self): + unicode(self.items()) + + def __str__(self): + str(self.items()) + def test(): import doctest diff --git a/isis/model/tests/test_mapper.py b/isis/model/tests/test_mapper.py index 054b741..fc56be5 100644 --- a/isis/model/tests/test_mapper.py +++ b/isis/model/tests/test_mapper.py @@ -27,7 +27,7 @@ ... >>> 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): @@ -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' @@ -191,17 +194,26 @@ New CompositeText test >>> class OtherBook(Document): - ... author = CompositeTextProperty(subkeys=['role']) - ... - >>> other = OtherBook(author=[['_','Braz, Marcelo'],['role','writer']]) - >>> print other.author - [['_', 'Braz, Marcelo'], ['role', 'writer']] + ... 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 """ from isis.model import Document from isis.model import TextProperty, MultiTextProperty -from isis.model import CompositeTextProperty, MultiCompositeTextProperty +from isis.model import CompositeTextProperty, IsisCompositeTextProperty, MultiCompositeTextProperty from isis.model import ReferenceProperty, FileProperty, BooleanProperty def test(): From 23f4a922dd1317f1fb60bc652147d5eb80f23865 Mon Sep 17 00:00:00 2001 From: gustavofonseca Date: Fri, 27 May 2011 12:08:05 -0300 Subject: [PATCH 08/24] added the new implementation of MultiCompositeTextProperty, based on CompositeTextProperty.the old implementation of this type has been renamed to MultiIsisCompositeTextProperty --- isis/model/__init__.py | 3 ++- isis/model/mapper.py | 47 ++++++++++++++++++++++++++++----- isis/model/tests/test_mapper.py | 21 ++++++++++++--- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/isis/model/__init__.py b/isis/model/__init__.py index 4acaa6d..83903f8 100644 --- a/isis/model/__init__.py +++ b/isis/model/__init__.py @@ -1,6 +1,7 @@ # package from .mapper import Document from .mapper import TextProperty, MultiTextProperty -from .mapper import CompositeTextProperty, IsisCompositeTextProperty, MultiCompositeTextProperty +from .mapper import CompositeTextProperty, IsisCompositeTextProperty +from .mapper import MultiIsisCompositeTextProperty, MultiCompositeTextProperty from .mapper import ReferenceProperty, FileProperty, BooleanProperty from .couchdb import CouchdbDocument \ No newline at end of file diff --git a/isis/model/mapper.py b/isis/model/mapper.py index 120faf2..0c97e09 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -260,10 +260,10 @@ 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('subkey attribute must be tuple') + raise TypeError('subkeys argument must be tuple or list') self.subkeys = subkeys - def __set__(self, instance, value): + def __set__(self, instance, value): try: value_as_dict = dict(value) except ValueError: @@ -290,19 +290,18 @@ def _colander_schema(self, instance, value): return subfield - -class MultiCompositeTextProperty(CheckedProperty): +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): ''' @@ -319,6 +318,40 @@ 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) + for subkey in self.subkeys: + schema.add(colander.SchemaNode(colander.String(), name=subkey)) + + return colander.SchemaNode(colander.Sequence(), + schema, + name=self.name) + class ReferenceProperty(CheckedProperty): def __set__(self, instance, value): diff --git a/isis/model/tests/test_mapper.py b/isis/model/tests/test_mapper.py index fc56be5..556fa76 100644 --- a/isis/model/tests/test_mapper.py +++ b/isis/model/tests/test_mapper.py @@ -32,7 +32,7 @@ ... >>> 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() ... @@ -209,11 +209,24 @@ >>> 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, IsisCompositeTextProperty, MultiCompositeTextProperty +from isis.model import CompositeTextProperty, IsisCompositeTextProperty +from isis.model import MultiCompositeTextProperty, MultiIsisCompositeTextProperty from isis.model import ReferenceProperty, FileProperty, BooleanProperty def test(): From 7ee532ec4222670d8bc28bb930652d7e61fa8e3c Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Mon, 30 May 2011 10:52:21 -0300 Subject: [PATCH 09/24] modifications in CompositeTuple (now CompositeField) --- isis/model/mapper.py | 4 ++-- isis/model/subfield.py | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/isis/model/mapper.py b/isis/model/mapper.py index bde9799..1ddffd3 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -19,7 +19,7 @@ # along with this program. If not, see . from .ordered import OrderedProperty, OrderedModel -from .subfield import CompositeString, CompositeTuple +from .subfield import CompositeString, CompositeField import json import colander import deform @@ -274,7 +274,7 @@ def __set__(self, instance, value): raise TypeError('%r value must be a key-value structure' % self.name) try: - value = CompositeTuple(value_as_dict, self.subkeys) + value = CompositeField(value_as_dict, self.subkeys) except TypeError: raise TypeError('%r got an unexpected keyword' % self.name) diff --git a/isis/model/subfield.py b/isis/model/subfield.py index 318df7d..05bb649 100644 --- a/isis/model/subfield.py +++ b/isis/model/subfield.py @@ -90,42 +90,42 @@ def __str__(self): return str(self.__isis_raw) -class CompositeTuple(object): +class CompositeField(object): ''' Represent an Isis field, with subfields, using Python native datastructures - >>> author = CompositeTuple( [('name','Braz, Marcelo'),('role','writer')] ) + >>> author = CompositeField( [('name','Braz, Marcelo'),('role','writer')] ) >>> print author['name'] Braz, Marcelo >>> print author['role'] writer >>> author - CompositeTuple((('name', 'Braz, Marcelo'), ('role', 'writer'))) + CompositeField((('name', 'Braz, Marcelo'), ('role', 'writer'))) ''' def __init__(self, value, subkeys=None): if subkeys is None: subkeys = [item[0] for item in value] - composite = namedtuple('Composite', subkeys) try: value_as_dict = dict(value) except TypeError: raise TypeError('%r value must be a key-value structure' % self) - try: - self.value = composite(**value_as_dict) - except TypeError: - raise TypeError('%r got an unexpected keyword' % 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 getattr(self.value,key) + return dict(self.value)[key] def __repr__(self): - return "CompositeTuple(%s)" % str(self.items()) + return "CompositeField(%s)" % str(self.items()) def items(self): - return tuple(zip(self.value._fields, self.value)) + return self.value def __unicode__(self): unicode(self.items()) From 228ad46a28f32986d636336aee154260e07bcd64 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 28 May 2011 04:58:59 +0800 Subject: [PATCH 10/24] added .idea to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 23d559b..4585cc3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ *.orig dist/ *.egg-info/ +.idea +.idea/* From 922fa3f80f0371c2cfa41c08e560f62ee8b06211 Mon Sep 17 00:00:00 2001 From: gustavofonseca Date: Fri, 27 May 2011 23:08:05 +0800 Subject: [PATCH 11/24] added the new implementation of MultiCompositeTextProperty, based on CompositeTextProperty.the old implementation of this type has been renamed to MultiIsisCompositeTextProperty --- isis/model/__init__.py | 3 ++- isis/model/mapper.py | 47 ++++++++++++++++++++++++++++----- isis/model/tests/test_mapper.py | 21 ++++++++++++--- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/isis/model/__init__.py b/isis/model/__init__.py index 4acaa6d..83903f8 100644 --- a/isis/model/__init__.py +++ b/isis/model/__init__.py @@ -1,6 +1,7 @@ # package from .mapper import Document from .mapper import TextProperty, MultiTextProperty -from .mapper import CompositeTextProperty, IsisCompositeTextProperty, MultiCompositeTextProperty +from .mapper import CompositeTextProperty, IsisCompositeTextProperty +from .mapper import MultiIsisCompositeTextProperty, MultiCompositeTextProperty from .mapper import ReferenceProperty, FileProperty, BooleanProperty from .couchdb import CouchdbDocument \ No newline at end of file diff --git a/isis/model/mapper.py b/isis/model/mapper.py index 1ddffd3..f191370 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -264,10 +264,10 @@ 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('subkey attribute must be tuple') + raise TypeError('subkeys argument must be tuple or list') self.subkeys = subkeys - def __set__(self, instance, value): + def __set__(self, instance, value): try: value_as_dict = dict(value) except ValueError: @@ -294,19 +294,18 @@ def _colander_schema(self, instance, value): return subfield - -class MultiCompositeTextProperty(CheckedProperty): +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): ''' @@ -323,6 +322,40 @@ 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) + for subkey in self.subkeys: + schema.add(colander.SchemaNode(colander.String(), name=subkey)) + + return colander.SchemaNode(colander.Sequence(), + schema, + name=self.name) + class ReferenceProperty(CheckedProperty): def __set__(self, instance, value): diff --git a/isis/model/tests/test_mapper.py b/isis/model/tests/test_mapper.py index fc56be5..556fa76 100644 --- a/isis/model/tests/test_mapper.py +++ b/isis/model/tests/test_mapper.py @@ -32,7 +32,7 @@ ... >>> 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() ... @@ -209,11 +209,24 @@ >>> 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, IsisCompositeTextProperty, MultiCompositeTextProperty +from isis.model import CompositeTextProperty, IsisCompositeTextProperty +from isis.model import MultiCompositeTextProperty, MultiIsisCompositeTextProperty from isis.model import ReferenceProperty, FileProperty, BooleanProperty def test(): From d7404723a42a2662415968ea9e8649188ce93128 Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Mon, 30 May 2011 11:31:51 -0300 Subject: [PATCH 12/24] fixing flags for required fields --- isis/model/mapper.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/isis/model/mapper.py b/isis/model/mapper.py index f191370..909aea1 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -287,10 +287,14 @@ def _pystruct(self, instance, value): return value.items() def _colander_schema(self, instance, value): - subfield = colander.SchemaNode(colander.Mapping(), name=self.name) + 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)) + subfield.add(colander.SchemaNode(colander.String(), name=subkey, **kwargs)) return subfield @@ -349,12 +353,18 @@ def _pystruct(self, instance, 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)) + schema.add(colander.SchemaNode(colander.String(), name=subkey, **kwargs)) return colander.SchemaNode(colander.Sequence(), schema, - name=self.name) + name=self.name, + **kwargs) class ReferenceProperty(CheckedProperty): From 0510f1874bf32e0bc00d0a0c80c8aa5b10c12f40 Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Mon, 30 May 2011 21:52:21 +0800 Subject: [PATCH 13/24] modifications in CompositeTuple (now CompositeField) --- isis/model/mapper.py | 4 ++-- isis/model/subfield.py | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/isis/model/mapper.py b/isis/model/mapper.py index 0c97e09..81464b0 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -19,7 +19,7 @@ # along with this program. If not, see . from .ordered import OrderedProperty, OrderedModel -from .subfield import CompositeString, CompositeTuple +from .subfield import CompositeString, CompositeField import json import colander import deform @@ -270,7 +270,7 @@ def __set__(self, instance, value): raise TypeError('%r value must be a key-value structure' % self.name) try: - value = CompositeTuple(value_as_dict, self.subkeys) + value = CompositeField(value_as_dict, self.subkeys) except TypeError: raise TypeError('%r got an unexpected keyword' % self.name) diff --git a/isis/model/subfield.py b/isis/model/subfield.py index 318df7d..05bb649 100644 --- a/isis/model/subfield.py +++ b/isis/model/subfield.py @@ -90,42 +90,42 @@ def __str__(self): return str(self.__isis_raw) -class CompositeTuple(object): +class CompositeField(object): ''' Represent an Isis field, with subfields, using Python native datastructures - >>> author = CompositeTuple( [('name','Braz, Marcelo'),('role','writer')] ) + >>> author = CompositeField( [('name','Braz, Marcelo'),('role','writer')] ) >>> print author['name'] Braz, Marcelo >>> print author['role'] writer >>> author - CompositeTuple((('name', 'Braz, Marcelo'), ('role', 'writer'))) + CompositeField((('name', 'Braz, Marcelo'), ('role', 'writer'))) ''' def __init__(self, value, subkeys=None): if subkeys is None: subkeys = [item[0] for item in value] - composite = namedtuple('Composite', subkeys) try: value_as_dict = dict(value) except TypeError: raise TypeError('%r value must be a key-value structure' % self) - try: - self.value = composite(**value_as_dict) - except TypeError: - raise TypeError('%r got an unexpected keyword' % 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 getattr(self.value,key) + return dict(self.value)[key] def __repr__(self): - return "CompositeTuple(%s)" % str(self.items()) + return "CompositeField(%s)" % str(self.items()) def items(self): - return tuple(zip(self.value._fields, self.value)) + return self.value def __unicode__(self): unicode(self.items()) From d5a652cf856f4eb4ac4c11e0484f354f57103a50 Mon Sep 17 00:00:00 2001 From: Luciano Ramalho Date: Sat, 28 May 2011 04:58:59 +0800 Subject: [PATCH 14/24] added .idea to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 23d559b..4585cc3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ *.orig dist/ *.egg-info/ +.idea +.idea/* From 9254b857bacb27f83b94a0e899597a9a71228f42 Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Mon, 30 May 2011 22:31:51 +0800 Subject: [PATCH 15/24] fixing flags for required fields --- isis/model/mapper.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/isis/model/mapper.py b/isis/model/mapper.py index 81464b0..8910ede 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -283,10 +283,14 @@ def _pystruct(self, instance, value): return value.items() def _colander_schema(self, instance, value): - subfield = colander.SchemaNode(colander.Mapping(), name=self.name) + 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)) + subfield.add(colander.SchemaNode(colander.String(), name=subkey, **kwargs)) return subfield @@ -345,12 +349,18 @@ def _pystruct(self, instance, 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)) + schema.add(colander.SchemaNode(colander.String(), name=subkey, **kwargs)) return colander.SchemaNode(colander.Sequence(), schema, - name=self.name) + name=self.name, + **kwargs) class ReferenceProperty(CheckedProperty): From 75ca06e02816263c92b74da0e3f534ec20d2f333 Mon Sep 17 00:00:00 2001 From: gustavofonseca Date: Mon, 30 May 2011 15:08:31 -0300 Subject: [PATCH 16/24] CompositeTextProperty mandatory validation --- isis/model/mapper.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/isis/model/mapper.py b/isis/model/mapper.py index 0c97e09..9aa322e 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -225,7 +225,6 @@ def _colander_schema(self, instance, value): return schema - class IsisCompositeTextProperty(CheckedProperty): def __init__(self, subkeys=None, **kwargs): @@ -254,7 +253,6 @@ def _colander_schema(self, instance, value): subfield, name=self.name) - class CompositeTextProperty(CheckedProperty): def __init__(self, subkeys, **kwargs): @@ -280,13 +278,20 @@ def _pystruct(self, instance, value): ''' python representation for this property ''' + #composite_string = tuple((k, v) for k, v in value.items() if v is not None) + #return composite_string + return value.items() def _colander_schema(self, instance, value): - subfield = colander.SchemaNode(colander.Mapping(), name=self.name) + #option arg acts in each attribute + kwargs = {} + if not self.required: + kwargs.update({'missing':None}) + subfield = colander.SchemaNode(colander.Mapping(), name=self.name) for subkey in self.subkeys: - subfield.add(colander.SchemaNode(colander.String(), name=subkey)) + subfield.add(colander.SchemaNode(colander.String(), name=subkey, **kwargs)) return subfield @@ -344,13 +349,17 @@ def _pystruct(self, instance, value): return tuple(composite_text.items() for composite_text in value) def _colander_schema(self, instance, value): + kwargs = {'name':self.name} + if not self.required: + kwargs.update({'missing':None}) + schema = colander.SchemaNode(colander.Mapping(), name=self.name) for subkey in self.subkeys: schema.add(colander.SchemaNode(colander.String(), name=subkey)) return colander.SchemaNode(colander.Sequence(), schema, - name=self.name) + **kwargs) class ReferenceProperty(CheckedProperty): From 89494c2fe6b9385754d819a0febb6989b9941f0b Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Mon, 30 May 2011 16:12:49 -0300 Subject: [PATCH 17/24] starting new isisdm example: using multitextproerty --- examples/bibitex/bibitex.py | 54 ++++++++++++++++++++++++++++++++++++ examples/bibitex/forms.py | 12 ++++++++ examples/bibitex/models.py | 17 ++++++++++++ examples/bibitex/template.pt | 15 ++++++++++ 4 files changed, 98 insertions(+) create mode 100644 examples/bibitex/bibitex.py create mode 100644 examples/bibitex/forms.py create mode 100644 examples/bibitex/models.py create mode 100644 examples/bibitex/template.pt diff --git a/examples/bibitex/bibitex.py b/examples/bibitex/bibitex.py new file mode 100644 index 0000000..79e4e2e --- /dev/null +++ b/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') \ No newline at end of file diff --git a/examples/bibitex/forms.py b/examples/bibitex/forms.py new file mode 100644 index 0000000..4588e8b --- /dev/null +++ b/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',)) \ No newline at end of file diff --git a/examples/bibitex/models.py b/examples/bibitex/models.py new file mode 100644 index 0000000..7481d95 --- /dev/null +++ b/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() + + diff --git a/examples/bibitex/template.pt b/examples/bibitex/template.pt new file mode 100644 index 0000000..cb157e0 --- /dev/null +++ b/examples/bibitex/template.pt @@ -0,0 +1,15 @@ + + + + + + + +
+ +
+ + \ No newline at end of file From 227273a2baaeec23280a79a93acb6761afbd7c2d Mon Sep 17 00:00:00 2001 From: gustavofonseca Date: Mon, 30 May 2011 17:38:32 -0300 Subject: [PATCH 18/24] fixing doctests --- isis/model/tests/test_couchdb.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/isis/model/tests/test_couchdb.py b/isis/model/tests/test_couchdb.py index 472cd6a..ce310e9 100644 --- a/isis/model/tests/test_couchdb.py +++ b/isis/model/tests/test_couchdb.py @@ -22,7 +22,7 @@ >>> class BookWithAttachment(CouchdbDocument): ... title = TextProperty(required=True, validator=text_validator) - ... authors = MultiCompositeTextProperty(required=False, subkeys='fl') + ... authors = MultiIsisCompositeTextProperty(required=False, subkeys='fl') ... cover = FileProperty() ... @@ -91,6 +91,7 @@ from isis.model import CouchdbDocument from isis.model import TextProperty, MultiTextProperty from isis.model import CompositeTextProperty, MultiCompositeTextProperty, ReferenceProperty, FileProperty +from isis.model import IsisCompositeTextProperty, MultiIsisCompositeTextProperty from isis.model.couchdb import _attach_updated, _attach_exists import couchdbkit From d5d30871464b58498a727cd45c946a3566f3ae5e Mon Sep 17 00:00:00 2001 From: gustavofonseca Date: Tue, 31 May 2011 14:38:17 -0300 Subject: [PATCH 19/24] merge from gustavofonseca/isisdm --- isis/model/mapper.py | 16 ++++++---------- isis/model/tests/test_couchdb.py | 3 ++- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/isis/model/mapper.py b/isis/model/mapper.py index 909aea1..3734929 100644 --- a/isis/model/mapper.py +++ b/isis/model/mapper.py @@ -169,16 +169,13 @@ class FileProperty(CheckedProperty): def __set__(self, instance, value): if not isinstance(value, dict): raise TypeError('%r must be a dictionary' % self.name) - + if 'filename' not in value: try: value['filename'] = value['fp'].name except AttributeError: raise TypeError('%r must be a file' % self.name) - - if 'fp' in value: - value['md5'] = hashlib.md5(value['fp'].read()).hexdigest() - + super(FileProperty, self).__set__(instance, value) def _pystruct(self, instance, value): @@ -187,8 +184,7 @@ def _pystruct(self, instance, value): ''' if isinstance(value, dict): serializable_value = {'uid':value['uid'], - 'filename':value['filename'], - 'md5':value['md5'],} + 'filename':value['filename'],} return serializable_value return value @@ -287,12 +283,12 @@ def _pystruct(self, instance, value): return value.items() def _colander_schema(self, instance, value): - subfield = colander.SchemaNode(colander.Mapping(), name=self.name,) - + #option arg acts in each attribute kwargs = {} if not self.required: kwargs.update({'missing':None}) + subfield = colander.SchemaNode(colander.Mapping(), name=self.name) for subkey in self.subkeys: subfield.add(colander.SchemaNode(colander.String(), name=subkey, **kwargs)) @@ -339,7 +335,7 @@ def __set__(self, instance, value): raise TypeError('%r value must be tuple or list') try: - composite_texts = tuple(CompositeTuple(dict(composite_text), self.subkeys) for composite_text in value) + composite_texts = tuple(CompositeField(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) diff --git a/isis/model/tests/test_couchdb.py b/isis/model/tests/test_couchdb.py index 472cd6a..ce310e9 100644 --- a/isis/model/tests/test_couchdb.py +++ b/isis/model/tests/test_couchdb.py @@ -22,7 +22,7 @@ >>> class BookWithAttachment(CouchdbDocument): ... title = TextProperty(required=True, validator=text_validator) - ... authors = MultiCompositeTextProperty(required=False, subkeys='fl') + ... authors = MultiIsisCompositeTextProperty(required=False, subkeys='fl') ... cover = FileProperty() ... @@ -91,6 +91,7 @@ from isis.model import CouchdbDocument from isis.model import TextProperty, MultiTextProperty from isis.model import CompositeTextProperty, MultiCompositeTextProperty, ReferenceProperty, FileProperty +from isis.model import IsisCompositeTextProperty, MultiIsisCompositeTextProperty from isis.model.couchdb import _attach_updated, _attach_exists import couchdbkit From 406169e74b89d983e2ba4736abf3acc52821e66c Mon Sep 17 00:00:00 2001 From: Antonio Ribeiro Date: Tue, 31 May 2011 03:12:49 +0800 Subject: [PATCH 20/24] starting new isisdm example: using multitextproerty --- examples/bibitex/bibitex.py | 54 ++++++++++++++++++++++++++++++++++++ examples/bibitex/forms.py | 12 ++++++++ examples/bibitex/models.py | 17 ++++++++++++ examples/bibitex/template.pt | 15 ++++++++++ 4 files changed, 98 insertions(+) create mode 100644 examples/bibitex/bibitex.py create mode 100644 examples/bibitex/forms.py create mode 100644 examples/bibitex/models.py create mode 100644 examples/bibitex/template.pt diff --git a/examples/bibitex/bibitex.py b/examples/bibitex/bibitex.py new file mode 100644 index 0000000..79e4e2e --- /dev/null +++ b/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') \ No newline at end of file diff --git a/examples/bibitex/forms.py b/examples/bibitex/forms.py new file mode 100644 index 0000000..4588e8b --- /dev/null +++ b/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',)) \ No newline at end of file diff --git a/examples/bibitex/models.py b/examples/bibitex/models.py new file mode 100644 index 0000000..7481d95 --- /dev/null +++ b/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() + + diff --git a/examples/bibitex/template.pt b/examples/bibitex/template.pt new file mode 100644 index 0000000..cb157e0 --- /dev/null +++ b/examples/bibitex/template.pt @@ -0,0 +1,15 @@ + + + + + + + +
+ +
+ + \ No newline at end of file From f1f64f72a9322d55d947d4849bcc22fd6af063fb Mon Sep 17 00:00:00 2001 From: gustavofonseca Date: Tue, 31 May 2011 16:59:43 -0300 Subject: [PATCH 21/24] polishing the example app --- examples/bibitex/bibitex.py | 54 ++++++++++++++++++++++-------------- examples/bibitex/form.pt | 19 +++++++++++++ examples/bibitex/index.pt | 26 +++++++++++++++++ examples/bibitex/template.pt | 15 ---------- 4 files changed, 78 insertions(+), 36 deletions(-) create mode 100644 examples/bibitex/form.pt create mode 100644 examples/bibitex/index.pt delete mode 100644 examples/bibitex/template.pt diff --git a/examples/bibitex/bibitex.py b/examples/bibitex/bibitex.py index 79e4e2e..e4e5d13 100644 --- a/examples/bibitex/bibitex.py +++ b/examples/bibitex/bibitex.py @@ -10,29 +10,38 @@ import deform import couchdbkit +def new(request): + bibitex_form = BibitexForm.get_form() + if 'submit' in request.POST: + controls = request.POST.items() + try: + appstruct = bibitex_form.validate(controls) + except deform.ValidationFailure, e: + return render_to_response('bibitex:form.pt', + {'content': e.render()}) -def hello_world(request): - return Response('Hello world!') + bibitex = Bibitex.from_python(appstruct) + bibitex.save(db) -def goodbye_world(request): - return Response('Goodbye world!') + return Response('Saved under id: %s' % bibitex._id) + else: + if 'id' in request.matchdict: #edit + bibitex = Bibitex.get(db, request.matchdict['id']) + + return render_to_response('bibitex:form.pt', + {'content': bibitex_form.render(bibitex.to_python())}) + + return render_to_response('bibitex:form.pt', + {'content': bibitex_form.render()}) -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 index(request): + records = db.view('_all_docs', include_docs=True) -def view_entry(request): - pass + return render_to_response('bibitex:index.pt', + {'records':records}) if __name__ == '__main__': @@ -45,10 +54,13 @@ def view_entry(request): """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") + """Registering views and routes""" + config.add_view(view=index, route_name='index') + config.add_view(view=new, route_name='new') + config.add_view(view=new, route_name='edit') + config.add_route('index', '/') + config.add_route('new', '/new') + config.add_route('edit', '/edit/{id}') app = config.make_wsgi_app() - serve(app, host='0.0.0.0') \ No newline at end of file + serve(app, host='0.0.0.0:6543') \ No newline at end of file diff --git a/examples/bibitex/form.pt b/examples/bibitex/form.pt new file mode 100644 index 0000000..09b1bcc --- /dev/null +++ b/examples/bibitex/form.pt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/bibitex/index.pt b/examples/bibitex/index.pt new file mode 100644 index 0000000..d03f4ec --- /dev/null +++ b/examples/bibitex/index.pt @@ -0,0 +1,26 @@ + + + + + + +
+

ISISDM Sample Application

+

add

+ + + + + + + + + + + + +
Entry typeReference nameTitle
edit
+
+ + \ No newline at end of file diff --git a/examples/bibitex/template.pt b/examples/bibitex/template.pt deleted file mode 100644 index cb157e0..0000000 --- a/examples/bibitex/template.pt +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - -
- -
- - \ No newline at end of file From 5bf856b15b6cf45319b500d48ff37fe18c430f17 Mon Sep 17 00:00:00 2001 From: gustavofonseca Date: Tue, 31 May 2011 18:32:04 -0300 Subject: [PATCH 22/24] updating my working fork with the canonical repository --- examples/bibitex/template.pt | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 examples/bibitex/template.pt diff --git a/examples/bibitex/template.pt b/examples/bibitex/template.pt deleted file mode 100644 index cb157e0..0000000 --- a/examples/bibitex/template.pt +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - -
- -
- - \ No newline at end of file From ea29fd92387d7cce78c7c02b86cffa8433af5d8f Mon Sep 17 00:00:00 2001 From: gustavofonseca Date: Thu, 2 Jun 2011 17:02:40 -0300 Subject: [PATCH 23/24] creating a new release --- CHANGES.txt | 8 +++++++- setup.py | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 981cba2..59d190d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,12 @@ +0.2.0 +* Added CompositeTextProperty +* Added MultiCompositeTextProperty +* Added IsisCompositeTextProperty +* Added MultiIsisCompositeTextProperty + 0.1.1 --- * Added FileProperty datatype * Added BooleanProperty datatype * Embedded _rev and _id in automatic generated forms (couchdb api) -* Fixed bug that changes the _id attr of a persistent document \ No newline at end of file +* Fixed bug that changes the _id attr of a persistent document diff --git a/setup.py b/setup.py index c5b10f5..2821bce 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ requirements = ['colander', 'deform'] additional_files = ['README.txt', 'CHANGES.txt'] -setup(name='isisdm', version='0.1.1', +setup(name='isisdm', version='0.2.1', packages=find_packages(), long_description=README + "\n\n" + CHANGES, namespace_packages=['isis'], From de928df05e8ec4321f108734ed08c07582330cc1 Mon Sep 17 00:00:00 2001 From: gustavofonseca Date: Thu, 2 Jun 2011 18:13:55 -0300 Subject: [PATCH 24/24] creating a new release --- setup.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 2821bce..c7f526e 100644 --- a/setup.py +++ b/setup.py @@ -2,11 +2,15 @@ import os here = os.path.abspath(os.path.dirname(__file__)) -README = open(os.path.join(here, 'README.txt')).read() -CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() + +README_FILEPATH = os.path.join(here, 'README.txt') +CHANGES_FILEPATH = os.path.join(here, 'CHANGES.txt') + +README = open(README_FILEPATH).read() +CHANGES = open(CHANGES_FILEPATH).read() requirements = ['colander', 'deform'] -additional_files = ['README.txt', 'CHANGES.txt'] +additional_files = [README_FILEPATH, CHANGES_FILEPATH] setup(name='isisdm', version='0.2.1', packages=find_packages(),