Merge lp:~gtalent/openlp/easyworship6 into lp:openlp
- easyworship6
- Merge into trunk
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 |
Related bugs: |
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.
Commit message
Description of the change
I removed the empty lines and added an option for choosing a directory at any level of the EW6 database directory.
lp:~gtalent/openlp/openlp (revision 2704)
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
----
This adds support for importing EasyWorship 6 databases. It's been a while since I made the changes though, so I had to merge in the trunk changes beforehand to get it to pass the Jenkins tests.
lp:~gtalent/openlp/openlp (revision 2701)
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
Tomas Groth (tomasgroth) wrote : Posted in a previous version of this proposal | # |
Tomas Groth (tomasgroth) : Posted in a previous version of this proposal | # |
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.
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.
Raoul Snyman (raoul-snyman) wrote : | # |
I mean, it may be prudent. Don't you just love autocarrot...
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/
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.
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.
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?
- 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.
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.
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.
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.
Raoul Snyman (raoul-snyman) wrote : | # |
Hey Gary,
Is there any way we'd be able to detect if we can ignore the first line?
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\
{\fonttbl{\f0 Tahoma;}}^M
{\colortbl ;}^M
{\pard\
{\pard\
{\pard\
{\pard\
{\pard\
{\pard\
}
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.
Preview Diff
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' |
511 | Binary 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' |
513 | Binary 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 |
Generally I think it looks good! Just a few minor fixes.