More updates

This commit is contained in:
Tim Bentley 2014-03-06 22:05:15 +00:00
parent 3ae188c287
commit a0a2e5efbc
8 changed files with 177 additions and 187 deletions

View File

@ -107,7 +107,7 @@ class SongFormat(object):
Name of the format, e.g. ``'OpenLyrics'``
``'prefix'``
Prefix for Qt objects. Use mixedCase, e.g. ``'openLyrics'``
Prefix for Qt objects. Use mixedCase, e.g. ``'open_lyrics'``
See ``SongImportForm.add_file_select_item()``
Optional attributes for each song format:
@ -185,7 +185,7 @@ class SongFormat(object):
OpenLyrics: {
'class': OpenLyricsImport,
'name': 'OpenLyrics',
'prefix': 'openLyrics',
'prefix': 'open_lyrics',
'filter': '%s (*.xml)' % translate('SongsPlugin.ImportWizardForm', 'OpenLyrics Files'),
'comboBoxText': translate('SongsPlugin.ImportWizardForm', 'OpenLyrics or OpenLP 2.0 Exported Song')
},

View File

@ -97,21 +97,21 @@ class SongMediaItem(MediaManagerItem):
def add_end_header_bar(self):
self.toolbar.addSeparator()
## Song Maintenance Button ##
self.maintenanceAction = self.toolbar.add_toolbar_action('maintenanceAction',
icon=':/songs/song_maintenance.png',
triggers=self.on_song_maintenance_click)
self.maintenance_action = self.toolbar.add_toolbar_action('maintenance_action',
icon=':/songs/song_maintenance.png',
triggers=self.on_song_maintenance_click)
self.add_search_to_toolbar()
# Signals and slots
Registry().register_function('songs_load_list', self.on_song_list_load)
Registry().register_function('songs_preview', self.on_preview_click)
QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL('cleared()'), self.on_clear_text_button_click)
QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL('searchTypeChanged(int)'),
self.on_search_text_button_clicked)
QtCore.QObject.connect(
self.search_text_edit, QtCore.SIGNAL('searchTypeChanged(int)'), self.on_search_text_button_clicked)
def add_custom_context_actions(self):
create_widget_action(self.list_view, separator=True)
create_widget_action(self.list_view,
text=translate('OpenLP.MediaManagerItem', '&Clone'), icon=':/general/general_clone.png',
create_widget_action(
self.list_view, text=translate('OpenLP.MediaManagerItem', '&Clone'), icon=':/general/general_clone.png',
triggers=self.on_clone_click)
def on_focus(self):
@ -123,15 +123,15 @@ class SongMediaItem(MediaManagerItem):
"""
log.debug('config_updated')
self.search_as_you_type = Settings().value(self.settings_section + '/search as type')
self.updateServiceOnEdit = Settings().value(self.settings_section + '/update service on edit')
self.addSongFromService = Settings().value(self.settings_section + '/add song from service',)
self.update_service_on_edit = Settings().value(self.settings_section + '/update service on edit')
self.add_song_from_service = Settings().value(self.settings_section + '/add song from service',)
def retranslateUi(self):
self.search_text_label.setText('%s:' % UiStrings().Search)
self.search_text_button.setText(UiStrings().Search)
self.maintenanceAction.setText(SongStrings.SongMaintenance)
self.maintenanceAction.setToolTip(translate('SongsPlugin.MediaItem',
'Maintain the lists of authors, topics and books.'))
self.maintenance_action.setText(SongStrings.SongMaintenance)
self.maintenance_action.setToolTip(translate('SongsPlugin.MediaItem',
'Maintain the lists of authors, topics and books.'))
def initialise(self):
"""
@ -139,7 +139,7 @@ class SongMediaItem(MediaManagerItem):
"""
self.song_maintenance_form = SongMaintenanceForm(self.plugin.manager, self)
self.edit_song_form = EditSongForm(self, self.main_window, self.plugin.manager)
self.openLyrics = OpenLyrics(self.plugin.manager)
self.open_lyrics = OpenLyrics(self.plugin.manager)
self.search_text_edit.set_search_types([
(SongSearch.Entire, ':/songs/song_search_all.png',
translate('SongsPlugin.MediaItem', 'Entire Song'),
@ -154,8 +154,7 @@ class SongMediaItem(MediaManagerItem):
translate('SongsPlugin.MediaItem', 'Search Authors...')),
(SongSearch.Books, ':/songs/song_book_edit.png', SongStrings.SongBooks,
translate('SongsPlugin.MediaItem', 'Search Song Books...')),
(SongSearch.Themes, ':/slides/slide_theme.png',
UiStrings().Themes, UiStrings().SearchThemes)
(SongSearch.Themes, ':/slides/slide_theme.png', UiStrings().Themes, UiStrings().SearchThemes)
])
self.search_text_edit.set_current_search_type(Settings().value('%s/last search type' % self.settings_section))
self.config_update()
@ -172,64 +171,65 @@ class SongMediaItem(MediaManagerItem):
self.display_results_song(search_results)
elif search_type == SongSearch.Titles:
log.debug('Titles Search')
search_results = self.plugin.manager.get_all_objects(Song,
Song.search_title.like('%' + clean_string(search_keywords) + '%'))
search_string = '%' + clean_string(search_keywords) + '%'
search_results = self.plugin.manager.get_all_objects(Song, Song.search_title.like(search_string))
self.display_results_song(search_results)
elif search_type == SongSearch.Lyrics:
log.debug('Lyrics Search')
search_results = self.plugin.manager.get_all_objects(Song,
Song.search_lyrics.like('%' + clean_string(search_keywords) + '%'))
search_string = '%' + clean_string(search_keywords) + '%'
search_results = self.plugin.manager.get_all_objects(Song, Song.search_lyrics.like(search_string))
self.display_results_song(search_results)
elif search_type == SongSearch.Authors:
log.debug('Authors Search')
search_results = self.plugin.manager.get_all_objects(Author,
Author.display_name.like('%' + search_keywords + '%'), Author.display_name.asc())
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(
Author, Author.display_name.like(search_string), Author.display_name.asc())
self.display_results_author(search_results)
elif search_type == SongSearch.Books:
log.debug('Books Search')
search_results = self.plugin.manager.get_all_objects(Book,
Book.name.like('%' + search_keywords + '%'), Book.name.asc())
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(Book, Book.name.like(search_string), Book.name.asc())
song_number = False
if not search_results:
search_keywords = search_keywords.rpartition(' ')
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(Book,
Book.name.like('%' + search_keywords[0] + '%'), Book.name.asc())
Book.name.like(search_string), Book.name.asc())
song_number = re.sub(r'[^0-9]', '', search_keywords[2])
self.display_results_book(search_results, song_number)
elif search_type == SongSearch.Themes:
log.debug('Theme Search')
search_results = self.plugin.manager.get_all_objects(Song,
Song.theme_name.like('%' + search_keywords + '%'))
search_string = '%' + search_keywords + '%'
search_results = self.plugin.manager.get_all_objects(Song, Song.theme_name.like(search_string))
self.display_results_song(search_results)
self.check_search_result()
def search_entire(self, search_keywords):
return self.plugin.manager.get_all_objects(Song,
or_(Song.search_title.like('%' + clean_string(search_keywords) + '%'),
Song.search_lyrics.like('%' + clean_string(search_keywords) + '%'),
Song.comments.like('%' + search_keywords.lower() + '%')))
search_string = '%' + clean_string(search_keywords) + '%'
return self.plugin.manager.get_all_objects(
Song, or_(Song.search_title.like(search_string), Song.search_lyrics.like(search_string),
Song.comments.like(search_string)))
def on_song_list_load(self):
"""
Handle the exit from the edit dialog and trigger remote updates
of songs
Handle the exit from the edit dialog and trigger remote updates of songs
"""
log.debug('on_song_list_load - start')
# Called to redisplay the song list screen edit from a search or from the exit of the Song edit dialog. If
# remote editing is active Trigger it and clean up so it will not update again. Push edits to the service
# manager to update items
if self.edit_item and self.updateServiceOnEdit and not self.remote_triggered:
if self.edit_item and self.update_service_on_edit and not self.remote_triggered:
item = self.build_service_item(self.edit_item)
self.service_manager.replace_service_item(item)
self.on_search_text_button_clicked()
log.debug('on_song_list_load - finished')
def display_results_song(self, searchresults):
def display_results_song(self, search_results):
log.debug('display results Song')
self.save_auto_select_id()
self.list_view.clear()
searchresults.sort(key=lambda song: song.sort_key)
for song in searchresults:
search_results.sort(key=lambda song: song.sort_key)
for song in search_results:
# Do not display temporary songs
if song.temporary:
continue
@ -244,10 +244,10 @@ class SongMediaItem(MediaManagerItem):
self.list_view.setCurrentItem(song_name)
self.auto_select_id = -1
def display_results_author(self, searchresults):
def display_results_author(self, search_results):
log.debug('display results Author')
self.list_view.clear()
for author in searchresults:
for author in search_results:
for song in author.songs:
# Do not display temporary songs
if song.temporary:
@ -257,12 +257,11 @@ class SongMediaItem(MediaManagerItem):
song_name.setData(QtCore.Qt.UserRole, song.id)
self.list_view.addItem(song_name)
def display_results_book(self, searchresults, song_number=False):
def display_results_book(self, search_results, song_number=False):
log.debug('display results Book')
self.list_view.clear()
for book in searchresults:
songs = sorted(book.songs, key=lambda song:
int(re.match(r'[0-9]+', '0' + song.song_number).group()))
for book in search_results:
songs = sorted(book.songs, key=lambda song: int(re.match(r'[0-9]+', '0' + song.song_number).group()))
for song in songs:
# Do not display temporary songs
if song.temporary:
@ -305,9 +304,9 @@ class SongMediaItem(MediaManagerItem):
Registry().execute('songs_load_list')
def on_export_click(self):
if not hasattr(self, 'exportWizard'):
self.exportWizard = SongExportForm(self, self.plugin)
self.exportWizard.exec_()
if not hasattr(self, 'export_wizard'):
self.export_wizard = SongExportForm(self, self.plugin)
self.export_wizard.exec_()
def on_new_click(self):
log.debug('on_new_click')
@ -362,10 +361,10 @@ class SongMediaItem(MediaManagerItem):
"""
if check_item_selected(self.list_view, UiStrings().SelectDelete):
items = self.list_view.selectedIndexes()
if QtGui.QMessageBox.question(self,
UiStrings().ConfirmDelete,
if QtGui.QMessageBox.question(
self, UiStrings().ConfirmDelete,
translate('SongsPlugin.MediaItem', 'Are you sure you want to delete the %n selected song(s)?', '',
QtCore.QCoreApplication.CodecForTr, len(items)),
QtCore.QCoreApplication.CodecForTr, len(items)),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No:
return
@ -388,17 +387,23 @@ class SongMediaItem(MediaManagerItem):
self.edit_item = self.list_view.currentItem()
item_id = self.edit_item.data(QtCore.Qt.UserRole)
old_song = self.plugin.manager.get_object(Song, item_id)
song_xml = self.openLyrics.song_to_xml(old_song)
new_song = self.openLyrics.xml_to_song(song_xml)
new_song.title = '%s <%s>' % (new_song.title,
translate('SongsPlugin.MediaItem', 'copy', 'For song cloning'))
song_xml = self.open_lyrics.song_to_xml(old_song)
new_song = self.open_lyrics.xml_to_song(song_xml)
new_song.title = '%s <%s>' % \
(new_song.title, translate('SongsPlugin.MediaItem', 'copy', 'For song cloning'))
self.plugin.manager.save_object(new_song)
self.on_song_list_load()
def generate_slide_data(self, service_item, item=None, xmlVersion=False,
remote=False, context=ServiceItemContext.Service):
def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
context=ServiceItemContext.Service):
"""
Generate the slide data. Needs to be implemented by the plugin.
:param service_item: The service item to be built on
:param item: The Song item to be used
:param xml_version: The xml version (not used)
:param remote: Triggered from remote
:param context: Why is it being generated
"""
log.debug('generate_slide_data: %s, %s, %s' % (service_item, item, self.remote_song))
item_id = self._get_id_of_item_to_generate(item, self.remote_song)
@ -411,50 +416,45 @@ class SongMediaItem(MediaManagerItem):
song = self.plugin.manager.get_object(Song, item_id)
service_item.theme = song.theme_name
service_item.edit_id = item_id
if song.lyrics.startswith('<?xml version='):
verse_list = SongXML().get_verses(song.lyrics)
# no verse list or only 1 space (in error)
verse_tags_translated = False
if VerseType.from_translated_string(str(verse_list[0][0]['type'])) is not None:
verse_tags_translated = True
if not song.verse_order.strip():
for verse in verse_list:
# We cannot use from_loose_input() here, because database is supposed to contain English lowercase
# singlechar tags.
verse_tag = verse[0]['type']
verse_index = None
if len(verse_tag) > 1:
verse_index = VerseType.from_translated_string(verse_tag)
if verse_index is None:
verse_index = VerseType.from_string(verse_tag, None)
verse_list = SongXML().get_verses(song.lyrics)
# no verse list or only 1 space (in error)
verse_tags_translated = False
if VerseType.from_translated_string(str(verse_list[0][0]['type'])) is not None:
verse_tags_translated = True
if not song.verse_order.strip():
for verse in verse_list:
# We cannot use from_loose_input() here, because database is supposed to contain English lowercase
# singlechar tags.
verse_tag = verse[0]['type']
verse_index = None
if len(verse_tag) > 1:
verse_index = VerseType.from_translated_string(verse_tag)
if verse_index is None:
verse_index = VerseType.from_tag(verse_tag)
verse_tag = VerseType.translated_tags[verse_index].upper()
verse_def = '%s%s' % (verse_tag, verse[0]['label'])
service_item.add_from_text(str(verse[1]), verse_def)
else:
# Loop through the verse list and expand the song accordingly.
for order in song.verse_order.lower().split():
if not order:
break
for verse in verse_list:
if verse[0]['type'][0].lower() == order[0] and (verse[0]['label'].lower() == order[1:] or \
not order[1:]):
if verse_tags_translated:
verse_index = VerseType.from_translated_tag(verse[0]['type'])
else:
verse_index = VerseType.from_tag(verse[0]['type'])
verse_tag = VerseType.translated_tags[verse_index]
verse_def = '%s%s' % (verse_tag, verse[0]['label'])
service_item.add_from_text(verse[1], verse_def)
verse_index = VerseType.from_string(verse_tag, None)
if verse_index is None:
verse_index = VerseType.from_tag(verse_tag)
verse_tag = VerseType.translated_tags[verse_index].upper()
verse_def = '%s%s' % (verse_tag, verse[0]['label'])
service_item.add_from_text(str(verse[1]), verse_def)
else:
verses = song.lyrics.split('\n\n')
for slide in verses:
service_item.add_from_text(str(slide))
# Loop through the verse list and expand the song accordingly.
for order in song.verse_order.lower().split():
if not order:
break
for verse in verse_list:
if verse[0]['type'][0].lower() == \
order[0] and (verse[0]['label'].lower() == order[1:] or not order[1:]):
if verse_tags_translated:
verse_index = VerseType.from_translated_tag(verse[0]['type'])
else:
verse_index = VerseType.from_tag(verse[0]['type'])
verse_tag = VerseType.translated_tags[verse_index]
verse_def = '%s%s' % (verse_tag, verse[0]['label'])
service_item.add_from_text(verse[1], verse_def)
service_item.title = song.title
author_list = self.generate_footer(service_item, song)
service_item.data_string = {'title': song.search_title, 'authors': ', '.join(author_list)}
service_item.xml_version = self.openLyrics.song_to_xml(song)
service_item.xml_version = self.open_lyrics.song_to_xml(song)
# Add the audio file to the service item.
if song.media_files:
service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
@ -466,11 +466,8 @@ class SongMediaItem(MediaManagerItem):
Generates the song footer based on a song and adds details to a service item.
author_list is only required for initial song generation.
``item``
The service item to be amended
``song``
The song to be used to generate the footer
:param item: The service item to be amended
:param song: The song to be used to generate the footer
"""
author_list = [str(author.display_name) for author in song.authors]
item.audit = [
@ -481,8 +478,8 @@ class SongMediaItem(MediaManagerItem):
item.raw_footer.append(create_separated_list(author_list))
item.raw_footer.append(song.copyright)
if Settings().value('core/ccli number'):
item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') +
Settings().value('core/ccli number'))
item.raw_footer.append(translate('SongsPlugin.MediaItem',
'CCLI License: ') + Settings().value('core/ccli number'))
return author_list
def service_load(self, item):
@ -500,8 +497,8 @@ class SongMediaItem(MediaManagerItem):
Song.search_title == (re.compile(r'\W+', re.UNICODE).sub(' ',
item.data_string['title'].strip()) + '@').strip().lower(), Song.search_title.asc())
else:
search_results = self.plugin.manager.get_all_objects(Song,
Song.search_title == item.data_string['title'], Song.search_title.asc())
search_results = self.plugin.manager.get_all_objects(
Song, Song.search_title == item.data_string['title'], Song.search_title.asc())
edit_id = 0
add_song = True
if search_results:
@ -521,16 +518,16 @@ class SongMediaItem(MediaManagerItem):
# If there's any backing tracks, copy them over.
if item.background_audio:
self._update_background_audio(song, item)
if add_song and self.addSongFromService:
song = self.openLyrics.xml_to_song(item.xml_version)
if add_song and self.add_song_from_service:
song = self.open_lyrics.xml_to_song(item.xml_version)
# If there's any backing tracks, copy them over.
if item.background_audio:
self._update_background_audio(song, item)
editId = song.id
edit_id = song.id
self.on_search_text_button_clicked()
elif add_song and not self.addSongFromService:
elif add_song and not self.add_song_from_service:
# Make sure we temporary import formatting tags.
song = self.openLyrics.xml_to_song(item.xml_version, True)
song = self.open_lyrics.xml_to_song(item.xml_version, True)
# If there's any backing tracks, copy them over.
if item.background_audio:
self._update_background_audio(song, item)
@ -540,9 +537,11 @@ class SongMediaItem(MediaManagerItem):
self.generate_footer(item, song)
return item
def search(self, string, showError):
def search(self, string, show_error):
"""
Search for some songs
:param string: The string to show
:param show_error: Is this an error?
"""
search_results = self.search_entire(string)
return [[song.id, song.title] for song in search_results]

View File

@ -37,6 +37,7 @@ from openlp.plugins.songs.lib.songimport import SongImport
VERSE_TAGS = ['V', 'C', 'B', 'O', 'P', 'I', 'E']
class MediaShoutImport(SongImport):
"""
The :class:`MediaShoutImport` class provides the ability to import the
@ -53,38 +54,34 @@ class MediaShoutImport(SongImport):
Receive a single file to import.
"""
try:
conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};'
'DBQ=%s;PWD=6NOZ4eHK7k' % self.import_source)
conn = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb)};DBQ=%s;PWD=6NOZ4eHK7k' %
self.import_source)
except:
# Unfortunately no specific exception type
self.log_error(self.import_source,
translate('SongsPlugin.MediaShoutImport', 'Unable to open the MediaShout database.'))
self.log_error(self.import_source, translate('SongsPlugin.MediaShoutImport',
'Unable to open the MediaShout database.'))
return
cursor = conn.cursor()
cursor.execute('SELECT Record, Title, Author, Copyright, '
'SongID, CCLI, Notes FROM Songs ORDER BY Title')
cursor.execute('SELECT Record, Title, Author, Copyright, SongID, CCLI, Notes FROM Songs ORDER BY Title')
songs = cursor.fetchall()
self.import_wizard.progress_bar.setMaximum(len(songs))
for song in songs:
if self.stop_import_flag:
break
cursor.execute('SELECT Type, Number, Text FROM Verses '
'WHERE Record = %s ORDER BY Type, Number' % song.Record)
cursor.execute('SELECT Type, Number, Text FROM Verses WHERE Record = %s ORDER BY Type, Number'
% song.Record)
verses = cursor.fetchall()
cursor.execute('SELECT Type, Number, POrder FROM PlayOrder '
'WHERE Record = %s ORDER BY POrder' % song.Record)
cursor.execute('SELECT Type, Number, POrder FROM PlayOrder WHERE Record = %s ORDER BY POrder' % song.Record)
verse_order = cursor.fetchall()
cursor.execute('SELECT Name FROM Themes INNER JOIN SongThemes '
'ON SongThemes.ThemeId = Themes.ThemeId '
'WHERE SongThemes.Record = %s' % song.Record)
cursor.execute('SELECT Name FROM Themes INNER JOIN SongThemes ON SongThemes.ThemeId = Themes.ThemeId '
'WHERE SongThemes.Record = %s' % song.Record)
topics = cursor.fetchall()
cursor.execute('SELECT Name FROM Groups INNER JOIN SongGroups '
'ON SongGroups.GroupId = Groups.GroupId '
'WHERE SongGroups.Record = %s' % song.Record)
cursor.execute('SELECT Name FROM Groups INNER JOIN SongGroups ON SongGroups.GroupId = Groups.GroupId '
'WHERE SongGroups.Record = %s' % song.Record)
topics += cursor.fetchall()
self.processSong(song, verses, verse_order, topics)
self.process_song(song, verses, verse_order, topics)
def processSong(self, song, verses, verse_order, topics):
def process_song(self, song, verses, verse_order, topics):
"""
Create the song, i.e. title, verse etc.
"""

View File

@ -45,6 +45,7 @@ from .songimport import SongImport
log = logging.getLogger(__name__)
class OpenLPSongImport(SongImport):
"""
The :class:`OpenLPSongImport` class provides OpenLP with the ability to
@ -54,20 +55,17 @@ class OpenLPSongImport(SongImport):
"""
Initialise the import.
``manager``
The song manager for the running OpenLP installation.
``source_db``
The database providing the data to import.
:param manager: The song manager for the running OpenLP installation.
:param kwargs: The database providing the data to import.
"""
SongImport.__init__(self, manager, **kwargs)
self.sourceSession = None
self.source_session = None
def do_import(self, progressDialog=None):
def do_import(self, progress_dialog=None):
"""
Run the import for an OpenLP version 2 song database.
``progressDialog``
``progress_dialog``
The QProgressDialog used when importing songs from the FRW.
"""
@ -77,28 +75,24 @@ class OpenLPSongImport(SongImport):
"""
pass
class OldBook(BaseModel):
"""
Book model
"""
pass
class OldMediaFile(BaseModel):
"""
MediaFile model
"""
pass
class OldSong(BaseModel):
"""
Song model
"""
pass
class OldTopic(BaseModel):
"""
Topic model
@ -107,15 +101,15 @@ class OpenLPSongImport(SongImport):
# Check the file type
if not self.import_source.endswith('.sqlite'):
self.log_error(self.import_source,
translate('SongsPlugin.OpenLPSongImport', 'Not a valid OpenLP 2.0 song database.'))
self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport',
'Not a valid OpenLP 2.0 song database.'))
return
self.import_source = 'sqlite:///%s' % self.import_source
# Load the db file
engine = create_engine(self.import_source)
source_meta = MetaData()
source_meta.reflect(engine)
self.sourceSession = scoped_session(sessionmaker(bind=engine))
self.source_session = scoped_session(sessionmaker(bind=engine))
if 'media_files' in list(source_meta.tables.keys()):
has_media_files = True
else:
@ -143,14 +137,13 @@ class OpenLPSongImport(SongImport):
}
if has_media_files:
if isinstance(source_media_files_songs_table, Table):
song_props['media_files'] = relation(OldMediaFile,
backref='songs',
secondary=source_media_files_songs_table)
song_props['media_files'] = relation(OldMediaFile, backref='songs',
secondary=source_media_files_songs_table)
else:
song_props['media_files'] = relation(OldMediaFile,
backref='songs',
foreign_keys=[source_media_files_table.c.song_id],
primaryjoin=source_songs_table.c.id == source_media_files_table.c.song_id)
song_props['media_files'] = \
relation(OldMediaFile, backref='songs',
foreign_keys=[source_media_files_table.c.song_id],
primaryjoin=source_songs_table.c.id == source_media_files_table.c.song_id)
try:
class_mapper(OldAuthor)
except UnmappedClassError:
@ -168,7 +161,7 @@ class OpenLPSongImport(SongImport):
except UnmappedClassError:
mapper(OldTopic, source_topics_table)
source_songs = self.sourceSession.query(OldSong).all()
source_songs = self.source_session.query(OldSong).all()
if self.import_wizard:
self.import_wizard.progress_bar.setMaximum(len(source_songs))
for song in source_songs:
@ -212,17 +205,17 @@ class OpenLPSongImport(SongImport):
if has_media_files:
if song.media_files:
for media_file in song.media_files:
existing_media_file = self.manager.get_object_filtered(MediaFile,
MediaFile.file_name == media_file.file_name)
existing_media_file = self.manager.get_object_filtered(
MediaFile, MediaFile.file_name == media_file.file_name)
if existing_media_file:
new_song.media_files.append(existing_media_file)
else:
new_song.media_files.append(MediaFile.populate(file_name=media_file.file_name))
clean_song(self.manager, new_song)
self.manager.save_object(new_song)
if progressDialog:
progressDialog.setValue(progressDialog.value() + 1)
progressDialog.setLabelText(WizardStrings.ImportingType % new_song.title)
if progress_dialog:
progress_dialog.setValue(progress_dialog.value() + 1)
progress_dialog.setLabelText(WizardStrings.ImportingType % new_song.title)
else:
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % new_song.title)
if self.stop_import_flag:

