~raoul-snyman/openlp/python3

« back to all changes in this revision

Viewing changes to openlp/plugins/songs/lib/xml.py

  • Committer: Raoul Snyman
  • Date: 2013-04-03 06:51:39 UTC
  • Revision ID: raoul.snyman@saturnlaboratories.co.za-20130403065139-9qhs0xmlrcef4n2h
trying to migrate to py3k

Show diffs side-by-side

added added

removed removed

Lines of Context:
75
75
 
76
76
log = logging.getLogger(__name__)
77
77
 
78
 
NAMESPACE = u'http://openlyrics.info/namespace/2009/song'
 
78
NAMESPACE = 'http://openlyrics.info/namespace/2009/song'
79
79
NSMAP = '{' + NAMESPACE + '}' + '%s'
80
80
 
81
81
 
83
83
    """
84
84
    This class builds and parses the XML used to describe songs.
85
85
    """
86
 
    log.info(u'SongXML Loaded')
 
86
    log.info('SongXML Loaded')
87
87
 
88
88
    def __init__(self):
89
89
        """
90
90
        Set up the default variables.
91
91
        """
92
 
        self.song_xml = objectify.fromstring(u'<song version="1.0" />')
93
 
        self.lyrics = etree.SubElement(self.song_xml, u'lyrics')
 
92
        self.song_xml = objectify.fromstring('<song version="1.0" />')
 
93
        self.lyrics = etree.SubElement(self.song_xml, 'lyrics')
94
94
 
95
95
    def add_verse_to_lyrics(self, type, number, content, lang=None):
96
96
        """
112
112
            The verse's language code (ISO-639). This is not required, but
113
113
            should be added if available.
114
114
        """
115
 
        verse = etree.Element(u'verse', type=unicode(type),
116
 
            label=unicode(number))
 
115
        verse = etree.Element('verse', type=str(type),
 
116
            label=str(number))
117
117
        if lang:
118
 
            verse.set(u'lang', lang)
 
118
            verse.set('lang', lang)
119
119
        verse.text = etree.CDATA(content)
120
120
        self.lyrics.append(verse)
121
121
 
123
123
        """
124
124
        Extract our newly created XML song.
125
125
        """
