cours_NSI/scripts/main.py

206 lines
10 KiB
Python

def define_env(env):
"Hook function"
env.variables['compteur_exo'] = 0
@env.macro
def exercice(var = True, prem = None):
""" Crée des numérotations d'exercices automatiques :
indiquer exercice(..., 0) pour le premier exercice d'une page puis
exercice(....) pour les suivants (numéroation auto)
var == True, exercice en dehors d'une superfence.
var == False, exercice dans une superfence
"""
if prem is not None : env.variables['compteur_exo'] = prem
env.variables['compteur_exo'] += 1
root = f"Exercice { env.variables['compteur_exo']}"
return f"""exo \"{root}\"""" if var else '\"'+root+'\"'
@env.macro
def cours():
""" raccourci et formatage auto balise cours
"""
return f'done "Cours"'
@env.macro
def ext():
return f'ext "Pour aller plus loin"'
env.variables['term_counter'] = 0
env.variables['IDE_counter'] = 0
INFTY_SYMBOL = '\u221e'
from urllib.parse import unquote
@env.macro
def terminal() -> str:
"""
Purpose : Create a Python Terminal.
Methods : Two layers to avoid focusing on the Terminal. 1) Fake Terminal using CSS 2) A click hides the fake
terminal and triggers the actual Terminal.
"""
tc = env.variables['term_counter']
env.variables['term_counter'] += 1
return f"""<div onclick='start_term("id{tc}")' id="fake_id{tc}" class="terminal_f"><label class="terminal"><span>>>> </span></label></div><div id="id{tc}" class="hide"></div>"""
def read_ext_file(nom_script : str, path : str, filetype : str = 'py') -> str:
"""
Purpose : Read a Python file that is uploaded on the server.
Methods : The content of the file is hidden in the webpage. Replacing \n by a string makes it possible
to integrate the content in mkdocs admonitions.
"""
short_path = f"""docs/"""
try:
if path == "":
# print(nom_script, f"""{short_path}/scripts/{nom_script}.{filetype}""")
f = open(f"""{short_path}/scripts/{nom_script}.{filetype}""")
else:
# print('relp', f"""{short_path}/{path}/{nom_script}.{filetype}""")
# print(nom_script, f"""{short_path}/{path}/{nom_script}.{filetype}""")
f = open(f"""{short_path}/{path}/{nom_script}.{filetype}""")
# f = open(f"""{short_path}/scripts/{nom_script}.{filetype}""")
content = ''.join(f.readlines())
f.close()
content = content + "\n"
# Hack to integrate code lines in admonitions in mkdocs
# change backslash_newline by backslash-newline
return content.replace('\n','bksl-nl').replace('_','py-und').replace('*','py-str')
except :
return
def generate_content(nom_script : str, path : str, filetype : str = 'py') -> str:
"""
Purpose : Return content and current number IDE {tc}.
"""
tc = env.variables['IDE_counter']
env.variables['IDE_counter'] += 1
content = read_ext_file(nom_script, path, filetype)
if content is not None :
return content, tc
else : return "", tc
def create_upload_button(tc : str) -> str:
"""
Purpose : Create upload button for a IDE number {tc}.
Methods : Use an HTML input to upload a file from user. The user clicks on the button to fire a JS event
that triggers the hidden input.
"""
path_img = env.variables.page.abs_url.split('/')[1]
return f"""<button class="tooltip" onclick="document.getElementById('input_editor_{tc}').click()"><img src="/{path_img}/images/buttons/icons8-upload-64.png"><span class="tooltiptext">Téléverser</span></button>\
<input type="file" id="input_editor_{tc}" name="file" enctype="multipart/form-data" class="hide"/>"""
def create_unittest_button(tc: str, nom_script: str, path : str, mode: str, MAX : int = 5) -> str:
"""
Purpose : Generate the button for IDE {tc} to perform the unit tests if a valid test_script.py is present.
Methods : Hide the content in a div that is called in the Javascript
"""
stripped_nom_script = nom_script.split('/')[-1]
relative_path = '/'.join(nom_script.split('/')[:-1])
nom_script = f"{relative_path}/{stripped_nom_script}_test"
content = read_ext_file(nom_script, path)
if content is not None:
path_img = env.variables.page.abs_url.split('/')[1]
return f"""<span id="test_term_editor_{tc}" class="hide">{content}</span>\
<button class="tooltip" onclick=\'executeTest("{tc}","{mode}")\'>\
<img src="/{path_img}/images/buttons/icons8-check-64.png">\
<span class="tooltiptext">Valider</span></button><span class="compteur">\
{MAX}/{MAX}\
</span>"""
else:
return ''
def blank_space(s=0.3) -> str:
"""
Purpose : Return 5em blank spaces. Use to spread the buttons evenly
"""
# return f"""<span style="indent-text:{s}em"> </span>"""
return f"""<span style="display: inline-block; width:{s}em"></span>"""
def get_max_from_file(content : str) -> tuple:#[str, int]: # compatibilité Python antérieur 3.8
split_content = content.split('bksl-nl')
max_var = split_content[0]
if max_var[:4] != "#MAX":
MAX = 5
else:
value = max_var.split('=')[1].strip()
MAX = int(value) if value not in ['+', 1000] else INFTY_SYMBOL
i = 1
while split_content[i] == '':
i += 1
content = 'bksl-nl'.join(split_content[i:])
return content, MAX
def test_style(nom_script : str, element : str) -> bool:
guillemets = ["'", '"']
ide_style = ["", "v"]
styles = [f"""IDE{istyle}({i}{nom_script}{i}""" for i in guillemets for istyle in ide_style]
return any([style for style in styles if style in element])
def convert_url_to_utf8(nom : str) -> str:
return unquote(nom, encoding='utf-8')
@env.macro
def IDEv(nom_script : str = '', MAX : int = 5, SANS : str = "") -> str:
"""
Purpose : Easy macro to generate vertical IDE in Markdown mkdocs.
Methods : Fire the IDE function with 'v' mode.
"""
return IDE(nom_script, mode = 'v', MAX = MAX, SANS = SANS)
@env.macro
def IDE(nom_script : str = '', mode : str = 'h', MAX : int = 5, SANS : str = "") -> str:
"""
Purpose : Create an IDE (Editor+Terminal) on a Mkdocs document. {nom_script}.py is loaded on the editor if present.
Methods : Two modes are available : vertical or horizontal. Buttons are added through functional calls.
Last span hides the code content of the IDE if loaded.
"""
path_img = convert_url_to_utf8(env.variables.page.abs_url).split('/')[1]
path_file = '/'.join(filter(lambda folder: folder != "", convert_url_to_utf8(env.variables.page.abs_url).split('/')[2:-2]))
content, tc = generate_content(nom_script, path_file)
try:
f = open(f"docs/{path_file}/clef.txt", "r", encoding="utf8")
clef = f.read()
except:
clef = "" # base case -> no clef.txt file
content, max_from_file = get_max_from_file(content)
MAX = max_from_file if MAX == 5 else MAX
MAX = MAX if MAX not in ['+', 1000] else INFTY_SYMBOL
corr_content, tc = generate_content(f"""{'/'.join(nom_script.split('/')[:-1])}/{nom_script.split('/')[-1]}_corr""", path_file)
div_edit = f'<div class="ide_classe" data-max={MAX} data-exclude={"".join(SANS.split(" "))+"eval,exec"} >'
if mode == 'v':
div_edit += f'<div class="wrapper"><div class="interior_wrapper"><div id="editor_{tc}"></div></div><div id="term_editor_{tc}" class="term_editor"></div></div>'
else:
div_edit += f'<div class="wrapper_h"><div class="line" id="editor_{tc}"></div><div id="term_editor_{tc}" class="term_editor_h terminal_f_h"></div></div>'
div_edit += f"""<button class="tooltip" onclick='interpretACE("editor_{tc}","{mode}")'><img src="/{path_img}/images/buttons/icons8-play-64.png"><span class="tooltiptext">Lancer</span></button>"""
div_edit += create_unittest_button(tc, nom_script, path_file, mode, MAX)
div_edit += f"""{blank_space(1)}<button class="tooltip" onclick=\'downloadFile("editor_{tc}","{nom_script}")\'><img src="/{path_img}/images/buttons/icons8-download-64.png"><span class="tooltiptext">Télécharger</span></button>{blank_space()}"""
div_edit += create_upload_button(tc)
div_edit += f"""{blank_space(1)}<button class="tooltip" onclick=\'reload("{tc}","content")\'><img src="/{path_img}/images/buttons/icons8-restart-64.png"><span class="tooltiptext">Recharger</span></button>{blank_space()}"""
div_edit += f"""<button class="tooltip" onclick=\'saveEditor("{tc}","content")\'><img src="/{path_img}/images/buttons/icons8-save-64.png"><span class="tooltiptext">Sauvegarder</span></button>"""
div_edit += '</div>'
div_edit += f"""<span id="content_editor_{tc}" class="hide">{content}</span>"""
div_edit += f"""<span id="corr_content_editor_{tc}" class="hide" data-strudel="{str(clef)}">{corr_content}</span>"""
elt_insertion = [elt for elt in env.page.markdown.split("\n") if test_style(nom_script, elt)]
elt_insertion = elt_insertion[0] if len(elt_insertion) >=1 else ""
indent = " "*(len(elt_insertion) - len(elt_insertion.lstrip()))
if nom_script == '' : indent = " " # to avoid conflict with empty IDEs
if indent == "":
div_edit += f'''
{indent}--8<--- "docs/xtra/start_REM.md"
'''
div_edit += f'''
{indent}--8<--- "docs/{path_file if path_file != "" else 'scripts'}/{nom_script}_REM.md"''' if clef == "" else f""
if indent == "":
div_edit += f'''
{indent}--8<--- "docs/xtra/end_REM.md"
'''
return div_edit