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 = "

GenProp

" 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("

Par Hexamind

") gr.Markdown("") with gr.Row(): with gr.Column(): pass with gr.Column(scale=10): """ ===================================================== Input and style components ========================== """ gr.Markdown("

Vous êtes chargé de produire une proposition commerciale

") 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("

A des fins de démonstrations, la base de connaissance est alimentée depuis Wikipedia

") 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("

Vous êtes administrateur de GenProp

") 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