diff options
author | Aleksey Lim <alsroot@sugarlabs.org> | 2014-05-13 02:16:35 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@sugarlabs.org> | 2014-05-13 02:18:47 (GMT) |
commit | 92540d7c70b662aaf29fc6cbb93617f72dae9900 (patch) | |
tree | 43e49a08be950606586218b3fdf39e1407e0aa58 | |
parent | 745194765f8ba4a0ea5fcbda249b64d9743d6697 (diff) |
Count rating on demand
-rwxr-xr-x | sugar-network-node | 21 | ||||
-rw-r--r-- | sugar_network/model/post.py | 45 | ||||
-rw-r--r-- | sugar_network/model/routes.py | 4 | ||||
-rw-r--r-- | sugar_network/node/stats.py | 47 | ||||
-rwxr-xr-x | tests/units/model/post.py | 138 | ||||
-rwxr-xr-x | tests/units/node/stats.py | 68 |
6 files changed, 199 insertions, 124 deletions
diff --git a/sugar-network-node b/sugar-network-node index 5775e61..da20284 100755 --- a/sugar-network-node +++ b/sugar-network-node @@ -25,10 +25,12 @@ from sugar_network.toolkit import coroutine coroutine.inject() from sugar_network import db, node, toolkit +from sugar_network.model.post import Post from sugar_network.node.auth import SugarAuth, RootAuth from sugar_network.node import obs, master, slave, model, stats from sugar_network.toolkit.http import Connection from sugar_network.toolkit.router import Router +from sugar_network.toolkit.coroutine import this from sugar_network.toolkit import application, Option, enforce @@ -182,8 +184,8 @@ class Application(application.Daemon): model.presolve(join(node.data_root.value, 'files'), block=False) @application.command( - 're-generate node statistics', name='restat') - def restat(self): + 're-generate node statistics', name='stat') + def stat(self): enforce(not self.check_for_instance(), 'Node should be stopped') volume = model.Volume(node.data_root.value, master.RESOURCES) routes = slave.SlaveRoutes(node.master_api.value, volume=volume, @@ -196,18 +198,15 @@ class Application(application.Daemon): volume.close() @application.command( - 're-generate ratings', name='rating') - def rating(self): + 're-generate ratings', name='rate') + def rate(self): enforce(not self.check_for_instance(), 'Node should be stopped') - volume = model.Volume(node.data_root.value, master.RESOURCES) - routes = slave.SlaveRoutes(node.master_api.value, volume=volume, - auth=RootAuth()) + this.volume = model.Volume(node.data_root.value, master.RESOURCES) try: - volume.populate() - routes.stats_regen_rating(join(node.data_root.value, 'var'), - stats.stats_step.value, stats.stats_rras.value) + this.volume.populate() + Post.rating_regen() finally: - volume.close() + this.volume.close() def _ensure_instance(self): enforce(self.check_for_instance(), 'Node is not started') diff --git a/sugar_network/model/post.py b/sugar_network/model/post.py index a4c7dbf..511ae59 100644 --- a/sugar_network/model/post.py +++ b/sugar_network/model/post.py @@ -15,6 +15,7 @@ from sugar_network import db, model from sugar_network.toolkit.router import ACL +from sugar_network.toolkit.coroutine import this class Post(db.Resource): @@ -51,6 +52,12 @@ class Post(db.Resource): def vote(self, value): return value + @vote.setter + def vote(self, value): + if value: + self._update_rating(value, +1) + return value + @db.indexed_property(db.Aggregated, prefix='D', full_text=True, subtype=db.Localized()) def comments(self, value): @@ -76,3 +83,41 @@ class Post(db.Resource): @db.indexed_property(model.Rating, slot=3, acl=ACL.READ | ACL.LOCAL) def rating(self, value): return value + + def updated(self): + if self.posts.get('state') == 'deleted': + self._update_rating(self['vote'], -1) + db.Resource.updated(self) + + @staticmethod + def rating_regen(): + + def calc_rating(**kwargs): + rating = [0, 0] + alldocs, __ = this.volume['post'].find(not_vote=0, **kwargs) + for post in alldocs: + rating[0] += 1 + rating[1] += post['vote'] + return rating + + alldocs, __ = this.volume['context'].find() + for context in alldocs: + rating = calc_rating(topic='', context=context.guid) + this.volume['context'].update(context.guid, {'rating': rating}) + + alldocs, __ = this.volume['post'].find(topic='') + for topic in alldocs: + rating = calc_rating(topic=topic.guid) + this.volume['post'].update(topic.guid, {'rating': rating}) + + def _update_rating(self, vote, shift): + if self['topic']: + resource = this.volume['post'] + guid = self['topic'] + else: + resource = this.volume['context'] + guid = self['context'] + orig = resource[guid]['rating'] + resource.update(guid, { + 'rating': [orig[0] + shift, orig[1] + shift * vote], + }) diff --git a/sugar_network/model/routes.py b/sugar_network/model/routes.py index 63c98b1..38f9c26 100644 --- a/sugar_network/model/routes.py +++ b/sugar_network/model/routes.py @@ -97,3 +97,7 @@ class FrontRoutes(object): coroutine.select([rfile.fileno()], [], []) finally: self._spooler.notify_all(rfile) + + +this.broadcast = lambda event: None +this.localcast = lambda event: None diff --git a/sugar_network/node/stats.py b/sugar_network/node/stats.py index 1af23d9..c31b204 100644 --- a/sugar_network/node/stats.py +++ b/sugar_network/node/stats.py @@ -110,7 +110,6 @@ class StatRoutes(object): _rrd = None _stats = None - _rating = None _stated = False def stats_init(self, path, step, rras): @@ -118,7 +117,6 @@ class StatRoutes(object): self._rrd = Rrd(path, 'stats', _DS, step, rras) self._stats = self._rrd.values() - self._rating = {'context': {}, 'post': {}} if not self._stats: for field, traits in _DS.items(): @@ -129,7 +127,7 @@ class StatRoutes(object): self._stats[field] = value @postroute - def stat_on_postroute(self, result, exception, stat_rating=True): + def stat_on_postroute(self, result, exception): if self._rrd is None or exception is not None: return result @@ -144,18 +142,6 @@ class StatRoutes(object): stat = stat() self._stats[stat] += shift - if stat_rating: - rating = None - if stat == 'topics' and this.resource['type'] == 'review': - rating = self._rating['context'] - rating = rating.setdefault(this.resource['context'], [0, 0]) - elif stat == 'posts': - rating = self._rating['post'] - rating = rating.setdefault(this.resource['topic'], [0, 0]) - if rating is not None: - rating[0] += shift - rating[1] += shift * this.resource['vote'] - return result @route('GET', cmd='stats', arguments={ @@ -202,15 +188,6 @@ class StatRoutes(object): if traits['type'] == 'ABSOLUTE': self._stats[field] = 0 - for resource, stats_ in self._rating.items(): - directory = this.volume[resource] - for guid, (votes, reviews) in stats_.items(): - rating = directory[guid]['rating'] - directory.update(guid, { - 'rating': [rating[0] + votes, rating[1] + reviews], - }) - stats_.clear() - def stats_regen(self, path, step, rras): for i in Rrd(path, 'stats', _DS, step, rras).files: os.unlink(i) @@ -247,25 +224,5 @@ class StatRoutes(object): query='ctime:%s..%s' % (left, right)) for this.resource in items: this.request = Request(method='POST', path=[resource]) - self.stat_on_postroute(None, None, False) + self.stat_on_postroute(None, None) self.stats_commit(left + (right - left) / 2) - - def stats_regen_rating(self, path, step, rras): - - def calc_rating(**kwargs): - rating = [0, 0] - alldocs, __ = this.volume['post'].find(**kwargs) - for post in alldocs: - rating[0] += 1 - rating[1] += post['vote'] - return rating - - alldocs, __ = this.volume['context'].find() - for context in alldocs: - rating = calc_rating(type='review', context=context.guid) - this.volume['context'].update(context.guid, {'rating': rating}) - - alldocs, __ = this.volume['post'].find(topic='') - for topic in alldocs: - rating = calc_rating(topic=topic.guid) - this.volume['post'].update(topic.guid, {'rating': rating}) diff --git a/tests/units/model/post.py b/tests/units/model/post.py index 5e089ce..ff671cc 100755 --- a/tests/units/model/post.py +++ b/tests/units/model/post.py @@ -6,6 +6,7 @@ from __init__ import tests from sugar_network import db from sugar_network.client import Connection, keyfile from sugar_network.model.context import Context +from sugar_network.node.auth import RootAuth from sugar_network.model.post import Post from sugar_network.toolkit.coroutine import this from sugar_network.toolkit import http @@ -44,6 +45,143 @@ class PostTest(tests.Test): ['1', '3', '4'], [i.guid for i in directory.find(query='comments:foo')[0]]) + def test_ShiftContextRating(self): + volume = db.Volume('db', [Context, Post]) + this.volume = volume + + context = volume['context'].create({ + 'type': 'activity', + 'title': {}, + 'summary': {}, + 'description': {}, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + + volume['post'].create({ + 'context': context, + 'type': 'post', + 'title': {}, + 'message': {}, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + + volume['post'].create({ + 'context': context, + 'type': 'post', + 'title': {}, + 'message': {}, + 'vote': 0, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + + volume['post'].create({ + 'context': context, + 'type': 'post', + 'title': {}, + 'message': {}, + 'vote': 1, + }) + self.assertEqual([1, 1], volume['context'][context]['rating']) + + volume['post'].create({ + 'context': context, + 'type': 'post', + 'title': {}, + 'message': {}, + 'vote': 2, + }) + self.assertEqual([2, 3], volume['context'][context]['rating']) + + def test_ShiftContextRatingOnDeletes(self): + volume = self.start_master(auth=RootAuth()) + context = this.call(method='POST', path=['context'], content={'title': '', 'summary': '', 'description': '', 'type': 'activity'}) + + post1 = this.call(method='POST', path=['post'], content={'context': context, 'type': 'review', 'title': '', 'message': '', 'vote': 1}) + post2 = this.call(method='POST', path=['post'], content={'context': context, 'type': 'review', 'title': '', 'message': '', 'vote': 2}) + self.assertEqual([2, 3], volume['context'][context]['rating']) + + this.call(method='DELETE', path=['post', post1]) + self.assertEqual([1, 2], volume['context'][context]['rating']) + + this.call(method='DELETE', path=['post', post2]) + self.assertEqual([0, 0], volume['context'][context]['rating']) + + def test_ShiftTopicRating(self): + volume = db.Volume('db2', [Context, Post]) + this.volume = volume + + context = volume['context'].create({ + 'type': 'activity', + 'title': {}, + 'summary': {}, + 'description': {}, + }) + topic = volume['post'].create({ + 'context': context, + 'type': 'post', + 'title': {}, + 'message': {}, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + self.assertEqual([0, 0], volume['post'][topic]['rating']) + + volume['post'].create({ + 'context': context, + 'topic': topic, + 'type': 'post', + 'title': {}, + 'message': {}, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + self.assertEqual([0, 0], volume['post'][topic]['rating']) + + volume['post'].create({ + 'context': context, + 'topic': topic, + 'type': 'post', + 'title': {}, + 'message': {}, + 'vote': 0, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + self.assertEqual([0, 0], volume['post'][topic]['rating']) + + volume['post'].create({ + 'context': context, + 'topic': topic, + 'type': 'post', + 'title': {}, + 'message': {}, + 'vote': 1, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + self.assertEqual([1, 1], volume['post'][topic]['rating']) + + volume['post'].create({ + 'context': context, + 'topic': topic, + 'type': 'post', + 'title': {}, + 'message': {}, + 'vote': 2, + }) + self.assertEqual([0, 0], volume['context'][context]['rating']) + self.assertEqual([2, 3], volume['post'][topic]['rating']) + + def test_ShiftTopicRatingOnDeletes(self): + volume = self.start_master(auth=RootAuth()) + topic = this.call(method='POST', path=['post'], content={'context': '', 'type': 'post', 'title': '', 'message': ''}) + + post1 = this.call(method='POST', path=['post'], content={'context': '', 'topic': topic, 'type': 'review', 'title': '', 'message': '', 'vote': 1}) + post2 = this.call(method='POST', path=['post'], content={'context': '', 'topic': topic, 'type': 'review', 'title': '', 'message': '', 'vote': 2}) + self.assertEqual([2, 3], volume['post'][topic]['rating']) + + this.call(method='DELETE', path=['post', post1]) + self.assertEqual([1, 2], volume['post'][topic]['rating']) + + this.call(method='DELETE', path=['post', post2]) + self.assertEqual([0, 0], volume['post'][topic]['rating']) + if __name__ == '__main__': tests.main() diff --git a/tests/units/node/stats.py b/tests/units/node/stats.py index 348daec..91b4dee 100755 --- a/tests/units/node/stats.py +++ b/tests/units/node/stats.py @@ -371,74 +371,6 @@ class StatsTest(tests.Test): ], this.call(method='GET', cmd='stats', limit=10)) - def test_StatContextRating(self): - ts = int(time.time()) - volume = self.start_master(auth=RootAuth()) - self.node_routes.stats_init('.', 1, ['RRA:AVERAGE:0.5:1:10']) - - guid = this.call(method='POST', path=['context'], content={'title': '', 'summary': '', 'description': '', 'type': 'activity'}) - self.node_routes.stats_commit() - self.assertEqual([0, 0], volume['context'][guid]['rating']) - - post1 = this.call(method='POST', path=['post'], content={'context': guid, 'type': 'review', 'title': '', 'message': '', 'vote': 0}) - self.node_routes.stats_commit() - self.assertEqual([1, 0], volume['context'][guid]['rating']) - - post2 = this.call(method='POST', path=['post'], content={'context': guid, 'type': 'review', 'title': '', 'message': '', 'vote': 1}) - self.node_routes.stats_commit() - self.assertEqual([2, 1], volume['context'][guid]['rating']) - - post3 = this.call(method='POST', path=['post'], content={'context': guid, 'type': 'review', 'title': '', 'message': '', 'vote': 4}) - self.node_routes.stats_commit() - self.assertEqual([3, 5], volume['context'][guid]['rating']) - - this.call(method='DELETE', path=['post', post2]) - self.node_routes.stats_commit() - self.assertEqual([2, 4], volume['context'][guid]['rating']) - - this.call(method='DELETE', path=['post', post1]) - self.node_routes.stats_commit() - self.assertEqual([1, 4], volume['context'][guid]['rating']) - - this.call(method='DELETE', path=['post', post3]) - self.node_routes.stats_commit() - self.assertEqual([0, 0], volume['context'][guid]['rating']) - - def test_StatTopicRating(self): - ts = int(time.time()) - volume = self.start_master(auth=RootAuth()) - self.node_routes.stats_init('.', 1, ['RRA:AVERAGE:0.5:1:10']) - - guid = this.call(method='POST', path=['post'], content={'context': '', 'type': 'post', 'title': '', 'message': ''}) - self.assertEqual([0, 0], volume['post'][guid]['rating']) - - self.node_routes.stats_commit() - self.assertEqual([0, 0], volume['post'][guid]['rating']) - - post1 = this.call(method='POST', path=['post'], content={'context': '', 'topic': guid, 'type': 'post', 'title': '', 'message': '', 'vote': 0}) - self.node_routes.stats_commit() - self.assertEqual([1, 0], volume['post'][guid]['rating']) - - post2 = this.call(method='POST', path=['post'], content={'context': '', 'topic': guid, 'type': 'post', 'title': '', 'message': '', 'vote': 1}) - self.node_routes.stats_commit() - self.assertEqual([2, 1], volume['post'][guid]['rating']) - - post3 = this.call(method='POST', path=['post'], content={'context': '', 'topic': guid, 'type': 'post', 'title': '', 'message': '', 'vote': 4}) - self.node_routes.stats_commit() - self.assertEqual([3, 5], volume['post'][guid]['rating']) - - this.call(method='DELETE', path=['post', post2]) - self.node_routes.stats_commit() - self.assertEqual([2, 4], volume['post'][guid]['rating']) - - this.call(method='DELETE', path=['post', post1]) - self.node_routes.stats_commit() - self.assertEqual([1, 4], volume['post'][guid]['rating']) - - this.call(method='DELETE', path=['post', post3]) - self.node_routes.stats_commit() - self.assertEqual([0, 0], volume['post'][guid]['rating']) - if __name__ == '__main__': tests.main() |