Merge lp:~trb143/openlp/bug-772523 into lp:openlp

Proposed by Tim Bentley
Status: Merged
Merged at revision: 1833
Proposed branch: lp:~trb143/openlp/bug-772523
Merge into: lp:openlp
Diff against target: 349 lines (+91/-21)
11 files modified
openlp/core/lib/db.py (+8/-1)
openlp/core/lib/eventreceiver.py (+4/-0)
openlp/core/lib/serviceitem.py (+2/-0)
openlp/core/ui/maindisplay.py (+2/-2)
openlp/core/ui/servicemanager.py (+25/-6)
openlp/plugins/songs/forms/songexportform.py (+3/-0)
openlp/plugins/songs/lib/db.py (+2/-1)
openlp/plugins/songs/lib/mediaitem.py (+15/-3)
openlp/plugins/songs/lib/upgrade.py (+12/-1)
openlp/plugins/songs/lib/xml.py (+6/-7)
openlp/plugins/songs/songsplugin.py (+12/-0)
To merge this branch: bzr merge lp:~trb143/openlp/bug-772523
Reviewer Review Type Date Requested Status
Raoul Snyman Approve
Andreas Preikschat Pending
Review via email: mp+85370@code.launchpad.net

This proposal supersedes a proposal from 2011-12-12.

Description of the change

This patch changes your song database so back it up!

When a service is imported at present and we do not wish to save the songs we do not. This prevents the user from editing songs.
This change adds a "temporary" flag on the song database to allow those songs to be save and edited bit not via the plugin as the search will not show them.
On exit the songs will be deleted.

The code works until a service is loaded and then you get sqlalchemy errors.
I have no idea what is wrong but some help would be appreciated.

Amended code to add songs as per my email.

The code does set a correct value on existing databases.

To post a comment you must log in.
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Well, first remove your print statement.

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

You will need to set the default of the new column, something like this:

110 Column(u'temporary', types.Boolean(), default=False)\
111 .create(table=tables[u'songs'])
        connection = metadata.bind.connect()
        update_statement = tables[u'songs'].update().values(temporary=False)
        connection.execute(update_statement)

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Are you going to add the setting of the default value of the new column?

review: Needs Information
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

This looks fine, I'll approve it, but I haven't tested it.

review: Approve
Revision history for this message
Andreas Preikschat (googol-deactivatedaccount) wrote : Posted in a previous version of this proposal

1) delete your db
2) Open a service file ("Add missing songs ..." + "Update service...." enabled)
3) Disable "Add missing songs ..."
4) Restart OpenLP
5) Open the same service file
6) Edit the song in the media manager.

Result: Song in service manager is not updated.

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

This isn't all your code, but I did just need to point it out...

    editId, self.loadItem_uuid, temporary = message.split(u':')
    self.loadItem_editId = int(editId)
    self.loadItem_temporary = str_to_bool(temporary)

Um, two naming conventions in the same variable? It should either be self.load_item_edit_id or self.loadItemEditId. Or if you need a bunch of related things, what about a dictionary?

Also, is that first line backwards compatible with existing service files?

review: Needs Fixing
Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Fully backwardly comparable.
I will do a rename tonight and resubmit.

Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

158 + self.load_item_editId = int(editId)

Sorry, this one still has that "editId" instead of "edit_id".

