| | import streamlit as st |
| | import markdown2 |
| | import pdfkit |
| | from io import BytesIO |
| | from IPython.display import display, FileLink |
| | import base64 |
| | from langchain_core.messages import AIMessage, HumanMessage |
| | from datetime import datetime |
| | from download_chart import construct_plot |
| | from kaleido.scopes.plotly import PlotlyScope |
| | import pandas as pd |
| | import markdown |
| | from comparateur import get_table_empreintes_detailed |
| | from empreinte_export import get_carbon_footprint_html |
| |
|
| | def colored_circle(color): |
| | return f'<span style="display: inline-block; width: 15px; height: 15px; border-radius: 50%; background-color: {color};"></span>' |
| |
|
| | def list_to_markdown(lst): |
| | return "\n".join([f'<p>{colored_circle(item["color"])} <b>{item["name"]}</b>: Pouvoir:{item["y"]}% Influence:{item["x"]}%</p>' for item in lst[:20]]) |
| |
|
| | def categorize(row): |
| | if 50 <= row['pouvoir'] <= 100 and 0 <= row['influence'] < 50: |
| | return 'Rendre satisfait' |
| | elif 50 <= row['pouvoir'] <= 100 and 50 <= row['influence'] <= 100: |
| | return 'Gérer étroitement' |
| | elif 0 <= row['pouvoir'] < 50 and 0 <= row['influence'] < 50: |
| | return 'Suivre de près' |
| | elif 0 <= row['pouvoir'] < 50 and 50 <= row['influence'] <= 100: |
| | return 'Tenir informé' |
| | else: |
| | return 'Non catégorisé' |
| | |
| |
|
| |
|
| | @st.cache_data |
| | def convert_pp_to_csv(pp_grouped): |
| | if pp_grouped is None or len(pp_grouped) == 0: |
| | st.error("Aucune partie prenante n'a été définie") |
| | return None |
| | pp_df = pd.DataFrame(pp_grouped) |
| | pp_df.index.name = 'N°' |
| | pp_df.rename(columns={"name": "parties prenantes", "x": "influence", "y": "pouvoir"}, inplace=True) |
| | pp_df.drop(columns=['color'], inplace=True) |
| | |
| | pp_df['categorie'] = pp_df.apply(categorize, axis=1) |
| | pp_df = pp_df[["parties prenantes","categorie", "pouvoir", "influence"]] |
| | pp_df.rename_axis('N°', axis=1) |
| | return pp_df.to_csv(index=True,encoding="utf-8") |
| |
|
| | @st.cache_data |
| | def create_pdf_from_markdown(logo_path, conversation,summary,brand_name,graph_html,app_url,list_pps,used_models=None): |
| | |
| | markdown_text = "\n".join([f"### {entry['speaker']}:\n {entry['text']}\n ---" for entry in conversation]) |
| |
|
| | if not used_models: |
| | used_models = ["Aucun modèle IA n'a été utilisé"] |
| | html_used_models = "".join([f"<p>{model}</p>" for model in used_models]) |
| | |
| | markdown_summary = f"{summary}\n --- \n ---" |
| | markdown_list_pps = list_to_markdown(list_pps) |
| | |
| | html_content = markdown.markdown(markdown_text,extensions=['markdown.extensions.tables']) |
| | html_summary = markdown2.markdown(markdown_summary) |
| | html_list_pps = markdown2.markdown(markdown_list_pps) |
| | |
| | analysis_date = datetime.now().strftime("%Y-%m-%d") |
| | |
| | graph_html.update_layout(showlegend=False) |
| | img_bytes = PlotlyScope().transform( |
| | figure=graph_html, |
| | format="png", |
| | ) |
| | fig1 = f"data:image/png;base64,{base64.b64encode(img_bytes).decode('utf8')}" |
| | |
| |
|
| | html_template = f""" |
| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <title>Cartographie des parties prenantes {brand_name}</title> |
| | <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet"> |
| | <style> |
| | body {{ |
| | font-family: 'Roboto', sans-serif; |
| | margin: 20px; |
| | }} |
| | h1, h2, h3, h4, h5, h6 {{ |
| | font-weight: bold; |
| | }} |
| | .page-break {{ |
| | page-break-before: always; |
| | margin: 50px; |
| | height: 50px; |
| | }} |
| | </style> |
| | </head> |
| | <body> |
| | <div style="text-align: center;"> |
| | <h1>Cartographie des parties prenantes "{brand_name}"</h1> |
| | <p>Date de l'analyse IA RSE : {analysis_date}</p> |
| | <p>IA utilisées :</p> |
| | {html_used_models} |
| | <img src="{logo_path}" alt="Logo" style="width: 150px;"/> |
| | </div> |
| | <div class="page-break"></div> |
| | <div style="text-align: center; margin-top: 20px;"> |
| | <img src="{fig1}"> |
| | </div> |
| | {html_list_pps} |
| | <div class="page-break"></div> |
| | <h2>RESUME</h2> |
| | {html_summary} |
| | <div class="page-break"></div> |
| | <h2>Historique de la Conversation</h2> |
| | {html_content} |
| | <div class="page-break"></div> |
| | {get_carbon_footprint_html()} |
| | </body> |
| | </html> |
| | """ |
| |
|
| | with open("temp.html", "w",encoding="utf-8") as f: |
| | f.write(html_template) |
| | |
| | |
| | footer_html = f""" |
| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet"> |
| | <meta charset="UTF-8"> |
| | <style> |
| | body {{ |
| | font-family: 'Roboto', sans-serif; |
| | margin-top: 20px; |
| | }} |
| | .footer {{ |
| | width: 100%; |
| | font-size: 16px; |
| | display: flex; |
| | align-items: center; |
| | justify-content: space-between; |
| | padding: 10px 20px; |
| | }} |
| | .footer img {{ |
| | width: 100px; |
| | vertical-align: middle; |
| | margin-bottom: 0px; |
| | padding-bottom: 0px; |
| | |
| | }} |
| | .footer .center-text {{ |
| | text-align: center; |
| | |
| | }} |
| | .footer .page-number {{ |
| | text-align: right; |
| | }} |
| | .footer a {{ |
| | color: #0000EE; |
| | text-decoration: none; |
| | }} |
| | .page {{ |
| | font-weight: bold; |
| | font-size: 10px; |
| | margin-bottom: 0px; |
| | padding-bottom: 0px; |
| | }} |
| | |
| | </style> |
| | </head> |
| | <body> |
| | <div class="footer"> |
| | <img src="{logo_path}" alt="Logo" /> |
| | <div class="center-text"> |
| | bziiit | Open data & IA RSE | <a href="{app_url}">{app_url}</a> |
| | </div> |
| | <div class="page-number"> |
| | <span class="page"></span> |
| | </div> |
| | </div> |
| | </body> |
| | </html> |
| | """ |
| |
|
| | |
| | |
| | with open("footer.html", "w",encoding="utf-8") as f: |
| | f.write(footer_html) |
| |
|
| |
|
| | |
| | pdf = pdfkit.from_file("temp.html", options={ |
| | 'footer-html': 'footer.html', |
| | 'footer-right': '[page]/[toPage]', |
| | 'footer-font-size': '10', |
| | 'footer-spacing': '5', |
| | 'footer-line': True, |
| | 'margin-top': '5', |
| | }) |
| | return pdf |
| |
|
| | def get_conversation(): |
| | conversation = [] |
| | for message in st.session_state.chat_history: |
| | if isinstance(message, AIMessage): |
| | conversation.append({"speaker": "AI", "text": message.content}) |
| | elif isinstance(message, HumanMessage): |
| | conversation.append({"speaker": "Moi", "text": message.content}) |
| | return conversation |
| |
|
| |
|
| | def export_conversation(summary,used_models=None): |
| | brand_name = st.session_state["Nom de la marque"] |
| | app_url = "https://huggingface.co/spaces/bziiit/OpenData-Bordeaux-RSE" |
| | logo_path = "https://static.wixstatic.com/media/d7d3da_b69e03ae99224f7d8b6e358918e60071~mv2.png/v1/crop/x_173,y_0,w_1906,h_938/fill/w_242,h_119,al_c,q_85,usm_0.66_1.00_0.01,enc_auto/BZIIIT_LOGO-HORIZ-COULEUR.png" |
| | list_pps = st.session_state['pp_grouped'] |
| |
|
| | with st.spinner("Génération du PDF..."): |
| | conversation = get_conversation() |
| | image_path = "newplot.png" |
| | try: |
| | graph = construct_plot() |
| | |
| | except Exception as e: |
| | st.error("Erreur lors de la génération de la cartographie") |
| | graph = "" |
| | try: |
| | pdf = create_pdf_from_markdown(logo_path=logo_path, conversation=conversation,summary=summary,brand_name=brand_name,graph_html=graph,app_url=app_url,list_pps=list_pps,used_models=used_models) |
| | except Exception as e: |
| | pdf = None |
| |
|
| | if pdf: |
| | st.success("PDF généré avec succès!}") |
| | else: |
| | st.error("Erreur lors de la génération du PDF") |
| | |
| | return pdf |
| | |
| |
|
| |
|