← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~sfindlay/openlp/select-folder-powersong into lp:openlp

 

Samuel Findlay has proposed merging lp:~sfindlay/openlp/select-folder-powersong into lp:openlp.

Requested reviews:
  Raoul Snyman (raoul-snyman)

For more details, see:
https://code.launchpad.net/~sfindlay/openlp/select-folder-powersong/+merge/106491

Changes PowerSong importer to import a folder, rather than a selection of files. This involved implementing a getFolder method in core.ui.OpenLPWizard.

Rationale:
(1) PowerSong users always deal with their song databases as a whole, as a database folder. To ask them to select individual song files may be a foreign idea.
(2) Although PowerSong songs are stored in individual files, the filenames are unintelligible database IDs, not song titles. So there is no easy way for a user to pick and choose which songs to import anyway.

Test:
Download large PowerSong database of 1057 songs. 1050 should import successfully.
<https://docs.google.com/open?id=0B076ddXXPC2WUnA1VXZoY1FCVzA>
-- 
https://code.launchpad.net/~sfindlay/openlp/select-folder-powersong/+merge/106491
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/lib/ui.py'
--- openlp/core/lib/ui.py	2012-04-22 19:50:18 +0000
+++ openlp/core/lib/ui.py	2012-05-19 11:58:18 +0000
@@ -94,6 +94,7 @@
         self.NewService = translate('OpenLP.Ui', 'New Service')
         self.NewTheme = translate('OpenLP.Ui', 'New Theme')
         self.NextTrack = translate('OpenLP.Ui', 'Next Track')
+        self.NFdSs = translate('OpenLP.Ui', 'No Folder Selected', 'Singular')
         self.NFSs = translate('OpenLP.Ui', 'No File Selected', 'Singular')
         self.NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural')
         self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')

=== modified file 'openlp/core/ui/wizard.py'
--- openlp/core/ui/wizard.py	2012-05-03 12:50:10 +0000
+++ openlp/core/ui/wizard.py	2012-05-19 11:58:18 +0000
@@ -72,11 +72,14 @@
         'importer, you will need to install the "python-sqlite" '
         'module.')
     OpenTypeFile = unicode(translate('OpenLP.Ui', 'Open %s File'))
+    OpenTypeFolder = unicode(translate('OpenLP.Ui', 'Open %s Folder'))
     PercentSymbolFormat = unicode(translate('OpenLP.Ui', '%p%'))
     Ready = translate('OpenLP.Ui', 'Ready.')
     StartingImport = translate('OpenLP.Ui', 'Starting import...')
     YouSpecifyFile = unicode(translate('OpenLP.Ui', 'You need to specify at '
         'least one %s file to import from.', 'A file type e.g. OpenSong'))
+    YouSpecifyFolder = unicode(translate('OpenLP.Ui', 'You need to specify a '
+        '%s folder to import from.', 'A file type e.g. OpenSong'))
 
 
 class OpenLPWizard(QtGui.QWizard):
@@ -254,7 +257,7 @@
             The title of the dialog (unicode).
 
         ``editbox``
-            A editbox (QLineEdit).
+            An editbox (QLineEdit).
 
         ``filters``
             The file extension filters. It should contain the file description
@@ -265,11 +268,28 @@
         if filters:
             filters += u';;'
         filters += u'%s (*)' % UiStrings().AllFiles
-        filename = QtGui.QFileDialog.getOpenFileName(self, title,
+        filename = unicode(QtGui.QFileDialog.getOpenFileName(self, title,
             os.path.dirname(SettingsManager.get_last_dir(
-            self.plugin.settingsSection, 1)), filters)
+            self.plugin.settingsSection, 1)), filters))
         if filename:
             editbox.setText(filename)
             SettingsManager.set_last_dir(self.plugin.settingsSection,
                 filename, 1)
 
+    def getFolder(self, title, editbox):
+        """
+        Opens a QFileDialog and saves the selected folder to the given editbox.
+
+        ``title``
+            The title of the dialog (unicode).
+
+        ``editbox``
+            An editbox (QLineEdit).
+        """
+        folder = unicode(QtGui.QFileDialog.getExistingDirectory(self, title,
+            os.path.dirname(SettingsManager.get_last_dir(
+            self.plugin.settingsSection, 1)), QtGui.QFileDialog.ShowDirsOnly))
+        if folder:
+            editbox.setText(folder)
+            SettingsManager.set_last_dir(self.plugin.settingsSection,
+                folder, 1)

