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