Initial support for multiple songbooks

This commit is contained in:
Samuel Mehrbrodt 2016-01-04 13:11:24 +01:00
parent 339a8e9cd6
commit d2ab4862b4
6 changed files with 188 additions and 87 deletions

View File

@ -37,7 +37,7 @@ class Ui_EditSongDialog(object):
def setupUi(self, edit_song_dialog):
edit_song_dialog.setObjectName('edit_song_dialog')
edit_song_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
edit_song_dialog.resize(650, 400)
edit_song_dialog.resize(900, 600)
edit_song_dialog.setModal(True)
self.dialog_layout = QtWidgets.QVBoxLayout(edit_song_dialog)
self.dialog_layout.setSpacing(8)
@ -173,22 +173,33 @@ class Ui_EditSongDialog(object):
self.topic_remove_layout.addWidget(self.topic_remove_button)
self.topics_layout.addLayout(self.topic_remove_layout)
self.authors_right_layout.addWidget(self.topics_group_box)
self.song_book_group_box = QtWidgets.QGroupBox(self.authors_tab)
self.song_book_group_box.setObjectName('song_book_group_box')
self.song_book_layout = QtWidgets.QFormLayout(self.song_book_group_box)
self.song_book_layout.setObjectName('song_book_layout')
self.song_book_name_label = QtWidgets.QLabel(self.song_book_group_box)
self.song_book_name_label.setObjectName('song_book_name_label')
self.song_book_combo_box = create_combo_box(self.song_book_group_box, 'song_book_combo_box')
self.song_book_name_label.setBuddy(self.song_book_combo_box)
self.song_book_layout.addRow(self.song_book_name_label, self.song_book_combo_box)
self.song_book_number_label = QtWidgets.QLabel(self.song_book_group_box)
self.song_book_number_label.setObjectName('song_book_number_label')
self.song_book_number_edit = QtWidgets.QLineEdit(self.song_book_group_box)
self.song_book_number_edit.setObjectName('song_book_number_edit')
self.song_book_number_label.setBuddy(self.song_book_number_edit)
self.song_book_layout.addRow(self.song_book_number_label, self.song_book_number_edit)
self.authors_right_layout.addWidget(self.song_book_group_box)
self.songbook_group_box = QtWidgets.QGroupBox(self.authors_tab)
self.songbook_group_box.setObjectName('songbook_group_box')
self.songbooks_layout = QtWidgets.QVBoxLayout(self.songbook_group_box)
self.songbooks_layout.setObjectName('songbooks_layout')
self.songbook_add_layout = QtWidgets.QHBoxLayout()
self.songbook_add_layout.setObjectName('songbook_add_layout')
self.songbooks_combo_box = create_combo_box(self.songbook_group_box, 'songbooks_combo_box')
self.songbook_add_layout.addWidget(self.songbooks_combo_box)
self.songbook_entry_edit = QtWidgets.QLineEdit(self.songbook_group_box)
self.songbook_entry_edit.setMaximumWidth(100)
self.songbook_add_layout.addWidget(self.songbook_entry_edit)
self.songbook_add_button = QtWidgets.QPushButton(self.songbook_group_box)
self.songbook_add_button.setObjectName('songbook_add_button')
self.songbook_add_layout.addWidget(self.songbook_add_button)
self.songbooks_layout.addLayout(self.songbook_add_layout)
self.songbooks_list_view = QtWidgets.QListWidget(self.songbook_group_box)
self.songbooks_list_view.setAlternatingRowColors(True)
self.songbooks_list_view.setObjectName('songbooks_list_view')
self.songbooks_layout.addWidget(self.songbooks_list_view)
self.songbook_remove_layout = QtWidgets.QHBoxLayout()
self.songbook_remove_layout.setObjectName('songbook_remove_layout')
self.songbook_remove_layout.addStretch()
self.songbook_remove_button = QtWidgets.QPushButton(self.songbook_group_box)
self.songbook_remove_button.setObjectName('songbook_remove_button')
self.songbook_remove_layout.addWidget(self.songbook_remove_button)
self.songbooks_layout.addLayout(self.songbook_remove_layout)
self.authors_right_layout.addWidget(self.songbook_group_box)
self.authors_tab_layout.addLayout(self.authors_right_layout)
self.song_tab_widget.addTab(self.authors_tab, '')
# theme tab
@ -303,15 +314,15 @@ class Ui_EditSongDialog(object):
self.author_add_button.setText(translate('SongsPlugin.EditSongForm', '&Add to Song'))
self.author_edit_button.setText(translate('SongsPlugin.EditSongForm', '&Edit Author Type'))
self.author_remove_button.setText(translate('SongsPlugin.EditSongForm', '&Remove'))
self.maintenance_button.setText(translate('SongsPlugin.EditSongForm', '&Manage Authors, Topics, Song Books'))
self.topics_group_box.setTitle(SongStrings.Topic)
self.maintenance_button.setText(translate('SongsPlugin.EditSongForm', '&Manage Authors, Topics, Songbooks'))
self.topics_group_box.setTitle(SongStrings.Topics)
self.topic_add_button.setText(translate('SongsPlugin.EditSongForm', 'A&dd to Song'))
self.topic_remove_button.setText(translate('SongsPlugin.EditSongForm', 'R&emove'))
self.song_book_group_box.setTitle(SongStrings.SongBook)
self.song_book_name_label.setText(translate('SongsPlugin.EditSongForm', 'Book:'))
self.song_book_number_label.setText(translate('SongsPlugin.EditSongForm', 'Number:'))
self.songbook_group_box.setTitle(SongStrings.SongBooks)
self.songbook_add_button.setText(translate('SongsPlugin.EditSongForm', 'Add &to Song'))
self.songbook_remove_button.setText(translate('SongsPlugin.EditSongForm', 'Re&move'))
self.song_tab_widget.setTabText(self.song_tab_widget.indexOf(self.authors_tab),
translate('SongsPlugin.EditSongForm', 'Authors, Topics && Song Book'))
translate('SongsPlugin.EditSongForm', 'Authors, Topics && Songbooks'))
self.theme_group_box.setTitle(UiStrings().Theme)
self.theme_add_button.setText(translate('SongsPlugin.EditSongForm', 'New &Theme'))
self.rights_group_box.setTitle(translate('SongsPlugin.EditSongForm', 'Copyright Information'))