=== modified file 'openlp/plugins/songs/forms/songexportform.py'
--- openlp/plugins/songs/forms/songexportform.py	2012-04-02 19:34:49 +0000
+++ openlp/plugins/songs/forms/songexportform.py	2012-05-19 11:58:18 +0000
@@ -363,11 +363,5 @@
         Called when the *directoryButton* was clicked. Opens a dialog and writes
         the path to *directoryLineEdit*.
         """
-        path = unicode(QtGui.QFileDialog.getExistingDirectory(self,
-            translate('SongsPlugin.ExportWizardForm',
-            'Select Destination Folder'),
-            SettingsManager.get_last_dir(self.plugin.settingsSection, 1),
-            options=QtGui.QFileDialog.ShowDirsOnly))
-        SettingsManager.set_last_dir(self.plugin.settingsSection, path, 1)
-        self.directoryLineEdit.setText(path)
-
+        self.getFolder(translate('SongsPlugin.ExportWizardForm',
+            'Select Destination Folder'), self.directoryLineEdit)

=== modified file 'openlp/plugins/songs/forms/songimportform.py'
--- openlp/plugins/songs/forms/songimportform.py	2012-05-03 12:50:10 +0000
+++ openlp/plugins/songs/forms/songimportform.py	2012-05-19 11:58:18 +0000
@@ -28,6 +28,7 @@
 The song import functions for OpenLP.
 """
 import codecs
+import fnmatch
 import logging
 import os
 
@@ -105,6 +106,9 @@
         QtCore.QObject.connect(self.openLP1BrowseButton,
             QtCore.SIGNAL(u'clicked()'),
             self.onOpenLP1BrowseButtonClicked)
+        QtCore.QObject.connect(self.powerSongBrowseButton,
+            QtCore.SIGNAL(u'clicked()'),
+            self.onPowerSongBrowseButtonClicked)
         QtCore.QObject.connect(self.openLyricsAddButton,
             QtCore.SIGNAL(u'clicked()'),
             self.onOpenLyricsAddButtonClicked)
@@ -171,12 +175,6 @@
         QtCore.QObject.connect(self.foilPresenterRemoveButton,
             QtCore.SIGNAL(u'clicked()'),
             self.onFoilPresenterRemoveButtonClicked)
-        QtCore.QObject.connect(self.powerSongAddButton,
-            QtCore.SIGNAL(u'clicked()'),
-            self.onPowerSongAddButtonClicked)
-        QtCore.QObject.connect(self.powerSongRemoveButton,
-            QtCore.SIGNAL(u'clicked()'),
-            self.onPowerSongRemoveButtonClicked)
 
     def addCustomPages(self):
         """
@@ -224,7 +222,7 @@
         # Open Song
         self.addFileSelectItem(u'openSong', u'OpenSong')
         # PowerSong
-        self.addFileSelectItem(u'powerSong')
+        self.addFileSelectItem(u'powerSong', single_select=True)
         # SongBeamer
         self.addFileSelectItem(u'songBeamer')
         # Song Show Plus
@@ -290,6 +288,9 @@
             translate('SongsPlugin.ImportWizardForm', 'Filename:'))
         self.openLP1BrowseButton.setText(UiStrings().Browse)
         self.openLP1DisabledLabel.setText(WizardStrings.NoSqlite)
+        self.powerSongFilenameLabel.setText(
+            translate('SongsPlugin.ImportWizardForm', 'Folder:'))
+        self.powerSongBrowseButton.setText(UiStrings().Browse)
         self.openLyricsAddButton.setText(
             translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
         self.openLyricsRemoveButton.setText(
@@ -315,10 +316,6 @@
             translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
         self.dreamBeamRemoveButton.setText(
             translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
-        self.powerSongAddButton.setText(
-            translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
-        self.powerSongRemoveButton.setText(
-            translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
         self.songsOfFellowshipAddButton.setText(
             translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
         self.songsOfFellowshipRemoveButton.setText(
@@ -389,6 +386,7 @@
             source_format = self.formatComboBox.currentIndex()
             QtCore.QSettings().setValue(u'songs/last import type',
                 source_format)
+            import_class = SongFormat.get_class(source_format)
             if source_format == SongFormat.OpenLP2:
                 if self.openLP2FilenameEdit.text().isEmpty():
                     critical_error_message_box(UiStrings().NFSs,
@@ -401,6 +399,14 @@
                         WizardStrings.YouSpecifyFile % UiStrings().OLPV1)
                     self.openLP1BrowseButton.setFocus()
                     return False
+            elif source_format == SongFormat.PowerSong:
+                if self.powerSongFilenameEdit.text().isEmpty() or \
+                    not import_class.isValidSource(
+                    folder=self.powerSongFilenameEdit.text()):
+                    critical_error_message_box(UiStrings().NFdSs,
+                        WizardStrings.YouSpecifyFolder % WizardStrings.PS)
+                    self.powerSongBrowseButton.setFocus()
+                    return False
             elif source_format == SongFormat.OpenLyrics:
                 if self.openLyricsFileListWidget.count() == 0:
                     critical_error_message_box(UiStrings().NFSp,
@@ -431,12 +437,6 @@
                         WizardStrings.YouSpecifyFile % WizardStrings.DB)
                     self.dreamBeamAddButton.setFocus()
                     return False
-            elif source_format == SongFormat.PowerSong:
-                if self.powerSongFileListWidget.count() == 0:
-                    critical_error_message_box(UiStrings().NFSp,
-                        WizardStrings.YouSpecifyFile % WizardStrings.PS)
-                    self.powerSongAddButton.setFocus()
-                    return False
             elif source_format == SongFormat.SongsOfFellowship:
                 if self.songsOfFellowshipFileListWidget.count() == 0:
                     critical_error_message_box(UiStrings().NFSp,
@@ -546,6 +546,13 @@
             'openlp.org v1.x Databases')
         )
 
+    def onPowerSongBrowseButtonClicked(self):
+        """
+        Get PowerSong song database folder
+        """
+        self.getFolder(WizardStrings.OpenTypeFolder % WizardStrings.PS,
+            self.powerSongFilenameEdit)
+
     def onOpenLyricsAddButtonClicked(self):
         """
         Get OpenLyrics song database files