126
 
        return etree.tostring(self.song_xml, encoding=u'UTF-8',
 
126
        return etree.tostring(self.song_xml, encoding='UTF-8',
127
127
            xml_declaration=True)
128
128
 
129
129
    def get_verses(self, xml):
142
142
        """
143
143
        self.song_xml = None
144
144
        verse_list = []
145
 
        if not xml.startswith(u'<?xml') and not xml.startswith(u'<song'):
 
145
        if not xml.startswith('<?xml') and not xml.startswith('<song'):
146
146
            # This is an old style song, without XML. Let's handle it correctly
147
147
            # by iterating through the verses, and then recreating the internal
148
148
            # xml object as well.
149
 
            self.song_xml = objectify.fromstring(u'<song version="1.0" />')
150
 
            self.lyrics = etree.SubElement(self.song_xml, u'lyrics')
151
 
            verses = xml.split(u'\n\n')
 
149
            self.song_xml = objectify.fromstring('<song version="1.0" />')
 
150
            self.lyrics = etree.SubElement(self.song_xml, 'lyrics')
 
151
            verses = xml.split('\n\n')
152
152
            for count, verse in enumerate(verses):
153
 
                verse_list.append([{u'type': u'v', u'label': unicode(count)}, unicode(verse)])
154
 
                self.add_verse_to_lyrics(u'v', unicode(count), verse)
 
153
                verse_list.append([{'type': 'v', 'label': str(count)}, str(verse)])
 
154
                self.add_verse_to_lyrics('v', str(count), verse)
155
155
            return verse_list
156
 
        elif xml.startswith(u'<?xml'):
 
156
        elif xml.startswith('<?xml'):
157
157
            xml = xml[38:]
158
158
        try:
159
159
            self.song_xml = objectify.fromstring(xml)
160
160
        except etree.XMLSyntaxError:
161
 
            log.exception(u'Invalid xml %s', xml)
 
161
            log.exception('Invalid xml %s', xml)
162
162
        xml_iter = self.song_xml.getiterator()
163
163
        for element in xml_iter:
164
 
            if element.tag == u'verse':
 
164
            if element.tag == 'verse':
165
165
                if element.text is None:
166
 
                    element.text = u''
167
 
                verse_list.append([element.attrib, unicode(element.text)])
 
166
                    element.text = ''
 
167
                verse_list.append([element.attrib, str(element.text)])
168
168
        return verse_list
169
169
 
170
170
    def dump_xml(self):
257
257
        OpenLP supports this property.
258
258
 
259
259
    """
260
 
    IMPLEMENTED_VERSION = u'0.8'
 
260
    IMPLEMENTED_VERSION = '0.8'
261
261
    START_TAGS_REGEX = re.compile(r'\{(\w+)\}')
262
262
    END_TAGS_REGEX = re.compile(r'\{\/(\w+)\}')
263
 
    VERSE_TAG_SPLITTER = re.compile(u'([a-zA-Z]+)([0-9]*)([a-zA-Z]?)')
 
263
    VERSE_TAG_SPLITTER = re.compile('([a-zA-Z]+)([0-9]*)([a-zA-Z]?)')
264
264
 
265
265
    def __init__(self, manager):
266
266
        self.manager = manager
270
270
        Convert the song to OpenLyrics Format.
271
271
        """
272
272
        sxml = SongXML()
273
 
        song_xml = objectify.fromstring(u'<song/>')
 
273
        song_xml = objectify.fromstring('<song/>')
274
274
        # Append the necessary meta data to the song.
275
 
        song_xml.set(u'xmlns', NAMESPACE)
276
 
        song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION)
277
 
        application_name = u'OpenLP ' + get_application_version()[u'version']
278
 
        song_xml.set(u'createdIn', application_name)
279
 
        song_xml.set(u'modifiedIn', application_name)
 
275
        song_xml.set('xmlns', NAMESPACE)
 
276
        song_xml.set('version', OpenLyrics.IMPLEMENTED_VERSION)
 
277
        application_name = 'OpenLP ' + get_application_version()['version']
 
278
        song_xml.set('createdIn', application_name)
 
279
        song_xml.set('modifiedIn', application_name)
280
280
        # "Convert" 2012-08-27 11:49:15 to 2012-08-27T11:49:15.
281
 
        song_xml.set(u'modifiedDate', unicode(song.last_modified).replace(u' ', u'T'))
282
 
        properties = etree.SubElement(song_xml, u'properties')
283
 
        titles = etree.SubElement(properties, u'titles')
284
 
        self._add_text_to_element(u'title', titles, song.title)
 
281
        song_xml.set('modifiedDate', str(song.last_modified).replace(' ', 'T'))
 
282
        properties = etree.SubElement(song_xml, 'properties')
 
283
        titles = etree.SubElement(properties, 'titles')
 
284
        self._add_text_to_element('title', titles, song.title)
285
285
        if song.alternate_title:
286
 
            self._add_text_to_element(u'title', titles, song.alternate_title)
 
286
            self._add_text_to_element('title', titles, song.alternate_title)
287
287
        if song.comments:
288
 
            comments = etree.SubElement(properties, u'comments')
289
 
            self._add_text_to_element(u'comment', comments, song.comments)
 
288
            comments = etree.SubElement(properties, 'comments')
 
289
            self._add_text_to_element('comment', comments, song.comments)
290
290
        if song.copyright:
291
 
            self._add_text_to_element(u'copyright', properties, song.copyright)
 
291
            self._add_text_to_element('copyright', properties, song.copyright)
292
292
        if song.verse_order:
293
293
            self._add_text_to_element(
294
 
                u'verseOrder', properties, song.verse_order.lower())
 
294
                'verseOrder', properties, song.verse_order.lower())
295
295
        if song.ccli_number:
296
 
            self._add_text_to_element(u'ccliNo', properties, song.ccli_number)
 
296
            self._add_text_to_element('ccliNo', properties, song.ccli_number)
297
297
        if song.authors:
298
 
            authors = etree.SubElement(properties, u'authors')
 
298
            authors = etree.SubElement(properties, 'authors')
299
299
            for author in song.authors:
300
 
                self._add_text_to_element(u'author', authors, author.display_name)
 
300
                self._add_text_to_element('author', authors, author.display_name)
301
301
        book = self.manager.get_object_filtered(Book, Book.id == song.song_book_id)
302
302
        if book is not None:
303
303
            book = book.name
304
 
            songbooks = etree.SubElement(properties, u'songbooks')
305
 
            element = self._add_text_to_element(u'songbook', songbooks, None, book)
 
304
            songbooks = etree.SubElement(properties, 'songbooks')
 
305
            element = self._add_text_to_element('songbook', songbooks, None, book)
306
306
            if song.song_number:
307
 
                element.set(u'entry', song.song_number)
 
307
                element.set('entry', song.song_number)
308
308
        if song.topics:
309
 
            themes = etree.SubElement(properties, u'themes')
 
309
            themes = etree.SubElement(properties, 'themes')
310
310
            for topic in song.topics:
311
 
                self._add_text_to_element(u'theme', themes, topic.name)
 
311
                self._add_text_to_element('theme', themes, topic.name)
312
312
        # Process the formatting tags.
313
313
        # Have we any tags in song lyrics?
314
314
        tags_element = None
315
 
        match = re.search(u'\{/?\w+\}', song.lyrics, re.UNICODE)
 
315
        match = re.search('\{/?\w+\}', song.lyrics, re.UNICODE)
316
316
        if match:
317
317
            # Named 'format_' - 'format' is built-in fuction in Python.
318
 
            format_ = etree.SubElement(song_xml, u'format')
319
 
            tags_element = etree.SubElement(format_, u'tags')
320
 
            tags_element.set(u'application', u'OpenLP')
 
318
            format_ = etree.SubElement(song_xml, 'format')
 
319
            tags_element = etree.SubElement(format_, 'tags')
 
320
            tags_element.set('application', 'OpenLP')
321
321
        # Process the song's lyrics.
322
 
        lyrics = etree.SubElement(song_xml, u'lyrics')
 
322
        lyrics = etree.SubElement(song_xml, 'lyrics')
323
323
        verse_list = sxml.get_verses(song.lyrics)
324
324
        # Add a suffix letter to each verse
325
325
        verse_tags = []
326
326
        for verse in verse_list:
327
 
            verse_tag = verse[0][u'type'][0].lower()
328
 
            verse_number = verse[0][u'label']
 
327
            verse_tag = verse[0]['type'][0].lower()
 
328
            verse_number = verse[0]['label']
329
329
            verse_def = verse_tag + verse_number
330
330
            verse_tags.append(verse_def)
331
331
            # Create the letter from the number of duplicates
332
 
            verse[0][u'suffix'] = chr(96 + verse_tags.count(verse_def))
 
332
            verse[0]['suffix'] = chr(96 + verse_tags.count(verse_def))
333
333
        # If the verse tag is a duplicate use the suffix letter
334
334
        for verse in verse_list:
335
 
            verse_tag = verse[0][u'type'][0].lower()
336
 
            verse_number = verse[0][u'label']
 
335
            verse_tag = verse[0]['type'][0].lower()
 
336
            verse_number = verse[0]['label']
337
337
            verse_def = verse_tag + verse_number
338
338
            if verse_tags.count(verse_def) > 1:
339
 
                verse_def += verse[0][u'suffix']
340
 
            verse_element = self._add_text_to_element(u'verse', lyrics, None, verse_def)
341
 
            if u'lang' in verse[0]:
342
 
                verse_element.set(u'lang', verse[0][u'lang'])
 
339
                verse_def += verse[0]['suffix']
 
340
            verse_element = self._add_text_to_element('verse', lyrics, None, verse_def)
 
341
            if 'lang' in verse[0]:
 
342
                verse_element.set('lang', verse[0]['lang'])
343
343
            # Create a list with all "optional" verses.
344
344
            optional_verses = cgi.escape(verse[1])
345
 
            optional_verses = optional_verses.split(u'\n[---]\n')
346
 
            start_tags = u''
347
 
            end_tags = u''
 
345
            optional_verses = optional_verses.split('\n[---]\n')
 
346
            start_tags = ''
 
347
            end_tags = ''
348
348
            for index, optional_verse in enumerate(optional_verses):
349
349
                # Fix up missing end and start tags such as {r} or {/r}.
350
350
                optional_verse = start_tags + optional_verse
354
354
                lines_element = self._add_text_with_tags_to_lines(verse_element, optional_verse, tags_element)
355
355
                # Do not add the break attribute to the last lines element.
356
356
                if index < len(optional_verses) - 1:
357
 
                    lines_element.set(u'break', u'optional')
 
357
                    lines_element.set('break', 'optional')
358
358
        return self._extract_xml(song_xml)
359
359
 
360
360
    def _get_missing_tags(self, text):
375
375
        """
376
376
        tags = []
377
377
        for tag in FormattingTags.get_html_tags():
378
 
            if tag[u'start tag'] == u'{br}':
 
378
            if tag['start tag'] == '{br}':
379
379
                continue
380
 
            if text.count(tag[u'start tag']) != text.count(tag[u'end tag']):
381
 
                tags.append((text.find(tag[u'start tag']), tag[u'start tag'], tag[u'end tag']))
 
380
            if text.count(tag['start tag']) != text.count(tag['end tag']):
 
381
                tags.append((text.find(tag['start tag']), tag['start tag'], tag['end tag']))
382
382
        # Sort the lists, so that the tags which were opened first on the first
383
383
        # slide (the text we are checking) will be opened first on the next
384
384
        # slide as well.
389
389
            start_tags.append(tag[1])
390
390
            end_tags.append(tag[2])
391
391
        end_tags.reverse()
392
 
        return u''.join(start_tags), u''.join(end_tags)
 
392
        return ''.join(start_tags), ''.join(end_tags)
393
393
 
394
394
    def xml_to_song(self, xml, parse_and_temporary_save=False):
395
395
        """
407
407
        # No xml get out of here.
408
408
        if not xml:
409
409
            return None
410
 
        if xml[:5] == u'<?xml':
 
410
        if xml[:5] == '<?xml':
411
411
            xml = xml[38:]
412
412
        song_xml = objectify.fromstring(xml)
413
 
        if hasattr(song_xml, u'properties'):
 
413
        if hasattr(song_xml, 'properties'):
414
414
            properties = song_xml.properties
415
415
        else:
416
416
            return None
417
417
        # Formatting tags are new in OpenLyrics 0.8
418
 
        if float(song_xml.get(u'version')) > 0.7:
 
418
        if float(song_xml.get('version')) > 0.7:
419
419
            self._process_formatting_tags(song_xml, parse_and_temporary_save)
420
420
        song = Song()
421
421
        # Values will be set when cleaning the song.
422
 
        song.search_lyrics = u''
423
 
        song.verse_order = u''
424
 
        song.search_title = u''
 
422
        song.search_lyrics = ''
 
423
        song.verse_order = ''
 
424
        song.search_title = ''
425
425
        song.temporary = parse_and_temporary_save
426
426
        self._process_copyright(properties, song)
427
427
        self._process_cclinumber(properties, song)
438
438
 
439
439
    def _add_text_to_element(self, tag, parent, text=None, label=None):
440
440
        if label:
441
 
            element = etree.Element(tag, name=unicode(label))
 
441
            element = etree.Element(tag, name=str(label))
442
442
        else:
443
443
            element = etree.Element(tag)
444
444
        if text:
445
 
            element.text = unicode(text)
 
445
            element.text = str(text)
446
446
        parent.append(element)
447
447
        return element
448
448
 
454
454
        available_tags = FormattingTags.get_html_tags()
455
455
        start_tag = '{%s}' % tag_name
456
456
        for tag in available_tags:
457
 
            if tag[u'start tag'] == start_tag:
 
457
            if tag['start tag'] == start_tag:
458
458
                # Create new formatting tag in openlyrics xml.
459
 
                element = self._add_text_to_element(u'tag', tags_element)
460
 
                element.set(u'name', tag_name)
461
 
                element_open = self._add_text_to_element(u'open', element)
462
 
                element_open.text = etree.CDATA(tag[u'start html'])
 
459
                element = self._add_text_to_element('tag', tags_element)
 
460
                element.set('name', tag_name)
 
461
                element_open = self._add_text_to_element('open', element)
 
462
                element_open.text = etree.CDATA(tag['start html'])
463
463
                # Check if formatting tag contains end tag. Some formatting
464
464
                # tags e.g. {br} has only start tag. If no end tag is present
465
465
                # <close> element has not to be in OpenLyrics xml.
466
466
                if tag['end tag']:
467
 
                    element_close = self._add_text_to_element(u'close', element)
468
 
                    element_close.text = etree.CDATA(tag[u'end html'])
 
467
                    element_close = self._add_text_to_element('close', element)
 
468
                    element_close.text = etree.CDATA(tag['end html'])
469
469
 
470
470
    def _add_text_with_tags_to_lines(self, verse_element, text, tags_element):
471
471
        """
477
477
        # Replace start tags with xml syntax.
478
478
        for tag in start_tags:
479
479
            # Tags already converted to xml structure.
480
 
            xml_tags = tags_element.xpath(u'tag/attribute::name')
 
480
            xml_tags = tags_element.xpath('tag/attribute::name')
481
481
            # Some formatting tag has only starting part e.g. <br>.
482
482
            # Handle this case.
483
483
            if tag in end_tags:
484
 
                text = text.replace(u'{%s}' % tag, u'<tag name="%s">' % tag)
 
484
                text = text.replace('{%s}' % tag, '<tag name="%s">' % tag)
485
485
            else:
486
 
                text = text.replace(u'{%s}' % tag, u'<tag name="%s"/>' % tag)
 
486
                text = text.replace('{%s}' % tag, '<tag name="%s"/>' % tag)
487
487
            # Add tag to <format> element if tag not present.
488
488
            if tag not in xml_tags:
489
489
                self._add_tag_to_formatting(tag, tags_element)
490
490
        # Replace end tags.
491
491
        for tag in end_tags:
492
 
            text = text.replace(u'{/%s}' % tag, u'</tag>')
 
492
            text = text.replace('{/%s}' % tag, '</tag>')
493
493
        # Replace \n with <br/>.
494
 
        text = text.replace(u'\n', u'<br/>')
495
 
        element = etree.XML(u'<lines>%s</lines>' % text)
 
494
        text = text.replace('\n', '<br/>')
 
495
        element = etree.XML('<lines>%s</lines>' % text)
496
496
        verse_element.append(element)
497
497
        return element
498
498
 
500
500
        """
501
501
        Extract our newly created XML song.
502
502
        """
503
 
        return etree.tostring(xml, encoding=u'UTF-8',
 
503
        return etree.tostring(xml, encoding='UTF-8',
504
504
            xml_declaration=True)
505
505
 
506
506
    def _text(self, element):
511
511
            The element.
512
512
        """
513
513
        if element.text is not None:
514
 
            return unicode(element.text)
515
 
        return u''
 
514
            return str(element.text)
 
515
        return ''
516
516
 
517
517
    def _process_authors(self, properties, song):
518
518
        """
525
525
            The song object.
526
526
        """
527
527
        authors = []
528
 
        if hasattr(properties, u'authors'):
 
528
        if hasattr(properties, 'authors'):
529
529
            for author in properties.authors.author:
530
530
                display_name = self._text(author)
531
531
                if display_name:
536
536
            if author is None:
537
537
                # We need to create a new author, as the author does not exist.
538
538
                author = Author.populate(display_name=display_name,
539
 
                    last_name=display_name.split(u' ')[-1],
540
 
                    first_name=u' '.join(display_name.split(u' ')[:-1]))
 
539
                    last_name=display_name.split(' ')[-1],
 
540
                    first_name=' '.join(display_name.split(' ')[:-1]))
541
541
            song.authors.append(author)
542
542
 
543
543
    def _process_cclinumber(self, properties, song):
550
550
        ``song``
551
551
            The song object.
552
552
        """
553
 
        if hasattr(properties, u'ccliNo'):
 
553
        if hasattr(properties, 'ccliNo'):
554
554
            song.ccli_number = self._text(properties.ccliNo)
555
555
 
556
556
    def _process_comments(self, properties, song):
563
563
        ``song``
564
564
            The song object.
565
565
        """
566
 
        if hasattr(properties, u'comments'):
 
566
        if hasattr(properties, 'comments'):
567
567
            comments_list = []
568
568
            for comment in properties.comments.comment:
569
569
                comment_text = self._text(comment)
570
570
                if comment_text:
571
571
                    comments_list.append(comment_text)
572
 
            song.comments = u'\n'.join(comments_list)
 
572
            song.comments = '\n'.join(comments_list)
573
573
 
574
574
    def _process_copyright(self, properties, song):
575
575
        """
581
581
        ``song``
582
582
            The song object.
583
583
        """
584
 
        if hasattr(properties, u'copyright'):
 
584
        if hasattr(properties, 'copyright'):
585
585
            song.copyright = self._text(properties.copyright)
586
586
 
587
587
    def _process_formatting_tags(self, song_xml, temporary):
589
589
        Process the formatting tags from the song and either add missing tags
590
590
        temporary or permanently to the formatting tag list.
591
591
        """
592
 
        if not hasattr(song_xml, u'format'):
 
592
        if not hasattr(song_xml, 'format'):
593
593
            return
594
594
        found_tags = []
595
595
        for tag in song_xml.format.tags.getchildren():
596
 
            name = tag.get(u'name')
 
596
            name = tag.get('name')
597
597
            if name is None:
598
598
                continue
599
 
            start_tag = u'{%s}' % name[:5]
 
599
            start_tag = '{%s}' % name[:5]
600
600
            # Some tags have only start tag e.g. {br}
601
 
            end_tag = u'{/' + name[:5] + u'}' if hasattr(tag, 'close') else u''
 
601
            end_tag = '{/' + name[:5] + '}' if hasattr(tag, 'close') else ''
602
602
            openlp_tag = {
603
 
                u'desc': name,
604
 
                u'start tag': start_tag,
605
 
                u'end tag': end_tag,
606
 
                u'start html': tag.open.text,
 
603
                'desc': name,
 
604
                'start tag': start_tag,
 
605
                'end tag': end_tag,
 
606
                'start html': tag.open.text,
607
607
                # Some tags have only start html e.g. {br}
608
 
                u'end html': tag.close.text if hasattr(tag, 'close') else u'',
609
 
                u'protected': False,
 
608
                'end html': tag.close.text if hasattr(tag, 'close') else '',
 
609
                'protected': False,
610
610
            }
611
611
            # Add 'temporary' key in case the formatting tag should not be
612
612
            # saved otherwise it is supposed that formatting tag is permanent.
613
613
            if temporary:
614
 
                openlp_tag[u'temporary'] = temporary
 
614
                openlp_tag['temporary'] = temporary
615
615
            found_tags.append(openlp_tag)
616
 
        existing_tag_ids = [tag[u'start tag'] for tag in FormattingTags.get_html_tags()]
617
 
        new_tags = [tag for tag in found_tags if tag[u'start tag'] not in existing_tag_ids]
 
616
        existing_tag_ids = [tag['start tag'] for tag in FormattingTags.get_html_tags()]
 
617
        new_tags = [tag for tag in found_tags if tag['start tag'] not in existing_tag_ids]
618
618
        FormattingTags.add_html_tags(new_tags)
619
619
        FormattingTags.save_html_tags()
620
620
 
630
630
            The switch to enable/disable processing of line breaks <br/>.
631
631
            The <br/> is used since OpenLyrics 0.8.
632
632
        """
633
 
        text = u''
 
633
        text = ''
634
634
        use_endtag = True
635
635
        # Skip <comment> elements - not yet supported.
636
 
        if element.tag == NSMAP % u'comment':
 
636
        if element.tag == NSMAP % 'comment':
637
637
            if element.tail:
638
638
                # Append tail text at chord element.
639
639
                text += element.tail
640
640
            return text
641
641
        # Skip <chord> element - not yet supported.
642
 
        elif element.tag == NSMAP % u'chord':
 
642
        elif element.tag == NSMAP % 'chord':
643
643
            if element.tail:
644
644
                # Append tail text at chord element.
645
645
                text += element.tail
646
646
            return text
647
647
        # Convert line breaks <br/> to \n.
648
 
        elif newlines and element.tag == NSMAP % u'br':
649
 
            text += u'\n'
 
648
        elif newlines and element.tag == NSMAP % 'br':
 
649
            text += '\n'
650
650
            if element.tail:
651
651
                text += element.tail
652
652
            return text
653
653
        # Start formatting tag.
654
 
        if element.tag == NSMAP % u'tag':
655
 
            text += u'{%s}' % element.get(u'name')
 
654
        if element.tag == NSMAP % 'tag':
 
655
            text += '{%s}' % element.get('name')
656
656
            # Some formattings may have only start tag.
657
657
            # Handle this case if element has no children and contains no text.
658
658
            if not element and not element.text:
666
666
            text += self._process_lines_mixed_content(child, newlines)
667
667
        # Append text from tail and add formatting end tag.
668
668
        if element.tag == NSMAP % 'tag' and use_endtag:
669
 
            text += u'{/%s}' % element.get(u'name')
 
669
            text += '{/%s}' % element.get('name')
670
670
        # Append text from tail.
671
671
        if element.tail:
672
672
            text += element.tail
679
679
        ``lines``
680
680
            The lines object (lxml.objectify.ObjectifiedElement).
681
681
        """
682
 
        text = u''
 
682
        text = ''
683
683
        # Convert lxml.objectify to lxml.etree representation.
684
684
        lines = etree.tostring(lines)
685
685
        element = etree.XML(lines)
694
694
            # Loop over the "line" elements removing comments and chords.
695
695
            for line in element:
696
696
                # Skip comment lines.
697
 
                if line.tag == NSMAP % u'comment':
 
697
                if line.tag == NSMAP % 'comment':
698
698
                    continue
699
699
                if text:
700
 
                    text += u'\n'
 
700
                    text += '\n'
701
701
                text += self._process_lines_mixed_content(line, newlines=False)
702
702
        return text
703
703
 
729
729
                translate('OpenLP.OpenLyricsImportError', '<verse> tag is missing.'))
730
730
        # Loop over the "verse" elements.
731
731
        for verse in verse_list:
732
 
            text = u''
 
732
            text = ''
733
733
            # Loop over the "lines" elements.
734
734
            for lines in verse.lines:
735
735
                if text:
736
 
                    text += u'\n'
 
736
                    text += '\n'
737
737
                # Append text from "lines" element to verse text.
738
738
                text += self._process_verse_lines(lines,
739
 
                    version=song_xml.get(u'version'))
 
739
                    version=song_xml.get('version'))
