This commit is contained in:
Jonathan Corwin 2010-07-21 20:31:24 +01:00
commit 7a0339c496
39 changed files with 1225 additions and 621 deletions

View File

@ -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()
@ -178,36 +182,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 +227,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 +235,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

View File

@ -287,16 +287,16 @@ class GeneralTab(SettingsTab):
Translate the general settings tab to the currently selected language
"""
self.MonitorGroupBox.setTitle(translate('GeneralTab', 'Monitors'))
self.MonitorLabel.setText(
translate('OpenLP.GeneralTab', 'Select monitor for output display:'))
self.MonitorLabel.setText(translate('OpenLP.GeneralTab',
'Select monitor for output display:'))
self.DisplayOnMonitorCheck.setText(
translate('OpenLP.GeneralTab', 'Display if a single screen'))
self.StartupGroupBox.setTitle(
translate('OpenLP.GeneralTab', 'Application Startup'))
self.WarningCheckBox.setText(
translate('OpenLP.GeneralTab', 'Show blank screen warning'))
self.AutoOpenCheckBox.setText(
translate('OpenLP.GeneralTab', 'Automatically open the last service'))
self.AutoOpenCheckBox.setText(translate('OpenLP.GeneralTab',
'Automatically open the last service'))
self.ShowSplashCheckBox.setText(
translate('OpenLP.GeneralTab', 'Show the splash screen'))
self.SettingsGroupBox.setTitle(translate('OpenLP.GeneralTab',
@ -318,7 +318,8 @@ class GeneralTab(SettingsTab):
self.currentXValueLabel.setText(u'0')
self.currentYLabel.setText(translate('OpenLP.GeneralTab', 'Y'))
self.currentYValueLabel.setText(u'0')
self.currentHeightLabel.setText(translate('OpenLP.GeneralTab', 'Height'))
self.currentHeightLabel.setText(
translate('OpenLP.GeneralTab', 'Height'))
self.currentHeightValueLabel.setText(u'0')
self.currentWidthLabel.setText(translate('OpenLP.GeneralTab', 'Width'))
self.currentWidthValueLabel.setText(u'0')
@ -375,10 +376,12 @@ class GeneralTab(SettingsTab):
QtCore.QVariant(self.screens.current[u'size'].x())).toString())
self.customYValueEdit.setText(settings.value(u'y position',
QtCore.QVariant(self.screens.current[u'size'].y())).toString())
self.customHeightValueEdit.setText(settings.value(u'height',
QtCore.QVariant(self.screens.current[u'size'].height())).toString())
self.customWidthValueEdit.setText(settings.value(u'width',
QtCore.QVariant(self.screens.current[u'size'].width())).toString())
self.customHeightValueEdit.setText(
settings.value(u'height', QtCore.QVariant(
self.screens.current[u'size'].height())).toString())
self.customWidthValueEdit.setText(
settings.value(u'width', QtCore.QVariant(
self.screens.current[u'size'].width())).toString())
else:
self.customXValueEdit.setText(
unicode(self.screens.current[u'size'].x()))

View File

@ -264,6 +264,10 @@ class MainDisplay(DisplayWidget):
(self.screen[u'size'].width() - splash_image.width()) / 2,
(self.screen[u'size'].height() - splash_image.height()) / 2,
splash_image)
#build a blank transparent image
self.transparent = QtGui.QPixmap(
self.screen[u'size'].width(), self.screen[u'size'].height())
self.transparent.fill(QtCore.Qt.transparent)
self.displayImage(self.initialFrame)
self.repaint()
#Build a Black screen
@ -274,12 +278,6 @@ class MainDisplay(DisplayWidget):
QtGui.QImage.Format_ARGB32_Premultiplied)
painter.begin(self.blankFrame)
painter.fillRect(self.blankFrame.rect(), QtCore.Qt.black)
#build a blank transparent image
self.transparent = QtGui.QPixmap(
self.screen[u'size'].width(), self.screen[u'size'].height())
self.transparent.fill(QtCore.Qt.transparent)
# self.displayText.setPixmap(self.transparent)
#self.frameView(self.transparent)
# To display or not to display?
if not self.screen[u'primary']:
self.setVisible(True)
@ -410,6 +408,7 @@ class MainDisplay(DisplayWidget):
self.imageDisplay.setPixmap(QtGui.QPixmap.fromImage(frame))
else:
self.imageDisplay.setPixmap(frame)
self.frameView(self.transparent)
self.videoDisplay.setHtml(u'<html></html>')
def displayVideo(self, path):

View File

@ -108,8 +108,8 @@ class ServiceManager(QtGui.QWidget):
self.droppos = 0
#is a new service and has not been saved
self.isNew = True
self.serviceNoteForm = ServiceNoteForm()
self.serviceItemEditForm = ServiceItemEditForm()
self.serviceNoteForm = ServiceNoteForm(self.parent)
self.serviceItemEditForm = ServiceItemEditForm(self.parent)
#start with the layout
self.Layout = QtGui.QVBoxLayout(self)
self.Layout.setSpacing(0)

View File

@ -211,6 +211,14 @@ class ThemeManager(QtGui.QWidget):
'You must select a theme to delete.')):
item = self.ThemeListWidget.currentItem()
theme = unicode(item.text())
# confirm deletion
answer = QtGui.QMessageBox.question(self,
translate('ThemeManager', 'Delete Confirmation'),
translate('ThemeManager', 'Delete theme?'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No), QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.No:
return
# should be the same unless default
if theme != unicode(item.data(QtCore.Qt.UserRole).toString()):
QtGui.QMessageBox.critical(self,
@ -245,15 +253,14 @@ class ThemeManager(QtGui.QWidget):
The theme to delete.
"""
self.themelist.remove(theme)
th = theme + u'.png'
thumb = theme + u'.png'
try:
os.remove(os.path.join(self.path, th))
os.remove(os.path.join(self.thumbPath, th))
os.remove(os.path.join(self.path, thumb))
os.remove(os.path.join(self.thumbPath, thumb))
encoding = get_filesystem_encoding()
shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
except OSError:
#if not present do not worry
pass
log.exception(u'Error deleting theme %s', theme)
# As we do not reload the themes push out the change
# Reaload the list as the internal lists and events need
# to be triggered
@ -597,19 +604,21 @@ class ThemeManager(QtGui.QWidget):
if newThemeIndex != -1:
self.serviceComboBox.setCurrentIndex(newThemeIndex)
if self.editingDefault:
newThemeItem = self.ThemeListWidget.findItems(name,
QtCore.Qt.MatchExactly)[0]
newThemeIndex = self.ThemeListWidget.indexFromItem(
newThemeItem).row()
self.global_theme = unicode(
self.ThemeListWidget.item(newThemeIndex).text())
newName = unicode(translate('ThemeManager', '%s (default)')) % \
self.global_theme
self.ThemeListWidget.item(newThemeIndex).setText(newName)
QtCore.QSettings().setValue(
self.settingsSection + u'/global theme',
QtCore.QVariant(self.global_theme))
Receiver.send_message(u'theme_update_global', self.global_theme)
if self.saveThemeName != name:
newThemeItem = self.ThemeListWidget.findItems(name,
QtCore.Qt.MatchExactly)[0]
newThemeIndex = self.ThemeListWidget.indexFromItem(
newThemeItem).row()
self.global_theme = unicode(
self.ThemeListWidget.item(newThemeIndex).text())
newName = unicode(translate('ThemeManager',
'%s (default)')) % self.global_theme
self.ThemeListWidget.item(newThemeIndex).setText(newName)
QtCore.QSettings().setValue(
self.settingsSection + u'/global theme',
QtCore.QVariant(self.global_theme))
Receiver.send_message(u'theme_update_global',
self.global_theme)
self.editingDefault = False
self.pushThemes()
else:

View File

@ -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))

View File

@ -40,7 +40,7 @@ class BiblePlugin(Plugin):
self.weight = -9
self.icon_path = u':/plugins/plugin_bibles.png'
self.icon = build_icon(self.icon_path)
#Register the bible Manager
# Register the bible Manager.
self.status = PluginStatus.Active
self.manager = None
@ -62,7 +62,7 @@ class BiblePlugin(Plugin):
return BiblesTab(self.name)
def getMediaManagerItem(self):
# Create the BibleManagerItem object
# Create the BibleManagerItem object.
return BibleMediaItem(self, self.icon, self.name)
def addImportMenuItem(self, import_menu):
@ -71,7 +71,7 @@ class BiblePlugin(Plugin):
import_menu.addAction(self.ImportBibleItem)
self.ImportBibleItem.setText(
translate('BiblePlugin', '&Bible'))
# Signals and slots
# signals and slots
QtCore.QObject.connect(self.ImportBibleItem,
QtCore.SIGNAL(u'triggered()'), self.onBibleImportClick)
self.ImportBibleItem.setVisible(False)

