From b7c4fcd711a8fd16576921c549182866e1457979 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Sun, 17 Jan 2021 13:56:28 +0000 Subject: [PATCH 1/2] Enhancement: allow template functions add() and multiply() to take a variable number of arguments. --- manual/template_lang.rst | 4 ++-- src/calibre/utils/formatter_functions.py | 30 ++++++++++++++---------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/manual/template_lang.rst b/manual/template_lang.rst index 4824b56c6c..2b0c19ac3b 100644 --- a/manual/template_lang.rst +++ b/manual/template_lang.rst @@ -346,7 +346,7 @@ parameters can be statements (sequences of expressions). Note that the definitiv * ``and(value, value, ...)`` -- returns the string "1" if all values are not empty, otherwise returns the empty string. This function works well with test or first_non_empty. You can have as many values as you want. - * ``add(x, y)`` -- returns x + y. Throws an exception if either x or y are not numbers. + * ``add(x, y, ...)`` -- returns the sum of its arguments. Throws an exception if an argument is not a number. * ``assign(id, val)`` -- assigns val to id, then returns val. id must be an identifier, not an expression * ``approximate_formats()`` -- return a comma-separated list of formats that at one point were associated with the book. There is no guarantee that the list is correct, although it probably is. This function can be called in Template Program Mode using the template @@ -480,7 +480,7 @@ parameters can be statements (sequences of expressions). Note that the definitiv ``separator``, as are the items in the returned list. * ``mod(x)`` -- returns the remainder of ``x / y``, where ``x``, ``y``, and the result are integers. Throws an exception if either ``x`` or ``y`` is not a number. - * ``multiply(x, y)`` -- returns x * y. Throws an exception if either x or y are not numbers. + * ``multiply(x, y, ...)`` -- returns the product of its arguments. Throws an exception if any argument is not a number. * ``ondevice()`` -- return the string "Yes" if ``ondevice`` is set, otherwise return the empty string * ``or(value, value, ...)`` -- returns the string ``"1"`` if any value is not empty, otherwise returns the empty string. This function works well with test or `first_non_empty`. You can have as many values as you want. diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index 7e1ebd09f1..d8e38d667d 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -241,14 +241,17 @@ class BuiltinStrlen(BuiltinFormatterFunction): class BuiltinAdd(BuiltinFormatterFunction): name = 'add' - arg_count = 2 + arg_count = -1 category = 'Arithmetic' - __doc__ = doc = _('add(x, y) -- returns x + y. Throws an exception if either x or y are not numbers.') + __doc__ = doc = _('add(x, y, ...) -- returns the sum of its arguments. ' + 'Throws an exception if an argument is not a number.') - def evaluate(self, formatter, kwargs, mi, locals, x, y): - x = float(x if x and x != 'None' else 0) - y = float(y if y and y != 'None' else 0) - return unicode_type(x + y) + def evaluate(self, formatter, kwargs, mi, locals, *args): + res = 0 + for v in args: + v = float(v if v and v != 'None' else 0) + res += v + return unicode_type(res) class BuiltinSubtract(BuiltinFormatterFunction): @@ -265,14 +268,17 @@ class BuiltinSubtract(BuiltinFormatterFunction): class BuiltinMultiply(BuiltinFormatterFunction): name = 'multiply' - arg_count = 2 + arg_count = -1 category = 'Arithmetic' - __doc__ = doc = _('multiply(x, y) -- returns x * y. Throws an exception if either x or y are not numbers.') + __doc__ = doc = _('multiply(x, y, ...) -- returns the product of its arguments. ' + 'Throws an exception if any argument is not a number.') - def evaluate(self, formatter, kwargs, mi, locals, x, y): - x = float(x if x and x != 'None' else 0) - y = float(y if y and y != 'None' else 0) - return unicode_type(x * y) + def evaluate(self, formatter, kwargs, mi, locals, *args): + res = 1 + for v in args: + v = float(v if v and v != 'None' else 0) + res *= v + return unicode_type(res) class BuiltinDivide(BuiltinFormatterFunction): From cfbff3d98f4595667493ecd06d4f21cd4dccde37 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Sun, 17 Jan 2021 13:56:55 +0000 Subject: [PATCH 2/2] New template function: field_exists() --- manual/template_lang.rst | 1 + src/calibre/utils/formatter_functions.py | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/manual/template_lang.rst b/manual/template_lang.rst index 2b0c19ac3b..f09e19a523 100644 --- a/manual/template_lang.rst +++ b/manual/template_lang.rst @@ -390,6 +390,7 @@ parameters can be statements (sequences of expressions). Note that the definitiv `[[` for the `{` character and `]]` for the '}' character; they are converted automatically. Note also that prefixes and suffixes (the `|prefix|suffix` syntax) cannot be used in the argument to this function when using Template Program Mode. * ``field(name)`` -- returns the metadata field named by ``name``. + * ``field_exists(field_name)`` -- checks if a field (column) named ``field_name`` exists, returning '1' if so and '' if not. * ``finish_formatting(val, fmt, prefix, suffix)`` -- apply the format, prefix, and suffix to a value in the same way as done in a template like ``{series_index:05.2f| - |- }``. This function is provided to ease diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index d8e38d667d..ca581738d0 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -1894,6 +1894,19 @@ class BuiltinGlobals(BuiltinFormatterFunction): # The globals function is implemented in-line in the formatter raise NotImplementedError() +class BuiltinFieldExists(BuiltinFormatterFunction): + name = 'field_exists' + arg_count = 1 + category = 'If-then-else' + __doc__ = doc = _('field_exists(field_name) -- checks if a field ' + '(column) named field_name exists, returning ' + "'1' if so and '' if not.") + + def evaluate(self, formatter, kwargs, mi, locals, field_name): + if field_name.lower() in mi.all_field_keys(): + return '1' + return '' + _formatter_builtins = [ BuiltinAdd(), BuiltinAnd(), BuiltinApproximateFormats(), BuiltinArguments(), @@ -1903,7 +1916,8 @@ _formatter_builtins = [ BuiltinCmp(), BuiltinConnectedDeviceName(), BuiltinConnectedDeviceUUID(), BuiltinContains(), BuiltinCount(), BuiltinCurrentLibraryName(), BuiltinCurrentLibraryPath(), BuiltinDaysBetween(), BuiltinDivide(), BuiltinEval(), BuiltinFirstNonEmpty(), - BuiltinField(), BuiltinFinishFormatting(), BuiltinFirstMatchingCmp(), BuiltinFloor(), + BuiltinField(), BuiltinFieldExists(), + BuiltinFinishFormatting(), BuiltinFirstMatchingCmp(), BuiltinFloor(), BuiltinFormatDate(), BuiltinFormatNumber(), BuiltinFormatsModtimes(), BuiltinFormatsPaths(), BuiltinFormatsSizes(), BuiltinFractionalPart(), BuiltinGlobals(),