mirror of https://gitlab.com/openlp/openlp.git
Importer cleanups
This commit is contained in:
parent
75281b5cf1
commit
ef97095399
|
@ -2485,7 +2485,7 @@ $.mobile.transitionFallbacks = {};
|
|||
return path.stripHash( newPath ).replace( /[^\/]*\.[^\/*]+$/, '' );
|
||||
},
|
||||
|
||||
//return the substring of a filepath before the sub-page key, for making a server request
|
||||
//return the substring of a file_path before the sub-page key, for making a server request
|
||||
getFilePath: function( path ) {
|
||||
var splitkey = '&' + $.mobile.subPageUrlKey;
|
||||
return path && path.split( splitkey )[0].split( dialogHashKey )[0];
|
||||
|
|
|
@ -334,7 +334,7 @@ class SongImportForm(OpenLPWizard):
|
|||
|
||||
def perform_wizard(self):
|
||||
"""
|
||||
Perform the actual import. This method pulls in the correct importer class, and then runs the ``doImport``
|
||||
Perform the actual import. This method pulls in the correct importer class, and then runs the ``do_import``
|
||||
method of the importer to do the actual importing.
|
||||
"""
|
||||
source_format = self.current_format
|
||||
|
@ -349,7 +349,7 @@ class SongImportForm(OpenLPWizard):
|
|||
importer = self.plugin.import_songs(
|
||||
source_format,
|
||||
filenames=self.get_list_of_files(self.format_widgets[source_format]['file_list_widget']))
|
||||
importer.doImport()
|
||||
importer.do_import()
|
||||
self.progress_label.setText(WizardStrings.FinishedImport)
|
||||
|
||||
def on_error_copy_to_button_clicked(self):
|
||||
|
|
|
@ -197,11 +197,8 @@ class VerseType(object):
|
|||
"""
|
||||
Return the translated UPPERCASE tag for a given tag, used to show translated verse tags in UI
|
||||
|
||||
``verse_tag``
|
||||
The string to return a VerseType for
|
||||
|
||||
``default``
|
||||
Default return value if no matching tag is found
|
||||
:param verse_tag: The string to return a VerseType for
|
||||
:param default: Default return value if no matching tag is found
|
||||
"""
|
||||
verse_tag = verse_tag[0].lower()
|
||||
for num, tag in enumerate(VerseType.tags):
|
||||
|
@ -217,11 +214,8 @@ class VerseType(object):
|
|||
"""
|
||||
Return the translated name for a given tag
|
||||
|
||||
``verse_tag``
|
||||
The string to return a VerseType for
|
||||
|
||||
``default``
|
||||
Default return value if no matching tag is found
|
||||
:param verse_tag: The string to return a VerseType for
|
||||
:param default: Default return value if no matching tag is found
|
||||
"""
|
||||
verse_tag = verse_tag[0].lower()
|
||||
for num, tag in enumerate(VerseType.tags):
|
||||
|
@ -237,11 +231,8 @@ class VerseType(object):
|
|||
"""
|
||||
Return the VerseType for a given tag
|
||||
|
||||
``verse_tag``
|
||||
The string to return a VerseType for
|
||||
|
||||
``default``
|
||||
Default return value if no matching tag is found
|
||||
:param verse_tag: The string to return a VerseType for
|
||||
:param default: Default return value if no matching tag is found
|
||||
"""
|
||||
verse_tag = verse_tag[0].lower()
|
||||
for num, tag in enumerate(VerseType.tags):
|
||||
|
@ -257,11 +248,8 @@ class VerseType(object):
|
|||
"""
|
||||
Return the VerseType for a given tag
|
||||
|
||||
``verse_tag``
|
||||
The string to return a VerseType for
|
||||
|
||||
``default``
|
||||
Default return value if no matching tag is found
|
||||
:param verse_tag: The string to return a VerseType for
|
||||
:param default: Default return value if no matching tag is found
|
||||
"""
|
||||
verse_tag = verse_tag[0].lower()
|
||||
for num, tag in enumerate(VerseType.translated_tags):
|
||||
|
@ -277,11 +265,8 @@ class VerseType(object):
|
|||
"""
|
||||
Return the VerseType for a given string
|
||||
|
||||
``verse_name``
|
||||
The string to return a VerseType for
|
||||
|
||||
``default``
|
||||
Default return value if no matching tag is found
|
||||
:param verse_name: The string to return a VerseType for
|
||||
:param default: Default return value if no matching tag is found
|
||||
"""
|
||||
verse_name = verse_name.lower()
|
||||
for num, name in enumerate(VerseType.names):
|
||||
|
@ -294,8 +279,7 @@ class VerseType(object):
|
|||
"""
|
||||
Return the VerseType for a given string
|
||||
|
||||
``verse_name``
|
||||
The string to return a VerseType for
|
||||
:param verse_name: The string to return a VerseType for
|
||||
"""
|
||||
verse_name = verse_name.lower()
|
||||
for num, translation in enumerate(VerseType.translated_names):
|
||||
|
@ -307,11 +291,8 @@ class VerseType(object):
|
|||
"""
|
||||
Return the VerseType for a given string
|
||||
|
||||
``verse_name``
|
||||
The string to return a VerseType for
|
||||
|
||||
``default``
|
||||
Default return value if no matching tag is found
|
||||
:param verse_name: The string to return a VerseType for
|
||||
:param default: Default return value if no matching tag is found
|
||||
"""
|
||||
if len(verse_name) > 1:
|
||||
verse_index = VerseType.from_translated_string(verse_name)
|
||||
|
@ -331,11 +312,11 @@ def retrieve_windows_encoding(recommendation=None):
|
|||
Determines which encoding to use on an information source. The process uses both automated detection, which is
|
||||
passed to this method as a recommendation, and user confirmation to return an encoding.
|
||||
|
||||
``recommendation``
|
||||
A recommended encoding discovered programmatically for the user to confirm.
|
||||
:param recommendation: A recommended encoding discovered programmatically for the user to confirm.
|
||||
"""
|
||||
# map chardet result to compatible windows standard code page
|
||||
codepage_mapping = {'IBM866': 'cp866', 'TIS-620': 'cp874',
|
||||
codepage_mapping = {
|
||||
'IBM866': 'cp866', 'TIS-620': 'cp874',
|
||||
'SHIFT_JIS': 'cp932', 'GB2312': 'cp936', 'HZ-GB-2312': 'cp936',
|
||||
'EUC-KR': 'cp949', 'Big5': 'cp950', 'ISO-8859-2': 'cp1250',
|
||||
'windows-1250': 'cp1250', 'windows-1251': 'cp1251',
|
||||
|
@ -346,7 +327,8 @@ def retrieve_windows_encoding(recommendation=None):
|
|||
recommendation = codepage_mapping[recommendation]
|
||||
|
||||
# Show dialog for encoding selection
|
||||
encodings = [('cp1256', translate('SongsPlugin', 'Arabic (CP-1256)')),
|
||||
encodings = [
|
||||
('cp1256', translate('SongsPlugin', 'Arabic (CP-1256)')),
|
||||
('cp1257', translate('SongsPlugin', 'Baltic (CP-1257)')),
|
||||
('cp1250', translate('SongsPlugin', 'Central European (CP-1250)')),
|
||||
('cp1251', translate('SongsPlugin', 'Cyrillic (CP-1251)')),
|
||||
|
@ -367,17 +349,17 @@ def retrieve_windows_encoding(recommendation=None):
|
|||
recommended_index = index
|
||||
break
|
||||
if recommended_index > -1:
|
||||
choice = QtGui.QInputDialog.getItem(None,
|
||||
translate('SongsPlugin', 'Character Encoding'),
|
||||
translate('SongsPlugin', 'The codepage setting is responsible\n'
|
||||
'for the correct character representation.\nUsually you are fine with the preselected choice.'),
|
||||
choice = QtGui.QInputDialog.getItem(
|
||||
None, translate('SongsPlugin', 'Character Encoding'),
|
||||
translate('SongsPlugin', 'The codepage setting is responsible\nfor the correct character '
|
||||
'representation.\nUsually you are fine with the preselected choice.'),
|
||||
[pair[1] for pair in encodings], recommended_index, False)
|
||||
else:
|
||||
choice = QtGui.QInputDialog.getItem(None,
|
||||
translate('SongsPlugin', 'Character Encoding'),
|
||||
translate('SongsPlugin', 'Please choose the character encoding.\n'
|
||||
'The encoding is responsible for the correct character representation.'),
|
||||
[pair[1] for pair in encodings], 0, False)
|
||||
choice = QtGui.QInputDialog.getItem(
|
||||
None, translate('SongsPlugin', 'Character Encoding'),
|
||||
translate('SongsPlugin', 'Please choose the character encoding.\nThe encoding is responsible for the '
|
||||
'correct character representation.'),
|
||||
[pair[1] for pair in encodings], 0, False)
|
||||
if not choice[1]:
|
||||
return None
|
||||
return next(filter(lambda item: item[1] == choice[0], encodings))[0]
|
||||
|
@ -402,11 +384,8 @@ def clean_song(manager, song):
|
|||
Cleans the search title, rebuilds the search lyrics, adds a default author if the song does not have one and other
|
||||
clean ups. This should always called when a new song is added or changed.
|
||||
|
||||
``manager``
|
||||
The song's manager.
|
||||
|
||||
``song``
|
||||
The song object.
|
||||
:param manager: The song's manager.
|
||||
:param song: The song object.
|
||||
"""
|
||||
from .xml import SongXML
|
||||
|
||||
|
@ -425,8 +404,7 @@ def clean_song(manager, song):
|
|||
# keeps the database clean. This can be removed when everybody has cleaned his songs.
|
||||
song.lyrics = song.lyrics.replace('<lyrics language="en">', '<lyrics>')
|
||||
verses = SongXML().get_verses(song.lyrics)
|
||||
song.search_lyrics = ' '.join([clean_string(verse[1])
|
||||
for verse in verses])
|
||||
song.search_lyrics = ' '.join([clean_string(verse[1]) for verse in verses])
|
||||
# We need a new and clean SongXML instance.
|
||||
sxml = SongXML()
|
||||
# Rebuild the song's verses, to remove any wrong verse names (for example translated ones), which might have
|
||||
|
@ -466,8 +444,7 @@ def clean_song(manager, song):
|
|||
break
|
||||
else:
|
||||
verses = SongXML().get_verses(song.lyrics)
|
||||
song.search_lyrics = ' '.join([clean_string(verse[1])
|
||||
for verse in verses])
|
||||
song.search_lyrics = ' '.join([clean_string(verse[1]) for verse in verses])
|
||||
|
||||
# The song does not have any author, add one.
|
||||
if not song.authors:
|
||||
|
@ -484,17 +461,10 @@ def get_encoding(font, font_table, default_encoding, failed=False):
|
|||
"""
|
||||
Finds an encoding to use. Asks user, if necessary.
|
||||
|
||||
``font``
|
||||
The number of currently active font.
|
||||
|
||||
``font_table``
|
||||
Dictionary of fonts and respective encodings.
|
||||
|
||||
``default_encoding``
|
||||
The default encoding to use when font_table is empty or no font is used.
|
||||
|
||||
``failed``
|
||||
A boolean indicating whether the previous encoding didn't work.
|
||||
:param font: The number of currently active font.
|
||||
:param font_table: Dictionary of fonts and respective encodings.
|
||||
:param default_encoding: The default encoding to use when font_table is empty or no font is used.
|
||||
:param failed: A boolean indicating whether the previous encoding didn't work.
|
||||
"""
|
||||
encoding = None
|
||||
if font in font_table:
|
||||
|
@ -513,13 +483,11 @@ def strip_rtf(text, default_encoding=None):
|
|||
This function strips RTF control structures and returns an unicode string.
|
||||
|
||||
Thanks to Markus Jarderot (MizardX) for this code, used by permission.
|
||||
|
||||
http://stackoverflow.com/questions/188545
|
||||
|
||||
``text``
|
||||
RTF-encoded text, a string.
|
||||
|
||||
``default_encoding``
|
||||
Default encoding to use when no encoding is specified.
|
||||
:param text: RTF-encoded text, a string.
|
||||
:param default_encoding: Default encoding to use when no encoding is specified.
|
||||
"""
|
||||
# Current font is the font tag we last met.
|
||||
font = ''
|
||||
|
@ -623,11 +591,8 @@ def delete_song(song_id, song_plugin):
|
|||
Deletes a song from the database. Media files associated to the song
|
||||
are removed prior to the deletion of the song.
|
||||
|
||||
``song_id``
|
||||
The ID of the song to delete.
|
||||
|
||||
``song_plugin``
|
||||
The song plugin instance.
|
||||
:param song_id: The ID of the song to delete.
|
||||
:param song_plugin: The song plugin instance.
|
||||
"""
|
||||
media_files = song_plugin.manager.get_all_objects(MediaFile, MediaFile.song_id == song_id)
|
||||
for media_file in media_files:
|
||||
|
|
|
@ -41,24 +41,20 @@ log = logging.getLogger(__name__)
|
|||
|
||||
class CCLIFileImport(SongImport):
|
||||
"""
|
||||
The :class:`CCLIFileImport` class provides OpenLP with the ability to import
|
||||
CCLI SongSelect song files in both .txt and .usr formats. See
|
||||
`<http://www.ccli.com/>`_ for more details.
|
||||
The :class:`CCLIFileImport` class provides OpenLP with the ability to import CCLI SongSelect song files in
|
||||
both .txt and .usr formats. See `<http://www.ccli.com/>`_ for more details.
|
||||
"""
|
||||
|
||||
def __init__(self, manager, **kwargs):
|
||||
"""
|
||||
Initialise the import.
|
||||
|
||||
``manager``
|
||||
The song manager for the running OpenLP installation.
|
||||
|
||||
``filenames``
|
||||
The files to be imported.
|
||||
:param manager: The song manager for the running OpenLP installation.
|
||||
:param kwargs: The files to be imported.
|
||||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
"""
|
||||
Import either a ``.usr`` or a ``.txt`` SongSelect file.
|
||||
"""
|
||||
|
@ -86,11 +82,11 @@ class CCLIFileImport(SongImport):
|
|||
ext = os.path.splitext(filename)[1]
|
||||
if ext.lower() == '.usr':
|
||||
log.info('SongSelect .usr format file found: %s', filename)
|
||||
if not self.doImportUsrFile(lines):
|
||||
if not self.do_import_usr_file(lines):
|
||||
self.log_error(filename)
|
||||
elif ext.lower() == '.txt':
|
||||
log.info('SongSelect .txt format file found: %s', filename)
|
||||
if not self.doImportTxtFile(lines):
|
||||
if not self.do_import_txt_file(lines):
|
||||
self.log_error(filename)
|
||||
else:
|
||||
self.log_error(filename,
|
||||
|
@ -99,14 +95,11 @@ class CCLIFileImport(SongImport):
|
|||
if self.stop_import_flag:
|
||||
return
|
||||
|
||||
def doImportUsrFile(self, textList):
|
||||
def do_import_usr_file(self, text_list):
|
||||
"""
|
||||
The :func:`doImport_usr_file` method provides OpenLP with the ability
|
||||
to import CCLI SongSelect songs in *USR* file format.
|
||||
|
||||
``textList``
|
||||
An array of strings containing the usr file content.
|
||||
|
||||
**SongSelect .usr file format**
|
||||
|
||||
``[File]``
|
||||
|
@ -156,11 +149,12 @@ class CCLIFileImport(SongImport):
|
|||
*Fields* description
|
||||
e.g. *Words=Above all powers....* [/n = CR, /n/t = CRLF]
|
||||
|
||||
:param text_list: An array of strings containing the usr file content.
|
||||
"""
|
||||
log.debug('USR file text: %s', textList)
|
||||
log.debug('USR file text: %s', text_list)
|
||||
song_author = ''
|
||||
song_topics = ''
|
||||
for line in textList:
|
||||
for line in text_list:
|
||||
if line.startswith('[S '):
|
||||
ccli, line = line.split(']', 1)
|
||||
if ccli.startswith('[S A'):
|
||||
|
@ -177,7 +171,7 @@ class CCLIFileImport(SongImport):
|
|||
song_topics = line[7:].strip().replace(' | ', '/t')
|
||||
elif line.startswith('Fields='):
|
||||
# Fields contain single line indicating verse, chorus, etc,
|
||||
# /t delimited, same as with words field. store seperately
|
||||
# /t delimited, same as with words field. store separately
|
||||
# and process at end.
|
||||
song_fields = line[7:].strip()
|
||||
elif line.startswith('Words='):
|
||||
|
@ -228,13 +222,12 @@ class CCLIFileImport(SongImport):
|
|||
self.topics = [topic.strip() for topic in song_topics.split('/t')]
|
||||
return self.finish()
|
||||
|
||||
def doImportTxtFile(self, textList):
|
||||
def do_import_txt_file(self, text_list):
|
||||
"""
|
||||
The :func:`doImport_txt_file` method provides OpenLP with the ability
|
||||
to import CCLI SongSelect songs in *TXT* file format.
|
||||
|
||||
``textList``
|
||||
An array of strings containing the txt file content.
|
||||
:param text_list: An array of strings containing the txt file content.
|
||||
|
||||
SongSelect .txt file format::
|
||||
|
||||
|
@ -260,13 +253,13 @@ class CCLIFileImport(SongImport):
|
|||
# e.g. CCL-Liedlizenznummer: 14 / CCLI License No. 14
|
||||
|
||||
"""
|
||||
log.debug('TXT file text: %s', textList)
|
||||
log.debug('TXT file text: %s', text_list)
|
||||
line_number = 0
|
||||
check_first_verse_line = False
|
||||
verse_text = ''
|
||||
song_author = ''
|
||||
verse_start = False
|
||||
for line in textList:
|
||||
for line in text_list:
|
||||
clean_line = line.strip()
|
||||
if not clean_line:
|
||||
if line_number == 0:
|
||||
|
|
|
@ -87,6 +87,7 @@ class Topic(BaseModel):
|
|||
"""
|
||||
pass
|
||||
|
||||
|
||||
def init_schema(url):
|
||||
"""
|
||||
Setup the songs database connection and initialise the database schema.
|
||||
|
@ -183,8 +184,7 @@ def init_schema(url):
|
|||
# Definition of the "media_files" table
|
||||
media_files_table = Table('media_files', metadata,
|
||||
Column('id', types.Integer(), primary_key=True),
|
||||
Column('song_id', types.Integer(), ForeignKey('songs.id'),
|
||||
default=None),
|
||||
Column('song_id', types.Integer(), ForeignKey('songs.id'), default=None),
|
||||
Column('file_name', types.Unicode(255), nullable=False),
|
||||
Column('type', types.Unicode(64), nullable=False, default='audio'),
|
||||
Column('weight', types.Integer(), default=0)
|
||||
|
@ -200,8 +200,7 @@ def init_schema(url):
|
|||
# Definition of the "songs" table
|
||||
songs_table = Table('songs', metadata,
|
||||
Column('id', types.Integer(), primary_key=True),
|
||||
Column('song_book_id', types.Integer(),
|
||||
ForeignKey('song_books.id'), default=None),
|
||||
Column('song_book_id', types.Integer(), ForeignKey('song_books.id'), default=None),
|
||||
Column('title', types.Unicode(255), nullable=False),
|
||||
Column('alternate_title', types.Unicode(255)),
|
||||
Column('lyrics', types.UnicodeText, nullable=False),
|
||||
|
@ -214,8 +213,7 @@ def init_schema(url):
|
|||
Column('search_title', types.Unicode(255), index=True, nullable=False),
|
||||
Column('search_lyrics', types.UnicodeText, nullable=False),
|
||||
Column('create_date', types.DateTime(), default=func.now()),
|
||||
Column('last_modified', types.DateTime(), default=func.now(),
|
||||
onupdate=func.now()),
|
||||
Column('last_modified', types.DateTime(), default=func.now(), onupdate=func.now()),
|
||||
Column('temporary', types.Boolean(), default=False)
|
||||
)
|
||||
|
||||
|
@ -227,18 +225,14 @@ def init_schema(url):
|
|||
|
||||
# Definition of the "authors_songs" table
|
||||
authors_songs_table = Table('authors_songs', metadata,
|
||||
Column('author_id', types.Integer(),
|
||||
ForeignKey('authors.id'), primary_key=True),
|
||||
Column('song_id', types.Integer(),
|
||||
ForeignKey('songs.id'), primary_key=True)
|
||||
Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True),
|
||||
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True)
|
||||
)
|
||||
|
||||
# Definition of the "songs_topics" table
|
||||
songs_topics_table = Table('songs_topics', metadata,
|
||||
Column('song_id', types.Integer(),
|
||||
ForeignKey('songs.id'), primary_key=True),
|
||||
Column('topic_id', types.Integer(),
|
||||
ForeignKey('topics.id'), primary_key=True)
|
||||
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
|
||||
Column('topic_id', types.Integer(), ForeignKey('topics.id'), primary_key=True)
|
||||
)
|
||||
|
||||
mapper(Author, authors_table)
|
||||
|
@ -246,13 +240,10 @@ def init_schema(url):
|
|||
mapper(MediaFile, media_files_table)
|
||||
mapper(Song, songs_table,
|
||||
properties={
|
||||
'authors': relation(Author, backref='songs',
|
||||
secondary=authors_songs_table, lazy=False),
|
||||
'authors': relation(Author, backref='songs', secondary=authors_songs_table, lazy=False),
|
||||
'book': relation(Book, backref='songs'),
|
||||
'media_files': relation(MediaFile, backref='songs',
|
||||
order_by=media_files_table.c.weight),
|
||||
'topics': relation(Topic, backref='songs',
|
||||
secondary=songs_topics_table)
|
||||
'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight),
|
||||
'topics': relation(Topic, backref='songs', secondary=songs_topics_table)
|
||||
})
|
||||
mapper(Topic, topics_table)
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ from openlp.plugins.songs.lib.ui import SongStrings
|
|||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DreamBeamImport(SongImport):
|
||||
"""
|
||||
The :class:`DreamBeamImport` class provides the ability to import song files from
|
||||
|
@ -83,7 +84,7 @@ class DreamBeamImport(SongImport):
|
|||
* \*.xml
|
||||
"""
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
"""
|
||||
Receive a single file or a list of files to import.
|
||||
"""
|
||||
|
@ -103,7 +104,8 @@ class DreamBeamImport(SongImport):
|
|||
xml = etree.tostring(parsed_file).decode()
|
||||
song_xml = objectify.fromstring(xml)
|
||||
if song_xml.tag != 'DreamSong':
|
||||
self.log_error(file,
|
||||
self.log_error(
|
||||
file,
|
||||
translate('SongsPlugin.DreamBeamImport', 'Invalid DreamBeam song file. Missing DreamSong tag.'))
|
||||
continue
|
||||
if hasattr(song_xml, 'Version'):
|
||||
|
@ -127,9 +129,9 @@ class DreamBeamImport(SongImport):
|
|||
if hasattr(song_xml, 'Number'):
|
||||
self.song_number = str(song_xml.Number.text)
|
||||
if hasattr(song_xml, 'Sequence'):
|
||||
for LyricsSequenceItem in (song_xml.Sequence.iterchildren()):
|
||||
self.verse_order_list.append("%s%s" % (LyricsSequenceItem.get('Type')[:1],
|
||||
LyricsSequenceItem.get('Number')))
|
||||
for lyrics_sequence_item in (song_xml.Sequence.iterchildren()):
|
||||
self.verse_order_list.append("%s%s" % (lyrics_sequence_item.get('Type')[:1],
|
||||
lyrics_sequence_item.get('Number')))
|
||||
if hasattr(song_xml, 'Notes'):
|
||||
self.comments = str(song_xml.Notes.text)
|
||||
else:
|
||||
|
|
|
@ -37,6 +37,7 @@ from openlp.plugins.songs.lib.songimport import SongImport
|
|||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EasySlidesImport(SongImport):
|
||||
"""
|
||||
Import songs exported from EasySlides
|
||||
|
@ -50,7 +51,7 @@ class EasySlidesImport(SongImport):
|
|||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
log.info('Importing EasySlides XML file %s', self.import_source)
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
parsed_file = etree.parse(self.import_source, parser)
|
||||
|
@ -60,9 +61,9 @@ class EasySlidesImport(SongImport):
|
|||
for song in song_xml.Item:
|
||||
if self.stop_import_flag:
|
||||
return
|
||||
self._parseSong(song)
|
||||
self._parse_song(song)
|
||||
|
||||
def _parseSong(self, song):
|
||||
def _parse_song(self, song):
|
||||
self._success = True
|
||||
self._add_unicode_attribute('title', song.Title1, True)
|
||||
if hasattr(song, 'Title2'):
|
||||
|
@ -71,7 +72,7 @@ class EasySlidesImport(SongImport):
|
|||
self._add_unicode_attribute('song_number', song.SongNumber)
|
||||
if self.song_number == '0':
|
||||
self.song_number = ''
|
||||
self._addAuthors(song)
|
||||
self._add_authors(song)
|
||||
if hasattr(song, 'Copyright'):
|
||||
self._add_copyright(song.Copyright)
|
||||
if hasattr(song, 'LicenceAdmin1'):
|
||||
|
@ -80,7 +81,7 @@ class EasySlidesImport(SongImport):
|
|||
self._add_copyright(song.LicenceAdmin2)
|
||||
if hasattr(song, 'BookReference'):
|
||||
self._add_unicode_attribute('song_book_name', song.BookReference)
|
||||
self._parseAndAddLyrics(song)
|
||||
self._parse_and_add_lyrics(song)
|
||||
if self._success:
|
||||
if not self.finish():
|
||||
self.log_error(song.Title1 if song.Title1 else '')
|
||||
|
@ -94,14 +95,9 @@ class EasySlidesImport(SongImport):
|
|||
present _success is set to False so the importer can react
|
||||
appropriately.
|
||||
|
||||
``self_attribute``
|
||||
The attribute in the song model to populate.
|
||||
|
||||
``import_attribute``
|
||||
The imported value to convert to unicode and save to the song.
|
||||
|
||||
``mandatory``
|
||||
Signals that this attribute must exist in a valid song.
|
||||
:param self_attribute: The attribute in the song model to populate.
|
||||
:param import_attribute: The imported value to convert to unicode and save to the song.
|
||||
:param mandatory: Signals that this attribute must exist in a valid song.
|
||||
"""
|
||||
try:
|
||||
setattr(self, self_attribute, str(import_attribute).strip())
|
||||
|
@ -113,7 +109,7 @@ class EasySlidesImport(SongImport):
|
|||
if mandatory:
|
||||
self._success = False
|
||||
|
||||
def _addAuthors(self, song):
|
||||
def _add_authors(self, song):
|
||||
try:
|
||||
authors = str(song.Writer).split(',')
|
||||
self.authors = [author.strip() for author in authors if author.strip()]
|
||||
|
@ -125,11 +121,9 @@ class EasySlidesImport(SongImport):
|
|||
|
||||
def _add_copyright(self, element):
|
||||
"""
|
||||
Add a piece of copyright to the total copyright information for the
|
||||
song.
|
||||
Add a piece of copyright to the total copyright information for the song.
|
||||
|
||||
``element``
|
||||
The imported variable to get the data from.
|
||||
:param element: The imported variable to get the data from.
|
||||
"""
|
||||
try:
|
||||
self.add_copyright(str(element).strip())
|
||||
|
@ -139,7 +133,12 @@ class EasySlidesImport(SongImport):
|
|||
except AttributeError:
|
||||
pass
|
||||
|
||||
def _parseAndAddLyrics(self, song):
|
||||
def _parse_and_add_lyrics(self, song):
|
||||
"""
|
||||
Process the song lyrics
|
||||
|
||||
:param song: The song details
|
||||
"""
|
||||
try:
|
||||
lyrics = str(song.Contents).strip()
|
||||
except UnicodeDecodeError:
|
||||
|
@ -151,29 +150,29 @@ class EasySlidesImport(SongImport):
|
|||
lines = lyrics.split('\n')
|
||||
# we go over all lines first, to determine information,
|
||||
# which tells us how to parse verses later
|
||||
regionlines = {}
|
||||
separatorlines = 0
|
||||
region_lines = {}
|
||||
separator_lines = 0
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
elif line[1:7] == 'region':
|
||||
# this is region separator, probably [region 2]
|
||||
region = self._extractRegion(line)
|
||||
regionlines[region] = 1 + regionlines.get(region, 0)
|
||||
region = self._extract_region(line)
|
||||
region_lines[region] = 1 + region_lines.get(region, 0)
|
||||
elif line[0] == '[':
|
||||
separatorlines += 1
|
||||
separator_lines += 1
|
||||
# if the song has separators
|
||||
separators = (separatorlines > 0)
|
||||
separators = (separator_lines > 0)
|
||||
# the number of different regions in song - 1
|
||||
if len(regionlines) > 1:
|
||||
if len(region_lines) > 1:
|
||||
log.info('EasySlidesImport: the file contained a song named "%s"'
|
||||
'with more than two regions, but only two regions are tested, encountered regions were: %s',
|
||||
self.title, ','.join(list(regionlines.keys())))
|
||||
'with more than two regions, but only two regions are tested, encountered regions were: %s',
|
||||
self.title, ','.join(list(region_lines.keys())))
|
||||
# if the song has regions
|
||||
regions = (len(regionlines) > 0)
|
||||
regions = (len(region_lines) > 0)
|
||||
# if the regions are inside verses
|
||||
regionsInVerses = (regions and regionlines[list(regionlines.keys())[0]] > 1)
|
||||
regions_in_verses = (regions and region_lines[list(region_lines.keys())[0]] > 1)
|
||||
MarkTypes = {
|
||||
'CHORUS': VerseType.tags[VerseType.Chorus],
|
||||
'VERSE': VerseType.tags[VerseType.Verse],
|
||||
|
@ -185,21 +184,20 @@ class EasySlidesImport(SongImport):
|
|||
verses = {}
|
||||
# list as [region, versetype, versenum, instance]
|
||||
our_verse_order = []
|
||||
defaultregion = '1'
|
||||
reg = defaultregion
|
||||
default_region = '1'
|
||||
reg = default_region
|
||||
verses[reg] = {}
|
||||
# instance differentiates occurrences of same verse tag
|
||||
vt = 'V'
|
||||
vn = '1'
|
||||
inst = 1
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
if separators:
|
||||
# separators are used, so empty line means slide break
|
||||
# inside verse
|
||||
if self._listHas(verses, [reg, vt, vn, inst]):
|
||||
if self._list_has(verses, [reg, vt, vn, inst]):
|
||||
inst += 1
|
||||
else:
|
||||
# separators are not used, so empty line starts a new verse
|
||||
|
@ -207,9 +205,9 @@ class EasySlidesImport(SongImport):
|
|||
vn = len(verses[reg].get(vt, {})) + 1
|
||||
inst = 1
|
||||
elif line[0:7] == '[region':
|
||||
reg = self._extractRegion(line)
|
||||
reg = self._extract_region(line)
|
||||
verses.setdefault(reg, {})
|
||||
if not regionsInVerses:
|
||||
if not regions_in_verses:
|
||||
vt = 'V'
|
||||
vn = '1'
|
||||
inst = 1
|
||||
|
@ -224,10 +222,10 @@ class EasySlidesImport(SongImport):
|
|||
marker = match.group(1).strip()
|
||||
vn = match.group(2)
|
||||
vt = MarkTypes.get(marker, 'O') if marker else 'V'
|
||||
if regionsInVerses:
|
||||
region = defaultregion
|
||||
if regions_in_verses:
|
||||
region = default_region
|
||||
inst = 1
|
||||
if self._listHas(verses, [reg, vt, vn, inst]):
|
||||
if self._list_has(verses, [reg, vt, vn, inst]):
|
||||
inst = len(verses[reg][vt][vn]) + 1
|
||||
else:
|
||||
if not [reg, vt, vn, inst] in our_verse_order:
|
||||
|
@ -237,19 +235,17 @@ class EasySlidesImport(SongImport):
|
|||
verses[reg][vt][vn].setdefault(inst, [])
|
||||
verses[reg][vt][vn][inst].append(self.tidy_text(line))
|
||||
# done parsing
|
||||
|
||||
versetags = []
|
||||
# we use our_verse_order to ensure, we insert lyrics in the same order
|
||||
# as these appeared originally in the file
|
||||
for [reg, vt, vn, inst] in our_verse_order:
|
||||
if self._listHas(verses, [reg, vt, vn, inst]):
|
||||
if self._list_has(verses, [reg, vt, vn, inst]):
|
||||
# this is false, but needs user input
|
||||
lang = None
|
||||
versetag = '%s%s' % (vt, vn)
|
||||
versetags.append(versetag)
|
||||
lines = '\n'.join(verses[reg][vt][vn][inst])
|
||||
self.verses.append([versetag, lines, lang])
|
||||
|
||||
SeqTypes = {
|
||||
'p': 'P1',
|
||||
'q': 'P2',
|
||||
|
@ -273,23 +269,35 @@ class EasySlidesImport(SongImport):
|
|||
if tag in versetags:
|
||||
self.verse_order_list.append(tag)
|
||||
else:
|
||||
log.info('Got order item %s, which is not in versetags, dropping item from presentation order',
|
||||
tag)
|
||||
log.info('Got order item %s, which is not in versetags, dropping item from presentation order', tag)
|
||||
except UnicodeDecodeError:
|
||||
log.exception('Unicode decode error while decoding Sequence')
|
||||
self._success = False
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def _listHas(self, lst, subitems):
|
||||
for subitem in subitems:
|
||||
if subitem in lst:
|
||||
lst = lst[subitem]
|
||||
def _list_has(self, lst, sub_items):
|
||||
"""
|
||||
See if the list has sub items
|
||||
|
||||
:param lst: The list to check
|
||||
:param sub_items: sub item list
|
||||
:return:
|
||||
"""
|
||||
for sub_item in sub_items:
|
||||
if sub_item in lst:
|
||||
lst = lst[sub_item]
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _extractRegion(self, line):
|
||||
def _extract_region(self, line):
|
||||
# this was true already: line[0:7] == u'[region':
|
||||
"""
|
||||
Extract the region from text
|
||||
|
||||
:param line: The line of text
|
||||
:return:
|
||||
"""
|
||||
right_bracket = line.find(']')
|
||||
return line[7:right_bracket].strip()
|
||||
|
|
|
@ -75,7 +75,7 @@ class EasyWorshipSongImport(SongImport):
|
|||
def __init__(self, manager, **kwargs):
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
|
||||
def doImport(self):
|
||||
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) or not os.path.isfile(import_source_mb):
|
||||
|
@ -84,12 +84,12 @@ class EasyWorshipSongImport(SongImport):
|
|||
if db_size < 0x800:
|
||||
return
|
||||
db_file = open(self.import_source, 'rb')
|
||||
self.memoFile = open(import_source_mb, '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, first_block, num_fields = struct.unpack('<hhxb8xh17xh', db_file.read(35))
|
||||
if header_size != 0x800 or block_size < 1 or block_size > 4:
|
||||
db_file.close()
|
||||
self.memoFile.close()
|
||||
self.memo_file.close()
|
||||
return
|
||||
# Take a stab at how text is encoded
|
||||
self.encoding = 'cp1252'
|
||||
|
@ -124,20 +124,20 @@ class EasyWorshipSongImport(SongImport):
|
|||
db_file.seek(4 + (num_fields * 4) + 261, os.SEEK_CUR)
|
||||
field_names = db_file.read(header_size - db_file.tell()).split(b'\0', num_fields)
|
||||
field_names.pop()
|
||||
field_descs = []
|
||||
field_descriptions = []
|
||||
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.setRecordStruct(field_descs)
|
||||
field_descriptions.append(FieldDescEntry(field_name, field_type, field_size))
|
||||
self.set_record_struct(field_descriptions)
|
||||
# Pick out the field description indexes we will need
|
||||
try:
|
||||
success = True
|
||||
fi_title = self.findField(b'Title')
|
||||
fi_author = self.findField(b'Author')
|
||||
fi_copy = self.findField(b'Copyright')
|
||||
fi_admin = self.findField(b'Administrator')
|
||||
fi_words = self.findField(b'Words')
|
||||
fi_ccli = self.findField(b'Song Number')
|
||||
fi_title = self.find_field(b'Title')
|
||||
fi_author = self.find_field(b'Author')
|
||||
fi_copy = self.find_field(b'Copyright')
|
||||
fi_admin = self.find_field(b'Administrator')
|
||||
fi_words = self.find_field(b'Words')
|
||||
fi_ccli = self.find_field(b'Song Number')
|
||||
except IndexError:
|
||||
# This is the wrong table
|
||||
success = False
|
||||
|
@ -162,15 +162,15 @@ class EasyWorshipSongImport(SongImport):
|
|||
if self.stop_import_flag:
|
||||
break
|
||||
raw_record = db_file.read(record_size)
|
||||
self.fields = self.recordStruct.unpack(raw_record)
|
||||
self.fields = self.record_structure.unpack(raw_record)
|
||||
self.set_defaults()
|
||||
self.title = self.getField(fi_title).decode()
|
||||
self.title = self.get_field(fi_title).decode()
|
||||
# Get remaining fields.
|
||||
copy = self.getField(fi_copy)
|
||||
admin = self.getField(fi_admin)
|
||||
ccli = self.getField(fi_ccli)
|
||||
authors = self.getField(fi_author)
|
||||
words = self.getField(fi_words)
|
||||
copy = self.get_field(fi_copy)
|
||||
admin = self.get_field(fi_admin)
|
||||
ccli = self.get_field(fi_ccli)
|
||||
authors = self.get_field(fi_author)
|
||||
words = self.get_field(fi_words)
|
||||
# Set the SongImport object members.
|
||||
if copy:
|
||||
self.copyright = copy.decode()
|
||||
|
@ -230,21 +230,21 @@ class EasyWorshipSongImport(SongImport):
|
|||
self.add_verse(verse_split[-1].strip() if first_line_is_tag else verse, verse_type)
|
||||
if len(self.comments) > 5:
|
||||
self.comments += str(translate('SongsPlugin.EasyWorshipSongImport',
|
||||
'\n[above are Song Tags with notes imported from EasyWorship]'))
|
||||
'\n[above are Song Tags with notes imported from EasyWorship]'))
|
||||
if self.stop_import_flag:
|
||||
break
|
||||
if not self.finish():
|
||||
self.log_error(self.import_source)
|
||||
db_file.close()
|
||||
self.memoFile.close()
|
||||
self.memo_file.close()
|
||||
|
||||
def findField(self, field_name):
|
||||
return [i for i, x in enumerate(self.fieldDescs) if x.name == field_name][0]
|
||||
def find_field(self, field_name):
|
||||
return [i for i, x in enumerate(self.field_descriptions) if x.name == field_name][0]
|
||||
|
||||
def setRecordStruct(self, field_descs):
|
||||
def set_record_struct(self, field_descriptions):
|
||||
# Begin with empty field struct list
|
||||
fsl = ['>']
|
||||
for field_desc in field_descs:
|
||||
for field_desc in field_descriptions:
|
||||
if field_desc.field_type == FieldType.String:
|
||||
fsl.append('%ds' % field_desc.size)
|
||||
elif field_desc.field_type == FieldType.Int16:
|
||||
|
@ -261,12 +261,18 @@ class EasyWorshipSongImport(SongImport):
|
|||
fsl.append('Q')
|
||||
else:
|
||||
fsl.append('%ds' % field_desc.size)
|
||||
self.recordStruct = struct.Struct(''.join(fsl))
|
||||
self.fieldDescs = field_descs
|
||||
self.record_structure = struct.Struct(''.join(fsl))
|
||||
self.field_descriptions = field_descriptions
|
||||
|
||||
def getField(self, field_desc_index):
|
||||
def get_field(self, field_desc_index):
|
||||
"""
|
||||
Extract the field
|
||||
|
||||
:param field_desc_index: Field index value
|
||||
:return:
|
||||
"""
|
||||
field = self.fields[field_desc_index]
|
||||
field_desc = self.fieldDescs[field_desc_index]
|
||||
field_desc = self.field_descriptions[field_desc_index]
|
||||
# Return None in case of 'blank' entries
|
||||
if isinstance(field, bytes):
|
||||
if not field.rstrip(b'\0'):
|
||||
|
@ -281,23 +287,23 @@ class EasyWorshipSongImport(SongImport):
|
|||
elif field_desc.field_type == FieldType.Int32:
|
||||
return field ^ 0x80000000
|
||||
elif field_desc.field_type == FieldType.Logical:
|
||||
return (field ^ 0x80 == 1)
|
||||
return field ^ 0x80 == 1
|
||||
elif field_desc.field_type == FieldType.Memo or field_desc.field_type == FieldType.Blob:
|
||||
block_start, blob_size = struct.unpack_from('<II', field, len(field)-10)
|
||||
sub_block = block_start & 0xff
|
||||
block_start &= ~0xff
|
||||
self.memoFile.seek(block_start)
|
||||
memo_block_type, = struct.unpack('b', self.memoFile.read(1))
|
||||
self.memo_file.seek(block_start)
|
||||
memo_block_type, = struct.unpack('b', self.memo_file.read(1))
|
||||
if memo_block_type == 2:
|
||||
self.memoFile.seek(8, os.SEEK_CUR)
|
||||
self.memo_file.seek(8, os.SEEK_CUR)
|
||||
elif memo_block_type == 3:
|
||||
if sub_block > 63:
|
||||
return b''
|
||||
self.memoFile.seek(11 + (5 * sub_block), os.SEEK_CUR)
|
||||
sub_block_start, = struct.unpack('B', self.memoFile.read(1))
|
||||
self.memoFile.seek(block_start + (sub_block_start * 16))
|
||||
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 + (sub_block_start * 16))
|
||||
else:
|
||||
return b''
|
||||
return self.memoFile.read(blob_size)
|
||||
return self.memo_file.read(blob_size)
|
||||
else:
|
||||
return 0
|
||||
|
|
|
@ -118,7 +118,7 @@ class FoilPresenterImport(SongImport):
|
|||
SongImport.__init__(self, manager, **kwargs)
|
||||
self.FoilPresenter = FoilPresenter(self.manager, self)
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
"""
|
||||
Imports the songs.
|
||||
"""
|
||||
|
|
|
@ -82,6 +82,7 @@ if os.name == 'nt':
|
|||
except ImportError:
|
||||
log.exception('Error importing %s', 'WorshipCenterProImport')
|
||||
|
||||
|
||||
class SongFormatSelect(object):
|
||||
"""
|
||||
This is a special enumeration class listing available file selection modes.
|
||||
|
@ -99,47 +100,47 @@ class SongFormat(object):
|
|||
|
||||
Required attributes for each song format:
|
||||
|
||||
``u'class'``
|
||||
``'class'``
|
||||
Import class, e.g. ``OpenLyricsImport``
|
||||
|
||||
``u'name'``
|
||||
Name of the format, e.g. ``u'OpenLyrics'``
|
||||
``'name'``
|
||||
Name of the format, e.g. ``'OpenLyrics'``
|
||||
|
||||
``u'prefix'``
|
||||
Prefix for Qt objects. Use mixedCase, e.g. ``u'openLyrics'``
|
||||
``'prefix'``
|
||||
Prefix for Qt objects. Use mixedCase, e.g. ``'openLyrics'``
|
||||
See ``SongImportForm.add_file_select_item()``
|
||||
|
||||
Optional attributes for each song format:
|
||||
|
||||
``u'canDisable'``
|
||||
``'canDisable'``
|
||||
Whether song format importer is disablable.
|
||||
If ``True``, then ``u'disabledLabelText'`` must also be defined.
|
||||
If ``True``, then ``'disabledLabelText'`` must also be defined.
|
||||
|
||||
``u'availability'``
|
||||
``'availability'``
|
||||
Whether song format importer is available.
|
||||
|
||||
``u'selectMode'``
|
||||
``'selectMode'``
|
||||
Whether format accepts single file, multiple files, or single folder
|
||||
(as per ``SongFormatSelect`` options).
|
||||
|
||||
``u'filter'``
|
||||
``'filter'``
|
||||
File extension filter for ``QFileDialog``.
|
||||
|
||||
Optional/custom text Strings for ``SongImportForm`` widgets:
|
||||
|
||||
``u'comboBoxText'``
|
||||
Combo box selector (default value is the format's ``u'name'``).
|
||||
``'comboBoxText'``
|
||||
Combo box selector (default value is the format's ``'name'``).
|
||||
|
||||
``u'disabledLabelText'``
|
||||
``'disabledLabelText'``
|
||||
Required for disablable song formats.
|
||||
|
||||
``u'getFilesTitle'``
|
||||
Title for ``QFileDialog`` (default includes the format's ``u'name'``).
|
||||
``'getFilesTitle'``
|
||||
Title for ``QFileDialog`` (default includes the format's ``'name'``).
|
||||
|
||||
``u'invalidSourceMsg'``
|
||||
``'invalidSourceMsg'``
|
||||
Message displayed if ``is_valid_source()`` returns ``False``.
|
||||
|
||||
``u'descriptionText'``
|
||||
``'descriptionText'``
|
||||
Short description (1-2 lines) about the song format.
|
||||
"""
|
||||
# Song formats (ordered alphabetically after Generic)
|
||||
|
@ -200,8 +201,8 @@ class SongFormat(object):
|
|||
'prefix': 'generic',
|
||||
'canDisable': True,
|
||||
'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
|
||||
'The generic document/presentation importer has been disabled '
|
||||
'because OpenLP cannot access OpenOffice or LibreOffice.'),
|
||||
'The generic document/presentation importer has been disabled '
|
||||
'because OpenLP cannot access OpenOffice or LibreOffice.'),
|
||||
'getFilesTitle': translate('SongsPlugin.ImportWizardForm', 'Select Document/Presentation Files')
|
||||
},
|
||||
CCLI: {
|
||||
|
@ -241,13 +242,12 @@ class SongFormat(object):
|
|||
'prefix': 'mediaShout',
|
||||
'canDisable': True,
|
||||
'selectMode': SongFormatSelect.SingleFile,
|
||||
'filter': '%s (*.mdb)' % translate('SongsPlugin.ImportWizardForm',
|
||||
'MediaShout Database'),
|
||||
'filter': '%s (*.mdb)' % translate('SongsPlugin.ImportWizardForm', 'MediaShout Database'),
|
||||
'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
|
||||
'The MediaShout importer is only supported on Windows. It has '
|
||||
'been disabled due to a missing Python module. If you want to '
|
||||
'use this importer, you will need to install the "pyodbc" '
|
||||
'module.')
|
||||
'The MediaShout importer is only supported on Windows. It has '
|
||||
'been disabled due to a missing Python module. If you want to '
|
||||
'use this importer, you will need to install the "pyodbc" '
|
||||
'module.')
|
||||
},
|
||||
OpenSong: {
|
||||
'class': OpenSongImport,
|
||||
|
@ -259,15 +259,14 @@ class SongFormat(object):
|
|||
'name': 'PowerSong 1.0',
|
||||
'prefix': 'powerSong',
|
||||
'selectMode': SongFormatSelect.SingleFolder,
|
||||
'invalidSourceMsg': translate('SongsPlugin.ImportWizardForm',
|
||||
'You need to specify a valid PowerSong 1.0 database folder.')
|
||||
'invalidSourceMsg': translate('SongsPlugin.ImportWizardForm', 'You need to specify a valid PowerSong 1.0 '
|
||||
'database folder.')
|
||||
},
|
||||
SongBeamer: {
|
||||
'class': SongBeamerImport,
|
||||
'name': 'SongBeamer',
|
||||
'prefix': 'songBeamer',
|
||||
'filter': '%s (*.sng)' % translate('SongsPlugin.ImportWizardForm',
|
||||
'SongBeamer Files')
|
||||
'filter': '%s (*.sng)' % translate('SongsPlugin.ImportWizardForm', 'SongBeamer Files')
|
||||
},
|
||||
SongPro: {
|
||||
'class': SongProImport,
|
||||
|
@ -277,7 +276,7 @@ class SongFormat(object):
|
|||
'filter': '%s (*.txt)' % translate('SongsPlugin.ImportWizardForm', 'SongPro Text Files'),
|
||||
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'SongPro (Export File)'),
|
||||
'descriptionText': translate('SongsPlugin.ImportWizardForm',
|
||||
'In SongPro, export your songs using the File -> Export menu')
|
||||
'In SongPro, export your songs using the File -> Export menu')
|
||||
},
|
||||
SongShowPlus: {
|
||||
'class': SongShowPlusImport,
|
||||
|
@ -291,8 +290,8 @@ class SongFormat(object):
|
|||
'canDisable': True,
|
||||
'filter': '%s (*.rtf)' % translate('SongsPlugin.ImportWizardForm', 'Songs Of Fellowship Song Files'),
|
||||
'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
|
||||
'The Songs of Fellowship importer has been disabled because '
|
||||
'OpenLP cannot access OpenOffice or LibreOffice.')
|
||||
'The Songs of Fellowship importer has been disabled because '
|
||||
'OpenLP cannot access OpenOffice or LibreOffice.')
|
||||
},
|
||||
SundayPlus: {
|
||||
'class': SundayPlusImport,
|
||||
|
@ -304,8 +303,7 @@ class SongFormat(object):
|
|||
'class': WowImport,
|
||||
'name': 'Words of Worship',
|
||||
'prefix': 'wordsOfWorship',
|
||||
'filter': '%s (*.wsg *.wow-song)' %
|
||||
translate('SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files')
|
||||
'filter': '%s (*.wsg *.wow-song)' % translate('SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files')
|
||||
},
|
||||
WorshipCenterPro: {
|
||||
'name': 'WorshipCenter Pro',
|
||||
|
@ -314,8 +312,9 @@ class SongFormat(object):
|
|||
'selectMode': SongFormatSelect.SingleFile,
|
||||
'filter': '%s (*.mdb)' % translate('SongsPlugin.ImportWizardForm', 'WorshipCenter Pro Song Files'),
|
||||
'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
|
||||
'The WorshipCenter Pro importer is only supported on Windows. It has been disabled due to a missing '
|
||||
'Python module. If you want to use this importer, you will need to install the "pyodbc" module.')
|
||||
'The WorshipCenter Pro importer is only supported on Windows. It has been '
|
||||
'disabled due to a missing Python module. If you want to use this '
|
||||
'importer, you will need to install the "pyodbc" module.')
|
||||
},
|
||||
ZionWorx: {
|
||||
'class': ZionWorxImport,
|
||||
|
@ -324,9 +323,9 @@ class SongFormat(object):
|
|||
'selectMode': SongFormatSelect.SingleFile,
|
||||
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'ZionWorx (CSV)'),
|
||||
'descriptionText': translate('SongsPlugin.ImportWizardForm',
|
||||
'First convert your ZionWorx database to a CSV text file, as '
|
||||
'explained in the <a href="http://manual.openlp.org/songs.html'
|
||||
'#importing-from-zionworx">User Manual</a>.')
|
||||
'First convert your ZionWorx database to a CSV text file, as '
|
||||
'explained in the <a href="http://manual.openlp.org/songs.html'
|
||||
'#importing-from-zionworx">User Manual</a>.')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,8 +36,8 @@ from PyQt4 import QtCore, QtGui
|
|||
from sqlalchemy.sql import or_
|
||||
|
||||
from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
|
||||
from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \
|
||||
check_item_selected, create_separated_list
|
||||
from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, check_item_selected, \
|
||||
create_separated_list
|
||||
from openlp.core.lib.ui import create_widget_action
|
||||
from openlp.plugins.songs.forms.editsongform import EditSongForm
|
||||
from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm
|
||||
|
|
|
@ -48,7 +48,7 @@ class MediaShoutImport(SongImport):
|
|||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
"""
|
||||
Receive a single file to import.
|
||||
"""
|
||||
|
|
|
@ -63,7 +63,7 @@ class OpenLPSongImport(SongImport):
|
|||
SongImport.__init__(self, manager, **kwargs)
|
||||
self.sourceSession = None
|
||||
|
||||
def doImport(self, progressDialog=None):
|
||||
def do_import(self, progressDialog=None):
|
||||
"""
|
||||
Run the import for an OpenLP version 2 song database.
|
||||
|
||||
|
|
|
@ -58,18 +58,17 @@ class OooImport(SongImport):
|
|||
"""
|
||||
def __init__(self, manager, **kwargs):
|
||||
"""
|
||||
Initialise the class. Requires a songmanager class which is passed
|
||||
to SongImport for writing song to disk
|
||||
Initialise the class. Requires a songmanager class which is passed to SongImport for writing song to disk
|
||||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
self.document = None
|
||||
self.processStarted = False
|
||||
self.process_started = False
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
if not isinstance(self.import_source, list):
|
||||
return
|
||||
try:
|
||||
self.startOoo()
|
||||
self.start_ooo()
|
||||
except NoConnectException as exc:
|
||||
self.log_error(
|
||||
self.import_source[0],
|
||||
|
@ -82,34 +81,34 @@ class OooImport(SongImport):
|
|||
break
|
||||
filename = str(filename)
|
||||
if os.path.isfile(filename):
|
||||
self.openOooFile(filename)
|
||||
self.open_ooo_file(filename)
|
||||
if self.document:
|
||||
self.processOooDocument()
|
||||
self.closeOooFile()
|
||||
self.process_ooo_document()
|
||||
self.close_ooo_file()
|
||||
else:
|
||||
self.log_error(self.filepath, translate('SongsPlugin.SongImport', 'Unable to open file'))
|
||||
self.log_error(self.file_path, translate('SongsPlugin.SongImport', 'Unable to open file'))
|
||||
else:
|
||||
self.log_error(self.filepath, translate('SongsPlugin.SongImport', 'File not found'))
|
||||
self.closeOoo()
|
||||
self.log_error(self.file_path, translate('SongsPlugin.SongImport', 'File not found'))
|
||||
self.close_ooo()
|
||||
|
||||
def processOooDocument(self):
|
||||
def process_ooo_document(self):
|
||||
"""
|
||||
Handle the import process for OpenOffice files. This method facilitates
|
||||
allowing subclasses to handle specific types of OpenOffice files.
|
||||
"""
|
||||
if self.document.supportsService("com.sun.star.presentation.PresentationDocument"):
|
||||
self.processPres()
|
||||
self.process_presentation()
|
||||
if self.document.supportsService("com.sun.star.text.TextDocument"):
|
||||
self.processDoc()
|
||||
self.process_doc()
|
||||
|
||||
def startOoo(self):
|
||||
def start_ooo(self):
|
||||
"""
|
||||
Start OpenOffice.org process
|
||||
TODO: The presentation/Impress plugin may already have it running
|
||||
"""
|
||||
if os.name == 'nt':
|
||||
self.startOooProcess()
|
||||
self.desktop = self.oooManager.createInstance('com.sun.star.frame.Desktop')
|
||||
self.start_ooo_process()
|
||||
self.desktop = self.ooo_manager.createInstance('com.sun.star.frame.Desktop')
|
||||
else:
|
||||
context = uno.getComponentContext()
|
||||
resolver = context.ServiceManager.createInstanceWithContext('com.sun.star.bridge.UnoUrlResolver', context)
|
||||
|
@ -121,7 +120,7 @@ class OooImport(SongImport):
|
|||
except NoConnectException:
|
||||
time.sleep(0.1)
|
||||
log.exception("Failed to resolve uno connection")
|
||||
self.startOooProcess()
|
||||
self.start_ooo_process()
|
||||
loop += 1
|
||||
else:
|
||||
manager = uno_instance.ServiceManager
|
||||
|
@ -129,60 +128,62 @@ class OooImport(SongImport):
|
|||
return
|
||||
raise Exception('Unable to start LibreOffice')
|
||||
|
||||
def startOooProcess(self):
|
||||
def start_ooo_process(self):
|
||||
"""
|
||||
Start the OO Process
|
||||
"""
|
||||
try:
|
||||
if os.name == 'nt':
|
||||
self.oooManager = Dispatch('com.sun.star.ServiceManager')
|
||||
self.oooManager._FlagAsMethod('Bridge_GetStruct')
|
||||
self.oooManager._FlagAsMethod('Bridge_GetValueObject')
|
||||
self.ooo_manager = Dispatch('com.sun.star.ServiceManager')
|
||||
self.ooo_manager._FlagAsMethod('Bridge_GetStruct')
|
||||
self.ooo_manager._FlagAsMethod('Bridge_GetValueObject')
|
||||
else:
|
||||
cmd = get_uno_command()
|
||||
process = QtCore.QProcess()
|
||||
process.startDetached(cmd)
|
||||
self.processStarted = True
|
||||
self.process_started = True
|
||||
except:
|
||||
log.exception("startOooProcess failed")
|
||||
log.exception("start_ooo_process failed")
|
||||
|
||||
def openOooFile(self, filepath):
|
||||
def open_ooo_file(self, file_path):
|
||||
"""
|
||||
Open the passed file in OpenOffice.org Impress
|
||||
"""
|
||||
self.filepath = filepath
|
||||
self.file_path = file_path
|
||||
if os.name == 'nt':
|
||||
url = filepath.replace('\\', '/')
|
||||
url = file_path.replace('\\', '/')
|
||||
url = url.replace(':', '|').replace(' ', '%20')
|
||||
url = 'file:///' + url
|
||||
else:
|
||||
url = uno.systemPathToFileUrl(filepath)
|
||||
url = uno.systemPathToFileUrl(file_path)
|
||||
properties = []
|
||||
properties = tuple(properties)
|
||||
try:
|
||||
self.document = self.desktop.loadComponentFromURL(url, '_blank',
|
||||
0, properties)
|
||||
self.document = self.desktop.loadComponentFromURL(url, '_blank', 0, properties)
|
||||
if not self.document.supportsService("com.sun.star.presentation.PresentationDocument") and not \
|
||||
self.document.supportsService("com.sun.star.text.TextDocument"):
|
||||
self.closeOooFile()
|
||||
self.close_ooo_file()
|
||||
else:
|
||||
self.import_wizard.increment_progress_bar('Processing file ' + filepath, 0)
|
||||
self.import_wizard.increment_progress_bar('Processing file ' + file_path, 0)
|
||||
except AttributeError:
|
||||
log.exception("openOooFile failed: %s", url)
|
||||
log.exception("open_ooo_file failed: %s", url)
|
||||
return
|
||||
|
||||
def closeOooFile(self):
|
||||
def close_ooo_file(self):
|
||||
"""
|
||||
Close file.
|
||||
"""
|
||||
self.document.close(True)
|
||||
self.document = None
|
||||
|
||||
def closeOoo(self):
|
||||
def close_ooo(self):
|
||||
"""
|
||||
Close OOo. But only if we started it and not on windows
|
||||
"""
|
||||
if self.processStarted:
|
||||
if self.process_started:
|
||||
self.desktop.terminate()
|
||||
|
||||
def processPres(self):
|
||||
def process_presentation(self):
|
||||
"""
|
||||
Process the file
|
||||
"""
|
||||
|
@ -194,45 +195,50 @@ class OooImport(SongImport):
|
|||
self.import_wizard.increment_progress_bar('Import cancelled', 0)
|
||||
return
|
||||
slide = slides.getByIndex(slide_no)
|
||||
slidetext = ''
|
||||
slide_text = ''
|
||||
for idx in range(slide.getCount()):
|
||||
shape = slide.getByIndex(idx)
|
||||
if shape.supportsService("com.sun.star.drawing.Text"):
|
||||
if shape.getString().strip() != '':
|
||||
slidetext += shape.getString().strip() + '\n\n'
|
||||
if slidetext.strip() == '':
|
||||
slidetext = '\f'
|
||||
text += slidetext
|
||||
self.processSongsText(text)
|
||||
slide_text += shape.getString().strip() + '\n\n'
|
||||
if slide_text.strip() == '':
|
||||
slide_text = '\f'
|
||||
text += slide_text
|
||||
self.process_songs_text(text)
|
||||
return
|
||||
|
||||
def processDoc(self):
|
||||
def process_doc(self):
|
||||
"""
|
||||
Process the doc file, a paragraph at a time
|
||||
"""
|
||||
text = ''
|
||||
paragraphs = self.document.getText().createEnumeration()
|
||||
while paragraphs.hasMoreElements():
|
||||
paratext = ''
|
||||
para_text = ''
|
||||
paragraph = paragraphs.nextElement()
|
||||
if paragraph.supportsService("com.sun.star.text.Paragraph"):
|
||||
textportions = paragraph.createEnumeration()
|
||||
while textportions.hasMoreElements():
|
||||
textportion = textportions.nextElement()
|
||||
if textportion.BreakType in (PAGE_BEFORE, PAGE_BOTH):
|
||||
paratext += '\f'
|
||||
paratext += textportion.getString()
|
||||
if textportion.BreakType in (PAGE_AFTER, PAGE_BOTH):
|
||||
paratext += '\f'
|
||||
text += paratext + '\n'
|
||||
self.processSongsText(text)
|
||||
text_portions = paragraph.createEnumeration()
|
||||
while text_portions.hasMoreElements():
|
||||
text_portion = text_portions.nextElement()
|
||||
if text_portion.BreakType in (PAGE_BEFORE, PAGE_BOTH):
|
||||
para_text += '\f'
|
||||
para_text += text_portion.getString()
|
||||
if text_portion.BreakType in (PAGE_AFTER, PAGE_BOTH):
|
||||
para_text += '\f'
|
||||
text += para_text + '\n'
|
||||
self.process_songs_text(text)
|
||||
|
||||
def processSongsText(self, text):
|
||||
songtexts = self.tidy_text(text).split('\f')
|
||||
def process_songs_text(self, text):
|
||||
"""
|
||||
Process the songs text
|
||||
|
||||
:param text: The text.
|
||||
"""
|
||||
song_texts = self.tidy_text(text).split('\f')
|
||||
self.set_defaults()
|
||||
for songtext in songtexts:
|
||||
if songtext.strip():
|
||||
self.process_song_text(songtext.strip())
|
||||
for song_text in song_texts:
|
||||
if song_text.strip():
|
||||
self.process_song_text(song_text.strip())
|
||||
if self.check_complete():
|
||||
self.finish()
|
||||
self.set_defaults()
|
||||
|
|
|
@ -56,7 +56,7 @@ class OpenLyricsImport(SongImport):
|
|||
SongImport.__init__(self, manager, **kwargs)
|
||||
self.openLyrics = OpenLyrics(self.manager)
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
"""
|
||||
Imports the songs.
|
||||
"""
|
||||
|
|
|
@ -109,7 +109,7 @@ class OpenSongImport(SongImport):
|
|||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
|
||||
for filename in self.import_source:
|
||||
if self.stop_import_flag:
|
||||
|
|
|
@ -85,7 +85,7 @@ class PowerSongImport(SongImport):
|
|||
return True
|
||||
return False
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
"""
|
||||
Receive either a list of files or a folder (unicode) to import.
|
||||
"""
|
||||
|
|
|
@ -61,6 +61,7 @@ try:
|
|||
except ImportError:
|
||||
ITALIC = 2
|
||||
|
||||
|
||||
class SofImport(OooImport):
|
||||
"""
|
||||
Import songs provided on disks with the Songs of Fellowship music books
|
||||
|
@ -85,7 +86,7 @@ class SofImport(OooImport):
|
|||
OooImport.__init__(self, manager, **kwargs)
|
||||
self.song = False
|
||||
|
||||
def processOooDocument(self):
|
||||
def process_ooo_document(self):
|
||||
"""
|
||||
Handle the import process for SoF files.
|
||||
"""
|
||||
|
@ -108,7 +109,7 @@ class SofImport(OooImport):
|
|||
except RuntimeException as exc:
|
||||
log.exception('Error processing file: %s', exc)
|
||||
if not self.finish():
|
||||
self.log_error(self.filepath)
|
||||
self.log_error(self.file_path)
|
||||
|
||||
def processParagraph(self, paragraph):
|
||||
"""
|
||||
|
|
|
@ -98,7 +98,7 @@ class SongBeamerImport(SongImport):
|
|||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
"""
|
||||
Receive a single file or a list of files to import.
|
||||
"""
|
||||
|
|
|
@ -154,12 +154,22 @@ class SongImport(QtCore.QObject):
|
|||
return text
|
||||
|
||||
def process_song_text(self, text):
|
||||
"""
|
||||
Process the song text from import
|
||||
|
||||
:param text: Some text
|
||||
"""
|
||||
verse_texts = text.split('\n\n')
|
||||
for verse_text in verse_texts:
|
||||
if verse_text.strip() != '':
|
||||
self.process_verse_text(verse_text.strip())
|
||||
|
||||
def process_verse_text(self, text):
|
||||
"""
|
||||
Process the song verse text from import
|
||||
|
||||
:param text: Some text
|
||||
"""
|
||||
lines = text.split('\n')
|
||||
if text.lower().find(self.copyright_string) >= 0 or text.find(str(SongStrings.CopyrightSymbol)) >= 0:
|
||||
copyright_found = False
|
||||
|
@ -190,9 +200,8 @@ class SongImport(QtCore.QObject):
|
|||
|
||||
def parse_author(self, text):
|
||||
"""
|
||||
Add the author. OpenLP stores them individually so split by 'and', '&'
|
||||
and comma. However need to check for 'Mr and Mrs Smith' and turn it to
|
||||
'Mr Smith' and 'Mrs Smith'.
|
||||
Add the author. OpenLP stores them individually so split by 'and', '&' and comma. However need to check
|
||||
for 'Mr and Mrs Smith' and turn it to 'Mr Smith' and 'Mrs Smith'.
|
||||
"""
|
||||
for author in text.split(','):
|
||||
authors = author.split('&')
|
||||
|
@ -227,16 +236,10 @@ class SongImport(QtCore.QObject):
|
|||
attempt to detect duplicates. In this case it will just add to the verse
|
||||
order.
|
||||
|
||||
``verse_text``
|
||||
The text of the verse.
|
||||
|
||||
``verse_def``
|
||||
The verse tag can be v1/c1/b etc, or 'v' and 'c' (will count the
|
||||
:param verse_text: The text of the verse.
|
||||
:param verse_def: The verse tag can be v1/c1/b etc, or 'v' and 'c' (will count the
|
||||
verses/choruses itself) or None, where it will assume verse.
|
||||
|
||||
``lang``
|
||||
The language code (ISO-639) of the verse, for example *en* or *de*.
|
||||
|
||||
:param lang: The language code (ISO-639) of the verse, for example *en* or *de*.
|
||||
"""
|
||||
for (old_verse_def, old_verse, old_lang) in self.verses:
|
||||
if old_verse.strip() == verse_text.strip():
|
||||
|
@ -317,24 +320,24 @@ class SongImport(QtCore.QObject):
|
|||
song.comments = self.comments
|
||||
song.theme_name = self.theme_name
|
||||
song.ccli_number = self.ccli_number
|
||||
for authortext in self.authors:
|
||||
author = self.manager.get_object_filtered(Author, Author.display_name == authortext)
|
||||
for author_text in self.authors:
|
||||
author = self.manager.get_object_filtered(Author, Author.display_name == author_text)
|
||||
if not author:
|
||||
author = Author.populate(display_name=authortext,
|
||||
last_name=authortext.split(' ')[-1],
|
||||
first_name=' '.join(authortext.split(' ')[:-1]))
|
||||
author = Author.populate(display_name=author_text,
|
||||
last_name=author_text.split(' ')[-1],
|
||||
first_name=' '.join(author_text.split(' ')[:-1]))
|
||||
song.authors.append(author)
|
||||
if self.song_book_name:
|
||||
song_book = self.manager.get_object_filtered(Book, Book.name == self.song_book_name)
|
||||
if song_book is None:
|
||||
song_book = Book.populate(name=self.song_book_name, publisher=self.song_book_pub)
|
||||
song.book = song_book
|
||||
for topictext in self.topics:
|
||||
if not topictext:
|
||||
for topic_text in self.topics:
|
||||
if not topic_text:
|
||||
continue
|
||||
topic = self.manager.get_object_filtered(Topic, Topic.name == topictext)
|
||||
topic = self.manager.get_object_filtered(Topic, Topic.name == topic_text)
|
||||
if topic is None:
|
||||
topic = Topic.populate(name=topictext)
|
||||
topic = Topic.populate(name=topic_text)
|
||||
song.topics.append(topic)
|
||||
# We need to save the song now, before adding the media files, so that
|
||||
# we know where to save the media files to.
|
||||
|
@ -357,14 +360,14 @@ class SongImport(QtCore.QObject):
|
|||
This method copies the media file to the correct location and returns
|
||||
the new file location.
|
||||
|
||||
``filename``
|
||||
The file to copy.
|
||||
:param song_id:
|
||||
:param filename: The file to copy.
|
||||
"""
|
||||
if not hasattr(self, 'save_path'):
|
||||
self.save_path = os.path.join(AppLocation.get_section_data_path(self.import_wizard.plugin.name),
|
||||
'audio', str(song_id))
|
||||
'audio', str(song_id))
|
||||
check_directory_exists(self.save_path)
|
||||
if not filename.startswith(self.save_path):
|
||||
oldfile, filename = filename, os.path.join(self.save_path, os.path.split(filename)[1])
|
||||
shutil.copyfile(oldfile, filename)
|
||||
old_file, filename = filename, os.path.join(self.save_path, os.path.split(filename)[1])
|
||||
shutil.copyfile(old_file, filename)
|
||||
return filename
|
||||
|
|
|
@ -74,7 +74,7 @@ class SongProImport(SongImport):
|
|||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
"""
|
||||
Receive a single file or a list of files to import.
|
||||
"""
|
||||
|
|
|
@ -94,7 +94,7 @@ class SongShowPlusImport(SongImport):
|
|||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
"""
|
||||
Receive a single file or a list of files to import.
|
||||
"""
|
||||
|
|
|
@ -62,7 +62,7 @@ class SundayPlusImport(SongImport):
|
|||
SongImport.__init__(self, manager, **kwargs)
|
||||
self.encoding = 'us-ascii'
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
|
||||
for filename in self.import_source:
|
||||
if self.stop_import_flag:
|
||||
|
|
|
@ -51,7 +51,7 @@ class WorshipCenterProImport(SongImport):
|
|||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
"""
|
||||
Receive a single file to import.
|
||||
"""
|
||||
|
|
|
@ -101,7 +101,7 @@ class WowImport(SongImport):
|
|||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
"""
|
||||
Receive a single file or a list of files to import.
|
||||
"""
|
||||
|
|
|
@ -78,7 +78,7 @@ class ZionWorxImport(SongImport):
|
|||
* Note: This is the default format of the Python ``csv`` module.
|
||||
|
||||
"""
|
||||
def doImport(self):
|
||||
def do_import(self):
|
||||
"""
|
||||
Receive a CSV file (from a ZionWorx database dump) to import.
|
||||
"""
|
||||
|
|
|
@ -289,7 +289,7 @@ class SongsPlugin(Plugin):
|
|||
self.application.process_events()
|
||||
for db in song_dbs:
|
||||
importer = OpenLPSongImport(self.manager, filename=db)
|
||||
importer.doImport(progress)
|
||||
importer.do_import(progress)
|
||||
self.application.process_events()
|
||||
progress.setValue(song_count)
|
||||
self.media_item.on_search_text_button_clicked()
|
||||
|
|
|
@ -154,13 +154,13 @@ class TestEasyWorshipSongImport(TestCase):
|
|||
|
||||
def find_field_exists_test(self):
|
||||
"""
|
||||
Test finding an existing field in a given list using the :mod:`findField`
|
||||
Test finding an existing field in a given list using the :mod:`find_field`
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions.
|
||||
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
|
||||
mocked_manager = MagicMock()
|
||||
importer = EasyWorshipSongImport(mocked_manager)
|
||||
importer.fieldDescs = TEST_FIELD_DESCS
|
||||
importer.field_descriptions = TEST_FIELD_DESCS
|
||||
|
||||
# WHEN: Called with a field name that exists
|
||||
existing_fields = ['Title', 'Text Percentage Bottom', 'RecID', 'Default Background', 'Words',
|
||||
|
@ -168,28 +168,28 @@ class TestEasyWorshipSongImport(TestCase):
|
|||
for field_name in existing_fields:
|
||||
|
||||
# THEN: The item corresponding the index returned should have the same name attribute
|
||||
self.assertEquals(importer.fieldDescs[importer.findField(field_name)].name, field_name)
|
||||
self.assertEquals(importer.field_descriptions[importer.find_field(field_name)].name, field_name)
|
||||
|
||||
def find_non_existing_field_test(self):
|
||||
"""
|
||||
Test finding an non-existing field in a given list using the :mod:`findField`
|
||||
Test finding an non-existing field in a given list using the :mod:`find_field`
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions
|
||||
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
|
||||
mocked_manager = MagicMock()
|
||||
importer = EasyWorshipSongImport(mocked_manager)
|
||||
importer.fieldDescs = TEST_FIELD_DESCS
|
||||
importer.field_descriptions = TEST_FIELD_DESCS
|
||||
|
||||
# WHEN: Called with a field name that does not exist
|
||||
non_existing_fields = ['BK Gradient Shading', 'BK Gradient Variant', 'Favorite', 'Copyright']
|
||||
for field_name in non_existing_fields:
|
||||
|
||||
# THEN: The importer object should not be None
|
||||
self.assertRaises(IndexError, importer.findField, field_name)
|
||||
self.assertRaises(IndexError, importer.find_field, field_name)
|
||||
|
||||
def set_record_struct_test(self):
|
||||
"""
|
||||
Test the :mod:`setRecordStruct` module
|
||||
Test the :mod:`set_record_struct` module
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out struct class, and a mocked out "manager" and a list of
|
||||
# field descriptions
|
||||
|
@ -198,17 +198,17 @@ class TestEasyWorshipSongImport(TestCase):
|
|||
mocked_manager = MagicMock()
|
||||
importer = EasyWorshipSongImport(mocked_manager)
|
||||
|
||||
# WHEN: setRecordStruct is called with a list of field descriptions
|
||||
return_value = importer.setRecordStruct(TEST_FIELD_DESCS)
|
||||
# WHEN: set_record_struct is called with a list of field descriptions
|
||||
return_value = importer.set_record_struct(TEST_FIELD_DESCS)
|
||||
|
||||
# THEN: setRecordStruct should return None and Struct should be called with a value representing
|
||||
# THEN: set_record_struct should return None and Struct should be called with a value representing
|
||||
# the list of field descriptions
|
||||
self.assertIsNone(return_value, 'setRecordStruct should return None')
|
||||
self.assertIsNone(return_value, 'set_record_struct should return None')
|
||||
mocked_struct.Struct.assert_called_with('>50sHIB250s250s10sQ')
|
||||
|
||||
def get_field_test(self):
|
||||
"""
|
||||
Test the :mod:`getField` module
|
||||
Test the :mod:`get_field` module
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out "manager", an encoding and some test data and known results
|
||||
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
|
||||
|
@ -216,20 +216,20 @@ class TestEasyWorshipSongImport(TestCase):
|
|||
importer = EasyWorshipSongImport(mocked_manager)
|
||||
importer.encoding = TEST_DATA_ENCODING
|
||||
importer.fields = TEST_FIELDS
|
||||
importer.fieldDescs = TEST_FIELD_DESCS
|
||||
importer.field_descriptions = TEST_FIELD_DESCS
|
||||
field_results = [(0, b'A Heart Like Thine'), (1, 100), (2, 102), (3, True), (6, None), (7, None)]
|
||||
|
||||
# WHEN: Called with test data
|
||||
for field_index, result in field_results:
|
||||
return_value = importer.getField(field_index)
|
||||
return_value = importer.get_field(field_index)
|
||||
|
||||
# THEN: getField should return the known results
|
||||
# THEN: get_field should return the known results
|
||||
self.assertEquals(return_value, result,
|
||||
'getField should return "%s" when called with "%s"' % (result, TEST_FIELDS[field_index]))
|
||||
'get_field should return "%s" when called with "%s"' % (result, TEST_FIELDS[field_index]))
|
||||
|
||||
def get_memo_field_test(self):
|
||||
"""
|
||||
Test the :mod:`getField` module
|
||||
Test the :mod:`get_field` module
|
||||
"""
|
||||
for test_results in GET_MEMO_FIELD_TEST_RESULTS:
|
||||
# GIVEN: A mocked out SongImport class, a mocked out "manager", a mocked out memo_file and an encoding
|
||||
|
@ -237,20 +237,20 @@ class TestEasyWorshipSongImport(TestCase):
|
|||
mocked_manager = MagicMock()
|
||||
mocked_memo_file = MagicMock()
|
||||
importer = EasyWorshipSongImport(mocked_manager)
|
||||
importer.memoFile = mocked_memo_file
|
||||
importer.memo_file = mocked_memo_file
|
||||
importer.encoding = TEST_DATA_ENCODING
|
||||
|
||||
# WHEN: Supplied with test fields and test field descriptions
|
||||
importer.fields = TEST_FIELDS
|
||||
importer.fieldDescs = TEST_FIELD_DESCS
|
||||
importer.field_descriptions = TEST_FIELD_DESCS
|
||||
field_index = test_results[0]
|
||||
mocked_memo_file.read.return_value = test_results[1]
|
||||
get_field_result = test_results[2]['return']
|
||||
get_field_read_calls = test_results[2]['read']
|
||||
get_field_seek_calls = test_results[2]['seek']
|
||||
|
||||
# THEN: getField should return the appropriate value with the appropriate mocked objects being called
|
||||
self.assertEquals(importer.getField(field_index), get_field_result)
|
||||
# THEN: get_field should return the appropriate value with the appropriate mocked objects being called
|
||||
self.assertEquals(importer.get_field(field_index), get_field_result)
|
||||
for call in get_field_read_calls:
|
||||
mocked_memo_file.read.assert_any_call(call)
|
||||
for call in get_field_seek_calls:
|
||||
|
@ -261,7 +261,7 @@ class TestEasyWorshipSongImport(TestCase):
|
|||
|
||||
def do_import_source_test(self):
|
||||
"""
|
||||
Test the :mod:`doImport` module opens the correct files
|
||||
Test the :mod:`do_import` module opens the correct files
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out "manager"
|
||||
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
|
||||
|
@ -273,14 +273,14 @@ class TestEasyWorshipSongImport(TestCase):
|
|||
# WHEN: Supplied with an import source
|
||||
importer.import_source = 'Songs.DB'
|
||||
|
||||
# THEN: doImport should return None having called os.path.isfile
|
||||
self.assertIsNone(importer.doImport(), 'doImport should return None')
|
||||
# THEN: do_import should return None having called os.path.isfile
|
||||
self.assertIsNone(importer.do_import(), 'do_import should return None')
|
||||
mocked_os_path.isfile.assert_any_call('Songs.DB')
|
||||
mocked_os_path.isfile.assert_any_call('Songs.MB')
|
||||
|
||||
def do_import_database_validity_test(self):
|
||||
"""
|
||||
Test the :mod:`doImport` module handles invalid database files correctly
|
||||
Test the :mod:`do_import` module handles invalid database files correctly
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, os.path and a mocked out "manager"
|
||||
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
|
||||
|
@ -293,13 +293,13 @@ class TestEasyWorshipSongImport(TestCase):
|
|||
# WHEN: DB file size is less than 0x800
|
||||
mocked_os_path.getsize.return_value = 0x7FF
|
||||
|
||||
# THEN: doImport should return None having called os.path.isfile
|
||||
self.assertIsNone(importer.doImport(), 'doImport should return None when db_size is less than 0x800')
|
||||
# THEN: do_import should return None having called os.path.isfile
|
||||
self.assertIsNone(importer.do_import(), 'do_import should return None when db_size is less than 0x800')
|
||||
mocked_os_path.getsize.assert_any_call('Songs.DB')
|
||||
|
||||
def do_import_memo_validty_test(self):
|
||||
"""
|
||||
Test the :mod:`doImport` module handles invalid memo files correctly
|
||||
Test the :mod:`do_import` module handles invalid memo files correctly
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out "manager"
|
||||
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
|
||||
|
@ -316,9 +316,9 @@ class TestEasyWorshipSongImport(TestCase):
|
|||
struct_unpack_return_values = [(0, 0x700, 2, 0, 0), (0, 0x800, 0, 0, 0), (0, 0x800, 5, 0, 0)]
|
||||
mocked_struct.unpack.side_effect = struct_unpack_return_values
|
||||
|
||||
# THEN: doImport should return None having called closed the open files db and memo files.
|
||||
# THEN: do_import should return None having called closed the open files db and memo files.
|
||||
for effect in struct_unpack_return_values:
|
||||
self.assertIsNone(importer.doImport(), 'doImport should return None when db_size is less than 0x800')
|
||||
self.assertIsNone(importer.do_import(), 'do_import should return None when db_size is less than 0x800')
|
||||
self.assertEqual(mocked_open().close.call_count, 2,
|
||||
'The open db and memo files should have been closed')
|
||||
mocked_open().close.reset_mock()
|
||||
|
@ -326,7 +326,7 @@ class TestEasyWorshipSongImport(TestCase):
|
|||
|
||||
def code_page_to_encoding_test(self):
|
||||
"""
|
||||
Test the :mod:`doImport` converts the code page to the encoding correctly
|
||||
Test the :mod:`do_import` converts the code page to the encoding correctly
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, a mocked out "manager"
|
||||
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
|
||||
|
@ -345,8 +345,8 @@ class TestEasyWorshipSongImport(TestCase):
|
|||
mocked_struct.unpack.side_effect = struct_unpack_return_values
|
||||
mocked_retrieve_windows_encoding.return_value = False
|
||||
|
||||
# THEN: doImport should return None having called retrieve_windows_encoding with the correct encoding.
|
||||
self.assertIsNone(importer.doImport(), 'doImport should return None when db_size is less than 0x800')
|
||||
# THEN: do_import should return None having called retrieve_windows_encoding with the correct encoding.
|
||||
self.assertIsNone(importer.do_import(), 'do_import should return None when db_size is less than 0x800')
|
||||
mocked_retrieve_windows_encoding.assert_call(encoding)
|
||||
|
||||
def file_import_test(self):
|
||||
|
@ -378,9 +378,9 @@ class TestEasyWorshipSongImport(TestCase):
|
|||
# WHEN: Importing each file
|
||||
importer.import_source = os.path.join(TEST_PATH, 'Songs.DB')
|
||||
|
||||
# THEN: doImport should return none, the song data should be as expected, and finish should have been
|
||||
# THEN: do_import should return none, the song data should be as expected, and finish should have been
|
||||
# called.
|
||||
self.assertIsNone(importer.doImport(), 'doImport should return None when it has completed')
|
||||
self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed')
|
||||
for song_data in SONG_TEST_DATA:
|
||||
title = song_data['title']
|
||||
author_calls = song_data['authors']
|
||||
|
|
|
@ -73,7 +73,7 @@ class TestSongBeamerImport(TestCase):
|
|||
|
||||
def invalid_import_source_test(self):
|
||||
"""
|
||||
Test SongBeamerImport.doImport handles different invalid import_source values
|
||||
Test SongBeamerImport.do_import handles different invalid import_source values
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
|
||||
with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'):
|
||||
|
@ -87,14 +87,14 @@ class TestSongBeamerImport(TestCase):
|
|||
for source in ['not a list', 0]:
|
||||
importer.import_source = source
|
||||
|
||||
# THEN: doImport should return none and the progress bar maximum should not be set.
|
||||
self.assertIsNone(importer.doImport(), 'doImport should return None when import_source is not a list')
|
||||
# THEN: do_import should return none and the progress bar maximum should not be set.
|
||||
self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list')
|
||||
self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False,
|
||||
'setMaxium on import_wizard.progress_bar should not have been called')
|
||||
|
||||
def valid_import_source_test(self):
|
||||
"""
|
||||
Test SongBeamerImport.doImport handles different invalid import_source values
|
||||
Test SongBeamerImport.do_import handles different invalid import_source values
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
|
||||
with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'):
|
||||
|
@ -107,10 +107,10 @@ class TestSongBeamerImport(TestCase):
|
|||
# WHEN: Import source is a list
|
||||
importer.import_source = ['List', 'of', 'files']
|
||||
|
||||
# THEN: doImport should return none and the progress bar setMaximum should be called with the length of
|
||||
# THEN: do_import should return none and the progress bar setMaximum should be called with the length of
|
||||
# import_source.
|
||||
self.assertIsNone(importer.doImport(),
|
||||
'doImport should return None when import_source is a list and stop_import_flag is True')
|
||||
self.assertIsNone(importer.do_import(),
|
||||
'do_import should return None when import_source is a list and stop_import_flag is True')
|
||||
mocked_import_wizard.progress_bar.setMaximum.assert_called_with(len(importer.import_source))
|
||||
|
||||
def file_import_test(self):
|
||||
|
@ -140,9 +140,9 @@ class TestSongBeamerImport(TestCase):
|
|||
song_book_name = SONG_TEST_DATA[song_file]['song_book_name']
|
||||
song_number = SONG_TEST_DATA[song_file]['song_number']
|
||||
|
||||
# THEN: doImport should return none, the song data should be as expected, and finish should have been
|
||||
# THEN: do_import should return none, the song data should be as expected, and finish should have been
|
||||
# called.
|
||||
self.assertIsNone(importer.doImport(), 'doImport should return None when it has completed')
|
||||
self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed')
|
||||
self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (song_file, title))
|
||||
for verse_text, verse_tag in add_verse_calls:
|
||||
mocked_add_verse.assert_any_call(verse_text, verse_tag)
|
||||
|
|
|
@ -43,6 +43,7 @@ TEST_PATH = os.path.abspath(
|
|||
|
||||
|
||||
class TestSongShowPlusFileImport(SongImportTestHelper):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.importer_class_name = 'SongShowPlusImport'
|
||||
self.importer_module_name = 'songshowplusimport'
|
||||
|
@ -75,7 +76,7 @@ class TestSongShowPlusImport(TestCase):
|
|||
|
||||
def invalid_import_source_test(self):
|
||||
"""
|
||||
Test SongShowPlusImport.doImport handles different invalid import_source values
|
||||
Test SongShowPlusImport.do_import handles different invalid import_source values
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
|
||||
with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'):
|
||||
|
@ -89,14 +90,14 @@ class TestSongShowPlusImport(TestCase):
|
|||
for source in ['not a list', 0]:
|
||||
importer.import_source = source
|
||||
|
||||
# THEN: doImport should return none and the progress bar maximum should not be set.
|
||||
self.assertIsNone(importer.doImport(), 'doImport should return None when import_source is not a list')
|
||||
# THEN: do_import should return none and the progress bar maximum should not be set.
|
||||
self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list')
|
||||
self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False,
|
||||
'setMaximum on import_wizard.progress_bar should not have been called')
|
||||
|
||||
def valid_import_source_test(self):
|
||||
"""
|
||||
Test SongShowPlusImport.doImport handles different invalid import_source values
|
||||
Test SongShowPlusImport.do_import handles different invalid import_source values
|
||||
"""
|
||||
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
|
||||
with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'):
|
||||
|
@ -109,10 +110,10 @@ class TestSongShowPlusImport(TestCase):
|
|||
# WHEN: Import source is a list
|
||||
importer.import_source = ['List', 'of', 'files']
|
||||
|
||||
# THEN: doImport should return none and the progress bar setMaximum should be called with the length of
|
||||
# THEN: do_import should return none and the progress bar setMaximum should be called with the length of
|
||||
# import_source.
|
||||
self.assertIsNone(importer.doImport(),
|
||||
'doImport should return None when import_source is a list and stop_import_flag is True')
|
||||
self.assertIsNone(importer.do_import(),
|
||||
'do_import should return None when import_source is a list and stop_import_flag is True')
|
||||
mocked_import_wizard.progress_bar.setMaximum.assert_called_with(len(importer.import_source))
|
||||
|
||||
def to_openlp_verse_tag_test(self):
|
||||
|
|
|
@ -165,12 +165,12 @@ class TestWorshipCenterProSongImport(TestCase):
|
|||
pyodbc_errors = [pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError]
|
||||
mocked_pyodbc_connect.side_effect = pyodbc_errors
|
||||
|
||||
# WHEN: Calling the doImport method
|
||||
# WHEN: Calling the do_import method
|
||||
for effect in pyodbc_errors:
|
||||
return_value = importer.doImport()
|
||||
return_value = importer.do_import()
|
||||
|
||||
# THEN: doImport should return None, and pyodbc, translate & log_error are called with known calls
|
||||
self.assertIsNone(return_value, 'doImport should return None when pyodbc raises an exception.')
|
||||
# THEN: do_import should return None, and pyodbc, translate & log_error are called with known calls
|
||||
self.assertIsNone(return_value, 'do_import should return None when pyodbc raises an exception.')
|
||||
mocked_pyodbc_connect.assert_called_with( 'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source')
|
||||
mocked_translate.assert_called_with('SongsPlugin.WorshipCenterProImport',
|
||||
'Unable to connect the WorshipCenter Pro database.')
|
||||
|
@ -198,13 +198,13 @@ class TestWorshipCenterProSongImport(TestCase):
|
|||
importer.stop_import_flag = False
|
||||
importer.finish = mocked_finish
|
||||
|
||||
# WHEN: Calling the doImport method
|
||||
return_value = importer.doImport()
|
||||
# WHEN: Calling the do_import method
|
||||
return_value = importer.do_import()
|
||||
|
||||
|
||||
# THEN: doImport should return None, and pyodbc, import_wizard, importer.title and add_verse are called with
|
||||
# THEN: do_import should return None, and pyodbc, import_wizard, importer.title and add_verse are called with
|
||||
# known calls
|
||||
self.assertIsNone(return_value, 'doImport should return None when pyodbc raises an exception.')
|
||||
self.assertIsNone(return_value, 'do_import should return None when pyodbc raises an exception.')
|
||||
mocked_pyodbc.connect.assert_called_with('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=import_source')
|
||||
mocked_pyodbc.connect().cursor.assert_any_call()
|
||||
mocked_pyodbc.connect().cursor().execute.assert_called_with('SELECT ID, Field, Value FROM __SONGDATA')
|
||||
|
|
|
@ -107,9 +107,9 @@ class SongImportTestHelper(TestCase):
|
|||
topics = self._get_data(result_data, 'topics')
|
||||
verse_order_list = self._get_data(result_data, 'verse_order_list')
|
||||
|
||||
# THEN: doImport should return none, the song data should be as expected, and finish should have been
|
||||
# THEN: do_import should return none, the song data should be as expected, and finish should have been
|
||||
# called.
|
||||
self.assertIsNone(importer.doImport(), 'doImport should return None when it has completed')
|
||||
self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed')
|
||||
self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (source_file_name, title))
|
||||
for author in author_calls:
|
||||
self.mocked_parse_author.assert_any_call(author)
|
||||
|
|
Loading…
Reference in New Issue