GenProp / src /view /view.py
adrien.aribaut-gaudin
feat: requirement part fonctionnal
3ca15d8
Raw
History Blame Contribute Delete
26.9 kB
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