"""Ready-made connections ("Pictures sorting" and so on): a list of patterns plus a few settings, installed between an input and an output you pick. Built-ins live here. Your own are TOML files in ~/.config/SaraSuite/SaraSort/Templates/, same keys as SortTemplate: Name = "3D prints" Description = "Models and slicer files." Icon = "VIEW_IN_AR" Patterns = ["*.stl", "*.3mf"] NewFileName = "~^" # optional; OutputDirConf.toml's default otherwise SuggestedOutput = "~/Prints" # optional [OutputSettings] # optional, top-level keys of the output's config DeleteOrg = 1 [InputSettings] # optional, top-level keys of the input's config SortDirs = 0 """ import re from dataclasses import asdict, dataclass, field from pathlib import Path from typing import Optional import tomlkit from Core import UserTemplatesDir @dataclass class SortTemplate: Name: str Description: str = "" Icon: str = "FOLDER_COPY_OUTLINED" Patterns: list[str] = field(default_factory=list) NewFileName: str = "" # empty: OutputDirConf.toml's default SuggestedOutput: str = "" OutputSettings: dict = field(default_factory=dict) InputSettings: dict = field(default_factory=dict) FilePath: Optional[Path] = None # set for your own templates @property def BuiltIn(self) -> bool: return self.FilePath is None BuiltInTemplates = [ SortTemplate( "Pictures", "Photos, screenshots and other images.", "IMAGE_OUTLINED", ["*.jp*g", "*.png", "*.gif", "*.webp", "*.bmp", "*.svg", "*.heic", "*.tif*", "*.avif"], SuggestedOutput="~/Pictures", ), SortTemplate( "Videos", "Video files.", "MOVIE_OUTLINED", ["*.mp4", "*.mkv", "*.webm", "*.mov", "*.avi", "*.m4v"], SuggestedOutput="~/Videos", ), SortTemplate( "Music", "Audio files.", "MUSIC_NOTE_OUTLINED", ["*.mp3", "*.flac", "*.ogg", "*.opus", "*.wav", "*.m4a", "*.aac"], SuggestedOutput="~/Music", ), SortTemplate( "Documents", "PDFs, office files and text.", "DESCRIPTION_OUTLINED", ["*.pdf", "*.doc*", "*.odt", "*.rtf", "*.txt", "*.md", "*.xls*", "*.ods", "*.csv", "*.ppt*", "*.odp", "*.epub"], SuggestedOutput="~/Documents", ), SortTemplate( "Archives (unpacked)", "Archives, unpacked in the output and the archive removed.", "FOLDER_ZIP_OUTLINED", ["*.zip", "*.tar", "*.tar.gz", "*.tgz", "*.tar.xz", "*.tar.bz2"], OutputSettings={"Unzip": ["zip", "tar", "tar.gz", "tgz", "tar.xz", "tar.bz2"]}, ), SortTemplate( "Git repositories", "Folders (e.g. cloned repos) are zipped by the input, moved and unzipped in the output.", "ACCOUNT_TREE_OUTLINED", ["*.zip"], SuggestedOutput="~/Applications", OutputSettings={"Unzip": ["zip"]}, InputSettings={"SortDirs": 1}, ), SortTemplate( "Logs", "Log files, renamed with today's date in front.", "RECEIPT_LONG_OUTLINED", ["*.log", "*.log.*"], NewFileName="[y].[m].[d]_~^", ), SortTemplate( "3D printing", "Models, OpenSCAD files and slicer output.", "VIEW_IN_AR_OUTLINED", ["*.stl", "*.3mf", "*.scad", "*.step", "*.stp", "*.obj", "*.gcode", "*.bgcode"], ), SortTemplate( "Installers", "AppImages, packages and Flatpak refs.", "INSTALL_DESKTOP_OUTLINED", ["*.AppImage", "*.rpm", "*.deb", "*.flatpak", "*.flatpakref"], ), SortTemplate( "Backup copy", "Copies everything instead of moving it (DeleteOrg = 0).", "CONTENT_COPY_OUTLINED", ["*"], OutputSettings={"DeleteOrg": 0}, ), ] def UserTemplates() -> list[SortTemplate]: if not UserTemplatesDir.is_dir(): return [] Found = [] Known = set(SortTemplate.__dataclass_fields__) - {"FilePath"} for FilePath in sorted(UserTemplatesDir.glob("*.toml")): try: Data = tomlkit.parse(FilePath.read_text()).unwrap() except (OSError, tomlkit.exceptions.TOMLKitError): continue Values = {Key: Value for Key, Value in Data.items() if Key in Known} Values.setdefault("Name", FilePath.stem) Found.append(SortTemplate(**Values, FilePath=FilePath)) return Found def AllTemplates() -> list[SortTemplate]: return BuiltInTemplates + UserTemplates() def SaveUserTemplate(Template: SortTemplate) -> Path: UserTemplatesDir.mkdir(parents=True, exist_ok=True) FileName = re.sub(r"[^A-Za-z0-9]+", "", Template.Name.title()) or "Template" FilePath = UserTemplatesDir / f"{FileName}.toml" Data = {Key: Value for Key, Value in asdict(Template).items() if Key != "FilePath" and Value not in ("", {}, [])} Data["Patterns"] = Template.Patterns FilePath.write_text(tomlkit.dumps(Data)) return FilePath def DeleteUserTemplate(Template: SortTemplate) -> None: if Template.FilePath: Template.FilePath.unlink(missing_ok=True)