add migration from users_to_feeds table to subscriptions table

This commit is contained in:
Kyle Mahan 2015-04-19 09:29:10 -07:00
parent 3dad2d530e
commit 2e20ead09b
11 changed files with 183 additions and 99 deletions

View file

@ -0,0 +1,74 @@
"""empty message
Revision ID: 564f5a5061f
Revises: 5824a5f06dd
Create Date: 2015-04-19 07:49:24.416817
"""
# revision identifiers, used by Alembic.
revision = '564f5a5061f'
down_revision = '5824a5f06dd'
from alembic import op
import sqlalchemy as sa
from woodwind.models import JsonType
def upgrade():
### commands auto generated by Alembic - please adjust! ###
subsc = op.create_table(
'subscription',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), sa.ForeignKey('user.id'), nullable=False),
sa.Column('feed_id', sa.Integer(), sa.ForeignKey('feed.id'), nullable=False),
sa.Column('name', sa.String(length=256), nullable=True),
sa.Column('tags', JsonType(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
feed = sa.table(
'feed',
sa.column('id', sa.Integer()),
sa.column('name', sa.String()))
u2f = sa.table(
'users_to_feeds',
sa.column('user_id', sa.Integer()),
sa.column('feed_id', sa.Integer()))
values = sa.select(
[u2f.c.user_id, u2f.c.feed_id, feed.c.name]
).select_from(
u2f.join(feed, feed.c.id == u2f.c.feed_id)
)
op.execute(subsc.insert().from_select(
['user_id', 'feed_id', 'name'], values))
op.drop_table('users_to_feeds')
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
u2f = op.create_table(
'users_to_feeds',
sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=True),
sa.Column('feed_id', sa.INTEGER(), autoincrement=False, nullable=True),
sa.ForeignKeyConstraint(['feed_id'], ['feed.id'], name='users_to_feeds_feed_id_fkey'),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], name='users_to_feeds_user_id_fkey')
)
subsc = sa.table(
'subscription',
sa.Column('user_id', sa.Integer()),
sa.Column('feed_id', sa.Integer()),
)
op.execute(u2f.insert().from_select(
['user_id', 'feed_id'],
sa.select([subsc.c.user_id, subsc.c.feed_id])))
op.drop_table('subscription')
### end Alembic commands ###

View file

