From f964c5d1ce57e0d66f04a2309bde5dcbf1238d31 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Sat, 17 Jul 2010 18:36:03 +0100 Subject: [PATCH 01/14] Add alternate_title field --- openlp/plugins/songs/forms/editsongform.py | 5 ++--- openlp/plugins/songs/lib/db.py | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index c0db7b741..e2646b07b 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -180,7 +180,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.loadBooks() self.song = self.songmanager.get_object(Song, id) self.TitleEditItem.setText(self.song.title) - title = self.song.search_title.split(u'@') + self.AlternativeEdit.setText(self.song.alternate_title) if self.song.song_book_id != 0: book_name = self.songmanager.get_object(Book, self.song.song_book_id) @@ -198,8 +198,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): id = 0 self.song.theme_name = None self.ThemeSelectionComboItem.setCurrentIndex(id) - if len(title) > 1: - self.AlternativeEdit.setText(title[1]) if self.song.copyright: self.CopyrightEditItem.setText(self.song.copyright) else: @@ -622,6 +620,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): def saveSong(self): self.song.title = unicode(self.TitleEditItem.text()) + self.song.alternate_title = unicode(self.AlternativeEdit.text()) self.song.copyright = unicode(self.CopyrightEditItem.text()) self.song.search_title = self.song.title + u'@' + \ unicode(self.AlternativeEdit.text()) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 655043144..794501cc7 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -88,6 +88,7 @@ def init_schema(url): Column(u'song_book_id', types.Integer, ForeignKey(u'song_books.id'), default=0), Column(u'title', types.Unicode(255), nullable=False), + Column(u'alternate_title', types.Unicode(255)), Column(u'lyrics', types.UnicodeText, nullable=False), Column(u'verse_order', types.Unicode(128)), Column(u'copyright', types.Unicode(255)), From f71f27c890886ea9328a5edc8891f4956f90b259 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 19 Jul 2010 00:37:24 +0100 Subject: [PATCH 02/14] Database refactor --- openlp/core/lib/db.py | 38 ++++----- openlp/plugins/alerts/forms/alertform.py | 3 +- openlp/plugins/bibles/lib/manager.py | 3 +- openlp/plugins/custom/customplugin.py | 6 +- openlp/plugins/custom/lib/mediaitem.py | 2 +- openlp/plugins/songs/forms/editsongform.py | 8 +- .../songs/forms/songmaintenanceform.py | 14 ++-- openlp/plugins/songs/lib/mediaitem.py | 8 +- openlp/plugins/songs/songsplugin.py | 7 +- .../songusage/forms/songusagedeleteform.py | 7 +- .../songusage/forms/songusagedetailform.py | 10 ++- openlp/plugins/songusage/lib/__init__.py | 1 - openlp/plugins/songusage/lib/manager.py | 81 ------------------- openlp/plugins/songusage/songusageplugin.py | 8 +- 14 files changed, 57 insertions(+), 139 deletions(-) delete mode 100644 openlp/plugins/songusage/lib/manager.py diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 8acc79541..735df4efb 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -178,36 +178,24 @@ class Manager(object): """ return self.session.query(object_class).filter(filter_clause).first() - def get_all_objects(self, object_class, order_by_ref=None): + def get_all_objects(self, object_class, filter_clause=None, + order_by_ref=None): """ Returns all the objects from the database ``object_class`` The type of objects to return + ``filter_clause`` + The filter governing selection of objects to return. Defaults to + None. + ``order_by_ref`` Any parameters to order the returned objects by. Defaults to None. """ query = self.session.query(object_class) - if order_by_ref is not None: - return query.order_by(order_by_ref).all() - return query.all() - - def get_all_objects_filtered(self, object_class, filter_clause, - order_by_ref=None): - """ - Returns a selection of objects from the database - - ``object_class`` - The type of objects to return - - ``filter_clause`` - The filter governing selection of objects to return - - ``order_by_ref`` - Any parameters to order the returned objects by. Defaults to None. - """ - query = self.session.query(object_class).filter(filter_clause) + if filter_clause: + query = query.filter(filter_clause) if order_by_ref is not None: return query.order_by(order_by_ref).all() return query.all() @@ -235,7 +223,7 @@ class Manager(object): else: return True - def delete_all_objects(self, object_class): + def delete_all_objects(self, object_class, filter_clause=None): """ Delete all object records @@ -243,11 +231,13 @@ class Manager(object): The type of object to delete """ try: - self.session.query(object_class).delete(synchronize_session=False) + query = self.session.query(object_class) + if filter_clause: + query = query.filter(filter_clause) + query.delete(synchronize_session=False) self.session.commit() return True except InvalidRequestError: self.session.rollback() - log.exception(u'Failed to delete all %s records', - object_class.__name__) + log.exception(u'Failed to delete %s records', object_class.__name__) return False diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index 388d35751..71edd8e9f 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -62,7 +62,8 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): def loadList(self): self.AlertListWidget.clear() - alerts = self.manager.get_all_objects(AlertItem, AlertItem.text) + alerts = self.manager.get_all_objects(AlertItem, + order_by_ref=AlertItem.text) for alert in alerts: item_name = QtGui.QListWidgetItem(alert.text) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(alert.id)) diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index 39f3c255b..f862e1d1c 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -198,7 +198,8 @@ class BibleManager(object): u'name': book.name, u'chapters': self.db_cache[bible].get_chapter_count(book.name) } - for book in self.db_cache[bible].get_all_objects(Book, Book.id) + for book in self.db_cache[bible].get_all_objects(Book, + order_by_ref=Book.id) ] def get_chapter_count(self, bible, book): diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py index 2cdbac362..79ccb1f9b 100644 --- a/openlp/plugins/custom/customplugin.py +++ b/openlp/plugins/custom/customplugin.py @@ -75,7 +75,7 @@ class CustomPlugin(Plugin): Returns True if the theme is being used, otherwise returns False. """ - if self.custommanager.get_all_objects_filtered(CustomSlide, + if self.custommanager.get_all_objects(CustomSlide, CustomSlide.theme_name == theme): return True return False @@ -91,8 +91,8 @@ class CustomPlugin(Plugin): ``newTheme`` The new name the plugin should now use. """ - customsUsingTheme = self.custommanager.get_all_objects_filtered( - CustomSlide, CustomSlide.theme_name == oldTheme) + customsUsingTheme = self.custommanager.get_all_objects(CustomSlide, + CustomSlide.theme_name == oldTheme) for custom in customsUsingTheme: custom.theme_name = newTheme self.custommanager.save_object(custom) diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index 34ffeeac4..cdea8cb48 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -73,7 +73,7 @@ class CustomMediaItem(MediaManagerItem): def initialise(self): self.loadCustomListView(self.parent.custommanager.get_all_objects( - CustomSlide, CustomSlide.title)) + CustomSlide, order_by_ref=CustomSlide.title)) #Called to redisplay the song list screen edith 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. diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index e2646b07b..3fa801595 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -118,7 +118,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.TopicRemoveButton.setEnabled(False) def loadAuthors(self): - authors = self.songmanager.get_all_objects(Author, Author.display_name) + authors = self.songmanager.get_all_objects(Author, + order_by_ref=Author.display_name) self.AuthorsSelectionComboItem.clear() self.AuthorsSelectionComboItem.addItem(u'') for author in authors: @@ -128,7 +129,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): row, QtCore.QVariant(author.id)) def loadTopics(self): - topics = self.songmanager.get_all_objects(Topic, Topic.name) + topics = self.songmanager.get_all_objects(Topic, + order_by_ref=Topic.name) self.SongTopicCombo.clear() self.SongTopicCombo.addItem(u'') for topic in topics: @@ -137,7 +139,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.SongTopicCombo.setItemData(row, QtCore.QVariant(topic.id)) def loadBooks(self): - books = self.songmanager.get_all_objects(Book, Book.name) + books = self.songmanager.get_all_objects(Book, order_by_ref=Book.name) self.SongbookCombo.clear() self.SongbookCombo.addItem(u'') for book in books: diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 2a848ad98..259dbcaf7 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -102,7 +102,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): Reloads the Authors list. """ self.AuthorsListWidget.clear() - authors = self.songmanager.get_all_objects(Author, Author.display_name) + authors = self.songmanager.get_all_objects(Author, + order_by_ref=Author.display_name) for author in authors: if author.display_name: author_name = QtGui.QListWidgetItem(author.display_name) @@ -117,7 +118,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): Reloads the Topics list. """ self.TopicsListWidget.clear() - topics = self.songmanager.get_all_objects(Topic, Topic.name) + topics = self.songmanager.get_all_objects(Topic, + order_by_ref=Topic.name) for topic in topics: topic_name = QtGui.QListWidgetItem(topic.name) topic_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id)) @@ -128,7 +130,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): Reloads the Books list. """ self.BooksListWidget.clear() - books = self.songmanager.get_all_objects(Book, Book.name) + books = self.songmanager.get_all_objects(Book, order_by_ref=Book.name) for book in books: book_name = QtGui.QListWidgetItem(u'%s (%s)' % (book.name, book.publisher)) @@ -140,7 +142,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): Returns False if the given Author is already in the list otherwise True. """ - authors = self.songmanager.get_all_objects_filtered(Author, + authors = self.songmanager.get_all_objects(Author, and_( Author.first_name == new_author.first_name, Author.last_name == new_author.last_name, @@ -165,7 +167,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): """ Returns False if the given Topic is already in the list otherwise True. """ - topics = self.songmanager.get_all_objects_filtered(Topic, + topics = self.songmanager.get_all_objects(Topic, Topic.name == new_topic.name) if len(topics) > 0: # If we edit an existing Topic, we need to make sure that we do @@ -185,7 +187,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): """ Returns False if the given Book is already in the list otherwise True. """ - books = self.songmanager.get_all_objects_filtered(Book, + books = self.songmanager.get_all_objects(Book, and_(Book.name == new_book.name, Book.publisher == new_book.publisher)) if len(books) > 0: diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 93f376d1f..0b9ef789d 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -164,20 +164,20 @@ class SongMediaItem(MediaManagerItem): search_type = self.SearchTypeComboBox.currentIndex() if search_type == 0: log.debug(u'Titles Search') - search_results = self.parent.manager.get_all_objects_filtered(Song, + search_results = self.parent.manager.get_all_objects(Song, Song.search_title.like(u'%' + search_keywords + u'%'), Song.search_title.asc()) self.displayResultsSong(search_results) elif search_type == 1: log.debug(u'Lyrics Search') - search_results = self.parent.manager.get_all_objects_filtered(Song, + search_results = self.parent.manager.get_all_objects(Song, Song.search_lyrics.like(u'%' + search_keywords + u'%'), Song.search_lyrics.asc()) self.displayResultsSong(search_results) elif search_type == 2: log.debug(u'Authors Search') - search_results = self.parent.manager.get_all_objects_filtered( - Author, Author.display_name.like(u'%' + search_keywords + u'%'), + search_results = self.parent.manager.get_all_objects(Author, + Author.display_name.like(u'%' + search_keywords + u'%'), Author.display_name.asc()) self.displayResultsAuthor(search_results) #Called to redisplay the song list screen edith from a search diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index e7be7cae3..11a057f92 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -69,7 +69,7 @@ class SongsPlugin(Plugin): log.info(u'Songs Initialising') Plugin.initialise(self) self.mediaItem.displayResultsSong( - self.manager.get_all_objects(Song, Song.title)) + self.manager.get_all_objects(Song, order_by_ref=Song.title)) def getMediaManagerItem(self): """ @@ -198,8 +198,7 @@ class SongsPlugin(Plugin): Returns True if the theme is being used, otherwise returns False. """ - if self.manager.get_all_objects_filtered(Song, - Song.theme_name == theme): + if self.manager.get_all_objects(Song, Song.theme_name == theme): return True return False @@ -214,7 +213,7 @@ class SongsPlugin(Plugin): ``newTheme`` The new name the plugin should now use. """ - songsUsingTheme = self.manager.get_all_objects_filtered(Song, + songsUsingTheme = self.manager.get_all_objects(Song, Song.theme_name == oldTheme) for song in songsUsingTheme: song.theme_name = newTheme diff --git a/openlp/plugins/songusage/forms/songusagedeleteform.py b/openlp/plugins/songusage/forms/songusagedeleteform.py index 97e032413..4ded872f0 100644 --- a/openlp/plugins/songusage/forms/songusagedeleteform.py +++ b/openlp/plugins/songusage/forms/songusagedeleteform.py @@ -25,8 +25,9 @@ from PyQt4 import QtGui -from songusagedeletedialog import Ui_SongUsageDeleteDialog from openlp.core.lib import translate +from openlp.plugins.songusage.lib.db import SongUsageItem +from songusagedeletedialog import Ui_SongUsageDeleteDialog class SongUsageDeleteForm(QtGui.QDialog, Ui_SongUsageDeleteDialog): """ @@ -52,6 +53,6 @@ class SongUsageDeleteForm(QtGui.QDialog, Ui_SongUsageDeleteDialog): QtGui.QMessageBox.Cancel) if ret == QtGui.QMessageBox.Ok: deleteDate = self.DeleteCalendar.selectedDate().toPyDate() - self.songusagemanager.delete_to_date(deleteDate) + self.songusagemanager.delete_all_objects(SongUsageItem, + SongUsageItem.usagedate <= deleteDate) self.close() - diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py index be1b8221c..ac65ce857 100644 --- a/openlp/plugins/songusage/forms/songusagedetailform.py +++ b/openlp/plugins/songusage/forms/songusagedetailform.py @@ -27,9 +27,10 @@ import logging import os from PyQt4 import QtCore, QtGui +from sqlalchemy.sql import and_ from openlp.core.lib import SettingsManager, translate - +from openlp.plugins.songusage.lib.db import SongUsageItem from songusagedetaildialog import Ui_SongUsageDetailDialog log = logging.getLogger(__name__) @@ -74,8 +75,11 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog): filename = u'usage_detail_%s_%s.txt' % ( self.FromDate.selectedDate().toString(u'ddMMyyyy'), self.ToDate.selectedDate().toString(u'ddMMyyyy')) - usage = self.parent.songusagemanager.get_songusage_for_period( - self.FromDate.selectedDate(), self.ToDate.selectedDate()) + usage = self.parent.songusagemanager.get_all_objects( + SongUsageItem, and_( + SongUsageItem.usagedate >= self.FromDate.selectedDate().toPyDate(), + SongUsageItem.usagedate < self.ToDate.selectedDate().toPyDate()), + [SongUsageItem.usagedate, SongUsageItem.usagetime]) outname = os.path.join(unicode(self.FileLineEdit.text()), filename) file = None try: diff --git a/openlp/plugins/songusage/lib/__init__.py b/openlp/plugins/songusage/lib/__init__.py index ae8425317..bd83e0532 100644 --- a/openlp/plugins/songusage/lib/__init__.py +++ b/openlp/plugins/songusage/lib/__init__.py @@ -25,4 +25,3 @@ """ The :mod:`lib` module contains the library functions for the songusage plugin. """ -from openlp.plugins.songusage.lib.manager import SongUsageManager diff --git a/openlp/plugins/songusage/lib/manager.py b/openlp/plugins/songusage/lib/manager.py deleted file mode 100644 index 2c34f3a54..000000000 --- a/openlp/plugins/songusage/lib/manager.py +++ /dev/null @@ -1,81 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # -# Gorven, Scott Guerrieri, 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:`manager` module provides song usage specific database query code -""" -import logging - -from sqlalchemy.exceptions import InvalidRequestError - -from openlp.core.lib.db import Manager -from openlp.plugins.songusage.lib.db import init_schema, SongUsageItem - -log = logging.getLogger(__name__) - -class SongUsageManager(Manager): - """ - The Song Manager provides a central location for all database code. This - class takes care of connecting to the database and running all the queries. - """ - log.info(u'SongUsage manager loaded') - - def __init__(self): - """ - Creates the connection to the database, and creates the tables if they - don't exist. - """ - log.debug(u'SongUsage Initialising') - Manager.__init__(self, u'songusage', init_schema) - log.debug(u'SongUsage Initialised') - - def get_songusage_for_period(self, start_date, end_date): - """ - Returns the details of SongUsage for a designated time period - - ``start_date`` - The start of the period to return - - ``end_date`` - The end of the period to return - """ - return self.session.query(SongUsageItem) \ - .filter(SongUsageItem.usagedate >= start_date.toPyDate()) \ - .filter(SongUsageItem.usagedate < end_date.toPyDate()) \ - .order_by(SongUsageItem.usagedate, SongUsageItem.usagetime).all() - - def delete_to_date(self, date): - """ - Delete SongUsage records before given date - """ - try: - self.session.query(SongUsageItem) \ - .filter(SongUsageItem.usagedate <= date) \ - .delete(synchronize_session=False) - self.session.commit() - return True - except InvalidRequestError: - self.session.rollback() - log.exception(u'Failed to delete all Song Usage items to %s' % date) - return False diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py index 07e7271a1..c7a8a30aa 100644 --- a/openlp/plugins/songusage/songusageplugin.py +++ b/openlp/plugins/songusage/songusageplugin.py @@ -24,15 +24,15 @@ ############################################################################### import logging - from datetime import datetime + from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, Receiver, build_icon, translate -from openlp.plugins.songusage.lib import SongUsageManager +from openlp.core.lib.db import Manager from openlp.plugins.songusage.forms import SongUsageDetailForm, \ SongUsageDeleteForm -from openlp.plugins.songusage.lib.db import SongUsageItem +from openlp.plugins.songusage.lib.db import init_schema, SongUsageItem log = logging.getLogger(__name__) @@ -117,7 +117,7 @@ class SongUsagePlugin(Plugin): QtCore.QVariant(False)).toBool() self.SongUsageStatus.setChecked(self.SongUsageActive) if self.songusagemanager is None: - self.songusagemanager = SongUsageManager() + self.songusagemanager = Manager(u'songusage', init_schema) self.SongUsagedeleteform = SongUsageDeleteForm(self.songusagemanager) self.SongUsagedetailform = SongUsageDetailForm(self) self.SongUsageMenu.menuAction().setVisible(True) From dd8c6f378b6e03b49938af032c95eaeb590224e8 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 19 Jul 2010 09:50:16 +0100 Subject: [PATCH 03/14] Fix alt title field --- openlp/plugins/songs/forms/editsongform.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 3fa801595..6a626e65a 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -182,7 +182,10 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.loadBooks() self.song = self.songmanager.get_object(Song, id) self.TitleEditItem.setText(self.song.title) - self.AlternativeEdit.setText(self.song.alternate_title) + if self.song.alternate_title: + self.AlternativeEdit.setText(self.song.alternate_title) + else: + self.AlternativeEdit.setText(u'') if self.song.song_book_id != 0: book_name = self.songmanager.get_object(Book, self.song.song_book_id) From 9e2b876be350d9d3460a475c73341a6dcdcdcc8c Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 19 Jul 2010 13:18:56 +0100 Subject: [PATCH 04/14] Add audio files to DB --- openlp/plugins/songs/lib/db.py | 36 +++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index 794501cc7..e090d7442 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -32,6 +32,12 @@ from sqlalchemy.orm import mapper, relation from openlp.core.lib.db import BaseModel, init_db +class AudioFile(BaseModel): + """ + AudioFile model + """ + pass + class Author(BaseModel): """ Author model @@ -75,6 +81,12 @@ def init_schema(url): Column(u'display_name', types.Unicode(255), nullable=False) ) + # Definition of the "audio_files" table + audio_files_table = Table(u'audio_files', metadata, + Column(u'id', types.Integer, primary_key=True), + Column(u'file_name', types.Unicode(255), nullable=False) + ) + # Definition of the "song_books" table song_books_table = Table(u'song_books', metadata, Column(u'id', types.Integer, primary_key=True), @@ -114,6 +126,14 @@ def init_schema(url): ForeignKey(u'songs.id'), primary_key=True) ) + # Definition of the "songs_audio_files" table + songs_audio_files_table = Table(u'songs_audio_files', metadata, + Column(u'song_id', types.Integer, + ForeignKey(u'songs.id'), primary_key=True), + Column(u'audio_file_id', types.Integer, + ForeignKey(u'audio_files.id'), primary_key=True) + ) + # Definition of the "songs_topics" table songs_topics_table = Table(u'songs_topics', metadata, Column(u'song_id', types.Integer, @@ -123,6 +143,7 @@ def init_schema(url): ) # Define table indexes + Index(u'audio_files_id', audio_files_table.c.id) Index(u'authors_id', authors_table.c.id) Index(u'authors_display_name_id', authors_table.c.display_name, authors_table.c.id) @@ -133,19 +154,28 @@ def init_schema(url): authors_songs_table.c.song_id) Index(u'authors_songs_song', authors_songs_table.c.song_id, authors_songs_table.c.author_id) + Index(u'songs_audio_files_file', songs_audio_files_table.c.audio_file_id, + songs_audio_files_table.c.song_id) + Index(u'songs_audio_files_song', songs_audio_files_table.c.song_id, + songs_audio_files_table.c.audio_file_id) Index(u'topics_song_topic', songs_topics_table.c.topic_id, songs_topics_table.c.song_id) Index(u'topics_song_song', songs_topics_table.c.song_id, songs_topics_table.c.topic_id) + mapper(AudioFile, audio_files_table) mapper(Author, authors_table) mapper(Book, song_books_table) mapper(Song, songs_table, - properties={'authors': relation(Author, backref='songs', - secondary=authors_songs_table), + properties={ + 'audio_files': relation(AudioFile, backref='songs', + secondary=songs_audio_files_table), + 'authors': relation(Author, backref='songs', + secondary=authors_songs_table), 'book': relation(Book, backref='songs'), 'topics': relation(Topic, backref='songs', - secondary=songs_topics_table)}) + secondary=songs_topics_table) + }) mapper(Topic, topics_table) metadata.create_all(checkfirst=True) From 86ad4b1071b8cfe72883ab72b8d6e30d0c886395 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 19 Jul 2010 16:39:42 +0100 Subject: [PATCH 05/14] Fix DB definition ordering --- openlp/plugins/songs/lib/db.py | 38 +++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index e090d7442..acf388035 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -73,6 +73,12 @@ def init_schema(url): """ session, metadata = init_db(url, auto_flush=False) + # Definition of the "audio_files" table + audio_files_table = Table(u'audio_files', metadata, + Column(u'id', types.Integer, primary_key=True), + Column(u'file_name', types.Unicode(255), nullable=False) + ) + # Definition of the "authors" table authors_table = Table(u'authors', metadata, Column(u'id', types.Integer, primary_key=True), @@ -81,12 +87,6 @@ def init_schema(url): Column(u'display_name', types.Unicode(255), nullable=False) ) - # Definition of the "audio_files" table - audio_files_table = Table(u'audio_files', metadata, - Column(u'id', types.Integer, primary_key=True), - Column(u'file_name', types.Unicode(255), nullable=False) - ) - # Definition of the "song_books" table song_books_table = Table(u'song_books', metadata, Column(u'id', types.Integer, primary_key=True), @@ -118,6 +118,14 @@ def init_schema(url): Column(u'name', types.Unicode(128), nullable=False) ) + # Definition of the "audio_files_songs" table + audio_files_songs_table = Table(u'audio_files_songs', metadata, + Column(u'audio_file_id', types.Integer, + ForeignKey(u'audio_files.id'), primary_key=True), + Column(u'song_id', types.Integer, + ForeignKey(u'songs.id'), primary_key=True) + ) + # Definition of the "authors_songs" table authors_songs_table = Table(u'authors_songs', metadata, Column(u'author_id', types.Integer, @@ -126,14 +134,6 @@ def init_schema(url): ForeignKey(u'songs.id'), primary_key=True) ) - # Definition of the "songs_audio_files" table - songs_audio_files_table = Table(u'songs_audio_files', metadata, - Column(u'song_id', types.Integer, - ForeignKey(u'songs.id'), primary_key=True), - Column(u'audio_file_id', types.Integer, - ForeignKey(u'audio_files.id'), primary_key=True) - ) - # Definition of the "songs_topics" table songs_topics_table = Table(u'songs_topics', metadata, Column(u'song_id', types.Integer, @@ -150,14 +150,14 @@ def init_schema(url): Index(u'song_books_id', song_books_table.c.id) Index(u'songs_id', songs_table.c.id) Index(u'topics_id', topics_table.c.id) + Index(u'audio_files_songs_file', audio_files_songs_table.c.audio_file_id, + audio_files_songs_table.c.song_id) + Index(u'audio_files_songs_song', audio_files_songs_table.c.song_id, + audio_files_songs_table.c.audio_file_id) Index(u'authors_songs_author', authors_songs_table.c.author_id, authors_songs_table.c.song_id) Index(u'authors_songs_song', authors_songs_table.c.song_id, authors_songs_table.c.author_id) - Index(u'songs_audio_files_file', songs_audio_files_table.c.audio_file_id, - songs_audio_files_table.c.song_id) - Index(u'songs_audio_files_song', songs_audio_files_table.c.song_id, - songs_audio_files_table.c.audio_file_id) Index(u'topics_song_topic', songs_topics_table.c.topic_id, songs_topics_table.c.song_id) Index(u'topics_song_song', songs_topics_table.c.song_id, @@ -169,7 +169,7 @@ def init_schema(url): mapper(Song, songs_table, properties={ 'audio_files': relation(AudioFile, backref='songs', - secondary=songs_audio_files_table), + secondary=audio_files_songs_table), 'authors': relation(Author, backref='songs', secondary=authors_songs_table), 'book': relation(Book, backref='songs'), From 978372ca9f82227e18bf83ce8f31f7bd57080d74 Mon Sep 17 00:00:00 2001 From: andreas Date: Mon, 19 Jul 2010 20:34:03 +0200 Subject: [PATCH 06/14] added check preventing adding a topic/author twice to the song --- openlp/plugins/songs/forms/editsongform.py | 31 ++++++++++++++++------ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index c0db7b741..0a3ed0a3d 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -304,10 +304,18 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): elif item > 0: item_id = (self.AuthorsSelectionComboItem.itemData(item)).toInt()[0] author = self.songmanager.get_object(Author, item_id) - self.song.authors.append(author) - author_item = QtGui.QListWidgetItem(unicode(author.display_name)) - author_item.setData(QtCore.Qt.UserRole, QtCore.QVariant(author.id)) - self.AuthorsListView.addItem(author_item) + if author in self.song.authors: + QtGui.QMessageBox.warning(self, + translate('SongsPlugin.EditSongForm', 'Error'), + translate('SongsPlugin.EditSongForm', 'This author is ' + 'already in the list.')) + else: + self.song.authors.append(author) + author_item = QtGui.QListWidgetItem(unicode( + author.display_name)) + author_item.setData(QtCore.Qt.UserRole, + QtCore.QVariant(author.id)) + self.AuthorsListView.addItem(author_item) self.AuthorsSelectionComboItem.setCurrentIndex(0) else: QtGui.QMessageBox.warning(self, @@ -355,10 +363,17 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): elif item > 0: item_id = (self.SongTopicCombo.itemData(item)).toInt()[0] topic = self.songmanager.get_object(Topic, item_id) - self.song.topics.append(topic) - topic_item = QtGui.QListWidgetItem(unicode(topic.name)) - topic_item.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id)) - self.TopicsListView.addItem(topic_item) + if topic in self.song.topics: + QtGui.QMessageBox.warning(self, + translate('SongsPlugin.EditSongForm', 'Error'), + translate('SongsPlugin.EditSongForm', 'This topic is ' + 'already in the list.')) + else: + self.song.topics.append(topic) + topic_item = QtGui.QListWidgetItem(unicode(topic.name)) + topic_item.setData(QtCore.Qt.UserRole, + QtCore.QVariant(topic.id)) + self.TopicsListView.addItem(topic_item) self.SongTopicCombo.setCurrentIndex(0) else: QtGui.QMessageBox.warning(self, From 42cff457623dc970241e624171732c3f8782f7f5 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 19 Jul 2010 21:45:53 +0100 Subject: [PATCH 07/14] Complete refactor for new code --- openlp/plugins/songs/forms/songmaintenanceform.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index c9d3a7978..67f60bfee 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -394,7 +394,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): and_(Author.first_name == old_author.first_name, Author.last_name == old_author.last_name, Author.display_name == old_author.display_name)) - songs = self.songmanager.get_all_objects_filtered(Song, + songs = self.songmanager.get_all_objects(Song, Song.authors.contains(old_author)) for song in songs: # We check if the song has already existing_author as author. If @@ -414,7 +414,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): ''' existing_topic = self.songmanager.get_object_filtered(Topic, Topic.name == old_topic.name) - songs = self.songmanager.get_all_objects_filtered(Song, + songs = self.songmanager.get_all_objects(Song, Song.topics.contains(old_topic)) for song in songs: # We check if the song has already existing_topic as topic. If that @@ -435,7 +435,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): existing_book = self.songmanager.get_object_filtered(Book, and_(Book.name == old_book.name, Book.publisher == old_book.publisher)) - songs = self.songmanager.get_all_objects_filtered(Song, + songs = self.songmanager.get_all_objects(Song, Song.song_book_id == old_book.id) for song in songs: song.song_book_id = existing_book.id From 80eb8e36af87086a5b08312ad08023487ed3abaf Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Mon, 19 Jul 2010 21:48:10 +0100 Subject: [PATCH 08/14] Import fix --- openlp/plugins/songs/lib/test/test_opensongimport.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/test/test_opensongimport.py b/openlp/plugins/songs/lib/test/test_opensongimport.py index 8c974adbc..bcd74ba17 100644 --- a/openlp/plugins/songs/lib/test/test_opensongimport.py +++ b/openlp/plugins/songs/lib/test/test_opensongimport.py @@ -22,11 +22,10 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### + from openlp.plugins.songs.lib.opensongimport import OpenSongImport from openlp.core.lib.db import Manager from openlp.plugins.songs.lib.db import init_schema -from openlp.plugins.songs.songsplugin import SongsPlugin -import sys def test(): manager = Manager(u'songs', init_schema) From 7e623ea2268d3c4b09b0441b21749968b9dad7c5 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 20 Jul 2010 09:33:22 +0100 Subject: [PATCH 09/14] 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', From 11c90c3eb79da633e655c7f696e07abd4d2bbab0 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 20 Jul 2010 09:37:15 +0100 Subject: [PATCH 10/14] Fix song book form ampersands --- openlp/plugins/songs/forms/songbookdialog.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openlp/plugins/songs/forms/songbookdialog.py b/openlp/plugins/songs/forms/songbookdialog.py index 0401ff75b..a2e5658cd 100644 --- a/openlp/plugins/songs/forms/songbookdialog.py +++ b/openlp/plugins/songs/forms/songbookdialog.py @@ -41,6 +41,7 @@ class Ui_SongBookDialog(object): QtGui.QFormLayout.LabelRole, self.NameLabel) self.NameEdit = QtGui.QLineEdit(SongBookDialog) self.NameEdit.setObjectName(u'NameEdit') + self.NameLabel.setBuddy(self.NameEdit) self.SongBookLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.NameEdit) self.PublisherLabel = QtGui.QLabel(SongBookDialog) @@ -49,6 +50,7 @@ class Ui_SongBookDialog(object): QtGui.QFormLayout.LabelRole, self.PublisherLabel) self.PublisherEdit = QtGui.QLineEdit(SongBookDialog) self.PublisherEdit.setObjectName(u'PublisherEdit') + self.PublisherLabel.setBuddy(self.PublisherEdit) self.SongBookLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.PublisherEdit) self.ButtonBox = QtGui.QDialogButtonBox(SongBookDialog) From a4bbb0ef282947945b49da0835e7fe9a88c95c97 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 20 Jul 2010 10:21:15 +0100 Subject: [PATCH 11/14] Clarify import type --- openlp/plugins/songs/songsplugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 5e1c94e57..bf1c0cb23 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -162,7 +162,7 @@ class SongsPlugin(Plugin): self.ImportOpenLPSongItem = QtGui.QAction(import_menu) self.ImportOpenLPSongItem.setObjectName(u'ImportOpenLPSongItem') self.ImportOpenLPSongItem.setText(translate('SongsPlugin', - 'OpenLP v2 (temporary)')) + 'OpenLP v2 Songs (temporary)')) self.ImportOpenLPSongItem.setToolTip(translate('SongsPlugin', 'Import an OpenLP v2 song database')) self.ImportOpenLPSongItem.setStatusTip(translate('SongsPlugin', From 1738040b8713bf8c8326caa885cb4ccdca35b55b Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 20 Jul 2010 12:02:03 +0100 Subject: [PATCH 12/14] Save Song Book (Bug #607030) --- openlp/plugins/songs/forms/editsongform.py | 33 +++++++++++----------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index c0db7b741..f2dc826e9 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -382,23 +382,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.TopicsListView.takeItem(row) def onSongBookComboChanged(self, item): - item = int(self.SongbookCombo.currentIndex()) - text = unicode(self.SongbookCombo.currentText()) - if item == 0 and text: - if QtGui.QMessageBox.question(self, - translate('SongsPlugin.EditSongForm', 'Add Book'), - translate('SongsPlugin.EditSongForm', 'This song book does ' - 'not exist, do you want to add it?'), - QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, - QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes: - book = Book.populate(name=text) - self.songmanager.save_object(book) - self.song.book = book - self.loadBooks() - else: - return - elif item >= 1: - item = int(self.SongbookCombo.currentIndex()) + if item >= 1: self.song.song_book_id = \ (self.SongbookCombo.itemData(item)).toInt()[0] else: @@ -616,6 +600,21 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): def accept(self): log.debug(u'accept') + item = int(self.SongbookCombo.currentIndex()) + text = unicode(self.SongbookCombo.currentText()) + if item == 0 and text: + if QtGui.QMessageBox.question(self, + translate('SongsPlugin.EditSongForm', 'Add Book'), + translate('SongsPlugin.EditSongForm', 'This song book does ' + 'not exist, do you want to add it?'), + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, + QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes: + book = Book.populate(name=text) + self.songmanager.save_object(book) + self.song.book = book + self.loadBooks() + else: + return if self.saveSong(): Receiver.send_message(u'songs_load_list') self.close() From cb17897d4726e66163a9fa59f3d16eb1021e3f32 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 20 Jul 2010 13:43:21 +0100 Subject: [PATCH 13/14] Media rather than Audio --- openlp/plugins/songs/lib/db.py | 57 ++++++++++++------------- openlp/plugins/songs/lib/olpimport.py | 60 ++++++++++++++------------- 2 files changed, 60 insertions(+), 57 deletions(-) diff --git a/openlp/plugins/songs/lib/db.py b/openlp/plugins/songs/lib/db.py index acf388035..156a5e383 100644 --- a/openlp/plugins/songs/lib/db.py +++ b/openlp/plugins/songs/lib/db.py @@ -32,12 +32,6 @@ from sqlalchemy.orm import mapper, relation from openlp.core.lib.db import BaseModel, init_db -class AudioFile(BaseModel): - """ - AudioFile model - """ - pass - class Author(BaseModel): """ Author model @@ -52,6 +46,12 @@ class Book(BaseModel): return u'' % ( str(self.id), self.name, self.publisher) +class MediaFile(BaseModel): + """ + MediaFile model + """ + pass + class Song(BaseModel): """ Song model @@ -73,12 +73,6 @@ def init_schema(url): """ session, metadata = init_db(url, auto_flush=False) - # Definition of the "audio_files" table - audio_files_table = Table(u'audio_files', metadata, - Column(u'id', types.Integer, primary_key=True), - Column(u'file_name', types.Unicode(255), nullable=False) - ) - # Definition of the "authors" table authors_table = Table(u'authors', metadata, Column(u'id', types.Integer, primary_key=True), @@ -87,6 +81,13 @@ def init_schema(url): Column(u'display_name', types.Unicode(255), nullable=False) ) + # Definition of the "media_files" table + media_files_table = Table(u'media_files', metadata, + Column(u'id', types.Integer, primary_key=True), + Column(u'file_name', types.Unicode(255), nullable=False), + Column(u'type', types.Unicode(64), nullable=False, default=u'audio') + ) + # Definition of the "song_books" table song_books_table = Table(u'song_books', metadata, Column(u'id', types.Integer, primary_key=True), @@ -118,14 +119,6 @@ def init_schema(url): Column(u'name', types.Unicode(128), nullable=False) ) - # Definition of the "audio_files_songs" table - audio_files_songs_table = Table(u'audio_files_songs', metadata, - Column(u'audio_file_id', types.Integer, - ForeignKey(u'audio_files.id'), primary_key=True), - Column(u'song_id', types.Integer, - ForeignKey(u'songs.id'), primary_key=True) - ) - # Definition of the "authors_songs" table authors_songs_table = Table(u'authors_songs', metadata, Column(u'author_id', types.Integer, @@ -134,6 +127,14 @@ def init_schema(url): ForeignKey(u'songs.id'), primary_key=True) ) + # Definition of the "media_files_songs" table + media_files_songs_table = Table(u'media_files_songs', metadata, + Column(u'media_file_id', types.Integer, + ForeignKey(u'media_files.id'), primary_key=True), + Column(u'song_id', types.Integer, + ForeignKey(u'songs.id'), primary_key=True) + ) + # Definition of the "songs_topics" table songs_topics_table = Table(u'songs_topics', metadata, Column(u'song_id', types.Integer, @@ -143,36 +144,36 @@ def init_schema(url): ) # Define table indexes - Index(u'audio_files_id', audio_files_table.c.id) Index(u'authors_id', authors_table.c.id) Index(u'authors_display_name_id', authors_table.c.display_name, authors_table.c.id) + Index(u'media_files_id', media_files_table.c.id) Index(u'song_books_id', song_books_table.c.id) Index(u'songs_id', songs_table.c.id) Index(u'topics_id', topics_table.c.id) - Index(u'audio_files_songs_file', audio_files_songs_table.c.audio_file_id, - audio_files_songs_table.c.song_id) - Index(u'audio_files_songs_song', audio_files_songs_table.c.song_id, - audio_files_songs_table.c.audio_file_id) Index(u'authors_songs_author', authors_songs_table.c.author_id, authors_songs_table.c.song_id) Index(u'authors_songs_song', authors_songs_table.c.song_id, authors_songs_table.c.author_id) + Index(u'media_files_songs_file', media_files_songs_table.c.media_file_id, + media_files_songs_table.c.song_id) + Index(u'media_files_songs_song', media_files_songs_table.c.song_id, + media_files_songs_table.c.media_file_id) Index(u'topics_song_topic', songs_topics_table.c.topic_id, songs_topics_table.c.song_id) Index(u'topics_song_song', songs_topics_table.c.song_id, songs_topics_table.c.topic_id) - mapper(AudioFile, audio_files_table) mapper(Author, authors_table) mapper(Book, song_books_table) + mapper(MediaFile, media_files_table) mapper(Song, songs_table, properties={ - 'audio_files': relation(AudioFile, backref='songs', - secondary=audio_files_songs_table), 'authors': relation(Author, backref='songs', secondary=authors_songs_table), 'book': relation(Book, backref='songs'), + 'media_files': relation(MediaFile, backref='songs', + secondary=media_files_songs_table), 'topics': relation(Topic, backref='songs', secondary=songs_topics_table) }) diff --git a/openlp/plugins/songs/lib/olpimport.py b/openlp/plugins/songs/lib/olpimport.py index 433e59b28..a73684e00 100644 --- a/openlp/plugins/songs/lib/olpimport.py +++ b/openlp/plugins/songs/lib/olpimport.py @@ -34,16 +34,10 @@ from sqlalchemy.orm import class_mapper, mapper, relation, scoped_session, \ 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 +from openlp.plugins.songs.lib.db import Author, Book, Song, Topic #, MediaFile log = logging.getLogger(__name__) -class OldAudioFile(BaseModel): - """ - AudioFile model - """ - pass - class OldAuthor(BaseModel): """ Author model @@ -56,6 +50,12 @@ class OldBook(BaseModel): """ pass +class OldMediaFile(BaseModel): + """ + MediaFile model + """ + pass + class OldSong(BaseModel): """ Song model @@ -88,24 +88,24 @@ class OpenLPSongImport(object): 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 + if u'media_files' in source_meta.tables.keys(): + has_media_files = True else: - has_audio_files = False + has_media_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'] + if has_media_files: + source_media_files_table = source_meta.tables[u'media_files'] + source_media_files_songs_table = \ + source_meta.tables[u'media_files_songs'] try: - class_mapper(OldAudioFile) + class_mapper(OldMediaFile) except UnmappedClassError: - mapper(OldAudioFile, source_audio_files_table) + mapper(OldMediaFile, source_media_files_table) song_props = { 'authors': relation(OldAuthor, backref='songs', secondary=source_authors_songs_table), @@ -113,9 +113,9 @@ class OpenLPSongImport(object): '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) + if has_media_files: + song_props['media_files'] = relation(OldMediaFile, backref='songs', + secondary=source_media_files_songs_table) try: class_mapper(OldAuthor) except UnmappedClassError: @@ -137,7 +137,7 @@ class OpenLPSongImport(object): for song in source_songs: new_song = Song() new_song.title = song.title - if has_audio_files: + if has_media_files: new_song.alternate_title = song.alternate_title else: new_song.alternate_title = u'' @@ -185,14 +185,16 @@ class OpenLPSongImport(object): 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) +# if has_media_files: +# if song.media_files: +# for media_file in song.media_files: +# existing_media_file = \ +# self.master_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)) self.master_manager.save_object(new_song) engine.dispose() From cb431fea2af21a1f0cb18684765f49de79206563 Mon Sep 17 00:00:00 2001 From: Jon Tibble Date: Tue, 20 Jul 2010 16:22:05 +0100 Subject: [PATCH 14/14] Fix song object creation (Bug #607034) --- openlp/core/lib/db.py | 8 ++++++-- openlp/plugins/songs/forms/editsongform.py | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 8acc79541..70def9490 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -135,16 +135,20 @@ class Manager(object): settings.endGroup() self.session = init_schema(self.db_url) - def save_object(self, object_instance): + def save_object(self, object_instance, commit=True): """ Save an object to the database ``object_instance`` The object to save + + ``commit`` + Commit the session with this object """ try: self.session.add(object_instance) - self.session.commit() + if commit: + self.session.commit() return True except InvalidRequestError: self.session.rollback() diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index f2dc826e9..691918782 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -290,7 +290,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes: author = Author.populate(first_name=text.rsplit(u' ', 1)[0], last_name=text.rsplit(u' ', 1)[1], display_name=text) - self.songmanager.save_object(author) + self.songmanager.save_object(author, False) self.song.authors.append(author) author_item = QtGui.QListWidgetItem( unicode(author.display_name)) @@ -342,7 +342,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes: topic = Topic.populate(name=text) - self.songmanager.save_object(topic) + self.songmanager.save_object(topic, False) self.song.topics.append(topic) topic_item = QtGui.QListWidgetItem(unicode(topic.name)) topic_item.setData(QtCore.Qt.UserRole,