mirror of
https://github.com/TagStudioDev/TagStudio.git
synced 2026-02-01 15:49:09 +00:00
Merge pull request #142 from Hidorikun/test-support-2
Add pytest support
This commit is contained in:
@@ -183,7 +183,12 @@ _Skip these steps if launching from the .sh script on Linux/macOS._
|
||||
- Linux/macOS: `source .venv/bin/activate`
|
||||
|
||||
3. Install the required packages:
|
||||
`pip install -r requirements.txt`
|
||||
|
||||
- required to run the app: `pip install -r requirements.txt`
|
||||
- required to develop: `pip install -r requirements-dev.txt`
|
||||
|
||||
|
||||
To run all the tests use `python -m pytest tests/` from the `tagstudio` folder.
|
||||
|
||||
_Learn more about setting up a virtual environment [here](https://docs.python.org/3/tutorial/venv.html)._
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
ruff==0.4.2
|
||||
pre-commit==3.7.0
|
||||
pytest==8.2.0
|
||||
Pyinstaller==6.6.0
|
||||
mypy==1.10.0
|
||||
|
||||
136
tagstudio/src/core/constants.py
Normal file
136
tagstudio/src/core/constants.py
Normal file
@@ -0,0 +1,136 @@
|
||||
VERSION: str = "9.2.1" # Major.Minor.Patch
|
||||
VERSION_BRANCH: str = "Pre-Release" # 'Alpha', 'Beta', or '' for Full Release
|
||||
|
||||
# The folder & file names where TagStudio keeps its data relative to a library.
|
||||
TS_FOLDER_NAME: str = ".TagStudio"
|
||||
BACKUP_FOLDER_NAME: str = "backups"
|
||||
COLLAGE_FOLDER_NAME: str = "collages"
|
||||
LIBRARY_FILENAME: str = "ts_library.json"
|
||||
|
||||
# TODO: Turn this whitelist into a user-configurable blacklist.
|
||||
IMAGE_TYPES: list[str] = [
|
||||
"png",
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"jpg_large",
|
||||
"jpeg_large",
|
||||
"jfif",
|
||||
"gif",
|
||||
"tif",
|
||||
"tiff",
|
||||
"heic",
|
||||
"heif",
|
||||
"webp",
|
||||
"bmp",
|
||||
"svg",
|
||||
"avif",
|
||||
"apng",
|
||||
"jp2",
|
||||
"j2k",
|
||||
"jpg2",
|
||||
]
|
||||
VIDEO_TYPES: list[str] = [
|
||||
"mp4",
|
||||
"webm",
|
||||
"mov",
|
||||
"hevc",
|
||||
"mkv",
|
||||
"avi",
|
||||
"wmv",
|
||||
"flv",
|
||||
"gifv",
|
||||
"m4p",
|
||||
"m4v",
|
||||
"3gp",
|
||||
]
|
||||
AUDIO_TYPES: list[str] = [
|
||||
"mp3",
|
||||
"mp4",
|
||||
"mpeg4",
|
||||
"m4a",
|
||||
"aac",
|
||||
"wav",
|
||||
"flac",
|
||||
"alac",
|
||||
"wma",
|
||||
"ogg",
|
||||
"aiff",
|
||||
]
|
||||
DOC_TYPES: list[str] = ["txt", "rtf", "md", "doc", "docx", "pdf", "tex", "odt", "pages"]
|
||||
PLAINTEXT_TYPES: list[str] = [
|
||||
"txt",
|
||||
"md",
|
||||
"css",
|
||||
"html",
|
||||
"xml",
|
||||
"json",
|
||||
"js",
|
||||
"ts",
|
||||
"ini",
|
||||
"htm",
|
||||
"csv",
|
||||
"php",
|
||||
"sh",
|
||||
"bat",
|
||||
]
|
||||
SPREADSHEET_TYPES: list[str] = ["csv", "xls", "xlsx", "numbers", "ods"]
|
||||
PRESENTATION_TYPES: list[str] = ["ppt", "pptx", "key", "odp"]
|
||||
ARCHIVE_TYPES: list[str] = ["zip", "rar", "tar", "tar.gz", "tgz", "7z"]
|
||||
PROGRAM_TYPES: list[str] = ["exe", "app"]
|
||||
SHORTCUT_TYPES: list[str] = ["lnk", "desktop", "url"]
|
||||
|
||||
ALL_FILE_TYPES: list[str] = (
|
||||
IMAGE_TYPES
|
||||
+ VIDEO_TYPES
|
||||
+ AUDIO_TYPES
|
||||
+ DOC_TYPES
|
||||
+ SPREADSHEET_TYPES
|
||||
+ PRESENTATION_TYPES
|
||||
+ ARCHIVE_TYPES
|
||||
+ PROGRAM_TYPES
|
||||
+ SHORTCUT_TYPES
|
||||
)
|
||||
|
||||
BOX_FIELDS = ["tag_box", "text_box"]
|
||||
TEXT_FIELDS = ["text_line", "text_box"]
|
||||
DATE_FIELDS = ["datetime"]
|
||||
|
||||
TAG_COLORS = [
|
||||
"",
|
||||
"black",
|
||||
"dark gray",
|
||||
"gray",
|
||||
"light gray",
|
||||
"white",
|
||||
"light pink",
|
||||
"pink",
|
||||
"red",
|
||||
"red orange",
|
||||
"orange",
|
||||
"yellow orange",
|
||||
"yellow",
|
||||
"lime",
|
||||
"light green",
|
||||
"mint",
|
||||
"green",
|
||||
"teal",
|
||||
"cyan",
|
||||
"light blue",
|
||||
"blue",
|
||||
"blue violet",
|
||||
"violet",
|
||||
"purple",
|
||||
"lavender",
|
||||
"berry",
|
||||
"magenta",
|
||||
"salmon",
|
||||
"auburn",
|
||||
"dark brown",
|
||||
"brown",
|
||||
"light brown",
|
||||
"blonde",
|
||||
"peach",
|
||||
"warm gray",
|
||||
"cool gray",
|
||||
"olive",
|
||||
]
|
||||
@@ -22,9 +22,15 @@ from typing_extensions import Self
|
||||
import ujson
|
||||
|
||||
from src.core.json_typing import JsonCollation, JsonEntry, JsonLibary, JsonTag
|
||||
from src.core import ts_core
|
||||
from src.core.utils.str import strip_punctuation
|
||||
from src.core.utils.web import strip_web_protocol
|
||||
from src.core.constants import (
|
||||
BACKUP_FOLDER_NAME,
|
||||
COLLAGE_FOLDER_NAME,
|
||||
TEXT_FIELDS,
|
||||
TS_FOLDER_NAME,
|
||||
VERSION,
|
||||
)
|
||||
|
||||
TYPE = ["file", "meta", "alt", "mask"]
|
||||
|
||||
@@ -444,8 +450,8 @@ class Library:
|
||||
path = os.path.normpath(path).rstrip("\\")
|
||||
|
||||
# If '.TagStudio' is included in the path, trim the path up to it.
|
||||
if ts_core.TS_FOLDER_NAME in path:
|
||||
path = path.split(ts_core.TS_FOLDER_NAME)[0]
|
||||
if TS_FOLDER_NAME in path:
|
||||
path = path.split(TS_FOLDER_NAME)[0]
|
||||
|
||||
try:
|
||||
self.clear_internal_vars()
|
||||
@@ -462,12 +468,12 @@ class Library:
|
||||
def verify_ts_folders(self) -> None:
|
||||
"""Verifies/creates folders required by TagStudio."""
|
||||
|
||||
full_ts_path = os.path.normpath(f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}")
|
||||
full_ts_path = os.path.normpath(f"{self.library_dir}/{TS_FOLDER_NAME}")
|
||||
full_backup_path = os.path.normpath(
|
||||
f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}/{ts_core.BACKUP_FOLDER_NAME}"
|
||||
f"{self.library_dir}/{TS_FOLDER_NAME}/{BACKUP_FOLDER_NAME}"
|
||||
)
|
||||
full_collage_path = os.path.normpath(
|
||||
f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}/{ts_core.COLLAGE_FOLDER_NAME}"
|
||||
f"{self.library_dir}/{TS_FOLDER_NAME}/{COLLAGE_FOLDER_NAME}"
|
||||
)
|
||||
|
||||
if not os.path.isdir(full_ts_path):
|
||||
@@ -505,17 +511,13 @@ class Library:
|
||||
path = os.path.normpath(path).rstrip("\\")
|
||||
|
||||
# If '.TagStudio' is included in the path, trim the path up to it.
|
||||
if ts_core.TS_FOLDER_NAME in path:
|
||||
path = path.split(ts_core.TS_FOLDER_NAME)[0]
|
||||
if TS_FOLDER_NAME in path:
|
||||
path = path.split(TS_FOLDER_NAME)[0]
|
||||
|
||||
if os.path.exists(
|
||||
os.path.normpath(f"{path}/{ts_core.TS_FOLDER_NAME}/ts_library.json")
|
||||
):
|
||||
if os.path.exists(os.path.normpath(f"{path}/{TS_FOLDER_NAME}/ts_library.json")):
|
||||
try:
|
||||
with open(
|
||||
os.path.normpath(
|
||||
f"{path}/{ts_core.TS_FOLDER_NAME}/ts_library.json"
|
||||
),
|
||||
os.path.normpath(f"{path}/{TS_FOLDER_NAME}/ts_library.json"),
|
||||
"r",
|
||||
encoding="utf-8",
|
||||
) as file:
|
||||
@@ -724,11 +726,9 @@ class Library:
|
||||
# If the Library is loaded, continue other processes.
|
||||
if return_code == 1:
|
||||
if not os.path.exists(
|
||||
os.path.normpath(f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}")
|
||||
os.path.normpath(f"{self.library_dir}/{TS_FOLDER_NAME}")
|
||||
):
|
||||
os.makedirs(
|
||||
os.path.normpath(f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}")
|
||||
)
|
||||
os.makedirs(os.path.normpath(f"{self.library_dir}/{TS_FOLDER_NAME}"))
|
||||
|
||||
self._map_filenames_to_entry_ids()
|
||||
|
||||
@@ -775,7 +775,7 @@ class Library:
|
||||
"""
|
||||
|
||||
file_to_save: JsonLibary = {
|
||||
"ts-version": ts_core.VERSION,
|
||||
"ts-version": VERSION,
|
||||
"ignored_extensions": [],
|
||||
"tags": [],
|
||||
"collations": [],
|
||||
@@ -813,7 +813,7 @@ class Library:
|
||||
self.verify_ts_folders()
|
||||
|
||||
with open(
|
||||
os.path.normpath(f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}/{filename}"),
|
||||
os.path.normpath(f"{self.library_dir}/{TS_FOLDER_NAME}/{filename}"),
|
||||
"w",
|
||||
encoding="utf-8",
|
||||
) as outfile:
|
||||
@@ -842,7 +842,7 @@ class Library:
|
||||
self.verify_ts_folders()
|
||||
with open(
|
||||
os.path.normpath(
|
||||
f"{self.library_dir}/{ts_core.TS_FOLDER_NAME}/{ts_core.BACKUP_FOLDER_NAME}/{filename}"
|
||||
f"{self.library_dir}/{TS_FOLDER_NAME}/{BACKUP_FOLDER_NAME}/{filename}"
|
||||
),
|
||||
"w",
|
||||
encoding="utf-8",
|
||||
@@ -899,13 +899,13 @@ class Library:
|
||||
# Scans the directory for files, keeping track of:
|
||||
# - Total file count
|
||||
# - Files without library entries
|
||||
# for type in ts_core.TYPES:
|
||||
# for type in TYPES:
|
||||
start_time = time.time()
|
||||
for f in glob.glob(self.library_dir + "/**/*", recursive=True):
|
||||
# p = Path(os.path.normpath(f))
|
||||
if (
|
||||
"$RECYCLE.BIN" not in f
|
||||
and ts_core.TS_FOLDER_NAME not in f
|
||||
and TS_FOLDER_NAME not in f
|
||||
and "tagstudio_thumbs" not in f
|
||||
and not os.path.isdir(f)
|
||||
):
|
||||
@@ -2121,7 +2121,7 @@ class Library:
|
||||
# entry = self.entries[entry_index]
|
||||
entry = self.get_entry(entry_id)
|
||||
field_type = self.get_field_obj(field_id)["type"]
|
||||
if field_type in ts_core.TEXT_FIELDS:
|
||||
if field_type in TEXT_FIELDS:
|
||||
entry.fields.append({int(field_id): ""})
|
||||
elif field_type == "tag_box":
|
||||
entry.fields.append({int(field_id): []})
|
||||
|
||||
@@ -8,143 +8,7 @@ import json
|
||||
import os
|
||||
|
||||
from src.core.library import Entry, Library
|
||||
|
||||
VERSION: str = "9.2.1" # Major.Minor.Patch
|
||||
VERSION_BRANCH: str = "Pre-Release" # 'Alpha', 'Beta', or '' for Full Release
|
||||
|
||||
# The folder & file names where TagStudio keeps its data relative to a library.
|
||||
TS_FOLDER_NAME: str = ".TagStudio"
|
||||
BACKUP_FOLDER_NAME: str = "backups"
|
||||
COLLAGE_FOLDER_NAME: str = "collages"
|
||||
LIBRARY_FILENAME: str = "ts_library.json"
|
||||
|
||||
# TODO: Turn this whitelist into a user-configurable blacklist.
|
||||
IMAGE_TYPES: list[str] = [
|
||||
"png",
|
||||
"jpg",
|
||||
"jpeg",
|
||||
"jpg_large",
|
||||
"jpeg_large",
|
||||
"jfif",
|
||||
"gif",
|
||||
"tif",
|
||||
"tiff",
|
||||
"heic",
|
||||
"heif",
|
||||
"webp",
|
||||
"bmp",
|
||||
"svg",
|
||||
"avif",
|
||||
"apng",
|
||||
"jp2",
|
||||
"j2k",
|
||||
"jpg2",
|
||||
]
|
||||
VIDEO_TYPES: list[str] = [
|
||||
"mp4",
|
||||
"webm",
|
||||
"mov",
|
||||
"hevc",
|
||||
"mkv",
|
||||
"avi",
|
||||
"wmv",
|
||||
"flv",
|
||||
"gifv",
|
||||
"m4p",
|
||||
"m4v",
|
||||
"3gp",
|
||||
]
|
||||
AUDIO_TYPES: list[str] = [
|
||||
"mp3",
|
||||
"mp4",
|
||||
"mpeg4",
|
||||
"m4a",
|
||||
"aac",
|
||||
"wav",
|
||||
"flac",
|
||||
"alac",
|
||||
"wma",
|
||||
"ogg",
|
||||
"aiff",
|
||||
]
|
||||
DOC_TYPES: list[str] = ["txt", "rtf", "md", "doc", "docx", "pdf", "tex", "odt", "pages"]
|
||||
PLAINTEXT_TYPES: list[str] = [
|
||||
"txt",
|
||||
"md",
|
||||
"css",
|
||||
"html",
|
||||
"xml",
|
||||
"json",
|
||||
"js",
|
||||
"ts",
|
||||
"ini",
|
||||
"htm",
|
||||
"csv",
|
||||
"php",
|
||||
"sh",
|
||||
"bat",
|
||||
]
|
||||
SPREADSHEET_TYPES: list[str] = ["csv", "xls", "xlsx", "numbers", "ods"]
|
||||
PRESENTATION_TYPES: list[str] = ["ppt", "pptx", "key", "odp"]
|
||||
ARCHIVE_TYPES: list[str] = ["zip", "rar", "tar", "tar.gz", "tgz", "7z"]
|
||||
PROGRAM_TYPES: list[str] = ["exe", "app"]
|
||||
SHORTCUT_TYPES: list[str] = ["lnk", "desktop", "url"]
|
||||
|
||||
ALL_FILE_TYPES: list[str] = (
|
||||
IMAGE_TYPES
|
||||
+ VIDEO_TYPES
|
||||
+ AUDIO_TYPES
|
||||
+ DOC_TYPES
|
||||
+ SPREADSHEET_TYPES
|
||||
+ PRESENTATION_TYPES
|
||||
+ ARCHIVE_TYPES
|
||||
+ PROGRAM_TYPES
|
||||
+ SHORTCUT_TYPES
|
||||
)
|
||||
|
||||
BOX_FIELDS = ["tag_box", "text_box"]
|
||||
TEXT_FIELDS = ["text_line", "text_box"]
|
||||
DATE_FIELDS = ["datetime"]
|
||||
|
||||
TAG_COLORS = [
|
||||
"",
|
||||
"black",
|
||||
"dark gray",
|
||||
"gray",
|
||||
"light gray",
|
||||
"white",
|
||||
"light pink",
|
||||
"pink",
|
||||
"red",
|
||||
"red orange",
|
||||
"orange",
|
||||
"yellow orange",
|
||||
"yellow",
|
||||
"lime",
|
||||
"light green",
|
||||
"mint",
|
||||
"green",
|
||||
"teal",
|
||||
"cyan",
|
||||
"light blue",
|
||||
"blue",
|
||||
"blue violet",
|
||||
"violet",
|
||||
"purple",
|
||||
"lavender",
|
||||
"berry",
|
||||
"magenta",
|
||||
"salmon",
|
||||
"auburn",
|
||||
"dark brown",
|
||||
"brown",
|
||||
"light brown",
|
||||
"blonde",
|
||||
"peach",
|
||||
"warm gray",
|
||||
"cool gray",
|
||||
"olive",
|
||||
]
|
||||
from src.core.constants import TS_FOLDER_NAME, TEXT_FIELDS
|
||||
|
||||
|
||||
class TagStudioCore:
|
||||
|
||||
@@ -20,7 +20,7 @@ from PySide6.QtWidgets import (
|
||||
|
||||
from src.core.library import Library, Tag
|
||||
from src.core.palette import ColorType, get_tag_color
|
||||
from src.core.ts_core import TAG_COLORS
|
||||
from src.core.constants import TAG_COLORS
|
||||
from src.qt.widgets.panel import PanelWidget, PanelModal
|
||||
from src.qt.widgets.tag import TagWidget
|
||||
from src.qt.modals.tag_search import TagSearchPanel
|
||||
|
||||
@@ -49,9 +49,9 @@ from humanfriendly import format_timespan
|
||||
|
||||
from src.core.enums import SettingItems
|
||||
from src.core.library import ItemType
|
||||
from src.core.ts_core import (
|
||||
from src.core.ts_core import TagStudioCore
|
||||
from src.core.constants import (
|
||||
PLAINTEXT_TYPES,
|
||||
TagStudioCore,
|
||||
TAG_COLORS,
|
||||
DATE_FIELDS,
|
||||
TEXT_FIELDS,
|
||||
|
||||
@@ -24,7 +24,7 @@ from PySide6.QtCore import (
|
||||
)
|
||||
|
||||
from src.core.library import Library
|
||||
from src.core.ts_core import DOC_TYPES, VIDEO_TYPES, IMAGE_TYPES
|
||||
from src.core.constants import DOC_TYPES, VIDEO_TYPES, IMAGE_TYPES
|
||||
|
||||
|
||||
ERROR = f"[ERROR]"
|
||||
|
||||
@@ -23,8 +23,9 @@ from PySide6.QtWidgets import (
|
||||
QCheckBox,
|
||||
)
|
||||
|
||||
|
||||
from src.core.library import ItemType, Library, Entry
|
||||
from src.core.ts_core import AUDIO_TYPES, VIDEO_TYPES, IMAGE_TYPES
|
||||
from src.core.constants import AUDIO_TYPES, VIDEO_TYPES, IMAGE_TYPES
|
||||
from src.qt.flowlayout import FlowWidget
|
||||
from src.qt.helpers.file_opener import FileOpenerHelper
|
||||
from src.qt.widgets.thumb_renderer import ThumbRenderer
|
||||
|
||||
@@ -29,7 +29,7 @@ from humanfriendly import format_size
|
||||
|
||||
from src.core.enums import SettingItems, Theme
|
||||
from src.core.library import Entry, ItemType, Library
|
||||
from src.core.ts_core import VIDEO_TYPES, IMAGE_TYPES
|
||||
from src.core.constants import VIDEO_TYPES, IMAGE_TYPES
|
||||
from src.qt.helpers.file_opener import FileOpenerLabel, FileOpenerHelper, open_file
|
||||
from src.qt.modals.add_field import AddFieldModal
|
||||
from src.qt.widgets.thumb_renderer import ThumbRenderer
|
||||
|
||||
@@ -24,7 +24,7 @@ from PIL import (
|
||||
from PIL.Image import DecompressionBombError
|
||||
from PySide6.QtCore import QObject, Signal, QSize
|
||||
from PySide6.QtGui import QPixmap
|
||||
from src.core.ts_core import PLAINTEXT_TYPES, VIDEO_TYPES, IMAGE_TYPES
|
||||
from src.core.constants import PLAINTEXT_TYPES, VIDEO_TYPES, IMAGE_TYPES
|
||||
|
||||
ImageFile.LOAD_TRUNCATED_IMAGES = True
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
from src.core.library import Tag
|
||||
|
||||
|
||||
class TestTags:
|
||||
def test_construction(self):
|
||||
tag = Tag(
|
||||
id=1,
|
||||
name="Tag Name",
|
||||
shorthand="TN",
|
||||
aliases=["First A", "Second A"],
|
||||
subtags_ids=[2, 3, 4],
|
||||
color="",
|
||||
)
|
||||
assert tag
|
||||
def test_construction():
|
||||
tag = Tag(
|
||||
id=1,
|
||||
name="Tag Name",
|
||||
shorthand="TN",
|
||||
aliases=["First A", "Second A"],
|
||||
subtags_ids=[2, 3, 4],
|
||||
color="",
|
||||
)
|
||||
assert tag
|
||||
|
||||
def test_empty_construction(self):
|
||||
tag = Tag(id=1, name="", shorthand="", aliases=[], subtags_ids=[], color="")
|
||||
assert tag
|
||||
|
||||
def test_empty_construction():
|
||||
tag = Tag(id=1, name="", shorthand="", aliases=[], subtags_ids=[], color="")
|
||||
assert tag
|
||||
|
||||
Reference in New Issue
Block a user