Browse Source

Merge branch 'poc_html_rendering'

pull/112/head
Oliverpool 9 years ago
parent
commit
061411e3ae
  1. 44
      patacrep/songs/__init__.py
  2. 62
      patacrep/songs/chordpro/__init__.py
  3. 5
      patacrep/songs/chordpro/ast.py
  4. 2
      patacrep/songs/chordpro/data/chordpro/content_image
  5. 2
      patacrep/songs/chordpro/data/chordpro/content_partition
  6. 36
      patacrep/songs/chordpro/data/chordpro/song
  7. 3
      patacrep/songs/chordpro/data/chordpro/song_body
  8. 34
      patacrep/songs/chordpro/data/chordpro/song_header
  9. 1
      patacrep/songs/chordpro/data/html/content_chord
  10. 6
      patacrep/songs/chordpro/data/html/content_chordlist
  11. 1
      patacrep/songs/chordpro/data/html/content_comment
  12. 31
      patacrep/songs/chordpro/data/html/content_define
  13. 3
      patacrep/songs/chordpro/data/html/content_define_list
  14. 3
      patacrep/songs/chordpro/data/html/content_error
  15. 1
      patacrep/songs/chordpro/data/html/content_guitar_comment
  16. 1
      patacrep/songs/chordpro/data/html/content_image
  17. 3
      patacrep/songs/chordpro/data/html/content_line
  18. 3
      patacrep/songs/chordpro/data/html/content_metadata_cover
  19. 1
      patacrep/songs/chordpro/data/html/content_newline
  20. 1
      patacrep/songs/chordpro/data/html/content_partition
  21. 1
      patacrep/songs/chordpro/data/html/content_space
  22. 5
      patacrep/songs/chordpro/data/html/content_tablature
  23. 6
      patacrep/songs/chordpro/data/html/content_verse
  24. 1
      patacrep/songs/chordpro/data/html/content_word
  25. 5
      patacrep/songs/chordpro/data/html/song
  26. 3
      patacrep/songs/chordpro/data/html/song_body
  27. 30
      patacrep/songs/chordpro/data/html/song_header
  28. 4
      patacrep/songs/chordpro/data/latex/song
  29. 3
      patacrep/songs/chordpro/data/latex/song_body
  30. 4
      patacrep/songs/latex/__init__.py

44
patacrep/songs/__init__.py

@ -128,7 +128,7 @@ class Song:
self.titles = []
self.data = {}
self.cached = None
self.parse(config)
self._parse(config)
# Post processing of data
self.datadir = datadir
@ -165,19 +165,19 @@ class Song:
def __repr__(self):
return repr((self.titles, self.data, self.fullpath))
def render(self, output, output_format):
def render(self, output_format, output=None, *args, **kwargs):
"""Return the code rendering this song.
Arguments:
- output: Name of the output file.
- output_format: Format of the output file (latex, chordpro...)
- output: Name of the output file, or `None` if irrelevant.
"""
method = "render_{}".format(output_format)
if hasattr(self, method):
return getattr(self, method)(output)
return getattr(self, method)(output, *args, **kwargs)
raise NotImplementedError()
def parse(self, config): # pylint: disable=no-self-use
def _parse(self, config): # pylint: disable=no-self-use
"""Parse song.
It set the following attributes:
@ -206,16 +206,20 @@ class Song:
"""Search for a file name.
:param str filename: The name, as provided in the chordpro file (with or without extension).
:param list extensions: Possible extensions (with '.')
:param list extensions: Possible extensions (with '.'). Default is no extension.
:param iterator directories: Other directories where to search for the file
The directory where the Song file is stored is added to the list.
Returns None if nothing found.
This function can also be used as a preprocessor for a renderer: for
instance, it can compile a file, place it in a temporary folder, and
return the path to the compiled file.
"""
if extensions is None:
extensions = ['']
if directories is None:
directories = []
directories = self.config['datadir']
songdir = os.path.dirname(self.fullpath)
@ -223,28 +227,22 @@ class Song:
for extension in extensions:
fullpath = os.path.join(directory, filename + extension)
if os.path.isfile(fullpath):
return fullpath
return os.path.abspath(fullpath)
return None
def search_image(self, filename):
def search_image(self, filename, none_if_not_found=False):
"""Search for an image file"""
datadir_img = self.get_datadirs('img')
filepath = self.search_file(filename, ['', '.jpg', '.png'], datadir_img)
return filepath if filepath else filename
filepath = self.search_file(
filename,
['', '.jpg', '.png'],
self.get_datadirs('img'),
)
return filepath if none_if_not_found or filepath else filename
@property
def cover_filepath(self):
"""Get the path to the cover file (or None if not found)"""
filename = str(self.data.get('cov', ''))
if not filename:
return None
datadir_img = self.get_datadirs('img')
return self.search_file(filename, ['', '.jpg', '.png'], datadir_img)
def search_partition(self, filename):
def search_partition(self, filename, none_if_not_found=False):
"""Search for a lilypond file"""
filepath = self.search_file(filename, ['', '.ly'])
return filepath if filepath else filename
return filepath if none_if_not_found or filepath else filename
def unprefixed_title(title, prefixes):
"""Remove the first prefix of the list in the beginning of title (if any).

