diff --git a/openlp.pyw b/openlp.pyw index 3b97a33d9..987b2946c 100755 --- a/openlp.pyw +++ b/openlp.pyw @@ -68,6 +68,10 @@ class OpenLP(QtGui.QApplication): global log log.info(u'OpenLP Application Loaded') + def notify(self, obj, evt): + #TODO needed for presentation exceptions + return QtGui.QApplication.notify(self, obj, evt) + def run(self): """ Run the OpenLP application. @@ -131,6 +135,7 @@ class OpenLP(QtGui.QApplication): if show_splash: # now kill the splashscreen self.splash.finish(self.mainWindow) + self.mainWindow.repaint() self.mainWindow.versionCheck() return self.exec_() @@ -143,7 +148,7 @@ def main(): usage = u'Usage: %prog [options] [qt-options]' parser = OptionParser(usage=usage) parser.add_option("-l", "--log-level", dest="loglevel", - default="info", metavar="LEVEL", + default="warning", metavar="LEVEL", help="Set logging to LEVEL level. Valid values are " "\"debug\", \"info\", \"warning\".") parser.add_option("-p", "--portable", dest="portable", @@ -154,7 +159,7 @@ def main(): help="Set the Qt4 style (passed directly to Qt4).") # Set up logging filename = u'openlp.log' - logfile = FileHandler(filename) + logfile = FileHandler(filename, u'w') logfile.setFormatter(logging.Formatter( u'%(asctime)s %(name)-15s %(levelname)-8s %(message)s')) log.addHandler(logfile) diff --git a/openlp/core/lib/baselistwithdnd.py b/openlp/core/lib/baselistwithdnd.py index d2537e0e4..f7095550a 100644 --- a/openlp/core/lib/baselistwithdnd.py +++ b/openlp/core/lib/baselistwithdnd.py @@ -32,6 +32,7 @@ class BaseListWithDnD(QtGui.QListWidget): def __init__(self, parent=None): QtGui.QListWidget.__init__(self, parent) + self.parent = parent # this must be set by the class which is inheriting assert(self.PluginName) @@ -47,4 +48,5 @@ class BaseListWithDnD(QtGui.QListWidget): mimeData = QtCore.QMimeData() drag.setMimeData(mimeData) mimeData.setText(self.PluginName) - dropAction = drag.start(QtCore.Qt.CopyAction) \ No newline at end of file + dropAction = drag.start(QtCore.Qt.CopyAction) + diff --git a/openlp/core/lib/eventreceiver.py b/openlp/core/lib/eventreceiver.py index f3b43d2b7..9bd7cd652 100644 --- a/openlp/core/lib/eventreceiver.py +++ b/openlp/core/lib/eventreceiver.py @@ -104,6 +104,9 @@ class EventReceiver(QtCore.QObject): ``remote_edit_clear`` Informs all components that remote edit has been aborted. + ``presentation types`` + Informs all components of the presentation types supported. + """ global log log = logging.getLogger(u'EventReceiver') @@ -161,4 +164,5 @@ class Receiver(): """ Get the global ``eventreceiver`` instance. """ - return Receiver.eventreceiver \ No newline at end of file + return Receiver.eventreceiver + diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 6f6f82818..5f490eed1 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -253,7 +253,7 @@ class MediaManagerItem(QtGui.QWidget): def addListViewToToolBar(self): #Add the List widget - self.ListView = self.ListViewWithDnD_class() + self.ListView = self.ListViewWithDnD_class(self) self.ListView.uniformItemSizes = True self.ListView.setGeometry(QtCore.QRect(10, 100, 256, 591)) self.ListView.setSpacing(1) @@ -400,4 +400,4 @@ class MediaManagerItem(QtGui.QWidget): if self.generateSlideData(service_item): return service_item else: - return None \ No newline at end of file + return None diff --git a/openlp/core/lib/rendermanager.py b/openlp/core/lib/rendermanager.py index b47eaa313..1e8363228 100644 --- a/openlp/core/lib/rendermanager.py +++ b/openlp/core/lib/rendermanager.py @@ -28,7 +28,7 @@ import logging from PyQt4 import QtCore from renderer import Renderer -from openlp.core.lib import ThemeLevel, resize_image +from openlp.core.lib import ThemeLevel class RenderManager(object): """ @@ -64,8 +64,6 @@ class RenderManager(object): self.theme_level = u'' self.override_background = None self.themedata = None - self.save_bg_frame = None - self.override_background_changed = False def update_display(self, screen_number): """ @@ -134,22 +132,6 @@ class RenderManager(object): self.calculate_default(self.screens.current[u'size']) self.renderer.set_theme(self.themedata) self.build_text_rectangle(self.themedata) - #Replace the background image from renderer with one from image - if self.override_background: - if self.save_bg_frame is None: - self.save_bg_frame = self.renderer.bg_frame - if self.override_background_changed: - self.renderer.bg_frame = resize_image( - self.override_background, self.width, self.height) - self.override_background_changed = False - else: - if self.override_background_changed: - self.renderer.bg_frame = resize_image( - self.override_background, self.width, self.height) - self.override_background_changed = False - if self.save_bg_frame: - self.renderer.bg_frame = self.save_bg_frame - self.save_bg_frame = None def build_text_rectangle(self, theme): """ diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 11b89743d..514d0b2dc 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -29,7 +29,7 @@ import os from PyQt4 import QtCore, QtGui from PyQt4.phonon import Phonon -from openlp.core.lib import Receiver +from openlp.core.lib import Receiver, resize_image class DisplayWidget(QtGui.QWidget): """ @@ -95,10 +95,12 @@ class MainDisplay(DisplayWidget): self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject) Phonon.createPath(self.mediaObject, self.video) Phonon.createPath(self.mediaObject, self.audio) - self.display = QtGui.QLabel(self) - self.display.setScaledContents(True) - self.alertDisplay = QtGui.QLabel(self) - self.alertDisplay.setScaledContents(True) + self.display_image = QtGui.QLabel(self) + self.display_image.setScaledContents(True) + self.display_text = QtGui.QLabel(self) + self.display_text.setScaledContents(True) + self.display_alert = QtGui.QLabel(self) + self.display_alert.setScaledContents(True) self.primary = True self.displayBlank = False self.blankFrame = None @@ -122,7 +124,7 @@ class MainDisplay(DisplayWidget): QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'media_play'), self.onMediaPlay) QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'media_pause'), self.onMediaPaws) + QtCore.SIGNAL(u'media_pause'), self.onMediaPause) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'media_stop'), self.onMediaStop) @@ -138,11 +140,13 @@ class MainDisplay(DisplayWidget): self.setGeometry(self.screen[u'size']) self.alertScreenPosition = self.screen[u'size'].height() * 0.9 self.alertHeight = self.screen[u'size'].height() - self.alertScreenPosition - self.alertDisplay.setGeometry( + self.display_alert.setGeometry( QtCore.QRect(0, self.alertScreenPosition, self.screen[u'size'].width(),self.alertHeight)) self.video.setGeometry(self.screen[u'size']) - self.display.resize(self.screen[u'size'].width(), + self.display_image.resize(self.screen[u'size'].width(), + self.screen[u'size'].height()) + self.display_text.resize(self.screen[u'size'].width(), self.screen[u'size'].height()) #Build a custom splash screen self.InitialFrame = QtGui.QImage( @@ -157,7 +161,8 @@ class MainDisplay(DisplayWidget): (self.screen[u'size'].width() - splash_image.width()) / 2, (self.screen[u'size'].height() - splash_image.height()) / 2, splash_image) - self.frameView(self.InitialFrame) + self.display_image.setPixmap(QtGui.QPixmap.fromImage(self.InitialFrame)) + self.repaint() #Build a Black screen painter = QtGui.QPainter() self.blankFrame = QtGui.QImage( @@ -165,11 +170,14 @@ class MainDisplay(DisplayWidget): self.screen[u'size'].height(), QtGui.QImage.Format_ARGB32_Premultiplied) painter.begin(self.blankFrame) + #TODO make black when testing finished painter.fillRect(self.blankFrame.rect(), QtCore.Qt.red) - #buid a blank transparent image + #build a blank transparent image self.transparent = QtGui.QPixmap(self.screen[u'size'].width(), self.screen[u'size'].height()) self.transparent.fill(QtCore.Qt.transparent) + self.display_alert.setPixmap(self.transparent) + self.frameView(self.transparent) # To display or not to display? if not self.screen[u'primary']: self.showFullScreen() @@ -181,6 +189,8 @@ class MainDisplay(DisplayWidget): def resetDisplay(self): if self.primary: self.setVisible(False) + else: + self.showFullScreen() def hideDisplay(self): self.setVisible(False) @@ -188,6 +198,17 @@ class MainDisplay(DisplayWidget): def showDisplay(self): if not self.primary: self.setVisible(True) + self.showFullScreen() + + def addImageWithText(self, frame): + frame = resize_image(frame, + self.screen[u'size'].width(), + self.screen[u'size'].height() ) + self.display_image.setPixmap(QtGui.QPixmap.fromImage(frame)) +# self.display_image.show() +# if not self.isVisible(): +# self.setVisible(True) +# self.showFullScreen() def frameView(self, frame, transition=False): """ @@ -199,18 +220,21 @@ class MainDisplay(DisplayWidget): if not self.displayBlank: if transition: if self.frame is not None: - self.display.setPixmap(QtGui.QPixmap.fromImage(self.frame)) + self.display_text.setPixmap(QtGui.QPixmap.fromImage(self.frame)) self.repaint() self.frame = None if frame[u'trans'] is not None: - self.display.setPixmap(QtGui.QPixmap.fromImage(frame[u'trans'])) + self.display_text.setPixmap(QtGui.QPixmap.fromImage(frame[u'trans'])) self.repaint() self.frame = frame[u'trans'] - self.display.setPixmap(QtGui.QPixmap.fromImage(frame[u'main'])) + self.display_text.setPixmap(QtGui.QPixmap.fromImage(frame[u'main'])) self.display_frame = frame[u'main'] self.repaint() else: - self.display.setPixmap(QtGui.QPixmap.fromImage(frame)) + if isinstance(frame, QtGui.QPixmap): + self.display_text.setPixmap(frame) + else: + self.display_text.setPixmap(QtGui.QPixmap.fromImage(frame)) self.display_frame = frame if not self.isVisible(): self.setVisible(True) @@ -219,14 +243,12 @@ class MainDisplay(DisplayWidget): def blankDisplay(self, blanked=True): if blanked: self.displayBlank = True - self.display.setPixmap(QtGui.QPixmap.fromImage(self.blankFrame)) + self.display_text.setPixmap(QtGui.QPixmap.fromImage(self.blankFrame)) else: self.displayBlank = False if self.display_frame: self.frameView(self.display_frame) -# if blanked != self.parent.LiveController.blankButton.isChecked(): -# self.parent.LiveController.blankButton.setChecked(self.displayBlank) -# self.parent.generalConfig.set_config(u'screen blank', self.displayBlank) + def displayAlert(self, text=u''): """ @@ -236,8 +258,11 @@ class MainDisplay(DisplayWidget): display text """ log.debug(u'display alert called %s' % text) + self.parent.StatusBar.showMessage(self.trUtf8(u'')) self.alertList.append(text) if self.timer_id != 0 or self.mediaLoaded: + self.parent.StatusBar.showMessage(\ + self.trUtf8(u'Alert message created and delayed')) return self.generateAlert() @@ -269,22 +294,24 @@ class MainDisplay(DisplayWidget): painter.drawText( x, y + metrics.height() - metrics.descent() - 1, text) painter.end() - self.alertDisplay.setPixmap(alertframe) - self.alertDisplay.setVisible(True) + self.display_alert.setPixmap(alertframe) + self.display_alert.setVisible(True) # check to see if we have a timer running if self.timer_id == 0: self.timer_id = self.startTimer(int(alertTab.timeout) * 1000) def timerEvent(self, event): if event.timerId() == self.timer_id: - self.alertDisplay.setPixmap(self.transparent) + self.display_alert.setPixmap(self.transparent) self.killTimer(self.timer_id) self.timer_id = 0 self.generateAlert() def onMediaQueue(self, message): log.debug(u'Queue new media message %s' % message) - self.display.close() + self.display_image.close() + self.display_text.close() + self.display_alert.close() file = os.path.join(message[1], message[2]) if self.firstTime: self.mediaObject.setCurrentSource(Phonon.MediaSource(file)) @@ -300,15 +327,16 @@ class MainDisplay(DisplayWidget): self.display_frame = self.blankFrame self.firstTime = True self.mediaLoaded = True - self.display.hide() - self.alertDisplay.hide() + self.display_image.hide() + self.display_text.hide() + self.display_alert.hide() self.video.setFullScreen(True) self.video.setVisible(True) self.mediaObject.play() - if self.primary: - self.setVisible(True) + self.setVisible(True) + self.hide() - def onMediaPaws(self): + def onMediaPause(self): log.debug(u'Media paused by user') self.mediaObject.pause() @@ -319,11 +347,10 @@ class MainDisplay(DisplayWidget): def onMediaFinish(self): log.debug(u'Reached end of media playlist') - if self.primary: - self.setVisible(False) self.mediaObject.stop() self.mediaObject.clearQueue() self.mediaLoaded = False self.video.setVisible(False) - self.display.show() + self.display_text.show() + self.display_image.show() self.blankDisplay(False) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 8e93fbf12..226c629e8 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -50,7 +50,6 @@ media_manager_style = """ border-color: palette(light); } """ - class Ui_MainWindow(object): def setupUi(self, MainWindow): """ diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 514e39077..47e8c2ea8 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -225,12 +225,17 @@ class ServiceManager(QtGui.QWidget): QtCore.SIGNAL(u'update_themes'), self.updateThemeList) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'remote_edit_clear'), self.onRemoteEditClear) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'presentation types'), self.onPresentationTypes) # Last little bits of setting up self.config = PluginConfig(u'ServiceManager') self.servicePath = self.config.get_data_path() self.service_theme = unicode( self.config.get_config(u'service theme', u'')) + def onPresentationTypes(self, presentation_types): + self.presentation_types = presentation_types + def onMoveSelectionUp(self): """ Moves the selection up the window @@ -433,10 +438,10 @@ class ServiceManager(QtGui.QWidget): for item in self.serviceItems: service.append({u'serviceitem':item[u'service_item'].get_service_repr()}) if item[u'service_item'].uses_file(): - for frame in item[u'service_item'].get_frames: + for frame in item[u'service_item'].get_frames(): path_from = unicode(os.path.join( item[u'service_item'].service_item_path, - frame.get_frame_title())) + frame[u'title'])) zip.write(path_from) file = open(servicefile, u'wb') cPickle.dump(service, file) @@ -499,7 +504,8 @@ class ServiceManager(QtGui.QWidget): serviceitem = ServiceItem() serviceitem.RenderManager = self.parent.RenderManager serviceitem.set_from_service(item, self.servicePath) - self.addServiceItem(serviceitem) + if self.validateItem(serviceitem): + self.addServiceItem(serviceitem) try: if os.path.isfile(p_file): os.remove(p_file) @@ -516,6 +522,14 @@ class ServiceManager(QtGui.QWidget): self.serviceName = name[len(name) - 1] self.parent.serviceChanged(True, self.serviceName) + def validateItem(self, serviceItem): +# print "---" +# print serviceItem.name +# print serviceItem.title +# print serviceItem.service_item_path +# print serviceItem.service_item_type + return True + def cleanUp(self): """ Empties the servicePath of temporary files @@ -617,7 +631,7 @@ class ServiceManager(QtGui.QWidget): else: pos = parentitem.data(0, QtCore.Qt.UserRole).toInt()[0] count = item.data(0, QtCore.Qt.UserRole).toInt()[0] - #adjuest for zero based arrays + #adjust for zero based arrays pos = pos - 1 return pos, count diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 298f5ab23..7db681502 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -249,32 +249,33 @@ class ThemeManager(QtGui.QWidget): log.debug(u'Load themes from dir') self.themelist = [] self.ThemeListWidget.clear() - for root, dirs, files in os.walk(self.path): - for name in files: - if name.endswith(u'.png'): - #check to see file is in theme root directory - theme = os.path.join(self.path, name) - if os.path.exists(theme): - (path, filename) = os.path.split(unicode(file)) - textName = os.path.splitext(name)[0] - if textName == self.global_theme: - name = u'%s (%s)' % (textName, - self.trUtf8('default')) - else: - name = textName - thumb = os.path.join(self.thumbPath, u'%s.png' % textName) - item_name = QtGui.QListWidgetItem(name) - if os.path.exists(thumb): - icon = build_icon(thumb) - else: - icon = build_icon(theme) - pixmap = icon.pixmap(QtCore.QSize(88,50)) - pixmap.save(thumb, u'png') - item_name.setIcon(icon) - item_name.setData(QtCore.Qt.UserRole, - QtCore.QVariant(textName)) - self.ThemeListWidget.addItem(item_name) - self.themelist.append(textName) + #root, dirs, files = os.walk(self.path) + dirList = os.listdir(self.path) + for name in dirList: + if name.endswith(u'.png'): + #check to see file is in theme root directory + theme = os.path.join(self.path, name) + if os.path.exists(theme): + (path, filename) = os.path.split(unicode(file)) + textName = os.path.splitext(name)[0] + if textName == self.global_theme: + name = u'%s (%s)' % (textName, + self.trUtf8('default')) + else: + name = textName + thumb = os.path.join(self.thumbPath, u'%s.png' % textName) + item_name = QtGui.QListWidgetItem(name) + if os.path.exists(thumb): + icon = build_icon(thumb) + else: + icon = build_icon(theme) + pixmap = icon.pixmap(QtCore.QSize(88,50)) + pixmap.save(thumb, u'png') + item_name.setIcon(icon) + item_name.setData(QtCore.Qt.UserRole, + QtCore.QVariant(textName)) + self.ThemeListWidget.addItem(item_name) + self.themelist.append(textName) self.pushThemes() def pushThemes(self): diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 912a02212..d560b0bce 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -40,12 +40,12 @@ class BiblePlugin(Plugin): self.weight = -9 self.icon = build_icon(u':/media/media_bible.png') #Register the bible Manager - self.biblemanager = None + self.manager = None def initialise(self): log.info(u'bibles Initialising') - if self.biblemanager is None: - self.biblemanager = BibleManager(self.config) + if self.manager is None: + self.manager = BibleManager(self, self.config) Plugin.initialise(self) self.insert_toolbox_item() self.ImportBibleItem.setVisible(True) @@ -90,4 +90,5 @@ class BiblePlugin(Plugin): about_text = self.trUtf8('Bible Plugin
This ' 'plugin allows bible verses from different sources to be ' 'displayed on the screen during the service.') - return about_text \ No newline at end of file + return about_text + diff --git a/openlp/plugins/bibles/forms/bibleimportwizard.py b/openlp/plugins/bibles/forms/bibleimportwizard.py index 1f655a52d..59e38e39a 100644 --- a/openlp/plugins/bibles/forms/bibleimportwizard.py +++ b/openlp/plugins/bibles/forms/bibleimportwizard.py @@ -91,15 +91,6 @@ class Ui_BibleImportWizard(object): self.OsisLayout.setMargin(0) self.OsisLayout.setSpacing(8) self.OsisLayout.setObjectName(u'OsisLayout') - self.OsisBibleNameLabel = QtGui.QLabel(self.OsisPage) - self.OsisBibleNameLabel.setIndent(0) - self.OsisBibleNameLabel.setObjectName(u'OsisBibleNameLabel') - self.OsisLayout.setWidget(0, QtGui.QFormLayout.LabelRole, - self.OsisBibleNameLabel) - self.OsisBibleNameEdit = QtGui.QLineEdit(self.OsisPage) - self.OsisBibleNameEdit.setObjectName(u'OsisBibleNameEdit') - self.OsisLayout.setWidget(0, QtGui.QFormLayout.FieldRole, - self.OsisBibleNameEdit) self.OsisLocationLabel = QtGui.QLabel(self.OsisPage) self.OsisLocationLabel.setObjectName(u'OsisLocationLabel') self.OsisLayout.setWidget(1, QtGui.QFormLayout.LabelRole, @@ -302,13 +293,11 @@ class Ui_BibleImportWizard(object): self.ImportProgressLabel.setObjectName(u'ImportProgressLabel') self.ImportLayout.addWidget(self.ImportProgressLabel) self.ImportProgressBar = QtGui.QProgressBar(self.ImportPage) - self.ImportProgressBar.setProperty(u'value', 0) - self.ImportProgressBar.setInvertedAppearance(False) + self.ImportProgressBar.setValue(0) self.ImportProgressBar.setObjectName(u'ImportProgressBar') self.ImportLayout.addWidget(self.ImportProgressBar) BibleImportWizard.addPage(self.ImportPage) - self.retranslateUi(BibleImportWizard) self.FormatWidget.setCurrentIndex(0) self.WebDownloadTabWidget.setCurrentIndex(0) @@ -334,7 +323,6 @@ class Ui_BibleImportWizard(object): self.FormatComboBox.setItemText(1, self.trUtf8('CSV')) self.FormatComboBox.setItemText(2, self.trUtf8('OpenSong')) self.FormatComboBox.setItemText(3, self.trUtf8('Web Download')) - self.OsisBibleNameLabel.setText(self.trUtf8('Bible Name:')) self.OsisLocationLabel.setText(self.trUtf8('File Location:')) self.BooksLocationLabel.setText(self.trUtf8('Books Location:')) self.VerseLocationLabel.setText(self.trUtf8('Verse Location:')) @@ -362,4 +350,4 @@ class Ui_BibleImportWizard(object): self.ImportPage.setSubTitle( self.trUtf8('Please wait while your Bible is imported.')) self.ImportProgressLabel.setText(self.trUtf8('Ready.')) - #self.ImportProgressBar.setFormat(u'%p') + self.ImportProgressBar.setFormat(u'%p%') diff --git a/openlp/plugins/bibles/forms/importwizardform.py b/openlp/plugins/bibles/forms/importwizardform.py index da95d968f..2f5e84867 100644 --- a/openlp/plugins/bibles/forms/importwizardform.py +++ b/openlp/plugins/bibles/forms/importwizardform.py @@ -23,6 +23,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### +import csv import logging import os import os.path @@ -44,8 +45,8 @@ class DownloadLocation(object): } @classmethod - def get_name(class_, id): - return class_.Names[id] + def get_name(cls, id): + return cls.Names[id] class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): @@ -58,7 +59,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): log = logging.getLogger(u'BibleImportForm') log.info(u'BibleImportForm loaded') - def __init__(self, parent, config, biblemanager, bibleplugin): + def __init__(self, parent, config, manager, bibleplugin): ''' Constructor ''' @@ -67,10 +68,10 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): self.registerFields() self.finishButton = self.button(QtGui.QWizard.FinishButton) self.cancelButton = self.button(QtGui.QWizard.CancelButton) - self.biblemanager = biblemanager + self.manager = manager self.config = config self.bibleplugin = bibleplugin - self.biblemanager.set_process_dialog(self) + self.manager.set_process_dialog(self) self.web_bible_list = {} self.loadWebBibles() QtCore.QObject.connect(self.LocationComboBox, @@ -95,9 +96,9 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): QtCore.SIGNAL(u'currentIdChanged(int)'), self.onCurrentIdChanged) - def show(self): + def exec_(self): self.setDefaults() - return QtGui.QWizard.show() + return QtGui.QWizard.exec_(self) def validateCurrentPage(self): if self.currentId() == 0: @@ -106,14 +107,6 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): elif self.currentId() == 1: # Select page if self.field(u'source_format').toInt()[0] == BibleFormat.OSIS: - if self.field(u'osis_biblename').toString() == u'': - QtGui.QMessageBox.critical(self, - self.trUtf8('Invalid Bible Name'), - self.trUtf8('You need to specify a name for your ' - 'Bible!'), - QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) - self.OsisBibleNameEdit.setFocus() - return False if self.field(u'osis_location').toString() == u'': QtGui.QMessageBox.critical(self, self.trUtf8('Invalid Bible Location'), @@ -168,6 +161,15 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) self.CopyrightEdit.setFocus() return False + elif self.manager.exists( + self.field(u'license_version').toString()): + QtGui.QMessageBox.critical(self, + self.trUtf8('Bible Exists'), + self.trUtf8('This Bible already exists! Please import ' + 'a different Bible or first delete the existing one.'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) + self.VersionNameEdit.setFocus() + return False return True if self.currentId() == 3: # Progress page @@ -208,8 +210,6 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): def registerFields(self): self.SelectPage.registerField( u'source_format', self.FormatComboBox) - self.SelectPage.registerField( - u'osis_biblename', self.OsisBibleNameEdit) self.SelectPage.registerField( u'osis_location', self.OSISLocationEdit) self.SelectPage.registerField( @@ -237,23 +237,22 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): def setDefaults(self): self.setField(u'source_format', 0) - self.setField(u'osis_biblename', u'') - self.setField(u'osis_location', u'') - self.setField(u'csv_booksfile', u'') - self.setField(u'csv_versefile', u'') - self.setField(u'opensong_file', u'') - self.setField(u'web_location', 0) + self.setField(u'osis_location', '') + self.setField(u'csv_booksfile', '') + self.setField(u'csv_versefile', '') + self.setField(u'opensong_file', '') + self.setField(u'web_location', DownloadLocation.Crosswalk) self.setField(u'web_biblename', self.BibleComboBox) self.setField(u'proxy_server', - self.config.get_config(u'proxy address', u'')) + self.config.get_config(u'proxy address', '')) self.setField(u'proxy_username', - self.config.get_config(u'proxy username',u'')) + self.config.get_config(u'proxy username','')) self.setField(u'proxy_password', - self.config.get_config(u'proxy password',u'')) + self.config.get_config(u'proxy password','')) self.setField(u'license_version', self.VersionNameEdit) self.setField(u'license_copyright', self.CopyrightEdit) self.setField(u'license_permission', self.PermissionEdit) - self.onLocationComboBoxChanged(0) + self.onLocationComboBoxChanged(DownloadLocation.Crosswalk) def loadWebBibles(self): """ @@ -266,29 +265,33 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): fbibles = None try: self.web_bible_list[DownloadLocation.Crosswalk] = {} - fbibles = open(os.path.join(filepath, u'crosswalkbooks.csv'), 'r') - for line in fbibles: - parts = line.split(u',') - self.web_bible_list[DownloadLocation.Crosswalk][parts[0]] = \ - parts[1].rstrip() + books_file = open(os.path.join(filepath, u'crosswalkbooks.csv'), 'r') + dialect = csv.Sniffer().sniff(books_file.read(1024)) + books_file.seek(0) + books_reader = csv.reader(books_file, dialect) + for line in books_reader: + self.web_bible_list[DownloadLocation.Crosswalk][line[0]] = \ + unicode(line[1], u'utf-8').strip() except: log.exception(u'Crosswalk resources missing') finally: - if fbibles: - fbibles.close() + if books_file: + books_file.close() #Load and store BibleGateway Bibles try: self.web_bible_list[DownloadLocation.BibleGateway] = {} - fbibles = open(os.path.join(filepath, u'biblegateway.csv'), 'r') - for line in fbibles: - parts = line.split(u',') - self.web_bible_list[DownloadLocation.BibleGateway][parts[0]] = \ - parts[1].rstrip() + books_file = open(os.path.join(filepath, u'biblegateway.csv'), 'r') + dialect = csv.Sniffer().sniff(books_file.read(1024)) + books_file.seek(0) + books_reader = csv.reader(books_file, dialect) + for line in books_reader: + self.web_bible_list[DownloadLocation.BibleGateway][line[0]] = \ + unicode(line[1], u'utf-8').strip() except: log.exception(u'Biblegateway resources missing') finally: - if fbibles: - fbibles.close() + if books_file: + books_file.close() def getFileName(self, title, editbox): filename = QtGui.QFileDialog.getOpenFileName(self, title, @@ -316,22 +319,22 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): success = False if bible_type == BibleFormat.OSIS: # Import an OSIS bible - success = self.biblemanager.register_osis_file_bible( - unicode(self.field(u'license_version').toString()), - unicode(self.field(u'osis_location').toString()) + success = self.manager.import_bible(BibleFormat.OSIS, + name=unicode(self.field(u'license_version').toString()), + filename=unicode(self.field(u'osis_location').toString()) ) elif bible_type == BibleFormat.CSV: # Import a CSV bible - success = self.biblemanager.register_csv_file_bible( - unicode(self.field(u'license_version').toString()), - self.field(u'csv_booksfile').toString(), - self.field(u'csv_versefile').toString() + success = self.manager.import_bible(BibleFormat.CSV, + name=unicode(self.field(u'license_version').toString()), + booksfile=self.field(u'csv_booksfile').toString(), + versefile=self.field(u'csv_versefile').toString() ) elif bible_type == BibleFormat.OpenSong: # Import an OpenSong bible - success = self.biblemanager.register_opensong_bible( - unicode(self.field(u'license_version').toString()), - self.field(u'opensong_file').toString() + success = self.manager.import_bible(BibleFormat.OpenSong, + name=unicode(self.field(u'license_version').toString()), + filename=self.field(u'opensong_file').toString() ) elif bible_type == BibleFormat.WebDownload: # Import a bible from the web @@ -343,21 +346,22 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): elif download_location == DownloadLocation.BibleGateway: bible = self.web_bible_list[DownloadLocation.BibleGateway][ unicode(self.BibleComboBox.currentText())] - success = self.biblemanager.register_http_bible( - unicode(self.field(u'license_version').toString()), - unicode(DownloadLocation.get_name(download_location)), - unicode(bible), - unicode(self.field(u'proxy_server').toString()), - unicode(self.field(u'proxy_username').toString()), - unicode(self.field(u'proxy_password').toString()) + success = self.manager.import_bible(BibleFormat.WebDownload, + name=unicode(self.field(u'license_version').toString()), + download_source=unicode(DownloadLocation.get_name(download_location)), + download_name=unicode(bible), + proxy_server=unicode(self.field(u'proxy_server').toString()), + proxy_username=unicode(self.field(u'proxy_username').toString()), + proxy_password=unicode(self.field(u'proxy_password').toString()) ) if success: - self.biblemanager.save_meta_data( + self.manager.save_meta_data( unicode(self.field(u'license_version').toString()), unicode(self.field(u'license_version').toString()), unicode(self.field(u'license_copyright').toString()), unicode(self.field(u'license_permission').toString()) ) + self.manager.reload_bibles() self.ImportProgressLabel.setText(self.trUtf8('Finished import.')) else: self.ImportProgressLabel.setText( @@ -367,4 +371,4 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard): self.ImportProgressBar.setValue(self.ImportProgressBar.maximum()) self.finishButton.setVisible(True) self.cancelButton.setVisible(False) - Receiver.send_message(u'process_events') + Receiver.send_message(u'process_events') \ No newline at end of file diff --git a/openlp/plugins/bibles/lib/bibleDBimpl.py b/openlp/plugins/bibles/lib/bibleDBimpl.py deleted file mode 100644 index 9c65a7eaa..000000000 --- a/openlp/plugins/bibles/lib/bibleDBimpl.py +++ /dev/null @@ -1,190 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, # -# Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -import os -import logging - -from common import BibleCommon -from openlp.plugins.bibles.lib.models import * - -class BibleDBImpl(BibleCommon): - global log - log = logging.getLogger(u'BibleDBImpl') - log.info(u'BibleDBimpl loaded') - - def __init__(self, biblepath, biblename, config): - # Connect to database - self.config = config - self.biblefile = os.path.join(biblepath, biblename + u'.sqlite') - log.debug(u'Load bible %s on path %s', biblename, self.biblefile) - db_type = self.config.get_config(u'db type', u'sqlite') - db_url = u'' - if db_type == u'sqlite': - db_url = u'sqlite:///' + self.biblefile - else: - db_url = u'%s://%s:%s@%s/%s' % \ - (db_type, self.config.get_config(u'db username'), - self.config.get_config(u'db password'), - self.config.get_config(u'db hostname'), - self.config.get_config(u'db database')) - self.metadata, self.session = init_models(db_url) - self.metadata.create_all(checkfirst=True) - - def create_tables(self): - log.debug(u'createTables') - self.save_meta(u'dbversion', u'2') - self._load_testament(u'Old Testament') - self._load_testament(u'New Testament') - self._load_testament(u'Apocrypha') - - def add_verse(self, bookid, chap, vse, text): - verse = Verse() - verse.book_id = bookid - verse.chapter = chap - verse.verse = vse - verse.text = text - self.session.add(verse) - return verse - - def save_verses(self): - log.debug('Saving verses...') - self.session.commit() - - def create_chapter(self, bookid, chap, textlist): - log.debug(u'create_chapter %s,%s', bookid, chap) - #text list has book and chapter as first to elements of the array - for verse_number, verse_text in textlist.iteritems(): - verse = Verse() - verse.book_id = bookid - verse.chapter = chap - verse.verse = verse_number - verse.text = verse_text - self.session.add(verse) - self.session.commit() - - def create_book(self, bookname, bookabbrev, testament=1): - log.debug(u'create_book %s,%s', bookname, bookabbrev) - book = Book() - book.testament_id = testament - book.name = bookname - book.abbreviation = bookabbrev - self.session.add(book) - self.session.commit() - return book - - def save_meta(self, key, value): - log.debug(u'save_meta %s/%s', key, value) - bmeta = BibleMeta() - bmeta.key = key - bmeta.value = value - self.session.add(bmeta) - self.session.commit() - - def get_meta(self, metakey): - log.debug(u'get meta %s', metakey) - return self.session.query(BibleMeta).filter_by(key=metakey).first() - - def delete_meta(self, metakey): - biblemeta = self.get_meta(metakey) - try: - self.session.delete(biblemeta) - self.session.commit() - return True - except: - return False - - def _load_testament(self, testament): - log.debug(u'load_testaments %s', testament) - test = ONTestament() - test.name = testament - self.session.add(test) - self.session.commit() - - def get_bible_books(self): - log.debug(u'get_bible_books') - return self.session.query(Book).order_by(Book.id).all() - - def get_max_bible_book_verses(self, bookname, chapter): - log.debug(u'get_max_bible_book_verses %s, %s', bookname, chapter) - verse = self.session.query(Verse).join(Book).filter( - Book.name == bookname).filter( - Verse.chapter == chapter).order_by(Verse.verse.desc()).first() - if verse == None: - return 0 - else: - return verse.verse - - def get_max_bible_book_chapter(self, bookname): - log.debug(u'get_max_bible_book_chapter %s', bookname) - verse = self.session.query(Verse).join(Book).filter( - Book.name == bookname).order_by(Verse.chapter.desc()).first() - if verse == None: - return 0 - else: - return verse.chapter - - def get_bible_book(self, bookname): - log.debug(u'get_bible_book %s', bookname) - book = self.session.query(Book).filter( - Book.name.like(bookname + u'%')).first() - if book is None: - book = self.session.query(Book).filter( - Book.abbreviation.like(bookname + u'%')).first() - return book - - def get_bible_chapter(self, id, chapter): - log.debug(u'get_bible_chapter %s, %s', id, chapter) - return self.session.query(Verse).filter_by(chapter=chapter).filter_by( - book_id=id).first() - - def get_bible_text(self, bookname, chapter, sverse, everse): - log.debug(u'get_bible_text %s, %s, %s, %s', bookname, chapter, sverse, - everse) - #Look up book name or abbreviation - book = self.get_bible_book(bookname) - if book: - bookname = book.name - log.debug(u'bookname corrected to %s' % bookname) - verses = self.session.query(Verse).join(Book).filter( - Book.name == bookname).filter(Verse.chapter == chapter).filter( - Verse.verse>=sverse).filter(Verse.verse<=everse).order_by( - Verse.verse).all() - return verses - - def get_verses_from_text(self, versetext): - log.debug(u'get_verses_from_text %s',versetext) - versetext = u'%%%s%%' % versetext - verses = self.session.query(Verse).filter( - Verse.text.like(versetext)).all() - return verses - - def dump_bible(self): - log.debug( u'.........Dumping Bible Database') - log.debug( '...............................Books ') - books = self.session.query(Book).all() - log.debug(books) - log.debug( u'...............................Verses ') - verses = self.session.query(Verse).all() - log.debug(verses) \ No newline at end of file diff --git a/openlp/plugins/bibles/lib/bibleHTTPimpl.py b/openlp/plugins/bibles/lib/bibleHTTPimpl.py deleted file mode 100644 index f8cad5c18..000000000 --- a/openlp/plugins/bibles/lib/bibleHTTPimpl.py +++ /dev/null @@ -1,228 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, # -# Carsten Tinggaard # -# --------------------------------------------------------------------------- # -# This program is free software; you can redistribute it and/or modify it # -# under the terms of the GNU General Public License as published by the Free # -# Software Foundation; version 2 of the License. # -# # -# This program is distributed in the hope that it will be useful, but WITHOUT # -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # -# more details. # -# # -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -# Temple Place, Suite 330, Boston, MA 02111-1307 USA # -############################################################################### - -import logging - -from common import BibleCommon, SearchResults - -class BGExtract(BibleCommon): - global log - log = logging.getLogger(u'BibleHTTPMgr(BG_extract)') - log.info(u'BG_extract loaded') - - def __init__(self, proxyurl= None): - log.debug(u'init %s', proxyurl) - self.proxyurl = proxyurl - - def get_bible_chapter(self, version, bookname, chapter) : - """ - Access and decode bibles via the BibleGateway website - - ``Version`` - The version of the bible like 31 for New International version - - ``bookname`` - Name of the Book - - ``chapter`` - Chapter number - """ - log.debug(u'get_bible_chapter %s,%s,%s', - version, bookname, chapter) - urlstring = \ - u'http://www.biblegateway.com/passage/?search=%s+%d&version=%s' % \ - (bookname, chapter, version) - log.debug(u'BibleGateway urm = %s' % urlstring) - xml_string = self._get_web_text(urlstring, self.proxyurl) - verseSearch = u' -1: - # clear out string - verseText = u'' - versePos = xml_string.find(u'', versePos) + 6 - i = xml_string.find(verseSearch, versePos + 1) - # Not sure if this is needed now - if i == -1: - i = xml_string.find(u' 0 and j < i: - i = j - verseText = xml_string[versePos + 7 : i ] - # store the verse - bible[verse] = self._clean_text(verseText) - versePos = -1 - else: - verseText = xml_string[versePos: i] - start_tag = verseText.find(verseFootnote) - while start_tag > -1: - end_tag = verseText.find(u'') - verseText = verseText[:start_tag] + verseText[end_tag + 6:len(verseText)] - start_tag = verseText.find(verseFootnote) - # Chop off verse and start again - xml_string = xml_string[i:] - #look for the next verse - versePos = xml_string.find(verseSearch) - # store the verse - bible[verse] = self._clean_text(verseText) - verse += 1 - return SearchResults(bookname, chapter, bible) - -class CWExtract(BibleCommon): - global log - log = logging.getLogger(u'BibleHTTPMgr(CWExtract)') - log.info(u'CWExtract loaded') - - def __init__(self, proxyurl=None): - log.debug(u'init %s', proxyurl) - self.proxyurl = proxyurl - - def get_bible_chapter(self, version, bookname, chapter) : - log.debug(u'getBibleChapter %s,%s,%s', - version,bookname, chapter) - """ - Access and decode bibles via the Crosswalk website - - ``version`` - The version of the bible like niv for New International Version - - ``bookname`` - Text name of in english e.g. 'gen' for Genesis - - ``chapter`` - Chapter number - """ - log.debug(u'get_bible_chapter %s,%s,%s', - version, bookname, chapter) - bookname = bookname.replace(u' ', u'') - urlstring = u'http://bible.crosswalk.com/OnlineStudyBible/bible.cgi?word=%s+%d&version=%s'\ - % (bookname, chapter, version) - xml_string = self._get_web_text(urlstring, self.proxyurl) - ## Strip Book Title from Heading to return it to system - ## - i = xml_string.find(u'') - j = xml_string.find(u'-', i) - book_title = xml_string[i + 7:j] - book_title = book_title.rstrip() - log.debug(u'Book Title %s', book_title) - i = book_title.rfind(u' ') - book_chapter = book_title[i+1:len(book_title)].rstrip() - book_title = book_title[:i].rstrip() - log.debug(u'Book Title %s', book_title) - log.debug(u'Book Chapter %s', book_chapter) - # Strip Verse Data from Page and build an array - - i = xml_string.find(u'NavCurrentChapter') - xml_string = xml_string[i:len(xml_string)] - i = xml_string.find(u'<TABLE') - xml_string = xml_string[i:len(xml_string)] - i = xml_string.find(u'<B>') - #remove the <B> at the front - xml_string = xml_string[i + 3 :len(xml_string)] - # Remove the heading for the book - i = xml_string.find(u'<B>') - #remove the <B> at the front - xml_string = xml_string[i + 3 :len(xml_string)] - versePos = xml_string.find(u'<BLOCKQUOTE>') - bible = {} - while versePos > 0: - verseText = u'' - versePos = xml_string.find(u'<B><I>', versePos) + 6 - i = xml_string.find(u'</I></B>', versePos) - # Got the Chapter - verse = xml_string[versePos:i] - # move the starting position to begining of the text - versePos = i + 8 - # find the start of the next verse - i = xml_string.find(u'<B><I>', versePos) - if i == -1: - i = xml_string.find(u'</BLOCKQUOTE>',versePos) - verseText = xml_string[versePos: i] - versePos = 0 - else: - verseText = xml_string[versePos: i] - versePos = i - bible[verse] = self._clean_text(verseText) - return SearchResults(book_title, book_chapter, bible) - -class BibleHTTPImpl(): - global log - log = logging.getLogger(u'BibleHTTPMgr') - log.info(u'BibleHTTP manager loaded') - def __init__(self): - """ - Finds all the bibles defined for the system - Creates an Interface Object for each bible containing connection - information - - Throws Exception if no Bibles are found. - - Init confirms the bible exists and stores the database path. - """ - self.biblesource = u'' - self.proxyurl = None - self.bibleid = None - - def set_proxy(self, proxyurl): - """ - Set the Proxy Url - """ - log.debug(u'set_proxy %s', proxyurl) - self.proxyurl = proxyurl - - def set_bibleid(self, bibleid): - """ - Set the bible id. - The shore identifier of the the bible. - """ - log.debug(u'set_bibleid %s', bibleid) - self.bibleid = bibleid - - def set_bible_source(self, biblesource): - """ - Set the source of where the bible text is coming from - """ - log.debug(u'set_bible_source %s', biblesource) - self.biblesource = biblesource - - def get_bible_chapter(self, version, bookname, chapter): - """ - Receive the request and call the relevant handler methods - """ - log.debug(u'get_bible_chapter %s,%s,%s', - version, bookname, chapter) - log.debug(u'biblesource = %s', self.biblesource) - try: - if self.biblesource.lower() == u'crosswalk': - ev = CWExtract(self.proxyurl) - else: - ev = BGExtract(self.proxyurl) - return ev.get_bible_chapter(self.bibleid, bookname, chapter) - except: - log.exception("Failed to get bible chapter") \ No newline at end of file diff --git a/openlp/plugins/bibles/lib/common.py b/openlp/plugins/bibles/lib/common.py index 3669b8e5f..cd9b5cf35 100644 --- a/openlp/plugins/bibles/lib/common.py +++ b/openlp/plugins/bibles/lib/common.py @@ -26,8 +26,97 @@ import urllib2 import chardet import logging +import re -class SearchResults: +only_verses = re.compile(r'([\w .]+)[ ]+([0-9]+)[ ]*[:|v|V][ ]*([0-9]+)' + r'(?:[ ]*-[ ]*([0-9]+|end))?(?:[ ]*,[ ]*([0-9]+)(?:[ ]*-[ ]*([0-9]+|end))?)?', + re.UNICODE) +chapter_range = re.compile(r'([\w .]+)[ ]+([0-9]+)[ ]*[:|v|V][ ]*' + r'([0-9]+)[ ]*-[ ]*([0-9]+)[ ]*[:|v|V][ ]*([0-9]+)', + re.UNICODE) + +log = logging.getLogger(__name__) + +def parse_reference(reference): + """ + This is the über-awesome function that takes a person's typed in string + and converts it to a reference list, a list of references to be queried + from the Bible database files. + + The reference list is a list of tuples, with each tuple structured like + this:: + + (book, chapter, start_verse, end_verse) + """ + reference = reference.strip() + log.debug('parse_reference("%s")', reference) + reference_list = [] + # We start with the most "complicated" match first, so that they are found + # first, and we don't have any "false positives". + match = chapter_range.match(reference) + if match: + log.debug('Found a chapter range.') + book = match.group(1) + from_verse = match.group(3) + to_verse = match.group(5) + if int(match.group(2)) == int(match.group(4)): + reference_list.append( + (match.group(1), int(match.group(2)), from_verse, to_verse) + ) + else: + if int(match.group(2)) > int(match.group(4)): + from_chapter = int(match.group(4)) + to_chapter = int(match.group(2)) + else: + from_chapter = int(match.group(2)) + to_chapter = int(match.group(4)) + for chapter in xrange(from_chapter, to_chapter + 1): + if chapter == from_chapter: + reference_list.append( + (match.group(1), chapter, from_verse, -1) + ) + elif chapter == to_chapter: + reference_list.append( + (match.group(1), chapter, 1, to_verse) + ) + else: + reference_list.append( + (match.group(1), chapter, 1, -1) + ) + else: + match = only_verses.match(reference) + if match: + log.debug('Found a verse range.') + book = match.group(1) + chapter = match.group(2) + verse = match.group(3) + if match.group(4) is None: + reference_list.append((book, chapter, verse, verse)) + elif match.group(5) is None: + end_verse = match.group(4) + if end_verse == u'end': + end_verse = -1 + reference_list.append((book, chapter, verse, end_verse)) + elif match.group(6) is None: + reference_list.extend([ + (book, chapter, verse, match.group(4)), + (book, chapter, match.group(5), match.group(5)) + ]) + else: + end_verse = match.group(6) + if end_verse == u'end': + end_verse = -1 + reference_list.extend([ + (book, chapter, verse, match.group(4)), + (book, chapter, match.group(5), end_verse) + ]) + else: + log.debug('Didn\'t find anything.') + log.debug(reference_list) + return reference_list + + +class SearchResults(object): """ Encapsulate a set of search results. This is Bible-type independant. """ @@ -81,12 +170,6 @@ class BibleCommon(object): log = logging.getLogger(u'BibleCommon') log.info(u'BibleCommon') - def __init__(self): - """ - An empty constructor... not sure why I'm here. - """ - pass - def _get_web_text(self, urlstring, proxyurl): """ Get the HTML from the web page. @@ -165,4 +248,4 @@ class BibleCommon(object): text = text[:start_tag] + text[end_tag + 1:] start_tag = text.find(u'<') text = text.replace(u'>', u'') - return text.rstrip().lstrip() \ No newline at end of file + return text.rstrip().lstrip() diff --git a/openlp/plugins/bibles/lib/bibleCSVimpl.py b/openlp/plugins/bibles/lib/csvbible.py similarity index 50% rename from openlp/plugins/bibles/lib/bibleCSVimpl.py rename to openlp/plugins/bibles/lib/csvbible.py index cc837a6db..94ebf3ce7 100644 --- a/openlp/plugins/bibles/lib/bibleCSVimpl.py +++ b/openlp/plugins/bibles/lib/csvbible.py @@ -25,96 +25,97 @@ import logging import chardet +import csv -from openlp.plugins.bibles.lib.common import BibleCommon from openlp.core.lib import Receiver +from db import BibleDB -class BibleCSVImpl(BibleCommon): - global log - log = logging.getLogger(u'BibleCSVImpl') - log.info(u'BibleCVSImpl loaded') - def __init__(self, bibledb): +log = logging.getLogger(__name__) + +class CSVBible(BibleDB): + """ + This class provides a specialisation for importing of CSV Bibles. + """ + + def __init__(self, parent, **kwargs): """ Loads a Bible from a pair of CVS files passed in This class assumes the files contain all the information and a clean bible is being loaded. """ - self.bibledb = bibledb - self.loadbible = True - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'openlpstopimport'), self.stop_import) + BibleDB.__init__(self, parent, **kwargs) + log.info(self.__class__.__name__) + if u'booksfile' not in kwargs: + raise KeyError(u'You have to supply a file to import books from.') + self.booksfile = kwargs[u'booksfile'] + if u'versesfile' not in kwargs: + raise KeyError(u'You have to supply a file to import verses from.') + self.versesfile = kwargs[u'versesfile'] + #QtCore.QObject.connect(Receiver.get_receiver(), + # QtCore.SIGNAL(u'openlpstopimport'), self.stop_import) def stop_import(self): - self.loadbible = False + """ + Stops the import of the Bible. + """ + log.debug('Stopping import!') + self.stop_import = True - def load_data(self, booksfile, versesfile, dialogobject): + def do_import(self): #Populate the Tables success = True - fbooks = None + books_file = None try: - fbooks = open(booksfile, 'r') - count = 0 - for line in fbooks: + books_file = open(self.booksfile, 'r') + dialect = csv.Sniffer().sniff(books_file.read(1024)) + books_file.seek(0) + books_reader = csv.reader(books_file, dialect) + for line in books_reader: # cancel pressed - if not self.loadbible: + if self.stop_import: break - details = chardet.detect(line) - line = unicode(line, details['encoding']) - p = line.split(u',') - p1 = p[1].replace(u'"', u'') - p2 = p[2].replace(u'"', u'') - p3 = p[3].replace(u'"', u'') - self.bibledb.create_book(p2, p3, int(p1)) - count += 1 - #Flush the screen events - if count % 3 == 0: - Receiver.send_message(u'process_events') - count = 0 + details = chardet.detect(line[1]) + self.create_book(unicode(line[1], details['encoding']), + line[2], int(line[0])) + Receiver.send_message(u'process_events') except: log.exception(u'Loading books from file failed') success = False finally: - if fbooks: - fbooks.close() + if books_file: + books_file.close() if not success: return False - fverse = None + verse_file = None try: - fverse = open(versesfile, 'r') - count = 0 book_ptr = None - for line in fverse: - if not self.loadbible: # cancel pressed + verse_file = open(versesfile, 'r') + dialect = csv.Sniffer().sniff(verse_file.read(1024)) + verse_file.seek(0) + verse_reader = csv.reader(verse_file, dialect) + for line in verse_reader: + if self.stop_import: # cancel pressed break - details = chardet.detect(line) - line = unicode(line, details['encoding']) - # split into 3 units and leave the rest as a single field - p = line.split(u',', 3) - p0 = p[0].replace(u'"', u'') - p3 = p[3].replace(u'"', u'') - if book_ptr is not p0: - book = self.bibledb.get_bible_book(p0) + details = chardet.detect(line[3]) + if book_ptr != line[0]: + book = self.get_book(line[0]) book_ptr = book.name - # increament the progress bar - dialogobject.incrementProgressBar(u'Importing %s %s' % \ - book.name) - self.bibledb.add_verse(book.id, p[1], p[2], p3) - count += 1 - #Every x verses repaint the screen - if count % 3 == 0: - Receiver.send_message(u'process_events') - count = 0 - self.bibledb.save_verses() + self.wizard.incrementProgressBar( + u'Importing %s %s' % (book.name, line[1])) + self.commit() + self.create_verse(book.id, line[1], line[2], + unicode(line[3], details['encoding'])) + Receiver.send_message(u'process_events') + self.commit() except: log.exception(u'Loading verses from file failed') success = False finally: - if fverse: - fverse.close() - if not self.loadbible: - dialogobject.incrementProgressBar(u'Import canceled!') - dialogobject.ImportProgressBar.setValue( - dialogobject.ImportProgressBar.maximum()) + if verse_file: + verse_file.close() + if self.stop_import: + self.wizard.incrementProgressBar(u'Import canceled!') return False else: - return success \ No newline at end of file + return success + diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py new file mode 100644 index 000000000..deedf9d21 --- /dev/null +++ b/openlp/plugins/bibles/lib/db.py @@ -0,0 +1,286 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2010 Raoul Snyman # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, # +# Carsten Tinggaard # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### + +import os +import logging +import chardet + +from sqlalchemy import or_ +from PyQt4 import QtCore + +from openlp.plugins.bibles.lib.models import * + +log = logging.getLogger(__name__) + +class BibleDB(QtCore.QObject): + """ + This class represents a database-bound Bible. It is used as a base class + for all the custom importers, so that the can implement their own import + methods, but benefit from the database methods in here via inheritance, + rather than depending on yet another object. + """ + + def __init__(self, parent, **kwargs): + """ + The constructor loads up the database and creates and initialises the + tables if the database doesn't exist. + + **Required keyword arguments:** + + ``path`` + The path to the bible database file. + + ``name`` + The name of the database. This is also used as the file name for + SQLite databases. + + ``config`` + The configuration object, passed in from the plugin. + """ + log.info(u'BibleDBimpl loaded') + QtCore.QObject.__init__(self) + if u'path' not in kwargs: + raise KeyError(u'Missing keyword argument "path".') + if u'name' not in kwargs: + raise KeyError(u'Missing keyword argument "name".') + if u'config' not in kwargs: + raise KeyError(u'Missing keyword argument "config".') + self.stop_import = False + self.name = kwargs[u'name'] + self.config = kwargs[u'config'] + self.db_file = os.path.join(kwargs[u'path'], + u'%s.sqlite' % kwargs[u'name']) + log.debug(u'Load bible %s on path %s', kwargs[u'name'], self.db_file) + db_type = self.config.get_config(u'db type', u'sqlite') + db_url = u'' + if db_type == u'sqlite': + db_url = u'sqlite:///' + self.db_file + else: + db_url = u'%s://%s:%s@%s/%s' % \ + (db_type, self.config.get_config(u'db username'), + self.config.get_config(u'db password'), + self.config.get_config(u'db hostname'), + self.config.get_config(u'db database')) + self.metadata, self.session = init_models(db_url) + self.metadata.create_all(checkfirst=True) + + def register(self, wizard): + """ + This method basically just initialialises the database. It is called + from the Bible Manager when a Bible is imported. Descendant classes + may want to override this method to supply their own custom + initialisation as well. + """ + self.wizard = wizard + self.create_tables() + return self.name + + def commit(self): + log.debug('Committing...') + self.session.commit() + + def create_tables(self): + log.debug(u'createTables') + self.create_meta(u'dbversion', u'2') + self.create_testament(u'Old Testament') + self.create_testament(u'New Testament') + self.create_testament(u'Apocrypha') + + def create_testament(self, testament): + log.debug(u'BibleDB.create_testament("%s")', testament) + self.session.add(Testament.populate(name=testament)) + self.commit() + + def create_book(self, name, abbrev, testament=1): + log.debug(u'create_book %s,%s', name, abbrev) + book = Book.populate(name=name, abbreviation=abbrev, + testament_id=testament) + self.session.add(book) + self.commit() + return book + + def create_chapter(self, book_id, chapter, textlist): + log.debug(u'create_chapter %s,%s', book_id, chapter) + #text list has book and chapter as first two elements of the array + for verse_number, verse_text in textlist.iteritems(): + verse = Verse.populate( + book_id = book_id, + chapter = chapter, + verse = verse_number, + text = verse_text + ) + self.session.add(verse) + self.commit() + + def create_verse(self, book_id, chapter, verse, text): + if not isinstance(text, unicode): + details = chardet.detect(text) + text = unicode(text, details[u'encoding']) + verse = Verse.populate( + book_id=book_id, + chapter=chapter, + verse=verse, + text=text + ) + self.session.add(verse) + return verse + + def create_meta(self, key, value): + log.debug(u'save_meta %s/%s', key, value) + self.session.add(BibleMeta.populate(key=key, value=value)) + self.commit() + + def get_books(self): + log.debug(u'BibleDB.get_books()') + return self.session.query(Book).order_by(Book.id).all() + + def get_book(self, book): + log.debug(u'BibleDb.get_book("%s")', book) + db_book = self.session.query(Book)\ + .filter(Book.name.like(book + u'%'))\ + .first() + if db_book is None: + db_book = self.session.query(Book)\ + .filter(Book.abbreviation.like(book + u'%'))\ + .first() + return db_book + + def get_chapter(self, id, chapter): + log.debug(u'BibleDB.get_chapter("%s", %s)', id, chapter) + return self.session.query(Verse)\ + .filter_by(chapter=chapter)\ + .filter_by(book_id=id)\ + .first() + + def get_verses(self, reference_list): + """ + This is probably the most used function. It retrieves the list of + verses based on the user's query. + + ``reference_list`` + This is the list of references the media manager item wants. It is + a list of tuples, with the following format:: + + (book, chapter, start_verse, end_verse) + + Therefore, when you are looking for multiple items, simply break + them up into references like this, bundle them into a list. This + function then runs through the list, and returns an amalgamated + list of ``Verse`` objects. For example:: + + [(u'Genesis', 1, 1, 1), (u'Genesis', 2, 2, 3)] + """ + log.debug(u'BibleDB.get_verses: %s', reference_list) + verse_list = [] + for book, chapter, start_verse, end_verse in reference_list: + db_book = self.get_book(book) + if end_verse == -1: + end_verse = self.get_verse_count(book, chapter) + if db_book: + book = db_book.name + log.debug(u'Book name corrected to "%s"', book) + verses = self.session.query(Verse)\ + .filter_by(book_id=db_book.id)\ + .filter_by(chapter=chapter)\ + .filter(Verse.verse >= start_verse)\ + .filter(Verse.verse <= end_verse)\ + .order_by(Verse.verse)\ + .all() + verse_list.extend(verses) + return verse_list + + def verse_search(self, text): + """ + Search for verses containing text ``text``. + + ``text`` + The text to search for. If the text contains commas, it will be + split apart and OR'd on the list of values. If the text just + contains spaces, it will split apart and AND'd on the list of + values. + """ + log.debug(u'BibleDB.verse_search("%s")', text) + verses = self.session.query(Verse) + if text.find(u',') > -1: + or_clause = [] + keywords = [u'%%%s%%' % keyword.strip() for keyword in text.split(u',')] + for keyword in keywords: + or_clause.append(Verse.text.like(keyword)) + verses = verses.filter(or_(*or_clause)) + else: + keywords = [u'%%%s%%' % keyword.strip() for keyword in text.split(u' ')] + for keyword in keywords: + verses = verses.filter(Verse.text.like(keyword)) + verses = verses.all() + return verses + + def get_chapter_count(self, book): + log.debug(u'BibleDB.get_chapter_count("%s")', book) + count = self.session.query(Verse.chapter).join(Book)\ + .filter(Book.name==book)\ + .distinct().count() + #verse = self.session.query(Verse).join(Book).filter( + # Book.name == bookname).order_by(Verse.chapter.desc()).first() + if not count: + return 0 + else: + return count + + def get_verse_count(self, book, chapter): + log.debug(u'BibleDB.get_verse_count("%s", %s)', book, chapter) + count = self.session.query(Verse).join(Book)\ + .filter(Book.name==book)\ + .filter(Verse.chapter==chapter)\ + .count() + #verse = self.session.query(Verse).join(Book).filter( + # Book.name == bookname).filter( + # Verse.chapter == chapter).order_by(Verse.verse.desc()).first() + if not count: + return 0 + else: + return count + + def get_meta(self, key): + log.debug(u'get meta %s', key) + return self.session.query(BibleMeta).get(key) + + def delete_meta(self, metakey): + biblemeta = self.get_meta(metakey) + try: + self.session.delete(biblemeta) + self.commit() + return True + except: + return False + + def dump_bible(self): + log.debug(u'.........Dumping Bible Database') + log.debug('...............................Books ') + books = self.session.query(Book).all() + log.debug(books) + log.debug(u'...............................Verses ') + verses = self.session.query(Verse).all() + log.debug(verses) + diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py new file mode 100644 index 000000000..1cf92e4d2 --- /dev/null +++ b/openlp/plugins/bibles/lib/http.py @@ -0,0 +1,361 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2010 Raoul Snyman # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, # +# Carsten Tinggaard # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### + +import logging +import urllib2 +import os +import sqlite3 + +from BeautifulSoup import BeautifulSoup + +from openlp.core.lib import Receiver +from common import BibleCommon, SearchResults +from db import BibleDB +from openlp.plugins.bibles.lib.models import Book + +class HTTPBooks(object): + cursor = None + + @staticmethod + def get_cursor(): + if HTTPBooks.cursor is None: + filepath = os.path.join(os.path.dirname(os.path.abspath(__file__)), + u'..', u'resources', u'httpbooks.sqlite') + conn = sqlite3.connect(filepath) + HTTPBooks.cursor = conn.cursor() + return HTTPBooks.cursor + + @staticmethod + def run_sql(query, parameters=()): + cursor = HTTPBooks.get_cursor() + cursor.execute(query, parameters) + return cursor.fetchall() + + @staticmethod + def get_books(): + books = HTTPBooks.run_sql(u'SELECT id, testament_id, name, ' + u'abbreviation, chapters FROM books ORDER BY id') + book_list = [] + for book in books: + book_list.append({ + u'id': book[0], + u'testament_id': book[1], + u'name': unicode(book[2]), + u'abbreviation': unicode(book[3]), + u'chapters': book[4] + }) + return book_list + + @staticmethod + def get_book(name): + if not isinstance(name, unicode): + name = unicode(name) + books = HTTPBooks.run_sql(u'SELECT id, testament_id, name, ' + u'abbreviation, chapters FROM books WHERE name = ? OR ' + u'abbreviation = ?', (name, name)) + if len(books) > 0: + return { + u'id': books[0][0], + u'testament_id': books[0][1], + u'name': unicode(books[0][2]), + u'abbreviation': unicode(books[0][3]), + u'chapters': books[0][4] + } + else: + return None + + @staticmethod + def get_chapter(name, chapter): + if not isinstance(name, int): + chapter = int(chapter) + book = HTTPBooks.get_book(name) + chapters = HTTPBooks.run_sql(u'SELECT id, book_id, chapter, ' + u'verses FROM chapters WHERE book_id = ?', (book[u'id'],)) + if len(chapters) > 0: + return { + u'id': chapters[0][0], + u'book_id': chapters[0][1], + u'chapter': chapters[0][2], + u'verses': chapters[0][3] + } + else: + return None + + @staticmethod + def get_chapter_count(book): + details = HTTPBooks.get_book(book) + if details: + return details[u'chapters'] + return 0 + + @staticmethod + def get_verse_count(book, chapter): + details = HTTPBooks.get_chapter(book, chapter) + if details: + return details[u'verses'] + return 0 + + +class BGExtract(BibleCommon): + global log + log = logging.getLogger(u'BibleHTTPMgr(BG_extract)') + log.info(u'BG_extract loaded') + + def __init__(self, proxyurl=None): + log.debug(u'init %s', proxyurl) + self.proxyurl = proxyurl + + def get_bible_chapter(self, version, bookname, chapter) : + """ + Access and decode bibles via the BibleGateway website + + ``Version`` + The version of the bible like 31 for New International version + + ``bookname`` + Name of the Book + + ``chapter`` + Chapter number + """ + log.debug(u'get_bible_chapter %s, %s, %s', version, bookname, chapter) + urlstring = u'http://www.biblegateway.com/passage/?search=%s+%s' \ + u'&version=%s' % (bookname, chapter, version) + log.debug(u'BibleGateway url = %s' % urlstring) + xml_string = self._get_web_text(urlstring, self.proxyurl) + verseSearch = u'<sup class=\"versenum' + verseFootnote = u'<sup class=\'footnote' + verse = 1 + i = xml_string.find(u'result-text-style-normal') + 26 + xml_string = xml_string[i:len(xml_string)] + versePos = xml_string.find(verseSearch) + bible = {} + while versePos > -1: + # clear out string + verseText = u'' + versePos = xml_string.find(u'</sup>', versePos) + 6 + i = xml_string.find(verseSearch, versePos + 1) + # Not sure if this is needed now + if i == -1: + i = xml_string.find(u'</div', versePos + 1) + j = xml_string.find(u'<strong', versePos + 1) + if j > 0 and j < i: + i = j + verseText = xml_string[versePos + 7 : i ] + # store the verse + bible[verse] = self._clean_text(verseText) + versePos = -1 + else: + verseText = xml_string[versePos: i] + start_tag = verseText.find(verseFootnote) + while start_tag > -1: + end_tag = verseText.find(u'</sup>') + verseText = verseText[:start_tag] + verseText[end_tag + 6:len(verseText)] + start_tag = verseText.find(verseFootnote) + # Chop off verse and start again + xml_string = xml_string[i:] + #look for the next verse + versePos = xml_string.find(verseSearch) + # store the verse + bible[verse] = self._clean_text(verseText) + verse += 1 + return SearchResults(bookname, chapter, bible) + +class CWExtract(BibleCommon): + log.info(u'%s loaded', __name__) + + def __init__(self, proxyurl=None): + log.debug(u'init %s', proxyurl) + self.proxyurl = proxyurl + + def get_bible_chapter(self, version, bookname, chapter): + log.debug(u'%s %s, %s, %s', __name__, version, bookname, chapter) + """ + Access and decode bibles via the Crosswalk website + + ``version`` + The version of the bible like niv for New International Version + + ``bookname`` + Text name of in english e.g. 'gen' for Genesis + + ``chapter`` + Chapter number + """ + log.debug(u'get_bible_chapter %s,%s,%s', + version, bookname, chapter) + bookname = bookname.replace(u' ', u'') + chapter_url = u'http://www.biblestudytools.com/%s/%s/%s.html' % \ + (version, bookname.lower(), chapter) + log.debug(u'URL: %s', chapter_url) + page = urllib2.urlopen(chapter_url) + if not page: + return None + soup = BeautifulSoup(page) + htmlverses = soup.findAll(u'span', u'versetext') + verses = {} + for verse in htmlverses: + Receiver.send_message(u'process_events') + versenumber = int(verse.contents[0].contents[0]) + versetext = u'' + for part in verse.contents: + if str(part)[0] != u'<': + versetext = versetext + part + versetext = versetext.strip(u'\n\r\t ') + verses[versenumber] = versetext + return SearchResults(bookname, chapter, verses) + + +class HTTPBible(BibleDB): + log.info(u'%s loaded', __name__) + + def __init__(self, parent, **kwargs): + """ + Finds all the bibles defined for the system + Creates an Interface Object for each bible containing connection + information + + Throws Exception if no Bibles are found. + + Init confirms the bible exists and stores the database path. + """ + BibleDB.__init__(self, parent, **kwargs) + if u'download_source' not in kwargs: + raise KeyError(u'Missing keyword argument "download_source"') + if u'download_name' not in kwargs: + raise KeyError(u'Missing keyword argument "download_name"') + self.download_source = kwargs[u'download_source'] + self.download_name = kwargs[u'download_name'] + if u'proxy_server' in kwargs: + self.proxy_server = kwargs[u'proxy_server'] + else: + self.proxy_server = None + if u'proxy_username' in kwargs: + self.proxy_username = kwargs[u'proxy_username'] + else: + self.proxy_username = None + if u'proxy_password' in kwargs: + self.proxy_password = kwargs[u'proxy_password'] + else: + self.proxy_password = None + + def do_import(self): + self.wizard.ImportProgressBar.setMaximum(2) + self.wizard.incrementProgressBar('Registering bible...') + self.create_meta(u'download source', self.download_source) + self.create_meta(u'download name', self.download_name) + if self.proxy_server: + self.create_meta(u'proxy server', self.proxy_server) + if self.proxy_username: + # store the proxy userid + self.create_meta(u'proxy username', self.proxy_username) + if self.proxy_password: + # store the proxy password + self.create_meta(u'proxy password', self.proxy_password) + self.wizard.incrementProgressBar('Registered.') + return True + + def get_verses(self, reference_list): + """ + A reimplementation of the ``BibleDB.get_verses`` method, this one is + specifically for web Bibles. It first checks to see if the particular + chapter exists in the DB, and if not it pulls it from the web. If the + chapter DOES exist, it simply pulls the verses from the DB using the + ancestor method. + + ``reference_list`` + This is the list of references the media manager item wants. It is + a list of tuples, with the following format:: + + (book, chapter, start_verse, end_verse) + + Therefore, when you are looking for multiple items, simply break + them up into references like this, bundle them into a list. This + function then runs through the list, and returns an amalgamated + list of ``Verse`` objects. For example:: + + [(u'Genesis', 1, 1, 1), (u'Genesis', 2, 2, 3)] + """ + for reference in reference_list: + log.debug('Reference: %s', reference) + book = reference[0] + db_book = self.get_book(book) + if not db_book: + book_details = self.lookup_book(book) + if not book_details: + Receiver.send_message(u'bible_nobook') + return [] + db_book = self.create_book(book_details[u'name'], + book_details[u'abbreviation'], book_details[u'testament_id']) + book = db_book.name + if BibleDB.get_verse_count(self, book, reference[1]) == 0: + Receiver.send_message(u'bible_showprogress') + Receiver.send_message(u'process_events') + search_results = self.get_chapter(self.name, book, reference[1]) + if search_results and search_results.has_verselist(): + ## We have found a book of the bible lets check to see + ## if it was there. By reusing the returned book name + ## we get a correct book. For example it is possible + ## to request ac and get Acts back. + bookname = search_results.get_book() + # check to see if book/chapter exists + db_book = self.get_book(bookname) + self.create_chapter(db_book.id, search_results.get_chapter(), + search_results.get_verselist()) + Receiver.send_message(u'bible_hideprogress') + Receiver.send_message(u'process_events') + return BibleDB.get_verses(self, reference_list) + + def get_chapter(self, version, book, chapter): + """ + Receive the request and call the relevant handler methods + """ + log.debug(u'get_chapter %s, %s, %s', version, book, chapter) + log.debug(u'source = %s', self.download_source) + try: + if self.download_source.lower() == u'crosswalk': + ev = CWExtract(self.proxy_server) + else: + ev = BGExtract(self.proxy_server) + return ev.get_bible_chapter(self.download_name, book, chapter) + except: + log.exception("Failed to get bible chapter") + return None + + def get_books(self): + return [Book.populate(name=book['name']) for book in HTTPBooks.get_books()] + + def lookup_book(self, book): + return HTTPBooks.get_book(book) + + def get_chapter_count(self, book): + return HTTPBooks.get_chapter_count(book) + + def get_verse_count(self, book, chapter): + return HTTPBooks.get_verse_count(book, chapter) + + def set_proxy_server(self, server): + self.proxy_server = server + diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index d44a18262..965d0433f 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -26,37 +26,60 @@ import logging import os -from bibleOpenSongimpl import BibleOpenSongImpl -from bibleOSISimpl import BibleOSISImpl -from bibleCSVimpl import BibleCSVImpl -from bibleDBimpl import BibleDBImpl -from bibleHTTPimpl import BibleHTTPImpl +from common import parse_reference +from opensong import OpenSongBible +from osis import OSISBible +from csvbible import CSVBible +from db import BibleDB +from http import HTTPBible class BibleMode(object): + """ + This is basically an enumeration class which specifies the mode of a Bible. + Mode refers to whether or not a Bible in OpenLP is a full Bible or needs to + be downloaded from the Internet on an as-needed basis. + """ Full = 1 Partial = 2 class BibleFormat(object): + """ + This is a special enumeration class that holds the various types of Bibles, + plus a few helper functions to facilitate generic handling of Bible types + for importing. + """ Unknown = -1 OSIS = 0 CSV = 1 OpenSong = 2 WebDownload = 3 - @classmethod - def get_handler(class_, id): - if id == class_.OSIS: - return BibleOSISImpl - elif id == class_.CSV: - return BibleCSVImpl - elif id == class_.OpenSong: - return BibleOpenSongImpl - elif id == class_.WebDownload: - return BibleHTTPImpl + @staticmethod + def get_class(id): + """ + Return the appropriate imeplementation class. + """ + if id == BibleFormat.OSIS: + return OSISBible + elif id == BibleFormat.CSV: + return CSVBible + elif id == BibleFormat.OpenSong: + return OpenSongBible + elif id == BibleFormat.WebDownload: + return HTTPBible else: return None + @staticmethod + def list(): + return [ + BibleFormat.OSIS, + BibleFormat.CSV, + BibleFormat.OpenSong, + BibleFormat.WebDownload + ] + class BibleManager(object): """ @@ -66,300 +89,139 @@ class BibleManager(object): log = logging.getLogger(u'BibleManager') log.info(u'Bible manager loaded') - def __init__(self, config): + def __init__(self, parent, config): """ - Finds all the bibles defined for the system and creates an - interface object for each bible containing connection - information. Throws Exception if no Bibles are found. + Finds all the bibles defined for the system and creates an interface + object for each bible containing connection information. Throws + Exception if no Bibles are found. Init confirms the bible exists and stores the database path. ``config`` The plugin's configuration object. """ - self.config = config log.debug(u'Bible Initialising') + self.config = config + self.parent = parent self.web = u'Web' - # dict of bible database objects - self.bible_db_cache = None - # dict of bible http readers - self.bible_http_cache = None - self.biblePath = self.config.get_data_path() - #get proxy name for screen - self.proxyname = self.config.get_config(u'proxy name') - self.bibleSuffix = u'sqlite' - self.dialogobject = None + self.db_cache = None + self.path = self.config.get_data_path() + self.proxy_name = self.config.get_config(u'proxy name') + self.suffix = u'sqlite' + self.import_wizard = None self.reload_bibles() self.media = None def reload_bibles(self): + """ + Reloads the Bibles from the available Bible databases on disk. If a web + Bible is encountered, an instance of HTTPBible is loaded instead of the + BibleDB class. + """ log.debug(u'Reload bibles') - files = self.config.get_files(self.bibleSuffix) + files = self.config.get_files(self.suffix) log.debug(u'Bible Files %s', files) - self.bible_db_cache = {} - self.bible_http_cache = {} - # books of the bible with testaments - self.book_testaments = {} - # books of the bible with chapter count - self.book_chapters = [] - # books of the bible with abbreviation - self.book_abbreviations = {} - self.web_bibles_present = False - for f in files: - nme = f.split(u'.') - bname = nme[0] - self.bible_db_cache[bname] = BibleDBImpl(self.biblePath, - bname, self.config) + self.db_cache = {} + for filename in files: + name, extension = os.path.splitext(filename) + self.db_cache[name] = BibleDB(self.parent, path=self.path, name=name, config=self.config) # look to see if lazy load bible exists and get create getter. - biblesource = self.bible_db_cache[bname].get_meta(u'WEB') - if biblesource: - self.web_bibles_present = True - nhttp = BibleHTTPImpl() - # tell The Server where to get the verses from. - nhttp.set_bible_source(biblesource.value) - self.bible_http_cache [bname] = nhttp - # look to see if lazy load bible exists and get create getter. - meta = self.bible_db_cache[bname].get_meta(u'proxy') - proxy = None - if meta: - proxy = meta.value - # tell The Server where to get the verses from. - nhttp.set_proxy(proxy) - # look to see if lazy load bible exists and get create getter. - bibleid = self.bible_db_cache[bname].get_meta(u'bibleid').value - # tell The Server where to get the verses from. - nhttp.set_bibleid(bibleid) - else: - # makes the Full / partial code easier. - self.bible_http_cache [bname] = None - if self.web_bibles_present: - # books of the bible linked to bibleid {osis, name} - self.book_testaments = {} - # books of the bible linked to bibleid {osis, abbrev} - self.book_abbreviations = {} - filepath = os.path.split(os.path.abspath(__file__))[0] - filepath = os.path.abspath(os.path.join( - filepath, u'..', u'resources',u'httpbooks.csv')) - fbibles = None - try: - fbibles = open(filepath, u'r') - for line in fbibles: - p = line.split(u',') - self.book_abbreviations[p[0]] = p[1].replace(u'\n', '') - self.book_testaments[p[0]] = p[2].replace(u'\n', '') - self.book_chapters.append({u'book':p[0], u'total':p[3].replace(u'\n', '')}) - except: - log.exception(u'Failed to load bible') - finally: - if fbibles: - fbibles.close() - log.debug(u'Bible Initialised') + source = self.db_cache[name].get_meta(u'download source') + if source: + download_name = self.db_cache[name].get_meta(u'download name').value + meta_proxy = self.db_cache[name].get_meta(u'proxy url') + web_bible = HTTPBible(self.parent, path=self.path, name=name, + config=self.config, download_source=source.value, + download_name=download_name) + if meta_proxy: + web_bible.set_proxy_server(meta_proxy.value) + #del self.db_cache[name] + self.db_cache[name] = web_bible + log.debug(u'Bibles reloaded') - def set_process_dialog(self, dialogobject): + def set_process_dialog(self, wizard): """ Sets the reference to the dialog with the progress bar on it. - ``dialogobject`` - The reference to the dialog. + ``dialog`` + The reference to the import wizard. """ - self.dialogobject = dialogobject + self.import_wizard = wizard def import_bible(self, type, **kwargs): """ Register a bible in the bible cache, and then import the verses. ``type`` - What type of Bible, + What type of Bible, one of the ``BibleFormat`` values. + + ``**kwargs`` + Keyword arguments to send to the actual importer class. """ - pass + class_ = BibleFormat.get_class(type) + kwargs['path'] = self.path + kwargs['config'] = self.config + importer = class_(self.parent, **kwargs) + name = importer.register(self.import_wizard) + self.db_cache[name] = importer + return importer.do_import() - def register_http_bible(self, biblename, biblesource, bibleid, - proxyurl=None, proxyid=None, proxypass=None): + def get_bibles(self): """ - Return a list of bibles from a given URL. The selected Bible - can then be registered and LazyLoaded into a database. - - ``biblename`` - The name of the bible to register. - - ``biblesource`` - Where this Bible stores it's verses. - - ``bibleid`` - The identifier for a Bible. - - ``proxyurl`` - Defaults to *None*. An optional URL to a proxy server. - - ``proxyid`` - Defaults to *None*. A username for logging into the proxy - server. - - ``proxypass`` - Defaults to *None*. The password to accompany the username. - """ - log.debug(u'register_HTTP_bible %s, %s, %s, %s, %s, %s', - biblename, biblesource, bibleid, proxyurl, proxyid, proxypass) - if self._is_new_bible(biblename): - # Create new Bible - nbible = BibleDBImpl(self.biblePath, biblename, self.config) - # Create Database - nbible.create_tables() - self.bible_db_cache[biblename] = nbible - nhttp = BibleHTTPImpl() - nhttp.set_bible_source(biblesource) - self.bible_http_cache[biblename] = nhttp - # register a lazy loading interest - nbible.save_meta(u'WEB', biblesource) - # store the web id of the bible - nbible.save_meta(u'bibleid', bibleid) - if proxyurl: - # store the proxy URL - nbible.save_meta(u'proxy', proxyurl) - nhttp.set_proxy(proxyurl) - if proxyid: - # store the proxy userid - nbible.save_meta(u'proxyid', proxyid) - if proxypass: - # store the proxy password - nbible.save_meta(u'proxypass', proxypass) - return True - else: - log.debug(u'register_http_file_bible %s not created already exists', - biblename) - return False - - def register_csv_file_bible(self, biblename, booksfile, versefile): - """ - Method to load a bible from a set of files into a database. - If the database exists it is deleted and the database is reloaded - from scratch. - """ - log.debug(u'register_CSV_file_bible %s,%s,%s', - biblename, booksfile, versefile) - if self._is_new_bible(biblename): - # Create new Bible - nbible = BibleDBImpl(self.biblePath, biblename, self.config) - # Create database - nbible.create_tables() - # Cache the database for use later - self.bible_db_cache[biblename] = nbible - # Create the loader and pass in the database - bcsv = BibleCSVImpl(nbible) - return bcsv.load_data(booksfile, versefile, self.dialogobject) - else: - log.debug(u'register_csv_file_bible %s not created already exists', - biblename) - return False - - def register_osis_file_bible(self, biblename, osisfile): - """ - Method to load a bible from a osis xml file extracted from Sword bible - viewer. If the database exists it is deleted and the database is - reloaded from scratch. - """ - log.debug(u'register_OSIS_file_bible %s, %s', biblename, osisfile) - if self._is_new_bible(biblename): - # Create new Bible - nbible = BibleDBImpl(self.biblePath, biblename, self.config) - # Create Database - nbible.create_tables() - # Cache the database for use later - self.bible_db_cache[biblename] = nbible - # Create the loader and pass in the database - bosis = BibleOSISImpl(self.biblePath, nbible) - return bosis.load_data(osisfile, self.dialogobject) - else: - log.debug( - u'register_OSIS_file_bible %s, %s not created already exists', - biblename, osisfile) - return False - - def register_opensong_bible(self, biblename, opensongfile): - """ - Method to load a bible from an OpenSong xml file. If the database - exists it is deleted and the database is reloaded from scratch. - """ - log.debug(u'register_opensong_file_bible %s, %s', biblename, opensongfile) - if self._is_new_bible(biblename): - # Create new Bible - nbible = BibleDBImpl(self.biblePath, biblename, self.config) - # Create Database - nbible.create_tables() - # Cache the database for use later - self.bible_db_cache[biblename] = nbible - # Create the loader and pass in the database - bcsv = BibleOpenSongImpl(self.biblePath, nbible) - bcsv.load_data(opensongfile, self.dialogobject) - return True - else: - log.debug(u'register_opensong_file_bible %s, %s not created ' - u'already exists', biblename, opensongfile) - return False - - def get_bibles(self, mode=BibleMode.Full): - """ - Returns a list of Books of the bible. When ``mode`` is set to - ``BibleMode.Full`` this method returns all the Bibles for the - Advanced Search, and when the mode is ``BibleMode.Partial`` - this method returns all the bibles for the Quick Search. + Returns a list of the names of available Bibles. """ log.debug(u'get_bibles') - bible_list = [] - for bible_name, bible_object in self.bible_db_cache.iteritems(): - if self.bible_http_cache[bible_name]: - bible_name = u'%s (%s)' % (bible_name, self.web) - bible_list.append(bible_name) - return bible_list + return [name for name, bible in self.db_cache.iteritems()] - def is_bible_web(self, bible): - pos_end = bible.find(u' (%s)' % self.web) - if pos_end != -1: - return True, bible[:pos_end] - return False, bible - - def get_bible_books(self): + def get_books(self, bible): """ - Returns a list of the books of the bible - """ - log.debug(u'get_bible_books') - return self.book_chapters + Returns a list of Bible books, and the number of chapters in that book. - def get_book_chapter_count(self, book): + ``bible`` + Unicode. The Bible to get the list of books from. + """ + log.debug(u'BibleManager.get_books("%s")', bible) + return [ + { + u'name': book.name, + u'chapters': self.db_cache[bible].get_chapter_count(book.name) + } + for book in self.db_cache[bible].get_books() + ] + + def get_chapter_count(self, bible, book): """ Returns the number of Chapters for a given book """ log.debug(u'get_book_chapter_count %s', book) - return self.book_chapters[book] + return self.db_cache[bible].get_chapter_count(book) - def get_book_verse_count(self, bible, book, chapter): + def get_verse_count(self, bible, book, chapter): """ Returns all the number of verses for a given book and chapterMaxBibleBookVerses """ - log.debug(u'get_book_verse_count %s,%s,%s', bible, book, chapter) - web, bible = self.is_bible_web(bible) - if web: - count = self.bible_db_cache[bible].get_max_bible_book_verses( - book, chapter) - if count == 0: - # Make sure the first chapter has been downloaded - self.get_verse_text(bible, book, chapter, chapter, 1, 1) - count = self.bible_db_cache[bible].get_max_bible_book_verses( - book, chapter) - return count - else: - return self.bible_db_cache[bible].get_max_bible_book_verses( - book, chapter) + log.debug(u'BibleManager.get_verse_count("%s", "%s", %s)', bible, book, chapter) + return self.db_cache[bible].get_verse_count(book, chapter) - def get_verse_from_text(self, bible, versetext): + def get_verses(self, bible, versetext): """ - Returns all the number of verses for a given - book and chapterMaxBibleBookVerses + Parses a scripture reference, fetches the verses from the Bible + specified, and returns a list of ``Verse`` objects. + + ``bible`` + Unicode. The Bible to use. + + ``versetext`` + Unicode. The scripture reference. Valid scripture references are: + + - Genesis 1:1 + - Genesis 1:1-10 + - Genesis 1:1-2:10 """ - log.debug(u'get_verses_from_text %s,%s', bible, versetext) - web, bible = self.is_bible_web(bible) - return self.bible_db_cache[bible].get_verses_from_text(versetext) + log.debug(u'BibleManager.get_verses("%s", "%s")', bible, versetext) + reflist = parse_reference(versetext) + return self.db_cache[bible].get_verses(reflist) def save_meta_data(self, bible, version, copyright, permissions): """ @@ -367,124 +229,28 @@ class BibleManager(object): """ log.debug(u'save_meta data %s,%s, %s,%s', bible, version, copyright, permissions) - self.bible_db_cache[bible].save_meta(u'Version', version) - self.bible_db_cache[bible].save_meta(u'Copyright', copyright) - self.bible_db_cache[bible].save_meta(u'Permissions', permissions) + self.db_cache[bible].create_meta(u'Version', version) + self.db_cache[bible].create_meta(u'Copyright', copyright) + self.db_cache[bible].create_meta(u'Permissions', permissions) def get_meta_data(self, bible, key): """ Returns the meta data for a given key """ log.debug(u'get_meta %s,%s', bible, key) - web, bible = self.is_bible_web(bible) - return self.bible_db_cache[bible].get_meta(key) + return self.db_cache[bible].get_meta(key) - def get_verse_text(self, bible, bookname, schapter, echapter, sverse, - everse=0): - """ - Returns a list of verses for a given Book, Chapter and ranges of verses. - If the end verse(everse) is less then the start verse(sverse) - then only one verse is returned - - ``bible`` - The name of the bible to be used - - Rest can be guessed at ! - """ - text = [] - self.media.setQuickMessage(u'') - log.debug(u'get_verse_text %s,%s,%s,%s,%s,%s', - bible, bookname, schapter, echapter, sverse, everse) - # check to see if book/chapter exists fow HTTP bibles and load cache - # if necessary - web, bible = self.is_bible_web(bible) - if self.bible_http_cache[bible]: - book = self.bible_db_cache[bible].get_bible_book(bookname) - if book is None: - log.debug(u'get_verse_text : new book') - for chapter in range(schapter, echapter + 1): - self.media.setQuickMessage( - unicode(self.media.trUtf8('Downloading %s: %s')) % - (bookname, chapter)) - search_results = \ - self.bible_http_cache[bible].get_bible_chapter( - bible, bookname, chapter) - if search_results.has_verselist() : - ## We have found a book of the bible lets check to see - ## if it was there. By reusing the returned book name - ## we get a correct book. For example it is possible - ## to request ac and get Acts back. - bookname = search_results.get_book() - # check to see if book/chapter exists - book = self.bible_db_cache[bible].get_bible_book( - bookname) - if book is None: - ## Then create book, chapter and text - book = self.bible_db_cache[bible].create_book( - bookname, self.book_abbreviations[bookname], - self.book_testaments[bookname]) - log.debug(u'New http book %s, %s, %s', - book, book.id, book.name) - self.bible_db_cache[bible].create_chapter( - book.id, search_results.get_chapter(), - search_results.get_verselist()) - else: - ## Book exists check chapter and texts only. - v = self.bible_db_cache[bible].get_bible_chapter( - book.id, chapter) - if v is None: - self.media.setQuickMessage( - unicode(self.media.trUtf8('%Downloading %s: %s'))\ - % (bookname, chapter)) - self.bible_db_cache[bible].create_chapter( - book.id, chapter, - search_results.get_verselist()) - else: - log.debug(u'get_verse_text : old book') - for chapter in range(schapter, echapter + 1): - v = self.bible_db_cache[bible].get_bible_chapter( - book.id, chapter) - if v is None: - try: - self.media.setQuickMessage(\ - unicode(self.media.trUtf8('Downloading %s: %s')) - % (bookname, chapter)) - search_results = \ - self.bible_http_cache[bible].get_bible_chapter( - bible, bookname, chapter) - if search_results.has_verselist(): - self.bible_db_cache[bible].create_chapter( - book.id, search_results.get_chapter(), - search_results.get_verselist()) - except: - log.exception(u'Problem getting scripture online') - #Now get verses from database - if schapter == echapter: - text = self.bible_db_cache[bible].get_bible_text(bookname, - schapter, sverse, everse) - else: - for i in range (schapter, echapter + 1): - if i == schapter: - start = sverse - end = self.get_book_verse_count(bible, bookname, i) - elif i == echapter: - start = 1 - end = everse - else: - start = 1 - end = self.get_book_verse_count(bible, bookname, i) - - txt = self.bible_db_cache[bible].get_bible_text( - bookname, i, start, end) - text.extend(txt) - return text - - def _is_new_bible(self, name): + def exists(self, name): """ Check cache to see if new bible """ - for bible, o in self.bible_db_cache.iteritems(): + if not isinstance(name, unicode): + name = unicode(name) + for bible, db_object in self.db_cache.iteritems(): log.debug(u'Bible from cache in is_new_bible %s', bible) + if not isinstance(bible, unicode): + bible = unicode(bible) if bible == name: - return False - return True \ No newline at end of file + return True + return False + diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index f7ede9bac..4fce998ae 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -31,13 +31,19 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import MediaManagerItem, Receiver, str_to_bool, \ BaseListWithDnD from openlp.plugins.bibles.forms import ImportWizardForm -from openlp.plugins.bibles.lib.manager import BibleMode class BibleListView(BaseListWithDnD): + """ + Drag and drop capable list for Bibles. + """ def __init__(self, parent=None): self.PluginName = u'Bibles' BaseListWithDnD.__init__(self, parent) + def resizeEvent(self, event): + self.parent.onListViewResize(event.size().width(), event.size().width()) + + class BibleMediaItem(MediaManagerItem): """ This is the custom media manager item for Bibles. @@ -52,6 +58,7 @@ class BibleMediaItem(MediaManagerItem): self.IconPath = u'songs/song' self.ListViewWithDnD_class = BibleListView self.servicePath = None + self.lastReference = [] MediaManagerItem.__init__(self, parent, icon, title) # place to store the search results self.search_results = {} @@ -237,6 +244,24 @@ class BibleMediaItem(MediaManagerItem): QtCore.SIGNAL(u'pressed()'), self.onQuickSearchButton) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'config_updated'), self.configUpdated) + # Other stuff + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'bible_showprogress'), self.onSearchProgressShow) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'bible_hideprogress'), self.onSearchProgressHide) + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'bible_nobook'), self.onNoBookFound) + + def addListViewToToolBar(self): + MediaManagerItem.addListViewToToolBar(self) + # Progress Bar + self.SearchProgress = QtGui.QProgressBar(self) + self.SearchProgress.setFormat('%p%') + self.SearchProgress.setMaximum(3) + self.SearchProgress.setGeometry(self.ListView.geometry().left(), + self.ListView.geometry().top(), 81, 23) + self.SearchProgress.setVisible(False) + self.SearchProgress.setObjectName(u'SearchProgress') def configUpdated(self): if str_to_bool( @@ -277,7 +302,7 @@ class BibleMediaItem(MediaManagerItem): def initialise(self): log.debug(u'bible manager initialise') - self.parent.biblemanager.media = self + self.parent.manager.media = self self.loadBibles() self.configUpdated() log.debug(u'bible manager initialise complete') @@ -297,23 +322,40 @@ class BibleMediaItem(MediaManagerItem): self.AdvancedSecondBibleComboBox.clear() self.QuickSecondBibleComboBox.addItem(u'') self.AdvancedSecondBibleComboBox.addItem(u'') - bibles = self.parent.biblemanager.get_bibles(BibleMode.Full) + bibles = self.parent.manager.get_bibles() # load bibles into the combo boxes + first = True for bible in bibles: self.QuickVersionComboBox.addItem(bible) self.QuickSecondBibleComboBox.addItem(bible) - # Without HTTP - bibles = self.parent.biblemanager.get_bibles(BibleMode.Partial) - first = True - # load bibles into the combo boxes - for bible in bibles: self.AdvancedVersionComboBox.addItem(bible) self.AdvancedSecondBibleComboBox.addItem(bible) if first: first = False - # use the first bible as the trigger self.initialiseBible(bible) + def onListViewResize(self, width, height): + self.SearchProgress.setGeometry(self.ListView.geometry().x(), + (self.ListView.geometry().y() + self.ListView.geometry().height())\ + - 23, 81, 23) + + def onSearchProgressShow(self): + self.SearchProgress.setVisible(True) + self.SearchProgress.setMinimum(0) + self.SearchProgress.setMaximum(2) + self.SearchProgress.setValue(1) + + def onSearchProgressHide(self): + self.SearchProgress.setVisible(False) + + def onNoBookFound(self): + QtGui.QMessageBox.critical(self, + self.trUtf8('No Book Found'), + self.trUtf8('No matching book could be found in this Bible.'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok), + QtGui.QMessageBox.Ok + ) + def onAdvancedVersionComboBox(self): self.initialiseBible( unicode(self.AdvancedVersionComboBox.currentText())) @@ -326,11 +368,8 @@ class BibleMediaItem(MediaManagerItem): self.AdvancedBookComboBox.itemData(item).toInt()[0]) def onNewClick(self): - #self.bibleimportform = BibleImportForm( - # self.parent.config, self.parent.biblemanager, self) - #self.bibleimportform.exec_() self.bibleimportform = ImportWizardForm(self, self.parent.config, - self.parent.biblemanager, self.parent) + self.parent.manager, self.parent) self.bibleimportform.exec_() self.reloadBibles() @@ -339,14 +378,13 @@ class BibleMediaItem(MediaManagerItem): self.adjustComboBox(frm, self.verses, self.AdvancedToVerse) def onAdvancedToChapter(self): - text1 = unicode(self.AdvancedFromChapter.currentText()) - text2 = unicode(self.AdvancedToChapter.currentText()) - if text1 != text2: + frm = unicode(self.AdvancedFromChapter.currentText()) + to = unicode(self.AdvancedToChapter.currentText()) + if frm != to: bible = unicode(self.AdvancedVersionComboBox.currentText()) book = unicode(self.AdvancedBookComboBox.currentText()) # get the verse count for new chapter - verses = self.parent.biblemanager.get_book_verse_count( - bible, book, int(text2)) + verses = self.parent.manager.get_verse_count(bible, book, int(to)) self.adjustComboBox(1, verses, self.AdvancedToVerse) def onAdvancedSearchButton(self): @@ -357,10 +395,13 @@ class BibleMediaItem(MediaManagerItem): chapter_to = int(self.AdvancedToChapter.currentText()) verse_from = int(self.AdvancedFromVerse.currentText()) verse_to = int(self.AdvancedToVerse.currentText()) - self.search_results = self.parent.biblemanager.get_verse_text( - bible, book, chapter_from, chapter_to, verse_from, verse_to) + versetext = u'%s %s:%s-%s:%s' % (book, chapter_from, verse_from, \ + chapter_to, verse_to) + self.search_results = self.parent.manager.get_verses(bible, versetext) if self.ClearAdvancedSearchComboBox.currentIndex() == 0: self.ListView.clear() + self.lastReference = [] + self.lastReference.append(versetext) self.displayResults(bible) def onAdvancedFromChapter(self): @@ -369,7 +410,7 @@ class BibleMediaItem(MediaManagerItem): cf = int(self.AdvancedFromChapter.currentText()) self.adjustComboBox(cf, self.chapters_from, self.AdvancedToChapter) # get the verse count for new chapter - vse = self.parent.biblemanager.get_book_verse_count(bible, book, cf) + vse = self.parent.manager.get_verse_count(bible, book, cf) self.adjustComboBox(1, vse, self.AdvancedFromVerse) self.adjustComboBox(1, vse, self.AdvancedToVerse) @@ -379,11 +420,9 @@ class BibleMediaItem(MediaManagerItem): text = unicode(self.QuickSearchEdit.displayText()) if self.ClearQuickSearchComboBox.currentIndex() == 0: self.ListView.clear() - if self.QuickSearchComboBox.currentIndex() == 1: - self.search_results = self.parent.biblemanager.get_verse_from_text( - bible, text) - else: - self.searchByReference(bible, text) + self.lastReference = [] + self.lastReference.append(text) + self.search_results = self.parent.manager.get_verses(bible, text) if self.search_results: self.displayResults(bible) @@ -396,60 +435,63 @@ class BibleMediaItem(MediaManagerItem): raw_slides = [] raw_footer = [] bible_text = u'' + #If we want to use a 2nd translation / version + bible2 = u'' + if self.SearchTabWidget.currentIndex() == 0: + bible2 = unicode(self.QuickSecondBibleComboBox.currentText()) + else: + bible2 = unicode(self.AdvancedSecondBibleComboBox.currentText()) + if bible2: + bible2_verses = [] + for scripture in self.lastReference: + bible2_verses.extend(self.parent.manager.get_verses(bible2, scripture)) + bible2_version = self.parent.manager.get_meta_data(bible2, u'Version') + bible2_copyright = self.parent.manager.get_meta_data(bible2, u'Copyright') + bible2_permission = self.parent.manager.get_meta_data(bible2, u'Permission') + # Let's loop through the main lot, and assemble our verses for item in items: bitem = self.ListView.item(item.row()) - text = unicode((bitem.data(QtCore.Qt.UserRole)).toString()) - search_verse = text[:text.find(u'(')] - bible = text[text.find(u'(') + 1:-1] - self.searchByReference(bible, search_verse) - book = self.search_results[0].book.name - chapter = unicode(self.search_results[0].chapter) - verse = unicode(self.search_results[0].verse) - text = self.search_results[0].text + reference = bitem.data(QtCore.Qt.UserRole).toPyObject() + bible = unicode(reference[QtCore.QString('bible')]) + book = unicode(reference[QtCore.QString('book')]) + chapter = unicode(reference[QtCore.QString('chapter')]) + verse = unicode(reference[QtCore.QString('verse')]) + text = unicode(reference[QtCore.QString('text')]) + version = unicode(reference[QtCore.QString('version')]) + copyright = unicode(reference[QtCore.QString('copyright')]) + permission = unicode(reference[QtCore.QString('permission')]) if self.parent.settings_tab.display_style == 1: - loc = self.formatVerse(old_chapter, chapter, verse, u'(u', u')') + verse_text = self.formatVerse(old_chapter, chapter, verse, u'(u', u')') elif self.parent.settings_tab.display_style == 2: - loc = self.formatVerse(old_chapter, chapter, verse, u'{', u'}') + verse_text = self.formatVerse(old_chapter, chapter, verse, u'{', u'}') elif self.parent.settings_tab.display_style == 3: - loc = self.formatVerse(old_chapter, chapter, verse, u'[', u']') + verse_text = self.formatVerse(old_chapter, chapter, verse, u'[', u']') else: - loc = self.formatVerse(old_chapter, chapter, verse, u'', u'') + verse_text = self.formatVerse(old_chapter, chapter, verse, u'', u'') old_chapter = chapter - footer = u'%s (%s %s)' % (book, self.version, self.copyright) + footer = u'%s (%s %s)' % (book, version, copyright) #If not found throws and error so add.s - try: - raw_footer.index(footer) - except: + if footer not in raw_footer: raw_footer.append(footer) - #If we want to use a 2nd translation / version - bible2 = u'' - if self.SearchTabWidget.currentIndex() == 0: - bible2 = unicode(self.QuickSecondBibleComboBox.currentText()) - else: - bible2 = unicode(self.AdvancedSecondBibleComboBox.currentText()) - if len(bible2) > 0: - self.searchByReference(bible2, search_verse) - footer = u'%s (%s %s)' % (book, self.version, self.copyright) + if bible2: + footer = u'%s (%s %s)' % (book, version, copyright) #If not found throws and error so add.s - try: - raw_footer.index(footer) - except: + if footer not in raw_footer: raw_footer.append(footer) - bible_text = u'%s %s \n\n\n %s %s)' % \ - (loc, text, loc, self.search_results[0].text) + bible_text = u'%s %s \n\n %s %s' % \ + (verse_text, text, verse_text, bible2_verses[item.row()].text) raw_slides.append(bible_text) bible_text = u'' else: #Paragraph style force new line per verse if self.parent.settings_tab.layout_style == 1: text = text + u'\n\n' - bible_text = u'%s %s %s' % (bible_text, loc, text) + bible_text = u'%s %s %s' % (bible_text, verse_text, text) #if we are verse per slide then create slide if self.parent.settings_tab.layout_style == 0: raw_slides.append(bible_text) bible_text = u'' - service_item.title = u'%s %s' % (book, loc) - + service_item.title = u'%s %s' % (book, verse_text) if len(self.parent.settings_tab.bible_theme) == 0: service_item.theme = None else: @@ -463,40 +505,39 @@ class BibleMediaItem(MediaManagerItem): return True def formatVerse(self, old_chapter, chapter, verse, opening, closing): - loc = opening + verse_text = opening if old_chapter != chapter: - loc += chapter + u':' + verse_text += chapter + u':' elif not self.parent.settings_tab.show_new_chapters: - loc += chapter + u':' - loc += verse - loc += closing - return loc + verse_text += chapter + u':' + verse_text += verse + verse_text += closing + return verse_text def reloadBibles(self): log.debug(u'Reloading Bibles') - self.parent.biblemanager.reload_bibles() + self.parent.manager.reload_bibles() self.loadBibles() def initialiseBible(self, bible): log.debug(u'initialiseBible %s', bible) - book_data = self.parent.biblemanager.get_bible_books() + book_data = self.parent.manager.get_books(bible) self.AdvancedBookComboBox.clear() first = True for book in book_data: row = self.AdvancedBookComboBox.count() - self.AdvancedBookComboBox.addItem(book[u'book']) + self.AdvancedBookComboBox.addItem(book[u'name']) self.AdvancedBookComboBox.setItemData( - row, QtCore.QVariant(book[u'total'])) + row, QtCore.QVariant(book[u'chapters'])) if first: first = False self.initialiseChapterVerse( - bible, book[u'book'], book[u'total']) + bible, book[u'name'], book[u'chapters']) def initialiseChapterVerse(self, bible, book, chapters): log.debug(u'initialiseChapterVerse %s, %s', bible, book) self.chapters_from = chapters - self.verses = self.parent.biblemanager.get_book_verse_count(bible, - book, 1) + self.verses = self.parent.manager.get_verse_count(bible, book, 1) if self.verses == 0: self.AdvancedSearchButton.setEnabled(False) self.AdvancedMessage.setText(self.trUtf8('Bible not fully loaded')) @@ -515,12 +556,30 @@ class BibleMediaItem(MediaManagerItem): combo.addItem(unicode(i)) def displayResults(self, bible): + version = self.parent.manager.get_meta_data(bible, u'Version') + copyright = self.parent.manager.get_meta_data(bible, u'Copyright') + permission = self.parent.manager.get_meta_data(bible, u'Permission') + if not permission: + permission = u'' + else: + permission = permission.value for count, verse in enumerate(self.search_results): - bible_text = u' %s %d:%d (%s)' % (verse.book.name, - verse.chapter, verse.verse, bible) + bible_text = u' %s %d:%d (%s)' % \ + (verse.book.name, verse.chapter, verse.verse, bible) bible_verse = QtGui.QListWidgetItem(bible_text) - bible_verse.setData(QtCore.Qt.UserRole, - QtCore.QVariant(bible_text)) + #bible_verse.setData(QtCore.Qt.UserRole, + # QtCore.QVariant(bible_text)) + vdict = { + 'bible': QtCore.QVariant(bible), + 'version': QtCore.QVariant(version.value), + 'copyright': QtCore.QVariant(copyright.value), + 'permission': QtCore.QVariant(permission), + 'book': QtCore.QVariant(verse.book.name), + 'chapter': QtCore.QVariant(verse.chapter), + 'verse': QtCore.QVariant(verse.verse), + 'text': QtCore.QVariant(verse.text) + } + bible_verse.setData(QtCore.Qt.UserRole, QtCore.QVariant(vdict)) self.ListView.addItem(bible_verse) row = self.ListView.setCurrentRow(count) if row: @@ -528,85 +587,4 @@ class BibleMediaItem(MediaManagerItem): def searchByReference(self, bible, search): log.debug(u'searchByReference %s, %s', bible, search) - book = u'' - start_chapter = u'' - end_chapter = u'' - start_verse = u'' - end_verse = u'' - search = search.replace(u' ', u' ').strip() - #original = search - message = None - # Remove book beware 0 index arrays - for i in range (len(search)-1, 0, - 1): - if search[i] == u' ': - book = search[:i] - # remove book from string - search = search[i:] - break - # allow V or v for verse instead of : - search = search.replace(u'v', ':') - search = search.replace(u'V', ':') - search = search.strip() - colon = search.find(u':') - if colon == -1: - # number : found - i = search.rfind(u' ') - if i == -1: - chapter = u'' - else: - chapter = search[i:len(search)] - hyphen = chapter.find(u'-') - if hyphen != -1: - start_chapter= chapter[:hyphen] - end_chapter= chapter[hyphen + 1:len(chapter)] - else: - start_chapter = chapter - else: - # more complex - sp = search.split(u'-') #find first - sp1 = sp[0].split(u':') - if len(sp1) == 1: - start_chapter = sp1[0] - start_verse = 1 - else: - start_chapter = sp1[0] - start_verse = sp1[1] - if len(sp)== 1: - end_chapter = start_chapter - end_verse = start_verse - else: - sp1 = sp[1].split(u':') - if len(sp1) == 1: - end_chapter = start_chapter - end_verse = sp1[0] - else: - end_chapter = sp1[0] - end_verse = sp1[1] - if end_chapter == u'': - end_chapter = start_chapter.rstrip() - if start_verse == u'': - if end_verse == u'': - start_verse = 1 - else: - start_verse = end_verse - if end_verse == u'': - end_verse = 99 - if start_chapter == u'': - message = self.trUtf8('No chapter found for search criteria') - log.debug(u'results = %s @ %s : %s @ %s : %s'% \ - (unicode(book), unicode(start_chapter), unicode(end_chapter), - unicode(start_verse), unicode(end_verse))) - if message is None: - self.search_results = None - self.search_results = self.parent.biblemanager.get_verse_text( - bible, book, int(start_chapter), int(end_chapter), - int(start_verse), int(end_verse)) - self.copyright = unicode(self.parent.biblemanager.get_meta_data( - bible, u'Copyright').value) - self.permissions = unicode(self.parent.biblemanager.get_meta_data( - bible, u'Permissions').value) - self.version = unicode(self.parent.biblemanager.get_meta_data( - bible, u'Version').value) - else: - QtGui.QMessageBox.information( - self, self.trUtf8('Information'), message) \ No newline at end of file + self.search_results = self.parent.manager.get_verses(bible, search) diff --git a/openlp/plugins/bibles/lib/models.py b/openlp/plugins/bibles/lib/models.py index 931133921..2802cb27f 100644 --- a/openlp/plugins/bibles/lib/models.py +++ b/openlp/plugins/bibles/lib/models.py @@ -50,7 +50,7 @@ class BibleMeta(BaseModel): pass -class ONTestament(BaseModel): +class Testament(BaseModel): """ Bible Testaments """ @@ -101,8 +101,8 @@ verse_table = Table(u'verse', metadata, Column(u'text', types.UnicodeText, index=True), ) mapper(BibleMeta, meta_table) -mapper(ONTestament, testament_table, +mapper(Testament, testament_table, properties={'books': relation(Book, backref='testament')}) mapper(Book, book_table, properties={'verses': relation(Verse, backref='book')}) -mapper(Verse, verse_table) \ No newline at end of file +mapper(Verse, verse_table) diff --git a/openlp/plugins/bibles/lib/bibleOpenSongimpl.py b/openlp/plugins/bibles/lib/opensong.py similarity index 52% rename from openlp/plugins/bibles/lib/bibleOpenSongimpl.py rename to openlp/plugins/bibles/lib/opensong.py index 575d1bf0b..f222a545c 100644 --- a/openlp/plugins/bibles/lib/bibleOpenSongimpl.py +++ b/openlp/plugins/bibles/lib/opensong.py @@ -24,101 +24,86 @@ ############################################################################### import logging -import chardet -import codecs from lxml import objectify from PyQt4 import QtCore from openlp.core.lib import Receiver +from db import BibleDB -class BibleOpenSongImpl(): +log = logging.getLogger(__name__) + +class OpenSongBible(BibleDB): """ - OSIS Bible format importer class. + OpenSong Bible format importer class. """ - global log - log = logging.getLogger(__name__) - log.info(u'BibleOpenSongImpl loaded') - def __init__(self, biblepath, bibledb): + def __init__(self, parent, **kwargs): """ - Constructor to create and set up an instance of the - BibleOpenSongImpl class. - - ``biblepath`` - This does not seem to be used. - - ``bibledb`` - A reference to a Bible database object. + Constructor to create and set up an instance of the OpenSongBible + class. This class is used to import Bibles from OpenSong's XML format. """ - log.info(u'BibleOpenSongImpl Initialising') - self.bibledb = bibledb - self.loadbible = True - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'openlpstopimport'), self.stop_import) + log.debug(__name__) + BibleDB.__init__(self, parent, **kwargs) + if 'filename' not in kwargs: + raise KeyError(u'You have to supply a file name to import from.') + self.filename = kwargs['filename'] + #QtCore.QObject.connect(Receiver.get_receiver(), + # QtCore.SIGNAL(u'openlpstopimport'), self.stop_import) def stop_import(self): """ Stops the import of the Bible. """ - self.loadbible = False + log.debug('Stopping import!') + self.stop_import = True - def load_data(self, bible_file, dialogobject=None): + def do_import(self): """ Loads a Bible from file. - - ``bible_file`` - The file to import from. - - ``dialogobject`` - The Import dialog, so that we can increase the counter on - the progress bar. """ - log.info(u'Load data for %s' % bible_file) - bible_file = unicode(bible_file) - detect_file = None - try: - detect_file = open(bible_file, u'r') - details = chardet.detect(detect_file.read(2048)) - except: - log.exception(u'Failed to detect OpenSong file encoding') - return - finally: - if detect_file: - detect_file.close() - opensong_bible = None + log.debug(u'Starting OpenSong import from "%s"' % self.filename) + self.filename = unicode(self.filename, u'utf-8') + self.wizard.incrementProgressBar(u'Preparing for import...') + file = None success = True try: - opensong_bible = codecs.open(bible_file, u'r', details['encoding']) - opensong = objectify.parse(opensong_bible) + # NOTE: We don't need to do any of the normal encoding detection + # here, because lxml does it's own encoding detection, and the two + # mechanisms together interfere with each other. + file = open(self.filename, u'r') + opensong = objectify.parse(file) bible = opensong.getroot() for book in bible.b: - if not self.loadbible: + if self.stop_import: break - dbbook = self.bibledb.create_book(book.attrib[u'n'], - book.attrib[u'n'][:4]) + db_book = self.create_book(unicode(book.attrib[u'n']), + unicode(book.attrib[u'n'][:4])) for chapter in book.c: - if not self.loadbible: + if self.stop_import: break for verse in chapter.v: - if not self.loadbible: + if self.stop_import: break - self.bibledb.add_verse(dbbook.id, chapter.attrib[u'n'], - verse.attrib[u'n'], verse.text) + self.create_verse( + db_book.id, + int(chapter.attrib[u'n']), + int(verse.attrib[u'n']), + unicode(verse.text) + ) Receiver.send_message(u'process_events') - dialogobject.incrementProgressBar(u'Importing %s %s' % \ - (dbbook.name, str(chapter.attrib[u'n']))) - self.bibledb.save_verses() + self.wizard.incrementProgressBar( + QtCore.QString('%s %s %s' % (self.trUtf8('Importing'),\ + db_book.name, chapter.attrib[u'n']))) + self.commit() except: log.exception(u'Loading bible from OpenSong file failed') success = False finally: - if opensong_bible: - opensong_bible.close() - if not self.loadbible: - dialogobject.incrementProgressBar(u'Import canceled!') - dialogobject.ImportProgressBar.setValue( - dialogobject.ImportProgressBar.maximum()) + if file: + file.close() + if self.stop_import: + self.wizard.incrementProgressBar(u'Import canceled!') return False else: return success diff --git a/openlp/plugins/bibles/lib/bibleOSISimpl.py b/openlp/plugins/bibles/lib/osis.py similarity index 79% rename from openlp/plugins/bibles/lib/bibleOSISimpl.py rename to openlp/plugins/bibles/lib/osis.py index 0f1aafe5d..1e636e8fc 100644 --- a/openlp/plugins/bibles/lib/bibleOSISimpl.py +++ b/openlp/plugins/bibles/lib/osis.py @@ -30,11 +30,10 @@ import chardet import codecs import re -from PyQt4 import QtCore - from openlp.core.lib import Receiver +from db import BibleDB -class BibleOSISImpl(): +class OSISBible(BibleDB): """ OSIS Bible format importer class. """ @@ -42,18 +41,16 @@ class BibleOSISImpl(): log = logging.getLogger(u'BibleOSISImpl') log.info(u'BibleOSISImpl loaded') - def __init__(self, biblepath, bibledb): + def __init__(self, parent, **kwargs): """ - Constructor to create and set up an instance of the - BibleOSISImpl class. - - ``biblepath`` - This does not seem to be used. - - ``bibledb`` - A reference to a Bible database object. + Constructor to create and set up an instance of the OpenSongBible + class. This class is used to import Bibles from OpenSong's XML format. """ - log.info(u'BibleOSISImpl Initialising') + log.debug(__name__) + BibleDB.__init__(self, parent, **kwargs) + if u'filename' not in kwargs: + raise KeyError(u'You have to supply a file name to import from.') + self.filename = kwargs[u'filename'] self.verse_regex = re.compile( r'<verse osisID="([a-zA-Z0-9 ]*).([0-9]*).([0-9]*)">(.*?)</verse>') self.note_regex = re.compile(r'<note(.*?)>(.*?)</note>') @@ -66,13 +63,11 @@ class BibleOSISImpl(): self.w_regex = re.compile(r'<w (.*?)>') self.q_regex = re.compile(r'<q (.*?)>') self.spaces_regex = re.compile(r'([ ]{2,})') - self.bibledb = bibledb self.books = {} filepath = os.path.split(os.path.abspath(__file__))[0] filepath = os.path.abspath(os.path.join( filepath, u'..', u'resources', u'osisbooks.csv')) fbibles = None - self.loadbible = True try: fbibles = open(filepath, u'r') for line in fbibles: @@ -84,31 +79,24 @@ class BibleOSISImpl(): finally: if fbibles: fbibles.close() - QtCore.QObject.connect(Receiver.get_receiver(), - QtCore.SIGNAL(u'openlpstopimport'), self.stop_import) + #QtCore.QObject.connect(Receiver.get_receiver(), + # QtCore.SIGNAL(u'openlpstopimport'), self.stop_import) def stop_import(self): """ Stops the import of the Bible. """ log.debug('Stopping import!') - self.loadbible = False + self.stop_import = True - def load_data(self, osisfile_record, dialogobject=None): + def do_import(self): """ Loads a Bible from file. - - ``osisfile_record`` - The file to import from. - - ``dialogobject`` - The Import dialog, so that we can increase the counter on - the progress bar. """ - log.info(u'Load data for %s' % osisfile_record) + log.debug(u'Starting OSIS import from "%s"' % self.filename) detect_file = None try: - detect_file = open(osisfile_record, u'r') + detect_file = open(self.filename, u'r') details = chardet.detect(detect_file.read(3000)) except: log.exception(u'Failed to detect OSIS file encoding') @@ -119,12 +107,12 @@ class BibleOSISImpl(): osis = None success = True try: - osis = codecs.open(osisfile_record, u'r', details['encoding']) + osis = codecs.open(self.filename, u'r', details['encoding']) last_chapter = 0 testament = 1 db_book = None for file_record in osis: - if not self.loadbible: + if self.stop_import: break match = self.verse_regex.search(file_record) if match: @@ -142,13 +130,13 @@ class BibleOSISImpl(): testament) if last_chapter == 0: if book == u'Gen': - dialogobject.ImportProgressBar.setMaximum(1188) + self.wizard.ImportProgressBar.setMaximum(1188) else: - dialogobject.ImportProgressBar.setMaximum(260) + self.wizard.ImportProgressBar.setMaximum(260) if last_chapter != chapter: if last_chapter != 0: self.bibledb.save_verses() - dialogobject.incrementProgressBar( + self.wizard.incrementProgressBar( u'Importing %s %s...' % \ (self.books[match.group(1)][0], chapter)) last_chapter = chapter @@ -170,20 +158,19 @@ class BibleOSISImpl(): .replace(u'</lg>', u'').replace(u'</q>', u'')\ .replace(u'</div>', u'') verse_text = self.spaces_regex.sub(u' ', verse_text) - self.bibledb.add_verse(db_book.id, chapter, verse, verse_text) + self.create_verse(db_book.id, chapter, verse, verse_text) Receiver.send_message(u'process_events') - self.bibledb.save_verses() - dialogobject.incrementProgressBar(u'Finishing import...') + self.commit() + self.wizard.incrementProgressBar(u'Finishing import...') except: log.exception(u'Loading bible from OSIS file failed') success = False finally: if osis: osis.close() - if not self.loadbible: - dialogobject.incrementProgressBar(u'Import canceled!') - dialogobject.ImportProgressBar.setValue( - dialogobject.ImportProgressBar.maximum()) + if self.stop_import: + self.wizard.incrementProgressBar(u'Import canceled!') return False else: - return success \ No newline at end of file + return success + diff --git a/openlp/plugins/bibles/resources/httpbooks.csv b/openlp/plugins/bibles/resources/httpbooks.csv deleted file mode 100644 index 2d8afa20e..000000000 --- a/openlp/plugins/bibles/resources/httpbooks.csv +++ /dev/null @@ -1,66 +0,0 @@ -Genesis,Gen,1,50 -Exodus,Exod,1,40 -Leviticus,Lev,1,27 -Numbers,Num,1,36 -Deuteronomy,Deut,1,34 -Joshua,Josh,1,24 -Judges,Judg,1,21 -Ruth,Ruth,1,4 -1 Samual,1Sam,1,31 -2 Samual,2Sam,1,24 -1 Kings,1Kgs,1,22 -2 Kings,2Kgs,1,25 -1 Chronicles,1Chr,1,29 -2 Chronicles,2Chr,1,36 -Ezra,Esra,1,10 -Nehemiah,Neh,1,13 -Esther,Esth,1,10 -Job,Job,1,42 -Psalms,Ps,1,150 -Proverbs,Prov,1,31 -Ecclesiastes,Eccl,1,12 -Song of Songs,Song,1,8 -Isaiah,Isa,1,66 -Jeremiah,Jer,1,5 -Lamentations,Lam,1,5 -Ezekiel,Ezek,1,48 -Daniel,Dan,1,12 -Hosea,Hos,1,14 -Joel,Joel,1,3 -Amos,Amos,1,9 -Obad,Obad,1,1 -Jonah,Jonah,1,4 -Micah,Mic,1,7 -Naham,Nah,1,3 -Habakkuk,Hab,1,3 -Zephaniah,Zeph,1,3 -Haggai,Hag,1,2 -Zechariah,Zech,1,3 -Malachi,Mal,1,4 -Matthew,Matt,2,28 -Mark,Mark,2,16 -Luke,Luke,2,24 -John,John,2,21 -Acts,Acts,2,28 -Romans,Rom,2,16 -1 Corinthans,1Cor,2,16 -2 Corinthans,2Cor,2,13 -Galatians,Gal,2,6 -Ephesians,Eph,2,6 -Philippians,Phil,2,4 -Colossians,Col,2,4 -1 Thessalonians,1Thess,2,5 -2 Thessalonians,2Thess,2,3 -1 Timothy,1Tim,2,6 -2 Timothy,2Tim,2,4 -Titus,Titus,2,3 -Philemon,Phlm,2,1 -Hebrews,Heb,2,13 -James,Jas,2,5 -1 Peter,1Pet,2,5 -2 Peter,2Pet,2,3 -1 John,1John,2,5 -2 John,2John,2,1 -3 John,3John,2,1 -Jude,Jude,2,1 -Revelation,Rev,2,22 diff --git a/openlp/plugins/bibles/resources/httpbooks.sqlite b/openlp/plugins/bibles/resources/httpbooks.sqlite new file mode 100644 index 000000000..ea0c40530 Binary files /dev/null and b/openlp/plugins/bibles/resources/httpbooks.sqlite differ diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index 98eb5598a..e701c0938 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -153,10 +153,10 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog): sxml.add_verse_to_lyrics(u'custom', unicode(count), unicode(self.VerseListView.item(i).text())) count += 1 - self.customSlide.title = unicode(self.TitleEdit.displayText()) - self.customSlide.text = unicode(sxml.extract_xml()) - self.customSlide.credits = unicode(self.CreditEdit.displayText()) - self.customSlide.theme_name = unicode(self.ThemeComboBox.currentText()) + self.customSlide.title = unicode(self.TitleEdit.displayText(), u'utf-8') + self.customSlide.text = unicode(sxml.extract_xml(), u'utf-8') + self.customSlide.credits = unicode(self.CreditEdit.displayText(), u'utf-8') + self.customSlide.theme_name = unicode(self.ThemeComboBox.currentText(), u'utf-8') self.custommanager.save_slide(self.customSlide) return True @@ -257,4 +257,4 @@ class EditCustomForm(QtGui.QDialog, Ui_customEditDialog): if len(self.VerseTextEdit.toPlainText()) > 0: self.VerseTextEdit.setFocus() return False, self.trUtf8('You have unsaved data') - return True, u'' \ No newline at end of file + return True, u'' diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 9ee3a0c6f..8ea1df64b 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -172,7 +172,6 @@ class ImageMediaItem(MediaManagerItem): filename = unicode((bitem.data(QtCore.Qt.UserRole)).toString()) self.OverrideLabel.setText(bitem.text()) frame = QtGui.QImage(unicode(filename)) - self.parent.render_manager.override_background = frame - self.parent.render_manager.override_background_changed = True + self.parent.live_controller.parent.mainDisplay.addImageWithText(frame) else: - MediaManagerItem.onPreviewClick(self) \ No newline at end of file + MediaManagerItem.onPreviewClick(self) diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index dc0a3bf82..b58a9affc 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -62,10 +62,12 @@ class ImpressController(PresentationController): """ log.debug(u'Initialising') PresentationController.__init__(self, plugin, u'Impress') + self.supports= [u'.odp', u'.ppt', u'.pps', u'.pptx', u'.ppsx'] self.process = None self.document = None self.presentation = None self.controller = None + self.desktop = None def check_available(self): """ @@ -85,7 +87,7 @@ class ImpressController(PresentationController): It is not displayed to the user but is available to the UNO interface when required. """ - log.debug(u'start Openoffice') + log.debug(u'start process Openoffice') if os.name == u'nt': self.manager = self.get_com_servicemanager() self.manager._FlagAsMethod(u'Bridge_GetStruct') @@ -101,7 +103,7 @@ class ImpressController(PresentationController): """ Called at system exit to clean up any running presentations """ - log.debug(u'Kill') + log.debug(u'Kill OpenOffice') self.close_presentation() if os.name != u'nt': desktop = self.get_uno_desktop() @@ -121,8 +123,9 @@ class ImpressController(PresentationController): ``presentation`` The file name of the presentatios to the run. """ - log.debug(u'LoadPresentation') + log.debug(u'Load Presentation OpenOffice') self.store_filename(presentation) + #print "s.dsk1 ", self.desktop if os.name == u'nt': desktop = self.get_com_desktop() if desktop is None: @@ -135,6 +138,7 @@ class ImpressController(PresentationController): if desktop is None: return self.desktop = desktop + #print "s.dsk2 ", self.desktop properties = [] properties.append(self.create_property(u'Minimized', True)) properties = tuple(properties) @@ -153,9 +157,9 @@ class ImpressController(PresentationController): """ Create thumbnail images for presentation """ + log.debug(u'create thumbnails OpenOffice') if self.check_thumbnails(): return - if os.name == u'nt': thumbdir = u'file:///' + self.thumbnailpath.replace( u'\\', u'/').replace(u':', u'|').replace(u' ', u'%20') @@ -170,13 +174,14 @@ class ImpressController(PresentationController): page = pages.getByIndex(idx) doc.getCurrentController().setCurrentPage(page) path = u'%s/%s%s.png'% (thumbdir, self.thumbnailprefix, - unicode(idx+1)) + unicode(idx + 1)) try: doc.storeToURL(path , props) except: log.exception(u'%s\nUnable to store preview' % path) def create_property(self, name, value): + log.debug(u'create property OpenOffice') if os.name == u'nt': prop = self.manager.Bridge_GetStruct(u'com.sun.star.beans.PropertyValue') else: @@ -186,7 +191,7 @@ class ImpressController(PresentationController): return prop def get_uno_desktop(self): - log.debug(u'getUNODesktop') + log.debug(u'get UNO Desktop Openoffice') ctx = None loop = 0 context = uno.getComponentContext() @@ -196,6 +201,7 @@ class ImpressController(PresentationController): try: ctx = resolver.resolve(u'uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext') except: + log.exception(u'Unable to fine running instance ') self.start_process() loop += 1 try: @@ -208,7 +214,7 @@ class ImpressController(PresentationController): return None def get_com_desktop(self): - log.debug(u'getCOMDesktop') + log.debug(u'get COM Desktop OpenOffice') try: desktop = self.manager.createInstance(u'com.sun.star.frame.Desktop') return desktop @@ -217,7 +223,7 @@ class ImpressController(PresentationController): return None def get_com_servicemanager(self): - log.debug(u'get_com_servicemanager') + log.debug(u'get_com_servicemanager openoffice') try: return Dispatch(u'com.sun.star.ServiceManager') except: @@ -230,6 +236,7 @@ class ImpressController(PresentationController): Triggerent by new object being added to SlideController orOpenLP being shut down """ + log.debug(u'close Presentation OpenOffice') if self.document: if self.presentation: try: @@ -242,32 +249,44 @@ class ImpressController(PresentationController): self.document = None def is_loaded(self): + log.debug(u'is loaded OpenOffice') + #print "is_loaded " if self.presentation is None or self.document is None: + #print "no present or document" return False try: if self.document.getPresentation() is None: + #print "no getPresentation" return False except: return False return True def is_active(self): + log.debug(u'is active OpenOffice') + #print "is_active " if not self.is_loaded(): + #print "False " return False + #print "self.con ", self.controller if self.controller is None: return False - return self.controller.isRunning() and self.controller.isActive() + return True def unblank_screen(self): + log.debug(u'unblank screen OpenOffice') return self.controller.resume() def blank_screen(self): + log.debug(u'blank screen OpenOffice') self.controller.blankScreen(0) def stop_presentation(self): + log.debug(u'stop presentation OpenOffice') self.controller.deactivate() def start_presentation(self): + log.debug(u'start presentation OpenOffice') if self.controller is None or not self.controller.isRunning(): self.presentation.start() # start() returns before the getCurrentComponent is ready. Try for 5 seconds diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index c1394c547..0b14f797d 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -63,7 +63,13 @@ class PresentationMediaItem(MediaManagerItem): def retranslateUi(self): self.OnNewPrompt = self.trUtf8('Select Presentation(s)') - self.OnNewFileMasks = self.trUtf8('Presentations (*.ppt *.pps *.odp)') + fileType = u'' + for controller in self.controllers: + if self.controllers[controller].enabled: + for type in self.controllers[controller].supports: + if fileType.find(type) == -1: + fileType += type + u' ' + self.OnNewFileMasks = self.trUtf8('Presentations (%s)' % fileType) def requiredIcons(self): MediaManagerItem.requiredIcons(self) @@ -151,4 +157,4 @@ class PresentationMediaItem(MediaManagerItem): service_item.add_from_command(path, name, img) i = i + 1 img = controller.get_slide_preview_file(i) - return True \ No newline at end of file + return True diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index d64314c76..18b644112 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -52,6 +52,7 @@ class PowerpointController(PresentationController): """ log.debug(u'Initialising') PresentationController.__init__(self, plugin, u'Powerpoint') + self.supports= [u'.ppt', u'.pps'] self.process = None self.presentation = None @@ -255,4 +256,4 @@ class PowerpointController(PresentationController): if os.path.isfile(path): return path else: - return None \ No newline at end of file + return None diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 02b280540..0cf2405f1 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -49,6 +49,7 @@ class PptviewController(PresentationController): log.debug(u'Initialising') self.process = None PresentationController.__init__(self, plugin, u'Powerpoint Viewer') + self.supports= [u'.ppt', u'.pps'] self.pptid = None def check_available(self): diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 82c014b64..db42a482d 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -118,7 +118,7 @@ class PresentationController(object): """ global log log = logging.getLogger(u'PresentationController') - log.info(u'loaded') + log.info(u'PresentationController loaded') def __init__(self, plugin=None, name=u'PresentationController'): """ @@ -136,6 +136,7 @@ class PresentationController(object): ``name`` Name of the application, to appear in the application """ + self.supports = [] self.plugin = plugin self.name = name self.available = self.check_available() @@ -313,4 +314,4 @@ class PresentationController(object): else: prefix = u'preview' Receiver.send_message(u'%s_slidecontroller_change' % prefix, - self.slidenumber - 1) \ No newline at end of file + self.slidenumber - 1) diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index a380e9bb0..502557508 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -26,7 +26,7 @@ import os import logging -from openlp.core.lib import Plugin, build_icon +from openlp.core.lib import Plugin, build_icon, Receiver from openlp.plugins.presentations.lib import * class PresentationPlugin(Plugin): @@ -51,6 +51,12 @@ class PresentationPlugin(Plugin): log.info(u'Presentations Initialising') Plugin.initialise(self) self.insert_toolbox_item() + presentation_types = [] + for controller in self.controllers: + if self.controllers[controller].enabled: + presentation_types.append({u'%s' % controller : self.controllers[controller].supports}) + Receiver.send_message( + u'presentation types', presentation_types) def finalise(self): log.info(u'Plugin Finalise') @@ -106,4 +112,4 @@ class PresentationPlugin(Plugin): 'the ability to show presentations using a number of different ' 'programs. The choice of available presentation programs is ' 'available to the user in a drop down box.') - return about_text \ No newline at end of file + return about_text diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 9aef321e5..f5fe0686b 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -185,8 +185,13 @@ class SongMediaItem(MediaManagerItem): if author_list != u'': author_list = author_list + u', ' author_list = author_list + author.display_name - song_detail = unicode(self.trUtf8('%s (%s)' % \ - (unicode(song.title), unicode(author_list)))) + if not isinstance(author_list, unicode): + author_list = unicode(author_list, u'utf8') + if isinstance(song.title, unicode): + song_title = song.title + else: + song_title = unicode(song.title, u'utf8') + song_detail = u'%s (%s)' % (song_title, author_list) song_name = QtGui.QListWidgetItem(song_detail) song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song.id)) self.ListView.addItem(song_name) @@ -339,4 +344,4 @@ class SongMediaItem(MediaManagerItem): service_item.audit = [ song.title, author_audit, song.copyright, song.ccli_number ] - return True \ No newline at end of file + return True diff --git a/openlp/plugins/songusage/forms/songusagedeleteform.py b/openlp/plugins/songusage/forms/songusagedeleteform.py index b20f13c6b..98faf26ad 100644 --- a/openlp/plugins/songusage/forms/songusagedeleteform.py +++ b/openlp/plugins/songusage/forms/songusagedeleteform.py @@ -33,11 +33,11 @@ class SongUsageDeleteForm(QtGui.QDialog, Ui_SongUsageDeleteDialog): """ Class documentation goes here. """ - def __init__(self, auditmanager, parent=None): + def __init__(self, songusagemanager, parent=None): """ Constructor """ - self.auditmanager = auditmanager + self.songusagemanager = songusagemanager QtGui.QDialog.__init__(self, parent) self.setupUi(self) @@ -52,5 +52,5 @@ class SongUsageDeleteForm(QtGui.QDialog, Ui_SongUsageDeleteDialog): if ret == QtGui.QMessageBox.Ok: qDeleteDate = self.DeleteCalendar.selectedDate() deleteDate = date(qDeleteDate.year(), qDeleteDate.month(), qDeleteDate.day()) - self.auditmanager.delete_to_date(deleteDate) - self.close() \ No newline at end of file + self.songusagemanager.delete_to_date(deleteDate) + self.close() diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py index 93b6d2e98..ead6b5166 100644 --- a/openlp/plugins/songusage/forms/songusagedetailform.py +++ b/openlp/plugins/songusage/forms/songusagedetailform.py @@ -25,10 +25,14 @@ import os from PyQt4 import QtCore, QtGui +import logging from songusagedetaildialog import Ui_SongUsageDetailDialog class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog): + global log + log = logging.getLogger(u'SongUsageDetailForm') + log.info(u'SongUsage Detail Form loaded') """ Class documentation goes here. """ @@ -106,19 +110,19 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog): self.close() def detailedReport(self): - print "detailed" - filename = u'audit_det_%s_%s.txt' % \ + log.debug(u'Detailed report generated') + filename = u'usage_detail_%s_%s.txt' % \ (self.FromDateEdit.date().toString(u'ddMMyyyy'), self.ToDateEdit.date().toString(u'ddMMyyyy')) - audits = self.parent.auditmanager.get_all_audits() + usage = self.parent.songusagemanager.get_all_songusage() outname = os.path.join(unicode(self.FileLineEdit.text()), filename) file = None try: file = open(outname, u'w') - for audit in audits: + for instance in usage: record = u'\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\n' % \ - (audit.auditdate,audit.audittime, audit.title, - audit.copyright, audit.ccl_number , audit.authors) + (instance.usagedate,instance.usagetime, instance.title, + instance.copyright, instance.ccl_number , instance.authors) file.write(record) except: log.exception(u'Failed to write out audit records') @@ -127,8 +131,7 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog): file.close() def summaryReport(self): - print "summary" + log.debug(u'Summary report generated') filename = u'audit_sum_%s_%s.txt' % \ (self.FromDateEdit.date().toString(u'ddMMyyyy'), self.ToDateEdit.date().toString(u'ddMMyyyy')) - print filename \ No newline at end of file diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py index effc06657..802f73d3d 100644 --- a/openlp/plugins/songusage/songusageplugin.py +++ b/openlp/plugins/songusage/songusageplugin.py @@ -141,7 +141,7 @@ class SongUsagePlugin(Plugin): SongUsageitem.authors = u'' for author in SongUsageData[1]: SongUsageitem.authors += author + u' ' - self.songusagemanager.insert_SongUsage(SongUsageitem) + self.songusagemanager.insert_songusage(SongUsageitem) def onSongUsageDelete(self): self.SongUsagedeleteform.exec_() @@ -154,4 +154,4 @@ class SongUsagePlugin(Plugin): about_text = self.trUtf8('<b>SongUsage Plugin</b><br>This plugin ' 'records the use of songs and when they have been used during ' 'a live service') - return about_text \ No newline at end of file + return about_text diff --git a/scripts/bible-1to2-converter.py b/scripts/bible-1to2-converter.py new file mode 100755 index 000000000..b1e9b6897 --- /dev/null +++ b/scripts/bible-1to2-converter.py @@ -0,0 +1,306 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2010 Raoul Snyman # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Maikel Stuivenberg, Martin Thompson, Jon Tibble, # +# Carsten Tinggaard # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### + +import sys +import os +import sqlite +import sqlite3 + +from optparse import OptionParser +from traceback import format_tb as get_traceback + +# Some global options to be used throughout the import process +verbose = False +debug = False +old_cursor = None +new_cursor = None + +# SQL create statments +create_statements = [ + (u'table "book"', u"""CREATE TABLE book ( + id INTEGER NOT NULL, + testament_id INTEGER, + name VARCHAR(30), + abbreviation VARCHAR(5), + PRIMARY KEY (id), + FOREIGN KEY(testament_id) REFERENCES testament (id) +)"""), + (u'table "metadata"', u"""CREATE TABLE metadata ( + "key" VARCHAR(255) NOT NULL, + value VARCHAR(255), + PRIMARY KEY ("key") +)"""), + (u'table "testament"', u"""CREATE TABLE testament ( + id INTEGER NOT NULL, + name VARCHAR(30), + PRIMARY KEY (id) +)"""), + (u'table "verse"', u"""CREATE TABLE verse ( + id INTEGER NOT NULL, + book_id INTEGER, + chapter INTEGER, + verse INTEGER, + text TEXT, + PRIMARY KEY (id), + FOREIGN KEY(book_id) REFERENCES book (id) +)"""), + (u'index "idx_abbrev"', + u"""CREATE INDEX idx_abbrev ON book (abbreviation, id)"""), + (u'index "idx_chapter_verse_book', + u"""CREATE INDEX idx_chapter_verse_book ON verse (chapter, verse, book_id, id)"""), + (u'index "idx_chapter_verse_text"', + u"""CREATE INDEX idx_chapter_verse_text ON verse (text, verse, book_id, id)"""), + (u'index "idx_name"', + u"""CREATE INDEX idx_name ON book (name, id)""") +] + +def display_sql(sql, params): + prepared_params = [] + for param in params: + if isinstance(param, basestring): + prepared_params.append(u'"%s"' % param) + elif isinstance(param, (int, long)): + prepared_params.append(u'%d' % param) + elif isinstance(param, (float, complex)): + prepared_params.append(u'%f' % param) + else: + prepared_params.append(u'"%s"' % str(param)) + for prepared_param in prepared_params: + sql = sql.replace(u'?', prepared_param, 1) + return sql + +def create_database(): + global new_cursor, create_statements + if debug or verbose: + print 'Creating new database:' + else: + print 'Creating new database...', + for statement_type, sql_create in create_statements: + if debug: + print '... ', sql_create.replace('\n', ' ').replace(' ', ' ') + elif verbose: + print '... creating %s...' % statement_type, + new_cursor.execute(sql_create) + if verbose and not debug: + print 'done.' + if not verbose and not debug: + print 'done.' + +def import_bible(): + global old_cursor, new_cursor, debug, verbose + if debug or verbose: + print 'Importing metadata:' + else: + print 'Importing metadata...', + if debug: + print '... SELECT "key", "value" FROM metadata' + elif verbose: + print '... fetching metadata from old database...', + old_cursor.execute(u'SELECT "key", "value" FROM metadata') + rows = old_cursor.fetchall() + if not debug and verbose: + print 'done.' + for row in rows: + key = unicode(row[0], u'cp1252') + value = unicode(row[1], u'cp1252') + sql_insert = u'INSERT INTO metadata '\ + '("key", "value") '\ + 'VALUES (?, ?)' + sql_params = (key, value) + if debug: + print '...', display_sql(sql_insert, sql_params) + elif verbose: + print '... importing "%s"' % key + new_cursor.execute(sql_insert, sql_params) + if not verbose and not debug: + print 'done.' + if debug or verbose: + print 'Importing testaments:' + else: + print 'Importing testaments...', + if debug: + print '... SELECT id, name FROM testament' + elif verbose: + print '... fetching testaments from old database...', + old_cursor.execute(u'SELECT id, name FROM testament') + rows = old_cursor.fetchall() + if not debug and verbose: + print 'done.' + for row in rows: + id = int(row[0]) + name = unicode(row[1], u'cp1252') + sql_insert = u'INSERT INTO testament '\ + '(id, name) '\ + 'VALUES (?, ?)' + sql_params = (id, name) + if debug: + print '...', display_sql(sql_insert, sql_params) + elif verbose: + print '... importing "%s"' % name + new_cursor.execute(sql_insert, sql_params) + if not verbose and not debug: + print 'done.' + if debug or verbose: + print 'Importing books:' + else: + print 'Importing books...', + if debug: + print '... SELECT id, testament_id, name, abbreviation FROM book' + elif verbose: + print '... fetching books from old database...', + old_cursor.execute(u'SELECT id, testament_id, name, abbreviation FROM book') + rows = old_cursor.fetchall() + if not debug and verbose: + print 'done.' + book_map = {} + for row in rows: + testament_id = int(row[1]) + name = unicode(row[2], u'cp1252') + abbreviation = unicode(row[3], u'cp1252') + sql_insert = u'INSERT INTO book '\ + '(id, testament_id, name, abbreviation) '\ + 'VALUES (NULL, ?, ?, ?)' + sql_params = (testament_id, name, abbreviation) + if debug: + print '...', display_sql(sql_insert, sql_params) + elif verbose: + print '... importing "%s"' % name + new_cursor.execute(sql_insert, sql_params) + book_map[row[0]] = new_cursor.lastrowid + if debug: + print ' >>> (old) books.id =', row[0], ' (new) books.id =', book_map[row[0]] + if not verbose and not debug: + print 'done.' + if debug or verbose: + print 'Importing verses:' + else: + print 'Importing verses...', + if debug: + print '... SELECT id, book_id, chapter, verse, text || \'\' AS text FROM verse...', + elif verbose: + print '... fetching verses from old database...', + old_cursor.execute(u'SELECT id, book_id, chapter, verse, text || \'\' AS text FROM verse') + rows = old_cursor.fetchall() + if debug or verbose: + print 'done.' + song_map = {} + for row in rows: + book_id = int(row[1]) + chapter = int(row[2]) + verse = int(row[3]) + text = unicode(row[4], u'cp1252') + sql_insert = u'INSERT INTO verse '\ + '(id, book_id, chapter, verse, text) '\ + 'VALUES (NULL, ?, ?, ?, ?)' + sql_params = (book_map[book_id], chapter, verse, text) + if debug: + print '...', display_sql(sql_insert, sql_params) + elif verbose: + print '... importing "%s..."' % text[:17] + new_cursor.execute(sql_insert, sql_params) + if not verbose and not debug: + print 'done.' + +def main(old_db, new_db): + global old_cursor, new_cursor, debug + old_connection = None + new_connection = None + try: + old_connection = sqlite.connect(old_db) + except: + if debug: + errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ + + str(sys.exc_info()[1]) + else: + errormsg = sys.exc_info()[1] + print 'There was a problem connecting to the old database:', errormsg + return 1 + try: + new_connection = sqlite3.connect(new_db) + except: + if debug: + errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ + + str(sys.exc_info()[1]) + else: + errormsg = sys.exc_info()[1] + print 'There was a problem creating the new database:', errormsg + return 1 + old_cursor = old_connection.cursor() + new_cursor = new_connection.cursor() + try: + create_database() + except: + if debug: + errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ + + str(sys.exc_info()[1]) + else: + errormsg = sys.exc_info()[1] + print 'There was a problem creating the database:', errormsg + return 1 + try: + import_bible() + new_connection.commit() + except: + new_connection.rollback() + if debug: + errormsg = '\n' + ''.join(get_traceback(sys.exc_info()[2]))\ + + str(sys.exc_info()[1]) + else: + errormsg = sys.exc_info()[1] + print 'There was a problem importing songs:', errormsg + return 1 + print 'Import complete.' + +if __name__ == u'__main__': + option_parser = OptionParser(usage='Usage: %prog [options] OLDDATABASE NEWDATABASE') + option_parser.add_option('-o', '--overwrite', dest='overwrite', default=False, + action=u'store_true', help='Overwrite database file if it already exists.') + option_parser.add_option('-v', '--verbose', dest='verbose', default=False, + action=u'store_true', help='Outputs additional progress data.') + option_parser.add_option('-d', '--debug', dest='debug', default=False, + action=u'store_true', help='Outputs raw SQL statements (overrides verbose).') + options, arguments = option_parser.parse_args() + if len(arguments) < 2: + if len(arguments) == 0: + option_parser.error('Please specify an old database and a new database.') + else: + option_parser.error('Please specify a new database.') + old_db = os.path.abspath(arguments[0]) + new_db = os.path.abspath(arguments[1]) + if not os.path.isfile(old_db): + option_parser.error('Old database file ("%s") is not a file.' % old_db) + if not os.path.exists(old_db): + option_parser.error('Old database file ("%s") does not exist.' % old_db) + if os.path.exists(new_db): + if not options.overwrite: + option_parser.error('New database file ("%s") exists. If you want to overwrite it, specify the --overwrite option.' % new_db) + else: + if not os.path.isfile(new_db): + option_parser.error('New database file ("%s") is not a file.' % new_db) + os.unlink(new_db) + verbose = options.verbose + debug = options.debug + main(old_db, new_db) diff --git a/version.txt b/version.txt index fcd5e288e..d20160fa2 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.9.0-696 +1.9.0-700