def displayText(self, val, locale): d = val.toDateTime() if d <= UNDEFINED_QDATETIME: return '' self.format = tweaks['gui_pubdate_display_format'] if self.format is None: self.format = 'MMM yyyy' return format_date(qt_to_dt(d, as_utc=False), self.format)
def __init__(self, date_read, is_read_only=False, default_to_today=False): if date_read == UNDEFINED_DATE and default_to_today: date_read = now() if is_read_only: QTableWidgetItem.__init__(self, format_date(date_read, None), QtGui.QTableWidgetItem.UserType) self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled) else: QTableWidgetItem.__init__(self, '', QtGui.QTableWidgetItem.UserType) self.setData(Qt.DisplayRole, QDateTime(date_read))
def evaluate(self, formatter, kwargs, mi, locals, val, format_string): if not val or val == "None": return "" try: dt = parse_date(val) s = format_date(dt, format_string) except: s = "BAD DATE" return s
def setup_ui(self): self.l = l = QVBoxLayout(self) self.splitter = s = QSplitter(self) s.setChildrenCollapsible(False) l.addWidget(s), l.addWidget(self.bb) self.bb.setStandardButtons(self.bb.Yes | self.bb.No) self.left = w = QWidget(self) s.addWidget(w) w.l = l = QVBoxLayout(w) l.setContentsMargins(0, 0, 0, 0) self.la = la = QLabel(self.msg) la.setWordWrap(True) l.addWidget(la) self.confirm = c = QCheckBox(_('Show this confirmation again'), self) c.setChecked(True) c.stateChanged.connect(self.toggle) l.addWidget(c) self.right = r = QTextBrowser(self) series = '' mi, fm = self.mi, field_metadata if mi.series: series = _('{num} of {series}').format(num=mi.format_series_index(), series='<i>%s</i>' % mi.series) r.setHtml(''' <h3 style="text-align:center">{mb}</h3> <p><b>{title}</b> - <i>{authors}</i><br></p> <table> <tr><td>{fm[timestamp][name]}:</td><td>{date}</td></tr> <tr><td>{fm[pubdate][name]}:</td><td>{published}</td></tr> <tr><td>{fm[formats][name]}:</td><td>{formats}</td></tr> <tr><td>{fm[series][name]}:</td><td>{series}</td></tr> </table> '''.format( mb=_('Target book'), title=mi.title, authors=authors_to_string(mi.authors), date=format_date(mi.timestamp, tweaks['gui_timestamp_display_format']), fm=fm, published=(format_date(mi.pubdate, tweaks['gui_pubdate_display_format']) if mi.pubdate else ''), formats=', '.join(mi.formats or ()), series=series )) s.addWidget(r)
def data_as_text(self, book, col): if col == 0: return unicode(book.gui_rank+1) if col == 1: t = book.title if book.title else _('Unknown') a = authors_to_string(book.authors) if book.authors else '' return '<b>%s</b><br><i>%s</i>' % (t, a) if col == 2: d = format_date(book.pubdate, 'yyyy') if book.pubdate else _('Unknown') p = book.publisher if book.publisher else '' return '<b>%s</b><br><i>%s</i>' % (d, p)
def __init__(self, date_read, is_read_only=False, default_to_today=False, fmt=None): # debug_print("DateTableWidgetItem:__init__ - date_read=", date_read) if date_read is None or date_read == UNDEFINED_DATE and default_to_today: date_read = now() if is_read_only: QTableWidgetItem.__init__(self, format_date(date_read, fmt), QTableWidgetItem.UserType) self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled) self.setData(Qt.DisplayRole, QDateTime(date_read)) else: QTableWidgetItem.__init__(self, '', QTableWidgetItem.UserType) self.setData(Qt.DisplayRole, QDateTime(date_read))
def data_as_text(self, book, col): if col == 0: return unicode(book.gui_rank + 1) if col == 1: t = book.title if book.title else _("Unknown") a = authors_to_string(book.authors) if book.authors else "" return "<b>%s</b><br><i>%s</i>" % (t, a) if col == 2: d = format_date(book.pubdate, "yyyy") if book.pubdate else _("Unknown") p = book.publisher if book.publisher else "" return "<b>%s</b><br><i>%s</i>" % (d, p)
def __init__(self, mi, parent=None): QTextBrowser.__init__(self, parent) series = '' fm = field_metadata if mi.series: series = _('{num} of {series}').format(num=mi.format_series_index(), series='<i>%s</i>' % mi.series) self.setHtml(''' <h3 style="text-align:center">{mb}</h3> <p><b>{title}</b> - <i>{authors}</i><br></p> <table> <tr><td>{fm[timestamp][name]}:</td><td>{date}</td></tr> <tr><td>{fm[pubdate][name]}:</td><td>{published}</td></tr> <tr><td>{fm[formats][name]}:</td><td>{formats}</td></tr> <tr><td>{fm[series][name]}:</td><td>{series}</td></tr> </table> '''.format( mb=_('Target book'), title=mi.title, authors=authors_to_string(mi.authors), date=format_date(mi.timestamp, tweaks['gui_timestamp_display_format']), fm=fm, published=(format_date(mi.pubdate, tweaks['gui_pubdate_display_format']) if mi.pubdate else ''), formats=', '.join(mi.formats or ()), series=series ))
def sort_key(self, sort_by_pubdate=False, sort_by_name=False): if sort_by_pubdate: pub_date = self.pubdate() if pub_date is not None and pub_date.year > 101: return format_date(pub_date, 'yyyyMMdd') else: series = self.orig_series_name() series_number = self.orig_series_index() if self.orig_series_index() is not None else -1 debug_print("sort_key - series_number=", series_number) debug_print("sort_key - series_number.__class__=", series_number.__class__) if series: if sort_by_name: return '%s%06.2f'% (series, series_number) else: return '%06.2f%s'% (series_number, series) return ''
def __init__(self, date_read, is_read_only=False, default_to_today=False, fmt=None): if (date_read == UNDEFINED_DATE) and default_to_today: date_read = now() if is_read_only: QTableWidgetItem.__init__(self, format_date(date_read, fmt), QtGui.QTableWidgetItem.UserType) self.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled) else: QTableWidgetItem.__init__(self, '', QtGui.QTableWidgetItem.UserType) dt = UNDEFINED_QDATETIME if date_read is None else QDateTime( date_read) self.setData(Qt.DisplayRole, dt)
def format_field_extended(self, key, series_with_index=True): from calibre.ebooks.metadata import authors_to_string ''' returns the tuple (display_name, formatted_value, original_value, field_metadata) ''' from calibre.utils.date import format_date # Handle custom series index if key.startswith('#') and key.endswith('_index'): tkey = key[:-6] # strip the _index cmeta = self.get_user_metadata(tkey, make_copy=False) if cmeta and cmeta['datatype'] == 'series': if self.get(tkey): res = self.get_extra(tkey) return (str(cmeta['name'] + '_index'), self.format_series_index(res), res, cmeta) else: return (str(cmeta['name'] + '_index'), '', '', cmeta) if key in self.custom_field_keys(): res = self.get(key, None) # get evaluates all necessary composites cmeta = self.get_user_metadata(key, make_copy=False) name = str(cmeta['name']) if res is None or res == '': # can't check "not res" because of numeric fields return (name, res, None, None) orig_res = res datatype = cmeta['datatype'] if datatype == 'text' and cmeta['is_multiple']: res = cmeta['is_multiple']['list_to_ui'].join(res) elif datatype == 'series' and series_with_index: if self.get_extra(key) is not None: res = res + \ ' [%s]'%self.format_series_index(val=self.get_extra(key)) elif datatype == 'datetime': res = format_date( res, cmeta['display'].get('date_format', 'dd MMM yyyy')) elif datatype == 'bool': res = _('Yes') if res else _('No') elif datatype == 'rating': res = '%.2g' % (res / 2.0) elif datatype in ['int', 'float']: try: fmt = cmeta['display'].get('number_format', None) res = fmt.format(res) except: pass return (name, str(res), orig_res, cmeta) # convert top-level ids into their value if key in TOP_LEVEL_IDENTIFIERS: fmeta = field_metadata['identifiers'] name = key res = self.get(key, None) return (name, res, res, fmeta) # Translate aliases into the standard field name fmkey = field_metadata.search_term_to_field_key(key) if fmkey in field_metadata and field_metadata[fmkey]['kind'] == 'field': res = self.get(key, None) fmeta = field_metadata[fmkey] name = str(fmeta['name']) if res is None or res == '': return (name, res, None, None) orig_res = res name = str(fmeta['name']) datatype = fmeta['datatype'] if key == 'authors': res = authors_to_string(res) elif key == 'series_index': res = self.format_series_index(res) elif datatype == 'text' and fmeta['is_multiple']: if isinstance(res, dict): res = [k + ':' + v for k, v in list(res.items())] res = fmeta['is_multiple']['list_to_ui'].join( sorted([_f for _f in res if _f], key=sort_key)) elif datatype == 'series' and series_with_index: res = res + ' [%s]' % self.format_series_index() elif datatype == 'datetime': res = format_date( res, fmeta['display'].get('date_format', 'dd MMM yyyy')) elif datatype == 'rating': res = '%.2g' % (res / 2.0) elif key == 'size': res = human_readable(res) return (name, str(res), orig_res, fmeta) return (None, None, None, None)
def _get_display_release_date(self, date_value, format='dd MMM yyyy'): if date_value and date_value != UNDEFINED_DATE: return format_date(date_value, format) return None
def displayText(self, val, locale): d = val.toDateTime() if d <= UNDEFINED_QDATETIME: return '' return format_date(qt_to_dt(d, as_utc=False), self.format)
def displayText(self, val, locale): d = qt_to_dt(val) if is_date_undefined(d): return '' return format_date(d, self.format)
def evaluate(self, formatter, kwargs, mi, locals): return format_date(now(), "iso")
def format_field_extended(self, key, series_with_index=True): from calibre.ebooks.metadata import authors_to_string ''' returns the tuple (display_name, formatted_value, original_value, field_metadata) ''' from calibre.utils.date import format_date # Handle custom series index if key.startswith('#') and key.endswith('_index'): tkey = key[:-6] # strip the _index cmeta = self.get_user_metadata(tkey, make_copy=False) if cmeta and cmeta['datatype'] == 'series': if self.get(tkey): res = self.get_extra(tkey) return (unicode(cmeta['name']+'_index'), self.format_series_index(res), res, cmeta) else: return (unicode(cmeta['name']+'_index'), '', '', cmeta) if key in self.custom_field_keys(): res = self.get(key, None) # get evaluates all necessary composites cmeta = self.get_user_metadata(key, make_copy=False) name = unicode(cmeta['name']) if res is None or res == '': # can't check "not res" because of numeric fields return (name, res, None, None) orig_res = res datatype = cmeta['datatype'] if datatype == 'text' and cmeta['is_multiple']: res = cmeta['is_multiple']['list_to_ui'].join(res) elif datatype == 'series' and series_with_index: if self.get_extra(key) is not None: res = res + \ ' [%s]'%self.format_series_index(val=self.get_extra(key)) elif datatype == 'datetime': res = format_date(res, cmeta['display'].get('date_format','dd MMM yyyy')) elif datatype == 'bool': res = _('Yes') if res else _('No') elif datatype == 'rating': res = u'%.2g'%(res/2.0) elif datatype in ['int', 'float']: try: fmt = cmeta['display'].get('number_format', None) res = fmt.format(res) except: pass return (name, unicode(res), orig_res, cmeta) # convert top-level ids into their value if key in TOP_LEVEL_IDENTIFIERS: fmeta = field_metadata['identifiers'] name = key res = self.get(key, None) return (name, res, res, fmeta) # Translate aliases into the standard field name fmkey = field_metadata.search_term_to_field_key(key) if fmkey in field_metadata and field_metadata[fmkey]['kind'] == 'field': res = self.get(key, None) fmeta = field_metadata[fmkey] name = unicode(fmeta['name']) if res is None or res == '': return (name, res, None, None) orig_res = res name = unicode(fmeta['name']) datatype = fmeta['datatype'] if key == 'authors': res = authors_to_string(res) elif key == 'series_index': res = self.format_series_index(res) elif datatype == 'text' and fmeta['is_multiple']: if isinstance(res, dict): res = [k + ':' + v for k,v in res.items()] res = fmeta['is_multiple']['list_to_ui'].join(sorted(filter(None, res), key=sort_key)) elif datatype == 'series' and series_with_index: res = res + ' [%s]'%self.format_series_index() elif datatype == 'datetime': res = format_date(res, fmeta['display'].get('date_format','dd MMM yyyy')) elif datatype == 'rating': res = u'%.2g'%(res/2.0) elif key == 'size': res = human_readable(res) return (name, unicode(res), orig_res, fmeta) return (None, None, None, None)
def __init__(self, dt, render_template): self.dt = as_local_time(dt) self.is_date_undefined = dt is None or is_date_undefined(dt) self.default_render = '' if self.is_date_undefined else escape(format_date(self.dt, render_template))
def displayText(self, val, locale): d = convert_qvariant(val) # if d <= UNDEFINED_QDATETIME: if not d or d <= UNDEFINED_QDATETIME or d == '': return '' return format_date(qt_to_dt(d, as_utc=False), self.format)
def __getattr__(self, template): with suppress(Exception): if not self.is_date_undefined: return escape(format_date(self.dt, template)) return ''
def evaluate(self, formatter, kwargs, mi, locals): return format_date(now(), 'iso')
def evaluate(self, formatter, kwargs, mi, locals, fmt): fmt_data = mi.get("format_metadata", {}) data = sorted(fmt_data.items(), key=lambda x: x[1]["mtime"], reverse=True) return ",".join(k.upper() + ":" + format_date(v["mtime"], fmt) for k, v in data)
def mi_to_html( mi, field_list=None, default_author_link=None, use_roman_numbers=True, rating_font='Liberation Serif', rtl=False, comments_heading_pos='hide', for_qt=False, ): if field_list is None: field_list = get_field_list(mi) ans = [] comment_fields = [] isdevice = not hasattr(mi, 'id') row = '<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) heading_position = disp.get('heading_position', comments_heading_pos) if heading_position == 'side': ans.append((field, row % (name, val))) else: if heading_position == '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, '<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_action(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_action(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 = 'devpath' if isdevice else 'path' loc = path if isdevice else book_id pathstr = _('Click to open') extra = '' if isdevice: durl = path if durl.startswith('mtp:::'): durl = ':::'.join((durl.split(':::'))[2:]) extra = '<br><span style="font-size:smaller">%s</span>'%( prepare_string_for_xml(durl)) link = '<a href="%s" title="%s">%s</a>%s' % (action(scheme, loc=loc), 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, 'action':action('format', book_id=book_id, fmt=x, path=path or '', fname=mi.format_files.get(x, '')) } for x in mi.formats) fmts = ['<a title="{bpath}{sep}{fname}.{ext}" href="{action}">{fmt}</a>'.format(**x) for x in data] ans.append((field, row % (name, ', '.join(fmts)))) elif field == 'identifiers': urls = urls_from_identifiers(mi.identifiers, sort_results=True) links = [ '<a href="%s" title="%s:%s">%s</a>' % ( action('identifier', url=url, name=namel, id_type=id_typ, value=id_val, field='identifiers', book_id=book_id), a(id_typ), a(id_val), p(namel)) for namel, id_typ, id_val, url in urls] links = ', '.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 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('<a title="%s" href="%s">%s</a>'%(a(lt), action('author', url=link, name=aut, title=lt), aut)) else: authors.append(aut) ans.append((field, row % (name, ' & '.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_action_with_data('languages', n, book_id), _( 'Search calibre for books with the language: {}').format(n), n) for n in names] ans.append((field, row % (name, ', '.join(names)))) elif field == 'publisher': if not mi.publisher: continue val = '<a href="%s" title="%s">%s</a>' % ( search_action_with_data('publisher', mi.publisher, book_id), _('Click to see books with {0}: {1}').format(metadata['name'], a(mi.publisher)), 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">' '<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_action_with_data(st, series, book_id, field), tt=p(_('Click to see books in this series'))) elif metadata['datatype'] == 'datetime': aval = getattr(mi, field) if is_date_undefined(aval): continue aval = format_date(aval, 'yyyy-MM-dd') if val == aval: val = '<a href="%s" title="%s">%s</a>' % ( search_action_with_data(field, str(aval), book_id, None, original_value=val), a( _('Click to see books with {0}: {1}').format(metadata['name'] or field, val)), val) else: val = '<a href="%s" title="%s">%s</a>' % ( search_action_with_data(field, str(aval), book_id, None, original_value=val), a( _('Click to see books with {0}: {1} (derived from {2})').format( metadata['name'] or field, aval, val)), val) 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">%s</a>' % ( search_action_with_data(st, x, book_id, field), _('Click to see books with {0}: {1}').format( metadata['name'] or field, a(x)), 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">%s</a>' % ( search_action_with_data(st, val, book_id, field), a( _('Click to see books with {0}: {1}').format(metadata['name'] or field, val)), p(val)) elif metadata['datatype'] == 'bool': val = '<a href="%s" title="%s">%s</a>' % ( search_action_with_data(field, val, book_id, None), a( _('Click to see books with {0}: {1}').format(metadata['name'] or field, val)), val) else: try: aval = str(getattr(mi, field)) if not aval: continue if val == aval: val = '<a href="%s" title="%s">%s</a>' % ( search_action_with_data(field, str(aval), book_id, None, original_value=val), a( _('Click to see books with {0}: {1}').format(metadata['name'] or field, val)), val) else: val = '<a href="%s" title="%s">%s</a>' % ( search_action_with_data(field, str(aval), book_id, None, original_value=val), a( _('Click to see books with {0}: {1} (derived from {2})').format( metadata['name'] or field, aval, val)), val) except Exception: import traceback traceback.print_exc() ans.append((field, row % (name, val))) dc = getattr(mi, 'device_collections', []) if dc: dc = ', '.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 = ['<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' rans = '<style>table.fields td { vertical-align:top}</style><table class="fields" style="direction: %s; ' % direction if not for_qt: # This causes wasted space at the edge of the table in Qt's rich text # engine, see https://bugs.launchpad.net/calibre/+bug/1881488 margin = 'left' if rtl else 'right' rans += 'margin-{}: auto; '.format(margin) return '{}">{}</table>'.format(rans, '\n'.join(ans)), comment_fields
def _get_display_release_date(self, date_value, format="dd MMM yyyy"): if date_value and date_value != UNDEFINED_DATE: return QVariant(format_date(date_value, format)) return NONE