diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index ff6c85192..6a0326c84 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -63,9 +63,9 @@ class DuplicateSongRemovalForm(OpenLPWizard): ``plugin`` The songs plugin. """ - self.duplicateSongList = [] - self.reviewCurrentCount = 0 - self.reviewTotalCount = 0 + self.duplicate_song_list = [] + self.review_current_count = 0 + self.review_total_count = 0 OpenLPWizard.__init__(self, parent, plugin, u'duplicateSongRemovalWizard', u':/wizards/wizard_duplicateremoval.bmp', False) @@ -87,44 +87,46 @@ class DuplicateSongRemovalForm(OpenLPWizard): Add song wizard specific pages. """ #add custom pages - self.searchingPage = QtGui.QWizardPage() - self.searchingPage.setObjectName(u'searchingPage') - self.searchingVerticalLayout = QtGui.QVBoxLayout(self.searchingPage) - self.searchingVerticalLayout.setObjectName(u'searchingVerticalLayout') - self.duplicateSearchProgressBar = QtGui.QProgressBar(self.searchingPage) - self.duplicateSearchProgressBar.setObjectName(u'duplicateSearchProgressBar') - self.duplicateSearchProgressBar.setFormat(WizardStrings.PercentSymbolFormat) - self.searchingVerticalLayout.addWidget(self.duplicateSearchProgressBar) - self.foundDuplicatesEdit = QtGui.QPlainTextEdit(self.searchingPage) - self.foundDuplicatesEdit.setUndoRedoEnabled(False) - self.foundDuplicatesEdit.setReadOnly(True) - self.foundDuplicatesEdit.setObjectName(u'foundDuplicatesEdit') - self.searchingVerticalLayout.addWidget(self.foundDuplicatesEdit) - self.searchingPageId = self.addPage(self.searchingPage) - self.reviewPage = QtGui.QWizardPage() - self.reviewPage.setObjectName(u'reviewPage') - self.reviewLayout = QtGui.QVBoxLayout(self.reviewPage) - self.reviewLayout.setObjectName(u'reviewLayout') - self.songsHorizontalScrollArea = QtGui.QScrollArea(self.reviewPage) - self.songsHorizontalScrollArea.setObjectName(u'songsHorizontalScrollArea') - self.songsHorizontalScrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) - self.songsHorizontalScrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) - self.songsHorizontalScrollArea.setFrameStyle(QtGui.QFrame.NoFrame) - self.songsHorizontalScrollArea.setWidgetResizable(True) - self.songsHorizontalScrollArea.setStyleSheet(u'QScrollArea#songsHorizontalScrollArea {background-color:transparent;}') - self.songsHorizontalSongsWidget = QtGui.QWidget(self.songsHorizontalScrollArea) - self.songsHorizontalSongsWidget.setObjectName(u'songsHorizontalSongsWidget') - self.songsHorizontalSongsWidget.setStyleSheet(u'QWidget#songsHorizontalSongsWidget {background-color:transparent;}') - self.songsHorizontalLayout = QtGui.QHBoxLayout(self.songsHorizontalSongsWidget) - self.songsHorizontalLayout.setObjectName(u'songsHorizontalLayout') - self.songsHorizontalLayout.setSizeConstraint(QtGui.QLayout.SetMinAndMaxSize) - self.songsHorizontalScrollArea.setWidget(self.songsHorizontalSongsWidget) - self.reviewLayout.addWidget(self.songsHorizontalScrollArea) - self.reviewPageId = self.addPage(self.reviewPage) + self.searching_page = QtGui.QWizardPage() + self.searching_page.setObjectName(u'searching_page') + self.searching_vertical_layout = QtGui.QVBoxLayout(self.searching_page) + self.searching_vertical_layout.setObjectName(u'searching_vertical_layout') + self.duplicate_search_progress_bar = QtGui.QProgressBar(self.searching_page) + self.duplicate_search_progress_bar.setObjectName(u'duplicate_search_progress_bar') + self.duplicate_search_progress_bar.setFormat(WizardStrings.PercentSymbolFormat) + self.searching_vertical_layout.addWidget(self.duplicate_search_progress_bar) + self.found_duplicates_edit = QtGui.QPlainTextEdit(self.searching_page) + self.found_duplicates_edit.setUndoRedoEnabled(False) + self.found_duplicates_edit.setReadOnly(True) + self.found_duplicates_edit.setObjectName(u'found_duplicates_edit') + self.searching_vertical_layout.addWidget(self.found_duplicates_edit) + self.searching_page_id = self.addPage(self.searching_page) + self.review_page = QtGui.QWizardPage() + self.review_page.setObjectName(u'review_page') + self.review_layout = QtGui.QVBoxLayout(self.review_page) + self.review_layout.setObjectName(u'review_layout') + self.songs_horizontal_scroll_area = QtGui.QScrollArea(self.review_page) + self.songs_horizontal_scroll_area.setObjectName(u'songs_horizontal_scroll_area') + self.songs_horizontal_scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.songs_horizontal_scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.songs_horizontal_scroll_area.setFrameStyle(QtGui.QFrame.NoFrame) + self.songs_horizontal_scroll_area.setWidgetResizable(True) + self.songs_horizontal_scroll_area.setStyleSheet( + u'QScrollArea#songs_horizontal_scroll_area {background-color:transparent;}') + self.songs_horizontal_songs_widget = QtGui.QWidget(self.songs_horizontal_scroll_area) + self.songs_horizontal_songs_widget.setObjectName(u'songs_horizontal_songs_widget') + self.songs_horizontal_songs_widget.setStyleSheet( + u'QWidget#songs_horizontal_songs_widget {background-color:transparent;}') + self.songs_horizontal_layout = QtGui.QHBoxLayout(self.songs_horizontal_songs_widget) + self.songs_horizontal_layout.setObjectName(u'songs_horizontal_layout') + self.songs_horizontal_layout.setSizeConstraint(QtGui.QLayout.SetMinAndMaxSize) + self.songs_horizontal_scroll_area.setWidget(self.songs_horizontal_songs_widget) + self.review_layout.addWidget(self.songs_horizontal_scroll_area) + self.review_page_id = self.addPage(self.review_page) #add a dummy page to the end, to prevent the finish button to appear and the next button do disappear on the #review page - self.dummyPage = QtGui.QWizardPage() - self.dummyPageId = self.addPage(self.dummyPage) + self.dummy_page = QtGui.QWizardPage() + self.dummy_page_id = self.addPage(self.dummy_page) def retranslateUi(self): """ @@ -137,53 +139,54 @@ class DuplicateSongRemovalForm(OpenLPWizard): u'This wizard will help you to remove duplicate songs from the song database. You will have a chance to ' u'review every potential duplicate song before it is deleted. So no songs will be deleted without your ' u'explicit approval.')) - self.searchingPage.setTitle(translate(u'Wizard', u'Searching for duplicate songs.')) - self.searchingPage.setSubTitle(translate(u'Wizard', u'The song database is searched for double songs.')) + self.searching_page.setTitle(translate(u'Wizard', u'Searching for duplicate songs.')) + self.searching_page.setSubTitle(translate(u'Wizard', u'The song database is searched for double songs.')) self.updateReviewCounterText() - self.reviewPage.setSubTitle(translate(u'Wizard', + self.review_page.setSubTitle(translate(u'Wizard', u'Here you can decide which songs to remove and which ones to keep.')) def updateReviewCounterText(self): """ Set the wizard review page header text. """ - self.reviewPage.setTitle(translate(u'Wizard', u'Review duplicate songs (%s/%s)') % \ - (self.reviewCurrentCount, self.reviewTotalCount)) + self.review_page.setTitle(translate(u'Wizard', u'Review duplicate songs (%s/%s)') % \ + (self.review_current_count, self.review_total_count)) - def customPageChanged(self, pageId): + def customPageChanged(self, page_id): """ Called when changing the wizard page. - ``pageId`` + ``page_id`` ID of the page the wizard changed to. """ #hide back button self.button(QtGui.QWizard.BackButton).hide() - if pageId == self.searchingPageId: + if page_id == self.searching_page_id: #search duplicate songs - maxSongs = self.plugin.manager.get_object_count(Song) - if maxSongs == 0 or maxSongs == 1: - self.duplicateSearchProgressBar.setMaximum(1) - self.duplicateSearchProgressBar.setValue(1) + max_songs = self.plugin.manager.get_object_count(Song) + if max_songs == 0 or max_songs == 1: + self.duplicate_search_progress_bar.setMaximum(1) + self.duplicate_search_progress_bar.setValue(1) self.notifyNoDuplicates() return # with x songs we have x*(x - 1) / 2 comparisons - maxProgressCount = maxSongs * (maxSongs - 1) / 2 - self.duplicateSearchProgressBar.setMaximum(maxProgressCount) + max_progress_count = max_songs * (max_songs - 1) / 2 + self.duplicate_search_progress_bar.setMaximum(max_progress_count) songs = self.plugin.manager.get_all_objects(Song) - for outerSongCounter in range(maxSongs - 1): - for innerSongCounter in range(outerSongCounter + 1, maxSongs): - doubleFinder = DuplicateSongFinder() - if doubleFinder.songsProbablyEqual(songs[outerSongCounter], songs[innerSongCounter]): - duplicateAdded = self.addDuplicatesToSongList(songs[outerSongCounter], songs[innerSongCounter]) - if duplicateAdded: - self.foundDuplicatesEdit.appendPlainText(songs[outerSongCounter].title + " = " + - songs[innerSongCounter].title) - self.duplicateSearchProgressBar.setValue(self.duplicateSearchProgressBar.value() + 1) - self.reviewTotalCount = len(self.duplicateSongList) - if self.reviewTotalCount == 0: + for outer_song_counter in range(max_songs - 1): + for inner_song_counter in range(outer_song_counter + 1, max_songs): + double_finder = DuplicateSongFinder() + if double_finder.songs_probably_equal(songs[outer_song_counter], songs[inner_song_counter]): + duplicate_added = self.addDuplicatesToSongList(songs[outer_song_counter], + songs[inner_song_counter]) + if duplicate_added: + self.found_duplicates_edit.appendPlainText(songs[outer_song_counter].title + " = " + + songs[inner_song_counter].title) + self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1) + self.review_total_count = len(self.duplicate_song_list) + if self.review_total_count == 0: self.notifyNoDuplicates() - elif pageId == self.reviewPageId: + elif page_id == self.review_page_id: self.processCurrentDuplicateEntry() def notifyNoDuplicates(self): @@ -198,7 +201,7 @@ class DuplicateSongRemovalForm(OpenLPWizard): QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) - def addDuplicatesToSongList(self, searchSong, duplicateSong): + def addDuplicatesToSongList(self, search_song, duplicate_song): """ Inserts a song duplicate (two similar songs) to the duplicate song list. If one of the two songs is already part of the duplicate song list, @@ -206,34 +209,34 @@ class DuplicateSongRemovalForm(OpenLPWizard): Returns True if at least one of the songs was added, False if both were already member of a group. - ``searchSong`` + ``search_song`` The song we searched the duplicate for. - ``duplicateSong`` + ``duplicate_song`` The duplicate song. """ - duplicateGroupFound = False - duplicateAdded = False - for duplicateGroup in self.duplicateSongList: + duplicate_group_found = False + duplicate_added = False + for duplicate_group in self.duplicate_song_list: #skip the first song in the duplicate lists, since the first one has to be an earlier song - if searchSong in duplicateGroup and not duplicateSong in duplicateGroup: - duplicateGroup.append(duplicateSong) - duplicateGroupFound = True - duplicateAdded = True + if search_song in duplicate_group and not duplicate_song in duplicate_group: + duplicate_group.append(duplicate_song) + duplicate_group_found = True + duplicate_added = True break - elif not searchSong in duplicateGroup and duplicateSong in duplicateGroup: - duplicateGroup.append(searchSong) - duplicateGroupFound = True - duplicateAdded = True + elif not search_song in duplicate_group and duplicate_song in duplicate_group: + duplicate_group.append(search_song) + duplicate_group_found = True + duplicate_added = True break - elif searchSong in duplicateGroup and duplicateSong in duplicateGroup: - duplicateGroupFound = True - duplicateAdded = False + elif search_song in duplicate_group and duplicate_song in duplicate_group: + duplicate_group_found = True + duplicate_added = False break - if not duplicateGroupFound: - self.duplicateSongList.append([searchSong, duplicateSong]) - duplicateAdded = True - return duplicateAdded + if not duplicate_group_found: + self.duplicate_song_list.append([search_song, duplicate_song]) + duplicate_added = True + return duplicate_added def onWizardExit(self): """ @@ -247,36 +250,36 @@ class DuplicateSongRemovalForm(OpenLPWizard): Set default form values for the song import wizard. """ self.restart() - self.duplicateSearchProgressBar.setValue(0) - self.foundDuplicatesEdit.clear() + self.duplicate_search_progress_bar.setValue(0) + self.found_duplicates_edit.clear() def validateCurrentPage(self): """ Controls whether we should switch to the next wizard page. This method loops on the review page as long as there are more song duplicates to review. """ - if self.currentId() == self.reviewPageId: + if self.currentId() == self.review_page_id: #as long as it's not the last duplicate list entry we revisit the review page - if len(self.duplicateSongList) == 1: + if len(self.duplicate_song_list) == 1: return True else: self.proceedToNextReview() return False return OpenLPWizard.validateCurrentPage(self) - def removeButtonClicked(self, songReviewWidget): + def removeButtonClicked(self, song_review_widget): """ Removes a song from the database, removes the GUI element representing the song on the review page, and disable the remove button if only one duplicate is left. - ``songReviewWidget`` + ``song_review_widget`` The SongReviewWidget whose song we should delete. """ #remove song from duplicate song list - self.duplicateSongList[-1].remove(songReviewWidget.song) + self.duplicate_song_list[-1].remove(song_review_widget.song) #remove song - item_id = songReviewWidget.song.id + item_id = song_review_widget.song.id media_files = self.plugin.manager.get_all_objects(MediaFile, MediaFile.song_id == item_id) for media_file in media_files: @@ -294,31 +297,31 @@ class DuplicateSongRemovalForm(OpenLPWizard): log.exception(u'Could not remove directory: %s', save_path) self.plugin.manager.delete_object(Song, item_id) # remove GUI elements - self.songsHorizontalLayout.removeWidget(songReviewWidget) - songReviewWidget.setParent(None) + self.songs_horizontal_layout.removeWidget(song_review_widget) + song_review_widget.setParent(None) # check if we only have one duplicate left # 4 stretches + 1 SongReviewWidget = 5 # the SongReviewWidget is then at position 2 - if len(self.duplicateSongList[-1]) == 1: - self.songsHorizontalLayout.itemAt(2).widget().songRemoveButton.setEnabled(False) + if len(self.duplicate_song_list[-1]) == 1: + self.songs_horizontal_layout.itemAt(2).widget().song_remove_button.setEnabled(False) def proceedToNextReview(self): """ Removes the previous review UI elements and calls processCurrentDuplicateEntry. """ #remove last duplicate group - self.duplicateSongList.pop() + self.duplicate_song_list.pop() # remove all previous elements - for i in reversed(range(self.songsHorizontalLayout.count())): - item = self.songsHorizontalLayout.itemAt(i) + for i in reversed(range(self.songs_horizontal_layout.count())): + item = self.songs_horizontal_layout.itemAt(i) if isinstance(item, QtGui.QWidgetItem): - # the order is important here, if the .setParent(None) call is done before the .removeItem() call, a - # segfault occurs + # The order is important here, if the .setParent(None) call is done + # before the .removeItem() call, a segfault occurs. widget = item.widget() - self.songsHorizontalLayout.removeItem(item) + self.songs_horizontal_layout.removeItem(item) widget.setParent(None) else: - self.songsHorizontalLayout.removeItem(item) + self.songs_horizontal_layout.removeItem(item) #process next set of duplicates self.processCurrentDuplicateEntry() @@ -329,23 +332,23 @@ class DuplicateSongRemovalForm(OpenLPWizard): duplicate song group, hide the "next" button and show the "finish" button. """ # update counter - self.reviewCurrentCount = self.reviewTotalCount - (len(self.duplicateSongList) - 1) + self.review_current_count = self.review_total_count - (len(self.duplicate_song_list) - 1) self.updateReviewCounterText() # add song elements to the UI - if len(self.duplicateSongList) > 0: + if len(self.duplicate_song_list) > 0: # a stretch doesn't seem to stretch endlessly, so I add two to get enough stetch for 1400x1050 - self.songsHorizontalLayout.addStretch() - self.songsHorizontalLayout.addStretch() - for duplicate in self.duplicateSongList[-1]: - songReviewWidget = SongReviewWidget(self.reviewPage, duplicate) - QtCore.QObject.connect(songReviewWidget, + self.songs_horizontal_layout.addStretch() + self.songs_horizontal_layout.addStretch() + for duplicate in self.duplicate_song_list[-1]: + song_review_widget = SongReviewWidget(self.review_page, duplicate) + QtCore.QObject.connect(song_review_widget, QtCore.SIGNAL(u'songRemoveButtonClicked(PyQt_PyObject)'), self.removeButtonClicked) - self.songsHorizontalLayout.addWidget(songReviewWidget) - self.songsHorizontalLayout.addStretch() - self.songsHorizontalLayout.addStretch() + self.songs_horizontal_layout.addWidget(song_review_widget) + self.songs_horizontal_layout.addStretch() + self.songs_horizontal_layout.addStretch() #change next button to finish button on last review - if len(self.duplicateSongList) == 1: + if len(self.duplicate_song_list) == 1: self.button(QtGui.QWizard.FinishButton).show() self.button(QtGui.QWizard.FinishButton).setEnabled(True) self.button(QtGui.QWizard.NextButton).hide() @@ -370,119 +373,119 @@ class SongReviewWidget(QtGui.QWidget): self.song = song self.setupUi() self.retranslateUi() - QtCore.QObject.connect(self.songRemoveButton, QtCore.SIGNAL(u'clicked()'), self.onRemoveButtonClicked) + QtCore.QObject.connect(self.song_remove_button, QtCore.SIGNAL(u'clicked()'), self.onRemoveButtonClicked) def setupUi(self): - self.songVerticalLayout = QtGui.QVBoxLayout(self) - self.songVerticalLayout.setObjectName(u'songVerticalLayout') - self.songGroupBox = QtGui.QGroupBox(self) - self.songGroupBox.setObjectName(u'songGroupBox') - self.songGroupBox.setMinimumWidth(300) - self.songGroupBox.setMaximumWidth(300) - self.songGroupBoxLayout = QtGui.QVBoxLayout(self.songGroupBox) - self.songGroupBoxLayout.setObjectName(u'songGroupBoxLayout') - self.songInfoFormLayout = QtGui.QFormLayout() - self.songInfoFormLayout.setObjectName(u'songInfoFormLayout') + self.song_vertical_layout = QtGui.QVBoxLayout(self) + self.song_vertical_layout.setObjectName(u'song_vertical_layout') + self.song_group_box = QtGui.QGroupBox(self) + self.song_group_box.setObjectName(u'song_group_box') + self.song_group_box.setMinimumWidth(300) + self.song_group_box.setMaximumWidth(300) + self.song_group_box_layout = QtGui.QVBoxLayout(self.song_group_box) + self.song_group_box_layout.setObjectName(u'song_group_box_layout') + self.song_info_form_layout = QtGui.QFormLayout() + self.song_info_form_layout.setObjectName(u'song_info_form_layout') #title - self.songTitleLabel = QtGui.QLabel(self) - self.songTitleLabel.setObjectName(u'songTitleLabel') - self.songInfoFormLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.songTitleLabel) - self.songTitleContent = QtGui.QLabel(self) - self.songTitleContent.setObjectName(u'songTitleContent') - self.songTitleContent.setText(self.song.title) - self.songTitleContent.setWordWrap(True) - self.songInfoFormLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.songTitleContent) + self.song_title_label = QtGui.QLabel(self) + self.song_title_label.setObjectName(u'song_title_label') + self.song_info_form_layout.setWidget(0, QtGui.QFormLayout.LabelRole, self.song_title_label) + self.song_title_content = QtGui.QLabel(self) + self.song_title_content.setObjectName(u'song_title_content') + self.song_title_content.setText(self.song.title) + self.song_title_content.setWordWrap(True) + self.song_info_form_layout.setWidget(0, QtGui.QFormLayout.FieldRole, self.song_title_content) #alternate title - self.songAlternateTitleLabel = QtGui.QLabel(self) - self.songAlternateTitleLabel.setObjectName(u'songAlternateTitleLabel') - self.songInfoFormLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.songAlternateTitleLabel) - self.songAlternateTitleContent = QtGui.QLabel(self) - self.songAlternateTitleContent.setObjectName(u'songAlternateTitleContent') - self.songAlternateTitleContent.setText(self.song.alternate_title) - self.songAlternateTitleContent.setWordWrap(True) - self.songInfoFormLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.songAlternateTitleContent) + self.song_alternate_title_label = QtGui.QLabel(self) + self.song_alternate_title_label.setObjectName(u'song_alternate_title_label') + self.song_info_form_layout.setWidget(1, QtGui.QFormLayout.LabelRole, self.song_alternate_title_label) + self.song_alternate_title_content = QtGui.QLabel(self) + self.song_alternate_title_content.setObjectName(u'song_alternate_title_content') + self.song_alternate_title_content.setText(self.song.alternate_title) + self.song_alternate_title_content.setWordWrap(True) + self.song_info_form_layout.setWidget(1, QtGui.QFormLayout.FieldRole, self.song_alternate_title_content) #CCLI number - self.songCCLINumberLabel = QtGui.QLabel(self) - self.songCCLINumberLabel.setObjectName(u'songCCLINumberLabel') - self.songInfoFormLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.songCCLINumberLabel) - self.songCCLINumberContent = QtGui.QLabel(self) - self.songCCLINumberContent.setObjectName(u'songCCLINumberContent') - self.songCCLINumberContent.setText(self.song.ccli_number) - self.songCCLINumberContent.setWordWrap(True) - self.songInfoFormLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.songCCLINumberContent) + self.song_ccli_number_label = QtGui.QLabel(self) + self.song_ccli_number_label.setObjectName(u'song_ccli_number_label') + self.song_info_form_layout.setWidget(2, QtGui.QFormLayout.LabelRole, self.song_ccli_number_label) + self.song_ccli_number_content = QtGui.QLabel(self) + self.song_ccli_number_content.setObjectName(u'song_ccli_number_content') + self.song_ccli_number_content.setText(self.song.ccli_number) + self.song_ccli_number_content.setWordWrap(True) + self.song_info_form_layout.setWidget(2, QtGui.QFormLayout.FieldRole, self.song_ccli_number_content) #copyright - self.songCopyrightLabel = QtGui.QLabel(self) - self.songCopyrightLabel.setObjectName(u'songCopyrightLabel') - self.songInfoFormLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.songCopyrightLabel) - self.songCopyrightContent = QtGui.QLabel(self) - self.songCopyrightContent.setObjectName(u'songCopyrightContent') - self.songCopyrightContent.setWordWrap(True) - self.songCopyrightContent.setText(self.song.copyright) - self.songInfoFormLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.songCopyrightContent) + self.song_copyright_label = QtGui.QLabel(self) + self.song_copyright_label.setObjectName(u'song_copyright_label') + self.song_info_form_layout.setWidget(3, QtGui.QFormLayout.LabelRole, self.song_copyright_label) + self.song_copyright_content = QtGui.QLabel(self) + self.song_copyright_content.setObjectName(u'song_copyright_content') + self.song_copyright_content.setWordWrap(True) + self.song_copyright_content.setText(self.song.copyright) + self.song_info_form_layout.setWidget(3, QtGui.QFormLayout.FieldRole, self.song_copyright_content) #comments - self.songCommentsLabel = QtGui.QLabel(self) - self.songCommentsLabel.setObjectName(u'songCommentsLabel') - self.songInfoFormLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.songCommentsLabel) - self.songCommentsContent = QtGui.QLabel(self) - self.songCommentsContent.setObjectName(u'songCommentsContent') - self.songCommentsContent.setText(self.song.comments) - self.songCommentsContent.setWordWrap(True) - self.songInfoFormLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.songCommentsContent) + self.song_comments_label = QtGui.QLabel(self) + self.song_comments_label.setObjectName(u'song_comments_label') + self.song_info_form_layout.setWidget(4, QtGui.QFormLayout.LabelRole, self.song_comments_label) + self.song_comments_content = QtGui.QLabel(self) + self.song_comments_content.setObjectName(u'song_comments_content') + self.song_comments_content.setText(self.song.comments) + self.song_comments_content.setWordWrap(True) + self.song_info_form_layout.setWidget(4, QtGui.QFormLayout.FieldRole, self.song_comments_content) #authors - self.songAuthorsLabel = QtGui.QLabel(self) - self.songAuthorsLabel.setObjectName(u'songAuthorsLabel') - self.songInfoFormLayout.setWidget(5, QtGui.QFormLayout.LabelRole, self.songAuthorsLabel) - self.songAuthorsContent = QtGui.QLabel(self) - self.songAuthorsContent.setObjectName(u'songAuthorsContent') - self.songAuthorsContent.setWordWrap(True) - authorsText = u'' + self.song_authors_label = QtGui.QLabel(self) + self.song_authors_label.setObjectName(u'song_authors_label') + self.song_info_form_layout.setWidget(5, QtGui.QFormLayout.LabelRole, self.song_authors_label) + self.song_authors_content = QtGui.QLabel(self) + self.song_authors_content.setObjectName(u'song_authors_content') + self.song_authors_content.setWordWrap(True) + authors_text = u'' for author in self.song.authors: - authorsText += author.display_name + ', ' - if authorsText: - authorsText = authorsText[:-2] - self.songAuthorsContent.setText(authorsText) - self.songInfoFormLayout.setWidget(5, QtGui.QFormLayout.FieldRole, self.songAuthorsContent) + authors_text += author.display_name + ', ' + if authors_text: + authors_text = authors_text[:-2] + self.song_authors_content.setText(authors_text) + self.song_info_form_layout.setWidget(5, QtGui.QFormLayout.FieldRole, self.song_authors_content) #verse order - self.songVerseOrderLabel = QtGui.QLabel(self) - self.songVerseOrderLabel.setObjectName(u'songVerseOrderLabel') - self.songInfoFormLayout.setWidget(6, QtGui.QFormLayout.LabelRole, self.songVerseOrderLabel) - self.songVerseOrderContent = QtGui.QLabel(self) - self.songVerseOrderContent.setObjectName(u'songVerseOrderContent') - self.songVerseOrderContent.setText(self.song.verse_order) - self.songVerseOrderContent.setWordWrap(True) - self.songInfoFormLayout.setWidget(6, QtGui.QFormLayout.FieldRole, self.songVerseOrderContent) + self.song_verse_order_label = QtGui.QLabel(self) + self.song_verse_order_label.setObjectName(u'song_verse_order_label') + self.song_info_form_layout.setWidget(6, QtGui.QFormLayout.LabelRole, self.song_verse_order_label) + self.song_verse_order_content = QtGui.QLabel(self) + self.song_verse_order_content.setObjectName(u'song_verse_order_content') + self.song_verse_order_content.setText(self.song.verse_order) + self.song_verse_order_content.setWordWrap(True) + self.song_info_form_layout.setWidget(6, QtGui.QFormLayout.FieldRole, self.song_verse_order_content) #verses - self.songGroupBoxLayout.addLayout(self.songInfoFormLayout) - self.songInfoVerseGroupBox = QtGui.QGroupBox(self.songGroupBox) - self.songInfoVerseGroupBox.setObjectName(u'songInfoVerseGroupBox') - self.songInfoVerseGroupBoxLayout = QtGui.QFormLayout(self.songInfoVerseGroupBox) - songXml = SongXML() - verses = songXml.get_verses(self.song.lyrics) + self.song_group_box_layout.addLayout(self.song_info_form_layout) + self.song_info_verse_group_box = QtGui.QGroupBox(self.song_group_box) + self.song_info_verse_group_box.setObjectName(u'song_info_verse_group_box') + self.song_info_verse_group_box_layout = QtGui.QFormLayout(self.song_info_verse_group_box) + song_xml = SongXML() + verses = song_xml.get_verses(self.song.lyrics) for verse in verses: - verseMarker = verse[0]['type'] + verse[0]['label'] - verseLabel = QtGui.QLabel(self.songInfoVerseGroupBox) - verseLabel.setText(verse[1]) - verseLabel.setWordWrap(True) - self.songInfoVerseGroupBoxLayout.addRow(verseMarker, verseLabel) - self.songGroupBoxLayout.addWidget(self.songInfoVerseGroupBox) - self.songGroupBoxLayout.addStretch() - self.songVerticalLayout.addWidget(self.songGroupBox) - self.songRemoveButton = QtGui.QPushButton(self) - self.songRemoveButton.setObjectName(u'songRemoveButton') - self.songRemoveButton.setIcon(build_icon(u':/songs/song_delete.png')) - self.songRemoveButton.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) - self.songVerticalLayout.addWidget(self.songRemoveButton, alignment = QtCore.Qt.AlignHCenter) + verse_marker = verse[0]['type'] + verse[0]['label'] + verse_label = QtGui.QLabel(self.song_info_verse_group_box) + verse_label.setText(verse[1]) + verse_label.setWordWrap(True) + self.song_info_verse_group_box_layout.addRow(verse_marker, verse_label) + self.song_group_box_layout.addWidget(self.song_info_verse_group_box) + self.song_group_box_layout.addStretch() + self.song_vertical_layout.addWidget(self.song_group_box) + self.song_remove_button = QtGui.QPushButton(self) + self.song_remove_button.setObjectName(u'song_remove_button') + self.song_remove_button.setIcon(build_icon(u':/songs/song_delete.png')) + self.song_remove_button.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + self.song_vertical_layout.addWidget(self.song_remove_button, alignment = QtCore.Qt.AlignHCenter) def retranslateUi(self): - self.songRemoveButton.setText(u'Remove') - self.songTitleLabel.setText(u'Title:') - self.songAlternateTitleLabel.setText(u'Alternate Title:') - self.songCCLINumberLabel.setText(u'CCLI Number:') - self.songVerseOrderLabel.setText(u'Verse Order:') - self.songCopyrightLabel.setText(u'Copyright:') - self.songCommentsLabel.setText(u'Comments:') - self.songAuthorsLabel.setText(u'Authors:') - self.songInfoVerseGroupBox.setTitle(u'Verses') + self.song_remove_button.setText(u'Remove') + self.song_title_label.setText(u'Title:') + self.song_alternate_title_label.setText(u'Alternate Title:') + self.song_ccli_number_label.setText(u'CCLI Number:') + self.song_verse_order_label.setText(u'Verse Order:') + self.song_copyright_label.setText(u'Copyright:') + self.song_comments_label.setText(u'Comments:') + self.song_authors_label.setText(u'Authors:') + self.song_info_verse_group_box.setTitle(u'Verses') def onRemoveButtonClicked(self): """ diff --git a/openlp/plugins/songs/lib/duplicatesongfinder.py b/openlp/plugins/songs/lib/duplicatesongfinder.py index e497c885f..38062632d 100644 --- a/openlp/plugins/songs/lib/duplicatesongfinder.py +++ b/openlp/plugins/songs/lib/duplicatesongfinder.py @@ -38,23 +38,23 @@ class DuplicateSongFinder(object): The algorithm is based on the diff algorithm. First a diffset is calculated for two songs. To compensate for typos all differences that are smaller than a - limit (minFragmentSize) are removed and the surrounding equal parts are merged. + limit (min_fragment_size) are removed and the surrounding equal parts are merged. Finally two conditions can qualify a song tuple to be a duplicate: - 1. There is a block of equal content that is at least minBlockSize large. + 1. There is a block of equal content that is at least min_block_size large. This condition should hit for all larger songs that have a long enough equal part. Even if only one verse is equal this condition should still hit. 2. Two thirds of the smaller song is contained in the larger song. This condition should hit if one of the two songs (or both) is small (smaller - than the minBlockSize), but most of the song is contained in the other song. + than the min_block_size), but most of the song is contained in the other song. """ def __init__(self): - self.minFragmentSize = 5 - self.minBlockSize = 70 - self.maxTypoSize = 3 + self.min_fragment_size = 5 + self.min_block_size = 70 + self.max_typo_size = 3 - def songsProbablyEqual(self, song1, song2): + def songs_probably_equal(self, song1, song2): """ Calculate and return whether two songs are probably equal. @@ -72,15 +72,15 @@ class DuplicateSongFinder(object): large = song1.search_lyrics differ = difflib.SequenceMatcher(a=large, b=small) diff_tuples = differ.get_opcodes() - diff_no_typos = self.__removeTypos(diff_tuples) + diff_no_typos = self.__remove_typos(diff_tuples) #print(diff_no_typos) - if self.__lengthOfEqualBlocks(diff_no_typos) >= self.minBlockSize or \ - self.__lengthOfLongestEqualBlock(diff_no_typos) > len(small) * 2 / 3: + if self.__length_of_equal_blocks(diff_no_typos) >= self.min_block_size or \ + self.__length_of_longest_equal_block(diff_no_typos) > len(small) * 2 / 3: return True else: return False - def __opLength(self, opcode): + def __op_length(self, opcode): """ Return the length of a given difference. @@ -89,57 +89,57 @@ class DuplicateSongFinder(object): """ return max(opcode[2] - opcode[1], opcode[4] - opcode[3]) - def __removeTypos(self, diff): + def __remove_typos(self, diff): """ - Remove typos from a diff set. A typo is a small difference (minFragmentSize). + Remove typos from a diff set. A typo is a small difference (min_fragment_size). ``diff`` The diff set to remove the typos from. """ #remove typo at beginning of string if len(diff) >= 2: - if diff[0][0] != "equal" and self.__opLength(diff[0]) <= self.maxTypoSize and \ - self.__opLength(diff[1]) >= self.minFragmentSize: + if diff[0][0] != "equal" and self.__op_length(diff[0]) <= self.max_typo_size and \ + self.__op_length(diff[1]) >= self.min_fragment_size: del diff[0] #remove typos in the middle of string if len(diff) >= 3: for index in range(len(diff) - 3, -1, -1): - if self.__opLength(diff[index]) >= self.minFragmentSize and \ - diff[index + 1][0] != "equal" and self.__opLength(diff[index + 1]) <= self.maxTypoSize and \ - self.__opLength(diff[index + 2]) >= self.minFragmentSize: + if self.__op_length(diff[index]) >= self.min_fragment_size and \ + diff[index + 1][0] != "equal" and self.__op_length(diff[index + 1]) <= self.max_typo_size and \ + self.__op_length(diff[index + 2]) >= self.min_fragment_size: del diff[index + 1] #remove typo at the end of string if len(diff) >= 2: - if self.__opLength(diff[-2]) >= self.minFragmentSize and \ - diff[-1][0] != "equal" and self.__opLength(diff[-1]) <= self.maxTypoSize: + if self.__op_length(diff[-2]) >= self.min_fragment_size and \ + diff[-1][0] != "equal" and self.__op_length(diff[-1]) <= self.max_typo_size: del diff[-1] #merge fragments for index in range(len(diff) - 2, -1, -1): - if diff[index][0] == "equal" and self.__opLength(diff[index]) >= self.minFragmentSize and \ - diff[index + 1][0] == "equal" and self.__opLength(diff[index + 1]) >= self.minFragmentSize: + if diff[index][0] == "equal" and self.__op_length(diff[index]) >= self.min_fragment_size and \ + diff[index + 1][0] == "equal" and self.__op_length(diff[index + 1]) >= self.min_fragment_size: diff[index] = ("equal", diff[index][1], diff[index + 1][2], diff[index][3], diff[index + 1][4]) del diff[index + 1] return diff - def __lengthOfEqualBlocks(self, diff): + def __length_of_equal_blocks(self, diff): """ Return the total length of all equal blocks in a diff set. - Blocks smaller than minBlockSize are not counted. + Blocks smaller than min_block_size are not counted. ``diff`` The diff set to return the length for. """ length = 0 for element in diff: - if element[0] == "equal" and self.__opLength(element) >= self.minBlockSize: - length += self.__opLength(element) + if element[0] == "equal" and self.__op_length(element) >= self.min_block_size: + length += self.__op_length(element) return length - def __lengthOfLongestEqualBlock(self, diff): + def __length_of_longest_equal_block(self, diff): """ Return the length of the largest equal block in a diff set. @@ -148,7 +148,7 @@ class DuplicateSongFinder(object): """ length = 0 for element in diff: - if element[0] == "equal" and self.__opLength(element) > length: - length = self.__opLength(element) + if element[0] == "equal" and self.__op_length(element) > length: + length = self.__op_length(element) return length diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index d98f97773..6de41da37 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -37,27 +37,22 @@ class TestLib(TestCase): def songs_probably_equal_test(self): """ - Test the DuplicateSongFinder.songsProbablyEqual function. + Test the DuplicateSongFinder.songs_probably_equal function. """ - full_lyrics =u'''amazing grace how sweet the sound that saved a wretch like me i once was lost but now am found was - blind but now i see twas grace that taught my heart to fear and grace my fears relieved how precious did that grace - appear the hour i first believed through many dangers toils and snares i have already come tis grace that brought - me safe thus far and grace will lead me home the lord has promised good to me his word my hope secures he will my - shield and portion be as long as life endures yea when this flesh and heart shall fail and mortal life shall cease - i shall possess within the veil a life of joy and peace when weve been here ten thousand years bright shining as - the sun weve no less days to sing gods praise than when weve first begun''' - short_lyrics =u'''twas grace that taught my heart to fear and grace my fears relieved how precious did that grace - appear the hour i first believed''' - error_lyrics =u'''amazing grace how sweet the sound that saved a wretch like me i once was lost but now am found was - blind but now i see twas grace that taught my heart to fear and grace my fears relieved how precious did that grace - appear the hour i first believedxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx snares i have already come tis grace that brought - me safe thus far and grace will lead me home the lord has promised good to me his word my hope secures he will my - shield andwhen this flcsh and heart shall fail and mortal life shall cease - i shall possess within the veila lifeofjoy and peace when weve been here ten thousand years bright shining as - the sun weve no less days to sing gods praise than when weve first begun''' - different_lyrics=u'''on a hill far away stood an old rugged cross the emblem of suffering and shame and i love that - old cross where the dearest and best for a world of lost sinners was slain so ill cherish the old rugged cross till - my trophies at last i lay down i will cling to the old rugged cross and exchange it some day for a crown''' + full_lyrics =u'''amazing grace how sweet the sound that saved a wretch like me i once was lost but now am + found was blind but now i see twas grace that taught my heart to fear and grace my fears relieved how + precious did that grace appear the hour i first believed through many dangers toils and snares i have already + come tis grace that brought me safe thus far and grace will lead me home''' + short_lyrics =u'''twas grace that taught my heart to fear and grace my fears relieved how precious did that + grace appear the hour i first believed''' + error_lyrics =u'''amazing how sweet the trumpet that saved a wrench like me i once was losst but now am + found waf blind but now i see it was grace that taught my heart to fear and grace my fears relieved how + precious did that grace appppppppear the hour i first believedxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx snares i have + already come to this grace that brought me safe so far and grace will lead me home''' + different_lyrics=u'''on a hill far away stood an old rugged cross the emblem of suffering and shame and i love + that old cross where the dearest and best for a world of lost sinners was slain so ill cherish the old rugged + cross till my trophies at last i lay down i will cling to the old rugged cross and exchange it some day for a + crown''' dsf = DuplicateSongFinder() song1 = MagicMock() song2 = MagicMock() @@ -67,7 +62,7 @@ class TestLib(TestCase): song2.search_lyrics = full_lyrics #WHEN: We compare those songs for equality - result = dsf.songsProbablyEqual(song1, song2) + result = dsf.songs_probably_equal(song1, song2) #THEN: The result should be True assert result is True, u'The result should be True' @@ -77,7 +72,7 @@ class TestLib(TestCase): song2.search_lyrics = short_lyrics #WHEN: We compare those songs for equality - result = dsf.songsProbablyEqual(song1, song2) + result = dsf.songs_probably_equal(song1, song2) #THEN: The result should be True assert result is True, u'The result should be True' @@ -87,7 +82,7 @@ class TestLib(TestCase): song2.search_lyrics = error_lyrics #WHEN: We compare those songs for equality - result = dsf.songsProbablyEqual(song1, song2) + result = dsf.songs_probably_equal(song1, song2) #THEN: The result should be True assert result is True, u'The result should be True' @@ -97,7 +92,7 @@ class TestLib(TestCase): song2.search_lyrics = different_lyrics #WHEN: We compare those songs for equality - result = dsf.songsProbablyEqual(song1, song2) + result = dsf.songs_probably_equal(song1, song2) #THEN: The result should be False assert result is False, u'The result should be False'