740
740
                # Add an optional split to the verse text.
741
 
                if lines.get(u'break') is not None:
742
 
                    text += u'\n[---]'
743
 
            verse_def = verse.get(u'name', u' ').lower()
 
741
                if lines.get('break') is not None:
 
742
                    text += '\n[---]'
 
743
            verse_def = verse.get('name', ' ').lower()
744
744
            verse_tag, verse_number, verse_part = OpenLyrics.VERSE_TAG_SPLITTER.search(verse_def).groups()
745
745
            if verse_tag not in VerseType.Tags:
746
746
                verse_tag = VerseType.Tags[VerseType.Other]
747
747
            # OpenLyrics allows e. g. "c", but we need "c1". However, this does
748
748
            # not correct the verse order.
749
749
            if not verse_number:
750
 
                verse_number = u'1'
751
 
            lang = verse.get(u'lang')
752
 
            translit = verse.get(u'translit')
 
750
                verse_number = '1'
 
751
            lang = verse.get('lang')
 
752
            translit = verse.get('translit')
753
753
            # In OpenLP 1.9.6 we used v1a, v1b ... to represent visual slide
754
754
            # breaks. In OpenLyrics 0.7 an attribute has been added.
755
 
            if song_xml.get(u'modifiedIn') in (u'1.9.6', u'OpenLP 1.9.6') and \
