diff --git a/manual/template_lang.rst b/manual/template_lang.rst index 1a467f0e6d..1c6b356e04 100644 --- a/manual/template_lang.rst +++ b/manual/template_lang.rst @@ -469,7 +469,7 @@ In `GPM` the functions described in `Single Function Mode` all require an additi * ``booksize()`` -- returns the value of the calibre 'size' field. Returns '' if there are no formats. * ``check_yes_no(field_name, is_undefined, is_false, is_true)`` -- checks if the value of the yes/no field named by the lookup name ``field_name`` is one of the values specified by the parameters, returning ``'yes'`` if a match is found otherwise returning the empty string. Set the parameter ``is_undefined``, ``is_false``, or ``is_true`` to 1 (the number) to check that condition, otherwise set it to 0. Example: - ``check_yes_no("#bool", 1, 0, 1)`` returns ``'yes'`` if the yes/no field ``#bool`` is either True or undefined (neither True nor False). + ``check_yes_no("#bool", 1, 0, 1)`` returns ``'Yes'`` if the yes/no field ``#bool`` is either True or undefined (neither True nor False). More than one of ``is_undefined``, ``is_false``, or ``is_true`` can be set to 1. * ``ceiling(x)`` -- returns the smallest integer greater than or equal to ``x``. Throws an exception if ``x`` is not a number. @@ -493,6 +493,9 @@ In `GPM` the functions described in `Single Function Mode` all require an additi * ``days_between(date1, date2)`` -- return the number of days between ``date1`` and ``date2``. The number is positive if ``date1`` is greater than ``date2``, otherwise negative. If either ``date1`` or ``date2`` are not dates, the function returns the empty string. * ``divide(x, y)`` -- returns ``x / y``. Throws an exception if either ``x`` or ``y`` are not numbers. This function can usually be replaced by the ``/`` operator. * ``eval(string)`` -- evaluates the string as a program, passing the local variables. This permits using the template processor to construct complex results from local variables. In :ref:`Template Program Mode `, because the `{` and `}` characters are interpreted before the template is evaluated you must use `[[` 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 :ref:`Template Program Mode `. +* ``extra_file_size(file_name)`` -- returns the size in bytes of the extra file ``file_name`` in the book's ``data/`` folder if it exists, otherwise ``-1``. See also the functions ``has_extra_files()``, ``extra_file_names()`` and ``extra_file_modtime()``. This function can be used only in the GUI. +* ``extra_file_modtime(file_name, format_spec)`` -- returns the modification time of the extra file ``file_name`` in the book's ``data/`` folder if it exists, otherwise ``-1``. The modtime is formatted according to ``format_string`` (see ``format_date()`` for details). If ``format_string`` is the empty string, returns the modtime as the floating point number of seconds since the epoch. See also the functions ``has_extra_files()``, ``extra_file_names()`` and ``extra_file_size()``. The epoch is OS dependent. This function can be used only in the GUI. +* ``extra_file_names(sep)`` -- returns a ``sep``-separated list of extra files in the book's ``data/`` folder. See also the functions ``has_extra_files()``, ``extra_file_size()`` and ``extra_file_modtime()``. This function can be used only in the GUI. * ``field(lookup_name)`` -- returns the value of the metadata field with lookup name ``lookup_name``. * ``field_exists(field_name)`` -- checks if a field (column) with the lookup name ``field_name`` exists, returning ``'1'`` if so and the empty string 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 conversion of complex single-function- or template-program-mode templates to `GPM` Templates. For example, the following program produces the same output as the above template:: @@ -554,6 +557,7 @@ In `GPM` the functions described in `Single Function Mode` all require an additi * ``formats_sizes()`` -- return a comma-separated list of colon-separated ``FMT:SIZE`` items giving the sizes in bytes of the formats of a book. You can use the select function to get the size for a specific format. Note that format names are always uppercase, as in EPUB. * ``fractional_part(x)`` -- returns the value after the decimal point. For example, ``fractional_part(3.14)`` returns ``0.14``. Throws an exception if ``x`` is not a number. * ``has_cover()`` -- return ``'Yes'`` if the book has a cover, otherwise the empty string. +* ``has_extra_files()`` -- returns ``'Yes'`` if there are any extra files for the book (files in the folder ``data/`` in the book's folder), otherwise ``''`` (the empty string). See also the functions ``extra_file_names()``, ``extra_file_size()`` and ``extra_file_modtime()`` This function can be used only in the GUI. * ``is_marked()`` -- check whether the book is `marked` in calibre. If it is then return the value of the mark, either ``'true'`` (lower case) or a comma-separated list of named marks. Returns ``''`` (the empty string) if the book is not marked. This function works only in the GUI. * ``language_codes(lang_strings)`` -- return the `language codes `_ for the language names passed in `lang_strings`. The strings must be in the language of the current locale. ``Lang_strings`` is a comma-separated list. * ``list_contains(value, separator, [ pattern, found_val, ]* not_found_val)`` -- (Alias of ``in_list``) Interpreting the value as a list of items separated by ``separator``, evaluate the ``pattern`` against each value in the list. If the ``pattern`` matches any value then return ``found_val``, otherwise return ``not_found_val``. The ``pattern`` and ``found_value`` can be repeated as many times as desired, permitting returning different values depending on the search. The patterns are checked in order. The first match is returned. Aliases: ``in_list()``, ``list_contains()`` diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index e3b6bf8346..1cc73c4a2a 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -2144,14 +2144,14 @@ class BuiltinCheckYesNo(BuiltinFormatterFunction): res = getattr(mi, field, None) if res is None: if is_undefined == '1': - return 'yes' + return 'Yes' return "" if not isinstance(res, bool): raise ValueError(_('check_yes_no requires the field be a Yes/No custom column')) if is_false == '1' and not res: - return 'yes' + return 'Yes' if is_true == '1' and res: - return 'yes' + return 'Yes' return "" @@ -2387,6 +2387,95 @@ class BuiltinBookValues(BuiltinFormatterFunction): raise ValueError(e) +class BuiltinHasExtraFiles(BuiltinFormatterFunction): + name = 'has_extra_files' + arg_count = 0 + category = 'Template database functions' + __doc__ = doc = _("has_extra_files() -- returns 'Yes' if there are any extra " + "files, otherwise '' (the empty string). " + 'This function can be used only in the GUI.') + + def evaluate(self, formatter, kwargs, mi, locals): + db = self.get_database(mi).new_api + try: + files = db.list_extra_files(mi.id, use_cache=True, pattern='data/**/*') + return 'Yes' if files else '' + except Exception as e: + traceback.print_exc() + raise ValueError(e) + + +class BuiltinExtraFileNames(BuiltinFormatterFunction): + name = 'extra_file_names' + arg_count = 1 + category = 'Template database functions' + __doc__ = doc = _("extra_file_names(sep) -- returns a sep-separated list of " + "extra files in the book's 'data/' folder. " + 'This function can be used only in the GUI.') + + def evaluate(self, formatter, kwargs, mi, locals, sep): + db = self.get_database(mi).new_api + try: + files = db.list_extra_files(mi.id, use_cache=True, pattern='data/**/*') + return sep.join([file[0][5:] for file in files]) + except Exception as e: + traceback.print_exc() + raise ValueError(e) + + +class BuiltinExtraFileSize(BuiltinFormatterFunction): + name = 'extra_file_size' + arg_count = 1 + category = 'Template database functions' + __doc__ = doc = _("extra_file_size(file_name) -- returns the size in bytes of " + "the extra file 'file_name' in the book's 'data/' folder if " + "it exists, otherwise -1." + 'This function can be used only in the GUI.') + + def evaluate(self, formatter, kwargs, mi, locals, file_name): + db = self.get_database(mi).new_api + try: + file_name = 'data/' + file_name + files = db.list_extra_files(mi.id, use_cache=True, pattern='data/**/*') + for f in files: + if f[0] == file_name: + return str(f[2].st_size) + return str(-1) + except Exception as e: + traceback.print_exc() + raise ValueError(e) + + +class BuiltinExtraFileModtime(BuiltinFormatterFunction): + name = 'extra_file_modtime' + arg_count = 2 + category = 'Template database functions' + __doc__ = doc = _("extra_file_modtime(file_name, format_spec) -- returns the " + "modification time of the extra file 'file_name' in the " + "book's 'data/' folder if it exists, otherwise -1.0. The " + "modtime is formatted according to 'format_string' " + "(see format_date()). If 'format_string' is empty, returns " + "the modtime as the floating point number of seconds since " + "the epoch. The epoch is OS dependent. " + "This function can be used only in the GUI.") + + def evaluate(self, formatter, kwargs, mi, locals, file_name, format_string): + db = self.get_database(mi).new_api + try: + file_name = 'data/' + file_name + files = db.list_extra_files(mi.id, use_cache=True, pattern='data/**/*') + for f in files: + if f[0] == file_name: + val = f[2].st_mtime + if format_string: + return format_date(datetime.fromtimestamp(val), format_string) + return str(val) + return str(1.0) + except Exception as e: + traceback.print_exc() + raise ValueError(e) + + _formatter_builtins = [ BuiltinAdd(), BuiltinAnd(), BuiltinApproximateFormats(), BuiltinArguments(), BuiltinAssign(), @@ -2396,12 +2485,13 @@ _formatter_builtins = [ BuiltinCmp(), BuiltinConnectedDeviceName(), BuiltinConnectedDeviceUUID(), BuiltinContains(), BuiltinCount(), BuiltinCurrentLibraryName(), BuiltinCurrentLibraryPath(), BuiltinCurrentVirtualLibraryName(), BuiltinDateArithmetic(), - BuiltinDaysBetween(), BuiltinDivide(), BuiltinEval(), BuiltinFirstNonEmpty(), - BuiltinField(), BuiltinFieldExists(), + BuiltinDaysBetween(), BuiltinDivide(), BuiltinEval(), + BuiltinExtraFileNames(), BuiltinExtraFileSize(), BuiltinExtraFileModtime(), + BuiltinFirstNonEmpty(), BuiltinField(), BuiltinFieldExists(), BuiltinFinishFormatting(), BuiltinFirstMatchingCmp(), BuiltinFloor(), BuiltinFormatDate(), BuiltinFormatNumber(), BuiltinFormatsModtimes(), BuiltinFormatsPaths(), BuiltinFormatsSizes(), BuiltinFractionalPart(), - BuiltinGlobals(), + BuiltinGlobals(), BuiltinHasExtraFiles(), BuiltinHasCover(), BuiltinHumanReadable(), BuiltinIdentifierInList(), BuiltinIfempty(), BuiltinLanguageCodes(), BuiltinLanguageStrings(), BuiltinInList(), BuiltinIsMarked(), BuiltinListCountMatching(),