@@ -620,22 +627,6 @@
         """
         self.removeSelectedItems(self.dreamBeamFileListWidget)
 
-    def onPowerSongAddButtonClicked(self):
-        """
-        Get PowerSong song database files
-        """
-        self.getFiles(WizardStrings.OpenTypeFile % WizardStrings.PS,
-            self.powerSongFileListWidget, u'%s (*.song)'
-            % translate('SongsPlugin.ImportWizardForm',
-                'PowerSong 1.0 Song Files')
-        )
-
-    def onPowerSongRemoveButtonClicked(self):
-        """
-        Remove selected PowerSong files from the import list
-        """
-        self.removeSelectedItems(self.powerSongFileListWidget)
-
     def onSongsOfFellowshipAddButtonClicked(self):
         """
         Get Songs of Fellowship song database files
@@ -748,12 +739,12 @@
         self.formatComboBox.setCurrentIndex(last_import_type)
         self.openLP2FilenameEdit.setText(u'')
         self.openLP1FilenameEdit.setText(u'')
+        self.powerSongFilenameEdit.setText(u'')
         self.openLyricsFileListWidget.clear()
         self.openSongFileListWidget.clear()
         self.wordsOfWorshipFileListWidget.clear()
         self.ccliFileListWidget.clear()
         self.dreamBeamFileListWidget.clear()
-        self.powerSongFileListWidget.clear()
         self.songsOfFellowshipFileListWidget.clear()
         self.genericFileListWidget.clear()
         self.easySlidesFilenameEdit.setText(u'')
@@ -794,6 +785,11 @@
                 filename=unicode(self.openLP1FilenameEdit.text()),
                 plugin=self.plugin
             )
+        elif source_format == SongFormat.PowerSong:
+            # Import PowerSong folder
+            importer = self.plugin.importSongs(SongFormat.PowerSong,
+                folder=unicode(self.powerSongFilenameEdit.text())
+            )
         elif source_format == SongFormat.OpenLyrics:
             # Import OpenLyrics songs
             importer = self.plugin.importSongs(SongFormat.OpenLyrics,
@@ -821,12 +817,6 @@
                 filenames=self.getListOfFiles(
                     self.dreamBeamFileListWidget)
             )
-        elif source_format == SongFormat.PowerSong:
-            # Import PowerSong songs
-            importer = self.plugin.importSongs(SongFormat.PowerSong,
-                filenames=self.getListOfFiles(
-                    self.powerSongFileListWidget)
-            )
         elif source_format == SongFormat.SongsOfFellowship:
             # Import a Songs of Fellowship RTF file
             importer = self.plugin.importSongs(SongFormat.SongsOfFellowship,
@@ -864,11 +854,7 @@
                 filenames=self.getListOfFiles(self.foilPresenterFileListWidget)
             )
         importer.doImport()
-        if importer.errorLog:
-            self.progressLabel.setText(translate(
-                'SongsPlugin.SongImportForm', 'Your song import failed.'))
-        else:
-            self.progressLabel.setText(WizardStrings.FinishedImport)
+        self.progressLabel.setText(WizardStrings.FinishedImport)
 
     def onErrorCopyToButtonClicked(self):
         """

=== modified file 'openlp/plugins/songs/lib/powersongimport.py'
--- openlp/plugins/songs/lib/powersongimport.py	2012-05-03 12:50:10 +0000
+++ openlp/plugins/songs/lib/powersongimport.py	2012-05-19 11:58:18 +0000
@@ -29,8 +29,11 @@
 PowerSong songs into the OpenLP database.
 """
 import logging
+import fnmatch
+import os
 
 from openlp.core.lib import translate
