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)
Exemple #2
0
    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)
Exemple #3
0
    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
Exemple #4
0
    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
Exemple #5
0
    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)
Exemple #6
0
    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)
Exemple #7
0
    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)
Exemple #8
0
    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
Exemple #9
0
    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)
Exemple #10
0
    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))
Exemple #11
0
    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.')
Exemple #12
0
    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))
Exemple #13
0
    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.')
Exemple #14
0
    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
Exemple #15
0
    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()
Exemple #16
0
    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'
        ]
Exemple #17
0
    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()
Exemple #18
0
 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
Exemple #19
0
 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
Exemple #20
0
    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'])
Exemple #21
0
    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()