Added slice, sslice filters
This commit is contained in:
@@ -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"]`:
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)}")
|
||||
|
||||
Reference in New Issue
Block a user