Thread: Pen names
View Single Post
Old 06-20-2023, 01:58 PM   #15
chaley
Grand Sorcerer
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: 12,453
Karma: 8012886
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
Although I don't use pen names, for fun I looked at how I would solve the problem for myself.

I started with @capink's idea to use a 3-level Aliases user category: Aliases.real_name.pen_names.

I then wanted to add links to book details for:
  • All books by the person with this pen name. By this I mean all books by the middle level of the Aliases category: real_name.
  • All pen names for the author of this book. These links search for books by the displayed pen names.
I also wanted to add a column icon to the author column indicating that that an author for the book has pen names.

I added two composite columns: Real names and Pen names. The Real Names column is a "Column built from other columns, behaves like tags." The Pen names column is a "Column built from other columns, show as HTML."

This screen capture shows the result. The comma in the names in the Real names column looks a bit weird because commas cannot in in values in tags-like composite columns. I used a quill image for the column icon.
Click image for larger version

Name:	Clipboard02.jpg
Views:	395
Size:	253.9 KB
ID:	202172

Clicking a "Real name" link will select all books by that author, including pen names. Clicking a "Pen name" link will select only books with that pen name as an author.

The pen names column is implemented using calibre search URLs.

Here are the column definitions and templates. The templates need a bit more work for performance.

Real names:
Click image for larger version

Name:	Clipboard03.jpg
Views:	335
Size:	45.9 KB
ID:	202173
Code:
python:
def evaluate(book, context):
	from collections import defaultdict
	from calibre.utils.icu import sort_key

	alias_category = 'Aliases.'
	alias_len = len(alias_category)
	fake_comma = '⸴'

	db = context.db.new_api
	aliases = defaultdict(list)
	# Parse the user categories, getting the real names. Build an inverted dict
	# pen_name: list(real_names). Why a list? Because a pen name can be used by
	# more than one real author.
	for real_name,pen_names in {cat[alias_len:]:vals
			for cat,vals in db.pref('user_categories').items()
				if cat.startswith(alias_category)}.items():
		for pn in pen_names:
			aliases[pn[0]].append(real_name)

	# For each author (pen name) for this book, get the real author(s)
	ans = set()
	for aut in book.authors:
		if aut in aliases:
			for real_name in aliases[aut]:
				# Replace commas with a fake comma because commas are used 
				# by calibre to separate values
				ans.add(real_name.replace(',', fake_comma) )
	return ', '.join(sorted(ans, key=sort_key))
Pen names:
Click image for larger version

Name:	Clipboard04.jpg
Views:	322
Size:	49.6 KB
ID:	202174
Code:
python:
def evaluate(book, context):
	from collections import defaultdict
	from calibre.utils.icu import sort_key

	alias_category = 'Aliases.'
	link_break = '<br/>'

	db = context.db.new_api

	# Build a set of other pen names for each author that is a pen name.
	# This loop only processes categories prefixed with alias_category
	all_pen_names = defaultdict(set)
	for pen_names in [[v[0] for v in vals]
			for cat,vals in db.pref('user_categories').items()
				if cat.startswith(alias_category)]:
		for pn in pen_names:
			all_pen_names[pn].update(pen_names)

	# Build the set of all pen names for all the authors of this book. Use a set
	# to remove duplicates.
	auts = set()
	for p in book.authors:
		if p in all_pen_names:
			auts.update(all_pen_names[p])

	# Now build the list of calibre search link URLs , one for each pen name.
	ans = list()
	for p in sorted(auts, key=sort_key): 
		# Replace any spaces with non-breaking spaces to prevent a name from
		# wrapping over two lines.
		ans.append(f'''<a href="calibre://search/_?eq='''
			f'''{('authors:"='+p + '"').encode().hex()}">'''
			f'''{p.replace(' ', '&nbsp;')}</a>''')

	# Combine all the URLS into a single string with the URLS separated by link_break.
	return link_break.join(ans)
EDIT 1:
  • Templates: added comments, now sort using locale rules, small performance enhancements.
  • It is best to hide the pen names column in the book list. It doesn't show anything useable and would be calculated when it isn't needed, for example during scrolling.
  • Change 'alias_category' if you use something other than "Aliases" as the alias parent user category name.
  • In 'Pen Names', change 'link_break' to ', ' (comma space) if you don't want each pen name on a separate line.

EDIT 2: added links to @capink's post describing the user category setup.

Last edited by chaley; 06-21-2023 at 03:34 PM.
chaley is offline   Reply With Quote