+from openlp.core.ui.wizard import WizardStrings
 from openlp.plugins.songs.lib.songimport import SongImport
 
 log = logging.getLogger(__name__)
@@ -69,13 +72,39 @@
         * .song
     """
 
+    @staticmethod
+    def isValidSource(**kwargs):
+        """
+        Checks if source is a PowerSong 1.0 folder:
+            * is a directory
+            * contains at least one *.song file
+        """
+        if u'folder' in kwargs:
+            dir = kwargs[u'folder']
+            if os.path.isdir(dir):
+                for file in os.listdir(dir):
+                    if fnmatch.fnmatch(file, u'*.song'):
+                        return True
+        return False
+
     def doImport(self):
         """
-        Receive a list of files to import.
+        Receive either a list of files or a folder (unicode) to import.
         """
-        if not isinstance(self.importSource, list):
+        if isinstance(self.importSource, unicode):
+            if os.path.isdir(self.importSource):
+                dir = self.importSource
+                self.importSource = []
+                for file in os.listdir(dir):
+                    if fnmatch.fnmatch(file, u'*.song'):
+                        self.importSource.append(os.path.join(dir, file))
+            else:
+                self.importSource = u''
+        if not self.importSource or not isinstance(self.importSource, list):
             self.logError(unicode(translate('SongsPlugin.PowerSongImport',
-                'No files to import.')))
+                'No songs to import.')),
+                unicode(translate('SongsPlugin.PowerSongImport',
+                'No %s files found.' % WizardStrings.PS)))
             return
         self.importWizard.progressBar.setMaximum(len(self.importSource))
         for file in self.importSource:
@@ -92,9 +121,10 @@
                         field = self._readString(song_data)
                     except ValueError:
                         parse_error = True
-                        self.logError(file, unicode(
+                        self.logError(os.path.basename(file), unicode(
                             translate('SongsPlugin.PowerSongImport',
-                            'Invalid PowerSong file. Unexpected byte value.')))
+                            'Invalid %s file. Unexpected byte value.'
+                            % WizardStrings.PS)))
                         break
                     else:
                         if label == u'TITLE':
@@ -110,26 +140,26 @@
                 continue
             # Check that file had TITLE field
             if not self.title:
-                self.logError(file, unicode(
+                self.logError(os.path.basename(file), unicode(
                     translate('SongsPlugin.PowerSongImport',
-                    'Invalid PowerSong file. Missing "TITLE" header.')))
+                    'Invalid %s file. Missing "TITLE" header.'
+                    % WizardStrings.PS)))
                 continue
             # Check that file had COPYRIGHTLINE label
             if not found_copyright:
-                self.logError(file, unicode(
+                self.logError(self.title, unicode(
                     translate('SongsPlugin.PowerSongImport',
-                    '"%s" Invalid PowerSong file. Missing "COPYRIGHTLINE" '
-                    'header.' % self.title)))
+                    'Invalid %s file. Missing "COPYRIGHTLINE" '
+                    'header.' % WizardStrings.PS)))
                 continue
             # Check that file had at least one verse
             if not self.verses:
-                self.logError(file, unicode(
+                self.logError(self.title, unicode(
                     translate('SongsPlugin.PowerSongImport',
-                    '"%s" Verses not found. Missing "PART" header.'
-                    % self.title)))
+                    'Verses not found. Missing "PART" header.')))
                 continue
             if not self.finish():
-                self.logError(file)
+                self.logError(self.title)
 
     def _readString(self, file_object):
         """

=== modified file 'openlp/plugins/songs/lib/songimport.py'
--- openlp/plugins/songs/lib/songimport.py	2012-05-01 14:00:53 +0000
+++ openlp/plugins/songs/lib/songimport.py	2012-05-19 11:58:18 +0000
@@ -50,6 +50,13 @@
     whether the authors etc already exist and add them or refer to them
     as necessary
     """
+    @staticmethod
+    def isValidSource(**kwargs):
+        """
+        Override this method to validate the source prior to import.
+        """
+        pass
+
     def __init__(self, manager, **kwargs):
         """
         Initialise and create defaults for properties
@@ -65,14 +72,16 @@
             self.importSource = kwargs[u'filename']
         elif u'filenames' in kwargs:
             self.importSource = kwargs[u'filenames']
+        elif u'folder' in kwargs:
+            self.importSource = kwargs[u'folder']
         else:
-            raise KeyError(u'Keyword arguments "filename[s]" not supplied.')
+            raise KeyError(
+                u'Keyword arguments "filename[s]" or "folder" not supplied.')
         log.debug(self.importSource)
         self.importWizard = None
         self.song = None
         self.stopImportFlag = False
         self.setDefaults()
-        self.errorLog = []
         QtCore.QObject.connect(Receiver.get_receiver(),
             QtCore.SIGNAL(u'openlp_stop_wizard'), self.stopImport)
 


Follow ups