Skip to content

Commit 4942d16

Browse files
refactor: cleanup the refresh_dir code, update tests (#494)
* feat: take Ignore List into consideration when refreshing directory * undo the extension check in refresh_dir
1 parent af642a7 commit 4942d16

File tree

8 files changed

+47
-44
lines changed

8 files changed

+47
-44
lines changed

.github/workflows/mypy.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ jobs:
2323

2424
- name: Install dependencies
2525
run: |
26-
pip install -r requirements.txt
27-
pip install mypy==1.11.2
26+
python -m pip install --upgrade uv
27+
uv pip install --system -r requirements.txt
28+
uv pip install --system mypy==1.11.2
2829
mkdir tagstudio/.mypy_cache
2930
3031
- uses: tsuyoshicho/action-mypy@v4

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

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
selectinload,
2626
make_transient,
2727
)
28-
from typing import TYPE_CHECKING
2928

3029
from .db import make_tables
3130
from .enums import TagColor, FilterState, FieldTypeEnum
@@ -46,10 +45,6 @@
4645
BACKUP_FOLDER_NAME,
4746
)
4847

49-
if TYPE_CHECKING:
50-
from ...utils.dupe_files import DupeRegistry
51-
from ...utils.missing_files import MissingRegistry
52-
5348
LIBRARY_FILENAME: str = "ts_library.sqlite"
5449

5550
logger = structlog.get_logger(__name__)
@@ -100,11 +95,6 @@ class Library:
10095
engine: Engine | None
10196
folder: Folder | None
10297

103-
ignored_extensions: list[str]
104-
105-
missing_tracker: "MissingRegistry"
106-
dupe_tracker: "DupeRegistry"
107-
10898
def close(self):
10999
if self.engine:
110100
self.engine.dispose()
@@ -182,9 +172,6 @@ def open_library(
182172
session.commit()
183173
self.folder = folder
184174

185-
# load ignored extensions
186-
self.ignored_extensions = self.prefs(LibraryPrefs.EXTENSION_LIST)
187-
188175
@property
189176
def default_fields(self) -> list[BaseField]:
190177
with Session(self.engine) as session:
Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
import time
1+
from time import time
22
from collections.abc import Iterator
33
from dataclasses import dataclass, field
44
from pathlib import Path
55

6+
import structlog
7+
68
from src.core.constants import TS_FOLDER_NAME
79
from src.core.library import Library, Entry
810

11+
logger = structlog.get_logger(__name__)
12+
913

1014
@dataclass
1115
class RefreshDirTracker:
1216
library: Library
13-
dir_file_count: int = 0
1417
files_not_in_library: list[Path] = field(default_factory=list)
1518

1619
@property
@@ -36,38 +39,40 @@ def save_new_files(self) -> Iterator[int]:
3639

3740
self.files_not_in_library = []
3841

39-
def refresh_dir(self) -> Iterator[int]:
42+
def refresh_dir(self, lib_path: Path) -> Iterator[int]:
4043
"""Scan a directory for files, and add those relative filenames to internal variables."""
41-
if self.library.folder is None:
42-
raise ValueError("No folder set.")
44+
if self.library.library_dir is None:
45+
raise ValueError("No library directory set.")
4346

44-
start_time = time.time()
45-
self.files_not_in_library = []
46-
self.dir_file_count = 0
47+
start_time_total = time()
48+
start_time_loop = time()
4749

48-
lib_path = self.library.folder.path
50+
self.files_not_in_library = []
51+
dir_file_count = 0
4952

5053
for path in lib_path.glob("**/*"):
5154
str_path = str(path)
52-
if (
53-
path.is_dir()
54-
or "$RECYCLE.BIN" in str_path
55-
or TS_FOLDER_NAME in str_path
56-
or "tagstudio_thumbs" in str_path
57-
):
55+
if path.is_dir():
5856
continue
5957

60-
suffix = path.suffix.lower().lstrip(".")
61-
if suffix in self.library.ignored_extensions:
58+
if "$RECYCLE.BIN" in str_path or TS_FOLDER_NAME in str_path:
6259
continue
6360

64-
self.dir_file_count += 1
61+
dir_file_count += 1
6562
relative_path = path.relative_to(lib_path)
6663
# TODO - load these in batch somehow
6764
if not self.library.has_path_entry(relative_path):
6865
self.files_not_in_library.append(relative_path)
6966

70-
end_time = time.time()
7167
# Yield output every 1/30 of a second
72-
if (end_time - start_time) > 0.034:
73-
yield self.dir_file_count
68+
if (time() - start_time_loop) > 0.034:
69+
yield dir_file_count
70+
start_time_loop = time()
71+
72+
end_time_total = time()
73+
logger.info(
74+
"Directory scan time",
75+
path=lib_path,
76+
duration=(end_time_total - start_time_total),
77+
new_files_count=dir_file_count,
78+
)

tagstudio/src/qt/ts_qt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ def add_new_files_callback(self):
675675
)
676676
pw.show()
677677

