formations/setup_main_tex_file.py

388 lines
16 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/python
# -*- coding:utf-8 -*-
import argparse
import re
import random
import time
from os import path, makedirs, listdir
from jinja2 import Environment, FileSystemLoader
import pygit2
LICENSES = {'CC-by-sa-2.0': 'license-cc-by-sa-2.0',
}
TEMPLATES = {
'beamer': {'fragment': 'frame.tex',
'fragment_pratique': 'frame-pratique.tex',
'master': 'main-beamer.tex'},
'article': {'fragment': 'fragment.tex',
'fragment_pratique': 'fragment-pratique.tex',
'master': 'main-article.tex'},
'questionnaire': {'fragment': 'fragment.tex',
'fragment_pratique': 'fragment-pratique.tex',
'master': 'main-questionnaire.tex'}
}
LATEX_SUBS = [(re.compile('_'), '\\_'),
]
DOCUMENTCLASS_RE = re.compile(r'\\documentclass\{(?P<document_class>.+?)\}')
SKBCONFIG_RE = re.compile(r'\\skbconfig\[\n\s*root\s*=\s*(?P<root>.*),\n\s*rep\s*=\s*(?P<rep>.*),\n\s*pub\s*=\s*(?P<pub>.*),\n\s*fig\s*=\s*(?P<fig>.*),\n\s*sli\s*=\s*(?P<sli>.*),\n\s*acr\s*=\s*(?P<acr>.*),\n\s*bib\s*=\s*(?P<bib>.*)\n\s*\]\{skblocal.tex\}', re.M)
SKBINPUT_RE = re.compile(r'[^%]\\skbinput\[from=(?P<rep>.*?)(,.*)?\]\{(?P<tex>.*?)\}', re.M)
def get_unique_name(base):
now = time.localtime()
year = str(now[0])
month = str(now[1]).rjust(2, '0')
day = str(now[2]).rjust(2, '0')
rand = str(random.randint(0, 100)).rjust(2, '0')
return '-'.join([base, year, month, day, rand]).decode('utf-8')
def escape_tex(value):
newval = value
for pattern, replacement in LATEX_SUBS:
newval = pattern.sub(replacement, newval)
return newval
def normalize_branch(args):
if 'master' in args:
return path.dirname(args.master).replace('/', '')
elif 'directory' in args:
return args.directory.replace('/', '')
else:
raise Exception('No sufficient information to create branch')
def git_integration(func):
def inner(args):
try:
repo = pygit2.Repository('./')
except:
repo = None
if repo:
branch_name = normalize_branch(args)
if not branch_name in repo.branches.local:
master_ref = repo.references['refs/heads/master']
commit = master_ref.peel()
repo.branches.local.create(branch_name, commit)
# état du dépôt, si il nest pas propre, sortir
# checkout sur la branche master
# création dune branche spécifique pour le document à partir de la branche master
# création des fichiers
func(args)
if repo: # à transformer en decorateur éventuellement
print('add et commit')
# ajout tout le contenu hors presentations dans la branche master
# ajout de tout le contenu de presentations dans la branche spécifique
return inner
def main():
@git_integration
def init(args):
"""
init function
"""
def get_institutes_logos(institutes_list=None):
if not institutes_list:
return []
institutes_logos = []
known_logos = {path.splitext(path.basename(l))[0]:l for l in listdir('./figures/logos')}
for institute in institutes_list:
if institute in known_logos:
institutes_logos.append(known_logos[institute])
else:
print(f'Unknown institute {institute}')
print(f'Replacing with missing.png')
institutes_logos.append('missing.png')
return institutes_logos
root = '../'
if args.directory:
root = root + re.sub(r'[\w-]+/?', '../', args.directory)
name = 'master.tex'
title = args.title
if not title:
title = 'FIXME'
author = args.author
if not author:
author = 'Cadoles'
client = args.client
if not client:
client = 'FIXME'
institutes = get_institutes_logos(args.institutes)
logos_count = len(institutes) + 1
directory = args.directory
if not directory:
directory = ''
license = LICENSES.get(args.license, 'license-cc-by-sa-2.0')
document_class = args.format
content = 'sli' if document_class == 'beamer' else 'rep'
env = {'root': root,
'class': document_class,
'content': content,
'title': title,
'author': author,
'client': client,
'license': license,
'institutes': institutes,
'logos_count': logos_count}
master = TEMPLATES[document_class]['master']
master_dir = path.join('presentations', directory)
programme_dir = path.join(master_dir, 'programme')
resources = [(master_dir, master),
(master_dir, 'programme.tex'),
(master_dir, 'support.tex'),
(programme_dir, 'contenu.tex'),
(programme_dir, 'duree.tex'),
(programme_dir, 'evaluation.tex'),
(programme_dir, 'moyens.tex'),
(programme_dir, 'objectifs.tex'),
(programme_dir, 'prerequis.tex'),
(programme_dir, 'public.tex'),
]
for directory, template_file in resources:
template = jinja_env.get_template(template_file)
rendered_template = template.render(**env)
if not path.exists(directory):
makedirs(directory)
template_dest_name = name if template_file == master else template_file
with open(path.join(directory, template_dest_name), 'w') as rendered_file:
rendered_file.write(rendered_template)
@git_integration
def update(args):
"""
update function
"""
with open(args.master, 'r') as master:
tex_master = master.read()
tex_class = DOCUMENTCLASS_RE.search(tex_master)
tex_skbconfig = SKBCONFIG_RE.search(tex_master)
tex_skbinputs = SKBINPUT_RE.finditer(tex_master)
fragment = TEMPLATES[tex_class.group('document_class')]['fragment']
fragment_pratique = TEMPLATES[tex_class.group('document_class')]['fragment_pratique']
for skbinput in tex_skbinputs:
rep = path.dirname(skbinput.group('tex'))
rep = path.join(tex_skbconfig.group(skbinput.group('rep')), rep)
tex_name = path.basename(skbinput.group('tex'))
basename = '{0}.tex'.format(tex_name)
dest = path.join(rep, basename)
if not path.isfile(dest):
print(dest)
if not path.isdir(rep):
makedirs(rep)
template = jinja_env.get_template(fragment_pratique if tex_name.endswith('-pratique') else fragment)
env = {'title': basename, 'subtitle': '',
'name': dest}
rendered_template = template.render(**env)
with open(dest, 'w') as rendered_file:
rendered_file.write(rendered_template)
@git_integration
def outline(args):
"""
outline creation
"""
part_level = 0
section_level = 1
subsection_level = 2
frametitle_level = 3
framesubtitle_level = 4
def file_path_from_skbinput(skbinput_re, master, skbconfig):
rel_path = path.join(skbconfig.group('root'), skbconfig.group(skbinput_re.group('rep')), skbinput_re.group('tex')) + '.tex'
root_path = path.abspath(path.dirname(master))
return path.normpath(path.join(root_path, rel_path))
def reorder_lists(*args):
reordered_list = []
for l in args:
reordered_list.extend(l)
reordered_list.sort(key=lambda x: x[0])
return reordered_list
def outline_from_include(include, start, document_class):
frametitle_re = re.compile(r'\\frametitle\{(?P<name>.*?)\}')
framesubtitle_re = re.compile(r'\\framesubtitle\{(?P<name>.*?)\}')
skbheading_re = re.compile(r'\\skbheading\{(?P<name>.*?)\}')
with open(include, 'r') as include_fh:
content = include_fh.read()
if document_class == 'beamer':
frametitles = frametitle_re.finditer(content)
framesubtitles = framesubtitle_re.finditer(content)
frametitles_list = [(ft.start(), frametitle_level, ft.group('name')) for ft in frametitles]
framesubtitles_list = [(fs.start(), framesubtitle_level, fs.group('name')) for fs in framesubtitles]
frame_list = reorder_lists(frametitles_list, framesubtitles_list)
if frame_list:
div = int('1{}'.format('0'*len(str(frame_list[-1][0]))))
return [(start + f[0]/div, f[1], f[2]) for f in frame_list]
else:
return []
def filter_outlines(headers_list, max_level=None):
filtered_outlines = []
default_max_level = max([hl[1] for hl in headers_list])
if not max_level:
max_level = default_max_level
temp_max_level = default_max_level
buffered_header = {l: None for l in range(max_level + 1)}
filtered_out = ['Pratique', 'Plan', 'Licence du document']
for header in headers_list:
if header[1] <= min(max_level, default_max_level, temp_max_level):
if header[2] in filtered_out:
temp_max_level = header[1] + 1
continue
elif header[2] != buffered_header[header[1]]:
buffered_header[header[1]] = header[2]
for bf in buffered_header:
if bf > header[1]:
buffered_header[bf] = None
filtered_outlines.append(header)
temp_max_level = default_max_level
return filtered_outlines
def outline_format(headers_list):
levels = list(set([hl[1] for hl in headers_list]))
levels.sort()
flattened_levels = {l: levels.index(l) for l in levels}
for header in headers_list:
print('{}{}'.format('\t' * flattened_levels[header[1]], header[2]))
def render_outline(header_list, master):
item = "\\item {content}"
itemize = "\\begin{{itemize}}\n{content}\n\\end{{itemize}}"
content_file = path.join(path.dirname(path.abspath(master)), 'programme', 'contenu.tex')
with open(content_file, 'w') as content_fh:
content_fh.write(itemize.format(content='\n'.join([item.format(content=c[2]) for c in header_list])))
def structure_outline(header_list):
root = Outline('Contenu')
current_outline = root
for header in header_list:
if header[1] > current_outline.level:
parent = current_outline
elif header[1] == current_outline.level:
parent = current_outline.get_parent()
elif header[1] == current_outline.level - 1:
parent = current_outline.get_parent().get_parent()
else:
parent = root
current_outline = Outline(header[2], parent, header[1])
parent.add_child(current_outline)
return root
section_re = re.compile(r'\\section\{(?P<name>.*?)\}')
part_re = re.compile(r'\\part\{(?P<name>.*?)}')
subsection_re = re.compile(r'\\subsection\{(?P<name>.*?)\}')
with open(args.master, 'r') as master_tex:
master = master_tex.read()
skbconfig = SKBCONFIG_RE.search(master)
document_class = DOCUMENTCLASS_RE.search(master).group('document_class')
parts = part_re.finditer(master)
sections = section_re.finditer(master)
subsections = subsection_re.finditer(master)
includes = SKBINPUT_RE.finditer(master)
parts_list = [(part.start(), part_level, part.group('name')) for part in parts]
sections_list = [(section.start(), section_level, section.group('name')) for section in sections]
includes_list = [element for skbinput in includes for element in outline_from_include(file_path_from_skbinput(skbinput, args.master, skbconfig), skbinput.start(), document_class)]
subsections_list = [(subsection.start(), subsection_level, subsection.group('name')) for subsection in subsections]
structured_outline = structure_outline(filter_outlines(reorder_lists(parts_list, sections_list, includes_list, subsections_list)))
content_file = path.join(path.dirname(path.abspath(args.master)), 'programme', 'contenu.tex')
with open(content_file, 'w') as content_fh:
content_fh.write(structured_outline.render())
jinja_loader = FileSystemLoader('./templates')
jinja_env = Environment(loader=jinja_loader,
block_start_string='((*',
block_end_string='*))',
variable_start_string='(((',
variable_end_string=')))',
comment_start_string='((=',
comment_end_string='=))',
trim_blocks=True)
jinja_env.filters['escape_tex'] = escape_tex
parser = argparse.ArgumentParser(description="Préparation des fichiers tex")
subparsers = parser.add_subparsers(help='Aide des sous-commandes')
parser_init = subparsers.add_parser('init', help='Initialisation du fichier maître')
parser_init.add_argument('-f', '--format', help="Format du document", required=True)
#parser_init.add_argument('-n', '--name', help="Nom du fichier à créer", required=True)
parser_init.add_argument('-a', '--author', help="Auteur de la formation")
parser_init.add_argument('-c', '--client', help="Client")
parser_init.add_argument('-t', '--title', help="Titre de la formation")
parser_init.add_argument('-l', '--license', help="Termes de mise à disposition de la formation")
parser_init.add_argument('-d', '--directory', help="Sous-répertoires où créer le fichier", required=True)
parser_init.add_argument('-i', '--institutes', nargs='*', help="Instituts dont les logos sont requis")
parser_init.set_defaults(func=init)
parser_update = subparsers.add_parser('update', help='Mise à jour des fichiers inclus')
parser_update.add_argument('-m', '--master', help="Emplacement du fichier maître", required=True)
parser_update.set_defaults(func=update)
parser_outline = subparsers.add_parser('outline', help="Création du programme à partir du fichier maître")
parser_outline.add_argument('-m', '--master', help="Emplacement du fichier maître", required=True)
parser_outline.set_defaults(func=outline)
args = parser.parse_args()
args.func(args)
class Outline:
item = "{indent}\\item {content}"
itemize = "\n{indent}\\begin{{itemize}}\n{content}\n{indent}\\end{{itemize}}"
def __init__(self, content, parent=None, level=-1):
self.children = []
self.parent = parent
self.level = level
self.content = content
def is_leaf(self):
if self.children:
return False
return True
def add_child(self, child):
self.children.append(child)
def get_children(self):
return self.children
def get_parent(self):
return self.parent
def render(self):
if self.get_parent():
rendered = self.item.format(indent=' '*self.level, content=self.content)
else:
rendered = ''
if not self.is_leaf():
rendered += self.itemize.format(indent=' '*self.level, content='\n'.join([c.render() for c in self.get_children()]))
return rendered
def __repr__(self):
return f"<Outline {self.content} - {self.level}>"
if __name__ == '__main__':
main()