This commit is contained in:
Tim Bentley 2012-07-22 06:21:46 +01:00
commit 5881af9151
24 changed files with 466 additions and 85 deletions

View File

@ -208,6 +208,20 @@ class OpenLP(QtGui.QApplication):
return QtGui.QApplication.event(self, event)
def set_up_logging(log_path):
"""
Setup our logging using log_path
"""
check_directory_exists(log_path)
filename = os.path.join(log_path, u'openlp.log')
logfile = logging.FileHandler(filename, u'w')
logfile.setFormatter(logging.Formatter(
u'%(asctime)s %(name)-55s %(levelname)-8s %(message)s'))
log.addHandler(logfile)
if log.isEnabledFor(logging.DEBUG):
print 'Logging to:', filename
def main(args=None):
"""
The main function which parses command line options and then runs
@ -231,21 +245,19 @@ def main(args=None):
help='Set the Qt4 style (passed directly to Qt4).')
parser.add_option('--testing', dest='testing',
action='store_true', help='Run by testing framework')
# Set up logging
log_path = AppLocation.get_directory(AppLocation.CacheDir)
check_directory_exists(log_path)
filename = os.path.join(log_path, u'openlp.log')
logfile = logging.FileHandler(filename, u'w')
logfile.setFormatter(logging.Formatter(
u'%(asctime)s %(name)-55s %(levelname)-8s %(message)s'))
log.addHandler(logfile)
# Parse command line options and deal with them.
# Use args supplied programatically if possible.
(options, args) = parser.parse_args(args) if args else parser.parse_args()
if options.portable:
app_path = AppLocation.get_directory(AppLocation.AppDir)
set_up_logging(os.path.abspath(os.path.join(app_path, u'..',
u'..', u'Other')))
log.info(u'Running portable')
else:
set_up_logging(AppLocation.get_directory(AppLocation.CacheDir))
qt_args = []
if options.loglevel.lower() in ['d', 'debug']:
log.setLevel(logging.DEBUG)
print 'Logging to:', filename
elif options.loglevel.lower() in ['w', 'warning']:
log.setLevel(logging.WARNING)
else:
@ -261,11 +273,9 @@ def main(args=None):
app.setOrganizationName(u'OpenLP')
app.setOrganizationDomain(u'openlp.org')
if options.portable:
log.info(u'Running portable')
app.setApplicationName(u'OpenLPPortable')
Settings.setDefaultFormat(Settings.IniFormat)
# Get location OpenLPPortable.ini
app_path = AppLocation.get_directory(AppLocation.AppDir)
portable_settings_file = os.path.abspath(os.path.join(app_path, u'..',
u'..', u'Data', u'OpenLP.ini'))
# Make this our settings file

View File

@ -545,6 +545,10 @@ class AdvancedTab(SettingsTab):
self.currentDataPath))
self.defaultColorButton.setStyleSheet(
u'background-color: %s' % self.defaultColor)
# Don't allow data directory move if running portable.
if Settings().value(u'advanced/is portable',
QtCore.QVariant(False)).toBool():
self.dataDirectoryGroupBox.hide()
def save(self):
"""

View File

