1#!/usr/local/bin/python3.8 2# vim:fileencoding=utf-8 3 4''' 5Created on 13 Jan 2011 6 7@author: charles 8''' 9 10 11__license__ = 'GPL v3' 12__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' 13__docformat__ = 'restructuredtext en' 14 15import inspect, re, traceback, numbers 16from datetime import datetime, timedelta 17from math import trunc, floor, ceil, modf 18from contextlib import suppress 19 20from calibre import human_readable, prints 21from calibre.constants import DEBUG 22from calibre.ebooks.metadata import title_sort 23from calibre.utils.config import tweaks 24from calibre.utils.titlecase import titlecase 25from calibre.utils.icu import capitalize, strcmp, sort_key 26from calibre.utils.date import parse_date, format_date, now, UNDEFINED_DATE 27from calibre.utils.localization import calibre_langcode_to_name, canonicalize_lang 28from polyglot.builtins import iteritems, itervalues 29 30 31class FormatterFunctions: 32 33 error_function_body = ('def evaluate(self, formatter, kwargs, mi, locals):\n' 34 '\treturn "' + 35 _('Duplicate user function name {0}. ' 36 'Change the name or ensure that the functions are identical') + '"') 37 38 def __init__(self): 39 self._builtins = {} 40 self._functions = {} 41 self._functions_from_library = {} 42 43 def register_builtin(self, func_class): 44 if not isinstance(func_class, FormatterFunction): 45 raise ValueError('Class %s is not an instance of FormatterFunction'%( 46 func_class.__class__.__name__)) 47 name = func_class.name 48 if name in self._functions: 49 raise ValueError('Name %s already used'%name) 50 self._builtins[name] = func_class 51 self._functions[name] = func_class 52 for a in func_class.aliases: 53 self._functions[a] = func_class 54 55 def _register_function(self, func_class, replace=False): 56 if not isinstance(func_class, FormatterFunction): 57 raise ValueError('Class %s is not an instance of FormatterFunction'%( 58 func_class.__class__.__name__)) 59 name = func_class.name 60 if not replace and name in self._functions: 61 raise ValueError('Name %s already used'%name) 62 self._functions[name] = func_class 63 64 def register_functions(self, library_uuid, funcs): 65 self._functions_from_library[library_uuid] = funcs 66 self._register_functions() 67 68 def _register_functions(self): 69 for compiled_funcs in itervalues(self._functions_from_library): 70 for cls in compiled_funcs: 71 f = self._functions.get(cls.name, None) 72 replace = False 73 if f is not None: 74 existing_body = f.program_text 75 new_body = cls.program_text 76 if new_body != existing_body: 77 # Change the body of the template function to one that will 78 # return an error message. Also change the arg count to 79 # -1 (variable) to avoid template compilation errors 80 replace = True 81 func = [cls.name, '', -1, self.error_function_body.format(cls.name)] 82 cls = compile_user_function(*func) 83 else: 84 continue 85 formatter_functions()._register_function(cls, replace=replace) 86 87 def unregister_functions(self, library_uuid): 88 if library_uuid in self._functions_from_library: 89 for cls in self._functions_from_library[library_uuid]: 90 self._functions.pop(cls.name, None) 91 self._functions_from_library.pop(library_uuid) 92 self._register_functions() 93 94 def get_builtins(self): 95 return self._builtins 96 97 def get_builtins_and_aliases(self): 98 res = {} 99 for f in itervalues(self._builtins): 100 res[f.name] = f 101 for a in f.aliases: 102 res[a] = f 103 return res 104 105 def get_functions(self): 106 return self._functions 107 108 def reset_to_builtins(self): 109 self._functions = {} 110 for n,c in self._builtins.items(): 111 self._functions[n] = c 112 for a in c.aliases: 113 self._functions[a] = c 114 115 116_ff = FormatterFunctions() 117 118 119def formatter_functions(): 120 global _ff 121 return _ff 122 123 124class FormatterFunction: 125 126 doc = _('No documentation provided') 127 name = 'no name provided' 128 category = 'Unknown' 129 arg_count = 0 130 aliases = [] 131 is_python = True 132 133 def evaluate(self, formatter, kwargs, mi, locals, *args): 134 raise NotImplementedError() 135 136 def eval_(self, formatter, kwargs, mi, locals, *args): 137 ret = self.evaluate(formatter, kwargs, mi, locals, *args) 138 if isinstance(ret, (bytes, str)): 139 return ret 140 if isinstance(ret, list): 141 return ','.join(ret) 142 if isinstance(ret, (numbers.Number, bool)): 143 return str(ret) 144 145 146class BuiltinFormatterFunction(FormatterFunction): 147 148 def __init__(self): 149 formatter_functions().register_builtin(self) 150 eval_func = inspect.getmembers(self.__class__, 151 lambda x: inspect.isfunction(x) and x.__name__ == 'evaluate') 152 try: 153 lines = [l[4:] for l in inspect.getsourcelines(eval_func[0][1])[0]] 154 except: 155 lines = [] 156 self.program_text = ''.join(lines) 157 158 159class BuiltinStrcmp(BuiltinFormatterFunction): 160 name = 'strcmp' 161 arg_count = 5 162 category = 'Relational' 163 __doc__ = doc = _('strcmp(x, y, lt, eq, gt) -- does a case-insensitive comparison of x ' 164 'and y as strings. Returns lt if x < y. Returns eq if x == y. ' 165 'Otherwise returns gt. In many cases the lexical comparison operators ' 166 '(>, <, == etc) can replace this function.') 167 168 def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt): 169 v = strcmp(x, y) 170 if v < 0: 171 return lt 172 if v == 0: 173 return eq 174 return gt 175 176 177class BuiltinCmp(BuiltinFormatterFunction): 178 name = 'cmp' 179 category = 'Relational' 180 arg_count = 5 181 __doc__ = doc = _('cmp(x, y, lt, eq, gt) -- compares x and y after converting both to ' 182 'numbers. Returns lt if x < y. Returns eq if x == y. Otherwise returns gt. ' 183 'In many cases the numeric comparison operators ' 184 '(>#, <#, ==# etc) can replace this function.') 185 186 def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt): 187 x = float(x if x and x != 'None' else 0) 188 y = float(y if y and y != 'None' else 0) 189 if x < y: 190 return lt 191 if x == y: 192 return eq 193 return gt 194 195 196class BuiltinFirstMatchingCmp(BuiltinFormatterFunction): 197 name = 'first_matching_cmp' 198 category = 'Relational' 199 arg_count = -1 200 __doc__ = doc = _('first_matching_cmp(val, [cmp1, result1,]+, else_result) -- ' 201 'compares "val < cmpN" in sequence, returning resultN for ' 202 'the first comparison that succeeds. Returns else_result ' 203 'if no comparison succeeds. Example: ' 204 'first_matching_cmp(10,5,"small",10,"middle",15,"large","giant") ' 205 'returns "large". The same example with a first value of 16 returns "giant".') 206 207 def evaluate(self, formatter, kwargs, mi, locals, *args): 208 if (len(args) % 2) != 0: 209 raise ValueError(_('first_matching_cmp requires an even number of arguments')) 210 val = float(args[0] if args[0] and args[0] != 'None' else 0) 211 for i in range(1, len(args) - 1, 2): 212 c = float(args[i] if args[i] and args[i] != 'None' else 0) 213 if val < c: 214 return args[i+1] 215 return args[len(args)-1] 216 217 218class BuiltinStrcat(BuiltinFormatterFunction): 219 name = 'strcat' 220 arg_count = -1 221 category = 'String manipulation' 222 __doc__ = doc = _('strcat(a [, b]*) -- can take any number of arguments. Returns the ' 223 'string formed by concatenating all the arguments') 224 225 def evaluate(self, formatter, kwargs, mi, locals, *args): 226 i = 0 227 res = '' 228 for i in range(0, len(args)): 229 res += args[i] 230 return res 231 232 233class BuiltinStrlen(BuiltinFormatterFunction): 234 name = 'strlen' 235 arg_count = 1 236 category = 'String manipulation' 237 __doc__ = doc = _('strlen(a) -- Returns the length of the string passed as ' 238 'the argument') 239 240 def evaluate(self, formatter, kwargs, mi, locals, a): 241 try: 242 return len(a) 243 except: 244 return -1 245 246 247class BuiltinAdd(BuiltinFormatterFunction): 248 name = 'add' 249 arg_count = -1 250 category = 'Arithmetic' 251 __doc__ = doc = _('add(x [, y]*) -- returns the sum of its arguments. ' 252 'Throws an exception if an argument is not a number. ' 253 'This function can often be ' 254 'replaced with the + operator.') 255 256 def evaluate(self, formatter, kwargs, mi, locals, *args): 257 res = 0 258 for v in args: 259 v = float(v if v and v != 'None' else 0) 260 res += v 261 return str(res) 262 263 264class BuiltinSubtract(BuiltinFormatterFunction): 265 name = 'subtract' 266 arg_count = 2 267 category = 'Arithmetic' 268 __doc__ = doc = _('subtract(x, y) -- returns x - y. Throws an exception if ' 269 'either x or y are not numbers. This function can often be ' 270 'replaced with the - operator.') 271 272 def evaluate(self, formatter, kwargs, mi, locals, x, y): 273 x = float(x if x and x != 'None' else 0) 274 y = float(y if y and y != 'None' else 0) 275 return str(x - y) 276 277 278class BuiltinMultiply(BuiltinFormatterFunction): 279 name = 'multiply' 280 arg_count = -1 281 category = 'Arithmetic' 282 __doc__ = doc = _('multiply(x [, y]*) -- returns the product of its arguments. ' 283 'Throws an exception if any argument is not a number. ' 284 'This function can often be replaced with the * operator.') 285 286 def evaluate(self, formatter, kwargs, mi, locals, *args): 287 res = 1 288 for v in args: 289 v = float(v if v and v != 'None' else 0) 290 res *= v 291 return str(res) 292 293 294class BuiltinDivide(BuiltinFormatterFunction): 295 name = 'divide' 296 arg_count = 2 297 category = 'Arithmetic' 298 __doc__ = doc = _('divide(x, y) -- returns x / y. Throws an exception if ' 299 'either x or y are not numbers.' 300 ' This function can often be replaced with the / operator.') 301 302 def evaluate(self, formatter, kwargs, mi, locals, x, y): 303 x = float(x if x and x != 'None' else 0) 304 y = float(y if y and y != 'None' else 0) 305 return str(x / y) 306 307 308class BuiltinCeiling(BuiltinFormatterFunction): 309 name = 'ceiling' 310 arg_count = 1 311 category = 'Arithmetic' 312 __doc__ = doc = _('ceiling(x) -- returns the smallest integer greater ' 313 'than or equal to x. Throws an exception if x is ' 314 'not a number.') 315 316 def evaluate(self, formatter, kwargs, mi, locals, x): 317 x = float(x if x and x != 'None' else 0) 318 return str(int(ceil(x))) 319 320 321class BuiltinFloor(BuiltinFormatterFunction): 322 name = 'floor' 323 arg_count = 1 324 category = 'Arithmetic' 325 __doc__ = doc = _('floor(x) -- returns the largest integer less ' 326 'than or equal to x. Throws an exception if x is ' 327 'not a number.') 328 329 def evaluate(self, formatter, kwargs, mi, locals, x): 330 x = float(x if x and x != 'None' else 0) 331 return str(int(floor(x))) 332 333 334class BuiltinRound(BuiltinFormatterFunction): 335 name = 'round' 336 arg_count = 1 337 category = 'Arithmetic' 338 __doc__ = doc = _('round(x) -- returns the nearest integer to x. ' 339 'Throws an exception if x is not a number.') 340 341 def evaluate(self, formatter, kwargs, mi, locals, x): 342 x = float(x if x and x != 'None' else 0) 343 return str(int(round(x))) 344 345 346class BuiltinMod(BuiltinFormatterFunction): 347 name = 'mod' 348 arg_count = 2 349 category = 'Arithmetic' 350 __doc__ = doc = _('mod(x) -- returns floor(remainder of x / y). ' 351 'Throws an exception if either x or y is not a number.') 352 353 def evaluate(self, formatter, kwargs, mi, locals, x, y): 354 x = float(x if x and x != 'None' else 0) 355 y = float(y if y and y != 'None' else 0) 356 return str(int(x % y)) 357 358 359class BuiltinFractionalPart(BuiltinFormatterFunction): 360 name = 'fractional_part' 361 arg_count = 1 362 category = 'Arithmetic' 363 __doc__ = doc = _('fractional_part(x) -- returns the value after the decimal ' 364 'point. For example, fractional_part(3.14) returns 0.14. ' 365 'Throws an exception if x is not a number.') 366 367 def evaluate(self, formatter, kwargs, mi, locals, x): 368 x = float(x if x and x != 'None' else 0) 369 return str(modf(x)[0]) 370 371 372class BuiltinTemplate(BuiltinFormatterFunction): 373 name = 'template' 374 arg_count = 1 375 category = 'Recursion' 376 377 __doc__ = doc = _('template(x) -- evaluates x as a template. The evaluation is done ' 378 'in its own context, meaning that variables are not shared between ' 379 'the caller and the template evaluation. Because the { and } ' 380 'characters are special, you must use [[ for the { character and ' 381 ']] for the } character; they are converted automatically. ' 382 'For example, template(\'[[title_sort]]\') will evaluate the ' 383 'template {title_sort} and return its value. Note also that ' 384 'prefixes and suffixes (the `|prefix|suffix` syntax) cannot be ' 385 'used in the argument to this function when using template program mode.') 386 387 def evaluate(self, formatter, kwargs, mi, locals, template): 388 template = template.replace('[[', '{').replace(']]', '}') 389 return formatter.__class__().safe_format(template, kwargs, 'TEMPLATE', mi) 390 391 392class BuiltinEval(BuiltinFormatterFunction): 393 name = 'eval' 394 arg_count = 1 395 category = 'Recursion' 396 __doc__ = doc = _('eval(template) -- evaluates the template, passing the local ' 397 'variables (those \'assign\'ed to) instead of the book metadata. ' 398 ' This permits using the template processor to construct complex ' 399 'results from local variables. Because the { and } ' 400 'characters are special, you must use [[ for the { character and ' 401 ']] for the } character; they are converted automatically. ' 402 'Note also that prefixes and suffixes (the `|prefix|suffix` syntax) ' 403 'cannot be used in the argument to this function when using ' 404 'template program mode.') 405 406 def evaluate(self, formatter, kwargs, mi, locals, template): 407 from calibre.utils.formatter import EvalFormatter 408 template = template.replace('[[', '{').replace(']]', '}') 409 return EvalFormatter().safe_format(template, locals, 'EVAL', None) 410 411 412class BuiltinAssign(BuiltinFormatterFunction): 413 name = 'assign' 414 arg_count = 2 415 category = 'Other' 416 __doc__ = doc = _('assign(id, val) -- assigns val to id, then returns val. ' 417 'id must be an identifier, not an expression. ' 418 'This function can often be replaced with the = operator.') 419 420 def evaluate(self, formatter, kwargs, mi, locals, target, value): 421 locals[target] = value 422 return value 423 424 425class BuiltinListSplit(BuiltinFormatterFunction): 426 name = 'list_split' 427 arg_count = 3 428 category = 'List manipulation' 429 __doc__ = doc = _('list_split(list_val, sep, id_prefix) -- splits the list_val ' 430 "into separate values using 'sep', then assigns the values " 431 "to variables named 'id_prefix_N' where N is the position " 432 "of the value in the list. The first item has position 0 (zero). " 433 "The function returns the last element in the list. " 434 "Example: split('one:two:foo', ':', 'var') is equivalent " 435 "to var_0 = 'one'; var_1 = 'two'; var_2 = 'foo'.") 436 437 def evaluate(self, formatter, kwargs, mi, locals, list_val, sep, id_prefix): 438 l = [v.strip() for v in list_val.split(sep)] 439 res = '' 440 for i,v in enumerate(l): 441 res = locals[id_prefix+'_'+str(i)] = v 442 return res 443 444 445class BuiltinPrint(BuiltinFormatterFunction): 446 name = 'print' 447 arg_count = -1 448 category = 'Other' 449 __doc__ = doc = _('print(a[, b]*) -- prints the arguments to standard output. ' 450 'Unless you start calibre from the command line (calibre-debug -g), ' 451 'the output will go to a black hole.') 452 453 def evaluate(self, formatter, kwargs, mi, locals, *args): 454 print(args) 455 return '' 456 457 458class BuiltinField(BuiltinFormatterFunction): 459 name = 'field' 460 arg_count = 1 461 category = 'Get values from metadata' 462 __doc__ = doc = _('field(lookup_name) -- returns the metadata field named by lookup_name') 463 464 def evaluate(self, formatter, kwargs, mi, locals, name): 465 return formatter.get_value(name, [], kwargs) 466 467 468class BuiltinRawField(BuiltinFormatterFunction): 469 name = 'raw_field' 470 arg_count = -1 471 category = 'Get values from metadata' 472 __doc__ = doc = _('raw_field(lookup_name [, optional_default]) -- returns the ' 473 'metadata field named by lookup_name without applying any formatting. ' 474 'It evaluates and returns the optional second argument ' 475 "'default' if the field is undefined ('None').") 476 477 def evaluate(self, formatter, kwargs, mi, locals, name, default=None): 478 res = getattr(mi, name, None) 479 if res is None and default is not None: 480 return default 481 if isinstance(res, list): 482 fm = mi.metadata_for_field(name) 483 if fm is None: 484 return ', '.join(res) 485 return fm['is_multiple']['list_to_ui'].join(res) 486 return str(res) 487 488 489class BuiltinRawList(BuiltinFormatterFunction): 490 name = 'raw_list' 491 arg_count = 2 492 category = 'Get values from metadata' 493 __doc__ = doc = _('raw_list(lookup_name, separator) -- returns the metadata list ' 494 'named by lookup_name without applying any formatting or sorting and ' 495 'with items separated by separator.') 496 497 def evaluate(self, formatter, kwargs, mi, locals, name, separator): 498 res = getattr(mi, name, None) 499 if not isinstance(res, list): 500 return "%s is not a list" % name 501 return separator.join(res) 502 503 504class BuiltinSubstr(BuiltinFormatterFunction): 505 name = 'substr' 506 arg_count = 3 507 category = 'String manipulation' 508 __doc__ = doc = _('substr(str, start, end) -- returns the start\'th through the end\'th ' 509 'characters of str. The first character in str is the zero\'th ' 510 'character. If end is negative, then it indicates that many ' 511 'characters counting from the right. If end is zero, then it ' 512 'indicates the last character. For example, substr(\'12345\', 1, 0) ' 513 'returns \'2345\', and substr(\'12345\', 1, -1) returns \'234\'.') 514 515 def evaluate(self, formatter, kwargs, mi, locals, str_, start_, end_): 516 return str_[int(start_): len(str_) if int(end_) == 0 else int(end_)] 517 518 519class BuiltinLookup(BuiltinFormatterFunction): 520 name = 'lookup' 521 arg_count = -1 522 category = 'Iterating over values' 523 __doc__ = doc = _('lookup(val, [pattern, field,]+ else_field) -- ' 524 'like switch, except the arguments are field (metadata) names, not ' 525 'text. The value of the appropriate field will be fetched and used. ' 526 'Note that because composite columns are fields, you can use this ' 527 'function in one composite field to use the value of some other ' 528 'composite field. This is extremely useful when constructing ' 529 'variable save paths') 530 531 def evaluate(self, formatter, kwargs, mi, locals, val, *args): 532 if len(args) == 2: # here for backwards compatibility 533 if val: 534 return formatter.vformat('{'+args[0].strip()+'}', [], kwargs) 535 else: 536 return formatter.vformat('{'+args[1].strip()+'}', [], kwargs) 537 if (len(args) % 2) != 1: 538 raise ValueError(_('lookup requires either 2 or an odd number of arguments')) 539 i = 0 540 while i < len(args): 541 if i + 1 >= len(args): 542 return formatter.vformat('{' + args[i].strip() + '}', [], kwargs) 543 if re.search(args[i], val, flags=re.I): 544 return formatter.vformat('{'+args[i+1].strip() + '}', [], kwargs) 545 i += 2 546 547 548class BuiltinTest(BuiltinFormatterFunction): 549 name = 'test' 550 arg_count = 3 551 category = 'If-then-else' 552 __doc__ = doc = _('test(val, text if not empty, text if empty) -- return `text if not ' 553 'empty` if val is not empty, otherwise return `text if empty`') 554 555 def evaluate(self, formatter, kwargs, mi, locals, val, value_if_set, value_not_set): 556 if val: 557 return value_if_set 558 else: 559 return value_not_set 560 561 562class BuiltinContains(BuiltinFormatterFunction): 563 name = 'contains' 564 arg_count = 4 565 category = 'If-then-else' 566 __doc__ = doc = _('contains(val, pattern, text if match, text if not match) -- checks ' 567 'if val contains matches for the regular expression `pattern`. ' 568 'Returns `text if match` if matches are found, otherwise it returns ' 569 '`text if no match`') 570 571 def evaluate(self, formatter, kwargs, mi, locals, 572 val, test, value_if_present, value_if_not): 573 if re.search(test, val, flags=re.I): 574 return value_if_present 575 else: 576 return value_if_not 577 578 579class BuiltinSwitch(BuiltinFormatterFunction): 580 name = 'switch' 581 arg_count = -1 582 category = 'Iterating over values' 583 __doc__ = doc = _('switch(val, [pattern, value,]+ else_value) -- ' 584 'for each `pattern, value` pair, checks if `val` matches ' 585 'the regular expression `pattern` and if so, returns that ' 586 '`value`. If no pattern matches, then `else_value` is returned. ' 587 'You can have as many `pattern, value` pairs as you want') 588 589 def evaluate(self, formatter, kwargs, mi, locals, val, *args): 590 if (len(args) % 2) != 1: 591 raise ValueError(_('switch requires an odd number of arguments')) 592 i = 0 593 while i < len(args): 594 if i + 1 >= len(args): 595 return args[i] 596 if re.search(args[i], val, flags=re.I): 597 return args[i+1] 598 i += 2 599 600 601class BuiltinStrcatMax(BuiltinFormatterFunction): 602 name = 'strcat_max' 603 arg_count = -1 604 category = 'String manipulation' 605 __doc__ = doc = _('strcat_max(max, string1 [, prefix2, string2]*) -- ' 606 'Returns a string formed by concatenating the arguments. The ' 607 'returned value is initialized to string1. `Prefix, string` ' 608 'pairs are added to the end of the value as long as the ' 609 'resulting string length is less than `max`. String1 is returned ' 610 'even if string1 is longer than max. You can pass as many ' 611 '`prefix, string` pairs as you wish.') 612 613 def evaluate(self, formatter, kwargs, mi, locals, *args): 614 if len(args) < 2: 615 raise ValueError(_('strcat_max requires 2 or more arguments')) 616 if (len(args) % 2) != 0: 617 raise ValueError(_('strcat_max requires an even number of arguments')) 618 try: 619 max = int(args[0]) 620 except: 621 raise ValueError(_('first argument to strcat_max must be an integer')) 622 623 i = 2 624 result = args[1] 625 try: 626 while i < len(args): 627 if (len(result) + len(args[i]) + len(args[i+1])) > max: 628 break 629 result = result + args[i] + args[i+1] 630 i += 2 631 except: 632 pass 633 return result.strip() 634 635 636class BuiltinInList(BuiltinFormatterFunction): 637 name = 'in_list' 638 arg_count = -1 639 category = 'List lookup' 640 __doc__ = doc = _('in_list(val, separator, [ pattern, found_val, ]+ not_found_val) -- ' 641 'treating val as a list of items separated by separator, ' 642 'if the pattern matches any of the list values then return found_val.' 643 'If the pattern matches no list value then return ' 644 'not_found_val. The pattern and found_value pairs can be repeated as ' 645 'many times as desired. The patterns are checked in order. The ' 646 'found_val for the first match is returned. ' 647 'Aliases: in_list(), list_contains()') 648 aliases = ['list_contains'] 649 650 def evaluate(self, formatter, kwargs, mi, locals, val, sep, *args): 651 if (len(args) % 2) != 1: 652 raise ValueError(_('in_list requires an odd number of arguments')) 653 l = [v.strip() for v in val.split(sep) if v.strip()] 654 i = 0 655 while i < len(args): 656 if i + 1 >= len(args): 657 return args[i] 658 sf = args[i] 659 fv = args[i+1] 660 if l: 661 for v in l: 662 if re.search(sf, v, flags=re.I): 663 return fv 664 i += 2 665 666 667class BuiltinStrInList(BuiltinFormatterFunction): 668 name = 'str_in_list' 669 arg_count = -1 670 category = 'List lookup' 671 __doc__ = doc = _('str_in_list(val, separator, [string, found_val, ]+ not_found_val) -- ' 672 'treating val as a list of items separated by separator, if the ' 673 'string matches any of the list values then return found_val.' 674 'If the string matches no list value then return ' 675 'not_found_val. The comparison is exact match (not contains) and is ' 676 'case insensitive. The string and found_value pairs can be repeated as ' 677 'many times as desired. The patterns are checked in order. The ' 678 'found_val for the first match is returned.') 679 680 def evaluate(self, formatter, kwargs, mi, locals, val, sep, *args): 681 if (len(args) % 2) != 1: 682 raise ValueError(_('str_in_list requires an odd number of arguments')) 683 l = [v.strip() for v in val.split(sep) if v.strip()] 684 i = 0 685 while i < len(args): 686 if i + 1 >= len(args): 687 return args[i] 688 sf = args[i] 689 fv = args[i+1] 690 c = [v.strip() for v in sf.split(sep) if v.strip()] 691 if l: 692 for v in l: 693 for t in c: 694 if strcmp(t, v) == 0: 695 return fv 696 i += 2 697 698 699class BuiltinIdentifierInList(BuiltinFormatterFunction): 700 name = 'identifier_in_list' 701 arg_count = 4 702 category = 'List lookup' 703 __doc__ = doc = _('identifier_in_list(val, id, found_val, not_found_val) -- ' 704 'treat val as a list of identifiers separated by commas, ' 705 'comparing the string against each value in the list. An identifier ' 706 'has the format "identifier:value". The id parameter should be ' 707 'either "id" or "id:regexp". The first case matches if there is any ' 708 'identifier with that id. The second case matches if the regexp ' 709 'matches the identifier\'s value. If there is a match, ' 710 'return found_val, otherwise return not_found_val.') 711 712 def evaluate(self, formatter, kwargs, mi, locals, val, ident, fv, nfv): 713 l = [v.strip() for v in val.split(',') if v.strip()] 714 (id, _, regexp) = ident.partition(':') 715 if not id: 716 return nfv 717 id += ':' 718 if l: 719 for v in l: 720 if v.startswith(id): 721 if not regexp or re.search(regexp, v[len(id):], flags=re.I): 722 return fv 723 return nfv 724 725 726class BuiltinRe(BuiltinFormatterFunction): 727 name = 're' 728 arg_count = 3 729 category = 'String manipulation' 730 __doc__ = doc = _('re(val, pattern, replacement) -- return val after applying ' 731 'the regular expression. All instances of `pattern` are replaced ' 732 'with `replacement`. As in all of calibre, these are ' 733 'Python-compatible regular expressions') 734 735 def evaluate(self, formatter, kwargs, mi, locals, val, pattern, replacement): 736 return re.sub(pattern, replacement, val, flags=re.I) 737 738 739class BuiltinReGroup(BuiltinFormatterFunction): 740 name = 're_group' 741 arg_count = -1 742 category = 'String manipulation' 743 __doc__ = doc = _('re_group(val, pattern [, template_for_group]*) -- ' 744 'return a string made by applying the regular expression pattern ' 745 'to the val and replacing each matched instance with the string ' 746 'computed by replacing each matched group by the value returned ' 747 'by the corresponding template. The original matched value for the ' 748 'group is available as $. In template program mode, like for ' 749 'the template and the eval functions, you use [[ for { and ]] for }.' 750 ' The following example in template program mode looks for series ' 751 'with more than one word and uppercases the first word: ' 752 "{series:'re_group($, \"(\\S* )(.*)\", \"[[$:uppercase()]]\", \"[[$]]\")'}") 753 754 def evaluate(self, formatter, kwargs, mi, locals, val, pattern, *args): 755 from calibre.utils.formatter import EvalFormatter 756 757 def repl(mo): 758 res = '' 759 if mo and mo.lastindex: 760 for dex in range(0, mo.lastindex): 761 gv = mo.group(dex+1) 762 if gv is None: 763 continue 764 if len(args) > dex: 765 template = args[dex].replace('[[', '{').replace(']]', '}') 766 res += EvalFormatter().safe_format(template, {'$': gv}, 767 'EVAL', None, strip_results=False) 768 else: 769 res += gv 770 return res 771 return re.sub(pattern, repl, val, flags=re.I) 772 773 774class BuiltinSwapAroundComma(BuiltinFormatterFunction): 775 name = 'swap_around_comma' 776 arg_count = 1 777 category = 'String manipulation' 778 __doc__ = doc = _('swap_around_comma(val) -- given a value of the form ' 779 '"B, A", return "A B". This is most useful for converting names ' 780 'in LN, FN format to FN LN. If there is no comma, the function ' 781 'returns val unchanged') 782 783 def evaluate(self, formatter, kwargs, mi, locals, val): 784 return re.sub(r'^(.*?),\s*(.*$)', r'\2 \1', val, flags=re.I).strip() 785 786 787class BuiltinIfempty(BuiltinFormatterFunction): 788 name = 'ifempty' 789 arg_count = 2 790 category = 'If-then-else' 791 __doc__ = doc = _('ifempty(val, text if empty) -- return val if val is not empty, ' 792 'otherwise return `text if empty`') 793 794 def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty): 795 if val: 796 return val 797 else: 798 return value_if_empty 799 800 801class BuiltinShorten(BuiltinFormatterFunction): 802 name = 'shorten' 803 arg_count = 4 804 category = 'String manipulation' 805 __doc__ = doc = _('shorten(val, left chars, middle text, right chars) -- Return a ' 806 'shortened version of val, consisting of `left chars` ' 807 'characters from the beginning of val, followed by ' 808 '`middle text`, followed by `right chars` characters from ' 809 'the end of the string. `Left chars` and `right chars` must be ' 810 'integers. For example, assume the title of the book is ' 811 '`Ancient English Laws in the Times of Ivanhoe`, and you want ' 812 'it to fit in a space of at most 15 characters. If you use ' 813 '{title:shorten(9,-,5)}, the result will be `Ancient E-anhoe`. ' 814 'If the field\'s length is less than left chars + right chars + ' 815 'the length of `middle text`, then the field will be used ' 816 'intact. For example, the title `The Dome` would not be changed.') 817 818 def evaluate(self, formatter, kwargs, mi, locals, 819 val, leading, center_string, trailing): 820 l = max(0, int(leading)) 821 t = max(0, int(trailing)) 822 if len(val) > l + len(center_string) + t: 823 return val[0:l] + center_string + ('' if t == 0 else val[-t:]) 824 else: 825 return val 826 827 828class BuiltinCount(BuiltinFormatterFunction): 829 name = 'count' 830 arg_count = 2 831 category = 'List manipulation' 832 aliases = ['list_count'] 833 834 __doc__ = doc = _('count(val, separator) -- interprets the value as a list of items ' 835 'separated by `separator`, returning the number of items in the ' 836 'list. Most lists use a comma as the separator, but authors ' 837 'uses an ampersand. Examples: {tags:count(,)}, {authors:count(&)}. ' 838 'Aliases: count(), list_count()') 839 840 def evaluate(self, formatter, kwargs, mi, locals, val, sep): 841 return str(len([v for v in val.split(sep) if v])) 842 843 844class BuiltinListCountMatching(BuiltinFormatterFunction): 845 name = 'list_count_matching' 846 arg_count = 3 847 category = 'List manipulation' 848 aliases = ['count_matching'] 849 850 __doc__ = doc = _('list_count_matching(list, pattern, separator) -- ' 851 "interprets 'list' as a list of items separated by 'separator', " 852 'returning the number of items in the list that match the regular ' 853 "expression 'pattern'. Aliases: list_count_matching(), count_matching()") 854 855 def evaluate(self, formatter, kwargs, mi, locals, list_, pattern, sep): 856 res = 0 857 for v in [x.strip() for x in list_.split(sep) if x.strip()]: 858 if re.search(pattern, v, flags=re.I): 859 res += 1 860 return str(res) 861 862 863class BuiltinListitem(BuiltinFormatterFunction): 864 name = 'list_item' 865 arg_count = 3 866 category = 'List lookup' 867 __doc__ = doc = _('list_item(val, index, separator) -- interpret the value as a list of ' 868 'items separated by `separator`, returning the `index`th item. ' 869 'The first item is number zero. The last item can be returned ' 870 'using `list_item(-1,separator)`. If the item is not in the list, ' 871 'then the empty value is returned. The separator has the same ' 872 'meaning as in the count function.') 873 874 def evaluate(self, formatter, kwargs, mi, locals, val, index, sep): 875 if not val: 876 return '' 877 index = int(index) 878 val = val.split(sep) 879 try: 880 return val[index].strip() 881 except: 882 return '' 883 884 885class BuiltinSelect(BuiltinFormatterFunction): 886 name = 'select' 887 arg_count = 2 888 category = 'List lookup' 889 __doc__ = doc = _('select(val, key) -- interpret the value as a comma-separated list ' 890 'of items, with the items being "id:value". Find the pair with the ' 891 'id equal to key, and return the corresponding value. Returns the ' 892 'empty string if no match is found.' 893 ) 894 895 def evaluate(self, formatter, kwargs, mi, locals, val, key): 896 if not val: 897 return '' 898 vals = [v.strip() for v in val.split(',')] 899 tkey = key+':' 900 for v in vals: 901 if v.startswith(tkey): 902 return v[len(tkey):] 903 return '' 904 905 906class BuiltinApproximateFormats(BuiltinFormatterFunction): 907 name = 'approximate_formats' 908 arg_count = 0 909 category = 'Get values from metadata' 910 __doc__ = doc = _('approximate_formats() -- return a comma-separated ' 911 'list of formats that at one point were associated with the ' 912 'book. There is no guarantee that this list is correct, ' 913 'although it probably is. ' 914 'This function can be called in template program mode using ' 915 'the template "{:\'approximate_formats()\'}". ' 916 'Note that format names are always uppercase, as in EPUB. ' 917 'This function works only in the GUI. If you want to use these values ' 918 'in save-to-disk or send-to-device templates then you ' 919 'must make a custom "Column built from other columns", use ' 920 'the function in that column\'s template, and use that ' 921 'column\'s value in your save/send templates' 922 ) 923 924 def evaluate(self, formatter, kwargs, mi, locals): 925 if hasattr(mi, '_proxy_metadata'): 926 fmt_data = mi._proxy_metadata.db_approx_formats 927 if not fmt_data: 928 return '' 929 data = sorted(fmt_data) 930 return ','.join(v.upper() for v in data) 931 return _('This function can be used only in the GUI') 932 933 934class BuiltinFormatsModtimes(BuiltinFormatterFunction): 935 name = 'formats_modtimes' 936 arg_count = 1 937 category = 'Get values from metadata' 938 __doc__ = doc = _('formats_modtimes(date_format) -- return a comma-separated ' 939 'list of colon-separated items representing modification times ' 940 'for the formats of a book. The date_format parameter ' 941 'specifies how the date is to be formatted. See the ' 942 'format_date function for details. You can use the select ' 943 'function to get the mod time for a specific ' 944 'format. Note that format names are always uppercase, ' 945 'as in EPUB.' 946 ) 947 948 def evaluate(self, formatter, kwargs, mi, locals, fmt): 949 fmt_data = mi.get('format_metadata', {}) 950 try: 951 data = sorted(fmt_data.items(), key=lambda x:x[1]['mtime'], reverse=True) 952 return ','.join(k.upper()+':'+format_date(v['mtime'], fmt) 953 for k,v in data) 954 except: 955 return '' 956 957 958class BuiltinFormatsSizes(BuiltinFormatterFunction): 959 name = 'formats_sizes' 960 arg_count = 0 961 category = 'Get values from metadata' 962 __doc__ = doc = _('formats_sizes() -- return a comma-separated list of ' 963 'colon-separated items representing sizes in bytes ' 964 'of the formats of a book. You can use the select ' 965 'function to get the size for a specific ' 966 'format. Note that format names are always uppercase, ' 967 'as in EPUB.' 968 ) 969 970 def evaluate(self, formatter, kwargs, mi, locals): 971 fmt_data = mi.get('format_metadata', {}) 972 try: 973 return ','.join(k.upper()+':'+str(v['size']) for k,v in iteritems(fmt_data)) 974 except: 975 return '' 976 977 978class BuiltinFormatsPaths(BuiltinFormatterFunction): 979 name = 'formats_paths' 980 arg_count = 0 981 category = 'Get values from metadata' 982 __doc__ = doc = _('formats_paths() -- return a comma-separated list of ' 983 'colon-separated items representing full path to ' 984 'the formats of a book. You can use the select ' 985 'function to get the path for a specific ' 986 'format. Note that format names are always uppercase, ' 987 'as in EPUB.') 988 989 def evaluate(self, formatter, kwargs, mi, locals): 990 fmt_data = mi.get('format_metadata', {}) 991 try: 992 return ','.join(k.upper()+':'+str(v['path']) for k,v in iteritems(fmt_data)) 993 except: 994 return '' 995 996 997class BuiltinHumanReadable(BuiltinFormatterFunction): 998 name = 'human_readable' 999 arg_count = 1 1000 category = 'Formatting values' 1001 __doc__ = doc = _('human_readable(v) -- return a string ' 1002 'representing the number v in KB, MB, GB, etc.' 1003 ) 1004 1005 def evaluate(self, formatter, kwargs, mi, locals, val): 1006 try: 1007 return human_readable(round(float(val))) 1008 except: 1009 return '' 1010 1011 1012class BuiltinFormatNumber(BuiltinFormatterFunction): 1013 name = 'format_number' 1014 arg_count = 2 1015 category = 'Formatting values' 1016 __doc__ = doc = _('format_number(v, template) -- format the number v using ' 1017 'a Python formatting template such as "{0:5.2f}" or ' 1018 '"{0:,d}" or "${0:5,.2f}". The field_name part of the ' 1019 'template must be a 0 (zero) (the "{0:" in the above examples). ' 1020 'See the template language and Python documentation for more ' 1021 'examples. You can leave off the leading "{0:" and trailing ' 1022 '"}" if the template contains only a format. Returns the empty ' 1023 'string if formatting fails.' 1024 ) 1025 1026 def evaluate(self, formatter, kwargs, mi, locals, val, template): 1027 if val == '' or val == 'None': 1028 return '' 1029 if '{' not in template: 1030 template = '{0:' + template + '}' 1031 try: 1032 v1 = float(val) 1033 except: 1034 return '' 1035 try: # Try formatting the value as a float 1036 return template.format(v1) 1037 except: 1038 pass 1039 try: # Try formatting the value as an int 1040 v2 = trunc(v1) 1041 if v2 == v1: 1042 return template.format(v2) 1043 except: 1044 pass 1045 return '' 1046 1047 1048class BuiltinSublist(BuiltinFormatterFunction): 1049 name = 'sublist' 1050 arg_count = 4 1051 category = 'List manipulation' 1052 __doc__ = doc = _('sublist(val, start_index, end_index, separator) -- interpret the ' 1053 'value as a list of items separated by `separator`, returning a ' 1054 'new list made from the `start_index` to the `end_index` item. ' 1055 'The first item is number zero. If an index is negative, then it ' 1056 'counts from the end of the list. As a special case, an end_index ' 1057 'of zero is assumed to be the length of the list. Examples using ' 1058 'basic template mode and assuming that the tags column (which is ' 1059 'comma-separated) contains "A, B, C": ' 1060 '{tags:sublist(0,1,\\\\,)} returns "A". ' 1061 '{tags:sublist(-1,0,\\\\,)} returns "C". ' 1062 '{tags:sublist(0,-1,\\\\,)} returns "A, B".' 1063 ) 1064 1065 def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index, sep): 1066 if not val: 1067 return '' 1068 si = int(start_index) 1069 ei = int(end_index) 1070 # allow empty list items so counts are what the user expects 1071 val = [v.strip() for v in val.split(sep)] 1072 1073 if sep == ',': 1074 sep = ', ' 1075 try: 1076 if ei == 0: 1077 return sep.join(val[si:]) 1078 else: 1079 return sep.join(val[si:ei]) 1080 except: 1081 return '' 1082 1083 1084class BuiltinSubitems(BuiltinFormatterFunction): 1085 name = 'subitems' 1086 arg_count = 3 1087 category = 'List manipulation' 1088 __doc__ = doc = _('subitems(val, start_index, end_index) -- This function is used to ' 1089 'break apart lists of items such as genres. It interprets the value ' 1090 'as a comma-separated list of items, where each item is a period-' 1091 'separated list. Returns a new list made by first finding all the ' 1092 'period-separated items, then for each such item extracting the ' 1093 '`start_index` to the `end_index` components, then combining ' 1094 'the results back together. The first component in a period-' 1095 'separated list has an index of zero. If an index is negative, ' 1096 'then it counts from the end of the list. As a special case, an ' 1097 'end_index of zero is assumed to be the length of the list. ' 1098 'Example using basic template mode and assuming a #genre value of ' 1099 '"A.B.C": {#genre:subitems(0,1)} returns "A". {#genre:subitems(0,2)} ' 1100 'returns "A.B". {#genre:subitems(1,0)} returns "B.C". Assuming a #genre ' 1101 'value of "A.B.C, D.E.F", {#genre:subitems(0,1)} returns "A, D". ' 1102 '{#genre:subitems(0,2)} returns "A.B, D.E"') 1103 1104 period_pattern = re.compile(r'(?<=[^\.\s])\.(?=[^\.\s])', re.U) 1105 1106 def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index): 1107 if not val: 1108 return '' 1109 si = int(start_index) 1110 ei = int(end_index) 1111 has_periods = '.' in val 1112 items = [v.strip() for v in val.split(',') if v.strip()] 1113 rv = set() 1114 for item in items: 1115 if has_periods and '.' in item: 1116 components = self.period_pattern.split(item) 1117 else: 1118 components = [item] 1119 try: 1120 if ei == 0: 1121 t = '.'.join(components[si:]).strip() 1122 else: 1123 t = '.'.join(components[si:ei]).strip() 1124 if t: 1125 rv.add(t) 1126 except: 1127 pass 1128 return ', '.join(sorted(rv, key=sort_key)) 1129 1130 1131class BuiltinFormatDate(BuiltinFormatterFunction): 1132 name = 'format_date' 1133 arg_count = 2 1134 category = 'Formatting values' 1135 __doc__ = doc = _('format_date(val, format_string) -- format the value, ' 1136 'which must be a date, using the format_string, returning a string. ' 1137 'The formatting codes are: ' 1138 'd : the day as number without a leading zero (1 to 31) ' 1139 'dd : the day as number with a leading zero (01 to 31) ' 1140 'ddd : the abbreviated localized day name (e.g. "Mon" to "Sun"). ' 1141 'dddd : the long localized day name (e.g. "Monday" to "Sunday"). ' 1142 'M : the month as number without a leading zero (1 to 12). ' 1143 'MM : the month as number with a leading zero (01 to 12) ' 1144 'MMM : the abbreviated localized month name (e.g. "Jan" to "Dec"). ' 1145 'MMMM : the long localized month name (e.g. "January" to "December"). ' 1146 'yy : the year as two digit number (00 to 99). ' 1147 'yyyy : the year as four digit number. ' 1148 'h : the hours without a leading 0 (0 to 11 or 0 to 23, depending on am/pm) ' 1149 'hh : the hours with a leading 0 (00 to 11 or 00 to 23, depending on am/pm) ' 1150 'm : the minutes without a leading 0 (0 to 59) ' 1151 'mm : the minutes with a leading 0 (00 to 59) ' 1152 's : the seconds without a leading 0 (0 to 59) ' 1153 'ss : the seconds with a leading 0 (00 to 59) ' 1154 'ap : use a 12-hour clock instead of a 24-hour clock, with "ap" replaced by the localized string for am or pm ' 1155 'AP : use a 12-hour clock instead of a 24-hour clock, with "AP" replaced by the localized string for AM or PM ' 1156 'iso : the date with time and timezone. Must be the only format present ' 1157 'to_number: the date as a floating point number ' 1158 'from_number[:fmt]: format the timestamp using fmt if present otherwise iso') 1159 1160 def evaluate(self, formatter, kwargs, mi, locals, val, format_string): 1161 if not val or val == 'None': 1162 return '' 1163 try: 1164 if format_string == 'to_number': 1165 s = parse_date(val).timestamp() 1166 elif format_string.startswith('from_number'): 1167 val = datetime.fromtimestamp(float(val)) 1168 f = format_string[12:] 1169 s = format_date(val, f if f else 'iso') 1170 else: 1171 s = format_date(parse_date(val), format_string) 1172 return s 1173 except: 1174 s = 'BAD DATE' 1175 return s 1176 1177 1178class BuiltinUppercase(BuiltinFormatterFunction): 1179 name = 'uppercase' 1180 arg_count = 1 1181 category = 'String case changes' 1182 __doc__ = doc = _('uppercase(val) -- return val in upper case') 1183 1184 def evaluate(self, formatter, kwargs, mi, locals, val): 1185 return val.upper() 1186 1187 1188class BuiltinLowercase(BuiltinFormatterFunction): 1189 name = 'lowercase' 1190 arg_count = 1 1191 category = 'String case changes' 1192 __doc__ = doc = _('lowercase(val) -- return val in lower case') 1193 1194 def evaluate(self, formatter, kwargs, mi, locals, val): 1195 return val.lower() 1196 1197 1198class BuiltinTitlecase(BuiltinFormatterFunction): 1199 name = 'titlecase' 1200 arg_count = 1 1201 category = 'String case changes' 1202 __doc__ = doc = _('titlecase(val) -- return val in title case') 1203 1204 def evaluate(self, formatter, kwargs, mi, locals, val): 1205 return titlecase(val) 1206 1207 1208class BuiltinCapitalize(BuiltinFormatterFunction): 1209 name = 'capitalize' 1210 arg_count = 1 1211 category = 'String case changes' 1212 __doc__ = doc = _('capitalize(val) -- return val capitalized') 1213 1214 def evaluate(self, formatter, kwargs, mi, locals, val): 1215 return capitalize(val) 1216 1217 1218class BuiltinBooksize(BuiltinFormatterFunction): 1219 name = 'booksize' 1220 arg_count = 0 1221 category = 'Get values from metadata' 1222 __doc__ = doc = _('booksize() -- return value of the size field. ' 1223 'This function works only in the GUI. If you want to use this value ' 1224 'in save-to-disk or send-to-device templates then you ' 1225 'must make a custom "Column built from other columns", use ' 1226 'the function in that column\'s template, and use that ' 1227 'column\'s value in your save/send templates') 1228 1229 def evaluate(self, formatter, kwargs, mi, locals): 1230 if hasattr(mi, '_proxy_metadata'): 1231 try: 1232 v = mi._proxy_metadata.book_size 1233 if v is not None: 1234 return str(mi._proxy_metadata.book_size) 1235 return '' 1236 except: 1237 pass 1238 return '' 1239 return _('This function can be used only in the GUI') 1240 1241 1242class BuiltinOndevice(BuiltinFormatterFunction): 1243 name = 'ondevice' 1244 arg_count = 0 1245 category = 'Get values from metadata' 1246 __doc__ = doc = _('ondevice() -- return Yes if ondevice is set, otherwise return ' 1247 'the empty string. This function works only in the GUI. If you want to ' 1248 'use this value in save-to-disk or send-to-device templates then you ' 1249 'must make a custom "Column built from other columns", use ' 1250 'the function in that column\'s template, and use that ' 1251 'column\'s value in your save/send templates') 1252 1253 def evaluate(self, formatter, kwargs, mi, locals): 1254 if hasattr(mi, '_proxy_metadata'): 1255 if mi._proxy_metadata.ondevice_col: 1256 return _('Yes') 1257 return '' 1258 return _('This function can be used only in the GUI') 1259 1260 1261class BuiltinAnnotationCount(BuiltinFormatterFunction): 1262 name = 'annotation_count' 1263 arg_count = 0 1264 category = 'Get values from metadata' 1265 __doc__ = doc = _('annotation_count() -- return the total number of annotations ' 1266 'of all types attached to the current book. ' 1267 'This function works only in the GUI.') 1268 1269 def evaluate(self, formatter, kwargs, mi, locals): 1270 with suppress(Exception): 1271 from calibre.gui2.ui import get_gui 1272 c = get_gui().current_db.new_api.annotation_count_for_book(mi.id) 1273 return '' if c == 0 else str(c) 1274 return _('This function can be used only in the GUI') 1275 1276 1277class BuiltinIsMarked(BuiltinFormatterFunction): 1278 name = 'is_marked' 1279 arg_count = 0 1280 category = 'Get values from metadata' 1281 __doc__ = doc = _("is_marked() -- check whether the book is 'marked' in " 1282 "calibre. If it is then return the value of the mark, " 1283 "either 'true' or the comma-separated list of named " 1284 "marks. Returns '' if the book is not marked.") 1285 1286 def evaluate(self, formatter, kwargs, mi, locals): 1287 with suppress(Exception): 1288 from calibre.gui2.ui import get_gui 1289 c = get_gui().current_db.data.get_marked(mi.id) 1290 return c if c else '' 1291 return _('This function can be used only in the GUI') 1292 1293 1294class BuiltinSeriesSort(BuiltinFormatterFunction): 1295 name = 'series_sort' 1296 arg_count = 0 1297 category = 'Get values from metadata' 1298 __doc__ = doc = _('series_sort() -- return the series sort value') 1299 1300 def evaluate(self, formatter, kwargs, mi, locals): 1301 if mi.series: 1302 return title_sort(mi.series) 1303 return '' 1304 1305 1306class BuiltinHasCover(BuiltinFormatterFunction): 1307 name = 'has_cover' 1308 arg_count = 0 1309 category = 'Get values from metadata' 1310 __doc__ = doc = _('has_cover() -- return Yes if the book has a cover, ' 1311 'otherwise return the empty string') 1312 1313 def evaluate(self, formatter, kwargs, mi, locals): 1314 if mi.has_cover: 1315 return _('Yes') 1316 return '' 1317 1318 1319class BuiltinFirstNonEmpty(BuiltinFormatterFunction): 1320 name = 'first_non_empty' 1321 arg_count = -1 1322 category = 'Iterating over values' 1323 __doc__ = doc = _('first_non_empty(value [, value]*) -- ' 1324 'returns the first value that is not empty. If all values are ' 1325 'empty, then the empty string is returned. ' 1326 'You can have as many values as you want.') 1327 1328 def evaluate(self, formatter, kwargs, mi, locals, *args): 1329 i = 0 1330 while i < len(args): 1331 if args[i]: 1332 return args[i] 1333 i += 1 1334 return '' 1335 1336 1337class BuiltinAnd(BuiltinFormatterFunction): 1338 name = 'and' 1339 arg_count = -1 1340 category = 'Boolean' 1341 __doc__ = doc = _('and(value [, value]*) -- ' 1342 'returns the string "1" if all values are not empty, otherwise ' 1343 'returns the empty string. This function works well with test or ' 1344 'first_non_empty. You can have as many values as you want. In many ' 1345 'cases the && operator can replace this function.') 1346 1347 def evaluate(self, formatter, kwargs, mi, locals, *args): 1348 i = 0 1349 while i < len(args): 1350 if not args[i]: 1351 return '' 1352 i += 1 1353 return '1' 1354 1355 1356class BuiltinOr(BuiltinFormatterFunction): 1357 name = 'or' 1358 arg_count = -1 1359 category = 'Boolean' 1360 __doc__ = doc = _('or(value [, value]*) -- ' 1361 'returns the string "1" if any value is not empty, otherwise ' 1362 'returns the empty string. This function works well with test or ' 1363 'first_non_empty. You can have as many values as you want. In many ' 1364 'cases the || operator can replace this function.') 1365 1366 def evaluate(self, formatter, kwargs, mi, locals, *args): 1367 i = 0 1368 while i < len(args): 1369 if args[i]: 1370 return '1' 1371 i += 1 1372 return '' 1373 1374 1375class BuiltinNot(BuiltinFormatterFunction): 1376 name = 'not' 1377 arg_count = 1 1378 category = 'Boolean' 1379 __doc__ = doc = _('not(value) -- ' 1380 'returns the string "1" if the value is empty, otherwise ' 1381 'returns the empty string. This function works well with test or ' 1382 'first_non_empty. In many cases the ! operator can replace this ' 1383 'function.') 1384 1385 def evaluate(self, formatter, kwargs, mi, locals, val): 1386 return '' if val else '1' 1387 1388 1389class BuiltinListUnion(BuiltinFormatterFunction): 1390 name = 'list_union' 1391 arg_count = 3 1392 category = 'List manipulation' 1393 __doc__ = doc = _('list_union(list1, list2, separator) -- ' 1394 'return a list made by merging the items in list1 and list2, ' 1395 'removing duplicate items using a case-insensitive comparison. If ' 1396 'items differ in case, the one in list1 is used. ' 1397 'The items in list1 and list2 are separated by separator, as are ' 1398 'the items in the returned list. Aliases: list_union(), merge_lists()') 1399 aliases = ['merge_lists'] 1400 1401 def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator): 1402 res = {icu_lower(l.strip()): l.strip() for l in list2.split(separator) if l.strip()} 1403 res.update({icu_lower(l.strip()): l.strip() for l in list1.split(separator) if l.strip()}) 1404 if separator == ',': 1405 separator = ', ' 1406 return separator.join(res.values()) 1407 1408 1409class BuiltinListRemoveDuplicates(BuiltinFormatterFunction): 1410 name = 'list_remove_duplicates' 1411 arg_count = 2 1412 category = 'List manipulation' 1413 __doc__ = doc = _('list_remove_duplicates(list, separator) -- ' 1414 'return a list made by removing duplicate items in the source list. ' 1415 'If items differ only in case, the last of them is returned. ' 1416 'The items in source list are separated by separator, as are ' 1417 'the items in the returned list.') 1418 1419 def evaluate(self, formatter, kwargs, mi, locals, list_, separator): 1420 res = {icu_lower(l.strip()): l.strip() for l in list_.split(separator) if l.strip()} 1421 if separator == ',': 1422 separator = ', ' 1423 return separator.join(res.values()) 1424 1425 1426class BuiltinListDifference(BuiltinFormatterFunction): 1427 name = 'list_difference' 1428 arg_count = 3 1429 category = 'List manipulation' 1430 __doc__ = doc = _('list_difference(list1, list2, separator) -- ' 1431 'return a list made by removing from list1 any item found in list2, ' 1432 'using a case-insensitive comparison. The items in list1 and list2 ' 1433 'are separated by separator, as are the items in the returned list.') 1434 1435 def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator): 1436 l1 = [l.strip() for l in list1.split(separator) if l.strip()] 1437 l2 = {icu_lower(l.strip()) for l in list2.split(separator) if l.strip()} 1438 1439 res = [] 1440 for i in l1: 1441 if icu_lower(i) not in l2 and i not in res: 1442 res.append(i) 1443 if separator == ',': 1444 return ', '.join(res) 1445 return separator.join(res) 1446 1447 1448class BuiltinListIntersection(BuiltinFormatterFunction): 1449 name = 'list_intersection' 1450 arg_count = 3 1451 category = 'List manipulation' 1452 __doc__ = doc = _('list_intersection(list1, list2, separator) -- ' 1453 'return a list made by removing from list1 any item not found in list2, ' 1454 'using a case-insensitive comparison. The items in list1 and list2 ' 1455 'are separated by separator, as are the items in the returned list.') 1456 1457 def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator): 1458 l1 = [l.strip() for l in list1.split(separator) if l.strip()] 1459 l2 = {icu_lower(l.strip()) for l in list2.split(separator) if l.strip()} 1460 1461 res = [] 1462 for i in l1: 1463 if icu_lower(i) in l2 and i not in res: 1464 res.append(i) 1465 if separator == ',': 1466 return ', '.join(res) 1467 return separator.join(res) 1468 1469 1470class BuiltinListSort(BuiltinFormatterFunction): 1471 name = 'list_sort' 1472 arg_count = 3 1473 category = 'List manipulation' 1474 __doc__ = doc = _('list_sort(list, direction, separator) -- ' 1475 'return list sorted using a case-insensitive sort. If direction is ' 1476 'zero, the list is sorted ascending, otherwise descending. The list items ' 1477 'are separated by separator, as are the items in the returned list.') 1478 1479 def evaluate(self, formatter, kwargs, mi, locals, list1, direction, separator): 1480 res = [l.strip() for l in list1.split(separator) if l.strip()] 1481 if separator == ',': 1482 return ', '.join(sorted(res, key=sort_key, reverse=direction != "0")) 1483 return separator.join(sorted(res, key=sort_key, reverse=direction != "0")) 1484 1485 1486class BuiltinListEquals(BuiltinFormatterFunction): 1487 name = 'list_equals' 1488 arg_count = 6 1489 category = 'List manipulation' 1490 __doc__ = doc = _('list_equals(list1, sep1, list2, sep2, yes_val, no_val) -- ' 1491 'return yes_val if list1 and list2 contain the same items, ' 1492 'otherwise return no_val. The items are determined by splitting ' 1493 'each list using the appropriate separator character (sep1 or ' 1494 'sep2). The order of items in the lists is not relevant. ' 1495 'The comparison is case insensitive.') 1496 1497 def evaluate(self, formatter, kwargs, mi, locals, list1, sep1, list2, sep2, yes_val, no_val): 1498 s1 = {icu_lower(l.strip()) for l in list1.split(sep1) if l.strip()} 1499 s2 = {icu_lower(l.strip()) for l in list2.split(sep2) if l.strip()} 1500 if s1 == s2: 1501 return yes_val 1502 return no_val 1503 1504 1505class BuiltinListRe(BuiltinFormatterFunction): 1506 name = 'list_re' 1507 arg_count = 4 1508 category = 'List manipulation' 1509 __doc__ = doc = _('list_re(src_list, separator, include_re, opt_replace) -- ' 1510 'Construct a list by first separating src_list into items using ' 1511 'the separator character. For each item in the list, check if it ' 1512 'matches include_re. If it does, then add it to the list to be ' 1513 'returned. If opt_replace is not the empty string, then apply the ' 1514 'replacement before adding the item to the returned list.') 1515 1516 def evaluate(self, formatter, kwargs, mi, locals, src_list, separator, include_re, opt_replace): 1517 l = [l.strip() for l in src_list.split(separator) if l.strip()] 1518 res = [] 1519 for item in l: 1520 if re.search(include_re, item, flags=re.I) is not None: 1521 if opt_replace: 1522 item = re.sub(include_re, opt_replace, item) 1523 for i in [t.strip() for t in item.split(separator) if t.strip()]: 1524 if i not in res: 1525 res.append(i) 1526 if separator == ',': 1527 return ', '.join(res) 1528 return separator.join(res) 1529 1530 1531class BuiltinListReGroup(BuiltinFormatterFunction): 1532 name = 'list_re_group' 1533 arg_count = -1 1534 category = 'List manipulation' 1535 __doc__ = doc = _('list_re_group(src_list, separator, include_re, search_re [, group_template]+) -- ' 1536 'Like list_re except replacements are not optional. It ' 1537 'uses re_group(list_item, search_re, group_template, ...) when ' 1538 'doing the replacements on the resulting list.') 1539 1540 def evaluate(self, formatter, kwargs, mi, locals, src_list, separator, include_re, 1541 search_re, *args): 1542 from calibre.utils.formatter import EvalFormatter 1543 1544 l = [l.strip() for l in src_list.split(separator) if l.strip()] 1545 res = [] 1546 for item in l: 1547 def repl(mo): 1548 newval = '' 1549 if mo and mo.lastindex: 1550 for dex in range(0, mo.lastindex): 1551 gv = mo.group(dex+1) 1552 if gv is None: 1553 continue 1554 if len(args) > dex: 1555 template = args[dex].replace('[[', '{').replace(']]', '}') 1556 newval += EvalFormatter().safe_format(template, {'$': gv}, 1557 'EVAL', None, strip_results=False) 1558 else: 1559 newval += gv 1560 return newval 1561 if re.search(include_re, item, flags=re.I) is not None: 1562 item = re.sub(search_re, repl, item, flags=re.I) 1563 for i in [t.strip() for t in item.split(separator) if t.strip()]: 1564 if i not in res: 1565 res.append(i) 1566 if separator == ',': 1567 return ', '.join(res) 1568 return separator.join(res) 1569 1570 1571class BuiltinToday(BuiltinFormatterFunction): 1572 name = 'today' 1573 arg_count = 0 1574 category = 'Date functions' 1575 __doc__ = doc = _('today() -- ' 1576 'return a date string for today. This value is designed for use in ' 1577 'format_date or days_between, but can be manipulated like any ' 1578 'other string. The date is in ISO format.') 1579 1580 def evaluate(self, formatter, kwargs, mi, locals): 1581 return format_date(now(), 'iso') 1582 1583 1584class BuiltinDaysBetween(BuiltinFormatterFunction): 1585 name = 'days_between' 1586 arg_count = 2 1587 category = 'Date functions' 1588 __doc__ = doc = _('days_between(date1, date2) -- ' 1589 'return the number of days between date1 and date2. The number is ' 1590 'positive if date1 is greater than date2, otherwise negative. If ' 1591 'either date1 or date2 are not dates, the function returns the ' 1592 'empty string.') 1593 1594 def evaluate(self, formatter, kwargs, mi, locals, date1, date2): 1595 try: 1596 d1 = parse_date(date1) 1597 if d1 == UNDEFINED_DATE: 1598 return '' 1599 d2 = parse_date(date2) 1600 if d2 == UNDEFINED_DATE: 1601 return '' 1602 except: 1603 return '' 1604 i = d1 - d2 1605 return '%.1f'%(i.days + (i.seconds/(24.0*60.0*60.0))) 1606 1607 1608class BuiltinDateArithmetic(BuiltinFormatterFunction): 1609 name = 'date_arithmetic' 1610 arg_count = -1 1611 category = 'Date functions' 1612 __doc__ = doc = _('date_arithmetic(date, calc_spec, fmt) -- ' 1613 "Calculate a new date from 'date' using 'calc_spec'. Return the " 1614 "new date formatted according to optional 'fmt': if not supplied " 1615 "then the result will be in iso format. The calc_spec is a string " 1616 "formed by concatenating pairs of 'vW' (valueWhat) where 'v' is a " 1617 "possibly-negative number and W is one of the following letters: " 1618 "s: add 'v' seconds to 'date' " 1619 "m: add 'v' minutes to 'date' " 1620 "h: add 'v' hours to 'date' " 1621 "d: add 'v' days to 'date' " 1622 "w: add 'v' weeks to 'date' " 1623 "y: add 'v' years to 'date', where a year is 365 days. " 1624 "Example: '1s3d-1m' will add 1 second, add 3 days, and subtract 1 " 1625 "minute from 'date'.") 1626 1627 calc_ops = { 1628 's': lambda v: timedelta(seconds=v), 1629 'm': lambda v: timedelta(minutes=v), 1630 'h': lambda v: timedelta(hours=v), 1631 'd': lambda v: timedelta(days=v), 1632 'w': lambda v: timedelta(weeks=v), 1633 'y': lambda v: timedelta(days=v * 365), 1634 } 1635 1636 def evaluate(self, formatter, kwargs, mi, locals, date, calc_spec, fmt=None): 1637 try: 1638 d = parse_date(date) 1639 if d == UNDEFINED_DATE: 1640 return '' 1641 while calc_spec: 1642 mo = re.match(r'([-+\d]+)([smhdwy])', calc_spec) 1643 if mo is None: 1644 raise ValueError( 1645 _("{0}: invalid calculation specifier '{1}'").format( 1646 'date_arithmetic', calc_spec)) 1647 d += self.calc_ops[mo[2]](int(mo[1])) 1648 calc_spec = calc_spec[len(mo[0]):] 1649 return format_date(d, fmt if fmt else 'iso') 1650 except ValueError as e: 1651 raise e 1652 except Exception as e: 1653 traceback.print_exc() 1654 raise ValueError(_("{0}: error: {1}").format('date_arithmetic', str(e))) 1655 1656 1657class BuiltinLanguageStrings(BuiltinFormatterFunction): 1658 name = 'language_strings' 1659 arg_count = 2 1660 category = 'Get values from metadata' 1661 __doc__ = doc = _('language_strings(lang_codes, localize) -- ' 1662 'return the strings for the language codes passed in lang_codes. ' 1663 'If localize is zero, return the strings in English. If ' 1664 'localize is not zero, return the strings in the language of ' 1665 'the current locale. Lang_codes is a comma-separated list.') 1666 1667 def evaluate(self, formatter, kwargs, mi, locals, lang_codes, localize): 1668 retval = [] 1669 for c in [c.strip() for c in lang_codes.split(',') if c.strip()]: 1670 try: 1671 n = calibre_langcode_to_name(c, localize != '0') 1672 if n: 1673 retval.append(n) 1674 except: 1675 pass 1676 return ', '.join(retval) 1677 1678 1679class BuiltinLanguageCodes(BuiltinFormatterFunction): 1680 name = 'language_codes' 1681 arg_count = 1 1682 category = 'Get values from metadata' 1683 __doc__ = doc = _('language_codes(lang_strings) -- ' 1684 'return the language codes for the strings passed in lang_strings. ' 1685 'The strings must be in the language of the current locale. ' 1686 'Lang_strings is a comma-separated list.') 1687 1688 def evaluate(self, formatter, kwargs, mi, locals, lang_strings): 1689 retval = [] 1690 for c in [c.strip() for c in lang_strings.split(',') if c.strip()]: 1691 try: 1692 cv = canonicalize_lang(c) 1693 if cv: 1694 retval.append(canonicalize_lang(cv)) 1695 except: 1696 pass 1697 return ', '.join(retval) 1698 1699 1700class BuiltinCurrentLibraryName(BuiltinFormatterFunction): 1701 name = 'current_library_name' 1702 arg_count = 0 1703 category = 'Get values from metadata' 1704 __doc__ = doc = _('current_library_name() -- ' 1705 'return the last name on the path to the current calibre library. ' 1706 'This function can be called in template program mode using the ' 1707 'template "{:\'current_library_name()\'}".') 1708 1709 def evaluate(self, formatter, kwargs, mi, locals): 1710 from calibre.library import current_library_name 1711 return current_library_name() 1712 1713 1714class BuiltinCurrentLibraryPath(BuiltinFormatterFunction): 1715 name = 'current_library_path' 1716 arg_count = 0 1717 category = 'Get values from metadata' 1718 __doc__ = doc = _('current_library_path() -- ' 1719 'return the path to the current calibre library. This function can ' 1720 'be called in template program mode using the template ' 1721 '"{:\'current_library_path()\'}".') 1722 1723 def evaluate(self, formatter, kwargs, mi, locals): 1724 from calibre.library import current_library_path 1725 return current_library_path() 1726 1727 1728class BuiltinFinishFormatting(BuiltinFormatterFunction): 1729 name = 'finish_formatting' 1730 arg_count = 4 1731 category = 'Formatting values' 1732 __doc__ = doc = _('finish_formatting(val, fmt, prefix, suffix) -- apply the ' 1733 'format, prefix, and suffix to a value in the same way as ' 1734 'done in a template like `{series_index:05.2f| - |- }`. For ' 1735 'example, the following program produces the same output ' 1736 'as the above template: ' 1737 'program: finish_formatting(field("series_index"), "05.2f", " - ", " - ")') 1738 1739 def evaluate(self, formatter, kwargs, mi, locals_, val, fmt, prefix, suffix): 1740 if not val: 1741 return val 1742 return prefix + formatter._do_format(val, fmt) + suffix 1743 1744 1745class BuiltinVirtualLibraries(BuiltinFormatterFunction): 1746 name = 'virtual_libraries' 1747 arg_count = 0 1748 category = 'Get values from metadata' 1749 __doc__ = doc = _('virtual_libraries() -- return a comma-separated list of ' 1750 'Virtual libraries that contain this book. This function ' 1751 'works only in the GUI. If you want to use these values ' 1752 'in save-to-disk or send-to-device templates then you ' 1753 'must make a custom "Column built from other columns", use ' 1754 'the function in that column\'s template, and use that ' 1755 'column\'s value in your save/send templates') 1756 1757 def evaluate(self, formatter, kwargs, mi, locals_): 1758 with suppress(Exception): 1759 from calibre.gui2.ui import get_gui 1760 a = get_gui().current_db.data.get_virtual_libraries_for_books((mi.id,)) 1761 return ', '.join(a[mi.id]) 1762 return _('This function can be used only in the GUI') 1763 1764 1765class BuiltinCurrentVirtualLibraryName(BuiltinFormatterFunction): 1766 name = 'current_virtual_library_name' 1767 arg_count = 0 1768 category = 'Get values from metadata' 1769 __doc__ = doc = _('current_virtual_library_name() -- ' 1770 'return the name of the current virtual library if there is one, ' 1771 'otherwise the empty string. Library name case is preserved. ' 1772 'Example: "program: current_virtual_library_name()".') 1773 1774 def evaluate(self, formatter, kwargs, mi, locals): 1775 with suppress(Exception): 1776 from calibre.gui2.ui import get_gui 1777 return get_gui().current_db.data.get_base_restriction_name() 1778 return _('This function can be used only in the GUI') 1779 1780 1781class BuiltinUserCategories(BuiltinFormatterFunction): 1782 name = 'user_categories' 1783 arg_count = 0 1784 category = 'Get values from metadata' 1785 __doc__ = doc = _('user_categories() -- return a comma-separated list of ' 1786 'the user categories that contain this book. This function ' 1787 'works only in the GUI. If you want to use these values ' 1788 'in save-to-disk or send-to-device templates then you ' 1789 'must make a custom "Column built from other columns", use ' 1790 'the function in that column\'s template, and use that ' 1791 'column\'s value in your save/send templates') 1792 1793 def evaluate(self, formatter, kwargs, mi, locals_): 1794 if hasattr(mi, '_proxy_metadata'): 1795 cats = {k for k, v in iteritems(mi._proxy_metadata.user_categories) if v} 1796 cats = sorted(cats, key=sort_key) 1797 return ', '.join(cats) 1798 return _('This function can be used only in the GUI') 1799 1800 1801class BuiltinTransliterate(BuiltinFormatterFunction): 1802 name = 'transliterate' 1803 arg_count = 1 1804 category = 'String manipulation' 1805 __doc__ = doc = _('transliterate(a) -- Returns a string in a latin alphabet ' 1806 'formed by approximating the sound of the words in the ' 1807 'source string. For example, if the source is "{0}"' 1808 ' the function returns "{1}".').format( 1809 "Фёдор Миха́йлович Достоевский", 'Fiodor Mikhailovich Dostoievskii') 1810 1811 def evaluate(self, formatter, kwargs, mi, locals, source): 1812 from calibre.utils.filenames import ascii_text 1813 return ascii_text(source) 1814 1815 1816class BuiltinAuthorLinks(BuiltinFormatterFunction): 1817 name = 'author_links' 1818 arg_count = 2 1819 category = 'Get values from metadata' 1820 __doc__ = doc = _('author_links(val_separator, pair_separator) -- returns ' 1821 'a string containing a list of authors and that author\'s ' 1822 'link values in the ' 1823 'form author1 val_separator author1link pair_separator ' 1824 'author2 val_separator author2link etc. An author is ' 1825 'separated from its link value by the val_separator string ' 1826 'with no added spaces. author:linkvalue pairs are separated ' 1827 'by the pair_separator string argument with no added spaces. ' 1828 'It is up to you to choose separator strings that do ' 1829 'not occur in author names or links. An author is ' 1830 'included even if the author link is empty.') 1831 1832 def evaluate(self, formatter, kwargs, mi, locals, val_sep, pair_sep): 1833 if hasattr(mi, '_proxy_metadata'): 1834 link_data = mi._proxy_metadata.author_link_map 1835 if not link_data: 1836 return '' 1837 names = sorted(link_data.keys(), key=sort_key) 1838 return pair_sep.join(n + val_sep + link_data[n] for n in names) 1839 return _('This function can be used only in the GUI') 1840 1841 1842class BuiltinAuthorSorts(BuiltinFormatterFunction): 1843 name = 'author_sorts' 1844 arg_count = 1 1845 category = 'Get values from metadata' 1846 __doc__ = doc = _('author_sorts(val_separator) -- returns a string ' 1847 'containing a list of author\'s sort values for the ' 1848 'authors of the book. The sort is the one in the author ' 1849 'metadata (different from the author_sort in books). The ' 1850 'returned list has the form author sort 1 val_separator ' 1851 'author sort 2 etc. The author sort values in this list ' 1852 'are in the same order as the authors of the book. If ' 1853 'you want spaces around val_separator then include them ' 1854 'in the separator string') 1855 1856 def evaluate(self, formatter, kwargs, mi, locals, val_sep): 1857 sort_data = mi.author_sort_map 1858 if not sort_data: 1859 return '' 1860 names = [sort_data.get(n) for n in mi.authors if n.strip()] 1861 return val_sep.join(n for n in names) 1862 1863 1864class BuiltinConnectedDeviceName(BuiltinFormatterFunction): 1865 name = 'connected_device_name' 1866 arg_count = 1 1867 category = 'Get values from metadata' 1868 __doc__ = doc = _("connected_device_name(storage_location) -- if a device is " 1869 "connected then return the device name, otherwise return " 1870 "the empty string. Each storage location on a device can " 1871 "have a different name. The location names are 'main', " 1872 "'carda' and 'cardb'. This function works only in the GUI.") 1873 1874 def evaluate(self, formatter, kwargs, mi, locals, storage_location): 1875 with suppress(Exception): 1876 # Do the import here so that we don't entangle the GUI when using 1877 # command line functions 1878 from calibre.gui2.ui import get_gui 1879 info = get_gui().device_manager.get_current_device_information() 1880 if info is None: 1881 return '' 1882 try: 1883 if storage_location not in {'main', 'carda', 'cardb'}: 1884 raise ValueError( 1885 _('connected_device_name: invalid storage location "{}"' 1886 .format(storage_location))) 1887 info = info['info'][4] 1888 if storage_location not in info: 1889 return '' 1890 return info[storage_location]['device_name'] 1891 except Exception: 1892 traceback.print_exc() 1893 raise 1894 return _('This function can be used only in the GUI') 1895 1896 1897class BuiltinConnectedDeviceUUID(BuiltinFormatterFunction): 1898 name = 'connected_device_uuid' 1899 arg_count = 1 1900 category = 'Get values from metadata' 1901 __doc__ = doc = _("connected_device_uuid(storage_location) -- if a device is " 1902 "connected then return the device uuid (unique id), " 1903 "otherwise return the empty string. Each storage location " 1904 "on a device has a different uuid. The location names are " 1905 "'main', 'carda' and 'cardb'. This function works only in " 1906 "the GUI.") 1907 1908 def evaluate(self, formatter, kwargs, mi, locals, storage_location): 1909 with suppress(Exception): 1910 # Do the import here so that we don't entangle the GUI when using 1911 # command line functions 1912 from calibre.gui2.ui import get_gui 1913 info = get_gui().device_manager.get_current_device_information() 1914 if info is None: 1915 return '' 1916 try: 1917 if storage_location not in {'main', 'carda', 'cardb'}: 1918 raise ValueError( 1919 _('connected_device_name: invalid storage location "{}"' 1920 .format(storage_location))) 1921 info = info['info'][4] 1922 if storage_location not in info: 1923 return '' 1924 return info[storage_location]['device_store_uuid'] 1925 except Exception: 1926 traceback.print_exc() 1927 raise 1928 return _('This function can be used only in the GUI') 1929 1930 1931class BuiltinCheckYesNo(BuiltinFormatterFunction): 1932 name = 'check_yes_no' 1933 arg_count = 4 1934 category = 'If-then-else' 1935 __doc__ = doc = _('check_yes_no(field_name, is_undefined, is_false, is_true) ' 1936 '-- checks the value of the yes/no field named by the ' 1937 'lookup key field_name for a value specified by the ' 1938 'parameters, returning "yes" if a match is found, otherwise ' 1939 'returning an empty string. Set the parameter is_undefined, ' 1940 'is_false, or is_true to 1 (the number) to check that ' 1941 'condition, otherwise set it to 0. Example: ' 1942 'check_yes_no("#bool", 1, 0, 1) returns "yes" if the ' 1943 'yes/no field "#bool" is either undefined (neither True ' 1944 'nor False) or True. More than one of is_undefined, ' 1945 'is_false, or is_true can be set to 1. This function ' 1946 'is usually used by the test() or is_empty() functions.') 1947 1948 def evaluate(self, formatter, kwargs, mi, locals, field, is_undefined, is_false, is_true): 1949 res = getattr(mi, field, None) 1950 if res is None: 1951 if is_undefined == '1': 1952 return 'yes' 1953 return "" 1954 if not isinstance(res, bool): 1955 raise ValueError(_('check_yes_no requires the field be a Yes/No custom column')) 1956 if is_false == '1' and not res: 1957 return 'yes' 1958 if is_true == '1' and res: 1959 return 'yes' 1960 return "" 1961 1962 1963class BuiltinRatingToStars(BuiltinFormatterFunction): 1964 name = 'rating_to_stars' 1965 arg_count = 2 1966 category = 'Formatting values' 1967 __doc__ = doc = _('rating_to_stars(value, use_half_stars) ' 1968 '-- Returns the rating as string of star characters. ' 1969 'The value is a number between 0 and 5. Set use_half_stars ' 1970 'to 1 if you want half star characters for custom ratings ' 1971 'columns that support non-integer ratings, for example 2.5.') 1972 1973 def evaluate(self, formatter, kwargs, mi, locals, value, use_half_stars): 1974 if not value: 1975 return '' 1976 err_msg = _('The rating must be a number between 0 and 5') 1977 try: 1978 v = float(value) * 2 1979 except: 1980 raise ValueError(err_msg) 1981 if v < 0 or v > 10: 1982 raise ValueError(err_msg) 1983 from calibre.ebooks.metadata import rating_to_stars 1984 return rating_to_stars(v, use_half_stars == '1') 1985 1986 1987class BuiltinSwapAroundArticles(BuiltinFormatterFunction): 1988 name = 'swap_around_articles' 1989 arg_count = 2 1990 category = 'String manipulation' 1991 __doc__ = doc = _('swap_around_articles(val, separator) ' 1992 '-- returns the val with articles moved to the end. ' 1993 'The value can be a list, in which case each member ' 1994 'of the list is processed. If the value is a list then ' 1995 'you must provide the list value separator. If no ' 1996 'separator is provided then the value is treated as ' 1997 'being a single value, not a list.') 1998 1999 def evaluate(self, formatter, kwargs, mi, locals, val, separator): 2000 if not val: 2001 return '' 2002 if not separator: 2003 return title_sort(val).replace(',', ';') 2004 result = [] 2005 try: 2006 for v in [x.strip() for x in val.split(separator)]: 2007 result.append(title_sort(v).replace(',', ';')) 2008 except: 2009 traceback.print_exc() 2010 return separator.join(sorted(result, key=sort_key)) 2011 2012 2013class BuiltinArguments(BuiltinFormatterFunction): 2014 name = 'arguments' 2015 arg_count = -1 2016 category = 'other' 2017 __doc__ = doc = _('arguments(id[=expression] [, id[=expression]]*) ' 2018 '-- Used in a stored template to retrieve the arguments ' 2019 'passed in the call. It both declares and initializes ' 2020 'local variables, effectively parameters. The variables ' 2021 'are positional; they get the value of the value given ' 2022 'in the call in the same position. If the corresponding ' 2023 'parameter is not provided in the call then arguments ' 2024 'assigns that variable the provided default value. If ' 2025 'there is no default value then the variable is set to ' 2026 'the empty string.') 2027 2028 def evaluate(self, formatter, kwargs, mi, locals, *args): 2029 # The arguments function is implemented in-line in the formatter 2030 raise NotImplementedError() 2031 2032 2033class BuiltinGlobals(BuiltinFormatterFunction): 2034 name = 'globals' 2035 arg_count = -1 2036 category = 'other' 2037 __doc__ = doc = _('globals(id[=expression] [, id[=expression]]*) ' 2038 '-- Retrieves "global variables" that can be passed into ' 2039 'the formatter. It both declares and initializes local ' 2040 'variables with the names of the global variables passed ' 2041 'in. If the corresponding variable is not provided in ' 2042 'the passed-in globals then it assigns that variable the ' 2043 'provided default value. If there is no default value ' 2044 'then the variable is set to the empty string.') 2045 2046 def evaluate(self, formatter, kwargs, mi, locals, *args): 2047 # The globals function is implemented in-line in the formatter 2048 raise NotImplementedError() 2049 2050 2051class BuiltinSetGlobals(BuiltinFormatterFunction): 2052 name = 'set_globals' 2053 arg_count = -1 2054 category = 'other' 2055 __doc__ = doc = _('globals(id[=expression] [, id[=expression]]*) ' 2056 '-- Retrieves "global variables" that can be passed into ' 2057 'the formatter. It both declares and initializes local ' 2058 'variables with the names of the global variables passed ' 2059 'in. If the corresponding variable is not provided in ' 2060 'the passed-in globals then it assigns that variable the ' 2061 'provided default value. If there is no default value ' 2062 'then the variable is set to the empty string.') 2063 2064 def evaluate(self, formatter, kwargs, mi, locals, *args): 2065 # The globals function is implemented in-line in the formatter 2066 raise NotImplementedError() 2067 2068 2069class BuiltinFieldExists(BuiltinFormatterFunction): 2070 name = 'field_exists' 2071 arg_count = 1 2072 category = 'If-then-else' 2073 __doc__ = doc = _('field_exists(field_name) -- checks if a field ' 2074 '(column) named field_name exists, returning ' 2075 "'1' if so and '' if not.") 2076 2077 def evaluate(self, formatter, kwargs, mi, locals, field_name): 2078 if field_name.lower() in mi.all_field_keys(): 2079 return '1' 2080 return '' 2081 2082 2083class BuiltinCharacter(BuiltinFormatterFunction): 2084 name = 'character' 2085 arg_count = 1 2086 category = 'String manipulation' 2087 __doc__ = doc = _('character(character_name) -- returns the ' 2088 'character named by character_name. For example, ' 2089 r"character('newline') returns a newline character ('\n'). " 2090 "The supported character names are 'newline', 'return', " 2091 "'tab', and 'backslash'.") 2092 2093 def evaluate(self, formatter, kwargs, mi, locals, character_name): 2094 # The globals function is implemented in-line in the formatter 2095 raise NotImplementedError() 2096 2097 2098_formatter_builtins = [ 2099 BuiltinAdd(), BuiltinAnd(), BuiltinApproximateFormats(), BuiltinArguments(), 2100 BuiltinAssign(), 2101 BuiltinAuthorLinks(), BuiltinAuthorSorts(), BuiltinBooksize(), 2102 BuiltinCapitalize(), BuiltinCharacter(), BuiltinCheckYesNo(), BuiltinCeiling(), 2103 BuiltinCmp(), BuiltinConnectedDeviceName(), BuiltinConnectedDeviceUUID(), BuiltinContains(), 2104 BuiltinCount(), BuiltinCurrentLibraryName(), BuiltinCurrentLibraryPath(), 2105 BuiltinCurrentVirtualLibraryName(), BuiltinDateArithmetic(), 2106 BuiltinDaysBetween(), BuiltinDivide(), BuiltinEval(), BuiltinFirstNonEmpty(), 2107 BuiltinField(), BuiltinFieldExists(), 2108 BuiltinFinishFormatting(), BuiltinFirstMatchingCmp(), BuiltinFloor(), 2109 BuiltinFormatDate(), BuiltinFormatNumber(), BuiltinFormatsModtimes(), 2110 BuiltinFormatsPaths(), BuiltinFormatsSizes(), BuiltinFractionalPart(), 2111 BuiltinGlobals(), 2112 BuiltinHasCover(), BuiltinHumanReadable(), BuiltinIdentifierInList(), 2113 BuiltinIfempty(), BuiltinLanguageCodes(), BuiltinLanguageStrings(), 2114 BuiltinInList(), BuiltinIsMarked(), BuiltinListCountMatching(), 2115 BuiltinListDifference(), BuiltinListEquals(), 2116 BuiltinListIntersection(), BuiltinListitem(), BuiltinListRe(), 2117 BuiltinListReGroup(), BuiltinListRemoveDuplicates(), BuiltinListSort(), 2118 BuiltinListSplit(), BuiltinListUnion(),BuiltinLookup(), 2119 BuiltinLowercase(), BuiltinMod(), BuiltinMultiply(), BuiltinNot(), BuiltinOndevice(), 2120 BuiltinOr(), BuiltinPrint(), BuiltinRatingToStars(), BuiltinRawField(), BuiltinRawList(), 2121 BuiltinRe(), BuiltinReGroup(), BuiltinRound(), BuiltinSelect(), BuiltinSeriesSort(), 2122 BuiltinSetGlobals(), BuiltinShorten(), BuiltinStrcat(), BuiltinStrcatMax(), 2123 BuiltinStrcmp(), BuiltinStrInList(), BuiltinStrlen(), BuiltinSubitems(), 2124 BuiltinSublist(),BuiltinSubstr(), BuiltinSubtract(), BuiltinSwapAroundArticles(), 2125 BuiltinSwapAroundComma(), BuiltinSwitch(), 2126 BuiltinTemplate(), BuiltinTest(), BuiltinTitlecase(), 2127 BuiltinToday(), BuiltinTransliterate(), BuiltinUppercase(), 2128 BuiltinUserCategories(), BuiltinVirtualLibraries(), BuiltinAnnotationCount() 2129] 2130 2131 2132class FormatterUserFunction(FormatterFunction): 2133 2134 def __init__(self, name, doc, arg_count, program_text, is_python): 2135 self.is_python = is_python 2136 self.name = name 2137 self.doc = doc 2138 self.arg_count = arg_count 2139 self.program_text = program_text 2140 self.cached_parse_tree = None 2141 2142 def to_pref(self): 2143 return [self.name, self.doc, self.arg_count, self.program_text] 2144 2145 2146tabs = re.compile(r'^\t*') 2147 2148 2149def function_pref_is_python(pref): 2150 if isinstance(pref, list): 2151 pref = pref[3] 2152 if pref.startswith('def'): 2153 return True 2154 if pref.startswith('program'): 2155 return False 2156 raise ValueError('Unknown program type in formatter function pref') 2157 2158 2159def function_pref_name(pref): 2160 return pref[0] 2161 2162 2163def compile_user_function(name, doc, arg_count, eval_func): 2164 if not function_pref_is_python(eval_func): 2165 return FormatterUserFunction(name, doc, arg_count, eval_func, False) 2166 2167 def replace_func(mo): 2168 return mo.group().replace('\t', ' ') 2169 2170 func = ' ' + '\n '.join([tabs.sub(replace_func, line) 2171 for line in eval_func.splitlines()]) 2172 prog = ''' 2173from calibre.utils.formatter_functions import FormatterUserFunction 2174from calibre.utils.formatter_functions import formatter_functions 2175class UserFunction(FormatterUserFunction): 2176''' + func 2177 locals_ = {} 2178 if DEBUG and tweaks.get('enable_template_debug_printing', False): 2179 print(prog) 2180 exec(prog, locals_) 2181 cls = locals_['UserFunction'](name, doc, arg_count, eval_func, True) 2182 return cls 2183 2184 2185def compile_user_template_functions(funcs): 2186 compiled_funcs = {} 2187 for func in funcs: 2188 try: 2189 # Force a name conflict to test the logic 2190 # if func[0] == 'myFunc2': 2191 # func[0] = 'myFunc3' 2192 2193 # Compile the function so that the tab processing is done on the 2194 # source. This helps ensure that if the function already is defined 2195 # then white space differences don't cause them to compare differently 2196 2197 cls = compile_user_function(*func) 2198 cls.is_python = function_pref_is_python(func) 2199 compiled_funcs[cls.name] = cls 2200 except Exception: 2201 try: 2202 func_name = func[0] 2203 except Exception: 2204 func_name = 'Unknown' 2205 prints('**** Compilation errors in user template function "%s" ****' % func_name) 2206 traceback.print_exc(limit=10) 2207 prints('**** End compilation errors in %s "****"' % func_name) 2208 return compiled_funcs 2209 2210 2211def load_user_template_functions(library_uuid, funcs, precompiled_user_functions=None): 2212 unload_user_template_functions(library_uuid) 2213 if precompiled_user_functions: 2214 compiled_funcs = precompiled_user_functions 2215 else: 2216 compiled_funcs = compile_user_template_functions(funcs) 2217 formatter_functions().register_functions(library_uuid, list(compiled_funcs.values())) 2218 2219 2220def unload_user_template_functions(library_uuid): 2221 formatter_functions().unregister_functions(library_uuid) 2222