Added slice, sslice filters

This commit is contained in:
Rhet Turnbull
2022-05-28 22:07:31 -07:00
parent 6f1e81b038
commit 9e9266ec9c
3 changed files with 66 additions and 0 deletions

View File

@@ -55,6 +55,8 @@ Valid filters are:
- `append(x)`: Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].
- `prepend(x)`: Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].
- `remove(x)`: Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].
- `slice(start:stop:step)`: Slice list using same semantics as Python's list slicing, e.g. slice(1:3): ['a', 'b', 'c', 'd'] => ['b', 'c']; slice(1:4:2): ['a', 'b', 'c', 'd'] => ['b', 'd']; slice(1:): ['a', 'b', 'c', 'd'] => ['b', 'c', 'd']; slice(:-1): ['a', 'b', 'c', 'd'] => ['a', 'b', 'c']; slice(::-1): ['a', 'b', 'c', 'd'] => ['d', 'c', 'b', 'a']. See also sslice().
- `sslice(start:stop:step)`: [s(tring) slice] Slice values in a list using same semantics as Python's string slicing, e.g. sslice(1:3):'abcd => 'bc'; sslice(1:4:2): 'abcd' => 'bd', etc. See also slice().
e.g. if Photo keywords are `["FOO","bar"]`:

View File

@@ -260,6 +260,12 @@ FILTER_VALUES = {
"append(x)": "Append x to list of values, e.g. append(d): ['a', 'b', 'c'] => ['a', 'b', 'c', 'd'].",
"prepend(x)": "Prepend x to list of values, e.g. prepend(d): ['a', 'b', 'c'] => ['d', 'a', 'b', 'c'].",
"remove(x)": "Remove x from list of values, e.g. remove(b): ['a', 'b', 'c'] => ['a', 'c'].",
"slice(start:stop:step)": "Slice list using same semantics as Python's list slicing, "
+ "e.g. slice(1:3): ['a', 'b', 'c', 'd'] => ['b', 'c']; slice(1:4:2): ['a', 'b', 'c', 'd'] => ['b', 'd']; "
+ "slice(1:): ['a', 'b', 'c', 'd'] => ['b', 'c', 'd']; slice(:-1): ['a', 'b', 'c', 'd'] => ['a', 'b', 'c']; "
+ "slice(::-1): ['a', 'b', 'c', 'd'] => ['d', 'c', 'b', 'a']. See also sslice().",
"sslice(start:stop:step)": "[s(tring) slice] Slice values in a list using same semantics as Python's string slicing, "
+ "e.g. sslice(1:3):'abcd => 'bc'; sslice(1:4:2): 'abcd' => 'bd', etc. See also slice().",
}
# Just the substitutions without the braces
@@ -1166,6 +1172,8 @@ class PhotoTemplate:
"append",
"prepend",
"remove",
"slice",
"sslice",
] and (args is None or not len(args)):
raise SyntaxError(f"{filter_} requires arguments")
@@ -1247,6 +1255,13 @@ class PhotoTemplate:
elif filter_ == "remove":
# remove value from list
value = [v for v in values if v != args]
elif filter_ == "slice":
# slice list of values
value = values[create_slice(args)]
elif filter_ == "sslice":
# slice each value in a list
slice_ = create_slice(args)
value = [v[slice_] for v in values]
elif filter_.startswith("function:"):
value = self.get_template_value_filter_function(filter_, args, values)
else:
@@ -1671,3 +1686,25 @@ def _get_detected_text(photo, confidence=TEXT_DETECTION_CONFIDENCE_THRESHOLD):
# so the first time this gets called is slow but repeated accesses are fast
detected_text = photo._detected_text()
return [text for text, conf in detected_text if conf >= confidence]
def create_slice(args):
"""Create a slice object from a string of args in form "start:end:step" """
slice_args = args.split(":")
if len(slice_args) == 1:
start = int(slice_args[0] or 0)
end = None
step = None
elif len(slice_args) == 2:
start, end = slice_args
start = int(start) if start != "" else None
end = int(end) if end != "" else None
step = None
elif len(slice_args) == 3:
start, end, step = slice_args
start = int(start) if start != "" else None
end = int(end) if end != "" else None
step = int(step) if step != "" else None
else:
raise SyntaxError(f"Invalid slice: {args}")
return slice(start, end, step)

View File

@@ -227,6 +227,15 @@ TEMPLATE_VALUES = {
"{descr|chop(6)|autosplit|append(Restaurant)|join( )}": "Jack Rose Dining Restaurant",
"{descr|chomp(4)|autosplit|prepend(Mack)|join( )}": "Mack Rose Dining Saloon",
"{descr|autosplit|remove(Rose)|join( )}": "Jack Dining Saloon",
"{descr|sslice(0:3)}": "Jac",
"{descr|sslice(5:11)}": "Rose D",
"{descr|sslice(:-6)}": "Jack Rose Dining ",
"{descr|sslice(::2)}": "Jc oeDnn aon",
"{descr|autosplit|slice(1:3)|join()}": "RoseDining",
"{descr|autosplit|slice(2:)|join()}": "DiningSaloon",
"{descr|autosplit|slice(:2)|join()}": "JackRose",
"{descr|autosplit|slice(:-1)|join()}": "JackRoseDining",
"{descr|autosplit|slice(::2)|join()}": "JackDining",
}
@@ -1297,3 +1306,21 @@ def test_moment(photosdb):
for template, value in UUID_MOMENT[uuid]["templates"].items():
rendered, _ = photo.render_template(template)
assert rendered == value
def test_bad_slice(photosdb):
"""Test invalid {|slice} filter"""
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
# bad field raises SyntaxError
# bad function raises ValueError
with pytest.raises((SyntaxError, ValueError)):
rendered, _ = photo.render_template("{photo.original_filename|slice(1:2:3:4)}")
def test_bad_sslice(photosdb):
"""Test invalid {|sslice} filter"""
photo = photosdb.get_photo(UUID_MULTI_KEYWORDS)
# bad field raises SyntaxError
# bad function raises ValueError
with pytest.raises((SyntaxError, ValueError)):
rendered, _ = photo.render_template("{photo.original_filename|sslice(1:2:3:4)}")