#!/usr/bin/python # -*- coding: utf-8 -*- from asyncio import Lock from sqlite3 import connect, Error, IntegrityError import sys import time DBLOCK = Lock() class DatabaseSQLite: #from slixfeed.log import Logger #from slixfeed.utilities import DateAndTime, Url # DBLOCK = Lock() #logger = Logger(__name__) def create_connection(db_file): """ Create a database connection to the SQLite database specified by db_file. Parameters ---------- db_file : str Path to database file. Returns ------- conn : object Connection object or None. """ time_begin = time.time() function_name = sys._getframe().f_code.co_name # message_log = '{}' # logger.debug(message_log.format(function_name)) conn = None try: conn = connect(db_file) conn.execute("PRAGMA foreign_keys = ON") # return conn except Error as e: print(e) # logger.warning('Error creating a connection to database {}.'.format(db_file)) # logger.error(e) time_end = time.time() difference = time_end - time_begin if difference > 1: logger.warning('{} (time: {})'.format(function_name, difference)) return conn def create_tables(sql_filename, db_file): """ Create SQLite tables. Parameters ---------- db_file : str Path to database file. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {}' # .format(function_name, db_file)) with DatabaseSQLite.create_connection(db_file) as conn: # Read the SQL script from the file with open(sql_filename, 'r') as sql_file: sql_script = sql_file.read() cur = conn.cursor() # Execute the SQL script try: cur.executescript(sql_script) print("Table created successfully.") except sqlite3.Error as e: print(f"An error occurred: {e}") async def associate_entries_tags_jids(db_file, entry): async with DBLOCK: with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() jid = entry['jid'] url_hash = entry['url_hash'] entry_id = DatabaseSQLite.get_entry_id_by_url_hash(db_file, url_hash) jid_id = DatabaseSQLite.get_jid_id_by_jid(db_file, jid) if entry_id: for tag in entry['tags']: tag_id = DatabaseSQLite.get_tag_id_by_tag(db_file, tag) cet_id = DatabaseSQLite.get_combination_id_by_entry_id_tag_id_jid_id(db_file, entry_id, tag_id, jid_id) if not cet_id: sql = ( """ INSERT INTO combination_entries_tags_jids ( entry_id, tag_id, jid_id) VALUES ( ?, ?, ?); """ ) par = (entry_id, tag_id, jid_id) try: cur.execute(sql, par) except IntegrityError as e: print('associate_entries_tags_jids') print(e) async def add_tags(db_file, entries): """ Batch insertion of tags. Parameters ---------- db_file : str Path to database file. entries : list Set of entries. Returns ------- None. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {}' # .format(function_name, db_file)) async with DBLOCK: with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() for entry in entries: tags = entry['tags'] for tag in tags: # sql = ( # """ # INSERT OR IGNORE INTO main_tags(tag) VALUES (?); # """ # ) if not DatabaseSQLite.get_tag_id_by_tag(db_file, tag): sql = ( """ INSERT INTO main_tags(tag) VALUES(?); """ ) par = (tag,) try: cur.execute(sql, par) except IntegrityError as e: print(e) async def add_new_entries(db_file, entries): """ Batch insert of new entries into table entries. Parameters ---------- db_file : str Path to database file. entries : list Set of entries. Returns ------- None. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {}' # .format(function_name, db_file)) async with DBLOCK: with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() for entry in entries: url_hash = entry['url_hash'] url = entry['link'] title = entry['title'] summary = entry['summary'] jid = entry['jid'] date_first = entry['published'] date_last = entry['published'] # instances = entry['instances'] # Import entries jid_id = DatabaseSQLite.get_jid_id_by_jid(db_file, jid) sql = ( """ INSERT INTO main_entries( url_hash, url, title, summary, jid_id, date_first, date_last) VALUES( ?, ?, ?, ?, ?, ?, ?); """ ) par = (url_hash, url, title, summary, jid_id, date_first, date_last) try: cur.execute(sql, par) except IntegrityError as e: print(e) print(jid_id) print(entry) # logger.warning("Skipping: " + str(url)) # logger.error(e) # TODO An additional function to ssociate jid_id (jid) with entry_id (hash_url) async def set_jid(db_file, jid): """ Add a JID to database. Parameters ---------- db_file : str Path to database file. jid : str A Jabber ID. Returns ------- None. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} jid: {}' # .format(function_name, db_file, jid)) sql = ( """ INSERT INTO main_jids( jid) VALUES( ?); """ ) par = (jid, ) async with DBLOCK: with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() try: cur.execute(sql, par) except IntegrityError as e: print(e) # logger.warning("Skipping: " + str(url)) # logger.error(e) def get_entries_count(db_file): """ Get entries count. Parameters ---------- db_file : str Path to database file. Returns ------- result : tuple Number. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {}' # .format(function_name, db_file)) sql = ( """ SELECT count FROM main_statistics WHERE type = "entries"; """ ) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql).fetchone() return result[0] if result and len(result) == 1 else result def get_combination_id_by_entry_id_tag_id_jid_id(db_file, entry_id, tag_id, jid_id): """ Get ID by a given Entry ID and a given Tag ID and a given Jabber ID. Parameters ---------- db_file : str Path to database file. entry_id : str Entry ID. tag_id : str Tag ID. jid_id : str Jabber ID. Returns ------- result : tuple ID. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} entry_id: {} tag_id: {} jid_id: {}' # .format(function_name, db_file, entry_id, tag_id, jid_id)) sql = ( """ SELECT id FROM combination_entries_tags_jids WHERE entry_id = :entry_id AND tag_id = :tag_id AND jid_id = :jid_id; """ ) par = { "entry_id": entry_id, "tag_id": tag_id, "jid_id": jid_id } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result async def delete_combination_row_by_url_hash_and_tag_and_jid(db_file, url_hash, tags, jid): """ Delete a row by a given entry ID and a given Jabber ID and given tags. Parameters ---------- db_file : str Path to database file. url_hash : str URL hash. tags : list Tags. jid : str Jabber ID. Returns ------- None. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} url_hash: {} tag_id: {} jid_id: {}' # .format(function_name, db_file, url_hash, tag_id, jid_id)) sql = ( """ DELETE FROM combination_entries_tags_jids WHERE entry_id = (SELECT id FROM main_entries WHERE url_hash = :url_hash) AND tag_id = (SELECT id FROM main_tags WHERE tag = :tag) AND jid_id = (SELECT id FROM main_jids WHERE jid = :jid); """ ) async with DBLOCK: with DatabaseSQLite.create_connection(db_file) as conn: for tag in tags: par = { "url_hash": url_hash, "tag": tag, "jid": jid } cur = conn.cursor() cur.execute(sql, par) def get_tag_id_and_instances_by_tag(db_file, tag): """ Get a tag ID and instances by a given tag. Parameters ---------- db_file : str Path to database file. tag : str Tag. Returns ------- result : tuple Tag ID. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} tag: {}' # .format(function_name, db_file, tag)) sql = ( """ SELECT id, instances FROM main_tags WHERE tag = ?; """ ) par = (tag,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() # return result[0] if result else None, None if not result: result = None, None return result def get_tags_and_instances_by_url_hash(db_file, url_hash): """ Get tags and instances by a given URL hash. Parameters ---------- db_file : str Path to database file. url_hash : str A hash of a URL. Returns ------- result : tuple Tags and instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT mt.tag, mt.instances FROM main_tags AS mt INNER JOIN combination_entries_tags_jids AS co ON mt.id = co.tag_id INNER JOIN main_entries AS me ON me.id = co.entry_id WHERE me.url_hash = ? ORDER BY mt.instances DESC; """ ) par = (url_hash,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_tags_and_instances_by_entry_id(db_file, entry_id): """ Get tags and instances by a given ID entry. Parameters ---------- db_file : str Path to database file. entry_id : str An ID of an entry. Returns ------- result : tuple Tags and instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT main_tags.tag, main_tags.instances FROM main_tags INNER JOIN combination_entries_tags_jids ON main_tags.id = combination_entries_tags_jids.tag_id WHERE combination_entries_tags_jids.entry_id = ? ORDER BY main_tags.instances DESC; """ ) par = (entry_id,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_jids_and_tags_by_entry_id(db_file, entry_id): """ Get JIDs and tags by a given ID entry. Parameters ---------- db_file : str Path to database file. entry_id : str An ID of an entry. Returns ------- result : tuple JIDs and tags. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT main_jids.jid, main_tags.tag FROM main_tags INNER JOIN combination_entries_tags_jids ON main_tags.id = combination_entries_tags_jids.tag_id INNER JOIN main_jids ON main_jids.id = combination_entries_tags_jids.jid_id WHERE combination_entries_tags_jids.entry_id = ? ORDER BY main_tags.instances DESC; """ ) par = (entry_id,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_jids_and_tags_by_url_hash(db_file, url_hash): """ Get JIDs and tags by a given URI hash. Parameters ---------- db_file : str Path to database file. url_hash : str A URL hash of an entry. Returns ------- result : tuple JIDs and tags. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT main_jids.jid, main_tags.tag FROM main_tags INNER JOIN combination_entries_tags_jids ON main_tags.id = combination_entries_tags_jids.tag_id INNER JOIN main_jids ON main_jids.id = combination_entries_tags_jids.jid_id INNER JOIN main_entries ON main_entries.id = combination_entries_tags_jids.entry_id WHERE main_entries.url_hash = ? ORDER BY main_tags.instances DESC; """ ) par = (url_hash,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_tag_id_by_tag(db_file, tag): """ Get a tag ID by a given tag. Parameters ---------- db_file : str Path to database file. tag : str Tag. Returns ------- result : tuple Tag ID. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} tag: {}' # .format(function_name, db_file, tag)) sql = ( """ SELECT id FROM main_tags WHERE tag = ?; """ ) par = (tag,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result def get_entry_id_by_url_hash(db_file, url_hash): """ Get an entry ID by a given URL hash. Parameters ---------- db_file : str Path to database file. url_hash : str MD5 hash of URL. Returns ------- result : tuple Entry ID. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} url_hash: {}' # .format(function_name, db_file, url_hash)) sql = ( """ SELECT id FROM main_entries WHERE url_hash = ?; """ ) par = (url_hash,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result def get_entry_instances_by_url_hash(db_file, url_hash): """ Get value of entry instances by a given URL hash. Parameters ---------- db_file : str Path to database file. url_hash : str MD5 hash of URL. Returns ------- result : tuple Value of entry instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} url_hash: {}' # .format(function_name, db_file, url_hash)) sql = ( """ SELECT instances FROM main_entries WHERE url_hash = ?; """ ) par = (url_hash,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result def get_entry_by_url_hash(db_file, url_hash): """ Get entry of a given URL hash. Parameters ---------- db_file : str Path to database file. url_hash : str MD5 hash of URL. Returns ------- result : tuple Entry properties. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} url_hash: {}' # .format(function_name, db_file, url_hash)) sql = ( """ SELECT * FROM main_entries WHERE url_hash = ?; """ ) par = (url_hash,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result def get_entries_new(db_file, index_first): """ Get new entries. Parameters ---------- db_file : str Path to database file. index_first : str . Returns ------- result : tuple Entries properties. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) # NOTE Consider date_first sql = ( """ SELECT * FROM main_entries ORDER BY date_first DESC LIMIT 10 OFFSET ?; """ ) par = (index_first,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_entries_popular(db_file, index_first): """ Get popular entries. Parameters ---------- db_file : str Path to database file. index_first : str . Returns ------- result : tuple Entries properties. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) # NOTE Consider date_first sql = ( """ SELECT * FROM main_entries ORDER BY instances DESC LIMIT 10 OFFSET ?; """ ) par = (index_first,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_entries_recent(db_file, index_first): """ Get recent entries. Parameters ---------- db_file : str Path to database file. index_first : str . Returns ------- result : tuple Entries properties. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) # NOTE Consider date_first sql = ( """ SELECT * FROM main_entries ORDER BY date_last DESC LIMIT 10 OFFSET ?; """ ) par = (index_first,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_entries_by_query(db_file, query, index_first): """ Get entries by a query. Parameters ---------- db_file : str Path to database file. query : str Search query. index_first : str . Returns ------- result : tuple Entries properties. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) # NOTE Consider date_first sql = ( """ SELECT * FROM main_entries WHERE title LIKE :query OR url LIKE :query OR summary LIKE :query ORDER BY instances DESC LIMIT 10 OFFSET :index_first; """ ) par = { "query": f'%{query}%', "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_entries_count_by_query(db_file, query): """ Get entries count by a query. Parameters ---------- db_file : str Path to database file. query : str Search query. Returns ------- result : tuple Entries properties. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {}' # .format(function_name, db_file)) # NOTE Consider date_first sql = ( """ SELECT COUNT(id) FROM main_entries WHERE title LIKE :query OR url LIKE :query OR summary LIKE :query ORDER BY instances DESC; """ ) par = { "query": f'%{query}%', } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result def get_entries_by_jid_and_tag(db_file, jid, tag, index_first): """ Get entries by a tag and a Jabber ID. Parameters ---------- db_file : str Path to database file. tag : str Tag. jid : str Jabber ID. index_first : str . Returns ------- result : tuple Entries properties. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} tag: {} jid: {} index_first: {}' # .format(function_name, db_file, tag, jid, index_first)) # NOTE Consider date_first sql = ( """ SELECT DISTINCT me.* FROM main_entries AS me INNER JOIN combination_entries_tags_jids AS co ON co.entry_id = me.id INNER JOIN main_jids AS mj ON mj.id = co.jid_id INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE mj.jid = :jid AND mt.tag = :tag ORDER BY instances DESC LIMIT 10 OFFSET :index_first; """ ) par = { "jid": jid, "tag": tag, "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_entries_count_by_jid_and_tag(db_file, jid, tag): """ Get entries count by a tag and a Jabber ID. Parameters ---------- db_file : str Path to database file. tag : str Tag. jid : str Jabber ID. Returns ------- result : tuple Entries properties. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} tag: {} jid: {}' # .format(function_name, db_file, tag, jid)) # NOTE Consider date_first sql = ( """ SELECT COUNT(DISTINCT me.id) FROM main_entries AS me INNER JOIN combination_entries_tags_jids AS co ON co.entry_id = me.id INNER JOIN main_jids AS mj ON mj.id = co.jid_id INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE mj.jid = :jid AND mt.tag = :tag; """ ) par = { "jid": jid, "tag": tag } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result def get_entries_by_jid_and_query(db_file, jid, query, index_first): """ Get entries by a query and a Jabber ID. Parameters ---------- db_file : str Path to database file. query : str Search query. jid : str Jabber ID. index_first : str . Returns ------- result : tuple Entries properties. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} query: {} jid: {} index_first: {}' # .format(function_name, db_file, query, jid, index_first)) # NOTE Consider date_first sql = ( """ SELECT DISTINCT me.* FROM main_entries AS me INNER JOIN combination_entries_tags_jids AS co ON co.entry_id = me.id INNER JOIN main_jids AS mj ON mj.id = co.jid_id WHERE mj.jid = :jid AND (title LIKE :query OR url LIKE :query OR summary LIKE :query) ORDER BY instances DESC LIMIT 10 OFFSET :index_first; """ ) par = { "jid": jid, "query": f'%{query}%', "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_entries_count_by_jid_and_query(db_file, jid, query): """ Get entries count by a query and a Jabber ID. Parameters ---------- db_file : str Path to database file. query : str Search query. jid : str Jabber ID. Returns ------- result : tuple Entries properties. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} query: {} jid: {}' # .format(function_name, db_file, query, jid)) # NOTE Consider date_first sql = ( """ SELECT COUNT(DISTINCT me.id) FROM main_entries AS me INNER JOIN combination_entries_tags_jids AS co ON co.entry_id = me.id INNER JOIN main_jids AS mj ON mj.id = co.jid_id WHERE mj.jid = :jid AND (title LIKE :query OR url LIKE :query OR summary LIKE :query); """ ) par = { "jid": jid, "query": f'%{query}%' } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result def get_entries_by_jid(db_file, jid, index_first): """ Get entries by a Jabber ID. Parameters ---------- db_file : str Path to database file. jid : str Jabber ID. index_first : str . Returns ------- result : tuple Entries properties. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} jid: {} index_first: {}' # .format(function_name, db_file, jid, index_first)) # NOTE Consider date_first sql = ( """ SELECT DISTINCT me.* FROM main_entries AS me INNER JOIN combination_entries_tags_jids AS co ON co.entry_id = me.id INNER JOIN main_jids AS mj ON mj.id = co.jid_id WHERE mj.jid = :jid ORDER BY instances DESC LIMIT 10 OFFSET :index_first; """ ) par = { "jid": jid, "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_entries_count_by_jid(db_file, jid): """ Get entries count by a Jabber ID. Parameters ---------- db_file : str Path to database file. jid : str Jabber ID. Returns ------- result : tuple Entries properties. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} jid: {}' # .format(function_name, db_file, jid)) # NOTE Consider date_first sql = ( """ SELECT COUNT(DISTINCT me.id) FROM main_entries AS me INNER JOIN combination_entries_tags_jids AS co ON co.entry_id = me.id INNER JOIN main_jids AS mj ON mj.id = co.jid_id WHERE mj.jid = :jid; """ ) par = { "jid": jid } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result def get_entries_count_by_tag(db_file, tag): """ Get entries count by a given tag. Parameters ---------- db_file : str Path to database file. tag : str A tag. Returns ------- result : tuple Entries. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} tag: {}' # .format(function_name, db_file, tag)) sql = ( """ SELECT COUNT(DISTINCT entries.id) FROM main_entries AS entries INNER JOIN combination_entries_tags_jids AS co ON entries.id = co.entry_id INNER JOIN main_tags AS tags ON tags.id = co.tag_id WHERE tags.tag = :tag; """ ) par = (tag,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result def get_entries_popular_by_tag(db_file, tag, index_first): """ Get popular entries by a given tag. Parameters ---------- db_file : str Path to database file. tag : str A tag. index_first : str . Returns ------- result : tuple Entries. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} tag: {}' # .format(function_name, db_file, tag)) sql = ( """ SELECT DISTINCT entries.* FROM main_entries AS entries INNER JOIN combination_entries_tags_jids AS co ON entries.id = co.entry_id INNER JOIN main_tags AS tags ON tags.id = co.tag_id WHERE tags.tag = :tag ORDER BY entries.instances DESC LIMIT 10 OFFSET :index_first; """ ) par = { "tag": tag, "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_entries_recent_by_tag(db_file, tag, index_first): """ Get recent entries by a given tag. Parameters ---------- db_file : str Path to database file. tag : str A tag. index_first : str . Returns ------- result : tuple Entries. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} tag: {}' # .format(function_name, db_file, tag)) sql = ( """ SELECT DISTINCT entries.* FROM main_entries AS entries INNER JOIN combination_entries_tags_jids AS co ON entries.id = co.entry_id INNER JOIN main_tags AS tags ON tags.id = co.tag_id WHERE tags.tag = :tag ORDER BY date_last DESC LIMIT 10 OFFSET :index_first; """ ) par = { "tag": tag, "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_entries_new_by_tag(db_file, tag, index_first): """ Get new entries by a given tag. Parameters ---------- db_file : str Path to database file. tag : str A tag. index_first : str . Returns ------- result : tuple Entries. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} tag: {}' # .format(function_name, db_file, tag)) sql = ( """ SELECT DISTINCT entries.* FROM main_entries AS entries INNER JOIN combination_entries_tags_jids AS co ON entries.id = co.entry_id INNER JOIN main_tags AS tags ON tags.id = co.tag_id WHERE tags.tag = :tag ORDER BY date_first DESC LIMIT 10 OFFSET :index_first; """ ) par = { "tag": tag, "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_tags_30(db_file): """ Get 30 tags. Parameters ---------- db_file : str Path to database file. Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT tag, instances FROM main_tags ORDER BY instances DESC LIMIT 30; """ ) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql).fetchall() return result def get_30_tags_by_entries_popular(db_file, index_first): """ Get 30 tags by currently viewed popular entries. Parameters ---------- db_file : str Path to database file. index_first : str . Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT DISTINCT mt.tag, mt.instances FROM combination_entries_tags_jids AS co INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE co.entry_id IN ( SELECT id FROM main_entries ORDER BY instances DESC LIMIT 10 OFFSET ? ) ORDER BY mt.instances DESC LIMIT 30; """ ) par = (index_first,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_30_tags_by_entries_new_by_tag(db_file, tag, index_first): """ Get 30 tags by currently viewed new entries by a given tag. Parameters ---------- db_file : str Path to database file. index_first : str . Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT DISTINCT mt.tag, mt.instances FROM combination_entries_tags_jids AS co INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE co.entry_id IN ( SELECT DISTINCT entries.id FROM main_entries AS entries INNER JOIN combination_entries_tags_jids AS co ON entries.id = co.entry_id INNER JOIN main_tags AS tags ON tags.id = co.tag_id WHERE tags.tag = :tag ORDER BY date_first DESC LIMIT 10 OFFSET :index_first ) ORDER BY mt.instances DESC LIMIT 30; """ ) par = { "tag": tag, "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_30_tags_by_entries_popular_by_tag(db_file, tag, index_first): """ Get 30 tags by currently viewed popular entries by a given tag. Parameters ---------- db_file : str Path to database file. index_first : str . Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT DISTINCT mt.tag, mt.instances FROM combination_entries_tags_jids AS co INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE co.entry_id IN ( SELECT DISTINCT entries.id FROM main_entries AS entries INNER JOIN combination_entries_tags_jids AS co ON entries.id = co.entry_id INNER JOIN main_tags AS tags ON tags.id = co.tag_id WHERE tags.tag = :tag ORDER BY entries.instances DESC LIMIT 10 OFFSET :index_first ) ORDER BY mt.instances DESC LIMIT 30; """ ) par = { "tag": tag, "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_30_tags_by_entries_recent_by_tag(db_file, tag, index_first): """ Get 30 tags by currently viewed recent entries by a given tag. Parameters ---------- db_file : str Path to database file. index_first : str . Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT DISTINCT mt.tag, mt.instances FROM combination_entries_tags_jids AS co INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE co.entry_id IN ( SELECT DISTINCT entries.id FROM main_entries AS entries INNER JOIN combination_entries_tags_jids AS co ON entries.id = co.entry_id INNER JOIN main_tags AS tags ON tags.id = co.tag_id WHERE tags.tag = :tag ORDER BY date_last DESC LIMIT 10 OFFSET :index_first ) ORDER BY mt.instances DESC LIMIT 30; """ ) par = { "tag": tag, "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_30_tags_by_entries_new(db_file, index_first): """ Get 30 tags by currently viewed new entries. Parameters ---------- db_file : str Path to database file. index_first : str . Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT DISTINCT mt.tag, mt.instances FROM combination_entries_tags_jids AS co INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE co.entry_id IN ( SELECT id FROM main_entries ORDER BY date_first DESC LIMIT 10 OFFSET ? ) ORDER BY mt.instances DESC LIMIT 30; """ ) par = (index_first,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_30_tags_by_entries_recent(db_file, index_first): """ Get 30 tags by currently viewed recent entries. Parameters ---------- db_file : str Path to database file. index_first : str . Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT DISTINCT mt.tag, mt.instances FROM combination_entries_tags_jids AS co INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE co.entry_id IN ( SELECT id FROM main_entries ORDER BY date_last DESC LIMIT 10 OFFSET ? ) ORDER BY mt.instances DESC LIMIT 30; """ ) par = (index_first,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_30_tags_by_entries_by_query_recent(db_file, query, index_first): """ Get 30 tags by currently viewed entries by query. Parameters ---------- db_file : str Path to database file. query : str A search query. index_first : str . Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT DISTINCT mt.tag, mt.instances FROM combination_entries_tags_jids AS co INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE co.entry_id IN ( SELECT id FROM main_entries WHERE title LIKE :query OR url LIKE :query OR summary LIKE :query ORDER BY instances DESC LIMIT 10 OFFSET :index_first ) ORDER BY mt.instances DESC LIMIT 30; """ ) par = { "query": f'%{query}%', "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_30_tags_by_jid_and_tag(db_file, jid, tag, index_first): """ Get 30 tags by Jabber ID and tags. Parameters ---------- db_file : str Path to database file. jid : str Jabber ID. tag : str A tag. index_first : str . Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT DISTINCT mt.tag, mt.instances FROM combination_entries_tags_jids AS co INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE co.entry_id IN ( SELECT co.entry_id FROM main_entries AS me INNER JOIN combination_entries_tags_jids AS co ON co.entry_id = me.id INNER JOIN main_jids AS mj ON mj.id = co.jid_id INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE mj.jid = :jid AND mt.tag = :tag LIMIT 10 OFFSET :index_first ) ORDER BY mt.instances DESC LIMIT 30; """ ) par = { "jid": jid, "tag": tag, "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_30_tags_by_jid_and_query(db_file, jid, query, index_first): """ Get 30 tags by Jabber ID and query. Parameters ---------- db_file : str Path to database file. jid : str Jabber ID. query : str A search query. index_first : str . Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT DISTINCT mt.tag, mt.instances FROM combination_entries_tags_jids AS co INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE co.entry_id IN ( SELECT co.entry_id FROM main_entries AS me INNER JOIN combination_entries_tags_jids AS co ON co.entry_id = me.id INNER JOIN main_jids AS mj ON mj.id = co.jid_id INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE mj.jid = :jid AND (title LIKE :query OR url LIKE :query OR summary LIKE :query) LIMIT 10 OFFSET :index_first ) ORDER BY mt.instances DESC LIMIT 30; """ ) par = { "jid": jid, "query": f'%{query}%', "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_30_tags_by_jid(db_file, jid, index_first): """ Get 30 tags by Jabber ID. Parameters ---------- db_file : str Path to database file. jid : str Jabber ID. Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT DISTINCT mt.tag, mt.instances FROM combination_entries_tags_jids AS co INNER JOIN main_tags AS mt ON mt.id = co.tag_id WHERE co.entry_id IN ( SELECT DISTINCT me.id FROM main_entries AS me INNER JOIN combination_entries_tags_jids AS co ON co.entry_id = me.id INNER JOIN main_jids AS mj ON mj.id = co.jid_id WHERE mj.jid = :jid ORDER BY instances DESC LIMIT 10 OFFSET :index_first ) ORDER BY mt.instances DESC LIMIT 30; """ ) par = { "jid": jid, "index_first": index_first } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_tags_500(db_file): """ Get 500 tags. Parameters ---------- db_file : str Path to database file. Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {}' # .format(function_name, db_file)) sql = ( """ WITH Common500Tags AS ( SELECT tag, instances FROM main_tags ORDER BY instances DESC LIMIT 500 ) SELECT tag, instances FROM Common500Tags ORDER BY tag ASC; """ ) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql).fetchall() return result def get_500_tags_by_jid_sorted_by_name(db_file, jid): """ Get 500 tags by Jabber ID, sorted by name. Parameters ---------- db_file : str Path to database file. jid : str Jabber ID. Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT mt.tag, COUNT(*) AS instances FROM main_tags mt JOIN combination_entries_tags_jids combination ON mt.id = combination.tag_id JOIN main_jids mj ON combination.jid_id = mj.id WHERE mj.jid = :jid GROUP BY mt.tag LIMIT 500; """ ) par = { "jid": jid } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_500_tags_by_jid_sorted_by_instance(db_file, jid): """ Get 500 tags by Jabber ID, sorted by instance. Parameters ---------- db_file : str Path to database file. jid : str Jabber ID. Returns ------- result : tuple Tags and number of instances. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT mt.tag, COUNT(*) AS instances FROM main_tags mt JOIN combination_entries_tags_jids combination ON mt.id = combination.tag_id JOIN main_jids mj ON combination.jid_id = mj.id WHERE mj.jid = :jid GROUP BY mt.tag ORDER BY instances DESC LIMIT 500; """ ) par = { "jid": jid } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result # FIXME It appear that the wrong table is fetched # The table to be fetched is combination_entries_tags_jids def is_jid_associated_with_url_hash(db_file, jid, url_hash): """ Check whether a given Jabber ID is associated with a given URL hash. Parameters ---------- db_file : str Path to database file. jid : str A Jabber ID. url_hash : str An MD5 checksuum of a URL. Returns ------- result : tuple Tags. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} jid: {} url_hash: {}' # .format(function_name, db_file, jid, url_hash)) sql = ( """ SELECT mj.jid, me.url_hash FROM main_jids AS mj INNER JOIN combination_entries_tags_jids AS co ON mj.id = co.jid_id INNER JOIN main_entries AS me ON me.id = co.entry_id WHERE mj.jid = :jid AND me.url_hash = :url_hash; """ ) par = { "jid": jid, "url_hash": url_hash } with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result #deassociate_entry_from_jid async def delete_combination_row_by_jid_and_url_hash(db_file, url_hash, jid): """ Remove association of a given Jabber ID and a given URL hash. Parameters ---------- db_file : str Path to database file. jid : str A Jabber ID. url_hash : str An MD5 checksuum of a URL. Returns ------- None. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} jid: {} url_hash: {}' # .format(function_name, db_file, jid, url_hash)) sql = ( """ DELETE FROM combination_entries_tags_jids WHERE id IN ( SELECT co.id FROM combination_entries_tags_jids co JOIN main_entries me ON co.entry_id = me.id JOIN main_jids mj ON co.jid_id = mj.id WHERE me.url_hash = :url_hash AND mj.jid = :jid ); """ ) par = { "jid": jid, "url_hash": url_hash } async with DBLOCK: with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() cur.execute(sql, par) # NOTE The result was ordered by number of instances # ORDER BY main_tags.instances DESC # And has been changed to order of alphabet # ORDER BY main_tags.tag ASC def get_tags_by_entry_id(db_file, entry_id): """ Get tags by an ID entry. Parameters ---------- db_file : str Path to database file. entry_id : str An ID of an entry. Returns ------- result : tuple Tags. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} index_first: {}' # .format(function_name, db_file, index_first)) sql = ( """ SELECT DISTINCT main_tags.tag FROM main_tags INNER JOIN combination_entries_tags_jids ON main_tags.id = combination_entries_tags_jids.tag_id WHERE combination_entries_tags_jids.entry_id = ? ORDER BY main_tags.tag ASC LIMIT 5; """ ) par = (entry_id,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchall() return result def get_jid_id_by_jid(db_file, jid): """ Get id of a given jid. Parameters ---------- db_file : str Path to database file. jid : str Jabber ID. Returns ------- result : tuple ID. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} jid: {}' # .format(function_name, db_file, jid)) sql = ( """ SELECT id FROM main_jids WHERE jid = ?; """ ) par = (jid,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result def get_jid_by_jid_id(db_file, jid_id): """ Get jid of a given jid_id. Parameters ---------- db_file : str Path to database file. jid_id : str ID of Jabber ID. Returns ------- result : tuple ID. """ function_name = sys._getframe().f_code.co_name # logger.debug('{}: db_file: {} jid_id: {}' # .format(function_name, db_file, jid_id)) sql = ( """ SELECT jid FROM main_jids WHERE id = ?; """ ) par = (jid_id,) with DatabaseSQLite.create_connection(db_file) as conn: cur = conn.cursor() result = cur.execute(sql, par).fetchone() return result[0] if result and len(result) == 1 else result