Merge lp:~sam92/openlp/bug-1533081 into lp:openlp

Proposed by Samuel Mehrbrodt
Status: Merged
Merged at revision: 2609
Proposed branch: lp:~sam92/openlp/bug-1533081
Merge into: lp:openlp
Diff against target: 249 lines (+122/-23)
3 files modified
openlp/core/utils/__init__.py (+1/-1)
openlp/plugins/songs/lib/importers/openlp.py (+32/-22)
tests/utils/test_pep8.py (+89/-0)
To merge this branch: bzr merge lp:~sam92/openlp/bug-1533081
Reviewer Review Type Date Requested Status
Raoul Snyman Approve
Tim Bentley Approve
Review via email: mp+282817@code.launchpad.net

Description of the change

* Fix importing older song dbs by migrating them first
* Fix comparing songs without title

lp:~sam92/openlp/bug-1533081 (revision 2615)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/1260/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/1184/
[SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/1123/
[FAILURE] https://ci.openlp.io/job/Branch-04a-Windows_Functional_Tests/959/
Stopping after failure

To post a comment you must log in.
Revision history for this message
Tim Bentley (trb143) :
review: Approve
Revision history for this message
Raoul Snyman (raoul-snyman) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/utils/__init__.py'
2--- openlp/core/utils/__init__.py 2015-12-31 22:46:06 +0000
3+++ openlp/core/utils/__init__.py 2016-01-15 21:49:07 +0000
4@@ -529,7 +529,7 @@
5 key = [int(part) if part.isdigit() else get_locale_key(part) for part in key]
6 # Python 3 does not support comparison of different types anymore. So make sure, that we do not compare str
7 # and int.
8- if string[0].isdigit():
9+ if string and string[0].isdigit():
10 return [b''] + key
11 return key
12
13
14=== modified file 'openlp/plugins/songs/lib/importers/openlp.py'
15--- openlp/plugins/songs/lib/importers/openlp.py 2015-12-31 22:46:06 +0000
16+++ openlp/plugins/songs/lib/importers/openlp.py 2016-01-15 21:49:07 +0000
17@@ -24,15 +24,18 @@
18 song databases into the current installation database.
19 """
20 import logging
21+import os
22+import shutil
23+import tempfile
24
25 from sqlalchemy import create_engine, MetaData, Table
26 from sqlalchemy.orm import class_mapper, mapper, relation, scoped_session, sessionmaker
27 from sqlalchemy.orm.exc import UnmappedClassError
28
29 from openlp.core.common import translate
30-from openlp.core.lib.db import BaseModel
31+from openlp.core.lib.db import BaseModel, upgrade_db
32 from openlp.core.ui.wizard import WizardStrings
33-from openlp.plugins.songs.lib import clean_song
34+from openlp.plugins.songs.lib import clean_song, upgrade
35 from openlp.plugins.songs.lib.db import Author, Book, Song, Topic, MediaFile
36 from .songimport import SongImport
37
38@@ -67,12 +70,6 @@
39 """
40 pass
41
42- class OldBook(BaseModel):
43- """
44- Book model
45- """
46- pass
47-
48 class OldMediaFile(BaseModel):
49 """
50 MediaFile model
51@@ -91,26 +88,38 @@
52 """
53 pass
54
55+ class OldSongBookEntry(BaseModel):
56+ """
57+ SongbookEntry model
58+ """
59+ pass
60+
61 # Check the file type
62 if not self.import_source.endswith('.sqlite'):
63 self.log_error(self.import_source, translate('SongsPlugin.OpenLPSongImport',
64 'Not a valid OpenLP 2 song database.'))
65 return
66- self.import_source = 'sqlite:///%s' % self.import_source
67+ # Operate on a copy - We might need to apply migrations to the db and don't want to change the existing db
68+ import_db = os.path.join(tempfile.gettempdir(), os.path.basename(self.import_source))
69+ shutil.copy(self.import_source, import_db)
70+ import_db_url = 'sqlite:///%s' % import_db
71+ # Apply migrations
72+ upgrade_db(import_db_url, upgrade)
73 # Load the db file
74- engine = create_engine(self.import_source)
75+ engine = create_engine(import_db_url)
76 source_meta = MetaData()
77 source_meta.reflect(engine)
78 self.source_session = scoped_session(sessionmaker(bind=engine))
79+
80 if 'media_files' in list(source_meta.tables.keys()):
81 has_media_files = True
82 else:
83 has_media_files = False
84 source_authors_table = source_meta.tables['authors']
85- source_song_books_table = source_meta.tables['song_books']
86 source_songs_table = source_meta.tables['songs']
87 source_topics_table = source_meta.tables['topics']
88 source_authors_songs_table = source_meta.tables['authors_songs']
89+ source_songs_songbooks_table = source_meta.tables['songs_songbooks']
90 source_songs_topics_table = source_meta.tables['songs_topics']
91 source_media_files_songs_table = None
92 if has_media_files:
93@@ -122,8 +131,8 @@
94 mapper(OldMediaFile, source_media_files_table)
95 song_props = {
96 'authors': relation(OldAuthor, backref='songs', secondary=source_authors_songs_table),
97- 'book': relation(OldBook, backref='songs'),
98- 'topics': relation(OldTopic, backref='songs', secondary=source_songs_topics_table)
99+ 'topics': relation(OldTopic, backref='songs', secondary=source_songs_topics_table),
100+ 'songbook_entries': relation(OldSongBookEntry, backref='song'),
101 }
102 if has_media_files:
103 if isinstance(source_media_files_songs_table, Table):
104@@ -139,10 +148,6 @@
105 except UnmappedClassError:
106 mapper(OldAuthor, source_authors_table)
107 try:
108- class_mapper(OldBook)
109- except UnmappedClassError:
110- mapper(OldBook, source_song_books_table)
111- try:
112 class_mapper(OldSong)
113 except UnmappedClassError:
114 mapper(OldSong, source_songs_table, properties=song_props)
115@@ -150,6 +155,10 @@
116 class_mapper(OldTopic)
117 except UnmappedClassError:
118 mapper(OldTopic, source_topics_table)
119+ try:
120+ class_mapper(OldSongBookEntry)
121+ except UnmappedClassError:
122+ mapper(OldSongBookEntry, source_songs_songbooks_table)
123
124 source_songs = self.source_session.query(OldSong).all()
125 if self.import_wizard:
126@@ -166,7 +175,6 @@
127 # Values will be set when cleaning the song.
128 new_song.search_title = ''
129 new_song.search_lyrics = ''
130- new_song.song_number = song.song_number
131 new_song.lyrics = song.lyrics
132 new_song.verse_order = song.verse_order
133 new_song.copyright = song.copyright
134@@ -181,11 +189,12 @@
135 last_name=author.last_name,
136 display_name=author.display_name)
137 new_song.add_author(existing_author)
138- if song.book:
139- existing_song_book = self.manager.get_object_filtered(Book, Book.name == song.book.name)
140+ for songbook_entry in song.songbook_entries:
141+ existing_song_book = self.manager.get_object_filtered(Book, Book.name == songbook_entry.book.name)
142 if existing_song_book is None:
143- existing_song_book = Book.populate(name=song.book.name, publisher=song.book.publisher)
144- new_song.book = existing_song_book
145+ existing_song_book = Book.populate(name=songbook_entry.book.name,
146+ publisher=songbook_entry.book.publisher)
147+ new_song.add_songbook_entry(existing_song_book, songbook_entry.entry)
148 if song.topics:
149 for topic in song.topics:
150 existing_topic = self.manager.get_object_filtered(Topic, Topic.name == topic.name)
151@@ -212,3 +221,4 @@
152 break
153 self.source_session.close()
154 engine.dispose()
155+ os.remove(import_db)
156
157=== added file 'tests/utils/test_pep8.py'
158--- tests/utils/test_pep8.py 1970-01-01 00:00:00 +0000
159+++ tests/utils/test_pep8.py 2016-01-15 21:49:07 +0000
160@@ -0,0 +1,89 @@
161+# -*- coding: utf-8 -*-
162+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
163+
164+###############################################################################
165+# OpenLP - Open Source Lyrics Projection #
166+# --------------------------------------------------------------------------- #
167+# Copyright (c) 2008-2016 OpenLP Developers #
168+# --------------------------------------------------------------------------- #
169+# This program is free software; you can redistribute it and/or modify it #
170+# under the terms of the GNU General Public License as published by the Free #
171+# Software Foundation; version 2 of the License. #
172+# #
173+# This program is distributed in the hope that it will be useful, but WITHOUT #
174+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
175+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
176+# more details. #
177+# #
178+# You should have received a copy of the GNU General Public License along #
179+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
180+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
181+###############################################################################
182+
183+"""
184+Package to test for pep8 errors
185+Inspired by http://blog.jameskyle.org/2014/05/pep8-pylint-tests-with-nose-xunit/
186+"""
187+
188+import os
189+import pep8
190+import re
191+
192+from unittest import TestCase
193+
194+from tests.utils.constants import OPENLP_PATH
195+from tests.utils import is_running_on_ci
196+
197+PROJ_ROOT = 'openlp'
198+
199+
200+class TestPep8(TestCase):
201+
202+ def pep8_test(self):
203+ """
204+ Test for pep8 conformance
205+ """
206+ pattern = re.compile(r'.*({0}.*\.py)'.format(PROJ_ROOT))
207+ pep8style = pep8.StyleGuide(reporter=CustomReport, config_file=os.path.join(OPENLP_PATH, 'setup.cfg'))
208+
209+ report = pep8style.init_report()
210+ pep8style.input_dir(OPENLP_PATH)
211+
212+ for error in report.results:
213+ msg = "{path}: {code} {row}, {col} - {text}"
214+
215+ match = pattern.match(error['path'])
216+ if match:
217+ rel_path = match.group(1)
218+ else:
219+ rel_path = error['path']
220+
221+ print(msg.format(
222+ path=rel_path,
223+ code=error['code'],
224+ row=error['row'],
225+ col=error['col'],
226+ text=error['text']
227+ ))
228+ if report.results:
229+ self.fail("Please fix the PEP8 warnings.")
230+
231+
232+class CustomReport(pep8.StandardReport):
233+ """
234+ Collect report into an array of results.
235+ """
236+ results = []
237+
238+ def get_file_results(self):
239+ if self._deferred_print:
240+ self._deferred_print.sort()
241+ for line_number, offset, code, text, _ in self._deferred_print:
242+ self.results.append({
243+ 'path': self.filename,
244+ 'row': self.line_offset + line_number,
245+ 'col': offset + 1,
246+ 'code': code,
247+ 'text': text,
248+ })
249+ return self.file_errors