Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
5f85808
Correct order_items title
May 15, 2015
138c9d7
add pseudo method to retrieve song data
May 19, 2015
03f66fd
Include https://github.com/oliverpool/chord-diagram
May 19, 2015
b264ada
Include more information and the chords on the song page
May 19, 2015
3d5e9ba
Fix the Letter overview color in case of late less loading
May 19, 2015
6f2a68f
First step for editing songs
Sep 5, 2015
a1c00d7
Improve song display
Sep 8, 2015
282a37f
In chord rendering: 0 != None
Sep 11, 2015
d2e443c
Nice alterations for displayed chords
Sep 11, 2015
89bfa17
Improve album display
Sep 11, 2015
525b93f
Patacrep actually supports 'capo'
Sep 15, 2015
1afc3c0
Hide languge in the SongView
Sep 15, 2015
1acf21a
Use patacrep methods to retrieve song content
Sep 17, 2015
ca96b50
Manage define rendering in template
Sep 17, 2015
8daf18f
Patacrep change to retrieve the cover fullpath
Sep 17, 2015
0f340c1
Reflect last patacrep changes
Sep 18, 2015
bedd64f
Render the body of the song with patacrep
Sep 20, 2015
88f2997
Forgotten template file
Sep 20, 2015
f9a00e5
Coherent space indentation
Sep 20, 2015
129acd7
Implement a template tag to render a song part
Sep 20, 2015
46efc39
Automatically adapth filepath to be a URL
Sep 21, 2015
6fc80dc
Remove the static tag from the template for the cover
Sep 21, 2015
9d3ef71
Reflect last patacrep changes
Sep 22, 2015
cbf5bb7
Created a Chordpro2HtmlSong class
Sep 23, 2015
8564455
`patacrep.songs.chordpro.ChordproSong.render()` signature changed
Sep 23, 2015
0198d18
Redefining `search_*()` to render files
Sep 23, 2015
9e77139
Deleted method `Song.cover_filepath`
Sep 23, 2015
27c5c44
Removed useless trace
Sep 23, 2015
cbb1909
Return None for search_file if the file wasn't found
Sep 24, 2015
a87efa8
Replace the soncover filter with a search_image filter
Sep 24, 2015
eb25492
Deprectad method get_cover_url
Sep 24, 2015
08716be
useless import
Sep 24, 2015
bdb6711
Template tag render_song removal
Sep 24, 2015
5c1e810
Filters reorganization
Sep 24, 2015
aeabeb9
Merge pull request #139 from patacrep/poc_html_rendering
Sep 24, 2015
f6e2c4e
Gather songbook building steps
Oct 18, 2015
5cf4836
Move songbook reading inside patacrep wrapper
Oct 18, 2015
3423321
request.REQUEST will be deprecated in django 1.9
Oct 18, 2015
4d400c7
Generic fk and relation belong to module fields in django 1.8
Oct 18, 2015
2d21c2c
Merge pull request #141 from patacrep/django_warnings
Luthaf Oct 18, 2015
2b0308e
Merge pull request #140 from patacrep/patacrep_bridge
Luthaf Oct 18, 2015
50d4c12
Reflect last patacrep changes
Oct 21, 2015
a8ac5b5
Remove useless custom template
Oct 21, 2015
87c1f29
Song content is visible only to connected users
Oct 21, 2015
d33293c
_read_song method is useless
Oct 21, 2015
8af8ad0
Rename patanet class to HtmlSong
Oct 21, 2015
dde8a48
Remove useless argument
Oct 22, 2015
7411638
Import songs refletc the last patacrep changes
Oct 27, 2015
cb8417c
Add a cleancatalog command to remove the songs, artists and songbook …
Oct 27, 2015
d2cecf0
Remove useless imports
Oct 27, 2015
cde15b1
Merge pull request #142 from patacrep/import_songs
Oct 27, 2015
765346e
Improve 'choose songbook' message when there is no songbook and corre…
Oct 27, 2015
1f3a326
Slight refactoring of import_songs
Oct 28, 2015
3f2a11e
Use __str__ instead of __unicode__ (Python3)
Oct 28, 2015
1d681d6
Split the songbook preparation and generation steps
Oct 28, 2015
69f3f48
Fix bug in admin interface
Oct 28, 2015
791ae8f
Add a link to .tex file in admin interface
Oct 28, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion generator/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,11 @@ def log_link(self, obj):
match = re.search('([^" \']+\.log)', msg)
if(match):
url = settings.MEDIA_URL +'PDF/' + match.group(0)
return mark_safe("<a href='%s'>Voir les logs</a>" % url)
texurl = url[:-3] + "tex"
return mark_safe(
"Fichier <a href='%s'>.log</a>, <a href='%s'>.tex</a>"
% (url, texurl)
)
return None

