diff --git a/organize/walker.py b/organize/walker.py index f217be04..7afcbb7e 100644 --- a/organize/walker.py +++ b/organize/walker.py @@ -3,6 +3,7 @@ from pathlib import Path from typing import Iterable, Iterator, List, Literal, NamedTuple, Optional, Set +from natsort import os_sorted from pydantic import Field from pydantic.dataclasses import dataclass @@ -57,7 +58,10 @@ def scandir(top: str, collectfiles: bool = True) -> ScandirResult: result.dirs.append(entry) elif collectfiles: result.nondirs.append(entry) - return result + return ScandirResult( + dirs=os_sorted(result.dirs, key=lambda x: x.name), + nondirs=os_sorted(result.nondirs, key=lambda x: x.name), + ) class DirActions(NamedTuple): diff --git a/poetry.lock b/poetry.lock index 5fedfda2..3b898363 100644 --- a/poetry.lock +++ b/poetry.lock @@ -830,6 +830,21 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "natsort" +version = "8.4.0" +description = "Simple yet flexible natural sorting in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c"}, + {file = "natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581"}, +] + +[package.extras] +fast = ["fastnumbers (>=2.0.0)"] +icu = ["PyICU (>=1.0.0)"] + [[package]] name = "packaging" version = "23.2" @@ -1553,4 +1568,4 @@ docs = ["markupsafe", "mkdocs", "mkdocs-autorefs", "mkdocs-include-markdown-plug [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "0a1ad3d51b096c4b73f270ccd43c16f6abbeb204dce72c1ac09711f471fc197a" +content-hash = "575390204771d58aa66010132e4656b318d58861c8d0fa96ce2360ae96f5f0c1" diff --git a/pyproject.toml b/pyproject.toml index 22c48245..fd4aef82 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ docx2txt = "^0.8" ExifRead = "2.3.2" # Pinned: https://github.com/tfeldmann/organize/issues/267 Jinja2 = "^3.1.2" macos-tags = { version = "^1.5.1", markers = "sys_platform == 'darwin'" } +natsort = "^8.4.0" pdfminer-six = "^20231228" platformdirs = "^4.0.0" pydantic = "^2.3.0" diff --git a/tests/core/test_filter_mode.py b/tests/core/test_filter_mode.py index f5ceaba8..8721477d 100644 --- a/tests/core/test_filter_mode.py +++ b/tests/core/test_filter_mode.py @@ -31,7 +31,7 @@ def test_filter_mode(fs, testoutput, filter_mode, expected_msgs): @pytest.mark.parametrize( "filter_mode, expected_msgs", ( - ("any", ["foo", "baz", "x"]), + ("any", ["baz", "foo", "x"]), ("all", ["x"]), ("none", []), ), diff --git a/tests/core/test_location.py b/tests/core/test_location.py index 8e46631a..8dda850a 100644 --- a/tests/core/test_location.py +++ b/tests/core/test_location.py @@ -49,7 +49,7 @@ def test_multiple_pathes(fs, testoutput): - echo: '{path.name}' """ ).execute(simulate=False, output=testoutput) - assert testoutput.messages == ["foo.txt", "bar.txt"] * 2 + assert testoutput.messages == ["bar.txt", "foo.txt"] * 2 def test_multiple_pathes_single_location(fs, testoutput): @@ -66,7 +66,7 @@ def test_multiple_pathes_single_location(fs, testoutput): - echo: '{path.name}' """ ).execute(simulate=False, output=testoutput) - assert testoutput.messages == ["foo.txt", "bar.txt"] * 2 + assert testoutput.messages == ["bar.txt", "foo.txt", "bar.txt", "foo.txt"] def test_multiple_dirs(fs, testoutput): diff --git a/tests/core/test_walker.py b/tests/core/test_walker.py index 1011b6c9..330bc90c 100644 --- a/tests/core/test_walker.py +++ b/tests/core/test_walker.py @@ -147,3 +147,28 @@ def test_exclude_dirs(fs): "test", ) assert len(list(Walker(exclude_dirs=["subC"]).files("/test"))) == 4 + + +def test_order(fs): + make_files( + { + "2024": {"004": "", "001": "", "003": "", "002": ""}, + "1989": {"D": "", "C": "", "A": "", "B": ""}, + "2000": {"B": {"2": "", "1": ""}, "A": {"1": "", "2": ""}}, + }, + "test", + ) + assert list(Walker().files("/test")) == [ + Path("/test/1989/A"), + Path("/test/1989/B"), + Path("/test/1989/C"), + Path("/test/1989/D"), + Path("/test/2000/A/1"), + Path("/test/2000/A/2"), + Path("/test/2000/B/1"), + Path("/test/2000/B/2"), + Path("/test/2024/001"), + Path("/test/2024/002"), + Path("/test/2024/003"), + Path("/test/2024/004"), + ] diff --git a/tests/filters/test_extension.py b/tests/filters/test_extension.py index 83edd47f..0487523e 100644 --- a/tests/filters/test_extension.py +++ b/tests/filters/test_extension.py @@ -51,7 +51,7 @@ def test_filename_move(fs, testoutput): """ Config.from_string(config).execute(simulate=False, output=testoutput) assert testoutput.messages == [ - "Found JPG file: test", "Found JPG file: asd", "Found JPG file: camel", + "Found JPG file: test", ] diff --git a/tests/filters/test_python.py b/tests/filters/test_python.py index de80ca5e..232241c8 100644 --- a/tests/filters/test_python.py +++ b/tests/filters/test_python.py @@ -70,9 +70,9 @@ def test_python_dict(fs, testoutput): """ Config.from_string(config).execute(simulate=False, output=testoutput) assert testoutput.messages == [ - "100 foo", "200 bar", "300 baz", + "100 foo", ] diff --git a/tests/test_api.py b/tests/test_api.py index 3e253877..6f4f9da7 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -21,4 +21,4 @@ def test_api(fs, testoutput): ] ) config.execute(simulate=False, output=testoutput) - assert testoutput.messages == ["FOOFOO", "BAR", "BAZ"] + assert testoutput.messages == ["BAR", "BAZ", "FOOFOO"]