Skip to content

Commit e382f89

Browse files
committed
add_field_tag
1 parent 4d1460b commit e382f89

File tree

5 files changed

+66
-46
lines changed

5 files changed

+66
-46
lines changed

tagstudio/src/core/library/alchemy/fields.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from typing import TYPE_CHECKING, Union, Type, TypeVar, Any
77

88
from sqlalchemy import ForeignKey
9-
from sqlalchemy.orm import Mapped, mapped_column, relationship
9+
from sqlalchemy.orm import Mapped, mapped_column, relationship, Session
1010

1111
from .db import Base
1212

@@ -110,7 +110,12 @@ def __init__(
110110
super().__init__()
111111

112112
def __key(self):
113-
return (self.type, self.name, str(self.tag_ids))
113+
# tags are not bound to session, dont show them
114+
return (
115+
self.id,
116+
self.type,
117+
self.name,
118+
) # str(self.tag_ids))
114119

115120
def __hash__(self):
116121
return hash(self.__key())

tagstudio/src/core/library/alchemy/library.py

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ def verify_ts_folders(self, library_dir: Path) -> None:
170170

171171
def verify_default_tags(self, tag_list: list) -> list:
172172
"""
173-
Ensures that the default builtin tags are present in the Library's
173+
Ensure that the default builtin tags are present in the Library's
174174
save file. Takes in and returns the tag dictionary from the JSON file.
175175
"""
176176
missing: list = []
@@ -181,7 +181,7 @@ def verify_default_tags(self, tag_list: list) -> list:
181181
return tag_list
182182

183183
def clear_internal_vars(self):
184-
"""Clears the internal variables of the Library object."""
184+
"""Clear the internal variables of the Library object."""
185185
self.library_dir = None
186186
self.missing_files = []
187187
self.dupe_files = []
@@ -201,13 +201,11 @@ def refresh_dir(self) -> Iterator[int]:
201201

202202
for path in self.library_dir.glob("**/*"):
203203
str_path = str(path)
204-
if any(
205-
[
206-
path.is_dir()
207-
or "$RECYCLE.BIN" in str_path
208-
or TS_FOLDER_NAME in str_path
209-
or "tagstudio_thumbs" in str_path
210-
]
204+
if (
205+
path.is_dir()
206+
or "$RECYCLE.BIN" in str_path
207+
or TS_FOLDER_NAME in str_path
208+
or "tagstudio_thumbs" in str_path
211209
):
212210
continue
213211

@@ -527,22 +525,15 @@ def add_tag(self, tag: Tag) -> bool:
527525
else:
528526
return True
529527

530-
def add_tag_to_field(
528+
def add_field_tag(
531529
self,
532-
tag: int | Tag,
530+
tag: Tag,
533531
field: TagBoxField,
534532
) -> None:
535-
if isinstance(tag, Tag):
536-
tag = tag.id
537-
538533
with Session(self.engine) as session, session.begin():
539-
tag_object = session.scalars(select(Tag).where(Tag.id == tag)).one()
540-
541-
field_ = session.scalars(
542-
select(TagBoxField).where(TagBoxField.id == field.id)
543-
).one()
544-
545-
field_.tags.add(tag_object)
534+
field.tags = field.tags | {tag}
535+
session.add(field)
536+
session.commit()
546537

547538
def add_tag_to_entry_meta_tags(self, tag: int | Tag, entry_id: int) -> None:
548539
if isinstance(tag, Tag):

tagstudio/src/qt/widgets/preview_panel.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -692,12 +692,18 @@ def update_widgets(self):
692692
)
693693

694694
self.selected = list(self.driver.selected)
695+
logger.info(
696+
"update_widgets enum common_fields", common_fields=self.common_fields
697+
)
695698
for i, f in enumerate(self.common_fields):
696-
logger.info("update_widgets enum common_fields", index=i, field=f)
697699
self.write_container(i, f)
698700

701+
logger.info(
702+
"update_widgets enum mixed_fields",
703+
self.mixed_fields,
704+
start=len(self.common_fields),
705+
)
699706
for i, f in enumerate(self.mixed_fields, start=len(self.common_fields)):
700-
logger.info("update_widgets enum mixed_fields", index=i, field=f)
701707
self.write_container(i, f, mixed=True)
702708

703709
# Hide leftover containers
@@ -724,7 +730,9 @@ def set_tags_updated_slot(self, slot: object):
724730
self.tags_updated.connect(slot)
725731
self.is_connected = True
726732

727-
def write_container(self, index: int, field, mixed: bool = False):
733+
def write_container(
734+
self, index: int, field: TagBoxField | TextField, mixed: bool = False
735+
):
728736
"""Update/Create data for a FieldContainer."""
729737
# Remove 'Add Field' button from scroll_layout, to be re-added later.
730738
self.scroll_layout.takeAt(self.scroll_layout.count() - 1).widget()
@@ -741,13 +749,14 @@ def write_container(self, index: int, field, mixed: bool = False):
741749
container.set_title(field.name)
742750
container.set_inline(False)
743751
title = f"{field.name} (Tag Box)"
752+
744753
if not mixed:
745754
entry = self.driver.frame_content[self.selected[0]]
746755
inner_container = container.get_inner_widget()
747756
if isinstance(inner_container, TagBoxWidget):
748757
# TODO
749758
# inner_container.set_item(entry)
750-
# inner_container.set_tags(field.tags) # type: ignore
759+
inner_container.set_tags(list(field.tags))
751760

752761
try:
753762
inner_container.updated.disconnect()
@@ -756,6 +765,10 @@ def write_container(self, index: int, field, mixed: bool = False):
756765
pass
757766

758767
else:
768+
logger.info(
769+
"inner_container is not instance of TagBoxWidget",
770+
container=inner_container,
771+
)
759772
inner_container = TagBoxWidget(
760773
entry,
761774
title,

tagstudio/src/qt/widgets/tag_box.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def __init__(
4141
) -> None:
4242
super().__init__(title)
4343

44-
self.item = item
44+
# self.item = item
4545
self.driver = driver # Used for creating tag click callbacks that search entries for that tag.
4646
# self.field_index = field_index
4747
self.tags = tags
@@ -86,21 +86,16 @@ def __init__(
8686

8787
self.set_tags(tags)
8888

89-
def set_item(self, item):
90-
self.item = item
91-
9289
def set_tags(self, tags: list[Tag]):
9390
is_recycled = False
94-
if self.base_layout.itemAt(0):
95-
while self.base_layout.itemAt(0) and self.base_layout.itemAt(1):
96-
self.base_layout.takeAt(0).widget().deleteLater()
91+
while self.base_layout.itemAt(0) and self.base_layout.itemAt(1):
92+
self.base_layout.takeAt(0).widget().deleteLater()
9793
is_recycled = True
9894

9995
for tag in tags:
10096
tw = TagWidget(tag, True, True)
10197
tw.on_click.connect(
10298
lambda tag_id=tag.id: (
103-
print("tag widget clicked on_click emited", tag_id),
10499
self.driver.main_window.searchField.setText(f"tag_id:{tag_id}"), # type: ignore
105100
self.driver.filter_items(FilterState(id=tag_id)), # type: ignore[func-returns-value]
106101
)
@@ -109,6 +104,7 @@ def set_tags(self, tags: list[Tag]):
109104
tw.on_remove.connect(lambda tag_id=tag.id: self.remove_tag(tag_id))
110105
tw.on_edit.connect(lambda tag_id=tag.id: self.edit_tag(tag_id))
111106
self.base_layout.addWidget(tw)
107+
112108
self.tags = tags
113109

114110
# Move or add the '+' button.
@@ -128,7 +124,7 @@ def edit_tag(self, tag_id: int):
128124
tag = self.driver.lib.get_tag(tag_id)
129125
self.edit_modal = PanelModal(
130126
btp,
131-
tag.name,
127+
tag.name, # TODO - display name including subtags
132128
"Edit Tag",
133129
done_callback=self.driver.preview_panel.update_widgets,
134130
has_save=True,
@@ -142,17 +138,17 @@ def edit_tag(self, tag_id: int):
142138
def add_tag_callback(self, tag_id: int):
143139
logger.info("add_tag_callback", tag_id=tag_id, selected=self.driver.selected)
144140

141+
tag = self.driver.lib.get_tag(tag_id=tag_id)
142+
145143
for idx in self.driver.selected:
146144
entry: Entry = self.driver.frame_content[idx]
147145

148146
# TODO - add tag to correct field
149147
tag_field: TagBoxField = entry.tag_box_fields[0]
148+
self.driver.lib.add_field_tag(tag, tag_field)
150149

151-
tag = self.driver.lib.get_tag(tag_id=tag_id)
152-
153-
tag_field.tags.add(tag)
154-
155-
self.updated.emit()
150+
# TODO - this was originally in the loop, is it needed there?
151+
self.updated.emit()
156152

157153
if tag_id in (TAG_FAVORITE, TAG_ARCHIVED):
158154
self.driver.update_badges()

tagstudio/tests/alchemy/test_library.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import random
22
import string
3-
from copy import copy
43
from pathlib import Path
54
from tempfile import TemporaryDirectory
65

@@ -25,7 +24,7 @@ def generate_entry(*, path: Path = None) -> Entry:
2524

2625

2726
@pytest.fixture
28-
def tag_fixture():
27+
def generate_tag():
2928
def inner(**kwargs):
3029
params = dict(name="foo", color=TagColor.red) | kwargs
3130
return Tag(**params)
@@ -59,12 +58,12 @@ def test_library_add_file():
5958
assert lib.has_item(entry.path)
6059

6160

62-
def test_create_tag(library, tag_fixture):
61+
def test_create_tag(library, generate_tag):
6362
# tag already exists
64-
assert not library.add_tag(tag_fixture())
63+
assert not library.add_tag(generate_tag())
6564

6665
# new tag name
67-
assert library.add_tag(tag_fixture(name="bar"))
66+
assert library.add_tag(generate_tag(name="bar"))
6867

6968

7069
def test_library_search(library, tag_fixture):
@@ -153,3 +152,19 @@ def test_add_field_to_entry(library):
153152
entry = [x for x in library.entries if x.path == item_path][0]
154153
# meta tags and tags field present
155154
assert len(entry.tag_box_fields) == 2
155+
156+
157+
def test_add_field_tag(library, generate_tag):
158+
# Given
159+
entry = library.entries[0]
160+
tag_name = "xxx"
161+
tag = generate_tag(name=tag_name)
162+
tag_field = entry.tag_box_fields[0]
163+
164+
# When
165+
library.add_field_tag(tag, tag_field)
166+
167+
# Then
168+
entry = [x for x in library.entries if x.id == entry.id][0]
169+
tag_field = entry.tag_box_fields[0]
170+
assert [x.name for x in tag_field.tags if x.name == tag_name]

0 commit comments

Comments
 (0)