|
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") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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([]) |
|
wiki_db_var: [str] = gr.State([]) |
|
my_files_db_var: [str] = gr.State([]) |
|
db_collection_var: str = gr.State("-1") |
|
|
|
""" |
|
=================================================== |
|
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() |
|
|
|
|
|
|
|
|
|
|
|
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_) |
|
|
|
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), |
|
|
|
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 |
|
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), |
|
|
|
} |
|
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] |
|
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]) |
|
|
|
|
|
return formatdoc |
|
|