Rename lots of variables and methods. Shorten some too long lines.

This commit is contained in:
Patrick Zimmermann 2013-02-14 21:57:13 +01:00
parent bbd491baa8
commit 2a16a6ebcb
3 changed files with 268 additions and 270 deletions

View File

@ -63,9 +63,9 @@ class DuplicateSongRemovalForm(OpenLPWizard):
``plugin`` ``plugin``
The songs plugin. The songs plugin.
""" """
self.duplicateSongList = [] self.duplicate_song_list = []
self.reviewCurrentCount = 0 self.review_current_count = 0
self.reviewTotalCount = 0 self.review_total_count = 0
OpenLPWizard.__init__(self, parent, plugin, u'duplicateSongRemovalWizard', OpenLPWizard.__init__(self, parent, plugin, u'duplicateSongRemovalWizard',
u':/wizards/wizard_duplicateremoval.bmp', False) u':/wizards/wizard_duplicateremoval.bmp', False)
@ -87,44 +87,46 @@ class DuplicateSongRemovalForm(OpenLPWizard):
Add song wizard specific pages. Add song wizard specific pages.
""" """
#add custom pages #add custom pages
self.searchingPage = QtGui.QWizardPage() self.searching_page = QtGui.QWizardPage()
self.searchingPage.setObjectName(u'searchingPage') self.searching_page.setObjectName(u'searching_page')
self.searchingVerticalLayout = QtGui.QVBoxLayout(self.searchingPage) self.searching_vertical_layout = QtGui.QVBoxLayout(self.searching_page)
self.searchingVerticalLayout.setObjectName(u'searchingVerticalLayout') self.searching_vertical_layout.setObjectName(u'searching_vertical_layout')
self.duplicateSearchProgressBar = QtGui.QProgressBar(self.searchingPage) self.duplicate_search_progress_bar = QtGui.QProgressBar(self.searching_page)
self.duplicateSearchProgressBar.setObjectName(u'duplicateSearchProgressBar') self.duplicate_search_progress_bar.setObjectName(u'duplicate_search_progress_bar')
self.duplicateSearchProgressBar.setFormat(WizardStrings.PercentSymbolFormat) self.duplicate_search_progress_bar.setFormat(WizardStrings.PercentSymbolFormat)
self.searchingVerticalLayout.addWidget(self.duplicateSearchProgressBar) self.searching_vertical_layout.addWidget(self.duplicate_search_progress_bar)
self.foundDuplicatesEdit = QtGui.QPlainTextEdit(self.searchingPage) self.found_duplicates_edit = QtGui.QPlainTextEdit(self.searching_page)
self.foundDuplicatesEdit.setUndoRedoEnabled(False) self.found_duplicates_edit.setUndoRedoEnabled(False)
self.foundDuplicatesEdit.setReadOnly(True) self.found_duplicates_edit.setReadOnly(True)
self.foundDuplicatesEdit.setObjectName(u'foundDuplicatesEdit') self.found_duplicates_edit.setObjectName(u'found_duplicates_edit')
self.searchingVerticalLayout.addWidget(self.foundDuplicatesEdit) self.searching_vertical_layout.addWidget(self.found_duplicates_edit)
self.searchingPageId = self.addPage(self.searchingPage) self.searching_page_id = self.addPage(self.searching_page)
self.reviewPage = QtGui.QWizardPage() self.review_page = QtGui.QWizardPage()
self.reviewPage.setObjectName(u'reviewPage') self.review_page.setObjectName(u'review_page')
self.reviewLayout = QtGui.QVBoxLayout(self.reviewPage) self.review_layout = QtGui.QVBoxLayout(self.review_page)
self.reviewLayout.setObjectName(u'reviewLayout') self.review_layout.setObjectName(u'review_layout')
self.songsHorizontalScrollArea = QtGui.QScrollArea(self.reviewPage) self.songs_horizontal_scroll_area = QtGui.QScrollArea(self.review_page)
self.songsHorizontalScrollArea.setObjectName(u'songsHorizontalScrollArea') self.songs_horizontal_scroll_area.setObjectName(u'songs_horizontal_scroll_area')
self.songsHorizontalScrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.songs_horizontal_scroll_area.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.songsHorizontalScrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) self.songs_horizontal_scroll_area.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.songsHorizontalScrollArea.setFrameStyle(QtGui.QFrame.NoFrame) self.songs_horizontal_scroll_area.setFrameStyle(QtGui.QFrame.NoFrame)
self.songsHorizontalScrollArea.setWidgetResizable(True) self.songs_horizontal_scroll_area.setWidgetResizable(True)
self.songsHorizontalScrollArea.setStyleSheet(u'QScrollArea#songsHorizontalScrollArea {background-color:transparent;}') self.songs_horizontal_scroll_area.setStyleSheet(
self.songsHorizontalSongsWidget = QtGui.QWidget(self.songsHorizontalScrollArea) u'QScrollArea#songs_horizontal_scroll_area {background-color:transparent;}')
self.songsHorizontalSongsWidget.setObjectName(u'songsHorizontalSongsWidget') self.songs_horizontal_songs_widget = QtGui.QWidget(self.songs_horizontal_scroll_area)
self.songsHorizontalSongsWidget.setStyleSheet(u'QWidget#songsHorizontalSongsWidget {background-color:transparent;}') self.songs_horizontal_songs_widget.setObjectName(u'songs_horizontal_songs_widget')
self.songsHorizontalLayout = QtGui.QHBoxLayout(self.songsHorizontalSongsWidget) self.songs_horizontal_songs_widget.setStyleSheet(
self.songsHorizontalLayout.setObjectName(u'songsHorizontalLayout') u'QWidget#songs_horizontal_songs_widget {background-color:transparent;}')
self.songsHorizontalLayout.setSizeConstraint(QtGui.QLayout.SetMinAndMaxSize) self.songs_horizontal_layout = QtGui.QHBoxLayout(self.songs_horizontal_songs_widget)
self.songsHorizontalScrollArea.setWidget(self.songsHorizontalSongsWidget) self.songs_horizontal_layout.setObjectName(u'songs_horizontal_layout')
self.reviewLayout.addWidget(self.songsHorizontalScrollArea) self.songs_horizontal_layout.setSizeConstraint(QtGui.QLayout.SetMinAndMaxSize)
self.reviewPageId = self.addPage(self.reviewPage) 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 #add a dummy page to the end, to prevent the finish button to appear and the next button do disappear on the
#review page #review page
self.dummyPage = QtGui.QWizardPage() self.dummy_page = QtGui.QWizardPage()
self.dummyPageId = self.addPage(self.dummyPage) self.dummy_page_id = self.addPage(self.dummy_page)
def retranslateUi(self): 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'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'review every potential duplicate song before it is deleted. So no songs will be deleted without your '
u'explicit approval.')) u'explicit approval.'))
self.searchingPage.setTitle(translate(u'Wizard', u'Searching for duplicate songs.')) self.searching_page.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.setSubTitle(translate(u'Wizard', u'The song database is searched for double songs.'))
self.updateReviewCounterText() 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.')) u'Here you can decide which songs to remove and which ones to keep.'))
def updateReviewCounterText(self): def updateReviewCounterText(self):
""" """
Set the wizard review page header text. Set the wizard review page header text.
""" """
self.reviewPage.setTitle(translate(u'Wizard', u'Review duplicate songs (%s/%s)') % \ self.review_page.setTitle(translate(u'Wizard', u'Review duplicate songs (%s/%s)') % \
(self.reviewCurrentCount, self.reviewTotalCount)) (self.review_current_count, self.review_total_count))
def customPageChanged(self, pageId): def customPageChanged(self, page_id):
""" """
Called when changing the wizard page. Called when changing the wizard page.
``pageId`` ``page_id``
ID of the page the wizard changed to. ID of the page the wizard changed to.
""" """
#hide back button #hide back button
self.button(QtGui.QWizard.BackButton).hide() self.button(QtGui.QWizard.BackButton).hide()
if pageId == self.searchingPageId: if page_id == self.searching_page_id:
#search duplicate songs #search duplicate songs
maxSongs = self.plugin.manager.get_object_count(Song) max_songs = self.plugin.manager.get_object_count(Song)
if maxSongs == 0 or maxSongs == 1: if max_songs == 0 or max_songs == 1:
self.duplicateSearchProgressBar.setMaximum(1) self.duplicate_search_progress_bar.setMaximum(1)
self.duplicateSearchProgressBar.setValue(1) self.duplicate_search_progress_bar.setValue(1)
self.notifyNoDuplicates() self.notifyNoDuplicates()
return return
# with x songs we have x*(x - 1) / 2 comparisons # with x songs we have x*(x - 1) / 2 comparisons
maxProgressCount = maxSongs * (maxSongs - 1) / 2 max_progress_count = max_songs * (max_songs - 1) / 2
self.duplicateSearchProgressBar.setMaximum(maxProgressCount) self.duplicate_search_progress_bar.setMaximum(max_progress_count)
songs = self.plugin.manager.get_all_objects(Song) songs = self.plugin.manager.get_all_objects(Song)
for outerSongCounter in range(maxSongs - 1): for outer_song_counter in range(max_songs - 1):
for innerSongCounter in range(outerSongCounter + 1, maxSongs): for inner_song_counter in range(outer_song_counter + 1, max_songs):
doubleFinder = DuplicateSongFinder() double_finder = DuplicateSongFinder()
if doubleFinder.songsProbablyEqual(songs[outerSongCounter], songs[innerSongCounter]): if double_finder.songs_probably_equal(songs[outer_song_counter], songs[inner_song_counter]):
duplicateAdded = self.addDuplicatesToSongList(songs[outerSongCounter], songs[innerSongCounter]) duplicate_added = self.addDuplicatesToSongList(songs[outer_song_counter],
if duplicateAdded: songs[inner_song_counter])
self.foundDuplicatesEdit.appendPlainText(songs[outerSongCounter].title + " = " + if duplicate_added:
songs[innerSongCounter].title) self.found_duplicates_edit.appendPlainText(songs[outer_song_counter].title + " = " +
self.duplicateSearchProgressBar.setValue(self.duplicateSearchProgressBar.value() + 1) songs[inner_song_counter].title)
self.reviewTotalCount = len(self.duplicateSongList) self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1)
if self.reviewTotalCount == 0: self.review_total_count = len(self.duplicate_song_list)
if self.review_total_count == 0:
self.notifyNoDuplicates() self.notifyNoDuplicates()
elif pageId == self.reviewPageId: elif page_id == self.review_page_id:
self.processCurrentDuplicateEntry() self.processCurrentDuplicateEntry()
def notifyNoDuplicates(self): def notifyNoDuplicates(self):
@ -198,7 +201,7 @@ class DuplicateSongRemovalForm(OpenLPWizard):
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) 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. 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, 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 Returns True if at least one of the songs was added, False if both were already
member of a group. member of a group.
``searchSong`` ``search_song``
The song we searched the duplicate for. The song we searched the duplicate for.
``duplicateSong`` ``duplicate_song``
The duplicate song. The duplicate song.
""" """
duplicateGroupFound = False duplicate_group_found = False
duplicateAdded = False duplicate_added = False
for duplicateGroup in self.duplicateSongList: 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 #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: if search_song in duplicate_group and not duplicate_song in duplicate_group:
duplicateGroup.append(duplicateSong) duplicate_group.append(duplicate_song)
duplicateGroupFound = True duplicate_group_found = True
duplicateAdded = True duplicate_added = True
break break
elif not searchSong in duplicateGroup and duplicateSong in duplicateGroup: elif not search_song in duplicate_group and duplicate_song in duplicate_group:
duplicateGroup.append(searchSong) duplicate_group.append(search_song)
duplicateGroupFound = True duplicate_group_found = True
duplicateAdded = True duplicate_added = True
break break
elif searchSong in duplicateGroup and duplicateSong in duplicateGroup: elif search_song in duplicate_group and duplicate_song in duplicate_group:
duplicateGroupFound = True duplicate_group_found = True
duplicateAdded = False duplicate_added = False
break break
if not duplicateGroupFound: if not duplicate_group_found:
self.duplicateSongList.append([searchSong, duplicateSong]) self.duplicate_song_list.append([search_song, duplicate_song])
duplicateAdded = True duplicate_added = True
return duplicateAdded return duplicate_added
def onWizardExit(self): def onWizardExit(self):
""" """
@ -247,36 +250,36 @@ class DuplicateSongRemovalForm(OpenLPWizard):
Set default form values for the song import wizard. Set default form values for the song import wizard.
""" """
self.restart() self.restart()
self.duplicateSearchProgressBar.setValue(0) self.duplicate_search_progress_bar.setValue(0)
self.foundDuplicatesEdit.clear() self.found_duplicates_edit.clear()
def validateCurrentPage(self): def validateCurrentPage(self):
""" """
Controls whether we should switch to the next wizard page. This method loops 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. 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 #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 return True
else: else:
self.proceedToNextReview() self.proceedToNextReview()
return False return False
return OpenLPWizard.validateCurrentPage(self) 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 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 song on the review page, and disable the remove button if only one duplicate
is left. is left.
``songReviewWidget`` ``song_review_widget``
The SongReviewWidget whose song we should delete. The SongReviewWidget whose song we should delete.
""" """
#remove song from duplicate song list #remove song from duplicate song list
self.duplicateSongList[-1].remove(songReviewWidget.song) self.duplicate_song_list[-1].remove(song_review_widget.song)
#remove song #remove song
item_id = songReviewWidget.song.id item_id = song_review_widget.song.id
media_files = self.plugin.manager.get_all_objects(MediaFile, media_files = self.plugin.manager.get_all_objects(MediaFile,
MediaFile.song_id == item_id) MediaFile.song_id == item_id)
for media_file in media_files: for media_file in media_files:
@ -294,31 +297,31 @@ class DuplicateSongRemovalForm(OpenLPWizard):
log.exception(u'Could not remove directory: %s', save_path) log.exception(u'Could not remove directory: %s', save_path)
self.plugin.manager.delete_object(Song, item_id) self.plugin.manager.delete_object(Song, item_id)
# remove GUI elements # remove GUI elements
self.songsHorizontalLayout.removeWidget(songReviewWidget) self.songs_horizontal_layout.removeWidget(song_review_widget)
songReviewWidget.setParent(None) song_review_widget.setParent(None)
# check if we only have one duplicate left # check if we only have one duplicate left
# 4 stretches + 1 SongReviewWidget = 5 # 4 stretches + 1 SongReviewWidget = 5
# the SongReviewWidget is then at position 2 # the SongReviewWidget is then at position 2
if len(self.duplicateSongList[-1]) == 1: if len(self.duplicate_song_list[-1]) == 1:
self.songsHorizontalLayout.itemAt(2).widget().songRemoveButton.setEnabled(False) self.songs_horizontal_layout.itemAt(2).widget().song_remove_button.setEnabled(False)
def proceedToNextReview(self): def proceedToNextReview(self):
""" """
Removes the previous review UI elements and calls processCurrentDuplicateEntry. Removes the previous review UI elements and calls processCurrentDuplicateEntry.
""" """
#remove last duplicate group #remove last duplicate group
self.duplicateSongList.pop() self.duplicate_song_list.pop()
# remove all previous elements # remove all previous elements
for i in reversed(range(self.songsHorizontalLayout.count())): for i in reversed(range(self.songs_horizontal_layout.count())):
item = self.songsHorizontalLayout.itemAt(i) item = self.songs_horizontal_layout.itemAt(i)
if isinstance(item, QtGui.QWidgetItem): if isinstance(item, QtGui.QWidgetItem):
# the order is important here, if the .setParent(None) call is done before the .removeItem() call, a # The order is important here, if the .setParent(None) call is done
# segfault occurs # before the .removeItem() call, a segfault occurs.
widget = item.widget() widget = item.widget()
self.songsHorizontalLayout.removeItem(item) self.songs_horizontal_layout.removeItem(item)
widget.setParent(None) widget.setParent(None)
else: else:
self.songsHorizontalLayout.removeItem(item) self.songs_horizontal_layout.removeItem(item)
#process next set of duplicates #process next set of duplicates
self.processCurrentDuplicateEntry() self.processCurrentDuplicateEntry()
@ -329,23 +332,23 @@ class DuplicateSongRemovalForm(OpenLPWizard):
duplicate song group, hide the "next" button and show the "finish" button. duplicate song group, hide the "next" button and show the "finish" button.
""" """
# update counter # 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() self.updateReviewCounterText()
# add song elements to the UI # 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 # a stretch doesn't seem to stretch endlessly, so I add two to get enough stetch for 1400x1050
self.songsHorizontalLayout.addStretch() self.songs_horizontal_layout.addStretch()
self.songsHorizontalLayout.addStretch() self.songs_horizontal_layout.addStretch()
for duplicate in self.duplicateSongList[-1]: for duplicate in self.duplicate_song_list[-1]:
songReviewWidget = SongReviewWidget(self.reviewPage, duplicate) song_review_widget = SongReviewWidget(self.review_page, duplicate)
QtCore.QObject.connect(songReviewWidget, QtCore.QObject.connect(song_review_widget,
QtCore.SIGNAL(u'songRemoveButtonClicked(PyQt_PyObject)'), QtCore.SIGNAL(u'songRemoveButtonClicked(PyQt_PyObject)'),
self.removeButtonClicked) self.removeButtonClicked)
self.songsHorizontalLayout.addWidget(songReviewWidget) self.songs_horizontal_layout.addWidget(song_review_widget)
self.songsHorizontalLayout.addStretch() self.songs_horizontal_layout.addStretch()
self.songsHorizontalLayout.addStretch() self.songs_horizontal_layout.addStretch()
#change next button to finish button on last review #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).show()
self.button(QtGui.QWizard.FinishButton).setEnabled(True) self.button(QtGui.QWizard.FinishButton).setEnabled(True)
self.button(QtGui.QWizard.NextButton).hide() self.button(QtGui.QWizard.NextButton).hide()
@ -370,119 +373,119 @@ class SongReviewWidget(QtGui.QWidget):
self.song = song self.song = song
self.setupUi() self.setupUi()
self.retranslateUi() 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): def setupUi(self):
self.songVerticalLayout = QtGui.QVBoxLayout(self) self.song_vertical_layout = QtGui.QVBoxLayout(self)
self.songVerticalLayout.setObjectName(u'songVerticalLayout') self.song_vertical_layout.setObjectName(u'song_vertical_layout')
self.songGroupBox = QtGui.QGroupBox(self) self.song_group_box = QtGui.QGroupBox(self)
self.songGroupBox.setObjectName(u'songGroupBox') self.song_group_box.setObjectName(u'song_group_box')
self.songGroupBox.setMinimumWidth(300) self.song_group_box.setMinimumWidth(300)
self.songGroupBox.setMaximumWidth(300) self.song_group_box.setMaximumWidth(300)
self.songGroupBoxLayout = QtGui.QVBoxLayout(self.songGroupBox) self.song_group_box_layout = QtGui.QVBoxLayout(self.song_group_box)
self.songGroupBoxLayout.setObjectName(u'songGroupBoxLayout') self.song_group_box_layout.setObjectName(u'song_group_box_layout')
self.songInfoFormLayout = QtGui.QFormLayout() self.song_info_form_layout = QtGui.QFormLayout()
self.songInfoFormLayout.setObjectName(u'songInfoFormLayout') self.song_info_form_layout.setObjectName(u'song_info_form_layout')
#title #title
self.songTitleLabel = QtGui.QLabel(self) self.song_title_label = QtGui.QLabel(self)
self.songTitleLabel.setObjectName(u'songTitleLabel') self.song_title_label.setObjectName(u'song_title_label')
self.songInfoFormLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.songTitleLabel) self.song_info_form_layout.setWidget(0, QtGui.QFormLayout.LabelRole, self.song_title_label)
self.songTitleContent = QtGui.QLabel(self) self.song_title_content = QtGui.QLabel(self)
self.songTitleContent.setObjectName(u'songTitleContent') self.song_title_content.setObjectName(u'song_title_content')
self.songTitleContent.setText(self.song.title) self.song_title_content.setText(self.song.title)
self.songTitleContent.setWordWrap(True) self.song_title_content.setWordWrap(True)
self.songInfoFormLayout.setWidget(0, QtGui.QFormLayout.FieldRole, self.songTitleContent) self.song_info_form_layout.setWidget(0, QtGui.QFormLayout.FieldRole, self.song_title_content)
#alternate title #alternate title
self.songAlternateTitleLabel = QtGui.QLabel(self) self.song_alternate_title_label = QtGui.QLabel(self)
self.songAlternateTitleLabel.setObjectName(u'songAlternateTitleLabel') self.song_alternate_title_label.setObjectName(u'song_alternate_title_label')
self.songInfoFormLayout.setWidget(1, QtGui.QFormLayout.LabelRole, self.songAlternateTitleLabel) self.song_info_form_layout.setWidget(1, QtGui.QFormLayout.LabelRole, self.song_alternate_title_label)
self.songAlternateTitleContent = QtGui.QLabel(self) self.song_alternate_title_content = QtGui.QLabel(self)
self.songAlternateTitleContent.setObjectName(u'songAlternateTitleContent') self.song_alternate_title_content.setObjectName(u'song_alternate_title_content')
self.songAlternateTitleContent.setText(self.song.alternate_title) self.song_alternate_title_content.setText(self.song.alternate_title)
self.songAlternateTitleContent.setWordWrap(True) self.song_alternate_title_content.setWordWrap(True)
self.songInfoFormLayout.setWidget(1, QtGui.QFormLayout.FieldRole, self.songAlternateTitleContent) self.song_info_form_layout.setWidget(1, QtGui.QFormLayout.FieldRole, self.song_alternate_title_content)
#CCLI number #CCLI number
self.songCCLINumberLabel = QtGui.QLabel(self) self.song_ccli_number_label = QtGui.QLabel(self)
self.songCCLINumberLabel.setObjectName(u'songCCLINumberLabel') self.song_ccli_number_label.setObjectName(u'song_ccli_number_label')
self.songInfoFormLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.songCCLINumberLabel) self.song_info_form_layout.setWidget(2, QtGui.QFormLayout.LabelRole, self.song_ccli_number_label)
self.songCCLINumberContent = QtGui.QLabel(self) self.song_ccli_number_content = QtGui.QLabel(self)
self.songCCLINumberContent.setObjectName(u'songCCLINumberContent') self.song_ccli_number_content.setObjectName(u'song_ccli_number_content')
self.songCCLINumberContent.setText(self.song.ccli_number) self.song_ccli_number_content.setText(self.song.ccli_number)
self.songCCLINumberContent.setWordWrap(True) self.song_ccli_number_content.setWordWrap(True)
self.songInfoFormLayout.setWidget(2, QtGui.QFormLayout.FieldRole, self.songCCLINumberContent) self.song_info_form_layout.setWidget(2, QtGui.QFormLayout.FieldRole, self.song_ccli_number_content)
#copyright #copyright
self.songCopyrightLabel = QtGui.QLabel(self) self.song_copyright_label = QtGui.QLabel(self)
self.songCopyrightLabel.setObjectName(u'songCopyrightLabel') self.song_copyright_label.setObjectName(u'song_copyright_label')
self.songInfoFormLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.songCopyrightLabel) self.song_info_form_layout.setWidget(3, QtGui.QFormLayout.LabelRole, self.song_copyright_label)
self.songCopyrightContent = QtGui.QLabel(self) self.song_copyright_content = QtGui.QLabel(self)
self.songCopyrightContent.setObjectName(u'songCopyrightContent') self.song_copyright_content.setObjectName(u'song_copyright_content')
self.songCopyrightContent.setWordWrap(True) self.song_copyright_content.setWordWrap(True)
self.songCopyrightContent.setText(self.song.copyright) self.song_copyright_content.setText(self.song.copyright)
self.songInfoFormLayout.setWidget(3, QtGui.QFormLayout.FieldRole, self.songCopyrightContent) self.song_info_form_layout.setWidget(3, QtGui.QFormLayout.FieldRole, self.song_copyright_content)
#comments #comments
self.songCommentsLabel = QtGui.QLabel(self) self.song_comments_label = QtGui.QLabel(self)
self.songCommentsLabel.setObjectName(u'songCommentsLabel') self.song_comments_label.setObjectName(u'song_comments_label')
self.songInfoFormLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.songCommentsLabel) self.song_info_form_layout.setWidget(4, QtGui.QFormLayout.LabelRole, self.song_comments_label)
self.songCommentsContent = QtGui.QLabel(self) self.song_comments_content = QtGui.QLabel(self)
self.songCommentsContent.setObjectName(u'songCommentsContent') self.song_comments_content.setObjectName(u'song_comments_content')
self.songCommentsContent.setText(self.song.comments) self.song_comments_content.setText(self.song.comments)
self.songCommentsContent.setWordWrap(True) self.song_comments_content.setWordWrap(True)
self.songInfoFormLayout.setWidget(4, QtGui.QFormLayout.FieldRole, self.songCommentsContent) self.song_info_form_layout.setWidget(4, QtGui.QFormLayout.FieldRole, self.song_comments_content)
#authors #authors
self.songAuthorsLabel = QtGui.QLabel(self) self.song_authors_label = QtGui.QLabel(self)
self.songAuthorsLabel.setObjectName(u'songAuthorsLabel') self.song_authors_label.setObjectName(u'song_authors_label')
self.songInfoFormLayout.setWidget(5, QtGui.QFormLayout.LabelRole, self.songAuthorsLabel) self.song_info_form_layout.setWidget(5, QtGui.QFormLayout.LabelRole, self.song_authors_label)
self.songAuthorsContent = QtGui.QLabel(self) self.song_authors_content = QtGui.QLabel(self)
self.songAuthorsContent.setObjectName(u'songAuthorsContent') self.song_authors_content.setObjectName(u'song_authors_content')
self.songAuthorsContent.setWordWrap(True) self.song_authors_content.setWordWrap(True)
authorsText = u'' authors_text = u''
for author in self.song.authors: for author in self.song.authors:
authorsText += author.display_name + ', ' authors_text += author.display_name + ', '
if authorsText: if authors_text:
authorsText = authorsText[:-2] authors_text = authors_text[:-2]
self.songAuthorsContent.setText(authorsText) self.song_authors_content.setText(authors_text)
self.songInfoFormLayout.setWidget(5, QtGui.QFormLayout.FieldRole, self.songAuthorsContent) self.song_info_form_layout.setWidget(5, QtGui.QFormLayout.FieldRole, self.song_authors_content)
#verse order #verse order
self.songVerseOrderLabel = QtGui.QLabel(self) self.song_verse_order_label = QtGui.QLabel(self)
self.songVerseOrderLabel.setObjectName(u'songVerseOrderLabel') self.song_verse_order_label.setObjectName(u'song_verse_order_label')
self.songInfoFormLayout.setWidget(6, QtGui.QFormLayout.LabelRole, self.songVerseOrderLabel) self.song_info_form_layout.setWidget(6, QtGui.QFormLayout.LabelRole, self.song_verse_order_label)
self.songVerseOrderContent = QtGui.QLabel(self) self.song_verse_order_content = QtGui.QLabel(self)
self.songVerseOrderContent.setObjectName(u'songVerseOrderContent') self.song_verse_order_content.setObjectName(u'song_verse_order_content')
self.songVerseOrderContent.setText(self.song.verse_order) self.song_verse_order_content.setText(self.song.verse_order)
self.songVerseOrderContent.setWordWrap(True) self.song_verse_order_content.setWordWrap(True)
self.songInfoFormLayout.setWidget(6, QtGui.QFormLayout.FieldRole, self.songVerseOrderContent) self.song_info_form_layout.setWidget(6, QtGui.QFormLayout.FieldRole, self.song_verse_order_content)
#verses #verses
self.songGroupBoxLayout.addLayout(self.songInfoFormLayout) self.song_group_box_layout.addLayout(self.song_info_form_layout)
self.songInfoVerseGroupBox = QtGui.QGroupBox(self.songGroupBox) self.song_info_verse_group_box = QtGui.QGroupBox(self.song_group_box)
self.songInfoVerseGroupBox.setObjectName(u'songInfoVerseGroupBox') self.song_info_verse_group_box.setObjectName(u'song_info_verse_group_box')
self.songInfoVerseGroupBoxLayout = QtGui.QFormLayout(self.songInfoVerseGroupBox) self.song_info_verse_group_box_layout = QtGui.QFormLayout(self.song_info_verse_group_box)
songXml = SongXML() song_xml = SongXML()
verses = songXml.get_verses(self.song.lyrics) verses = song_xml.get_verses(self.song.lyrics)
for verse in verses: for verse in verses:
verseMarker = verse[0]['type'] + verse[0]['label'] verse_marker = verse[0]['type'] + verse[0]['label']
verseLabel = QtGui.QLabel(self.songInfoVerseGroupBox) verse_label = QtGui.QLabel(self.song_info_verse_group_box)
verseLabel.setText(verse[1]) verse_label.setText(verse[1])
verseLabel.setWordWrap(True) verse_label.setWordWrap(True)
self.songInfoVerseGroupBoxLayout.addRow(verseMarker, verseLabel) self.song_info_verse_group_box_layout.addRow(verse_marker, verse_label)
self.songGroupBoxLayout.addWidget(self.songInfoVerseGroupBox) self.song_group_box_layout.addWidget(self.song_info_verse_group_box)
self.songGroupBoxLayout.addStretch() self.song_group_box_layout.addStretch()
self.songVerticalLayout.addWidget(self.songGroupBox) self.song_vertical_layout.addWidget(self.song_group_box)
self.songRemoveButton = QtGui.QPushButton(self) self.song_remove_button = QtGui.QPushButton(self)
self.songRemoveButton.setObjectName(u'songRemoveButton') self.song_remove_button.setObjectName(u'song_remove_button')
self.songRemoveButton.setIcon(build_icon(u':/songs/song_delete.png')) self.song_remove_button.setIcon(build_icon(u':/songs/song_delete.png'))
self.songRemoveButton.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) self.song_remove_button.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
self.songVerticalLayout.addWidget(self.songRemoveButton, alignment = QtCore.Qt.AlignHCenter) self.song_vertical_layout.addWidget(self.song_remove_button, alignment = QtCore.Qt.AlignHCenter)
def retranslateUi(self): def retranslateUi(self):
self.songRemoveButton.setText(u'Remove') self.song_remove_button.setText(u'Remove')
self.songTitleLabel.setText(u'Title:') self.song_title_label.setText(u'Title:')
self.songAlternateTitleLabel.setText(u'Alternate Title:') self.song_alternate_title_label.setText(u'Alternate Title:')
self.songCCLINumberLabel.setText(u'CCLI Number:') self.song_ccli_number_label.setText(u'CCLI Number:')
self.songVerseOrderLabel.setText(u'Verse Order:') self.song_verse_order_label.setText(u'Verse Order:')
self.songCopyrightLabel.setText(u'Copyright:') self.song_copyright_label.setText(u'Copyright:')
self.songCommentsLabel.setText(u'Comments:') self.song_comments_label.setText(u'Comments:')
self.songAuthorsLabel.setText(u'Authors:') self.song_authors_label.setText(u'Authors:')
self.songInfoVerseGroupBox.setTitle(u'Verses') self.song_info_verse_group_box.setTitle(u'Verses')
def onRemoveButtonClicked(self): def onRemoveButtonClicked(self):
""" """

