GenProp / src /view /view.py
adrien.aribaut-gaudin
feat: requirement part fonctionnal
3ca15d8
import gradio as gr
from typing import Dict
import asyncio
import os
from src.control.controller import Controller
from Levenshtein import distance
from src.tools.list_tool import delete_duplicate_styles
def run(config: Dict, controller: Controller):
"""
=====================================================
Global variables
================
"""
controller.clear_docs()
title = "<h1 style=text-align:center;display:block;font-size:4.5em;color:#08a2d2;font-weight:bold;margin-top:4%;padding-bottom:1%>GenProp</h1>"
with gr.Blocks(theme=gr.themes.Soft(primary_hue=gr.themes.colors.blue, secondary_hue=gr.themes.colors.orange)) as formatdoc:
gr.Markdown(title)
gr.Markdown("<p style=color:#08a2d2;font-size:1.5em;padding-bottom:2%;text-align:center>Par Hexamind</p>")
gr.Markdown("")
with gr.Row():
with gr.Column():
pass
with gr.Column(scale=10):
"""
=====================================================
Input and style components
==========================
"""
gr.Markdown("<p style=font-size:1em;>Vous êtes chargé de produire une proposition commerciale</p>")
with gr.Accordion("Charger votre proposition", open=True) as input_acc:
input_files_comp = gr.File(file_count="multiple", file_types=[".docx"], label="Document")
with gr.Accordion("Appliquer les styles", open=False) as style_acc:
templates_radio = gr.Radio(
label="Templates",
choices=config['templates'],
value=config['templates'][config['default_template_index']],
)
with gr.Row():
options_btn = gr.CheckboxGroup(choices=config['options'],
label="Options",
interactive=True)
with gr.Accordion("Mapper les styles de liste", open=False) \
as list_acc:
with gr.Column(scale=2):
list_style_comps = [gr.Dropdown(visible=False, interactive=True)
for _ in range(config['max_styles'])]
with gr.Accordion("Mapper les autres styles non présents dans le template", open=False) \
as newstyles_acc:
with gr.Column(scale=2):
newstyle_comps = [gr.Dropdown(visible=False, interactive=True)
for _ in range(config['max_styles'])]
log_comp = gr.Textbox(label="Journal des modifications", visible=False)
output_styles_files_comp = gr.File(file_count="multiple", file_types=[".docx"], visible=False)
with gr.Row():
run_style_btn = gr.Button("Appliquer le template et les modifications de style", visible=False)
clear_style_btn = gr.Button("Annuler les modifications de style", visible=False)
"""
===============================================
Generation components
======================
"""
with gr.Accordion("Compléter automatiquement la proposition", open=False) as gen_acc:
generate_option_btn = gr.Radio(
label="Automatically generate a draft based on your own database",
choices=["Auto generation", "No generation"],
value="No generation",
interactive=True,
visible=False,
)
db_list_comp = gr.CheckboxGroup(
label="Base de connaissance",
info="Ces documents constituent la source de référence. Désélectionner pour qu'ils ne soient "
"pas pris en compte lors de la génération automatiqueF",
visible=True,
interactive=True,
)
db_reset_btn = gr.Button("Effacer la base de connaissance", visible=False, size="sm") \
with gr.Column(visible=True):
gr.Markdown("<p style=font-size:1em;text-align:center;>A des fins de démonstrations, la base de connaissance est alimentée depuis Wikipedia</p>")
wiki_fetch_btn = gr.Button("Rechercher les pages Wikipedia", visible=True, size="sm")
wiki_list_comp = gr.CheckboxGroup(
label="Sélectionner les pages à ajouter dans la base de connaissance",
visible=False,
interactive=True,
)
with gr.Column():
wiki_add_to_db_btn = \
gr.Button("Ajouter les documents sélectionnés à la base de connaissance",
visible=False, size="sm")
# wiki_clear_btn = gr.Button("Effacer les choix de documents", visible=False, size="sm") \
# with gr.Tab("Depuis le disque local (en cours de développement)"):
# my_files_list_comp = gr.Files(
# label="Charger ses documents",
# visible=True,
# )
# my_files_add_to_db_btn = gr.Button("Add files to sources", visible=False, size="sm")
add_close_btn = gr.Button("Close", visible=False, size="sm")
with gr.Row():
db_add_doc_btn = gr.Button("Ajouter de nouveaux documents", visible=False, size="sm")\
output_files_comp = gr.Files(file_count="multiple", visible=False)
generate_btn = gr.Button("Générer", interactive=True)
clear_btn = gr.Button('Nettoyer', visible=False)
rerun_btn = gr.Button('Relancer', visible=False)
"""
===============================================
Verification requirements components
======================
"""
with gr.Accordion("Générer la réponse aux exigences (en cours de développement)", open=False, visible=True) as exigences_acc:
input_csv_comp = gr.File(file_count='multiple', file_types=[".xlsx"], label="Fichier d'exigences (xlsx only)")
with gr.Row():
verif_btn = gr.Button("Générer la réponse aux exigences (en cours de développement)", visible=False)
output_csv_comp = gr.File(file_count="single", file_types=[".xlsx"], visible=False)
gr.Markdown("")
gr.Markdown("")
gr.Markdown("<p style=font-size:1em;>Vous êtes administrateur de GenProp</p>")
with gr.Accordion("Gérer les templates", open=False) as gestions_templates_acc:
templates_radio_modif = gr.Radio(
interactive=True,
label="Templates",
choices=config['templates'],
value=config['templates'][config['default_template_index']],
)
with gr.Row():
add_template_btn = gr.UploadButton("Ajouter un template",file_count="single", file_types=[".docx"])
delete_curr_template_btn = gr.Button("Supprimer le template sélectionné")
with gr.Accordion("Gérer la base de connaissances (en cours de développement)", open=False):
pass
with gr.Column():
pass
"""
===================================================
state variables
===============
"""
wiki_source_var: [str] = gr.State([]) # list of wikipage titles of interest for the input text tasks
wiki_db_var: [str] = gr.State([]) # list of wiki document titles in the db (as seen from the UI)
my_files_db_var: [str] = gr.State([]) # list of titles of the files uploaded in the db (as seen from the UI)
db_collection_var: str = gr.State("-1") # name of the collection of documents sources in the db # list of styles to modify
"""
===================================================
Input and styles functions and listeners
========================================
"""
def input_csv_fn(input_csv_):
if len(input_csv_) > 1:
raise gr.Error(f'Please upload only one file')
else:
input_csv_ = input_csv_[0]
if not input_csv_.name.endswith('.xlsx'):
raise gr.Error(f'File {input_csv_.name} is not a xlsx file, please upload only xlsx files')
else:
controller.set_input_csv(input_csv_)
update_ = {
verif_btn: gr.update(visible=True),
}
return update_
input_csv_comp.upload(input_csv_fn,
inputs=[input_csv_comp],
outputs=[verif_btn],
show_progress="full")
def input_csv_clear_fn():
controller.clear_input_csv()
update_ = {
verif_btn: gr.update(visible=False),
}
return update_
input_csv_comp.clear(
input_csv_clear_fn,
inputs=[],
outputs=[verif_btn]
)
def generate_requirements_excel():
controller.generate_response_to_requirements()
output_path = [f"{controller.excel_doc_path}/{f}" for f in os.listdir(controller.excel_doc_path)]
update_ = {
output_csv_comp: gr.update(value=output_path, visible=True),
}
return update_
verif_btn.click(generate_requirements_excel,
inputs=[],
outputs=[output_csv_comp],show_progress="full")
def input_files_upload_fn(input_files_):
for files in input_files_:
if(not files.name.endswith('.docx')):
raise gr.Error(f'File {files.name} is not a docx file, please upload only docx files')
else:
continue
controller.clear_docs()
controller.copy_docs(input_files_)
update_ = {
newstyles_acc: gr.update(open=True),
style_acc: gr.update(visible=True),
run_style_btn: gr.update(visible=True),
clear_style_btn: gr.update(visible=True),
list_acc: gr.update(open=True),
}
newstyles_update = newstyles_fn()
# misapplied_styles = misapplied_styles_fn()
# for val in misapplied_styles.values():
# if val > 0:
# doc = list(misapplied_styles.keys())[list(misapplied_styles.values()).index(val)]
# gr.Warning(f"{val} paragraphs were detected in the document {doc.name} because their styles are not well applied. Please review your document for better results.")
update_.update(newstyles_update)
return update_
input_files_comp.upload(input_files_upload_fn,
inputs=[input_files_comp],
outputs=[style_acc, newstyles_acc, run_style_btn, clear_style_btn, list_acc] + newstyle_comps + list_style_comps
)
def input_file_clear_fn():
controller.clear_docs()
update_ = {
options_btn: gr.update(value=[]),
log_comp: gr.update(value="", visible=False),
output_styles_files_comp: gr.update(value=[], visible=False),
newstyles_acc: gr.update(open=False),
style_acc: gr.update(open=False),
gen_acc: gr.update(open=False),
output_files_comp: gr.update(visible=False),
run_style_btn: gr.update(visible=False),
clear_style_btn: gr.update(visible=False),
list_acc: gr.update(open=False),
exigences_acc: gr.update(value=""),
}
newstyles_update_ = newstyles_reset()
list_style_update_ = newliststyle_reset()
update_.update(newstyles_update_)
update_.update(list_style_update_)
return update_
input_files_comp.clear(
input_file_clear_fn,
inputs=[],
outputs=[options_btn, output_styles_files_comp, output_files_comp, log_comp, newstyles_acc, list_acc,
gen_acc, style_acc, run_style_btn, clear_style_btn, exigences_acc] + newstyle_comps + list_style_comps
)
def misapplied_styles_fn():
res = controller.retrieve_number_of_misapplied_styles()
return res
def newstyles_fn():
update_ = {}
update_.update(newliststyle_reset())
update_.update(newstyles_reset())
different_styles, all_template_styles = controller.get_difference_with_template()
all_template_styles_names = [style.name for style in all_template_styles]
list_styles_to_update = controller.get_list_styles()
get_label_list = lambda i: f"style: {list_styles_to_update[i]['list_style']}"
list_style_update_ = {
list_style_comps[i]: gr.update(visible=i < len(list_styles_to_update),
choices=sorted(all_template_styles_names, key=lambda x: distance(x, list_styles_to_update[i]['list_style'])),
value=None,
label=get_label_list(i)) if i < len(list_styles_to_update) else ''
for i in range(config['max_styles'])
}
update_.update(list_style_update_)
#delete styles in different_styles that are already in list_styles_to_update
different_styles = delete_duplicate_styles(list_styles_to_update, different_styles)
adapted_template_styles = []
for i in range(len(different_styles)):
adapted_template_styles.append([style.name for style in all_template_styles if style.type == different_styles[i]['style'].type])
get_label = lambda i: f"style: {different_styles[i]['style'].name}"
newstyles_update_ = {
newstyle_comps[i]: gr.update(visible=i < len(different_styles),
#sort the styles using levenstein distance function
choices=sorted(adapted_template_styles[i], key=lambda x: distance(x, different_styles[i]['style'].name)),
value=None,
label=get_label(i)) if i < len(different_styles) else ''
for i in range(len(different_styles))
}
update_.update(newstyles_update_)
return update_
def newliststyle_reset():
update_ = {
list_style_comps[i]: gr.update(visible=False,
choices=[],
value=None,
label='')
for i in range(config['max_styles'])
}
return update_
def newstyles_reset():
update_ = {
newstyle_comps[i]: gr.update(visible=False,
choices=[],
value=None,
label='')
for i in range(config['max_styles'])
}
return update_
def templates_fn(templates_):
controller.set_template(templates_)
update_ = newstyles_fn()
return update_
templates_radio.change(templates_fn,
inputs=[templates_radio],
outputs=[newstyles_acc, list_acc] + newstyle_comps + list_style_comps)
def newstyle_fns(src_index: int):
def newstyle_fn(newstyle_):
controller.update_style(src_index, newstyle_)
return newstyle_fn
def change_list_style_fn(src_index: int):
def change_list_style_fn(list_style_):
controller.update_list_style(src_index, list_style_)
return change_list_style_fn
def add_template_fn(template):
controller.add_template(template)
update_ = {
templates_radio: gr.update(choices=[t for t in os.listdir(config['templates_path']) if t.endswith((".docx"))]),
templates_radio_modif: gr.update(choices=[t for t in os.listdir(config['templates_path']) if t.endswith((".docx"))]),
}
return update_
def delete_curr_template_fn(template):
controller.delete_curr_template(template)
update_ = {
templates_radio: gr.update(choices=[t for t in os.listdir(config['templates_path']) if t.endswith((".docx"))]),
templates_radio_modif: gr.update(choices=[t for t in os.listdir(config['templates_path']) if t.endswith((".docx"))]),
options_btn: gr.update(value=[]),
log_comp: gr.update(value="", visible=False),
output_styles_files_comp: gr.update(value=[], visible=False),
newstyles_acc: gr.update(open=False),
run_style_btn: gr.update(visible=True),
list_acc: gr.update(open=False),
}
return update_
add_template_btn.upload(add_template_fn,
inputs=[add_template_btn],
outputs=[templates_radio,templates_radio_modif])
delete_curr_template_btn.click(delete_curr_template_fn,
inputs=[templates_radio],
outputs=[templates_radio, options_btn, log_comp, output_styles_files_comp, newstyles_acc, run_style_btn, list_acc, templates_radio_modif])
for src_index, newstyle_comp in enumerate(newstyle_comps):
newstyle_comp.input(newstyle_fns(src_index), inputs=[newstyle_comp], outputs=[],show_progress="full")
for src_index, list_style_comp in enumerate(list_style_comps):
list_style_comp.input(change_list_style_fn(src_index), inputs=[list_style_comp], outputs=[],show_progress="full")
def clear_style_fn(input_files_):
controller.clear_docs()
if input_files_:
controller.copy_docs(input_files_)
controller.set_template()
update_ = {
options_btn: gr.update(value=[]),
log_comp: gr.update(value="", visible=False),
output_styles_files_comp: gr.update(value=[], visible=False),
newstyles_acc: gr.update(open=False),
run_style_btn: gr.update(visible=True),
list_acc: gr.update(open=False),
templates_radio: gr.update(value=config['templates'][config['default_template_index']]),
}
newstyles_update_ = newstyles_fn()
update_.update(newstyles_update_)
return update_
clear_style_btn.click(clear_style_fn,
inputs=[input_files_comp],
outputs=[options_btn, output_styles_files_comp, log_comp, newstyles_acc, list_acc, run_style_btn, templates_radio]
+ newstyle_comps + list_style_comps
)
def run_style_fn(options_btn_):
print(f"options activated : {options_btn_}")
controller.apply_template(options_btn_)
log = controller.get_log()
new_docs_path = controller.generated_docs_path
output_paths = [f"{new_docs_path}/{f}" for f in os.listdir(new_docs_path)]
print(f"output_paths: {output_paths}")
update_ = {
log_comp: gr.update(value=log, visible=True),
output_styles_files_comp: gr.update(value=output_paths, visible=True),
run_style_btn: gr.update(visible=False),
}
return update_
run_style_btn.click(run_style_fn,
inputs=[options_btn],
outputs=[log_comp, output_styles_files_comp, run_style_btn] + newstyle_comps, show_progress="full")
"""
=====================================================
Generation functions
====================
"""
def generate_option_fn(db_collection_):
id_ = controller.get_or_create_collection(db_collection_)
update_ = {
db_collection_var: id_,
}
return update_
def wiki_fetch1_fn():
"""
fetch the wikifiles interesting for solving the tasks as defined in the input doc
"""
update_ = {
wiki_list_comp: gr.update(visible=True),
}
return update_
async def wiki_fetch2_fn():
"""
fetch the wikifiles interesting for solving the tasks as defined in the input doc
"""
wiki_interesting_files = await controller.wiki_fetch()
print(f"wiki_interesting_files: {wiki_interesting_files}")
wiki_files = wiki_interesting_files # [w for w in wiki_interesting_files if w not in wiki_db_files_]
update_ = {
wiki_list_comp: gr.update(visible=True, value=[], choices=wiki_files),
wiki_source_var: wiki_interesting_files,
wiki_add_to_db_btn: gr.update(visible=True),
# wiki_clear_btn: gr.update(visible=True), #Button to clear the choices that are by default all ticked
}
return update_
async def wiki_add_to_db_fn(wiki_list_, wiki_source_, wiki_db_, db_list_, db_collection_):
"""
adds the wikipages to the db source
"""
wiki_to_add = [wiki for wiki in wiki_list_ if wiki not in wiki_db_]
db_list_ += wiki_to_add
wiki_db_ += wiki_to_add
wiki_source_remaining = [wiki for wiki in wiki_source_ if wiki not in wiki_db_]
async_upload_and_store_tasks = [asyncio.create_task(controller.wiki_upload_and_store(wiki, db_collection_)) for wiki in wiki_to_add] # A DEPLACER DANS LE CONTROLLER
await asyncio.gather(*async_upload_and_store_tasks)
db_not_empty = 0 < len(db_list_)
wiki_to_add_not_empty = 0 < len(wiki_source_remaining)
update_ = {
wiki_db_var: wiki_db_,
wiki_list_comp: gr.update(value=[], choices=wiki_source_remaining),
wiki_add_to_db_btn: gr.update(visible=wiki_to_add_not_empty),
db_list_comp: gr.update(
visible=True,
value=db_list_,
choices=db_list_,
label="Database content"),
db_reset_btn: gr.update(visible=db_not_empty),
generate_btn: gr.update(visible=True, interactive=db_not_empty),
}
return update_
def generate_fn1():
update_ = {
output_files_comp: gr.update(visible=True)
}
return update_
async def generate_fn2(db_collection_, db_list_):
output_files = await controller.generate_doc_from_db(collection_name=db_collection_,
from_files=db_list_)
update_ = {
output_files_comp: gr.update(value=output_files, visible=True),
}
return update_
"""
=====================================================
Generation listeners
====================
"""
wiki_fetch_btn \
.click(wiki_fetch1_fn, inputs=[], outputs=[wiki_list_comp]) \
.then(wiki_fetch2_fn,
inputs=[],
outputs=[wiki_list_comp, wiki_source_var, wiki_add_to_db_btn])
wiki_add_to_db_btn\
.click(generate_option_fn,
inputs=[db_collection_var],
outputs=[db_collection_var])\
.then(wiki_add_to_db_fn,
inputs=[wiki_list_comp, wiki_source_var, wiki_db_var, db_list_comp, db_collection_var],
outputs=[db_list_comp, wiki_list_comp, wiki_db_var,
generate_btn, wiki_add_to_db_btn, db_reset_btn])
generate_btn\
.click(generate_fn1,
inputs=[],
outputs=[output_files_comp])\
.then(generate_fn2,
inputs=[db_collection_var, db_list_comp],
outputs=[output_files_comp])
"""
=====================================================
Clear and rerun functions and listeners
=======================================
"""
def clear_fn():
update_ = {
input_files_comp: gr.update(value=None),
output_files_comp: gr.update(value=None, visible=False),
clear_btn: gr.update(visible=False),
rerun_btn: gr.update(visible=False),
}
return update_
clear_btn.click(clear_fn,
inputs=[],
outputs=[input_files_comp, output_files_comp, clear_btn, rerun_btn])
# wiki_clear_btn.click(clear_choices_fn, inputs=[], outputs=[wiki_list_comp]) #listener for the clear button of the wiki choices
return formatdoc