log_link.allow_tags = True
Expand Down
22 changes: 10 additions & 12 deletions generator/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
from generator.models import Songbook, Layout, Task
from django.conf import settings

from patacrep.build import SongbookBuilder
from patacrep.errors import SongbookError
from generator.patacrep import build_songbook

import os
import hashlib
Expand All @@ -45,8 +44,8 @@ def __str__(self):
return "[PDF Generator error] {0}". format(self.message)


def generate_songbook(songbook, layout):
"""Generate a PDF file by combining a songbook and a layout"""
def prepare_songbook(songbook, layout):
"""Return the filename and songbook content"""

content = {}
content.update(songbook.get_as_json())
Expand All @@ -57,18 +56,17 @@ def generate_songbook(songbook, layout):
tmpfile = str(songbook.id) + '-' + str(layout.id) + '-' + \
hashlib.sha1(str(content).encode()).hexdigest()[0:20]

return tmpfile, content

def generate_songbook(filename, content):
"""Generate a PDF file from the content"""
try:
os.chdir(SONGBOOKS_PDFS)
except OSError:
os.makedirs(SONGBOOKS_PDFS)
os.chdir(SONGBOOKS_PDFS)

builder = SongbookBuilder(content, tmpfile)

for step in ["tex", "pdf", "sbx", "pdf", "clean"]:
try:
builder.build_steps([step])
except SongbookError as e:
raise GeneratorError("Error during the step '{0}': {1}".format(step, e))
steps = ["tex", "pdf", "sbx", "pdf", "clean"]
build_songbook(content, filename, steps)