review: Needs Fixing
Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp/core/lib/db.py'
--- openlp/core/lib/db.py 2011-12-03 12:51:40 +0000
+++ openlp/core/lib/db.py 2011-12-12 18:05:26 +0000
@@ -358,10 +358,17 @@
358358
359 def delete_all_objects(self, object_class, filter_clause=None):359 def delete_all_objects(self, object_class, filter_clause=None):
360 """360 """
361 Delete all object records361 Delete all object records.
362 This method should only be used for simple tables and not ones with
363 relationships. The relationships are not deleted from the database and
364 this will lead to database corruptions.
362365
363 ``object_class``366 ``object_class``
364 The type of object to delete367 The type of object to delete
368
369 ``filter_clause``
370 The filter governing selection of objects to return. Defaults to
371 None.
365 """372 """
366 try:373 try:
367 query = self.session.query(object_class)374 query = self.session.query(object_class)
368375
=== modified file 'openlp/core/lib/eventreceiver.py'
--- openlp/core/lib/eventreceiver.py 2011-10-22 11:28:47 +0000
+++ openlp/core/lib/eventreceiver.py 2011-12-12 18:05:26 +0000
@@ -115,8 +115,12 @@
115 ``slidecontroller_live_stop_loop``115 ``slidecontroller_live_stop_loop``
116 Stop the loop on the main display.116 Stop the loop on the main display.
117117
118
118 **Servicemanager related signals**119 **Servicemanager related signals**
119120
121 ``servicemanager_new_service``
122 A new service is being loaded or created.
123
120 ``servicemanager_previous_item``124 ``servicemanager_previous_item``
121 Display the previous item in the service.125 Display the previous item in the service.
122126
123127
=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py 2011-11-13 08:57:29 +0000
+++ openlp/core/lib/serviceitem.py 2011-12-12 18:05:26 +0000
@@ -119,6 +119,7 @@
119 self.image_border = u'#000000'119 self.image_border = u'#000000'
120 self.background_audio = []120 self.background_audio = []
121 self.theme_overwritten = False121 self.theme_overwritten = False
122 self.temporary_edit = False
122 self._new_item()123 self._new_item()
123124
124 def _new_item(self):125 def _new_item(self):
@@ -365,6 +366,7 @@
365 """366 """
366 self._uuid = other._uuid367 self._uuid = other._uuid
367 self.notes = other.notes368 self.notes = other.notes
369 self.temporary_edit = other.temporary_edit
368 # Copy theme over if present.370 # Copy theme over if present.
369 if other.theme is not None:371 if other.theme is not None:
370 self.theme = other.theme372 self.theme = other.theme
371373
=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py 2011-12-02 20:17:57 +0000
+++ openlp/core/ui/maindisplay.py 2011-12-12 18:05:26 +0000
@@ -45,7 +45,7 @@
4545
46class Display(QtGui.QGraphicsView):46class Display(QtGui.QGraphicsView):
47 """47 """
48 This is a general display screen class. Here the general display settings 48 This is a general display screen class. Here the general display settings
49 will done. It will be used as specialized classes by Main Display and49 will done. It will be used as specialized classes by Main Display and
50 Preview display.50 Preview display.
51 """51 """
@@ -66,7 +66,7 @@
66 """66 """
67 Set up and build the screen base67 Set up and build the screen base
68 """68 """
69 log.debug(u'Start Display base setup (live = %s)' % self.isLive) 69 log.debug(u'Start Display base setup (live = %s)' % self.isLive)
70 self.setGeometry(self.screen[u'size'])70 self.setGeometry(self.screen[u'size'])
71 log.debug(u'Setup webView')71 log.debug(u'Setup webView')
72 self.webView = QtWebKit.QWebView(self)72 self.webView = QtWebKit.QWebView(self)
7373
=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py 2011-12-09 11:50:25 +0000
+++ openlp/core/ui/servicemanager.py 2011-12-12 18:05:26 +0000
@@ -37,7 +37,7 @@
37from PyQt4 import QtCore, QtGui37from PyQt4 import QtCore, QtGui
3838
39from openlp.core.lib import OpenLPToolbar, ServiceItem, Receiver, build_icon, \39from openlp.core.lib import OpenLPToolbar, ServiceItem, Receiver, build_icon, \
40 ItemCapabilities, SettingsManager, translate40 ItemCapabilities, SettingsManager, translate, str_to_bool
41from openlp.core.lib.theme import ThemeLevel41from openlp.core.lib.theme import ThemeLevel
42from openlp.core.lib.ui import UiStrings, critical_error_message_box, \42from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
43 context_menu_action, context_menu_separator, find_and_set_in_combo_box43 context_menu_action, context_menu_separator, find_and_set_in_combo_box
@@ -465,6 +465,7 @@
465 self.setModified(False)465 self.setModified(False)
466 QtCore.QSettings(). \466 QtCore.QSettings(). \
467 setValue(u'servicemanager/last file',QtCore.QVariant(u''))467 setValue(u'servicemanager/last file',QtCore.QVariant(u''))
468 Receiver.send_message(u'servicemanager_new_service')
468469
469 def saveFile(self):470 def saveFile(self):
470 """471 """
@@ -663,13 +664,14 @@
663 serviceItem.renderer = self.mainwindow.renderer664 serviceItem.renderer = self.mainwindow.renderer
664 serviceItem.set_from_service(item, self.servicePath)665 serviceItem.set_from_service(item, self.servicePath)
665 self.validateItem(serviceItem)666 self.validateItem(serviceItem)
666 self.loadItem_uuid = 0667 self.load_item_uuid = 0
667 if serviceItem.is_capable(ItemCapabilities.OnLoadUpdate):668 if serviceItem.is_capable(ItemCapabilities.OnLoadUpdate):
668 Receiver.send_message(u'%s_service_load' %669 Receiver.send_message(u'%s_service_load' %
669 serviceItem.name.lower(), serviceItem)670 serviceItem.name.lower(), serviceItem)
670 # if the item has been processed671 # if the item has been processed
671 if serviceItem._uuid == self.loadItem_uuid:672 if serviceItem._uuid == self.load_item_uuid:
672 serviceItem.edit_id = int(self.loadItem_editId)673 serviceItem.edit_id = int(self.load_item_edit_id)
674 serviceItem.temporary_edit = self.load_item_temporary
673 self.addServiceItem(serviceItem, repaint=False)675 self.addServiceItem(serviceItem, repaint=False)
674 delete_file(p_file)676 delete_file(p_file)
675 self.setFileName(fileName)677 self.setFileName(fileName)
@@ -999,6 +1001,17 @@
999 painter.drawImage(0, 0, overlay)1001 painter.drawImage(0, 0, overlay)
1000 painter.end()1002 painter.end()
1001 treewidgetitem.setIcon(0, build_icon(icon))1003 treewidgetitem.setIcon(0, build_icon(icon))
1004 elif serviceitem.temporary_edit:
1005 icon = QtGui.QImage(serviceitem.icon)
1006 icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio,
1007 QtCore.Qt.SmoothTransformation)
1008 overlay = QtGui.QImage(':/general/general_export.png')
1009 overlay = overlay.scaled(40, 40, QtCore.Qt.KeepAspectRatio,
1010 QtCore.Qt.SmoothTransformation)
1011 painter = QtGui.QPainter(icon)
1012 painter.drawImage(40, 0, overlay)
1013 painter.end()
1014 treewidgetitem.setIcon(0, build_icon(icon))
1002 else:1015 else:
1003 treewidgetitem.setIcon(0, serviceitem.iconic_representation)1016 treewidgetitem.setIcon(0, serviceitem.iconic_representation)
1004 else:1017 else:
@@ -1006,6 +1019,11 @@
1006 build_icon(u':/general/general_delete.png'))1019 build_icon(u':/general/general_delete.png'))
1007 treewidgetitem.setText(0, serviceitem.get_display_title())1020 treewidgetitem.setText(0, serviceitem.get_display_title())
1008 tips = []1021 tips = []
1022 if serviceitem.temporary_edit:
1023 tips.append(u'<strong>%s:</strong> <em>%s</em>' %
1024 (unicode(translate('OpenLP.ServiceManager', 'Edit')),
1025 (unicode(translate('OpenLP.ServiceManager',
1026 'Service copy only')))))
1009 if serviceitem.theme and serviceitem.theme != -1:1027 if serviceitem.theme and serviceitem.theme != -1:
1010 tips.append(u'<strong>%s:</strong> <em>%s</em>' %1028 tips.append(u'<strong>%s:</strong> <em>%s</em>' %
1011 (unicode(translate('OpenLP.ServiceManager', 'Slide theme')),1029 (unicode(translate('OpenLP.ServiceManager', 'Slide theme')),
@@ -1127,8 +1145,9 @@
1127 Triggered from plugins to update service items.1145 Triggered from plugins to update service items.
1128 Save the values as they will be used as part of the service load1146 Save the values as they will be used as part of the service load
1129 """1147 """
1130 editId, self.loadItem_uuid = message.split(u':')1148 edit_id, self.load_item_uuid, temporary = message.split(u':')
1131 self.loadItem_editId = int(editId)1149 self.load_item_edit_id = int(edit_id)
1150 self.load_item_temporary = str_to_bool(temporary)
11321151
1133 def replaceServiceItem(self, newItem):1152 def replaceServiceItem(self, newItem):
1134 """1153 """
11351154
=== modified file 'openlp/plugins/songs/forms/songexportform.py'
--- openlp/plugins/songs/forms/songexportform.py 2011-08-26 15:48:58 +0000
+++ openlp/plugins/songs/forms/songexportform.py 2011-12-12 18:05:26 +0000
@@ -252,6 +252,9 @@
252 songs = self.plugin.manager.get_all_objects(Song)252 songs = self.plugin.manager.get_all_objects(Song)
253 songs.sort(cmp=locale.strcoll, key=lambda song: song.title.lower())253 songs.sort(cmp=locale.strcoll, key=lambda song: song.title.lower())
254 for song in songs:254 for song in songs:
255 # No need to export temporary songs.
256 if song.temporary:
257 continue
255 authors = u', '.join([author.display_name258 authors = u', '.join([author.display_name
256 for author in song.authors])259 for author in song.authors])
257 title = u'%s (%s)' % (unicode(song.title), authors)260 title = u'%s (%s)' % (unicode(song.title), authors)
258261
=== modified file 'openlp/plugins/songs/lib/db.py'
--- openlp/plugins/songs/lib/db.py 2011-08-26 23:04:54 +0000
+++ openlp/plugins/songs/lib/db.py 2011-12-12 18:05:26 +0000
@@ -199,7 +199,8 @@
199 Column(u'search_lyrics', types.UnicodeText, nullable=False),199 Column(u'search_lyrics', types.UnicodeText, nullable=False),
200 Column(u'create_date', types.DateTime(), default=func.now()),200 Column(u'create_date', types.DateTime(), default=func.now()),
201 Column(u'last_modified', types.DateTime(), default=func.now(),201 Column(u'last_modified', types.DateTime(), default=func.now(),
202 onupdate=func.now())202 onupdate=func.now()),
203 Column(u'temporary', types.Boolean(), default=False)
203 )204 )
204205
205 # Definition of the "topics" table206 # Definition of the "topics" table
206207
=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py 2011-11-24 22:34:27 +0000
+++ openlp/plugins/songs/lib/mediaitem.py 2011-12-12 18:05:26 +0000
@@ -270,6 +270,9 @@
270 searchresults.sort(270 searchresults.sort(
271 cmp=locale.strcoll, key=lambda song: song.title.lower())271 cmp=locale.strcoll, key=lambda song: song.title.lower())
272 for song in searchresults:272 for song in searchresults:
273 # Do not display temporary songs
274 if song.temporary:
275 continue
273 author_list = [author.display_name for author in song.authors]276 author_list = [author.display_name for author in song.authors]
274 song_title = unicode(song.title)277 song_title = unicode(song.title)
275 song_detail = u'%s (%s)' % (song_title, u', '.join(author_list))278 song_detail = u'%s (%s)' % (song_title, u', '.join(author_list))
@@ -286,6 +289,9 @@
286 self.listView.clear()289 self.listView.clear()
287 for author in searchresults:290 for author in searchresults:
288 for song in author.songs:291 for song in author.songs:
292 # Do not display temporary songs
293 if song.temporary:
294 continue
289 song_detail = u'%s (%s)' % (author.display_name, song.title)295 song_detail = u'%s (%s)' % (author.display_name, song.title)
290 song_name = QtGui.QListWidgetItem(song_detail)296 song_name = QtGui.QListWidgetItem(song_detail)
291 song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song.id))297 song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song.id))
@@ -534,6 +540,7 @@
534 Song.search_title.asc())540 Song.search_title.asc())
535 editId = 0541 editId = 0
536 add_song = True542 add_song = True
543 temporary = False
537 if search_results:544 if search_results:
538 for song in search_results:545 for song in search_results:
539 author_list = item.data_string[u'authors']546 author_list = item.data_string[u'authors']
@@ -559,13 +566,18 @@
559 self._updateBackgroundAudio(song, item)566 self._updateBackgroundAudio(song, item)
560 editId = song.id567 editId = song.id
561 self.onSearchTextButtonClick()568 self.onSearchTextButtonClick()
562 else:569 elif add_song and not self.addSongFromService:
563 # Make sure we temporary import formatting tags.570 # Make sure we temporary import formatting tags.
564 self.openLyrics.xml_to_song(item.xml_version, True)571 song = self.openLyrics.xml_to_song(item.xml_version, True)
572 # If there's any backing tracks, copy them over.
573 if len(item.background_audio) > 0:
574 self._updateBackgroundAudio(song, item)
575 editId = song.id
576 temporary = True
565 # Update service with correct song id.577 # Update service with correct song id.
566 if editId:578 if editId:
567 Receiver.send_message(u'service_item_update',579 Receiver.send_message(u'service_item_update',
568 u'%s:%s' % (editId, item._uuid))580 u'%s:%s:%s' % (editId, item._uuid, temporary))
569581
570 def search(self, string):582 def search(self, string):
571 """583 """
572584
=== modified file 'openlp/plugins/songs/lib/upgrade.py'
--- openlp/plugins/songs/lib/upgrade.py 2011-12-02 20:17:57 +0000
+++ openlp/plugins/songs/lib/upgrade.py 2011-12-12 18:05:26 +0000
@@ -33,7 +33,9 @@
33from sqlalchemy.sql.expression import func33from sqlalchemy.sql.expression import func
34from migrate.changeset.constraint import ForeignKeyConstraint34from migrate.changeset.constraint import ForeignKeyConstraint
3535
36__version__ = 236from openlp.plugins.songs.lib.db import Song
37
38__version__ = 3
3739
38def upgrade_setup(metadata):40def upgrade_setup(metadata):
39 """41 """
@@ -86,3 +88,12 @@
86 Column(u'last_modified', types.DateTime(), default=func.now())\88 Column(u'last_modified', types.DateTime(), default=func.now())\
87 .create(table=tables[u'songs'])89 .create(table=tables[u'songs'])
8890
91def upgrade_3(session, metadata, tables):
92 """
93 Version 3 upgrade.
94
95 This upgrade adds a temporary song flag to the songs table
96 """
97 Column(u'temporary', types.Boolean(), default=False)\
98 .create(table=tables[u'songs'])
99
89100
=== modified file 'openlp/plugins/songs/lib/xml.py'
--- openlp/plugins/songs/lib/xml.py 2011-10-09 19:51:44 +0000
+++ openlp/plugins/songs/lib/xml.py 2011-12-12 18:05:26 +0000
@@ -346,7 +346,7 @@
346 lines_element.set(u'break', u'optional')346 lines_element.set(u'break', u'optional')
347 return self._extract_xml(song_xml)347 return self._extract_xml(song_xml)
348348
349 def xml_to_song(self, xml, parse_and_not_save=False):349 def xml_to_song(self, xml, parse_and_temporary_save=False):
350 """350 """
351 Create and save a song from OpenLyrics format xml to the database. Since351 Create and save a song from OpenLyrics format xml to the database. Since
352 we also export XML from external sources (e. g. OpenLyrics import), we352 we also export XML from external sources (e. g. OpenLyrics import), we
@@ -355,9 +355,9 @@
355 ``xml``355 ``xml``
356 The XML to parse (unicode).356 The XML to parse (unicode).
357357
358 ``parse_and_not_save``358 ``parse_and_temporary_save``
359 Switch to skip processing the whole song and to prevent storing the359 Switch to skip processing the whole song and storing the songs in
360 songs to the database. Defaults to ``False``.360 the database with a temporary flag. Defaults to ``False``.
361 """361 """
362 # No xml get out of here.362 # No xml get out of here.
363 if not xml:363 if not xml:
@@ -371,14 +371,13 @@
371 return None371 return None
372 # Formatting tags are new in OpenLyrics 0.8372 # Formatting tags are new in OpenLyrics 0.8
373 if float(song_xml.get(u'version')) > 0.7:373 if float(song_xml.get(u'version')) > 0.7:
374 self._process_formatting_tags(song_xml, parse_and_not_save)374 self._process_formatting_tags(song_xml, parse_and_temporary_save)
375 if parse_and_not_save:
376 return
377 song = Song()375 song = Song()
378 # Values will be set when cleaning the song.376 # Values will be set when cleaning the song.
379 song.search_lyrics = u''377 song.search_lyrics = u''
380 song.verse_order = u''378 song.verse_order = u''
381 song.search_title = u''379 song.search_title = u''
380 song.temporary = parse_and_temporary_save
382 self._process_copyright(properties, song)381 self._process_copyright(properties, song)
383 self._process_cclinumber(properties, song)382 self._process_cclinumber(properties, song)
384 self._process_titles(properties, song)383 self._process_titles(properties, song)
385384
=== modified file 'openlp/plugins/songs/songsplugin.py'
--- openlp/plugins/songs/songsplugin.py 2011-12-09 12:35:18 +0000
+++ openlp/plugins/songs/songsplugin.py 2011-12-12 18:05:26 +0000
@@ -78,6 +78,10 @@
78 action_list.add_action(self.songExportItem, unicode(UiStrings().Export))78 action_list.add_action(self.songExportItem, unicode(UiStrings().Export))
79 action_list.add_action(self.toolsReindexItem,79 action_list.add_action(self.toolsReindexItem,
80 unicode(UiStrings().Tools))80 unicode(UiStrings().Tools))
81 QtCore.QObject.connect(Receiver.get_receiver(),
82 QtCore.SIGNAL(u'servicemanager_new_service'),
83 self.clearTemporarySongs)
84
8185
82 def addImportMenuItem(self, import_menu):86 def addImportMenuItem(self, import_menu):
83 """87 """
@@ -265,6 +269,8 @@
265 Time to tidy up on exit269 Time to tidy up on exit
266 """270 """
267 log.info(u'Songs Finalising')271 log.info(u'Songs Finalising')
272 self.clearTemporarySongs()
273 # Clean up files and connections
268 self.manager.finalise()274 self.manager.finalise()
269 self.songImportItem.setVisible(False)275 self.songImportItem.setVisible(False)
270 self.songExportItem.setVisible(False)276 self.songExportItem.setVisible(False)
@@ -277,3 +283,9 @@
277 action_list.remove_action(self.toolsReindexItem,283 action_list.remove_action(self.toolsReindexItem,
278 unicode(UiStrings().Tools))284 unicode(UiStrings().Tools))
279 Plugin.finalise(self)285 Plugin.finalise(self)
286
287 def clearTemporarySongs(self):
288 # Remove temporary songs
289 songs = self.manager.get_all_objects(Song, Song.temporary == True)
290 for song in songs:
291 self.manager.delete_object(Song, song.id)