Merge lp:~gtalent/openlp/easyworship6 into lp:openlp

Proposed by Gary Talent
Status: Merged
Merged at revision: 2741
Proposed branch: lp:~gtalent/openlp/easyworship6
Merge into: lp:openlp
Diff against target: 513 lines (+177/-78)
4 files modified
openlp/plugins/songs/lib/__init__.py (+6/-3)
openlp/plugins/songs/lib/importer.py (+31/-21)
openlp/plugins/songs/lib/importers/easyworship.py (+116/-37)
tests/functional/openlp_plugins/songs/test_ewimport.py (+24/-17)
To merge this branch: bzr merge lp:~gtalent/openlp/easyworship6
Reviewer Review Type Date Requested Status
Raoul Snyman Approve
Tomas Groth Approve
Review via email: mp+321504@code.launchpad.net

This proposal supersedes a proposal from 2017-03-29.

Description of the change

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

Generally I think it looks good! Just a few minor fixes.

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

I agree with Tomas, this is great. I used it the other evening to convert a user's EW6 database, and it seemed to work perfectly. Again, just a few issues, and then we can merge this.

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Hey Gary, there's an easier way to do your _find_files method. Use the os.walk() function. Also, if may be president to have a maximum depth just in case the user selected an incorrect directory.

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

I mean, it may be prudent. Don't you just love autocarrot...

Revision history for this message
Gary Talent (gtalent) wrote :

I don't think the os.walk() option is quite the same. That would iterate over every file and directory in the base EW6 directory, but we know it's a much more limited set of options. It has to be Databases/Data/Songs.db, Data/Songs.db, or Songs.db. Also, if the user was at some point messing around in the EW6 directory and made a copy of the Databases or Databases/Data directories, the os.walk() solution could pick up the wrong files. Switching to os.walk() would only save about 5 lines of code, and it would be less efficient and introduce a possible bug.

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

> That would iterate over every file and directory in the base EW6 directory

Actually, it only iterates over every directory, not every file. It gives you a list of files, and you can either iterate over the files yourself (which is usually what people do with it), or you can just check if a particular file is in the list of files.

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Hi Gary,

One other thing. The person I imported their EW6 database for said that the importer added the title of each verse at the top of each verse, so Verse 1 would start with "Verse 1" and then the rest of the lines of the verse. I don't see anything like that in your code, but I thought I'd just check that with you any way.

Revision history for this message
Gary Talent (gtalent) wrote :

OK, thanks for the path_list[-1]. That should be handy.

We've been using our import for months and haven't found any issues like that. Could you send me a copy of their database?

lp:~gtalent/openlp/easyworship6 updated
2705. By Gary Talent

EW6 Importer: Cleanup array cropping.

2706. By Gary Talent

Fix error message for invalid EasyWorship 6 database directories.
The old one was copied from the EasyWorship 2009 database importer.

2707. By Gary Talent

Cleanup EW6 invalid directory messages.

Revision history for this message
Gary Talent (gtalent) wrote :

Also, do you have any idea what their localization settings would be?

I think this is probably an issue with the RTF interpreter. There were some problems with how it parsed Unicode characters in some places, but I addressed it for the problems we were having.

EW (2009 and 6) places the Verse/Chorus/etc. as the first line of each slides words in the database, so I suppose this shouldn't be an altogether surprising bug.

Revision history for this message
Gary Talent (gtalent) wrote :

It looks like they have invalid titles before their songs. We can parse the titles out, but that will require a very different approach to parsing RTF text.

Revision history for this message
Gary Talent (gtalent) wrote :

Or, rather, they have invalid labels on their slides. EW just ignores it, but the importer assumes it's part of the slide if it's not one of a few different labels. The plain text form is really insufficient for differentiating between what EW considers the slide label and the slide text.

This is not an EW6 specific issue. It would read an EW 2009 database the same way.

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Hey Gary,

Is there any way we'd be able to detect if we can ignore the first line?

Revision history for this message
Gary Talent (gtalent) wrote :

It needs a better RTF parser. Right now, it just spits out the plain text and assumes the first line is the label if it starts with Verse/Chorus/etc, but EasyWorship identifies the label as being a particular type of RTF tag.