View File

@ -38,23 +38,23 @@ class DuplicateSongFinder(object):
The algorithm is based on the diff algorithm. The algorithm is based on the diff algorithm.
First a diffset is calculated for two songs. First a diffset is calculated for two songs.
To compensate for typos all differences that are smaller than a To compensate for typos all differences that are smaller than a
limit (<maxTypoSize) and are surrounded by larger equal blocks limit (<max_typo_size) and are surrounded by larger equal blocks
(>minFragmentSize) are removed and the surrounding equal parts are merged. (>min_fragment_size) are removed and the surrounding equal parts are merged.
Finally two conditions can qualify a song tuple to be a duplicate: 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 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. 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. 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 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): def __init__(self):
self.minFragmentSize = 5 self.min_fragment_size = 5
self.minBlockSize = 70 self.min_block_size = 70
self.maxTypoSize = 3 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. Calculate and return whether two songs are probably equal.
@ -72,15 +72,15 @@ class DuplicateSongFinder(object):
large = song1.search_lyrics large = song1.search_lyrics
differ = difflib.SequenceMatcher(a=large, b=small) differ = difflib.SequenceMatcher(a=large, b=small)
diff_tuples = differ.get_opcodes() diff_tuples = differ.get_opcodes()
diff_no_typos = self.__removeTypos(diff_tuples) diff_no_typos = self.__remove_typos(diff_tuples)
#print(diff_no_typos) #print(diff_no_typos)
if self.__lengthOfEqualBlocks(diff_no_typos) >= self.minBlockSize or \ if self.__length_of_equal_blocks(diff_no_typos) >= self.min_block_size or \
self.__lengthOfLongestEqualBlock(diff_no_typos) > len(small) * 2 / 3: self.__length_of_longest_equal_block(diff_no_typos) > len(small) * 2 / 3:
return True return True
else: else:
return False return False
def __opLength(self, opcode): def __op_length(self, opcode):
""" """
Return the length of a given difference. Return the length of a given difference.
@ -89,57 +89,57 @@ class DuplicateSongFinder(object):
""" """
return max(opcode[2] - opcode[1], opcode[4] - opcode[3]) 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 (<maxTypoSize) Remove typos from a diff set. A typo is a small difference (<max_typo_size)
surrounded by larger equal passages (>minFragmentSize). surrounded by larger equal passages (>min_fragment_size).
``diff`` ``diff``
The diff set to remove the typos from. The diff set to remove the typos from.
""" """
#remove typo at beginning of string #remove typo at beginning of string
if len(diff) >= 2: if len(diff) >= 2:
if diff[0][0] != "equal" and self.__opLength(diff[0]) <= self.maxTypoSize and \ if diff[0][0] != "equal" and self.__op_length(diff[0]) <= self.max_typo_size and \
self.__opLength(diff[1]) >= self.minFragmentSize: self.__op_length(diff[1]) >= self.min_fragment_size:
del diff[0] del diff[0]
#remove typos in the middle of string #remove typos in the middle of string
if len(diff) >= 3: if len(diff) >= 3:
for index in range(len(diff) - 3, -1, -1): for index in range(len(diff) - 3, -1, -1):
if self.__opLength(diff[index]) >= self.minFragmentSize and \ if self.__op_length(diff[index]) >= self.min_fragment_size and \
diff[index + 1][0] != "equal" and self.__opLength(diff[index + 1]) <= self.maxTypoSize and \ diff[index + 1][0] != "equal" and self.__op_length(diff[index + 1]) <= self.max_typo_size and \
self.__opLength(diff[index + 2]) >= self.minFragmentSize: self.__op_length(diff[index + 2]) >= self.min_fragment_size:
del diff[index + 1] del diff[index + 1]
#remove typo at the end of string #remove typo at the end of string
if len(diff) >= 2: if len(diff) >= 2:
if self.__opLength(diff[-2]) >= self.minFragmentSize and \ if self.__op_length(diff[-2]) >= self.min_fragment_size and \
diff[-1][0] != "equal" and self.__opLength(diff[-1]) <= self.maxTypoSize: diff[-1][0] != "equal" and self.__op_length(diff[-1]) <= self.max_typo_size:
del diff[-1] del diff[-1]
#merge fragments #merge fragments
for index in range(len(diff) - 2, -1, -1): for index in range(len(diff) - 2, -1, -1):
if diff[index][0] == "equal" and self.__opLength(diff[index]) >= self.minFragmentSize and \ if diff[index][0] == "equal" and self.__op_length(diff[index]) >= self.min_fragment_size and \
diff[index + 1][0] == "equal" and self.__opLength(diff[index + 1]) >= self.minFragmentSize: 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] = ("equal", diff[index][1], diff[index + 1][2], diff[index][3],
diff[index + 1][4]) diff[index + 1][4])
del diff[index + 1] del diff[index + 1]
return diff 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. 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`` ``diff``
The diff set to return the length for. The diff set to return the length for.
""" """
length = 0 length = 0
for element in diff: for element in diff:
if element[0] == "equal" and self.__opLength(element) >= self.minBlockSize: if element[0] == "equal" and self.__op_length(element) >= self.min_block_size:
length += self.__opLength(element) length += self.__op_length(element)
return length 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. Return the length of the largest equal block in a diff set.
@ -148,7 +148,7 @@ class DuplicateSongFinder(object):
""" """
length = 0 length = 0
for element in diff: for element in diff:
if element[0] == "equal" and self.__opLength(element) > length: if element[0] == "equal" and self.__op_length(element) > length:
length = self.__opLength(element) length = self.__op_length(element)
return length return length

