2010-06-12 01:52:04 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2013-01-06 17:25:49 +00:00
|
|
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
2010-06-12 01:52:04 +00:00
|
|
|
|
|
|
|
###############################################################################
|
|
|
|
# OpenLP - Open Source Lyrics Projection #
|
|
|
|
# --------------------------------------------------------------------------- #
|
2013-12-24 08:56:50 +00:00
|
|
|
# Copyright (c) 2008-2014 Raoul Snyman #
|
|
|
|
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
2012-06-22 14:14:53 +00:00
|
|
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
2012-11-11 21:16:14 +00:00
|
|
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
2012-10-21 13:16:22 +00:00
|
|
|
# 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, #
|
2012-12-01 07:57:54 +00:00
|
|
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
2010-06-12 01:52:04 +00:00
|
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
# 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:`db` module provides the database and schema that is the backend for
|
|
|
|
the Songs plugin
|
|
|
|
"""
|
|
|
|
|
2012-10-16 15:23:37 +00:00
|
|
|
import re
|
|
|
|
|
2011-07-07 18:03:12 +00:00
|
|
|
from sqlalchemy import Column, ForeignKey, Table, types
|
2012-10-14 11:11:48 +00:00
|
|
|
from sqlalchemy.orm import mapper, relation, reconstructor
|
2014-03-30 17:38:26 +00:00
|
|
|
from sqlalchemy.sql.expression import func, text
|
2010-06-12 01:52:04 +00:00
|
|
|
|
2011-08-25 09:22:48 +00:00
|
|
|
from openlp.core.lib.db import BaseModel, init_db
|
2013-03-31 10:31:54 +00:00
|
|
|
from openlp.core.utils import get_natural_key
|
2014-03-28 16:06:16 +00:00
|
|
|
from openlp.core.lib import translate
|
2010-06-12 01:52:04 +00:00
|
|
|
|
2012-11-08 21:28:42 +00:00
|
|
|
|
2010-06-12 01:52:04 +00:00
|
|
|
class Author(BaseModel):
|
|
|
|
"""
|
|
|
|
Author model
|
|
|
|
"""
|
2014-03-28 16:06:16 +00:00
|
|
|
def get_display_name(self, author_type=None):
|
|
|
|
if author_type:
|
2014-04-21 14:40:54 +00:00
|
|
|
return "%s (%s)" % (self.display_name, AuthorType.Types[author_type])
|
2014-03-28 16:06:16 +00:00
|
|
|
return self.display_name
|
|
|
|
|
2014-04-01 21:07:49 +00:00
|
|
|
|
2014-03-28 16:06:16 +00:00
|
|
|
class AuthorSong(BaseModel):
|
|
|
|
"""
|
|
|
|
Relationship between Authors and Songs (many to many).
|
|
|
|
Need to define this relationship table explicit to get access to the
|
|
|
|
Association Object (author_type).
|
|
|
|
http://docs.sqlalchemy.org/en/latest/orm/relationships.html#association-object
|
|
|
|
"""
|
2010-06-12 01:52:04 +00:00
|
|
|
pass
|
|
|
|
|
2014-04-01 21:07:49 +00:00
|
|
|
|
2014-03-30 17:23:36 +00:00
|
|
|
class AuthorType(object):
|
|
|
|
"""
|
|
|
|
Enumeration for Author types.
|
|
|
|
They are defined by OpenLyrics: http://openlyrics.info/dataformat.html#authors
|
2014-04-08 18:52:05 +00:00
|
|
|
|
|
|
|
The 'words+music' type is not an official type, but is provided for convenience.
|
2014-03-30 17:23:36 +00:00
|
|
|
"""
|
|
|
|
Words = 'words'
|
|
|
|
Music = 'music'
|
2014-04-08 18:52:05 +00:00
|
|
|
WordsAndMusic = 'words+music'
|
2014-03-30 17:23:36 +00:00
|
|
|
Translation = 'translation'
|
|
|
|
Types = {
|
|
|
|
Words: translate('OpenLP.Ui', 'Words'),
|
|
|
|
Music: translate('OpenLP.Ui', 'Music'),
|
2014-04-08 18:52:05 +00:00
|
|
|
WordsAndMusic: translate('OpenLP.Ui', 'Words and Music'),
|
2014-03-30 17:23:36 +00:00
|
|
|
Translation: translate('OpenLP.Ui', 'Translation')
|
|
|
|
}
|
2011-01-18 16:42:59 +00:00
|
|
|
|
2014-04-01 21:07:49 +00:00
|
|
|
|
2010-06-12 01:52:04 +00:00
|
|
|
class Book(BaseModel):
|
|
|
|
"""
|
|
|
|
Book model
|
|
|
|
"""
|
|
|
|
def __repr__(self):
|
2013-08-31 18:17:38 +00:00
|
|
|
return '<Book id="%s" name="%s" publisher="%s" />' % (str(self.id), self.name, self.publisher)
|
2010-06-12 01:52:04 +00:00
|
|
|
|
2014-04-01 21:07:49 +00:00
|
|
|
|
2010-07-20 12:43:21 +00:00
|
|
|
class MediaFile(BaseModel):
|
|
|
|
"""
|
|
|
|
MediaFile model
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
2011-01-18 16:42:59 +00:00
|
|
|
|
2010-06-12 01:52:04 +00:00
|
|
|
class Song(BaseModel):
|
|
|
|
"""
|
|
|
|
Song model
|
|
|
|
"""
|
2014-03-28 16:06:16 +00:00
|
|
|
|
2012-10-14 11:11:48 +00:00
|
|
|
def __init__(self):
|
2013-07-07 14:08:47 +00:00
|
|
|
self.sort_key = []
|
2012-11-08 21:28:42 +00:00
|
|
|
|
2012-10-14 11:11:48 +00:00
|
|
|
@reconstructor
|
|
|
|
def init_on_load(self):
|
|
|
|
"""
|
2013-03-31 10:31:54 +00:00
|
|
|
Precompute a natural sorting, locale aware sorting key.
|
2012-10-14 11:11:48 +00:00
|
|
|
|
|
|
|
Song sorting is performance sensitive operation.
|
2013-03-31 10:31:54 +00:00
|
|
|
To get maximum speed lets precompute the sorting key.
|
2012-10-14 11:11:48 +00:00
|
|
|
"""
|
2013-03-31 10:31:54 +00:00
|
|
|
self.sort_key = get_natural_key(self.title)
|
2010-06-12 01:52:04 +00:00
|
|
|
|
2014-05-07 09:51:59 +00:00
|
|
|
def add_author(self, author, author_type=None, ignore_type=False):
|
|
|
|
"""
|
|
|
|
Add an author to the song if it not yet exists
|
|
|
|
|
|
|
|
:return: True if the author has been added successfully. False if it was already there.
|
|
|
|
"""
|
|
|
|
for author_song in self.authors_songs:
|
|
|
|
if author_song.author == author and (ignore_type or author_song.author_type == author_type):
|
|
|
|
return False
|
|
|
|
new_author_song = AuthorSong()
|
|
|
|
new_author_song.author = author
|
|
|
|
new_author_song.author_type = author_type
|
|
|
|
self.authors_songs.append(new_author_song)
|
|
|
|
return True
|
|
|
|
|
|
|
|
def remove_author(self, author, author_type=None, ignore_type=False):
|
|
|
|
"""
|
|
|
|
Remove an existing author from the song
|
|
|
|
|
|
|
|
:return: True if the author has been removed successfully. False if it could not be found.
|
|
|
|
"""
|
|
|
|
for author_song in self.authors_songs:
|
|
|
|
if author_song.author == author and (ignore_type or author_song.author_type == author_type):
|
|
|
|
self.authors_songs.remove(author_song)
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2011-01-18 16:42:59 +00:00
|
|
|
|
2010-06-12 01:52:04 +00:00
|
|
|
class Topic(BaseModel):
|
|
|
|
"""
|
|
|
|
Topic model
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
2014-03-06 20:40:08 +00:00
|
|
|
|
2010-06-12 01:52:04 +00:00
|
|
|
def init_schema(url):
|
|
|
|
"""
|
2011-01-18 16:42:59 +00:00
|
|
|
Setup the songs database connection and initialise the database schema.
|
2010-06-12 01:52:04 +00:00
|
|
|
|
2014-03-17 19:05:55 +00:00
|
|
|
:param url: The database to setup
|
2011-01-18 16:42:59 +00:00
|
|
|
The song database contains the following tables:
|
2011-02-03 17:11:41 +00:00
|
|
|
|
2011-01-14 17:33:48 +00:00
|
|
|
* authors
|
|
|
|
* authors_songs
|
|
|
|
* media_files
|
|
|
|
* media_files_songs
|
|
|
|
* song_books
|
|
|
|
* songs
|
|
|
|
* songs_topics
|
|
|
|
* topics
|
|
|
|
|
2011-02-03 17:11:41 +00:00
|
|
|
**authors** Table
|
2011-01-14 17:33:48 +00:00
|
|
|
This table holds the names of all the authors. It has the following
|
|
|
|
columns:
|
|
|
|
|
2011-02-03 17:11:41 +00:00
|
|
|
* id
|
|
|
|
* first_name
|
|
|
|
* last_name
|
|
|
|
* display_name
|
|
|
|
|
|
|
|
**authors_songs Table**
|
2011-01-18 16:42:59 +00:00
|
|
|
This is a bridging table between the *authors* and *songs* tables, which
|
2011-01-14 17:33:48 +00:00
|
|
|
serves to create a many-to-many relationship between the two tables. It
|
|
|
|
has the following columns:
|
2011-02-03 17:11:41 +00:00
|
|
|
|
|
|
|
* author_id
|
|
|
|
* song_id
|
2014-03-28 16:06:16 +00:00
|
|
|
* author_type
|
2011-02-03 17:11:41 +00:00
|
|
|
|
|
|
|
**media_files Table**
|
|
|
|
* id
|
|
|
|
* file_name
|
|
|
|
* type
|
|
|
|
|
|
|
|
**song_books Table**
|
2011-01-18 16:42:59 +00:00
|
|
|
The *song_books* table holds a list of books that a congregation gets
|
2011-01-14 17:33:48 +00:00
|
|
|
their songs from, or old hymnals now no longer used. This table has the
|
|
|
|
following columns:
|
|
|
|
|
2011-02-03 17:11:41 +00:00
|
|
|
* id
|
|
|
|
* name
|
|
|
|
* publisher
|
|
|
|
|
|
|
|
**songs Table**
|
2011-01-14 17:33:48 +00:00
|
|
|
This table contains the songs, and each song has a list of attributes.
|
2011-01-18 16:42:59 +00:00
|
|
|
The *songs* table has the following columns:
|
2011-02-03 17:11:41 +00:00
|
|
|
|
|
|
|
* id
|
|
|
|
* song_book_id
|
|
|
|
* title
|
|
|
|
* alternate_title
|
|
|
|
* lyrics
|
|
|
|
* verse_order
|
|
|
|
* copyright
|
|
|
|
* comments
|
|
|
|
* ccli_number
|
|
|
|
* song_number
|
|
|
|
* theme_name
|
|
|
|
* search_title
|
|
|
|
* search_lyrics
|
|
|
|
|
|
|
|
**songs_topics Table**
|
2011-01-18 16:42:59 +00:00
|
|
|
This is a bridging table between the *songs* and *topics* tables, which
|
2011-01-14 17:33:48 +00:00
|
|
|
serves to create a many-to-many relationship between the two tables. It
|
|
|
|
has the following columns:
|
|
|
|
|
2011-02-03 17:11:41 +00:00
|
|
|
* song_id
|
|
|
|
* topic_id
|
|
|
|
|
|
|
|
**topics Table**
|
2011-01-14 17:33:48 +00:00
|
|
|
The topics table holds a selection of topics that songs can cover. This
|
|
|
|
is useful when a worship leader wants to select songs with a certain
|
|
|
|
theme. This table has the following columns:
|
2011-02-03 17:11:41 +00:00
|
|
|
|
|
|
|
* id
|
|
|
|
* name
|
2010-06-12 01:52:04 +00:00
|
|
|
"""
|
2010-08-28 19:32:24 +00:00
|
|
|
session, metadata = init_db(url)
|
2010-06-12 01:52:04 +00:00
|
|
|
|
|
|
|
# Definition of the "authors" table
|
2014-03-21 21:38:08 +00:00
|
|
|
authors_table = Table(
|
|
|
|
'authors', metadata,
|
2013-08-31 18:17:38 +00:00
|
|
|
Column('id', types.Integer(), primary_key=True),
|
|
|
|
Column('first_name', types.Unicode(128)),
|
|
|
|
Column('last_name', types.Unicode(128)),
|
|
|
|
Column('display_name', types.Unicode(255), index=True, nullable=False)
|
2010-06-12 01:52:04 +00:00
|
|
|
)
|
|
|
|
|
2010-07-20 12:43:21 +00:00
|
|
|
# Definition of the "media_files" table
|
2014-03-21 21:38:08 +00:00
|
|
|
media_files_table = Table(
|
|
|
|
'media_files', metadata,
|
2013-08-31 18:17:38 +00:00
|
|
|
Column('id', types.Integer(), primary_key=True),
|
2014-03-06 20:40:08 +00:00
|
|
|
Column('song_id', types.Integer(), ForeignKey('songs.id'), default=None),
|
2013-08-31 18:17:38 +00:00
|
|
|
Column('file_name', types.Unicode(255), nullable=False),
|
|
|
|
Column('type', types.Unicode(64), nullable=False, default='audio'),
|
|
|
|
Column('weight', types.Integer(), default=0)
|
2010-07-20 12:43:21 +00:00
|
|
|
)
|
|
|
|
|
2010-06-12 01:52:04 +00:00
|
|
|
# Definition of the "song_books" table
|
2014-03-21 21:38:08 +00:00
|
|
|
song_books_table = Table(
|
|
|
|
'song_books', metadata,
|
2013-08-31 18:17:38 +00:00
|
|
|
Column('id', types.Integer(), primary_key=True),
|
|
|
|
Column('name', types.Unicode(128), nullable=False),
|
|
|
|
Column('publisher', types.Unicode(128))
|
2010-06-12 01:52:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
# Definition of the "songs" table
|
2014-03-21 21:38:08 +00:00
|
|
|
songs_table = Table(
|
|
|
|
'songs', metadata,
|
2013-08-31 18:17:38 +00:00
|
|
|
Column('id', types.Integer(), primary_key=True),
|
2014-03-06 20:40:08 +00:00
|
|
|
Column('song_book_id', types.Integer(), ForeignKey('song_books.id'), default=None),
|
2013-08-31 18:17:38 +00:00
|
|
|
Column('title', types.Unicode(255), nullable=False),
|
|
|
|
Column('alternate_title', types.Unicode(255)),
|
|
|
|
Column('lyrics', types.UnicodeText, nullable=False),
|
|
|
|
Column('verse_order', types.Unicode(128)),
|
|
|
|
Column('copyright', types.Unicode(255)),
|
|
|
|
Column('comments', types.UnicodeText),
|
|
|
|
Column('ccli_number', types.Unicode(64)),
|
|
|
|
Column('song_number', types.Unicode(64)),
|
|
|
|
Column('theme_name', types.Unicode(128)),
|
|
|
|
Column('search_title', types.Unicode(255), index=True, nullable=False),
|
|
|
|
Column('search_lyrics', types.UnicodeText, nullable=False),
|
|
|
|
Column('create_date', types.DateTime(), default=func.now()),
|
2014-03-06 20:40:08 +00:00
|
|
|
Column('last_modified', types.DateTime(), default=func.now(), onupdate=func.now()),
|
2013-08-31 18:17:38 +00:00
|
|
|
Column('temporary', types.Boolean(), default=False)
|
2010-06-12 01:52:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
# Definition of the "topics" table
|
2014-03-21 21:38:08 +00:00
|
|
|
topics_table = Table(
|
|
|
|
'topics', metadata,
|
2013-08-31 18:17:38 +00:00
|
|
|
Column('id', types.Integer(), primary_key=True),
|
|
|
|
Column('name', types.Unicode(128), index=True, nullable=False)
|
2010-06-12 01:52:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
# Definition of the "authors_songs" table
|
2014-03-21 21:38:08 +00:00
|
|
|
authors_songs_table = Table(
|
|
|
|
'authors_songs', metadata,
|
2014-03-06 20:40:08 +00:00
|
|
|
Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True),
|
2014-03-28 16:06:16 +00:00
|
|
|
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
|
2014-03-30 17:38:26 +00:00
|
|
|
Column('author_type', types.String(), primary_key=True, nullable=False, server_default=text('""'))
|
2010-07-20 12:43:21 +00:00
|
|
|
)
|
|
|
|
|
2010-06-12 01:52:04 +00:00
|
|
|
# Definition of the "songs_topics" table
|
2014-03-21 21:38:08 +00:00
|
|
|
songs_topics_table = Table(
|
|
|
|
'songs_topics', metadata,
|
2014-03-06 20:40:08 +00:00
|
|
|
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
|
|
|
|
Column('topic_id', types.Integer(), ForeignKey('topics.id'), primary_key=True)
|
2010-06-12 01:52:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
mapper(Author, authors_table)
|
2014-03-28 16:06:16 +00:00
|
|
|
mapper(AuthorSong, authors_songs_table, properties={
|
|
|
|
'author': relation(Author)
|
|
|
|
})
|
2010-06-12 01:52:04 +00:00
|
|
|
mapper(Book, song_books_table)
|
2010-07-20 12:43:21 +00:00
|
|
|
mapper(MediaFile, media_files_table)
|
2014-03-21 21:38:08 +00:00
|
|
|
mapper(Song, songs_table, properties={
|
2014-05-07 09:51:59 +00:00
|
|
|
# Use the authors_songs relation when you need access to the 'author_type' attribute
|
|
|
|
# or when creating new relations
|
2014-03-28 16:06:16 +00:00
|
|
|
'authors_songs': relation(AuthorSong, cascade="all, delete-orphan"),
|
2014-05-07 09:51:59 +00:00
|
|
|
'authors': relation(Author, secondary=authors_songs_table, viewonly=True),
|
2014-03-21 21:38:08 +00:00
|
|
|
'book': relation(Book, backref='songs'),
|
|
|
|
'media_files': relation(MediaFile, backref='songs', order_by=media_files_table.c.weight),
|
|
|
|
'topics': relation(Topic, backref='songs', secondary=songs_topics_table)
|
|
|
|
})
|
2010-06-12 01:52:04 +00:00
|
|
|
mapper(Topic, topics_table)
|
|
|
|
|
2010-06-12 02:14:18 +00:00
|
|
|
metadata.create_all(checkfirst=True)
|
2011-01-14 17:33:48 +00:00
|
|
|
return session
|