@ -130,15 +130,15 @@ class Ui_FormattingTagDialog(object):
translate('OpenLP.FormattingTagDialog', 'Description'))
self.tagLabel.setText(translate('OpenLP.FormattingTagDialog', 'Tag'))
self.startTagLabel.setText(
translate('OpenLP.FormattingTagDialog', 'Start tag'))
translate('OpenLP.FormattingTagDialog', 'Start HTML'))
self.endTagLabel.setText(
translate('OpenLP.FormattingTagDialog', 'End tag'))
translate('OpenLP.FormattingTagDialog', 'End HTML'))
self.deletePushButton.setText(UiStrings().Delete)
self.newPushButton.setText(UiStrings().New)
self.tagTableWidget.horizontalHeaderItem(0).setText(
translate('OpenLP.FormattingTagDialog', 'Description'))
self.tagTableWidget.horizontalHeaderItem(1).setText(
translate('OpenLP.FormattingTagDialog', 'Tag Id'))
translate('OpenLP.FormattingTagDialog', 'Tag'))
self.tagTableWidget.horizontalHeaderItem(2).setText(
translate('OpenLP.FormattingTagDialog', 'Start HTML'))
self.tagTableWidget.horizontalHeaderItem(3).setText(

View File

@ -49,7 +49,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
QtGui.QDialog.__init__(self, parent)
self.setupUi(self)
QtCore.QObject.connect(self.tagTableWidget,
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onRowSelected)
QtCore.SIGNAL(u'itemSelectionChanged()'),self.onRowSelected)
QtCore.QObject.connect(self.newPushButton,
QtCore.SIGNAL(u'clicked()'), self.onNewClicked)
QtCore.QObject.connect(self.savePushButton,
@ -145,6 +145,9 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
"""
if self.selected != -1:
FormattingTags.remove_html_tag(self.selected)
# As the first items are protected we should not have to take care
# of negative indexes causing tracebacks.
self.tagTableWidget.selectRow(self.selected - 1)
self.selected = -1
FormattingTags.save_html_tags()
self._reloadTable()

View File

@ -39,7 +39,8 @@ log = logging.getLogger(__name__)
from PyQt4 import QtCore, QtGui
from openlp.core.lib import OpenLPToolbar, ServiceItem, Receiver, build_icon, \
ItemCapabilities, SettingsManager, translate, str_to_bool
ItemCapabilities, SettingsManager, translate, str_to_bool, \
check_directory_exists
from openlp.core.lib.theme import ThemeLevel
from openlp.core.lib.settings import Settings
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
@ -556,8 +557,7 @@ class ServiceManager(QtGui.QWidget):
audio_from = os.path.join(self.servicePath, audio_from)
save_file = os.path.join(self.servicePath, audio_to)
save_path = os.path.split(save_file)[0]
if not os.path.exists(save_path):
os.makedirs(save_path)
check_directory_exists(save_path)
if not os.path.exists(save_file):
shutil.copy(audio_from, save_file)
zip.write(audio_from, audio_to.encode(u'utf-8'))

View File

@ -226,7 +226,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
def onCurrentIdChanged(self, pageId):
"""
Detects Page changes and updates as approprate.
Detects Page changes and updates as appropriate.
"""
enabled = self.page(pageId) == self.areaPositionPage
self.setOption(QtGui.QWizard.HaveCustomButton1, enabled)

View File

@ -172,6 +172,11 @@ def _get_os_dir_path(dir_type):
u'Library', u'Application Support', u'openlp')
else:
if dir_type == AppLocation.LanguageDir:
prefixes = [u'/usr/local', u'/usr']
for prefix in prefixes:
directory = os.path.join(prefix, u'share', u'openlp')
if os.path.exists(directory):
return directory
return os.path.join(u'/usr', u'share', u'openlp')
if XDG_BASE_AVAILABLE:
if dir_type == AppLocation.ConfigDir:

View File

@ -227,9 +227,6 @@ class ImpressDocument(PresentationDocument):
OpenOffice task started earlier. If OpenOffice is not present is is
started. Once the environment is available the presentation is loaded
and started.
``presentation``
The file name of the presentatios to the run.
"""
log.debug(u'Load Presentation OpenOffice')
if os.name == u'nt':

View File

@ -119,10 +119,7 @@ class PowerpointDocument(PresentationDocument):
def load_presentation(self):
"""
Called when a presentation is added to the SlideController.
Opens the PowerPoint file using the process created earlier
``presentation``
The file name of the presentations to run.
Opens the PowerPoint file using the process created earlier.
"""
log.debug(u'load_presentation')
if not self.controller.process or not self.controller.process.Visible:

View File

@ -118,9 +118,6 @@ class PptviewDocument(PresentationDocument):
Called when a presentation is added to the SlideController.
It builds the environment, starts communcations with the background
PptView task started earlier.
``presentation``
The file name of the presentations to run.
"""
log.debug(u'LoadPresentation')
renderer = self.controller.plugin.renderer

View File

@ -47,7 +47,7 @@ class PresentationDocument(object):
**Hook Functions**
``load_presentation(presentation)``
``load_presentation()``
Load a presentation file
``close_presentation()``
@ -104,11 +104,8 @@ class PresentationDocument(object):
def load_presentation(self):
"""
Called when a presentation is added to the SlideController.
Loads the presentation and starts it
``presentation``
The file name of the presentations to the run.
Called when a presentation is added to the SlideController. Loads the
presentation and starts it.
Returns False if the file could not be opened
"""

View File

@ -61,14 +61,14 @@ class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog):
def onFirstNameEditTextEdited(self, display_name):
if not self._autoDisplayName:
return
if not self.lastNameEdit.text():
if self.lastNameEdit.text():
display_name = display_name + u' ' + self.lastNameEdit.text()
self.displayEdit.setText(display_name)
def onLastNameEditTextEdited(self, display_name):
if not self._autoDisplayName:
return
if not self.firstNameEdit.text():
if self.firstNameEdit.text():
display_name = self.firstNameEdit.text() + u' ' + display_name
self.displayEdit.setText(display_name)

View File

@ -34,7 +34,7 @@ import shutil
from PyQt4 import QtCore, QtGui
from openlp.core.lib import PluginStatus, Receiver, MediaType, translate, \
create_separated_list
create_separated_list, check_directory_exists
from openlp.core.lib.ui import UiStrings, set_case_insensitive_completer, \
critical_error_message_box, find_and_set_in_combo_box
from openlp.core.utils import AppLocation
@ -880,8 +880,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
save_path = os.path.join(
AppLocation.get_section_data_path(self.mediaitem.plugin.name),
'audio', str(self.song.id))
if not os.path.exists(save_path):
os.makedirs(save_path)
check_directory_exists(save_path)
self.song.media_files = []
files = []
for row in xrange(self.audioListWidget.count()):

View File

@ -476,7 +476,7 @@ def get_encoding(font, font_table, default_encoding, failed=False):
Dictionary of fonts and respective encodings.
``default_encoding``
The defaul encoding to use when font_table is empty or no font is used.
The default encoding to use when font_table is empty or no font is used.
``failed``
A boolean indicating whether the previous encoding didn't work.

View File

@ -48,15 +48,8 @@ class EasySlidesImport(SongImport):
Initialise the class.
"""
SongImport.__init__(self, manager, **kwargs)
self.commit = True
def doImport(self):
"""
Import either each of the files in self.importSources - each element of
which can be either a single opensong file, or a zipfile containing
multiple opensong files. If `self.commit` is set False, the
import will not be committed to the database (useful for test scripts).
"""
log.info(u'Importing EasySlides XML file %s', self.importSource)
parser = etree.XMLParser(remove_blank_text=True)
parsed_file = etree.parse(self.importSource, parser)

View File

@ -483,8 +483,11 @@ class FoilPresenter(object):
# Process verse order
verse_order = []
verse_strophenr = []
for strophennummer in foilpresenterfolie.reihenfolge.strophennummer:
verse_strophenr.append(strophennummer)
try:
for strophennummer in foilpresenterfolie.reihenfolge.strophennummer:
verse_strophenr.append(strophennummer)
except AttributeError:
pass
# Currently we do not support different "parts"!
if u'0' in temp_verse_order:
for vers in temp_verse_order_backup:
@ -538,12 +541,17 @@ class FoilPresenter(object):
``song``
The song object.
"""
for title_string in foilpresenterfolie.titel.titelstring:
if not song.title:
song.title = self._child(title_string)
song.alternate_title = u''
else:
song.alternate_title = self._child(title_string)
try:
for title_string in foilpresenterfolie.titel.titelstring:
if not song.title:
song.title = self._child(title_string)
song.alternate_title = u''
else:
song.alternate_title = self._child(title_string)
except AttributeError:
# Use first line of first verse
first_line = self._child(foilpresenterfolie.strophen.strophe.text_)
song.title = first_line.split('\n')[0]
def _process_topics(self, foilpresenterfolie, song):
"""

View File

@ -28,6 +28,7 @@
"""
The :mod:`importer` modules provides the general song import functionality.
"""
import os
import logging
from openlp.core.lib import translate
@ -44,6 +45,7 @@ from powersongimport import PowerSongImport
from ewimport import EasyWorshipSongImport
from songbeamerimport import SongBeamerImport
from songshowplusimport import SongShowPlusImport
from songproimport import SongProImport
from sundayplusimport import SundayPlusImport
from foilpresenterimport import FoilPresenterImport
from zionworximport import ZionWorxImport
@ -67,6 +69,13 @@ try:
except ImportError:
log.exception('Error importing %s', 'OooImport')
HAS_OOO = False
HAS_MEDIASHOUT = False
if os.name == u'nt':
try:
from mediashoutimport import MediaShoutImport
HAS_MEDIASHOUT = True
except ImportError:
log.exception('Error importing %s', 'MediaShoutImport')
class SongFormatSelect(object):
@ -100,6 +109,7 @@ class SongFormat(object):
``u'canDisable'``
Whether song format importer is disablable.
If ``True``, then ``u'disabledLabelText'`` must also be defined.
``u'availability'``
Whether song format importer is available.
@ -141,15 +151,16 @@ class SongFormat(object):
EasySlides = 6
EasyWorship = 7
FoilPresenter = 8
OpenSong = 9
PowerSong = 10
SongBeamer = 11
SongShowPlus = 12
SongsOfFellowship = 13
SundayPlus = 14
WordsOfWorship = 15
ZionWorx = 16
#CSV = 17
MediaShout = 9
OpenSong = 10
PowerSong = 11
SongBeamer = 12
SongPro = 13
SongShowPlus = 14
SongsOfFellowship = 15
SundayPlus = 16
WordsOfWorship = 17
ZionWorx = 18
# Set optional attribute defaults
__defaults__ = {
@ -158,7 +169,8 @@ class SongFormat(object):
u'selectMode': SongFormatSelect.MultipleFiles,
u'filter': u'',
u'comboBoxText': None,
u'disabledLabelText': u'',
u'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
'This importer has been disabled.'),
u'getFilesTitle': None,
u'invalidSourceMsg': None,
u'descriptionText': None
@ -240,6 +252,19 @@ class SongFormat(object):
u'filter': u'%s (*.foil)' % translate(
'SongsPlugin.ImportWizardForm', 'Foilpresenter Song Files')
},
MediaShout: {
u'name': u'MediaShout',
u'prefix': u'mediaShout',
u'canDisable': True,
u'selectMode': SongFormatSelect.SingleFile,
u'filter': u'%s (*.mdb)' % translate('SongsPlugin.ImportWizardForm',
'MediaShout Database'),
u'disabledLabelText': translate('SongsPlugin.ImportWizardForm',
'The MediaShout importer is only supported on Windows. It has '
'been disabled due to a missing Python module. If you want to '
'use this importer, you will need to install the "pyodbc" '
'module.')
},
OpenSong: {
u'class': OpenSongImport,
u'name': WizardStrings.OS,
@ -260,6 +285,18 @@ class SongFormat(object):
u'filter': u'%s (*.sng)' % translate('SongsPlugin.ImportWizardForm',
'SongBeamer Files')
},
SongPro: {
u'class': SongProImport,
u'name': u'SongPro',
u'prefix': u'songPro',
u'selectMode': SongFormatSelect.SingleFile,
u'filter': u'%s (*.txt)' % translate('SongsPlugin.ImportWizardForm',
'SongPro Text Files'),
u'comboBoxText': translate('SongsPlugin.ImportWizardForm',
'SongPro (Export File)'),
u'descriptionText': translate('SongsPlugin.ImportWizardForm',
'In SongPro, export your songs using the File -> Export menu')
},
SongShowPlus: {
u'class': SongShowPlusImport,
u'name': u'SongShow Plus',
@ -302,12 +339,6 @@ class SongFormat(object):
'First convert your ZionWorx database to a CSV text file, as '
'explained in the <a href="http://manual.openlp.org/songs.html'
'#importing-from-zionworx">User Manual</a>.')
# },
# CSV: {
# u'class': CSVImport,
# u'name': WizardStrings.CSV,
# u'prefix': u'csv',
# u'selectMode': SongFormatSelect.SingleFile
}
}
@ -326,9 +357,11 @@ class SongFormat(object):
SongFormat.EasySlides,
SongFormat.EasyWorship,
SongFormat.FoilPresenter,
SongFormat.MediaShout,
SongFormat.OpenSong,
SongFormat.PowerSong,
SongFormat.SongBeamer,
SongFormat.SongPro,
SongFormat.SongShowPlus,
SongFormat.SongsOfFellowship,
SongFormat.SundayPlus,
@ -383,5 +416,8 @@ if HAS_SOF:
SongFormat.set(SongFormat.Generic, u'availability', HAS_OOO)
if HAS_OOO:
SongFormat.set(SongFormat.Generic, u'class', OooImport)
SongFormat.set(SongFormat.MediaShout, u'availability', HAS_MEDIASHOUT)
if HAS_MEDIASHOUT:
SongFormat.set(SongFormat.MediaShout, u'class', MediaShoutImport)
__all__ = [u'SongFormat', u'SongFormatSelect']