62
patacrep/songs/chordpro/__init__.py

@ -14,9 +14,24 @@ class ChordproSong(Song):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.jinjaenv = None
def parse(self, config):
@staticmethod
def iter_template_paths(templatedirs, output_format):
"""Iterate over paths in which templates are to be searched.
:param iterator templatedirs: Iterators of additional directories (the
default hard-coded template directory is returned last).
:param str output_format: Song output format, which is appended to
each directory.
"""
for directory in templatedirs:
yield os.path.join(directory, output_format)
yield os.path.join(
os.path.abspath(pkg_resources.resource_filename(__name__, 'data')),
output_format,
)
def _parse(self, config):
"""Parse content, and return the dictionary of song data."""
with encoding.open_read(self.fullpath, encoding=self.encoding) as song:
song = parse_song(song.read(), self.fullpath)
@ -28,46 +43,39 @@ class ChordproSong(Song):
'song': song,
}
def render(self, output, output_format):
def render(self, output_format, output=None, template="song", templatedirs=None): # pylint: disable=arguments-differ
if templatedirs is None:
templatedirs = []
context = {
'language': self.languages[0],
"path": files.relpath(self.fullpath, os.path.dirname(output)),
"titles": self.titles,
"authors": self.authors,
"metadata": self.data,
"render": self._render_ast,
"config": self.config,
"content": self.cached['song'].content,
}
self.jinjaenv = Environment(loader=FileSystemLoader(os.path.join(
os.path.abspath(pkg_resources.resource_filename(__name__, 'data')),
output_format,
)))
self.jinjaenv.filters['search_image'] = self.search_image
jinjaenv = Environment(loader=FileSystemLoader(
self.iter_template_paths(templatedirs, output_format)
))
jinjaenv.filters['search_image'] = self.search_image
jinjaenv.filters['search_partition'] = self.search_partition
self.jinjaenv.filters['search_partition'] = self.search_partition
return self._render_ast(
context,
self.cached['song'].content,
template="song",
)
@contextfunction
def _render_ast(self, context, content, template=None):
"""Render ``content``."""
if isinstance(context, dict):
context['content'] = content
else:
context.vars['content'] = content
if template is None:
template = content.template()
return Renderer(
template=template,
encoding='utf8',
jinjaenv=self.jinjaenv,
jinjaenv=jinjaenv,
).template.render(context)
@staticmethod
@contextfunction
def _render_ast(context, content):
"""Render ``content``."""
context.vars['content'] = content
return context.environment.get_template(content.template()).render(context)
SONG_PARSERS = {
'sgc': ChordproSong,
}

5
patacrep/songs/chordpro/ast.py

@ -153,6 +153,11 @@ class Chord(AST):
# pylint: disable=too-many-arguments
self.chord = chord
@property
def pretty_chord(self):
"""Return the chord with nicer (utf8) alteration"""
return self.chord.replace('b', '').replace('#', '')
class Verse(AST):
"""A verse (or bridge, or chorus)"""
_template = "verse"

2
patacrep/songs/chordpro/data/chordpro/content_image

