ahhh im super good at computing

This commit is contained in:
codl 2017-08-02 01:35:09 +02:00
parent 07e37cab02
commit e08a0eea13
No known key found for this signature in database
GPG Key ID: 6CD7C8891ED1233A
9 changed files with 146 additions and 29 deletions

View File

@ -1,7 +1,6 @@
from datetime import timedelta from datetime import timedelta
SCALES = [ SCALES = [
('seconds', timedelta(seconds=1)),
('minutes', timedelta(minutes=1)), ('minutes', timedelta(minutes=1)),
('hours', timedelta(hours=1)), ('hours', timedelta(hours=1)),
('days', timedelta(days=1)), ('days', timedelta(days=1)),
@ -21,13 +20,13 @@ def decompose_interval(attrname):
def scale(self): def scale(self):
if getattr(self, attrname) == timedelta(0): if getattr(self, attrname) == timedelta(0):
return timedelta(seconds=1) return timedelta(minutes=1)
for m in scales: for m in scales:
if getattr(self, attrname) % m == timedelta(0): if getattr(self, attrname) % m == timedelta(0):
return m return m
return timedelta(seconds=1) return timedelta(minutes=1)
@scale.setter @scale.setter
def scale(self, value): def scale(self, value):

View File

@ -1,7 +1,7 @@
from twitter import Twitter, OAuth from twitter import Twitter, OAuth
from werkzeug.urls import url_decode from werkzeug.urls import url_decode
from model import OAuthToken, Account, Post from model import OAuthToken, Account, Post
from app import db from app import db, app
from math import inf from math import inf
from datetime import datetime from datetime import datetime
import locale import locale
@ -45,30 +45,22 @@ def receive_verifier(oauth_token, oauth_verifier, consumer_key=None, consumer_se
return new_token return new_token
def get_twitter_for_acc(account, consumer_key=None, consumer_secret=None): def get_twitter_for_acc(account):
token = account.tokens[0]
consumer_key = app.config['TWITTER_CONSUMER_KEY']
consumer_secret = app.config['TWITTER_CONSUMER_SECRET']
token = OAuthToken.query.with_parent(account).order_by(db.desc(OAuthToken.created_at)).first()
t = Twitter( t = Twitter(
auth=OAuth(token.token, token.token_secret, consumer_key, consumer_secret)) auth=OAuth(token.token, token.token_secret, consumer_key, consumer_secret))
return t return t
locale.setlocale(locale.LC_TIME, 'C') locale.setlocale(locale.LC_TIME, 'C')
def csv_tweet_to_json_tweet(tweet, account): def tweet_to_post(tweet, post=None):
tweet.update({ if not post:
'id': int(tweet['tweet_id']), post = Post()
'id_str': tweet['tweet_id'], post.twitter_id = tweet['id_str']
'created_at': datetime.strptime(tweet['timestamp'],
'%Y-%m-%d %H:%M:%S %z')\
.strftime('%a %b %d %H:%M:%S %z %Y'),
'user': {
'id': int(account.twitter_id),
'id_str': account.twitter_id
}
})
return tweet
def tweet_to_post(tweet):
post = Post(twitter_id=tweet['id_str'])
try: try:
post.created_at = datetime.strptime(tweet['created_at'], '%a %b %d %H:%M:%S %z %Y') post.created_at = datetime.strptime(tweet['created_at'], '%a %b %d %H:%M:%S %z %Y')
except ValueError: except ValueError:
@ -79,10 +71,12 @@ def tweet_to_post(tweet):
else: else:
post.body = tweet['text'] post.body = tweet['text']
post.author_id = 'twitter:{}'.format(tweet['user']['id_str']) post.author_id = 'twitter:{}'.format(tweet['user']['id_str'])
if 'favorited' in tweet:
post.favourite = tweet['favorited']
return post return post
def fetch_acc(account, cursor, consumer_key=None, consumer_secret=None): def fetch_acc(account, cursor, consumer_key=None, consumer_secret=None):
t = get_twitter_for_acc(account, consumer_key=consumer_key, consumer_secret=consumer_secret) t = get_twitter_for_acc(account)
user = t.account.verify_credentials() user = t.account.verify_credentials()
@ -117,3 +111,24 @@ def fetch_acc(account, cursor, consumer_key=None, consumer_secret=None):
return kwargs return kwargs
def refresh_posts(posts):
t = get_twitter_for_acc(posts[0].author)
tweets = t.statuses.lookup(_id=",".join((post.twitter_id for post in posts)),
trim_user = True, tweet_mode = 'extended')
refreshed_posts = list()
for post in posts:
tweet = next((tweet for tweet in tweets if tweet['id_str'] == post.twitter_id), None)
if not tweet:
session.delete(post)
else:
post = db.session.merge(tweet_to_post(tweet))
refreshed_posts.append(post)
return refreshed_posts
def delete(post):
t = get_twitter_for_acc(post.author)
t.statuses.destroy(id=post.twitter_id)
db.session.delete(post)

View File

@ -0,0 +1,28 @@
"""empty message
Revision ID: 7379bfac4379
Revises: 995890c26959
Create Date: 2017-08-01 23:35:17.924428
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '7379bfac4379'
down_revision = '995890c26959'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('posts', sa.Column('favourite', sa.Boolean(), server_default='FALSE', nullable=False))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('posts', 'favourite')
# ### end Alembic commands ###

View File

@ -0,0 +1,30 @@
"""empty message
Revision ID: 995890c26959
Revises: e036b007017c
Create Date: 2017-08-01 23:24:22.223674
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '995890c26959'
down_revision = 'e036b007017c'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('accounts', sa.Column('policy_keep_favourites', sa.Boolean(), server_default='TRUE', nullable=True))
op.drop_column('accounts', 'policy_ignore_favourites')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('accounts', sa.Column('policy_ignore_favourites', sa.BOOLEAN(), server_default=sa.text('true'), autoincrement=False, nullable=True))
op.drop_column('accounts', 'policy_keep_favourites')
# ### end Alembic commands ###

View File

@ -5,6 +5,7 @@ from app import db
from twitter import Twitter, OAuth from twitter import Twitter, OAuth
import secrets import secrets
from lib import decompose_interval from lib import decompose_interval
from datetime import timedelta
class TimestampMixin(object): class TimestampMixin(object):
created_at = db.Column(db.DateTime, server_default=db.func.now()) created_at = db.Column(db.DateTime, server_default=db.func.now())
@ -42,7 +43,7 @@ class Account(TimestampMixin, RemoteIDMixin):
policy_enabled = db.Column(db.Boolean, server_default='FALSE', nullable=False) policy_enabled = db.Column(db.Boolean, server_default='FALSE', nullable=False)
policy_keep_latest = db.Column(db.Integer, server_default='0') policy_keep_latest = db.Column(db.Integer, server_default='0')
policy_ignore_favourites = db.Column(db.Boolean, server_default='TRUE') policy_keep_favourites = db.Column(db.Boolean, server_default='TRUE')
policy_delete_every = db.Column(db.Interval, server_default='0') policy_delete_every = db.Column(db.Interval, server_default='0')
policy_keep_younger = db.Column(db.Interval, server_default='0') policy_keep_younger = db.Column(db.Interval, server_default='0')
@ -104,6 +105,14 @@ class Post(db.Model, TimestampMixin, RemoteIDMixin):
author = db.relationship(Account, author = db.relationship(Account,
backref=db.backref('posts', order_by=lambda: db.desc(Post.created_at))) backref=db.backref('posts', order_by=lambda: db.desc(Post.created_at)))
favourite = db.Column(db.Boolean, server_default='FALSE', nullable=False)
def __repr__(self):
snippet = self.body
if len(snippet) > 20:
snippet = snippet[:19] + ""
return '<Post ({}, "{}", Author: {})>'.format(self.id, snippet, self.author_id)
class TwitterArchive(db.Model, TimestampMixin): class TwitterArchive(db.Model, TimestampMixin):
__tablename__ = 'twitter_archives' __tablename__ = 'twitter_archives'

View File

@ -73,7 +73,7 @@ def upload_tweet_archive():
def settings(): def settings():
if request.method == 'POST': if request.method == 'POST':
for attr in ('policy_enabled', for attr in ('policy_enabled',
'policy_ignore_favourites', 'policy_keep_favourites',
'policy_keep_latest', 'policy_keep_latest',
'policy_delete_every_significand', 'policy_delete_every_significand',
'policy_delete_every_scale', 'policy_delete_every_scale',

View File

@ -11,6 +11,7 @@ from zipfile import ZipFile
from io import BytesIO, TextIOWrapper from io import BytesIO, TextIOWrapper
import json import json
from kombu import Queue from kombu import Queue
import random
app = Celery('tasks', broker=flaskapp.config['CELERY_BROKER'], task_serializer='pickle') app = Celery('tasks', broker=flaskapp.config['CELERY_BROKER'], task_serializer='pickle')
app.conf.task_queues = ( app.conf.task_queues = (
@ -105,8 +106,43 @@ def periodic_cleanup():
delete(synchronize_session=False) delete(synchronize_session=False)
db.session.commit() db.session.commit()
@app.task
def queue_deletes():
eligible_accounts = Account.query.filter(Account.policy_enabled == True).\
filter(Account.last_delete + Account.policy_delete_every < db.func.now())
for account in eligible_accounts:
delete_from_account.s(account.id).apply_async()
@app.task
def delete_from_account(account_id):
account = Account.query.get(account_id)
latest_n_posts = db.session.query(Post.id).with_parent(account).order_by(db.desc(Post.created_at)).limit(account.policy_keep_latest)
posts = Post.query.with_parent(account).\
filter(Post.created_at + account.policy_keep_younger <= db.func.now()).\
filter(~Post.id.in_(latest_n_posts)).\
order_by(db.func.random()).limit(100).all()
if account.service == 'twitter':
posts = lib.twitter.refresh_posts(posts)
eligible = list((post for post in posts if not account.policy_keep_favourites or not post.favourite))
if eligible:
if account.policy_delete_every == timedelta(0):
print("deleting all {} eligible posts for {}".format(len(eligible), account))
for post in eligible:
lib.twitter.delete(post)
else:
post = random.choice(list((post for post in posts if not account.policy_keep_favourites or not post.favourite)))
print("deleting {}".format(post))
lib.twitter.delete(post)
account.last_delete = db.func.now()
db.session.commit()
app.add_periodic_task(6*60*60, periodic_cleanup) app.add_periodic_task(6*60*60, periodic_cleanup)
app.add_periodic_task(60, queue_fetch_for_most_stale_accounts) app.add_periodic_task(45, queue_fetch_for_most_stale_accounts)
app.add_periodic_task(45, queue_deletes)
if __name__ == '__main__': if __name__ == '__main__':
app.worker_main() app.worker_main()

View File

@ -11,7 +11,7 @@
<h2>Recent posts ({{g.viewer.account.post_count()}} total posts):</h2> <h2>Recent posts ({{g.viewer.account.post_count()}} total posts):</h2>
{% for post in posts %} {% for post in posts %}
<p>{{post.body}}</p> <p>{{ "♥ " if post.favourite }}{{post.body}}</p>
{% else %} {% else %}
<p>no posts :(</p> <p>no posts :(</p>
{% endfor %} {% endfor %}

View File

@ -22,8 +22,8 @@
latest posts latest posts
</p> </p>
<p>Keep posts that I have favourited <p>Keep posts that I have favourited
<label><input type=radio name=policy_ignore_favourites value=true {{ "checked" if g.viewer.account.policy_ignore_favourites }}> Yes</label> <label><input type=radio name=policy_keep_favourites value=true {{ "checked" if g.viewer.account.policy_keep_favourites }}> Yes</label>
<label><input type=radio name=policy_ignore_favourites value=false {{ "checked" if not g.viewer.account.policy_ignore_favourites }}> No</label> <label><input type=radio name=policy_keep_favourites value=false {{ "checked" if not g.viewer.account.policy_keep_favourites }}> No</label>
</p> </p>
<input type=submit value='Save'> <input type=submit value='Save'>
</form> </form>