View Single Post
Old 02-19-2012, 07:47 AM   #13
chaley
"chaley", not "charley"
chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.chaley ought to be getting tired of karma fortunes by now.
 
Posts: 4,919
Karma: 802172
Join Date: Jan 2010
Location: France
Device: Many android devices
The complexity of what you want to do has reached the point where the right solution is to write a custom python template function. That requires some python programming skills.

The following template will work:
Code:
{#genre:'list_union('', re(list_re($, ',', '(?i)^(?:thrillers\.|history\.)?(.*)', '\1'), '\.', ', '), ',')'}
This template will have a significant performance impact as your library grows. My guess is that you won't notice much until you cross a thousand books or so, but it will be there. A custom template function will probably be some 100 times faster than this template.

Working from the inside out, the template uses list_re to prepare a list with the undesired prefixes removed. In the example, if it finds "thrillers." or "history.", then it removes it. The word "thrillers" by itself is left because it doesn't have a period after it. The 're' at the next level out converts all dots to commas. The list_union removes duplicates.

For completeness, here is a custom template function that does the job (I think). You can use preferences -> template functions to define it.
Code:
name: whatever you want to call it. I called it "myFunc"
arg count: 2
documentation: whatever you want to say
program code:
def evaluate(self, formatter, kwargs, mi, locals, l, excludes):
	from calibre.utils.icu import sort_key
	excl = [v.strip() for v in excludes.split(',')]
	items = [v.strip() for v in l.split(',')]
	result = set()
	for item in items:
		parts = item.split('.')
		if len(parts) > 1 and parts[0].lower() in excl:
			start = 1
		else:
			start = 0
		result.update(parts[start:])
	return ', '.join(sorted(result, key=sort_key))
You call the function using a template like the following:
Code:
{#genre:'myFunc($, 'thrillers, history')'}
The template uses template program mode because the second argument contains commas inside the string value.

The function returns a sorted list of items with all genres split into separate items at the period. Genre prefixes in the second argument ('thrillers, history' in the example) are removed from the result, but genres exactly equaling one of these items are not removed.
chaley is offline   Reply With Quote