Tag functionality added
This commit is contained in:
@@ -4,9 +4,71 @@ from uuid import uuid4
|
||||
from backend.app.core.security import clean_url
|
||||
from backend.app.database import get_connection
|
||||
|
||||
MAX_TAGS = 10
|
||||
|
||||
def create_link(user_id: str, title: str, url: str, comment: str, timestamp: str | None):
|
||||
|
||||
def normalize_tags(tags: list[str] | None) -> list[str]:
|
||||
normalized = []
|
||||
normalized_keys = set()
|
||||
for tag in tags or []:
|
||||
value = tag.strip()
|
||||
if value and not value.startswith('#'):
|
||||
value = f'#{value}'
|
||||
key = value.casefold()
|
||||
if value and key not in normalized_keys:
|
||||
normalized.append(value)
|
||||
normalized_keys.add(key)
|
||||
if len(normalized) > MAX_TAGS:
|
||||
raise ValueError(f'A link can have at most {MAX_TAGS} tags')
|
||||
return normalized
|
||||
|
||||
|
||||
def save_link_tags(conn, link_id: str, tags: list[str]) -> None:
|
||||
canonical_tags = []
|
||||
for tag in tags:
|
||||
tag_row = conn.execute(
|
||||
'SELECT id FROM tags WHERE lower(name) = lower(?)',
|
||||
(tag,),
|
||||
).fetchone()
|
||||
if tag_row is None:
|
||||
conn.execute(
|
||||
'INSERT INTO tags (id, name) VALUES (?, ?)',
|
||||
(str(uuid4()), tag),
|
||||
)
|
||||
tag_row = conn.execute('SELECT id FROM tags WHERE name = ?', (tag,)).fetchone()
|
||||
conn.execute(
|
||||
'INSERT OR IGNORE INTO link_tags (link_id, tag_id) VALUES (?, ?)',
|
||||
(link_id, tag_row['id']),
|
||||
)
|
||||
canonical_tags.append(conn.execute(
|
||||
'SELECT name FROM tags WHERE id = ?',
|
||||
(tag_row['id'],),
|
||||
).fetchone()['name'])
|
||||
return canonical_tags
|
||||
|
||||
|
||||
def get_link_tags(conn, link_id: str) -> list[str]:
|
||||
rows = conn.execute(
|
||||
'''
|
||||
SELECT tags.name FROM tags
|
||||
JOIN link_tags ON link_tags.tag_id = tags.id
|
||||
WHERE link_tags.link_id = ? ORDER BY tags.name
|
||||
''',
|
||||
(link_id,),
|
||||
).fetchall()
|
||||
return [row['name'] for row in rows]
|
||||
|
||||
|
||||
def create_link(
|
||||
user_id: str,
|
||||
title: str,
|
||||
url: str,
|
||||
comment: str,
|
||||
timestamp: str | None,
|
||||
tags: list[str] | None = None,
|
||||
):
|
||||
cleaned_url = clean_url(url)
|
||||
normalized_tags = normalize_tags(tags)
|
||||
created_at = datetime.now(timezone.utc).isoformat()
|
||||
record = {
|
||||
'id': str(uuid4()),
|
||||
@@ -37,7 +99,9 @@ def create_link(user_id: str, title: str, url: str, comment: str, timestamp: str
|
||||
record['is_public'],
|
||||
),
|
||||
)
|
||||
stored_tags = save_link_tags(conn, record['id'], normalized_tags)
|
||||
conn.commit()
|
||||
record['tags'] = stored_tags
|
||||
return record
|
||||
|
||||
|
||||
@@ -54,11 +118,22 @@ def list_public_links(username: str | None = None):
|
||||
''',
|
||||
(username, username),
|
||||
).fetchall()
|
||||
return [dict(row) for row in rows]
|
||||
records = [dict(row) for row in rows]
|
||||
for record in records:
|
||||
record['tags'] = get_link_tags(conn, record['id'])
|
||||
return records
|
||||
|
||||
|
||||
def update_link(link_id: str, user_id: str, title: str, url: str, comment: str):
|
||||
def update_link(
|
||||
link_id: str,
|
||||
user_id: str,
|
||||
title: str,
|
||||
url: str,
|
||||
comment: str,
|
||||
tags: list[str] | None = None,
|
||||
):
|
||||
cleaned_url = clean_url(url)
|
||||
normalized_tags = normalize_tags(tags)
|
||||
with get_connection() as conn:
|
||||
cursor = conn.execute(
|
||||
'''
|
||||
@@ -70,9 +145,13 @@ def update_link(link_id: str, user_id: str, title: str, url: str, comment: str):
|
||||
)
|
||||
if cursor.rowcount == 0:
|
||||
return None
|
||||
conn.execute('DELETE FROM link_tags WHERE link_id = ?', (link_id,))
|
||||
stored_tags = save_link_tags(conn, link_id, normalized_tags)
|
||||
conn.commit()
|
||||
row = conn.execute('SELECT * FROM links WHERE id = ?', (link_id,)).fetchone()
|
||||
return dict(row)
|
||||
record = dict(row)
|
||||
record['tags'] = stored_tags
|
||||
return record
|
||||
|
||||
|
||||
def list_public_users():
|
||||
@@ -81,3 +160,15 @@ def list_public_users():
|
||||
'SELECT username FROM users ORDER BY username'
|
||||
).fetchall()
|
||||
return [row['username'] for row in rows]
|
||||
|
||||
|
||||
def list_tags():
|
||||
with get_connection() as conn:
|
||||
rows = conn.execute('SELECT name FROM tags ORDER BY name').fetchall()
|
||||
tags = []
|
||||
seen = set()
|
||||
for row in rows:
|
||||
if row['name'].casefold() not in seen:
|
||||
tags.append(row['name'])
|
||||
seen.add(row['name'].casefold())
|
||||
return tags
|
||||
|
||||
Reference in New Issue
Block a user