Browse Source

Code refactoring. Work in progress: songbook no longer works

- more code documentation #4
- bases of potential new template system #9

- Continue implementing the template system (whether old or new).
pull/15/head
Louis 11 years ago
parent
commit
a715c8fc00
  1. 17
      TODO.md
  2. 2
      share/templates/default.tmpl
  3. 312
      songbook_core/build.py

17
TODO.md

@ -0,0 +1,17 @@
Templates
=========
- [ ] Implémenter le moteur de templates
Songs
=====
- [x] Dans un fichier .sb, si la clef 'songs' n'est pas définie, toutes les chansons de 'datadir/songs' sont incluses dans le carnet de chants.
- [ ] Dans songbook-data, modifier les fichiers .sb où 'songs' est défini à 'all', et supprimer cette clef.
\DataImgDirectory
=================
- [x] La commande LaTeX dataImgDirectory n'est plus traitée de manière particulière.
- [ ] Ajouter dans le moteur de templates un « truc » pour pouvoir, dans le template, accéder à datadir. Par exemple, un template veut inclure un fichier tex 'preface.tex', peut apparaitre dans le template quelque chose du genre '\include{<datadir>/preface}'. Le moteur de template remplacera alors '<datadir>' par le répertoire contenu dans Songbook().config['datadir'].
- [ ] Modifier les templates (songbook-data) pour remplacer la chaîne '\DataImgDirectory' (ou assimilée) à quelque chose du genre '<datadir>/img'.

2
share/templates/default.tmpl

@ -72,8 +72,6 @@
\newindex{titleidx}{\getname_title} \newindex{titleidx}{\getname_title}
\newauthorindex{authidx}{\getname_auth} \newauthorindex{authidx}{\getname_auth}
\graphicspath{\getDataImgDirectory}
\definecolor{SongNumberBgColor}{HTML}{\getsongnumberbgcolor} \definecolor{SongNumberBgColor}{HTML}{\getsongnumberbgcolor}
\definecolor{NoteBgColor}{HTML}{\getnotebgcolor} \definecolor{NoteBgColor}{HTML}{\getnotebgcolor}
\definecolor{IndexBgColor}{HTML}{\getindexbgcolor} \definecolor{IndexBgColor}{HTML}{\getindexbgcolor}

312
songbook_core/build.py

