diff options
author | Mike Crute <mcrute@gmail.com> | 2014-06-04 13:22:11 -0400 |
---|---|---|
committer | Mike Crute <mcrute@gmail.com> | 2014-06-04 13:22:11 -0400 |
commit | c68030ada8a1c7bf0b90be17aa37315d8e2ad89b (patch) | |
tree | ba9535fc2eb293522bf9ae47a5d22eeec5484444 | |
parent | ee2972cc3b82edb60c77abddda5b0ff23c9c9797 (diff) | |
download | web_rss_reader-c68030ada8a1c7bf0b90be17aa37315d8e2ad89b.tar.bz2 web_rss_reader-c68030ada8a1c7bf0b90be17aa37315d8e2ad89b.tar.xz web_rss_reader-c68030ada8a1c7bf0b90be17aa37315d8e2ad89b.zip |
Clean up a few bugs
-rwxr-xr-x | app.py | 36 | ||||
-rw-r--r-- | templates/items.html | 83 |
2 files changed, 95 insertions, 24 deletions
@@ -8,6 +8,7 @@ from collections import defaultdict | |||
8 | 8 | ||
9 | from flask import Flask, render_template, jsonify, request, make_response | 9 | from flask import Flask, render_template, jsonify, request, make_response |
10 | 10 | ||
11 | DB_PATH = "/home/mcrute/.newsbeuter/cache.db" | ||
11 | app = Flask(__name__) | 12 | app = Flask(__name__) |
12 | 13 | ||
13 | 14 | ||
@@ -95,15 +96,14 @@ class DBReader(object): | |||
95 | ''' | 96 | ''' |
96 | 97 | ||
97 | def __init__(self, db_path): | 98 | def __init__(self, db_path): |
98 | self.db_path = db_path | 99 | self.con = sqlite3.connect(db_path) |
100 | self.con.row_factory = RSSItem.from_db_row | ||
99 | 101 | ||
100 | def _get_connection(self): | 102 | def close(self): |
101 | conn = sqlite3.connect(self.db_path) | 103 | return self.con.close() |
102 | conn.row_factory = RSSItem.from_db_row | ||
103 | return conn | ||
104 | 104 | ||
105 | def _fetch(self, where=None, params=()): | 105 | def _fetch(self, where=None, params=()): |
106 | with self._get_connection() as con: | 106 | with self.con as con: |
107 | curs = con.cursor() | 107 | curs = con.cursor() |
108 | args = { 'where': '', 'where_cond': '' } | 108 | args = { 'where': '', 'where_cond': '' } |
109 | 109 | ||
@@ -114,7 +114,7 @@ class DBReader(object): | |||
114 | return curs.fetchall() | 114 | return curs.fetchall() |
115 | 115 | ||
116 | def update_unread(self, id, unread=True): | 116 | def update_unread(self, id, unread=True): |
117 | with self._get_connection() as con: | 117 | with self.con as con: |
118 | con.execute( | 118 | con.execute( |
119 | 'UPDATE rss_item SET unread = ? WHERE id = ?', | 119 | 'UPDATE rss_item SET unread = ? WHERE id = ?', |
120 | [1 if unread else 0, id]) | 120 | [1 if unread else 0, id]) |
@@ -139,14 +139,14 @@ class DBReader(object): | |||
139 | return self._fetch("i.feedurl = ?", [RSSFeed.parse_token(token)]) | 139 | return self._fetch("i.feedurl = ?", [RSSFeed.parse_token(token)]) |
140 | 140 | ||
141 | def get_feeds(self): | 141 | def get_feeds(self): |
142 | with self._get_connection() as con: | 142 | with self.con as con: |
143 | con.row_factory = RSSFeed.from_db_row | 143 | con.row_factory = RSSFeed.from_db_row |
144 | curs = con.cursor() | 144 | curs = con.cursor() |
145 | curs.execute('SELECT rssurl, url, title FROM rss_feed') | 145 | curs.execute('SELECT rssurl, url, title FROM rss_feed') |
146 | return curs.fetchall() | 146 | return curs.fetchall() |
147 | 147 | ||
148 | def get_feed(self, token): | 148 | def get_feed(self, token): |
149 | with self._get_connection() as con: | 149 | with self.con as con: |
150 | con.row_factory = RSSFeed.from_db_row | 150 | con.row_factory = RSSFeed.from_db_row |
151 | curs = con.cursor() | 151 | curs = con.cursor() |
152 | curs.execute('SELECT rssurl, url, title FROM rss_feed WHERE rssurl = ?', [RSSFeed.parse_token(token)]) | 152 | curs.execute('SELECT rssurl, url, title FROM rss_feed WHERE rssurl = ?', [RSSFeed.parse_token(token)]) |
@@ -159,38 +159,40 @@ def json_list(data): | |||
159 | 159 | ||
160 | @app.route('/') | 160 | @app.route('/') |
161 | def index(): | 161 | def index(): |
162 | reader = DBReader('../../cache.db') | 162 | reader = DBReader(DB_PATH) |
163 | return render_template('items.html', items=reader.get_unread()) | 163 | return render_template('items.html', items=reader.get_unread()) |
164 | 164 | ||
165 | 165 | ||
166 | @app.route('/feed/') | 166 | @app.route('/feed/') |
167 | def feed_list(): | 167 | def feed_list(): |
168 | reader = DBReader('../../cache.db') | 168 | reader = DBReader(DB_PATH) |
169 | return jsonify({ 'feeds': json_list(reader.get_feeds()) }) | 169 | return jsonify({ 'feeds': json_list(reader.get_feeds()) }) |
170 | 170 | ||
171 | 171 | ||
172 | @app.route('/feed/<token>') | 172 | @app.route('/feed/<token>') |
173 | def feed(token): | 173 | def feed(token): |
174 | reader = DBReader('../../cache.db') | 174 | reader = DBReader(DB_PATH) |
175 | return jsonify(reader.get_feed(token).to_json()) | 175 | return jsonify(reader.get_feed(token).to_json()) |
176 | 176 | ||
177 | 177 | ||
178 | @app.route('/feed/<token>/items') | 178 | @app.route('/feed/<token>/items') |
179 | def feed_items(token): | 179 | def feed_items(token): |
180 | reader = DBReader('../../cache.db') | 180 | reader = DBReader(DB_PATH) |
181 | return jsonify({ 'items': json_list(reader.get_unread_for_feed(token)) }) | 181 | unread = reader.get_unread_for_feed(token) |
182 | return jsonify({ 'items': json_list(unread), "count": len(unread) }) | ||
182 | 183 | ||
183 | 184 | ||
184 | @app.route('/feed/<token>/items/unread') | 185 | @app.route('/feed/<token>/items/unread') |
185 | def unread_feed_items(token): | 186 | def unread_feed_items(token): |
186 | reader = DBReader('../../cache.db') | 187 | reader = DBReader(DB_PATH) |
187 | return jsonify({ 'items': json_list(reader.get_unread_for_feed(token, True)) }) | 188 | unread = reader.get_unread_for_feed(token, True) |
189 | return jsonify({ 'items': json_list(unread), "count": len(unread) }) | ||
188 | 190 | ||
189 | 191 | ||
190 | @app.route("/item/<int:entry_id>", methods=["GET", "POST"]) | 192 | @app.route("/item/<int:entry_id>", methods=["GET", "POST"]) |
191 | def item(entry_id): | 193 | def item(entry_id): |
192 | #post read=1 | 194 | #post read=1 |
193 | reader = DBReader('../../cache.db') | 195 | reader = DBReader(DB_PATH) |
194 | 196 | ||
195 | if request.method == 'POST': | 197 | if request.method == 'POST': |
196 | try: | 198 | try: |
diff --git a/templates/items.html b/templates/items.html index 093b211..22adbba 100644 --- a/templates/items.html +++ b/templates/items.html | |||
@@ -64,10 +64,71 @@ | |||
64 | </script> | 64 | </script> |
65 | 65 | ||
66 | <script type="text/javascript"> | 66 | <script type="text/javascript"> |
67 | /* | ||
68 | * Viewport - jQuery selectors for finding elements in viewport | ||
69 | * | ||
70 | * Copyright (c) 2008-2009 Mika Tuupola | ||
71 | * | ||
72 | * Licensed under the MIT license: | ||
73 | * http://www.opensource.org/licenses/mit-license.php | ||
74 | * | ||
75 | * Project home: | ||
76 | * http://www.appelsiini.net/projects/viewport | ||
77 | * | ||
78 | */ | ||
79 | (function($) { | ||
80 | |||
81 | $.belowthefold = function(element, settings) { | ||
82 | var fold = $(window).height() + $(window).scrollTop(); | ||
83 | return fold <= $(element).offset().top - settings.threshold; | ||
84 | }; | ||
85 | |||
86 | $.abovethetop = function(element, settings) { | ||
87 | var top = $(window).scrollTop(); | ||
88 | return top >= $(element).offset().top + $(element).height() - settings.threshold; | ||
89 | }; | ||
90 | |||
91 | $.rightofscreen = function(element, settings) { | ||
92 | var fold = $(window).width() + $(window).scrollLeft(); | ||
93 | return fold <= $(element).offset().left - settings.threshold; | ||
94 | }; | ||
95 | |||
96 | $.leftofscreen = function(element, settings) { | ||
97 | var left = $(window).scrollLeft(); | ||
98 | return left >= $(element).offset().left + $(element).width() - settings.threshold; | ||
99 | }; | ||
100 | |||
101 | $.inviewport = function(element, settings) { | ||
102 | return !$.rightofscreen(element, settings) && !$.leftofscreen(element, settings) && !$.belowthefold(element, settings) && !$.abovethetop(element, settings); | ||
103 | }; | ||
104 | |||
105 | $.extend($.expr[':'], { | ||
106 | "below-the-fold": function(a, i, m) { | ||
107 | return $.belowthefold(a, {threshold : 0}); | ||
108 | }, | ||
109 | "above-the-top": function(a, i, m) { | ||
110 | return $.abovethetop(a, {threshold : 0}); | ||
111 | }, | ||
112 | "left-of-screen": function(a, i, m) { | ||
113 | return $.leftofscreen(a, {threshold : 0}); | ||
114 | }, | ||
115 | "right-of-screen": function(a, i, m) { | ||
116 | return $.rightofscreen(a, {threshold : 0}); | ||
117 | }, | ||
118 | "in-viewport": function(a, i, m) { | ||
119 | return $.inviewport(a, {threshold : 0}); | ||
120 | } | ||
121 | }); | ||
122 | })(jQuery); | ||
123 | </script> | ||
124 | |||
125 | <script type="text/javascript"> | ||
67 | $(document).ready(function() { | 126 | $(document).ready(function() { |
68 | $("a.headline").on("click", function(event) { | 127 | $("a.headline").on("click", function(event) { |
69 | event.preventDefault(); | 128 | event.preventDefault(); |
70 | var target = $(event.target); | 129 | var target = $(event.target); |
130 | $(".entries .highlighted").removeClass("highlighted"); | ||
131 | target.parents("li").addClass("highlighted"); | ||
71 | target.off("click"); | 132 | target.off("click"); |
72 | $.ajax(target.parents("li").attr("data-url")).done(function(data) { | 133 | $.ajax(target.parents("li").attr("data-url")).done(function(data) { |
73 | if (data.no_content) { | 134 | if (data.no_content) { |
@@ -115,9 +176,13 @@ | |||
115 | var element = $(".entries .highlighted"); | 176 | var element = $(".entries .highlighted"); |
116 | 177 | ||
117 | switch (event.keyCode) { | 178 | switch (event.keyCode) { |
118 | case 74: // J | 179 | case 74: // j |
119 | element.removeClass("highlighted"); | 180 | element.removeClass("highlighted"); |
120 | 181 | ||
182 | if (event.shiftKey) { | ||
183 | element.find("a.headline").trigger("mark-read"); | ||
184 | } | ||
185 | |||
121 | if (element.length == 0) { | 186 | if (element.length == 0) { |
122 | $(".entries li:first-child").first().addClass("highlighted"); | 187 | $(".entries li:first-child").first().addClass("highlighted"); |
123 | } else { | 188 | } else { |
@@ -129,9 +194,11 @@ | |||
129 | } | 194 | } |
130 | } | 195 | } |
131 | 196 | ||
132 | $.scrollTo(".highlighted", { margin: true, offset: -50 }); | 197 | if (!$(".entries .highlighted").is(":in-viewport")) { |
198 | $.scrollTo(".entries .highlighted", { margin: true, offset: -50 }); | ||
199 | } | ||
133 | break; | 200 | break; |
134 | case 75: // K | 201 | case 75: // k |
135 | element.removeClass("highlighted"); | 202 | element.removeClass("highlighted"); |
136 | 203 | ||
137 | if (element.length == 0) { | 204 | if (element.length == 0) { |
@@ -145,9 +212,11 @@ | |||
145 | } | 212 | } |
146 | } | 213 | } |
147 | 214 | ||
148 | $.scrollTo(".highlighted", { margin: true, offset: -50 }); | 215 | if (!$(".entries .highlighted").is(":in-viewport")) { |
216 | $.scrollTo(".entries .highlighted", { margin: true, offset: -50 }); | ||
217 | } | ||
149 | break; | 218 | break; |
150 | case 79: // O | 219 | case 79: // o |
151 | var target = $(element).attr("data-target-url"); | 220 | var target = $(element).attr("data-target-url"); |
152 | if (target) { | 221 | if (target) { |
153 | window.open(target, "_blank"); | 222 | window.open(target, "_blank"); |
@@ -155,7 +224,7 @@ | |||
155 | element.find("a.headline").trigger("click"); | 224 | element.find("a.headline").trigger("click"); |
156 | } | 225 | } |
157 | break; | 226 | break; |
158 | case 82: // R | 227 | case 82: // r |
159 | element.find("a.headline").trigger("mark-read"); | 228 | element.find("a.headline").trigger("mark-read"); |
160 | break; | 229 | break; |
161 | } | 230 | } |
@@ -167,7 +236,7 @@ | |||
167 | <h1>RSS Entries</h1> | 236 | <h1>RSS Entries</h1> |
168 | {% for feed, records in items %} | 237 | {% for feed, records in items %} |
169 | <div class="feed"> | 238 | <div class="feed"> |
170 | <h2>{{ feed }} <a href="#" class="mark-all-read">Mark All Read</a></h2> | 239 | <h2>{{ feed }} ({{ records|length }}) <a href="#" class="mark-all-read">Mark All Read</a></h2> |
171 | <ul class="entries"> | 240 | <ul class="entries"> |
172 | {% for record in records %} | 241 | {% for record in records %} |
173 | <li data-url="{{ record.self_link }}" > | 242 | <li data-url="{{ record.self_link }}" > |