View File

@ -35,7 +35,7 @@ from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStri
from openlp.core.lib import FileDialog, PluginStatus, MediaType, create_separated_list
from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
from openlp.plugins.songs.lib import VerseType, clean_song
from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile
from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile, SongBookEntry
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib.openlyricsxml import SongXML
from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
@ -69,6 +69,9 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
self.topic_add_button.clicked.connect(self.on_topic_add_button_clicked)
self.topic_remove_button.clicked.connect(self.on_topic_remove_button_clicked)
self.topics_list_view.itemClicked.connect(self.on_topic_list_view_clicked)
self.songbook_add_button.clicked.connect(self.on_songbook_add_button_clicked)
self.songbook_remove_button.clicked.connect(self.on_songbook_remove_button_clicked)
self.songbooks_list_view.itemClicked.connect(self.on_songbook_list_view_clicked)
self.copyright_insert_button.clicked.connect(self.on_copyright_insert_button_triggered)
self.verse_add_button.clicked.connect(self.on_verse_add_button_clicked)
self.verse_list_widget.doubleClicked.connect(self.on_verse_edit_button_clicked)
@ -125,6 +128,11 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
author_item.setData(QtCore.Qt.UserRole, (author.id, author_type))
self.authors_list_view.addItem(author_item)
def add_songbookentry_to_list(self, songbook_id, songbook_name, entry):
songbookentry_item = QtWidgets.QListWidgetItem(SongBookEntry.get_display_name(songbook_name, entry))
songbookentry_item.setData(QtCore.Qt.UserRole, (songbook_id, entry))
self.songbooks_list_view.addItem(songbookentry_item)
def _extract_verse_order(self, verse_order):
"""
Split out the verse order
@ -219,17 +227,6 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount())
if not result:
return False
text = self.song_book_combo_box.currentText()
if self.song_book_combo_box.findText(text, QtCore.Qt.MatchExactly) < 0:
if QtWidgets.QMessageBox.question(
self, translate('SongsPlugin.EditSongForm', 'Add Book'),
translate('SongsPlugin.EditSongForm', 'This song book does not exist, do you want to add it?'),
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
book = Book.populate(name=text, publisher='')
self.manager.save_object(book)
else:
return False
# Validate tags (lp#1199639)
misplaced_tags = []
verse_tags = []
@ -327,6 +324,9 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
if self.topics_combo_box.hasFocus() and self.topics_combo_box.currentText():
self.on_topic_add_button_clicked()
return
if self.songbooks_combo_box.hasFocus() and self.songbooks_combo_box.currentText():
self.on_songbook_add_button_clicked()
return
QtWidgets.QDialog.keyPressEvent(self, event)
def initialise(self):
@ -367,12 +367,12 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
self.topics = []
self._load_objects(Topic, self.topics_combo_box, self.topics)
def load_books(self):
def load_songbooks(self):
"""
Load the song books into the combobox
Load the Songbooks into the combobox
"""
self.books = []
self._load_objects(Book, self.song_book_combo_box, self.books)
self.songbooks = []
self._load_objects(Book, self.songbooks_combo_box, self.songbooks)
def load_themes(self, theme_list):
"""
@ -413,12 +413,12 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
self.verse_list_widget.setRowCount(0)
self.authors_list_view.clear()
self.topics_list_view.clear()
self.songbooks_list_view.clear()
self.audio_list_widget.clear()
self.title_edit.setFocus()
self.song_book_number_edit.clear()
self.load_authors()
self.load_topics()
self.load_books()
self.load_songbooks()
self.load_media_files()
self.theme_combo_box.setEditText('')
self.theme_combo_box.setCurrentIndex(0)
@ -437,18 +437,11 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
self.song_tab_widget.setCurrentIndex(0)
self.load_authors()
self.load_topics()
self.load_books()
self.load_songbooks()
self.load_media_files()
self.song = self.manager.get_object(Song, song_id)
self.title_edit.setText(self.song.title)
self.alternative_edit.setText(
self.song.alternate_title if self.song.alternate_title else '')
if self.song.song_book_id != 0:
book_name = self.manager.get_object(Book, self.song.song_book_id)
find_and_set_in_combo_box(self.song_book_combo_box, str(book_name.name))
else:
self.song_book_combo_box.setEditText('')
self.song_book_combo_box.setCurrentIndex(0)
self.alternative_edit.setText(self.song.alternate_title if self.song.alternate_title else '')
if self.song.theme_name:
find_and_set_in_combo_box(self.theme_combo_box, str(self.song.theme_name))
else:
@ -458,7 +451,6 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
self.copyright_edit.setText(self.song.copyright if self.song.copyright else '')
self.comments_edit.setPlainText(self.song.comments if self.song.comments else '')
self.ccli_number_edit.setText(self.song.ccli_number if self.song.ccli_number else '')
self.song_book_number_edit.setText(self.song.song_number if self.song.song_number else '')
# lazy xml migration for now
self.verse_list_widget.clear()
self.verse_list_widget.setRowCount(0)
@ -520,6 +512,9 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
topic_name = QtWidgets.QListWidgetItem(str(topic.name))
topic_name.setData(QtCore.Qt.UserRole, topic.id)
self.topics_list_view.addItem(topic_name)
self.songbooks_list_view.clear()
for songbookentry in self.song.songbookentries:
self.add_songbookentry_to_list(songbookentry.songbook.id, songbookentry.songbook.name, songbookentry.entry)
self.audio_list_widget.clear()
for media in self.song.media_files:
media_file = QtWidgets.QListWidgetItem(os.path.split(media.file_name)[1])
@ -678,6 +673,48 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
row = self.topics_list_view.row(item)
self.topics_list_view.takeItem(row)
def on_songbook_add_button_clicked(self):
item = int(self.songbooks_combo_box.currentIndex())
text = self.songbooks_combo_box.currentText()
if item == 0 and text:
if QtWidgets.QMessageBox.question(
self, translate('SongsPlugin.EditSongForm', 'Add Songbook'),
translate('SongsPlugin.EditSongForm', 'This Songbook does not exist, do you want to add it?'),
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No,
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
songbook = Book.populate(name=text)
self.manager.save_object(songbook)
self.add_songbookentry_to_list(songbook.id, songbook.name, self.songbook_entry_edit.text())
self.load_songbooks()
self.songbooks_combo_box.setCurrentIndex(0)
self.songbook_entry_edit.setText("")
else:
return
elif item > 0:
item_id = (self.songbooks_combo_box.itemData(item))
songbook = self.manager.get_object(Book, item_id)
if self.songbooks_list_view.findItems(str(songbook.name), QtCore.Qt.MatchExactly):
critical_error_message_box(
message=translate('SongsPlugin.EditSongForm', 'This Songbook is already in the list.'))
else:
self.add_songbookentry_to_list(songbook.id, songbook.name, self.songbook_entry_edit.text())
self.songbooks_combo_box.setCurrentIndex(0)
self.songbook_entry_edit.setText("")
else:
QtWidgets.QMessageBox.warning(
self, UiStrings().NISs,
translate('SongsPlugin.EditSongForm', 'You have not selected a valid Songbook. Either select a '
'Songbook from the list, or type in a new Songbook and click the "Add to Song" '
'button to add the new Songbook.'))
def on_songbook_list_view_clicked(self):
self.songbook_remove_button.setEnabled(True)
def on_songbook_remove_button_clicked(self):
self.songbook_remove_button.setEnabled(False)
row = self.songbooks_list_view.row(self.songbooks_list_view.currentItem())
self.songbooks_list_view.takeItem(row)
def on_verse_list_view_clicked(self):
self.verse_edit_button.setEnabled(True)
self.verse_delete_button.setEnabled(True)
@ -838,17 +875,10 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
"""
Maintenance button pressed
"""
temp_song_book = None
item = int(self.song_book_combo_box.currentIndex())
text = self.song_book_combo_box.currentText()
if item == 0 and text:
temp_song_book = text
self.media_item.song_maintenance_form.exec(True)
self.load_authors()
self.load_books()
self.load_songbooks()
self.load_topics()
if temp_song_book:
self.song_book_combo_box.setEditText(temp_song_book)
def on_preview(self, button):
"""
@ -928,7 +958,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
log.debug('SongEditForm.clearCaches')
self.authors = []
self.themes = []
self.books = []
self.songbooks = []
self.topics = []
def reject(self):
@ -977,12 +1007,6 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
order.append('%s%s' % (verse_tag, verse_num))
self.song.verse_order = ' '.join(order)
self.song.ccli_number = self.ccli_number_edit.text()
self.song.song_number = self.song_book_number_edit.text()
book_name = self.song_book_combo_box.currentText()
if book_name:
self.song.book = self.manager.get_object_filtered(Book, Book.name == book_name)
else:
self.song.book = None
theme_name = self.theme_combo_box.currentText()
if theme_name:
self.song.theme_name = theme_name
@ -1001,6 +1025,12 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
topic = self.manager.get_object(Topic, topic_id)
if topic is not None:
self.song.topics.append(topic)
for row in range(self.songbooks_list_view.count()):
item = self.songbooks_list_view.item(row)
songbook_id = item.data(QtCore.Qt.UserRole)[0]
songbook = self.manager.get_object(Book, songbook_id)
entry = item.data(QtCore.Qt.UserRole)[1]
self.song.add_songbookentry(songbook, entry)
# Save the song here because we need a valid id for the audio files.
clean_song(self.manager, self.song)
self.manager.save_object(self.song)

View File

@ -160,6 +160,31 @@ class Song(BaseModel):
self.authors_songs.remove(author_song)
return
def add_songbookentry(self, songbook, entry):
"""
Add a Songbook Entry to the song if it not yet exists
:param songbook_name: Name of the Songbook.
:param entry: Entry in the Songbook (usually a number)
"""
for songbookentry in self.songbookentries:
if songbookentry.songbook.name == songbook.name and songbookentry.entry == entry:
return
new_songbookentry = SongBookEntry()
new_songbookentry.songbook = songbook
new_songbookentry.entry = entry
self.songbookentries.append(new_songbookentry)
class SongBookEntry(BaseModel):
"""
SongBookEntry model
"""
@staticmethod
def get_display_name(songbook_name, entry):
if entry:
return "%s #%s" % (songbook_name, entry)
return songbook_name
class Topic(BaseModel):
"""
@ -182,6 +207,7 @@ def init_schema(url):
* media_files_songs
* song_books
* songs
* songs_songbooks
* songs_topics
* topics
@ -222,7 +248,6 @@ def init_schema(url):
The *songs* table has the following columns:
* id
* song_book_id
* title
* alternate_title
* lyrics
@ -230,11 +255,17 @@ def init_schema(url):
* copyright
* comments
* ccli_number
* song_number
* theme_name
* search_title
* search_lyrics
**songs_songsbooks Table**
This is a mapping table between the *songs* and the *song_books* tables. It has the following columns:
* songbook_id
* song_id
* entry # The song number, like 120 or 550A
**songs_topics Table**
This is a bridging table between the *songs* and *topics* tables, which
serves to create a many-to-many relationship between the two tables. It
@ -284,7 +315,6 @@ def init_schema(url):
songs_table = Table(
'songs', metadata,
Column('id', types.Integer(), primary_key=True),
Column('song_book_id', types.Integer(), ForeignKey('song_books.id'), default=None),
Column('title', types.Unicode(255), nullable=False),
Column('alternate_title', types.Unicode(255)),
Column('lyrics', types.UnicodeText, nullable=False),
@ -292,7 +322,6 @@ def init_schema(url):
Column('copyright', types.Unicode(255)),
Column('comments', types.UnicodeText),
Column('ccli_number', types.Unicode(64)),
Column('song_number', types.Unicode(64)),
Column('theme_name', types.Unicode(128)),
Column('search_title', types.Unicode(255), index=True, nullable=False),
Column('search_lyrics', types.UnicodeText, nullable=False),
@ -316,6 +345,14 @@ def init_schema(url):
Column('author_type', types.Unicode(255), primary_key=True, nullable=False, server_default=text('""'))
)
# Definition of the "songs_songbooks" table
songs_songbooks_table = Table(
'songs_songbooks', metadata,
Column('songbook_id', types.Integer(), ForeignKey('song_books.id'), primary_key=True),
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
Column('entry', types.Unicode(255), primary_key=True, nullable=False)
)
# Definition of the "songs_topics" table
songs_topics_table = Table(
'songs_topics', metadata,
@ -329,6 +366,9 @@ def init_schema(url):
mapper(AuthorSong, authors_songs_table, properties={
'author': relation(Author)
})
mapper(SongBookEntry, songs_songbooks_table, properties={
'songbook': relation(Book)
})
mapper(Book, song_books_table)
mapper(MediaFile, media_files_table)
mapper(Song, songs_table, properties={
@ -337,8 +377,8 @@ def init_schema(url):
'authors_songs': relation(AuthorSong, cascade="all, delete-orphan"),
# Use lazy='joined' to always load authors when the song is fetched from the database (bug 1366198)
'authors': relation(Author, secondary=authors_songs_table, viewonly=True, lazy='joined'),
'book': relation(Book, backref='songs'),
'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight),
'songbookentries': relation(SongBookEntry, cascade="all, delete-orphan"),
'topics': relation(Topic, backref='songs', secondary=songs_topics_table)
})
mapper(Topic, topics_table)

View File

@ -266,13 +266,12 @@ class OpenLyrics(object):
element.set('type', AuthorType.Music)
else:
element.set('type', author_song.author_type)
book = self.manager.get_object_filtered(Book, Book.id == song.song_book_id)
if book is not None:
book = book.name
if song.songbookentries:
songbooks = etree.SubElement(properties, 'songbooks')
element = self._add_text_to_element('songbook', songbooks, None, book)
if song.song_number:
element.set('entry', song.song_number)
for songbookentry in song.songbookentries:
element = self._add_text_to_element('songbook', songbooks, None, songbookentry.songbook)
if songbookentry.entry:
element.set('entry', songbookentry.entry)
if song.topics:
themes = etree.SubElement(properties, 'themes')
for topic in song.topics:
@ -744,8 +743,6 @@ class OpenLyrics(object):
:param properties: The property object (lxml.objectify.ObjectifiedElement).
:param song: The song object.
"""
song.song_book_id = None
song.song_number = ''
if hasattr(properties, 'songbooks'):
for songbook in properties.songbooks.songbook:
book_name = songbook.get('name', '')
@ -755,10 +752,7 @@ class OpenLyrics(object):
# We need to create a book, because it does not exist.
book = Book.populate(name=book_name, publisher='')
self.manager.save_object(book)
song.song_book_id = book.id
song.song_number = songbook.get('entry', '')
# We only support one song book, so take the first one.
break
song.add_songbookentry(book, songbook.get('entry', ''))
def _process_titles(self, properties, song):
"""

View File

@ -35,8 +35,8 @@ class SongStrings(object):
Authors = translate('OpenLP.Ui', 'Authors', 'Plural')
AuthorUnknown = translate('OpenLP.Ui', 'Author Unknown') # Used to populate the database.
CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.')
SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular')
SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural')
SongBook = translate('OpenLP.Ui', 'Songbook', 'Singular')
SongBooks = translate('OpenLP.Ui', 'Songbooks', 'Plural')
SongIncomplete = translate('OpenLP.Ui', 'Title and/or verses not found')
SongMaintenance = translate('OpenLP.Ui', 'Song Maintenance')
Topic = translate('OpenLP.Ui', 'Topic', 'Singular')

View File

@ -29,10 +29,10 @@ from sqlalchemy import Table, Column, ForeignKey, types
from sqlalchemy.sql.expression import func, false, null, text
from openlp.core.lib.db import get_upgrade_op
from openlp.core.common import trace_error_handler
from openlp.core.utils.db import drop_columns
log = logging.getLogger(__name__)
__version__ = 4
__version__ = 5
# TODO: When removing an upgrade path the ftw-data needs updating to the minimum supported version
@ -117,3 +117,29 @@ def upgrade_4(session, metadata):
op.rename_table('authors_songs_tmp', 'authors_songs')
else:
log.warning('Skipping upgrade_4 step of upgrading the song db')
def upgrade_5(session, metadata):
"""
Version 5 upgrade.
This upgrade adds support for multiple songbooks
"""
op = get_upgrade_op(session)
songs_table = Table('songs', metadata)
if 'song_book_id' in [col.name for col in songs_table.c.values()]:
log.warning('Skipping upgrade_5 step of upgrading the song db')
return
# Create the mapping table (songs <-> songbooks)
op.create_table('songs_songbooks',
Column('songbook_id', types.Integer(), ForeignKey('song_books.id'), primary_key=True),
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
Column('entry', types.Unicode(255), primary_key=True, nullable=False))
# Migrate old data
op.execute('INSERT INTO songs_songbooks SELECT song_book_id, id, song_number FROM songs\
WHERE song_book_id NOT NULL AND song_number NOT NULL')
# Drop old columns
drop_columns(op, 'songs', ['song_book_id', 'song_number'])