Add support for tags

This commit is contained in:
Schimon Jehudah 2024-02-25 19:21:10 +00:00
parent afeaa8707b
commit 5135186717
4 changed files with 302 additions and 19 deletions

View file

@ -211,6 +211,22 @@ def create_tables(db_file):
); );
""" """
) )
feeds_tags_table_sql = (
"""
CREATE TABLE IF NOT EXISTS feeds_tags (
id INTEGER NOT NULL,
feed_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
FOREIGN KEY ("feed_id") REFERENCES "feeds" ("id")
ON UPDATE CASCADE
ON DELETE CASCADE,
FOREIGN KEY ("tag_id") REFERENCES "tags" ("id")
ON UPDATE CASCADE
ON DELETE CASCADE,
PRIMARY KEY ("id")
);
"""
)
# TODO # TODO
# Consider parameter unique: # Consider parameter unique:
# entry_id TEXT NOT NULL UNIQUE, # entry_id TEXT NOT NULL UNIQUE,
@ -246,6 +262,15 @@ def create_tables(db_file):
); );
""" """
) )
tags_table_sql = (
"""
CREATE TABLE IF NOT EXISTS tags (
id INTEGER NOT NULL,
tag TEXT NOT NULL UNIQUE,
PRIMARY KEY ("id")
);
"""
)
cur = conn.cursor() cur = conn.cursor()
# cur = get_cursor(db_file) # cur = get_cursor(db_file)
cur.execute(archive_table_sql) cur.execute(archive_table_sql)
@ -254,10 +279,12 @@ def create_tables(db_file):
cur.execute(feeds_state_table_sql) cur.execute(feeds_state_table_sql)
cur.execute(feeds_properties_table_sql) cur.execute(feeds_properties_table_sql)
cur.execute(feeds_rules_table_sql) cur.execute(feeds_rules_table_sql)
cur.execute(feeds_tags_table_sql)
cur.execute(filters_table_sql) cur.execute(filters_table_sql)
# cur.execute(statistics_table_sql) # cur.execute(statistics_table_sql)
cur.execute(settings_table_sql) cur.execute(settings_table_sql)
cur.execute(status_table_sql) cur.execute(status_table_sql)
cur.execute(tags_table_sql)
def get_cursor(db_file): def get_cursor(db_file):
@ -598,7 +625,8 @@ async def remove_feed_by_index(db_file, ix):
# cur.execute(sql, par) # cur.execute(sql, par)
sql = ( sql = (
""" """
DELETE FROM feeds DELETE
FROM feeds
WHERE id = ? WHERE id = ?
""" """
) )
@ -606,6 +634,231 @@ async def remove_feed_by_index(db_file, ix):
cur.execute(sql, par) cur.execute(sql, par)
def get_tags_by_feed_id(db_file, feed_id):
"""
Get tags of given feed.
Parameters
----------
db_file : str
Path to database file.
feed_id : str
Feed ID.
Returns
-------
result : list
List of tags.
"""
with create_connection(db_file) as conn:
cur = conn.cursor()
sql = (
"""
SELECT tags.tag
FROM tags
INNER JOIN feeds_tags ON tags.id = feeds_tags.tag_id
INNER JOIN feeds ON feeds.id = feeds_tags.feed_id
WHERE feeds.id = ?;
"""
)
par = (feed_id,)
result = cur.execute(sql, par).fetchall()
return result
async def set_feed_id_and_tag_id(db_file, feed_id, tag_id):
"""
Set Feed ID and Tag ID.
Parameters
----------
db_file : str
Path to database file.
feed_id : str
Feed ID
tag_id : str
Tag ID
"""
async with DBLOCK:
with create_connection(db_file) as conn:
cur = conn.cursor()
sql = (
"""
INSERT
INTO feeds_tags(
feed_id, tag_id)
VALUES(
:feed_id, :tag_id)
"""
)
par = {
"feed_id": feed_id,
"tag_id": tag_id
}
cur.execute(sql, par)
def get_tag_id(db_file, tag):
"""
Get ID of given tag. Check whether tag exist.
Parameters
----------
db_file : str
Path to database file.
tag : str
Tag.
Returns
-------
ix : str
Tag ID.
"""
with create_connection(db_file) as conn:
cur = conn.cursor()
sql = (
"""
SELECT id
FROM tags
WHERE tag = ?
"""
)
par = (tag,)
ix = cur.execute(sql, par).fetchone()
return ix
def is_tag_id_associated(db_file, tag_id):
"""
Check whether tag_id is associated with any feed.
Parameters
----------
db_file : str
Path to database file.
tag_id : str
Tag ID.
Returns
-------
tag_id : str
Tag ID.
"""
with create_connection(db_file) as conn:
cur = conn.cursor()
sql = (
"""
SELECT tag_id
FROM feeds_tags
WHERE tag_id = :tag_id
"""
)
par = {
"tag_id": tag_id
}
tag_id = cur.execute(sql, par).fetchone()
return tag_id
async def delete_tag_by_index(db_file, ix):
async with DBLOCK:
with create_connection(db_file) as conn:
cur = conn.cursor()
sql = (
"""
DELETE
FROM tags
WHERE id = :id
"""
)
par = {
"id": ix
}
cur.execute(sql, par)
def is_tag_id_of_feed_id(db_file, tag_id, feed_id):
"""
Check whether given tag is related with given feed.
Parameters
----------
db_file : str
Path to database file.
feed_id : str
Feed ID.
tag_id : str
Tag ID.
Returns
-------
tag_id : str
Tag ID.
"""
with create_connection(db_file) as conn:
cur = conn.cursor()
sql = (
"""
SELECT tag_id
FROM feeds_tags
WHERE tag_id = :tag_id AND feed_id = :feed_id
"""
)
par = {
"tag_id": tag_id,
"feed_id": feed_id
}
tag_id = cur.execute(sql, par).fetchone()
return tag_id
async def delete_feed_id_tag_id(db_file, feed_id, tag_id):
async with DBLOCK:
with create_connection(db_file) as conn:
cur = conn.cursor()
sql = (
"""
DELETE
FROM feeds_tags
WHERE tag_id = :tag_id AND feed_id = :feed_id
"""
)
par = {
"tag_id": tag_id,
"feed_id": feed_id
}
cur.execute(sql, par)
async def set_new_tag(db_file, tag):
"""
Set new Tag
Parameters
----------
db_file : str
Path to database file.
tag : str
Tag
"""
async with DBLOCK:
with create_connection(db_file) as conn:
cur = conn.cursor()
sql = (
"""
INSERT
INTO tags(
tag)
VALUES(
:tag)
"""
)
par = {
"tag": tag
}
cur.execute(sql, par)
async def get_feed_id_and_name(db_file, url): async def get_feed_id_and_name(db_file, url):
""" """
Get Id and Name of feed. Get Id and Name of feed.
@ -1112,7 +1365,8 @@ async def delete_archived_entry(cur, ix):
""" """
sql = ( sql = (
""" """
DELETE FROM archive DELETE
FROM archive
WHERE id = ? WHERE id = ?
""" """
) )
@ -1469,7 +1723,8 @@ async def maintain_archive(db_file, limit):
if difference > 0: if difference > 0:
sql = ( sql = (
""" """
DELETE FROM archive DELETE
FROM archive
WHERE id WHERE id
IN ( IN (
SELECT id SELECT id

View file

@ -1,2 +1,2 @@
__version__ = '0.1.21' __version__ = '0.1.22'
__version_info__ = (0, 1, 21) __version_info__ = (0, 1, 22)

View file

@ -621,14 +621,14 @@ class Slixfeed(slixmpp.ClientXMPP):
ftype='text-single', ftype='text-single',
label='Allow list', label='Allow list',
value=value, value=value,
desc=('Keywords to allow (comma-separated keywords).')) desc='Keywords to allow (comma-separated keywords).')
value = sqlite.get_filter_value(db_file, 'deny') value = sqlite.get_filter_value(db_file, 'deny')
if value: value = str(value[0]) if value: value = str(value[0])
form.add_field(var='deny', form.add_field(var='deny',
ftype='text-single', ftype='text-single',
label='Deny list', label='Deny list',
value=value, value=value,
desc=('Keywords to deny (comma-separated keywords).')) desc='Keywords to deny (comma-separated keywords).')
session['allow_complete'] = True session['allow_complete'] = True
session['has_next'] = False session['has_next'] = False
session['next'] = self._handle_filters_complete session['next'] = self._handle_filters_complete
@ -1366,9 +1366,14 @@ class Slixfeed(slixmpp.ClientXMPP):
feed_id = feed_id[0] feed_id = feed_id[0]
title = sqlite.get_feed_title(db_file, feed_id) title = sqlite.get_feed_title(db_file, feed_id)
title = title[0] title = title[0]
tags_result = sqlite.get_tags_by_feed_id(db_file, feed_id)
tags_sorted = sorted(x[0] for x in tags_result)
tags = ', '.join(tags_sorted)
form['instructions'] = 'Editing subscription #{}'.format(feed_id) form['instructions'] = 'Editing subscription #{}'.format(feed_id)
else: else:
form['instructions'] = 'Adding subscription' form['instructions'] = 'Adding subscription'
title = ''
tags = ''
form.add_field(ftype='fixed', form.add_field(ftype='fixed',
value='Properties') value='Properties')
form.add_field(var='name', form.add_field(var='name',
@ -1391,11 +1396,14 @@ class Slixfeed(slixmpp.ClientXMPP):
label='ID #', label='ID #',
value=feed_id_str) value=feed_id_str)
options.addOption(feed_id_str, feed_id_str) options.addOption(feed_id_str, feed_id_str)
form.add_field(var='tags', form.add_field(var='tags_new',
ftype='text-single', ftype='text-single',
# ftype='text-multi',
label='Tags', label='Tags',
value='') desc='Comma-separated tags.',
value=tags)
form.add_field(var='tags_old',
ftype='hidden',
value=tags)
form.add_field(ftype='fixed', form.add_field(ftype='fixed',
value='Options') value='Options')
options = form.add_field(var='priority', options = form.add_field(var='priority',
@ -1443,8 +1451,32 @@ class Slixfeed(slixmpp.ClientXMPP):
await sqlite.set_enabled_status(db_file, feed_id, enabled_status) await sqlite.set_enabled_status(db_file, feed_id, enabled_status)
name = values['name'] name = values['name']
await sqlite.set_feed_title(db_file, feed_id, name) await sqlite.set_feed_title(db_file, feed_id, name)
values['priority'] priority = values['priority']
values['tags'] tags_new = values['tags_new']
tags_old = values['tags_old']
# Add new tags
for tag in tags_new.split(','):
tag = tag.strip()
if not tag:
continue
tag_id = sqlite.get_tag_id(db_file, tag)
if not tag_id:
await sqlite.set_new_tag(db_file, tag)
tag_id = sqlite.get_tag_id(db_file, tag)
tag_id = tag_id[0]
if not sqlite.is_tag_id_of_feed_id(db_file, tag_id, feed_id):
await sqlite.set_feed_id_and_tag_id(db_file, feed_id, tag_id)
# Remove tags that were not submitted
for tag in tags_old[0].split(','):
tag = tag.strip()
if not tag:
continue
if tag not in tags_new:
tag_id = sqlite.get_tag_id(db_file, tag)
tag_id = tag_id[0]
await sqlite.delete_feed_id_tag_id(db_file, feed_id, tag_id)
sqlite.is_tag_id_associated(db_file, tag_id)
await sqlite.delete_tag_by_index(db_file, tag_id)
# form = self['xep_0004'].make_form('form', 'Subscription') # form = self['xep_0004'].make_form('form', 'Subscription')
# form['instructions'] = ('📁️ Subscription #{} has been {}' # form['instructions'] = ('📁️ Subscription #{} has been {}'
# .format(feed_id, action)) # .format(feed_id, action))

View file

@ -39,7 +39,7 @@ from slixfeed.xmpp.muc import XmppGroupchat
from slixfeed.xmpp.message import XmppMessage from slixfeed.xmpp.message import XmppMessage
from slixfeed.xmpp.presence import XmppPresence from slixfeed.xmpp.presence import XmppPresence
from slixfeed.xmpp.upload import XmppUpload from slixfeed.xmpp.upload import XmppUpload
from slixfeed.xmpp.utility import get_chat_type from slixfeed.xmpp.utility import get_chat_type, is_moderator
import time import time
@ -81,9 +81,7 @@ async def message(self, message):
if (message['muc']['nick'] == self.alias): if (message['muc']['nick'] == self.alias):
return return
jid_full = str(message['from']) jid_full = str(message['from'])
alias = jid_full[jid_full.index('/')+1:] if not is_moderator(self, jid, jid_full):
role = self.plugin['xep_0045'].get_jid_property(jid, alias, 'role')
if role != 'moderator':
return return
# NOTE This is an exceptional case in which we treat # NOTE This is an exceptional case in which we treat
@ -132,9 +130,7 @@ async def message(self, message):
# return # return
# approved = False # approved = False
jid_full = str(message['from']) jid_full = str(message['from'])
role = self.plugin['xep_0045'].get_jid_property( if not is_moderator(self, jid, jid_full):
jid, jid_full[jid_full.index('/')+1:], 'role')
if role != 'moderator':
return return
# if role == 'moderator': # if role == 'moderator':
# approved = True # approved = True