refactored template code to fix #213
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,7 +7,7 @@
|
||||
<key>hostuuid</key>
|
||||
<string>9575E48B-8D5F-5654-ABAC-4431B1167324</string>
|
||||
<key>pid</key>
|
||||
<integer>2964</integer>
|
||||
<integer>40469</integer>
|
||||
<key>processname</key>
|
||||
<string>photolibraryd</string>
|
||||
<key>uid</key>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,24 +3,24 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BackgroundHighlightCollection</key>
|
||||
<date>2020-06-24T04:02:13Z</date>
|
||||
<date>2020-10-17T23:45:25Z</date>
|
||||
<key>BackgroundHighlightEnrichment</key>
|
||||
<date>2020-06-24T04:02:12Z</date>
|
||||
<date>2020-10-17T23:45:25Z</date>
|
||||
<key>BackgroundJobAssetRevGeocode</key>
|
||||
<date>2020-06-24T04:02:13Z</date>
|
||||
<date>2020-10-17T23:45:25Z</date>
|
||||
<key>BackgroundJobSearch</key>
|
||||
<date>2020-06-24T04:02:13Z</date>
|
||||
<date>2020-10-17T23:45:25Z</date>
|
||||
<key>BackgroundPeopleSuggestion</key>
|
||||
<date>2020-06-24T04:02:12Z</date>
|
||||
<date>2020-10-17T23:45:25Z</date>
|
||||
<key>BackgroundUserBehaviorProcessor</key>
|
||||
<date>2020-06-24T04:02:13Z</date>
|
||||
<date>2020-10-17T23:45:25Z</date>
|
||||
<key>PhotoAnalysisGraphLastBackgroundGraphConsistencyUpdateJobDateKey</key>
|
||||
<date>2020-05-30T02:16:06Z</date>
|
||||
<date>2020-10-17T23:45:33Z</date>
|
||||
<key>PhotoAnalysisGraphLastBackgroundGraphRebuildJobDate</key>
|
||||
<date>2020-05-29T04:31:37Z</date>
|
||||
<date>2020-10-17T23:45:24Z</date>
|
||||
<key>PhotoAnalysisGraphLastBackgroundMemoryGenerationJobDate</key>
|
||||
<date>2020-06-24T04:02:13Z</date>
|
||||
<date>2020-10-17T23:45:26Z</date>
|
||||
<key>SiriPortraitDonation</key>
|
||||
<date>2020-06-24T04:02:13Z</date>
|
||||
<date>2020-10-17T23:45:25Z</date>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,7 +3,7 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NumberOfFacesProcessedOnLastRun</key>
|
||||
<integer>7</integer>
|
||||
<integer>11</integer>
|
||||
<key>ProcessedInQuiescentState</key>
|
||||
<true/>
|
||||
<key>SuggestedMeIdentifier</key>
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>FaceIDModelLastGenerationKey</key>
|
||||
<date>2020-05-29T03:44:04Z</date>
|
||||
<date>2020-10-17T23:45:32Z</date>
|
||||
<key>LastContactClassificationKey</key>
|
||||
<date>2020-05-29T04:31:40Z</date>
|
||||
<date>2020-10-17T23:45:54Z</date>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -24,6 +24,7 @@ KEYWORDS = [
|
||||
"St. James's Park",
|
||||
"UK",
|
||||
"United Kingdom",
|
||||
"foo/bar",
|
||||
]
|
||||
# Photos 5 includes blank person for detected face
|
||||
PERSONS = ["Katie", "Suzy", "Maria", _UNKNOWN_PERSON]
|
||||
@@ -47,6 +48,7 @@ KEYWORDS_DICT = {
|
||||
"St. James's Park": 1,
|
||||
"UK": 1,
|
||||
"United Kingdom": 1,
|
||||
"foo/bar": 1,
|
||||
}
|
||||
PERSONS_DICT = {"Katie": 3, "Suzy": 2, "Maria": 2, _UNKNOWN_PERSON: 1}
|
||||
ALBUM_DICT = {
|
||||
|
||||
@@ -229,8 +229,23 @@ CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES_PATHSEP = [
|
||||
"2019-10:11 Paris Clermont/IMG_4547.jpg",
|
||||
]
|
||||
|
||||
|
||||
CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES_KEYWORD_PATHSEP = [
|
||||
"foo:bar/foo:bar_IMG_3092.heic"
|
||||
]
|
||||
|
||||
CLI_EXPORTED_FILENAME_TEMPLATE_LONG_DESCRIPTION = [
|
||||
"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. "
|
||||
"Aenean commodo ligula eget dolor. Aenean massa. "
|
||||
"Cum sociis natoque penatibus et magnis dis parturient montes, "
|
||||
"nascetur ridiculus mus. Donec quam felis, ultricies nec, "
|
||||
"pellentesque eu, pretium q.tif"
|
||||
]
|
||||
|
||||
CLI_EXPORT_UUID = "D79B8D77-BFFC-460B-9312-034F2877D35B"
|
||||
CLI_EXPORT_UUID_STATUE = "3DD2C897-F19E-4CA6-8C22-B027D5A71907"
|
||||
CLI_EXPORT_UUID_KEYWORD_PATHSEP = "7783E8E6-9CAC-40F3-BE22-81FB7051C266"
|
||||
CLI_EXPORT_UUID_LONG_DESCRIPTION = "8846E3E6-8AC8-4857-8448-E3D025784410"
|
||||
|
||||
CLI_EXPORT_UUID_FILENAME = "Pumkins2.jpg"
|
||||
|
||||
@@ -568,10 +583,7 @@ def test_query_uuid_from_file_1():
|
||||
|
||||
# build list of uuids we got from the output JSON
|
||||
json_got = json.loads(result.output)
|
||||
uuid_got = []
|
||||
for photo in json_got:
|
||||
uuid_got.append(photo["uuid"])
|
||||
|
||||
uuid_got = [photo["uuid"] for photo in json_got]
|
||||
assert sorted(UUID_EXPECTED_FROM_FILE) == sorted(uuid_got)
|
||||
|
||||
|
||||
@@ -601,10 +613,7 @@ def test_query_uuid_from_file_2():
|
||||
|
||||
# build list of uuids we got from the output JSON
|
||||
json_got = json.loads(result.output)
|
||||
uuid_got = []
|
||||
for photo in json_got:
|
||||
uuid_got.append(photo["uuid"])
|
||||
|
||||
uuid_got = [photo["uuid"] for photo in json_got]
|
||||
uuid_expected = UUID_EXPECTED_FROM_FILE.copy()
|
||||
uuid_expected.append(UUID_NOT_FROM_FILE)
|
||||
assert sorted(uuid_expected) == sorted(uuid_got)
|
||||
@@ -1965,13 +1974,12 @@ def test_export_filename_template_2():
|
||||
assert sorted(files) == sorted(CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES2)
|
||||
|
||||
|
||||
def test_export_filename_template_pathsep_in_name():
|
||||
def test_export_filename_template_pathsep_in_name_1():
|
||||
""" export photos using filename template with folder_album and "/" in album name """
|
||||
import locale
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import osxphotos
|
||||
from osxphotos.__main__ import export
|
||||
|
||||
locale.setlocale(locale.LC_ALL, "en_US")
|
||||
@@ -1998,6 +2006,71 @@ def test_export_filename_template_pathsep_in_name():
|
||||
assert pathlib.Path(fname).is_file()
|
||||
|
||||
|
||||
def test_export_filename_template_pathsep_in_name_2():
|
||||
""" export photos using filename template with keyword and "/" in keyword """
|
||||
import locale
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
from osxphotos.__main__ import export
|
||||
|
||||
locale.setlocale(locale.LC_ALL, "en_US")
|
||||
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
# pylint: disable=not-context-manager
|
||||
with runner.isolated_filesystem():
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
os.path.join(cwd, PHOTOS_DB_15_6),
|
||||
".",
|
||||
"-V",
|
||||
"--directory",
|
||||
"{keyword}",
|
||||
"--filename",
|
||||
"{keyword}_{original_name}",
|
||||
"--uuid",
|
||||
CLI_EXPORT_UUID_KEYWORD_PATHSEP,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
for fname in CLI_EXPORTED_FILENAME_TEMPLATE_FILENAMES_KEYWORD_PATHSEP:
|
||||
assert pathlib.Path(fname).is_file()
|
||||
|
||||
|
||||
def test_export_filename_template_long_description():
|
||||
""" export photos using filename template with description that exceeds max length """
|
||||
import locale
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import osxphotos
|
||||
from osxphotos.__main__ import export
|
||||
|
||||
locale.setlocale(locale.LC_ALL, "en_US")
|
||||
|
||||
runner = CliRunner()
|
||||
cwd = os.getcwd()
|
||||
# pylint: disable=not-context-manager
|
||||
with runner.isolated_filesystem():
|
||||
result = runner.invoke(
|
||||
export,
|
||||
[
|
||||
os.path.join(cwd, PHOTOS_DB_15_6),
|
||||
".",
|
||||
"-V",
|
||||
"--filename",
|
||||
"{descr}",
|
||||
"--uuid",
|
||||
CLI_EXPORT_UUID_LONG_DESCRIPTION,
|
||||
],
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
for fname in CLI_EXPORTED_FILENAME_TEMPLATE_LONG_DESCRIPTION:
|
||||
assert pathlib.Path(fname).is_file()
|
||||
|
||||
|
||||
def test_export_filename_template_3():
|
||||
""" test --filename with invalid template """
|
||||
import glob
|
||||
@@ -2489,9 +2562,8 @@ def test_export_sidecar_keyword_template():
|
||||
"EXIF:ModifyDate": "2020:04:11 12:34:16"}]"""
|
||||
)[0]
|
||||
|
||||
json_file = open("Pumkins2.jpg.json", "r")
|
||||
json_got = json.load(json_file)[0]
|
||||
json_file.close()
|
||||
with open("Pumkins2.jpg.json", "r") as json_file:
|
||||
json_got = json.load(json_file)[0]
|
||||
|
||||
# some gymnastics to account for different sort order in different pythons
|
||||
for k, v in json_got.items():
|
||||
|
||||
117
tests/test_path_utils.py
Normal file
117
tests/test_path_utils.py
Normal file
@@ -0,0 +1,117 @@
|
||||
""" Test path_utils.py """
|
||||
|
||||
def test_sanitize_filename():
|
||||
from osxphotos.path_utils import sanitize_filename
|
||||
from osxphotos._constants import MAX_FILENAME_LEN
|
||||
|
||||
# basic sanitize
|
||||
filenames = {
|
||||
"Foobar.txt": "Foobar.txt",
|
||||
"Foo:bar.txt": "Foo:bar.txt",
|
||||
"Foo/bar.txt": "Foo:bar.txt",
|
||||
"Foo//.txt": "Foo::.txt",
|
||||
}
|
||||
for filename, sanitized in filenames.items():
|
||||
filename = sanitize_filename(filename)
|
||||
assert filename == sanitized
|
||||
|
||||
# sanitize with replacement
|
||||
filenames = {
|
||||
"Foobar.txt": "Foobar.txt",
|
||||
"Foo:bar.txt": "Foo:bar.txt",
|
||||
"Foo/bar.txt": "Foo_bar.txt",
|
||||
"Foo//.txt": "Foo__.txt",
|
||||
}
|
||||
for filename, sanitized in filenames.items():
|
||||
filename = sanitize_filename(filename, replacement="_")
|
||||
assert filename == sanitized
|
||||
|
||||
# filename too long
|
||||
filename = "foo" + "x" * 512
|
||||
new_filename = sanitize_filename(filename)
|
||||
assert len(new_filename) == MAX_FILENAME_LEN
|
||||
assert new_filename == "foo" + "x" * 252
|
||||
|
||||
# filename too long with extension
|
||||
filename = "x" * 512 + ".jpeg"
|
||||
new_filename = sanitize_filename(filename)
|
||||
assert len(new_filename) == MAX_FILENAME_LEN
|
||||
assert new_filename == "x" * 250 + ".jpeg"
|
||||
|
||||
# more than one extension
|
||||
filename = "foo.bar" + "x" * 255 + ".foo.bar.jpeg"
|
||||
new_filename = sanitize_filename(filename)
|
||||
assert len(new_filename) == MAX_FILENAME_LEN
|
||||
assert new_filename == "foo.bar" + "x" * 243 + ".jpeg"
|
||||
|
||||
# shorter than drop count
|
||||
filename = "foo." + "x" * 256
|
||||
new_filename = sanitize_filename(filename)
|
||||
assert len(new_filename) == MAX_FILENAME_LEN
|
||||
assert new_filename == "foo." + "x" * 251
|
||||
|
||||
|
||||
def test_sanitize_dirname():
|
||||
from osxphotos.path_utils import sanitize_dirname
|
||||
from osxphotos._constants import MAX_DIRNAME_LEN
|
||||
|
||||
# basic sanitize
|
||||
dirnames = {
|
||||
"Foobar": "Foobar",
|
||||
"Foo:bar": "Foo:bar",
|
||||
"Foo/bar": "Foo:bar",
|
||||
"Foo//": "Foo::",
|
||||
}
|
||||
for dirname, sanitized in dirnames.items():
|
||||
dirname = sanitize_dirname(dirname)
|
||||
assert dirname == sanitized
|
||||
|
||||
# sanitize with replacement
|
||||
dirnames = {
|
||||
"Foobar": "Foobar",
|
||||
"Foo:bar": "Foo:bar",
|
||||
"Foo/bar": "Foo_bar",
|
||||
"Foo//": "Foo__",
|
||||
}
|
||||
for dirname, sanitized in dirnames.items():
|
||||
dirname = sanitize_dirname(dirname, replacement="_")
|
||||
assert dirname == sanitized
|
||||
|
||||
# dirname too long
|
||||
dirname = "foo" + "x" * 512 + "bar"
|
||||
new_dirname = sanitize_dirname(dirname)
|
||||
assert len(new_dirname) == MAX_DIRNAME_LEN
|
||||
assert new_dirname == "foo" + "x" * 252
|
||||
|
||||
def test_sanitize_pathpart():
|
||||
from osxphotos.path_utils import sanitize_pathpart
|
||||
from osxphotos._constants import MAX_DIRNAME_LEN
|
||||
|
||||
# basic sanitize
|
||||
dirnames = {
|
||||
"Foobar": "Foobar",
|
||||
"Foo:bar": "Foo:bar",
|
||||
"Foo/bar": "Foo:bar",
|
||||
"Foo//": "Foo::",
|
||||
}
|
||||
for dirname, sanitized in dirnames.items():
|
||||
dirname = sanitize_pathpart(dirname)
|
||||
assert dirname == sanitized
|
||||
|
||||
# sanitize with replacement
|
||||
dirnames = {
|
||||
"Foobar": "Foobar",
|
||||
"Foo:bar": "Foo:bar",
|
||||
"Foo/bar": "Foo_bar",
|
||||
"Foo//": "Foo__",
|
||||
}
|
||||
for dirname, sanitized in dirnames.items():
|
||||
dirname = sanitize_pathpart(dirname, replacement="_")
|
||||
assert dirname == sanitized
|
||||
|
||||
# dirname too long
|
||||
dirname = "foo" + "x" * 512 + "bar"
|
||||
new_dirname = sanitize_pathpart(dirname)
|
||||
assert len(new_dirname) == MAX_DIRNAME_LEN
|
||||
assert new_dirname == "foo" + "x" * 252
|
||||
|
||||
Reference in New Issue
Block a user