|
@@ -3,7 +3,7 @@ import datetime
|
|
|
from passlib.apps import custom_app_context as pwd
|
|
|
import peewee
|
|
|
import playhouse.shortcuts
|
|
|
-from typing import List, Optional, Tuple
|
|
|
+from typing import Iterator, List, Optional, Tuple
|
|
|
|
|
|
import lc.config as c
|
|
|
import lc.error as e
|
|
@@ -144,10 +144,9 @@ class Link(Model):
|
|
|
user=user,
|
|
|
)
|
|
|
for tag_name in link.tags:
|
|
|
- t = Tag.get_or_create_tag(user, tag_name)
|
|
|
- HasTag.create(
|
|
|
- link=l, tag=t,
|
|
|
- )
|
|
|
+ tag = Tag.get_or_create_tag(user, tag_name)
|
|
|
+ for t in tag.get_family():
|
|
|
+ HasTag.get_or_create(link=l, tag=t)
|
|
|
return l
|
|
|
|
|
|
def update_from_request(self, user: User, link: r.Link):
|
|
@@ -222,11 +221,26 @@ class Tag(Model):
|
|
|
)
|
|
|
return links, pagination
|
|
|
|
|
|
+ def get_family(self) -> Iterator["Tag"]:
|
|
|
+ yield self
|
|
|
+ p = self
|
|
|
+ while (p := p.parent) :
|
|
|
+ yield p
|
|
|
+
|
|
|
+ BAD_TAG_CHARS = set("{}[]\\()#?")
|
|
|
+
|
|
|
@staticmethod
|
|
|
- def get_or_create_tag(user: User, tag_name: str):
|
|
|
+ def is_valid_tag_name(tag_name: str) -> bool:
|
|
|
+ return all((c not in Tag.BAD_TAG_CHARS for c in tag_name))
|
|
|
+
|
|
|
+ @staticmethod
|
|
|
+ def get_or_create_tag(user: User, tag_name: str) -> "Tag":
|
|
|
if (t := Tag.get_or_none(name=tag_name, user=user)) :
|
|
|
return t
|
|
|
|
|
|
+ if not Tag.is_valid_tag_name(tag_name):
|
|
|
+ raise e.BadTagName(tag_name)
|
|
|
+
|
|
|
parent = None
|
|
|
if "/" in tag_name:
|
|
|
parent_name = tag_name[: tag_name.rindex("/")]
|