Merge lp:~sfindlay/openlp/zionworx-import into lp:openlp
- zionworx-import
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 1986 |
Proposed branch: | lp:~sfindlay/openlp/zionworx-import |
Merge into: | lp:openlp |
Prerequisite: | lp:~sfindlay/openlp/refactor-song-import |
Diff against target: |
342 lines (+202/-14) 5 files modified
openlp/core/ui/wizard.py (+1/-1) openlp/plugins/bibles/lib/csvbible.py (+1/-1) openlp/plugins/songs/forms/songimportform.py (+37/-9) openlp/plugins/songs/lib/importer.py (+21/-3) openlp/plugins/songs/lib/zionworximport.py (+142/-0) |
To merge this branch: | bzr merge lp:~sfindlay/openlp/zionworx-import |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jonathan Corwin (community) | Approve | ||
Raoul Snyman | Approve | ||
Phill | Pending | ||
Review via email: mp+109433@code.launchpad.net |
This proposal supersedes a proposal from 2012-06-07.
Commit message
Added ZionWorx importer. Added descriptionLabel to song import wizard.
Description of the change
Added ZionWorx song database importer.
* Tested on win7 x64 with databases from 3 users. Total 3171 songs, including non-English characters.
* ZionWorx [1] is freeware and windows-only
* Users can download freeware utility "TurboDB Data Exchange" [2] (Win/Linux) and use this command to dump their ZionWorx database to a CSV file which can then be imported by OpenLP:
>> tdbdatax MainTable.dat songstable.csv -fsdf -s, -qd
* Since the importer is not a direct ZionWorx import, I added a descriptionLabel widget to the import wizard, pointing users to the Manual for further info.
* This descriptionLabel widget is also available to other importers (see bug 832345) [3]
[1] http://
[2] http://
[3] https:/
Jonathan Corwin (j-corwin) wrote : Posted in a previous version of this proposal | # |
Raoul Snyman (raoul-snyman) : | # |
Jonathan Corwin (j-corwin) : | # |
Preview Diff
1 | === modified file 'openlp/core/ui/wizard.py' | |||
2 | --- openlp/core/ui/wizard.py 2012-06-08 21:10:26 +0000 | |||
3 | +++ openlp/core/ui/wizard.py 2012-06-08 21:10:26 +0000 | |||
4 | @@ -99,7 +99,7 @@ | |||
5 | 99 | 99 | ||
6 | 100 | def setupUi(self, image): | 100 | def setupUi(self, image): |
7 | 101 | """ | 101 | """ |
9 | 102 | Set up the wizard UI | 102 | Set up the wizard UI. |
10 | 103 | """ | 103 | """ |
11 | 104 | self.setModal(True) | 104 | self.setModal(True) |
12 | 105 | self.setWizardStyle(QtGui.QWizard.ModernStyle) | 105 | self.setWizardStyle(QtGui.QWizard.ModernStyle) |
13 | 106 | 106 | ||
14 | === modified file 'openlp/plugins/bibles/lib/csvbible.py' | |||
15 | --- openlp/plugins/bibles/lib/csvbible.py 2011-12-27 10:33:55 +0000 | |||
16 | +++ openlp/plugins/bibles/lib/csvbible.py 2012-06-08 21:10:26 +0000 | |||
17 | @@ -73,7 +73,7 @@ | |||
18 | 73 | 73 | ||
19 | 74 | def __init__(self, parent, **kwargs): | 74 | def __init__(self, parent, **kwargs): |
20 | 75 | """ | 75 | """ |
22 | 76 | Loads a Bible from a set of CVS files. | 76 | Loads a Bible from a set of CSV files. |
23 | 77 | This class assumes the files contain all the information and | 77 | This class assumes the files contain all the information and |
24 | 78 | a clean bible is being loaded. | 78 | a clean bible is being loaded. |
25 | 79 | """ | 79 | """ |
26 | 80 | 80 | ||
27 | === modified file 'openlp/plugins/songs/forms/songimportform.py' | |||
28 | --- openlp/plugins/songs/forms/songimportform.py 2012-06-08 21:10:26 +0000 | |||
29 | +++ openlp/plugins/songs/forms/songimportform.py 2012-06-08 21:10:26 +0000 | |||
30 | @@ -133,6 +133,9 @@ | |||
31 | 133 | self.formatLayout.setItem(1, QtGui.QFormLayout.LabelRole, | 133 | self.formatLayout.setItem(1, QtGui.QFormLayout.LabelRole, |
32 | 134 | self.formatSpacer) | 134 | self.formatSpacer) |
33 | 135 | self.sourceLayout.addLayout(self.formatLayout) | 135 | self.sourceLayout.addLayout(self.formatLayout) |
34 | 136 | self.formatHSpacing = self.formatLayout.horizontalSpacing() | ||
35 | 137 | self.formatVSpacing = self.formatLayout.verticalSpacing() | ||
36 | 138 | self.formatLayout.setVerticalSpacing(0) | ||
37 | 136 | self.stackSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, | 139 | self.stackSpacer = QtGui.QSpacerItem(10, 0, QtGui.QSizePolicy.Fixed, |
38 | 137 | QtGui.QSizePolicy.Expanding) | 140 | QtGui.QSizePolicy.Expanding) |
39 | 138 | self.formatStack = QtGui.QStackedLayout() | 141 | self.formatStack = QtGui.QStackedLayout() |
40 | @@ -160,11 +163,15 @@ | |||
41 | 160 | self.sourcePage.setSubTitle(WizardStrings.ImportSelectLong) | 163 | self.sourcePage.setSubTitle(WizardStrings.ImportSelectLong) |
42 | 161 | self.formatLabel.setText(WizardStrings.FormatLabel) | 164 | self.formatLabel.setText(WizardStrings.FormatLabel) |
43 | 162 | for format in SongFormat.get_format_list(): | 165 | for format in SongFormat.get_format_list(): |
48 | 163 | format_name, custom_combo_text, select_mode = SongFormat.get( | 166 | format_name, custom_combo_text, description_text, select_mode = \ |
49 | 164 | format, u'name', u'comboBoxText', u'selectMode') | 167 | SongFormat.get(format, u'name', u'comboBoxText', |
50 | 165 | combo_box_text = custom_combo_text if custom_combo_text \ | 168 | u'descriptionText', u'selectMode') |
51 | 166 | else format_name | 169 | combo_box_text = (custom_combo_text if custom_combo_text else |
52 | 170 | format_name) | ||
53 | 167 | self.formatComboBox.setItemText(format, combo_box_text) | 171 | self.formatComboBox.setItemText(format, combo_box_text) |
54 | 172 | if description_text is not None: | ||
55 | 173 | self.formatWidgets[format][u'descriptionLabel'].setText( | ||
56 | 174 | description_text) | ||
57 | 168 | if select_mode == SongFormatSelect.MultipleFiles: | 175 | if select_mode == SongFormatSelect.MultipleFiles: |
58 | 169 | self.formatWidgets[format][u'addButton'].setText( | 176 | self.formatWidgets[format][u'addButton'].setText( |
59 | 170 | translate('SongsPlugin.ImportWizardForm', 'Add Files...')) | 177 | translate('SongsPlugin.ImportWizardForm', 'Add Files...')) |
60 | @@ -205,10 +212,16 @@ | |||
61 | 205 | spacer.changeSize( | 212 | spacer.changeSize( |
62 | 206 | max_label_width - labels[index].minimumSizeHint().width(), 0, | 213 | max_label_width - labels[index].minimumSizeHint().width(), 0, |
63 | 207 | QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) | 214 | QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) |
64 | 215 | # Align descriptionLabels with rest of layout | ||
65 | 216 | for format in SongFormat.get_format_list(): | ||
66 | 217 | if SongFormat.get(format, u'descriptionText') is not None: | ||
67 | 218 | self.formatWidgets[format][u'descriptionSpacer'].changeSize( | ||
68 | 219 | max_label_width + self.formatHSpacing, 0, | ||
69 | 220 | QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) | ||
70 | 208 | 221 | ||
71 | 209 | def customPageChanged(self, pageId): | 222 | def customPageChanged(self, pageId): |
72 | 210 | """ | 223 | """ |
74 | 211 | Called when changing to a page other than the progress page | 224 | Called when changing to a page other than the progress page. |
75 | 212 | """ | 225 | """ |
76 | 213 | if self.page(pageId) == self.sourcePage: | 226 | if self.page(pageId) == self.sourcePage: |
77 | 214 | self.onCurrentIndexChanged(self.formatStack.currentIndex()) | 227 | self.onCurrentIndexChanged(self.formatStack.currentIndex()) |
78 | @@ -235,8 +248,8 @@ | |||
79 | 235 | else: | 248 | else: |
80 | 236 | import_source = \ | 249 | import_source = \ |
81 | 237 | self.formatWidgets[format][u'filepathEdit'].text() | 250 | self.formatWidgets[format][u'filepathEdit'].text() |
84 | 238 | error_title = UiStrings().IFSs if select_mode == \ | 251 | error_title = (UiStrings().IFSs if select_mode == |
85 | 239 | SongFormatSelect.SingleFile else UiStrings().IFdSs | 252 | SongFormatSelect.SingleFile else UiStrings().IFdSs) |
86 | 240 | focus_button = self.formatWidgets[format][u'browseButton'] | 253 | focus_button = self.formatWidgets[format][u'browseButton'] |
87 | 241 | if not class_.isValidSource(import_source): | 254 | if not class_.isValidSource(import_source): |
88 | 242 | critical_error_message_box(error_title, error_msg) | 255 | critical_error_message_box(error_title, error_msg) |
89 | @@ -395,8 +408,8 @@ | |||
90 | 395 | 408 | ||
91 | 396 | def addFileSelectItem(self): | 409 | def addFileSelectItem(self): |
92 | 397 | format = self.currentFormat | 410 | format = self.currentFormat |
95 | 398 | prefix, can_disable, select_mode = SongFormat.get(format, u'prefix', | 411 | prefix, can_disable, description_text, select_mode = SongFormat.get( |
96 | 399 | u'canDisable', u'selectMode') | 412 | format, u'prefix', u'canDisable', u'descriptionText', u'selectMode') |
97 | 400 | page = QtGui.QWidget() | 413 | page = QtGui.QWidget() |
98 | 401 | page.setObjectName(prefix + u'Page') | 414 | page.setObjectName(prefix + u'Page') |
99 | 402 | if can_disable: | 415 | if can_disable: |
100 | @@ -406,10 +419,25 @@ | |||
101 | 406 | importLayout = QtGui.QVBoxLayout(importWidget) | 419 | importLayout = QtGui.QVBoxLayout(importWidget) |
102 | 407 | importLayout.setMargin(0) | 420 | importLayout.setMargin(0) |
103 | 408 | importLayout.setObjectName(prefix + u'ImportLayout') | 421 | importLayout.setObjectName(prefix + u'ImportLayout') |
104 | 422 | if description_text is not None: | ||
105 | 423 | descriptionLayout = QtGui.QHBoxLayout() | ||
106 | 424 | descriptionLayout.setObjectName(prefix + u'DescriptionLayout') | ||
107 | 425 | descriptionSpacer = QtGui.QSpacerItem(0, 0, QtGui.QSizePolicy.Fixed, | ||
108 | 426 | QtGui.QSizePolicy.Fixed) | ||
109 | 427 | descriptionLayout.addSpacerItem(descriptionSpacer) | ||
110 | 428 | descriptionLabel = QtGui.QLabel(importWidget) | ||
111 | 429 | descriptionLabel.setWordWrap(True) | ||
112 | 430 | descriptionLabel.setOpenExternalLinks(True) | ||
113 | 431 | descriptionLabel.setObjectName(prefix + u'DescriptionLabel') | ||
114 | 432 | descriptionLayout.addWidget(descriptionLabel) | ||
115 | 433 | importLayout.addLayout(descriptionLayout) | ||
116 | 434 | self.formatWidgets[format][u'descriptionLabel'] = descriptionLabel | ||
117 | 435 | self.formatWidgets[format][u'descriptionSpacer'] = descriptionSpacer | ||
118 | 409 | if select_mode == SongFormatSelect.SingleFile or \ | 436 | if select_mode == SongFormatSelect.SingleFile or \ |
119 | 410 | select_mode == SongFormatSelect.SingleFolder: | 437 | select_mode == SongFormatSelect.SingleFolder: |
120 | 411 | filepathLayout = QtGui.QHBoxLayout() | 438 | filepathLayout = QtGui.QHBoxLayout() |
121 | 412 | filepathLayout.setObjectName(prefix + u'FilepathLayout') | 439 | filepathLayout.setObjectName(prefix + u'FilepathLayout') |
122 | 440 | filepathLayout.setContentsMargins(0, self.formatVSpacing, 0, 0) | ||
123 | 413 | filepathLabel = QtGui.QLabel(importWidget) | 441 | filepathLabel = QtGui.QLabel(importWidget) |
124 | 414 | filepathLabel.setObjectName(prefix + u'FilepathLabel') | 442 | filepathLabel.setObjectName(prefix + u'FilepathLabel') |
125 | 415 | filepathLayout.addWidget(filepathLabel) | 443 | filepathLayout.addWidget(filepathLabel) |
126 | 416 | 444 | ||
127 | === modified file 'openlp/plugins/songs/lib/importer.py' | |||
128 | --- openlp/plugins/songs/lib/importer.py 2012-06-08 21:10:26 +0000 | |||
129 | +++ openlp/plugins/songs/lib/importer.py 2012-06-08 21:10:26 +0000 | |||
130 | @@ -44,6 +44,7 @@ | |||
131 | 44 | from songbeamerimport import SongBeamerImport | 44 | from songbeamerimport import SongBeamerImport |
132 | 45 | from songshowplusimport import SongShowPlusImport | 45 | from songshowplusimport import SongShowPlusImport |
133 | 46 | from foilpresenterimport import FoilPresenterImport | 46 | from foilpresenterimport import FoilPresenterImport |
134 | 47 | from zionworximport import ZionWorxImport | ||
135 | 47 | # Imports that might fail | 48 | # Imports that might fail |
136 | 48 | log = logging.getLogger(__name__) | 49 | log = logging.getLogger(__name__) |
137 | 49 | try: | 50 | try: |
138 | @@ -111,6 +112,8 @@ | |||
139 | 111 | Title for ``QFileDialog`` (default includes the format's ``u'name'``). | 112 | Title for ``QFileDialog`` (default includes the format's ``u'name'``). |
140 | 112 | ``u'invalidSourceMsg'`` | 113 | ``u'invalidSourceMsg'`` |
141 | 113 | Message displayed if ``isValidSource()`` returns ``False``. | 114 | Message displayed if ``isValidSource()`` returns ``False``. |
142 | 115 | ``u'descriptionText'`` | ||
143 | 116 | Short description (1-2 lines) about the song format. | ||
144 | 114 | """ | 117 | """ |
145 | 115 | # Song formats (ordered alphabetically after Generic) | 118 | # Song formats (ordered alphabetically after Generic) |
146 | 116 | # * Numerical order of song formats is significant as it determines the | 119 | # * Numerical order of song formats is significant as it determines the |
147 | @@ -131,7 +134,8 @@ | |||
148 | 131 | SongShowPlus = 12 | 134 | SongShowPlus = 12 |
149 | 132 | SongsOfFellowship = 13 | 135 | SongsOfFellowship = 13 |
150 | 133 | WordsOfWorship = 14 | 136 | WordsOfWorship = 14 |
152 | 134 | #CSV = 15 | 137 | ZionWorx = 15 |
153 | 138 | #CSV = 16 | ||
154 | 135 | 139 | ||
155 | 136 | # Set optional attribute defaults | 140 | # Set optional attribute defaults |
156 | 137 | __defaults__ = { | 141 | __defaults__ = { |
157 | @@ -142,7 +146,8 @@ | |||
158 | 142 | u'comboBoxText': None, | 146 | u'comboBoxText': None, |
159 | 143 | u'disabledLabelText': u'', | 147 | u'disabledLabelText': u'', |
160 | 144 | u'getFilesTitle': None, | 148 | u'getFilesTitle': None, |
162 | 145 | u'invalidSourceMsg': None | 149 | u'invalidSourceMsg': None, |
163 | 150 | u'descriptionText': None | ||
164 | 146 | } | 151 | } |
165 | 147 | 152 | ||
166 | 148 | # Set attribute values for each Song Format | 153 | # Set attribute values for each Song Format |
167 | @@ -264,6 +269,18 @@ | |||
168 | 264 | u'prefix': u'wordsOfWorship', | 269 | u'prefix': u'wordsOfWorship', |
169 | 265 | u'filter': u'%s (*.wsg *.wow-song)' % translate( | 270 | u'filter': u'%s (*.wsg *.wow-song)' % translate( |
170 | 266 | 'SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files') | 271 | 'SongsPlugin.ImportWizardForm', 'Words Of Worship Song Files') |
171 | 272 | }, | ||
172 | 273 | ZionWorx: { | ||
173 | 274 | u'class': ZionWorxImport, | ||
174 | 275 | u'name': u'ZionWorx', | ||
175 | 276 | u'prefix': u'zionWorx', | ||
176 | 277 | u'selectMode': SongFormatSelect.SingleFile, | ||
177 | 278 | u'comboBoxText': translate('SongsPlugin.ImportWizardForm', | ||
178 | 279 | 'ZionWorx (CSV)'), | ||
179 | 280 | u'descriptionText': translate('SongsPlugin.ImportWizardForm', | ||
180 | 281 | 'First convert your ZionWorx database to a CSV text file, as ' | ||
181 | 282 | 'explained in the <a href="http://manual.openlp.org/songs.html' | ||
182 | 283 | '#importing-from-zionworx">User Manual</a>.') | ||
183 | 267 | # }, | 284 | # }, |
184 | 268 | # CSV: { | 285 | # CSV: { |
185 | 269 | # u'class': CSVImport, | 286 | # u'class': CSVImport, |
186 | @@ -293,7 +310,8 @@ | |||
187 | 293 | SongFormat.SongBeamer, | 310 | SongFormat.SongBeamer, |
188 | 294 | SongFormat.SongShowPlus, | 311 | SongFormat.SongShowPlus, |
189 | 295 | SongFormat.SongsOfFellowship, | 312 | SongFormat.SongsOfFellowship, |
191 | 296 | SongFormat.WordsOfWorship | 313 | SongFormat.WordsOfWorship, |
192 | 314 | SongFormat.ZionWorx | ||
193 | 297 | ] | 315 | ] |
194 | 298 | 316 | ||
195 | 299 | @staticmethod | 317 | @staticmethod |
196 | 300 | 318 | ||
197 | === added file 'openlp/plugins/songs/lib/zionworximport.py' | |||
198 | --- openlp/plugins/songs/lib/zionworximport.py 1970-01-01 00:00:00 +0000 | |||
199 | +++ openlp/plugins/songs/lib/zionworximport.py 2012-06-08 21:10:26 +0000 | |||
200 | @@ -0,0 +1,142 @@ | |||
201 | 1 | # -*- coding: utf-8 -*- | ||
202 | 2 | # vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 | ||
203 | 3 | |||
204 | 4 | ############################################################################### | ||
205 | 5 | # OpenLP - Open Source Lyrics Projection # | ||
206 | 6 | # --------------------------------------------------------------------------- # | ||
207 | 7 | # Copyright (c) 2008-2012 Raoul Snyman # | ||
208 | 8 | # Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan # | ||
209 | 9 | # Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, # | ||
210 | 10 | # Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias # | ||
211 | 11 | # Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # | ||
212 | 12 | # Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund # | ||
213 | 13 | # --------------------------------------------------------------------------- # | ||
214 | 14 | # This program is free software; you can redistribute it and/or modify it # | ||
215 | 15 | # under the terms of the GNU General Public License as published by the Free # | ||
216 | 16 | # Software Foundation; version 2 of the License. # | ||
217 | 17 | # # | ||
218 | 18 | # This program is distributed in the hope that it will be useful, but WITHOUT # | ||
219 | 19 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # | ||
220 | 20 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # | ||
221 | 21 | # more details. # | ||
222 | 22 | # # | ||
223 | 23 | # You should have received a copy of the GNU General Public License along # | ||
224 | 24 | # with this program; if not, write to the Free Software Foundation, Inc., 59 # | ||
225 | 25 | # Temple Place, Suite 330, Boston, MA 02111-1307 USA # | ||
226 | 26 | ############################################################################### | ||
227 | 27 | """ | ||
228 | 28 | The :mod:`zionworximport` module provides the functionality for importing | ||
229 | 29 | ZionWorx songs into the OpenLP database. | ||
230 | 30 | """ | ||
231 | 31 | import csv | ||
232 | 32 | import logging | ||
233 | 33 | |||
234 | 34 | from openlp.core.lib import translate | ||
235 | 35 | from openlp.plugins.songs.lib.songimport import SongImport | ||
236 | 36 | |||
237 | 37 | log = logging.getLogger(__name__) | ||
238 | 38 | |||
239 | 39 | class ZionWorxImport(SongImport): | ||
240 | 40 | """ | ||
241 | 41 | The :class:`ZionWorxImport` class provides the ability to import songs | ||
242 | 42 | from ZionWorx, via a dump of the ZionWorx database to a CSV file. | ||
243 | 43 | |||
244 | 44 | ZionWorx song database fields: | ||
245 | 45 | |||
246 | 46 | * ``SongNum`` Song ID. (Discarded by importer) | ||
247 | 47 | * ``Title1`` Main Title. | ||
248 | 48 | * ``Title2`` Alternate Title. | ||
249 | 49 | * ``Lyrics`` Song verses, separated by blank lines. | ||
250 | 50 | * ``Writer`` Song author(s). | ||
251 | 51 | * ``Copyright`` Copyright information | ||
252 | 52 | * ``Keywords`` (Discarded by importer) | ||
253 | 53 | * ``DefaultStyle`` (Discarded by importer) | ||
254 | 54 | |||
255 | 55 | ZionWorx has no native export function; it uses the proprietary TurboDB | ||
256 | 56 | database engine. The TurboDB vendor, dataWeb, provides tools which can | ||
257 | 57 | export TurboDB tables to other formats, such as freeware console tool | ||
258 | 58 | TurboDB Data Exchange which is available for Windows and Linux. This command | ||
259 | 59 | exports the ZionWorx songs table to a CSV file: | ||
260 | 60 | |||
261 | 61 | ``tdbdatax MainTable.dat songstable.csv -fsdf -s, -qd`` | ||
262 | 62 | |||
263 | 63 | * -f Table format: ``sdf`` denotes text file. | ||
264 | 64 | * -s Separator character between fields. | ||
265 | 65 | * -q Quote character surrounding fields. ``d`` denotes double-quote. | ||
266 | 66 | |||
267 | 67 | CSV format expected by importer: | ||
268 | 68 | |||
269 | 69 | * Field separator character is comma ``,`` | ||
270 | 70 | * Fields surrounded by double-quotes ``"``. This enables fields (such as | ||
271 | 71 | Lyrics) to include new-lines and commas. Double-quotes within a field | ||
272 | 72 | are denoted by two double-quotes ``""`` | ||
273 | 73 | * Note: This is the default format of the Python ``csv`` module. | ||
274 | 74 | |||
275 | 75 | """ | ||
276 | 76 | def doImport(self): | ||
277 | 77 | """ | ||
278 | 78 | Receive a CSV file (from a ZionWorx database dump) to import. | ||
279 | 79 | """ | ||
280 | 80 | # Used to strip control chars (10=LF, 13=CR, 127=DEL) | ||
281 | 81 | self.control_chars_map = dict.fromkeys( | ||
282 | 82 | range(10) + [11, 12] + range(14,32) + [127]) | ||
283 | 83 | with open(self.importSource, 'rb') as songs_file: | ||
284 | 84 | fieldnames = [u'SongNum', u'Title1', u'Title2', u'Lyrics', | ||
285 | 85 | u'Writer', u'Copyright', u'Keywords', u'DefaultStyle'] | ||
286 | 86 | songs_reader = csv.DictReader(songs_file, fieldnames) | ||
287 | 87 | try: | ||
288 | 88 | records = list(songs_reader) | ||
289 | 89 | except csv.Error, e: | ||
290 | 90 | self.logError(unicode(translate('SongsPlugin.ZionWorxImport', | ||
291 | 91 | 'Error reading CSV file.')), | ||
292 | 92 | unicode(translate('SongsPlugin.ZionWorxImport', | ||
293 | 93 | 'Line %d: %s' % (songs_reader.line_num, e)))) | ||
294 | 94 | return | ||
295 | 95 | num_records = len(records) | ||
296 | 96 | log.info(u'%s records found in CSV file' % num_records) | ||
297 | 97 | self.importWizard.progressBar.setMaximum(num_records) | ||
298 | 98 | for index, record in enumerate(records, 1): | ||
299 | 99 | if self.stopImportFlag: | ||
300 | 100 | return | ||
301 | 101 | self.setDefaults() | ||
302 | 102 | try: | ||
303 | 103 | self.title = self._decode(record[u'Title1']) | ||
304 | 104 | if record[u'Title2']: | ||
305 | 105 | self.alternateTitle = self._decode(record[u'Title2']) | ||
306 | 106 | self.parseAuthor(self._decode(record[u'Writer'])) | ||
307 | 107 | self.addCopyright(self._decode(record[u'Copyright'])) | ||
308 | 108 | lyrics = self._decode(record[u'Lyrics']) | ||
309 | 109 | except UnicodeDecodeError, e: | ||
310 | 110 | self.logError(unicode(translate( | ||
311 | 111 | 'SongsPlugin.ZionWorxImport', 'Record %d' % index)), | ||
312 | 112 | unicode(translate('SongsPlugin.ZionWorxImport', | ||
313 | 113 | 'Decoding error: %s' % e))) | ||
314 | 114 | continue | ||
315 | 115 | except TypeError, e: | ||
316 | 116 | self.logError(unicode(translate( | ||
317 | 117 | 'SongsPlugin.ZionWorxImport', 'File not valid ZionWorx ' | ||
318 | 118 | 'CSV format.')), u'TypeError: %s' % e) | ||
319 | 119 | return | ||
320 | 120 | verse = u'' | ||
321 | 121 | for line in lyrics.splitlines(): | ||
322 | 122 | if line and not line.isspace(): | ||
323 | 123 | verse += line + u'\n' | ||
324 | 124 | elif verse: | ||
325 | 125 | self.addVerse(verse) | ||
326 | 126 | verse = u'' | ||
327 | 127 | if verse: | ||
328 | 128 | self.addVerse(verse) | ||
329 | 129 | title = self.title | ||
330 | 130 | if not self.finish(): | ||
331 | 131 | self.logError(unicode(translate( | ||
332 | 132 | 'SongsPlugin.ZionWorxImport', 'Record %d' % index)) | ||
333 | 133 | + (u': "' + title + u'"' if title else u'')) | ||
334 | 134 | |||
335 | 135 | def _decode(self, str): | ||
336 | 136 | """ | ||
337 | 137 | Decodes CSV input to unicode, stripping all control characters (except | ||
338 | 138 | new lines). | ||
339 | 139 | """ | ||
340 | 140 | # This encoding choice seems OK. ZionWorx has no option for setting the | ||
341 | 141 | # encoding for its songs, so we assume encoding is always the same. | ||
342 | 142 | return unicode(str, u'cp1252').translate(self.control_chars_map) |
189-191: Would it be possible to translate the paragraph into English? :) Remember the users may not be technical so lots of "dump", "CSV" type phrases might scare folk off.