add POST request to get comment counts for multiple URLs
The old way via `GET /count?uri=...` still works, but is now deprecated and might be removed in future releases. The new way is much more efficient especially fore multiple listings. The internal implemention is improvable though.
This commit is contained in:
parent
0d6d072281
commit
5ce48de94a
@ -8,6 +8,8 @@ if not PY2K:
|
|||||||
map, zip, filter = map, zip, filter
|
map, zip, filter = map, zip, filter
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
|
iteritems = lambda dikt: iter(dikt.items())
|
||||||
|
|
||||||
text_type = str
|
text_type = str
|
||||||
string_types = (str, )
|
string_types = (str, )
|
||||||
|
|
||||||
@ -18,6 +20,8 @@ else:
|
|||||||
map, zip, filter = imap, izip, ifilter
|
map, zip, filter = imap, izip, ifilter
|
||||||
reduce = reduce
|
reduce = reduce
|
||||||
|
|
||||||
|
iteritems = lambda dikt: dikt.iteritems()
|
||||||
|
|
||||||
text_type = unicode
|
text_type = unicode
|
||||||
string_types = (str, unicode)
|
string_types = (str, unicode)
|
||||||
|
|
||||||
|
@ -175,14 +175,18 @@ class Comments:
|
|||||||
return {'likes': likes + 1, 'dislikes': dislikes}
|
return {'likes': likes + 1, 'dislikes': dislikes}
|
||||||
return {'likes': likes, 'dislikes': dislikes + 1}
|
return {'likes': likes, 'dislikes': dislikes + 1}
|
||||||
|
|
||||||
def count(self, uri):
|
def count(self, *urls):
|
||||||
"""
|
"""
|
||||||
Return comment count for :param:`uri`.
|
Return comment count for one ore more urls..
|
||||||
"""
|
"""
|
||||||
return self.db.execute([
|
|
||||||
'SELECT COUNT(comments.id) FROM comments INNER JOIN threads ON',
|
threads = dict(self.db.execute([
|
||||||
' threads.uri=? AND comments.tid=threads.id AND comments.mode=1;'],
|
'SELECT threads.uri, COUNT(comments.id) FROM comments',
|
||||||
(uri, )).fetchone()
|
'LEFT OUTER JOIN threads ON threads.id = tid',
|
||||||
|
'GROUP BY threads.uri'
|
||||||
|
]).fetchall())
|
||||||
|
|
||||||
|
return [threads.get(url, 0) for url in urls]
|
||||||
|
|
||||||
def purge(self, delta):
|
def purge(self, delta):
|
||||||
"""
|
"""
|
||||||
|
@ -139,13 +139,11 @@ define(["app/lib/promise", "app/globals"], function(Q, globals) {
|
|||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
var count = function(tid) {
|
var count = function(urls) {
|
||||||
var deferred = Q.defer();
|
var deferred = Q.defer();
|
||||||
curl("GET", endpoint + "/count?" + qs({uri: tid || location}), null, function(rv) {
|
curl("POST", endpoint + "/count", JSON.stringify(urls), function(rv) {
|
||||||
if (rv.status === 200) {
|
if (rv.status === 200) {
|
||||||
deferred.resolve(JSON.parse(rv.body));
|
deferred.resolve(JSON.parse(rv.body));
|
||||||
} else if (rv.status === 404) {
|
|
||||||
deferred.resolve(0);
|
|
||||||
} else {
|
} else {
|
||||||
deferred.reject(rv.body);
|
deferred.reject(rv.body);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
define(["app/api", "app/dom", "app/markup"], function(api, $, Mark) {
|
define(["app/api", "app/dom", "app/markup"], function(api, $, Mark) {
|
||||||
return function() {
|
return function() {
|
||||||
|
|
||||||
|
var objs = {};
|
||||||
|
|
||||||
$.each("a", function(el) {
|
$.each("a", function(el) {
|
||||||
if (! el.href.match("#isso-thread$")) {
|
if (! el.href.match("#isso-thread$")) {
|
||||||
return;
|
return;
|
||||||
@ -9,9 +12,28 @@ define(["app/api", "app/dom", "app/markup"], function(api, $, Mark) {
|
|||||||
el.href.match("^(.+)#isso-thread$")[1]
|
el.href.match("^(.+)#isso-thread$")[1]
|
||||||
.replace(/^.*\/\/[^\/]+/, '');
|
.replace(/^.*\/\/[^\/]+/, '');
|
||||||
|
|
||||||
api.count(tid).then(function(rv) {
|
if (tid in objs) {
|
||||||
el.textContent = Mark.up("{{ i18n-num-comments | pluralize : `n` }}", {n: rv});
|
objs[tid].push(el);
|
||||||
|
} else {
|
||||||
|
objs[tid] = [el];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var urls = Object.keys(objs);
|
||||||
|
|
||||||
|
api.count(urls).then(function(rv) {
|
||||||
|
for (var key in objs) {
|
||||||
|
if (objs.hasOwnProperty(key)) {
|
||||||
|
|
||||||
|
var index = urls.indexOf(key);
|
||||||
|
|
||||||
|
for (var i = 0; i < objs[key].length; i++) {
|
||||||
|
objs[key][i].textContent = Mark.up(
|
||||||
|
"{{ i18n-num-comments | pluralize : `n` }}",
|
||||||
|
{n: rv[index]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -16,7 +16,7 @@ from werkzeug.exceptions import BadRequest, Forbidden, NotFound
|
|||||||
from isso.compat import text_type as str
|
from isso.compat import text_type as str
|
||||||
|
|
||||||
from isso import utils, local
|
from isso import utils, local
|
||||||
from isso.utils import http, parse, html, JSONResponse as JSON
|
from isso.utils import http, parse, JSONResponse as JSON
|
||||||
from isso.utils.crypto import pbkdf2
|
from isso.utils.crypto import pbkdf2
|
||||||
from isso.views import requires
|
from isso.views import requires
|
||||||
|
|
||||||
@ -61,6 +61,7 @@ class API(object):
|
|||||||
('fetch', ('GET', '/')),
|
('fetch', ('GET', '/')),
|
||||||
('new', ('POST', '/new')),
|
('new', ('POST', '/new')),
|
||||||
('count', ('GET', '/count')),
|
('count', ('GET', '/count')),
|
||||||
|
('counts', ('POST', '/count')),
|
||||||
('view', ('GET', '/id/<int:id>')),
|
('view', ('GET', '/id/<int:id>')),
|
||||||
('edit', ('PUT', '/id/<int:id>')),
|
('edit', ('PUT', '/id/<int:id>')),
|
||||||
('delete', ('DELETE', '/id/<int:id>')),
|
('delete', ('DELETE', '/id/<int:id>')),
|
||||||
@ -352,6 +353,7 @@ class API(object):
|
|||||||
nv = self.comments.vote(False, id, utils.anonymize(str(request.remote_addr)))
|
nv = self.comments.vote(False, id, utils.anonymize(str(request.remote_addr)))
|
||||||
return JSON(nv, 200)
|
return JSON(nv, 200)
|
||||||
|
|
||||||
|
# TODO: remove someday (replaced by :func:`counts`)
|
||||||
@requires(str, 'uri')
|
@requires(str, 'uri')
|
||||||
def count(self, environ, request, uri):
|
def count(self, environ, request, uri):
|
||||||
|
|
||||||
@ -362,5 +364,14 @@ class API(object):
|
|||||||
|
|
||||||
return JSON(rv, 200)
|
return JSON(rv, 200)
|
||||||
|
|
||||||
|
def counts(self, environ, request):
|
||||||
|
|
||||||
|
data = request.get_json()
|
||||||
|
|
||||||
|
if not isinstance(data, list) and not all(isinstance(x, str) for x in data):
|
||||||
|
raise BadRequest("JSON must be a list of URLs")
|
||||||
|
|
||||||
|
return JSON(self.comments.count(*data), 200)
|
||||||
|
|
||||||
def checkip(self, env, req):
|
def checkip(self, env, req):
|
||||||
return Response(utils.anonymize(str(req.remote_addr)), 200)
|
return Response(utils.anonymize(str(req.remote_addr)), 200)
|
||||||
|
@ -22,6 +22,8 @@ from isso import Isso, core
|
|||||||
from isso.utils import http
|
from isso.utils import http
|
||||||
from isso.views import comments
|
from isso.views import comments
|
||||||
|
|
||||||
|
from isso.compat import iteritems
|
||||||
|
|
||||||
from fixtures import curl, loads, FakeIP, JSONClient
|
from fixtures import curl, loads, FakeIP, JSONClient
|
||||||
http.curl = curl
|
http.curl = curl
|
||||||
|
|
||||||
@ -274,6 +276,17 @@ class TestComments(unittest.TestCase):
|
|||||||
rv = self.get('/count?uri=%2Fpath%2F')
|
rv = self.get('/count?uri=%2Fpath%2F')
|
||||||
self.assertEqual(rv.status_code, 404)
|
self.assertEqual(rv.status_code, 404)
|
||||||
|
|
||||||
|
def testMultipleCounts(self):
|
||||||
|
|
||||||
|
expected = {'a': 1, 'b': 2, 'c': 0}
|
||||||
|
|
||||||
|
for uri, count in iteritems(expected):
|
||||||
|
for _ in range(count):
|
||||||
|
self.post('/new?uri=%s' % uri, data=json.dumps({"text": "..."}))
|
||||||
|
|
||||||
|
rv = self.post('/count', data=json.dumps(list(expected.keys())))
|
||||||
|
self.assertEqual(loads(rv.data), list(expected.values()))
|
||||||
|
|
||||||
def testModify(self):
|
def testModify(self):
|
||||||
self.post('/new?uri=test', data=json.dumps({"text": "Tpyo"}))
|
self.post('/new?uri=test', data=json.dumps({"text": "Tpyo"}))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user