@ -1 +1 @@
{image: (( content.argument ))}
{image: (( content.argument|search_image ))}

2
patacrep/songs/chordpro/data/chordpro/content_partition

@ -1 +1 @@
{partition: ((content.argument))}
{partition: ((content.argument|search_partition))}

36
patacrep/songs/chordpro/data/chordpro/song

@ -1,35 +1,3 @@
(* if language is defined -*)
{language: (( language ))}
(* endif *)
(* if metadata.columns is defined -*)
{columns: (( metadata.columns ))}
(* endif *)
(* if metadata.capo is defined -*)
{capo: (( metadata.capo ))}
(* endif *)
(* include 'song_header' *)
(*- for title in titles -*)
{title: (( title ))}
(* endfor -*)
(*- for author in authors -*)
{artist: (( author[1] ))(( author[0] ))}
(* endfor *)
(*- for key in ['album', 'copyright', 'cov', 'tag'] *)
(* if key in metadata -*)
{(( key )): (( metadata[key] ))}
(* endif *)
(* endfor *)
(*- for key in metadata.keys -*)
{key: (( key.keyword )): (( key.argument ))}
(* endfor *)
(*- for chord in metadata['define'] *)
((- render(chord) ))
(* endfor *)
(* for item in content -*)
(( render(item) ))
(* endfor *)
(* include 'song_body' *)

3
patacrep/songs/chordpro/data/chordpro/song_body

@ -0,0 +1,3 @@
(* for item in content -*)
(( render(item) ))
(* endfor *)

34
patacrep/songs/chordpro/data/chordpro/song_header

@ -0,0 +1,34 @@
(* if language is defined -*)
{language: (( language ))}
(* endif *)
(* if metadata.columns is defined -*)
{columns: (( metadata.columns ))}
(* endif *)
(* if metadata.capo is defined -*)
{capo: (( metadata.capo ))}
(* endif *)
(*- for title in titles -*)
{title: (( title ))}
(* endfor -*)
(*- for author in authors -*)
{artist: (( author[1] ))(( author[0] ))}
(* endfor *)
(*- for key in ['album', 'copyright', 'tag'] *)
(* if key in metadata -*)
{(( key )): (( metadata[key] ))}
(* endif *)
(* endfor *)
(* if 'cov' in metadata -*)
{(( 'cov' )): (( metadata['cov'].argument|search_image ))}
(* endif *)
(*- for key in metadata.keys -*)
{key: (( key.keyword )): (( key.argument ))}
(* endfor *)
(*- for chord in metadata['define'] *)
((- render(chord) ))
(* endfor *)

1
patacrep/songs/chordpro/data/html/content_chord

@ -0,0 +1 @@
((- content.pretty_chord -))

6
patacrep/songs/chordpro/data/html/content_chordlist

@ -0,0 +1,6 @@
<span class="chord">
(*- for chord in content.chords -*)
(* if not loop.first *) (* endif -*)
(( render(chord) -))
(* endfor -*)
</span>

1
patacrep/songs/chordpro/data/html/content_comment

@ -0,0 +1 @@
<div class="comment">(( content.argument ))</div>

31
patacrep/songs/chordpro/data/html/content_define

@ -0,0 +1,31 @@
<div(( " " -))
class="chord-diagram"(( " " -))
data-shift="
(*- if content.basefret -*)
((content.basefret))
(*- else -*)
0
(*- endif *)
((- '" ' -))
data-frets="
(*- for fret in content.frets -*)
(* if fret is none -*)
x
(*- else -*)
(( fret -))
(* endif -*)
(* endfor -*)
((- '" ' -))
(* if content.fingers -*)
data-fingers="
(*- for finger in content.fingers -*)
(* if finger is none -*)
-
(*- else -*)
(( finger -))
(* endif -*)
(* endfor -*)
((- '" ' -))
(* endif -*)
data-name="(( content.pretty_key ))"(( " " -))
></div>

3
patacrep/songs/chordpro/data/html/content_define_list

@ -0,0 +1,3 @@
(*- for chord in metadata['define'] *)
((- render(chord) ))
(* endfor *)

3
patacrep/songs/chordpro/data/html/content_error

