| """ |
| Module définissant l'interface utilisateur Gradio sous forme d'assistant progressif (wizard). |
| Version améliorée avec zone de prévisualisation fixe et meilleur placement des éléments. |
| """ |
| import gradio as gr |
| from utils import collect_author_info, ensure_default_supports |
| from text_analyzer import analyze_work_description, get_explanation |
| from config import (CONTRACT_TYPES, CESSION_MODES, ADDITIONAL_RIGHTS, |
| AUTHOR_TYPES, CIVILITY_OPTIONS, SUPPORTS_OPTIONS) |
| import time |
|
|
|
|
| def create_wizard_interface(generate_pdf_fn, preview_contract_fn): |
| """ |
| Crée l'interface utilisateur Gradio avec navigation progressive par étapes. |
| |
| Args: |
| generate_pdf_fn: Fonction pour générer le PDF |
| preview_contract_fn: Fonction pour prévisualiser le contrat |
| |
| Returns: |
| gr.Blocks: L'interface Gradio configurée |
| """ |
| |
| TOTAL_STEPS = 6 |
| |
| with gr.Blocks(title="Assistant de Contrats de Cession", css="wizard_style.css") as demo: |
| |
| current_step = gr.State(value=1) |
| contract_data = gr.State(value={ |
| "type_contrat": [], |
| "type_cession": "Gratuite", |
| "droits_cedes": [], |
| "exclusivite": False, |
| "auteur_type": "Personne physique", |
| "auteur_info": {}, |
| "description_oeuvre": "", |
| "description_image": "", |
| "supports": [], |
| "remuneration": "" |
| }) |
| |
| gr.Markdown("# Assistant de Création de Contrat de Cession") |
| gr.Markdown("Cet assistant vous guide pas à pas dans la création d'un contrat adapté à vos besoins spécifiques.") |
| |
| |
| with gr.Row(): |
| |
| with gr.Column(scale=3): |
| |
| progress_bar = gr.Slider( |
| minimum=1, |
| maximum=TOTAL_STEPS, |
| value=1, |
| step=1, |
| interactive=False, |
| label="Progression" |
| ) |
| progress_text = gr.Markdown("**Étape 1 sur 6**: Type d'œuvre") |
| |
| |
| with gr.Group(visible=True) as step1_group: |
| gr.Markdown("## Décrivez votre projet") |
| gr.Markdown(""" |
| Décrivez en quelques mots l'œuvre ou le contenu pour lequel vous souhaitez établir un contrat. |
| Exemples: "Une chanson que j'ai composée", "Des photos de mannequins", "Un logo pour une entreprise", etc. |
| """) |
| |
| project_description = gr.Textbox( |
| label="Description de votre projet", |
| placeholder="Ex: Une vidéo où je me filme en train de jouer ma composition au piano", |
| lines=3 |
| ) |
| |
| analyze_btn = gr.Button("Analyser mon projet", variant="secondary") |
| |
| contract_type_suggestion = gr.Markdown( |
| value="Complétez la description et cliquez sur 'Analyser mon projet' pour obtenir une suggestion.", |
| elem_id="contract-suggestion" |
| ) |
| |
| gr.Markdown("### Type de contrat nécessaire") |
| contract_type = gr.CheckboxGroup( |
| CONTRACT_TYPES, |
| label="Sélectionnez le(s) type(s) de contrat", |
| value=[] |
| ) |
| |
| |
| with gr.Group(visible=False) as step2_group: |
| gr.Markdown("## Mode de cession et droits") |
| |
| gr.Markdown("### Mode de cession") |
| gr.Markdown(""" |
| La cession peut se faire à titre gratuit ou onéreux (moyennant rémunération). |
| Une cession gratuite limite les droits cédés aux droits de base (reproduction et représentation). |
| """) |
| |
| cession_mode = gr.Radio( |
| CESSION_MODES, |
| label="La cession se fait-elle à titre gratuit ou onéreux?", |
| value="Gratuite" |
| ) |
| |
| |
| with gr.Group(visible=False) as group_rights: |
| gr.Markdown("### Droits supplémentaires (cession onéreuse)") |
| gr.Markdown(""" |
| Pour une cession onéreuse, vous pouvez céder des droits supplémentaires. |
| Les droits de reproduction et de représentation sont toujours inclus. |
| """) |
| |
| additional_rights = gr.CheckboxGroup( |
| ADDITIONAL_RIGHTS, |
| label="Sélectionnez les droits supplémentaires à céder", |
| value=[] |
| ) |
| |
| gr.Markdown("### Exclusivité") |
| gr.Markdown(""" |
| L'exclusivité signifie que le cédant ne pourra pas exploiter lui-même l'œuvre |
| ni céder les mêmes droits à d'autres personnes pendant la durée du contrat. |
| """) |
| |
| exclusivity = gr.Checkbox( |
| label="Cession exclusive", |
| value=False, |
| info="Cochez cette case pour une cession exclusive" |
| ) |
| |
| |
| with gr.Group(visible=False) as step3_group: |
| gr.Markdown("## Informations sur l'auteur/modèle") |
| |
| author_type = gr.Radio( |
| AUTHOR_TYPES, |
| label="L'auteur/modèle est:", |
| value="Personne physique" |
| ) |
| |
| |
| with gr.Group() as group_physical_person: |
| civility = gr.Radio( |
| CIVILITY_OPTIONS, |
| label="Civilité", |
| value="M." |
| ) |
| |
| with gr.Row(): |
| last_name = gr.Textbox( |
| label="Nom", |
| placeholder="Nom de famille" |
| ) |
| first_name = gr.Textbox( |
| label="Prénom", |
| placeholder="Prénom" |
| ) |
| |
| with gr.Row(): |
| birth_date = gr.Textbox( |
| label="Date de naissance (facultatif)", |
| placeholder="JJ/MM/AAAA" |
| ) |
| nationality = gr.Textbox( |
| label="Nationalité", |
| placeholder="Ex: française" |
| ) |
| |
| address = gr.Textbox( |
| label="Adresse complète", |
| placeholder="Numéro, rue, code postal, ville" |
| ) |
| |
| contact_physical = gr.Textbox( |
| label="Moyen de contact (email, téléphone)", |
| placeholder="Email et/ou téléphone" |
| ) |
| |
| |
| with gr.Group(visible=False) as group_legal_entity: |
| company_name = gr.Textbox( |
| label="Nom de la société", |
| placeholder="Dénomination sociale" |
| ) |
| |
| with gr.Row(): |
| legal_status = gr.Textbox( |
| label="Statut juridique", |
| placeholder="Ex: SARL, SAS, EURL, etc." |
| ) |
| rcs_number = gr.Textbox( |
| label="Numéro RCS", |
| placeholder="Ex: 123 456 789 R.C.S. Paris" |
| ) |
| |
| company_address = gr.Textbox( |
| label="Adresse du siège social", |
| placeholder="Adresse complète du siège" |
| ) |
| |
| contact_company = gr.Textbox( |
| label="Moyen de contact (email, téléphone)", |
| placeholder="Email et/ou téléphone" |
| ) |
| |
| |
| with gr.Group(visible=False) as step4_group: |
| description_title = gr.Markdown("## Description détaillée") |
| |
| |
| with gr.Group(visible=True) as group_work_description: |
| gr.Markdown("### Description de l'œuvre") |
| gr.Markdown(""" |
| Décrivez précisément l'œuvre concernée par la cession de droits. |
| Cette description sera intégrée dans le contrat pour identifier sans ambiguïté l'objet de la cession. |
| """) |
| |
| work_description = gr.Textbox( |
| label="Description de l'œuvre", |
| placeholder="Titre, format, dimensions, support, technique utilisée, date de création, etc.", |
| lines=5 |
| ) |
| |
| |
| with gr.Group(visible=False) as group_image_description: |
| gr.Markdown("### Description des images") |
| gr.Markdown(""" |
| Décrivez précisément les images ou vidéos concernées par la cession du droit à l'image. |
| Précisez le contexte, la date et le lieu de prise de vue, le nombre d'images concernées, etc. |
| """) |
| |
| image_description = gr.Textbox( |
| label="Description des images/vidéos", |
| placeholder="Ex: Séance photo réalisée le [date] à [lieu], comprenant X photographies où apparaît [nom du modèle]", |
| lines=5 |
| ) |
| |
| |
| with gr.Group(visible=False) as step5_group: |
| gr.Markdown("## Supports d'exploitation") |
| gr.Markdown(""" |
| Sélectionnez les supports sur lesquels l'œuvre et/ou l'image pourra être exploitée. |
| Le site web et Discord de Tellers sont automatiquement inclus. |
| """) |
| |
| exploitation_supports = gr.CheckboxGroup( |
| SUPPORTS_OPTIONS, |
| label="Sur quels supports les droits seront-ils exploités?", |
| value=["Réseaux sociaux (Facebook, Instagram, Twitter, etc.)"] |
| ) |
| |
| |
| with gr.Group(visible=False) as group_remuneration: |
| gr.Markdown("### Rémunération") |
| gr.Markdown(""" |
| Précisez les modalités de rémunération pour cette cession onéreuse. |
| Cela peut être un montant forfaitaire ou proportionnel aux recettes. |
| """) |
| |
| remuneration_details = gr.Textbox( |
| label="Modalités de rémunération", |
| placeholder="Ex: 500€ versés à la signature, 5% des recettes versés trimestriellement", |
| lines=3 |
| ) |
| |
| |
| with gr.Group(visible=False) as step6_group: |
| gr.Markdown("## Validation et génération du contrat") |
| gr.Markdown(""" |
| Vous avez complété toutes les étapes nécessaires. |
| Vérifiez le contrat dans l'aperçu à droite, puis générez le PDF final. |
| """) |
| |
| gr.Markdown("### Options de génération") |
| contract_name = gr.Textbox( |
| label="Nom du fichier PDF (optionnel)", |
| placeholder="Ex: Contrat_Cession_Dupont_2025", |
| value="" |
| ) |
| |
| generate_button = gr.Button("Générer le PDF", variant="primary", elem_id="generate-btn") |
| |
| |
| with gr.Group() as generation_status_group: |
| generation_status = gr.Markdown("", elem_id="generation-status") |
| generation_progress = gr.Slider( |
| minimum=0, |
| maximum=100, |
| value=0, |
| step=1, |
| interactive=False, |
| label="Progression", |
| visible=False |
| ) |
| |
| |
| with gr.Group(visible=False) as download_group: |
| gr.Markdown("### Téléchargement") |
| pdf_output = gr.File(label="Votre contrat est prêt!") |
| |
| |
| with gr.Row(): |
| back_button = gr.Button("Précédent", variant="secondary") |
| next_button = gr.Button("Suivant", variant="primary") |
| |
| |
| with gr.Column(scale=2): |
| |
| preview_header = gr.Markdown("## Aperçu du contrat en temps réel") |
| preview_info = gr.Markdown( |
| "Au fur et à mesure que vous remplissez le formulaire, votre contrat se construit ici." |
| ) |
| |
| |
| contract_preview = gr.HTML( |
| value="<div class='fixed-preview'>*Commencez à remplir le formulaire pour voir l'aperçu du contrat*</div>", |
| elem_id="contract-preview" |
| ) |
| |
| |
| |
| |
| def update_progress(step): |
| progress_text_value = f"**Étape {step} sur {TOTAL_STEPS}**: " |
| |
| if step == 1: |
| progress_text_value += "Type d'œuvre" |
| elif step == 2: |
| progress_text_value += "Mode de cession et droits" |
| elif step == 3: |
| progress_text_value += "Informations sur l'auteur/modèle" |
| elif step == 4: |
| progress_text_value += "Description détaillée" |
| elif step == 5: |
| progress_text_value += "Supports d'exploitation" |
| elif step == 6: |
| progress_text_value += "Validation et génération" |
| |
| return step, progress_text_value |
| |
| |
| def analyze_project(description): |
| """Analyse la description et suggère le type de contrat approprié.""" |
| if not description.strip(): |
| return "Veuillez fournir une description pour obtenir une suggestion.", [] |
| |
| detected_types = analyze_work_description(description) |
| explanation = get_explanation(detected_types) |
| |
| return explanation, detected_types |
| |
| |
| analyze_btn.click( |
| fn=analyze_project, |
| inputs=[project_description], |
| outputs=[contract_type_suggestion, contract_type] |
| ) |
| |
| |
| def next_step(current, data, |
| |
| project_desc, contract_types, |
| |
| cession_type, rights, is_exclusive, |
| |
| author_type_val, civility_val, last_name_val, first_name_val, birth_date_val, |
| nationality_val, address_val, contact_physical_val, company_name_val, |
| legal_status_val, rcs_val, company_address_val, contact_company_val, |
| |
| work_desc, image_desc, |
| |
| supports_val, remuneration_val): |
| """Passe à l'étape suivante et met à jour les données du contrat.""" |
| |
| |
| if current == 1: |
| data["project_description"] = project_desc |
| data["type_contrat"] = contract_types |
| elif current == 2: |
| data["type_cession"] = cession_type |
| data["droits_cedes"] = rights if rights else [] |
| data["exclusivite"] = is_exclusive |
| elif current == 3: |
| data["auteur_type"] = author_type_val |
| |
| |
| if author_type_val == "Personne physique": |
| author_info = { |
| "gentille": civility_val, |
| "nom": last_name_val, |
| "prenom": first_name_val, |
| "date_naissance": birth_date_val, |
| "nationalite": nationality_val, |
| "adresse": address_val, |
| "contact": contact_physical_val |
| } |
| else: |
| author_info = { |
| "nom_societe": company_name_val, |
| "statut": legal_status_val, |
| "rcs": rcs_val, |
| "siege": company_address_val, |
| "contact": contact_company_val |
| } |
| |
| data["auteur_info"] = author_info |
| elif current == 4: |
| data["description_oeuvre"] = work_desc |
| data["description_image"] = image_desc |
| elif current == 5: |
| data["supports"] = supports_val |
| data["remuneration"] = remuneration_val |
| |
| |
| if current >= TOTAL_STEPS: |
| current = TOTAL_STEPS |
| else: |
| current += 1 |
| |
| |
| step1_visibility = (current == 1) |
| step2_visibility = (current == 2) |
| step3_visibility = (current == 3) |
| step4_visibility = (current == 4) |
| step5_visibility = (current == 5) |
| step6_visibility = (current == 6) |
| |
| |
| rights_visibility = (current == 2 and cession_type == "Onéreuse") |
| remuneration_visibility = (current == 5 and data["type_cession"] == "Onéreuse") |
| |
| |
| show_work_desc = True |
| show_image_desc = False |
| |
| if current == 4: |
| show_work_desc = "Auteur (droits d'auteur)" in data["type_contrat"] |
| show_image_desc = "Image (droit à l'image)" in data["type_contrat"] |
| |
| |
| show_physical_person = (current == 3 and author_type_val == "Personne physique") |
| show_legal_entity = (current == 3 and author_type_val == "Personne morale") |
| |
| |
| preview = preview_contract(data) |
| |
| |
| new_progress, progress_text_val = update_progress(current) |
| |
| return ( |
| |
| current, data, |
| |
| new_progress, progress_text_val, |
| |
| gr.update(visible=step1_visibility), gr.update(visible=step2_visibility), |
| gr.update(visible=step3_visibility), gr.update(visible=step4_visibility), |
| gr.update(visible=step5_visibility), gr.update(visible=step6_visibility), |
| |
| gr.update(visible=rights_visibility), gr.update(visible=remuneration_visibility), |
| gr.update(visible=show_work_desc), gr.update(visible=show_image_desc), |
| gr.update(visible=show_physical_person), gr.update(visible=show_legal_entity), |
| |
| preview |
| ) |
| |
| |
| def previous_step(current, data): |
| """Revient à l'étape précédente.""" |
| |
| if current <= 1: |
| current = 1 |
| else: |
| current -= 1 |
| |
| |
| step1_visibility = (current == 1) |
| step2_visibility = (current == 2) |
| step3_visibility = (current == 3) |
| step4_visibility = (current == 4) |
| step5_visibility = (current == 5) |
| step6_visibility = (current == 6) |
| |
| |
| rights_visibility = (current == 2 and data["type_cession"] == "Onéreuse") |
| remuneration_visibility = (current == 5 and data["type_cession"] == "Onéreuse") |
| |
| |
| show_work_desc = True |
| show_image_desc = False |
| |
| if current == 4: |
| show_work_desc = "Auteur (droits d'auteur)" in data["type_contrat"] |
| show_image_desc = "Image (droit à l'image)" in data["type_contrat"] |
| |
| |
| show_physical_person = (current == 3 and data["auteur_type"] == "Personne physique") |
| show_legal_entity = (current == 3 and data["auteur_type"] == "Personne morale") |
| |
| |
| preview = preview_contract(data) |
| |
| |
| new_progress, progress_text_val = update_progress(current) |
| |
| return ( |
| |
| current, data, |
| |
| new_progress, progress_text_val, |
| |
| gr.update(visible=step1_visibility), gr.update(visible=step2_visibility), |
| gr.update(visible=step3_visibility), gr.update(visible=step4_visibility), |
| gr.update(visible=step5_visibility), gr.update(visible=step6_visibility), |
| |
| gr.update(visible=rights_visibility), gr.update(visible=remuneration_visibility), |
| gr.update(visible=show_work_desc), gr.update(visible=show_image_desc), |
| gr.update(visible=show_physical_person), gr.update(visible=show_legal_entity), |
| |
| preview |
| ) |
| |
| |
| def update_cession_mode_display(mode): |
| """Met à jour l'affichage des champs liés au mode de cession.""" |
| is_onereux = (mode == "Onéreuse") |
| return gr.update(visible=is_onereux) |
| |
| |
| def update_author_type_display(type_val): |
| """Met à jour l'affichage des champs liés au type d'auteur.""" |
| is_physical = (type_val == "Personne physique") |
| return gr.update(visible=is_physical), gr.update(visible=not is_physical) |
| |
| |
| def generate_pdf(contract_data, filename): |
| """Génère le PDF du contrat avec indication de progression.""" |
| |
| |
| yield gr.update(value="Préparation des données..."), gr.update(visible=True, value=25), gr.update(visible=False), None |
| time.sleep(0.5) |
| |
| |
| yield gr.update(value="Construction du contrat..."), gr.update(visible=True, value=50), gr.update(visible=False), None |
| time.sleep(0.5) |
| |
| |
| yield gr.update(value="Génération du PDF..."), gr.update(visible=True, value=75), gr.update(visible=False), None |
| |
| |
| pdf_path = generate_pdf_fn( |
| contract_data["type_contrat"], |
| contract_data["type_cession"], |
| contract_data["auteur_type"], |
| contract_data["auteur_info"], |
| contract_data["description_oeuvre"], |
| contract_data["description_image"], |
| contract_data["supports"], |
| contract_data["droits_cedes"], |
| contract_data["remuneration"], |
| contract_data["exclusivite"] |
| ) |
| |
| |
| yield gr.update(value="Contrat PDF généré avec succès!"), gr.update(visible=True, value=100), gr.update(visible=True), pdf_path |
| |
| |
| def preview_contract(data): |
| """Génère un aperçu HTML formaté du contrat.""" |
| |
| |
| if not data.get("type_contrat"): |
| return "<div class='fixed-preview'>*Complétez au moins le type de contrat pour voir l'aperçu*</div>" |
| |
| |
| try: |
| preview_text = preview_contract_fn( |
| data.get("type_contrat", []), |
| data.get("type_cession", "Gratuite"), |
| data.get("auteur_type", "Personne physique"), |
| data.get("auteur_info", {}), |
| data.get("description_oeuvre", ""), |
| data.get("description_image", ""), |
| data.get("supports", []), |
| data.get("droits_cedes", []), |
| data.get("remuneration", ""), |
| data.get("exclusivite", False) |
| ) |
| |
| |
| preview_html = preview_text.replace("\n", "<br>") |
| |
| |
| for ligne in preview_text.split("\n"): |
| if ligne.strip().startswith("ARTICLE") or ligne.strip().isupper(): |
| preview_html = preview_html.replace(ligne, f"<h3>{ligne}</h3>") |
| |
| |
| return f"<div class='fixed-preview'>{preview_html}</div>" |
| except Exception as e: |
| return f"<div class='fixed-preview'>*Erreur de prévisualisation: {str(e)}*</div>" |
| |
| |
| |
| |
| next_button.click( |
| fn=next_step, |
| inputs=[ |
| current_step, contract_data, |
| |
| project_description, contract_type, |
| |
| cession_mode, additional_rights, exclusivity, |
| |
| author_type, civility, last_name, first_name, birth_date, |
| nationality, address, contact_physical, company_name, |
| legal_status, rcs_number, company_address, contact_company, |
| |
| work_description, image_description, |
| |
| exploitation_supports, remuneration_details |
| ], |
| outputs=[ |
| current_step, contract_data, |
| |
| progress_bar, progress_text, |
| |
| step1_group, step2_group, step3_group, step4_group, step5_group, step6_group, |
| |
| group_rights, group_remuneration, |
| group_work_description, group_image_description, |
| group_physical_person, group_legal_entity, |
| |
| contract_preview |
| ] |
| ) |
| |
| back_button.click( |
| fn=previous_step, |
| inputs=[current_step, contract_data], |
| outputs=[ |
| current_step, contract_data, |
| |
| progress_bar, progress_text, |
| |
| step1_group, step2_group, step3_group, step4_group, step5_group, step6_group, |
| |
| group_rights, group_remuneration, |
| group_work_description, group_image_description, |
| group_physical_person, group_legal_entity, |
| |
| contract_preview |
| ] |
| ) |
| |
| |
| cession_mode.change( |
| fn=update_cession_mode_display, |
| inputs=[cession_mode], |
| outputs=[group_rights] |
| ) |
| |
| author_type.change( |
| fn=update_author_type_display, |
| inputs=[author_type], |
| outputs=[group_physical_person, group_legal_entity] |
| ) |
| |
| |
| generate_button.click( |
| fn=generate_pdf, |
| inputs=[contract_data, contract_name], |
| outputs=[ |
| generation_status, generation_progress, |
| download_group, pdf_output |
| ] |
| ) |
| |
| return demo |