View File

@ -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):

View File

@ -129,8 +129,7 @@ class BibleMediaItem(MediaManagerItem):
self.QuickClearLabel.setObjectName(u'QuickSearchLabel')
self.QuickLayout.addWidget(self.QuickClearLabel, 4, 0, 1, 1)
self.ClearQuickSearchComboBox = QtGui.QComboBox(self.QuickTab)
self.ClearQuickSearchComboBox.setObjectName(
u'ClearQuickSearchComboBox')
self.ClearQuickSearchComboBox.setObjectName(u'ClearQuickSearchComboBox')
self.QuickLayout.addWidget(self.ClearQuickSearchComboBox, 4, 1, 1, 2)
self.QuickSearchButtonLayout = QtGui.QHBoxLayout()
self.QuickSearchButtonLayout.setMargin(0)
@ -168,8 +167,7 @@ class BibleMediaItem(MediaManagerItem):
self.AdvancedVersionComboBox.setObjectName(u'AdvancedVersionComboBox')
self.AdvancedLayout.addWidget(self.AdvancedVersionComboBox, 0, 1, 1, 2)
self.AdvancedSecondBibleLabel = QtGui.QLabel(self.AdvancedTab)
self.AdvancedSecondBibleLabel.setObjectName(
u'AdvancedSecondBibleLabel')
self.AdvancedSecondBibleLabel.setObjectName(u'AdvancedSecondBibleLabel')
self.AdvancedLayout.addWidget(self.AdvancedSecondBibleLabel, 1, 0, 1, 1)
self.AdvancedSecondBibleComboBox = QtGui.QComboBox(self.AdvancedTab)
self.AdvancedSecondBibleComboBox.setSizeAdjustPolicy(
@ -223,8 +221,7 @@ class BibleMediaItem(MediaManagerItem):
u'AdvancedSearchButtonLayout')
self.AdvancedSearchButtonSpacer = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.AdvancedSearchButtonLayout.addItem(
self.AdvancedSearchButtonSpacer)
self.AdvancedSearchButtonLayout.addItem(self.AdvancedSearchButtonSpacer)
self.AdvancedSearchButton = QtGui.QPushButton(self.AdvancedTab)
self.AdvancedSearchButton.setObjectName(u'AdvancedSearchButton')
self.AdvancedSearchButtonLayout.addWidget(self.AdvancedSearchButton)
@ -511,14 +508,14 @@ class BibleMediaItem(MediaManagerItem):
reference = bitem.data(QtCore.Qt.UserRole)
if isinstance(reference, QtCore.QVariant):
reference = reference.toPyObject()
bible = self._decodeQtObject(reference, 'bible')
#bible = self._decodeQtObject(reference, 'bible')
book = self._decodeQtObject(reference, 'book')
chapter = self._decodeQtObject(reference, 'chapter')
verse = self._decodeQtObject(reference, 'verse')
text = self._decodeQtObject(reference, 'text')
version = self._decodeQtObject(reference, 'version')
copyright = self._decodeQtObject(reference, 'copyright')
permission = self._decodeQtObject(reference, 'permission')
#permission = self._decodeQtObject(reference, 'permission')
if self.parent.settings_tab.display_style == 1:
verse_text = self.formatVerse(old_chapter, chapter, verse,
u'(u', u')')
@ -618,8 +615,7 @@ class BibleMediaItem(MediaManagerItem):
else:
self.AdvancedSearchButton.setEnabled(True)
self.AdvancedMessage.setText(u'')
self.adjustComboBox(1, self.chapters_from,
self.AdvancedFromChapter)
self.adjustComboBox(1, self.chapters_from, self.AdvancedFromChapter)
self.adjustComboBox(1, self.chapters_from, self.AdvancedToChapter)
self.adjustComboBox(1, self.verses, self.AdvancedFromVerse)
self.adjustComboBox(1, self.verses, self.AdvancedToVerse)

View File

@ -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)

View File

@ -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.

View File