View File

@ -27,8 +27,8 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`openlyricsexport` module provides the functionality for exporting
songs from the database to the OpenLyrics format.
The :mod:`openlyricsexport` module provides the functionality for exporting songs from the database to the OpenLyrics
format.
"""
import logging
import os
@ -62,15 +62,15 @@ class OpenLyricsExport(object):
Export the songs.
"""
log.debug('started OpenLyricsExport')
openLyrics = OpenLyrics(self.manager)
open_lyrics = OpenLyrics(self.manager)
self.parent.progress_bar.setMaximum(len(self.songs))
for song in self.songs:
self.application.process_events()
if self.parent.stop_export_flag:
return False
self.parent.increment_progress_bar(translate('SongsPlugin.OpenLyricsExport', 'Exporting "%s"...') %
song.title)
xml = openLyrics.song_to_xml(song)
song.title)
xml = open_lyrics.song_to_xml(song)
tree = etree.ElementTree(etree.fromstring(xml.encode()))
filename = '%s (%s)' % (song.title, ', '.join([author.display_name for author in song.authors]))
filename = clean_filename(filename)
@ -78,8 +78,8 @@ class OpenLyricsExport(object):
filename = '%s.xml' % filename[0:250 - len(self.save_path)]
# Pass a file object, because lxml does not cope with some special
# characters in the path (see lp:757673 and lp:744337).
tree.write(open(os.path.join(self.save_path, filename), 'wb'),
encoding='utf-8', xml_declaration=True, pretty_print=True)
tree.write(open(os.path.join(self.save_path, filename), 'wb'), encoding='utf-8', xml_declaration=True,
pretty_print=True)
return True
def _get_application(self):