@ -65,6 +65,11 @@ def to_value(parameter, data):
def format_declaration(name, parameter): def format_declaration(name, parameter):
"""Return LaTeX code to declare variale 'name'.
'parameter' is the corresponding line of the template (which can provide
default value).
"""
value = "" value = ""
if "default" in parameter: if "default" in parameter:
value = parameter["default"] value = parameter["default"]
@ -76,6 +81,7 @@ def format_declaration(name, parameter):
def format_definition(name, value): def format_definition(name, value):
"""Return LaTeX code to define and set default value of variable 'name'"""
return r'\set@{name}{{{value}}}'.format(name=name, value=value) + EOL return r'\set@{name}{{{value}}}'.format(name=name, value=value) + EOL
@ -102,134 +108,180 @@ def clean(basename):
except Exception as exception: except Exception as exception:
raise errors.CleaningError(basename + ext, exception) raise errors.CleaningError(basename + ext, exception)
DEFAULT_AUTHWORDS = {
"after": ["by"],
"ignore": ["unknown"],
"sep": ["and"],
}
# pylint: disable=too-few-public-methods
class Songbook(object):
"""Reprensent a songbook (.sb) file.
def make_tex_file(songbook, output): - Low level: provide a Python representation of the values stored in the
"""Create the LaTeX file corresponding to the .sb file given in argument.""" '.sb' file.
datadir = songbook['datadir'] - High level: provide some utility functions to manipulate these data.
name = output[:-4] """
template_dir = os.path.join(datadir, 'templates')
songs = [] def __init__(self, raw_songbook, basename):
super(Songbook, self).__init__()
prefixes_tex = "" self.basename = basename
prefixes = [] # Default values: will be updated while parsing raw_songbook
self.config = {
authwords_tex = "" 'template': os.path.join(
authwords = {"after": ["by"], "ignore": ["unknown"], "sep": ["and"]} __SHAREDIR__,
"templates",
# parse the songbook data "default.tmpl",
if "template" in songbook: ),
template = songbook["template"] 'titleprefixwords': [],
del songbook["template"] 'authwords': {},
else: 'lang': 'french',
template = os.path.join(__SHAREDIR__, "templates", "default.tmpl") 'sort': [u"by", u"album", u"@title"],
if "songs" in songbook: 'songs': None,
songs = songbook["songs"] 'datadir': os.path.abspath('.'),
del songbook["songs"] }
if "titleprefixwords" in songbook: self.songslist = None
prefixes = songbook["titleprefixwords"] self._parse(raw_songbook)
for prefix in songbook["titleprefixwords"]: self._set_songs_default()
prefixes_tex += r"\titleprefixword{%s}" % prefix + EOL
songbook["titleprefixwords"] = prefixes_tex def _set_songs_default(self):
if "authwords" in songbook: """Set the default values for the Song() class."""
# Populating default value Song.sort = self.config['sort']
for key in ["after", "sep", "ignore"]: Song.prefixes = self.config['titleprefixwords']
if key not in songbook["authwords"]: Song.authwords['after'] = [
songbook["authwords"][key] = authwords[key] re.compile(r"^.*%s\b(.*)" % after)
# Processing authwords values for after
authwords = songbook["authwords"] in self.config['authwords']["after"]
for key in ["after", "sep", "ignore"]:
for word in authwords[key]:
if key == "after":
authwords_tex += r"\auth%sword{%s}" % ("by", word) + EOL
else:
authwords_tex += r"\auth%sword{%s}" % (key, word) + EOL
songbook["authwords"] = authwords_tex
if "after" in authwords:
authwords["after"] = [re.compile(r"^.*%s\b(.*)" % after)
for after in authwords["after"]]
if "sep" in authwords:
authwords["sep"] = [" %s" % sep for sep in authwords["sep"]] + [","]
authwords["sep"] = [re.compile(r"^(.*)%s (.*)$" % sep)
for sep in authwords["sep"]]
if "lang" not in songbook:
songbook["lang"] = "french"
if "sort" in songbook:
sort = songbook["sort"]
del songbook["sort"]
else:
sort = [u"by", u"album", u"@title"]
Song.sort = sort
Song.prefixes = prefixes
Song.authwords = authwords
parameters = parse_template(os.path.join(template_dir, template))
# compute songslist
if songs == "all":
songs = [
os.path.relpath(filename, os.path.join(datadir, 'songs'))
for filename
in recursive_find(os.path.join(datadir, 'songs'), '*.sg')
] ]
songslist = SongsList(datadir, songbook["lang"]) Song.authwords['ignore'] = self.config['authwords']['ignore']
songslist.append_list(songs) Song.authwords['sep'] = [
re.compile(r"^(.*)%s (.*)$" % sep)
songbook["languages"] = ",".join(songslist.languages()) for sep
in ([
# output relevant fields " %s" % sep
out = codecs.open(output, 'w', 'utf-8') for sep
out.write('%% This file has been automatically generated, do not edit!\n') in self.config['authwords']["sep"]
out.write(r'\makeatletter' + EOL) ] + [','])
# output automatic parameters
out.write(format_declaration("name", {"default": name}))
out.write(format_declaration("songslist", {"type": "stringlist"}))
# output template parameter command
for name, parameter in parameters.iteritems():
out.write(format_declaration(name, parameter))
# output template parameter values
for name, value in songbook.iteritems():
if name in parameters:
out.write(format_definition(
name,
to_value(parameters[name], value),
))
if len(songs) > 0:
out.write(format_definition('songslist', songslist.latex()))
out.write(r'\makeatother' + EOL)
# output template
comment_pattern = re.compile(r"^\s*%")
with codecs.open(
os.path.join(template_dir, template), 'r', 'utf-8'
) as template_file:
content = [
line
for line
in template_file
if not comment_pattern.match(line)
] ]
for index, line in enumerate(content):
if re.compile("getDataImgDirectory").search(line): def _parse(self, raw_songbook):
if os.path.abspath(os.path.join(datadir, "img")).startswith( """Parse raw_songbook.
os.path.abspath(os.path.dirname(output))
): The principle is: some special keys have their value processed; others
imgdir = os.path.relpath( are stored verbatim in self.config.
os.path.join(datadir, "img"), """
os.path.dirname(output) self.config.update(raw_songbook)
)
else: ### Some post-processing
imgdir = os.path.abspath(os.path.join(datadir, "img")) # Compute song list
line = line.replace(r"\getDataImgDirectory", ' {%s/} ' % imgdir) if self.config['songs'] is None:
content[index] = line self.config['songs'] = [
os.path.relpath(
out.write(u''.join(content)) filename,
out.close() os.path.join(self.config['datadir'], 'songs'),
)
for filename
in recursive_find(
os.path.join(self.config['datadir'], 'songs'),
'*.sg',
)
]
self.songslist = SongsList(self.config['datadir'], self.config["lang"])
self.songslist.append_list(self.config['songs'])
# Ensure self.config['authwords'] contains all entries
for (key, value) in DEFAULT_AUTHWORDS.items():
if key not in self.config['authwords']:
self.config['authwords'][key] = value
def write_tex(self, output):
r"""Build the '.tex' file corresponding to self.
Arguments:
- output: a file object, in which the file will be written.
TODO: Update this.
Quelques notes concernant le rendu
- self.config['titleprefixwords']
> return EOL.join([
> r"\titleprefixwords{%s}" % prefix
> for prefix
> in self.config['titleprefixwords']
> ])
- self.config['authwords']:
> # Processing authwords values
> tex = []
> for key in ["after", "sep", "ignore"]:
> for word in self.config['authwords'][key]:
> if key == "after":
> tex.append(r"\auth%sword{%s}" % ("by", word))
> else:
> tex.append(r"\auth%sword{%s}" % (key, word))
> return EOL.join(tex)
- Liste des langues utilisées (pour chargement par babel):
self.songlist.languages(). Donc la commande pour Babel peut
ressembler à :
> r"\PassOptionsToPackage{%s}{babel}" % ",".join(
> self.songslist.languages()
> )
"""
parameters = parse_template(os.path.join(
self.config['datadir'],
'templates',
self.config['template']
))
output.write((
'%% This file has been automatically '
'generated, do not edit!\n'
))
output.write(r'\makeatletter' + EOL)
# output automatic parameters
output.write(format_declaration("name", {"default": self.basename}))
output.write(format_declaration("songslist", {"type": "stringlist"}))
# output template parameter command
for name, parameter in parameters.iteritems():
output.write(format_declaration(name, parameter))
# output template parameter values
for name, value in self.config.iteritems():
if name in parameters:
output.write(format_definition(
name,
to_value(parameters[name], value),
))
if len(self.config['songs']) > 0:
output.write(format_definition('songslist', self.songslist.latex()))
output.write(r'\makeatother' + EOL)
# output template
comment_pattern = re.compile(r"^\s*%")
with codecs.open(
os.path.join(
self.config['datadir'],
'templates',
self.config['template']
),
'r',
'utf-8',
) as template_file:
content = [
line
for line
in template_file
if not comment_pattern.match(line)
]
output.write(u''.join(content))
def buildsongbook( def buildsongbook(
songbook, raw_songbook,
basename, basename,
interactive=False, interactive=False,
logger=logging.getLogger() logger=logging.getLogger()
@ -237,15 +289,15 @@ def buildsongbook(
"""Build a songbook """Build a songbook
Arguments: Arguments:
- songbook: Python representation of the .sb songbook configuration file. - raw_songbook: Python representation of the .sb songbook configuration
file.
- basename: basename of the songbook to be built. - basename: basename of the songbook to be built.
- interactive: in False, do not expect anything from stdin. - interactive: in False, do not expect anything from stdin.
""" """
tex_file = basename + ".tex" songbook = Songbook(raw_songbook, basename)
with codecs.open("{}.tex".format(basename), 'w', 'utf-8') as output:
# Make TeX file songbook.write_tex(output)
make_tex_file(songbook, tex_file)
if not 'TEXINPUTS' in os.environ.keys(): if not 'TEXINPUTS' in os.environ.keys():
os.environ['TEXINPUTS'] = '' os.environ['TEXINPUTS'] = ''
@ -254,7 +306,7 @@ def buildsongbook(
'latex', 'latex',
) )
os.environ['TEXINPUTS'] += os.pathsep + os.path.join( os.environ['TEXINPUTS'] += os.pathsep + os.path.join(
songbook['datadir'], songbook.config['datadir'],
'latex', 'latex',
) )
@ -265,7 +317,7 @@ def buildsongbook(
pdflatex_options.append("-halt-on-error") pdflatex_options.append("-halt-on-error")
# First pdflatex pass # First pdflatex pass
if subprocess.call(["pdflatex"] + pdflatex_options + [tex_file]): if subprocess.call(["pdflatex"] + pdflatex_options + [basename]):
raise errors.LatexCompilationError(basename) raise errors.LatexCompilationError(basename)
# Make index # Make index
@ -278,7 +330,7 @@ def buildsongbook(
index_file.close() index_file.close()
# Second pdflatex pass # Second pdflatex pass
if subprocess.call(["pdflatex"] + pdflatex_options + [tex_file]): if subprocess.call(["pdflatex"] + pdflatex_options + [basename]):
raise errors.LatexCompilationError(basename) raise errors.LatexCompilationError(basename)
# Cleaning # Cleaning

Loading…
Cancel
Save