@ -71,7 +71,6 @@ class User(db.Model):
class Feed(db.Model): class Feed(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
users = db.relationship(User, secondary='users_to_feeds', backref='feeds')
# the name of this feed # the name of this feed
name = db.Column(db.String(256)) name = db.Column(db.String(256))
# url that we subscribed to; periodically check if the feed url # url that we subscribed to; periodically check if the feed url
@ -108,12 +107,16 @@ class Feed(db.Model):
class Subscription(db.Model): class Subscription(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
users = db.relationship(User, backref='subscriptions') user_id = db.Column(db.Integer, db.ForeignKey(User.id))
feed_id = db.Column(db.Integer, db.ForeignKey(Feed.id))
# user-editable name of this subscribed feed # user-editable name of this subscribed feed
name = db.Column(db.String(256)) name = db.Column(db.String(256))
feed = db.relationship(Feed, backref='subscriptions')
tags = db.Column(JsonType) tags = db.Column(JsonType)
user = db.relationship(User, backref='subscriptions')
feed = db.relationship(Feed, backref='subscriptions')
class Entry(db.Model): class Entry(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

View file

@ -405,7 +405,7 @@ article {
article.reply-context { article.reply-context {
margin-bottom: 0; margin-bottom: 0;
background-color: #f3f3f3; } background-color: #f3f3f3; }
article.reply-context img { article.reply-context .content img {
max-height: 240px; max-height: 240px;
max-width: 240px; } max-width: 240px; }
article div { article div {
@ -421,7 +421,7 @@ article {
border-bottom: 1px solid #687d77; border-bottom: 1px solid #687d77;
margin-bottom: 0.5em; } margin-bottom: 0.5em; }
article header img { article header img {
vertical-align: text-middle; vertical-align: middle;
margin: inherit; margin: inherit;
display: inline; display: inline;
max-width: 1.2em; max-width: 1.2em;

View file

@ -76,8 +76,7 @@ article {
&.reply-context { &.reply-context {
margin-bottom: 0; margin-bottom: 0;
background-color: #f3f3f3; background-color: #f3f3f3;
.content img {
img {
max-height: 240px; max-height: 240px;
max-width: 240px; max-width: 240px;
} }
@ -100,7 +99,7 @@ article {
header { header {
img { img {
vertical-align: text-middle; vertical-align: middle;
margin: inherit; margin: inherit;
display: inline; display: inline;
max-width: 1.2em; max-width: 1.2em;

View file

@ -234,7 +234,8 @@ def notify_feed_updated(session, feed, entries):
entries = sorted(entries, key=lambda e: (e.retrieved, e.published), entries = sorted(entries, key=lambda e: (e.retrieved, e.published),
reverse=True) reverse=True)
for user in feed.users: for s in feed.subscriptions:
for user in s.users:
with flask_app.test_request_context(): with flask_app.test_request_context():
flask_login.login_user(user, remember=True) flask_login.login_user(user, remember=True)
message = json.dumps({ message = json.dumps({
@ -245,7 +246,8 @@ def notify_feed_updated(session, feed, entries):
for e in entries for e in entries
], ],
}) })
for topic in 'user:{}'.format(user.id), 'feed:{}'.format(feed.id): for topic in ('user={}'.format(user.id),
'user={}&feed={}'.format(user.id, feed.id)):
redis.publish('woodwind_notify:{}'.format(topic), message) redis.publish('woodwind_notify:{}'.format(topic), message)

View file

@ -13,7 +13,7 @@
<h1>{{ context.title }}</h1> <h1>{{ context.title }}</h1>
{% endif %} {% endif %}
{% if context.content %} {% if context.content %}
<div> <div class="content">
{{ context.content_cleaned | add_preview }} {{ context.content_cleaned | add_preview }}
</div> </div>
{% endif %} {% endif %}
@ -42,7 +42,7 @@
<h1>{{ entry.title }}</h1> <h1>{{ entry.title }}</h1>
{% endif %} {% endif %}
{% if entry.content %} {% if entry.content %}
<div> <div class="content">
{{ entry.content_cleaned | add_preview }} {{ entry.content_cleaned | add_preview }}
</div> </div>
{% endif %} {% endif %}
@ -50,7 +50,9 @@
<footer> <footer>
<a href="{{ entry.permalink }}">{{ entry.published | relative_time }}</a> <a href="{{ entry.permalink }}">{{ entry.published | relative_time }}</a>
<a href="{{ url_for('.index', entry=entry.permalink) }}" target="_blank">&#x21d7;</a> <a href="{{ url_for('.index', entry=entry.permalink) }}" target="_blank">
<img src="{{ url_for('static', filename='open_in_new_window2.png') }}" style="vertical-align:middle;" />
</a>
{% if entry._syndicated_copies %} {% if entry._syndicated_copies %}
(also on{% for copy in entry._syndicated_copies %} <a href="{{ copy.permalink }}">{{ copy.permalink | domain_for_url }}</a>{% endfor %}) (also on{% for copy in entry._syndicated_copies %} <a href="{{ copy.permalink }}">{{ copy.permalink | domain_for_url }}</a>{% endfor %})

View file

@ -27,7 +27,7 @@
<a href="{{ url_for('.index') }}">Home</a> <a href="{{ url_for('.index') }}">Home</a>
</li> </li>
<li> <li>
<a href="{{ url_for('.feeds') }}">Feeds</a> <a href="{{ url_for('.subscriptions') }}">Subscriptions</a>
</li> </li>
<li> <li>
<a href="{{ url_for('.settings') }}">Settings</a> <a href="{{ url_for('.settings') }}">Settings</a>

View file

@ -9,34 +9,38 @@
{% endblock header %} {% endblock header %}
{% block body %} {% block body %}
<p>
<article>
{{ subscriptions | count }} subscriptions
<form action="{{ url_for('.update_all') }}" method="POST"> <form action="{{ url_for('.update_all') }}" method="POST">
<button type="submit">Poll All</button> <button type="submit">Poll All</button>
</form> </form>
</p> </article>
{% for s in subscriptions %}
{% for feed in feeds %}
<article> <article>
<div> <div>
<a href="{{ url_for('.index', feed=feed.get_feed_code()) }}">View only posts from this feed</a> <a href="{{ url_for('.index', subscription=s.id) }}">View only posts from this feed</a>
</div> </div>
<form action="{{ url_for('.edit_feed') }}" method="POST"> <form action="{{ url_for('.edit_subscription') }}" method="POST">
<input type="hidden" name="id" value="{{ feed.id }}"/> <input type="hidden" name="id" value="{{ s.id }}"/>
<label>Name</label> <label>Name</label>
<input type="text" name="name" value="{{ feed.name }}"/> <input type="text" name="name" value="{{ s.name }}"/>
<label>URL</label> <label>URL</label>
<input type="text" name="feed" value="{{ feed.feed }}"/> <input type="text" name="feed" value="{{ s.feed.feed }}" editable="false" />
<button type="submit">Save Edits</button> <button type="submit">Save Edits</button>
</form> </form>
<form action="{{ url_for('.update_feed') }}" method="POST"> <form action="{{ url_for('.update_feed') }}" method="POST">
<input type="hidden" name="id" value="{{ feed.id }}"/> <input type="hidden" name="id" value="{{ s.feed.id }}"/>
<button type="submit">Poll Now</button> <button type="submit">Poll Now</button>
</form> </form>
<form action="{{ url_for('.unsubscribe_feed') }}" method="POST"> <form action="{{ url_for('.unsubscribe') }}" method="POST">
<input type="hidden" name="id" value="{{ feed.id }}"/> <input type="hidden" name="id" value="{{ s.id }}"/>
<button type="submit">Unsubscribe</button> <button type="submit">Unsubscribe</button>
</form> </form>
@ -46,13 +50,13 @@
<div class="feed-details" id="details-{{loop.index}}"> <div class="feed-details" id="details-{{loop.index}}">
<strong>Details</strong> <strong>Details</strong>
<ul> <ul>
<li>Last checked: {{feed.last_checked | relative_time}}</li> <li>Last checked: {{s.feed.last_checked | relative_time}}</li>
<li>Last updated: {{feed.last_updated | relative_time}}</li> <li>Last updated: {{s.feed.last_updated | relative_time}}</li>
<li>PuSH hub: {{feed.push_hub}}</li> <li>PuSH hub: {{s.feed.push_hub}}</li>
<li>PuSH topic: {{feed.push_topic}}</li> <li>PuSH topic: {{s.feed.push_topic}}</li>
<li>PuSH verified: {{feed.push_verified}}</li> <li>PuSH verified: {{s.feed.push_verified}}</li>
<li>PuSH last ping: {{feed.last_pinged | relative_time}}</li> <li>PuSH last ping: {{s.feed.last_pinged | relative_time}}</li>
<li>PuSH expiry: {{feed.push_expiry | relative_time}}</li> <li>PuSH expiry: {{s.feed.push_expiry | relative_time}}</li>
</ul> </ul>
</div> </div>
</article> </article>

View file

@ -1,6 +1,6 @@
from . import tasks from . import tasks
from .extensions import db, login_mgr, micropub from .extensions import db, login_mgr, micropub
from .models import Feed, Entry, User from .models import Feed, Entry, User, Subscription
import flask.ext.login as flask_login import flask.ext.login as flask_login
import binascii import binascii
import bs4 import bs4
@ -28,13 +28,13 @@ def index():
if flask_login.current_user.is_authenticated(): if flask_login.current_user.is_authenticated():
per_page = flask.current_app.config.get('PER_PAGE', 30) per_page = flask.current_app.config.get('PER_PAGE', 30)
offset = (page - 1) * per_page offset = (page - 1) * per_page
entry_query = Entry.query
entry_query = entry_query\ entry_query = Entry.query\
.options(sqlalchemy.orm.subqueryload(Entry.feed), .options(sqlalchemy.orm.subqueryload(Entry.feed),
sqlalchemy.orm.subqueryload(Entry.reply_context))\ sqlalchemy.orm.subqueryload(Entry.reply_context))\
.join(Entry.feed)\ .join(Entry.feed)\
.join(Feed.users)\ .join(Feed.subscriptions)\
.join(Subscription.user)\
.filter(User.id == flask_login.current_user.id) .filter(User.id == flask_login.current_user.id)
if 'entry' in flask.request.args: if 'entry' in flask.request.args:
@ -47,17 +47,16 @@ def index():
entries = [entry] entries = [entry]
has_older = False has_older = False
else: else:
if 'feed' in flask.request.args: if 'subscription' in flask.request.args:
#feed_hex = flask.request.args.get('feed').encode() subsc_id = flask.request.args.get('subscription')
#feed_url = binascii.unhexlify(feed_hex).decode('utf-8') subsc = Subscription.query.get(subsc_id)
feed_url = flask.request.args.get('feed') if not subsc:
feed = Feed.query.filter_by(feed=feed_url).first()
if not feed:
flask.abort(404) flask.abort(404)
entry_query = entry_query.filter(Feed.feed == feed_url) entry_query = entry_query.filter(Subscription.id == subsc_id)
ws_topic = 'feed:{}'.format(feed.id) ws_topic = 'user={}&feed={}'.format(subsc.user.id,
subsc.feed.id)
else: else:
ws_topic = 'user:{}'.format(flask_login.current_user.id) ws_topic = 'user={}'.format(flask_login.current_user.id)
entries = entry_query.order_by(Entry.retrieved.desc(), entries = entry_query.order_by(Entry.retrieved.desc(),
Entry.published.desc())\ Entry.published.desc())\
@ -74,12 +73,13 @@ def install():
return 'Success!' return 'Success!'
@views.route('/feeds') @views.route('/subscriptions')
@flask_login.login_required @flask_login.login_required
def feeds(): def subscriptions():
feeds = flask_login.current_user.feeds subscs = flask_login.current_user.subscriptions
sorted_feeds = sorted(feeds, key=lambda f: f.name and f.name.lower()) sorted_subscs = sorted(subscs, key=lambda s: s.name and s.name.lower())
return flask.render_template('feeds.jinja2', feeds=sorted_feeds) return flask.render_template('subscriptions.jinja2',
subscriptions=sorted_subscs)
@views.route('/settings', methods=['GET', 'POST']) @views.route('/settings', methods=['GET', 'POST'])
@ -114,47 +114,44 @@ def settings():
def update_feed(): def update_feed():
feed_id = flask.request.form.get('id') feed_id = flask.request.form.get('id')
tasks.q.enqueue(tasks.update_feed, feed_id) tasks.q.enqueue(tasks.update_feed, feed_id)
return flask.redirect(flask.url_for('.feeds')) return flask.redirect(flask.url_for('.subscriptions'))
@views.route('/update_all', methods=['POST']) @views.route('/update_all', methods=['POST'])
@flask_login.login_required @flask_login.login_required
def update_all(): def update_all():
for feed in flask_login.current_user.feeds: for s in flask_login.current_user.subscriptions:
tasks.q.enqueue(tasks.update_feed, feed.id) tasks.q.enqueue(tasks.update_feed, s.feed.id)
return flask.redirect(flask.url_for('.feeds')) return flask.redirect(flask.url_for('.subscriptions'))
@views.route('/unsubscribe_feed', methods=['POST']) @views.route('/unsubscribe', methods=['POST'])
@flask_login.login_required @flask_login.login_required
def unsubscribe_feed(): def unsubscribe():
feed_id = flask.request.form.get('id') subsc_id = flask.request.form.get('id')
feed = Feed.query.get(feed_id) subsc = Subscription.query.get(subsc_id)
subsc.delete()
db.session.commit()
flask.flash('Unsubscribed {} ({})'.format(subsc.name))
return flask.redirect(flask.url_for('.subscriptions'))
feeds = flask_login.current_user.feeds
feeds.remove(feed) @views.route('/edit_subscription', methods=['POST'])
@flask_login.login_required
def edit_subscription():
subsc_id = flask.request.form.get('id')
subsc_name = flask.request.form.get('name')
#feed_url = flask.request.form.get('feed')
subsc = Subscription.query.get(subsc_id)
if subsc_name:
subsc.name = subsc_name
#if feed_url:
# feed.feed = feed_url
db.session.commit() db.session.commit()
flask.flash('Unsubscribed {} ({})'.format(feed.name, feed.feed)) flask.flash('Edited {} ({})'.format(subsc.name))
return flask.redirect(flask.url_for('.feeds')) return flask.redirect(flask.url_for('.subscriptions'))
@views.route('/edit_feed', methods=['POST'])
@flask_login.login_required
def edit_feed():
feed_id = flask.request.form.get('id')
feed_name = flask.request.form.get('name')
feed_url = flask.request.form.get('feed')
feed = Feed.query.get(feed_id)
if feed_name:
feed.name = feed_name
if feed_url:
feed.feed = feed_url
db.session.commit()
flask.flash('Edited {} ({})'.format(feed.name, feed.feed))
return flask.redirect(flask.url_for('.feeds'))
@views.route('/logout') @views.route('/logout')
@ -289,7 +286,7 @@ def subscribe():
return flask.render_template('subscribe.jinja2') return flask.render_template('subscribe.jinja2')
def add_subscription(origin, feed_url, type): def add_subscription(origin, feed_url, type, tags=['stream']):
feed = Feed.query.filter_by(feed=feed_url, type=type).first() feed = Feed.query.filter_by(feed=feed_url, type=type).first()
if not feed: if not feed:
if type == 'html': if type == 'html':
@ -308,7 +305,10 @@ def add_subscription(origin, feed_url, type):
origin=origin, feed=feed_url, type=type) origin=origin, feed=feed_url, type=type)
if feed: if feed:
db.session.add(feed) db.session.add(feed)
flask_login.current_user.feeds.append(feed)
flask_login.current_user.subscriptions.append(
Subscription(feed=feed, name=feed.name, tags=tags))
db.session.commit() db.session.commit()
# go ahead and update the fed # go ahead and update the fed
tasks.q.enqueue(tasks.update_feed, feed.id) tasks.q.enqueue(tasks.update_feed, feed.id)