add mechanism for keeeping track of a user's instances
This commit is contained in:
parent
0c99c13ef5
commit
447923b1f1
|
@ -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)
|
|
@ -210,11 +210,11 @@ def delete(post):
|
||||||
raise TemporaryError(e)
|
raise TemporaryError(e)
|
||||||
|
|
||||||
|
|
||||||
def suggested_instances(limit=5, min_popularity=5):
|
def suggested_instances(limit=5, min_popularity=5, blacklist=tuple()):
|
||||||
return (
|
return (
|
||||||
MastodonInstance.query
|
MastodonInstance.query
|
||||||
.filter(MastodonInstance.popularity > min_popularity)
|
.filter(MastodonInstance.popularity > min_popularity)
|
||||||
.order_by(db.desc(MastodonInstance.instance == 'mastodon.social'),
|
.filter(~MastodonInstance.instance.in_(blacklist))
|
||||||
db.desc(MastodonInstance.popularity),
|
.order_by(db.desc(MastodonInstance.popularity),
|
||||||
MastodonInstance.instance)
|
MastodonInstance.instance)
|
||||||
.limit(limit).all())
|
.limit(limit).all())
|
||||||
|
|
|
@ -6,6 +6,7 @@ import libforget.mastodon
|
||||||
from libforget.auth import require_auth, csrf,\
|
from libforget.auth import require_auth, csrf,\
|
||||||
get_viewer
|
get_viewer
|
||||||
from model import Session, TwitterArchive, MastodonApp
|
from model import Session, TwitterArchive, MastodonApp
|
||||||
|
from libforget.known_instances import KnownInstances
|
||||||
from app import app, db, sentry, imgproxy
|
from app import app, db, sentry, imgproxy
|
||||||
import tasks
|
import tasks
|
||||||
from zipfile import BadZipFile
|
from zipfile import BadZipFile
|
||||||
|
@ -264,7 +265,13 @@ def mastodon_login_step2(instance_url):
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
g.viewer = session
|
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')
|
@app.route('/sentry/setup.js')
|
||||||
|
|
|
@ -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")
|
Loading…
Reference in New Issue