Added expand_inplace to PhotoTemplate.render
This commit is contained in:
@@ -642,7 +642,14 @@ class PhotoInfo:
|
|||||||
otherwise returns False """
|
otherwise returns False """
|
||||||
return self._info["raw_is_original"]
|
return self._info["raw_is_original"]
|
||||||
|
|
||||||
def render_template(self, template_str, none_str="_", path_sep=None):
|
def render_template(
|
||||||
|
self,
|
||||||
|
template_str,
|
||||||
|
none_str="_",
|
||||||
|
path_sep=None,
|
||||||
|
expand_inplace=False,
|
||||||
|
inplace_sep=None,
|
||||||
|
):
|
||||||
"""Renders a template string for PhotoInfo instance using PhotoTemplate
|
"""Renders a template string for PhotoInfo instance using PhotoTemplate
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -650,12 +657,22 @@ class PhotoInfo:
|
|||||||
none_str: a str to use if template field renders to None, default is "_".
|
none_str: a str to use if template field renders to None, default is "_".
|
||||||
path_sep: a single character str to use as path separator when joining
|
path_sep: a single character str to use as path separator when joining
|
||||||
fields like folder_album; if not provided, defaults to os.path.sep
|
fields like folder_album; if not provided, defaults to os.path.sep
|
||||||
|
expand_inplace: expand multi-valued substitutions in-place as a single string
|
||||||
|
instead of returning individual strings
|
||||||
|
inplace_sep: optional string to use as separator between multi-valued keywords
|
||||||
|
with expand_inplace; default is ','
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
([rendered_strings], [unmatched]): tuple of list of rendered strings and list of unmatched template values
|
([rendered_strings], [unmatched]): tuple of list of rendered strings and list of unmatched template values
|
||||||
"""
|
"""
|
||||||
template = PhotoTemplate(self)
|
template = PhotoTemplate(self)
|
||||||
return template.render(template_str, none_str, path_sep)
|
return template.render(
|
||||||
|
template_str,
|
||||||
|
none_str=none_str,
|
||||||
|
path_sep=path_sep,
|
||||||
|
expand_inplace=expand_inplace,
|
||||||
|
inplace_sep=inplace_sep,
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _longitude(self):
|
def _longitude(self):
|
||||||
|
|||||||
@@ -2232,8 +2232,7 @@ class PhotosDB:
|
|||||||
logging.debug(f"Could not find person '{person}' in database")
|
logging.debug(f"Could not find person '{person}' in database")
|
||||||
photos_sets.append(person_set)
|
photos_sets.append(person_set)
|
||||||
|
|
||||||
# sourcery off
|
if from_date or to_date: # sourcery off
|
||||||
if from_date or to_date:
|
|
||||||
dsel = self._dbphotos
|
dsel = self._dbphotos
|
||||||
if from_date:
|
if from_date:
|
||||||
dsel = {
|
dsel = {
|
||||||
|
|||||||
@@ -124,13 +124,24 @@ class PhotoTemplate:
|
|||||||
# gets initialized in get_template_value
|
# gets initialized in get_template_value
|
||||||
self.today = None
|
self.today = None
|
||||||
|
|
||||||
def render(self, template, none_str="_", path_sep=None):
|
def render(
|
||||||
|
self,
|
||||||
|
template,
|
||||||
|
none_str="_",
|
||||||
|
path_sep=None,
|
||||||
|
expand_inplace=False,
|
||||||
|
inplace_sep=None,
|
||||||
|
):
|
||||||
""" Render a filename or directory template
|
""" Render a filename or directory template
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
template: str template
|
template: str template
|
||||||
none_str: str to use default for None values, default is '_'
|
none_str: str to use default for None values, default is '_'
|
||||||
path_sep: optional character to use as path separator, default is os.path.sep
|
path_sep: optional character to use as path separator, default is os.path.sep
|
||||||
|
expand_inplace: expand multi-valued substitutions in-place as a single string
|
||||||
|
instead of returning individual strings
|
||||||
|
inplace_sep: optional string to use as separator between multi-valued keywords
|
||||||
|
with expand_inplace; default is ','
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
([rendered_strings], [unmatched]): tuple of list of rendered strings and list of unmatched template values
|
([rendered_strings], [unmatched]): tuple of list of rendered strings and list of unmatched template values
|
||||||
@@ -141,6 +152,9 @@ class PhotoTemplate:
|
|||||||
elif path_sep is not None and len(path_sep) != 1:
|
elif path_sep is not None and len(path_sep) != 1:
|
||||||
raise ValueError(f"path_sep must be single character: {path_sep}")
|
raise ValueError(f"path_sep must be single character: {path_sep}")
|
||||||
|
|
||||||
|
if inplace_sep is None:
|
||||||
|
inplace_sep = ","
|
||||||
|
|
||||||
# the rendering happens in two phases:
|
# the rendering happens in two phases:
|
||||||
# phase 1: handle all the single-value template substitutions
|
# phase 1: handle all the single-value template substitutions
|
||||||
# results in a single string with all the template fields replaced
|
# results in a single string with all the template fields replaced
|
||||||
@@ -226,13 +240,19 @@ class PhotoTemplate:
|
|||||||
for str_template in rendered_strings:
|
for str_template in rendered_strings:
|
||||||
if regex_multi.search(str_template):
|
if regex_multi.search(str_template):
|
||||||
values = self.get_template_value_multi(field, path_sep)
|
values = self.get_template_value_multi(field, path_sep)
|
||||||
for val in values:
|
if expand_inplace:
|
||||||
|
# instead of returning multiple strings, join values into a single string
|
||||||
|
val = (
|
||||||
|
inplace_sep.join(sorted(values))
|
||||||
|
if values and values[0]
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
def lookup_template_value_multi(lookup_value, default):
|
def lookup_template_value_multi(lookup_value, default):
|
||||||
""" Closure passed to make_subst_function get_func
|
""" Closure passed to make_subst_function get_func
|
||||||
Capture val and field in the closure
|
Capture val and field in the closure
|
||||||
Allows make_subst_function to be re-used w/o modification
|
Allows make_subst_function to be re-used w/o modification
|
||||||
default is not used but required so signature matches get_template_value """
|
default is not used but required so signature matches get_template_value """
|
||||||
if lookup_value == field:
|
if lookup_value == field:
|
||||||
return val
|
return val
|
||||||
else:
|
else:
|
||||||
@@ -242,10 +262,33 @@ class PhotoTemplate:
|
|||||||
self, none_str, get_func=lookup_template_value_multi
|
self, none_str, get_func=lookup_template_value_multi
|
||||||
)
|
)
|
||||||
new_string = regex_multi.sub(subst, str_template)
|
new_string = regex_multi.sub(subst, str_template)
|
||||||
new_strings.add(new_string)
|
|
||||||
|
|
||||||
# update rendered_strings for the next field to process
|
# update rendered_strings for the next field to process
|
||||||
rendered_strings = new_strings
|
rendered_strings = {new_string}
|
||||||
|
else:
|
||||||
|
# create a new template string for each value
|
||||||
|
for val in values:
|
||||||
|
|
||||||
|
def lookup_template_value_multi(lookup_value, default):
|
||||||
|
""" Closure passed to make_subst_function get_func
|
||||||
|
Capture val and field in the closure
|
||||||
|
Allows make_subst_function to be re-used w/o modification
|
||||||
|
default is not used but required so signature matches get_template_value """
|
||||||
|
if lookup_value == field:
|
||||||
|
return val
|
||||||
|
else:
|
||||||
|
raise ValueError(
|
||||||
|
f"Unexpected value: {lookup_value}"
|
||||||
|
)
|
||||||
|
|
||||||
|
subst = make_subst_function(
|
||||||
|
self, none_str, get_func=lookup_template_value_multi
|
||||||
|
)
|
||||||
|
new_string = regex_multi.sub(subst, str_template)
|
||||||
|
new_strings.add(new_string)
|
||||||
|
|
||||||
|
# update rendered_strings for the next field to process
|
||||||
|
rendered_strings = new_strings
|
||||||
|
|
||||||
# find any {fields} that weren't replaced
|
# find any {fields} that weren't replaced
|
||||||
unmatched = []
|
unmatched = []
|
||||||
|
|||||||
@@ -168,8 +168,8 @@ def test_missing():
|
|||||||
photos = photosdb.photos(uuid=["od0fmC7NQx+ayVr+%i06XA"])
|
photos = photosdb.photos(uuid=["od0fmC7NQx+ayVr+%i06XA"])
|
||||||
assert len(photos) == 1
|
assert len(photos) == 1
|
||||||
p = photos[0]
|
p = photos[0]
|
||||||
assert p.path == None
|
assert p.path is None
|
||||||
assert p.ismissing == True
|
assert p.ismissing
|
||||||
|
|
||||||
|
|
||||||
def test_favorite():
|
def test_favorite():
|
||||||
|
|||||||
@@ -458,3 +458,48 @@ def test_subst_strftime():
|
|||||||
|
|
||||||
rendered, unmatched = photo.render_template("{created.strftime}")
|
rendered, unmatched = photo.render_template("{created.strftime}")
|
||||||
assert rendered[0] == "_"
|
assert rendered[0] == "_"
|
||||||
|
|
||||||
|
|
||||||
|
def test_subst_expand_inplace_1():
|
||||||
|
""" Test that substitutions are correct when expand_inplace=True """
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
|
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB_15_1)
|
||||||
|
# one album, one keyword, two persons
|
||||||
|
photo = photosdb.photos(uuid=[UUID_DICT["1_1_2"]])[0]
|
||||||
|
|
||||||
|
template = "{person}"
|
||||||
|
expected = ["Katie,Suzy"]
|
||||||
|
rendered, unknown = photo.render_template(template, expand_inplace=True)
|
||||||
|
assert sorted(rendered) == sorted(expected)
|
||||||
|
|
||||||
|
|
||||||
|
def test_subst_expand_inplace_2():
|
||||||
|
""" Test that substitutions are correct when expand_inplace=True """
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
|
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB_15_1)
|
||||||
|
# one album, one keyword, two persons
|
||||||
|
photo = photosdb.photos(uuid=[UUID_DICT["1_1_2"]])[0]
|
||||||
|
|
||||||
|
template = "{person}-{keyword}"
|
||||||
|
expected = ["Katie,Suzy-Kids"]
|
||||||
|
rendered, unknown = photo.render_template(template, expand_inplace=True)
|
||||||
|
assert sorted(rendered) == sorted(expected)
|
||||||
|
|
||||||
|
|
||||||
|
def test_subst_expand_inplace_3():
|
||||||
|
""" Test that substitutions are correct when expand_inplace=True and inplace_sep specified"""
|
||||||
|
import osxphotos
|
||||||
|
|
||||||
|
photosdb = osxphotos.PhotosDB(dbfile=PHOTOS_DB_15_1)
|
||||||
|
# one album, one keyword, two persons
|
||||||
|
photo = photosdb.photos(uuid=[UUID_DICT["1_1_2"]])[0]
|
||||||
|
|
||||||
|
template = "{person}-{keyword}"
|
||||||
|
expected = ["Katie; Suzy-Kids"]
|
||||||
|
rendered, unknown = photo.render_template(
|
||||||
|
template, expand_inplace=True, inplace_sep="; "
|
||||||
|
)
|
||||||
|
assert sorted(rendered) == sorted(expected)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user