@ -97,7 +97,7 @@ class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog):
if QtGui.QMessageBox.critical(
self, translate('SongsPlugin.AuthorsForm', 'Error'),
translate('SongsPlugin.AuthorsForm',
'You haven\'t set a display name for the '
'You have not set a display name for the '
'author, would you like me to combine the first and '
'last names for you?'),
QtGui.QMessageBox.StandardButtons(

View File

@ -430,7 +430,7 @@ class Ui_EditSongDialog(object):
self.AuthorRemoveButton.setText(
translate('SongsPlugin.EditSongForm', '&Remove'))
self.MaintenanceButton.setText(translate('SongsPlugin.EditSongForm',
'&Manage Authors, Topics, Books'))
'&Manage Authors, Topics, Song Books'))
self.TopicGroupBox.setTitle(
translate('SongsPlugin.EditSongForm', 'Topic'))
self.TopicAddButton.setText(
@ -441,7 +441,8 @@ class Ui_EditSongDialog(object):
translate('SongsPlugin.EditSongForm', 'Song Book'))
self.SongTabWidget.setTabText(
self.SongTabWidget.indexOf(self.AuthorsTab),
translate('SongsPlugin.EditSongForm', 'Authors, Topics && Book'))
translate('SongsPlugin.EditSongForm',
'Authors, Topics && Song Book'))
self.ThemeGroupBox.setTitle(
translate('SongsPlugin.EditSongForm', 'Theme'))
self.ThemeAddButton.setText(

View File

@ -48,7 +48,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
"""
QtGui.QDialog.__init__(self, parent)
self.parent = parent
#can this be automated?
# can this be automated?
self.width = 400
self.setupUi(self)
# Connecting signals and slots
@ -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:
@ -169,7 +171,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.loadAuthors()
self.loadTopics()
self.loadBooks()
#it's a new song to preview is not possible
# it's a new song to preview is not possible
self.previewButton.setVisible(False)
def loadSong(self, id, preview):
@ -180,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)
title = self.song.search_title.split(u'@')
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)
@ -198,8 +203,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:
@ -218,7 +221,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.CCLNumberEdit.setText(self.song.ccli_number)
else:
self.CCLNumberEdit.setText(u'')
#lazy xml migration for now
# lazy xml migration for now
self.VerseListWidget.clear()
self.VerseListWidget.setRowCount(0)
self.VerseListWidget.setColumnWidth(0, self.width)
@ -260,7 +263,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
topic_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id))
self.TopicsListView.addItem(topic_name)
self.TitleEditItem.setFocus(QtCore.Qt.OtherFocusReason)
#if not preview hide the preview button
# if not preview hide the preview button
self.previewButton.setVisible(False)
if preview:
self.previewButton.setVisible(True)
@ -285,12 +288,16 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
if QtGui.QMessageBox.question(self,
translate('SongsPlugin.EditSongForm', 'Add Author'),
translate('SongsPlugin.EditSongForm', 'This author does not '
'exist, do you want to add them?'),
'exist, do you want to add them?'),
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
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)
if text.find(u' ') == -1:
author = Author.populate(first_name=u'', last_name=u'',
display_name=text)
else:
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, False)
self.song.authors.append(author)
author_item = QtGui.QListWidgetItem(
unicode(author.display_name))
@ -304,18 +311,26 @@ 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,
translate('SongsPlugin.EditSongForm', 'No Author Selected'),
translate('SongsPlugin.EditSongForm', 'You have not selected '
'a valid author. Either select an author from the list, '
'or type in a new author and click the "Add Author to '
'Song" button to add the new author.'),
'a valid author. Either select an author from the list, '
'or type in a new author and click the "Add Author to '
'Song" button to add the new author.'),
QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)
def onAuthorsListViewPressed(self):
@ -338,11 +353,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
if QtGui.QMessageBox.question(self,
translate('SongsPlugin.EditSongForm', 'Add Topic'),
translate('SongsPlugin.EditSongForm', 'This topic does not '
'exist, do you want to add it?'),
'exist, do you want to add it?'),
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,
@ -355,18 +370,25 @@ 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,
translate('SongsPlugin.EditSongForm', 'No Topic Selected'),
translate('SongsPlugin.EditSongForm', 'You have not selected '
'a valid topic. Either select a topic from the list, or '
'type in a new topic and click the "Add Topic to Song" '
'button to add the new topic.'),
'a valid topic. Either select a topic from the list, or '
'type in a new topic and click the "Add Topic to Song" '
'button to add the new topic.'),
QtGui.QMessageBox.Ok, QtGui.QMessageBox.Ok)
def onTopicListViewPressed(self):
@ -382,23 +404,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:
@ -406,7 +412,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
def onThemeComboChanged(self, item):
if item == 0:
#None means no Theme
# None means no Theme
self.song.theme_name = None
else:
them_name = unicode(self.ThemeSelectionComboItem.itemText(item))
@ -443,7 +449,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
data = u'%s:%s' % (verse, subVerse)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(data))
item.setText(afterText)
#number of lines has change so repaint the list moving the data
# number of lines has change so repaint the list moving the data
if len(tempText.split(u'\n')) != len(afterText.split(u'\n')):
tempList = {}
tempId = {}
@ -482,7 +488,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
for count, parts in enumerate(match.split(u']---\n')):
if len(parts) > 1:
if count == 0:
#make sure the tag is correctly cased
# make sure the tag is correctly cased
variant = u'%s%s' % \
(parts[0:1].upper(), parts[1:].lower())
else:
@ -519,7 +525,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.EditSongForm', 'Error'),
translate('SongsPlugin.EditSongForm',
'You need to type in a song title.'))
'You need to type in a song title.'))
return False
if self.VerseListWidget.rowCount() == 0:
self.SongTabWidget.setCurrentIndex(0)
@ -527,7 +533,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.EditSongForm', 'Error'),
translate('SongsPlugin.EditSongForm',
'You need to type in at least one verse.'))
'You need to type in at least one verse.'))
return False
if self.AuthorsListView.count() == 0:
self.SongTabWidget.setCurrentIndex(1)
@ -535,8 +541,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
answer = QtGui.QMessageBox.warning(self,
translate('SongsPlugin.EditSongForm', 'Warning'),
translate('SongsPlugin.EditSongForm',
'You have not added any authors for this song. Do you '
'want to add an author now?'),
'You have not added any authors for this song. Do you '
'want to add an author now?'),
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.Yes:
return False
@ -567,9 +573,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.EditSongForm', 'Error'),
unicode(translate('SongsPlugin.EditSongForm',
'The verse order is invalid. There is no verse '
'corresponding to %s. Valid entries are %s.')) % \
(order_names[count], valid))
'The verse order is invalid. There is no verse '
'corresponding to %s. Valid entries are %s.')) % \
(order_names[count], valid))
return False
for count, verse in enumerate(verses):
if verse not in order:
@ -578,10 +584,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
answer = QtGui.QMessageBox.warning(self,
translate('SongsPlugin.EditSongForm', 'Warning'),
unicode(translate('SongsPlugin.EditSongForm',
'You have not used %s anywhere in the verse '
'order. Are you sure you want to save the song '
'like this?')) % \
verse_names[count].replace(u':', u' '),
'You have not used %s anywhere in the verse '
'order. Are you sure you want to save the song '
'like this?')) % verse_names[count].replace(u':', u' '),
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.No:
return False
@ -616,12 +621,28 @@ 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, publisher=u'')
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()
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())

View File

@ -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)
@ -68,7 +70,7 @@ class Ui_SongBookDialog(object):
def retranslateUi(self, SongBookDialog):
SongBookDialog.setWindowTitle(
translate('SongsPlugin.SongBookForm', 'Edit Book'))
translate('SongsPlugin.SongBookForm', 'Song Book Maintenance'))
self.NameLabel.setText(translate('SongsPlugin.SongBookForm', '&Name:'))
self.PublisherLabel.setText(
translate('SongsPlugin.SongBookForm', '&Publisher:'))

View File

@ -178,7 +178,8 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
SettingsManager.get_last_dir(self.songsplugin.settingsSection, 1))
if filename:
editbox.setText(filename)
self.config.set_last_dir(filename, 1)
SettingsManager.set_last_dir(self.songsplugin.settingsSection,
filename, 1)
def incrementProgressBar(self, status_text):
log.debug(u'IncrementBar %s', status_text)
@ -253,4 +254,4 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.ImportProgressBar.setValue(self.ImportProgressBar.maximum())
self.finishButton.setVisible(True)
self.cancelButton.setVisible(False)
Receiver.send_message(u'process_events')
Receiver.send_message(u'process_events')

View File

@ -109,16 +109,16 @@ class Ui_SongImportWizard(object):
self.OpenLyricsButtonLayout.setSpacing(8)
self.OpenLyricsButtonLayout.setObjectName(u'OpenLyricsButtonLayout')
self.OpenLyricsAddButton = QtGui.QPushButton(self.OpenLyricsPage)
self.OpenLyricsAddButton.setIcon(
build_icon(u':/general/general_open.png'))
openIcon = build_icon(u':/general/general_open.png')
self.OpenLyricsAddButton.setIcon(openIcon)
self.OpenLyricsAddButton.setObjectName(u'OpenLyricsAddButton')
self.OpenLyricsButtonLayout.addWidget(self.OpenLyricsAddButton)
self.OpenLyricsButtonSpacer = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.OpenLyricsButtonLayout.addItem(self.OpenLyricsButtonSpacer)
self.OpenLyricsRemoveButton = QtGui.QPushButton(self.OpenLyricsPage)
self.OpenLyricsRemoveButton.setIcon(
build_icon(u':/general/general_delete.png'))
deleteIcon = build_icon(u':/general/general_delete.png')
self.OpenLyricsRemoveButton.setIcon(deleteIcon)
self.OpenLyricsRemoveButton.setObjectName(u'OpenLyricsRemoveButton')
self.OpenLyricsButtonLayout.addWidget(self.OpenLyricsRemoveButton)
self.OpenLyricsLayout.addLayout(self.OpenLyricsButtonLayout)
@ -136,14 +136,14 @@ class Ui_SongImportWizard(object):
self.OpenSongButtonLayout.setSpacing(8)
self.OpenSongButtonLayout.setObjectName(u'OpenSongButtonLayout')
self.OpenSongAddButton = QtGui.QPushButton(self.OpenSongPage)
self.OpenSongAddButton.setIcon(self.OpenIcon)
self.OpenSongAddButton.setIcon(openIcon)
self.OpenSongAddButton.setObjectName(u'OpenSongAddButton')
self.OpenSongButtonLayout.addWidget(self.OpenSongAddButton)
self.OpenSongButtonSpacer = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.OpenSongButtonLayout.addItem(self.OpenSongButtonSpacer)
self.OpenSongRemoveButton = QtGui.QPushButton(self.OpenSongPage)
self.OpenSongRemoveButton.setIcon(self.DeleteIcon)
self.OpenSongRemoveButton.setIcon(deleteIcon)
self.OpenSongRemoveButton.setObjectName(u'OpenSongRemoveButton')
self.OpenSongButtonLayout.addWidget(self.OpenSongRemoveButton)
self.OpenSongLayout.addLayout(self.OpenSongButtonLayout)
@ -161,14 +161,14 @@ class Ui_SongImportWizard(object):
self.CCLIButtonLayout.setSpacing(8)
self.CCLIButtonLayout.setObjectName(u'CCLIButtonLayout')
self.CCLIAddButton = QtGui.QPushButton(self.CCLIPage)
self.CCLIAddButton.setIcon(self.OpenIcon)
self.CCLIAddButton.setIcon(openIcon)
self.CCLIAddButton.setObjectName(u'CCLIAddButton')
self.CCLIButtonLayout.addWidget(self.CCLIAddButton)
self.CCLIButtonSpacer = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.CCLIButtonLayout.addItem(self.CCLIButtonSpacer)
self.CCLIRemoveButton = QtGui.QPushButton(self.CCLIPage)
self.CCLIRemoveButton.setIcon(self.DeleteIcon)
self.CCLIRemoveButton.setIcon(deleteIcon)
self.CCLIRemoveButton.setObjectName(u'CCLIRemoveButton')
self.CCLIButtonLayout.addWidget(self.CCLIRemoveButton)
self.CCLILayout.addLayout(self.CCLIButtonLayout)
@ -190,7 +190,7 @@ class Ui_SongImportWizard(object):
self.CSVFilenameEdit.setObjectName(u'CSVFilenameEdit')
self.CSVFileLayout.addWidget(self.CSVFilenameEdit)
self.CSVBrowseButton = QtGui.QToolButton(self.CSVPage)
self.CSVBrowseButton.setIcon(self.OpenIcon)
self.CSVBrowseButton.setIcon(openIcon)
self.CSVBrowseButton.setObjectName(u'CSVBrowseButton')
self.CSVFileLayout.addWidget(self.CSVBrowseButton)
self.CSVLayout.setLayout(0, QtGui.QFormLayout.FieldRole,
@ -213,14 +213,11 @@ class Ui_SongImportWizard(object):
self.ImportProgressBar.setObjectName(u'ImportProgressBar')
self.ImportLayout.addWidget(self.ImportProgressBar)
SongImportWizard.addPage(self.ImportPage)
self.retranslateUi(SongImportWizard)
self.FormatStackedWidget.setCurrentIndex(0)
QtCore.QObject.connect(
self.FormatComboBox,
QtCore.QObject.connect(self.FormatComboBox,
QtCore.SIGNAL(u'currentIndexChanged(int)'),
self.FormatStackedWidget.setCurrentIndex
)
self.FormatStackedWidget.setCurrentIndex)
QtCore.QMetaObject.connectSlotsByName(SongImportWizard)
def retranslateUi(self, SongImportWizard):
@ -275,4 +272,3 @@ class Ui_SongImportWizard(object):
translate('SongsPlugin.ImportWizardForm', 'Ready.'))
self.ImportProgressBar.setFormat(
translate('SongsPlugin.ImportWizardForm', '%p%'))

View File

@ -217,7 +217,7 @@ class Ui_SongMaintenanceDialog(object):
self.TypeListWidget.item(1).setText(
translate('SongsPlugin.SongMaintenanceForm', 'Topics'))
self.TypeListWidget.item(2).setText(
translate('SongsPlugin.SongMaintenanceForm', 'Books/Hymnals'))
translate('SongsPlugin.SongMaintenanceForm', 'Song Books'))
self.AuthorAddButton.setText(
translate('SongsPlugin.SongMaintenanceForm', '&Add'))
self.AuthorEditButton.setText(

View File

@ -26,9 +26,9 @@
from PyQt4 import QtGui, QtCore
from sqlalchemy.sql import and_
from openlp.core.lib import translate
from openlp.core.lib import Receiver, translate
from openlp.plugins.songs.forms import AuthorsForm, TopicsForm, SongBookForm
from openlp.plugins.songs.lib.db import Author, Book, Topic
from openlp.plugins.songs.lib.db import Author, Book, Topic, Song
from songmaintenancedialog import Ui_SongMaintenanceDialog
class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
@ -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))
@ -137,16 +139,13 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
def checkAuthor(self, new_author, edit=False):
"""
Returns False when the given Author is already in the list elsewise
Returns False if the given Author is already in the list otherwise
True.
"""
authors = self.songmanager.get_all_objects_filtered(Author,
and_(
Author.first_name == new_author.first_name,
Author.last_name == new_author.last_name,
Author.display_name == new_author.display_name
)
)
authors = self.songmanager.get_all_objects(Author,
and_(Author.first_name == new_author.first_name,
Author.last_name == new_author.last_name,
Author.display_name == new_author.display_name))
if len(authors) > 0:
# If we edit an existing Author, we need to make sure that we do
# not return False when nothing has changed (because this would
@ -163,11 +162,10 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
def checkTopic(self, new_topic, edit=False):
"""
Returns False when the given Topic is already in the list elsewise True.
Returns False if the given Topic is already in the list otherwise True.
"""
topics = self.songmanager.get_all_objects_filtered(Topic,
Topic.name == new_topic.name
)
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
# not return False when nothing has changed (because this would
@ -184,25 +182,22 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
def checkBook(self, new_book, edit=False):
"""
Returns False when the given Book is already in the list elsewise True.
Returns False if the given Book is already in the list otherwise True.
"""
books = self.songmanager.get_all_objects_filtered(Book,
and_(
Book.name == new_book.name,
Book.publisher == new_book.publisher
)
)
books = self.songmanager.get_all_objects(Book,
and_(Book.name == new_book.name,
Book.publisher == new_book.publisher))
if len(books) > 0:
# If we edit an existing Book, we need to make sure that we do
# not return False when nothing has changed (because this would
# cause an error message later on).
if edit:
if books[0].id == new_book.id:
return True
else:
return False
# If we edit an existing Book, we need to make sure that we do
# not return False when nothing has changed (because this would
# cause an error message later on).
if edit:
if books[0].id == new_book.id:
return True
else:
return False
else:
return False
else:
return True
@ -216,11 +211,16 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
if self.checkAuthor(author):
if self.songmanager.save_object(author):
self.resetAuthors()
else:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
'Could not add your author.'))
else:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
'Could not add your author.'))
'This author already exists.'))
def onTopicAddButtonClick(self):
if self.topicform.exec_():
@ -228,25 +228,34 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
if self.checkTopic(topic):
if self.songmanager.save_object(topic):
self.resetTopics()
else:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
'Could not add your topic.'))
else:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
'Could not add your topic.'))
'This topic already exists.'))
def onBookAddButtonClick(self):
if self.bookform.exec_():
book = Book.populate(
name=unicode(self.bookform.NameEdit.text()),
book = Book.populate(name=unicode(self.bookform.NameEdit.text()),
publisher=unicode(self.bookform.PublisherEdit.text()))
if self.checkBook(book):
if self.songmanager.save_object(book):
self.resetBooks()
else:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
'Could not add your book.'))
else:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
'Could not add your book.'))
'This book already exists.'))
def onAuthorEditButtonClick(self):
author_id = self._getCurrentItemId(self.AuthorsListWidget)
@ -270,6 +279,24 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
if self.checkAuthor(author, True):
if self.songmanager.save_object(author):
self.resetAuthors()
Receiver.send_message(u'songs_load_list')
else:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm',
'Error'),
translate('SongsPlugin.SongMaintenanceForm',
'Could not save your changes.'))
elif QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm', 'The author %s'
' already exists. Would you like to make songs with author '
'%s use the existing author %s?' % (author.display_name,
temp_display_name, author.display_name)),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes:
self.mergeAuthors(author)
self.resetAuthors()
Receiver.send_message(u'songs_load_list')
else:
# We restore the author's old first and last name as well as
# his display name.
@ -279,7 +306,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
'Could not save your author.'))
'Could not save your modified author, because he '
'already exists.'))
def onTopicEditButtonClick(self):
topic_id = self._getCurrentItemId(self.TopicsListWidget)
@ -293,18 +321,37 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
if self.checkTopic(topic, True):
if self.songmanager.save_object(topic):
self.resetTopics()
else:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm',
'Error'),
translate('SongsPlugin.SongMaintenanceForm',
'Could not save your changes.'))
elif QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm', 'The topic %s '
'already exists. Would you like to make songs with topic %s'
' use the existing topic %s?' % (topic.name, temp_name,
topic.name)),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes:
self.mergeTopics(topic)
self.resetTopics()
else:
# We restore the topics's old name.
topic.name = temp_name
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
'Could not save your topic.'))
'Could not save your modified topic, because it '
'already exists.'))
def onBookEditButtonClick(self):
book_id = self._getCurrentItemId(self.BooksListWidget)
if book_id != -1:
book = self.songmanager.get_object(Book, book_id)
if book.publisher is None:
book.publisher = u''
self.bookform.NameEdit.setText(book.name)
self.bookform.PublisherEdit.setText(book.publisher)
# Save the book's name and publisher for the case that they have to
@ -317,18 +364,89 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
if self.checkBook(book, True):
if self.songmanager.save_object(book):
self.resetBooks()
else:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm',
'Error'),
translate('SongsPlugin.SongMaintenanceForm',
'Could not save your changes.'))
elif QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm', 'The book %s '
'already exists. Would you like to make songs with book %s '
'use the existing book %s?' % (book.name, temp_name,
book.name)),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes:
self.mergeBooks(book)
self.resetBooks()
else:
# We restore the book's old name and publisher.
book.name = temp_name
book.publisher = temp_publisher
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
translate('SongsPlugin.SongMaintenanceForm',
'Could not save your book.'))
def mergeAuthors(self, old_author):
'''
Merges two authors into one author.
``old_author``
The author which will be deleted afterwards.
'''
existing_author = self.songmanager.get_object_filtered(Author,
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(Song,
Song.authors.contains(old_author))
for song in songs:
# We check if the song has already existing_author as author. If
# that is not the case we add it.
if existing_author not in song.authors:
song.authors.append(existing_author)
song.authors.remove(old_author)
self.songmanager.save_object(song)
self.songmanager.delete_object(Author, old_author.id)
def mergeTopics(self, old_topic):
'''
Merges two topics into one topic.
``old_topic``
The topic which will be deleted afterwards.
'''
existing_topic = self.songmanager.get_object_filtered(Topic,
Topic.name == old_topic.name)
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
# is not the case we add it.
if existing_topic not in song.topics:
song.topics.append(existing_topic)
song.topics.remove(old_topic)
self.songmanager.save_object(song)
self.songmanager.delete_object(Topic, old_topic.id)
def mergeBooks(self, old_book):
'''
Merges two books into one book.
``old_book``
The book which will be deleted afterwards.
'''
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(Song,
Song.song_book_id == old_book.id)
for song in songs:
song.song_book_id = existing_book.id
self.songmanager.save_object(song)
self.songmanager.delete_object(Book, old_book.id)
def onAuthorDeleteButtonClick(self):
"""
Delete the author if the author is not attached to any songs
Delete the author if the author is not attached to any songs.
"""
self._deleteItem(Author, self.AuthorsListWidget, self.resetAuthors,
translate('SongsPlugin.SongMaintenanceForm', 'Delete Author'),
@ -341,7 +459,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
def onTopicDeleteButtonClick(self):
"""
Delete the Book is the Book is not attached to any songs
Delete the Book is the Book is not attached to any songs.
"""
self._deleteItem(Topic, self.TopicsListWidget, self.resetTopics,
translate('SongsPlugin.SongMaintenanceForm', 'Delete Topic'),
@ -354,7 +472,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
def onBookDeleteButtonClick(self):
"""
Delete the Book is the Book is not attached to any songs
Delete the Book is the Book is not attached to any songs.
"""
self._deleteItem(Book, self.BooksListWidget, self.resetBooks,
translate('SongsPlugin.SongMaintenanceForm', 'Delete Book'),

View File

@ -141,6 +141,8 @@ from xml import LyricsXML, SongXMLBuilder, SongXMLParser
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

View File

@ -46,6 +46,12 @@ class Book(BaseModel):
return u'<Book id="%s" name="%s" publisher="%s" />' % (
str(self.id), self.name, self.publisher)
class MediaFile(BaseModel):
"""
MediaFile model
"""
pass
class Song(BaseModel):
"""
Song model
@ -75,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),
@ -88,6 +101,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)),
@ -113,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,
@ -125,6 +147,7 @@ def init_schema(url):
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)
@ -132,6 +155,10 @@ 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'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,
@ -139,12 +166,17 @@ def init_schema(url):
mapper(Author, authors_table)
mapper(Book, song_books_table)
mapper(MediaFile, media_files_table)
mapper(Song, songs_table,
properties={'authors': relation(Author, backref='songs',
secondary=authors_songs_table),
properties={
'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)})
secondary=songs_topics_table)
})
mapper(Topic, topics_table)
metadata.create_all(checkfirst=True)

