def show_details(self, index): f = rating_font() book = self.model().data(index, Qt.UserRole) parts = [ '<center>', '<h2>%s</h2>'%book.title, '<div><i>%s</i></div>'%authors_to_string(book.authors), ] if not book.is_null('series'): series = book.format_field('series') if series[1]: parts.append('<div>%s: %s</div>'%series) if not book.is_null('rating'): style = 'style=\'font-family:"%s"\''%f parts.append('<div %s>%s</div>'%(style, rating_to_stars(int(2 * book.rating)))) parts.append('</center>') if book.identifiers: urls = urls_from_identifiers(book.identifiers) ids = ['<a href="%s">%s</a>'%(url, name) for name, ign, ign, url in urls] if ids: parts.append('<div><b>%s:</b> %s</div><br>'%(_('See at'), ', '.join(ids))) if book.tags: parts.append('<div>%s</div><div>\u00a0</div>'%', '.join(book.tags)) if book.comments: parts.append(comments_to_html(book.comments)) self.show_details_signal.emit(''.join(parts))
def data(self, index, role=Qt.DisplayRole): if role == Qt.DisplayRole: val = index.row() * (1 if self.is_half_star else 2) return rating_to_stars(val, self.is_half_star) or self.null_text if role == Qt.FontRole: return QApplication.instance().font() if index.row( ) == 0 else self.rating_font
def show_details(self, index): f = rating_font() book = self.model().data(index, Qt.UserRole) parts = [ '<center>', '<h2>%s</h2>' % book.title, '<div><i>%s</i></div>' % authors_to_string(book.authors), ] if not book.is_null('series'): series = book.format_field('series') if series[1]: parts.append('<div>%s: %s</div>' % series) if not book.is_null('rating'): style = 'style=\'font-family:"%s"\'' % f parts.append('<div %s>%s</div>' % (style, rating_to_stars(int(2 * book.rating)))) parts.append('</center>') if book.identifiers: urls = urls_from_identifiers(book.identifiers) ids = [ '<a href="%s">%s</a>' % (url, name) for name, ign, ign, url in urls ] if ids: parts.append('<div><b>%s:</b> %s</div><br>' % (_('See at'), ', '.join(ids))) if book.tags: parts.append('<div>%s</div><div>\u00a0</div>' % ', '.join(book.tags)) if book.comments: parts.append(comments_to_html(book.comments)) self.show_details_signal.emit(''.join(parts))
def subtitle(self, index): if gprefs['show_rating_in_cover_browser']: try: return rating_to_stars(self.model.rating(index) * 2) except: pass return ''
def subtitle(self, index): try: db = self.model.db.new_api if not self.template_inited: self.init_template(db) field = db.pref('cover_browser_subtitle_field', 'rating') if field and field != 'none': book_id = self.model.id(index) fm = db.field_metadata[field] if fm['datatype'] == 'rating': val = db.field_for(field, book_id, default_value=0) if val: return rating_to_stars( val, allow_half_stars=db.field_metadata[field] ['display'].get('allow_half_stars')) else: return self.render_template('{%s}' % field, index, db).replace('&', '&&') except Exception: if not self.subtitle_error_reported: self.subtitle_error_reported = True import traceback traceback.print_exc() return ''
def ACQUISITION_ENTRY(book_id, updated, request_context): field_metadata = request_context.db.field_metadata mi = request_context.db.get_metadata(book_id) extra = [] if (mi.rating or 0) > 0: rating = rating_to_stars(mi.rating) extra.append(_('RATING: %s<br />')%rating) if mi.tags: extra.append(_('TAGS: %s<br />')%xml(format_tag_string(mi.tags, None))) if mi.series: extra.append(_('SERIES: %(series)s [%(sidx)s]<br />')% dict(series=xml(mi.series), sidx=fmt_sidx(float(mi.series_index)))) for key in filter(request_context.ctx.is_field_displayable, field_metadata.ignorable_field_keys()): name, val = mi.format_field(key) if val: fm = field_metadata[key] datatype = fm['datatype'] if datatype == 'text' and fm['is_multiple']: extra.append('%s: %s<br />'% (xml(name), xml(format_tag_string(val, fm['is_multiple']['ui_to_list'], joinval=fm['is_multiple']['list_to_ui'])))) elif datatype == 'comments' or (fm['datatype'] == 'composite' and fm['display'].get('contains_html', False)): extra.append('%s: %s<br />'%(xml(name), comments_to_html(str(val)))) else: extra.append('%s: %s<br />'%(xml(name), xml(str(val)))) if mi.comments: comments = comments_to_html(mi.comments) extra.append(comments) if extra: extra = html_to_lxml('\n'.join(extra)) ans = E.entry(TITLE(mi.title), E.author(E.name(authors_to_string(mi.authors))), ID('urn:uuid:' + mi.uuid), UPDATED(mi.last_modified), E.published(mi.timestamp.isoformat())) if mi.pubdate and not is_date_undefined(mi.pubdate): ans.append(ans.makeelement('{%s}date' % DC_NS)) ans[-1].text = mi.pubdate.isoformat() if len(extra): ans.append(E.content(extra, type='xhtml')) get = partial(request_context.ctx.url_for, '/get', book_id=book_id, library_id=request_context.library_id) if mi.formats: fm = mi.format_metadata for fmt in mi.formats: fmt = fmt.lower() mt = guess_type('a.'+fmt)[0] if mt: link = E.link(type=mt, href=get(what=fmt), rel="http://opds-spec.org/acquisition") ffm = fm.get(fmt.upper()) if ffm: link.set('length', str(ffm['size'])) link.set('mtime', ffm['mtime'].isoformat()) ans.append(link) ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel="http://opds-spec.org/cover")) ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel="http://opds-spec.org/thumbnail")) ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel="http://opds-spec.org/image")) ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel="http://opds-spec.org/image/thumbnail")) return ans
def ACQUISITION_ENTRY(book_id, updated, request_context): field_metadata = request_context.db.field_metadata mi = request_context.db.get_metadata(book_id) extra = [] if mi.rating > 0: rating = rating_to_stars(mi.rating) extra.append(_('RATING: %s<br />')%rating) if mi.tags: extra.append(_('TAGS: %s<br />')%xml(format_tag_string(mi.tags, None))) if mi.series: extra.append(_('SERIES: %(series)s [%(sidx)s]<br />')% dict(series=xml(mi.series), sidx=fmt_sidx(float(mi.series_index)))) for key in filter(request_context.ctx.is_field_displayable, field_metadata.ignorable_field_keys()): name, val = mi.format_field(key) if val: fm = field_metadata[key] datatype = fm['datatype'] if datatype == 'text' and fm['is_multiple']: extra.append('%s: %s<br />'% (xml(name), xml(format_tag_string(val, fm['is_multiple']['ui_to_list'], joinval=fm['is_multiple']['list_to_ui'])))) elif datatype == 'comments' or (fm['datatype'] == 'composite' and fm['display'].get('contains_html', False)): extra.append('%s: %s<br />'%(xml(name), comments_to_html(unicode(val)))) else: extra.append('%s: %s<br />'%(xml(name), xml(unicode(val)))) if mi.comments: comments = comments_to_html(mi.comments) extra.append(comments) if extra: extra = html_to_lxml('\n'.join(extra)) ans = E.entry(TITLE(mi.title), E.author(E.name(authors_to_string(mi.authors))), ID('urn:uuid:' + mi.uuid), UPDATED(mi.last_modified), E.published(mi.timestamp.isoformat())) if mi.pubdate and not is_date_undefined(mi.pubdate): ans.append(ans.makeelement('{%s}date' % DC_NS)) ans[-1].text = mi.pubdate.isoformat() if len(extra): ans.append(E.content(extra, type='xhtml')) get = partial(request_context.ctx.url_for, '/get', book_id=book_id, library_id=request_context.library_id) if mi.formats: fm = mi.format_metadata for fmt in mi.formats: fmt = fmt.lower() mt = guess_type('a.'+fmt)[0] if mt: link = E.link(type=mt, href=get(what=fmt), rel="http://opds-spec.org/acquisition") ffm = fm.get(fmt.upper()) if ffm: link.set('length', str(ffm['size'])) link.set('mtime', ffm['mtime'].isoformat()) ans.append(link) ans.append(E.link(type='image/jpeg', href=get(what='cover'), rel="http://opds-spec.org/cover")) ans.append(E.link(type='image/jpeg', href=get(what='thumb'), rel="http://opds-spec.org/thumbnail")) return ans
def __init__(self, name, table, bools_are_tristate, get_template_functions): self.name, self.table = name, table dt = self.metadata['datatype'] self.has_text_data = dt in { 'text', 'comments', 'series', 'enumeration' } self.table_type = self.table.table_type self._sort_key = (sort_key if dt in ('text', 'series', 'enumeration') else IDENTITY) # This will be compared to the output of sort_key() which is a # bytestring, therefore it is safer to have it be a bytestring. # Coercing an empty bytestring to unicode will never fail, but the # output of sort_key cannot be coerced to unicode self._default_sort_key = b'' if dt in {'int', 'float', 'rating'}: self._default_sort_key = 0 self._sort_key = numeric_sort_key elif dt == 'bool': self._default_sort_key = None self._sort_key = bool_sort_key(bools_are_tristate) elif dt == 'datetime': self._default_sort_key = UNDEFINED_DATE if tweaks['sort_dates_using_visible_fields']: fmt = None if name in {'timestamp', 'pubdate', 'last_modified'}: fmt = tweaks['gui_%s_display_format' % name] elif self.metadata['is_custom']: fmt = self.metadata.get('display', {}).get('date_format', None) self._sort_key = partial(clean_date_for_sort, fmt=fmt) elif dt == 'comments' or name == 'identifiers': self._default_sort_key = '' if self.name == 'languages': self._sort_key = lambda x: sort_key(calibre_langcode_to_name(x)) self.is_multiple = (bool(self.metadata['is_multiple']) or self.name == 'formats') self.sort_sort_key = True if self.is_multiple and '&' in self.metadata['is_multiple'][ 'list_to_ui']: self._sort_key = lambda x: sort_key(author_to_author_sort(x)) self.sort_sort_key = False self.default_value = {} if name == 'identifiers' else ( ) if self.is_multiple else None self.category_formatter = unicode_type if dt == 'rating': if self.metadata['display'].get('allow_half_stars', False): self.category_formatter = lambda x: rating_to_stars(x, True) else: self.category_formatter = rating_to_stars elif name == 'languages': self.category_formatter = calibre_langcode_to_name self.writer = Writer(self) self.series_field = None self.get_template_functions = get_template_functions
def __init__(self, name, table, bools_are_tristate, get_template_functions): self.name, self.table = name, table dt = self.metadata['datatype'] self.has_text_data = dt in {'text', 'comments', 'series', 'enumeration'} self.table_type = self.table.table_type self._sort_key = (sort_key if dt in ('text', 'series', 'enumeration') else IDENTITY) # This will be compared to the output of sort_key() which is a # bytestring, therefore it is safer to have it be a bytestring. # Coercing an empty bytestring to unicode will never fail, but the # output of sort_key cannot be coerced to unicode self._default_sort_key = b'' if dt in {'int', 'float', 'rating'}: self._default_sort_key = 0 elif dt == 'bool': self._default_sort_key = None self._sort_key = bool_sort_key(bools_are_tristate) elif dt == 'datetime': self._default_sort_key = UNDEFINED_DATE if tweaks['sort_dates_using_visible_fields']: fmt = None if name in {'timestamp', 'pubdate', 'last_modified'}: fmt = tweaks['gui_%s_display_format' % name] elif self.metadata['is_custom']: fmt = self.metadata.get('display', {}).get('date_format', None) self._sort_key = partial(clean_date_for_sort, fmt=fmt) elif dt == 'comments' or name == 'identifiers': self._default_sort_key = '' if self.name == 'languages': self._sort_key = lambda x:sort_key(calibre_langcode_to_name(x)) self.is_multiple = (bool(self.metadata['is_multiple']) or self.name == 'formats') self.sort_sort_key = True if self.is_multiple and '&' in self.metadata['is_multiple']['list_to_ui']: self._sort_key = lambda x: sort_key(author_to_author_sort(x)) self.sort_sort_key = False self.default_value = {} if name == 'identifiers' else () if self.is_multiple else None self.category_formatter = unicode_type if dt == 'rating': if self.metadata['display'].get('allow_half_stars', False): self.category_formatter = lambda x: rating_to_stars(x, True) else: self.category_formatter = rating_to_stars elif name == 'languages': self.category_formatter = calibre_langcode_to_name self.writer = Writer(self) self.series_field = None self.get_template_functions = get_template_functions
def render_field(self, db, book_id): is_stars = False try: field = db.pref('field_under_covers_in_grid', 'title') if field == 'size': ans = human_readable(db.field_for(field, book_id, default_value=0)) else: mi = db.get_proxy_metadata(book_id) display_name, ans, val, fm = mi.format_field_extended(field) if fm and fm['datatype'] == 'rating': ans = rating_to_stars(val, fm['display'].get('allow_half_stars', False)) is_stars = True return ('' if ans is None else unicode_type(ans)), is_stars except Exception: if DEBUG: import traceback traceback.print_exc() return '', is_stars
def subtitle(self, index): try: db = self.model.db.new_api field = db.pref('cover_browser_subtitle_field', 'rating') if field and field != 'none': book_id = self.model.id(index) fm = db.field_metadata[field] if fm['datatype'] == 'rating': val = db.field_for(field, book_id, default_value=0) if val: return rating_to_stars(val, allow_half_stars=db.field_metadata[field]['display'].get('allow_half_stars')) else: return self.render_template('{%s}' % field, index, db).replace('&', '&&') except Exception: if not self.subtitle_error_reported: self.subtitle_error_reported = True import traceback traceback.print_exc() return ''
def __init__(self, *args, **kwargs): QStyledItemDelegate.__init__(self, *args, **kwargs) self.old_look = False self.rating_pat = re.compile(r'[%s]' % rating_to_stars(3, True)) self.rating_font = QFont(rating_font()) self.completion_data = None
def generate_html(comments): args = dict(xmlns=XHTML_NS, title_str=title_str, css=css, title=title, author=author, publisher=publisher, pubdate_label=_('Published'), pubdate=pubdate, series_label=_('Series'), series=series, rating_label=_('Rating'), rating=rating, tags_label=_('Tags'), tags=tags, comments=comments, footer='', searchable_tags=' '.join(escape(t)+'ttt' for t in tags.tags_list), ) for key in mi.custom_field_keys(): m = mi.get_user_metadata(key, False) or {} try: display_name, val = mi.format_field_extended(key)[:2] dkey = key.replace('#', '_') dt = m.get('datatype') if dt == 'series': args[dkey] = Series(mi.get(key), mi.get(key + '_index')) elif dt == 'rating': args[dkey] = rating_to_stars(mi.get(key), m.get('display', {}).get('allow_half_stars', False)) elif dt == 'comments': val = val or '' display = m.get('display', {}) ctype = display.get('interpret_as') or 'html' if ctype == 'long-text': val = '<pre style="white-space:pre-wrap">%s</pre>' % escape(val) elif ctype == 'short-text': val = '<span>%s</span>' % escape(val) elif ctype == 'markdown': val = markdown(val) else: val = comments_to_html(val) args[dkey] = val else: args[dkey] = escape(val) args[dkey+'_label'] = escape(display_name) except Exception: # if the val (custom column contents) is None, don't add to args pass if False: print("Custom column values available in jacket template:") for key in args.keys(): if key.startswith('_') and not key.endswith('_label'): print(" %s: %s" % ('#' + key[1:], args[key])) # Used in the comment describing use of custom columns in templates # Don't change this unless you also change it in template.xhtml args['_genre_label'] = args.get('_genre_label', '{_genre_label}') args['_genre'] = args.get('_genre', '{_genre}') formatter = SafeFormatter() generated_html = formatter.format(template, **args) # Post-process the generated html to strip out empty header items soup = BeautifulSoup(generated_html) if not series: series_tag = soup.find(attrs={'class':'cbj_series'}) if series_tag is not None: series_tag.extract() if not rating: rating_tag = soup.find(attrs={'class':'cbj_rating'}) if rating_tag is not None: rating_tag.extract() if not tags: tags_tag = soup.find(attrs={'class':'cbj_tags'}) if tags_tag is not None: tags_tag.extract() if not pubdate: pubdate_tag = soup.find(attrs={'class':'cbj_pubdata'}) if pubdate_tag is not None: pubdate_tag.extract() if output_profile.short_name != 'kindle': hr_tag = soup.find('hr', attrs={'class':'cbj_kindle_banner_hr'}) if hr_tag is not None: hr_tag.extract() return strip_encoding_declarations( soup.renderContents('utf-8').decode('utf-8'))
def __init__(self, *args, **kwargs): QStyledItemDelegate.__init__(self, *args, **kwargs) self.old_look = gprefs['tag_browser_old_look'] self.rating_pat = re.compile(r'[%s]' % rating_to_stars(3, True)) self.rating_font = QFont(rating_font())
def mi_to_html(mi, field_list=None, default_author_link=None, use_roman_numbers=True, rating_font='Liberation Serif', rtl=False): if field_list is None: field_list = get_field_list(mi) ans = [] comment_fields = [] isdevice = not hasattr(mi, 'id') row = u'<td class="title">%s</td><td class="value">%s</td>' p = prepare_string_for_xml a = partial(prepare_string_for_xml, attribute=True) book_id = getattr(mi, 'id', 0) for field in (field for field, display in field_list if display): try: metadata = mi.metadata_for_field(field) except: continue if not metadata: continue if field == 'sort': field = 'title_sort' if metadata['is_custom'] and metadata['datatype'] in { 'bool', 'int', 'float' }: isnull = mi.get(field) is None else: isnull = mi.is_null(field) if isnull: continue name = metadata['name'] if not name: name = field name += ':' disp = metadata['display'] if metadata['datatype'] == 'comments' or field == 'comments': val = getattr(mi, field) if val: ctype = disp.get('interpret_as') or 'html' val = force_unicode(val) if ctype == 'long-text': val = '<pre style="white-space:pre-wrap">%s</pre>' % p(val) elif ctype == 'short-text': val = '<span>%s</span>' % p(val) elif ctype == 'markdown': val = markdown(val) else: val = comments_to_html(val) if disp.get('heading_position', 'hide') == 'side': ans.append((field, row % (name, val))) else: if disp.get('heading_position', 'hide') == 'above': val = '<h3 class="comments-heading">%s</h3>%s' % ( p(name), val) comment_fields.append( '<div id="%s" class="comments">%s</div>' % (field.replace('#', '_'), val)) elif metadata['datatype'] == 'rating': val = getattr(mi, field) if val: star_string = rating_to_stars( val, disp.get('allow_half_stars', False)) ans.append( (field, u'<td class="title">%s</td><td class="rating value" ' 'style=\'font-family:"%s"\'>%s</td>' % (name, rating_font, star_string))) elif metadata['datatype'] == 'composite': val = getattr(mi, field) if val: val = force_unicode(val) if disp.get('contains_html', False): ans.append((field, row % (name, comments_to_html(val)))) else: if not metadata['is_multiple']: val = '<a href="%s" title="%s">%s</a>' % (search_href( field, val), _('Click to see books with {0}: {1}').format( metadata['name'], a(val)), p(val)) else: all_vals = [ v.strip() for v in val.split( metadata['is_multiple']['list_to_ui']) if v.strip() ] links = [ '<a href="%s" title="%s">%s</a>' % (search_href(field, x), _('Click to see books with {0}: {1}').format( metadata['name'], a(x)), p(x)) for x in all_vals ] val = metadata['is_multiple']['list_to_ui'].join(links) ans.append((field, row % (name, val))) elif field == 'path': if mi.path: path = force_unicode(mi.path, filesystem_encoding) scheme = u'devpath' if isdevice else u'path' url = prepare_string_for_xml( path if isdevice else unicode(book_id), True) pathstr = _('Click to open') extra = '' if isdevice: durl = url if durl.startswith('mtp:::'): durl = ':::'.join((durl.split(':::'))[2:]) extra = '<br><span style="font-size:smaller">%s</span>' % ( prepare_string_for_xml(durl)) link = u'<a href="%s:%s" title="%s">%s</a>%s' % ( scheme, url, prepare_string_for_xml(path, True), pathstr, extra) ans.append((field, row % (name, link))) elif field == 'formats': if isdevice: continue path = mi.path or '' bpath = '' if path: h, t = os.path.split(path) bpath = os.sep.join((os.path.basename(h), t)) data = ({ 'fmt': x, 'path': a(path or ''), 'fname': a(mi.format_files.get(x, '')), 'ext': x.lower(), 'id': book_id, 'bpath': bpath, 'sep': os.sep } for x in mi.formats) fmts = [ u'<a data-full-path="{path}{sep}{fname}.{ext}" title="{bpath}{sep}{fname}.{ext}" href="format:{id}:{fmt}">{fmt}</a>' .format(**x) for x in data ] ans.append((field, row % (name, u', '.join(fmts)))) elif field == 'identifiers': urls = urls_from_identifiers(mi.identifiers) links = [ u'<a href="%s" title="%s:%s" data-item="%s">%s</a>' % (a(url), a(id_typ), a(id_val), a(item_data(field, id_typ, book_id)), p(namel)) for namel, id_typ, id_val, url in urls ] links = u', '.join(links) if links: ans.append((field, row % (_('Ids') + ':', links))) elif field == 'authors': authors = [] formatter = EvalFormatter() for aut in mi.authors: link = '' if mi.author_link_map.get(aut): link = lt = mi.author_link_map[aut] elif default_author_link: if isdevice and default_author_link == 'search-calibre': default_author_link = DEFAULT_AUTHOR_LINK if default_author_link.startswith('search-'): which_src = default_author_link.partition('-')[2] link, lt = author_search_href(which_src, title=mi.title, author=aut) else: vals = { 'author': qquote(aut), 'title': qquote(mi.title) } try: vals['author_sort'] = qquote( mi.author_sort_map[aut]) except KeyError: vals['author_sort'] = qquote(aut) link = lt = formatter.safe_format( default_author_link, vals, '', vals) aut = p(aut) if link: authors.append( u'<a calibre-data="authors" title="%s" href="%s">%s</a>' % (a(lt), a(link), aut)) else: authors.append(aut) ans.append((field, row % (name, u' & '.join(authors)))) elif field == 'languages': if not mi.languages: continue names = filter(None, map(calibre_langcode_to_name, mi.languages)) names = [ '<a href="%s" title="%s">%s</a>' % (search_href('languages', n), _('Search calibre for books with the language: {}').format(n), n) for n in names ] ans.append((field, row % (name, u', '.join(names)))) elif field == 'publisher': if not mi.publisher: continue val = '<a href="%s" title="%s" data-item="%s">%s</a>' % ( search_href('publisher', mi.publisher), _('Click to see books with {0}: {1}').format( metadata['name'], a(mi.publisher)), a(item_data('publisher', mi.publisher, book_id)), p(mi.publisher)) ans.append((field, row % (name, val))) elif field == 'title': # otherwise title gets metadata['datatype'] == 'text' # treatment below with a click to search link (which isn't # too bad), and a right-click 'Delete' option to delete # the title (which is bad). val = mi.format_field(field)[-1] ans.append((field, row % (name, val))) else: val = mi.format_field(field)[-1] if val is None: continue val = p(val) if metadata['datatype'] == 'series': sidx = mi.get(field + '_index') if sidx is None: sidx = 1.0 try: st = metadata['search_terms'][0] except Exception: st = field series = getattr(mi, field) val = _( '%(sidx)s of <a href="%(href)s" title="%(tt)s" data-item="%(data)s">' '<span class="%(cls)s">%(series)s</span></a>') % dict( sidx=fmt_sidx(sidx, use_roman=use_roman_numbers), cls="series_name", series=p(series), href=search_href(st, series), data=a(item_data(field, series, book_id)), tt=p(_('Click to see books in this series'))) elif metadata['datatype'] == 'datetime': aval = getattr(mi, field) if is_date_undefined(aval): continue elif metadata['datatype'] == 'text' and metadata['is_multiple']: try: st = metadata['search_terms'][0] except Exception: st = field all_vals = mi.get(field) if not metadata.get('display', {}).get('is_names', False): all_vals = sorted(all_vals, key=sort_key) links = [ '<a href="%s" title="%s" data-item="%s">%s</a>' % (search_href( st, x), _('Click to see books with {0}: {1}').format( metadata['name'], a(x)), a(item_data(field, x, book_id)), p(x)) for x in all_vals ] val = metadata['is_multiple']['list_to_ui'].join(links) elif metadata['datatype'] == 'text' or metadata[ 'datatype'] == 'enumeration': # text/is_multiple handled above so no need to add the test to the if try: st = metadata['search_terms'][0] except Exception: st = field val = '<a href="%s" title="%s" data-item="%s">%s</a>' % ( search_href(st, val), a( _('Click to see books with {0}: {1}').format( metadata['name'], val)), a(item_data(field, val, book_id)), p(val)) ans.append((field, row % (name, val))) dc = getattr(mi, 'device_collections', []) if dc: dc = u', '.join(sorted(dc, key=sort_key)) ans.append(('device_collections', row % (_('Collections') + ':', dc))) def classname(field): try: dt = mi.metadata_for_field(field)['datatype'] except: dt = 'text' return 'datatype_%s' % dt ans = [ u'<tr id="%s" class="%s">%s</tr>' % (fieldl.replace('#', '_'), classname(fieldl), html) for fieldl, html in ans ] # print '\n'.join(ans) direction = 'rtl' if rtl else 'ltr' margin = 'left' if rtl else 'right' return u'<style>table.fields td { vertical-align:top}</style>' + \ u'<table class="fields" style="direction: %s; margin-%s:auto">%s</table>'%( direction, margin, u'\n'.join(ans)), comment_fields
def __init__(self, *args, **kwargs): QStyledItemDelegate.__init__(self, *args, **kwargs) self.old_look = False self.rating_pat = re.compile(r'[%s]' % rating_to_stars(3, True)) self.rating_font = QFont(rating_font())
def stars(num, is_half_star=False): return rating_to_stars(num, is_half_star)
def mi_to_html(mi, field_list=None, default_author_link=None, use_roman_numbers=True, rating_font='Liberation Serif', rtl=False): if field_list is None: field_list = get_field_list(mi) ans = [] comment_fields = [] isdevice = not hasattr(mi, 'id') row = u'<td class="title">%s</td><td class="value">%s</td>' p = prepare_string_for_xml a = partial(prepare_string_for_xml, attribute=True) book_id = getattr(mi, 'id', 0) for field in (field for field, display in field_list if display): try: metadata = mi.metadata_for_field(field) except: continue if not metadata: continue if field == 'sort': field = 'title_sort' if metadata['is_custom'] and metadata['datatype'] in {'bool', 'int', 'float'}: isnull = mi.get(field) is None else: isnull = mi.is_null(field) if isnull: continue name = metadata['name'] if not name: name = field name += ':' disp = metadata['display'] if metadata['datatype'] == 'comments' or field == 'comments': val = getattr(mi, field) if val: ctype = disp.get('interpret_as') or 'html' val = force_unicode(val) if ctype == 'long-text': val = '<pre style="white-space:pre-wrap">%s</pre>' % p(val) elif ctype == 'short-text': val = '<span>%s</span>' % p(val) elif ctype == 'markdown': val = markdown(val) else: val = comments_to_html(val) if disp.get('heading_position', 'hide') == 'side': ans.append((field, row % (name, val))) else: if disp.get('heading_position', 'hide') == 'above': val = '<h3 class="comments-heading">%s</h3>%s' % (p(name), val) comment_fields.append('<div id="%s" class="comments">%s</div>' % (field.replace('#', '_'), val)) elif metadata['datatype'] == 'rating': val = getattr(mi, field) if val: star_string = rating_to_stars(val, disp.get('allow_half_stars', False)) ans.append((field, u'<td class="title">%s</td><td class="rating value" ' 'style=\'font-family:"%s"\'>%s</td>'%( name, rating_font, star_string))) elif metadata['datatype'] == 'composite': val = getattr(mi, field) if val: val = force_unicode(val) if disp.get('contains_html', False): ans.append((field, row % (name, comments_to_html(val)))) else: if not metadata['is_multiple']: val = '<a href="%s" title="%s">%s</a>' % ( search_href(field, val), _('Click to see books with {0}: {1}').format(metadata['name'], a(val)), p(val)) else: all_vals = [v.strip() for v in val.split(metadata['is_multiple']['list_to_ui']) if v.strip()] links = ['<a href="%s" title="%s">%s</a>' % ( search_href(field, x), _('Click to see books with {0}: {1}').format( metadata['name'], a(x)), p(x)) for x in all_vals] val = metadata['is_multiple']['list_to_ui'].join(links) ans.append((field, row % (name, val))) elif field == 'path': if mi.path: path = force_unicode(mi.path, filesystem_encoding) scheme = u'devpath' if isdevice else u'path' url = prepare_string_for_xml(path if isdevice else unicode(book_id), True) pathstr = _('Click to open') extra = '' if isdevice: durl = url if durl.startswith('mtp:::'): durl = ':::'.join((durl.split(':::'))[2:]) extra = '<br><span style="font-size:smaller">%s</span>'%( prepare_string_for_xml(durl)) link = u'<a href="%s:%s" title="%s">%s</a>%s' % (scheme, url, prepare_string_for_xml(path, True), pathstr, extra) ans.append((field, row % (name, link))) elif field == 'formats': if isdevice: continue path = mi.path or '' bpath = '' if path: h, t = os.path.split(path) bpath = os.sep.join((os.path.basename(h), t)) data = ({ 'fmt':x, 'path':a(path or ''), 'fname':a(mi.format_files.get(x, '')), 'ext':x.lower(), 'id':book_id, 'bpath':bpath, 'sep':os.sep } for x in mi.formats) fmts = [u'<a data-full-path="{path}{sep}{fname}.{ext}" title="{bpath}{sep}{fname}.{ext}" href="format:{id}:{fmt}">{fmt}</a>'.format(**x) for x in data] ans.append((field, row % (name, u', '.join(fmts)))) elif field == 'identifiers': urls = urls_from_identifiers(mi.identifiers) links = [u'<a href="%s" title="%s:%s" data-item="%s">%s</a>' % (a(url), a(id_typ), a(id_val), a(item_data(field, id_typ, book_id)), p(namel)) for namel, id_typ, id_val, url in urls] links = u', '.join(links) if links: ans.append((field, row % (_('Ids')+':', links))) elif field == 'authors' and not isdevice: authors = [] formatter = EvalFormatter() for aut in mi.authors: link = '' if mi.author_link_map[aut]: link = lt = mi.author_link_map[aut] elif default_author_link: if default_author_link == 'search-calibre': link = search_href('authors', aut) lt = a(_('Search the calibre library for books by %s') % aut) else: vals = {'author': aut.replace(' ', '+')} try: vals['author_sort'] = mi.author_sort_map[aut].replace(' ', '+') except: vals['author_sort'] = aut.replace(' ', '+') link = lt = a(formatter.safe_format(default_author_link, vals, '', vals)) aut = p(aut) if link: authors.append(u'<a calibre-data="authors" title="%s" href="%s">%s</a>'%(lt, link, aut)) else: authors.append(aut) ans.append((field, row % (name, u' & '.join(authors)))) elif field == 'languages': if not mi.languages: continue names = filter(None, map(calibre_langcode_to_name, mi.languages)) ans.append((field, row % (name, u', '.join(names)))) elif field == 'publisher': if not mi.publisher: continue val = '<a href="%s" title="%s" data-item="%s">%s</a>' % ( search_href('publisher', mi.publisher), _('Click to see books with {0}: {1}').format(metadata['name'], a(mi.publisher)), a(item_data('publisher', mi.publisher, book_id)), p(mi.publisher)) ans.append((field, row % (name, val))) elif field == 'title': # otherwise title gets metadata['datatype'] == 'text' # treatment below with a click to search link (which isn't # too bad), and a right-click 'Delete' option to delete # the title (which is bad). val = mi.format_field(field)[-1] ans.append((field, row % (name, val))) else: val = mi.format_field(field)[-1] if val is None: continue val = p(val) if metadata['datatype'] == 'series': sidx = mi.get(field+'_index') if sidx is None: sidx = 1.0 try: st = metadata['search_terms'][0] except Exception: st = field series = getattr(mi, field) val = _( '%(sidx)s of <a href="%(href)s" title="%(tt)s" data-item="%(data)s">' '<span class="%(cls)s">%(series)s</span></a>') % dict( sidx=fmt_sidx(sidx, use_roman=use_roman_numbers), cls="series_name", series=p(series), href=search_href(st, series), data=a(item_data(field, series, book_id)), tt=p(_('Click to see books in this series'))) elif metadata['datatype'] == 'datetime': aval = getattr(mi, field) if is_date_undefined(aval): continue elif metadata['datatype'] == 'text' and metadata['is_multiple']: try: st = metadata['search_terms'][0] except Exception: st = field all_vals = mi.get(field) if not metadata.get('display', {}).get('is_names', False): all_vals = sorted(all_vals, key=sort_key) links = ['<a href="%s" title="%s" data-item="%s">%s</a>' % ( search_href(st, x), _('Click to see books with {0}: {1}').format( metadata['name'], a(x)), a(item_data(field, x, book_id)), p(x)) for x in all_vals] val = metadata['is_multiple']['list_to_ui'].join(links) elif metadata['datatype'] == 'text' or metadata['datatype'] == 'enumeration': # text/is_multiple handled above so no need to add the test to the if try: st = metadata['search_terms'][0] except Exception: st = field val = '<a href="%s" title="%s" data-item="%s">%s</a>' % ( search_href(st, val), a(_('Click to see books with {0}: {1}').format(metadata['name'], val)), a(item_data(field, val, book_id)), p(val)) ans.append((field, row % (name, val))) dc = getattr(mi, 'device_collections', []) if dc: dc = u', '.join(sorted(dc, key=sort_key)) ans.append(('device_collections', row % (_('Collections')+':', dc))) def classname(field): try: dt = mi.metadata_for_field(field)['datatype'] except: dt = 'text' return 'datatype_%s'%dt ans = [u'<tr id="%s" class="%s">%s</tr>'%(fieldl.replace('#', '_'), classname(fieldl), html) for fieldl, html in ans] # print '\n'.join(ans) direction = 'rtl' if rtl else 'ltr' margin = 'left' if rtl else 'right' return u'<table class="fields" style="direction: %s; margin-%s:auto">%s</table>'%(direction, margin, u'\n'.join(ans)), comment_fields
def generate_html(comments): args = dict(xmlns=XHTML_NS, title_str=title_str, css=css, title=title, author=author, publisher=publisher, pubdate_label=_('Published'), pubdate=pubdate, series_label=_('Series'), series=series, rating_label=_('Rating'), rating=rating, tags_label=_('Tags'), tags=tags, comments=comments, footer='', searchable_tags=' '.join(escape(t)+'ttt' for t in tags.tags_list), ) for key in mi.custom_field_keys(): m = mi.get_user_metadata(key, False) or {} try: display_name, val = mi.format_field_extended(key)[:2] dkey = key.replace('#', '_') dt = m.get('datatype') if dt == 'series': args[dkey] = Series(mi.get(key), mi.get(key + '_index')) elif dt == 'rating': args[dkey] = rating_to_stars(mi.get(key), m.get('display', {}).get('allow_half_stars', False)) elif dt == 'comments': val = val or '' display = m.get('display', {}) ctype = display.get('interpret_as') or 'html' if ctype == 'long-text': val = '<pre style="white-space:pre-wrap">%s</pre>' % escape(val) elif ctype == 'short-text': val = '<span>%s</span>' % escape(val) elif ctype == 'markdown': val = markdown(val) else: val = comments_to_html(val) args[dkey] = val else: args[dkey] = escape(val) args[dkey+'_label'] = escape(display_name) except Exception: # if the val (custom column contents) is None, don't add to args pass if False: print("Custom column values available in jacket template:") for key in args.keys(): if key.startswith('_') and not key.endswith('_label'): print(" %s: %s" % ('#' + key[1:], args[key])) # Used in the comment describing use of custom columns in templates # Don't change this unless you also change it in template.xhtml args['_genre_label'] = args.get('_genre_label', '{_genre_label}') args['_genre'] = args.get('_genre', '{_genre}') formatter = SafeFormatter() generated_html = formatter.format(template, **args) has_data['series'] = bool(series) has_data['tags'] = bool(tags) has_data['rating'] = bool(rating) has_data['pubdate'] = bool(pubdate) return strip_encoding_declarations(generated_html)
def data(self, index, role=Qt.DisplayRole): if role == Qt.DisplayRole: val = index.row() * (1 if self.is_half_star else 2) return rating_to_stars(val, self.is_half_star) or self.null_text if role == Qt.FontRole: return QApplication.instance().font() if index.row() == 0 else self.rating_font
def displayText(self, value, locale): return rating_to_stars(value, self.is_half_star)
def generate_html(comments): display = Attributes() args = dict( xmlns=XHTML_NS, title_str=title_str, identifiers=Identifiers(mi.identifiers), css=css, title=title, author=author, publisher=publisher, pubdate_label=_('Published'), pubdate=pubdate, series_label=ngettext('Series', 'Series', 1), series=series, rating_label=_('Rating'), rating=rating, tags_label=_('Tags'), tags=tags, comments=comments, footer='', display=display, searchable_tags=' '.join( escape(t) + 'ttt' for t in tags.tags_list), ) for key in mi.custom_field_keys(): m = mi.get_user_metadata(key, False) or {} try: display_name, val = mi.format_field_extended(key)[:2] dkey = key.replace('#', '_') dt = m.get('datatype') if dt == 'series': args[dkey] = Series(mi.get(key), mi.get(key + '_index')) elif dt == 'rating': args[dkey] = rating_to_stars( mi.get(key), m.get('display', {}).get('allow_half_stars', False)) elif dt == 'comments': val = val or '' ctype = m.get('display', {}).get('interpret_as') or 'html' if ctype == 'long-text': val = '<pre style="white-space:pre-wrap">%s</pre>' % escape( val) elif ctype == 'short-text': val = '<span>%s</span>' % escape(val) elif ctype == 'markdown': val = markdown(val) else: val = comments_to_html(val) args[dkey] = val else: args[dkey] = escape(val) args[dkey + '_label'] = escape(display_name) setattr(display, dkey, 'none' if mi.is_null(key) else 'initial') except Exception: # if the val (custom column contents) is None, don't add to args pass if False: print("Custom column values available in jacket template:") for key in args.keys(): if key.startswith('_') and not key.endswith('_label'): print(" {}: {}".format('#' + key[1:], args[key])) # Used in the comment describing use of custom columns in templates # Don't change this unless you also change it in template.xhtml args['_genre_label'] = args.get('_genre_label', '{_genre_label}') args['_genre'] = args.get('_genre', '{_genre}') has_data['series'] = bool(series) has_data['tags'] = bool(tags) has_data['rating'] = bool(rating) has_data['pubdate'] = bool(pubdate) for k, v in has_data.items(): setattr(display, k, 'initial' if v else 'none') display.title = 'initial' if mi.identifiers: display.identifiers = 'initial' formatter = SafeFormatter() generated_html = formatter.format(template, **args) return strip_encoding_declarations(generated_html)
def generate_html(comments): args = dict( xmlns=XHTML_NS, title_str=title_str, css=css, title=title, author=author, publisher=publisher, pubdate_label=_('Published'), pubdate=pubdate, series_label=_('Series'), series=series, rating_label=_('Rating'), rating=rating, tags_label=_('Tags'), tags=tags, comments=comments, footer='', searchable_tags=' '.join( escape(t) + 'ttt' for t in tags.tags_list), ) for key in mi.custom_field_keys(): m = mi.get_user_metadata(key, False) or {} try: display_name, val = mi.format_field_extended(key)[:2] dkey = key.replace('#', '_') dt = m.get('datatype') if dt == 'series': args[dkey] = Series(mi.get(key), mi.get(key + '_index')) elif dt == 'rating': args[dkey] = rating_to_stars( mi.get(key), m.get('display', {}).get('allow_half_stars', False)) else: args[dkey] = escape(val) args[dkey + '_label'] = escape(display_name) except Exception: # if the val (custom column contents) is None, don't add to args pass if False: print("Custom column values available in jacket template:") for key in args.keys(): if key.startswith('_') and not key.endswith('_label'): print(" %s: %s" % ('#' + key[1:], args[key])) # Used in the comment describing use of custom columns in templates # Don't change this unless you also change it in template.xhtml args['_genre_label'] = args.get('_genre_label', '{_genre_label}') args['_genre'] = args.get('_genre', '{_genre}') formatter = SafeFormatter() generated_html = formatter.format(template, **args) # Post-process the generated html to strip out empty header items soup = BeautifulSoup(generated_html) if not series: series_tag = soup.find(attrs={'class': 'cbj_series'}) if series_tag is not None: series_tag.extract() if not rating: rating_tag = soup.find(attrs={'class': 'cbj_rating'}) if rating_tag is not None: rating_tag.extract() if not tags: tags_tag = soup.find(attrs={'class': 'cbj_tags'}) if tags_tag is not None: tags_tag.extract() if not pubdate: pubdate_tag = soup.find(attrs={'class': 'cbj_pubdata'}) if pubdate_tag is not None: pubdate_tag.extract() if output_profile.short_name != 'kindle': hr_tag = soup.find('hr', attrs={'class': 'cbj_kindle_banner_hr'}) if hr_tag is not None: hr_tag.extract() return strip_encoding_declarations( soup.renderContents('utf-8').decode('utf-8'))
def ACQUISITION_ENTRY(item, version, db, updated, CFM, CKEYS, prefix): FM = db.FIELD_MAP title = item[FM['title']] if not title: title = _('Unknown') authors = item[FM['authors']] if not authors: authors = _('Unknown') authors = ' & '.join([i.replace('|', ',') for i in authors.split(',')]) extra = [] rating = item[FM['rating']] if rating > 0: rating = rating_to_stars(rating) extra.append(_('RATING: %s<br />') % rating) tags = item[FM['tags']] if tags: extra.append( _('TAGS: %s<br />') % xml( format_tag_string( tags, ',', ignore_max=True, no_tag_count=True))) series = item[FM['series']] if series: extra.append( _('SERIES: %(series)s [%(sidx)s]<br />') % dict(series=xml(series), sidx=fmt_sidx(float(item[FM['series_index']])))) mi = db.get_metadata(item[CFM['id']['rec_index']], index_is_id=True) for key in CKEYS: name, val = mi.format_field(key) if val: datatype = CFM[key]['datatype'] if datatype == 'text' and CFM[key]['is_multiple']: extra.append( '%s: %s<br />' % (xml(name), xml( format_tag_string( val, CFM[key]['is_multiple']['ui_to_list'], ignore_max=True, no_tag_count=True, joinval=CFM[key]['is_multiple']['list_to_ui'])))) elif datatype == 'comments' or (CFM[key]['datatype'] == 'composite' and CFM[key]['display'].get( 'contains_html', False)): extra.append('%s: %s<br />' % (xml(name), comments_to_html(unicode(val)))) else: extra.append('%s: %s<br />' % (xml(name), xml(unicode(val)))) comments = item[FM['comments']] if comments: comments = comments_to_html(comments) extra.append(comments) if extra: extra = html_to_lxml('\n'.join(extra)) idm = 'calibre' if version == 0 else 'uuid' id_ = 'urn:%s:%s' % (idm, item[FM['uuid']]) ans = E.entry(TITLE(title), E.author(E.name(authors)), ID(id_), UPDATED(item[FM['last_modified']]), E.published(item[FM['timestamp']].isoformat())) if mi.pubdate and not is_date_undefined(mi.pubdate): ans.append(ans.makeelement('{%s}date' % DC_NS)) ans[-1].text = mi.pubdate.isoformat() if len(extra): ans.append(E.content(extra, type='xhtml')) formats = item[FM['formats']] if formats: book_id = item[FM['id']] for fmt in formats.split(','): fmt = fmt.lower() mt = guess_type('a.' + fmt)[0] href = prefix + '/get/%s/%s' % (fmt, book_id) if mt: link = E.link(type=mt, href=href) if version > 0: link.set('rel', "http://opds-spec.org/acquisition") fm = db.format_metadata(book_id, fmt) if fm: link.set('length', str(fm['size'])) link.set('mtime', fm['mtime'].isoformat()) ans.append(link) ans.append( E.link(type='image/jpeg', href=prefix + '/get/cover/%s' % item[FM['id']], rel="x-stanza-cover-image" if version == 0 else "http://opds-spec.org/cover")) ans.append( E.link(type='image/jpeg', href=prefix + '/get/thumb/%s' % item[FM['id']], rel="x-stanza-cover-image-thumbnail" if version == 0 else "http://opds-spec.org/thumbnail")) return ans
def ACQUISITION_ENTRY(item, version, db, updated, CFM, CKEYS, prefix): FM = db.FIELD_MAP title = item[FM['title']] if not title: title = _('Unknown') authors = item[FM['authors']] if not authors: authors = _('Unknown') authors = ' & '.join([i.replace('|', ',') for i in authors.split(',')]) extra = [] rating = item[FM['rating']] if rating > 0: rating = rating_to_stars(rating) extra.append(_('RATING: %s<br />')%rating) tags = item[FM['tags']] if tags: extra.append(_('TAGS: %s<br />')%xml(format_tag_string(tags, ',', ignore_max=True, no_tag_count=True))) series = item[FM['series']] if series: extra.append(_('SERIES: %(series)s [%(sidx)s]<br />')% dict(series=xml(series), sidx=fmt_sidx(float(item[FM['series_index']])))) mi = db.get_metadata(item[CFM['id']['rec_index']], index_is_id=True) for key in CKEYS: name, val = mi.format_field(key) if val: datatype = CFM[key]['datatype'] if datatype == 'text' and CFM[key]['is_multiple']: extra.append('%s: %s<br />'% (xml(name), xml(format_tag_string(val, CFM[key]['is_multiple']['ui_to_list'], ignore_max=True, no_tag_count=True, joinval=CFM[key]['is_multiple']['list_to_ui'])))) elif datatype == 'comments' or (CFM[key]['datatype'] == 'composite' and CFM[key]['display'].get('contains_html', False)): extra.append('%s: %s<br />'%(xml(name), comments_to_html(unicode(val)))) else: extra.append('%s: %s<br />'%(xml(name), xml(unicode(val)))) comments = item[FM['comments']] if comments: comments = comments_to_html(comments) extra.append(comments) if extra: extra = html_to_lxml('\n'.join(extra)) idm = 'calibre' if version == 0 else 'uuid' id_ = 'urn:%s:%s'%(idm, item[FM['uuid']]) ans = E.entry(TITLE(title), E.author(E.name(authors)), ID(id_), UPDATED(item[FM['last_modified']]), E.published(item[FM['timestamp']].isoformat())) if mi.pubdate and not is_date_undefined(mi.pubdate): ans.append(ans.makeelement('{%s}date' % DC_NS)) ans[-1].text = mi.pubdate.isoformat() if len(extra): ans.append(E.content(extra, type='xhtml')) formats = item[FM['formats']] if formats: book_id = item[FM['id']] for fmt in formats.split(','): fmt = fmt.lower() mt = guess_type('a.'+fmt)[0] href = prefix + '/get/%s/%s'%(fmt, book_id) if mt: link = E.link(type=mt, href=href) if version > 0: link.set('rel', "http://opds-spec.org/acquisition") fm = db.format_metadata(book_id, fmt) if fm: link.set('length', str(fm['size'])) link.set('mtime', fm['mtime'].isoformat()) ans.append(link) ans.append(E.link(type='image/jpeg', href=prefix+'/get/cover/%s'%item[FM['id']], rel="x-stanza-cover-image" if version == 0 else "http://opds-spec.org/cover")) ans.append(E.link(type='image/jpeg', href=prefix+'/get/thumb/%s'%item[FM['id']], rel="x-stanza-cover-image-thumbnail" if version == 0 else "http://opds-spec.org/thumbnail")) return ans