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):
id = db.Column(db.Integer, primary_key=True)
users = db.relationship(User, secondary='users_to_feeds', backref='feeds')
# the name of this feed
name = db.Column(db.String(256))
# url that we subscribed to; periodically check if the feed url
@ -108,12 +107,16 @@ class Feed(db.Model):
class Subscription(db.Model):
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
name = db.Column(db.String(256))
feed = db.relationship(Feed, backref='subscriptions')
tags = db.Column(JsonType)
user = db.relationship(User, backref='subscriptions')
feed = db.relationship(Feed, backref='subscriptions')
class Entry(db.Model):
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 {
margin-bottom: 0;
background-color: #f3f3f3; }
article.reply-context img {
article.reply-context .content img {
max-height: 240px;
max-width: 240px; }
article div {
@ -421,7 +421,7 @@ article {
border-bottom: 1px solid #687d77;
margin-bottom: 0.5em; }
article header img {
vertical-align: text-middle;
vertical-align: middle;
margin: inherit;
display: inline;
max-width: 1.2em;

View file

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

View file

@ -234,19 +234,21 @@ def notify_feed_updated(session, feed, entries):
entries = sorted(entries, key=lambda e: (e.retrieved, e.published),
reverse=True)
for user in feed.users:
with flask_app.test_request_context():
flask_login.login_user(user, remember=True)
message = json.dumps({
'user': user.id,
'feed': feed.id,
'entries': [
render_template('_entry.jinja2', feed=feed, entry=e)
for e in entries
],
})
for topic in 'user:{}'.format(user.id), 'feed:{}'.format(feed.id):
redis.publish('woodwind_notify:{}'.format(topic), message)
for s in feed.subscriptions:
for user in s.users:
with flask_app.test_request_context():
flask_login.login_user(user, remember=True)
message = json.dumps({
'user': user.id,
'feed': feed.id,
'entries': [
render_template('_entry.jinja2', feed=feed, entry=e)
for e in entries
],
})
for topic in ('user={}'.format(user.id),
'user={}&feed={}'.format(user.id, feed.id)):
redis.publish('woodwind_notify:{}'.format(topic), message)
def is_content_equal(e1, e2):

View file

@ -13,7 +13,7 @@
<h1>{{ context.title }}</h1>
{% endif %}
{% if context.content %}
<div>
<div class="content">
{{ context.content_cleaned | add_preview }}
</div>
{% endif %}
@ -42,7 +42,7 @@
<h1>{{ entry.title }}</h1>
{% endif %}
{% if entry.content %}
<div>
<div class="content">
{{ entry.content_cleaned | add_preview }}
</div>
{% endif %}
@ -50,7 +50,9 @@
<footer>
<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 %}
(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>
</li>
<li>
<a href="{{ url_for('.feeds') }}">Feeds</a>
<a href="{{ url_for('.subscriptions') }}">Subscriptions</a>
</li>
<li>
<a href="{{ url_for('.settings') }}">Settings</a>

View file

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

View file

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