diff options
author | Aleksey Lim <alsroot@sugarlabs.org> | 2014-03-12 11:17:00 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@sugarlabs.org> | 2014-03-12 11:17:00 (GMT) |
commit | ae31651d6ae31215db903530115bf340ae9f98f5 (patch) | |
tree | 7d5a5083eee504a74d4b5ca4c249959e83f78392 | |
parent | 47be127e9955013ddc5563013ded534cc3952ef1 (diff) |
More cleanups
* iterate metadata props in the same order all time;
* clear separation between original and posted props in db.Resource;
* access to assetes directory from blobs iface.
26 files changed, 320 insertions, 112 deletions
diff --git a/sugar_network/assets/__init__.py b/sugar_network/assets/__init__.py new file mode 100644 index 0000000..7ad1378 --- /dev/null +++ b/sugar_network/assets/__init__.py @@ -0,0 +1,19 @@ +# Copyright (C) 2014 Aleksey Lim +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from os.path import dirname + + +PATH = dirname(__file__) diff --git a/blobs/activity.svg b/sugar_network/assets/activity.svg index c5302fd..c5302fd 100644 --- a/blobs/activity.svg +++ b/sugar_network/assets/activity.svg diff --git a/blobs/book.svg b/sugar_network/assets/book.svg index 92fb811..92fb811 100644 --- a/blobs/book.svg +++ b/sugar_network/assets/book.svg diff --git a/blobs/favicon.ico b/sugar_network/assets/favicon.ico Binary files differindex 80e42ba..80e42ba 100644 --- a/blobs/favicon.ico +++ b/sugar_network/assets/favicon.ico diff --git a/blobs/group.svg b/sugar_network/assets/group.svg index c9a6b64..c9a6b64 100644 --- a/blobs/group.svg +++ b/sugar_network/assets/group.svg diff --git a/blobs/missing-logo.png b/sugar_network/assets/missing-logo.png Binary files differindex 98be121..98be121 100644 --- a/blobs/missing-logo.png +++ b/sugar_network/assets/missing-logo.png diff --git a/blobs/missing.png b/sugar_network/assets/missing.png Binary files differindex 91a65a8..91a65a8 100644 --- a/blobs/missing.png +++ b/sugar_network/assets/missing.png diff --git a/blobs/missing.svg b/sugar_network/assets/missing.svg index 7e6a568..7e6a568 100644 --- a/blobs/missing.svg +++ b/sugar_network/assets/missing.svg diff --git a/blobs/package-logo.png b/sugar_network/assets/package-logo.png Binary files differindex c6cf086..c6cf086 100644 --- a/blobs/package-logo.png +++ b/sugar_network/assets/package-logo.png diff --git a/blobs/package.png b/sugar_network/assets/package.png Binary files differindex 24bd5ac..24bd5ac 100644 --- a/blobs/package.png +++ b/sugar_network/assets/package.png diff --git a/blobs/package.svg b/sugar_network/assets/package.svg index a5fd32d..a5fd32d 100644 --- a/blobs/package.svg +++ b/sugar_network/assets/package.svg diff --git a/sugar_network/db/blobs.py b/sugar_network/db/blobs.py index 084ffde..cfbe517 100644 --- a/sugar_network/db/blobs.py +++ b/sugar_network/db/blobs.py @@ -20,7 +20,7 @@ import mimetypes from contextlib import contextmanager from os.path import exists, abspath, join, dirname, isdir -from sugar_network import toolkit +from sugar_network import toolkit, assets from sugar_network.toolkit.router import File from sugar_network.toolkit import http, ranges, enforce @@ -40,11 +40,16 @@ class Blobs(object): def root(self): return self._root - def path(self, *args): - if len(args) == 1 and len(args[0]) == 40 and '.' not in args[0]: - return self._blob_path(args[0]) - else: - return join(self._root, 'files', *args) + def path(self, path=None): + if path is None: + return join(self._root, 'files') + if isinstance(path, basestring): + path = path.split(os.sep) + if len(path) == 1 and len(path[0]) == 40 and '.' not in path[0]: + return self._blob_path(path[0]) + if path[0] == 'assets': + return join(assets.PATH, *path[1:]) + return join(self._root, 'files', *path) def post(self, content, mime_type=None, digest_to_assert=None, meta=None): if meta is None: @@ -110,9 +115,11 @@ class Blobs(object): return File(path, digest, _read_meta(path)) elif isdir(path): return _lsdir(path, digest) + elif exists(path): + return File(path, digest) def delete(self, path): - self._delete(path, None) + self._delete(self.path(path), None) def populate(self, path=None, recursive=True): for __ in self.diff([[1, None]], path or '', recursive): @@ -127,7 +134,7 @@ class Blobs(object): enforce(not [i for i in path if i == '..'], http.BadRequest, 'Relative paths are not allowed') is_files = True - root = self.path(*path) + root = self.path(path) checkin_seqno = None for root, __, files in os.walk(root): @@ -203,7 +210,6 @@ class Blobs(object): os.utime(path, (seqno, seqno)) def _delete(self, path, seqno): - path = self.path(path) if exists(path + _META_SUFFIX): if seqno is None: seqno = self._seqno.next() diff --git a/sugar_network/db/directory.py b/sugar_network/db/directory.py index 3ef4b91..9ebf907 100644 --- a/sugar_network/db/directory.py +++ b/sugar_network/db/directory.py @@ -202,7 +202,7 @@ class Directory(object): if doc.post_seqno is not None and doc.exists: # No need in after-merge event, further commit event # is enough to avoid increasing events flow - self._index.store(guid, doc.props, self._preindex) + self._index.store(guid, doc.origs, self._preindex) return seqno @@ -227,18 +227,16 @@ class Directory(object): doc = self.resource(guid, self._storage.get(guid), changes) for prop in self.metadata: enforce(doc[prop] is not None, 'Empty %r property', prop) - return doc.props + return doc def _prestore(self, guid, changes, event): - doc = self.resource(guid, self._storage.get(guid)) + doc = self.resource(guid, self._storage.get(guid), posts=changes) doc.post_seqno = self._seqno.next() + for prop in changes.keys(): + doc.post(prop, changes[prop]) for prop in self.metadata.keys(): - value = changes.get(prop) - if value is None: - enforce(doc[prop] is not None, 'Empty %r property', prop) - else: - doc.post(prop, value) - return doc.props + enforce(doc[prop] is not None, 'Empty %r property', prop) + return doc def _postdelete(self, guid, event): self._storage.delete(guid) diff --git a/sugar_network/db/metadata.py b/sugar_network/db/metadata.py index ecefdab..31cace1 100644 --- a/sugar_network/db/metadata.py +++ b/sugar_network/db/metadata.py @@ -102,11 +102,17 @@ class Metadata(dict): self[prop.name] = prop + self._keys = dict.keys(self) + self._keys.sort() + @property def name(self): """Resource type name.""" return self._name + def keys(self): + return self._keys + def __getitem__(self, prop_name): enforce(prop_name in self, http.NotFound, 'There is no %r property in %r', prop_name, self.name) diff --git a/sugar_network/db/resource.py b/sugar_network/db/resource.py index 9d94929..d17637d 100644 --- a/sugar_network/db/resource.py +++ b/sugar_network/db/resource.py @@ -29,8 +29,9 @@ class Resource(object): #: Whether these resources should be migrated from slave-to-master only one_way = False - def __init__(self, guid, record, cached_props=None): - self.props = cached_props or {} + def __init__(self, guid, record, origs=None, posts=None): + self.origs = origs or {} + self.posts = posts or {} self.guid = guid self.is_new = not bool(guid) self.record = record @@ -69,7 +70,7 @@ class Resource(object): @layer.setter def layer(self, value): - orig = self['layer'] + orig = self.orig('layer') if 'deleted' in value: if this.request.method != 'POST' and 'deleted' not in orig: self.deleted() @@ -91,7 +92,7 @@ class Resource(object): def restored(self): pass - def get(self, prop): + def get(self, prop, default=None): """Get document's property value. :param prop: @@ -100,15 +101,31 @@ class Resource(object): `prop` value """ + value = self.posts.get(prop) + if value is None: + value = self.orig(prop) + if value is None: + value = default + return value + + def orig(self, prop): + """Get document's property original value. + + :param prop: + property name to get value + :returns: + `prop` value + + """ prop = self.metadata[prop] - value = self.props.get(prop.name) + value = self.origs.get(prop.name) if value is None and self.record is not None: meta = self.record.get(prop.name) if meta is not None: value = meta.get('value') else: value = prop.default - self.props[prop.name] = value + self.origs[prop.name] = value return value def properties(self, props): @@ -168,18 +185,15 @@ class Resource(object): for agg in value.values(): agg['seqno'] = self.post_seqno if isinstance(prop, Composite): - old_value = self[prop.name] - if old_value: - old_value.update(value) - value = old_value + orig_value = self.orig(prop.name) + if orig_value: + orig_value.update(value) + value = orig_value self.record.set(prop.name, value=value, seqno=self.post_seqno, **meta) - self.props[prop.name] = value - - def _set(self, prop, value): - self.props[prop] = value + self.posts[prop.name] = value def __contains__(self, prop): - return prop in self.props + return prop in self.origs or prop in self.posts def __getitem__(self, prop): return self.get(prop) diff --git a/sugar_network/db/routes.py b/sugar_network/db/routes.py index 153e0a7..e1f190c 100644 --- a/sugar_network/db/routes.py +++ b/sugar_network/db/routes.py @@ -40,8 +40,8 @@ class Routes(object): @route('POST', [None], acl=ACL.AUTH, mime_type='application/json') def create(self, request): with self._post(request, ACL.CREATE) as doc: - self.on_create(request, doc.props) - self.volume[request.resource].create(doc.props) + self.on_create(request, doc.posts) + self.volume[request.resource].create(doc.posts) self.after_post(doc) return doc['guid'] @@ -78,10 +78,10 @@ class Routes(object): @route('PUT', [None, None], acl=ACL.AUTH | ACL.AUTHOR) def update(self, request): with self._post(request, ACL.WRITE) as doc: - if not doc.props: + if not doc.posts: return - self.on_update(request, doc.props) - self.volume[request.resource].update(doc.guid, doc.props) + self.on_update(request, doc.posts) + self.volume[request.resource].update(doc.guid, doc.posts) self.after_post(doc) @route('PUT', [None, None, None], acl=ACL.AUTH | ACL.AUTHOR) @@ -209,32 +209,28 @@ class Routes(object): enforce(_GUID_RE.match(guid) is not None, http.BadRequest, 'Malformed %s GUID', guid) else: - doc.props['guid'] = toolkit.uuid() + doc.posts['guid'] = toolkit.uuid() for name, prop in directory.metadata.items(): if name not in content and prop.default is not None: - doc.props[name] = prop.default - orig = None - this.resource = doc + doc.posts[name] = prop.default else: doc = directory.get(request.guid) - orig = directory.get(request.guid) - this.resource = orig + this.resource = doc - def teardown(new): - if orig is None: - return - for name, orig_value in orig.props.items(): - if doc[name] == orig_value: - continue - prop = directory.metadata[name] - prop.teardown(doc[name] if new else orig_value) + def teardown(new, old): + for name, value in new.items(): + if old.get(name) != value: + directory.metadata[name].teardown(value) try: for name, value in content.items(): prop = directory.metadata[name] - prop.assert_access(access, orig[name] if orig else None) + prop.assert_access(access, doc.orig(name)) + if value is None: + doc.posts[name] = prop.default + continue try: - doc.props[name] = prop.typecast(value) + doc.posts[name] = prop.typecast(value) except Exception, error: error = 'Value %r for %r property is invalid: %s' % \ (value, prop.name, error) @@ -242,10 +238,10 @@ class Routes(object): raise http.BadRequest(error) yield doc except Exception: - teardown(True) + teardown(doc.posts, doc.origs) raise else: - teardown(False) + teardown(doc.origs, doc.posts) def _preget(self, request): reply = request.get('reply') diff --git a/sugar_network/model/__init__.py b/sugar_network/model/__init__.py index 5068a63..77a322c 100644 --- a/sugar_network/model/__init__.py +++ b/sugar_network/model/__init__.py @@ -58,6 +58,9 @@ RESOURCES = ( 'sugar_network.model.user', ) +ICON_SIZE = 55 +LOGO_SIZE = 140 + _logger = logging.getLogger('model') @@ -118,16 +121,6 @@ def generate_node_stats(volume): volume['post'].update(topic.guid, {'rating': rating}) -def populate_context_images(props, svg): - if 'guid' in props: - from sugar_network.toolkit.sugar import color_svg - svg = color_svg(svg, props['guid']) - blobs = this.volume.blobs - props['artifact_icon'] = blobs.post(svg, 'image/svg+xml').digest - props['icon'] = blobs.post(svg_to_png(svg, 55, 55), 'image/png').digest - props['logo'] = blobs.post(svg_to_png(svg, 140, 140), 'image/png').digest - - def load_bundle(blob, context=None, initial=False, extra_deps=None): contexts = this.volume['context'] context_type = None @@ -215,7 +208,7 @@ def load_bundle(blob, context=None, initial=False, extra_deps=None): patch = context_doc.format_patch(context_meta) if patch: this.call(method='PUT', path=['context', context], content=patch) - context_doc.props.update(patch) + context_doc.posts.update(patch) # TRANS: Release notes title title = i18n._('%(name)s %(version)s release') else: @@ -250,8 +243,19 @@ def _load_context_metadata(bundle, spec): result['guid'] = spec['context'] try: + from sugar_network.toolkit.sugar import color_svg + icon_file = bundle.extractfile(join(bundle.rootdir, spec['icon'])) - populate_context_images(result, icon_file.read()) + svg = color_svg(icon_file.read(), result['guid']) + blobs = this.volume.blobs + + result['artefact_icon'] = \ + blobs.post(svg, 'image/svg+xml').digest + result['icon'] = \ + blobs.post(svg_to_png(svg, ICON_SIZE), 'image/png').digest + result['logo'] = \ + blobs.post(svg_to_png(svg, LOGO_SIZE), 'image/png').digest + icon_file.close() except Exception: exception(_logger, 'Failed to load icon') diff --git a/sugar_network/model/context.py b/sugar_network/model/context.py index 951aad1..3aceacc 100644 --- a/sugar_network/model/context.py +++ b/sugar_network/model/context.py @@ -16,6 +16,7 @@ from sugar_network import db, model from sugar_network.toolkit.coroutine import this from sugar_network.toolkit.router import ACL +from sugar_network.toolkit import svg_to_png class Context(db.Resource): @@ -27,8 +28,34 @@ class Context(db.Resource): @type.setter def type(self, value): - if 'package' in value and 'common' not in self['layer']: - self.post('layer', self['layer'] + ['common']) + if 'package' in value: + self.post('icon', 'assets/package.png') + self.post('logo', 'assets/package-logo.png') + self.post('artefact_icon', 'assets/package.svg') + return value + + svg = None + blobs = this.volume.blobs + if not self['artefact_icon']: + for type_ in ('activity', 'book', 'group'): + if type_ in value: + with file(blobs.get('assets/%s.svg' % type_).path) as f: + svg = f.read() + from sugar_network.toolkit.sugar import color_svg + svg = color_svg(svg, self['guid']) + self.post('artefact_icon', + blobs.post(svg, 'image/svg+xml').digest) + break + for prop, png, size in ( + ('icon', 'assets/missing.png', model.ICON_SIZE), + ('logo', 'assets/missing-logo.svg', model.LOGO_SIZE), + ): + if self[prop]: + continue + if svg is not None: + png = blobs.post(svg_to_png(svg, size), 'image/png').digest + self.post(prop, png) + return value @db.indexed_property(db.Localized, slot=1, prefix='S', full_text=True) @@ -51,17 +78,15 @@ class Context(db.Resource): def mime_types(self, value): return value - @db.stored_property(db.Blob, mime_type='image/png', default='missing.png') + @db.stored_property(db.Blob, mime_type='image/png') def icon(self, value): return value - @db.stored_property(db.Blob, mime_type='image/svg+xml', - default='missing.svg') - def artifact_icon(self, value): + @db.stored_property(db.Blob, mime_type='image/svg+xml') + def artefact_icon(self, value): return value - @db.stored_property(db.Blob, mime_type='image/png', - default='missing-logo.png') + @db.stored_property(db.Blob, mime_type='image/png') def logo(self, value): return value diff --git a/sugar_network/model/post.py b/sugar_network/model/post.py index 107f354..21046f2 100644 --- a/sugar_network/model/post.py +++ b/sugar_network/model/post.py @@ -70,7 +70,7 @@ class Post(db.Resource): return value @db.stored_property(db.Blob, mime_type='image/png', - default='missing-logo.png') + default='assets/missing-logo.png') def preview(self, value): return value diff --git a/sugar_network/toolkit/__init__.py b/sugar_network/toolkit/__init__.py index 89a9d0f..67ee7da 100644 --- a/sugar_network/toolkit/__init__.py +++ b/sugar_network/toolkit/__init__.py @@ -432,10 +432,13 @@ class mkdtemp(str): shutil.rmtree(self) -def svg_to_png(data, w, h): +def svg_to_png(data, w, h=None): import rsvg import cairo + if h is None: + h = w + svg = rsvg.Handle(data=data) surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) context = cairo.Context(surface) diff --git a/tests/integration/master_personal.py b/tests/integration/master_personal.py index 7bfa6aa..7057c0b 100755 --- a/tests/integration/master_personal.py +++ b/tests/integration/master_personal.py @@ -77,7 +77,7 @@ class MasterPersonalTest(tests.Test): 'title': 'title_1', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo1', 'layer': 'pilot', }) @@ -86,7 +86,7 @@ class MasterPersonalTest(tests.Test): 'title': 'title_2', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo2', 'layer': 'pilot', }) @@ -97,7 +97,7 @@ class MasterPersonalTest(tests.Test): 'title': 'title_3', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo3', 'layer': 'pilot', }) @@ -106,7 +106,7 @@ class MasterPersonalTest(tests.Test): 'title': 'title_4', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo4', 'layer': 'pilot', }) @@ -133,7 +133,7 @@ class MasterPersonalTest(tests.Test): 'title': 'title_5', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo5', 'layer': 'pilot', }) @@ -146,7 +146,7 @@ class MasterPersonalTest(tests.Test): 'title': 'title_6', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo6', 'layer': 'pilot', }) diff --git a/tests/integration/master_slave.py b/tests/integration/master_slave.py index c9981df..8be40b0 100755 --- a/tests/integration/master_slave.py +++ b/tests/integration/master_slave.py @@ -65,7 +65,7 @@ class MasterSlaveTest(tests.Test): 'title': 'title1', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo1', 'layer': 'pilot', }) @@ -76,7 +76,7 @@ class MasterSlaveTest(tests.Test): 'title': 'title2', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo2', 'layer': 'pilot', }) @@ -117,7 +117,7 @@ class MasterSlaveTest(tests.Test): 'title': 'title3', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo3', 'layer': 'pilot', }) @@ -131,7 +131,7 @@ class MasterSlaveTest(tests.Test): 'title': 'title4', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo4', 'layer': 'pilot', }) @@ -191,7 +191,7 @@ class MasterSlaveTest(tests.Test): 'title': 'title_1', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo1', 'layer': 'pilot', }) @@ -200,7 +200,7 @@ class MasterSlaveTest(tests.Test): 'title': 'title_2', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo2', 'layer': 'pilot', }) @@ -211,7 +211,7 @@ class MasterSlaveTest(tests.Test): 'title': 'title_3', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo3', 'layer': 'pilot', }) @@ -220,7 +220,7 @@ class MasterSlaveTest(tests.Test): 'title': 'title_4', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo4', 'layer': 'pilot', }) @@ -245,7 +245,7 @@ class MasterSlaveTest(tests.Test): 'title': 'title_5', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo5', 'layer': 'pilot', }) @@ -258,7 +258,7 @@ class MasterSlaveTest(tests.Test): 'title': 'title_6', 'summary': 'summary', 'description': 'description', - 'artifact_icon': 'artifact_icon', + 'artefact_icon': 'artefact_icon', 'logo': 'logo6', 'layer': 'pilot', }) diff --git a/tests/units/db/resource.py b/tests/units/db/resource.py index 4870418..64187aa 100755 --- a/tests/units/db/resource.py +++ b/tests/units/db/resource.py @@ -379,7 +379,10 @@ class ResourceTest(tests.Test): def test_wipe(self): class Document(db.Resource): - pass + + @db.stored_property() + def prop(self, value): + return value directory = Directory(tests.tmpdir, Document, IndexWriter, _SessionSeqno()) guid = directory.create({'prop': '1'}) @@ -392,6 +395,93 @@ class ResourceTest(tests.Test): self.assertEqual([], [i.guid for i in directory.find()[0]]) assert not exists('db/document') + def test_ChangePassedPropsInSetters(self): + + class Document(db.Resource): + + @db.stored_property() + def prop1(self, value): + return value + + @db.stored_property() + def prop2(self, value): + return value + + @prop2.setter + def prop2(self, value): + self.post('prop1', value + '!') + self.post('prop3', value + '!') + return value + + @db.stored_property() + def prop3(self, value): + return value + + directory = Directory(tests.tmpdir, Document, IndexWriter, _SessionSeqno()) + guid = directory.create({'guid': 'guid', 'prop1': 'set1', 'prop2': 'set2', 'prop3': 'set3'}) + + doc = directory.get(guid) + self.assertEqual('set2!', doc['prop1']) + self.assertEqual('set2!', doc['prop3']) + + def test_ChangeDefaultPropsInSetters(self): + + class Document(db.Resource): + + @db.stored_property(default='default') + def prop1(self, value): + return value + + @db.stored_property() + def prop2(self, value): + return value + + @prop2.setter + def prop2(self, value): + self.post('prop1', value + '!') + self.post('prop3', value + '!') + return value + + @db.stored_property(default='default') + def prop3(self, value): + return value + + directory = Directory(tests.tmpdir, Document, IndexWriter, _SessionSeqno()) + guid = directory.create({'guid': 'guid', 'prop2': 'set2'}) + + doc = directory.get(guid) + self.assertEqual('set2!', doc['prop1']) + self.assertEqual('set2!', doc['prop3']) + + def test_SetMissedMandatoryPropsInSetters(self): + + class Document(db.Resource): + + @db.stored_property() + def prop1(self, value): + return value + + @db.stored_property() + def prop2(self, value): + return value + + @prop2.setter + def prop2(self, value): + self.post('prop1', value + '!') + self.post('prop3', value + '!') + return value + + @db.stored_property() + def prop3(self, value): + return value + + directory = Directory(tests.tmpdir, Document, IndexWriter, _SessionSeqno()) + guid = directory.create({'guid': 'guid', 'prop2': 'set2'}) + + doc = directory.get(guid) + self.assertEqual('set2!', doc['prop1']) + self.assertEqual('set2!', doc['prop3']) + class _SessionSeqno(object): diff --git a/tests/units/db/routes.py b/tests/units/db/routes.py index 2e1cabb..9f9131e 100755 --- a/tests/units/db/routes.py +++ b/tests/units/db/routes.py @@ -964,7 +964,7 @@ class RoutesTest(tests.Test): @prop.setter def prop(self, value): - return value + (self['prop'] or 0) + return value + (self.orig('prop') or 0) volume = db.Volume(tests.tmpdir, [TestDocument]) router = Router(db.Routes(volume)) @@ -1114,6 +1114,10 @@ class RoutesTest(tests.Test): def name(self, value): return value + @db.stored_property() + def pubkey(self, value): + return value + class Document(db.Resource): pass @@ -1146,6 +1150,10 @@ class RoutesTest(tests.Test): def name(self, value): return value + @db.stored_property() + def pubkey(self, value): + return value + class Document(db.Resource): pass @@ -1189,6 +1197,10 @@ class RoutesTest(tests.Test): def name(self, value): return value + @db.stored_property() + def pubkey(self, value): + return value + class Document(db.Resource): pass @@ -1272,6 +1284,10 @@ class RoutesTest(tests.Test): def name(self, value): return value + @db.stored_property() + def pubkey(self, value): + return value + class Document(db.Resource): pass @@ -1341,6 +1357,10 @@ class RoutesTest(tests.Test): def name(self, value): return value + @db.stored_property() + def pubkey(self, value): + return value + class Document(db.Resource): pass @@ -1394,6 +1414,10 @@ class RoutesTest(tests.Test): def name(self, value): return value + @db.stored_property() + def pubkey(self, value): + return value + class Document(db.Resource): pass @@ -1751,11 +1775,11 @@ class RoutesTest(tests.Test): class Document(db.Resource): - @db.stored_property(db.Aggregated, prefix='A', full_text=True) + @db.indexed_property(db.Aggregated, prefix='A', full_text=True) def comments(self, value): return value - @db.stored_property(prefix='B', full_text=False, default='') + @db.indexed_property(prefix='B', full_text=False, default='') def prop(self, value): return value diff --git a/tests/units/model/context.py b/tests/units/model/context.py index 16faa11..d33cb73 100755 --- a/tests/units/model/context.py +++ b/tests/units/model/context.py @@ -13,12 +13,13 @@ from sugar_network.client import IPCConnection, Connection, keyfile from sugar_network.model.context import Context from sugar_network.toolkit.coroutine import this from sugar_network.toolkit.router import Request -from sugar_network.toolkit import i18n, http, coroutine, enforce +from sugar_network.toolkit.sugar import color_svg +from sugar_network.toolkit import svg_to_png, i18n, http, coroutine, enforce class ContextTest(tests.Test): - def test_SetCommonLayerForPackages(self): + def test_PackageImages(self): volume = self.start_master() conn = Connection(auth=http.SugarAuth(keyfile.value)) @@ -28,25 +29,47 @@ class ContextTest(tests.Test): 'summary': 'summary', 'description': 'description', }) - self.assertEqual(['common'], conn.get(['context', guid, 'layer'])) + + assert conn.request('GET', ['context', guid, 'artefact_icon']).content == file(volume.blobs.get('assets/package.svg').path).read() + assert conn.request('GET', ['context', guid, 'icon']).content == file(volume.blobs.get('assets/package.png').path).read() + assert conn.request('GET', ['context', guid, 'logo']).content == file(volume.blobs.get('assets/package-logo.png').path).read() + + def test_ContextImages(self): + volume = self.start_master() + conn = Connection(auth=http.SugarAuth(keyfile.value)) guid = conn.post(['context'], { - 'type': 'package', + 'type': 'activity', 'title': 'title', 'summary': 'summary', 'description': 'description', - 'layer': 'foo', }) - self.assertEqual(['foo', 'common'], conn.get(['context', guid, 'layer'])) + svg = color_svg(file(volume.blobs.get('assets/activity.svg').path).read(), guid) + assert conn.request('GET', ['context', guid, 'artefact_icon']).content == svg + assert conn.request('GET', ['context', guid, 'icon']).content == svg_to_png(svg, 55).getvalue() + assert conn.request('GET', ['context', guid, 'logo']).content == svg_to_png(svg, 140).getvalue() guid = conn.post(['context'], { - 'type': 'package', + 'type': 'book', + 'title': 'title', + 'summary': 'summary', + 'description': 'description', + }) + svg = color_svg(file(volume.blobs.get('assets/book.svg').path).read(), guid) + assert conn.request('GET', ['context', guid, 'artefact_icon']).content == svg + assert conn.request('GET', ['context', guid, 'icon']).content == svg_to_png(svg, 55).getvalue() + assert conn.request('GET', ['context', guid, 'logo']).content == svg_to_png(svg, 140).getvalue() + + guid = conn.post(['context'], { + 'type': 'group', 'title': 'title', 'summary': 'summary', 'description': 'description', - 'layer': ['common', 'bar'], }) - self.assertEqual(['common', 'bar'], conn.get(['context', guid, 'layer'])) + svg = color_svg(file(volume.blobs.get('assets/group.svg').path).read(), guid) + assert conn.request('GET', ['context', guid, 'artefact_icon']).content == svg + assert conn.request('GET', ['context', guid, 'icon']).content == svg_to_png(svg, 55).getvalue() + assert conn.request('GET', ['context', guid, 'logo']).content == svg_to_png(svg, 140).getvalue() def test_Releases(self): volume = self.start_master() diff --git a/tests/units/model/model.py b/tests/units/model/model.py index a180f30..49fd1a3 100755 --- a/tests/units/model/model.py +++ b/tests/units/model/model.py @@ -74,8 +74,8 @@ class ModelTest(tests.Test): 'content-type': 'application/vnd.olpc-sugar', 'content-disposition': 'attachment; filename="Activity-1%s"' % (mimetypes.guess_extension('application/vnd.olpc-sugar') or ''), 'content-length': str(len(bundle)), - 'x-seqno': '4', - }, blobs.get(blob.digest)) + 'x-seqno': '7', + }, dict(blobs.get(blob.digest))) self.assertEqual('bundle_id', context) self.assertEqual([[1], 0], release['version']) self.assertEqual('developer', release['stability']) @@ -129,8 +129,8 @@ class ModelTest(tests.Test): 'content-type': 'application/pdf', 'content-disposition': 'attachment; filename="NonActivity-2.pdf"', 'content-length': str(len(bundle)), - 'x-seqno': '4', - }, blobs.get(blob.digest)) + 'x-seqno': '7', + }, dict(blobs.get(blob.digest))) self.assertEqual('bundle_id', context) self.assertEqual([[2], 0], release['version']) self.assertEqual(['GPL'], release['license']) @@ -409,7 +409,7 @@ class ModelTest(tests.Test): 'en': 'It has features one would expect of a standard image viewer, like zoom, rotate, etc.', }, context['description']) - self.assertEqual(svg, file(blobs.get(context['artifact_icon']).path).read()) + self.assertEqual(svg, file(blobs.get(context['artefact_icon']).path).read()) assert context['icon'] != 'missing.png' assert context['logo'] != 'missing-logo.png' self.assertEqual('http://wiki.sugarlabs.org/go/Activities/Image_Viewer', context['homepage']) |