|
24 | 24 | from sqlalchemy.exc import IntegrityError
|
25 | 25 | from sqlalchemy.orm import (
|
26 | 26 | Session,
|
| 27 | + aliased, |
27 | 28 | contains_eager,
|
28 | 29 | make_transient,
|
29 | 30 | selectinload,
|
@@ -417,13 +418,18 @@ def search_library(
|
417 | 418 | statement = select(Entry)
|
418 | 419 |
|
419 | 420 | if search.tag:
|
| 421 | + SubtagAlias = aliased(Tag) # noqa: N806 |
420 | 422 | statement = (
|
421 | 423 | statement.join(Entry.tag_box_fields)
|
422 | 424 | .join(TagBoxField.tags)
|
| 425 | + .outerjoin(Tag.aliases) |
| 426 | + .outerjoin(SubtagAlias, Tag.subtags) |
423 | 427 | .where(
|
424 | 428 | or_(
|
425 | 429 | Tag.name.ilike(search.tag),
|
426 | 430 | Tag.shorthand.ilike(search.tag),
|
| 431 | + TagAlias.name.ilike(search.tag), |
| 432 | + SubtagAlias.name.ilike(search.tag), |
427 | 433 | )
|
428 | 434 | )
|
429 | 435 | )
|
@@ -752,18 +758,23 @@ def add_entry_field_type(
|
752 | 758 | )
|
753 | 759 | return True
|
754 | 760 |
|
755 |
| - def add_tag(self, tag: Tag, subtag_ids: list[int] | None = None) -> Tag | None: |
| 761 | + def add_tag( |
| 762 | + self, |
| 763 | + tag: Tag, |
| 764 | + subtag_ids: set[int] | None = None, |
| 765 | + alias_names: set[str] | None = None, |
| 766 | + alias_ids: set[int] | None = None, |
| 767 | + ) -> Tag | None: |
756 | 768 | with Session(self.engine, expire_on_commit=False) as session:
|
757 | 769 | try:
|
758 | 770 | session.add(tag)
|
759 | 771 | session.flush()
|
760 | 772 |
|
761 |
| - for subtag_id in subtag_ids or []: |
762 |
| - subtag = TagSubtag( |
763 |
| - parent_id=tag.id, |
764 |
| - child_id=subtag_id, |
765 |
| - ) |
766 |
| - session.add(subtag) |
| 773 | + if subtag_ids is not None: |
| 774 | + self.update_subtags(tag, subtag_ids, session) |
| 775 | + |
| 776 | + if alias_ids is not None and alias_names is not None: |
| 777 | + self.update_aliases(tag, alias_ids, alias_names, session) |
767 | 778 |
|
768 | 779 | session.commit()
|
769 | 780 |
|
@@ -847,75 +858,101 @@ def save_library_backup_to_disk(self) -> Path:
|
847 | 858 |
|
848 | 859 | def get_tag(self, tag_id: int) -> Tag:
|
849 | 860 | with Session(self.engine) as session:
|
850 |
| - tags_query = select(Tag).options(selectinload(Tag.subtags)) |
| 861 | + tags_query = select(Tag).options(selectinload(Tag.subtags), selectinload(Tag.aliases)) |
851 | 862 | tag = session.scalar(tags_query.where(Tag.id == tag_id))
|
852 | 863 |
|
853 | 864 | session.expunge(tag)
|
854 | 865 | for subtag in tag.subtags:
|
855 | 866 | session.expunge(subtag)
|
856 | 867 |
|
| 868 | + for alias in tag.aliases: |
| 869 | + session.expunge(alias) |
| 870 | + |
857 | 871 | return tag
|
858 | 872 |
|
| 873 | + def get_alias(self, tag_id: int, alias_id: int) -> TagAlias: |
| 874 | + with Session(self.engine) as session: |
| 875 | + alias_query = select(TagAlias).where(TagAlias.id == alias_id, TagAlias.tag_id == tag_id) |
| 876 | + alias = session.scalar(alias_query.where(TagAlias.id == alias_id)) |
| 877 | + |
| 878 | + return alias |
| 879 | + |
859 | 880 | def add_subtag(self, base_id: int, new_tag_id: int) -> bool:
|
| 881 | + if base_id == new_tag_id: |
| 882 | + return False |
| 883 | + |
860 | 884 | # open session and save as parent tag
|
861 | 885 | with Session(self.engine) as session:
|
862 |
| - tag = TagSubtag( |
| 886 | + subtag = TagSubtag( |
863 | 887 | parent_id=base_id,
|
864 | 888 | child_id=new_tag_id,
|
865 | 889 | )
|
866 | 890 |
|
867 | 891 | try:
|
868 |
| - session.add(tag) |
| 892 | + session.add(subtag) |
869 | 893 | session.commit()
|
870 | 894 | return True
|
871 | 895 | except IntegrityError:
|
872 | 896 | session.rollback()
|
873 | 897 | logger.exception("IntegrityError")
|
874 | 898 | return False
|
875 | 899 |
|
876 |
| - def update_tag(self, tag: Tag, subtag_ids: list[int]) -> None: |
| 900 | + def remove_subtag(self, base_id: int, remove_tag_id: int) -> bool: |
| 901 | + with Session(self.engine) as session: |
| 902 | + p_id = base_id |
| 903 | + r_id = remove_tag_id |
| 904 | + remove = session.query(TagSubtag).filter_by(parent_id=p_id, child_id=r_id).one() |
| 905 | + session.delete(remove) |
| 906 | + session.commit() |
| 907 | + |
| 908 | + return True |
| 909 | + |
| 910 | + def update_tag( |
| 911 | + self, |
| 912 | + tag: Tag, |
| 913 | + subtag_ids: set[int] | None = None, |
| 914 | + alias_names: set[str] | None = None, |
| 915 | + alias_ids: set[int] | None = None, |
| 916 | + ) -> None: |
877 | 917 | """Edit a Tag in the Library."""
|
878 |
| - # TODO - maybe merge this with add_tag? |
| 918 | + self.add_tag(tag, subtag_ids, alias_names, alias_ids) |
879 | 919 |
|
880 |
| - if tag.shorthand: |
881 |
| - tag.shorthand = slugify(tag.shorthand) |
| 920 | + def update_aliases(self, tag, alias_ids, alias_names, session): |
| 921 | + prev_aliases = session.scalars(select(TagAlias).where(TagAlias.tag_id == tag.id)).all() |
882 | 922 |
|
883 |
| - if tag.aliases: |
884 |
| - # TODO |
885 |
| - ... |
| 923 | + for alias in prev_aliases: |
| 924 | + if alias.id not in alias_ids or alias.name not in alias_names: |
| 925 | + session.delete(alias) |
| 926 | + else: |
| 927 | + alias_ids.remove(alias.id) |
| 928 | + alias_names.remove(alias.name) |
886 | 929 |
|
887 |
| - # save the tag |
888 |
| - with Session(self.engine) as session: |
889 |
| - try: |
890 |
| - # update the existing tag |
891 |
| - session.add(tag) |
892 |
| - session.flush() |
| 930 | + for alias_name in alias_names: |
| 931 | + alias = TagAlias(alias_name, tag.id) |
| 932 | + session.add(alias) |
893 | 933 |
|
894 |
| - # load all tag's subtag to know which to remove |
895 |
| - prev_subtags = session.scalars( |
896 |
| - select(TagSubtag).where(TagSubtag.parent_id == tag.id) |
897 |
| - ).all() |
| 934 | + def update_subtags(self, tag, subtag_ids, session): |
| 935 | + if tag.id in subtag_ids: |
| 936 | + subtag_ids.remove(tag.id) |
898 | 937 |
|
899 |
| - for subtag in prev_subtags: |
900 |
| - if subtag.child_id not in subtag_ids: |
901 |
| - session.delete(subtag) |
902 |
| - else: |
903 |
| - # no change, remove from list |
904 |
| - subtag_ids.remove(subtag.child_id) |
| 938 | + # load all tag's subtag to know which to remove |
| 939 | + prev_subtags = session.scalars(select(TagSubtag).where(TagSubtag.parent_id == tag.id)).all() |
905 | 940 |
|
906 |
| - # create remaining items |
907 |
| - for subtag_id in subtag_ids: |
908 |
| - # add new subtag |
909 |
| - subtag = TagSubtag( |
910 |
| - parent_id=tag.id, |
911 |
| - child_id=subtag_id, |
912 |
| - ) |
913 |
| - session.add(subtag) |
| 941 | + for subtag in prev_subtags: |
| 942 | + if subtag.child_id not in subtag_ids: |
| 943 | + session.delete(subtag) |
| 944 | + else: |
| 945 | + # no change, remove from list |
| 946 | + subtag_ids.remove(subtag.child_id) |
914 | 947 |
|
915 |
| - session.commit() |
916 |
| - except IntegrityError: |
917 |
| - session.rollback() |
918 |
| - logger.exception("IntegrityError") |
| 948 | + # create remaining items |
| 949 | + for subtag_id in subtag_ids: |
| 950 | + # add new subtag |
| 951 | + subtag = TagSubtag( |
| 952 | + parent_id=tag.id, |
| 953 | + child_id=subtag_id, |
| 954 | + ) |
| 955 | + session.add(subtag) |
919 | 956 |
|
920 | 957 | def prefs(self, key: LibraryPrefs) -> Any:
|
921 | 958 | # load given item from Preferences table
|
|
0 commit comments