View File

@ -54,7 +54,7 @@ class OpenLyricsImport(SongImport):
"""
log.debug('initialise OpenLyricsImport')
SongImport.__init__(self, manager, **kwargs)
self.openLyrics = OpenLyrics(self.manager)
self.open_lyrics = OpenLyrics(self.manager)
def do_import(self):
"""
@ -71,11 +71,11 @@ class OpenLyricsImport(SongImport):
# special characters in the path (see lp:757673 and lp:744337).
parsed_file = etree.parse(open(file_path, 'r'), parser)
xml = etree.tostring(parsed_file).decode()
self.openLyrics.xml_to_song(xml)
self.open_lyrics.xml_to_song(xml)
except etree.XMLSyntaxError:
log.exception('XML syntax error in file %s' % file_path)
self.log_error(file_path, SongStrings.XMLSyntaxError)
except OpenLyricsError as exception:
log.exception('OpenLyricsException %d in file %s: %s'
% (exception.type, file_path, exception.log_message))
log.exception('OpenLyricsException %d in file %s: %s' %
(exception.type, file_path, exception.log_message))
self.log_error(file_path, exception.display_message)

View File

@ -115,10 +115,10 @@ class OpenSongImport(SongImport):
if self.stop_import_flag:
return
song_file = open(filename)
self.doImportFile(song_file)
self.do_import_file(song_file)
song_file.close()
def doImportFile(self, file):
def do_import_file(self, file):
"""
Process the OpenSong file - pass in a file-like object, not a file path.
"""
@ -132,7 +132,7 @@ class OpenSongImport(SongImport):
root = tree.getroot()
if root.tag != 'song':
self.log_error(file.name, str(
translate('SongsPlugin.OpenSongImport', ('Invalid OpenSong song file. Missing song tag.'))))
translate('SongsPlugin.OpenSongImport', 'Invalid OpenSong song file. Missing song tag.')))
return
fields = dir(root)
decode = {
@ -218,7 +218,7 @@ class OpenSongImport(SongImport):
for (verse_tag, verse_num, inst) in our_verse_order:
lines = '\n'.join(verses[verse_tag][verse_num][inst])
length = 0
while(length < len(verse_num) and verse_num[length].isnumeric()):
while length < len(verse_num) and verse_num[length].isnumeric():
length += 1
verse_def = '%s%s' % (verse_tag, verse_num[:length])
verse_joints[verse_def] = '%s\n[---]\n%s' % (verse_joints[verse_def], lines) \
@ -248,7 +248,7 @@ class OpenSongImport(SongImport):
if verse_num in verses.get(verse_tag, {}):
self.verse_order_list.append(verse_def)
else:
log.info('Got order %s but not in verse tags, dropping'
'this item from presentation order', verse_def)
log.info('Got order %s but not in verse tags, dropping this item from presentation order',
verse_def)
if not self.finish():
self.log_error(file.name)

View File

@ -39,6 +39,7 @@ from openlp.plugins.songs.lib.songimport import SongImport
log = logging.getLogger(__name__)
class PowerSongImport(SongImport):
"""
The :class:`PowerSongImport` class provides the ability to import song files
@ -90,7 +91,7 @@ class PowerSongImport(SongImport):
Receive either a list of files or a folder (unicode) to import.
"""
from .importer import SongFormat
PS_string = SongFormat.get(SongFormat.PowerSong, 'name')
ps_string = SongFormat.get(SongFormat.PowerSong, 'name')
if isinstance(self.import_source, str):
if os.path.isdir(self.import_source):
dir = self.import_source
@ -102,7 +103,7 @@ class PowerSongImport(SongImport):
self.import_source = ''
if not self.import_source or not isinstance(self.import_source, list):
self.log_error(translate('SongsPlugin.PowerSongImport', 'No songs to import.'),
translate('SongsPlugin.PowerSongImport', 'No %s files found.') % PS_string)
translate('SongsPlugin.PowerSongImport', 'No %s files found.') % ps_string)
return
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
for file in self.import_source:
@ -113,15 +114,15 @@ class PowerSongImport(SongImport):
with open(file, 'rb') as song_data:
while True:
try:
label = self._readString(song_data)
label = self._read_string(song_data)
if not label:
break
field = self._readString(song_data)
field = self._read_string(song_data)
except ValueError:
parse_error = True
self.log_error(os.path.basename(file), str(
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Unexpected byte value.')) %
PS_string)
ps_string)
break
else:
if label == 'TITLE':
@ -130,7 +131,7 @@ class PowerSongImport(SongImport):
self.parse_author(field)
elif label == 'COPYRIGHTLINE':
found_copyright = True
self._parseCopyrightCCLI(field)
self._parse_copyright_cCCLI(field)
elif label == 'PART':
self.add_verse(field)
if parse_error:
@ -138,13 +139,13 @@ class PowerSongImport(SongImport):
# Check that file had TITLE field
if not self.title:
self.log_error(os.path.basename(file), str(
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Missing "TITLE" header.')) % PS_string)
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Missing "TITLE" header.')) % ps_string)
continue
# Check that file had COPYRIGHTLINE label
if not found_copyright:
self.log_error(self.title, str(
translate('SongsPlugin.PowerSongImport', 'Invalid %s file. Missing "COPYRIGHTLINE" header.')) %
PS_string)
ps_string)
continue
# Check that file had at least one verse
if not self.verses:
@ -154,14 +155,14 @@ class PowerSongImport(SongImport):
if not self.finish():
self.log_error(self.title)
def _readString(self, file_object):
def _read_string(self, file_object):
"""
Reads in next variable-length string.
"""
string_len = self._read7BitEncodedInteger(file_object)
string_len = self._read_7_bit_encoded_integer(file_object)
return str(file_object.read(string_len), 'utf-8', 'ignore')
def _read7BitEncodedInteger(self, file_object):
def _read_7_bit_encoded_integer(self, file_object):
"""
Reads in a 32-bit integer in compressed 7-bit format.
@ -179,7 +180,7 @@ class PowerSongImport(SongImport):
# Check for corrupted stream (since max 5 bytes per 32-bit integer)
if i == 5:
raise ValueError
byte = self._readByte(file_object)
byte = self._read_byte(file_object)
# Strip high bit and shift left
val += (byte & 0x7f) << shift
shift += 7
@ -189,7 +190,7 @@ class PowerSongImport(SongImport):
i += 1
return val
def _readByte(self, file_object):
def _read_byte(self, file_object):
"""
Reads in next byte as an unsigned integer
@ -202,7 +203,7 @@ class PowerSongImport(SongImport):
else:
return ord(byte_str)
def _parseCopyrightCCLI(self, field):
def _parse_copyright_cCCLI(self, field):
"""
Look for CCLI song number, and get copyright
"""