import sqlite3 from sqlite3 import Error import os import functools import operator import math import re from Render import Article import Utils class Cache: def __init__(self, api): self.conn = None self.api = api file_path = os.path.expanduser("~") + '/.config/inomnibus/cache.db' create_categories = """create table if not exists categories ( id text primary key, name text not null, unread_count integer, timestamp integer, date text ) """ create_feeds = """create table if not exists feeds ( id text primary key, name text not null, unread_count integer, timestamp integer, date text, category_id text, foreign key (category_id) references categories (id) ) """ create_articles = """create table if not exists articles ( id text primary key, title text not null, timestamp integer, date text, content text, url text, is_read boolean, origin text, category_id text, foreign key (category_id) references categories (id), foreign key (origin) references feeds (id) ) """ create_favorites = """create table if not exists favorites ( id text primary key, title text not null, timestamp integer, date text, content text, url text ) """ create_links = """create table if not exists links ( id text, url text not null, foreign key (id) references articles (id) ) """ try: self.conn = sqlite3.connect(file_path) self.conn.cursor().execute(create_categories) self.conn.cursor().execute(create_feeds) self.conn.cursor().execute(create_articles) self.conn.cursor().execute(create_links) self.conn.cursor().execute(create_favorites) except Error as e: Utils.writeLog(e) def markStreamAsRead(self, streamId): cur = self.conn.cursor() if re.search("^feed/[0-9]+", streamId): table = "feeds" else: table = "categories" cur.execute("""select timestamp from """ + table + """ where id = ?""", (streamId,)) ts = cur.fetchone()[0] if self.api.markStreamAsRead(streamId, ts): if table == "feeds": cur.execute("""select unread_count from feeds where id = ?""", (streamId,)) uc = int(cur.fetchone()[0]) cur.execute("""update feeds set unread_count = 0 where id = ?""", (streamId,)) cur.execute("""update categories set unread_count = unread_count - :uc where id in (select category_id from feeds where id = :sID)""", {"uc": uc, "sID": streamId}) cur.execute("""update articles set is_read = 1 where origin = ?""", (streamId,)) else: cur.execute("""update categories set unread_count = 0 where id = ?""", (streamId,)) cur.execute("""update feeds set unread_count = 0 where id in (select id from feeds where category_id = ?)""", (streamId,)) cur.execute("""update articles set is_read = 1 where category_id = ?""", (streamId,)) self.conn.commit() def getArticle(self, id, is_fav): cur = self.conn.cursor() Utils.writeLog(is_fav) if is_fav: cur.execute("""select title,content,url from favorites where id = ?""", (id,)) else: cur.execute("""select title,content,url from articles where id = ?""", (id,)) return cur.fetchone() def toggleArticleStatus(self, id): inc = 0 cur = self.conn.cursor() cur.execute("""update articles set is_read = not is_read where id = ?""", (id,)) cur.execute("""select origin, category_id, is_read from articles where id = ?""", (id,)) feed_id, category_id, is_read = cur.fetchone() if is_read == 0: inc = 1 else: inc = -1 cur.execute("""update categories set unread_count = unread_count + ? where id = ?""", (inc, category_id,)) cur.execute("""update feeds set unread_count = unread_count + ? where id = ?""", (inc, feed_id,)) if self.api.toggleArticleStatus(id, is_read): self.conn.commit() else: self.conn.rollback() def toggleArticleStarred(self, id): inc = 0 cur = self.conn.cursor() is_favorite = 1 cur.execute("""select id from favorites where id = ?""", (id,)) if bool(cur.fetchone()): cur.execute("""delete from favorites where id = ?""", (id,)) is_favorite = 0 inc = -1 else: inc = 1 cur.execute("""select title, timestamp, date, content, url from articles where id = ?""", (id,)) title, timestamp, date, content, url = cur.fetchone() cur.execute("""insert into favorites(id, title, timestamp, date, content, url) values(:id, :title, :timestamp, :date ,:content, :url)""", {"id": id, "title": title, "timestamp": timestamp, "date": date, "content": content, "url": url}) cur.execute("""update categories set unread_count = unread_count + ? where id = ?""", (inc, "Favorites",)) if self.api.toggleArticleStarred(id, is_favorite): self.conn.commit() else: self.conn.rollback() def getArticleLinks(self, id): cur = self.conn.cursor() cur.execute("""select url from links where id = ?""", (id,)) links = functools.reduce(operator.iconcat, cur.fetchall(), []) return links def getArticlesFromFeed(self, feed_id, is_read): cur = self.conn.cursor() cur.execute("""select * from articles where origin = ? and is_read = ? order by timestamp desc""", (feed_id, is_read,)) return cur.fetchall() def getArticlesFromCategory(self, category_id, is_read): cur = self.conn.cursor() if category_id == "Favorites": cur.execute("""select * from favorites order by timestamp desc""") else: cur.execute("""select * from articles where category_id = ? and is_read = ? order by timestamp desc""", (category_id, is_read,)) return cur.fetchall() def getCategories(self, show_read): cur = self.conn.cursor() statement = """select * from categories where id = 'Favorites'""" cur.execute(statement) favorites = cur.fetchone() statement = """select * from categories where unread_count != 0 and id != 'Favorites' order by timestamp desc""" if show_read == 1: statement = """select * from categories where id != 'Favorites' order by timestamp desc""" cur.execute(statement) return [favorites, *cur.fetchall()] def getFeeds(self, category_id, show_read): statement = """select * from feeds where category_id = ? and unread_count != 0 order by timestamp desc""" if show_read == 1: statement = """select * from feeds where category_id = ? order by timestamp desc""" cur = self.conn.cursor() cur.execute(statement, (category_id,)) return cur.fetchall() def refresh(self): # noqa timestamps = {} cur = self.conn.cursor() cur.execute("""select id, timestamp from categories""") for row in cur.fetchall(): timestamps[row[0]] = row[1] Utils.writeLog(timestamps) self.api.refresh() for item in self.api.unreadCounts.keys(): if item[0:13] != "user/-/label/": continue data = self.api.unreadCounts[item] cur.execute("""insert into categories(id,name,unread_count,timestamp,date) values(:id,:name,:count,:ts,:d) on conflict(id) do update set unread_count = :count, timestamp = :ts, date = :d; """, {"id": item, "name": item[13:], "count": data[0], "ts": data[1], "d": data[2]}) self.conn.commit() for item in self.api.feeds: data = self.api.unreadCounts[item["id"]] cur.execute("""insert into feeds(id,name,unread_count,timestamp,date,category_id) values(:id,:name,:count,:ts,:d,:c_id) on conflict(id) do update set unread_count = :count, timestamp = :ts, date = :d,category_id = :c_id; """, {"id": item["id"], "name": item["title"], "count": data[0], "ts": data[1], "d": data[2], "c_id": item["categories"][0]["id"]}) self.conn.commit() yield "Fetching Favorites" cur.execute("""select timestamp from categories where id = 'Favorites'""") favoritesTimestampTuple = cur.fetchone() if favoritesTimestampTuple is None: cur.execute("""insert into categories(id,name,unread_count,timestamp,date) values('Favorites','Favorites',0,0,'')""") self.conn.commit() last_updated, last_updated_date, favorites = self.api.getFavorites() cur.execute("""update categories set unread_count = :count, timestamp = :ts, date = :d where id = 'Favorites'""", {"count": len(favorites), "ts": last_updated, "d": last_updated_date}) self.conn.commit() for favorite in favorites: favObj = Article(favorite) cur.execute("""insert or ignore into favorites(id,title,timestamp,date,content,url) values(:id,:t,:ts,:d,:c,:u)""", {"id": favObj.id, "t": favObj.title, "ts": favObj.timestamp, "d": favObj.date, "c": favObj.text, "u": favObj.url}) for link in favObj.links: cur.execute("""insert or ignore into links(id,url) values(?,?)""", (favObj.id, link)) self.conn.commit() cur.execute("""select c.id, c.name, c.unread_count, (select count(*) from articles a where a.category_id = c.id) as articles_num from categories c where c.id != 'Favorites'""") for row in cur.fetchall(): yield "Fetching category " + row[1] try: timestamp = str(math.floor(timestamps[row[0]] / 1000000)) except BaseException: timestamp = 0 articles = self.api.articlesFromCategory( row[0], count=str(row[2]), timestamp=timestamp, number=str(row[3])) for article in articles: articleObj = Article(article) cur.execute("""insert into articles(id,title,timestamp,date,content,url,is_read,origin,category_id) values(:id,:t,:ts,:d,:c,:u,:r,:o,:c_id) on conflict(id) do update set id = :id, title = :t, timestamp = :ts, date = :d, content = :c, url = :u, is_read = :r, category_id = :c_id, origin = :o; """, {"id": articleObj.id, "t": articleObj.title, "ts": articleObj.timestamp, "d": articleObj.date, "c": articleObj.text, "u": articleObj.url, "r": articleObj.is_read, "o": articleObj.origin, "c_id": row[0]}) for link in articleObj.links: cur.execute("""insert into links(id,url) values(?,?)""", (articleObj.id, link)) self.conn.commit()