add mechanism for keeeping track of a user's instances

This commit is contained in:
codl 2018-05-07 23:50:37 +02:00
parent 0c99c13ef5
commit 447923b1f1
No known key found for this signature in database
GPG Key ID: 6CD7C8891ED1233A
4 changed files with 154 additions and 4 deletions

View File

@ -0,0 +1,84 @@
import json
def find(predicate, iterator, default=None):
"""
returns the first element of iterator that matches predicate
or default if none is found
"""
try:
return next((el for el in iterator if predicate(el)))
except StopIteration:
return default
class KnownInstances(object):
def __init__(self, serialised=None, top_slots=3):
self.instances = list()
self.top_slots = top_slots
try:
unserialised = json.loads(serialised)
if not isinstance(unserialised, list):
return self.__default()
for instance in unserialised:
if 'instance' in instance and 'hits' in instance:
self.instances.append(dict(
instance=instance['instance'],
hits=instance['hits']
))
except (json.JSONDecodeError, TypeError):
return self.__default()
def __default(self):
self.instances = [{
"instance": "mastodon.social",
"hits": 5
}]
def clear(self):
self.instances = []
def bump(self, instance_name, bump_by=1):
instance = find(
lambda i: i['instance'] == instance_name,
self.instances)
if not instance:
instance = dict(instance=instance_name, hits=0)
self.instances.append(instance)
instance['hits'] += bump_by
def normalize(self):
"""
raises the top `top_slots` instances to the top,
making sure not to move instances that were already at
the top
"""
top_slots = self.top_slots
head = self.instances[:top_slots]
tail = self.instances[top_slots:]
if len(tail) == 0:
return
def key(instance):
return instance['hits']
for _ in range(top_slots):
head_min = min(head, key=key)
tail_max = max(tail, key=key)
if tail_max['hits'] > head_min['hits']:
# swap them
i = head.index(head_min)
j = tail.index(tail_max)
buf = head[i]
head[i] = tail[j]
tail[j] = buf
else:
break
self.instances = head + tail
def top(self):
head = self.instances[:self.top_slots]
return tuple((i['instance'] for i in head))
def serialize(self):
return json.dumps(self.instances)

View File

@ -210,11 +210,11 @@ def delete(post):
raise TemporaryError(e)
def suggested_instances(limit=5, min_popularity=5):
def suggested_instances(limit=5, min_popularity=5, blacklist=tuple()):
return (
MastodonInstance.query
.filter(MastodonInstance.popularity > min_popularity)
.order_by(db.desc(MastodonInstance.instance == 'mastodon.social'),
db.desc(MastodonInstance.popularity),
.filter(~MastodonInstance.instance.in_(blacklist))
.order_by(db.desc(MastodonInstance.popularity),
MastodonInstance.instance)
.limit(limit).all())

View File

@ -6,6 +6,7 @@ import libforget.mastodon
from libforget.auth import require_auth, csrf,\
get_viewer
from model import Session, TwitterArchive, MastodonApp
from libforget.known_instances import KnownInstances
from app import app, db, sentry, imgproxy
import tasks
from zipfile import BadZipFile
@ -264,7 +265,13 @@ def mastodon_login_step2(instance_url):
db.session.commit()
g.viewer = session
return redirect(url_for('index'))
ki = KnownInstances(request.cookies.get('forget_known_instances', ''))
ki.bump(instance_url)
resp = redirect(url_for('index'))
resp.set_cookie('forget_known_instances', ki.serialize())
return resp
@app.route('/sentry/setup.js')

View File

@ -0,0 +1,59 @@
from libforget.known_instances import KnownInstances
def test_known_instances_defaults():
ki = KnownInstances()
assert len(ki.instances) == 1
assert ki.instances[0]['instance'] == 'mastodon.social'
assert ki.instances[0]['hits'] > 0
def test_known_instances_clear():
ki = KnownInstances()
ki.clear()
assert len(ki.instances) == 0
def test_known_instances_deserialize():
ki = KnownInstances(""" [
{"instance": "chitter.xyz", "hits": 666, "foo": "bar"},
{"instance": "invalid"}
] """)
assert len(ki.instances) == 1
assert ki.instances[0]['instance'] == "chitter.xyz"
assert ki.instances[0]['hits'] == 666
def test_known_instances_bump():
ki = KnownInstances()
ki.bump('chitter.xyz')
assert len(ki.instances) == 2
assert ki.instances[1]['instance'] == "chitter.xyz"
assert ki.instances[1]['hits'] == 1
ki.bump('chitter.xyz')
assert len(ki.instances) == 2
assert ki.instances[1]['instance'] == "chitter.xyz"
assert ki.instances[1]['hits'] == 2
def test_known_instances_normalize_top():
ki = KnownInstances()
ki.clear()
ki.normalize()
assert len(ki.instances) == 0
ki.bump("a", 1)
ki.bump("b", 2)
ki.bump("c", 3)
ki.normalize()
assert ki.instances[0]['instance'] == "a"
assert ki.instances[1]['instance'] == "b"
assert ki.instances[2]['instance'] == "c"
ki.bump("d", 4)
ki.normalize()
assert ki.instances[0]['instance'] == "d"
assert ki.instances[3]['instance'] == "a"
assert ki.top() == ("d", "b", "c")