mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement #5724 (BibTex Export)
This commit is contained in:
parent
5c5bd4d1db
commit
77e7bf8d06
@ -361,6 +361,8 @@ def strftime(fmt, t=None):
|
|||||||
before 1900 '''
|
before 1900 '''
|
||||||
if t is None:
|
if t is None:
|
||||||
t = time.localtime()
|
t = time.localtime()
|
||||||
|
if hasattr(t, 'timetuple'):
|
||||||
|
t = t.timetuple()
|
||||||
early_year = t[0] < 1900
|
early_year = t[0] < 1900
|
||||||
if early_year:
|
if early_year:
|
||||||
replacement = 1900 if t[0]%4 == 0 else 1901
|
replacement = 1900 if t[0]%4 == 0 else 1901
|
||||||
|
@ -446,7 +446,7 @@ from calibre.devices.eb600.driver import EB600, COOL_ER, SHINEBOOK, \
|
|||||||
BOOQ, ELONEX, POCKETBOOK301, MENTOR
|
BOOQ, ELONEX, POCKETBOOK301, MENTOR
|
||||||
from calibre.devices.iliad.driver import ILIAD
|
from calibre.devices.iliad.driver import ILIAD
|
||||||
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
|
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
|
||||||
from calibre.devices.jetbook.driver import JETBOOK
|
from calibre.devices.jetbook.driver import JETBOOK, MIBUK
|
||||||
from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX
|
from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX
|
||||||
from calibre.devices.nook.driver import NOOK
|
from calibre.devices.nook.driver import NOOK
|
||||||
from calibre.devices.prs505.driver import PRS505
|
from calibre.devices.prs505.driver import PRS505
|
||||||
@ -467,12 +467,12 @@ from calibre.devices.kobo.driver import KOBO
|
|||||||
from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon, \
|
from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon, \
|
||||||
LibraryThing
|
LibraryThing
|
||||||
from calibre.ebooks.metadata.douban import DoubanBooks
|
from calibre.ebooks.metadata.douban import DoubanBooks
|
||||||
from calibre.library.catalog import CSV_XML, EPUB_MOBI
|
from calibre.library.catalog import CSV_XML, EPUB_MOBI, BIBTEX
|
||||||
from calibre.ebooks.epub.fix.unmanifested import Unmanifested
|
from calibre.ebooks.epub.fix.unmanifested import Unmanifested
|
||||||
from calibre.ebooks.epub.fix.epubcheck import Epubcheck
|
from calibre.ebooks.epub.fix.epubcheck import Epubcheck
|
||||||
|
|
||||||
plugins = [HTML2ZIP, PML2PMLZ, ArchiveExtract, GoogleBooks, ISBNDB, Amazon,
|
plugins = [HTML2ZIP, PML2PMLZ, ArchiveExtract, GoogleBooks, ISBNDB, Amazon,
|
||||||
LibraryThing, DoubanBooks, CSV_XML, EPUB_MOBI, Unmanifested, Epubcheck]
|
LibraryThing, DoubanBooks, CSV_XML, EPUB_MOBI, BIBTEX, Unmanifested, Epubcheck]
|
||||||
plugins += [
|
plugins += [
|
||||||
ComicInput,
|
ComicInput,
|
||||||
EPUBInput,
|
EPUBInput,
|
||||||
@ -517,6 +517,7 @@ plugins += [
|
|||||||
IREXDR1000,
|
IREXDR1000,
|
||||||
IREXDR800,
|
IREXDR800,
|
||||||
JETBOOK,
|
JETBOOK,
|
||||||
|
MIBUK,
|
||||||
SHINEBOOK,
|
SHINEBOOK,
|
||||||
POCKETBOOK360,
|
POCKETBOOK360,
|
||||||
POCKETBOOK301,
|
POCKETBOOK301,
|
||||||
|
84
src/calibre/gui2/catalog/catalog_bibtex.py
Normal file
84
src/calibre/gui2/catalog/catalog_bibtex.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
|
from __future__ import with_statement
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
|
||||||
|
from calibre.gui2 import gprefs
|
||||||
|
from calibre.gui2.catalog.catalog_bibtex_ui import Ui_Form
|
||||||
|
from PyQt4.Qt import QWidget, QListWidgetItem
|
||||||
|
|
||||||
|
class PluginWidget(QWidget, Ui_Form):
|
||||||
|
|
||||||
|
TITLE = _('BibTeX Options')
|
||||||
|
HELP = _('Options specific to')+' BibTeX '+_('output')
|
||||||
|
OPTION_FIELDS = [('bib_cit','{authors}{id}'),
|
||||||
|
('bib_entry', 0), #mixed
|
||||||
|
('bibfile_enc', 0), #utf-8
|
||||||
|
('bibfile_enctag', 0), #strict
|
||||||
|
('impcit', True) ]
|
||||||
|
|
||||||
|
sync_enabled = False
|
||||||
|
formats = set(['bib'])
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QWidget.__init__(self, parent)
|
||||||
|
self.setupUi(self)
|
||||||
|
from calibre.library.catalog import FIELDS
|
||||||
|
self.all_fields = []
|
||||||
|
for x in FIELDS :
|
||||||
|
if x != 'all':
|
||||||
|
self.all_fields.append(x)
|
||||||
|
QListWidgetItem(x, self.db_fields)
|
||||||
|
|
||||||
|
def initialize(self, name): #not working properly to update
|
||||||
|
self.name = name
|
||||||
|
fields = gprefs.get(name+'_db_fields', self.all_fields)
|
||||||
|
# Restore the activated db_fields from last use
|
||||||
|
for x in xrange(self.db_fields.count()):
|
||||||
|
item = self.db_fields.item(x)
|
||||||
|
item.setSelected(unicode(item.text()) in fields)
|
||||||
|
# Update dialog fields from stored options
|
||||||
|
for opt in self.OPTION_FIELDS:
|
||||||
|
opt_value = gprefs.get(self.name + '_' + opt[0], opt[1])
|
||||||
|
if opt[0] in ['bibfile_enc', 'bibfile_enctag', 'bib_entry']:
|
||||||
|
getattr(self, opt[0]).setCurrentIndex(opt_value)
|
||||||
|
elif opt[0] == 'impcit' :
|
||||||
|
getattr(self, opt[0]).setChecked(opt_value)
|
||||||
|
else:
|
||||||
|
getattr(self, opt[0]).setText(opt_value)
|
||||||
|
|
||||||
|
def options(self):
|
||||||
|
|
||||||
|
# Save the currently activated fields
|
||||||
|
fields = []
|
||||||
|
for x in xrange(self.db_fields.count()):
|
||||||
|
item = self.db_fields.item(x)
|
||||||
|
if item.isSelected():
|
||||||
|
fields.append(unicode(item.text()))
|
||||||
|
gprefs.set(self.name+'_db_fields', fields)
|
||||||
|
|
||||||
|
# Dictionary currently activated fields
|
||||||
|
if len(self.db_fields.selectedItems()):
|
||||||
|
opts_dict = {'fields':[unicode(item.text()) for item in self.db_fields.selectedItems()]}
|
||||||
|
else:
|
||||||
|
opts_dict = {'fields':['all']}
|
||||||
|
|
||||||
|
# Save/return the current options
|
||||||
|
# bib_cit stores as text
|
||||||
|
# 'bibfile_enc','bibfile_enctag' stores as int (Indexes)
|
||||||
|
for opt in self.OPTION_FIELDS:
|
||||||
|
if opt[0] in ['bibfile_enc', 'bibfile_enctag', 'bib_entry']:
|
||||||
|
opt_value = getattr(self,opt[0]).currentIndex()
|
||||||
|
elif opt[0] == 'impcit' :
|
||||||
|
opt_value = getattr(self, opt[0]).isChecked()
|
||||||
|
else :
|
||||||
|
opt_value = unicode(getattr(self, opt[0]).text())
|
||||||
|
gprefs.set(self.name + '_' + opt[0], opt_value)
|
||||||
|
|
||||||
|
opts_dict[opt[0]] = opt_value
|
||||||
|
|
||||||
|
return opts_dict
|
173
src/calibre/gui2/catalog/catalog_bibtex.ui
Normal file
173
src/calibre/gui2/catalog/catalog_bibtex.ui
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Form</class>
|
||||||
|
<widget class="QWidget" name="Form">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>579</width>
|
||||||
|
<height>411</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Bib file encoding:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Fields to include in output:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QComboBox" name="bibfile_enc">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">utf-8</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">cp1252</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>ascii/LaTeX</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1" rowspan="12">
|
||||||
|
<widget class="QListWidget" name="db_fields">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string extracomment="Select all fields to be exported"/>
|
||||||
|
</property>
|
||||||
|
<property name="selectionMode">
|
||||||
|
<enum>QAbstractItemView::MultiSelection</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Encoding configuration (change if you have errors) :</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QComboBox" name="bibfile_enctag">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>strict</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>replace</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>ignore</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>backslashreplace</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>60</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>BibTeX entry type:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QComboBox" name="bib_entry">
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>mixed</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>misc</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<property name="text">
|
||||||
|
<string>book</string>
|
||||||
|
</property>
|
||||||
|
</item>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QCheckBox" name="impcit">
|
||||||
|
<property name="text">
|
||||||
|
<string>Create a citation tag?</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Expression to form the BibTeX citation tag:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="10" column="0">
|
||||||
|
<widget class="QLineEdit" name="bib_cit"/>
|
||||||
|
</item>
|
||||||
|
<item row="11" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Some explanation about this template:
|
||||||
|
-The fields availables are 'author_sort', 'authors', 'id',
|
||||||
|
'isbn', 'pubdate', 'publisher', 'series_index', 'series',
|
||||||
|
'tags', 'timestamp', 'title', 'uuid'
|
||||||
|
-For list types ie authors and tags, only the first element
|
||||||
|
wil be selected.
|
||||||
|
-For time field, only the date will be used. </string>
|
||||||
|
</property>
|
||||||
|
<property name="scaledContents">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
@ -1,7 +1,9 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2010, Greg Riker <griker at hotmail.com>'
|
__copyright__ = '2010, Greg Riker <griker at hotmail.com>'
|
||||||
|
|
||||||
import datetime, htmlentitydefs, os, re, shutil
|
import datetime, htmlentitydefs, os, re, shutil, codecs
|
||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
@ -9,6 +11,7 @@ from copy import deepcopy
|
|||||||
from xml.sax.saxutils import escape
|
from xml.sax.saxutils import escape
|
||||||
|
|
||||||
from calibre import filesystem_encoding, prints, prepare_string_for_xml, strftime
|
from calibre import filesystem_encoding, prints, prepare_string_for_xml, strftime
|
||||||
|
from calibre.constants import preferred_encoding
|
||||||
from calibre.customize import CatalogPlugin
|
from calibre.customize import CatalogPlugin
|
||||||
from calibre.customize.conversion import OptionRecommendation, DummyReporter
|
from calibre.customize.conversion import OptionRecommendation, DummyReporter
|
||||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup, BeautifulStoneSoup, Tag, NavigableString
|
from calibre.ebooks.BeautifulSoup import BeautifulSoup, BeautifulStoneSoup, Tag, NavigableString
|
||||||
@ -21,6 +24,10 @@ FIELDS = ['all', 'author_sort', 'authors', 'comments',
|
|||||||
'series_index', 'series', 'size', 'tags', 'timestamp', 'title',
|
'series_index', 'series', 'size', 'tags', 'timestamp', 'title',
|
||||||
'uuid']
|
'uuid']
|
||||||
|
|
||||||
|
#Allowed fields for template
|
||||||
|
TEMPLATE_ALLOWED_FIELDS = [ 'author_sort', 'authors', 'id', 'isbn', 'pubdate',
|
||||||
|
'publisher', 'series_index', 'series', 'tags', 'timestamp', 'title', 'uuid' ]
|
||||||
|
|
||||||
class CSV_XML(CatalogPlugin):
|
class CSV_XML(CatalogPlugin):
|
||||||
'CSV/XML catalog generator'
|
'CSV/XML catalog generator'
|
||||||
|
|
||||||
@ -89,17 +96,20 @@ class CSV_XML(CatalogPlugin):
|
|||||||
fields = self.get_output_fields(opts)
|
fields = self.get_output_fields(opts)
|
||||||
|
|
||||||
if self.fmt == 'csv':
|
if self.fmt == 'csv':
|
||||||
outfile = open(path_to_output, 'w')
|
outfile = codecs.open(path_to_output, 'w', 'utf8')
|
||||||
|
|
||||||
# Output the field headers
|
# Output the field headers
|
||||||
outfile.write(u'%s\n' % u','.join(fields))
|
outfile.write(u'%s\n' % u','.join(fields))
|
||||||
|
|
||||||
# Output the entry fields
|
# Output the entry fields
|
||||||
for entry in data:
|
for entry in data:
|
||||||
outstr = ''
|
outstr = []
|
||||||
for (x, field) in enumerate(fields):
|
for field in fields:
|
||||||
item = entry[field]
|
item = entry[field]
|
||||||
if field == 'formats':
|
if item is None:
|
||||||
|
outstr.append('""')
|
||||||
|
continue
|
||||||
|
elif field == 'formats':
|
||||||
fmt_list = []
|
fmt_list = []
|
||||||
for format in item:
|
for format in item:
|
||||||
fmt_list.append(format.rpartition('.')[2].lower())
|
fmt_list.append(format.rpartition('.')[2].lower())
|
||||||
@ -111,18 +121,13 @@ class CSV_XML(CatalogPlugin):
|
|||||||
item = u'%s' % re.sub(r'[\D]', '', item)
|
item = u'%s' % re.sub(r'[\D]', '', item)
|
||||||
elif field in ['pubdate', 'timestamp']:
|
elif field in ['pubdate', 'timestamp']:
|
||||||
item = isoformat(item)
|
item = isoformat(item)
|
||||||
|
elif field == 'comments':
|
||||||
|
item = item.replace(u'\r\n',u' ')
|
||||||
|
item = item.replace(u'\n',u' ')
|
||||||
|
|
||||||
if x < len(fields) - 1:
|
outstr.append(u'"%s"' % unicode(item).replace('"','""'))
|
||||||
if item is not None:
|
|
||||||
outstr += u'"%s",' % unicode(item).replace('"','""')
|
outfile.write(u','.join(outstr) + u'\n')
|
||||||
else:
|
|
||||||
outstr += '"",'
|
|
||||||
else:
|
|
||||||
if item is not None:
|
|
||||||
outstr += u'"%s"\n' % unicode(item).replace('"','""')
|
|
||||||
else:
|
|
||||||
outstr += '""\n'
|
|
||||||
outfile.write(outstr.encode('utf-8'))
|
|
||||||
outfile.close()
|
outfile.close()
|
||||||
|
|
||||||
elif self.fmt == 'xml':
|
elif self.fmt == 'xml':
|
||||||
@ -181,6 +186,329 @@ class CSV_XML(CatalogPlugin):
|
|||||||
f.write(etree.tostring(root, encoding='utf-8',
|
f.write(etree.tostring(root, encoding='utf-8',
|
||||||
xml_declaration=True, pretty_print=True))
|
xml_declaration=True, pretty_print=True))
|
||||||
|
|
||||||
|
class BIBTEX(CatalogPlugin):
|
||||||
|
'BIBTEX catalog generator'
|
||||||
|
|
||||||
|
Option = namedtuple('Option', 'option, default, dest, action, help')
|
||||||
|
|
||||||
|
name = 'Catalog_BIBTEX'
|
||||||
|
description = 'BIBTEX catalog generator'
|
||||||
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
|
author = 'Sengian'
|
||||||
|
version = (1, 0, 0)
|
||||||
|
file_types = set(['bib'])
|
||||||
|
|
||||||
|
cli_options = [
|
||||||
|
Option('--fields',
|
||||||
|
default = 'all',
|
||||||
|
dest = 'fields',
|
||||||
|
action = None,
|
||||||
|
help = _('The fields to output when cataloging books in the '
|
||||||
|
'database. Should be a comma-separated list of fields.\n'
|
||||||
|
'Available fields: %s.\n'
|
||||||
|
"Default: '%%default'\n"
|
||||||
|
"Applies to: BIBTEX output format")%', '.join(FIELDS)),
|
||||||
|
|
||||||
|
Option('--sort-by',
|
||||||
|
default = 'id',
|
||||||
|
dest = 'sort_by',
|
||||||
|
action = None,
|
||||||
|
help = _('Output field to sort on.\n'
|
||||||
|
'Available fields: author_sort, id, rating, size, timestamp, title.\n'
|
||||||
|
"Default: '%default'\n"
|
||||||
|
"Applies to: BIBTEX output format")),
|
||||||
|
|
||||||
|
Option('--create-citation',
|
||||||
|
default = 'True',
|
||||||
|
dest = 'impcit',
|
||||||
|
action = None,
|
||||||
|
help = _('Create a citation for BibTeX entries.\n'
|
||||||
|
'Boolean value: True, False\n'
|
||||||
|
"Default: '%default'\n"
|
||||||
|
"Applies to: BIBTEX output format")),
|
||||||
|
|
||||||
|
Option('--citation-template',
|
||||||
|
default = '{authors}{id}',
|
||||||
|
dest = 'bib_cit',
|
||||||
|
action = None,
|
||||||
|
help = _('The template for citation creation from database fields.\n'
|
||||||
|
' Should be a template with {} enclosed fields.\n'
|
||||||
|
'Available fields: %s.\n'
|
||||||
|
"Default: '%%default'\n"
|
||||||
|
"Applies to: BIBTEX output format")%', '.join(TEMPLATE_ALLOWED_FIELDS)),
|
||||||
|
|
||||||
|
Option('--choose-encoding',
|
||||||
|
default = 'utf8',
|
||||||
|
dest = 'bibfile_enc',
|
||||||
|
action = None,
|
||||||
|
help = _('BibTeX file encoding output.\n'
|
||||||
|
'Available types: utf8, cp1252, ascii.\n'
|
||||||
|
"Default: '%default'\n"
|
||||||
|
"Applies to: BIBTEX output format")),
|
||||||
|
|
||||||
|
Option('--choose-encoding-configuration',
|
||||||
|
default = 'strict',
|
||||||
|
dest = 'bibfile_enctag',
|
||||||
|
action = None,
|
||||||
|
help = _('BibTeX file encoding flag.\n'
|
||||||
|
'Available types: strict, replace, ignore, backslashreplace.\n'
|
||||||
|
"Default: '%default'\n"
|
||||||
|
"Applies to: BIBTEX output format")),
|
||||||
|
|
||||||
|
Option('--entry-type',
|
||||||
|
default = 'book',
|
||||||
|
dest = 'bib_entry',
|
||||||
|
action = None,
|
||||||
|
help = _('Entry type for BibTeX catalog.\n'
|
||||||
|
'Available types: book, misc, mixed.\n'
|
||||||
|
"Default: '%default'\n"
|
||||||
|
"Applies to: BIBTEX output format"))]
|
||||||
|
|
||||||
|
def run(self, path_to_output, opts, db, notification=DummyReporter()):
|
||||||
|
|
||||||
|
from types import StringType, UnicodeType
|
||||||
|
|
||||||
|
from calibre.library.save_to_disk import preprocess_template
|
||||||
|
#Bibtex functions
|
||||||
|
from calibre.utils.bibtex import bibtex_author_format, utf8ToBibtex, ValidateCitationKey
|
||||||
|
|
||||||
|
def create_bibtex_entry(entry, fields, mode, template_citation,
|
||||||
|
asccii_bibtex = True, citation_bibtex = True):
|
||||||
|
|
||||||
|
#Bibtex doesn't like UTF-8 but keep unicode until writing
|
||||||
|
#Define starting chain or if book valid strict and not book return a Fail string
|
||||||
|
|
||||||
|
bibtex_entry = []
|
||||||
|
if mode != "misc" and check_entry_book_valid(entry) :
|
||||||
|
bibtex_entry.append(u'@book{')
|
||||||
|
elif mode != "book" :
|
||||||
|
bibtex_entry.append(u'@misc{')
|
||||||
|
else :
|
||||||
|
#case strict book
|
||||||
|
return ''
|
||||||
|
|
||||||
|
if citation_bibtex :
|
||||||
|
# Citation tag
|
||||||
|
bibtex_entry.append(make_bibtex_citation(entry, template_citation, asccii_bibtex))
|
||||||
|
bibtex_entry = [u' '.join(bibtex_entry)]
|
||||||
|
|
||||||
|
for field in fields:
|
||||||
|
item = entry[field]
|
||||||
|
#check if the field should be included (none or empty)
|
||||||
|
if item is None:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
if len(item) == 0 :
|
||||||
|
continue
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if field == 'authors' :
|
||||||
|
bibtex_entry.append(u'author = "%s"' % bibtex_author_format(item))
|
||||||
|
|
||||||
|
elif field in ['title', 'publisher', 'cover', 'uuid',
|
||||||
|
'author_sort', 'series'] :
|
||||||
|
bibtex_entry.append(u'%s = "%s"' % (field, utf8ToBibtex(item, asccii_bibtex)))
|
||||||
|
|
||||||
|
elif field == 'id' :
|
||||||
|
bibtex_entry.append(u'calibreid = "%s"' % int(item))
|
||||||
|
|
||||||
|
elif field == 'rating' :
|
||||||
|
bibtex_entry.append(u'rating = "%s"' % int(item))
|
||||||
|
|
||||||
|
elif field == 'size' :
|
||||||
|
bibtex_entry.append(u'%s = "%s octets"' % (field, int(item)))
|
||||||
|
|
||||||
|
elif field == 'tags' :
|
||||||
|
#A list to flatten
|
||||||
|
bibtex_entry.append(u'tags = "%s"' % utf8ToBibtex(u', '.join(item), asccii_bibtex))
|
||||||
|
|
||||||
|
elif field == 'comments' :
|
||||||
|
#\n removal
|
||||||
|
item = item.replace(u'\r\n',u' ')
|
||||||
|
item = item.replace(u'\n',u' ')
|
||||||
|
bibtex_entry.append(u'note = "%s"' % utf8ToBibtex(item, asccii_bibtex))
|
||||||
|
|
||||||
|
elif field == 'isbn' :
|
||||||
|
# Could be 9, 10 or 13 digits
|
||||||
|
bibtex_entry.append(u'isbn = "%s"' % re.sub(u'[\D]', u'', item))
|
||||||
|
|
||||||
|
elif field == 'formats' :
|
||||||
|
item = u', '.join([format.rpartition('.')[2].lower() for format in item])
|
||||||
|
bibtex_entry.append(u'formats = "%s"' % item)
|
||||||
|
|
||||||
|
elif field == 'series_index' :
|
||||||
|
bibtex_entry.append(u'volume = "%s"' % int(item))
|
||||||
|
|
||||||
|
elif field == 'timestamp' :
|
||||||
|
bibtex_entry.append(u'timestamp = "%s"' % isoformat(item).partition('T')[0])
|
||||||
|
|
||||||
|
elif field == 'pubdate' :
|
||||||
|
bibtex_entry.append(u'year = "%s"' % item.year)
|
||||||
|
bibtex_entry.append(u'month = "%s"' % utf8ToBibtex(strftime("%b", item),
|
||||||
|
asccii_bibtex))
|
||||||
|
|
||||||
|
bibtex_entry = u',\n '.join(bibtex_entry)
|
||||||
|
bibtex_entry += u' }\n\n'
|
||||||
|
|
||||||
|
return bibtex_entry
|
||||||
|
|
||||||
|
def check_entry_book_valid(entry):
|
||||||
|
#Check that the required fields are ok for a book entry
|
||||||
|
for field in ['title', 'authors', 'publisher'] :
|
||||||
|
if entry[field] is None or len(entry[field]) == 0 :
|
||||||
|
return False
|
||||||
|
if entry['pubdate'] is None :
|
||||||
|
return False
|
||||||
|
else :
|
||||||
|
return True
|
||||||
|
|
||||||
|
def make_bibtex_citation(entry, template_citation, asccii_bibtex):
|
||||||
|
|
||||||
|
#define a function to replace the template entry by its value
|
||||||
|
def tpl_replace(objtplname) :
|
||||||
|
|
||||||
|
tpl_field = re.sub(u'[\{\}]', u'', objtplname.group())
|
||||||
|
|
||||||
|
if tpl_field in TEMPLATE_ALLOWED_FIELDS :
|
||||||
|
if tpl_field in ['pubdate', 'timestamp'] :
|
||||||
|
tpl_field = isoformat(entry[tpl_field]).partition('T')[0]
|
||||||
|
elif tpl_field in ['tags', 'authors'] :
|
||||||
|
tpl_field =entry[tpl_field][0]
|
||||||
|
elif tpl_field in ['id', 'series_index'] :
|
||||||
|
tpl_field = str(entry[tpl_field])
|
||||||
|
else :
|
||||||
|
tpl_field = entry[tpl_field]
|
||||||
|
return tpl_field
|
||||||
|
else:
|
||||||
|
return u''
|
||||||
|
|
||||||
|
if len(template_citation) >0 :
|
||||||
|
tpl_citation = utf8ToBibtex(ValidateCitationKey(re.sub(u'\{[^{}]*\}',
|
||||||
|
tpl_replace, template_citation)), asccii_bibtex)
|
||||||
|
|
||||||
|
if len(tpl_citation) >0 :
|
||||||
|
return tpl_citation
|
||||||
|
|
||||||
|
if len(entry["isbn"]) > 0 :
|
||||||
|
template_citation = u'%s' % re.sub(u'[\D]',u'', entry["isbn"])
|
||||||
|
|
||||||
|
else :
|
||||||
|
template_citation = u'%s' % str(entry["id"])
|
||||||
|
|
||||||
|
if asccii_bibtex :
|
||||||
|
return ValidateCitationKey(template_citation.encode('ascii', 'replace'))
|
||||||
|
else :
|
||||||
|
return ValidateCitationKey(template_citation)
|
||||||
|
|
||||||
|
self.fmt = path_to_output.rpartition('.')[2]
|
||||||
|
self.notification = notification
|
||||||
|
|
||||||
|
# Combobox options
|
||||||
|
bibfile_enc = ['utf8', 'cp1252', 'ascii']
|
||||||
|
bibfile_enctag = ['strict', 'replace', 'ignore', 'backslashreplace']
|
||||||
|
bib_entry = ['mixed', 'misc', 'book']
|
||||||
|
|
||||||
|
# Needed beacause CLI return str vs int by widget
|
||||||
|
try:
|
||||||
|
bibfile_enc = bibfile_enc[opts.bibfile_enc]
|
||||||
|
bibfile_enctag = bibfile_enctag[opts.bibfile_enctag]
|
||||||
|
bib_entry = bib_entry[opts.bib_entry]
|
||||||
|
except:
|
||||||
|
if opts.bibfile_enc in bibfile_enc :
|
||||||
|
bibfile_enc = opts.bibfile_enc
|
||||||
|
else :
|
||||||
|
log(" WARNING: incorrect --choose-encoding flag, revert to default")
|
||||||
|
bibfile_enc = bibfile_enc[0]
|
||||||
|
if opts.bibfile_enctag in bibfile_enctag :
|
||||||
|
bibfile_enctag = opts.bibfile_enctag
|
||||||
|
else :
|
||||||
|
log(" WARNING: incorrect --choose-encoding-configuration flag, revert to default")
|
||||||
|
bibfile_enctag = bibfile_enctag[0]
|
||||||
|
if opts.bib_entry in bib_entry :
|
||||||
|
bib_entry = opts.bib_entry
|
||||||
|
else :
|
||||||
|
log(" WARNING: incorrect --entry-type flag, revert to default")
|
||||||
|
bib_entry = bib_entry[0]
|
||||||
|
|
||||||
|
if opts.verbose:
|
||||||
|
opts_dict = vars(opts)
|
||||||
|
log("%s(): Generating %s" % (self.name,self.fmt))
|
||||||
|
if opts_dict['search_text']:
|
||||||
|
log(" --search='%s'" % opts_dict['search_text'])
|
||||||
|
|
||||||
|
if opts_dict['ids']:
|
||||||
|
log(" Book count: %d" % len(opts_dict['ids']))
|
||||||
|
if opts_dict['search_text']:
|
||||||
|
log(" (--search ignored when a subset of the database is specified)")
|
||||||
|
|
||||||
|
if opts_dict['fields']:
|
||||||
|
if opts_dict['fields'] == 'all':
|
||||||
|
log(" Fields: %s" % ', '.join(FIELDS[1:]))
|
||||||
|
else:
|
||||||
|
log(" Fields: %s" % opts_dict['fields'])
|
||||||
|
|
||||||
|
log(" Output file will be encoded in %s with %s flag" % (bibfile_enc, bibfile_enctag))
|
||||||
|
|
||||||
|
log(" BibTeX entry type is %s with a citation like '%s' flag" % (bib_entry, opts_dict['bib_cit']))
|
||||||
|
|
||||||
|
# If a list of ids are provided, don't use search_text
|
||||||
|
if opts.ids:
|
||||||
|
opts.search_text = None
|
||||||
|
|
||||||
|
data = self.search_sort_db(db, opts)
|
||||||
|
|
||||||
|
if not len(data):
|
||||||
|
log.error("\nNo matching database entries for search criteria '%s'" % opts.search_text)
|
||||||
|
|
||||||
|
# Get the requested output fields as a list
|
||||||
|
fields = self.get_output_fields(opts)
|
||||||
|
|
||||||
|
if not len(data):
|
||||||
|
log.error("\nNo matching database entries for search criteria '%s'" % opts.search_text)
|
||||||
|
|
||||||
|
#Entries writing after Bibtex formating (or not)
|
||||||
|
if bibfile_enc != 'ascii' :
|
||||||
|
asccii_bibtex = False
|
||||||
|
else :
|
||||||
|
asccii_bibtex = True
|
||||||
|
|
||||||
|
#Check and go to default in case of bad CLI
|
||||||
|
if isinstance(opts.impcit, (StringType, UnicodeType)) :
|
||||||
|
if opts.impcit == 'False' :
|
||||||
|
citation_bibtex= False
|
||||||
|
elif opts.impcit == 'True' :
|
||||||
|
citation_bibtex= True
|
||||||
|
else :
|
||||||
|
log(" WARNING: incorrect --create-citation, revert to default")
|
||||||
|
citation_bibtex= True
|
||||||
|
else :
|
||||||
|
citation_bibtex= opts.impcit
|
||||||
|
|
||||||
|
template_citation = preprocess_template(opts.bib_cit)
|
||||||
|
|
||||||
|
#Open output and write entries
|
||||||
|
outfile = codecs.open(path_to_output, 'w', bibfile_enc, bibfile_enctag)
|
||||||
|
|
||||||
|
#File header
|
||||||
|
nb_entries = len(data)
|
||||||
|
|
||||||
|
#check in book strict if all is ok else throw a warning into log
|
||||||
|
if bib_entry == 'book' :
|
||||||
|
nb_books = len(filter(check_entry_book_valid, data))
|
||||||
|
if nb_books < nb_entries :
|
||||||
|
log(" WARNING: only %d entries in %d are book compatible" % (nb_books, nb_entries))
|
||||||
|
nb_entries = nb_books
|
||||||
|
|
||||||
|
outfile.write(u'%%%Calibre catalog\n%%%{0} entries in catalog\n\n'.format(nb_entries))
|
||||||
|
outfile.write(u'@preamble{"This catalog of %d entries was generated by calibre on %s"}\n\n'
|
||||||
|
% (nb_entries, nowf().strftime("%A, %d. %B %Y %H:%M").decode(preferred_encoding)))
|
||||||
|
|
||||||
|
for entry in data:
|
||||||
|
outfile.write(create_bibtex_entry(entry, fields, bib_entry, template_citation,
|
||||||
|
asccii_bibtex, citation_bibtex))
|
||||||
|
|
||||||
|
outfile.close()
|
||||||
|
|
||||||
class EPUB_MOBI(CatalogPlugin):
|
class EPUB_MOBI(CatalogPlugin):
|
||||||
'ePub catalog generator'
|
'ePub catalog generator'
|
||||||
|
2539
src/calibre/utils/bibtex.py
Normal file
2539
src/calibre/utils/bibtex.py
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user