Skip to content

feat: start storing date_created and date_modified #745

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/tagstudio/core/library/alchemy/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ class SortingModeEnum(enum.Enum):
DATE_ADDED = "file.date_added"
FILE_NAME = "generic.filename"
PATH = "file.path"

DATE_CREATED = "file.date_created"
DATE_MODIFIED = "file.date_modified"

@dataclass
class FilterState:
Expand Down
22 changes: 22 additions & 0 deletions src/tagstudio/core/library/alchemy/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from typing import TYPE_CHECKING
from uuid import uuid4
from warnings import catch_warnings
import platform

import structlog
from humanfriendly import format_timespan
Expand Down Expand Up @@ -223,6 +224,19 @@
self.folder = None
self.included_files = set()

def get_file_time(self, file_path: Path):
"""Get the creation and modification times of a file."""
stat = file_path.stat()

# st_birthtime on Windows and Mac, st_ctime on Linux.
if platform.system() in ["Windows", "Darwin"]: # Windows & macOS
date_created = datetime.fromtimestamp(stat.st_birthtime)

Check failure on line 233 in src/tagstudio/core/library/alchemy/library.py

View workflow job for this annotation

GitHub Actions / Run MyPy

[mypy] reported by reviewdog 🐶 "stat_result" has no attribute "st_birthtime" [attr-defined] Raw Output: /home/runner/work/TagStudio/TagStudio/src/tagstudio/core/library/alchemy/library.py:233:51: error: "stat_result" has no attribute "st_birthtime" [attr-defined]
else: # Linux
date_created = datetime.fromtimestamp(stat.st_ctime) # Linux lacks st_birthtime

date_modified = datetime.fromtimestamp(stat.st_mtime)
return date_created, date_modified

def migrate_json_to_sqlite(self, json_lib: JsonLibrary):
"""Migrate JSON library data to the SQLite database."""
logger.info("Starting Library Conversion...")
Expand Down Expand Up @@ -284,8 +298,12 @@
fields=[],
id=entry.id + 1, # JSON IDs start at 0 instead of 1
date_added=datetime.now(),
date_modified=date_modified,
date_created=date_created,
)
for entry in json_lib.entries
if (date_created := self.get_file_time(entry.path / entry.filename)[0]) is not None
and (date_modified := self.get_file_time(entry.path / entry.filename)[1]) is not None
]
)
for entry in json_lib.entries:
Expand Down Expand Up @@ -896,6 +914,10 @@
match search.sorting_mode:
case SortingModeEnum.DATE_ADDED:
sort_on = Entry.id
case SortingModeEnum.DATE_CREATED:
sort_on = Entry.date_created
case SortingModeEnum.DATE_MODIFIED:
sort_on = Entry.date_modified
case SortingModeEnum.FILE_NAME:
sort_on = func.lower(Entry.filename)
case SortingModeEnum.PATH:
Expand Down
2 changes: 1 addition & 1 deletion src/tagstudio/core/library/alchemy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ class ValueType(Base):
is_default: Mapped[bool]
position: Mapped[int]

# add relations to other tables
# add relations to otheu tables
text_fields: Mapped[list[TextField]] = relationship("TextField", back_populates="type")
datetime_fields: Mapped[list[DatetimeField]] = relationship(
"DatetimeField", back_populates="type"
Expand Down
27 changes: 22 additions & 5 deletions src/tagstudio/core/utils/refresh_dir.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import datetime as dt
from collections.abc import Iterator
from dataclasses import dataclass, field
from datetime import datetime as dt
from pathlib import Path
from time import time
import platform

import structlog

from tagstudio.core.constants import TS_FOLDER_NAME
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Entry
Expand All @@ -26,7 +26,6 @@
]
)


@dataclass
class RefreshDirTracker:
library: Library
Expand All @@ -36,6 +35,20 @@
def files_count(self) -> int:
return len(self.files_not_in_library)

# Created get_file_time() (basically same thing) in library
def get_file_times(self, file_path: Path):
"""Get the creation and modification times of a file."""
stat = file_path.stat()

# st_birthtime on Windows and Mac, st_ctime on Linux.
if platform.system() in ["Windows", "Darwin"]: # Windows & macOS
date_created = dt.datetime.fromtimestamp(stat.st_birthtime)

Check failure on line 45 in src/tagstudio/core/utils/refresh_dir.py

View workflow job for this annotation

GitHub Actions / Run MyPy

[mypy] reported by reviewdog 🐶 "stat_result" has no attribute "st_birthtime" [attr-defined] Raw Output: /home/runner/work/TagStudio/TagStudio/src/tagstudio/core/utils/refresh_dir.py:45:54: error: "stat_result" has no attribute "st_birthtime" [attr-defined]
else: # Linux
date_created = dt.datetime.fromtimestamp(stat.st_ctime) # Linux lacks st_birthtime

date_modified = dt.datetime.fromtimestamp(stat.st_mtime)
return date_created, date_modified

def save_new_files(self):
"""Save the list of files that are not in the library."""
if self.files_not_in_library:
Expand All @@ -44,9 +57,13 @@
path=entry_path,
folder=self.library.folder,
fields=[],
date_added=dt.now(),
date_added=dt.datetime.now(),
date_created=date_created,
date_modified=date_modified,
)
for entry_path in self.files_not_in_library
if (date_created := self.get_file_times(entry_path)[0]) is not None
and (date_modified := self.get_file_times(entry_path)[1]) is not None
]
self.library.add_entries(entries)

Expand Down Expand Up @@ -98,7 +115,7 @@
relative_path = f.relative_to(lib_path)
# TODO - load these in batch somehow
if not self.library.has_path_entry(relative_path):
self.files_not_in_library.append(relative_path)
self.files_not_in_library.append(f)

end_time_total = time()
yield dir_file_count
Expand Down
Loading