Merge lp:~raoul-snyman/openlp/bug-1557514 into lp:openlp

Proposed by Raoul Snyman
Status: Merged
Merged at revision: 2654
Proposed branch: lp:~raoul-snyman/openlp/bug-1557514
Merge into: lp:openlp
Diff against target: 294 lines (+139/-28)
4 files modified
.bzrignore (+1/-0)
openlp/plugins/songs/lib/db.py (+1/-1)
openlp/plugins/songs/lib/importers/openlp.py (+61/-27)
tests/functional/openlp_plugins/songs/test_openlpimporter.py (+76/-0)
To merge this branch: bzr merge lp:~raoul-snyman/openlp/bug-1557514
Reviewer Review Type Date Requested Status
Tim Bentley Approve
Tomas Groth Pending
Review via email: mp+293176@code.launchpad.net

This proposal supersedes a proposal from 2016-04-27.

To post a comment you must log in.
Revision history for this message
Tomas Groth (tomasgroth) : Posted in a previous version of this proposal
review: Approve
Revision history for this message
Tim Bentley (trb143) : Posted in a previous version of this proposal
review: Approve
Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Missing test!

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Darn, forgot to add the file.

Revision history for this message
Tim Bentley (trb143) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2016-02-11 21:05:41 +0000
+++ .bzrignore 2016-04-27 21:24:28 +0000
@@ -45,3 +45,4 @@
45*.kdev445*.kdev4
46coverage46coverage
47tags47tags
48output
4849
=== modified file 'openlp/plugins/songs/lib/db.py'
--- openlp/plugins/songs/lib/db.py 2016-04-03 19:44:09 +0000
+++ openlp/plugins/songs/lib/db.py 2016-04-27 21:24:28 +0000
@@ -383,7 +383,7 @@
383 # Use lazy='joined' to always load authors when the song is fetched from the database (bug 1366198)383 # Use lazy='joined' to always load authors when the song is fetched from the database (bug 1366198)
384 'authors': relation(Author, secondary=authors_songs_table, viewonly=True, lazy='joined'),384 'authors': relation(Author, secondary=authors_songs_table, viewonly=True, lazy='joined'),
385 'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight),385 'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight),
386 'songbook_entries': relation(SongBookEntry, backref='song', cascade="all, delete-orphan"),386 'songbook_entries': relation(SongBookEntry, backref='song', cascade='all, delete-orphan'),
387 'topics': relation(Topic, backref='songs', secondary=songs_topics_table)387 'topics': relation(Topic, backref='songs', secondary=songs_topics_table)
388 })388 })
389 mapper(Topic, topics_table)389 mapper(Topic, topics_table)
390390
=== modified file 'openlp/plugins/songs/lib/importers/openlp.py'
--- openlp/plugins/songs/lib/importers/openlp.py 2016-04-22 18:25:57 +0000
+++ openlp/plugins/songs/lib/importers/openlp.py 2016-04-27 21:24:28 +0000
@@ -51,7 +51,7 @@
51 :param manager: The song manager for the running OpenLP installation.51 :param manager: The song manager for the running OpenLP installation.
52 :param kwargs: The database providing the data to import.52 :param kwargs: The database providing the data to import.
53 """53 """
54 SongImport.__init__(self, manager, **kwargs)54 super(OpenLPSongImport, self).__init__(manager, **kwargs)
55 self.source_session = None55 self.source_session = None
5656
57 def do_import(self, progress_dialog=None):57 def do_import(self, progress_dialog=None):
@@ -63,49 +63,61 @@
6363
64 class OldAuthor(BaseModel):64 class OldAuthor(BaseModel):
65 """65 """
66 Author model66 Maps to the authors table
67 """67 """
68 pass68 pass
6969
70 class OldBook(BaseModel):70 class OldBook(BaseModel):
71 """71 """
72 Book model72 Maps to the songbooks table
73 """73 """
74 pass74 pass
7575
76 class OldMediaFile(BaseModel):76 class OldMediaFile(BaseModel):
77 """77 """
78 MediaFile model78 Maps to the media_files table
79 """79 """
80 pass80 pass
8181
82 class OldSong(BaseModel):82 class OldSong(BaseModel):
83 """83 """
84 Song model84 Maps to the songs table
85 """85 """
86 pass86 pass
8787
88 class OldTopic(BaseModel):88 class OldTopic(BaseModel):
89 """89 """
90 Topic model90 Maps to the topics table
91 """
92 pass
93
94 class OldSongBookEntry(BaseModel):
95 """
96 Maps to the songs_songbooks table
91 """97 """
92 pass98 pass
9399
94 # Check the file type100 # Check the file type
95 if not self.import_source.endswith('.sqlite'):101 if not isinstance(self.import_source, str) or not self.import_source.endswith('.sqlite'):
96 self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport',102 self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport',
97 'Not a valid OpenLP 2 song database.'))103 'Not a valid OpenLP 2 song database.'))
98 return104 return
99 self.import_source = 'sqlite:///%s' % self.import_source105 self.import_source = 'sqlite:///%s' % self.import_source
100 # Load the db file106 # Load the db file and reflect it
101 engine = create_engine(self.import_source)107 engine = create_engine(self.import_source)
102 source_meta = MetaData()108 source_meta = MetaData()
103 source_meta.reflect(engine)109 source_meta.reflect(engine)
104 self.source_session = scoped_session(sessionmaker(bind=engine))110 self.source_session = scoped_session(sessionmaker(bind=engine))
111 # Run some checks to see which version of the database we have
105 if 'media_files' in list(source_meta.tables.keys()):112 if 'media_files' in list(source_meta.tables.keys()):
106 has_media_files = True113 has_media_files = True
107 else:114 else:
108 has_media_files = False115 has_media_files = False
116 if 'songs_songbooks' in list(source_meta.tables.keys()):
117 has_songs_books = True
118 else:
119 has_songs_books = False
120 # Load up the tabls and map them out
109 source_authors_table = source_meta.tables['authors']121 source_authors_table = source_meta.tables['authors']
110 source_song_books_table = source_meta.tables['song_books']122 source_song_books_table = source_meta.tables['song_books']
111 source_songs_table = source_meta.tables['songs']123 source_songs_table = source_meta.tables['songs']
@@ -113,6 +125,7 @@
113 source_authors_songs_table = source_meta.tables['authors_songs']125 source_authors_songs_table = source_meta.tables['authors_songs']
114 source_songs_topics_table = source_meta.tables['songs_topics']126 source_songs_topics_table = source_meta.tables['songs_topics']
115 source_media_files_songs_table = None127 source_media_files_songs_table = None
128 # Set up media_files relations
116 if has_media_files:129 if has_media_files:
117 source_media_files_table = source_meta.tables['media_files']130 source_media_files_table = source_meta.tables['media_files']
118 source_media_files_songs_table = source_meta.tables.get('media_files_songs')131 source_media_files_songs_table = source_meta.tables.get('media_files_songs')
@@ -120,9 +133,15 @@
120 class_mapper(OldMediaFile)133 class_mapper(OldMediaFile)
121 except UnmappedClassError:134 except UnmappedClassError:
122 mapper(OldMediaFile, source_media_files_table)135 mapper(OldMediaFile, source_media_files_table)
136 if has_songs_books:
137 source_songs_songbooks_table = source_meta.tables['songs_songbooks']
138 try:
139 class_mapper(OldSongBookEntry)
140 except UnmappedClassError:
141 mapper(OldSongBookEntry, source_songs_songbooks_table, properties={'songbook': relation(OldBook)})
142 # Set up the songs relationships
123 song_props = {143 song_props = {
124 'authors': relation(OldAuthor, backref='songs', secondary=source_authors_songs_table),144 'authors': relation(OldAuthor, backref='songs', secondary=source_authors_songs_table),
125 'book': relation(OldBook, backref='songs'),
126 'topics': relation(OldTopic, backref='songs', secondary=source_songs_topics_table)145 'topics': relation(OldTopic, backref='songs', secondary=source_songs_topics_table)
127 }146 }
128 if has_media_files:147 if has_media_files:
@@ -134,6 +153,11 @@
134 relation(OldMediaFile, backref='songs',153 relation(OldMediaFile, backref='songs',
135 foreign_keys=[source_media_files_table.c.song_id],154 foreign_keys=[source_media_files_table.c.song_id],
136 primaryjoin=source_songs_table.c.id == source_media_files_table.c.song_id)155 primaryjoin=source_songs_table.c.id == source_media_files_table.c.song_id)
156 if has_songs_books:
157 song_props['songbook_entries'] = relation(OldSongBookEntry, backref='song', cascade='all, delete-orphan')
158 else:
159 song_props['book'] = relation(OldBook, backref='songs')
160 # Map the rest of the tables
137 try:161 try:
138 class_mapper(OldAuthor)162 class_mapper(OldAuthor)
139 except UnmappedClassError:163 except UnmappedClassError:
@@ -163,44 +187,54 @@
163 old_titles = song.search_title.split('@')187 old_titles = song.search_title.split('@')
164 if len(old_titles) > 1:188 if len(old_titles) > 1:
165 new_song.alternate_title = old_titles[1]189 new_song.alternate_title = old_titles[1]
166 # Values will be set when cleaning the song.190 # Transfer the values to the new song object
167 new_song.search_title = ''191 new_song.search_title = ''
168 new_song.search_lyrics = ''192 new_song.search_lyrics = ''
169 new_song.song_number = song.song_number
170 new_song.lyrics = song.lyrics193 new_song.lyrics = song.lyrics
171 new_song.verse_order = song.verse_order194 new_song.verse_order = song.verse_order
172 new_song.copyright = song.copyright195 new_song.copyright = song.copyright
173 new_song.comments = song.comments196 new_song.comments = song.comments
174 new_song.theme_name = song.theme_name197 new_song.theme_name = song.theme_name
175 new_song.ccli_number = song.ccli_number198 new_song.ccli_number = song.ccli_number
199 if hasattr(song, 'song_number') and song.song_number:
200 new_song.song_number = song.song_number
201 # Find or create all the authors and add them to the new song object
176 for author in song.authors:202 for author in song.authors:
177 existing_author = self.manager.get_object_filtered(Author, Author.display_name == author.display_name)203 existing_author = self.manager.get_object_filtered(Author, Author.display_name == author.display_name)
178 if existing_author is None:204 if not existing_author:
179 existing_author = Author.populate(205 existing_author = Author.populate(
180 first_name=author.first_name,206 first_name=author.first_name,
181 last_name=author.last_name,207 last_name=author.last_name,
182 display_name=author.display_name)208 display_name=author.display_name)
183 new_song.add_author(existing_author)209 new_song.add_author(existing_author)
184 if song.book:210 # Find or create all the topics and add them to the new song object
185 existing_song_book = self.manager.get_object_filtered(Book, Book.name == song.book.name)
186 if existing_song_book is None:
187 existing_song_book = Book.populate(name=song.book.name, publisher=song.book.publisher)
188 new_song.book = existing_song_book
189 if song.topics:211 if song.topics:
190 for topic in song.topics:212 for topic in song.topics:
191 existing_topic = self.manager.get_object_filtered(Topic, Topic.name == topic.name)213 existing_topic = self.manager.get_object_filtered(Topic, Topic.name == topic.name)
192 if existing_topic is None:214 if not existing_topic:
193 existing_topic = Topic.populate(name=topic.name)215 existing_topic = Topic.populate(name=topic.name)
194 new_song.topics.append(existing_topic)216 new_song.topics.append(existing_topic)
195 if has_media_files:217 # Find or create all the songbooks and add them to the new song object
196 if song.media_files:218 if has_songs_books and song.songbook_entries:
197 for media_file in song.media_files:219 for entry in song.songbook_entries:
198 existing_media_file = self.manager.get_object_filtered(220 existing_book = self.manager.get_object_filtered(Book, Book.name == entry.songbook.name)
199 MediaFile, MediaFile.file_name == media_file.file_name)221 if not existing_book:
200 if existing_media_file:222 existing_book = Book.populate(name=entry.songbook.name, publisher=entry.songbook.publisher)
201 new_song.media_files.append(existing_media_file)223 new_song.add_songbook_entry(existing_book, entry.entry)
202 else:224 elif song.book:
203 new_song.media_files.append(MediaFile.populate(file_name=media_file.file_name))225 existing_book = self.manager.get_object_filtered(Book, Book.name == song.book.name)
226 if not existing_book:
227 existing_book = Book.populate(name=song.book.name, publisher=song.book.publisher)
228 new_song.add_songbook_entry(existing_book, '')
229 # Find or create all the media files and add them to the new song object
230 if has_media_files and song.media_files:
231 for media_file in song.media_files:
232 existing_media_file = self.manager.get_object_filtered(
233 MediaFile, MediaFile.file_name == media_file.file_name)
234 if existing_media_file:
235 new_song.media_files.append(existing_media_file)
236 else:
237 new_song.media_files.append(MediaFile.populate(file_name=media_file.file_name))
204 clean_song(self.manager, new_song)238 clean_song(self.manager, new_song)
205 self.manager.save_object(new_song)239 self.manager.save_object(new_song)
206 if progress_dialog:240 if progress_dialog:
207241
=== added file 'tests/functional/openlp_plugins/songs/test_openlpimporter.py'
--- tests/functional/openlp_plugins/songs/test_openlpimporter.py 1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_plugins/songs/test_openlpimporter.py 2016-04-27 21:24:28 +0000
@@ -0,0 +1,76 @@
1# -*- coding: utf-8 -*-
2# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
3
4###############################################################################
5# OpenLP - Open Source Lyrics Projection #
6# --------------------------------------------------------------------------- #
7# Copyright (c) 2008-2016 OpenLP Developers #
8# --------------------------------------------------------------------------- #
9# This program is free software; you can redistribute it and/or modify it #
10# under the terms of the GNU General Public License as published by the Free #
11# Software Foundation; version 2 of the License. #
12# #
13# This program is distributed in the hope that it will be useful, but WITHOUT #
14# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
15# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
16# more details. #
17# #
18# You should have received a copy of the GNU General Public License along #
19# with this program; if not, write to the Free Software Foundation, Inc., 59 #
20# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
21###############################################################################
22"""
23This module contains tests for the OpenLP song importer.
24"""
25from unittest import TestCase
26
27from openlp.plugins.songs.lib.importers.openlp import OpenLPSongImport
28from openlp.core.common import Registry
29from tests.functional import patch, MagicMock
30
31
32class TestOpenLPImport(TestCase):
33 """
34 Test the functions in the :mod:`openlp` importer module.
35 """
36 def setUp(self):
37 """
38 Create the registry
39 """
40 Registry.create()
41
42 def create_importer_test(self):
43 """
44 Test creating an instance of the OpenLP database importer
45 """
46 # GIVEN: A mocked out SongImport class, and a mocked out "manager"
47 with patch('openlp.plugins.songs.lib.importers.openlp.SongImport'):
48 mocked_manager = MagicMock()
49
50 # WHEN: An importer object is created
51 importer = OpenLPSongImport(mocked_manager, filenames=[])
52
53 # THEN: The importer object should not be None
54 self.assertIsNotNone(importer, 'Import should not be none')
55
56 def invalid_import_source_test(self):
57 """
58 Test OpenLPSongImport.do_import handles different invalid import_source values
59 """
60 # GIVEN: A mocked out SongImport class, and a mocked out "manager"
61 with patch('openlp.plugins.songs.lib.importers.openlp.SongImport'):
62 mocked_manager = MagicMock()
63 mocked_import_wizard = MagicMock()
64 importer = OpenLPSongImport(mocked_manager, filenames=[])
65 importer.import_wizard = mocked_import_wizard
66 importer.stop_import_flag = True
67
68 # WHEN: Import source is not a list
69 for source in ['not a list', 0]:
70 importer.import_source = source
71
72 # THEN: do_import should return none and the progress bar maximum should not be set.
73 self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list')
74 self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False,
75 'setMaximum on import_wizard.progress_bar should not have been called')
76