openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #03677
[Merge] lp:~whydoubt/openlp/easyworship into lp:openlp
Jeffrey Smith has proposed merging lp:~whydoubt/openlp/easyworship into lp:openlp.
Requested reviews:
OpenLP Core (openlp-core)
Adds an importer for EasyWorship song databases.
Adds an entry in the song import wizard for EasyWorship.
--
https://code.launchpad.net/~whydoubt/openlp/easyworship/+merge/36045
Your team OpenLP Core is requested to review the proposed merge of lp:~whydoubt/openlp/easyworship into lp:openlp.
=== modified file 'openlp/plugins/songs/forms/songimportform.py'
--- openlp/plugins/songs/forms/songimportform.py 2010-09-13 21:09:13 +0000
+++ openlp/plugins/songs/forms/songimportform.py 2010-09-20 18:28:43 +0000
@@ -109,6 +109,9 @@
QtCore.QObject.connect(self.genericRemoveButton,
QtCore.SIGNAL(u'clicked()'),
self.onGenericRemoveButtonClicked)
+ QtCore.QObject.connect(self.ewBrowseButton,
+ QtCore.SIGNAL(u'clicked()'),
+ self.onEWBrowseButtonClicked)
QtCore.QObject.connect(self.cancelButton,
QtCore.SIGNAL(u'clicked(bool)'),
self.onCancelButtonClicked)
@@ -214,6 +217,16 @@
'presentation file to import from.'))
self.genericAddButton.setFocus()
return False
+ elif source_format == SongFormat.EasyWorship:
+ if self.ewFilenameEdit.text().isEmpty():
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.ImportWizardForm',
+ 'No EasyWorship Song Database Selected'),
+ translate('SongsPlugin.ImportWizardForm',
+ 'You need to select an EasyWorship song database '
+ 'file to import from.'))
+ self.ewBrowseButton.setFocus()
+ return False
return True
elif self.currentId() == 2:
# Progress page
@@ -322,6 +335,13 @@
def onGenericRemoveButtonClicked(self):
self.removeSelectedItems(self.genericFileListWidget)
+ def onEWBrowseButtonClicked(self):
+ self.getFileName(
+ translate('SongsPlugin.ImportWizardForm',
+ 'Select EasyWorship Database File'),
+ self.ewFilenameEdit
+ )
+
def onCancelButtonClicked(self, checked):
"""
Stop the import on pressing the cancel button.
@@ -350,6 +370,7 @@
self.ccliFileListWidget.clear()
self.songsOfFellowshipFileListWidget.clear()
self.genericFileListWidget.clear()
+ self.ewFilenameEdit.setText(u'')
#self.csvFilenameEdit.setText(u'')
def incrementProgressBar(self, status_text, increment=1):
@@ -420,6 +441,11 @@
importer = self.plugin.importSongs(SongFormat.Generic,
filenames=self.getListOfFiles(self.genericFileListWidget)
)
+ elif source_format == SongFormat.EasyWorship:
+ # Import an OpenLP 2.0 database
+ importer = self.plugin.importSongs(SongFormat.EasyWorship,
+ filename=unicode(self.ewFilenameEdit.text())
+ )
success = importer.do_import()
if success:
# reload songs
=== modified file 'openlp/plugins/songs/forms/songimportwizard.py'
--- openlp/plugins/songs/forms/songimportwizard.py 2010-09-17 20:06:41 +0000
+++ openlp/plugins/songs/forms/songimportwizard.py 2010-09-20 18:28:43 +0000
@@ -96,6 +96,7 @@
self.formatComboBox.addItem(u'')
self.formatComboBox.addItem(u'')
self.formatComboBox.addItem(u'')
+ self.formatComboBox.addItem(u'')
# self.formatComboBox.addItem(u'')
self.formatLayout.addWidget(self.formatComboBox)
self.formatSpacer = QtGui.QSpacerItem(40, 20,
@@ -413,6 +414,30 @@
self.genericImportLayout.addLayout(self.genericButtonLayout)
self.genericLayout.addWidget(self.genericImportWidget)
self.formatStackedWidget.addWidget(self.genericPage)
+ # EasyWorship
+ self.ewPage = QtGui.QWidget()
+ self.ewPage.setObjectName(u'ewPage')
+ self.ewLayout = QtGui.QFormLayout(self.ewPage)
+ self.ewLayout.setMargin(0)
+ self.ewLayout.setSpacing(8)
+ self.ewLayout.setObjectName(u'ewLayout')
+ self.ewFilenameLabel = QtGui.QLabel(self.ewPage)
+ self.ewFilenameLabel.setObjectName(u'ewFilenameLabel')
+ self.ewLayout.setWidget(0, QtGui.QFormLayout.LabelRole,
+ self.ewFilenameLabel)
+ self.ewFileLayout = QtGui.QHBoxLayout()
+ self.ewFileLayout.setSpacing(8)
+ self.ewFileLayout.setObjectName(u'ewFileLayout')
+ self.ewFilenameEdit = QtGui.QLineEdit(self.ewPage)
+ self.ewFilenameEdit.setObjectName(u'ewFilenameEdit')
+ self.ewFileLayout.addWidget(self.ewFilenameEdit)
+ self.ewBrowseButton = QtGui.QToolButton(self.ewPage)
+ self.ewBrowseButton.setIcon(openIcon)
+ self.ewBrowseButton.setObjectName(u'ewBrowseButton')
+ self.ewFileLayout.addWidget(self.ewBrowseButton)
+ self.ewLayout.setLayout(0, QtGui.QFormLayout.FieldRole,
+ self.ewFileLayout)
+ self.formatStackedWidget.addWidget(self.ewPage)
# Commented out for future use.
# self.csvPage = QtGui.QWidget()
# self.csvPage.setObjectName(u'CSVPage')
@@ -497,7 +522,9 @@
self.formatComboBox.setItemText(7,
translate('SongsPlugin.ImportWizardForm',
'Generic Document/Presentation'))
-# self.formatComboBox.setItemText(8,
+ self.formatComboBox.setItemText(8,
+ translate('SongsPlugin.ImportWizardForm', 'EasyWorship'))
+# self.formatComboBox.setItemText(9,
# translate('SongsPlugin.ImportWizardForm', 'CSV'))
self.openLP2FilenameLabel.setText(
translate('SongsPlugin.ImportWizardForm', 'Filename:'))
@@ -549,6 +576,10 @@
translate('SongsPlugin.ImportWizardForm', 'The generic document/'
'presentation importer has been disabled because OpenLP cannot '
'find OpenOffice.org on your computer.'))
+ self.ewFilenameLabel.setText(
+ translate('SongsPlugin.ImportWizardForm', 'Filename:'))
+ self.ewBrowseButton.setText(
+ translate('SongsPlugin.ImportWizardForm', 'Browse...'))
# self.csvFilenameLabel.setText(
# translate('SongsPlugin.ImportWizardForm', 'Filename:'))
# self.csvBrowseButton.setText(
=== added file 'openlp/plugins/songs/lib/ewimport.py'
--- openlp/plugins/songs/lib/ewimport.py 1970-01-01 00:00:00 +0000
+++ openlp/plugins/songs/lib/ewimport.py 2010-09-20 18:28:43 +0000
@@ -0,0 +1,249 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+"""
+The :mod:`ewimport` module provides the functionality for importing
+EasyWorship song databases into the current installation database.
+"""
+
+import sys
+import os
+import struct
+
+from songimport import SongImport
+
+def strip_rtf(blob):
+ depth = 0
+ control = False
+ clear_text = []
+ control_word = []
+ for c in blob:
+ if control:
+ # for delimiters, set control to False
+ if c == '{':
+ depth += 1
+ control = False
+ elif c == '}':
+ depth -= 1
+ control = False
+ elif c == '\\':
+ control = False
+ elif c.isspace():
+ control = False
+ else:
+ control_word.append(c)
+ if len(control_word) == 3 and control_word[0] == '\'':
+ control = False
+
+ if not control:
+ control_word_str = ''.join(control_word)
+ if control_word_str == 'par':
+ clear_text.append(u'\n')
+ # Really should take RTF character set into account
+ # but for now assume ANSI (Windows-1252) and call it good
+ if control_word_str[0] == '\'':
+ s = chr(int(control_word_str[1:3], 16))
+ clear_text.append(s.decode(u'windows-1252'))
+ del control_word[:]
+
+ if c == '\\':
+ control = True
+
+ elif c == '{':
+ depth += 1
+ elif c == '}':
+ depth -= 1
+ elif depth > 2:
+ continue
+
+ elif c == '\n' or c == '\r':
+ continue
+
+ elif c == '\\':
+ control = True
+ else:
+ clear_text.append(c)
+ return u''.join(clear_text)
+
+class FieldDescEntry:
+ def __init__(self, name, type, size):
+ self.name = name
+ self.type = type
+ self.size = size
+
+class EasyWorshipSongImport(SongImport):
+ """
+ The :class:`EasyWorshipSongImport` class provides OpenLP with the
+ ability to import EasyWorship song files.
+ """
+ def __init__(self, manager, **kwargs):
+ self.import_source = kwargs[u'filename']
+ SongImport.__init__(self, manager)
+
+ def do_import(self):
+ # Open the DB and MB files if they exist
+ import_source_mb = self.import_source.replace('.DB', '.MB')
+ if not os.path.isfile(self.import_source):
+ return False
+ if not os.path.isfile(import_source_mb):
+ return False
+ db_size = os.path.getsize(self.import_source)
+ if db_size < 0x800:
+ return False
+ db_file = open(self.import_source, 'rb')
+ self.memo_file = open(import_source_mb, 'rb')
+
+ # Don't accept files that are clearly not paradox files
+ record_size, header_size, block_size, next_block, num_fields \
+ = struct.unpack('<hhxb4xh21xh', db_file.read(35))
+ if header_size != 0x800 or block_size < 1 or block_size > 4:
+ db_file.close()
+ self.memo_file.close()
+ return False
+
+ # There does not appear to be a _reliable_ way of getting the number
+ # of songs/records, so let's use file blocks for measuring progress.
+ total_blocks = (db_size - header_size) / (block_size * 1024)
+ self.import_wizard.importProgressBar.setMaximum(total_blocks)
+
+ # Read the field description information
+ db_file.seek(120)
+ field_info = db_file.read(num_fields * 2)
+ db_file.seek(4 + (num_fields * 4) + 261, os.SEEK_CUR)
+ field_names = db_file.read(header_size - db_file.tell()).split('\0',
+ num_fields)
+ field_names.pop()
+ field_descs = []
+ for i,field_name in enumerate(field_names):
+ field_type, field_size = struct.unpack_from('BB', field_info, i * 2)
+ field_descs.append(FieldDescEntry(field_name, field_type,
+ field_size))
+ self.set_record_struct(field_descs)
+
+ # Pick out the field description indexes we will need
+ success = True
+ try:
+ fi_title = self.find_field(u'Title')
+ fi_author = self.find_field(u'Author')
+ fi_copy = self.find_field(u'Copyright')
+ fi_admin = self.find_field(u'Administrator')
+ fi_words = self.find_field(u'Words')
+ fi_ccli = self.find_field(u'Song Number')
+ except IndexError:
+ # This is the wrong table
+ success = False
+
+ while next_block != 0 and success:
+ db_file.seek(header_size + ((next_block - 1) * 1024 * block_size))
+ next_block, rec_count = struct.unpack('<h2xh', db_file.read(6))
+ rec_count = (rec_count + record_size) / record_size
+ for i in range(rec_count):
+ if self.stop_import_flag:
+ success = False
+ break
+ raw_record = db_file.read(record_size)
+ self.fields = self.record_struct.unpack(raw_record)
+
+ self.set_defaults()
+ self.title = self.get_field(fi_title)
+ self.import_wizard.incrementProgressBar(
+ u'Importing "%s"...' % self.title, 0)
+ self.copyright = self.get_field(fi_copy) + \
+ u', Administered by ' + self.get_field(fi_admin)
+ self.ccli_number = self.get_field(fi_ccli)
+
+ # Format the lyrics
+ if self.stop_import_flag:
+ success = False
+ break
+ words = self.get_field(fi_words)
+ words = strip_rtf(words)
+ for verse in words.split(u'\n\n'):
+ self.add_verse(verse.strip(), u'V')
+
+ # Split up the authors
+ authors = self.get_field(fi_author)
+ author_list = authors.split(u'/')
+ if len(author_list) < 2:
+ author_list = authors.split(u',')
+ for author_name in author_list:
+ self.add_author(author_name.strip())
+
+ if self.stop_import_flag:
+ success = False
+ break
+ self.finish()
+ if not self.stop_import_flag:
+ self.import_wizard.incrementProgressBar(u'')
+
+ db_file.close()
+ self.memo_file.close()
+
+ return success
+
+ def find_field(self, field_name):
+ return [i for i,x in enumerate(self.field_descs) \
+ if x.name == field_name][0]
+
+ def set_record_struct(self, field_descs):
+ # Begin with empty field struct list
+ fsl = ['>']
+ for field_desc in field_descs:
+ if field_desc.type == 1: # string
+ fsl.append('%ds' % field_desc.size)
+ elif field_desc.type == 3: # 16-bit int
+ fsl.append('H')
+ elif field_desc.type == 4: # 32-bit int
+ fsl.append('I')
+ elif field_desc.type == 9: # Logical
+ fsl.append('B')
+ elif field_desc.type == 0x0c: # Memo
+ fsl.append('%ds' % field_desc.size)
+ elif field_desc.type == 0x0d: # Blob
+ fsl.append('%ds' % field_desc.size)
+ elif field_desc.type == 0x15: # Timestamp
+ fsl.append('Q')
+ else:
+ fsl.append('%ds' % field_desc.size)
+ self.record_struct = struct.Struct(''.join(fsl))
+ self.field_descs = field_descs
+
+ def get_field(self, field_desc_index):
+ field = self.fields[field_desc_index]
+ field_desc = self.field_descs[field_desc_index]
+
+ # Check for 'blank' entries
+ if isinstance(field, str):
+ if len(field.rstrip('\0')) == 0:
+ return u''
+ elif field == 0:
+ return 0
+
+ if field_desc.type == 1: # string
+ return field.rstrip('\0').decode(u'windows-1252')
+ elif field_desc.type == 3: # 16-bit int
+ return field ^ 0x8000
+ elif field_desc.type == 4: # 32-bit int
+ return field ^ 0x80000000
+ elif field_desc.type == 9: # Logical
+ return (field ^ 0x80 == 1)
+ elif field_desc.type == 0x0c or field_desc.type == 0x0d:
+ sub_block, block_start, blob_size = \
+ struct.unpack_from('<bhxi', field, len(field)-10)
+ self.memo_file.seek(block_start * 256)
+ memo_block_type, = struct.unpack('b', self.memo_file.read(1))
+ if memo_block_type == 2:
+ self.memo_file.seek(8, os.SEEK_CUR)
+ elif memo_block_type == 3:
+ if sub_block < 0 or sub_block > 63:
+ return u'';
+ self.memo_file.seek(11 + (5 * sub_block), os.SEEK_CUR)
+ sub_block_start, = struct.unpack('B', self.memo_file.read(1))
+ self.memo_file.seek((block_start * 256) +
+ (sub_block_start * 16))
+ else:
+ return u'';
+
+ return self.memo_file.read(blob_size)
+ else:
+ return 0
=== modified file 'openlp/plugins/songs/lib/importer.py'
--- openlp/plugins/songs/lib/importer.py 2010-09-14 20:33:33 +0000
+++ openlp/plugins/songs/lib/importer.py 2010-09-20 18:28:43 +0000
@@ -28,6 +28,7 @@
from olpimport import OpenLPSongImport
from wowimport import WowImport
from cclifileimport import CCLIFileImport
+from ewimport import EasyWorshipSongImport
# Imports that might fail
try:
from olp1import import OpenLP1SongImport
@@ -61,7 +62,8 @@
CCLI = 5
SongsOfFellowship = 6
Generic = 7
- CSV = 8
+ #CSV = 8
+ EasyWorship = 8
@staticmethod
def get_class(format):
@@ -85,6 +87,8 @@
return OooImport
elif format == SongFormat.CCLI:
return CCLIFileImport
+ elif format == SongFormat.EasyWorship:
+ return EasyWorshipSongImport
# else:
return None
@@ -101,7 +105,8 @@
SongFormat.WordsOfWorship,
SongFormat.CCLI,
SongFormat.SongsOfFellowship,
- SongFormat.Generic
+ SongFormat.Generic,
+ SongFormat.EasyWorship
]
@staticmethod
Follow ups