678-
iterator = FunctionIterator(tracker.refresh_dir)
678+
iterator = FunctionIterator(lambda: tracker.refresh_dir(self.lib.library_dir))
679679
iterator.value.connect(
680680
lambda x: (
681681
pw.update_progress(x + 1),

tagstudio/tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def cwd():
2424
@pytest.fixture
2525
def library(request):
2626
# when no param is passed, use the default
27-
library_path = "/tmp/"
27+
library_path = "/dev/null/"
2828
if hasattr(request, "param"):
2929
if isinstance(request.param, TemporaryDirectory):
3030
library_path = request.param.name

tagstudio/tests/macros/test_dupe_entries.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88

99
def test_refresh_dupe_files(library):
10+
library.library_dir = "/tmp/"
1011
entry = Entry(
1112
folder=library.folder,
1213
path=pathlib.Path("bar/foo.txt"),

tagstudio/tests/macros/test_refresh_dir.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@
22
from tempfile import TemporaryDirectory
33

44
import pytest
5+
6+
from src.core.constants import LibraryPrefs
57
from src.core.utils.refresh_dir import RefreshDirTracker
68

79
CWD = pathlib.Path(__file__).parent
810

911

12+
@pytest.mark.parametrize("exclude_mode", [True, False])
1013
@pytest.mark.parametrize("library", [TemporaryDirectory()], indirect=True)
11-
def test_refresh_new_files(library):
14+
def test_refresh_new_files(library, exclude_mode):
15+
# Given
16+
library.set_prefs(LibraryPrefs.IS_EXCLUDE_LIST, exclude_mode)
17+
library.set_prefs(LibraryPrefs.EXTENSION_LIST, [".md"])
1218
registry = RefreshDirTracker(library=library)
19+
(library.library_dir / "FOO.MD").touch()
1320

14-
# touch new files to simulate new files
15-
(library.library_dir / "foo.md").touch()
16-
17-
assert not list(registry.refresh_dir())
21+
# When
22+
assert not list(registry.refresh_dir(library.library_dir))
1823

19-
assert registry.files_not_in_library == [pathlib.Path("foo.md")]
24+
# Then
25+
assert registry.files_not_in_library == [pathlib.Path("FOO.MD")]

tagstudio/tests/qt/test_preview_panel.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from pathlib import Path
2+
from tempfile import TemporaryDirectory
23

4+
import pytest
35

46
from src.core.library import Entry
57
from src.core.library.alchemy.enums import FieldTypeEnum
@@ -18,6 +20,7 @@ def test_update_widgets_not_selected(qt_driver, library):
1820
assert panel.file_label.text() == "No Items Selected"
1921

2022

23+
@pytest.mark.parametrize("library", [TemporaryDirectory()], indirect=True)
2124
def test_update_widgets_single_selected(qt_driver, library):
2225
qt_driver.frame_content = list(library.get_entries())
2326
qt_driver.selected = [0]

0 commit comments

Comments
 (0)