diff --git a/API_README.md b/API_README.md index c4553bba..907fff78 100644 --- a/API_README.md +++ b/API_README.md @@ -1815,6 +1815,9 @@ Valid filters are: - `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(). +- `filter(x)`: Filter list of values using predicate x; for example, `{folder_album|filter(contains Events)}` returns only folders/albums containing the word 'Events' in their path. +- `int`: Convert values in list to integer, e.g. 1.0 => 1. If value cannot be converted to integer, remove value from list. ['1.1', 'x'] => ['1']. See also float. +- `float`: Convert values in list to floating point number, e.g. 1 => 1.0. If value cannot be converted to float, remove value from list. ['1', 'x'] => ['1.0']. See also int. e.g. if Photo keywords are `["FOO","bar"]`: @@ -2005,7 +2008,7 @@ cog.out(get_template_field_table()) |{lf}|A line feed: '\n', alias for {newline}| |{cr}|A carriage return: '\r'| |{crlf}|a carriage return + line feed: '\r\n'| -|{osxphotos_version}|The osxphotos version, e.g. '0.51.1'| +|{osxphotos_version}|The osxphotos version, e.g. '0.51.2'| |{osxphotos_cmd_line}|The full command line used to run osxphotos| |{album}|Album(s) photo is contained in| |{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder| diff --git a/README.md b/README.md index 006853b8..b62b3c89 100644 --- a/README.md +++ b/README.md @@ -1527,6 +1527,15 @@ Valid filters are: • 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(). + • filter(x): Filter list of values using predicate x; for example, + {folder_album|filter(contains Events)} returns only folders/albums + containing the word 'Events' in their path. + • int: Convert values in list to integer, e.g. 1.0 => 1. If value cannot be + converted to integer, remove value from list. ['1.1', 'x'] => ['1']. See + also float. + • float: Convert values in list to floating point number, e.g. 1 => 1.0. If + value cannot be converted to float, remove value from list. ['1', 'x'] => + ['1.0']. See also int. e.g. if Photo keywords are ["FOO","bar"]: @@ -1955,7 +1964,7 @@ Substitution Description {lf} A line feed: '\n', alias for {newline} {cr} A carriage return: '\r' {crlf} a carriage return + line feed: '\r\n' -{osxphotos_version} The osxphotos version, e.g. '0.51.1' +{osxphotos_version} The osxphotos version, e.g. '0.51.2' {osxphotos_cmd_line} The full command line used to run osxphotos The following substitutions may result in multiple values. Thus if specified @@ -2245,6 +2254,9 @@ Valid filters are: - `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(). +- `filter(x)`: Filter list of values using predicate x; for example, `{folder_album|filter(contains Events)}` returns only folders/albums containing the word 'Events' in their path. +- `int`: Convert values in list to integer, e.g. 1.0 => 1. If value cannot be converted to integer, remove value from list. ['1.1', 'x'] => ['1']. See also float. +- `float`: Convert values in list to floating point number, e.g. 1 => 1.0. If value cannot be converted to float, remove value from list. ['1', 'x'] => ['1.0']. See also int. e.g. if Photo keywords are `["FOO","bar"]`: @@ -2432,7 +2444,7 @@ The following template field substitutions are availabe for use the templating s |{lf}|A line feed: '\n', alias for {newline}| |{cr}|A carriage return: '\r'| |{crlf}|a carriage return + line feed: '\r\n'| -|{osxphotos_version}|The osxphotos version, e.g. '0.51.1'| +|{osxphotos_version}|The osxphotos version, e.g. '0.51.2'| |{osxphotos_cmd_line}|The full command line used to run osxphotos| |{album}|Album(s) photo is contained in| |{folder_album}|Folder path + album photo is contained in. e.g. 'Folder/Subfolder/Album' or just 'Album' if no enclosing folder| diff --git a/docs/.buildinfo b/docs/.buildinfo index d61ea11a..d9b73ff0 100644 --- a/docs/.buildinfo +++ b/docs/.buildinfo @@ -1,4 +1,4 @@ # Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. -config: 25fbecf22c8bafc828d5134c4979a6d0 +config: 18c2b25ef56807d10442ba0ee766ff2f tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/docs/_modules/index.html b/docs/_modules/index.html index 6734e043..1d810cfe 100644 --- a/docs/_modules/index.html +++ b/docs/_modules/index.html @@ -5,7 +5,7 @@ - Overview: module code - osxphotos 0.51.1 documentation + Overview: module code - osxphotos 0.51.2 documentation @@ -123,7 +123,7 @@
-
osxphotos 0.51.1 documentation
+
osxphotos 0.51.2 documentation
@@ -146,7 +146,7 @@
@@ -146,7 +146,7 @@ +
[docs] def filter_predicate(self, value: str, args: str) -> bool: + """Return True if value passes predicate""" + + # extract function name and arguments + if not args: + raise SyntaxError("Filter predicate requires arguments") + args = args.split(None, 1) + if args[0] == "not": + args = args[1:] + return not self.filter_predicate(value, " ".join(args)) + + predicate = args[0] + conditional_value = args[1].split("|") + + def comparison_test(test_function): + """Perform numerical comparisons using test_function""" + # returns True if any of the values match the condition + try: + return any( + bool(test_function(float(value), float(c))) + for c in conditional_value + ) + except ValueError as e: + raise SyntaxError( + f"comparison operators may only be used with values that can be converted to numbers: {vals} {conditional_value}" + ) from e + + predicate_is_true = False + if predicate == "contains": + predicate_is_true = any(c in value for c in conditional_value) + elif predicate == "endswith": + predicate_is_true = any(value.endswith(c) for c in conditional_value) + elif predicate in ["matches", "=="]: + predicate_is_true = any(value == c for c in conditional_value) + elif predicate == "startswith": + predicate_is_true = any(value.startswith(c) for c in conditional_value) + elif predicate == "!=": + predicate_is_true = any(value != c for c in conditional_value) + elif predicate == "<": + predicate_is_true = comparison_test(lambda v, c: v < c) + elif predicate == "<=": + predicate_is_true = comparison_test(lambda v, c: v <= c) + elif predicate == ">": + predicate_is_true = comparison_test(lambda v, c: v > c) + elif predicate == ">=": + predicate_is_true = comparison_test(lambda v, c: v >= c) + else: + raise SyntaxError(f"Invalid predicate: {predicate}") + + return predicate_is_true
+
[docs] def get_template_value_multi(self, field, subfield, path_sep, default): """lookup value for template field (multi-value template substitutions) @@ -1926,6 +1994,24 @@ else: raise SyntaxError(f"Invalid slice: {args}") return slice(start, end, step) + + +def values_to_int(values: List[str]) -> List[str]: + """Convert a list of strings to str representation of ints, if possible, otherwise strip values from list""" + int_values = [] + for v in values: + with suppress(ValueError): + int_values.append(str(int(float(v)))) + return int_values + + +def values_to_float(values: List[str]) -> List[str]: + """Convert a list of strings to str representation of float, if possible, otherwise strip values from list""" + float_values = [] + for v in values: + with suppress(ValueError): + float_values.append(str(float(v))) + return float_values
diff --git a/docs/_sources/template_help.rst.txt b/docs/_sources/template_help.rst.txt index 58056e61..ba0c0a65 100644 --- a/docs/_sources/template_help.rst.txt +++ b/docs/_sources/template_help.rst.txt @@ -61,6 +61,9 @@ Valid filters are: * `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(). +* ``filter(x)``\ : Filter list of values using predicate x; for example, ``{folder_album|filter(contains Events)}`` returns only folders/albums containing the word 'Events' in their path. +* ``int``\ : Convert values in list to integer, e.g. 1.0 => 1. If value cannot be converted to integer, remove value from list. ['1.1', 'x'] => ['1']. See also float. +* ``float``\ : Convert values in list to floating point number, e.g. 1 => 1.0. If value cannot be converted to float, remove value from list. ['1', 'x'] => ['1.0']. See also int. e.g. if Photo keywords are ``["FOO","bar"]``\ : @@ -346,7 +349,7 @@ Template Substitutions * - {crlf} - a carriage return + line feed: '\r\n' * - {osxphotos_version} - - The osxphotos version, e.g. '0.51.1' + - The osxphotos version, e.g. '0.51.2' * - {osxphotos_cmd_line} - The full command line used to run osxphotos * - {album} diff --git a/docs/_static/documentation_options.js b/docs/_static/documentation_options.js index 148c7604..e467ec17 100644 --- a/docs/_static/documentation_options.js +++ b/docs/_static/documentation_options.js @@ -1,6 +1,6 @@ var DOCUMENTATION_OPTIONS = { URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), - VERSION: '0.51.1', + VERSION: '0.51.2', LANGUAGE: 'None', COLLAPSE_INDEX: false, BUILDER: 'html', diff --git a/docs/cli.html b/docs/cli.html index 91d26728..1c5dcf2a 100644 --- a/docs/cli.html +++ b/docs/cli.html @@ -6,7 +6,7 @@ - OSXPhotos Command Line Interface (CLI) - osxphotos 0.51.1 documentation + OSXPhotos Command Line Interface (CLI) - osxphotos 0.51.2 documentation @@ -124,7 +124,7 @@
@@ -147,7 +147,7 @@
@@ -145,7 +145,7 @@
@@ -147,7 +147,7 @@
@@ -147,7 +147,7 @@
@@ -147,7 +147,7 @@
@@ -145,7 +145,7 @@
@@ -147,7 +147,7 @@
@@ -144,7 +144,7 @@
@@ -147,7 +147,7 @@
@@ -147,7 +147,7 @@