From 3b71c89655aacc563e85823b7b0fada24e755042 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 2 Dec 2010 09:15:34 -0700 Subject: [PATCH] Add a count function to the template language. Make author_sort searchable. --- src/calibre/library/cli.py | 5 +++-- src/calibre/library/field_metadata.py | 2 +- src/calibre/manual/template_lang.rst | 5 +++-- src/calibre/utils/formatter.py | 13 +++++++++++-- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index 747cd59abb..01e8ad449b 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -565,8 +565,9 @@ datatype is one of: {0} 'applies if datatype is text.')) parser.add_option('--display', default='{}', help=_('A dictionary of options to customize how ' - 'the data in this column will be interpreted.')) - + 'the data in this column will be interpreted. This is a JSON ' + ' string. For enumeration columns, use ' + '--display=\'{"enum_values":["val1", "val2"]}\'')) return parser diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index d10dc5da71..9217aca566 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -177,7 +177,7 @@ class FieldMetadata(dict): 'is_multiple':None, 'kind':'field', 'name':None, - 'search_terms':[], + 'search_terms':['author_sort'], 'is_custom':False, 'is_category':False}), ('comments', {'table':None, diff --git a/src/calibre/manual/template_lang.rst b/src/calibre/manual/template_lang.rst index e1eb876cb7..1bef32fbd6 100644 --- a/src/calibre/manual/template_lang.rst +++ b/src/calibre/manual/template_lang.rst @@ -119,10 +119,11 @@ The functions available are: * ``ifempty(text)`` -- if the field is not empty, return the value of the field. Otherwise return `text`. * ``test(text if not empty, text if empty)`` -- return `text if not empty` if the field is not empty, otherwise return `text if empty`. * ``contains(pattern, text if match, text if not match`` -- checks if field contains matches for the regular expression `pattern`. Returns `text if match` if matches are found, otherwise it returns `text if no match`. - * ``switch(pattern, value, pattern, value, ..., else_value)`` -- for each ``pattern, value`` pair, checks if the field matches the regular expression ``pattern`` and if so, returns that ``value``. If no ``pattern`` matches, then ``else_value`` is returned. You can have as many ``pattern, value`` pairs as you want. + * ``count(separator)`` -- interprets the value as a list of items separated by `separator`, returning the number of items in the list. Most lists use a comma as the separator, but authors uses an ampersand. Examples: `{tags:count(,)}`, `{authors:count(&)}` + * ``lookup(pattern, field, pattern, field, ..., else_field)`` -- like switch, except the arguments are field (metadata) names, not text. The value of the appropriate field will be fetched and used. Note that because composite columns are fields, you can use this function in one composite field to use the value of some other composite field. This is extremely useful when constructing variable save paths (more later). * ``re(pattern, replacement)`` -- return the field after applying the regular expression. All instances of `pattern` are replaced with `replacement`. As in all of |app|, these are python-compatible regular expressions. * ``shorten(left chars, middle text, right chars)`` -- Return a shortened version of the field, consisting of `left chars` characters from the beginning of the field, followed by `middle text`, followed by `right chars` characters from the end of the string. `Left chars` and `right chars` must be integers. For example, assume the title of the book is `Ancient English Laws in the Times of Ivanhoe`, and you want it to fit in a space of at most 15 characters. If you use ``{title:shorten(9,-,5)}``, the result will be `Ancient E-nhoe`. If the field's length is less than ``left chars`` + ``right chars`` + the length of ``middle text``, then the field will be used intact. For example, the title `The Dome` would not be changed. - * ``lookup(pattern, field, pattern, field, ..., else_field)`` -- like switch, except the arguments are field (metadata) names, not text. The value of the appropriate field will be fetched and used. Note that because composite columns are fields, you can use this function in one composite field to use the value of some other composite field. This is extremely useful when constructing variable save paths (more later). + * ``switch(pattern, value, pattern, value, ..., else_value)`` -- for each ``pattern, value`` pair, checks if the field matches the regular expression ``pattern`` and if so, returns that ``value``. If no ``pattern`` matches, then ``else_value`` is returned. You can have as many ``pattern, value`` pairs as you want. Now, about using functions and formatting in the same field. Suppose you have an integer custom column called ``#myint`` that you want to see with leading zeros, as in ``003``. To do this, you would use a format of ``0>3s``. However, by default, if a number (integer or float) equals zero then the field produces the empty value, so zero values will produce nothing, not ``000``. If you really want to see ``000`` values, then you use both the format string and the ``ifempty`` function to change the empty value back to a zero. The field reference would be:: diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index 336ac2390b..15534a9c8a 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -79,6 +79,9 @@ class TemplateFormatter(string.Formatter): else: return val + def _count(self, val, sep): + return unicode(len(val.split(sep))) + functions = { 'uppercase' : (0, lambda s,x: x.upper()), 'lowercase' : (0, lambda s,x: x.lower()), @@ -91,6 +94,7 @@ class TemplateFormatter(string.Formatter): 'shorten' : (3, _shorten), 'switch' : (-1, _switch), 'test' : (2, _test), + 'count' : (1, _count), } format_string_re = re.compile(r'^(.*)\|(.*)\|(.*)$') @@ -136,8 +140,13 @@ class TemplateFormatter(string.Formatter): if fmt[colon:p] in self.functions: field = fmt[colon:p] func = self.functions[field] - args = self.arg_parser.scan(fmt[p+1:])[0] - args = [self.backslash_comma_to_comma.sub(',', a) for a in args] + if func[0] == 1: + # only one arg expected. Don't bother to scan. Avoids need + # for escaping characters + args = [fmt[p+1:-1]] + else: + args = self.arg_parser.scan(fmt[p+1:])[0] + args = [self.backslash_comma_to_comma.sub(',', a) for a in args] if (func[0] == 0 and (len(args) != 1 or args[0])) or \ (func[0] > 0 and func[0] != len(args)): raise ValueError('Incorrect number of arguments for function '+ fmt[0:p])