""" A module for generating html files using Jinja2.
Attributes
----------
ENV(Environment): A jinja2 enviroment for generating templates.
"""
import json
import logging
import os
import shutil
import jinja2
from kvalikirstu2 import utils
from kvalikirstu2 import study
from kvalikirstu2 import paragraph_analyzer
from kvalikirstu2 import argument_parser
from kvalikirstu2 import localization
ENV = jinja2.Environment(
loader=jinja2.PackageLoader('kvalikirstu2', 'templates'),
autoescape=jinja2.select_autoescape(['html', 'xml'])
)
logger = logging.getLogger(__name__)
[docs]def relpath_with_fwd_slash(path: str, start=None):
"""Produces a relative path with forward slashes
:param str path: The path for which the relative path should be determined.
:param str start: The path to which the path is relative to. If no argument is given, the current
working directory is used.
"""
return os.path.relpath(path, start).replace(os.sep, '/')
[docs]def get_html_path(output_path: str):
""" Gets the folder for all the "under the hood" files for the index document.
:param str output_path: The path where the index file goes to.
"""
args = argument_parser.get_args()
data_path = os.path.join(output_path, args.study_data_folder, args.index_data_folder)
return data_path
def _move_deps(output_path: str):
"""Moves the dependencies to the data folder in the output directory.
:param str output_path: The path the index is being generated in.
"""
logger.info('Moving dependencies to %s', output_path)
data_path = get_html_path(output_path)
kvalikirstu2_dir = os.path.dirname(__file__)
deps_dir = os.path.join(kvalikirstu2_dir, "index deps")
if os.path.exists(data_path):
shutil.rmtree(data_path)
shutil.copytree(deps_dir, data_path)
def _get_digits_for_filename(subject_count):
"""Gets how characters should be in a filename for the subject html files.
"""
return utils.number_of_digits(subject_count)
def _get_padded_number(index, digits):
"""Get zero padded number with 'digits' digits.
"""
return ("%s" % (index+1)).zfill(digits)
def _get_html_filepath(data_path, index, digits, metadata, language_code):
"""Gets the html filepath for the subject with the given index.
"""
args = argument_parser.get_args()
padded_number = _get_padded_number(index, digits)
if language_code:
filename = "%s_%s" % (padded_number, language_code)
else:
filename = padded_number
subject_path = os.path.join(data_path, args.html_file_name.format(study_number=metadata.extract_study_number(),
name=filename))
return subject_path
def _get_all_subjects_filepath(data_path, metadata, language_code):
"""Gets the html filepath for the all subjects file.
"""
args = argument_parser.get_args()
_ = localization._
if language_code:
filename = "%s_%s" % (_("all_subjects"), language_code)
else:
filename = _("all_subjects")
subject_path = os.path.join(data_path, args.html_file_name.format(study_number=metadata.extract_study_number(),
name=filename))
return subject_path
def _generate_subject_files(mapping, template_study: study.Study, translation_func):
"""Creates .html documents for individual subjects.
:param dict mapping: File mapping.
:param Study template_study: The study to be saved.
:param translation_func: Function used for translating messages from English to other languages.
"""
output_path = mapping.get_path('output')
logger.info('Generating subjects to path %s', output_path)
template = ENV.get_template("subject_template.xml")
for index in range(len(template_study.subjects)):
subject_path = mapping.get_path(str(index))
with open(subject_path, "w", encoding="utf-8") as file_stream:
file_stream.write(template.render(subject=template_study.subjects[index],
is_image=utils.is_image_file,
relpath=relpath_with_fwd_slash, output_path=output_path,
ParagraphType=paragraph_analyzer.ParagraphType,
index=index, _=translation_func))
def _generate_all_subjects_file(mapping, template_study: study.Study, translation_func):
"""Creates .html documents for all subjects.
:param dict: The file mapping.
:param Study template_study: The study to be saved.
:param translation_func: Function used for translating messages from English to other languages.
"""
subject_path = mapping.get_path('all')
output_path = mapping.get_path('output')
logger.debug('Generating all subjects page to %s', subject_path)
template = ENV.get_template("all_subjects_template.xml")
with open(subject_path, "w", encoding="utf-8") as file_stream:
file_stream.write(template.render(study=template_study,
is_image=utils.is_image_file,
relpath=relpath_with_fwd_slash, output_path=output_path,
ParagraphType=paragraph_analyzer.ParagraphType,
_=translation_func))
def _get_table_localization(_):
"""Gets the table localization.
:param _: The translation function.
"""
kvalikirstu2_dir = os.path.dirname(__file__)
path = os.path.join(kvalikirstu2_dir, "locale", "table_localization",
_("English.json"))
with open(path, mode='r', encoding='utf-8') as file_stream:
table_localization = json.load(file_stream)
return table_localization
def _ellipsis_value(value, max_chars):
"""Shortens a value if necessary.
:param value: Value that may be shortened.
:param max_chars: The maximum number of characters before the string gets shortened.
:return: The shortened string with a tooltip or the original.
"""
if len(value) > max_chars:
shortened = value[0:max_chars]
return '<span class="ellipsis" title="%s">%s…</span>' % (jinja2.escape(value), jinja2.escape(shortened))
return value
[docs]def get_index_path(output_path):
"""
:param output_path: The path of the output folder.
:return: The path of the index file.
"""
return os.path.join(output_path, "index.html")
[docs]def generate_index(output_path: str, template_study: study.Study, header_info, metadata):
""" Generates an index file for the study.
:param str output_path: The output path for the generated html index file.
:param Study template_study: The study from which the template is generated from.
:param header_info: Contains information about the study's headers.
:param metadata: The metadata of the study.
"""
logger.info('Generating index to path %s', output_path)
headers = header_info.get_index_headers()
alignments = header_info.get_header_alignments()
args = argument_parser.get_args()
# Delete old html folder to remove any old files
html_path = get_html_path(output_path)
if os.path.exists(html_path):
shutil.rmtree(html_path)
os.makedirs(html_path)
_move_deps(output_path)
mapping = _get_file_mapping(html_path, output_path, metadata, template_study)
translation_func = localization.get_translation_func(args.index_lang)
_generate_subject_files(mapping, template_study, translation_func)
_generate_all_subjects_file(mapping, template_study, translation_func)
index_path = get_index_path(output_path)
template = ENV.get_template("index_template.xml")
with open(index_path, "w", encoding="utf-8") as file:
file.write(template.render(headers=headers, study=template_study,
basename=os.path.basename, relpath=relpath_with_fwd_slash,
dirname=os.path.dirname, _=translation_func,
ParagraphType=paragraph_analyzer.ParagraphType,
file_mapping=mapping, header_info=header_info,
_get_padded_number=_get_padded_number,
digits=utils.number_of_digits(len(template_study.subjects)),
utils=utils, args=args, os=os, metadata=metadata, ellipsis=_ellipsis_value,
alignments=alignments, table_localization=_get_table_localization(translation_func)))
[docs]class HTMLFileMapping:
"""A class that handles the file mapping for HTML files.
"""
def __init__(self):
self.mapping = {}
[docs] def add_path(self, key, path):
"""Add a path with a key.
:param key: The key of the file.
:param path: The path of the file.
"""
self.mapping[key] = path
[docs] def get_subject_link(self, index):
"""Gets link to subject relative to output path.
:param index: The index of the subject.
"""
key = str(index)
return self.get_html_link(key)
[docs] def get_html_link(self, key):
"""Gets link to given key relative to output path.
:param key: The key of the file.
"""
return relpath_with_fwd_slash(self.mapping[key], self.mapping['output'])
[docs] def get_path(self, key):
"""Returns the output path for the given key.
:param key: The key of the file.
"""
return self.mapping[key]
def _get_file_mapping(data_path, output_path, metadata, template_study):
"""Returns an instance of HTMLFileMapping that contains information about HTML files.
:param data_path: The path of the html folder.
:param output_path: The study path.
:param metadata: The study metadata.
:param template_study: A study for which the index is being generated.
"""
language_codes = argument_parser.get_languages()
mapping = HTMLFileMapping()
subject_count = len(template_study.subjects)
digits = utils.number_of_digits(subject_count)
for index in range(subject_count):
lang_code = template_study.get_subject_language_code(index, language_codes)
mapping.add_path(str(index), _get_html_filepath(data_path, index, digits, metadata, lang_code))
mapping.add_path('all', _get_all_subjects_filepath(data_path, metadata,
template_study.get_language_code(language_codes)))
mapping.add_path('data', data_path)
mapping.add_path('output', output_path)
return mapping