View File

@ -37,27 +37,22 @@ class TestLib(TestCase):
def songs_probably_equal_test(self): 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 full_lyrics =u'''amazing grace how sweet the sound that saved a wretch like me i once was lost but now am
blind but now i see twas grace that taught my heart to fear and grace my fears relieved how precious did that grace found was blind but now i see twas grace that taught my heart to fear and grace my fears relieved how
appear the hour i first believed through many dangers toils and snares i have already come tis grace that brought precious did that grace appear the hour i first believed through many dangers toils and snares i have already
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 come tis grace that brought me safe thus far and grace will lead me home'''
shield and portion be as long as life endures yea when this flesh and heart shall fail and mortal life shall cease short_lyrics =u'''twas grace that taught my heart to fear and grace my fears relieved how precious did that
i shall possess within the veil a life of joy and peace when weve been here ten thousand years bright shining as grace appear the hour i first believed'''
the sun weve no less days to sing gods praise than when weve first begun''' error_lyrics =u'''amazing how sweet the trumpet that saved a wrench like me i once was losst but now am
short_lyrics =u'''twas grace that taught my heart to fear and grace my fears relieved how precious did that grace found waf blind but now i see it was grace that taught my heart to fear and grace my fears relieved how
appear the hour i first believed''' precious did that grace appppppppear the hour i first believedxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx snares i have
error_lyrics =u'''amazing grace how sweet the sound that saved a wretch like me i once was lost but now am found was already come to this grace that brought me safe so far and grace will lead me home'''
blind but now i see twas grace that taught my heart to fear and grace my fears relieved how precious did that grace different_lyrics=u'''on a hill far away stood an old rugged cross the emblem of suffering and shame and i love
appear the hour i first believedxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx snares i have already come tis grace that brought that old cross where the dearest and best for a world of lost sinners was slain so ill cherish the old rugged
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 cross till my trophies at last i lay down i will cling to the old rugged cross and exchange it some day for a
shield andwhen this flcsh and heart shall fail and mortal life shall cease crown'''
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'''
dsf = DuplicateSongFinder() dsf = DuplicateSongFinder()
song1 = MagicMock() song1 = MagicMock()
song2 = MagicMock() song2 = MagicMock()
@ -67,7 +62,7 @@ class TestLib(TestCase):
song2.search_lyrics = full_lyrics song2.search_lyrics = full_lyrics
#WHEN: We compare those songs for equality #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 #THEN: The result should be True
assert result is True, u'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 song2.search_lyrics = short_lyrics
#WHEN: We compare those songs for equality #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 #THEN: The result should be True
assert result is True, u'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 song2.search_lyrics = error_lyrics
#WHEN: We compare those songs for equality #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 #THEN: The result should be True
assert result is True, u'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 song2.search_lyrics = different_lyrics
#WHEN: We compare those songs for equality #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 #THEN: The result should be False
assert result is False, u'The result should be False' assert result is False, u'The result should be False'