Skip to content

Commit 60bf0f2

Browse files
committed
rename LibraryField, add is_default property
1 parent 704237f commit 60bf0f2

File tree

10 files changed

+100
-43
lines changed

10 files changed

+100
-43
lines changed

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from dataclasses import dataclass
3+
from dataclasses import dataclass, field
44
from enum import Enum
55
from typing import Any, TYPE_CHECKING
66

@@ -11,7 +11,7 @@
1111
from .enums import FieldTypeEnum
1212

1313
if TYPE_CHECKING:
14-
from .models import Entry, Tag, LibraryField
14+
from .models import Entry, Tag, ValueType
1515

1616

1717
class BaseField(Base):
@@ -23,10 +23,10 @@ def id(cls) -> Mapped[int]:
2323

2424
@declared_attr
2525
def type_key(cls) -> Mapped[str]:
26-
return mapped_column(ForeignKey("library_fields.key"))
26+
return mapped_column(ForeignKey("value_type.key"))
2727

2828
@declared_attr
29-
def type(cls) -> Mapped[LibraryField]:
29+
def type(cls) -> Mapped[ValueType]:
3030
return relationship(foreign_keys=[cls.type_key], lazy=False) # type: ignore
3131

3232
@declared_attr
@@ -127,21 +127,28 @@ def __eq__(self, value) -> bool:
127127
class DefaultField:
128128
id: int
129129
name: str
130-
type: Any # TextFieldTypes | TagBoxTypes | DateTimeTypes
130+
type: FieldTypeEnum
131+
is_default: bool = field(default=False)
131132

132133

133134
class _FieldID(Enum):
134135
"""Only for bootstrapping content of DB table"""
135136