Here's an example slide:

{\rtf1\ansi\deff0\sdeasyworship2^M
{\fonttbl{\f0 Tahoma;}}^M
{\colortbl ;}^M
{\pard\sdparawysiwghidden\sdlistlevel-1\qc\qdef\sdewparatemplatestyle102{\*\sdasfactor 1}{\*\sdasbaseline 48.6}\sdastextstyle102\plain\sdewtemplatestyle102\fs98{\*\sdfsreal 48.6}{\*\sdfsdef 48.6}\sdfsauto Slide1Label\par}^M
{\pard\sdparawysiwghidden\sdlistlevel-1\qc\qdef\sdewparatemplatestyle102\plain\sdewtemplatestyle102\fs98{\*\sdfsreal 48.6}{\*\sdfsdef 48.6}\sdfsauto MoreSlide1Label\par}^M
{\pard\qc\qdef\sdewparatemplatestyle101{\*\sdasfactor 0}\plain\sdewtemplatestyle101\fs98{\*\sdfsreal 48.6}{\*\sdfsdef 48.6}\sdfsauto Slide1Text\par}^M
{\pard\sdslidemarker\qc\qdef\sdewparatemplatestyle101\plain\sdewtemplatestyle101\fs98{\*\sdfsreal 48.6}{\*\sdfsdef 48.6}\sdfsauto\par}^M
{\pard\sdparawysiwghidden\sdlistlevel-1\qc\qdef\sdewparatemplatestyle102\plain\sdewtemplatestyle102\fs98{\*\sdfsreal 48.6}{\*\sdfsdef 48.6}\sdfsauto Slide2Label\par}^M
{\pard\qc\qdef\sdewparatemplatestyle101{\*\sdasfactor 1}{\*\sdasbaseline 48.6}\sdastextstyle101\plain\sdewtemplatestyle101\fs98{\*\sdfsreal 48.6}{\*\sdfsdef 48.6}\sdfsauto Slide2Text\par}^M
}

Revision history for this message
Tomas Groth (tomasgroth) wrote :

Just wanted to add something to the discussion...
I've had a look at the EW6 song DB that triggered the RTF vs labels discussion...
As you state we strip out RTF tags that could be used to identify label. Normally it works out anyway, because we use the language of OpenLP to look for the localized tag names. That didn't work in this particular case because the translations isn't active/included in normal development setup, and we don't actually have translations for this particular language!

So in summary, I think we should ignore this issue, since for most users it won't be an issue.

review: Approve
Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

