From 8164f786ecbda2e5319a4efd997d4703cd0730dc Mon Sep 17 00:00:00 2001
From: codl
Date: Fri, 28 Jul 2017 00:08:20 +0200
Subject: [PATCH] yes i do the fetching yes i do the cleaning up sessions
---
lib/twitter.py | 43 +++++++++++++++++++++++++++-
migrations/versions/8c2ce3a66650_.py | 28 ++++++++++++++++++
model.py | 8 +++---
routes.py | 12 +++++---
tasks.py | 18 ++++++++++--
templates/index.html | 2 +-
6 files changed, 98 insertions(+), 13 deletions(-)
create mode 100644 migrations/versions/8c2ce3a66650_.py
diff --git a/lib/twitter.py b/lib/twitter.py
index 6fdfd57..8f58f4b 100644
--- a/lib/twitter.py
+++ b/lib/twitter.py
@@ -1,7 +1,9 @@
from twitter import Twitter, OAuth
from werkzeug.urls import url_decode
-from model import OAuthToken, Account
+from model import OAuthToken, Account, Post
from app import db
+from math import inf
+from datetime import datetime
def get_login_url(callback='oob', consumer_key=None, consumer_secret=None):
twitter = Twitter(
@@ -38,3 +40,42 @@ def receive_verifier(oauth_token, oauth_verifier, consumer_key=None, consumer_se
new_token.account = acct
db.session.commit()
return new_token
+
+def get_twitter_for_acc(account, consumer_key=None, consumer_secret=None):
+ token = account.tokens[0]
+ t = Twitter(
+ auth=OAuth(token.token, token.token_secret, consumer_key, consumer_secret))
+ return t
+
+import locale
+locale.setlocale(locale.LC_TIME, 'C') # jeez i hate that i have to do this
+
+def fetch_posts_for_acc(account, consumer_key=None, consumer_secret=None):
+ t = get_twitter_for_acc(account, consumer_key=consumer_key, consumer_secret=consumer_secret)
+
+ kwargs = { 'user_id': account.remote_id, 'count': 200, 'trim_user': True }
+
+ #most_recent_post = Post.query.order_by(db.desc(Post.created_at)).filter(Post.author_id == account.remote_id).first()
+ #if most_recent_post:
+ # kwargs['since_id'] = most_recent_post.remote_id
+
+ while True:
+ tweets = t.statuses.user_timeline(**kwargs)
+
+ if len(tweets) == 0:
+ break
+
+ kwargs['max_id'] = +inf
+
+ for tweet in tweets:
+ post = Post(remote_id=tweet['id_str'])
+ post = db.session.merge(post)
+ post.created_at = datetime.strptime(tweet['created_at'], '%a %b %d %H:%M:%S %z %Y')
+ post.body = tweet['text']
+ post.author = account
+ kwargs['max_id'] = min(tweet['id'] - 1, kwargs['max_id'])
+
+
+ account.last_post_fetch = datetime.now()
+ db.session.commit()
+
diff --git a/migrations/versions/8c2ce3a66650_.py b/migrations/versions/8c2ce3a66650_.py
new file mode 100644
index 0000000..9ed3f23
--- /dev/null
+++ b/migrations/versions/8c2ce3a66650_.py
@@ -0,0 +1,28 @@
+"""empty message
+
+Revision ID: 8c2ce3a66650
+Revises: a5718ca3ead1
+Create Date: 2017-07-27 23:35:48.842519
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '8c2ce3a66650'
+down_revision = 'a5718ca3ead1'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ op.drop_constraint('fk_posts_author_remote_id_accounts', 'posts', type_='foreignkey')
+ op.alter_column('posts', 'author_remote_id', new_column_name='author_id')
+ op.create_foreign_key(op.f('fk_posts_author_id_accounts'), 'posts', 'accounts', ['author_id'], ['remote_id'])
+
+
+def downgrade():
+ op.drop_constraint(op.f('fk_posts_author_id_accounts'), 'posts', type_='foreignkey')
+ op.alter_column('posts', 'author_id', new_column_name='author_remote_id')
+ op.create_foreign_key('fk_posts_author_remote_id_accounts', 'posts', 'accounts', ['author_remote_id'], ['remote_id'])
diff --git a/model.py b/model.py
index bac5742..07d36b9 100644
--- a/model.py
+++ b/model.py
@@ -28,7 +28,7 @@ class Account(db.Model, TimestampMixin):
last_post_fetch = db.Column(db.DateTime, server_default='epoch')
- # backref: posts
+ # backref: tokens
class OAuthToken(db.Model, TimestampMixin):
__tablename__ = 'oauth_tokens'
@@ -37,7 +37,7 @@ class OAuthToken(db.Model, TimestampMixin):
token_secret = db.Column(db.String, nullable=False)
remote_id = db.Column(db.String, db.ForeignKey('accounts.remote_id'))
- account = db.relationship(Account)
+ account = db.relationship(Account, backref=db.backref('tokens', order_by=lambda: db.desc(OAuthToken.created_at)))
class Session(db.Model, TimestampMixin):
__tablename__ = 'sessions'
@@ -53,5 +53,5 @@ class Post(db.Model, TimestampMixin):
remote_id = db.Column(db.String, primary_key=True)
body = db.Column(db.String)
- author_remote_id = db.Column(db.String, db.ForeignKey('accounts.remote_id'))
- author = db.relationship(Account, lazy='joined', backref='posts')
+ author_id = db.Column(db.String, db.ForeignKey('accounts.remote_id'))
+ author = db.relationship(Account)
diff --git a/routes.py b/routes.py
index 10e723e..8a35989 100644
--- a/routes.py
+++ b/routes.py
@@ -2,7 +2,7 @@ from app import app
from flask import render_template, url_for, redirect, request, g, Response
from datetime import datetime
import lib.twitter
-from model import Account, Session
+from model import Account, Session, Post
from app import db
@app.before_request
@@ -21,7 +21,11 @@ def touch_viewer(resp):
@app.route('/')
def index():
- return render_template('index.html')
+ if g.viewer:
+ posts = Post.query.order_by(db.desc(Post.created_at)).limit(30)
+ return render_template('index.html', posts=posts)
+ else:
+ return render_template('index.html')
@app.route('/login/twitter')
def twitter_login_step1():
@@ -35,7 +39,7 @@ def twitter_login_step2():
oauth_token = request.args['oauth_token']
oauth_verifier = request.args['oauth_verifier']
token = lib.twitter.receive_verifier(oauth_token, oauth_verifier, **app.config.get_namespace("TWITTER_"))
- session = Session(account_remote_id = token.remote_id)
+ session = Session(account_id = token.remote_id)
db.session.add(session)
db.session.commit()
resp = Response(status=301, headers={"location": url_for('index')})
@@ -53,5 +57,5 @@ def logout():
@app.route('/debug')
def debug():
import tasks
- tasks.remove_old_sessions.delay()
+ tasks.fetch_posts.s('808418').delay()
return "hi"
diff --git a/tasks.py b/tasks.py
index 2429c7e..a789048 100644
--- a/tasks.py
+++ b/tasks.py
@@ -2,22 +2,34 @@ from celery import Celery
from app import app as flaskapp
from app import db
-from model import Session
+from model import Session, Account
+from lib.twitter import fetch_posts_for_acc
from datetime import timedelta
app = Celery('tasks', broker=flaskapp.config['CELERY_BROKER'], task_serializer='pickle')
@app.task
def remove_old_sessions():
- Session.query.filter(Session.updated_at < (db.func.now() - timedelta(minutes=30))).\
+ Session.query.filter(Session.updated_at < (db.func.now() - timedelta(hours=12))).\
delete(synchronize_session=False)
db.session.commit()
@app.task
def fetch_posts(remote_id):
- pass
+ fetch_posts_for_acc(Account.query.get(remote_id), **flaskapp.config.get_namespace("TWITTER_"))
+
+@app.task
+def queue_fetch_for_most_stale_accs(num=5, min_staleness=timedelta(hours=1)):
+ accs = Account.query\
+ .filter(Account.last_post_fetch < db.func.now() - min_staleness)\
+ .order_by(db.asc(Account.last_post_fetch))\
+ .limit(num)
+ for acc in accs:
+ fetch_posts.s(acc.remote_id).delay()
+
app.add_periodic_task(60*60, remove_old_sessions)
+app.add_periodic_task(60, queue_fetch_for_most_stale_accs)
diff --git a/templates/index.html b/templates/index.html
index 727959d..894f8a9 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -3,7 +3,7 @@
{{g.viewer.account.remote_display_name}}! Log out
your posts:
-{% for post in g.viewer.account.posts %}
+{% for post in posts %}
{{post.body}}
{% else %}
no posts :(