136-
TITLE = DefaultField(id=0, name="Title", type=FieldTypeEnum.TEXT_LINE)
137+
TITLE = DefaultField(
138+
id=0, name="Title", type=FieldTypeEnum.TEXT_LINE, is_default=True
139+
)
137140
AUTHOR = DefaultField(id=1, name="Author", type=FieldTypeEnum.TEXT_LINE)
138141
ARTIST = DefaultField(id=2, name="Artist", type=FieldTypeEnum.TEXT_LINE)
139142
URL = DefaultField(id=3, name="URL", type=FieldTypeEnum.TEXT_LINE)
140143
DESCRIPTION = DefaultField(id=4, name="Description", type=FieldTypeEnum.TEXT_LINE)
141144
NOTES = DefaultField(id=5, name="Notes", type=FieldTypeEnum.TEXT_BOX)
142145
TAGS = DefaultField(id=6, name="Tags", type=FieldTypeEnum.TAGS)
143-
TAGS_CONTENT = DefaultField(id=7, name="Content Tags", type=FieldTypeEnum.TAGS)
144-
TAGS_META = DefaultField(id=8, name="Meta Tags", type=FieldTypeEnum.TAGS)
146+
TAGS_CONTENT = DefaultField(
147+
id=7, name="Content Tags", type=FieldTypeEnum.TAGS, is_default=True
148+
)
149+
TAGS_META = DefaultField(
150+
id=8, name="Meta Tags", type=FieldTypeEnum.TAGS, is_default=True
151+
)
145152
COLLATION = DefaultField(id=9, name="Collation", type=FieldTypeEnum.TEXT_LINE)
146153
DATE = DefaultField(id=10, name="Date", type=FieldTypeEnum.DATETIME)
147154
DATE_CREATED = DefaultField(id=11, name="Date Created", type=FieldTypeEnum.DATETIME)

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

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
BaseField,
3939
)
4040
from .joins import TagSubtag, TagField
41-
from .models import Entry, Preferences, Tag, TagAlias, LibraryField, Folder
41+
from .models import Entry, Preferences, Tag, TagAlias, ValueType, Folder
4242
from ...constants import (
4343
LibraryPrefs,
4444
TS_FOLDER_NAME,
@@ -148,16 +148,17 @@ def open_library(
148148
for field in _FieldID:
149149
try:
150150
session.add(
151-
LibraryField(
151+
ValueType(
152+
key=field.name,
152153
name=field.value.name,
153154
type=field.value.type,
154-
order=field.value.id,
155-
key=field.name,
155+
position=field.value.id,
156+
is_default=field.value.is_default,
156157
)
157158
)
158159
session.commit()
159160
except IntegrityError:
160-
logger.debug("preference already exists", pref=pref)
161+
logger.debug("ValueType already exists", field=field)
161162
session.rollback()
162163

163164
# check if folder matching current path exists already
@@ -178,6 +179,17 @@ def open_library(
178179
# load ignored extensions
179180
self.ignored_extensions = self.prefs(LibraryPrefs.EXTENSION_LIST)
180181

182+
@property
183+
def default_fields(self) -> list[BaseField]:
184+
with Session(self.engine) as session:
185+
types = session.scalars(
186+
select(ValueType).where(
187+
# check if field is default
188+
ValueType.is_default.is_(True)
189+
)
190+
)
191+
return [x.as_field for x in types]
192+
181193
def delete_item(self, item):
182194
logger.info("deleting item", item=item)
183195
with Session(self.engine) as session:
@@ -579,23 +591,21 @@ def update_entry_field(
579591
session.commit()
580592

581593
@property
582-
def field_types(self) -> dict[str, LibraryField]:
594+
def field_types(self) -> dict[str, ValueType]:
583595
with Session(self.engine) as session:
584-
return {x.key: x for x in session.scalars(select(LibraryField)).all()}
596+
return {x.key: x for x in session.scalars(select(ValueType)).all()}
585597

586-
def get_library_field(self, field_key: str) -> LibraryField:
598+
def get_value_type(self, field_key: str) -> ValueType:
587599
with Session(self.engine) as session:
588-
field = session.scalar(
589-
select(LibraryField).where(LibraryField.key == field_key)
590-
)
600+
field = session.scalar(select(ValueType).where(ValueType.key == field_key))
591601
session.expunge(field)
592602
return field
593603

594604
def add_entry_field_type(
595605
self,
596606
entry_ids: list[int] | int,
597607
*,
598-
field: LibraryField | None = None,
608+
field: ValueType | None = None,
599609
field_id: _FieldID | str | None = None,
600610
value: str | datetime | list[str] | None = None,
601611
) -> bool:
@@ -615,7 +625,7 @@ def add_entry_field_type(
615625
if not field:
616626
if isinstance(field_id, _FieldID):
617627
field_id = field_id.name
618-
field = self.get_library_field(field_id)
628+
field = self.get_value_type(field_id)
619629

620630
field_model: TextField | DatetimeField | TagBoxField
621631
if field.type in (FieldTypeEnum.TEXT_LINE, FieldTypeEnum.TEXT_BOX):

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

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
FieldTypeEnum,
1414
_FieldID,
1515
BaseField,
16+
BooleanField,
1617
)
1718
from .joins import TagSubtag
1819
from ...constants import TAG_FAVORITE, TAG_ARCHIVED
@@ -139,7 +140,7 @@ def fields(self) -> list[BaseField]:
139140
fields.extend(self.tag_box_fields)
140141
fields.extend(self.text_fields)
141142
fields.extend(self.datetime_fields)
142-
fields = sorted(fields, key=lambda field: field.type.order)
143+
fields = sorted(fields, key=lambda field: field.type.position)
143144
return fields
144145

145146
@property
@@ -171,18 +172,11 @@ def __init__(
171172
self,
172173
path: Path,
173174
folder: Folder,
174-
fields: list[BaseField] | None = None,
175+
fields: list[BaseField],
175176
) -> None:
176177
self.path = path
177178
self.folder = folder
178179

179-
if fields is None:
180-
fields = [
181-
TagBoxField(type_key=_FieldID.TAGS_META.name, position=0),
182-
TagBoxField(type_key=_FieldID.TAGS_CONTENT.name, position=0),
183-
TextField(type_key=_FieldID.TITLE.name, position=0),
184-
]
185-
186180
for field in fields:
187181
if isinstance(field, TextField):
188182
self.text_fields.append(field)
@@ -210,22 +204,25 @@ def remove_tag(self, tag: Tag, field: TagBoxField | None = None) -> None:
210204
tag_box_field.tags.remove(tag)
211205

212206

213-
class LibraryField(Base):
207+
class ValueType(Base):
214208
"""Define Field Types in the Library.
215209
216210
Example:
217211
key: content_tags (this field is slugified `name`)
218212
name: Content Tags (this field is human readable name)
219213
kind: type of content (Text Line, Text Box, Tags, Datetime, Checkbox)
214+
is_default: Should the field be present in new Entry?
215+
order: position of the field widget in the Entry form
220216
221217
"""
222218

223-
__tablename__ = "library_fields"
219+
__tablename__ = "value_type"
224220

225221
key: Mapped[str] = mapped_column(primary_key=True)
226222
name: Mapped[str] = mapped_column(nullable=False)
227223
type: Mapped[FieldTypeEnum] = mapped_column(default=FieldTypeEnum.TEXT_LINE)
228-
order: Mapped[int]
224+
is_default: Mapped[bool]
225+
position: Mapped[int]
229226

230227
# add relations to other tables
231228
text_fields: Mapped[list[TextField]] = relationship(
@@ -237,9 +234,27 @@ class LibraryField(Base):
237234
tag_box_fields: Mapped[list[TagBoxField]] = relationship(
238235
"TagBoxField", back_populates="type"
239236
)
237+
boolean_fields: Mapped[list[BooleanField]] = relationship(
238+
"BooleanField", back_populates="type"
239+
)
240240

241-
242-
@event.listens_for(LibraryField, "before_insert")
241+
@property
242+
def as_field(self) -> BaseField:
243+
FieldClass = {
244+
FieldTypeEnum.TEXT_LINE: TextField,
245+
FieldTypeEnum.TEXT_BOX: TextField,
246+
FieldTypeEnum.TAGS: TagBoxField,
247+
FieldTypeEnum.DATETIME: DatetimeField,
248+
FieldTypeEnum.BOOLEAN: BooleanField,
249+
}
250+
251+
return FieldClass[self.type](
252+
type_key=self.key,
253+
position=self.position,
254+
)
255+
256+
257+
@event.listens_for(ValueType, "before_insert")
243258
def slugify_field_key(mapper, connection, target):
244259
"""Slugify the field key before inserting into the database."""
245260
if not target.key:

tagstudio/src/core/library/json/library.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1253,7 +1253,10 @@ def add_new_files_as_entries(self) -> list[int]:
12531253
path = Path(file)
12541254
# print(os.path.split(file))
12551255
entry = Entry(
1256-
id=self._next_entry_id, filename=path.name, path=path.parent, fields=[]
1256+
id=self._next_entry_id,
1257+
filename=path.name,
1258+
path=path.parent,
1259+
fields=[],
12571260
)
12581261
self._next_entry_id += 1
12591262
self.add_entry_to_library(entry)

tagstudio/src/core/utils/refresh_dir.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def save_new_files(self) -> Iterator[int]:
2828
Entry(
2929
path=entry_path,
3030
folder=self.library.folder,
31+
fields=self.library.default_fields,
3132
)
3233
]
3334
)

tagstudio/src/qt/widgets/preview_panel.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,7 @@ def update_widgets(self) -> bool:
676676
mixed_fields.append(f)
677677

678678
self.common_fields = common_fields
679-
self.mixed_fields = sorted(mixed_fields, key=lambda x: x.type.order)
679+
self.mixed_fields = sorted(mixed_fields, key=lambda x: x.type.position)
680680

681681
self.selected = list(self.driver.selected)
682682
logger.info(
@@ -946,7 +946,7 @@ def remove_field(self, field: BaseField):
946946
self.driver.update_badges(self.selected)
947947

948948
def update_field(self, field: BaseField, content: str) -> None:
949-
"""Remove a field from all selected Entries, given a field object."""
949+
"""Update a field in all selected Entries, given a field object."""
950950
assert isinstance(
951951
field, (TextField, DatetimeField, TagBoxField)
952952
), f"instance: {type(field)}"

tagstudio/tests/conftest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,20 +56,21 @@ def library(request):
5656
entry = Entry(
5757
folder=lib.folder,
5858
path=pathlib.Path("foo.txt"),
59+
fields=lib.default_fields,
5960
)
6061

6162
entry.tag_box_fields = [
6263
TagBoxField(type_key=_FieldID.TAGS.name, tags={tag}, position=0),
6364
TagBoxField(
6465
type_key=_FieldID.TAGS_META.name,
6566
position=0,
66-
# tags={tag2}
6767
),
6868
]
6969

7070
entry2 = Entry(
7171
folder=lib.folder,
7272
path=pathlib.Path("one/two/bar.md"),
73+
fields=lib.default_fields,
7374
)
7475
entry2.tag_box_fields = [
7576
TagBoxField(

tagstudio/tests/macros/test_dupe_entries.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ def test_refresh_dupe_files(library):
1010
entry = Entry(
1111
folder=library.folder,
1212
path=pathlib.Path("bar/foo.txt"),
13+
fields=library.default_fields,
1314
)
15+
1416
entry2 = Entry(
1517
folder=library.folder,
1618
path=pathlib.Path("foo/foo.txt"),
19+
fields=library.default_fields,
1720
)
1821

1922
library.add_entries([entry, entry2])

tagstudio/tests/qt/test_driver.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99

1010
def test_update_thumbs(qt_driver):
1111
qt_driver.frame_content = [
12-
Entry(folder=qt_driver.lib.folder, path=Path("/tmp/foo"))
12+
Entry(
13+
folder=qt_driver.lib.folder,
14+
path=Path("/tmp/foo"),
15+
fields=qt_driver.lib.default_fields,
16+
)
1317
]
1418

1519
qt_driver.item_thumbs = []

tagstudio/tests/test_library.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ def test_library_add_file():
2727
lib = Library()
2828
lib.open_library(tmp_dir)
2929

30-
entry = Entry(path=file_path, folder=lib.folder)
30+
entry = Entry(
31+
path=file_path,
32+
folder=lib.folder,
33+
fields=lib.default_fields,
34+
)
3135

3236
assert not lib.has_path_entry(entry.path)
3337

@@ -94,7 +98,10 @@ def test_get_entry(library, entry_min):
9498

9599

96100
def test_entries_count(library):
97-
entries = [Entry(path=Path(f"{x}.txt"), folder=library.folder) for x in range(10)]
101+
entries = [
102+
Entry(path=Path(f"{x}.txt"), folder=library.folder, fields=[])
103+
for x in range(10)
104+
]
98105
library.add_entries(entries)
99106
matches, page = library.search_library(
100107
FilterState(
@@ -111,6 +118,7 @@ def test_add_field_to_entry(library):
111118
entry = Entry(
112119
folder=library.folder,
113120
path=Path("xxx"),
121+
fields=library.default_fields,
114122
)
115123
# meta tags + content tags
116124
assert len(entry.tag_box_fields) == 2
@@ -208,7 +216,12 @@ def test_preferences(library):
208216

209217
def test_save_windows_path(library, generate_tag):
210218
# pretend we are on windows and create `Path`
211-
entry = Entry(path=PureWindowsPath("foo\\bar.txt"), folder=library.folder)
219+
220+
entry = Entry(
221+
path=PureWindowsPath("foo\\bar.txt"),
222+
folder=library.folder,
223+
fields=library.default_fields,
224+
)
212225
tag = generate_tag("win_path")
213226
tag_name = tag.name
214227

0 commit comments

Comments
 (0)