@ -0,0 +1,3 @@
ERROR : Template not found for "(( content.__class__.__name__ ))". See the logs for details.

1
patacrep/songs/chordpro/data/html/content_guitar_comment

@ -0,0 +1 @@
<div class="guitar_comment">(( content.argument ))</div>

1
patacrep/songs/chordpro/data/html/content_image

@ -0,0 +1 @@
<img src="(( content.argument|search_image ))">

3
patacrep/songs/chordpro/data/html/content_line

@ -0,0 +1,3 @@
(* for item in content.line -*)
(( render(item) ))
(*- endfor *)

3
patacrep/songs/chordpro/data/html/content_metadata_cover

@ -0,0 +1,3 @@
(* if 'cov' in metadata -*)
<img src="(( metadata['cov'].argument|search_image ))"><br>
(* endif *)

1
patacrep/songs/chordpro/data/html/content_newline

@ -0,0 +1 @@

1
patacrep/songs/chordpro/data/html/content_partition

@ -0,0 +1 @@
<a class="song-partition" href="(( content.argument|search_partition ))">((content.argument))</a>

1
patacrep/songs/chordpro/data/html/content_space

@ -0,0 +1 @@

5
patacrep/songs/chordpro/data/html/content_tablature

@ -0,0 +1,5 @@
<pre class="tablature">
(* for content in content.content *)
((- content ))
(* endfor *)
</pre>

6
patacrep/songs/chordpro/data/html/content_verse

@ -0,0 +1,6 @@
<p class="(( content.type ))">
(*- for line in content.lines -*)
(* if not loop.first *)<br>(* endif -*)
(( render(line) ))
(* endfor -*)
</p>

1
patacrep/songs/chordpro/data/html/content_word

@ -0,0 +1 @@
(( content.value ))

5
patacrep/songs/chordpro/data/html/song

@ -0,0 +1,5 @@
(* include 'song_header' *)
<div class="song_content">
(* include 'song_body' *)
</div>

3
patacrep/songs/chordpro/data/html/song_body

@ -0,0 +1,3 @@
(* for item in content -*)
(( render(item) ))
(* endfor *)

30
patacrep/songs/chordpro/data/html/song_header

@ -0,0 +1,30 @@
(*- for title in titles -*)
(* if loop.first *)
<h1 class="song-title">(( title ))</h1>
(* else *)
<h2 class="song-title">(( title ))</h2>
(* endif *)
(* endfor -*)
(*- for author in authors -*)
<h2 class="song-artist">(( author[1] ))(( author[0] ))</h2>
(* endfor *)
(*- for key in ['album', 'copyright', 'tag', 'columns', 'capo'] *)
(* if key in metadata -*)
<span class="song-(( key ))">(( key|capitalize )): (( metadata[key] ))</span><br/>
(* endif *)
(* endfor *)
(* if language is defined -*)
<span class="song-language">Language: (( language ))</span><br>
(* endif *)
(* include 'content_metadata_cover' *)
(*- for key in metadata.keys -*)
{key: (( key.keyword )): (( key.argument ))}
(* endfor *)
(* include 'content_define_list' *)

4
patacrep/songs/chordpro/data/latex/song

@ -43,8 +43,6 @@
(( render(chord) ))
(* endfor *)
(* for item in content -*)
(( render(item) ))
(* endfor *)
(* include 'song_body' *)
\endsong

3
patacrep/songs/chordpro/data/latex/song_body

@ -0,0 +1,3 @@
(* for item in content -*)
(( render(item) ))
(* endfor *)

4
patacrep/songs/latex/__init__.py

@ -14,7 +14,7 @@ from patacrep.songs import Song
class LatexSong(Song):
"""LaTeX song parser."""
def parse(self, __config):
def _parse(self, __config):
"""Parse content, and return the dictinory of song data."""
with encoding.open_read(self.fullpath, encoding=self.encoding) as song:
self.data = parse_song(song.read(), self.fullpath)
@ -30,6 +30,8 @@ class LatexSong(Song):
def render_latex(self, output):
"""Return the code rendering the song."""
if output is None:
raise ValueError(output)
path = files.path2posix(files.relpath(
self.fullpath,
os.path.dirname(output)

Loading…
Cancel
Save