756
 
                    song_xml.get(u'version') == u'0.7' and (verse_tag, verse_number, lang, translit) in verses:
757
 
                verses[(verse_tag, verse_number, lang, translit, None)] += u'\n[---]\n' + text
 
755
            if song_xml.get('modifiedIn') in ('1.9.6', 'OpenLP 1.9.6') and \
 
756
                    song_xml.get('version') == '0.7' and (verse_tag, verse_number, lang, translit) in verses:
 
757
                verses[(verse_tag, verse_number, lang, translit, None)] += '\n[---]\n' + text
758
758
            # Merge v1a, v1b, .... to v1.
759
759
            elif (verse_tag, verse_number, lang, translit, verse_part) in verses:
760
 
                verses[(verse_tag, verse_number, lang, translit, verse_part)] += u'\n' + text
 
760
                verses[(verse_tag, verse_number, lang, translit, verse_part)] += '\n' + text
761
761
            else:
762
762
                verses[(verse_tag, verse_number, lang, translit, verse_part)] = text
763
763
                verse_def_list.append((verse_tag, verse_number, lang, translit, verse_part))
764
764
        # We have to use a list to keep the order, as dicts are not sorted.
765
765
        for verse in verse_def_list:
766
766
            sxml.add_verse_to_lyrics(verse[0], verse[1], verses[verse], verse[2])