View File

@ -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

View File

@ -0,0 +1,200 @@
# -*- 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 #, MediaFile
log = logging.getLogger(__name__)
class OldAuthor(BaseModel):
"""
Author model
"""
pass
class OldBook(BaseModel):
"""
Book model
"""
pass
class OldMediaFile(BaseModel):
"""
MediaFile 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'media_files' in source_meta.tables.keys():
has_media_files = True
else:
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_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(OldMediaFile)
except UnmappedClassError:
mapper(OldMediaFile, source_media_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_media_files:
song_props['media_files'] = relation(OldMediaFile, backref='songs',
secondary=source_media_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_media_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.book:
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_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()

View File

@ -0,0 +1,242 @@
# -*- 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 #
###############################################################################
import logging
import os
from zipfile import ZipFile
from lxml import objectify
from openlp.plugins.songs.lib.songimport import SongImport
log = logging.getLogger(__name__)
class OpenSongImportError(Exception):
pass
class OpenSongImport(object):
"""
Import songs exported from OpenSong - the format is described loosly here:
http://www.opensong.org/d/manual/song_file_format_specification
However, it doesn't describe the <lyrics> section, so here's an attempt:
Verses can be expressed in one of 2 ways:
<lyrics>
[v1]List of words
Another Line
[v2]Some words for the 2nd verse
etc...
</lyrics>
The 'v' can be left out - it is implied
or:
<lyrics>
[V]
1List of words
2Some words for the 2nd Verse
1Another Line
2etc...
</lyrics>
Either or both forms can be used in one song. The Number does not
necessarily appear at the start of the line
The [v1] labels can have either upper or lower case Vs
Other labels can be used also:
C - Chorus
B - Bridge
Guitar chords can be provided 'above' the lyrics (the line is
preceeded by a'.') and _s can be used to signify long-drawn-out
words:
. A7 Bm
1 Some____ Words
Chords and _s are removed by this importer.
The verses etc. are imported and tagged appropriately.
The <presentation> tag is used to populate the OpenLP verse
display order field. The Author and Copyright tags are also
imported to the appropriate places.
"""
def __init__(self, songmanager):
"""
Initialise the class. Requires a songmanager class which
is passed to SongImport for writing song to disk
"""
self.songmanager = songmanager
self.song = None
def do_import(self, filename, commit=True):
"""
Import either a single opensong file, or a zipfile
containing multiple opensong files If the commit parameter is
set False, the import will not be committed to the database
(useful for test scripts)
"""
ext = os.path.splitext(filename)[1]
if ext.lower() == ".zip":
log.info('Zipfile found %s', filename)
z = ZipFile(filename, u'r')
for song in z.infolist():
parts = os.path.split(song.filename)
if parts[-1] == u'':
#No final part => directory
continue
songfile = z.open(song)
self.do_import_file(songfile)
if commit:
self.finish()
else:
log.info('Direct import %s', filename)
file = open(filename)
self.do_import_file(file)
if commit:
self.finish()
def do_import_file(self, file):
"""
Process the OpenSong file - pass in a file-like object,
not a filename
"""
self.song_import = SongImport(self.songmanager)
tree = objectify.parse(file)
root = tree.getroot()
fields = dir(root)
decode = {u'copyright':self.song_import.add_copyright,
u'ccli':u'ccli_number',
u'author':self.song_import.parse_author,
u'title':u'title',
u'aka':u'alternate_title',
u'hymn_number':u'song_number'}
for (attr, fn_or_string) in decode.items():
if attr in fields:
ustring = unicode(root.__getattr__(attr))
if type(fn_or_string) == type(u''):
self.song_import.__setattr__(fn_or_string, ustring)
else:
fn_or_string(ustring)
if u'theme' in fields:
self.song_import.topics.append(unicode(root.theme))
if u'alttheme' in fields:
self.song_import.topics.append(unicode(root.alttheme))
# data storage while importing
verses = {}
lyrics = unicode(root.lyrics)
# keep track of a "default" verse order, in case none is specified
our_verse_order = []
verses_seen = {}
# in the absence of any other indication, verses are the default,
# erm, versetype!
versetype = u'V'
for thisline in lyrics.split(u'\n'):
# remove comments
semicolon = thisline.find(u';')
if semicolon >= 0:
thisline = thisline[:semicolon]
thisline = thisline.strip()
if len(thisline) == 0:
continue
# skip inthisline guitar chords and page and column breaks
if thisline[0] == u'.' or thisline.startswith(u'---') \
or thisline.startswith(u'-!!'):
continue
# verse/chorus/etc. marker
if thisline[0] == u'[':
versetype = thisline[1].upper()
if versetype.isdigit():
versenum = versetype
versetype = u'V'
elif thisline[2] != u']':
# there's a number to go with it - extract that as well
right_bracket = thisline.find(u']')
versenum = thisline[2:right_bracket]
else:
# if there's no number, assume it's no.1
versenum = u'1'
continue
words = None
# number at start of line.. it's verse number
if thisline[0].isdigit():
versenum = thisline[0]
words = thisline[1:].strip()
if words is None and \
versenum is not None and \
versetype is not None:
words = thisline
if versenum is not None:
versetag = u'%s%s' % (versetype, versenum)
if not verses.has_key(versetype):
verses[versetype] = {}
if not verses[versetype].has_key(versenum):
# storage for lines in this verse
verses[versetype][versenum] = []
if not verses_seen.has_key(versetag):
verses_seen[versetag] = 1
our_verse_order.append(versetag)
if words:
# Tidy text and remove the ____s from extended words
words = self.song_import.tidy_text(words)
words = words.replace('_', '')
verses[versetype][versenum].append(words)
# done parsing
versetypes = verses.keys()
versetypes.sort()
versetags = {}
for versetype in versetypes:
versenums = verses[versetype].keys()
versenums.sort()
for num in versenums:
versetag = u'%s%s' % (versetype, num)
lines = u'\n'.join(verses[versetype][num])
self.song_import.verses.append([versetag, lines])
# Keep track of what we have for error checking later
versetags[versetag] = 1
# now figure out the presentation order
if u'presentation' in fields and root.presentation != u'':
order = unicode(root.presentation)
order = order.split()
else:
assert len(our_verse_order)>0
order = our_verse_order
for tag in order:
if len(tag) == 1:
tag = tag + u'1' # Assume it's no.1 if it's not there
if not versetags.has_key(tag):
log.warn(u'Got order %s but not in versetags, skipping', tag)
else:
self.song_import.verse_order_list.append(tag)
def finish(self):
""" Separate function, allows test suite to not pollute database"""
self.song_import.finish()

View File

@ -142,7 +142,7 @@ class SofImport(OooImport):
self.blanklines += 1
if self.blanklines > 1:
return
if self.song.get_title() != u'':
if self.song.title != u'':
self.finish_verse()
return
self.blanklines = 0
@ -166,8 +166,8 @@ class SofImport(OooImport):
self.finish_verse()
self.song.repeat_verse()
return
if self.song.get_title() == u'':
if self.song.get_copyright() == u'':
if self.song.title == u'':
if self.song.copyright == u'':
self.add_author(text)
else:
self.song.add_copyright(text)
@ -187,10 +187,10 @@ class SofImport(OooImport):
return text
if textportion.CharWeight == BOLD:
boldtext = text.strip()
if boldtext.isdigit() and self.song.get_song_number() == '':
if boldtext.isdigit() and self.song.song_number == '':
self.add_songnumber(boldtext)
return u''
if self.song.get_title() == u'':
if self.song.title == u'':
text = self.uncap_text(text)
self.add_title(text)
return text
@ -220,20 +220,17 @@ class SofImport(OooImport):
Add a song number, store as alternate title. Also use the song
number to work out which songbook we're in
"""
self.song.set_song_number(song_no)
self.song.set_alternate_title(song_no + u'.')
self.song.song_number = song_no
self.song.alternate_title = song_no + u'.'
self.song.song_book_pub = u'Kingsway Publications'
if int(song_no) <= 640:
self.song.set_song_book(u'Songs of Fellowship 1',
u'Kingsway Publications')
self.song.song_book = u'Songs of Fellowship 1'
elif int(song_no) <= 1150:
self.song.set_song_book(u'Songs of Fellowship 2',
u'Kingsway Publications')
self.song.song_book = u'Songs of Fellowship 2'
elif int(song_no) <= 1690:
self.song.set_song_book(u'Songs of Fellowship 3',
u'Kingsway Publications')
self.song.song_book = u'Songs of Fellowship 3'
else:
self.song.set_song_book(u'Songs of Fellowship 4',
u'Kingsway Publications')
self.song.song_book = u'Songs of Fellowship 4'
def add_title(self, text):
"""
@ -245,7 +242,7 @@ class SofImport(OooImport):
title = title[1:]
if title.endswith(u','):
title = title[:-1]
self.song.set_title(title)
self.song.title = title
def add_author(self, text):
"""
@ -283,7 +280,7 @@ class SofImport(OooImport):
splitat = None
else:
versetag = u'V'
splitat = self.verse_splits(self.song.get_song_number())
splitat = self.verse_splits(self.song.song_number)
if splitat:
ln = 0
verse = u''
@ -538,4 +535,3 @@ class SofImport(OooImport):
if song_number == 1119:
return 7
return None

