diff options
author | Aleksey Lim <alsroot@sugarlabs.org> | 2014-02-03 10:08:50 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@sugarlabs.org> | 2014-02-03 10:08:50 (GMT) |
commit | b4b008c1f302059221a1a43ed237e6d562ec7f97 (patch) | |
tree | 1c8b4dd71d6c165c461bbefe0847154ea333b91b /sugar_network | |
parent | 726ac1d66321ee7ac6b4cc7eff17001b5aeb6c5e (diff) |
Keep Post comments in aggregated property
Diffstat (limited to 'sugar_network')
-rw-r--r-- | sugar_network/db/__init__.py | 2 | ||||
-rw-r--r-- | sugar_network/db/directory.py | 29 | ||||
-rw-r--r-- | sugar_network/db/index.py | 15 | ||||
-rw-r--r-- | sugar_network/db/metadata.py | 8 | ||||
-rw-r--r-- | sugar_network/db/routes.py | 50 | ||||
-rw-r--r-- | sugar_network/model/post.py | 9 | ||||
-rw-r--r-- | sugar_network/node/routes.py | 25 | ||||
-rw-r--r-- | sugar_network/toolkit/router.py | 16 |
8 files changed, 125 insertions, 29 deletions
diff --git a/sugar_network/db/__init__.py b/sugar_network/db/__init__.py index b6bde81..2f22a36 100644 --- a/sugar_network/db/__init__.py +++ b/sugar_network/db/__init__.py @@ -351,7 +351,7 @@ Volume from sugar_network.db.metadata import \ indexed_property, stored_property, blob_property, \ - Property, StoredProperty, BlobProperty, IndexedProperty + Property, StoredProperty, BlobProperty, IndexedProperty, AggregatedType from sugar_network.db.index import index_flush_timeout, \ index_flush_threshold, index_write_queue from sugar_network.db.resource import Resource diff --git a/sugar_network/db/directory.py b/sugar_network/db/directory.py index 96a2923..944f73a 100644 --- a/sugar_network/db/directory.py +++ b/sugar_network/db/directory.py @@ -24,6 +24,7 @@ from sugar_network.toolkit.router import ACL from sugar_network.db.storage import Storage from sugar_network.db.metadata import BlobProperty, Metadata, GUID_PREFIX from sugar_network.db.metadata import IndexedProperty, StoredProperty +from sugar_network.db.metadata import AggregatedType from sugar_network.toolkit import http, exception, enforce @@ -278,9 +279,16 @@ class Directory(object): if isinstance(prop, BlobProperty): del meta['seqno'] else: - meta = {'mtime': meta['mtime'], - 'value': meta.get('value'), - } + value = meta.get('value') + if prop.typecast is AggregatedType: + value_ = {} + for key, agg in value.items(): + aggseqno = agg.pop('seqno') + if aggseqno >= start and \ + (not end or aggseqno <= end): + value_[key] = agg + value = value_ + meta = {'mtime': meta['mtime'], 'value': value} yield name, meta, seqno yield doc.guid, patch() @@ -303,6 +311,12 @@ class Directory(object): else: meta['seqno'] = (orig_meta or {}).get('seqno') or 0 meta.update(kwargs) + if self.metadata.get(prop).typecast is AggregatedType: + for agg in meta['value'].values(): + agg['seqno'] = meta['seqno'] + if orig_meta: + orig_meta['value'].update(meta['value']) + meta['value'] = orig_meta['value'] merge[prop] = meta if op is not None: patch[prop] = meta.get('value') @@ -365,7 +379,14 @@ class Directory(object): value = meta['value'] changes[name] = prop.default if value is None else value else: - if prop.localized: + if prop.typecast is AggregatedType: + for aggvalue in value.values(): + aggvalue['seqno'] = seqno + if existed: + value_ = record.get(name)['value'] + value_.update(value) + value = value_ + elif prop.localized: if not isinstance(value, dict): value = {toolkit.default_lang(): value} if existed and \ diff --git a/sugar_network/db/index.py b/sugar_network/db/index.py index aa75f80..7ff43bb 100644 --- a/sugar_network/db/index.py +++ b/sugar_network/db/index.py @@ -349,9 +349,9 @@ class IndexWriter(IndexReader): _logger.debug('Index %r object: %r', self.metadata.name, properties) - document = xapian.Document() + doc = xapian.Document() term_generator = xapian.TermGenerator() - term_generator.set_document(document) + term_generator.set_document(doc) for name, prop in self._props.items(): value = guid \ @@ -366,21 +366,20 @@ class IndexWriter(IndexReader): slotted_value = toolkit.gettext(value) or '' else: slotted_value = next(_fmt_prop_value(prop, value)) - document.add_value(prop.slot, slotted_value) + doc.add_value(prop.slot, slotted_value) if prop.prefix or prop.full_text: for value_ in _fmt_prop_value(prop, value): if prop.prefix: if prop.boolean: - document.add_boolean_term( - _term(prop.prefix, value_)) + doc.add_boolean_term(_term(prop.prefix, value_)) else: - document.add_term(_term(prop.prefix, value_)) + doc.add_term(_term(prop.prefix, value_)) if prop.full_text: term_generator.index_text(value_, 1, prop.prefix or '') term_generator.increase_termpos() - self._db.replace_document(_term(GUID_PREFIX, guid), document) + self._db.replace_document(_term(GUID_PREFIX, guid), doc) self._pending_updates += 1 if post_cb is not None: @@ -475,7 +474,7 @@ def _fmt_prop_value(prop, value): for i in value: for j in fmt(i): yield j - else: + elif value is not None: yield str(value) return fmt(value if prop.fmt is None else prop.fmt(value)) diff --git a/sugar_network/db/metadata.py b/sugar_network/db/metadata.py index 7cba5ce..55942a7 100644 --- a/sugar_network/db/metadata.py +++ b/sugar_network/db/metadata.py @@ -58,6 +58,10 @@ stored_property = lambda ** kwargs: indexed_property(StoredProperty, **kwargs) blob_property = lambda ** kwargs: indexed_property(BlobProperty, **kwargs) +class AggregatedType(dict): + pass + + class Metadata(dict): """Structure to describe the document. @@ -107,8 +111,8 @@ class Metadata(dict): return self._name def __getitem__(self, prop_name): - enforce(prop_name in self, 'There is no %r property in %r', - prop_name, self.name) + enforce(prop_name in self, http.NotFound, + 'There is no %r property in %r', prop_name, self.name) return dict.__getitem__(self, prop_name) diff --git a/sugar_network/db/routes.py b/sugar_network/db/routes.py index 123e001..19ad26c 100644 --- a/sugar_network/db/routes.py +++ b/sugar_network/db/routes.py @@ -25,6 +25,7 @@ from contextlib import contextmanager from os.path import exists from sugar_network import toolkit +from sugar_network.db.metadata import AggregatedType from sugar_network.db.metadata import BlobProperty, StoredProperty, LIST_TYPES from sugar_network.toolkit.router import Blob, ACL, route from sugar_network.toolkit import http, enforce @@ -114,6 +115,52 @@ class Routes(object): def get_prop_meta(self, request, response): self._prop_meta(request, response) + @route('POST', [None, None, None], + acl=ACL.AUTH, mime_type='application/json') + def insert_to_aggprop(self, request): + content = request.content or {} + enforce(isinstance(content, dict), http.BadRequest, 'Invalid value') + + directory = self.volume[request.resource] + prop = directory.metadata[request.prop] + + enforce(prop.typecast is AggregatedType, http.BadRequest, + 'Property is not aggregated') + prop.assert_access(ACL.INSERT) + self.on_aggprop_update(request, prop, None) + + if request.principal: + authors = content['author'] = {} + self._useradd(authors, request.principal, ACL.ORIGINAL) + guid = content.pop('guid') if 'guid' in content else toolkit.uuid() + props = {request.prop: {guid: content}} + event = {} + self.on_update(request, props, event) + directory.update(request.guid, props, event) + + return guid + + @route('DELETE', [None, None, None, None], + acl=ACL.AUTH, mime_type='application/json') + def remove_from_aggprop(self, request): + directory = self.volume[request.resource] + doc = directory.get(request.guid) + prop = directory.metadata[request.prop] + + enforce(prop.typecast is AggregatedType, http.BadRequest, + 'Property is not aggregated') + prop.assert_access(ACL.REMOVE) + + guid = request.path[3] + enforce(guid in doc[request.prop], http.NotFound, + 'No such aggregated item') + self.on_aggprop_update(request, prop, doc[request.prop][guid]) + + props = {request.prop: {guid: {}}} + event = {} + self.on_update(request, props, event) + directory.update(request.guid, props, event) + @route('PUT', [None, None], cmd='useradd', arguments={'role': 0}, acl=ACL.AUTH | ACL.AUTHOR) def useradd(self, request, user, role): @@ -145,6 +192,9 @@ class Routes(object): def on_update(self, request, props, event): props['mtime'] = int(time.time()) + def on_aggprop_update(self, request, prop, value): + pass + def after_post(self, doc): pass diff --git a/sugar_network/model/post.py b/sugar_network/model/post.py index 602ad02..fda366f 100644 --- a/sugar_network/model/post.py +++ b/sugar_network/model/post.py @@ -45,7 +45,7 @@ class Post(db.Resource): def title(self, value): return value - @db.indexed_property(prefix='D', full_text=True, localized=True, + @db.indexed_property(prefix='M', full_text=True, localized=True, acl=ACL.CREATE | ACL.READ) def message(self, value): return value @@ -59,6 +59,13 @@ class Post(db.Resource): def vote(self, value): return value + @db.indexed_property(prefix='D', typecast=db.AggregatedType, + full_text=True, default=db.AggregatedType(), + fmt=lambda x: [i.get('message') for i in x.values()], + acl=ACL.READ | ACL.INSERT | ACL.REMOVE) + def comments(self, value): + return value + @db.blob_property(mime_type='image/png') def preview(self, value): if value: diff --git a/sugar_network/node/routes.py b/sugar_network/node/routes.py index 4bc97e4..eb48c70 100644 --- a/sugar_network/node/routes.py +++ b/sugar_network/node/routes.py @@ -271,13 +271,7 @@ class NodeRoutes(model.VolumeRoutes, model.FrontRoutes): request.principal = request.authorization.login if op.acl & ACL.AUTHOR and request.guid: - if request.resource == 'user': - allowed = (request.principal == request.guid) - else: - doc = self.volume[request.resource].get(request.guid) - allowed = (request.principal in doc['author']) - enforce(allowed or self.authorize(request.principal, 'root'), - http.Forbidden, 'Operation is permitted only for authors') + self._enforce_authority(request) if op.acl & ACL.SUPERUSER: enforce(self.authorize(request.principal, 'root'), http.Forbidden, @@ -300,6 +294,12 @@ class NodeRoutes(model.VolumeRoutes, model.FrontRoutes): if 'deleted' in props.get('layer', []): event['event'] = 'delete' + def on_aggprop_update(self, request, prop, value): + if prop.acl & ACL.AUTHOR: + self._enforce_authority(request) + elif value is not None: + self._enforce_authority(request, value.get('author')) + def find(self, request, reply): limit = request.get('limit') if limit is None or limit < 0: @@ -402,6 +402,17 @@ class NodeRoutes(model.VolumeRoutes, model.FrontRoutes): del data[key] return result + def _enforce_authority(self, request, author=None): + if request.resource == 'user': + allowed = (request.principal == request.guid) + else: + if author is None: + doc = self.volume[request.resource].get(request.guid) + author = doc['author'] + allowed = request.principal in author + enforce(allowed or self.authorize(request.principal, 'root'), + http.Forbidden, 'Operation is permitted only for authors') + def generate_node_stats(volume, path): tmp_path = toolkit.mkdtemp() diff --git a/sugar_network/toolkit/router.py b/sugar_network/toolkit/router.py index 9a430b7..df57ff3 100644 --- a/sugar_network/toolkit/router.py +++ b/sugar_network/toolkit/router.py @@ -82,20 +82,24 @@ class ACL(object): WRITE = 1 << 3 READ = 1 << 4 DELETE = 1 << 5 - PUBLIC = CREATE | WRITE | READ | DELETE + INSERT = 1 << 6 + REMOVE = 1 << 7 + PUBLIC = CREATE | WRITE | READ | DELETE | INSERT | REMOVE - AUTH = 1 << 6 - AUTHOR = 1 << 7 - SUPERUSER = 1 << 8 + AUTH = 1 << 8 + AUTHOR = 1 << 9 + SUPERUSER = 1 << 10 - LOCAL = 1 << 9 - CALC = 1 << 10 + LOCAL = 1 << 11 + CALC = 1 << 12 NAMES = { CREATE: 'Create', WRITE: 'Write', READ: 'Read', DELETE: 'Delete', + INSERT: 'Insert', + REMOVE: 'Remove', } |