View File

@ -36,7 +36,8 @@ from PyQt4 import QtCore, QtGui
from sqlalchemy.sql import or_
from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
translate, check_item_selected, PluginStatus, create_separated_list
translate, check_item_selected, PluginStatus, create_separated_list, \
check_directory_exists
from openlp.core.lib.ui import UiStrings, create_widget_action
from openlp.core.lib.settings import Settings
from openlp.core.utils import AppLocation
@ -89,8 +90,7 @@ class SongMediaItem(MediaManagerItem):
dest_file = os.path.join(
AppLocation.get_section_data_path(self.plugin.name),
u'audio', str(song.id), os.path.split(bga)[1])
if not os.path.exists(os.path.split(dest_file)[0]):
os.makedirs(os.path.split(dest_file)[0])
check_directory_exists(os.path.split(dest_file)[0])
shutil.copyfile(os.path.join(
AppLocation.get_section_data_path(
u'servicemanager'), bga),

View File

@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2012 Raoul Snyman #
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Edwin Lunando, Joshua Miller, Stevan Pettit, #
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
# Simon Scudder, Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon #
# Tibble, Dave Warnock, Frode Woldsund #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`mediashoutimport` module provides the functionality for importing
a MediaShout database into the OpenLP database.
"""
import re
import os
import pyodbc
from openlp.core.lib import translate
from openlp.plugins.songs.lib.songimport import SongImport
VERSE_TAGS = [u'V', u'C', u'B', u'O', u'P', u'I', u'E']
class MediaShoutImport(SongImport):
"""
The :class:`MediaShoutImport` class provides the ability to import the
MediaShout Access Database
"""
def __init__(self, manager, **kwargs):
"""
Initialise the MediaShout importer.
"""
SongImport.__init__(self, manager, **kwargs)
def doImport(self):
"""
Receive a single file to import.
"""
try:
conn = pyodbc.connect(u'DRIVER={Microsoft Access Driver (*.mdb)};'
u'DBQ=%s;PWD=6NOZ4eHK7k' % self.importSource)
except:
# Unfortunately no specific exception type
self.logError(self.importSource,
translate('SongsPlugin.MediaShoutImport',
'Unable to open the MediaShout database.'))
return
cursor = conn.cursor()
cursor.execute(u'SELECT Record, Title, Author, Copyright, '
u'SongID, CCLI, Notes FROM Songs ORDER BY Title')
songs = cursor.fetchall()
self.importWizard.progressBar.setMaximum(len(songs))
for song in songs:
if self.stopImportFlag:
break
cursor.execute(u'SELECT Type, Number, Text FROM Verses '
u'WHERE Record = %s ORDER BY Type, Number' % song.Record)
verses = cursor.fetchall()
cursor.execute(u'SELECT Type, Number, POrder FROM PlayOrder '
u'WHERE Record = %s ORDER BY POrder' % song.Record)
verse_order = cursor.fetchall()
cursor.execute(u'SELECT Name FROM Themes INNER JOIN SongThemes '
u'ON SongThemes.ThemeId = Themes.ThemeId '
u'WHERE SongThemes.Record = %s' % song.Record)
topics = cursor.fetchall()
cursor.execute(u'SELECT Name FROM Groups INNER JOIN SongGroups '
u'ON SongGroups.GroupId = Groups.GroupId '
u'WHERE SongGroups.Record = %s' % song.Record)
topics += cursor.fetchall()
self.processSong(song, verses, verse_order, topics)
def processSong(self, song, verses, verse_order, topics):
"""
Create the song, i.e. title, verse etc.
"""
self.setDefaults()
self.title = song.Title
self.parseAuthor(song.Author)
self.addCopyright(song.Copyright)
self.comments = song.Notes
for topic in topics:
self.topics.append(topic.Name)
if u'-' in song.SongID:
self.songBookName, self.songNumber = song.SongID.split(u'-', 1)
else:
self.songBookName = song.SongID
for verse in verses:
tag = VERSE_TAGS[verse.Type] + unicode(verse.Number) \
if verse.Type < len(VERSE_TAGS) else u'O'
self.addVerse(verse.Text, tag)
for order in verse_order:
if order.Type < len(VERSE_TAGS):
self.verseOrderList.append(VERSE_TAGS[order.Type]
+ unicode(order.Number))
self.finish()

View File

@ -33,7 +33,7 @@ import os
from PyQt4 import QtCore
from openlp.core.lib import Receiver, translate
from openlp.core.lib import Receiver, translate, check_directory_exists
from openlp.core.ui.wizard import WizardStrings
from openlp.core.utils import AppLocation
from openlp.plugins.songs.lib import clean_song, VerseType
@ -388,8 +388,7 @@ class SongImport(QtCore.QObject):
AppLocation.get_section_data_path(
self.importWizard.plugin.name),
'audio', str(song_id))
if not os.path.exists(self.save_path):
os.makedirs(self.save_path)
check_directory_exists(self.save_path)
if not filename.startswith(self.save_path):
oldfile, filename = filename, os.path.join(self.save_path,
os.path.split(filename)[1])

View File

@ -0,0 +1,143 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2012 Raoul Snyman #
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Edwin Lunando, Joshua Miller, Stevan Pettit, #
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
# Simon Scudder, Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon #
# Tibble, Dave Warnock, Frode Woldsund #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`songproimport` module provides the functionality for importing SongPro
songs into the OpenLP database.
"""
import re
import os
from openlp.core.lib import translate
from openlp.plugins.songs.lib import strip_rtf
from openlp.plugins.songs.lib.songimport import SongImport
class SongProImport(SongImport):
"""
The :class:`SongProImport` class provides the ability to import song files
from SongPro export files.
**SongPro Song File Format:**
SongPro has the option to export under its File menu
This produces files containing single or multiple songs
The file is text with lines tagged with # followed by an identifier.
This is documented here: http://creationsoftware.com/ImportIdentifiers.php
An example here: http://creationsoftware.com/ExampleImportingManySongs.txt
#A - next line is the Song Author
#B - the lines following until next tagged line are the "Bridge" words
(can be in rtf or plain text) which we map as B1
#C - the lines following until next tagged line are the chorus words
(can be in rtf or plain text)
which we map as C1
#D - the lines following until next tagged line are the "Ending" words
(can be in rtf or plain text) which we map as E1
#E - this song ends here, so we process the song -
and start again at the next line
#G - next line is the Group
#M - next line is the Song Number
#N - next line are Notes
#R - next line is the SongCopyright
#O - next line is the Verse Sequence
#T - next line is the Song Title
#1 - #7 the lines following until next tagged line are the verse x words
(can be in rtf or plain text)
"""
def __init__(self, manager, **kwargs):
"""
Initialise the SongPro importer.
"""
SongImport.__init__(self, manager, **kwargs)
def doImport(self):
"""
Receive a single file or a list of files to import.
"""
self.encoding = None
with open(self.importSource, 'r') as songs_file:
self.importWizard.progressBar.setMaximum(0)
tag = u''
text = u''
for file_line in songs_file:
if self.stopImportFlag:
break
file_line = unicode(file_line, u'cp1252')
file_text = file_line.rstrip()
if file_text and file_text[0] == u'#':
self.processSection(tag, text.rstrip())
tag = file_text[1:]
text = u''
else:
text += file_line
def processSection(self, tag, text):
"""
Process a section of the song, i.e. title, verse etc.
"""
if tag == u'T':
self.setDefaults()
if text:
self.title = text
return
elif tag == u'E':
self.finish()
return
if u'rtf1' in text:
text, self.encoding = strip_rtf(text, self.encoding)
text = text.rstrip()
if not text:
return
if tag == u'A':
self.parseAuthor(text)
elif tag in [u'B', u'C']:
self.addVerse(text, tag)
elif tag == u'D':
self.addVerse(text, u'E')
elif tag == u'G':
self.topics.append(text)
elif tag == u'M':
matches = re.findall(r'\d+', text)
if matches:
self.songNumber = matches[-1]
self.songBookName = text[:text.rfind(self.songNumber)]
elif tag == u'N':
self.comments = text
elif tag == u'O':
for char in text:
if char == u'C':
self.verseOrderList.append(u'C1')
elif char == u'B':
self.verseOrderList.append(u'B1')
elif char == u'D':
self.verseOrderList.append(u'E1')
elif u'1' <= char <= u'7':
self.verseOrderList.append(u'V' + char)
elif tag == u'R':
self.addCopyright(text)
elif u'1' <= tag <= u'7':
self.addVerse(text, u'V' + tag[1:])

View File

@ -37,6 +37,9 @@ from openlp.plugins.songs.lib.songimport import SongImport
log = logging.getLogger(__name__)
# Used to strip control chars (except 10=LF, 13=CR)
CONTROL_CHARS_MAP = dict.fromkeys(range(10) + [11, 12] + range(14,32) + [127])
class ZionWorxImport(SongImport):
"""
The :class:`ZionWorxImport` class provides the ability to import songs
@ -78,13 +81,10 @@ class ZionWorxImport(SongImport):
"""
Receive a CSV file (from a ZionWorx database dump) to import.
"""
# Used to strip control chars (10=LF, 13=CR, 127=DEL)
self.control_chars_map = dict.fromkeys(
range(10) + [11, 12] + range(14,32) + [127])
with open(self.importSource, 'rb') as songs_file:
fieldnames = [u'SongNum', u'Title1', u'Title2', u'Lyrics',
field_names = [u'SongNum', u'Title1', u'Title2', u'Lyrics',
u'Writer', u'Copyright', u'Keywords', u'DefaultStyle']
songs_reader = csv.DictReader(songs_file, fieldnames)
songs_reader = csv.DictReader(songs_file, field_names)
try:
records = list(songs_reader)
except csv.Error, e:
@ -140,4 +140,4 @@ class ZionWorxImport(SongImport):
"""
# This encoding choice seems OK. ZionWorx has no option for setting the
# encoding for its songs, so we assume encoding is always the same.
return unicode(str, u'cp1252').translate(self.control_chars_map)
return unicode(str, u'cp1252').translate(CONTROL_CHARS_MAP)

34
resources/__init__.py Normal file
View File

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2012 Raoul Snyman #
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Edwin Lunando, Joshua Miller, Stevan Pettit, #
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
# Simon Scudder, Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon #
# Tibble, Dave Warnock, Frode Woldsund #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
The :mod:`resources` module contains a bunch of resources for OpenLP.
DO NOT REMOVE THIS FILE, IT IS REQUIRED FOR INCLUDING THE RESOURCES ON SOME
PLATFORMS!
"""

View File

@ -28,10 +28,56 @@
###############################################################################
from setuptools import setup, find_packages
import re
VERSION_FILE = 'openlp/.version'
SPLIT_ALPHA_DIGITS = re.compile(r'(\d+|\D+)')
def try_int(s):
"""
Convert string s to an integer if possible. Fail silently and return
the string as-is if it isn't an integer.
``s``
The string to try to convert.
"""
try:
return int(s)
except Exception:
return s
def natural_sort_key(s):
"""
Return a tuple by which s is sorted.
``s``
A string value from the list we want to sort.
"""
return map(try_int, SPLIT_ALPHA_DIGITS.findall(s))
def natural_compare(a, b):
"""
Compare two strings naturally and return the result.
``a``
A string to compare.
``b``
A string to compare.
"""
return cmp(natural_sort_key(a), natural_sort_key(b))
def natural_sort(seq, compare=natural_compare):
"""
Returns a copy of seq, sorted by natural string sort.
"""
import copy
temp = copy.copy(seq)
temp.sort(compare)
return temp
try:
# Try to import Bazaar
from bzrlib.branch import Branch
b = Branch.open_containing('.')[0]
b.lock_read()
@ -46,7 +92,8 @@ try:
if revision_id in tags:
version = u'%s' % tags[revision_id][0]
else:
version = '%s-bzr%s' % (sorted(b.tags.get_tag_dict().keys())[-1], revno)
version = '%s-bzr%s' % \
(natural_sort(b.tags.get_tag_dict().keys())[-1], revno)
ver_file = open(VERSION_FILE, u'w')
ver_file.write(version)
ver_file.close()