return tmpfile + ".pdf"
return filename + ".pdf"
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2014 The Patacrep Team
# Copyright (C) 2015 The Patacrep Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
Expand All @@ -14,12 +14,14 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""
Functions for song file (.sg) rendering.
"""
from django.core.management.base import BaseCommand

def parse_song(filename):
"""Parse song 'filename', and return the corresponding HTML code."""
# TODO
with open(filename) as fd:
return fd.read()
from generator.models import Artist, Songbook

class Command(BaseCommand):
args = ""
help = "Remove all songs, artists and songbooks from the db"

def handle(self, *args, **options):
Artist.objects.all().delete()
Songbook.objects.all().delete()
20 changes: 12 additions & 8 deletions generator/management/commands/importsongs.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,38 @@

from django.core.management.base import BaseCommand
from django.conf import settings
from django.db import transaction

from generator.management.songs import import_song
from generator.patacrep import extract_metadata
from generator.management.songs import import_songs

import os
from pathlib import PurePosixPath
import logging

LOGGER = logging.getLogger(__name__)
SONGS_DIR = os.path.join(settings.SONGS_LIBRARY_DIR, "songs")

@transaction.atomic
class Command(BaseCommand):
args = ""
help = "Import song information into db"

def handle(self, *args, **options):
metadata = []
for root, _dirs, filenames in os.walk(SONGS_DIR,
topdown=True,
onerror=_file_error,
followlinks=False):
for filename in filenames:
if filename.lower().endswith(".sg"):
filepath = os.path.realpath(os.path.join(root, filename))
if filename.lower().endswith(".sgc"):
fullpath = os.path.join(root, filename)
filepath = str(PurePosixPath(fullpath).relative_to(SONGS_DIR))
try:
import_song(filepath, SONGS_DIR)
metadata.append(extract_metadata(filepath, settings.SONGS_LIBRARY_DIR))
except IOError as e:
self.stderr.write("*** Failed processing file : "
+ filepath)
+ fullpath)
self.stderr.write(" I/O error({0}): {1}"
.format(e.errno, e.strerror))

import_songs(metadata)
def _file_error(error):
print(error)
171 changes: 99 additions & 72 deletions generator/management/songs.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,87 +16,114 @@
"""
Songs management utilities
"""
from django.conf.global_settings import LANGUAGES
from django.utils.text import slugify
from django.db import transaction

from generator.models import Song, Artist
from patacrep.latex import parse_song

import pygit2 as git
import pprint
import os
import logging

LOGGER = logging.getLogger(__name__)

def import_song(filepath, song_directory):
'''Import a song in the database'''
with open(filepath) as song:
data = parse_song(song.read(), filepath)
LOGGER.info("Processing " +
pprint.pformat(data['@titles'][0]))
@transaction.atomic
def import_songs(metadata):
"""Import all the metadata into the database"""
catalog = Catalog()
songs = []
for song in metadata:
title = song['titles'][0]
file_path = song['filepath']

if file_path in catalog.filepaths:
LOGGER.info("Skip: " + title
+ "\t\t\t\t\t- Already in Database")
continue

slug = catalog.get_song_slug(title)
artist_id = catalog.get_artist_id(song['authors'][0])
language = song['lang']
object_hash = git.hashfile(song['fullpath'])

data = {}
for i in (
'title', 'file_path',
'slug', 'artist_id',
'language', 'object_hash',
):
data[i] = locals()[i]

model = Song(**data)
LOGGER.info("Add: " + title)
songs.append(model)

Song.objects.bulk_create(songs)
LOGGER.info(
" -> " +
str(len(songs)) +
" songs added to the db")

class Catalog:
"""Class used to efficiently compute slugs and prevent database collision"""
def __init__(self):
self._init_songs()
self._init_artists()

def _init_songs(self):
songs = Song.objects.values_list('file_path', 'slug')
if not songs:
self.filepaths = set()
self.song_slugs = set()
else:
self.filepaths, self.song_slugs = map(set, zip(*songs))

def get_song_slug(self, title):
slug = slugify(title)
if slug in self.song_slugs:
i = 1
tmp_slug = slug + '-' + str(i)
while tmp_slug in self.song_slugs:
i += 1
tmp_slug = slug + '-' + str(i)

LOGGER.info(
"SlugCollision: "
+ slug + ". Use "
+ tmp_slug + " instead for " + title)
slug = tmp_slug

self.song_slugs.add(slug)
return slug

def _init_artists(self):
artists = Artist.objects.values_list('id', 'slug', 'name')
self.artists = { slug: (id, name) for id, slug, name in artists }

def get_artist_name(self, name_tuple):
last_name, first_name = name_tuple
if first_name:
artist_name = first_name + ' ' + last_name
else:
artist_name = last_name

return artist_name

artist_name = data['by']
artist_slug = slugify(artist_name)
def get_artist_id(self, name_tuple):
artist_name = self.get_artist_name(name_tuple)
artist_slug = slugify(artist_name)

artist_model, created = Artist.objects.get_or_create(
try:
id, name = self.artists[artist_slug]
if (name != artist_name):
LOGGER.warning(
"*** Artist name differs though "
"slugs are equal : "
+ artist_name + " / " + name)
return id
except KeyError:
model = Artist.objects.create(
slug=artist_slug,
defaults={'name': artist_name}
name=artist_name,
)
if not created:
if (artist_model.name != artist_name):
LOGGER.warning(
"*** Artist name differs though "
"slugs are equal : "
+ artist_name + " / "
+ artist_model.name)

if (len(data['@languages']) > 1):
LOGGER.warning("*** Multiple languages "
"in song file; we though"
" only support one. "
"Picking any.")
if (len(data['@languages']) > 0):
language_name = data["@languages"].pop()
language_code = next(
(x for x in LANGUAGES
if x[1].lower() == language_name.lower()
),
('', '')
)[0]
if language_code == '':
LOGGER.warning("*** No code found for "
"language : '" + language_name + "'")

song_title = data['@titles'][0]
song_slug = slugify(song_title)

object_hash = git.hashfile(filepath)
filepath_rel = os.path.relpath(filepath, song_directory)

import random

# For some reason - probably after having interrupted
# the generation - insertion fails because slug is
# empty, and there is already an empty one.
# We assign here a random value, that gets overwritten
# afterwards.
song_model, created = Song.objects.get_or_create(
title=song_title,
artist=artist_model,
defaults={
'title': song_title,
'language': language_code,
'file_path': filepath_rel,
'slug': ('%06x' % random.randrange(16**6))
})
if created:
if Song.objects.filter(slug=song_slug).exists():
song_slug += '-' + str(song_model.id)
song_model.slug = song_slug

else:
LOGGER.info("-> Already exists.")

artist_model.save()
song_model.object_hash = object_hash
song_model.save()
self.artists[artist_slug] = model.id, artist_name
return model.id
Loading