Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@sugarlabs.org>2014-05-13 02:16:35 (GMT)
committer Aleksey Lim <alsroot@sugarlabs.org>2014-05-13 02:18:47 (GMT)
commit92540d7c70b662aaf29fc6cbb93617f72dae9900 (patch)
tree43e49a08be950606586218b3fdf39e1407e0aa58
parent745194765f8ba4a0ea5fcbda249b64d9743d6697 (diff)
Count rating on demand
-rwxr-xr-xsugar-network-node21
-rw-r--r--sugar_network/model/post.py45
-rw-r--r--sugar_network/model/routes.py4
-rw-r--r--sugar_network/node/stats.py47
-rwxr-xr-xtests/units/model/post.py138
-rwxr-xr-xtests/units/node/stats.py68
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()