summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mcrute@gmail.com>2014-06-04 13:22:11 -0400
committerMike Crute <mcrute@gmail.com>2014-06-04 13:22:11 -0400
commitc68030ada8a1c7bf0b90be17aa37315d8e2ad89b (patch)
treeba9535fc2eb293522bf9ae47a5d22eeec5484444
parentee2972cc3b82edb60c77abddda5b0ff23c9c9797 (diff)
downloadweb_rss_reader-c68030ada8a1c7bf0b90be17aa37315d8e2ad89b.tar.bz2
web_rss_reader-c68030ada8a1c7bf0b90be17aa37315d8e2ad89b.tar.xz
web_rss_reader-c68030ada8a1c7bf0b90be17aa37315d8e2ad89b.zip
Clean up a few bugs
-rwxr-xr-xapp.py36
-rw-r--r--templates/items.html83
2 files changed, 95 insertions, 24 deletions
diff --git a/app.py b/app.py
index 755d55c..1740f51 100755
--- a/app.py
+++ b/app.py
@@ -8,6 +8,7 @@ from collections import defaultdict
8 8
9from flask import Flask, render_template, jsonify, request, make_response 9from flask import Flask, render_template, jsonify, request, make_response
10 10
11DB_PATH = "/home/mcrute/.newsbeuter/cache.db"
11app = Flask(__name__) 12app = 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('/')
161def index(): 161def 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/')
167def feed_list(): 167def 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>')
173def feed(token): 173def 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')
179def feed_items(token): 179def 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')
185def unread_feed_items(token): 186def 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"])
191def item(entry_id): 193def 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 }}" >