I agree.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/plugins/songs/lib/__init__.py'
2--- openlp/plugins/songs/lib/__init__.py 2017-01-15 21:12:55 +0000
3+++ openlp/plugins/songs/lib/__init__.py 2017-04-01 04:48:25 +0000
4@@ -512,10 +512,13 @@
5 elif not ignorable:
6 ebytes.append(int(hex_, 16))
7 elif tchar:
8- if curskip > 0:
9- curskip -= 1
10- elif not ignorable:
11+ if not ignorable:
12 ebytes += tchar.encode()
13+ if len(ebytes) >= curskip:
14+ ebytes = ebytes[curskip:]
15+ else:
16+ curskip -= len(ebytes)
17+ ebytes = ""
18 text = ''.join(out)
19 return text, default_encoding
20
21
22=== modified file 'openlp/plugins/songs/lib/importer.py'
23--- openlp/plugins/songs/lib/importer.py 2016-12-31 11:01:36 +0000
24+++ openlp/plugins/songs/lib/importer.py 2017-04-01 04:48:25 +0000
25@@ -158,26 +158,27 @@
26 DreamBeam = 4
27 EasySlides = 5
28 EasyWorshipDB = 6
29- EasyWorshipService = 7
30- FoilPresenter = 8
31- Lyrix = 9
32- MediaShout = 10
33- OpenSong = 11
34- OPSPro = 12
35- PowerPraise = 13
36- PowerSong = 14
37- PresentationManager = 15
38- ProPresenter = 16
39- SongBeamer = 17
40- SongPro = 18
41- SongShowPlus = 19
42- SongsOfFellowship = 20
43- SundayPlus = 21
44- VideoPsalm = 22
45- WordsOfWorship = 23
46- WorshipAssistant = 24
47- WorshipCenterPro = 25
48- ZionWorx = 26
49+ EasyWorshipSqliteDB = 7
50+ EasyWorshipService = 8
51+ FoilPresenter = 9
52+ Lyrix = 10
53+ MediaShout = 11
54+ OpenSong = 12
55+ OPSPro = 13
56+ PowerPraise = 14
57+ PowerSong = 15
58+ PresentationManager = 16
59+ ProPresenter = 17
60+ SongBeamer = 18
61+ SongPro = 19
62+ SongShowPlus = 20
63+ SongsOfFellowship = 21
64+ SundayPlus = 22
65+ VideoPsalm = 23
66+ WordsOfWorship = 24
67+ WorshipAssistant = 25
68+ WorshipCenterPro = 26
69+ ZionWorx = 27
70
71 # Set optional attribute defaults
72 __defaults__ = {
73@@ -242,8 +243,16 @@
74 'name': 'EasyWorship Song Database',
75 'prefix': 'ew',
76 'selectMode': SongFormatSelect.SingleFile,
77+ 'filter': '{text} (*.DB)'.format(text=translate('SongsPlugin.ImportWizardForm',
78+ 'EasyWorship Song Database'))
79+ },
80+ EasyWorshipSqliteDB: {
81+ 'class': EasyWorshipSongImport,
82+ 'name': 'EasyWorship 6 Song Database',
83+ 'prefix': 'ew',
84+ 'selectMode': SongFormatSelect.SingleFolder,
85 'filter': '{text} (*.db)'.format(text=translate('SongsPlugin.ImportWizardForm',
86- 'EasyWorship Song Database'))
87+ 'EasyWorship 6 Song Data Directory'))
88 },
89 EasyWorshipService: {
90 'class': EasyWorshipSongImport,
91@@ -430,6 +439,7 @@
92 SongFormat.DreamBeam,
93 SongFormat.EasySlides,
94 SongFormat.EasyWorshipDB,
95+ SongFormat.EasyWorshipSqliteDB,
96 SongFormat.EasyWorshipService,
97 SongFormat.FoilPresenter,
98 SongFormat.Lyrix,
99
100=== modified file 'openlp/plugins/songs/lib/importers/easyworship.py'
101--- openlp/plugins/songs/lib/importers/easyworship.py 2016-12-31 11:01:36 +0000
102+++ openlp/plugins/songs/lib/importers/easyworship.py 2017-04-01 04:48:25 +0000
103@@ -28,6 +28,7 @@
104 import re
105 import zlib
106 import logging
107+import sqlite3
108
109 from openlp.core.lib import translate
110 from openlp.plugins.songs.lib import VerseType
111@@ -77,8 +78,10 @@
112 """
113 if self.import_source.lower().endswith('ews'):
114 self.import_ews()
115- else:
116+ elif self.import_source.endswith('DB'):
117 self.import_db()
118+ else:
119+ self.import_sqlite_db()
120
121 def import_ews(self):
122 """
123@@ -125,8 +128,8 @@
124 else:
125 log.debug('Given ews file is of unknown version.')
126 return
127- entry_count = self.get_i32(file_pos)
128- entry_length = self.get_i16(file_pos + 4)
129+ entry_count = self.ews_get_i32(file_pos)
130+ entry_length = self.ews_get_i16(file_pos + 4)
131 file_pos += 6
132 self.import_wizard.progress_bar.setMaximum(entry_count)
133 # Loop over songs
134@@ -144,13 +147,13 @@
135 # 0x08 = Audio, 0x09 = Web
136 # 1410 Song number cstring 10
137 self.set_defaults()
138- self.title = self.get_string(file_pos + 0, 50)
139- authors = self.get_string(file_pos + 307, 50)
140- copyright = self.get_string(file_pos + 358, 100)
141- admin = self.get_string(file_pos + 459, 50)
142- cont_ptr = self.get_i32(file_pos + 800)
143- cont_type = self.get_i32(file_pos + 820)
144- self.ccli_number = self.get_string(file_pos + 1410, 10)
145+ self.title = self.ews_get_string(file_pos + 0, 50)
146+ authors = self.ews_get_string(file_pos + 307, 50)
147+ copyright = self.ews_get_string(file_pos + 358, 100)
148+ admin = self.ews_get_string(file_pos + 459, 50)
149+ cont_ptr = self.ews_get_i32(file_pos + 800)
150+ cont_type = self.ews_get_i32(file_pos + 820)
151+ self.ccli_number = self.ews_get_string(file_pos + 1410, 10)
152 # Only handle content type 1 (songs)
153 if cont_type != 1:
154 file_pos += entry_length
155@@ -164,9 +167,9 @@
156 # Checksum int32be 4 Alder-32 checksum.
157 # (unknown) 4 0x51 0x4b 0x03 0x04
158 # Content length int32le 4 Length of content after decompression
159- content_length = self.get_i32(cont_ptr)
160- deflated_content = self.get_bytes(cont_ptr + 4, content_length - 10)
161- deflated_length = self.get_i32(cont_ptr + 4 + content_length - 6)
162+ content_length = self.ews_get_i32(cont_ptr)
163+ deflated_content = self.ews_get_bytes(cont_ptr + 4, content_length - 10)
164+ deflated_length = self.ews_get_i32(cont_ptr + 4 + content_length - 6)
165 inflated_content = zlib.decompress(deflated_content, 15, deflated_length)
166 if copyright:
167 self.copyright = copyright
168@@ -196,7 +199,7 @@
169 Import the songs from the database
170 """
171 # Open the DB and MB files if they exist
172- import_source_mb = self.import_source.replace('.DB', '.MB').replace('.db', '.mb')
173+ import_source_mb = self.import_source.replace('.DB', '.MB')
174 if not os.path.isfile(self.import_source):
175 self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
176 'This file does not exist.'))
177@@ -260,16 +263,16 @@
178 for i, field_name in enumerate(field_names):
179 field_type, field_size = struct.unpack_from('BB', field_info, i * 2)
180 field_descriptions.append(FieldDescEntry(field_name, field_type, field_size))
181- self.set_record_struct(field_descriptions)
182+ self.db_set_record_struct(field_descriptions)
183 # Pick out the field description indexes we will need
184 try:
185 success = True
186- fi_title = self.find_field(b'Title')
187- fi_author = self.find_field(b'Author')
188- fi_copy = self.find_field(b'Copyright')
189- fi_admin = self.find_field(b'Administrator')
190- fi_words = self.find_field(b'Words')
191- fi_ccli = self.find_field(b'Song Number')
192+ fi_title = self.db_find_field(b'Title')
193+ fi_author = self.db_find_field(b'Author')
194+ fi_copy = self.db_find_field(b'Copyright')
195+ fi_admin = self.db_find_field(b'Administrator')
196+ fi_words = self.db_find_field(b'Words')
197+ fi_ccli = self.db_find_field(b'Song Number')
198 except IndexError:
199 # This is the wrong table
200 success = False
201@@ -297,13 +300,13 @@
202 raw_record = db_file.read(record_size)
203 self.fields = self.record_structure.unpack(raw_record)
204 self.set_defaults()
205- self.title = self.get_field(fi_title).decode(self.encoding)
206+ self.title = self.db_get_field(fi_title).decode(self.encoding)
207 # Get remaining fields.
208- copy = self.get_field(fi_copy)
209- admin = self.get_field(fi_admin)
210- ccli = self.get_field(fi_ccli)
211- authors = self.get_field(fi_author)
212- words = self.get_field(fi_words)
213+ copy = self.db_get_field(fi_copy)
214+ admin = self.db_get_field(fi_admin)
215+ ccli = self.db_get_field(fi_ccli)
216+ authors = self.db_get_field(fi_author)
217+ words = self.db_get_field(fi_words)
218 if copy:
219 self.copyright = copy.decode(self.encoding)
220 if admin:
221@@ -337,6 +340,82 @@
222 db_file.close()
223 self.memo_file.close()
224
225+ def _find_file(self, base_path, path_list):
226+ """
227+ Find the specified file, with the option of the file being at any level in the specified directory structure.
228+
229+ :param base_path: the location search in
230+ :param path_list: the targeted file, preceded by directories that may be their parents relative to the base_path
231+ :return: path for targeted file
232+ """
233+ target_file = ''
234+ while len(path_list) > 0:
235+ target_file = os.path.join(path_list[-1], target_file)
236+ path_list = path_list[:len(path_list) - 1]
237+ full_path = os.path.join(base_path, target_file)
238+ full_path = full_path[:len(full_path) - 1]
239+ if os.path.isfile(full_path):
240+ return full_path
241+ return ''
242+
243+ def import_sqlite_db(self):
244+ """
245+ Import the songs from an EasyWorship 6 SQLite database
246+ """
247+ songs_db_path = self._find_file(self.import_source, ["Databases", "Data", "Songs.db"])
248+ song_words_db_path = self._find_file(self.import_source, ["Databases", "Data", "SongWords.db"])
249+ invalid_dir_msg = 'This does not appear to be a valid Easy Worship 6 database directory.'
250+ # check to see if needed files are there
251+ if not os.path.isfile(songs_db_path):
252+ self.log_error(songs_db_path, translate('SongsPlugin.EasyWorshipSongImport', invalid_dir_msg))
253+ return
254+ if not os.path.isfile(song_words_db_path):
255+ self.log_error(song_words_db_path, translate('SongsPlugin.EasyWorshipSongImport', invalid_dir_msg))
256+ return
257+ # get database handles
258+ songs_conn = sqlite3.connect(songs_db_path)
259+ words_conn = sqlite3.connect(song_words_db_path)
260+ if songs_conn is None or words_conn is None:
261+ self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
262+ 'This is not a valid Easy Worship 6 database.'))
263+ songs_conn.close()
264+ words_conn.close()
265+ return
266+ songs_db = songs_conn.cursor()
267+ words_db = words_conn.cursor()
268+ if songs_conn is None or words_conn is None:
269+ self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
270+ 'This is not a valid Easy Worship 6 database.'))
271+ songs_conn.close()
272+ words_conn.close()
273+ return
274+ # Take a stab at how text is encoded
275+ self.encoding = 'cp1252'
276+ self.encoding = retrieve_windows_encoding(self.encoding)
277+ if not self.encoding:
278+ log.debug('No encoding set.')
279+ return
280+ # import songs
281+ songs = songs_db.execute('SELECT rowid,title,author,copyright,vendor_id FROM song;')
282+ for song in songs:
283+ song_id = song[0]
284+ # keep extra copy of title for error message because error check clears it
285+ self.title = title = song[1]
286+ self.author = song[2]
287+ self.copyright = song[3]
288+ self.ccli_number = song[4]
289+ words = words_db.execute('SELECT words FROM word WHERE song_id = ?;', (song_id,))
290+ self.set_song_import_object(self.author, words.fetchone()[0].encode())
291+ if not self.finish():
292+ self.log_error(self.import_source,
293+ translate('SongsPlugin.EasyWorshipSongImport',
294+ '"{title}" could not be imported. {entry}').
295+ format(title=title, entry=self.entry_error_log))
296+ # close database handles
297+ songs_conn.close()
298+ words_conn.close()
299+ return
300+
301 def set_song_import_object(self, authors, words):
302 """
303 Set the SongImport object members.
304@@ -409,7 +488,7 @@
305 self.comments += str(translate('SongsPlugin.EasyWorshipSongImport',
306 '\n[above are Song Tags with notes imported from EasyWorship]'))
307
308- def find_field(self, field_name):
309+ def db_find_field(self, field_name):
310 """
311 Find a field in the descriptions
312
313@@ -417,7 +496,7 @@
314 """
315 return [i for i, x in enumerate(self.field_descriptions) if x.name == field_name][0]
316
317- def set_record_struct(self, field_descriptions):
318+ def db_set_record_struct(self, field_descriptions):
319 """
320 Save the record structure
321
322@@ -445,7 +524,7 @@
323 self.record_structure = struct.Struct(''.join(fsl))
324 self.field_descriptions = field_descriptions
325
326- def get_field(self, field_desc_index):
327+ def db_get_field(self, field_desc_index):
328 """
329 Extract the field
330
331@@ -489,7 +568,7 @@
332 else:
333 return 0
334
335- def get_bytes(self, pos, length):
336+ def ews_get_bytes(self, pos, length):
337 """
338 Get bytes from ews_file
339
340@@ -500,7 +579,7 @@
341 self.ews_file.seek(pos)
342 return self.ews_file.read(length)
343
344- def get_string(self, pos, length):
345+ def ews_get_string(self, pos, length):
346 """
347 Get string from ews_file
348
349@@ -508,12 +587,12 @@
350 :param length: Characters to read
351 :return: String read
352 """
353- bytes = self.get_bytes(pos, length)
354+ bytes = self.ews_get_bytes(pos, length)
355 mask = '<' + str(length) + 's'
356 byte_str, = struct.unpack(mask, bytes)
357 return byte_str.decode(self.encoding).replace('\0', '').strip()
358
359- def get_i16(self, pos):
360+ def ews_get_i16(self, pos):
361 """
362 Get short int from ews_file
363
364@@ -521,19 +600,19 @@
365 :return: Short integer read
366 """
367
368- bytes = self.get_bytes(pos, 2)
369+ bytes = self.ews_get_bytes(pos, 2)
370 mask = '<h'
371 number, = struct.unpack(mask, bytes)
372 return number
373
374- def get_i32(self, pos):
375+ def ews_get_i32(self, pos):
376 """
377 Get long int from ews_file
378
379 :param pos: Position to read from
380 :return: Long integer read
381 """
382- bytes = self.get_bytes(pos, 4)
383+ bytes = self.ews_get_bytes(pos, 4)
384 mask = '<i'
385 number, = struct.unpack(mask, bytes)
386 return number
387
388=== modified file 'tests/functional/openlp_plugins/songs/test_ewimport.py'
389--- tests/functional/openlp_plugins/songs/test_ewimport.py 2016-12-31 11:01:36 +0000
390+++ tests/functional/openlp_plugins/songs/test_ewimport.py 2017-04-01 04:48:25 +0000
391@@ -188,7 +188,7 @@
392
393 def test_find_field_exists(self):
394 """
395- Test finding an existing field in a given list using the :mod:`find_field`
396+ Test finding an existing field in a given list using the :mod:`db_find_field`
397 """
398 # GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions.
399 with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
400@@ -202,11 +202,11 @@
401 for field_name in existing_fields:
402
403 # THEN: The item corresponding the index returned should have the same name attribute
404- self.assertEqual(importer.field_descriptions[importer.find_field(field_name)].name, field_name)
405+ self.assertEqual(importer.field_descriptions[importer.db_find_field(field_name)].name, field_name)
406
407 def test_find_non_existing_field(self):
408 """
409- Test finding an non-existing field in a given list using the :mod:`find_field`
410+ Test finding an non-existing field in a given list using the :mod:`db_find_field`
411 """
412 # GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions
413 with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
414@@ -219,11 +219,11 @@
415 for field_name in non_existing_fields:
416
417 # THEN: The importer object should not be None
418- self.assertRaises(IndexError, importer.find_field, field_name)
419+ self.assertRaises(IndexError, importer.db_find_field, field_name)
420
421 def test_set_record_struct(self):
422 """
423- Test the :mod:`set_record_struct` module
424+ Test the :mod:`db_set_record_struct` module
425 """
426 # GIVEN: A mocked out SongImport class, a mocked out struct class, and a mocked out "manager" and a list of
427 # field descriptions
428@@ -232,17 +232,17 @@
429 mocked_manager = MagicMock()
430 importer = EasyWorshipSongImport(mocked_manager, filenames=[])
431
432- # WHEN: set_record_struct is called with a list of field descriptions
433- return_value = importer.set_record_struct(TEST_FIELD_DESCS)
434+ # WHEN: db_set_record_struct is called with a list of field descriptions
435+ return_value = importer.db_set_record_struct(TEST_FIELD_DESCS)
436
437- # THEN: set_record_struct should return None and Struct should be called with a value representing
438+ # THEN: db_set_record_struct should return None and Struct should be called with a value representing
439 # the list of field descriptions
440- self.assertIsNone(return_value, 'set_record_struct should return None')
441+ self.assertIsNone(return_value, 'db_set_record_struct should return None')
442 mocked_struct.Struct.assert_called_with('>50sHIB250s250s10sQ')
443
444 def test_get_field(self):
445 """
446- Test the :mod:`get_field` module
447+ Test the :mod:`db_get_field` module
448 """
449 # GIVEN: A mocked out SongImport class, a mocked out "manager", an encoding and some test data and known results
450 with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
451@@ -255,16 +255,16 @@
452
453 # WHEN: Called with test data
454 for field_index, result in field_results:
455- return_value = importer.get_field(field_index)
456+ return_value = importer.db_get_field(field_index)
457
458- # THEN: get_field should return the known results
459+ # THEN: db_get_field should return the known results
460 self.assertEqual(return_value, result,
461- 'get_field should return "%s" when called with "%s"' %
462+ 'db_get_field should return "%s" when called with "%s"' %
463 (result, TEST_FIELDS[field_index]))
464
465 def test_get_memo_field(self):
466 """
467- Test the :mod:`get_field` module
468+ Test the :mod:`db_get_field` module
469 """
470 for test_results in GET_MEMO_FIELD_TEST_RESULTS:
471 # GIVEN: A mocked out SongImport class, a mocked out "manager", a mocked out memo_file and an encoding
472@@ -284,8 +284,9 @@
473 get_field_read_calls = test_results[2]['read']
474 get_field_seek_calls = test_results[2]['seek']
475
476- # THEN: get_field should return the appropriate value with the appropriate mocked objects being called
477- self.assertEqual(importer.get_field(field_index), get_field_result)
478+ # THEN: db_get_field should return the appropriate value with the appropriate mocked objects being
479+ # called
480+ self.assertEqual(importer.db_get_field(field_index), get_field_result)
481 for call in get_field_read_calls:
482 mocked_memo_file.read.assert_any_call(call)
483 for call in get_field_seek_calls:
484@@ -406,6 +407,12 @@
485 mocked_retrieve_windows_encoding.assert_any_call(encoding)
486
487 def test_db_file_import(self):
488+ return self._test_db_file_import(os.path.join(TEST_PATH, 'Songs.DB'))
489+
490+ def test_sqlite_db_file_import(self):
491+ return self._test_db_file_import(os.path.join(TEST_PATH, 'ew6'))
492+
493+ def _test_db_file_import(self, source_path):
494 """
495 Test the actual import of real song database files and check that the imported data is correct.
496 """
497@@ -433,7 +440,7 @@
498 importer.topics = []
499
500 # WHEN: Importing each file
501- importer.import_source = os.path.join(TEST_PATH, 'Songs.DB')
502+ importer.import_source = source_path
503 import_result = importer.do_import()
504
505 # THEN: do_import should return none, the song data should be as expected, and finish should have been
506
507=== added directory 'tests/resources/easyworshipsongs/ew6'
508=== added directory 'tests/resources/easyworshipsongs/ew6/Databases'
509=== added directory 'tests/resources/easyworshipsongs/ew6/Databases/Data'
510=== added file 'tests/resources/easyworshipsongs/ew6/Databases/Data/SongWords.db'
511Binary files tests/resources/easyworshipsongs/ew6/Databases/Data/SongWords.db 1970-01-01 00:00:00 +0000 and tests/resources/easyworshipsongs/ew6/Databases/Data/SongWords.db 2017-04-01 04:48:25 +0000 differ
512=== added file 'tests/resources/easyworshipsongs/ew6/Databases/Data/Songs.db'
513Binary files tests/resources/easyworshipsongs/ew6/Databases/Data/Songs.db 1970-01-01 00:00:00 +0000 and tests/resources/easyworshipsongs/ew6/Databases/Data/Songs.db 2017-04-01 04:48:25 +0000 differ