From 7e623ea2268d3c4b09b0441b21749968b9dad7c5 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 20 Jul 2010 09:33:22 +0100 Subject: [PATCH] OpenLP v2 song DB importer --- openlp/plugins/songs/lib/__init__.py | 1 + openlp/plugins/songs/lib/olpimport.py | 198 +++++++++++++++++++++++++ openlp/plugins/songs/lib/songimport.py | 11 +- openlp/plugins/songs/songsplugin.py | 35 ++++- 4 files changed, 238 insertions(+), 7 deletions(-) create mode 100644 openlp/plugins/songs/lib/olpimport.py diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 8be820d82..0e2b93bd6 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -142,6 +142,7 @@ from songstab import SongsTab from mediaitem import SongMediaItem from songimport import SongImport from opensongimport import OpenSongImport +from olpimport import OpenLPSongImport try: from sofimport import SofImport from oooimport import OooImport diff --git a/openlp/plugins/songs/lib/olpimport.py b/openlp/plugins/songs/lib/olpimport.py new file mode 100644 index 000000000..433e59b28 --- /dev/null +++ b/openlp/plugins/songs/lib/olpimport.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2010 Raoul Snyman # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # +# Thompson, Jon Tibble, Carsten Tinggaard # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +The :mod:`olpimport` module provides the functionality for importing OpenLP +song databases into the current installation database. +""" +import logging + +from sqlalchemy import create_engine, MetaData +from sqlalchemy.orm import class_mapper, mapper, relation, scoped_session, \ + sessionmaker +from sqlalchemy.orm.exc import UnmappedClassError + +from openlp.core.lib.db import BaseModel +from openlp.plugins.songs.lib.db import Author, Book, Song, Topic #, AudioFile + +log = logging.getLogger(__name__) + +class OldAudioFile(BaseModel): + """ + AudioFile model + """ + pass + +class OldAuthor(BaseModel): + """ + Author model + """ + pass + +class OldBook(BaseModel): + """ + Book model + """ + pass + +class OldSong(BaseModel): + """ + Song model + """ + pass + +class OldTopic(BaseModel): + """ + Topic model + """ + pass + +class OpenLPSongImport(object): + """ + + """ + def __init__(self, master_manager, source_db): + """ + + """ + self.master_manager = master_manager + self.import_source = source_db + self.source_session = None + + def import_source_v2_db(self): + """ + + """ + engine = create_engine(self.import_source) + source_meta = MetaData() + source_meta.reflect(engine) + self.source_session = scoped_session(sessionmaker(bind=engine)) + if u'audio_files' in source_meta.tables.keys(): + has_audio_files = True + else: + has_audio_files = False + source_authors_table = source_meta.tables[u'authors'] + source_song_books_table = source_meta.tables[u'song_books'] + source_songs_table = source_meta.tables[u'songs'] + source_topics_table = source_meta.tables[u'topics'] + source_authors_songs_table = source_meta.tables[u'authors_songs'] + source_songs_topics_table = source_meta.tables[u'songs_topics'] + if has_audio_files: + source_audio_files_table = source_meta.tables[u'audio_files'] + source_audio_files_songs_table = \ + source_meta.tables[u'audio_files_songs'] + try: + class_mapper(OldAudioFile) + except UnmappedClassError: + mapper(OldAudioFile, source_audio_files_table) + song_props = { + 'authors': relation(OldAuthor, backref='songs', + secondary=source_authors_songs_table), + 'book': relation(OldBook, backref='songs'), + 'topics': relation(OldTopic, backref='songs', + secondary=source_songs_topics_table) + } + if has_audio_files: + song_props['audio_files'] = relation(OldAudioFile, backref='songs', + secondary=source_audio_files_songs_table) + try: + class_mapper(OldAuthor) + except UnmappedClassError: + mapper(OldAuthor, source_authors_table) + try: + class_mapper(OldBook) + except UnmappedClassError: + mapper(OldBook, source_song_books_table) + try: + class_mapper(OldSong) + except UnmappedClassError: + mapper(OldSong, source_songs_table, properties=song_props) + try: + class_mapper(OldTopic) + except UnmappedClassError: + mapper(OldTopic, source_topics_table) + + source_songs = self.source_session.query(OldSong).all() + for song in source_songs: + new_song = Song() + new_song.title = song.title + if has_audio_files: + new_song.alternate_title = song.alternate_title + else: + new_song.alternate_title = u'' + new_song.search_title = song.search_title + new_song.song_number = song.song_number + new_song.lyrics = song.lyrics + new_song.search_lyrics = song.search_lyrics + new_song.verse_order = song.verse_order + new_song.copyright = song.copyright + new_song.comments = song.comments + new_song.theme_name = song.theme_name + new_song.ccli_number = song.ccli_number + if song.authors: + for author in song.authors: + existing_author = self.master_manager.get_object_filtered( + Author, Author.display_name == author.display_name) + if existing_author: + new_song.authors.append(existing_author) + else: + new_song.authors.append(Author.populate( + first_name=author.first_name, + last_name=author.last_name, + display_name=author.display_name)) + else: + au = self.master_manager.get_object_filtered(Author, + Author.display_name == u'Author Unknown') + if au: + new_song.authors.append(au) + else: + new_song.authors.append(Author.populate( + display_name=u'Author Unknown')) + if song.song_book_id != 0: + existing_song_book = self.master_manager.get_object_filtered( + Book, Book.name == song.book.name) + if existing_song_book: + new_song.book = existing_song_book + else: + new_song.book = Book.populate(name=song.book.name, + publisher=song.book.publisher) + if song.topics: + for topic in song.topics: + existing_topic = self.master_manager.get_object_filtered( + Topic, Topic.name == topic.name) + if existing_topic: + new_song.topics.append(existing_topic) + else: + new_song.topics.append(Topic.populate(name=topic.name)) +# if has_audio_files: +# if song.audio_files: +# for audio_file in song.audio_files: +# existing_audio_file = \ +# self.master_manager.get_object_filtered(AudioFile, +# AudioFile.file_name == audio_file.file_name) +# if existing_audio_file: +# new_song.audio_files.remove(audio_file) +# new_song.audio_files.append(existing_audio_file) + self.master_manager.save_object(new_song) + engine.dispose() diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index 9787d9e20..222bbeedd 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -50,7 +50,7 @@ class SongImport(object): self.song_number = u'' self.alternate_title = u'' self.copyright = u'' - self.comment = u'' + self.comments = u'' self.theme_name = u'' self.ccli_number = u'' self.authors = [] @@ -253,7 +253,7 @@ class SongImport(object): song.lyrics = unicode(sxml.extract_xml(), u'utf-8') song.verse_order = u' '.join(self.verse_order_list) song.copyright = self.copyright - song.comment = self.comment + song.comments = self.comments song.theme_name = self.theme_name song.ccli_number = self.ccli_number for authortext in self.authors: @@ -274,7 +274,8 @@ class SongImport(object): for topictext in self.topics: if len(topictext) == 0: continue - topic = self.manager.get_object_filtered(Topic, Topic.name == topictext) + topic = self.manager.get_object_filtered(Topic, + Topic.name == topictext) if topic is None: topic = Topic.populate(name=topictext) song.topics.append(topic) @@ -303,8 +304,8 @@ class SongImport(object): print u'NUMBER: ' + self.song_number for topictext in self.topics: print u'TOPIC: ' + topictext - if self.comment: - print u'COMMENT: ' + self.comment + if self.comments: + print u'COMMENTS: ' + self.comments if self.theme_name: print u'THEME: ' + self.theme_name if self.ccli_number: diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 69063ec19..5e1c94e57 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -30,7 +30,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, build_icon, PluginStatus, Receiver, \ translate from openlp.core.lib.db import Manager -from openlp.plugins.songs.lib import SongMediaItem, SongsTab +from openlp.plugins.songs.lib import OpenLPSongImport, SongMediaItem, SongsTab from openlp.plugins.songs.lib.db import init_schema, Song try: @@ -157,7 +157,19 @@ class SongsPlugin(Plugin): import_menu.addAction(self.ImportOpenSongItem) QtCore.QObject.connect(self.ImportOpenSongItem, QtCore.SIGNAL(u'triggered()'), self.onImportOpenSongItemClick) - + # OpenLP v2 import menu item - ditto above regarding refactoring into + # an import wizard + self.ImportOpenLPSongItem = QtGui.QAction(import_menu) + self.ImportOpenLPSongItem.setObjectName(u'ImportOpenLPSongItem') + self.ImportOpenLPSongItem.setText(translate('SongsPlugin', + 'OpenLP v2 (temporary)')) + self.ImportOpenLPSongItem.setToolTip(translate('SongsPlugin', + 'Import an OpenLP v2 song database')) + self.ImportOpenLPSongItem.setStatusTip(translate('SongsPlugin', + 'Import an OpenLP v2 song database')) + import_menu.addAction(self.ImportOpenLPSongItem) + QtCore.QObject.connect(self.ImportOpenLPSongItem, + QtCore.SIGNAL(u'triggered()'), self.onImportOpenLPSongItemClick) def addExportMenuItem(self, export_menu): """ @@ -218,6 +230,25 @@ class SongsPlugin(Plugin): QtGui.QMessageBox.Ok) Receiver.send_message(u'songs_load_list') + def onImportOpenLPSongItemClick(self): + filenames = QtGui.QFileDialog.getOpenFileNames(None, + translate('SongsPlugin', 'Select OpenLP database(s) to import...'), + u'', u'OpenLP databases (*.sqlite);;All Files (*)') + try: + for filename in filenames: + db_url = u'sqlite:///%s' % filename + importer = OpenLPSongImport(self.manager, db_url) + importer.import_source_v2_db() + QtGui.QMessageBox.information(None, translate('SongsPlugin', + 'Database(s) imported'), translate('SongsPlugin', 'Your ' + 'OpenLP v2 song databases have been successfully imported')) + except: + log.exception(u'Failed to import OpenLP v2 database(s)') + QtGui.QMessageBox.critical(None, translate('SongsPlugin', + 'Import Error'), translate('SongsPlugin', + 'Error importing OpenLP v2 database(s)')) + Receiver.send_message(u'songs_load_list') + def onImportOooItemClick(self): filenames = QtGui.QFileDialog.getOpenFileNames( None, translate('SongsPlugin',