def xml_to_song(self, xml): """ Create and save a song from Foilpresenter format xml to the database. :param xml: The XML to parse (unicode). """ # No xml get out of here. if not xml: return if xml[:5] == '<?xml': xml = xml[38:] song = Song() # Values will be set when cleaning the song. song.search_lyrics = '' song.verse_order = '' song.search_title = '' self.save_song = True # Because "text" seems to be an reserved word, we have to recompile it. xml = re.compile('<text>').sub('<text_>', xml) xml = re.compile('</text>').sub('</text_>', xml) song_xml = objectify.fromstring(xml) self._process_copyright(song_xml, song) self._process_cclinumber(song_xml, song) self._process_titles(song_xml, song) # The verse order is processed with the lyrics! self._process_lyrics(song_xml, song) self._process_comments(song_xml, song) self._process_authors(song_xml, song) self._process_songbooks(song_xml, song) self._process_topics(song_xml, song) if self.save_song: clean_song(self.manager, song) self.manager.save_object(song)
def xml_to_song(self, xml, parse_and_temporary_save=False): """ Create and save a song from OpenLyrics format xml to the database. Since we also export XML from external sources (e. g. OpenLyrics import), we cannot ensure, that it completely conforms to the OpenLyrics standard. ``xml`` The XML to parse (unicode). ``parse_and_temporary_save`` Switch to skip processing the whole song and storing the songs in the database with a temporary flag. Defaults to ``False``. """ # No xml get out of here. if not xml: return None if xml[:5] == u'<?xml': xml = xml[38:] song_xml = objectify.fromstring(xml) if hasattr(song_xml, u'properties'): properties = song_xml.properties else: return None # Formatting tags are new in OpenLyrics 0.8 if float(song_xml.get(u'version')) > 0.7: self._process_formatting_tags(song_xml, parse_and_temporary_save) song = Song() # Values will be set when cleaning the song. song.search_lyrics = u'' song.verse_order = u'' song.search_title = u'' song.temporary = parse_and_temporary_save self._process_copyright(properties, song) self._process_cclinumber(properties, song) self._process_titles(properties, song) # The verse order is processed with the lyrics! self._process_lyrics(properties, song_xml, song) self._process_comments(properties, song) self._process_authors(properties, song) self._process_songbooks(properties, song) self._process_topics(properties, song) clean_song(self.manager, song) self.manager.save_object(song) return song
def test_add_author(self): """ Test adding an author to a song """ # GIVEN: A song and an author song = Song() song.authors_songs = [] author = Author() author.first_name = "Max" author.last_name = "Mustermann" # WHEN: We add an author to the song song.add_author(author) # THEN: The author should have been added with author_type=None assert 1 == len(song.authors_songs) assert "Max" == song.authors_songs[0].author.first_name assert "Mustermann" == song.authors_songs[0].author.last_name assert song.authors_songs[0].author_type is None
def test_add_author_with_type(self): """ Test adding an author with a type specified to a song """ # GIVEN: A song and an author song = Song() song.authors_songs = [] author = Author() author.first_name = "Max" author.last_name = "Mustermann" # WHEN: We add an author to the song song.add_author(author, AuthorType.Words) # THEN: The author should have been added with author_type=None self.assertEqual(1, len(song.authors_songs)) self.assertEqual("Max", song.authors_songs[0].author.first_name) self.assertEqual("Mustermann", song.authors_songs[0].author.last_name) self.assertEqual(AuthorType.Words, song.authors_songs[0].author_type)
def test_remove_author_with_type(self): """ Test removing an author with a type specified from a song """ # GIVEN: A song with two authors song = Song() song.authors_songs = [] author = Author() song.add_author(author) song.add_author(author, AuthorType.Translation) # WHEN: We remove the author with a certain type song.remove_author(author, AuthorType.Translation) # THEN: It should have been removed and the other author should still be there self.assertEqual(1, len(song.authors_songs)) self.assertEqual(None, song.authors_songs[0].author_type)
def save_song(self, song): """ Save a song to the database, using the db_manager :param song: :return: """ db_song = Song.populate(title=song['title'], copyright=song['copyright'], ccli_number=song['ccli_number']) song_xml = SongXML() verse_order = [] for verse in song['verses']: if ' ' in verse['label']: verse_type, verse_number = verse['label'].split(' ', 1) else: verse_type = verse['label'] verse_number = 1 verse_type = VerseType.from_loose_input(verse_type) verse_number = int(verse_number) song_xml.add_verse_to_lyrics(VerseType.tags[verse_type], verse_number, verse['lyrics']) verse_order.append('{tag}{number}'.format( tag=VerseType.tags[verse_type], number=verse_number)) db_song.verse_order = ' '.join(verse_order) db_song.lyrics = song_xml.extract_xml() clean_song(self.db_manager, db_song) self.db_manager.save_object(db_song) db_song.authors_songs = [] for author_name in song['authors']: author = self.db_manager.get_object_filtered( Author, Author.display_name == author_name) if not author: name_parts = author_name.rsplit(' ', 1) first_name = name_parts[0] if len(name_parts) == 1: last_name = '' else: last_name = name_parts[1] author = Author.populate(first_name=first_name, last_name=last_name, display_name=author_name) db_song.add_author(author) for topic_name in song.get('topics', []): topic = self.db_manager.get_object_filtered( Topic, Topic.name == topic_name) if not topic: topic = Topic.populate(name=topic_name) db_song.topics.append(topic) self.db_manager.save_object(db_song) return db_song
def test_remove_author(self): """ Test removing an author from a song """ # GIVEN: A song with an author song = Song() song.authors_songs = [] author = Author() song.add_author(author) # WHEN: We remove the author song.remove_author(author) # THEN: It should have been removed self.assertEqual(0, len(song.authors_songs))
def test_add_songbooks(self): """ Test that adding songbooks to a song works correctly """ # GIVEN: A mocked song and songbook song = Song() song.songbook_entries = [] songbook = Book() songbook.name = "Thy Word" # WHEN: We add two songbooks to a Song song.add_songbook_entry(songbook, "120") song.add_songbook_entry(songbook, "550A") # THEN: The song should have two songbook entries self.assertEqual(len(song.songbook_entries), 2, 'There should be two Songbook entries.')
def xml_to_song(self, xml, parse_and_temporary_save=False): """ Create and save a song from OpenLyrics format xml to the database. Since we also export XML from external sources (e. g. OpenLyrics import), we cannot ensure, that it completely conforms to the OpenLyrics standard. :param xml: The XML to parse (unicode). :param parse_and_temporary_save: Switch to skip processing the whole song and storing the songs in the database with a temporary flag. Defaults to ``False``. """ # No xml get out of here. if not xml: return None if xml[:5] == '<?xml': xml = xml[38:] song_xml = objectify.fromstring(xml) if hasattr(song_xml, 'properties'): properties = song_xml.properties else: return None # Formatting tags are new in OpenLyrics 0.8 if float(song_xml.get('version')) > 0.7: self._process_formatting_tags(song_xml, parse_and_temporary_save) song = Song() # Values will be set when cleaning the song. song.search_lyrics = '' song.verse_order = '' song.search_title = '' song.temporary = parse_and_temporary_save self._process_copyright(properties, song) self._process_cclinumber(properties, song) self._process_song_key(properties, song) self._process_transpose(properties, song) self._process_titles(properties, song) # The verse order is processed with the lyrics! self._process_lyrics(properties, song_xml, song) self._process_comments(properties, song) self._process_authors(properties, song) self._process_songbooks(properties, song) self._process_topics(properties, song) clean_song(self.manager, song) self.manager.save_object(song) return song
def do_import(self, progress_dialog=None): """ Run the import for an OpenLP version 2 song database. :param progress_dialog: The QProgressDialog used when importing songs from the FRW. """ class OldAuthorSong(BaseModel): """ Maps to the authors_songs table """ pass class OldAuthor(BaseModel): """ Maps to the authors table """ pass class OldBook(BaseModel): """ Maps to the songbooks table """ pass class OldMediaFile(BaseModel): """ Maps to the media_files table """ pass class OldSong(BaseModel): """ Maps to the songs table """ pass class OldTopic(BaseModel): """ Maps to the topics table """ pass class OldSongBookEntry(BaseModel): """ Maps to the songs_songbooks table """ pass # Check the file type if self.import_source.suffix != '.sqlite': self.log_error( self.import_source, translate('SongsPlugin.OpenLPSongImport', 'Not a valid OpenLP 2 song database.')) return self.import_source = 'sqlite:///{url}'.format(url=self.import_source) # Load the db file and reflect it engine = create_engine(self.import_source) source_meta = MetaData() source_meta.reflect(engine) self.source_session = scoped_session(sessionmaker(bind=engine)) # Run some checks to see which version of the database we have table_list = list(source_meta.tables.keys()) if 'media_files' in table_list: has_media_files = True else: has_media_files = False if 'songs_songbooks' in table_list: has_songs_books = True else: has_songs_books = False if 'authors_songs' in table_list: has_authors_songs = True else: has_authors_songs = False # Load up the tabls and map them out try: source_authors_table = source_meta.tables['authors'] source_song_books_table = source_meta.tables['song_books'] source_songs_table = source_meta.tables['songs'] source_topics_table = source_meta.tables['topics'] source_authors_songs_table = source_meta.tables['authors_songs'] source_songs_topics_table = source_meta.tables['songs_topics'] source_media_files_songs_table = None except KeyError: self.log_error( self.import_source, translate('SongsPlugin.OpenLPSongImport', 'Not a valid OpenLP 2 song database.')) return # Set up media_files relations if has_media_files: source_media_files_table = source_meta.tables['media_files'] source_media_files_songs_table = source_meta.tables.get( 'media_files_songs') try: class_mapper(OldMediaFile) except UnmappedClassError: mapper(OldMediaFile, source_media_files_table) if has_songs_books: source_songs_songbooks_table = source_meta.tables[ 'songs_songbooks'] try: class_mapper(OldSongBookEntry) except UnmappedClassError: mapper(OldSongBookEntry, source_songs_songbooks_table, properties={'songbook': relation(OldBook)}) if has_authors_songs: try: class_mapper(OldAuthorSong) except UnmappedClassError: mapper(OldAuthorSong, source_authors_songs_table) if has_authors_songs and 'author_type' in source_authors_songs_table.c.keys( ): has_author_type = True else: has_author_type = False # Set up the songs relationships song_props = { 'authors': relation(OldAuthor, backref='songs', secondary=source_authors_songs_table), 'topics': relation(OldTopic, backref='songs', secondary=source_songs_topics_table) } if has_media_files: if isinstance(source_media_files_songs_table, Table): song_props['media_files'] = relation( OldMediaFile, backref='songs', secondary=source_media_files_songs_table) else: song_props['media_files'] = \ relation(OldMediaFile, backref='songs', foreign_keys=[source_media_files_table.c.song_id], primaryjoin=source_songs_table.c.id == source_media_files_table.c.song_id) if has_songs_books: song_props['songbook_entries'] = relation( OldSongBookEntry, backref='song', cascade='all, delete-orphan') else: song_props['book'] = relation(OldBook, backref='songs') if has_authors_songs: song_props['authors_songs'] = relation(OldAuthorSong) # Map the rest of the tables 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() if self.import_wizard: self.import_wizard.progress_bar.setMaximum(len(source_songs)) for song in source_songs: new_song = Song() new_song.title = song.title if has_media_files and hasattr(song, 'alternate_title'): new_song.alternate_title = song.alternate_title else: old_titles = song.search_title.split('@') if len(old_titles) > 1: new_song.alternate_title = old_titles[1] # Transfer the values to the new song object new_song.search_title = '' new_song.search_lyrics = '' new_song.lyrics = song.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 hasattr(song, 'song_number') and song.song_number: new_song.song_number = song.song_number # Find or create all the authors and add them to the new song object for author in song.authors: existing_author = self.manager.get_object_filtered( Author, Author.display_name == author.display_name) if not existing_author: existing_author = Author.populate( first_name=author.first_name, last_name=author.last_name, display_name=author.display_name) # If this is a new database, we need to import the author_type too author_type = None if has_author_type: for author_song in song.authors_songs: if author_song.author_id == author.id: author_type = author_song.author_type break new_song.add_author(existing_author, author_type) # Find or create all the topics and add them to the new song object if song.topics: for topic in song.topics: existing_topic = self.manager.get_object_filtered( Topic, Topic.name == topic.name) if not existing_topic: existing_topic = Topic.populate(name=topic.name) new_song.topics.append(existing_topic) # Find or create all the songbooks and add them to the new song object if has_songs_books and song.songbook_entries: for entry in song.songbook_entries: existing_book = self.manager.get_object_filtered( Book, Book.name == entry.songbook.name) if not existing_book: existing_book = Book.populate( name=entry.songbook.name, publisher=entry.songbook.publisher) new_song.add_songbook_entry(existing_book, entry.entry) elif hasattr(song, 'book') and song.book: existing_book = self.manager.get_object_filtered( Book, Book.name == song.book.name) if not existing_book: existing_book = Book.populate( name=song.book.name, publisher=song.book.publisher) # Get the song_number from "songs" table "song_number" field. (This is db structure from 2.2.1) # If there's a number, add it to the song, otherwise it will be "". existing_number = song.song_number if hasattr( song, 'song_number') else '' if existing_number: new_song.add_songbook_entry(existing_book, existing_number) else: new_song.add_songbook_entry(existing_book, '') # Find or create all the media files and add them to the new song object if has_media_files and song.media_files: for media_file in song.media_files: existing_media_file = self.manager.get_object_filtered( MediaFile, MediaFile.file_path == media_file.file_path) if existing_media_file: new_song.media_files.append(existing_media_file) else: new_song.media_files.append( MediaFile.populate(file_name=media_file.file_name)) clean_song(self.manager, new_song) self.manager.save_object(new_song) if progress_dialog: progress_dialog.setValue(progress_dialog.value() + 1) progress_dialog.setLabelText( WizardStrings.ImportingType.format(source=new_song.title)) else: self.import_wizard.increment_progress_bar( WizardStrings.ImportingType.format(source=new_song.title)) if self.stop_import_flag: break self.source_session.close() engine.dispose()
def test_build_song_footer_base_songbook(self): """ Test build songs footer with basic song and multiple songbooks """ # GIVEN: A Song and a Service Item song = Song() song.title = 'My Song' song.alternate_title = '' song.copyright = 'My copyright' song.authors_songs = [] song.songbook_entries = [] song.ccli_number = '' song.topics = None book1 = MagicMock() book1.name = 'My songbook' book2 = MagicMock() book2.name = 'Thy songbook' song.songbookentries = [] song.add_songbook_entry(book1, '12') song.add_songbook_entry(book2, '502A') service_item = ServiceItem(None) # WHEN: I generate the Footer with default settings self.media_item.generate_footer(service_item, song) # THEN: The songbook should not be in the footer assert service_item.raw_footer == ['My Song', 'My copyright'] # WHEN: I activate the "display songbook" option self.media_item.display_songbook = True self.media_item.generate_footer(service_item, song) # THEN: The songbook should be in the footer assert service_item.raw_footer == [ 'My Song', 'My copyright', 'My songbook #12, Thy songbook #502A' ]
def do_import(self, progress_dialog=None): """ Run the import for an OpenLP version 2 song database. :param progress_dialog: The QProgressDialog used when importing songs from the FRW. """ 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 # Check the file type if not self.import_source.endswith('.sqlite'): self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport', 'Not a valid OpenLP 2.0 song database.')) return self.import_source = 'sqlite:///%s' % self.import_source # Load the db file engine = create_engine(self.import_source) source_meta = MetaData() source_meta.reflect(engine) self.source_session = scoped_session(sessionmaker(bind=engine)) if 'media_files' in list(source_meta.tables.keys()): has_media_files = True else: has_media_files = False source_authors_table = source_meta.tables['authors'] source_song_books_table = source_meta.tables['song_books'] source_songs_table = source_meta.tables['songs'] source_topics_table = source_meta.tables['topics'] source_authors_songs_table = source_meta.tables['authors_songs'] source_songs_topics_table = source_meta.tables['songs_topics'] source_media_files_songs_table = None if has_media_files: source_media_files_table = source_meta.tables['media_files'] source_media_files_songs_table = source_meta.tables.get('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: if isinstance(source_media_files_songs_table, Table): song_props['media_files'] = relation(OldMediaFile, backref='songs', secondary=source_media_files_songs_table) else: song_props['media_files'] = \ relation(OldMediaFile, backref='songs', foreign_keys=[source_media_files_table.c.song_id], primaryjoin=source_songs_table.c.id == source_media_files_table.c.song_id) 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() if self.import_wizard: self.import_wizard.progress_bar.setMaximum(len(source_songs)) for song in source_songs: new_song = Song() new_song.title = song.title if has_media_files and hasattr(song, 'alternate_title'): new_song.alternate_title = song.alternate_title else: old_titles = song.search_title.split('@') if len(old_titles) > 1: new_song.alternate_title = old_titles[1] # Values will be set when cleaning the song. if hasattr(song, 'song_key'): new_song.song_key = song.song_key if hasattr(song, 'transpose_by'): new_song.transpose_by = song.transpose_by new_song.search_title = '' new_song.search_lyrics = '' new_song.song_number = song.song_number new_song.lyrics = song.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 for author in song.authors: existing_author = self.manager.get_object_filtered(Author, Author.display_name == author.display_name) if existing_author is None: existing_author = Author.populate( first_name=author.first_name, last_name=author.last_name, display_name=author.display_name) new_song.add_author(existing_author) if song.book: existing_song_book = self.manager.get_object_filtered(Book, Book.name == song.book.name) if existing_song_book is None: existing_song_book = Book.populate(name=song.book.name, publisher=song.book.publisher) new_song.book = existing_song_book if song.topics: for topic in song.topics: existing_topic = self.manager.get_object_filtered(Topic, Topic.name == topic.name) if existing_topic is None: existing_topic = Topic.populate(name=topic.name) new_song.topics.append(existing_topic) if has_media_files: if song.media_files: for media_file in song.media_files: existing_media_file = self.manager.get_object_filtered( MediaFile, MediaFile.file_name == media_file.file_name) if existing_media_file: new_song.media_files.append(existing_media_file) else: new_song.media_files.append(MediaFile.populate(file_name=media_file.file_name)) clean_song(self.manager, new_song) self.manager.save_object(new_song) if progress_dialog: progress_dialog.setValue(progress_dialog.value() + 1) progress_dialog.setLabelText(WizardStrings.ImportingType % new_song.title) else: self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % new_song.title) if self.stop_import_flag: break self.source_session.close() engine.dispose()
def finish(self): """ All fields have been set to this song. Write the song to disk. """ if not self.checkComplete(): self.setDefaults() return False log.info('committing song %s to database', self.title) song = Song() song.title = self.title if self.import_wizard is not None: self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % song.title) song.alternate_title = self.alternate_title # Values will be set when cleaning the song. song.search_title = '' song.search_lyrics = '' song.verse_order = '' song.song_number = self.songNumber verses_changed_to_other = {} sxml = SongXML() other_count = 1 for (verse_def, verse_text, lang) in self.verses: if verse_def[0].lower() in VerseType.tags: verse_tag = verse_def[0].lower() else: new_verse_def = '%s%d' % (VerseType.tags[VerseType.Other], other_count) verses_changed_to_other[verse_def] = new_verse_def other_count += 1 verse_tag = VerseType.tags[VerseType.Other] log.info('Versetype %s changing to %s', verse_def, new_verse_def) verse_def = new_verse_def sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang) song.lyrics = str(sxml.extract_xml(), 'utf-8') if not self.verseOrderList and self.verseOrderListGeneratedUseful: self.verseOrderList = self.verseOrderListGenerated self.verseOrderList = [verses_changed_to_other.get(v, v) for v in self.verseOrderList] song.verse_order = ' '.join(self.verseOrderList) song.copyright = self.copyright song.comments = self.comments song.theme_name = self.themeName song.ccli_number = self.ccliNumber for authortext in self.authors: author = self.manager.get_object_filtered(Author, Author.display_name == authortext) if not author: author = Author.populate(display_name=authortext, last_name=authortext.split(' ')[-1], first_name=' '.join(authortext.split(' ')[:-1])) song.authors.append(author) if self.songBookName: song_book = self.manager.get_object_filtered(Book, Book.name == self.songBookName) if song_book is None: song_book = Book.populate(name=self.songBookName, publisher=self.songBookPub) song.book = song_book for topictext in self.topics: if not topictext: continue topic = self.manager.get_object_filtered(Topic, Topic.name == topictext) if topic is None: topic = Topic.populate(name=topictext) song.topics.append(topic) # We need to save the song now, before adding the media files, so that # we know where to save the media files to. clean_song(self.manager, song) self.manager.save_object(song) # Now loop through the media files, copy them to the correct location, # and save the song again. for filename, weight in self.mediaFiles: media_file = self.manager.get_object_filtered(MediaFile, MediaFile.file_name == filename) if not media_file: if os.path.dirname(filename): filename = self.copyMediaFile(song.id, filename) song.media_files.append(MediaFile.populate(file_name=filename, weight=weight)) self.manager.save_object(song) self.setDefaults() return True
def finish(self): """ All fields have been set to this song. Write the song to disk. """ if not self.check_complete(): self.set_defaults() return False log.info( 'committing song {title} to database'.format(title=self.title)) song = Song() song.title = self.title if self.import_wizard is not None: # TODO: Verify format() with template variables self.import_wizard.increment_progress_bar( WizardStrings.ImportingType % song.title) song.alternate_title = self.alternate_title # Values will be set when cleaning the song. song.search_title = '' song.search_lyrics = '' song.verse_order = '' song.song_number = self.song_number verses_changed_to_other = {} sxml = SongXML() other_count = 1 for (verse_def, verse_text, lang) in self.verses: if verse_def[0].lower() in VerseType.tags: verse_tag = verse_def[0].lower() else: new_verse_def = '{tag}{count:d}'.format( tag=VerseType.tags[VerseType.Other], count=other_count) verses_changed_to_other[verse_def] = new_verse_def other_count += 1 verse_tag = VerseType.tags[VerseType.Other] log.info('Versetype {old} changing to {new}'.format( old=verse_def, new=new_verse_def)) verse_def = new_verse_def sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang) song.lyrics = str(sxml.extract_xml(), 'utf-8') if not self.verse_order_list and self.verse_order_list_generated_useful: self.verse_order_list = self.verse_order_list_generated self.verse_order_list = [ verses_changed_to_other.get(v, v) for v in self.verse_order_list ] song.verse_order = ' '.join(self.verse_order_list) song.copyright = self.copyright song.comments = self.comments song.theme_name = self.theme_name song.ccli_number = self.ccli_number for author_text, author_type in self.authors: author = self.manager.get_object_filtered( Author, Author.display_name == author_text) if not author: author = Author.populate(display_name=author_text, last_name=author_text.split(' ')[-1], first_name=' '.join( author_text.split(' ')[:-1])) song.add_author(author, author_type) if self.song_book_name: song_book = self.manager.get_object_filtered( Book, Book.name == self.song_book_name) if song_book is None: song_book = Book.populate(name=self.song_book_name, publisher=self.song_book_pub) song.add_songbook_entry(song_book, song.song_number) for topic_text in self.topics: if not topic_text: continue topic = self.manager.get_object_filtered(Topic, Topic.name == topic_text) if topic is None: topic = Topic.populate(name=topic_text) song.topics.append(topic) # We need to save the song now, before adding the media files, so that # we know where to save the media files to. clean_song(self.manager, song) self.manager.save_object(song) # Now loop through the media files, copy them to the correct location, # and save the song again. for filename, weight in self.media_files: media_file = self.manager.get_object_filtered( MediaFile, MediaFile.file_name == filename) if not media_file: if os.path.dirname(filename): filename = self.copy_media_file(song.id, filename) song.media_files.append( MediaFile.populate(file_name=filename, weight=weight)) self.manager.save_object(song) self.set_defaults() return True
def test_build_song_footer_base_songbook(self): """ Test build songs footer with basic song and multiple songbooks """ # GIVEN: A Song and a Service Item song = Song() song.title = 'My Song' song.copyright = 'My copyright' song.authors_songs = [] song.songbook_entries = [] song.ccli_number = '' book1 = MagicMock() book1.name = "My songbook" book2 = MagicMock() book2.name = "Thy songbook" song.songbookentries = [] song.add_songbook_entry(book1, '12') song.add_songbook_entry(book2, '502A') service_item = ServiceItem(None) # WHEN: I generate the Footer with default settings self.media_item.generate_footer(service_item, song) # THEN: The songbook should not be in the footer self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright']) # WHEN: I activate the "display songbook" option self.media_item.display_songbook = True self.media_item.generate_footer(service_item, song) # THEN: The songbook should be in the footer self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'My songbook #12, Thy songbook #502A'])
def do_import(self, progress_dialog=None): """ Run the import for an OpenLP version 2 song database. :param progress_dialog: The QProgressDialog used when importing songs from the FRW. """ class OldAuthor(BaseModel): """ Maps to the authors table """ pass class OldBook(BaseModel): """ Maps to the songbooks table """ pass class OldMediaFile(BaseModel): """ Maps to the media_files table """ pass class OldSong(BaseModel): """ Maps to the songs table """ pass class OldTopic(BaseModel): """ Maps to the topics table """ pass class OldSongBookEntry(BaseModel): """ Maps to the songs_songbooks table """ pass # Check the file type if not isinstance(self.import_source, str) or not self.import_source.endswith('.sqlite'): self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport', 'Not a valid OpenLP 2 song database.')) return self.import_source = 'sqlite:///{url}'.format(url=self.import_source) # Load the db file and reflect it engine = create_engine(self.import_source) source_meta = MetaData() source_meta.reflect(engine) self.source_session = scoped_session(sessionmaker(bind=engine)) # Run some checks to see which version of the database we have if 'media_files' in list(source_meta.tables.keys()): has_media_files = True else: has_media_files = False if 'songs_songbooks' in list(source_meta.tables.keys()): has_songs_books = True else: has_songs_books = False # Load up the tabls and map them out source_authors_table = source_meta.tables['authors'] source_song_books_table = source_meta.tables['song_books'] source_songs_table = source_meta.tables['songs'] source_topics_table = source_meta.tables['topics'] source_authors_songs_table = source_meta.tables['authors_songs'] source_songs_topics_table = source_meta.tables['songs_topics'] source_media_files_songs_table = None # Set up media_files relations if has_media_files: source_media_files_table = source_meta.tables['media_files'] source_media_files_songs_table = source_meta.tables.get('media_files_songs') try: class_mapper(OldMediaFile) except UnmappedClassError: mapper(OldMediaFile, source_media_files_table) if has_songs_books: source_songs_songbooks_table = source_meta.tables['songs_songbooks'] try: class_mapper(OldSongBookEntry) except UnmappedClassError: mapper(OldSongBookEntry, source_songs_songbooks_table, properties={'songbook': relation(OldBook)}) # Set up the songs relationships song_props = { 'authors': relation(OldAuthor, backref='songs', secondary=source_authors_songs_table), 'topics': relation(OldTopic, backref='songs', secondary=source_songs_topics_table) } if has_media_files: if isinstance(source_media_files_songs_table, Table): song_props['media_files'] = relation(OldMediaFile, backref='songs', secondary=source_media_files_songs_table) else: song_props['media_files'] = \ relation(OldMediaFile, backref='songs', foreign_keys=[source_media_files_table.c.song_id], primaryjoin=source_songs_table.c.id == source_media_files_table.c.song_id) if has_songs_books: song_props['songbook_entries'] = relation(OldSongBookEntry, backref='song', cascade='all, delete-orphan') else: song_props['book'] = relation(OldBook, backref='songs') # Map the rest of the tables 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() if self.import_wizard: self.import_wizard.progress_bar.setMaximum(len(source_songs)) for song in source_songs: new_song = Song() new_song.title = song.title if has_media_files and hasattr(song, 'alternate_title'): new_song.alternate_title = song.alternate_title else: old_titles = song.search_title.split('@') if len(old_titles) > 1: new_song.alternate_title = old_titles[1] # Transfer the values to the new song object new_song.search_title = '' new_song.search_lyrics = '' new_song.lyrics = song.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 hasattr(song, 'song_number') and song.song_number: new_song.song_number = song.song_number # Find or create all the authors and add them to the new song object for author in song.authors: existing_author = self.manager.get_object_filtered(Author, Author.display_name == author.display_name) if not existing_author: existing_author = Author.populate( first_name=author.first_name, last_name=author.last_name, display_name=author.display_name) new_song.add_author(existing_author) # Find or create all the topics and add them to the new song object if song.topics: for topic in song.topics: existing_topic = self.manager.get_object_filtered(Topic, Topic.name == topic.name) if not existing_topic: existing_topic = Topic.populate(name=topic.name) new_song.topics.append(existing_topic) # Find or create all the songbooks and add them to the new song object if has_songs_books and song.songbook_entries: for entry in song.songbook_entries: existing_book = self.manager.get_object_filtered(Book, Book.name == entry.songbook.name) if not existing_book: existing_book = Book.populate(name=entry.songbook.name, publisher=entry.songbook.publisher) new_song.add_songbook_entry(existing_book, entry.entry) elif song.book: existing_book = self.manager.get_object_filtered(Book, Book.name == song.book.name) if not existing_book: existing_book = Book.populate(name=song.book.name, publisher=song.book.publisher) new_song.add_songbook_entry(existing_book, '') # Find or create all the media files and add them to the new song object if has_media_files and song.media_files: for media_file in song.media_files: existing_media_file = self.manager.get_object_filtered( MediaFile, MediaFile.file_name == media_file.file_name) if existing_media_file: new_song.media_files.append(existing_media_file) else: new_song.media_files.append(MediaFile.populate(file_name=media_file.file_name)) clean_song(self.manager, new_song) self.manager.save_object(new_song) if progress_dialog: progress_dialog.setValue(progress_dialog.value() + 1) # TODO: Verify format() with template strings progress_dialog.setLabelText(WizardStrings.ImportingType % new_song.title) else: # TODO: Verify format() with template strings self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % new_song.title) if self.stop_import_flag: break self.source_session.close() engine.dispose()