767
 
        song_obj.lyrics = unicode(sxml.extract_xml(), u'utf-8')
 
767
        song_obj.lyrics = str(sxml.extract_xml(), 'utf-8')
768
768
        # Process verse order
769
 
        if hasattr(properties, u'verseOrder'):
 
769
        if hasattr(properties, 'verseOrder'):
770
770
            song_obj.verse_order = self._text(properties.verseOrder)
771
771
 
772
772
    def _process_songbooks(self, properties, song):
780
780
            The song object.
781
781
        """
782
782
        song.song_book_id = None
783
 
        song.song_number = u''
784
 
        if hasattr(properties, u'songbooks'):
 
783
        song.song_number = ''
 
784
        if hasattr(properties, 'songbooks'):
785
785
            for songbook in properties.songbooks.songbook:
786
 
                book_name = songbook.get(u'name', u'')
 
786
                book_name = songbook.get('name', '')
787
787
                if book_name:
788
788
                    book = self.manager.get_object_filtered(Book, Book.name == book_name)
789
789
                    if book is None:
790
790
                        # We need to create a book, because it does not exist.
791
 
                        book = Book.populate(name=book_name, publisher=u'')
 
791
                        book = Book.populate(name=book_name, publisher='')
792
792
                        self.manager.save_object(book)
793
793
                    song.song_book_id = book.id
794
 
                    song.song_number = songbook.get(u'entry', u'')
 
794
                    song.song_number = songbook.get('entry', '')
795
795
                    # We only support one song book, so take the first one.
796
796
                    break
797
797
 
808
808
        for title in properties.titles.title:
809
809
            if not song.title:
810
810
                song.title = self._text(title)
811
 
                song.alternate_title = u''
 
811
                song.alternate_title = ''
812
812
            else:
813
813
                song.alternate_title = self._text(title)
814
814
 
822
822
        ``song``
823
823
            The song object.
824
824
        """
825
 
        if hasattr(properties, u'themes'):
 
825
        if hasattr(properties, 'themes'):
826
826
            for topic_text in properties.themes.theme:
827
827
                topic_text = self._text(topic_text)
828
828
                if topic_text:
837
837
        """
838
838
        Debugging aid to dump XML so that we can see what we have.
839
839
        """
840
 
        return etree.tostring(xml, encoding=u'UTF-8', xml_declaration=True, pretty_print=True)
 
840
        return etree.tostring(xml, encoding='UTF-8', xml_declaration=True, pretty_print=True)
841
841
 
842
842
 
843
843
class OpenLyricsError(Exception):