View File

@ -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 = []
@ -123,53 +123,10 @@ class SongImport(object):
if len(lines) == 1:
self.parse_author(lines[0])
return
if not self.get_title():
self.set_title(lines[0])
if not self.title:
self.title = lines[0]
self.add_verse(text)
def get_title(self):
"""
Return the title
"""
return self.title
def get_copyright(self):
"""
Return the copyright
"""
return self.copyright
def get_song_number(self):
"""
Return the song number
"""
return self.song_number
def set_title(self, title):
"""
Set the title
"""
self.title = title
def set_alternate_title(self, title):
"""
Set the alternate title
"""
self.alternate_title = title
def set_song_number(self, song_number):
"""
Set the song number
"""
self.song_number = song_number
def set_song_book(self, song_book, publisher):
"""
Set the song book name and publisher
"""
self.song_book_name = song_book
self.song_book_pub = publisher
def add_copyright(self, copyright):
"""
Build the copyright field
@ -184,8 +141,8 @@ class SongImport(object):
"""
Add the author. OpenLP stores them individually so split by 'and', '&'
and comma.
However need to check for "Mr and Mrs Smith" and turn it to
"Mr Smith" and "Mrs Smith".
However need to check for 'Mr and Mrs Smith' and turn it to
'Mr Smith' and 'Mrs Smith'.
"""
for author in text.split(u','):
authors = author.split(u'&')
@ -267,7 +224,7 @@ class SongImport(object):
def commit_song(self):
"""
Write the song and it's fields to disk
Write the song and its fields to disk
"""
song = Song()
song.title = self.title
@ -296,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:
@ -315,11 +272,13 @@ class SongImport(object):
publisher=self.song_book_pub)
song.book = song_book
for topictext in self.topics:
if len(topictext) == 0:
continue
topic = self.manager.get_object_filtered(Topic,
Topic.name == topictext)
if topic is None:
topic = Topic.populate(name=topictext)
song.topics.append(topictext)
song.topics.append(topic)
self.manager.save_object(song)
def print_song(self):
@ -345,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:

View File

@ -28,7 +28,6 @@ import logging
#import os
from types import ListType
from xml.etree.ElementTree import ElementTree, XML
# Do we need these two lines?
#sys.path.append(os.path.abspath(u'./../../../..'))
@ -60,175 +59,6 @@ class SongFeatureError(SongException):
# TODO: Song: Import ChangingSong
# TODO: Song: Export ChangingSong
_BLANK_OPENSONG_XML = \
'''<?xml version="1.0" encoding="UTF-8"?>
<song>
<title></title>
<author></author>
<copyright></copyright>
<presentation></presentation>
<ccli></ccli>
<lyrics></lyrics>
<theme></theme>
<alttheme></alttheme>
</song>
'''
class _OpenSong(object):
"""
Class for import of OpenSong
"""
def __init__(self, xmlContent = None):
"""
Initialize from given xml content
"""
self._set_from_xml(_BLANK_OPENSONG_XML, 'song')
if xmlContent:
self._set_from_xml(xmlContent, 'song')
def _set_from_xml(self, xml, root_tag):
"""
Set song properties from given xml content.
``xml``
Formatted xml tags and values.
``root_tag``
The root tag of the xml.
"""
root = ElementTree(element=XML(xml))
xml_iter = root.getiterator()
for element in xml_iter:
if element.tag != root_tag:
text = element.text
if text is None:
val = text
elif isinstance(text, basestring):
# Strings need special handling to sort the colours out
if text[0] == u'$':
# This might be a hex number, let's try to convert it.
try:
val = int(text[1:], 16)
except ValueError:
pass
else:
# Let's just see if it's a integer.
try:
val = int(text)
except ValueError:
# Ok, it seems to be a string.
val = text
if hasattr(self, u'post_tag_hook'):
(element.tag, val) = \
self.post_tag_hook(element.tag, val)
setattr(self, element.tag, val)
def __str__(self):
"""
Return string with all public attributes
The string is formatted with one attribute per line
If the string is split on newline then the length of the
list is equal to the number of attributes
"""
attributes = []
for attrib in dir(self):
if not attrib.startswith(u'_'):
attributes.append(
u'%30s : %s' % (attrib, getattr(self, attrib)))
return u'\n'.join(attributes)
def _get_as_string(self):
"""
Return one string with all public attributes
"""
result = u''
for attrib in dir(self):
if not attrib.startswith(u'_'):
result += u'_%s_' % getattr(self, attrib)
return result
def get_author_list(self):
"""Convert author field to an authorlist
in OpenSong an author list may be separated by '/'
return as a string
"""
if self.author:
list = self.author.split(u' and ')
res = [item.strip() for item in list]
return u', '.join(res)
def get_category_array(self):
"""Convert theme and alttheme into category_array
return as a string
"""
res = []
if self.theme:
res.append(self.theme)
if self.alttheme:
res.append(self.alttheme)
return u', u'.join(res)
def _reorder_verse(self, tag, tmpVerse):
"""
Reorder the verse in case of first char is a number
tag -- the tag of this verse / verse group
tmpVerse -- list of strings
"""
res = []
for digit in '1234567890 ':
tagPending = True
for line in tmpVerse:
if line.startswith(digit):
if tagPending:
tagPending = False
tagChar = tag.strip(u'[]').lower()
if 'v' == tagChar:
newtag = "Verse"
elif 'c' == tagChar:
newtag = "Chorus"
elif 'b' == tagChar:
newtag = "Bridge"
elif 'p' == tagChar:
newtag = "Pre-chorus"
else:
newtag = tagChar
tagString = (u'# %s %s' % (newtag, digit)).rstrip()
res.append(tagString)
res.append(line[1:])
if (len(line) == 0) and (not tagPending):
res.append(line)
return res
def get_lyrics(self):
"""
Convert the lyrics to openlp lyrics format
return as list of strings
"""
lyrics = self.lyrics.split(u'\n')
tmpVerse = []
finalLyrics = []
tag = ""
for lyric in lyrics:
line = lyric.rstrip()
if not line.startswith(u'.'):
# drop all chords
tmpVerse.append(line)
if line:
if line.startswith(u'['):
tag = line
else:
reorderedVerse = self._reorder_verse(tag, tmpVerse)
finalLyrics.extend(reorderedVerse)
tag = ""
tmpVerse = []
# catch up final verse
reorderedVerse = self._reorder_verse(tag, tmpVerse)
finalLyrics.extend(reorderedVerse)
return finalLyrics
class Song(object):
"""Handling song properties and methods
@ -275,7 +105,7 @@ class Song(object):
show_author_list -- 0: no show, 1: show
show_copyright -- 0: no show, 1: show
show_song_cclino -- 0: no show, 1: show
theme -- name of theme or blank
theme_name -- name of theme or blank
category_array -- list of user defined properties (hymn, gospel)
song_book -- name of originating book
song_number -- number of the song, related to a songbook
@ -298,7 +128,7 @@ class Song(object):
self.show_copyright = 1
self.show_song_cclino = 1
self.show_title = 1
self.theme = ""
self.theme_name = ""
self.category_array = None
self.song_book = ""
self.song_number = ""
@ -307,40 +137,6 @@ class Song(object):
self.set_lyrics(u'')
return
def from_opensong_buffer(self, xmlcontent):
"""Initialize from buffer(string) of xml lines in opensong format"""
self._reset()
opensong = _OpenSong(xmlcontent)
if opensong.title:
self.set_title(opensong.title)
if opensong.copyright:
self.set_copyright(opensong.copyright)
if opensong.presentation:
self.set_verse_order(opensong.presentation)
if opensong.ccli:
self.set_song_cclino(opensong.ccli)
self.set_author_list(opensong.get_author_list())
self.set_category_array(opensong.get_category_array())
self.set_lyrics(opensong.get_lyrics())
def from_opensong_file(self, xmlfilename):
"""
Initialize from file containing xml
xmlfilename -- path to xml file
"""
osfile = None
try:
osfile = open(xmlfilename, 'r')
list = [line for line in osfile]
osfile.close()
xml = "".join(list)
self.from_opensong_buffer(xml)
except IOError:
log.exception(u'Failed to load opensong xml file')
finally:
if osfile:
osfile.close()
def _remove_punctuation(self, title):
"""Remove the puntuation chars from title
@ -366,10 +162,6 @@ class Song(object):
if len(self.search_title) < 1:
raise SongTitleError(u'The searchable title is empty')
def get_title(self):
"""Return title value"""
return self.title
def from_ccli_text_buffer(self, textList):
"""
Create song from a list of texts (strings) - CCLI text format expected
@ -424,7 +216,7 @@ class Song(object):
self.set_title(sName)
self.set_author_list(author_list)
self.set_copyright(sCopyright)
self.set_song_cclino(sCcli)
self.set_ccli_number(sCcli)
self.set_lyrics(lyrics)
def from_ccli_text_file(self, textFileName):
@ -479,21 +271,21 @@ class Song(object):
"""Set the copyright string"""
self.copyright = copyright
def get_song_cclino(self):
def get_ccli_number(self):
"""Return the songCclino"""
return self._assure_string(self.song_cclino)
return self._assure_string(self.ccli_number)
def set_song_cclino(self, song_cclino):
"""Set the song_cclino"""
self.song_cclino = song_cclino
def set_ccli_number(self, ccli_number):
"""Set the ccli_number"""
self.ccli_number = ccli_number
def get_theme(self):
def get_theme_name(self):
"""Return the theme name for the song"""
return self._assure_string(self.theme)
return self._assure_string(self.theme_name)
def set_theme(self, theme):
def set_theme_name(self, theme_name):
"""Set the theme name (string)"""
self.theme = theme
self.theme_name = theme_name
def get_song_book(self):
"""Return the song_book (string)"""
@ -532,9 +324,9 @@ class Song(object):
asOneString
True -- string:
"John Newton, A Parker"
'John Newton, A Parker'
False -- list of strings
["John Newton", u'A Parker"]
['John Newton', u'A Parker']
"""
if asOneString:
res = self._assure_string(self.author_list)
@ -557,9 +349,9 @@ class Song(object):
asOneString
True -- string:
"Hymn, Gospel"
'Hymn, Gospel'
False -- list of strings
["Hymn", u'Gospel"]
['Hymn', u'Gospel']
"""
if asOneString:
res = self._assure_string(self.category_array)
@ -601,13 +393,13 @@ class Song(object):
"""Set the show_copyright flag (bool)"""
self.show_copyright = show_copyright
def get_show_song_cclino(self):
def get_show_ccli_number(self):
"""Return the showSongCclino (string)"""
return self.show_song_cclino
return self.show_ccli_number
def set_show_song_cclino(self, show_song_cclino):
"""Set the show_song_cclino flag (bool)"""
self.show_song_cclino = show_song_cclino
def set_show_ccli_number(self, show_ccli_number):
"""Set the show_ccli_number flag (bool)"""
self.show_ccli_number = show_ccli_number
def get_lyrics(self):
"""Return the lyrics as a list of strings
@ -674,7 +466,7 @@ class Song(object):
slideNumber -- 1 .. numberOfSlides
Returns a list as:
[theme (string),
[theme_name (string),
title (string),
authorlist (string),
copyright (string),
@ -688,7 +480,7 @@ class Song(object):
raise SongSlideError(u'Slide number too high')
res = []
if self.show_title:
title = self.get_title()
title = self.title
else:
title = ""
if self.show_author_list:
@ -699,13 +491,13 @@ class Song(object):
cpright = self.get_copyright()
else:
cpright = ""
if self.show_song_cclino:
ccli = self.get_song_cclino()
if self.show_ccli_number:
ccli = self.get_ccli_number()
else:
ccli = ""
theme = self.get_theme()
theme_name = self.get_theme_name()
# examine the slide for a theme
res.append(theme)
res.append(theme_name)
res.append(title)
res.append(author)
res.append(cpright)

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<song>
<title>Martins Test</title>
<author>MartiÑ Thómpson</author>
<copyright>2010 Martin Thompson</copyright>
<hymn_number>1</hymn_number>
<presentation>V1 C V2 C2 V3 B1 V1</presentation>
<ccli>Blah</ccli>
<capo print="false"></capo>
<key></key>
<aka></aka>
<key_line></key_line>
<user1></user1>
<user2></user2>
<user3></user3>
<theme>TestTheme</theme>
<alttheme>TestAltTheme</alttheme>
<tempo></tempo>
<time_sig></time_sig>
<lyrics>;Comment
. A B C
1 v1 Line 1___
2 v2 Line 1___
. A B C7
1 V1 Line 2
2 V2 Line 2
[3]
V3 Line 1
V3 Line 2
[b1]
Bridge 1
---
-!!
Bridge 1 line 2
[C]
. A B
Chorus 1
[C2]
. A B
Chorus 2
</lyrics>
<style index="default_style">
<title enabled="true" valign="bottom" align="center" include_verse="false" margin-left="0" margin-right="0" margin-top="0" margin-bottom="0" font="Helvetica" size="26" bold="true" italic="true" underline="false" color="#FFFFFF" border="true" border_color="#000000" shadow="true" shadow_color="#000000" fill="false" fill_color="#000000"/>
<subtitle enabled="true" valign="bottom" align="center" descriptive="false" margin-left="0" margin-right="0" margin-top="0" margin-bottom="0" font="Helvetica" size="18" bold="true" italic="true" underline="false" color="#FFFFFF" border="true" border_color="#000000" shadow="true" shadow_color="#000000" fill="false" fill_color="#000000"/>
<song_subtitle>author</song_subtitle>
<body enabled="true" auto_scale="false" valign="middle" align="center" highlight_chorus="true" margin-left="0" margin-right="0" margin-top="0" margin-bottom="0" font="Helvetica" size="34" bold="true" italic="false" underline="false" color="#FFFFFF" border="true" border_color="#000000" shadow="true" shadow_color="#000000" fill="false" fill_color="#FF0000">
<tabs/>
</body>
<background strip_footer="0" color="#408080" position="1"/>
</style></song>

Binary file not shown.

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<song>
<title>Martins 2nd Test</title>
<author>Martin Thompson</author>
<copyright>2010 Martin Thompson</copyright>
<hymn_number>2</hymn_number>
<presentation></presentation>
<ccli>Blah</ccli>
<capo print="false"></capo>
<key></key>
<aka></aka>
<key_line></key_line>
<user1></user1>
<user2></user2>
<user3></user3>
<theme></theme>
<tempo></tempo>
<time_sig></time_sig>
<lyrics>;Comment
[V]
. A B C
1 v1 Line 1___
2 v2 Line 1___
. A B C7
1 V1 Line 2
2 V2 Line 2
[b1]
Bridge 1
Bridge 1 line 2
[C1]
Chorus 1
[C2]
Chorus 2
</lyrics>
<style index="default_style">
<title enabled="true" valign="bottom" align="center" include_verse="false" margin-left="0" margin-right="0" margin-top="0" margin-bottom="0" font="Helvetica" size="26" bold="true" italic="true" underline="false" color="#FFFFFF" border="true" border_color="#000000" shadow="true" shadow_color="#000000" fill="false" fill_color="#000000"/>
<subtitle enabled="true" valign="bottom" align="center" descriptive="false" margin-left="0" margin-right="0" margin-top="0" margin-bottom="0" font="Helvetica" size="18" bold="true" italic="true" underline="false" color="#FFFFFF" border="true" border_color="#000000" shadow="true" shadow_color="#000000" fill="false" fill_color="#000000"/>
<song_subtitle>author</song_subtitle>
<body enabled="true" auto_scale="false" valign="middle" align="center" highlight_chorus="true" margin-left="0" margin-right="0" margin-top="0" margin-bottom="0" font="Helvetica" size="34" bold="true" italic="false" underline="false" color="#FFFFFF" border="true" border_color="#000000" shadow="true" shadow_color="#000000" fill="false" fill_color="#FF0000">
<tabs/>
</body>
<background strip_footer="0" color="#408080" position="1"/>
</style></song>

View File

@ -0,0 +1,57 @@
from openlp.plugins.songs.lib.opensongimport import OpenSongImport
from openlp.plugins.songs.lib.db import init_schema
from openlp.core.lib.db import Manager
from glob import glob
from zipfile import ZipFile
import os
from traceback import print_exc
import sys
import codecs
def opensong_import_lots():
ziploc = u'/home/mjt/openlp/OpenSong_Data/'
files = []
#files = [u'test.opensong.zip', ziploc+u'ADond.zip']
files.extend(glob(ziploc+u'Songs.zip'))
#files.extend(glob(ziploc+u'SOF.zip'))
#files.extend(glob(ziploc+u'spanish_songs_for_opensong.zip'))
# files.extend(glob(ziploc+u'opensong_*.zip'))
errfile = codecs.open(u'import_lots_errors.txt', u'w', u'utf8')
manager = Manager(u'songs', init_schema)
for file in files:
print u'Importing', file
z = ZipFile(file, u'r')
for song in z.infolist():
# need to handle unicode filenames (CP437 - Winzip does this)
filename = song.filename#.decode('cp852')
parts = os.path.split(filename)
if parts[-1] == u'':
#No final part => directory
continue
print " ", file, ":",filename,
songfile = z.open(song)
#z.extract(song)
#songfile=open(filename, u'r')
o = OpenSongImport(manager)
try:
o.do_import_file(songfile)
# o.song_import.print_song()
except:
print "Failure",
errfile.write(u'Failure: %s:%s\n' %(file, filename.decode('cp437')))
songfile = z.open(song)
for l in songfile.readlines():
l = l.decode('utf8')
print(u' |%s\n' % l.strip())
errfile.write(u' |%s\n'%l.strip())
print_exc(3, file = errfile)
print_exc(3)
sys.exit(1)
# continue
#o.finish()
print "OK"
#os.unlink(filename)
# o.song_import.print_song()
if __name__ == "__main__":
opensong_import_lots()

View File

@ -0,0 +1,88 @@
# -*- 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 #
###############################################################################
from openlp.plugins.songs.lib.opensongimport import OpenSongImport
from openlp.core.lib.db import Manager
from openlp.plugins.songs.lib.db import init_schema
def test():
manager = Manager(u'songs', init_schema)
o = OpenSongImport(manager)
o.do_import(u'test.opensong', commit=False)
o.song_import.print_song()
assert o.song_import.copyright == u'2010 Martin Thompson'
assert o.song_import.authors == [u'MartiÑ Thómpson']
assert o.song_import.title == u'Martins Test'
assert o.song_import.alternate_title == u''
assert o.song_import.song_number == u'1'
assert [u'C1', u'Chorus 1'] in o.song_import.verses
assert [u'C2', u'Chorus 2'] in o.song_import.verses
assert not [u'C3', u'Chorus 3'] in o.song_import.verses
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses
assert o.song_import.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1']
assert o.song_import.ccli_number == u'Blah'
assert o.song_import.topics == [u'TestTheme', u'TestAltTheme']
o.do_import(u'test.opensong.zip', commit=False)
o.song_import.print_song()
o.finish()
assert o.song_import.copyright == u'2010 Martin Thompson'
assert o.song_import.authors == [u'MartiÑ Thómpson']
assert o.song_import.title == u'Martins Test'
assert o.song_import.alternate_title == u''
assert o.song_import.song_number == u'1'
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses
assert [u'C1', u'Chorus 1'] in o.song_import.verses
assert [u'C2', u'Chorus 2'] in o.song_import.verses
assert not [u'C3', u'Chorus 3'] in o.song_import.verses
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses
assert o.song_import.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1']
o = OpenSongImport(manager)
o.do_import(u'test2.opensong', commit=False)
# o.finish()
o.song_import.print_song()
assert o.song_import.copyright == u'2010 Martin Thompson'
assert o.song_import.authors == [u'Martin Thompson']
assert o.song_import.title == u'Martins 2nd Test'
assert o.song_import.alternate_title == u''
assert o.song_import.song_number == u'2'
print o.song_import.verses
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses
assert [u'C1', u'Chorus 1'] in o.song_import.verses
assert [u'C2', u'Chorus 2'] in o.song_import.verses
assert not [u'C3', u'Chorus 3'] in o.song_import.verses
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses
print o.song_import.verse_order_list
assert o.song_import.verse_order_list == [u'V1', u'V2', u'B1', u'C1', u'C2']
print "Tests passed"
pass
if __name__ == "__main__":
test()

View File

@ -76,8 +76,9 @@ class SongXMLBuilder(object):
``content``
The actual text of the verse to be stored.
"""
#log.debug(u'add_verse_to_lyrics %s, %s\n%s' % (type, number, content))
verse = etree.Element(u'verse', type=type, label=number)
# log.debug(u'add_verse_to_lyrics %s, %s\n%s' % (type, number, content))
verse = etree.Element(u'verse', type = unicode(type),
label = unicode(number))
verse.text = etree.CDATA(content)
self.lyrics.append(verse)

View File

@ -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:
@ -39,6 +39,8 @@ try:
except ImportError:
OOo_available = False
from openlp.plugins.songs.lib import OpenSongImport
log = logging.getLogger(__name__)
class SongsPlugin(Plugin):
@ -69,7 +71,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):
"""
@ -137,6 +139,37 @@ class SongsPlugin(Plugin):
QtCore.SIGNAL(u'triggered()'), self.onImportSofItemClick)
QtCore.QObject.connect(self.ImportOooItem,
QtCore.SIGNAL(u'triggered()'), self.onImportOooItemClick)
# OpenSong import menu item - will be removed and the
# functionality will be contained within the import wizard
self.ImportOpenSongItem = QtGui.QAction(import_menu)
self.ImportOpenSongItem.setObjectName(u'ImportOpenSongItem')
self.ImportOpenSongItem.setText(
translate('SongsPlugin',
'OpenSong (temp menu item)'))
self.ImportOpenSongItem.setToolTip(
translate('SongsPlugin',
'Import songs from OpenSong files' +
'(either raw text or ZIPfiles)'))
self.ImportOpenSongItem.setStatusTip(
translate('SongsPlugin',
'Import songs from OpenSong files' +
'(either raw text or ZIPfiles)'))
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 Songs (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):
"""
@ -177,6 +210,45 @@ class SongsPlugin(Plugin):
QtGui.QMessageBox.Ok)
Receiver.send_message(u'songs_load_list')
def onImportOpenSongItemClick(self):
filenames = QtGui.QFileDialog.getOpenFileNames(
None, translate('SongsPlugin',
'Open OpenSong file'),
u'', u'All files (*.*)')
try:
for filename in filenames:
importer = OpenSongImport(self.manager)
importer.do_import(unicode(filename))
except:
log.exception('Could not import OpenSong file')
QtGui.QMessageBox.critical(None,
translate('SongsPlugin',
'Import Error'),
translate('SongsPlugin',
'Error importing OpenSong file'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok),
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',
@ -198,8 +270,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 +285,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

View File

@ -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()

View File

@ -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:

View File

@ -25,4 +25,3 @@
"""
The :mod:`lib` module contains the library functions for the songusage plugin.
"""
from openlp.plugins.songusage.lib.manager import SongUsageManager

View File

@ -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

View File

@ -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)