Skip to content

Commit 51a9c16

Browse files
refactor: remove dead folders table (#1444)
* refactor: remove dead folders table * fix: remove folder param from all uses of Entry constructor * fix: missing filename column after migration * fix: bump db version to 300, since it is a breaking change * fix: add empty library fixture for version 202
1 parent 6aa0cf7 commit 51a9c16

9 files changed

Lines changed: 41 additions & 70 deletions

File tree

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

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

1010
DB_VERSION_CURRENT_KEY: str = "CURRENT"
1111
DB_VERSION_INITIAL_KEY: str = "INITIAL"
12-
DB_VERSION: int = 202
12+
DB_VERSION: int = 300
1313

1414
TAG_CHILDREN_QUERY = text("""
1515
WITH RECURSIVE ChildTags AS (

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

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from os import makedirs
1919
from pathlib import Path
2020
from typing import TYPE_CHECKING
21-
from uuid import uuid4
2221

2322
import sqlalchemy
2423
import structlog
@@ -95,7 +94,6 @@
9594
from tagstudio.core.library.alchemy.joins import TagEntry, TagParent
9695
from tagstudio.core.library.alchemy.models import (
9796
Entry,
98-
Folder,
9997
Namespace,
10098
Tag,
10199
TagAlias,
@@ -234,7 +232,6 @@ class Library:
234232

235233
library_dir: Path | None = None
236234
engine: Engine | None = None
237-
folder: Folder | None = None
238235
included_files: set[Path] = set()
239236

240237
def __init__(self) -> None:
@@ -259,7 +256,6 @@ def migrate_json_to_sqlite(self, json_lib: JsonLibrary):
259256
"""Migrate JSON library data to the SQLite database."""
260257
logger.info("Starting Library Conversion...")
261258
start_time = time.time()
262-
folder: Folder = Folder(path=self.library_dir, uuid=str(uuid4()))
263259

264260
# Tags
265261
for tag in json_lib.tags:
@@ -312,7 +308,6 @@ def migrate_json_to_sqlite(self, json_lib: JsonLibrary):
312308
[
313309
Entry(
314310
path=entry.path / entry.filename,
315-
folder=folder,
316311
fields=[],
317312
id=entry.id + 1, # NOTE: JSON IDs start at 0 instead of 1
318313
date_added=datetime.now(),
@@ -479,16 +474,6 @@ def create_sqlite_library(
479474
session.add(Version(key=DB_VERSION_CURRENT_KEY, value=DB_VERSION))
480475
session.flush()
481476

482-
# add folder for current path
483-
folder = Folder(
484-
path=library_dir,
485-
uuid=str(uuid4()),
486-
)
487-
session.add(folder)
488-
session.expunge(folder)
489-
session.flush()
490-
self.folder = folder
491-
492477
# Generate default .ts_ignore file
493478
try:
494479
ts_ignore_template = (
@@ -584,6 +569,7 @@ def open_sqlite_library(
584569
(self.__apply_db200_migration, 200, None), # changes: field tables
585570
(self.__apply_db201_migration, 201, 200), # changes: field tables
586571
(self.__apply_db202_migration, 202, None), # changes: tag_parents
572+
(self.__apply_db300_migration, 300, None), # changes: deletes folders
587573
]
588574
for migration, v, iv in migrations:
589575
if loaded_db_version < v and (iv is None or initial_db_version < iv):
@@ -601,22 +587,6 @@ def open_sqlite_library(
601587
)
602588
logger.info(f"[Library] Library migrated to DB version {DB_VERSION}")
603589

604-
with Session(self.engine) as session:
605-
# TODO: the folder logic has no use and was never finished, remove it
606-
# check if folder matching current path exists already
607-
# NOTE: this has been causing new Folders to be created when the library is moved, since
608-
# its introduction
609-
self.folder = session.scalar(select(Folder).where(Folder.path == library_dir))
610-
if not self.folder:
611-
folder = Folder(
612-
path=library_dir,
613-
uuid=str(uuid4()),
614-
)
615-
session.add(folder)
616-
session.expunge(folder)
617-
session.commit()
618-
self.folder = folder
619-
620590
# everything is fine, set the library path
621591
self.library_dir = library_dir
622592
return LibraryStatus(success=True, library_path=library_dir)
@@ -906,6 +876,44 @@ def __apply_db202_migration(self, session: Session, library_dir: Path):
906876
session.flush()
907877
logger.info("[Library][Migration][202] Verified TagParent table data")
908878

879+
def __apply_db300_migration(self, session: Session, library_dir: Path):
880+
## remove folder_id column from entries table
881+
# create new table in the desired scheme (without folder_id column)
882+
session.execute(
883+
text("""
884+
CREATE TABLE entries_new (
885+
id INTEGER NOT NULL,
886+
path VARCHAR NOT NULL,
887+
suffix VARCHAR NOT NULL,
888+
date_created DATETIME,
889+
date_modified DATETIME,
890+
date_added DATETIME,
891+
filename TEXT NOT NULL DEFAULT '',
892+
PRIMARY KEY (id),
893+
UNIQUE (path)
894+
)
895+
""")
896+
)
897+
session.flush()
898+
# transfer data to new table
899+
session.execute(
900+
text("""
901+
INSERT INTO entries_new (id, path, suffix, date_created, date_modified, date_added,
902+
filename)
903+
SELECT id, path, suffix, date_created, date_modified, date_added, filename
904+
FROM entries
905+
""")
906+
)
907+
# delete old table
908+
session.execute(text("DROP TABLE entries"))
909+
# rename new table to old table
910+
session.execute(text("ALTER TABLE entries_new RENAME TO entries"))
911+
session.flush()
912+
913+
## drop table "folders"
914+
session.execute(text("DROP TABLE folders"))
915+
session.flush()
916+
909917
@property
910918
def field_templates(self) -> Sequence[BaseFieldTemplate]:
911919
with Session(self.engine) as session:

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

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -182,23 +182,11 @@ def __ge__(self, other: "Tag") -> bool:
182182
return self.name >= other.name
183183

184184

185-
class Folder(Base):
186-
__tablename__ = "folders"
187-
188-
# TODO - implement this
189-
id: Mapped[int] = mapped_column(primary_key=True)
190-
path: Mapped[Path] = mapped_column(PathType, unique=True)
191-
uuid: Mapped[str] = mapped_column(unique=True)
192-
193-
194185
class Entry(Base):
195186
__tablename__ = "entries"
196187

197188
id: Mapped[int] = mapped_column(primary_key=True)
198189

199-
folder_id: Mapped[int] = mapped_column(ForeignKey("folders.id"))
200-
folder: Mapped[Folder] = relationship("Folder")
201-
202190
path: Mapped[Path] = mapped_column(PathType, unique=True)
203191
filename: Mapped[str] = mapped_column()
204192
suffix: Mapped[str] = mapped_column()
@@ -235,7 +223,6 @@ def is_archived(self) -> bool:
235223
def __init__(
236224
self,
237225
path: Path,
238-
folder: Folder,
239226
fields: list[BaseField],
240227
id: int | None = None,
241228
date_created: dt | None = None,
@@ -244,7 +231,6 @@ def __init__(
244231
) -> None:
245232
super().__init__()
246233
self.path = path
247-
self.folder = folder
248234
self.id = id # pyright: ignore[reportAttributeAccessIssue]
249235
self.filename = path.name
250236
self.suffix = path.suffix.lstrip(".").lower()

src/tagstudio/core/library/refresh.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from tagstudio.core.library.alchemy.models import Entry
1717
from tagstudio.core.library.ignore import PATH_GLOB_FLAGS, Ignore, ignore_to_glob
1818
from tagstudio.core.utils.silent_subprocess import silent_run # pyright: ignore
19-
from tagstudio.core.utils.types import unwrap
2019

2120
logger = structlog.get_logger(__name__)
2221

@@ -41,7 +40,6 @@ def save_new_files(self) -> Iterator[int]:
4140
entries = [
4241
Entry(
4342
path=entry_path,
44-
folder=unwrap(self.library.folder),
4543
fields=[],
4644
date_added=dt.now(),
4745
)

tests/conftest.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
from tagstudio.core.constants import THUMB_CACHE_NAME, TS_FOLDER_NAME
2121
from tagstudio.core.library.alchemy.library import Library
2222
from tagstudio.core.library.alchemy.models import Entry, Tag
23-
from tagstudio.core.utils.types import unwrap
2423
from tagstudio.qt.thumb_grid_layout import ThumbGridLayout
2524
from tagstudio.qt.ts_qt import QtDriver
2625

@@ -36,22 +35,18 @@ def file_mediatypes_library():
3635

3736
status = lib.open_library(Path(""), in_memory=True)
3837
assert status.success
39-
folder = unwrap(lib.folder)
4038

4139
entry1 = Entry(
42-
folder=folder,
4340
path=Path("foo.png"),
4441
fields=[TextField(name="Title", value="I'm a Test Title")],
4542
)
4643

4744
entry2 = Entry(
48-
folder=folder,
4945
path=Path("bar.png"),
5046
fields=[TextField(name="Title", value="I'm a Test Title")],
5147
)
5248

5349
entry3 = Entry(
54-
folder=folder,
5550
path=Path("baz.apng"),
5651
fields=[TextField(name="Title", value="I'm a Test Title")],
5752
)
@@ -87,7 +82,6 @@ def library(request, library_dir: Path): # pyright: ignore
8782
lib = Library()
8883
status = lib.open_library(library_path, in_memory=True)
8984
assert status.success
90-
folder = unwrap(lib.folder)
9185

9286
tag = Tag(
9387
name="foo",
@@ -116,15 +110,13 @@ def library(request, library_dir: Path): # pyright: ignore
116110
# default item with deterministic name
117111
entry = Entry(
118112
id=1,
119-
folder=folder,
120113
path=Path("foo.txt"),
121114
fields=[TextField(name="Title", value="I'm a Test Title")],
122115
)
123116
assert lib.add_tags_to_entries(entry.id, tag.id)
124117

125118
entry2 = Entry(
126119
id=2,
127-
folder=folder,
128120
path=Path("one/two/bar.md"),
129121
fields=[TextField(name="Title", value="I'm a Test Title")],
130122
)
Binary file not shown.
8 KB
Binary file not shown.

tests/macros/test_dupe_files.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,21 @@
88
from tagstudio.core.library.alchemy.library import Library
99
from tagstudio.core.library.alchemy.models import Entry
1010
from tagstudio.core.library.alchemy.registries.dupe_files_registry import DupeFilesRegistry
11-
from tagstudio.core.utils.types import unwrap
1211

1312
CWD = Path(__file__).parent
1413

1514

1615
def test_refresh_dupe_files(library: Library):
1716
library.library_dir = Path("/tmp/")
18-
folder = unwrap(library.folder)
1917

2018
fields: list[BaseField] = [TextField(name="Title", value="I'm a Test Title")]
2119

2220
entry = Entry(
23-
folder=folder,
2421
path=Path("bar/foo.txt"),
2522
fields=fields,
2623
)
2724

2825
entry2 = Entry(
29-
folder=folder,
3026
path=Path("foo/foo.txt"),
3127
fields=fields,
3228
)

tests/test_library.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ def test_library_add_file(library: Library):
7777
"""Check Entry.path handling for insert vs lookup"""
7878
entry = Entry(
7979
path=Path("bar.txt"),
80-
folder=unwrap(library.folder),
8180
fields=[TextField(name="Title", value="I'm a Test Title")],
8281
)
8382

@@ -139,8 +138,7 @@ def test_get_entry(library: Library, entry_min: Entry):
139138

140139

141140
def test_entries_count(library: Library):
142-
folder = unwrap(library.folder)
143-
entries = [Entry(path=Path(f"{x}.txt"), folder=folder, fields=[]) for x in range(10)]
141+
entries = [Entry(path=Path(f"{x}.txt"), fields=[]) for x in range(10)]
144142
new_ids = library.add_entries(entries)
145143
assert len(new_ids) == 10
146144

@@ -254,23 +252,20 @@ def test_update_entry_with_multiple_identical_text_fields(library: Library, entr
254252
def test_mirror_entry_fields(library: Library):
255253
# Create and add entries with fields
256254
entry_a = Entry(
257-
folder=unwrap(library.folder),
258255
path=Path("title_and_date.txt"),
259256
fields=[
260257
TextField(name="Title", value="I'm a Test Title"),
261258
DatetimeField(name="Date", value="2026-05-07 12:59:24"),
262259
],
263260
)
264261
entry_b = Entry(
265-
folder=unwrap(library.folder),
266262
path=Path("notes.txt"),
267263
fields=[
268264
TextField(name="Notes", value="These are my notes.\nNo peeking!", is_multiline=True),
269265
TextField(name="Title", value="I'm a Test Title"),
270266
],
271267
)
272268
entry_c = Entry(
273-
folder=unwrap(library.folder),
274269
path=Path("date_published.txt"),
275270
fields=[
276271
DatetimeField(name="Date Published", value="2000-01-01 12:00:00"),
@@ -319,22 +314,18 @@ def test_mirror_entry_fields(library: Library):
319314

320315

321316
def test_merge_entries(library: Library):
322-
folder = unwrap(library.folder)
323-
324317
tag_0: Tag = unwrap(library.add_tag(Tag(id=1010, name="tag_0")))
325318
tag_1: Tag = unwrap(library.add_tag(Tag(id=1011, name="tag_1")))
326319
tag_2: Tag = unwrap(library.add_tag(Tag(id=1012, name="tag_2")))
327320

328321
entry_a = Entry(
329-
folder=folder,
330322
path=Path("a"),
331323
fields=[
332324
TextField(name="Author", value="Author McAuthorson"),
333325
TextField(name="Description", value="test description", is_multiline=True),
334326
],
335327
)
336328
entry_b = Entry(
337-
folder=folder,
338329
path=Path("b"),
339330
fields=[TextField(name="Notes", value="test note", is_multiline=True)],
340331
)

0 commit comments

Comments
 (0)