.dockerignore DELETED
@@ -1,11 +0,0 @@
1
- __pycache__/
2
- *.pyc
3
- *.pyo
4
- *.pyd
5
- .Python
6
- env/
7
- venv/
8
- .git
9
- .dockerignore
10
- Dockerfile
11
- *.md
 
 
 
 
 
 
 
 
 
 
 
 
.gitattributes CHANGED
@@ -33,6 +33,3 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
- DATA_bziiit/op.pdf filter=lfs diff=lfs merge=lfs -text
37
- DATA_bziiit/vectorstore_op/index.faiss filter=lfs diff=lfs merge=lfs -text
38
- *.pdf filter=lfs diff=lfs merge=lfs -text
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
.gitignore CHANGED
@@ -1,4 +1,3 @@
1
  __pycache__/
2
- .streamlit
3
- DATA_bziiit/vectorstore_op
4
- .env
 
1
  __pycache__/
2
+ .streamlit/secrets.toml
3
+ .streamlit/.env
 
.streamlit/.env ADDED
@@ -0,0 +1 @@
 
 
1
+ API_TOKEN_PERPLEXITYAI = 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
.streamlit/config.toml DELETED
@@ -1,8 +0,0 @@
1
- [server]
2
- maxUploadSize = 5
3
-
4
- [theme]
5
- base="light"
6
- primaryColor="#63abdf"
7
- secondaryBackgroundColor="#fbf7f1"
8
- textColor="#011166"
 
 
 
 
 
 
 
 
 
AnalyseActionsRSE.py CHANGED
@@ -1,7 +1,5 @@
1
  import streamlit as st
2
  from ISO26000 import classify_actions_rse_ISO26000 as classify_iso26000
3
- from ODD import classify_actions_rse_ODD as classify_odd
4
- from impactscore import classify_actions_rse_IMPACTSCORE as classify_impactscore
5
  from impactscore import classify_actions_rse_IMPACTSCORE as classify_impactscore
6
  from data_manager import get_data
7
 
@@ -81,8 +79,39 @@ def display_analyse_actions_rse():
81
  st.markdown("""<hr style='border-color: darkgrey;'>""", unsafe_allow_html=True)
82
  st.markdown(f"**Total des actions RSE :** {total_actions}")
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  elif approach == "ODD Objectifs de Développement Durable (en cours de développement)":
85
- # Récupérer les données depuis data_manager.py
86
  data, total_hits = get_data()
87
 
88
  st.markdown("""<hr style='border-color: darkgrey;'>""", unsafe_allow_html=True)
@@ -117,95 +146,11 @@ def display_analyse_actions_rse():
117
 
118
  st.markdown("""<small>Source AGENDA 2030 EN FRANCE : <a href="https://www.agenda-2030.fr/17-objectifs-de-developpement-durable/?" target="_blank">https://impactscore.fr/comprendre-limpact-score/</a></small>""", unsafe_allow_html=True)
119
 
120
- st.markdown("""<hr style='border-color: darkgrey;'>""", unsafe_allow_html=True)
121
-
122
- pictograms = {
123
- "Pas de pauvreté": "🏚️",
124
- "Faim « Zéro »": "🌾",
125
- "Bonne santé et bien-être": "🏥",
126
- "Éducation de qualité": "🎓",
127
- "Égalité entre les sexes": "⚧️",
128
- "Eau propre et assainissement": "💧",
129
- "Énergie propre et d'un coût abordable": "⚡",
130
- "Travail décent et croissance économique": "👷",
131
- "Industrie, innovation et infrastructure": "🏭",
132
- "Inégalités réduites": "⚖️",
133
- "Villes et communautés durables": "🏙️",
134
- "Consommation et production responsables": "♻️",
135
- "Lutte contre les changements climatiques": "🌍",
136
- "Vie aquatique": "🐟",
137
- "Vie terrestre": "🌳",
138
- "Paix, justice et institutions efficaces": "⚖️",
139
- "Partenariats pour la réalisation des objectifs": "🤝",
140
- "Autres": "❓"
141
- }
142
-
143
-
144
- criteria_counts = classify_odd(data)
145
-
146
- total_actions = 0
147
-
148
- for category, actions in criteria_counts.items():
149
- if category in pictograms:
150
- st.subheader(f"{pictograms[category]} {category}")
151
- else:
152
- st.subheader(f"{pictograms['Autres']} Autres")
153
- total_actions += len(actions)
154
- for action in actions:
155
- nom_entreprise = action.get('nom_courant_denomination', 'Information non disponible')
156
- st.write(f"Entreprise : {action.get('name','N/A')}, Action RSE : {action.get('action_rse', 'N/A')}, Activité : {action.get('activity', 'N/A')}, Ville : {action.get('city', 'N/A')}")
157
-
158
- st.markdown("""<hr style='border-color: darkgrey;'>""", unsafe_allow_html=True)
159
- st.markdown(f"**Total des actions RSE :** {total_actions}")
160
-
161
- ### OBJECTIF DE DEVELOPPEMENT DURABLE ###
162
- elif approach == "Impact Score (en cours de développement)":
163
- # Récupérer les données depuis data_manager.py
164
- data, total_hits = get_data()
165
-
166
  st.markdown("""<hr style='border-color: darkgrey;'>""", unsafe_allow_html=True)
167
 
168
- st.markdown("""
169
- 🌳 **QU'EST-CE QUE L'IMPACT SCORE ?**
170
-
171
- Ce référentiel commun et unique a été co-construit par 30 réseaux d’entreprises afin de publier en transparence leurs données d’impact, exigence européenne depuis 2024.
172
-
173
- **COMMENT EST-IL STRUCTURÉE ?**
174
-
175
- IMPACT SCORE repose sur 3 piliers essentiels :
176
-
177
- - 🚫 LIMITATION DES EXTERNALITÉS NÉGATIVES
178
- - 💡 PARTAGE DU POUVOIR ET DE LA VALEUR
179
- - 🎯 STRATÉGIE À IMPACT
180
- """)
181
-
182
-
183
- st.markdown("""<small>Source MOUVEMENT IMPACT FRANCE : <a href="https://impactscore.fr/comprendre-limpact-score/" target="_blank">https://impactscore.fr/comprendre-limpact-score/</a></small>""", unsafe_allow_html=True)
184
-
185
- st.markdown("""<hr style='border-color: darkgrey;'>""", unsafe_allow_html=True)
186
-
187
- pictograms = {
188
- "Initiatives pour réduire l'empreinte carbone": "🌍",
189
- "Amélioration des conditions de travail": "👷",
190
- "Promotion du recyclage": "♻️",
191
- "Autres": "❓"
192
- }
193
-
194
  criteria_counts = classify_impactscore(data)
195
 
196
- total_actions = 0
197
-
198
- for category, actions in criteria_counts.items():
199
- if category in pictograms:
200
- st.subheader(f"{pictograms[category]} {category}")
201
- else:
202
- st.subheader(f"{pictograms['Autres']} Autres")
203
- total_actions += len(actions)
204
- for action in actions:
205
- nom_entreprise = action.get('nom_courant_denomination', 'Information non disponible')
206
- st.write(f"Entreprise : {action.get('name','N/A')}, Action RSE : {action.get('action_rse', 'N/A')}, Activité : {action.get('activity', 'N/A')}, Ville : {action.get('city', 'N/A')}")
207
-
208
- st.markdown("""<hr style='border-color: darkgrey;'>""", unsafe_allow_html=True)
209
  st.markdown(f"**Total des actions RSE :** {total_actions}")
210
 
211
  if approach == "Norme ISO 26000":
@@ -215,19 +160,5 @@ def display_analyse_actions_rse():
215
  for category, count in synthesis_sorted.items():
216
  st.write(f"{category}: {count} action(s) RSE")
217
 
218
- if approach == "ODD Objectifs de Développement Durable (en cours de développement)":
219
- st.subheader("Synthèse par catégorie ODD")
220
- synthesis = {category: len(actions) for category, actions in criteria_counts.items()}
221
- synthesis_sorted = dict(sorted(synthesis.items(), key=lambda item: item[1], reverse=True))
222
- for category, count in synthesis_sorted.items():
223
- st.write(f"{category}: {count} action(s) RSE")
224
-
225
- if approach == "Impact Score (en cours de développement)":
226
- st.subheader("Synthèse par catégorie ODD")
227
- synthesis = {category: len(actions) for category, actions in criteria_counts.items()}
228
- synthesis_sorted = dict(sorted(synthesis.items(), key=lambda item: item[1], reverse=True))
229
- for category, count in synthesis_sorted.items():
230
- st.write(f"{category}: {count} action(s) RSE")
231
-
232
  if __name__ == "__main__":
233
  display_analyse_actions_rse()
 
1
  import streamlit as st
2
  from ISO26000 import classify_actions_rse_ISO26000 as classify_iso26000
 
 
3
  from impactscore import classify_actions_rse_IMPACTSCORE as classify_impactscore
4
  from data_manager import get_data
5
 
 
79
  st.markdown("""<hr style='border-color: darkgrey;'>""", unsafe_allow_html=True)
80
  st.markdown(f"**Total des actions RSE :** {total_actions}")
81
 
82
+ elif approach == "Impact Score (en cours de développement)":
83
+ # Récupérer les données depuis data_manager.py
84
+ data, total_hits = get_data()
85
+
86
+ st.markdown("""<hr style='border-color: darkgrey;'>""", unsafe_allow_html=True)
87
+
88
+ st.markdown("""
89
+ 🌳 **QU'EST-CE QUE L'IMPACT SCORE ?**
90
+
91
+ Ce référentiel commun et unique a été co-construit par 30 réseaux d’entreprises afin de publier en transparence leurs données d’impact, exigence européenne depuis 2024.
92
+
93
+ **COMMENT EST-IL STRUCTURÉE ?**
94
+
95
+ IMPACT SCORE repose sur 3 piliers essentiels :
96
+
97
+ - 🚫 LIMITATION DES EXTERNALITÉS NÉGATIVES
98
+ - 💡 PARTAGE DU POUVOIR ET DE LA VALEUR
99
+ - 🎯 STRATÉGIE À IMPACT
100
+ """)
101
+
102
+
103
+ st.markdown("""<small>Source MOUVEMENT IMPACT FRANCE : <a href="https://impactscore.fr/comprendre-limpact-score/" target="_blank">https://impactscore.fr/comprendre-limpact-score/</a></small>""", unsafe_allow_html=True)
104
+
105
+ st.markdown("""<hr style='border-color: darkgrey;'>""", unsafe_allow_html=True)
106
+
107
+ criteria_counts = classify_impactscore(data)
108
+
109
+ total_actions = sum([len(actions) for actions in criteria_counts.values()])
110
+ st.markdown(f"**Total des actions RSE :** {total_actions}")
111
+
112
+ ### OBJECTIF DE DEVELOPPEMENT DURABLE ###
113
  elif approach == "ODD Objectifs de Développement Durable (en cours de développement)":
114
+ # Récupérer les données depuis data_manager.py
115
  data, total_hits = get_data()
116
 
117
  st.markdown("""<hr style='border-color: darkgrey;'>""", unsafe_allow_html=True)
 
146
 
147
  st.markdown("""<small>Source AGENDA 2030 EN FRANCE : <a href="https://www.agenda-2030.fr/17-objectifs-de-developpement-durable/?" target="_blank">https://impactscore.fr/comprendre-limpact-score/</a></small>""", unsafe_allow_html=True)
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  st.markdown("""<hr style='border-color: darkgrey;'>""", unsafe_allow_html=True)
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  criteria_counts = classify_impactscore(data)
152
 
153
+ total_actions = sum([len(actions) for actions in criteria_counts.values()])
 
 
 
 
 
 
 
 
 
 
 
 
154
  st.markdown(f"**Total des actions RSE :** {total_actions}")
155
 
156
  if approach == "Norme ISO 26000":
 
160
  for category, count in synthesis_sorted.items():
161
  st.write(f"{category}: {count} action(s) RSE")
162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  if __name__ == "__main__":
164
  display_analyse_actions_rse()
DATA_bziiit/vectorstore_op/index.faiss DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:0d308f98f3798328b0c1efdfb5ab5ade0e8b7b93f7f32ee439b54aa060879067
3
- size 1228845
 
 
 
 
DATA_bziiit/vectorstore_op/index.pkl DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:7b492225278bd4ba23d11fe72fa16f8abd9a023babcc6734901740ba34fd0ba7
3
- size 106874
 
 
 
 
Dockerfile DELETED
@@ -1,34 +0,0 @@
1
- # Use the official lightweight Python image.
2
- # https://hub.docker.com/_/python
3
- FROM python:3.9-slim
4
-
5
- # Set environment variables
6
- ENV PYTHONDONTWRITEBYTECODE 1
7
- ENV PYTHONUNBUFFERED 1
8
-
9
- # Create and set working directory
10
- WORKDIR /app
11
-
12
- RUN apt-get update \
13
- && apt-get install wkhtmltopdf -y \
14
- && mv /usr/bin/wkhtmltopdf /usr/local/bin/.
15
-
16
- # Copy system package requirements file
17
- COPY packages.txt .
18
-
19
- RUN apt-get update
20
-
21
- # Copy the application requirements file
22
- COPY requirements.txt .
23
-
24
- # Install Python dependencies
25
- RUN pip install --no-cache-dir -r requirements.txt
26
-
27
- # Copy the application code
28
- COPY . .
29
-
30
- # Expose the Streamlit port
31
- EXPOSE 8501
32
-
33
- # Run Streamlit app
34
- CMD ["streamlit", "run", "app.py"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ODD.py CHANGED
@@ -1,90 +1,66 @@
1
- from data_manager import get_data
2
-
3
- def classify_actions_rse_ODD(data):
4
- data, _ = get_data() # Récupérer les données depuis data_manager.py
5
-
6
- criteria = {
7
- "Pas de pauvreté": [],
8
- "Faim « Zéro »": [],
9
- "Bonne santé et bien-être": [],
10
- "Éducation de qualité": [],
11
- "Égalité entre les sexes": [],
12
- "Eau propre et assainissement": [],
13
- "Énergie propre et d'un coût abordable": [],
14
- "Travail décent et croissance économique": [],
15
- "Industrie, innovation et infrastructure": [],
16
- "Inégalités réduites": [],
17
- "Villes et communautés durables": [],
18
- "Consommation et production responsables": [],
19
- "Lutte contre les changements climatiques": [],
20
- "Vie aquatique": [],
21
- "Vie terrestre": [],
22
- "Paix, justice et institutions efficaces": [],
23
- "Partenariats pour la réalisation des objectifs": [],
24
- "Autres": []
25
- }
26
-
27
-
28
- # Keywords for each ISO 26000 category to help in classifying the actions
29
- keywords = {
30
- "Pas de pauvreté": ["pauvreté", "aide financière", "microcrédit", "inclusion financière", "projets anti-pauvreté", "programmes de soutien", "subventions sociales", "initiatives d'entreprenariat"],
31
- "Faim « Zéro »": ["faim", "sécurité alimentaire", "nutrition", "agriculture durable", "programmes de nutrition", "alimentation scolaire", "agroécologie", "technologies agricoles"],
32
- "Bonne santé et bien-être": ["santé", "bien-être", "vaccination", "hôpitaux", "soins médicaux", "prévention des maladies", "santé mentale", "accès aux soins"],
33
- "Éducation de qualité": ["éducation", "scolarisation", "formation", "alphabétisation", "éducation pour tous", "technologie éducative", "programmes éducatifs", "qualité de l'enseignement"],
34
- "Égalité entre les sexes": ["égalité des sexes", "droits des femmes", "empowerment des femmes", "lutte contre les violences de genre", "participation des femmes", "leadership féminin", "éducation des filles", "santé reproductive"],
35
- "Eau propre et assainissement": ["eau propre", "assainissement", "accès à l'eau", "traitement de l'eau", "gestion des ressources en eau", "sanitation", "qualité de l'eau", "hygiène"],
36
- "Énergie propre et d'un coût abordable": ["énergie renouvelable", "énergie propre", "efficacité énergétique", "technologies énergétiques", "accès à l'énergie", "énergie solaire", "énergie éolienne", "infrastructures énergétiques"],
37
- "Travail décent et croissance économique": ["emploi", "droits du travail", "croissance économique", "entreprise responsable", "économie inclusive", "emploi de qualité", "conditions de travail équitables", "sécurité de l'emploi"],
38
- "Industrie, innovation et infrastructure": ["industrie", "innovation", "infrastructure", "technologie", "recherche et développement", "industrialisation durable", "innovation technologique", "infrastructures résilientes"],
39
- "Inégalités réduites": ["réduction des inégalités", "inclusion sociale", "équité", "justice sociale", "accès équitable", "droits de l'homme", "intégration sociale", "lutte contre la discrimination"],
40
- "Villes et communautés durables": ["urbanisme durable", "communautés résilientes", "transports publics", "éco-quartiers", "développement urbain", "mobilité urbaine", "gestion urbaine", "logement abordable"],
41
- "Consommation et production responsables": ["consommation durable", "production durable", "économie circulaire", "recyclage", "gestion des déchets", "consommation éthique", "réduction de l'empreinte écologique", "sourcing responsable"],
42
- "Lutte contre les changements climatiques": ["changements climatiques", "atténuation", "adaptation au climat", "réduction des émissions", "politiques climatiques", "énergies vertes", "carboneutralité", "initiatives climatiques"],
43
- "Vie aquatique": ["océans", "mers", "conservation marine", "pêche durable", "biodiversité marine", "écosystèmes marins", "protection des habitats marins", "lutte contre la pollution marine"],
44
- "Vie terrestre": ["biodiversité terrestre", "conservation des forêts", "reforestation", "lutte contre la désertification", "protection de la faune", "aires protégées", "gestion durable des terres", "restauration des habitats"],
45
- "Paix, justice et institutions efficaces": ["paix", "justice", "lutte contre la corruption", "institutions solides", "état de droit", "droits humains", "transparence gouvernementale", "accès à la justice"],
46
- "Partenariats pour la réalisation des objectifs": ["partenariats mondiaux", "coopération internationale", "mobilisation des ressources", "alignement des politiques", "collaboration multisectorielle", "coopération transfrontalière", "engagement des parties prenantes", "synergies globales"]
47
- }
48
-
49
- keywords = {
50
- "Pas de pauvreté": ["pauvreté", "aide financière", "microcrédit", "inclusion financière", "soutien économique", "subventions", "réduction de la pauvreté"],
51
- "Faim « Zéro »": ["faim", "sécurité alimentaire", "nutrition", "agriculture durable", "aide alimentaire", "cultures résilientes"],
52
- "Bonne santé et bien-être": ["santé", "bien-être", "vaccination", "hôpitaux", "soins médicaux", "prévention des maladies", "santé publique", "accès aux soins"],
53
- "Éducation de qualité": ["éducation", "scolarisation", "formation", "alphabétisation", "accès à l'éducation", "qualité de l'enseignement"],
54
- "Égalité entre les sexes": ["égalité des sexes", "droits des femmes", "empowerment des femmes", "égalité de genre", "lutte contre les discriminations de genre", "participation des femmes", "leadership féminin", "éducation des filles", "santé reproductive", "violences de genre","leadership féminin", "parité", "lutte contre le harcèlement sexuel", "programmes de mentorat pour femmes", "initiatives pour l'équité salariale", "soutien à l'entrepreneuriat féminin", "formation sur la diversité", "sensibilisation au genre", "promotion de la diversité", "inclusion de genre"],
55
- "Eau propre et assainissement": ["eau propre", "assainissement", "accès à l'eau", "traitement de l'eau", "gestion durable de l'eau"],
56
- "Énergie propre et d'un coût abordable": ["énergie renouvelable", "énergie propre", "efficacité énergétique", "énergie solaire", "énergie éolienne", "réduction de la consommation énergétique"],
57
- "Travail décent et croissance économique": ["emploi", "droits du travail", "croissance économique", "entreprise responsable", "travail décent", "conditions de travail", "économie inclusive"],
58
- "Industrie, innovation et infrastructure": ["industrie", "innovation", "infrastructure", "technologie", "innovation industrielle", "développement durable industriel"],
59
- "Inégalités réduites": ["réduction des inégalités", "inclusion sociale", "équité", "justice sociale", "égalité des chances"],
60
- "Villes et communautés durables": ["urbanisme durable", "communautés résilientes", "transports publics", "développement urbain durable", "mobilité urbaine"],
61
- "Consommation et production responsables": ["consommation durable", "production durable", "économie circulaire", "recyclage", "gestion des déchets", "réduction de l'empreinte carbone"],
62
- "Lutte contre les changements climatiques": ["changements climatiques", "atténuation", "adaptation au climat", "réduction des émissions", "initiatives climatiques", "sobriété carbone"],
63
- "Vie aquatique": ["océans", "mers", "conservation marine", "pêche durable", "biodiversité aquatique", "protection des écosystèmes marins"],
64
- "Vie terrestre": ["biodiversité terrestre", "conservation des forêts", "reforestation", "protection des écosystèmes terrestres", "lutte contre la désertification"],
65
- "Paix, justice et institutions efficaces": ["paix", "justice", "lutte contre la corruption", "institutions solides", "état de droit", "transparence", "accessibilité de la justice"],
66
- "Partenariats pour la réalisation des objectifs": ["partenariats mondiaux", "coopération internationale", "engagement multi-acteurs", "collaboration stratégique", "synergie"]
67
- }
68
-
69
- #keywords.update(keywords_sdg)
70
-
71
- for record in data:
72
- action_rse = record.get("action_rse", "").lower()
73
- company_info = {
74
- "name": record.get("nom_courant_denomination", "N/A"),
75
- "action_rse": action_rse,
76
- "activity": record.get("libelle_section_naf", "N/A"),
77
- "city": record.get("commune", "N/A")
78
- }
79
- found_category = False
80
- for criterion, key_phrases in keywords.items():
81
- if any(key_phrase in action_rse for key_phrase in key_phrases):
82
- criteria[criterion].append(company_info)
83
- found_category = True
84
- break # Assuming each action belongs to one category only
85
-
86
- # Si l'action n'a pas été classifiée dans une catégorie existante, la placer dans "Autres"
87
- if not found_category:
88
- criteria["Autres"].append(company_info)
89
-
90
- return criteria
 
1
+ from data_manager import get_data
2
+
3
+ def classify_actions_rse_ODD(data):
4
+ data, _ = get_data()
5
+
6
+ criteria = {
7
+ "Pas de pauvreté": [],
8
+ "Faim zéro": [],
9
+ "Bonne santé et bien-être": [],
10
+ "Éducation de qualité": [],
11
+ "Égalité entre les sexes": [],
12
+ "Eau propre et assainissement": [],
13
+ "Énergie propre et d'un coût abordable": [],
14
+ "Travail décent et croissance économique": [],
15
+ "Industrie, innovation et infrastructure": [],
16
+ "Inégalités réduites": [],
17
+ "Villes et communautés durables": [],
18
+ "Consommation et production responsables": [],
19
+ "Mesures relatives à la lutte contre les changements climatiques": [],
20
+ "Vie aquatique": [],
21
+ "Vie terrestre": [],
22
+ "Paix, justice et institutions efficaces": [],
23
+ "Partenariats pour la réalisation des objectifs": [],
24
+ "Autres": []
25
+ }
26
+
27
+ keywords = {
28
+ "Pas de pauvreté": ["pauvreté", "précarité", "exclusion sociale", "accès aux ressources", "sécurité alimentaire"],
29
+ "Faim zéro": ["faim", "malnutrition", "sécurité alimentaire", "agriculture durable"],
30
+ "Bonne santé et bien-être": ["santé", "bien-être", "accès aux soins", "prévention des maladies"],
31
+ "Éducation de qualité": ["éducation", "alphabétisation", "accès à l'éducation", "formation professionnelle"],
32
+ "Égalité entre les sexes": ["égalité des genres", "droits des femmes", "participation des femmes"],
33
+ "Eau propre et assainissement": ["eau potable", "assainissement", "gestion de l'eau"],
34
+ "Énergie propre et d'un coût abordable": ["énergie propre", "énergies renouvelables", "accès à l'énergie"],
35
+ "Travail décent et croissance économique": ["travail décent", "emploi", "croissance économique"],
36
+ "Industrie, innovation et infrastructure": ["industrie", "innovation", "infrastructures"],
37
+ "Inégalités réduites": ["inégalités", "répartition des richesses", "justice sociale"],
38
+ "Villes et communautés durables": ["villes durables", "communautés durables", "urbanisation"],
39
+ "Consommation et production responsables": ["consommation responsable", "production responsable", "gestion des déchets"],
40
+ "Mesures relatives à la lutte contre les changements climatiques": ["changement climatique", "atténuation", "adaptation"],
41
+ "Vie aquatique": ["océans", "ressources marines", "conservation marine"],
42
+ "Vie terrestre": ["biodiversité", "conservation des écosystèmes", "déforestation"],
43
+ "Paix, justice et institutions efficaces": ["paix", "justice", "institutions"],
44
+ "Partenariats pour la réalisation des objectifs": ["partenariats", "coopération internationale", "engagement"],
45
+ }
46
+
47
+ for record in data:
48
+ action_rse = record.get("action_rse", "").lower()
49
+ company_info = {
50
+ "name": record.get("nom_courant_denomination", "N/A"),
51
+ "action_rse": action_rse,
52
+ "activity": record.get("libelle_section_naf", "N/A"),
53
+ "city": record.get("commune", "N/A")
54
+ }
55
+ found_category = False
56
+ for criterion, key_phrases in keywords.items():
57
+ if any(key_phrase in action_rse for key_phrase in key_phrases):
58
+ criteria[criterion].append(company_info)
59
+ found_category = True
60
+ break
61
+
62
+
63
+ if not found_category:
64
+ criteria["Autres"].append(company_info)
65
+
66
+ return criteria
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
RAG_PDF.py DELETED
@@ -1,77 +0,0 @@
1
- import streamlit as st
2
- from dotenv import load_dotenv
3
- from PyPDF2 import PdfReader
4
- from langchain.text_splitter import CharacterTextSplitter
5
- from langchain_community.embeddings import HuggingFaceInstructEmbeddings
6
- from langchain_community.vectorstores import FAISS
7
- from langchain_community.chat_models import ChatOpenAI
8
- from langchain.llms import HuggingFaceHub
9
- from langchain import hub
10
- from langchain_core.output_parsers import StrOutputParser
11
- from langchain_core.runnables import RunnablePassthrough
12
- import os
13
-
14
-
15
- def get_pdf_text(pdf_docs):
16
- text = ""
17
- for pdf in pdf_docs:
18
- pdf_reader = PdfReader(pdf)
19
- for page in pdf_reader.pages:
20
- text += page.extract_text()
21
- return text
22
-
23
- def get_text_chunks(text):
24
- text_splitter = CharacterTextSplitter(
25
- separator="\n",
26
- chunk_size=500, # the character length of the chunck
27
- chunk_overlap=100, # the character length of the overlap between chuncks
28
- length_function=len # the length function - in this case, character length (aka the python len() fn.)
29
- )
30
- chunks = text_splitter.split_text(text)
31
- return chunks
32
-
33
- def get_vectorstore(text_chunks):
34
- model_name = "hkunlp/instructor-xl"
35
- hf = HuggingFaceInstructEmbeddings(model_name=model_name)
36
- vectorstore = FAISS.from_texts(texts=text_chunks, embedding=hf)
37
- return vectorstore
38
-
39
- def get_conversation_chain(vectorstore):
40
- llm = HuggingFaceHub(repo_id="mistralai/Mistral-7B-Instruct-v0.2",model_kwargs={"Temperature": 0.5, "MaxTokens": 1024})
41
- retriever=vectorstore.as_retriever()
42
- prompt = hub.pull("rlm/rag-prompt")
43
- # Chain
44
- rag_chain = (
45
- {"context": retriever, "question": RunnablePassthrough()}
46
- | prompt
47
- | llm
48
- )
49
- response = rag_chain.invoke("A partir de documents PDF, concernant la transition écologique en France, proposer un plan de transition en fonction de la marque").split("\nAnswer:")[-1]
50
- return response
51
-
52
- def rag_pdf():
53
- load_dotenv()
54
- st.header("Utiliser l’IA pour générer un plan RSE simplifié")
55
-
56
- if "conversation" not in st.session_state:
57
- st.session_state.conversation = None
58
-
59
-
60
- with st.sidebar:
61
- st.subheader("INFOS SUR LA MARQUE")
62
- pdf_docs = st.file_uploader("Upload les documents concerant la marque et clique sur process", type="pdf",accept_multiple_files=True)
63
- if st.button("Process"):
64
- with st.spinner("Processing..."):#loading bar to enhance user experience
65
- #get pdf text in raw format
66
- raw_text = get_pdf_text(pdf_docs)
67
-
68
- #get text chunks
69
- text_chunks = get_text_chunks(raw_text)
70
-
71
- #create vectorstore
72
- vectorstore = get_vectorstore(text_chunks)
73
-
74
- #create conversation chain
75
- st.session_state.conversation = get_conversation_chain(vectorstore)
76
-
77
- st.write(st.session_state.conversation)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
RAG_PDF_WEB.py DELETED
@@ -1,234 +0,0 @@
1
- import streamlit as st
2
- from dotenv import load_dotenv
3
- from PyPDF2 import PdfReader
4
- from langchain.text_splitter import CharacterTextSplitter
5
- from langchain_community.embeddings import OpenAIEmbeddings
6
- from langchain_community.vectorstores import FAISS
7
- from langchain_community.chat_models import ChatOpenAI
8
- from langchain.llms import HuggingFaceHub
9
- from langchain import hub
10
- from langchain_core.output_parsers import StrOutputParser
11
- from langchain_core.runnables import RunnablePassthrough
12
- from langchain_community.document_loaders import WebBaseLoader
13
- import os
14
-
15
- from session import set_rag
16
- from partie_prenante_carte import complete_and_verify_url
17
-
18
- def get_docs_from_website(urls):
19
- loader = WebBaseLoader(urls, header_template={
20
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36',
21
- })
22
- docs = loader.load()
23
- return docs
24
-
25
- def get_pdf_text(pdf_docs):
26
- text = ""
27
- for pdf in pdf_docs:
28
- pdf_reader = PdfReader(pdf)
29
- for page in pdf_reader.pages:
30
- text += page.extract_text()
31
- return text
32
-
33
- def get_text_chunks(text):
34
- text_splitter = CharacterTextSplitter(
35
- separator="\n",
36
- chunk_size=1000, # the character length of the chunck
37
- chunk_overlap=200, # the character length of the overlap between chuncks
38
- length_function=len # the length function - in this case, character length (aka the python len() fn.)
39
- )
40
- chunks = text_splitter.split_text(text)
41
- return chunks
42
-
43
- def get_doc_chunks(docs):
44
- # Split the loaded data
45
- text_splitter = CharacterTextSplitter(separator='\n',
46
- chunk_size=500,
47
- chunk_overlap=40)
48
-
49
- docs = text_splitter.split_documents(docs)
50
- return docs
51
-
52
- def get_vectorstore_from_docs(doc_chunks):
53
- embedding = OpenAIEmbeddings(model="text-embedding-3-small")
54
- vectorstore = FAISS.from_documents(documents=doc_chunks, embedding=embedding)
55
- return vectorstore
56
-
57
- def get_vectorstore(text_chunks):
58
- embedding = OpenAIEmbeddings(model="text-embedding-3-small")
59
- vectorstore = FAISS.from_texts(texts=text_chunks, embedding=embedding)
60
- return vectorstore
61
-
62
- def get_conversation_chain(vectorstore):
63
- llm = ChatOpenAI(model="gpt-3.5-turbo",temperature=0.5, max_tokens=2048)
64
- retriever=vectorstore.as_retriever()
65
- prompt = hub.pull("rlm/rag-prompt")
66
-
67
- # Chain
68
- rag_chain = (
69
- {"context": retriever , "question": RunnablePassthrough()}
70
- | prompt
71
- | llm
72
- )
73
- return rag_chain
74
-
75
- def verify_and_complete_urls(urls):
76
- for i in range(len(urls)):
77
- is_valid, urls[i] = complete_and_verify_url(urls[i])
78
- return urls
79
-
80
- def rag_pdf_web():
81
-
82
- load_dotenv()
83
- st.header("INDIQUEZ VOS PAGES WEB ET/OU DOCUMENTS D’ENTREPRISE POUR AUDITER LE CONTENU RSE")
84
-
85
- option = st.radio("Source", ("A partir de votre site web", "A partir de vos documents entreprise"))
86
-
87
- vectorstore = None
88
- chain = None
89
-
90
- if option == "A partir de votre site web":
91
- url1 = st.text_input("URL 1")
92
- url2 = st.text_input("URL 2")
93
- url3 = st.text_input("URL 3")
94
- # Process the URLs
95
- sous_options = st.radio("Choisissez votre sous-section", ("Ambition, Vision, Missions, Valeurs", "3 piliers de la démarche RSE"))
96
- try:
97
- if st.button("Process"):
98
- with st.spinner("Processing..."):
99
- #get text from the website
100
- urls = [url1, url2, url3]
101
- filtered_urls = [url for url in urls if url]
102
-
103
- #verify and complete urls
104
- filtered_urls = verify_and_complete_urls(filtered_urls)
105
-
106
- #get text from the website
107
- docs = get_docs_from_website(filtered_urls)
108
-
109
- #get text chunks
110
- text_chunks = get_doc_chunks(docs)
111
-
112
- #create vectorstore
113
- vectorstore = get_vectorstore_from_docs(text_chunks)
114
-
115
- chain = get_conversation_chain(vectorstore)
116
-
117
- if sous_options == "Ambition, Vision, Missions, Valeurs":
118
- # question = '''voici les 4 points à génerer absolument, pas de reponse comme je ne sais pas; et n'oublie aucun des points , chaque paragraphe doit être de minimum 150 caractères:
119
- # \n
120
- # ### Ambition : \n
121
- # Quelle est l'ambition de l'entreprise ? (répondre avec maximum 250 caractères)
122
- # \n
123
- # ### Vision : \n
124
- # Quelle est la vision de l'entreprise ? (répondre avec maximum 250 caractères)
125
- # \n
126
- # ### Missions : \n
127
- # Quelles sont les missions de l'entreprise ? (répondre avec maximum 250 caractères)
128
- # \n
129
- # renvoie ta réponse en markdown et bien formatée'''
130
- # response = chain.invoke(question)
131
- # st.markdown(response.content)
132
-
133
- #ambition
134
- ambition = chain.invoke("Quelle est l'ambition de l'entreprise ? (répondre avec maximum 250 caractères)")
135
- st.markdown("### Ambition :")
136
- st.markdown(ambition.content)
137
-
138
- #vision
139
- ambition = chain.invoke(" Quelle est la vision de l'entreprise ? (répondre avec maximum 250 caractères)")
140
- st.markdown("### Vision :")
141
- st.markdown(ambition.content)
142
-
143
- #Mission
144
- ambition = chain.invoke(" Quelle est la vision de l'entreprise ? (répondre avec maximum 250 caractères)")
145
- st.markdown("### Mission :")
146
- st.markdown(ambition.content)
147
-
148
- #values
149
- values = chain.invoke("Quels sont les valeurs de l'entreprise ? (répondre avec 10 mots maximum en bullet points)")
150
- st.markdown("### Valeurs :")
151
- st.markdown(values.content)
152
-
153
- elif sous_options == "3 piliers de la démarche RSE":
154
- question = ''' suggère nous les 3 piliers principaux de la démarche RSE pour cette entreprise. N'oublie aucun pilier RSE , ca doit avoir ce format :
155
- \n
156
- ### le titre du pilier numero 1 \n
157
- -la description du pilier (répondre avec maximum 250 caractères)
158
- \n
159
- - 2 indicateurs cibles pertinents à atteindre avec suggestion de valeur cible min, max
160
- \n
161
- ### le titre du pilier numero 2 \n
162
- -la description du pilier (répondre avec maximum 250 caractères)
163
- \n
164
- - 2 indicateurs cibles pertinents à atteindre avec suggestion de valeur cible min, max
165
- \n
166
- ### le titre du pilier numero 3 \n
167
- -la description du pilier (répondre avec maximum 250 caractères)
168
- \n
169
- - 2 indicateurs cibles pertinents à atteindre avec suggestion de valeur cible min, max
170
- \n
171
- renvoie ta réponse en markdown et bien formatée
172
- '''
173
- response = chain.invoke(question)
174
- st.markdown(response.content)
175
- except Exception as e:
176
- st.error(f"Une erreur s'est produite : Url non valide ou problème de connexion à internet. Veuillez réessayer.")
177
-
178
- if option == "A partir de vos documents entreprise":
179
- pdf_docs = st.file_uploader("Upload les documents concernant la marque (maximum 3 fichiers de taille max de 5 Mo)", type="pdf", accept_multiple_files=True)
180
- # Process the PDF documents
181
- sous_options = st.radio("Choisissez votre sous-section", ("Ambition, Vision, Missions, Valeurs", "3 piliers de la démarche RSE"))
182
- try:
183
- if st.button("Process"):
184
- with st.spinner("Processing..."):
185
- #get pdf text in raw format
186
- raw_text = get_pdf_text(pdf_docs)
187
-
188
- #get text chunks
189
- text_chunks = get_text_chunks(raw_text)
190
-
191
- #create vectorstore
192
- vectorstore = get_vectorstore(text_chunks)
193
-
194
- chain = get_conversation_chain(vectorstore)
195
-
196
- if sous_options == "Ambition, Vision, Missions, Valeurs":
197
-
198
- #ambition
199
- ambition = chain.invoke("Quelle est l'ambition de l'entreprise ? (répondre avec maximum 250 caractères)")
200
- st.markdown("### Ambition :")
201
- st.markdown(ambition.content)
202
-
203
- #vision
204
- ambition = chain.invoke(" Quelle est la vision de l'entreprise ? (répondre avec maximum 250 caractères)")
205
- st.markdown("### Vision :")
206
- st.markdown(ambition.content)
207
-
208
- #Mission
209
- ambition = chain.invoke(" Quelle est la vision de l'entreprise ? (répondre avec maximum 250 caractères)")
210
- st.markdown("### Mission :")
211
- st.markdown(ambition.content)
212
-
213
- #values
214
- values = chain.invoke("Quels sont les valeurs de l'entreprise ? (répondre avec 10 mots maximum en bullet points)")
215
- st.markdown("### Valeurs :")
216
- st.markdown(values.content)
217
-
218
- elif sous_options == "3 piliers de la démarche RSE":
219
- question = ''' suggère nous les 3 piliers principaux de la démarche RSE pour cette entreprise. Pour chaque pilier RSE doit avoir ce format :
220
- \n
221
- ### le titre du ieme pilier \n
222
- -la description du pilier (répondre avec maximum 250 caractères)
223
- \n
224
- - 2 indicateurs cibles pertinents à atteindre avec suggestion de valeur cible min, max
225
- \n
226
- renvoie ta réponse en markdown et bien formatée
227
- '''
228
- response = chain.invoke(question)
229
- st.markdown(response.content)
230
- except Exception as e:
231
- st.error(f"Une erreur s'est produite : Pdf non valide ou problème de connexion à internet. Veuillez réessayer.")
232
-
233
- if vectorstore and chain:
234
- set_rag(vectorstore, chain)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,12 +1,13 @@
1
  ---
2
- title: Opendata Rse
3
- emoji: 📊
4
- colorFrom: yellow
5
- colorTo: gray
6
  sdk: streamlit
7
- sdk_version: 1.37.1
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: OpenData Projet RSE
3
+ emoji: 📈
4
+ colorFrom: red
5
+ colorTo: yellow
6
  sdk: streamlit
7
+ sdk_version: 1.32.2
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py CHANGED
@@ -8,9 +8,6 @@ from statistiques import main as display_statistics
8
  from ActionsRSE import display_actions_rse
9
  from AnalyseActionsRSE import display_analyse_actions_rse
10
  from partiesprenantes import display_materiality_partiesprenantes
11
- from partie_prenante_carte import display_pp
12
- from codecarbon import EmissionsTracker
13
-
14
 
15
  # Import modifiédes fonctions liées aux scripts
16
  from projetRSE import display_rse_projects
@@ -19,44 +16,14 @@ from entreprises_labellisees import display_labelled_companies
19
  from inspirezvous import *
20
  from collaborons import display_company_selection_for_materiality,display_materiality_matrix
21
  from documentations import display_documentation
22
- from RAG_PDF_WEB import rag_pdf_web
23
- from prompt import get_prompts_list,prompt_execution,execute_prompt
24
- from chat_with_pps import display_chat
25
- from high_chart import test_chart
26
- from chat_te import display_chat_te
27
- from empreinte_carbone import *
28
- from comparateur import *
29
- import base64
30
- import pandas as pd
31
-
32
-
33
- # Function to read and encode an SVG file to Base64
34
- def load_svg_as_base64(file_path):
35
- with open(file_path, "rb") as f:
36
- svg_data = f.read()
37
- return base64.b64encode(svg_data).decode()
38
 
39
  def main():
40
-
41
- if "emission" not in st.session_state:
42
- tracker = EmissionsTracker()
43
- tracker.start()
44
- st.session_state["emission"] = tracker
45
-
46
- if "partial_emissions" not in st.session_state:
47
- st.session_state["partial_emissions"] = {
48
- "Scrapping": {"cc": 0, "el": 0}, #cc for codecarbon , el for ecologits
49
- "extraction_pp": {"cc": 0, "el": 0},
50
- "cartographie": {"cc": 0, "el": 0},
51
- "chatbot": {"cc": 0, "el": 0},
52
- "download_rapport": {"cc": 0, "el": 0},
53
- }
54
 
55
  st.sidebar.title("OPEN DATA & IA au service de la RSE")
56
  section_principale = st.sidebar.radio(
57
  "Choisissez votre section",
58
- ["Data Bordeaux métropole", "Data bziiit","IA RSE","Documentation"],
59
- index=2
60
  )
61
 
62
  if section_principale == "Data Bordeaux métropole":
@@ -93,62 +60,30 @@ def main():
93
  elif section_principale == "IA RSE":
94
  ia_mode = st.sidebar.radio(
95
  "Choisissez votre sous-section",
96
- [
97
- "Audit flash RSE de vos contenus",
98
- "Parties prenantes",
99
- "Chatbot Parties Prenantes",
100
- "Empreinte carbone détaillée",
101
- # "Prompts RSE - TE",
102
- # "Chatbot RSE - TE",
103
- ],
104
- index=1
105
  )
106
-
107
  if ia_mode == "Parties prenantes":
108
- # data, bziiit_data = fetch_data()
109
- # selected_company = display_company_selection_for_materiality(data)
110
- # if selected_company:
111
- # display_materiality_partiesprenantes(selected_company, data, bziiit_data)
112
- display_pp()
113
-
114
- # elif ia_mode == "Matrice de matérialité":
115
- # data, bziiit_data = fetch_data()
116
- # selected_company = display_company_selection_for_materiality(data)
117
- # if selected_company:
118
- # display_materiality_matrix(selected_company, data, bziiit_data)
119
-
120
- elif ia_mode == "Chatbot Parties Prenantes":
121
- display_chat()
122
-
123
- elif ia_mode == "Audit flash RSE de vos contenus":
124
- rag_pdf_web()
125
-
126
- elif ia_mode == "Prompts RSE - TE":
127
- get_prompts_list()
128
-
129
- # elif ia_mode == "Générations de contenus RSE":
130
- # st.header("Exécutez un prompt")
131
 
132
- # selected_prompt = prompt_execution()
133
- # if selected_prompt:
134
- # execute_prompt(selected_prompt)
135
- elif ia_mode == "Chatbot RSE - TE":
136
- display_chat_te()
137
-
138
- elif ia_mode == "Empreinte carbone détaillée":
139
- display_carbon_footprint()
140
 
141
  elif section_principale == "Documentation":
142
  display_documentation()
143
 
144
- #carbon foot_print
145
- st.sidebar.markdown("---")
146
- display_cf_comparison(st.sidebar)
147
  # Instructions communes à toutes les sections
148
  st.sidebar.markdown("---")
149
  st.sidebar.markdown("Powered by **bziiit IA RSE**")
150
  st.sidebar.markdown("2024 : Open source en Licence MIT")
151
  st.sidebar.markdown("[email protected]")
152
-
 
153
  if __name__ == "__main__":
154
  main()
 
8
  from ActionsRSE import display_actions_rse
9
  from AnalyseActionsRSE import display_analyse_actions_rse
10
  from partiesprenantes import display_materiality_partiesprenantes
 
 
 
11
 
12
  # Import modifiédes fonctions liées aux scripts
13
  from projetRSE import display_rse_projects
 
16
  from inspirezvous import *
17
  from collaborons import display_company_selection_for_materiality,display_materiality_matrix
18
  from documentations import display_documentation
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
  def main():
21
+ st.markdown(":point_left: Cliquez pour vous inspirer", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  st.sidebar.title("OPEN DATA & IA au service de la RSE")
24
  section_principale = st.sidebar.radio(
25
  "Choisissez votre section",
26
+ ["Data Bordeaux métropole", "Data bziiit","IA RSE","Documentation"]
 
27
  )
28
 
29
  if section_principale == "Data Bordeaux métropole":
 
60
  elif section_principale == "IA RSE":
61
  ia_mode = st.sidebar.radio(
62
  "Choisissez votre sous-section",
63
+ ["Parties prenantes", "Matrice de matérialité"]
 
 
 
 
 
 
 
 
64
  )
 
65
  if ia_mode == "Parties prenantes":
66
+ data, bziiit_data = fetch_data()
67
+ selected_company = display_company_selection_for_materiality(data)
68
+ if selected_company:
69
+ display_materiality_partiesprenantes(selected_company, data, bziiit_data)
70
+ elif ia_mode == "Matrice de matérialité":
71
+ data, bziiit_data = fetch_data()
72
+ selected_company = display_company_selection_for_materiality(data)
73
+ if selected_company:
74
+ display_materiality_matrix(selected_company, data, bziiit_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
 
 
 
 
 
 
 
 
76
 
77
  elif section_principale == "Documentation":
78
  display_documentation()
79
 
80
+
 
 
81
  # Instructions communes à toutes les sections
82
  st.sidebar.markdown("---")
83
  st.sidebar.markdown("Powered by **bziiit IA RSE**")
84
  st.sidebar.markdown("2024 : Open source en Licence MIT")
85
  st.sidebar.markdown("[email protected]")
86
+ st.sidebar.markdown("---")
87
+
88
  if __name__ == "__main__":
89
  main()
chart.png DELETED
Binary file (22.5 kB)
 
chat_te.py DELETED
@@ -1,127 +0,0 @@
1
- import streamlit as st
2
- from langchain_core.messages import AIMessage, HumanMessage
3
- from langchain_community.chat_models import ChatOpenAI
4
- from dotenv import load_dotenv
5
- from langchain_core.output_parsers import StrOutputParser
6
- from langchain_core.prompts import ChatPromptTemplate
7
- from download_chart import construct_plot
8
- from langchain_core.runnables import RunnablePassthrough
9
- from langchain import hub
10
- from langchain_core.prompts.prompt import PromptTemplate
11
- from langchain_community.vectorstores import FAISS
12
- from langchain_community.embeddings import OpenAIEmbeddings
13
- from langchain_community.document_loaders import PyPDFLoader
14
- from langchain_experimental.text_splitter import SemanticChunker
15
- load_dotenv()
16
-
17
- def get_docs_from_pdf(file):
18
- loader = PyPDFLoader(file)
19
- docs = loader.load_and_split()
20
- return docs
21
-
22
- def get_doc_chunks(docs):
23
- text_splitter = SemanticChunker(OpenAIEmbeddings(model="text-embedding-3-small"))
24
- chunks = text_splitter.split_documents(docs)
25
- return chunks
26
-
27
- def get_vectorstore_from_docs(doc_chunks):
28
- embedding = OpenAIEmbeddings(model="text-embedding-3-small")
29
- vectorstore = FAISS.from_documents(documents=doc_chunks, embedding=embedding)
30
- return vectorstore
31
-
32
- def get_conversation_chain(vectorstore):
33
- llm = ChatOpenAI(model="gpt-4o",temperature=0.5, max_tokens=2048)
34
- retriever=vectorstore.as_retriever()
35
-
36
- prompt = hub.pull("rlm/rag-prompt")
37
- # Chain
38
- rag_chain = (
39
- {"context": retriever , "question": RunnablePassthrough()}
40
- | prompt
41
- | llm
42
- | StrOutputParser()
43
- )
44
- return rag_chain
45
-
46
- def create_db(file):
47
- # docs = get_docs_from_pdf(file)
48
- # doc_chunks = get_doc_chunks(docs)
49
- # vectorstore = get_vectorstore_from_docs(doc_chunks)
50
- vectorstore = FAISS.load_local(file, OpenAIEmbeddings(model="text-embedding-3-small"),allow_dangerous_deserialization= True)
51
- return vectorstore
52
-
53
- def get_response(chain,user_query, chat_history):
54
-
55
- template = """
56
- Chat history: {chat_history}
57
- User question: {user_question}
58
- """
59
-
60
-
61
- question = ChatPromptTemplate.from_template(template)
62
- question = question.format(chat_history=chat_history, user_question=user_query)
63
-
64
- return chain.stream(question)
65
-
66
-
67
- @st.dialog("Cast your vote")
68
- def vote(item):
69
- st.write(f"Why is {item} your favorite?")
70
- reason = st.text_input("Because...")
71
- if st.button("Submit"):
72
- st.rerun()
73
-
74
- def display_chat_te():
75
- # app config
76
- st.title("Chatbot")
77
-
78
- # session state
79
- if "chat_history_te" not in st.session_state:
80
- st.session_state.chat_history_te = [
81
- AIMessage(content="Salut, posez-moi vos question sur la transistion ecologique."),
82
- ]
83
- if "chain" not in st.session_state:
84
- db=create_db("./DATA_bziiit/vectorstore_op")
85
- chain = get_conversation_chain(db)
86
- st.session_state.chain = chain
87
-
88
- # conversation
89
- for message in st.session_state.chat_history_te:
90
- if isinstance(message, AIMessage):
91
- with st.chat_message("AI"):
92
- st.write(message.content)
93
- elif isinstance(message, HumanMessage):
94
- with st.chat_message("Moi"):
95
- st.write(message.content)
96
-
97
- style = """
98
- <style>
99
- .css-ocqkz7 {
100
- position: fixed;
101
- bottom: 0;
102
- width: 50%;
103
- justify-content: center;
104
- align-items: end;
105
- margin-bottom: 0.5rem;
106
- }
107
- </style>
108
- """
109
- # Inject the styling code for both elements
110
- # st.markdown(style, unsafe_allow_html=True)
111
-
112
- # # user input
113
- # col1 , col2 = st.columns([1,8])
114
- # if col1.button("chatbot"):
115
- # vote("chatbot")
116
- # with col2:
117
- user_query = st.chat_input(placeholder="c'est quoi la transition écologique ?")
118
- if user_query is not None and user_query != "":
119
- st.session_state.chat_history_te.append(HumanMessage(content=user_query))
120
-
121
- with st.chat_message("Moi"):
122
- st.markdown(user_query)
123
-
124
- with st.chat_message("AI"):
125
- response = st.write_stream(get_response(st.session_state.chain,user_query, st.session_state.chat_history_te))
126
-
127
- st.session_state.chat_history_te.append(AIMessage(content=response))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
chat_with_pps.py DELETED
@@ -1,437 +0,0 @@
1
- import streamlit as st
2
- from langchain_core.messages import AIMessage, HumanMessage
3
- from langchain_community.chat_models import ChatOpenAI
4
- from dotenv import load_dotenv
5
- from langchain_core.output_parsers import StrOutputParser
6
- from langchain_core.prompts import ChatPromptTemplate
7
- from langchain_mistralai.chat_models import ChatMistralAI
8
- from download_chart import construct_plot
9
- from prompt import get_prompts_list
10
- from high_chart import test_chart
11
- from export_doc import export_conversation,convert_pp_to_csv,get_conversation
12
- import random
13
- import pandas as pd
14
- from codecarbon import EmissionsTracker
15
-
16
- from ecologits.tracers.utils import compute_llm_impacts
17
- import time
18
- import itertools
19
-
20
-
21
-
22
- load_dotenv()
23
- def generate_random_color():
24
- # Generate random RGB values
25
- r = random.randint(0, 255)
26
- g = random.randint(0, 255)
27
- b = random.randint(0, 255)
28
-
29
- # Convert RGB to hexadecimal
30
- color_hex = '#{:02x}{:02x}{:02x}'.format(r, g, b)
31
-
32
- return color_hex
33
-
34
- def format_pp_add_viz(pp):
35
- y = 50
36
- x = 50
37
- for i in range(len(st.session_state['pp_grouped'])):
38
- if st.session_state['pp_grouped'][i]['y'] == y and st.session_state['pp_grouped'][i]['x'] == x:
39
- y += 5
40
- if y > 95:
41
- y = 50
42
- x += 5
43
- if st.session_state['pp_grouped'][i]['name'] == pp:
44
- return None
45
- else:
46
- st.session_state['pp_grouped'].append({'name':pp, 'x':x,'y':y, 'color':generate_random_color()})
47
-
48
-
49
- def format_context(partie_prenante_grouped,marque):
50
- context = "la marque est " + marque + ".\n"
51
- context += f"Le nombre de parties prenantes est {len(partie_prenante_grouped)} et ils sont les suivantes:\n"
52
- for i,partie_prenante in enumerate(partie_prenante_grouped):
53
- context += f"{i}.{partie_prenante['name']} est une partie prenante de {marque} et a un pouvoir de {partie_prenante['y']}% et une influence de {partie_prenante['x']}%.\n"
54
-
55
- segmentation = '''
56
- Les parties prenantes sont segmentées en 4 catégories:
57
- - Rendre satisfait: le pouvoir est entre 50 et 100 et l'influence est entre 0 et 50
58
- - Gérer étroitement: le pouvoir est entre 50 et 100 et l'influence est entre 50 et 100
59
- - Suivre de près: le pouvoir est entre 0 et 50 et l'influence est entre 0 et 50
60
- - Tenir informé: le pouvoir est entre 0 et 50 et l'influence est entre 50 et 100
61
- '''
62
-
63
- context += segmentation
64
- return context
65
-
66
-
67
- def get_response(user_query, chat_history, context,llm=None,history_limit=5,stream=True):
68
-
69
- template = """
70
- Fournir des réponses, en francais, précises et contextuelles en agissant comme un expert en affaires, en utilisant le contexte des parties prenantes et leur pouvoir en pourcentage et leur influence en pourcentage pour expliquer les implications pour la marque. Le modèle doit connecter les informations du contexte et de l'historique de la conversation pour donner une réponse éclairée à la dernière question posée.
71
-
72
- Contexte: {context}
73
-
74
- Chat history: {chat_history}
75
-
76
- User question: {user_question}
77
- """
78
-
79
- prompt = ChatPromptTemplate.from_template(template)
80
-
81
- #llm = ChatOpenAI(model="gpt-4o")
82
- if not llm:
83
- llm = ChatOpenAI(model="gpt-4o")
84
- elif llm == "GPT-4o":
85
- llm = ChatOpenAI(model="gpt-4o")
86
- elif llm == "Mistral (FR)":
87
- llm = ChatMistralAI(model_name="mistral-large-latest")
88
-
89
- chain = prompt | llm
90
-
91
- if not stream:
92
- return chain.invoke({
93
- "context": context,
94
- "chat_history": chat_history[-history_limit:],
95
- "user_question": user_query,
96
- })
97
-
98
- chain = chain | StrOutputParser()
99
-
100
- if history_limit:
101
- return chain.stream({
102
- "context": context,
103
- "chat_history": chat_history[-history_limit:],
104
- "user_question": user_query,
105
- })
106
-
107
- return chain.stream({
108
- "context": context,
109
- "chat_history": chat_history,
110
- "user_question": user_query,
111
- })
112
-
113
-
114
- def get_response_with_impact(user_query, chat_history, context,llm=None,history_limit=5,stream=True):
115
- model_vs_provider = {
116
- "Mistral (FR)": ["mistral-large-latest","mistralai"],
117
- "GPT-4o": ["gpt-4o","openai"]
118
- }
119
-
120
- if not stream:
121
-
122
- start = time.perf_counter()
123
- response = get_response(user_query, chat_history, context,llm,history_limit,stream)
124
- request_latency = time.perf_counter() - start
125
- token_count = response.response_metadata["token_usage"]["completion_tokens"]
126
-
127
- nbre_out_tokens = token_count
128
- model_name = model_vs_provider[st.session_state.model][0]
129
- model_provider = model_vs_provider[st.session_state.model][1]
130
-
131
- impact = compute_llm_impacts(
132
- provider=model_provider,
133
- model_name=model_name,
134
- output_token_count=nbre_out_tokens,
135
- request_latency=request_latency,
136
- )
137
-
138
- print(f"Request latency: {request_latency:.3f} s")
139
- print(f"Output token count: {nbre_out_tokens}")
140
- print(f"Impact: {impact.gwp.value} {impact.gwp.unit}")
141
-
142
- st.session_state["partial_emissions"]["chatbot"]["el"] += impact.gwp.value
143
-
144
- return response.content
145
-
146
- else:
147
- start = time.perf_counter()
148
- response_generator = get_response(user_query, chat_history, context,llm,history_limit,stream)
149
- wrapped_response_generator, token_count_generator = itertools.tee(response_generator)
150
- token_count = 0
151
-
152
- final_response = st.write_stream(wrapped_response_generator)
153
-
154
- request_latency = time.perf_counter() - start
155
-
156
- for _ in token_count_generator:
157
- token_count += 1
158
-
159
- nbre_out_tokens = token_count
160
- model_name = model_vs_provider[st.session_state.model][0]
161
- model_provider = model_vs_provider[st.session_state.model][1]
162
-
163
-
164
- impact = compute_llm_impacts(
165
- provider=model_provider,
166
- model_name=model_name,
167
- output_token_count=nbre_out_tokens,
168
- request_latency=request_latency,
169
- )
170
-
171
- print(f"Request latency: {request_latency:.3f} s")
172
- print(f"Output token count: {nbre_out_tokens}")
173
- print(f"Impact: {impact.gwp.value} {impact.gwp.unit}")
174
-
175
- st.session_state["partial_emissions"]["chatbot"]["el"] += impact.gwp.value
176
-
177
- return final_response
178
-
179
-
180
- def display_chart():
181
- if "pp_grouped" not in st.session_state or st.session_state['pp_grouped'] is None or len(st.session_state['pp_grouped']) == 0:
182
- st.warning("Aucune partie prenante n'a été définie")
183
- return None
184
- plot = construct_plot()
185
- st.plotly_chart(plot)
186
-
187
- @st.dialog("Choisissez un prompt",width="large")
188
- def show_prompts():
189
- get_prompts_list()
190
- if st.button("Fermer"):
191
- st.rerun()
192
-
193
- @st.dialog("Choisissez votre IA",width="small")
194
- def choose_model(index):
195
-
196
- model = st.radio("Choisissez votre IA", ["(US) ChatGpt 4.o","(FR) Mistral AI - Large (open source)"],index=index)
197
- if model == "(FR) Mistral AI - Large (open source)":
198
- st.session_state.model = "Mistral (FR)"
199
- if model == "(US) ChatGpt 4.o":
200
- st.session_state.model = "GPT-4o"
201
- if st.button("Valider"):
202
- st.rerun()
203
-
204
- @st.dialog("Ma cartographie",width="large")
205
- def disp_carto_in_chat():
206
- if test_chart() == "saved":
207
- st.rerun()
208
-
209
- @st.dialog("Télécharger",width="small")
210
- def dowmload_history(used_models=None):
211
- brand_name = st.session_state['Nom de la marque']
212
-
213
- format = st.radio("Choisissez le document à télécharger",[f"Rapport des parties prenantes (PDF)",f"Tableau des parties prenantes (CSV)",f"Historique de conversation (Fichier Texte)"],index=None)
214
- if format == f"Rapport des parties prenantes (PDF)":
215
- with st.spinner("Generation en cours..."):
216
- summary = get_response("Donne moi un RESUME de la Conversation", st.session_state.chat_history,format_context(st.session_state['pp_grouped'],st.session_state['Nom de la marque']),st.session_state.model)
217
- summary = ''.join(summary)
218
- pdf = export_conversation(AIMessage(content=summary).content,used_models=used_models)
219
-
220
- st.session_state["partial_emissions"]["download_rapport"]["cc"] = st.session_state["emission"].stop()
221
-
222
- if pdf:
223
- st.download_button("Télécharger le PDF", data=pdf, file_name=f"Cartographie {brand_name}.pdf", mime="application/pdf")
224
-
225
- if format == f"Tableau des parties prenantes (CSV)":
226
- csv = convert_pp_to_csv(st.session_state['pp_grouped'])
227
- if csv:
228
- st.download_button("Télécharger le CSV", data=csv, file_name=f"parties_prenantes -{brand_name}-.csv", mime="application/vnd.ms-excel")
229
-
230
- if format == f"Historique de conversation (Fichier Texte)":
231
- conv = get_conversation()
232
- if not conv:
233
- st.error("Une erreur s'est produite lors de la récupération de l'historique de conversation")
234
- return None
235
- else:
236
- conversation = "\n".join([f"{entry['speaker']}:\n{entry['text']}\n" for entry in conv])
237
- st.download_button("Télécharger l'historique de conversation", data=conversation, file_name=f"conversation {brand_name}.txt", mime="text/plain")
238
-
239
- if st.button("Fermer"):
240
- st.rerun()
241
-
242
- def add_existing_pps(pp,pouvoir,influence):
243
- for i in range(len(st.session_state['pp_grouped'])):
244
- if st.session_state['pp_grouped'][i]['name'] == pp:
245
- st.session_state['pp_grouped'][i]['x'] = influence
246
- st.session_state['pp_grouped'][i]['y'] = pouvoir
247
- return None
248
- st.session_state['pp_grouped'].append({'name':pp, 'x':influence,'y':pouvoir, 'color':generate_random_color()})
249
-
250
-
251
- def load_csv(file):
252
- df = pd.read_csv(file)
253
- for index, row in df.iterrows():
254
- add_existing_pps(row['parties prenantes'],row['pouvoir'],row['influence'])
255
-
256
-
257
- @st.dialog("Importer",width="small")
258
- def import_conversation():
259
- uploaded_file = st.file_uploader("Télécharger le fichier CSV", type="csv")
260
- if uploaded_file is not None:
261
- file_name = uploaded_file.name
262
- try:
263
- load_csv(file_name)
264
- brand_name_from_csv = file_name.split("-")[1]
265
- st.session_state["Nom de la marque"] = brand_name_from_csv
266
- st.rerun()
267
- except Exception as e:
268
- st.error("Erreur lors de la lecture du fichier")
269
-
270
- def extract_format_prompts_from_response(response):
271
- st.markdown("---")
272
- st.markdown("**En découvrir plus avec l'IA RSE bziiit**")
273
- prompts = response.split("\n")
274
- prompts = [prompt.strip() for prompt in prompts if prompt.strip() != ""]
275
- prompts_container = st.container()
276
- with prompts_container:
277
- for i,prompt in enumerate(prompts):
278
-
279
- temp_p = f"{prompt} ➡️"
280
- st.button(temp_p,key=f"exec_{i}",on_click=lambda i=i: st.session_state.chat_history.append(HumanMessage(content=prompts[i])))
281
-
282
-
283
-
284
- def extract_pp_from_query(query):
285
- return " ".join(query.split(" ")[1:])
286
-
287
- def display_prompts(prompts):
288
- for i,prompt in enumerate(prompts):
289
- col1,col2 = st.columns([9,1])
290
- col1.markdown(f"{prompt}")
291
- col2.button("➡️",key=f"execf_{i}",on_click=lambda i=i: st.session_state.chat_history.append(HumanMessage(content=prompts[i])))
292
-
293
-
294
- def display_chat():
295
-
296
- if "emission" not in st.session_state:
297
- tracker = EmissionsTracker()
298
- tracker.start()
299
- st.session_state["emission"] = tracker
300
- # app config
301
- st.title("CHATBOT")
302
- models_name = {
303
- "Mistral (FR)":1,
304
- "GPT-4o":0
305
- }
306
-
307
-
308
-
309
- generated_prompt_question = '''En fonction de l'historique, proposez trois prompts pour continuer la conversation. Utilisez les informations fournies et les implications discutées:
310
-
311
- - Prompt 1 : [Premier prompt suggéré]
312
- - Prompt 2 : [Deuxième prompt suggéré]
313
- - Prompt 3 : [Troisième prompt suggéré]
314
-
315
- LA LISTE DOIT ETRE EN FRANCAIS CHAQUE LIGNE SANS LE NUMERO DE PROMPT SEULEMENT LE TEXTE DE LA QUESTION
316
- '''
317
-
318
- # session state
319
- if "chat_history" not in st.session_state:
320
- st.session_state.chat_history = [
321
- AIMessage(content="Salut, voici votre cartographie des parties prenantes. Que puis-je faire pour vous ?"),
322
- ]
323
-
324
- if "model" not in st.session_state:
325
- st.session_state.model = "GPT-4o"
326
-
327
- if "used_models" not in st.session_state:
328
- st.session_state.used_models = []
329
-
330
- #sticky bar at the top
331
- header = st.container()
332
- col1,col2,col3, col4,col5,col6 = header.columns([2,3,2,3,2,1])
333
-
334
- if col1.button("Prompts"):
335
- show_prompts()
336
- if col2.button(f"Modèle: {st.session_state.model}"):
337
- index = models_name[st.session_state.model]
338
- choose_model(index)
339
- if col3.button("Ma Carto"):
340
- disp_carto_in_chat()
341
- if col4.button("Télécharger"):
342
- dowmload_history(st.session_state.used_models)
343
- header.write("""<div class='fixed-header'/>""", unsafe_allow_html=True)
344
-
345
- # Custom CSS for the sticky header
346
- st.markdown(
347
- """
348
- <style>
349
- div[data-testid="stVerticalBlock"] div:has(div.fixed-header) {
350
- position: sticky;
351
- top: 2.875rem;
352
- background-color: white;
353
- z-index: 999;
354
- }
355
- .fixed-header {
356
- border-bottom: 0px solid black;
357
- }
358
- </style>
359
- """,
360
- unsafe_allow_html=True
361
- )
362
-
363
- # conversation
364
- for message in st.session_state.chat_history:
365
- if isinstance(message, AIMessage):
366
- with st.chat_message("AI"):
367
- st.write(message.content)
368
- if "cartographie" in message.content:
369
- st.markdown("\n")
370
- display_chart()
371
- if message.content == st.session_state.chat_history[0].content:
372
- st.markdown("---")
373
- st.markdown("**En découvrir plus avec l'IA RSE bziiit**")
374
- first_prompts = ["En plus des parties prenantes déjà identifiées que tu peux consulter, quels groupes ou individus, impactés par les activités de mon organisation, devrais-je ajouter dans notre cartographie des parties prenantes ?",
375
- "Quels sont les principaux acteurs internes et externes qui influencent ou sont influencés par mon organisation, et comment leurs intérêts ou préoccupations peuvent varier selon les différents domaines d'activité ?",
376
- "En tenant compte de ma chaîne de valeur complète, quels sont les différentes parties prenantes stratégiques, incluant les partenaires commerciaux, les régulateurs, les groupes de pression, et la communauté, et comment leurs rôles et influences interagissent pour affecter les objectifs à court et long terme de mon organisation ?"]
377
- display_prompts(first_prompts)
378
- elif isinstance(message, HumanMessage):
379
- with st.chat_message("Moi"):
380
- st.write(message.content)
381
-
382
- #check if the last message is from the user , that means execute button has been clicked in the prompts
383
- last_message = st.session_state.chat_history[-1]
384
- if isinstance(last_message, HumanMessage):
385
- with st.chat_message("AI"):
386
- if last_message.content.startswith("/rajoute"):
387
- response = "Partie prenante ajoutée"
388
- st.write(response)
389
- st.session_state.chat_history.append(AIMessage(content=response))
390
- else:
391
- st.markdown(f"**{st.session_state.model}**")
392
- if st.session_state.model not in st.session_state.used_models:
393
- st.session_state.used_models.append(st.session_state.model)
394
- response = get_response_with_impact(last_message.content, st.session_state.chat_history,format_context(st.session_state['pp_grouped'],st.session_state['Nom de la marque']),st.session_state.model)
395
- st.session_state.chat_history.append(AIMessage(content=response))
396
- with st.spinner("Proposition de prompts..."):
397
- propositions_prompts = get_response_with_impact(generated_prompt_question, st.session_state.chat_history,format_context(st.session_state['pp_grouped'],st.session_state['Nom de la marque']),st.session_state.model,history_limit=1,stream=False)
398
- extract_format_prompts_from_response(propositions_prompts)
399
- st.session_state["partial_emissions"]["chatbot"]["cc"] = st.session_state["emission"].stop()
400
-
401
-
402
-
403
- if "pp_grouped" not in st.session_state or st.session_state['pp_grouped'] is None or len(st.session_state['pp_grouped']) == 0:
404
- st.session_state['pp_grouped'] = []
405
- if "Nom de la marque" not in st.session_state:
406
- st.session_state["Nom de la marque"] = ""
407
-
408
- # user input
409
- user_query = st.chat_input("Par ici...")
410
- if user_query is not None and user_query != "":
411
- st.session_state.chat_history.append(HumanMessage(content=user_query))
412
-
413
- with st.chat_message("Moi"):
414
- st.markdown(user_query)
415
- with st.chat_message("AI"):
416
- st.markdown(f"**{st.session_state.model}**")
417
- if st.session_state.model not in st.session_state.used_models:
418
- st.session_state.used_models.append(st.session_state.model)
419
- if user_query.startswith("/rajoute"):
420
- partie_prenante = extract_pp_from_query(user_query)
421
- format_pp_add_viz(partie_prenante)
422
- disp_carto_in_chat()
423
- else:
424
- #response = st.write_stream(get_response(user_query, st.session_state.chat_history,format_context(st.session_state['pp_grouped'],st.session_state['Nom de la marque']),st.session_state.model))
425
- response = get_response_with_impact(user_query, st.session_state.chat_history,format_context(st.session_state['pp_grouped'],st.session_state['Nom de la marque']),st.session_state.model)
426
-
427
- st.session_state.chat_history.append(AIMessage(content=response))
428
-
429
- with st.spinner("Proposition de prompts..."):
430
- propositions_prompts = get_response_with_impact(generated_prompt_question, st.session_state.chat_history,format_context(st.session_state['pp_grouped'],st.session_state['Nom de la marque']),st.session_state.model,history_limit=1,stream=False)
431
- extract_format_prompts_from_response(propositions_prompts)
432
- st.session_state["partial_emissions"]["chatbot"]["cc"] = st.session_state["emission"].stop()
433
-
434
-
435
-
436
-
437
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
collaborons.py CHANGED
@@ -59,7 +59,6 @@ def perform_chat(messages):
59
  client = openai.OpenAI(api_key=YOUR_API_KEY, base_url="https://api.perplexity.ai")
60
 
61
  response_stream = client.chat.completions.create(
62
-
63
  model="sonar-medium-online",
64
  messages=messages,
65
  stream=True
 
59
  client = openai.OpenAI(api_key=YOUR_API_KEY, base_url="https://api.perplexity.ai")
60
 
61
  response_stream = client.chat.completions.create(
 
62
  model="sonar-medium-online",
63
  messages=messages,
64
  stream=True
comparateur.py DELETED
@@ -1,54 +0,0 @@
1
-
2
- import streamlit as st
3
- import pandas as pd
4
-
5
- dict_comparaison_1kgCO2 = {
6
- "eau en litre":[374/100,"https://impactco2.fr/icons/eauenbouteille.svg"],
7
- "smartphones": [1.16/100,"https://impactco2.fr/icons/smartphone.svg"],
8
- "voiture en km": [460/100,"https://impactco2.fr/icons/voiturethermique.svg"],
9
- "tgv en km":[34130/100,"https://impactco2.fr/icons/tgv.svg"],
10
- }
11
-
12
- def compare(value_init, ratio_equivalent):
13
- return ratio_equivalent * value_init if ratio_equivalent else None
14
-
15
- def display_comparaison(container,value_init, ratio_equivalent,icon, unit):
16
- #value_init must be in kgCO2
17
- link_url = f"https://impactco2.fr/outils/comparateur?value={value_init}&comparisons=tgv,eauenbouteille,voiturethermique"
18
- container.markdown(f"<div style='text-align: center;'><a href='{link_url}' target='_blank'><img src='{icon}' alt='{unit}' width='50'></div>", unsafe_allow_html=True)
19
- container.markdown(f"<div style='text-align: center;'><b>{compare(value_init, ratio_equivalent):.2f}</b> {unit}</div>", unsafe_allow_html=True)
20
-
21
- def custom_cumsum(ecologits_series, codecarbon_series):
22
- cumsum = 0
23
- result = []
24
- for eco, code in zip(ecologits_series, codecarbon_series):
25
- if code == 0:
26
- result.append(0)
27
- else:
28
- cumsum += eco
29
- result.append(cumsum)
30
- return pd.Series(result, index=ecologits_series.index)
31
-
32
-
33
- def get_table_empreintes_detailed() -> pd.DataFrame:
34
- emissions_data = st.session_state["partial_emissions"]
35
- emissions_df = pd.DataFrame(emissions_data).T # Transpose to match the desired format
36
-
37
- # Rename columns and index values
38
- emissions_df = emissions_df.rename(columns={"cc": "CodeCarbon (Cumulative)", "el": "EcoLogits (par requete)"})
39
- emissions_df = emissions_df.applymap(lambda x: x * 1000)
40
- emissions_df = emissions_df.round(2)
41
- emissions_df = emissions_df.rename(index={
42
- "Scrapping": "Collecte de documents et scrapping",
43
- "extraction_pp": "Extraction des parties prenantes (serveur et IA)",
44
- "cartographie": "Sauvegarde de la cartographie",
45
- "chatbot": "Dialogue avec chatBot IA",
46
- "download_rapport": "Téléchargement cartographie"
47
- })
48
-
49
- # Make Ecologits column cumulative
50
- emissions_df["EcoLogits (Cumulative)"] = custom_cumsum(emissions_df["EcoLogits (par requete)"], emissions_df["CodeCarbon (Cumulative)"])
51
-
52
- emissions_df['Consommation Totale'] = emissions_df["CodeCarbon (Cumulative)"] + emissions_df["EcoLogits (Cumulative)"]
53
- emissions_df = emissions_df.round(2)
54
- return emissions_df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data_manager_bziiit.py CHANGED
@@ -34,22 +34,6 @@ def get_engaged_brands():
34
  st.error(f"Échec de récupération des marques engagées: {response.text}")
35
  return []
36
 
37
- # Fonction de récupération des prompts
38
- def get_prompts():
39
- url = f"{BASE_URL}/opendata/prompts"
40
- response = requests.get(url)
41
- if response.status_code == 200:
42
- return response.json()["response"]
43
- else:
44
- st.error(f"Échec de récupération des marques engagées: {response.text}")
45
- return []
46
-
47
- def get_prompt_by_id(id):
48
- prompts = get_prompts()
49
- for prompt in prompts:
50
- if prompt.get('id') == id:
51
- return prompt
52
-
53
  # Fonction consolidant les données labels + projets RSE + marques
54
  def get_bziiit_data():
55
  labels = get_labels()
 
34
  st.error(f"Échec de récupération des marques engagées: {response.text}")
35
  return []
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  # Fonction consolidant les données labels + projets RSE + marques
38
  def get_bziiit_data():
39
  labels = get_labels()
documentations.py CHANGED
@@ -1,12 +1,10 @@
1
  import streamlit as st
2
- from comparateur import get_table_empreintes_detailed
3
 
4
  def display_documentation():
5
 
6
  st.markdown("<hr style='border-color: darkgrey;'>", unsafe_allow_html=True) # Add this line
7
 
8
  st.title("OPEN DATA IA RSE Bordeaux Métropole")
9
-
10
  st.markdown("## La Data et l'IA au service des démarches RSE (Economie, Social, Environnemental)")
11
 
12
  st.image("DATA IA RSE Bordeaux Metropole.png", caption="Data IA RSE Bordeaux Metropole")
@@ -26,3 +24,5 @@ def display_documentation():
26
  </div>
27
  """, unsafe_allow_html=True)
28
 
 
 
 
1
  import streamlit as st
 
2
 
3
  def display_documentation():
4
 
5
  st.markdown("<hr style='border-color: darkgrey;'>", unsafe_allow_html=True) # Add this line
6
 
7
  st.title("OPEN DATA IA RSE Bordeaux Métropole")
 
8
  st.markdown("## La Data et l'IA au service des démarches RSE (Economie, Social, Environnemental)")
9
 
10
  st.image("DATA IA RSE Bordeaux Metropole.png", caption="Data IA RSE Bordeaux Metropole")
 
24
  </div>
25
  """, unsafe_allow_html=True)
26
 
27
+ if __name__ == "__main__":
28
+ main()
download_chart.py DELETED
@@ -1,124 +0,0 @@
1
- import streamlit as st
2
- import plotly.graph_objects as go
3
- import plotly.express as px
4
-
5
- from PIL import Image
6
- import pdfkit
7
- import pandas as pd
8
- import plotly.io as pio
9
- import tempfile
10
-
11
-
12
- def construct_plot():
13
- # Define data
14
- stakeholders = st.session_state['pp_grouped']
15
-
16
- if stakeholders is None or len(stakeholders) == 0:
17
- st.error("Aucune partie prenante n'a été définie")
18
- return None
19
- # Create plot
20
- fig = go.Figure()
21
-
22
- # Add category areas
23
- fig.add_shape(type="rect", x0=0, y0=50, x1=50, y1=100, fillcolor="lightblue", opacity=0.2, line_width=0)
24
- fig.add_shape(type="rect", x0=50, y0=50, x1=100, y1=100, fillcolor="lightyellow", opacity=0.2, line_width=0)
25
- fig.add_shape(type="rect", x0=0, y0=0, x1=50, y1=50, fillcolor="lightcoral", opacity=0.2, line_width=0)
26
- fig.add_shape(type="rect", x0=50, y0=0, x1=100, y1=50, fillcolor="lightcyan", opacity=0.2, line_width=0)
27
-
28
- # Add category titles
29
- fig.add_annotation(x=10, y=90, text="Rendre satisfait", showarrow=False)
30
- fig.add_annotation(x=60, y=90, text="Gérer étroitement", showarrow=False)
31
- fig.add_annotation(x=10, y=40, text="Suivre de près", showarrow=False)
32
- fig.add_annotation(x=60, y=40, text="Tenir informé", showarrow=False)
33
-
34
- x_array = [stakeholder['x'] for stakeholder in stakeholders]
35
- y_array = [stakeholder['y'] for stakeholder in stakeholders]
36
- name_array = [stakeholder['name'] for stakeholder in stakeholders]
37
-
38
- #color_sequence = px.colors.qualitative.Plotly
39
- # Add stakeholders to plot
40
- for i,stakeholder in enumerate(stakeholders):
41
- fig.add_trace(go.Scatter(
42
- x=[stakeholder['x']],
43
- y=[stakeholder['y']],
44
- mode='markers+text',
45
- marker=dict(color=stakeholder['color'],size=33),
46
- textposition="top center",
47
- name=stakeholder['name']
48
- ))
49
-
50
- # Update layout
51
- fig.update_layout(
52
- legend=dict( orientation="h", yanchor="bottom",y=1.02,title="Parties prenantes"),
53
- height=600,
54
- title=dict(text="Cartographie des parties prenantes", x=0.5, y=1, xanchor="center", yanchor="top"),
55
- xaxis=dict(title="Influence", range=[0, 100]),
56
- yaxis=dict(title="Pouvoir", range=[0, 100]),
57
- showlegend=True
58
- )
59
-
60
- # Display plot in Streamlit
61
- return fig
62
-
63
- def save_plot_as_pdf(fig, logo_path, title):
64
- st.write("saving plot as pdf")
65
-
66
- with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmpfile:
67
- st.write("tmpfile created")
68
- image_bytes = pio.to_image(fig, format='png')
69
- st.write("image_bytes")
70
-
71
- tmpfile.write(image_bytes)
72
- tmpfile.close()
73
- plot_image_path = tmpfile.name
74
-
75
- st.write(plot_image_path)
76
-
77
- html_content = f"""
78
- <!DOCTYPE html>
79
- <html lang="en">
80
- <head>
81
- <meta charset="UTF-8">
82
- <title>{title}</title>
83
- <style>
84
- body {{ font-family: Arial, sans-serif; text-align: center; }}
85
- .logo {{ width: 100px; margin-top: 20px; }}
86
- .title {{ font-size: 24px; margin-top: 20px; }}
87
- .plot-image {{ width: 80%; margin-top: 20px; }}
88
- </style>
89
- </head>
90
- <body>
91
- {"<img src='" + logo_path + "' class='logo'>" if logo_path else ""}
92
- <div class="title">{title}</div>
93
- <img src="{plot_image_path}" class="plot-image">
94
- </body>
95
- </html>
96
- """
97
-
98
- with tempfile.NamedTemporaryFile(delete=False, suffix=".html") as tmpfile:
99
- tmpfile.write(html_content.encode('utf-8'))
100
- tmpfile.close()
101
- html_path = tmpfile.name
102
-
103
- pdf_path = html_path.replace('figure.html', '.pdf')
104
- st.write(pdf_path)
105
- pdfkit.from_file(html_path, pdf_path)
106
- return pdf_path
107
-
108
- def download_pdf():
109
- # Construct plot
110
- fig = construct_plot()
111
-
112
- if fig is None:
113
- return None
114
- st.write("fig constructed")
115
-
116
- 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"
117
- pdf_title = "Cartographie des parties prenantes"
118
- # Button to download PDF
119
- pdf_path = save_plot_as_pdf(fig, logo_path, pdf_title)
120
-
121
- st.write("pdf saved")
122
- with open(pdf_path, "rb") as pdf_file:
123
- st.download_button(label="Download PDF", data=pdf_file, file_name="stakeholder_analysis.pdf")
124
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
emissions.csv DELETED
The diff for this file is too large to render. See raw diff
 
empreinte_carbone.py DELETED
@@ -1,120 +0,0 @@
1
- import streamlit as st
2
- from comparateur import get_table_empreintes_detailed
3
- from comparateur import *
4
- import base64
5
- import pandas as pd
6
- import altair as alt
7
-
8
- # Function to read and encode an SVG file to Base64
9
- def load_svg_as_base64(file_path):
10
- with open(file_path, "rb") as f:
11
- svg_data = f.read()
12
- return base64.b64encode(svg_data).decode()
13
-
14
- def color_scale(val):
15
- if val == '-':
16
- return 'background-color: {color}'
17
- elif val <= 1:
18
- color = '#008571' #'rgba(0,238,0,0.5)' # green with opacity
19
- elif val <= 10:
20
- color = '#83c2b8' # light green with opacity
21
- elif val <= 50:
22
- color = '#efcd82' # light yellow with opacity
23
- elif val <= 100:
24
- color = '#f2aa56' # light orange with opacity
25
- else:
26
- color = '#e87a58' # light red with opacity
27
- return f'background-color: {color};color:white'
28
-
29
- def display_cf_comparison(stm: st):
30
- svg_file_path = "feuille.svg"
31
- svg_base64 = load_svg_as_base64(svg_file_path)
32
- stm.markdown(
33
- f"""
34
- **Votre consommation carbone**
35
- <img src='data:image/svg+xml;base64,{svg_base64}' alt='svg' width='15' height='15' style='margin-left: 10px;'>
36
- """,
37
- unsafe_allow_html=True
38
- )
39
-
40
- serveur_emission = st.session_state['emission'].stop()
41
- emission_api = sum([value["el"] for value in st.session_state["partial_emissions"].values()])
42
-
43
- if serveur_emission is None :
44
- serveur_emission = 0
45
- if emission_api is None :
46
- emission_api = 0
47
- total_emission = serveur_emission + emission_api
48
-
49
- pourcentage_api = emission_api / total_emission
50
-
51
- pourcentage_serveur = serveur_emission / total_emission
52
-
53
-
54
-
55
- stm.markdown(f"<div style='text-align: center; margin-bottom: 10px;'><b>{total_emission*1000:.2f}</b> g eq. CO2</div>", unsafe_allow_html=True)
56
- stm.markdown("Dont :")
57
- stm.markdown(f"- Empreinte serveur (via CodeCarbon) : **{serveur_emission*1000:.2f}** g eq. CO2 ({pourcentage_serveur:.2%})")
58
- stm.write(f"- Empreinte IA (via EcoLogits) : **{emission_api*1000:.2f}** g eq. CO2 ({pourcentage_api:.2%})")
59
- # stm.markdown("(avec l'outil CodeCarbon)")
60
- c1,c2,c3 = stm.columns([1,1,1])
61
-
62
- c2.write("---")
63
-
64
- stm.markdown("**Votre équivalence**")
65
- col1,col2,col3 = stm.columns([1,1,1])
66
- display_comparaison(col1,total_emission,dict_comparaison_1kgCO2["eau en litre"][0]*1000,dict_comparaison_1kgCO2["eau en litre"][1],"ml")
67
- display_comparaison(col2,total_emission,dict_comparaison_1kgCO2["tgv en km"][0],dict_comparaison_1kgCO2["tgv en km"][1],"km")
68
- display_comparaison(col3,total_emission,dict_comparaison_1kgCO2["voiture en km"][0]*1000,dict_comparaison_1kgCO2["voiture en km"][1],"m")
69
- stm.markdown("\n")
70
- stm.markdown(
71
- f"""
72
- Powered by **ADEME**
73
- <a href='https://www.ademe.fr' target='_blank'><img src='https://www.ademe.fr/wp-content/uploads/2022/11/ademe-logo-2022-1.svg' alt='svg' width='30' height='30' style='margin-left: 10px;'>
74
- """,
75
- unsafe_allow_html=True
76
- )
77
-
78
- def display_carbon_footprint():
79
- st.title("EMPREINTE ÉNERGÉTIQUE DE L'APPLICATION IA CARTO RSE")
80
- display_cf_comparison(st)
81
- table = get_table_empreintes_detailed()
82
- # table[['Consommation Totale']] = table[['Consommation Totale']].map('${:,.2f}'.format)
83
-
84
- table.replace({0.00: '-'}, inplace=True)
85
- #just 2 digits after the comma
86
- styled_df = table[['Consommation Totale']].rename(columns={'Consommation Totale': 'Consommation totale (g eqCo2)'})
87
- styled_df = styled_df.round(2)
88
-
89
- styled_df = styled_df.style.applymap(color_scale, subset=['Consommation totale (g eqCo2)'])
90
- st.markdown("---")
91
- st.markdown("### DÉTAIL PAR TÂCHE")
92
- st.table(styled_df)
93
- with st.expander("Plus de détails"):
94
- st.table(table)
95
-
96
- st.markdown("### SYNTHESE (Dialogue IA et non IA)")
97
-
98
- serveur_emission = st.session_state['emission'].stop()
99
- emission_api = sum([value["el"] for value in st.session_state["partial_emissions"].values()])
100
- print(serveur_emission, emission_api)
101
- total_emission = serveur_emission + emission_api
102
-
103
- pourcentage_api = emission_api / total_emission
104
- pourcentage_serveur = serveur_emission / total_emission
105
-
106
- df = pd.DataFrame({"Catégorie": ["Identification + dessin","IA (extraction pp + dialogue)"], "valeur": [pourcentage_serveur, pourcentage_api]})
107
- color_scale_alt = alt.Scale(domain=['Identification + dessin', 'IA (extraction pp + dialogue)'], range=['#011166', '#63abdf'])
108
-
109
- base=alt.Chart(df).encode(
110
- theta=alt.Theta(field="valeur", type="quantitative", stack=True),
111
- color=alt.Color(field="Catégorie", type="nominal", scale=color_scale_alt),
112
- )
113
-
114
-
115
- pie = base.mark_arc(outerRadius=100)
116
- text = base.mark_text(radius=150,fill= "black",align='center', baseline='middle',fontSize=20).encode(alt.Text(field="valeur", type="quantitative", format=".2%"))
117
-
118
- chart = alt.layer(pie, text, data=df).resolve_scale(theta="independent")
119
- st.altair_chart(chart, use_container_width=True)
120
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
empreinte_export.py DELETED
@@ -1,172 +0,0 @@
1
- import streamlit as st
2
- import pandas as pd
3
- import altair as alt
4
- import base64
5
- import pdfkit
6
- import io
7
- from comparateur import *
8
-
9
- def load_svg_as_base64(svg_file_path):
10
- with open(svg_file_path, "rb") as svg_file:
11
- return base64.b64encode(svg_file.read()).decode()
12
-
13
- def save_pdf(html_content):
14
- pdf = pdfkit.from_string(html_content, False)
15
- return pdf
16
-
17
- def display_comparaison_html(value_init, ratio_equivalent, icon, unit):
18
- link_url = f"https://impactco2.fr/outils/comparateur?value={value_init}&comparisons=tgv,eauenbouteille,voiturethermique"
19
- html = f"""
20
- <div style='text-align: center;'>
21
- <a href='{link_url}' target='_blank'><img src='{icon}' alt='{unit}' width='50'></a>
22
- </div>
23
- <div style='text-align: center;'>
24
- <b>{compare(value_init, ratio_equivalent):.2f}</b> {unit}
25
- </div>
26
- """
27
- return html
28
-
29
- def display_cf_comparison():
30
- svg_file_path = "feuille.svg"
31
- svg_base64 = load_svg_as_base64(svg_file_path)
32
-
33
- html_content = f"""
34
- <div style='display: flex; align-items: center;'>
35
- <h4 style='margin: 0;'>Votre consommation Carbone</h4>
36
- <img src='data:image/svg+xml;base64,{svg_base64}' alt='svg' width='15' height='15' style='margin-left: 10px;'>
37
- </div>
38
- <br>
39
- """
40
-
41
- serveur_emission = st.session_state['emission'].stop()
42
- emission_api = sum([value["el"] for value in st.session_state["partial_emissions"].values()])
43
-
44
- total_emission = serveur_emission + emission_api
45
-
46
- pourcentage_api = emission_api / total_emission
47
- pourcentage_serveur = serveur_emission / total_emission
48
-
49
- html_content += f"<div style='text-align: center; margin-bottom: 10px;'><b>{total_emission*1000:.3f}</b> g eq. CO2</div>"
50
- html_content += f"<p>Dont :</p>"
51
- html_content += f"<p>- Empreinte serveur (via CodeCarbon) : <b>{serveur_emission*1000:.3f}</b> g eq. CO2 ({pourcentage_serveur:.2%})</p>"
52
- html_content += f"<p>- Empreinte IA (via EcoLogits) : <b>{emission_api*1000:.3f}</b> g eq. CO2 ({pourcentage_api:.2%})</p>"
53
-
54
- html_content += "<h4>Votre équivalence</h4>"
55
- html_content += """
56
- <div style='display: flex; justify-content: space-around;'>
57
- """
58
-
59
- html_content += f"""
60
- <div>
61
- {display_comparaison_html(total_emission, dict_comparaison_1kgCO2["eau en litre"][0]*1000, dict_comparaison_1kgCO2["eau en litre"][1], "ml")}
62
- </div>
63
- <div>
64
- {display_comparaison_html(total_emission, dict_comparaison_1kgCO2["tgv en km"][0], dict_comparaison_1kgCO2["tgv en km"][1], "km")}
65
- </div>
66
- <div>
67
- {display_comparaison_html(total_emission, dict_comparaison_1kgCO2["voiture en km"][0]*1000, dict_comparaison_1kgCO2["voiture en km"][1], "m")}
68
- </div>
69
- """
70
-
71
- html_content += "</div><br>"
72
-
73
-
74
- html_content += f"""
75
- <br>
76
- <div style='display: flex; align-items: center;'>
77
- <p>Powered by <b>ADEME</b></p>
78
- <a href='https://www.ademe.fr' target='_blank'><img src='https://www.ademe.fr/wp-content/uploads/2022/11/ademe-logo-2022-1.svg' alt='svg' width='30' height='30' style='margin-left: 10px;'></a>
79
- </div>
80
- <br>
81
- """
82
-
83
- #st.markdown(html_content, unsafe_allow_html=True)
84
- return html_content
85
-
86
- def color_scale(val):
87
- if val == '-':
88
- return 'background-color: white'
89
- elif val <= 1:
90
- return 'background-color: rgba(0,100,0,0.5)' # dark green with opacity
91
- elif val <= 10:
92
- return 'background-color: rgba(0,128,0,0.5)' # green with opacity
93
- elif val <= 50:
94
- return 'background-color: rgba(255,255,0,0.5)' # yellow with opacity
95
- elif val <= 100:
96
- return 'background-color: rgba(255,165,0,0.5)' # orange with opacity
97
- else:
98
- return 'background-color: rgba(255,0,0,0.5)' # red with opacity
99
-
100
-
101
- def get_carbon_footprint_html():
102
-
103
- html_content = "<h2>EMPREINTE ÉNERGÉTIQUE DE L'APPLICATION IA CARTO RSE</h2>"
104
- html_content += display_cf_comparison()
105
-
106
- table = get_table_empreintes_detailed()
107
- table.replace({0.00: '-'}, inplace=True)
108
- styled_df = table[['Consommation Totale']].rename(columns={'Consommation Totale': 'Consommation totale (g eqCo2)'})
109
- styled_df = styled_df.style.applymap(color_scale, subset=['Consommation totale (g eqCo2)'])
110
-
111
- html_content += """
112
- <style>
113
- .centered-table {
114
- margin-left: auto;
115
- margin-right: auto;
116
- border-collapse: collapse;
117
- width: 80%;
118
- }
119
- .centered-table th, .centered-table td {
120
- border: 1px solid #ddd;
121
- padding: 8px;
122
- text-align: left;
123
- }
124
- .centered-table th {
125
- background-color: #f2f2f2;
126
- }
127
- </style>
128
- """
129
-
130
- html_content += """
131
- <h2>DÉTAIL PAR TÂCHE</h2>
132
- <div style="overflow-x:auto;">
133
- """
134
-
135
- html_content += styled_df.set_table_attributes('class="centered-table"').to_html()
136
-
137
- html_content += """
138
- </div>
139
- """
140
-
141
- serveur_emission = st.session_state['emission'].stop()
142
- emission_api = sum([value["el"] for value in st.session_state["partial_emissions"].values()])
143
-
144
- total_emission = serveur_emission + emission_api
145
-
146
- pourcentage_api = emission_api / total_emission
147
- pourcentage_serveur = serveur_emission / total_emission
148
-
149
- df = pd.DataFrame({"Catégorie": ["Identification + dessin", "IA (extraction pp + dialogue)"], "valeur": [pourcentage_serveur, pourcentage_api]})
150
- color_scale_alt = alt.Scale(domain=['Identification + dessin', 'IA (extraction pp + dialogue)'], range=['#011166', '#63abdf'])
151
-
152
- base = alt.Chart(df).encode(
153
- theta=alt.Theta(field="valeur", type="quantitative", stack=True),
154
- color=alt.Color(field="Catégorie", type="nominal",scale=color_scale_alt)
155
- )
156
-
157
- pie = base.mark_arc(outerRadius=100)
158
- text = base.mark_text(radius=150, fill="black",align='center', baseline='middle',fontSize=14).encode(alt.Text(field="valeur", type="quantitative", format=".2%"))
159
-
160
- chart = alt.layer(pie, text, data=df).resolve_scale(theta="independent")
161
-
162
- html_content += """
163
- <h2>SYNTHESE (Dialogue IA et non IA)</h2>
164
- """
165
- chart.save("chart.png")
166
- with open("chart.png", "rb") as image_file:
167
- encoded_image = base64.b64encode(image_file.read()).decode()
168
-
169
- html_content += f'<div style="text-align:center;"><img src="data:image/png;base64,{encoded_image}" alt="Pie chart"></div>'
170
-
171
- return html_content
172
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
export_doc.py DELETED
@@ -1,242 +0,0 @@
1
- import streamlit as st
2
- import markdown2
3
- import pdfkit
4
- from io import BytesIO
5
- from IPython.display import display, FileLink
6
- import base64
7
- from langchain_core.messages import AIMessage, HumanMessage
8
- from datetime import datetime
9
- from download_chart import construct_plot
10
- from kaleido.scopes.plotly import PlotlyScope
11
- import pandas as pd
12
- import markdown
13
- from comparateur import get_table_empreintes_detailed
14
- from empreinte_export import get_carbon_footprint_html
15
-
16
- def colored_circle(color):
17
- return f'<span style="display: inline-block; width: 15px; height: 15px; border-radius: 50%; background-color: {color};"></span>'
18
-
19
- def list_to_markdown(lst):
20
- 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]])
21
-
22
- def categorize(row):
23
- if 50 <= row['pouvoir'] <= 100 and 0 <= row['influence'] < 50:
24
- return 'Rendre satisfait'
25
- elif 50 <= row['pouvoir'] <= 100 and 50 <= row['influence'] <= 100:
26
- return 'Gérer étroitement'
27
- elif 0 <= row['pouvoir'] < 50 and 0 <= row['influence'] < 50:
28
- return 'Suivre de près'
29
- elif 0 <= row['pouvoir'] < 50 and 50 <= row['influence'] <= 100:
30
- return 'Tenir informé'
31
- else:
32
- return 'Non catégorisé'
33
-
34
-
35
-
36
- @st.cache_data
37
- def convert_pp_to_csv(pp_grouped):
38
- if pp_grouped is None or len(pp_grouped) == 0:
39
- st.error("Aucune partie prenante n'a été définie")
40
- return None
41
- pp_df = pd.DataFrame(pp_grouped)
42
- pp_df.index.name = 'N°'
43
- pp_df.rename(columns={"name": "parties prenantes", "x": "influence", "y": "pouvoir"}, inplace=True)
44
- pp_df.drop(columns=['color'], inplace=True)
45
- # Apply the function to the DataFrame to create a new column 'categorie'
46
- pp_df['categorie'] = pp_df.apply(categorize, axis=1)
47
- pp_df = pp_df[["parties prenantes","categorie", "pouvoir", "influence"]]
48
- pp_df.rename_axis('N°', axis=1)
49
- return pp_df.to_csv(index=True,encoding="utf-8")
50
-
51
- @st.cache_data
52
- def create_pdf_from_markdown(logo_path, conversation,summary,brand_name,graph_html,app_url,list_pps,used_models=None):
53
- # Convertir la conversation en markdown
54
- markdown_text = "\n".join([f"### {entry['speaker']}:\n {entry['text']}\n ---" for entry in conversation])
55
-
56
- if not used_models:
57
- used_models = ["Aucun modèle IA n'a été utilisé"]
58
- html_used_models = "".join([f"<p>{model}</p>" for model in used_models])
59
-
60
- markdown_summary = f"{summary}\n --- \n ---"
61
- markdown_list_pps = list_to_markdown(list_pps)
62
- # Convertir le markdown en HTML
63
- html_content = markdown.markdown(markdown_text,extensions=['markdown.extensions.tables'])
64
- html_summary = markdown2.markdown(markdown_summary)
65
- html_list_pps = markdown2.markdown(markdown_list_pps)
66
-
67
- analysis_date = datetime.now().strftime("%Y-%m-%d")
68
- # image_base64 = base64.b64encode(image_path).decode('utf-8')
69
- graph_html.update_layout(showlegend=False)
70
- img_bytes = PlotlyScope().transform(
71
- figure=graph_html,
72
- format="png",
73
- )
74
- fig1 = f"data:image/png;base64,{base64.b64encode(img_bytes).decode('utf8')}"
75
- # Créer le HTML complet avec les images et le texte
76
-
77
- html_template = f"""
78
- <!DOCTYPE html>
79
- <html lang="en">
80
- <head>
81
- <meta charset="UTF-8">
82
- <title>Cartographie des parties prenantes {brand_name}</title>
83
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
84
- <style>
85
- body {{
86
- font-family: 'Roboto', sans-serif;
87
- margin: 20px;
88
- }}
89
- h1, h2, h3, h4, h5, h6 {{
90
- font-weight: bold;
91
- }}
92
- .page-break {{
93
- page-break-before: always;
94
- margin: 50px;
95
- height: 50px;
96
- }}
97
- </style>
98
- </head>
99
- <body>
100
- <div style="text-align: center;">
101
- <h1>Cartographie des parties prenantes "{brand_name}"</h1>
102
- <p>Date de l'analyse IA RSE : {analysis_date}</p>
103
- <p>IA utilisées :</p>
104
- {html_used_models}
105
- <img src="{logo_path}" alt="Logo" style="width: 150px;"/>
106
- </div>
107
- <div class="page-break"></div>
108
- <div style="text-align: center; margin-top: 20px;">
109
- <img src="{fig1}">
110
- </div>
111
- {html_list_pps}
112
- <div class="page-break"></div>
113
- <h2>RESUME</h2>
114
- {html_summary}
115
- <div class="page-break"></div>
116
- <h2>Historique de la Conversation</h2>
117
- {html_content}
118
- <div class="page-break"></div>
119
- {get_carbon_footprint_html()}
120
- </body>
121
- </html>
122
- """
123
-
124
- with open("temp.html", "w",encoding="utf-8") as f:
125
- f.write(html_template)
126
-
127
- # Create the footer HTML with the logo and app_url
128
- footer_html = f"""
129
- <!DOCTYPE html>
130
- <html lang="en">
131
- <head>
132
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
133
- <meta charset="UTF-8">
134
- <style>
135
- body {{
136
- font-family: 'Roboto', sans-serif;
137
- margin-top: 20px;
138
- }}
139
- .footer {{
140
- width: 100%;
141
- font-size: 16px;
142
- display: flex;
143
- align-items: center;
144
- justify-content: space-between;
145
- padding: 10px 20px;
146
- }}
147
- .footer img {{
148
- width: 100px;
149
- vertical-align: middle;
150
- margin-bottom: 0px;
151
- padding-bottom: 0px;
152
-
153
- }}
154
- .footer .center-text {{
155
- text-align: center;
156
-
157
- }}
158
- .footer .page-number {{
159
- text-align: right;
160
- }}
161
- .footer a {{
162
- color: #0000EE;
163
- text-decoration: none;
164
- }}
165
- .page {{
166
- font-weight: bold;
167
- font-size: 10px;
168
- margin-bottom: 0px;
169
- padding-bottom: 0px;
170
- }}
171
-
172
- </style>
173
- </head>
174
- <body>
175
- <div class="footer">
176
- <img src="{logo_path}" alt="Logo" />
177
- <div class="center-text">
178
- bziiit | Open data & IA RSE | <a href="{app_url}">{app_url}</a>
179
- </div>
180
- <div class="page-number">
181
- <span class="page"></span>
182
- </div>
183
- </div>
184
- </body>
185
- </html>
186
- """
187
-
188
-
189
- # Save the footer HTML to a temporary file
190
- with open("footer.html", "w",encoding="utf-8") as f:
191
- f.write(footer_html)
192
-
193
-
194
- # Convert HTML to PDF with header and footer
195
- pdf = pdfkit.from_file("temp.html", options={
196
- 'footer-html': 'footer.html',
197
- 'footer-right': '[page]/[toPage]',
198
- 'footer-font-size': '10',
199
- 'footer-spacing': '5',
200
- 'footer-line': True,
201
- 'margin-top': '5',
202
- })
203
- return pdf
204
-
205
- def get_conversation():
206
- conversation = []
207
- for message in st.session_state.chat_history:
208
- if isinstance(message, AIMessage):
209
- conversation.append({"speaker": "AI", "text": message.content})
210
- elif isinstance(message, HumanMessage):
211
- conversation.append({"speaker": "Moi", "text": message.content})
212
- return conversation
213
-
214
-
215
- def export_conversation(summary,used_models=None):
216
- brand_name = st.session_state["Nom de la marque"]
217
- app_url = "https://huggingface.co/spaces/bziiit/OpenData-Bordeaux-RSE"
218
- 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" # Replace with your image path
219
- list_pps = st.session_state['pp_grouped']
220
-
221
- with st.spinner("Génération du PDF..."):
222
- conversation = get_conversation()
223
- image_path = "newplot.png"
224
- try:
225
- graph = construct_plot()
226
- # graph = graph.to_html(full_html=False, include_plotlyjs='cdn')
227
- except Exception as e:
228
- st.error("Erreur lors de la génération de la cartographie")
229
- graph = ""
230
- try:
231
- 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)
232
- except Exception as e:
233
- pdf = None
234
-
235
- if pdf:
236
- st.success("PDF généré avec succès!}")
237
- else:
238
- st.error("Erreur lors de la génération du PDF")
239
-
240
- return pdf
241
-
242
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
feuille.svg DELETED
footer.html DELETED
@@ -1,59 +0,0 @@
1
-
2
- <!DOCTYPE html>
3
- <html lang="en">
4
- <head>
5
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
6
- <meta charset="UTF-8">
7
- <style>
8
- body {
9
- font-family: 'Roboto', sans-serif;
10
- margin-top: 20px;
11
- }
12
- .footer {
13
- width: 100%;
14
- font-size: 16px;
15
- display: flex;
16
- align-items: center;
17
- justify-content: space-between;
18
- padding: 10px 20px;
19
- }
20
- .footer img {
21
- width: 100px;
22
- vertical-align: middle;
23
- margin-bottom: 0px;
24
- padding-bottom: 0px;
25
-
26
- }
27
- .footer .center-text {
28
- text-align: center;
29
-
30
- }
31
- .footer .page-number {
32
- text-align: right;
33
- }
34
- .footer a {
35
- color: #0000EE;
36
- text-decoration: none;
37
- }
38
- .page {
39
- font-weight: bold;
40
- font-size: 10px;
41
- margin-bottom: 0px;
42
- padding-bottom: 0px;
43
- }
44
-
45
- </style>
46
- </head>
47
- <body>
48
- <div class="footer">
49
- <img src="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" alt="Logo" />
50
- <div class="center-text">
51
- bziiit | Open data & IA RSE | <a href="https://huggingface.co/spaces/bziiit/OpenData-Bordeaux-RSE">https://huggingface.co/spaces/bziiit/OpenData-Bordeaux-RSE</a>
52
- </div>
53
- <div class="page-number">
54
- <span class="page"></span>
55
- </div>
56
- </div>
57
- </body>
58
- </html>
59
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
high_chart.py DELETED
@@ -1,201 +0,0 @@
1
- import streamlit as st
2
- import st_hc as hct
3
- import download_chart as dc
4
- from export_doc import convert_pp_to_csv
5
-
6
-
7
-
8
- # Define the highchart definition
9
- cd2 = {
10
- "chart":{
11
- "animation":False
12
- },
13
- "title":{
14
- "text":"Cartographie des parties prenantes"
15
- },
16
- "tooltip":{
17
- "valueDecimals":0
18
- },
19
- "xAxis":{
20
- "title":{
21
- "text":"Influence"
22
- },
23
- "min":0,
24
- "max":100,
25
- "plotLines":[
26
- {
27
- "color":"gray",
28
- "dashStyle":"LongDash",
29
- "width":1,
30
- "value":50,
31
- "label":{
32
- "rotation":0,
33
- "y":290,
34
- "style":{
35
- "color":"blue",
36
- "font-size": "20px"
37
- },
38
- "text":"Tenir informé",
39
- },
40
- "zIndex":3
41
- },
42
- {
43
- "color":"gray",
44
- "dashStyle":"LongDash",
45
- "width":1,
46
- "value":50,
47
- "label":{
48
- "rotation":0,
49
- "y":25,
50
- "style":{
51
- "color":"orange",
52
- "font-size": "20px"
53
- },
54
- "text":"Gérer étroitement",
55
- },
56
- "zIndex":3
57
- },
58
- {
59
- "color":"gray",
60
- "dashStyle":"LongDash",
61
- "width":1,
62
- "value":0,
63
- "label":{
64
- "rotation":0,
65
- "y":25,
66
- "style":{
67
- "color":"green",
68
- "font-size": "20px"
69
- },
70
- "text":"Rendre satisfait",
71
- },
72
- "zIndex":3
73
- },
74
- {
75
- "color":"gray",
76
- "dashStyle":"LongDash",
77
- "width":1,
78
- "value":0,
79
- "label":{
80
- "rotation":0,
81
- "y":290,
82
- "style":{
83
- "color":"red",
84
- "font-size": "20px"
85
- },
86
- "text":"Suivre de près",
87
- },
88
- "zIndex":3
89
- },
90
- {
91
- "color":"gray",
92
- "dashStyle":"LongDash",
93
- "width":1,
94
- "value":100,
95
- "zIndex":3
96
-
97
- }
98
- ]
99
- },
100
- "yAxis":{
101
- "title":{
102
- "text":"Pouvoir"
103
- },
104
- "min":0,
105
- "max":100,
106
- "plotLines":[
107
- {
108
- "color":"gray",
109
- "dashStyle":"LongDash",
110
- "width":1,
111
- "value":50,
112
- "zIndex":3
113
-
114
- },
115
- {
116
- "color":"gray",
117
- "dashStyle":"LongDash",
118
- "width":1,
119
- "value":100,
120
- "zIndex":3
121
- },
122
- {
123
- "color":"gray",
124
- "dashStyle":"LongDash",
125
- "width":1,
126
- "value":0,
127
- "zIndex":3
128
- }
129
- ],
130
- },
131
- "tooltip":{
132
- "headerFormat":"",
133
- "pointFormat":"{point.name}",
134
- "valueDecimals":0
135
- },
136
- "series":[
137
- {
138
- "type":"bubble",
139
- "maxSize":50,
140
- "cursor":"move",
141
- "name":"Influence",
142
- "pointformat":"{point.name}",
143
- "dragDrop":{
144
- "draggableX":True,
145
- "draggableY":True,
146
- "dragMinY":0,
147
- "dragMaxY":100,
148
- "dragMinX":0,
149
- "dragMaxX":100,
150
- "dragPrecisionX":1,
151
- "dragPrecisionY":1,
152
- "dragSensitivity":0
153
- },
154
- "data":[],
155
- "colorByPoint":True,
156
-
157
- }
158
- ],
159
- "exporting": {
160
- "enabled": True
161
-
162
- },
163
- "legend":{
164
- "enabled":False
165
- }
166
- }
167
-
168
-
169
- def construct_data():
170
- data = []
171
- for i in range(10):
172
- object = {
173
- "x": 50+i*5,
174
- "y": 50,
175
- "name": f"Point{i}",
176
-
177
- }
178
- data.append(object)
179
-
180
- return data
181
-
182
-
183
-
184
-
185
- def test_chart():
186
- #The component can render any highchart definition
187
- if "pp_grouped" not in st.session_state or len(st.session_state['pp_grouped']) == 0:
188
- return None
189
-
190
- points = st.session_state['pp_grouped']
191
- cd2["series"][0]["data"] = points
192
- chart = hct.streamlit_highcharts(cd2,640) #640 is the chart height
193
- # if chart:
194
- # st.session_state['pp_grouped'] = chart
195
-
196
- col0,col1,col2 = st.columns([1,1,1])
197
-
198
- if col1.button("Sauvegarder",key="save"):
199
- st.session_state['pp_grouped'] = chart.copy()
200
- st.session_state["partial_emissions"]["cartographie"]["cc"] = st.session_state["emission"].stop()
201
- return "saved"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
htmlTemplates.py DELETED
@@ -1,44 +0,0 @@
1
- css = '''
2
- <style>
3
- .chat-message {
4
- padding: 1.5rem; border-radius: 0.5rem; margin-bottom: 1rem; display: flex
5
- }
6
- .chat-message.user {
7
- background-color: #2b313e
8
- }
9
- .chat-message.bot {
10
- background-color: #475063
11
- }
12
- .chat-message .avatar {
13
- width: 20%;
14
- }
15
- .chat-message .avatar img {
16
- max-width: 78px;
17
- max-height: 78px;
18
- border-radius: 50%;
19
- object-fit: cover;
20
- }
21
- .chat-message .message {
22
- width: 80%;
23
- padding: 0 1.5rem;
24
- color: #fff;
25
- }
26
- '''
27
-
28
- bot_template = '''
29
- <div class="chat-message bot">
30
- <div class="avatar">
31
- <img src="data:image/png;base64,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" style="max-height: 78px; max-width: 78px; border-radius: 50%; object-fit: cover;">
32
- </div>
33
- <div class="message">{{MSG}}</div>
34
- </div>
35
- '''
36
-
37
- user_template = '''
38
- <div class="chat-message user">
39
- <div class="avatar">
40
- <img src="https://media.licdn.com/dms/image/C4E03AQGvvkYN1sbUbA/profile-displayphoto-shrink_400_400/0/1588147295529?e=1714003200&v=beta&t=pAlX20zTPhqbi7ipYCkj_oUOhOgtvsy5TuJFikbH-f4">
41
- </div>
42
- <div class="message">{{MSG}}</div>
43
- </div>
44
- '''
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
packages.txt DELETED
@@ -1,2 +0,0 @@
1
- wkhtmltopdf
2
- xfonts-75dpi
 
 
 
partie_prenante_carte.py DELETED
@@ -1,490 +0,0 @@
1
- import streamlit as st
2
- import pandas as pd
3
- import numpy as np
4
- import re
5
- import random
6
- import time
7
-
8
- import streamlit as st
9
- from dotenv import load_dotenv
10
- from langchain_experimental.text_splitter import SemanticChunker
11
- from langchain_community.embeddings import OpenAIEmbeddings
12
- from langchain_community.vectorstores import FAISS
13
- from langchain_community.chat_models import ChatOpenAI
14
- from langchain import hub
15
- from langchain_core.runnables import RunnablePassthrough
16
- from langchain_community.document_loaders import WebBaseLoader,FireCrawlLoader,PyPDFLoader
17
- from langchain_core.prompts.prompt import PromptTemplate
18
- import os
19
- from high_chart import test_chart
20
- from chat_with_pps import get_response
21
- from ecologits.tracers.utils import compute_llm_impacts
22
- from codecarbon import EmissionsTracker
23
-
24
- load_dotenv()
25
-
26
- def get_docs_from_website(urls):
27
- loader = WebBaseLoader(urls, header_template={
28
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36',
29
- })
30
- try:
31
- docs = loader.load()
32
- return docs
33
- except Exception as e:
34
- return None
35
-
36
-
37
- def get_docs_from_website_fc(urls,firecrawl_api_key):
38
- docs = []
39
- try:
40
- for url in urls:
41
- loader = FireCrawlLoader(api_key=firecrawl_api_key, url = url,mode="scrape")
42
- docs+=loader.load()
43
- return docs
44
- except Exception as e:
45
- return None
46
-
47
-
48
- def get_doc_chunks(docs):
49
- # Split the loaded data
50
- # text_splitter = RecursiveCharacterTextSplitter(
51
- # chunk_size=500,
52
- # chunk_overlap=100)
53
-
54
- text_splitter = SemanticChunker(OpenAIEmbeddings(model="text-embedding-3-small"))
55
-
56
- docs = text_splitter.split_documents(docs)
57
- return docs
58
-
59
- def get_doc_chunks_fc(docs):
60
- # Split the loaded data
61
- # text_splitter = RecursiveCharacterTextSplitter(
62
- # chunk_size=500,
63
- # chunk_overlap=100)
64
-
65
- text_splitter = SemanticChunker(OpenAIEmbeddings(model="text-embedding-3-small"))
66
- docs_splitted = []
67
- for text in docs:
68
- text_splitted = text_splitter.split_text(text)
69
- docs_splitted+=text_splitted
70
- return docs_splitted
71
-
72
-
73
- def get_vectorstore_from_docs(doc_chunks):
74
- embedding = OpenAIEmbeddings(model="text-embedding-3-small")
75
- vectorstore = FAISS.from_documents(documents=doc_chunks, embedding=embedding)
76
- return vectorstore
77
-
78
- def get_vectorstore_from_text(texts):
79
- embedding = OpenAIEmbeddings(model="text-embedding-3-small")
80
- vectorstore = FAISS.from_texts(texts=texts, embedding=embedding)
81
- return vectorstore
82
-
83
- def get_conversation_chain(vectorstore):
84
- llm = ChatOpenAI(model="gpt-4o",temperature=0.5, max_tokens=2048)
85
-
86
- retriever=vectorstore.as_retriever()
87
-
88
- prompt = hub.pull("rlm/rag-prompt")
89
- # Chain
90
- rag_chain = (
91
- {"context": retriever , "question": RunnablePassthrough()}
92
- | prompt
93
- | llm
94
- )
95
- return rag_chain
96
-
97
- # FILL THE PROMPT FOR THE QUESTION VARIABLE THAT WILL BE USED IN THE RAG PROMPT, ATTENTION NOT CONFUSE WITH THE RAG PROMPT
98
- def fill_promptQ_template(input_variables, template):
99
- prompt = PromptTemplate(input_variables=["BRAND_NAME","BRAND_DESCRIPTION"], template=template)
100
- return prompt.format(BRAND_NAME=input_variables["BRAND_NAME"], BRAND_DESCRIPTION=input_variables["BRAND_DESCRIPTION"])
101
-
102
- def text_to_list(text):
103
- lines = text.replace("- ","").split('\n')
104
-
105
- lines = [line.split() for line in lines]
106
- items = [[' '.join(line[:-1]),line[-1]] for line in lines]
107
-
108
- # Assuming `items` is the list of items
109
- for item in items:
110
- item[1] = re.sub(r'\D', '', item[1])
111
- return items
112
-
113
- def delete_pp(pps):
114
- for pp in pps:
115
- for i in range(len(st.session_state['pp_grouped'])):
116
- if st.session_state['pp_grouped'][i]['name'] == pp:
117
- del st.session_state['pp_grouped'][i]
118
- break
119
-
120
- def display_list_urls():
121
- for index, item in enumerate(st.session_state["urls"]):
122
- emp = st.empty() # Create an empty placeholder
123
- col1, col2 = emp.columns([7, 3]) # Divide the space into two columns
124
-
125
- # Button to delete the entry, placed in the second column
126
- if col2.button("❌", key=f"but{index}"):
127
- temp = st.session_state['parties_prenantes'][index]
128
- delete_pp(temp)
129
- del st.session_state.urls[index]
130
- del st.session_state["parties_prenantes"][index]
131
- st.rerun() # Rerun the app to update the display
132
-
133
- if len(st.session_state.urls) > index:
134
- # Instead of using markdown, use an expander in the first column
135
- with col1.expander(f"Source {index+1}: {item}"):
136
- pp = st.session_state["parties_prenantes"][index]
137
- st.write(pd.DataFrame(pp, columns=["Partie prenante"]))
138
- else:
139
- emp.empty() # Clear the placeholder if the index exceeds the list
140
-
141
- def colored_circle(color):
142
- return f'<span style="display: inline-block; width: 15px; height: 15px; border-radius: 50%; background-color: {color};"></span>'
143
-
144
- def display_list_pps():
145
- for index, item in enumerate(st.session_state["pp_grouped"]):
146
- emp = st.empty()
147
- col1, col2 = emp.columns([7, 3])
148
-
149
- if col2.button("❌", key=f"butp{index}"):
150
-
151
- del st.session_state["pp_grouped"][index]
152
- st.rerun()
153
-
154
- if len(st.session_state["pp_grouped"]) > index:
155
- name = st.session_state["pp_grouped"][index]["name"]
156
- col1.markdown(f'<p>{colored_circle(st.session_state["pp_grouped"][index]["color"])} {st.session_state["pp_grouped"][index]["name"]}</p>',
157
- unsafe_allow_html=True
158
- )
159
- else:
160
- emp.empty()
161
-
162
-
163
-
164
- def extract_pp(docs,input_variables):
165
- template_extraction_PP = """
166
- Objectif : Identifiez toutes les parties prenantes de la marque suivante :
167
-
168
- Le nom de la marque de référence est le suivant : {BRAND_NAME}
169
-
170
- TA RÉPONSE DOIT ÊTRE SOUS FORME DE LISTE DE NOMS DE MARQUES, CHAQUE NOM SUR UNE LIGNE SÉPARÉE.
171
-
172
- """
173
- #don't forget to add the input variables from the maim function
174
-
175
- if docs == None:
176
- return "445"
177
-
178
- #get text chunks
179
- text_chunks = get_doc_chunks(docs)
180
-
181
- #create vectorstore
182
- vectorstore = get_vectorstore_from_docs(text_chunks)
183
-
184
- chain = get_conversation_chain(vectorstore)
185
-
186
- question = fill_promptQ_template(input_variables, template_extraction_PP)
187
-
188
- start = time.perf_counter()
189
- response = chain.invoke(question)
190
-
191
- response_latency = time.perf_counter() - start
192
- # version plus poussée a considérer
193
- # each item in the list is a list with the name of the brand and the similarity percentage
194
- # partie_prenante = text_to_list(response.content)
195
- if "ne sais pas" in response.content:
196
- return "444"
197
-
198
- #calculate impact
199
- nbre_out_tokens = response.response_metadata["token_usage"]["completion_tokens"]
200
- provider = "openai"
201
- model = "gpt-4o"
202
- impact = compute_llm_impacts(
203
- provider=provider,
204
- model_name=model,
205
- output_token_count=nbre_out_tokens,
206
- request_latency=response_latency,
207
- )
208
-
209
- st.session_state["partial_emissions"]["extraction_pp"]["el"] += impact.gwp.value
210
- #version simple
211
- partie_prenante = response.content.replace("- ","").split('\n')
212
- partie_prenante = [item.strip() for item in partie_prenante]
213
-
214
- return partie_prenante
215
-
216
- def generate_random_color():
217
- # Generate random RGB values
218
- r = random.randint(0, 255)
219
- g = random.randint(0, 255)
220
- b = random.randint(0, 255)
221
-
222
- # Convert RGB to hexadecimal
223
- color_hex = '#{:02x}{:02x}{:02x}'.format(r, g, b)
224
-
225
- return color_hex
226
-
227
-
228
- def format_pp_add_viz(pp):
229
- y = 50
230
- x = 50
231
- for i in range(len(st.session_state['pp_grouped'])):
232
- if st.session_state['pp_grouped'][i]['y'] == y and st.session_state['pp_grouped'][i]['x'] == x:
233
- y += 5
234
- if y > 95:
235
- y = 50
236
- x += 5
237
- if st.session_state['pp_grouped'][i]['name'] == pp:
238
- return None
239
- else:
240
- st.session_state['pp_grouped'].append({'name':pp, 'x':x,'y':y, 'color':generate_random_color()})
241
-
242
- def add_pp(new_pp, default_value=50):
243
- new_pp = sorted(new_pp)
244
- new_pp = [item.lower().capitalize().strip() for item in new_pp]
245
- st.session_state['parties_prenantes'].append(new_pp)
246
- for pp in new_pp:
247
- format_pp_add_viz(pp)
248
-
249
- def add_existing_pps(pp,pouvoir,influence):
250
- for i in range(len(st.session_state['pp_grouped'])):
251
- if st.session_state['pp_grouped'][i]['name'] == pp:
252
- st.session_state['pp_grouped'][i]['x'] = influence
253
- st.session_state['pp_grouped'][i]['y'] = pouvoir
254
- return None
255
- st.session_state['pp_grouped'].append({'name':pp, 'x':influence,'y':pouvoir, 'color':generate_random_color()})
256
-
257
- def load_csv(file):
258
- df = pd.read_csv(file)
259
- for index, row in df.iterrows():
260
- add_existing_pps(row['parties prenantes'],row['pouvoir'],row['influence'])
261
-
262
-
263
- def add_pp_input_text():
264
- new_pp = st.text_input("Ajouter une partie prenante")
265
- if st.button("Ajouter",key="add_single_pp"):
266
- format_pp_add_viz(new_pp)
267
-
268
-
269
- def complete_and_verify_url(partial_url):
270
- # Regex pattern for validating a URL
271
- regex = re.compile(
272
- r'^(?:http|ftp)s?://' # http:// or https://
273
- r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,8}\.?|' # domain
274
- r'localhost|' # localhost...
275
- r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
276
- r'(?::\d+)?' # optional port
277
- r'(?:/?|[/?]\S+)$', re.IGNORECASE)
278
-
279
- regex = re.compile(
280
- r'^(?:http|ftp)s?://' # http:// or https://
281
- r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,8}\.?|' # domain name
282
- r'localhost|' # or localhost
283
- r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # or IPv4 address
284
- r'(?::\d+)?' # optional port
285
- r'(?:[/?#][^\s]*)?$', # optional path, query, or fragment
286
- re.IGNORECASE)
287
-
288
- # Complete the URL if it doesn't have http:// or https://
289
- if not partial_url.startswith(('http://', 'https://', 'www.')):
290
- if not partial_url.startswith('www.'):
291
- complete_url = 'https://www.' + partial_url
292
- else:
293
- complete_url = 'https://' + partial_url
294
-
295
- elif partial_url.startswith('www.'):
296
- complete_url = 'https://' + partial_url
297
-
298
- else:
299
- complete_url = partial_url
300
-
301
- # Check if the URL is valid
302
- if re.match(regex, complete_url):
303
- return (True, complete_url)
304
- else:
305
- return (False, complete_url)
306
-
307
- @st.dialog("Conseil IA",width="large")
308
- def show_conseil_ia():
309
- prompt = "Prenant compte les données de l'entreprise (activité, produits, services ...), quelles sont les principales parties prenantes à animer pour une démarche RSE réussie ?"
310
- st.markdown(f"**{prompt}**")
311
- response = st.write_stream(get_response(prompt, "",st.session_state["latest_doc"][0].page_content))
312
- st.warning("Quittez et saisissez une autre URL")
313
-
314
- def display_pp():
315
- if "emission" not in st.session_state:
316
- tracker = EmissionsTracker()
317
- tracker.start()
318
- st.session_state["emission"] = tracker
319
-
320
- load_dotenv()
321
- fire_crawl_api_key = os.getenv("FIRECRAWL_API_KEY")
322
-
323
- #check if brand name and description are already set
324
- if "Nom de la marque" not in st.session_state:
325
- st.session_state["Nom de la marque"] = ""
326
-
327
- #check if urls and partie prenante are already set
328
- if "urls" not in st.session_state:
329
- st.session_state["urls"] = []
330
- if "parties_prenantes" not in st.session_state:
331
- st.session_state['parties_prenantes'] = []
332
- if "pp_grouped" not in st.session_state: #servira pour le plot et la cartographie des parties prenantes, regroupe sans doublons
333
- st.session_state['pp_grouped'] = []
334
- if "latest_doc" not in st.session_state:
335
- st.session_state['latest_doc'] = ""
336
- if "not_pp" not in st.session_state:
337
- st.session_state["not_pp"] = ""
338
-
339
-
340
- st.title("IDENTIFIER ET ANIMER VOS PARTIES PRENANTES")
341
- #set brand name and description
342
- brand_name = st.text_input("Nom de la marque", st.session_state["Nom de la marque"])
343
- st.session_state["Nom de la marque"] = brand_name
344
-
345
- option = st.radio("Source", ("A partir de votre site web", "A partir de vos documents entreprise","A partir de cartographie existante"))
346
-
347
- #if the user chooses to extract from website
348
- if option == "A partir de votre site web":
349
-
350
- url = st.text_input("Ajouter une URL")
351
-
352
- captions = ["L’IA prend en compte uniquement les textes contenus dans les pages web analysées","L’IA prend en compte les textes, les images et les liens URL contenus dans les pages web analysées"]
353
- scraping_option = st.radio("Mode", ("Analyse rapide", "Analyse profonde"),horizontal=True,captions = captions)
354
- #if the user clicks on the button
355
- if st.button("ajouter",key="add_pp"):
356
- st.session_state["not_pp"] = ""
357
- #complete and verify the url
358
- is_valid,url = complete_and_verify_url(url)
359
- if not is_valid:
360
- st.error("URL invalide")
361
- elif url in st.session_state["urls"] :
362
- st.error("URL déjà ajoutée")
363
-
364
- else:
365
- if scraping_option == "Analyse profonde":
366
- with st.spinner("Collecte des données..."):
367
- docs = get_docs_from_website_fc([url],fire_crawl_api_key)
368
- if docs is None:
369
- st.warning("Erreur lors de la collecte des données, 2eme essai avec collecte rapide...")
370
- with st.spinner("2eme essai, collecte rapide..."):
371
- docs = get_docs_from_website([url])
372
-
373
- if scraping_option == "Analyse rapide":
374
- with st.spinner("Collecte des données..."):
375
- docs = get_docs_from_website([url])
376
-
377
- if docs is None:
378
- st.error("Erreur lors de la collecte des données, URL unvalide")
379
- st.session_state["latest_doc"] = ""
380
- else:
381
- # Création de l'expander
382
- st.session_state["partial_emissions"]["Scrapping"]["cc"] = st.session_state["emission"].stop()
383
- st.session_state["latest_doc"] = docs
384
-
385
- with st.spinner("Processing..."):
386
-
387
- #handle the extraction
388
- input_variables = {"BRAND_NAME": brand_name, "BRAND_DESCRIPTION": ""}
389
- partie_prenante = extract_pp(docs, input_variables)
390
-
391
- if "444" in partie_prenante: #444 is the code for no brand found , chosen
392
- st.session_state["not_pp"] = "444"
393
-
394
- elif "445" in partie_prenante: #445 is the code for no website found with the given url
395
- st.error("Aucun site web trouvé avec l'url donnée")
396
- st.session_state["not_pp"] = ""
397
- else:
398
- st.session_state["not_pp"] = ""
399
- partie_prenante = sorted(partie_prenante)
400
- st.session_state["urls"].append(url)
401
- add_pp(partie_prenante)
402
- st.session_state["partial_emissions"]["extraction_pp"]["cc"] = st.session_state["emission"].stop()
403
-
404
-
405
- # alphabet = [ pp[0] for pp in partie_prenante]
406
- # pouvoir = [ 50 for _ in range(len(partie_prenante))]
407
- # df = pd.DataFrame({'partie_prenante': partie_prenante, 'pouvoir': pouvoir, 'code couleur': partie_prenante})
408
- # st.write(df)
409
-
410
- # c = (
411
- # alt.Chart(df)
412
- # .mark_circle(size=300)
413
- # .encode(x="partie_prenante", y=alt.Y("pouvoir",scale=alt.Scale(domain=[0,100])), color="code couleur")
414
- # )
415
- # st.subheader("Vertical Slider")
416
- # age = st.slider("How old are you?", 0, 130, 25)
417
- # st.write("I'm ", age, "years old")
418
-
419
- # disp_vertical_slider(partie_prenante)
420
- # st.altair_chart(c, use_container_width=True)
421
- if option =="A partir de vos documents entreprise":
422
-
423
- uploaded_file = st.file_uploader("Télécharger le fichier PDF", type="pdf")
424
- if uploaded_file is not None:
425
-
426
- if st.button("ajouter",key="add_pp_pdf"):
427
- st.session_state["not_pp"] = ""
428
-
429
- with st.spinner("Processing..."):
430
- file_name = uploaded_file.name
431
- with open(file_name, mode='wb') as w:
432
- w.write(uploaded_file.getvalue())
433
- pdf = PyPDFLoader(file_name)
434
- text = pdf.load()
435
- st.session_state["latest_doc"] = text
436
- input_variables = {"BRAND_NAME": brand_name, "BRAND_DESCRIPTION": ""}
437
- partie_prenante = extract_pp(text, input_variables)
438
-
439
- if "444" in partie_prenante: #444 is the code for no brand found , chosen
440
- st.session_state["not_pp"] = "444"
441
-
442
- elif "445" in partie_prenante: #445 is the code for no website found with the given url
443
- st.error("Aucun site web trouvé avec l'url donnée")
444
- st.session_state["not_pp"] = ""
445
-
446
- else:
447
- st.session_state["not_pp"] = ""
448
- partie_prenante = sorted(partie_prenante)
449
- st.session_state["urls"].append(file_name)
450
- add_pp(partie_prenante)
451
-
452
- if option == "A partir de cartographie existante":
453
- uploaded_file = st.file_uploader("Télécharger le fichier CSV", type="csv")
454
- if uploaded_file is not None:
455
- if st.button("ajouter",key="add_pp_csv"):
456
- file_name = uploaded_file.name
457
- with open(file_name, mode='wb') as w:
458
- w.write(uploaded_file.getvalue())
459
-
460
- try:
461
- load_csv(file_name)
462
- brand_name_from_csv = file_name.split("-")[1]
463
- st.session_state["Nom de la marque"] = brand_name_from_csv
464
- except Exception as e:
465
- st.error("Erreur lors de la lecture du fichier")
466
-
467
-
468
- if st.session_state["not_pp"] == "444":
469
- st.warning("Aucune parties prenantes n'est identifiable sur l'URL fournie. Fournissez une autre URL ou bien cliquez sur le boutton ci-dessous pour un Conseils IA")
470
-
471
- if st.button("Conseil IA"):
472
- show_conseil_ia()
473
- #display docs
474
- if st.session_state["latest_doc"] != "":
475
- with st.expander("Cliquez ici pour éditer et voir le document"):
476
- docs = st.session_state["latest_doc"]
477
- cleaned_text = re.sub(r'\n\n+', '\n\n', docs[0].page_content.strip())
478
- text_value = st.text_area("Modifier le texte ci-dessous:", value=cleaned_text, height=300)
479
- if st.button('Sauvegarder',key="save_doc_fake"):
480
- st.success("Texte sauvegardé avec succès!")
481
-
482
- display_list_urls()
483
- with st.expander("Liste des parties prenantes"):
484
- add_pp_input_text()
485
- display_list_pps()
486
- test_chart()
487
-
488
-
489
-
490
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
partiesprenantes.py CHANGED
@@ -30,7 +30,6 @@ def display_company_selection_for_materiality(data):
30
 
31
  selected_company = st.selectbox('Sélectionnez une entreprise', companies, index=0)
32
 
33
-
34
  # If the default selection is still selected, return None
35
  if selected_company == "Sélectionner l'entreprise engagée à découvrir":
36
  return None
 
30
 
31
  selected_company = st.selectbox('Sélectionnez une entreprise', companies, index=0)
32
 
 
33
  # If the default selection is still selected, return None
34
  if selected_company == "Sélectionner l'entreprise engagée à découvrir":
35
  return None
prompt.py DELETED
@@ -1,135 +0,0 @@
1
- import streamlit as st
2
- from st_copy_to_clipboard import st_copy_to_clipboard
3
- import pandas as pd
4
- from data_manager_bziiit import get_prompts
5
- from langchain_core.messages import AIMessage, HumanMessage
6
- from session import get_rag
7
-
8
- prompts = []
9
- def get_prompts_list():
10
- st.header("Prompts")
11
- prompts = get_prompts()
12
-
13
- # Check if prompts is a list of dictionaries
14
- if isinstance(prompts, list) and all(isinstance(i, dict) for i in prompts):
15
- # Create a DataFrame
16
- df = pd.DataFrame(prompts)
17
- # Check if 'name', 'context', and 'text' are in df columns
18
- if 'name' in df.columns and 'context' in df.columns and 'text' in df.columns:
19
- # Filter prompts with id_context as 'rse' or 'transition'
20
- df = df[df['id_context'].isin([
21
- 'identifier-parties-prenantes-organisation',
22
- 'animer-parties-prenantes-organisation',
23
- 'pour-accelerer-la-demarche-rse',
24
- 'pour-accelerer-la-demarche-transition-ecologique',
25
- 'ressources-humaines'
26
- ])]
27
-
28
- # Extract 'name' from 'context' dictionary
29
- df['context'] = df['context'].apply(lambda x: x.get('name') if isinstance(x, dict) else x)
30
-
31
- # Get first 50 characters from 'text' and add "..." at the end
32
- df['text'] = df['text'].apply(lambda x: x[:50] + "..." if isinstance(x, str) else x)
33
-
34
- # Group by 'context'
35
- grouped = df.groupby('context')
36
-
37
-
38
- for name, group in grouped:
39
- num = 1
40
-
41
- with st.expander(name):
42
- for i, row in group.iterrows():
43
- col1, col3, col4 = st.columns((0.4, 4, 2))
44
- col1.write(num) # index
45
- # col2.write(row['name']) # name
46
- col3.write(row['text']) # text
47
- num += 1
48
-
49
- button_phold = col4.empty() # create a placeholder
50
- but1, but2 = button_phold.columns(2)
51
-
52
- do_action = but1.button('Voir plus', key=f"v{i}")
53
- execute = but2.button('Executer', key=f"e{i}")
54
-
55
- if execute:
56
- st.session_state.chat_history.append(HumanMessage(content=prompts[i]['text']))
57
- st.rerun()
58
- if do_action:
59
- prompt_html = prompts[i]['text'].replace('\n', '<br>')
60
- prompt_metadata = extract_metadata(prompts[i])
61
-
62
- for text in prompt_metadata:
63
- prompt_html = prompt_html.replace(f"{text}", f"<span style='font-weight:bold'>{text}</span>")
64
-
65
- st.html(prompt_html)
66
-
67
- else:
68
- st.write("Data does not contain 'name', 'context', and 'text' fields.")
69
- else:
70
- st.write("Data is not in the expected format (list of dictionaries).")
71
-
72
-
73
- def prompt_execution():
74
- prompts = get_prompts()
75
-
76
- selected_prompt = st.selectbox("Choisissez un prompt", prompts, index=None, format_func=lambda prompt: prompt['name'])
77
- if selected_prompt:
78
- return selected_prompt
79
-
80
- return None
81
-
82
-
83
- def execute_prompt(prompt):
84
- # Initialiser les composants
85
- vectorstore, chain = get_rag()
86
-
87
- prompt_metadata = extract_metadata(prompt)
88
-
89
- prompt['metadata'] = prompt['text']
90
- prompt['html'] = prompt['text'].replace('\n', '<br>')
91
-
92
- if prompt_metadata:
93
- st.info("Données à compléter")
94
-
95
- # Demander à l'utilisateur de fournir des valeurs pour chaque métadonnée extraite
96
- user_inputs = {}
97
- for text in prompt_metadata:
98
- prompt['html'] = prompt['html'].replace(f"{text}", f"<span style='font-weight:bold'>{text}</span>")
99
-
100
- user_input = st.text_input(f"{text}")
101
- user_inputs[text] = user_input # Stocker la valeur de l'entrée utilisateur
102
-
103
- # Remplacer les valeurs par le texte correspondant dans prompt['text']
104
- for key, value in user_inputs.items():
105
- if value:
106
- prompt['html'] = prompt['html'].replace(f"{key}", f"<span style='color:#63ABDF;font-weight:bold' title='{key}'>{value}</span>")
107
- prompt['metadata'] = prompt['metadata'].replace(f"{key}", f"{value}")
108
-
109
- # Afficher les informations du prompt
110
- if prompt_metadata:
111
- st.markdown("---")
112
-
113
- st.html(prompt.get('html', 'No Text Provided'))
114
-
115
- if vectorstore and chain:
116
-
117
- if st.button("Exécuter le prompt"):
118
- with st.spinner("Processing..."):
119
- ambition = chain.invoke(prompt['metadata'])
120
- st.markdown("### Réponse :")
121
- st.markdown(ambition.content)
122
- else:
123
- st.error("RAG non configuré. Veuillez configurer votre RAG pour exécuter le prompt.")
124
-
125
- # Extraire le texte entre crochets dans le prompt
126
- def extract_metadata(prompt):
127
- extracted_text = []
128
- if 'text' in prompt:
129
- extracted_text = [word for word in prompt['text'].split() if word.startswith("[") and word.endswith("]")]
130
-
131
- # Supprimer les doublons et trier les métadonnées extraites
132
- prompt_metadata = list(set(extracted_text))
133
- prompt_metadata.sort(key=extracted_text.index) # Conserver l'ordre d'apparition initial
134
-
135
- return prompt_metadata
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
rag_funcs.py DELETED
@@ -1,14 +0,0 @@
1
- from firecrawl import FireCrawl
2
-
3
-
4
- def get_docs_from_website_fc(urls):
5
- app = FireCrawl()
6
- docs = []
7
- try:
8
- for url in urls:
9
- content = app.scrape_url(url)
10
- docs.append(content["markdown"])
11
- return docs
12
- except Exception as e:
13
- return None
14
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1,4 +1,4 @@
1
- streamlit==1.37.1
2
  pandas
3
  requests
4
  folium
@@ -12,33 +12,3 @@ plotly
12
  matplotlib
13
  openai
14
  beautifulsoup4
15
- python-dotenv
16
- faiss-cpu
17
- langchain
18
- huggingface-hub
19
- PyPDF2
20
- pypdf
21
- tiktoken
22
- openai
23
- InstructorEmbedding
24
- sentence-transformers==2.2.2
25
- langchainhub
26
- plotly==5.22.0
27
- altair
28
- altair[all]
29
- streamlit-vertical-slider
30
- streamlit_toggle
31
- langchain_experimental
32
- streamlit_draggable_list
33
- streamlit-highcharts
34
- pdfkit
35
- kaleido
36
- langchain-core
37
- langchain-mistralai
38
- firecrawl-py
39
- st_copy_to_clipboard
40
- fpdf2
41
- markdown2
42
- markdown
43
- codecarbon
44
- ecologits==0.1.7
 
1
+ streamlit
2
  pandas
3
  requests
4
  folium
 
12
  matplotlib
13
  openai
14
  beautifulsoup4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
session.py DELETED
@@ -1,31 +0,0 @@
1
- import streamlit as st
2
-
3
- def get_rag():
4
- if 'vectorstore' in st.session_state and 'chain' in st.session_state:
5
- vectorstore = st.session_state['vectorstore']
6
- chain = st.session_state['chain']
7
-
8
- return vectorstore, chain
9
- else:
10
- return None, None
11
-
12
- def set_rag(vectorstore, chain):
13
- st.session_state['vectorstore'] = vectorstore
14
- st.session_state['chain'] = chain
15
-
16
- def set_partie_prenante(partie_prenante):
17
- if "partie_prenante" not in st.session_state:
18
- st.session_state['partie_prenante'] = []
19
- st.session_state['partie_prenante'] = partie_prenante
20
-
21
- def get_partie_prenante():
22
- if 'parties_prenante' in st.session_state:
23
- return st.session_state['partie_prenante']
24
- else:
25
- return None
26
-
27
- def get_parties_prenantes():
28
- if 'parties_prenantes' in st.session_state:
29
- return st.session_state['parties_prenantes']
30
- else:
31
- return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
st_hc/__init__.py DELETED
@@ -1,2562 +0,0 @@
1
- from pathlib import Path
2
- from typing import Optional
3
-
4
- import streamlit as st
5
- import streamlit.components.v1 as components
6
-
7
- SAMPLE={
8
-
9
- "title": {
10
- "text": 'U.S Solar Employment Growth by Job Category, 2010-2020'
11
- },
12
-
13
- "subtitle": {
14
- "text": 'Source: <a href="https://irecusa.org/programs/solar-jobs-census/" target="_blank">IREC</a>'
15
- },
16
-
17
- "yAxis": {
18
- "title": {
19
- "text": 'Number of Employees'
20
- }
21
- },
22
-
23
- "xAxis": {
24
- "accessibility": {
25
- "rangeDescription": 'Range: 2010 to 2020'
26
- }
27
- },
28
-
29
- "legend": {
30
- "layout": 'vertical',
31
- "align": 'right',
32
- "verticalAlign": 'middle'
33
- },
34
-
35
- "plotOptions": {
36
- "series": {
37
- "label": {
38
- "connectorAllowed": False
39
- },
40
- "pointStart": 2010
41
- }
42
- },
43
-
44
- "series": [{
45
- "name": 'Installation & Developers',
46
- "data": [43934, 48656, 65165, 81827, 112143, 142383,
47
- 171533, 165174, 155157, 161454, 154610]
48
- }, {
49
- "name": 'Manufacturing',
50
- "data": [24916, 37941, 29742, 29851, 32490, 30282,
51
- 38121, 36885, 33726, 34243, 31050]
52
- }, {
53
- "name": 'Sales & Distribution',
54
- "data": [11744, 30000, 16005, 19771, 20185, 24377,
55
- 32147, 30912, 29243, 29213, 25663]
56
- }, {
57
- "name": 'Operations & Maintenance',
58
- "data": ["null", "null", "null", "null", "null", "null", "null",
59
- "null", 11164, 11218, 10077]
60
- }, {
61
- "name": 'Other',
62
- "data": [21908, 5548, 8105, 11248, 8989, 11816, 18274,
63
- 17300, 13053, 11906, 10073]
64
- }],
65
-
66
- "responsive": {
67
- "rules": [{
68
- "condition": {
69
- "maxWidth": 500
70
- },
71
- "chartOptions": {
72
- "legend": {
73
- "layout": 'horizontal',
74
- "align": 'center',
75
- "verticalAlign": 'bottom'
76
- }
77
- }
78
- }]
79
- }
80
-
81
- }
82
- SAMPLE2={
83
-
84
- "chart": {
85
- "type": 'streamgraph',
86
- "marginBottom": 30,
87
- "zoomType": 'x'
88
- },
89
-
90
- "title": {
91
- "floating": True,
92
- "align": 'left',
93
- "text": 'Winter Olympic Medal Wins'
94
- },
95
- "subtitle": {
96
- "floating": True,
97
- "align": 'left',
98
- "y": 30,
99
- "text": 'Source: <a href="https://www.sports-reference.com/olympics/winter/1924/">sports-reference.com</a>'
100
- },
101
-
102
- "xAxis": {
103
- "maxPadding": 0,
104
- "type": 'category',
105
- "crosshair": True,
106
- "categories": [
107
- '',
108
- '1924 Chamonix',
109
- '1928 St. Moritz',
110
- '1932 Lake Placid',
111
- '1936 Garmisch-Partenkirchen',
112
- '1940 <i>Cancelled (Sapporo)</i>',
113
- '1944 <i>Cancelled (Cortina d\'Ampezzo)</i>',
114
- '1948 St. Moritz',
115
- '1952 Oslo',
116
- '1956 Cortina d\'Ampezzo',
117
- '1960 Squaw Valley',
118
- '1964 Innsbruck',
119
- '1968 Grenoble',
120
- '1972 Sapporo',
121
- '1976 Innsbruck',
122
- '1980 Lake Placid',
123
- '1984 Sarajevo',
124
- '1988 Calgary',
125
- '1992 Albertville',
126
- '1994 Lillehammer',
127
- '1998 Nagano',
128
- '2002 Salt Lake City',
129
- '2006 Turin',
130
- '2010 Vancouver',
131
- '2014 Sochi'
132
- ],
133
- "labels": {
134
- "align": 'left',
135
- "reserveSpace": False,
136
- "rotation": 270
137
- },
138
- "lineWidth": 0,
139
- "margin": 20,
140
- "tickWidth": 0
141
- },
142
-
143
- "yAxis": {
144
- "visible": False,
145
- "startOnTick": False,
146
- "endOnTick": False
147
- },
148
-
149
- "legend": {
150
- "enabled": False
151
- },
152
-
153
- "annotations": [{
154
- "labels": [{
155
- "point": {
156
- "x": 5.5,
157
- "xAxis": 0,
158
- "y": 30,
159
- "yAxis": 0
160
- },
161
- "text": 'Cancelled<br>during<br>World War II'
162
- }, {
163
- "point": {
164
- "x": 18,
165
- "xAxis": 0,
166
- "y": 90,
167
- "yAxis": 0
168
- },
169
- "text": 'Soviet Union fell,<br>Germany united'
170
- }],
171
- "labelOptions": {
172
- "backgroundColor": 'rgba(255,255,255,0.5)',
173
- "borderColor": 'silver'
174
- }
175
- }],
176
-
177
- "plotOptions": {
178
- "series": {
179
- "label": {
180
- "minFontSize": 5,
181
- "maxFontSize": 15,
182
- "style": {
183
- "color": 'rgba(255,255,255,0.75)'
184
- }
185
- },
186
- "accessibility": {
187
- "exposeAsGroupOnly": True
188
- }
189
- }
190
- },
191
- "series": [{
192
- "name": "Finland",
193
- "data": [
194
- 0, 11, 4, 3, 6, 0, 0, 6, 9, 7, 8, 10, 5, 5, 7, 9, 13, 7,
195
- 7, 6, 12, 7, 9, 5, 5
196
- ]
197
- }, {
198
- "name": "Austria",
199
- "data": [
200
- 0, 3, 4, 2, 4, 0, 0, 8, 8, 11, 6, 12, 11, 5, 6, 7, 1, 10,
201
- 21, 9, 17, 17, 23, 16, 17
202
- ]
203
- }, {
204
- "name": "Sweden",
205
- "data": [
206
- 0, 2, 5, 3, 7, 0, 0, 10, 4, 10, 7, 7, 8, 4, 2, 4, 8, 6, 4,
207
- 3, 3, 7, 14, 11, 15
208
- ]
209
- }, {
210
- "name": "Norway",
211
- "data": [
212
- 0, 17, 15, 10, 15, 0, 0, 10, 16, 4, 6, 15, 14, 12, 7, 10,
213
- 9, 5, 20, 26, 25, 25, 19, 23, 26
214
- ]
215
- }, {
216
- "name": "U.S.",
217
- "data": [
218
- 0, 4, 6, 12, 4, 0, 0, 9, 11, 7, 10, 7, 7, 8, 10, 12, 8, 6,
219
- 11, 13, 13, 34, 25, 37, 28
220
- ]
221
- }, {
222
- "name": "East Germany",
223
- "data": [
224
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 14, 19, 23, 24, 25,
225
- 0, 0, 0, 0, 0, 0, 0
226
- ]
227
- }, {
228
- "name": "West Germany",
229
- "data": [
230
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 5, 10, 5, 4, 8, 0,
231
- 0, 0, 0, 0, 0, 0
232
- ]
233
- }, {
234
- "name": "Germany",
235
- "data": [
236
- 0, 0, 1, 2, 6, 0, 0, 0, 7, 2, 8, 9, 0, 0, 0, 0, 0, 0, 26,
237
- 24, 29, 36, 29, 30, 19
238
- ]
239
- }, {
240
- "name": "Netherlands",
241
- "data": [
242
- 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, 2, 9, 9, 6, 4, 0, 7, 4,
243
- 4, 11, 8, 9, 8, 24
244
- ]
245
- }, {
246
- "name": "Italy",
247
- "data": [
248
- 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 1, 4, 4, 5, 4, 2, 2, 5, 14,
249
- 20, 10, 13, 11, 5, 8
250
- ]
251
- }, {
252
- "name": "Canada",
253
- "data": [
254
- 0, 1, 1, 7, 1, 0, 0, 3, 2, 3, 4, 3, 3, 1, 3, 2, 4, 5, 7,
255
- 13, 15, 17, 24, 26, 25
256
- ]
257
- }, {
258
- "name": "Switzerland",
259
- "data": [
260
- 0, 3, 1, 1, 3, 0, 0, 10, 2, 6, 2, 0, 6, 10, 5, 5, 5, 15,
261
- 3, 9, 7, 11, 14, 9, 11
262
- ]
263
- }, {
264
- "name": "Great Britain",
265
- "data": [
266
- 0, 4, 1, 0, 3, 0, 0, 2, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0,
267
- 2, 1, 2, 1, 1, 4
268
- ]
269
- }, {
270
- "name": "France",
271
- "data": [
272
- 0, 3, 1, 1, 1, 0, 0, 5, 1, 0, 3, 7, 9, 3, 1, 1, 3, 2, 9,
273
- 5, 8, 11, 9, 11, 15
274
- ]
275
- }, {
276
- "name": "Hungary",
277
- "data": [
278
- 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
279
- 0, 0, 0, 0, 0, 0
280
- ]
281
- }, {
282
- "name": "Unified Team",
283
- "data": [
284
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23,
285
- 0, 0, 0, 0, 0, 0
286
- ]
287
- }, {
288
- "name": "Soviet Union",
289
- "data": [
290
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 21, 25, 13, 16, 27, 22, 25,
291
- 29, 0, 0, 0, 0, 0, 0, 0
292
- ]
293
- }, {
294
- "name": "Russia",
295
- "data": [
296
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
297
- 23, 18, 13, 22, 15, 33
298
- ]
299
- }, {
300
- "name": "Japan",
301
- "data": [
302
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 1, 1, 1, 7,
303
- 5, 10, 2, 1, 5, 8
304
- ]
305
- }, {
306
- "name": "Czechoslovakia",
307
- "data": [
308
- 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 4, 3, 1, 1, 6, 3, 3,
309
- 0, 0, 0, 0, 0, 0
310
- ]
311
- }, {
312
- "name": "Poland",
313
- "data": [
314
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0,
315
- 0, 0, 2, 2, 6, 6
316
- ]
317
- }, {
318
- "name": "Spain",
319
- "data": [
320
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
321
- 0, 0, 0, 0, 0, 0
322
- ]
323
- }, {
324
- "name": "China",
325
- "data": [
326
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
327
- 3, 8, 8, 11, 11, 9
328
- ]
329
- }, {
330
- "name": "South Korea",
331
- "data": [
332
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
333
- 6, 6, 4, 11, 14, 8
334
- ]
335
- }, {
336
- "name": "Czech Republic",
337
- "data": [
338
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
339
- 0, 3, 3, 4, 6, 8
340
- ]
341
- }, {
342
- "name": "Belarus",
343
- "data": [
344
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
345
- 2, 2, 1, 1, 3, 6
346
- ]
347
- }, {
348
- "name": "Kazakhstan",
349
- "data": [
350
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
351
- 3, 2, 0, 0, 1, 1
352
- ]
353
- }, {
354
- "name": "Bulgaria",
355
- "data": [
356
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
357
- 0, 1, 3, 1, 0, 0
358
- ]
359
- }, {
360
- "name": "Denmark",
361
- "data": [
362
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
363
- 0, 1, 0, 0, 0, 0
364
- ]
365
- }, {
366
- "name": "Ukraine",
367
- "data": [
368
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
369
- 2, 1, 0, 2, 0, 2
370
- ]
371
- }, {
372
- "name": "Australia",
373
- "data": [
374
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
375
- 1, 1, 2, 2, 3, 3
376
- ]
377
- }, {
378
- "name": "Belgium",
379
- "data": [
380
- 0, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
381
- 0, 1, 0, 0, 0, 0
382
- ]
383
- }, {
384
- "name": "Romania",
385
- "data": [
386
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
387
- 0, 0, 0, 0, 0, 0
388
- ]
389
- }, {
390
- "name": "Liechtenstein",
391
- "data": [
392
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 2, 1, 0,
393
- 0, 0, 0, 0, 0, 0
394
- ]
395
- }, {
396
- "name": "Yugoslavia",
397
- "data": [
398
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 0,
399
- 0, 0, 0, 0, 0, 0
400
- ]
401
- }, {
402
- "name": "Luxembourg",
403
- "data": [
404
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
405
- 0, 0, 0, 0, 0, 0
406
- ]
407
- }, {
408
- "name": "New Zealand",
409
- "data": [
410
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
411
- 0, 0, 0, 0, 0, 0
412
- ]
413
- }, {
414
- "name": "North Korea",
415
- "data": [
416
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1,
417
- 0, 0, 0, 0, 0, 0
418
- ]
419
- }, {
420
- "name": "Slovakia",
421
- "data": [
422
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
423
- 0, 0, 0, 1, 3, 1
424
- ]
425
- }, {
426
- "name": "Croatia",
427
- "data": [
428
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
429
- 0, 0, 4, 3, 3, 1
430
- ]
431
- }, {
432
- "name": "Slovenia",
433
- "data": [
434
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
435
- 3, 0, 1, 0, 3, 8
436
- ]
437
- }, {
438
- "name": "Latvia",
439
- "data": [
440
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
441
- 0, 0, 0, 1, 2, 4
442
- ]
443
- }, {
444
- "name": "Estonia",
445
- "data": [
446
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
447
- 0, 0, 3, 3, 1, 0
448
- ]
449
- }, {
450
- "name": "Uzbekistan",
451
- "data": [
452
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
453
- 1, 0, 0, 0, 0, 0
454
- ]
455
- }],
456
-
457
- "exporting": {
458
- "sourceWidth": 800,
459
- "sourceHeight": 600
460
- }
461
-
462
- }
463
- SAMPLE3={
464
-
465
- "chart": {
466
- "type": 'bubble',
467
- "plotBorderWidth": 1,
468
- "zoomType": 'xy'
469
- },
470
-
471
- "title": {
472
- "text": 'Highcharts bubbles with radial gradient fill'
473
- },
474
-
475
- "xAxis": {
476
- "gridLineWidth": 1,
477
- "accessibility": {
478
- "rangeDescription": 'Range: 0 to 100.'
479
- }
480
- },
481
-
482
- "yAxis": {
483
- "startOnTick": False,
484
- "endOnTick": False,
485
- "accessibility": {
486
- "rangeDescription": 'Range: 0 to 100.'
487
- }
488
- },
489
-
490
- "series": [{
491
- "data": [
492
- [9, 81, 63],
493
- [98, 5, 89],
494
- [51, 50, 73],
495
- [41, 22, 14],
496
- [58, 24, 20],
497
- [78, 37, 34],
498
- [55, 56, 53],
499
- [18, 45, 70],
500
- [42, 44, 28],
501
- [3, 52, 59],
502
- [31, 18, 97],
503
- [79, 91, 63],
504
- [93, 23, 23],
505
- [44, 83, 22]
506
- ],
507
- "marker": {
508
- "fillColor": {
509
- "radialGradient": { "cx": 0.4, "cy": 0.3, "r": 0.7 },
510
- "stops": [
511
- [0, 'rgba(255,255,255,0.5)'],
512
- [1, 'rgba(124,181,236,0.5)']
513
- ]
514
- }
515
- }
516
- }, {
517
- "data": [
518
- [42, 38, 20],
519
- [6, 18, 1],
520
- [1, 93, 55],
521
- [57, 2, 90],
522
- [80, 76, 22],
523
- [11, 74, 96],
524
- [88, 56, 10],
525
- [30, 47, 49],
526
- [57, 62, 98],
527
- [4, 16, 16],
528
- [46, 10, 11],
529
- [22, 87, 89],
530
- [57, 91, 82],
531
- [45, 15, 98]
532
- ],
533
- "marker": {
534
- "fillColor": {
535
- "radialGradient": { "cx": 0.4, "cy": 0.3, "r": 0.7 },
536
- "stops": [
537
- [0, 'rgba(255,255,255,0.5)'],
538
- [1, 'rgba(67,67,72,0.5)']
539
- ]
540
- }
541
- }
542
- }]
543
-
544
- }
545
- SAMPLE4={
546
-
547
- "chart":{
548
- "type":"packedbubble",
549
- "height":"100%"
550
- },
551
- "title":{
552
- "text":"Carbon emissions around the world (2014)"
553
- },
554
- "tooltip":{
555
- "useHTML":True,
556
- "pointFormat":"<b>{point.name}:</b> {point.value}m CO<sub>2</sub>"
557
- },
558
- "plotOptions":{
559
- "packedbubble":{
560
- "minSize":"20%",
561
- "maxSize":"100%",
562
- "zMin":0,
563
- "zMax":1000,
564
- "layoutAlgorithm":{
565
- "gravitationalConstant":0.05,
566
- "splitSeries":True,
567
- "seriesInteraction":False,
568
- "dragBetweenSeries":True,
569
- "parentNodeLimit":True
570
- },
571
- "dataLabels":{
572
- "enabled":True,
573
- "format":"{point.name}",
574
- "filter":{
575
- "property":"y",
576
- "operator":">",
577
- "value":250
578
- },
579
- "style":{
580
- "color":"black",
581
- "textOutline":"none",
582
- "fontWeight":"normal"
583
- }
584
- }
585
- }
586
- },
587
- "series":[
588
- {
589
- "name":"Europe",
590
- "data":[
591
- {
592
- "name":"Germany",
593
- "value":767.1
594
- },
595
- {
596
- "name":"Croatia",
597
- "value":20.7
598
- },
599
- {
600
- "name":"Belgium",
601
- "value":97.2
602
- },
603
- {
604
- "name":"Czech Republic",
605
- "value":111.7
606
- },
607
- {
608
- "name":"Netherlands",
609
- "value":158.1
610
- },
611
- {
612
- "name":"Spain",
613
- "value":241.6
614
- },
615
- {
616
- "name":"Ukraine",
617
- "value":249.1
618
- },
619
- {
620
- "name":"Poland",
621
- "value":298.1
622
- },
623
- {
624
- "name":"France",
625
- "value":323.7
626
- },
627
- {
628
- "name":"Romania",
629
- "value":78.3
630
- },
631
- {
632
- "name":"United Kingdom",
633
- "value":415.4
634
- },
635
- {
636
- "name":"Turkey",
637
- "value":353.2
638
- },
639
- {
640
- "name":"Italy",
641
- "value":337.6
642
- },
643
- {
644
- "name":"Greece",
645
- "value":71.1
646
- },
647
- {
648
- "name":"Austria",
649
- "value":69.8
650
- },
651
- {
652
- "name":"Belarus",
653
- "value":67.7
654
- },
655
- {
656
- "name":"Serbia",
657
- "value":59.3
658
- },
659
- {
660
- "name":"Finland",
661
- "value":54.8
662
- },
663
- {
664
- "name":"Bulgaria",
665
- "value":51.2
666
- },
667
- {
668
- "name":"Portugal",
669
- "value":48.3
670
- },
671
- {
672
- "name":"Norway",
673
- "value":44.4
674
- },
675
- {
676
- "name":"Sweden",
677
- "value":44.3
678
- },
679
- {
680
- "name":"Hungary",
681
- "value":43.7
682
- },
683
- {
684
- "name":"Switzerland",
685
- "value":40.2
686
- },
687
- {
688
- "name":"Denmark",
689
- "value":40
690
- },
691
- {
692
- "name":"Slovakia",
693
- "value":34.7
694
- },
695
- {
696
- "name":"Ireland",
697
- "value":34.6
698
- },
699
- {
700
- "name":"Croatia",
701
- "value":20.7
702
- },
703
- {
704
- "name":"Estonia",
705
- "value":19.4
706
- },
707
- {
708
- "name":"Slovenia",
709
- "value":16.7
710
- },
711
- {
712
- "name":"Lithuania",
713
- "value":12.3
714
- },
715
- {
716
- "name":"Luxembourg",
717
- "value":10.4
718
- },
719
- {
720
- "name":"Macedonia",
721
- "value":9.5
722
- },
723
- {
724
- "name":"Moldova",
725
- "value":7.8
726
- },
727
- {
728
- "name":"Latvia",
729
- "value":7.5
730
- },
731
- {
732
- "name":"Cyprus",
733
- "value":7.2
734
- }
735
- ]
736
- },
737
- {
738
- "name":"Africa",
739
- "data":[
740
- {
741
- "name":"Senegal",
742
- "value":8.2
743
- },
744
- {
745
- "name":"Cameroon",
746
- "value":9.2
747
- },
748
- {
749
- "name":"Zimbabwe",
750
- "value":13.1
751
- },
752
- {
753
- "name":"Ghana",
754
- "value":14.1
755
- },
756
- {
757
- "name":"Kenya",
758
- "value":14.1
759
- },
760
- {
761
- "name":"Sudan",
762
- "value":17.3
763
- },
764
- {
765
- "name":"Tunisia",
766
- "value":24.3
767
- },
768
- {
769
- "name":"Angola",
770
- "value":25
771
- },
772
- {
773
- "name":"Libya",
774
- "value":50.6
775
- },
776
- {
777
- "name":"Ivory Coast",
778
- "value":7.3
779
- },
780
- {
781
- "name":"Morocco",
782
- "value":60.7
783
- },
784
- {
785
- "name":"Ethiopia",
786
- "value":8.9
787
- },
788
- {
789
- "name":"United Republic of Tanzania",
790
- "value":9.1
791
- },
792
- {
793
- "name":"Nigeria",
794
- "value":93.9
795
- },
796
- {
797
- "name":"South Africa",
798
- "value":392.7
799
- },
800
- {
801
- "name":"Egypt",
802
- "value":225.1
803
- },
804
- {
805
- "name":"Algeria",
806
- "value":141.5
807
- }
808
- ]
809
- },
810
- {
811
- "name":"Oceania",
812
- "data":[
813
- {
814
- "name":"Australia",
815
- "value":409.4
816
- },
817
- {
818
- "name":"New Zealand",
819
- "value":34.1
820
- },
821
- {
822
- "name":"Papua New Guinea",
823
- "value":7.1
824
- }
825
- ]
826
- },
827
- {
828
- "name":"North America",
829
- "data":[
830
- {
831
- "name":"Costa Rica",
832
- "value":7.6
833
- },
834
- {
835
- "name":"Honduras",
836
- "value":8.4
837
- },
838
- {
839
- "name":"Jamaica",
840
- "value":8.3
841
- },
842
- {
843
- "name":"Panama",
844
- "value":10.2
845
- },
846
- {
847
- "name":"Guatemala",
848
- "value":12
849
- },
850
- {
851
- "name":"Dominican Republic",
852
- "value":23.4
853
- },
854
- {
855
- "name":"Cuba",
856
- "value":30.2
857
- },
858
- {
859
- "name":"USA",
860
- "value":5334.5
861
- },
862
- {
863
- "name":"Canada",
864
- "value":566
865
- },
866
- {
867
- "name":"Mexico",
868
- "value":456.3
869
- }
870
- ]
871
- },
872
- {
873
- "name":"South America",
874
- "data":[
875
- {
876
- "name":"El Salvador",
877
- "value":7.2
878
- },
879
- {
880
- "name":"Uruguay",
881
- "value":8.1
882
- },
883
- {
884
- "name":"Bolivia",
885
- "value":17.8
886
- },
887
- {
888
- "name":"Trinidad and Tobago",
889
- "value":34
890
- },
891
- {
892
- "name":"Ecuador",
893
- "value":43
894
- },
895
- {
896
- "name":"Chile",
897
- "value":78.6
898
- },
899
- {
900
- "name":"Peru",
901
- "value":52
902
- },
903
- {
904
- "name":"Colombia",
905
- "value":74.1
906
- },
907
- {
908
- "name":"Brazil",
909
- "value":501.1
910
- },
911
- {
912
- "name":"Argentina",
913
- "value":199
914
- },
915
- {
916
- "name":"Venezuela",
917
- "value":195.2
918
- }
919
- ]
920
- },
921
- {
922
- "name":"Asia",
923
- "data":[
924
- {
925
- "name":"Nepal",
926
- "value":6.5
927
- },
928
- {
929
- "name":"Georgia",
930
- "value":6.5
931
- },
932
- {
933
- "name":"Brunei Darussalam",
934
- "value":7.4
935
- },
936
- {
937
- "name":"Kyrgyzstan",
938
- "value":7.4
939
- },
940
- {
941
- "name":"Afghanistan",
942
- "value":7.9
943
- },
944
- {
945
- "name":"Myanmar",
946
- "value":9.1
947
- },
948
- {
949
- "name":"Mongolia",
950
- "value":14.7
951
- },
952
- {
953
- "name":"Sri Lanka",
954
- "value":16.6
955
- },
956
- {
957
- "name":"Bahrain",
958
- "value":20.5
959
- },
960
- {
961
- "name":"Yemen",
962
- "value":22.6
963
- },
964
- {
965
- "name":"Jordan",
966
- "value":22.3
967
- },
968
- {
969
- "name":"Lebanon",
970
- "value":21.1
971
- },
972
- {
973
- "name":"Azerbaijan",
974
- "value":31.7
975
- },
976
- {
977
- "name":"Singapore",
978
- "value":47.8
979
- },
980
- {
981
- "name":"Hong Kong",
982
- "value":49.9
983
- },
984
- {
985
- "name":"Syria",
986
- "value":52.7
987
- },
988
- {
989
- "name":"DPR Korea",
990
- "value":59.9
991
- },
992
- {
993
- "name":"Israel",
994
- "value":64.8
995
- },
996
- {
997
- "name":"Turkmenistan",
998
- "value":70.6
999
- },
1000
- {
1001
- "name":"Oman",
1002
- "value":74.3
1003
- },
1004
- {
1005
- "name":"Qatar",
1006
- "value":88.8
1007
- },
1008
- {
1009
- "name":"Philippines",
1010
- "value":96.9
1011
- },
1012
- {
1013
- "name":"Kuwait",
1014
- "value":98.6
1015
- },
1016
- {
1017
- "name":"Uzbekistan",
1018
- "value":122.6
1019
- },
1020
- {
1021
- "name":"Iraq",
1022
- "value":139.9
1023
- },
1024
- {
1025
- "name":"Pakistan",
1026
- "value":158.1
1027
- },
1028
- {
1029
- "name":"Vietnam",
1030
- "value":190.2
1031
- },
1032
- {
1033
- "name":"United Arab Emirates",
1034
- "value":201.1
1035
- },
1036
- {
1037
- "name":"Malaysia",
1038
- "value":227.5
1039
- },
1040
- {
1041
- "name":"Kazakhstan",
1042
- "value":236.2
1043
- },
1044
- {
1045
- "name":"Thailand",
1046
- "value":272
1047
- },
1048
- {
1049
- "name":"Taiwan",
1050
- "value":276.7
1051
- },
1052
- {
1053
- "name":"Indonesia",
1054
- "value":453
1055
- },
1056
- {
1057
- "name":"Saudi Arabia",
1058
- "value":494.8
1059
- },
1060
- {
1061
- "name":"Japan",
1062
- "value":1278.9
1063
- },
1064
- {
1065
- "name":"China",
1066
- "value":10540.8
1067
- },
1068
- {
1069
- "name":"India",
1070
- "value":2341.9
1071
- },
1072
- {
1073
- "name":"Russia",
1074
- "value":1766.4
1075
- },
1076
- {
1077
- "name":"Iran",
1078
- "value":618.2
1079
- },
1080
- {
1081
- "name":"Korea",
1082
- "value":610.1
1083
- }
1084
- ]
1085
- }
1086
- ]
1087
- }
1088
- SAMPLE5={
1089
- "chart":{
1090
- "type":"column"
1091
- },
1092
- "title":{
1093
- "align":"left",
1094
- "text":"Browser market shares. January, 2022"
1095
- },
1096
- "subtitle":{
1097
- "align":"left",
1098
- "text":"Click the columns to view versions. Source: <a href=\"http://statcounter.com\" target=\"_blank\">statcounter.com</a>"
1099
- },
1100
- "accessibility":{
1101
- "announceNewData":{
1102
- "enabled":True
1103
- }
1104
- },
1105
- "xAxis":{
1106
- "type":"category"
1107
- },
1108
- "yAxis":{
1109
- "title":{
1110
- "text":"Total percent market share"
1111
- }
1112
- },
1113
- "legend":{
1114
- "enabled":False
1115
- },
1116
- "plotOptions":{
1117
- "series":{
1118
- "borderWidth":0,
1119
- "dataLabels":{
1120
- "enabled":True,
1121
- "format":"{point.y:.1f}%"
1122
- }
1123
- }
1124
- },
1125
- "tooltip":{
1126
- "headerFormat":"<span style=\"font-size:11px\">{series.name}</span><br>",
1127
- "pointFormat":"<span style=\"color:{point.color}\">{point.name}</span>: <b>{point.y:.2f}%</b> of total<br/>"
1128
- },
1129
- "series":[
1130
- {
1131
- "name":"Browsers",
1132
- "colorByPoint":True,
1133
- "data":[
1134
- {
1135
- "name":"Chrome",
1136
- "y":63.06,
1137
- "drilldown":"Chrome"
1138
- },
1139
- {
1140
- "name":"Safari",
1141
- "y":19.84,
1142
- "drilldown":"Safari"
1143
- },
1144
- {
1145
- "name":"Firefox",
1146
- "y":4.18,
1147
- "drilldown":"Firefox"
1148
- },
1149
- {
1150
- "name":"Edge",
1151
- "y":4.12,
1152
- "drilldown":"Edge"
1153
- },
1154
- {
1155
- "name":"Opera",
1156
- "y":2.33,
1157
- "drilldown":"Opera"
1158
- },
1159
- {
1160
- "name":"Internet Explorer",
1161
- "y":0.45,
1162
- "drilldown":"Internet Explorer"
1163
- },
1164
- {
1165
- "name":"Other",
1166
- "y":1.582,
1167
- "drilldown":"null"
1168
- }
1169
- ]
1170
- }
1171
- ],
1172
- "drilldown":{
1173
- "breadcrumbs":{
1174
- "position":{
1175
- "align":"right"
1176
- }
1177
- },
1178
- "series":[
1179
- {
1180
- "name":"Chrome",
1181
- "id":"Chrome",
1182
- "data":[
1183
- [
1184
- "v65.0",
1185
- 0.1
1186
- ],
1187
- [
1188
- "v64.0",
1189
- 1.3
1190
- ],
1191
- [
1192
- "v63.0",
1193
- 53.02
1194
- ],
1195
- [
1196
- "v62.0",
1197
- 1.4
1198
- ],
1199
- [
1200
- "v61.0",
1201
- 0.88
1202
- ],
1203
- [
1204
- "v60.0",
1205
- 0.56
1206
- ],
1207
- [
1208
- "v59.0",
1209
- 0.45
1210
- ],
1211
- [
1212
- "v58.0",
1213
- 0.49
1214
- ],
1215
- [
1216
- "v57.0",
1217
- 0.32
1218
- ],
1219
- [
1220
- "v56.0",
1221
- 0.29
1222
- ],
1223
- [
1224
- "v55.0",
1225
- 0.79
1226
- ],
1227
- [
1228
- "v54.0",
1229
- 0.18
1230
- ],
1231
- [
1232
- "v51.0",
1233
- 0.13
1234
- ],
1235
- [
1236
- "v49.0",
1237
- 2.16
1238
- ],
1239
- [
1240
- "v48.0",
1241
- 0.13
1242
- ],
1243
- [
1244
- "v47.0",
1245
- 0.11
1246
- ],
1247
- [
1248
- "v43.0",
1249
- 0.17
1250
- ],
1251
- [
1252
- "v29.0",
1253
- 0.26
1254
- ]
1255
- ]
1256
- },
1257
- {
1258
- "name":"Firefox",
1259
- "id":"Firefox",
1260
- "data":[
1261
- [
1262
- "v58.0",
1263
- 1.02
1264
- ],
1265
- [
1266
- "v57.0",
1267
- 7.36
1268
- ],
1269
- [
1270
- "v56.0",
1271
- 0.35
1272
- ],
1273
- [
1274
- "v55.0",
1275
- 0.11
1276
- ],
1277
- [
1278
- "v54.0",
1279
- 0.1
1280
- ],
1281
- [
1282
- "v52.0",
1283
- 0.95
1284
- ],
1285
- [
1286
- "v51.0",
1287
- 0.15
1288
- ],
1289
- [
1290
- "v50.0",
1291
- 0.1
1292
- ],
1293
- [
1294
- "v48.0",
1295
- 0.31
1296
- ],
1297
- [
1298
- "v47.0",
1299
- 0.12
1300
- ]
1301
- ]
1302
- },
1303
- {
1304
- "name":"Internet Explorer",
1305
- "id":"Internet Explorer",
1306
- "data":[
1307
- [
1308
- "v11.0",
1309
- 6.2
1310
- ],
1311
- [
1312
- "v10.0",
1313
- 0.29
1314
- ],
1315
- [
1316
- "v9.0",
1317
- 0.27
1318
- ],
1319
- [
1320
- "v8.0",
1321
- 0.47
1322
- ]
1323
- ]
1324
- },
1325
- {
1326
- "name":"Safari",
1327
- "id":"Safari",
1328
- "data":[
1329
- [
1330
- "v11.0",
1331
- 3.39
1332
- ],
1333
- [
1334
- "v10.1",
1335
- 0.96
1336
- ],
1337
- [
1338
- "v10.0",
1339
- 0.36
1340
- ],
1341
- [
1342
- "v9.1",
1343
- 0.54
1344
- ],
1345
- [
1346
- "v9.0",
1347
- 0.13
1348
- ],
1349
- [
1350
- "v5.1",
1351
- 0.2
1352
- ]
1353
- ]
1354
- },
1355
- {
1356
- "name":"Edge",
1357
- "id":"Edge",
1358
- "data":[
1359
- [
1360
- "v16",
1361
- 2.6
1362
- ],
1363
- [
1364
- "v15",
1365
- 0.92
1366
- ],
1367
- [
1368
- "v14",
1369
- 0.4
1370
- ],
1371
- [
1372
- "v13",
1373
- 0.1
1374
- ]
1375
- ]
1376
- },
1377
- {
1378
- "name":"Opera",
1379
- "id":"Opera",
1380
- "data":[
1381
- [
1382
- "v50.0",
1383
- 0.96
1384
- ],
1385
- [
1386
- "v49.0",
1387
- 0.82
1388
- ],
1389
- [
1390
- "v12.1",
1391
- 0.14
1392
- ]
1393
- ]
1394
- }
1395
- ]
1396
- }
1397
- }
1398
- SAMPLE6={
1399
- "chart":{
1400
- "type":"tilemap",
1401
- "inverted":True,
1402
- "height":"80%"
1403
- },
1404
- "accessibility":{
1405
- "description":"A tile map represents the states of the USA by population in 2016. The hexagonal tiles are positioned to geographically echo the map of the USA. A color-coded legend states the population levels as below 1 million (beige), 1 to 5 million (orange), 5 to 20 million (pink) and above 20 million (hot pink). The chart is interactive, and the individual state data points are displayed upon hovering. Three states have a population of above 20 million: California (39.3 million), Texas (27.9 million) and Florida (20.6 million). The northern US region from Massachusetts in the Northwest to Illinois in the Midwest contains the highest concentration of states with a population of 5 to 20 million people. The southern US region from South Carolina in the Southeast to New Mexico in the Southwest contains the highest concentration of states with a population of 1 to 5 million people. 6 states have a population of less than 1 million people; these include Alaska, Delaware, Wyoming, North Dakota, South Dakota and Vermont. The state with the lowest population is Wyoming in the Northwest with 584,153 people.",
1406
- "screenReaderSection":{
1407
- "beforeChartFormat":"<h5>{chartTitle}</h5>""+""<div>{chartSubtitle}</div>""+""<div>{chartLongdesc}</div>""+""<div>{viewTableButton}</div>"
1408
- },
1409
- "point":{
1410
- "valueDescriptionFormat":"{index}. {xDescription}, {point.value}."
1411
- }
1412
- },
1413
- "title":{
1414
- "text":"U.S. states by population in 2016"
1415
- },
1416
- "subtitle":{
1417
- "text":"Source:<a href=\"https://simple.wikipedia.org/wiki/List_of_U.S._states_by_population\">Wikipedia</a>"
1418
- },
1419
- "xAxis":{
1420
- "visible":False
1421
- },
1422
- "yAxis":{
1423
- "visible":False
1424
- },
1425
- "colorAxis":{
1426
- "dataClasses":[
1427
- {
1428
- "from":0,
1429
- "to":1000000,
1430
- "color":"#F9EDB3",
1431
- "name":"< 1M"
1432
- },
1433
- {
1434
- "from":1000000,
1435
- "to":5000000,
1436
- "color":"#FFC428",
1437
- "name":"1M - 5M"
1438
- },
1439
- {
1440
- "from":5000000,
1441
- "to":20000000,
1442
- "color":"#FF7987",
1443
- "name":"5M - 20M"
1444
- },
1445
- {
1446
- "from":20000000,
1447
- "color":"#FF2371",
1448
- "name":"> 20M"
1449
- }
1450
- ]
1451
- },
1452
- "tooltip":{
1453
- "headerFormat":"",
1454
- "pointFormat":"The population of <b> {point.name}</b> is <b>{point.value}</b>"
1455
- },
1456
- "plotOptions":{
1457
- "series":{
1458
- "dataLabels":{
1459
- "enabled":True,
1460
- "format":"{point.hc-a2}",
1461
- "color":"#000000",
1462
- "style":{
1463
- "textOutline":False
1464
- }
1465
- }
1466
- }
1467
- },
1468
- "series":[
1469
- {
1470
- "name":"",
1471
- "data":[
1472
- {
1473
- "hc-a2":"AL",
1474
- "name":"Alabama",
1475
- "region":"South",
1476
- "x":6,
1477
- "y":7,
1478
- "value":4849377
1479
- },
1480
- {
1481
- "hc-a2":"AK",
1482
- "name":"Alaska",
1483
- "region":"West",
1484
- "x":0,
1485
- "y":0,
1486
- "value":737732
1487
- },
1488
- {
1489
- "hc-a2":"AZ",
1490
- "name":"Arizona",
1491
- "region":"West",
1492
- "x":5,
1493
- "y":3,
1494
- "value":6745408
1495
- },
1496
- {
1497
- "hc-a2":"AR",
1498
- "name":"Arkansas",
1499
- "region":"South",
1500
- "x":5,
1501
- "y":6,
1502
- "value":2994079
1503
- },
1504
- {
1505
- "hc-a2":"CA",
1506
- "name":"California",
1507
- "region":"West",
1508
- "x":5,
1509
- "y":2,
1510
- "value":39250017
1511
- },
1512
- {
1513
- "hc-a2":"CO",
1514
- "name":"Colorado",
1515
- "region":"West",
1516
- "x":4,
1517
- "y":3,
1518
- "value":5540545
1519
- },
1520
- {
1521
- "hc-a2":"CT",
1522
- "name":"Connecticut",
1523
- "region":"Northeast",
1524
- "x":3,
1525
- "y":11,
1526
- "value":3596677
1527
- },
1528
- {
1529
- "hc-a2":"DE",
1530
- "name":"Delaware",
1531
- "region":"South",
1532
- "x":4,
1533
- "y":9,
1534
- "value":935614
1535
- },
1536
- {
1537
- "hc-a2":"DC",
1538
- "name":"District of Columbia",
1539
- "region":"South",
1540
- "x":4,
1541
- "y":10,
1542
- "value":7288000
1543
- },
1544
- {
1545
- "hc-a2":"FL",
1546
- "name":"Florida",
1547
- "region":"South",
1548
- "x":8,
1549
- "y":8,
1550
- "value":20612439
1551
- },
1552
- {
1553
- "hc-a2":"GA",
1554
- "name":"Georgia",
1555
- "region":"South",
1556
- "x":7,
1557
- "y":8,
1558
- "value":10310371
1559
- },
1560
- {
1561
- "hc-a2":"HI",
1562
- "name":"Hawaii",
1563
- "region":"West",
1564
- "x":8,
1565
- "y":0,
1566
- "value":1419561
1567
- },
1568
- {
1569
- "hc-a2":"ID",
1570
- "name":"Idaho",
1571
- "region":"West",
1572
- "x":3,
1573
- "y":2,
1574
- "value":1634464
1575
- },
1576
- {
1577
- "hc-a2":"IL",
1578
- "name":"Illinois",
1579
- "region":"Midwest",
1580
- "x":3,
1581
- "y":6,
1582
- "value":12801539
1583
- },
1584
- {
1585
- "hc-a2":"IN",
1586
- "name":"Indiana",
1587
- "region":"Midwest",
1588
- "x":3,
1589
- "y":7,
1590
- "value":6596855
1591
- },
1592
- {
1593
- "hc-a2":"IA",
1594
- "name":"Iowa",
1595
- "region":"Midwest",
1596
- "x":3,
1597
- "y":5,
1598
- "value":3107126
1599
- },
1600
- {
1601
- "hc-a2":"KS",
1602
- "name":"Kansas",
1603
- "region":"Midwest",
1604
- "x":5,
1605
- "y":5,
1606
- "value":2904021
1607
- },
1608
- {
1609
- "hc-a2":"KY",
1610
- "name":"Kentucky",
1611
- "region":"South",
1612
- "x":4,
1613
- "y":6,
1614
- "value":4413457
1615
- },
1616
- {
1617
- "hc-a2":"LA",
1618
- "name":"Louisiana",
1619
- "region":"South",
1620
- "x":6,
1621
- "y":5,
1622
- "value":4649676
1623
- },
1624
- {
1625
- "hc-a2":"ME",
1626
- "name":"Maine",
1627
- "region":"Northeast",
1628
- "x":0,
1629
- "y":11,
1630
- "value":1330089
1631
- },
1632
- {
1633
- "hc-a2":"MD",
1634
- "name":"Maryland",
1635
- "region":"South",
1636
- "x":4,
1637
- "y":8,
1638
- "value":6016447
1639
- },
1640
- {
1641
- "hc-a2":"MA",
1642
- "name":"Massachusetts",
1643
- "region":"Northeast",
1644
- "x":2,
1645
- "y":10,
1646
- "value":6811779
1647
- },
1648
- {
1649
- "hc-a2":"MI",
1650
- "name":"Michigan",
1651
- "region":"Midwest",
1652
- "x":2,
1653
- "y":7,
1654
- "value":9928301
1655
- },
1656
- {
1657
- "hc-a2":"MN",
1658
- "name":"Minnesota",
1659
- "region":"Midwest",
1660
- "x":2,
1661
- "y":4,
1662
- "value":5519952
1663
- },
1664
- {
1665
- "hc-a2":"MS",
1666
- "name":"Mississippi",
1667
- "region":"South",
1668
- "x":6,
1669
- "y":6,
1670
- "value":2984926
1671
- },
1672
- {
1673
- "hc-a2":"MO",
1674
- "name":"Missouri",
1675
- "region":"Midwest",
1676
- "x":4,
1677
- "y":5,
1678
- "value":6093000
1679
- },
1680
- {
1681
- "hc-a2":"MT",
1682
- "name":"Montana",
1683
- "region":"West",
1684
- "x":2,
1685
- "y":2,
1686
- "value":1023579
1687
- },
1688
- {
1689
- "hc-a2":"NE",
1690
- "name":"Nebraska",
1691
- "region":"Midwest",
1692
- "x":4,
1693
- "y":4,
1694
- "value":1881503
1695
- },
1696
- {
1697
- "hc-a2":"NV",
1698
- "name":"Nevada",
1699
- "region":"West",
1700
- "x":4,
1701
- "y":2,
1702
- "value":2839099
1703
- },
1704
- {
1705
- "hc-a2":"NH",
1706
- "name":"New Hampshire",
1707
- "region":"Northeast",
1708
- "x":1,
1709
- "y":11,
1710
- "value":1326813
1711
- },
1712
- {
1713
- "hc-a2":"NJ",
1714
- "name":"New Jersey",
1715
- "region":"Northeast",
1716
- "x":3,
1717
- "y":10,
1718
- "value":8944469
1719
- },
1720
- {
1721
- "hc-a2":"NM",
1722
- "name":"New Mexico",
1723
- "region":"West",
1724
- "x":6,
1725
- "y":3,
1726
- "value":2085572
1727
- },
1728
- {
1729
- "hc-a2":"NY",
1730
- "name":"New York",
1731
- "region":"Northeast",
1732
- "x":2,
1733
- "y":9,
1734
- "value":19745289
1735
- },
1736
- {
1737
- "hc-a2":"NC",
1738
- "name":"North Carolina",
1739
- "region":"South",
1740
- "x":5,
1741
- "y":9,
1742
- "value":10146788
1743
- },
1744
- {
1745
- "hc-a2":"ND",
1746
- "name":"North Dakota",
1747
- "region":"Midwest",
1748
- "x":2,
1749
- "y":3,
1750
- "value":739482
1751
- },
1752
- {
1753
- "hc-a2":"OH",
1754
- "name":"Ohio",
1755
- "region":"Midwest",
1756
- "x":3,
1757
- "y":8,
1758
- "value":11614373
1759
- },
1760
- {
1761
- "hc-a2":"OK",
1762
- "name":"Oklahoma",
1763
- "region":"South",
1764
- "x":6,
1765
- "y":4,
1766
- "value":3878051
1767
- },
1768
- {
1769
- "hc-a2":"OR",
1770
- "name":"Oregon",
1771
- "region":"West",
1772
- "x":4,
1773
- "y":1,
1774
- "value":3970239
1775
- },
1776
- {
1777
- "hc-a2":"PA",
1778
- "name":"Pennsylvania",
1779
- "region":"Northeast",
1780
- "x":3,
1781
- "y":9,
1782
- "value":12784227
1783
- },
1784
- {
1785
- "hc-a2":"RI",
1786
- "name":"Rhode Island",
1787
- "region":"Northeast",
1788
- "x":2,
1789
- "y":11,
1790
- "value":1055173
1791
- },
1792
- {
1793
- "hc-a2":"SC",
1794
- "name":"South Carolina",
1795
- "region":"South",
1796
- "x":6,
1797
- "y":8,
1798
- "value":4832482
1799
- },
1800
- {
1801
- "hc-a2":"SD",
1802
- "name":"South Dakota",
1803
- "region":"Midwest",
1804
- "x":3,
1805
- "y":4,
1806
- "value":853175
1807
- },
1808
- {
1809
- "hc-a2":"TN",
1810
- "name":"Tennessee",
1811
- "region":"South",
1812
- "x":5,
1813
- "y":7,
1814
- "value":6651194
1815
- },
1816
- {
1817
- "hc-a2":"TX",
1818
- "name":"Texas",
1819
- "region":"South",
1820
- "x":7,
1821
- "y":4,
1822
- "value":27862596
1823
- },
1824
- {
1825
- "hc-a2":"UT",
1826
- "name":"Utah",
1827
- "region":"West",
1828
- "x":5,
1829
- "y":4,
1830
- "value":2942902
1831
- },
1832
- {
1833
- "hc-a2":"VT",
1834
- "name":"Vermont",
1835
- "region":"Northeast",
1836
- "x":1,
1837
- "y":10,
1838
- "value":626011
1839
- },
1840
- {
1841
- "hc-a2":"VA",
1842
- "name":"Virginia",
1843
- "region":"South",
1844
- "x":5,
1845
- "y":8,
1846
- "value":8411808
1847
- },
1848
- {
1849
- "hc-a2":"WA",
1850
- "name":"Washington",
1851
- "region":"West",
1852
- "x":2,
1853
- "y":1,
1854
- "value":7288000
1855
- },
1856
- {
1857
- "hc-a2":"WV",
1858
- "name":"West Virginia",
1859
- "region":"South",
1860
- "x":4,
1861
- "y":7,
1862
- "value":1850326
1863
- },
1864
- {
1865
- "hc-a2":"WI",
1866
- "name":"Wisconsin",
1867
- "region":"Midwest",
1868
- "x":2,
1869
- "y":5,
1870
- "value":5778708
1871
- },
1872
- {
1873
- "hc-a2":"WY",
1874
- "name":"Wyoming",
1875
- "region":"West",
1876
- "x":3,
1877
- "y":3,
1878
- "value":584153
1879
- }
1880
- ]
1881
- }
1882
- ]
1883
- }
1884
- SAMPLE7={
1885
- "chart":{
1886
- "type":"pie"
1887
- },
1888
- "title":{
1889
- "text":"Browser market shares. January, 2022"
1890
- },
1891
- "subtitle":{
1892
- "text":"Click the slices to view versions. Source: <a href=\"http://statcounter.com\" target=\"_blank\">statcounter.com</a>"
1893
- },
1894
- "accessibility":{
1895
- "announceNewData":{
1896
- "enabled":True
1897
- },
1898
- "point":{
1899
- "valueSuffix":"%"
1900
- }
1901
- },
1902
- "plotOptions":{
1903
- "series":{
1904
- "dataLabels":{
1905
- "enabled":True,
1906
- "format":"{point.name}: {point.y:.1f}%"
1907
- }
1908
- }
1909
- },
1910
- "tooltip":{
1911
- "headerFormat":"<span style=\"font-size:11px\">{series.name}</span><br>",
1912
- "pointFormat":"<span style=\"color:{point.color}\">{point.name}</span>: <b>{point.y:.2f}%</b> of total<br/>"
1913
- },
1914
- "series":[
1915
- {
1916
- "name":"Browsers",
1917
- "colorByPoint":True,
1918
- "data":[
1919
- {
1920
- "name":"Chrome",
1921
- "y":61.04,
1922
- "drilldown":"Chrome"
1923
- },
1924
- {
1925
- "name":"Safari",
1926
- "y":9.47,
1927
- "drilldown":"Safari"
1928
- },
1929
- {
1930
- "name":"Edge",
1931
- "y":9.32,
1932
- "drilldown":"Edge"
1933
- },
1934
- {
1935
- "name":"Firefox",
1936
- "y":8.15,
1937
- "drilldown":"Firefox"
1938
- },
1939
- {
1940
- "name":"Other",
1941
- "y":11.02,
1942
- "drilldown":"null"
1943
- }
1944
- ]
1945
- }
1946
- ],
1947
- "drilldown":{
1948
- "series":[
1949
- {
1950
- "name":"Chrome",
1951
- "id":"Chrome",
1952
- "data":[
1953
- [
1954
- "v97.0",
1955
- 36.89
1956
- ],
1957
- [
1958
- "v96.0",
1959
- 18.16
1960
- ],
1961
- [
1962
- "v95.0",
1963
- 0.54
1964
- ],
1965
- [
1966
- "v94.0",
1967
- 0.7
1968
- ],
1969
- [
1970
- "v93.0",
1971
- 0.8
1972
- ],
1973
- [
1974
- "v92.0",
1975
- 0.41
1976
- ],
1977
- [
1978
- "v91.0",
1979
- 0.31
1980
- ],
1981
- [
1982
- "v90.0",
1983
- 0.13
1984
- ],
1985
- [
1986
- "v89.0",
1987
- 0.14
1988
- ],
1989
- [
1990
- "v88.0",
1991
- 0.1
1992
- ],
1993
- [
1994
- "v87.0",
1995
- 0.35
1996
- ],
1997
- [
1998
- "v86.0",
1999
- 0.17
2000
- ],
2001
- [
2002
- "v85.0",
2003
- 0.18
2004
- ],
2005
- [
2006
- "v84.0",
2007
- 0.17
2008
- ],
2009
- [
2010
- "v83.0",
2011
- 0.21
2012
- ],
2013
- [
2014
- "v81.0",
2015
- 0.1
2016
- ],
2017
- [
2018
- "v80.0",
2019
- 0.16
2020
- ],
2021
- [
2022
- "v79.0",
2023
- 0.43
2024
- ],
2025
- [
2026
- "v78.0",
2027
- 0.11
2028
- ],
2029
- [
2030
- "v76.0",
2031
- 0.16
2032
- ],
2033
- [
2034
- "v75.0",
2035
- 0.15
2036
- ],
2037
- [
2038
- "v72.0",
2039
- 0.14
2040
- ],
2041
- [
2042
- "v70.0",
2043
- 0.11
2044
- ],
2045
- [
2046
- "v69.0",
2047
- 0.13
2048
- ],
2049
- [
2050
- "v56.0",
2051
- 0.12
2052
- ],
2053
- [
2054
- "v49.0",
2055
- 0.17
2056
- ]
2057
- ]
2058
- },
2059
- {
2060
- "name":"Safari",
2061
- "id":"Safari",
2062
- "data":[
2063
- [
2064
- "v15.3",
2065
- 0.1
2066
- ],
2067
- [
2068
- "v15.2",
2069
- 2.01
2070
- ],
2071
- [
2072
- "v15.1",
2073
- 2.29
2074
- ],
2075
- [
2076
- "v15.0",
2077
- 0.49
2078
- ],
2079
- [
2080
- "v14.1",
2081
- 2.48
2082
- ],
2083
- [
2084
- "v14.0",
2085
- 0.64
2086
- ],
2087
- [
2088
- "v13.1",
2089
- 1.17
2090
- ],
2091
- [
2092
- "v13.0",
2093
- 0.13
2094
- ],
2095
- [
2096
- "v12.1",
2097
- 0.16
2098
- ]
2099
- ]
2100
- },
2101
- {
2102
- "name":"Edge",
2103
- "id":"Edge",
2104
- "data":[
2105
- [
2106
- "v97",
2107
- 6.62
2108
- ],
2109
- [
2110
- "v96",
2111
- 2.55
2112
- ],
2113
- [
2114
- "v95",
2115
- 0.15
2116
- ]
2117
- ]
2118
- },
2119
- {
2120
- "name":"Firefox",
2121
- "id":"Firefox",
2122
- "data":[
2123
- [
2124
- "v96.0",
2125
- 4.17
2126
- ],
2127
- [
2128
- "v95.0",
2129
- 3.33
2130
- ],
2131
- [
2132
- "v94.0",
2133
- 0.11
2134
- ],
2135
- [
2136
- "v91.0",
2137
- 0.23
2138
- ],
2139
- [
2140
- "v78.0",
2141
- 0.16
2142
- ],
2143
- [
2144
- "v52.0",
2145
- 0.15
2146
- ]
2147
- ]
2148
- }
2149
- ]
2150
- }
2151
- }
2152
- SAMPLE8={
2153
- "title":{
2154
- "text":"Sales of petroleum products March, Norway",
2155
- "align":"left"
2156
- },
2157
- "xAxis":{
2158
- "categories":[
2159
- "Jet fuel",
2160
- "Duty-free diesel",
2161
- "Petrol",
2162
- "Diesel",
2163
- "Gas oil"
2164
- ]
2165
- },
2166
- "yAxis":{
2167
- "title":{
2168
- "text":"Million liter"
2169
- }
2170
- },
2171
- "labels":{
2172
- "items":[
2173
- {
2174
- "html":"Total liter",
2175
- "style":{
2176
- "left":"50px",
2177
- "top":"18px",
2178
- "color":"black"
2179
- }
2180
- }
2181
- ]
2182
- },
2183
- "series":[
2184
- {
2185
- "type":"column",
2186
- "name":"2020",
2187
- "data":[
2188
- 59,
2189
- 83,
2190
- 65,
2191
- 228,
2192
- 184
2193
- ]
2194
- },
2195
- {
2196
- "type":"column",
2197
- "name":"2021",
2198
- "data":[
2199
- 24,
2200
- 79,
2201
- 72,
2202
- 240,
2203
- 167
2204
- ]
2205
- },
2206
- {
2207
- "type":"column",
2208
- "name":"2022",
2209
- "data":[
2210
- 58,
2211
- 88,
2212
- 75,
2213
- 250,
2214
- 176
2215
- ]
2216
- },
2217
- {
2218
- "type":"spline",
2219
- "name":"Average",
2220
- "data":[
2221
- 47,
2222
- 83.33,
2223
- 70.66,
2224
- 239.33,
2225
- 175.66
2226
- ],
2227
- "marker":{
2228
- "lineWidth":2,
2229
- "fillColor":"black",
2230
- }
2231
- },
2232
- {
2233
- "type":"pie",
2234
- "name":"Liter",
2235
- "data":[
2236
- {
2237
- "name":"2020",
2238
- "y":619,
2239
- "color": "#7cb4ec"
2240
-
2241
- },
2242
- {
2243
- "name":"2021",
2244
- "y":586,
2245
- "color": "#434348"
2246
- },
2247
- {
2248
- "name":"2022",
2249
- "y":647,
2250
- "color":"#90ed7d"
2251
- }
2252
- ],
2253
- "center":[
2254
- 80,
2255
- 70
2256
- ],
2257
- "size":100,
2258
- "showInLegend":False,
2259
- "dataLabels":{
2260
- "enabled":False
2261
- }
2262
- }
2263
- ]
2264
- }
2265
- SAMPLE9={
2266
- "chart":{
2267
- "type":"solidgauge",
2268
- "height":"90%"
2269
- },
2270
- "title":{
2271
- "text":"Activity",
2272
- "style":{
2273
- "fontSize":"24px"
2274
- }
2275
- },
2276
- "tooltip":{
2277
- "borderWidth":0,
2278
- "backgroundColor":"none",
2279
- "shadow":False,
2280
- "style":{
2281
- "fontSize":"16px"
2282
- },
2283
- "valueSuffix":"%",
2284
- "pointFormat":"{series.name}<br><span style=\"font-size:2em; color: {point.color}; font-weight: bold\">{point.y}</span>",
2285
- "positioner":{
2286
- "x":"50px",
2287
- "y":100
2288
- }
2289
- },
2290
- "pane":{
2291
- "startAngle":0,
2292
- "endAngle":360,
2293
- "background":[
2294
- {
2295
- "radius":"112%",
2296
- "innerRadius":"88%",
2297
- "borderWidth":0
2298
- },
2299
- {
2300
- "radius":"87%",
2301
- "innerRadius":"63%",
2302
- "borderWidth":0
2303
- },
2304
- {
2305
- "radius":"62%",
2306
- "innerRadius":"38%",
2307
- "borderWidth":0
2308
- }
2309
- ]
2310
- },
2311
- "yAxis":{
2312
- "min":0,
2313
- "max":100,
2314
- "lineWidth":0,
2315
- "tickPositions":[
2316
-
2317
- ]
2318
- },
2319
- "plotOptions":{
2320
- "solidgauge":{
2321
- "dataLabels":{
2322
- "enabled":False
2323
- },
2324
- "linecap":"round",
2325
- "stickyTracking":False,
2326
- "rounded":True
2327
- }
2328
- },
2329
- "series":[
2330
- {
2331
- "name":"Move",
2332
- "data":[
2333
- {
2334
- "color":"lightgreen",
2335
- "radius":"112%",
2336
- "innerRadius":"88%",
2337
- "y":80
2338
- }
2339
- ]
2340
- },
2341
- {
2342
- "name":"Exercise",
2343
- "data":[
2344
- {
2345
- "color":"red",
2346
- "radius":"87%",
2347
- "innerRadius":"63%",
2348
- "y":65
2349
- }
2350
- ]
2351
- },
2352
- {
2353
- "name":"Stand",
2354
- "data":[
2355
- {
2356
- "color":"blue",
2357
- "radius":"62%",
2358
- "innerRadius":"38%",
2359
- "y":50
2360
- }
2361
- ]
2362
- }
2363
- ]
2364
- }
2365
- SAMPLE10={
2366
-
2367
- "chart": {
2368
- "type": 'gauge',
2369
- "plotBackgroundColor": "white",
2370
- "plotBackgroundImage": "white",
2371
- "plotBorderWidth": 0,
2372
- "plotShadow": False,
2373
- "height": '80%'
2374
- },
2375
-
2376
- "title": {
2377
- "text": 'Speedometer'
2378
- },
2379
-
2380
- "pane": {
2381
- "startAngle": -90,
2382
- "endAngle": 89.9,
2383
- "background": "null",
2384
- "center": ['50%', '75%'],
2385
- "size": '110%'
2386
- },
2387
-
2388
- "yAxis": {
2389
- "min": 0,
2390
- "max": 200,
2391
- "tickPixelInterval": 72,
2392
- "tickPosition": 'inside',
2393
- "tickColor": '#FFFFFF',
2394
- "tickLength": 20,
2395
- "tickWidth": 2,
2396
- "minorTickInterval": "null",
2397
- "labels": {
2398
- "distance": 20,
2399
- "style": {
2400
- "fontSize": '14px'
2401
- }
2402
- },
2403
- "plotBands": [{
2404
- "from": 0,
2405
- "to": 120,
2406
- "color": '#55BF3B',
2407
- "thickness": 20
2408
- }, {
2409
- "from": 120,
2410
- "to": 160,
2411
- "color": '#DDDF0D',
2412
- "thickness": 20
2413
- }, {
2414
- "from": 160,
2415
- "to": 200,
2416
- "color": '#DF5353',
2417
- "thickness": 20
2418
- }]
2419
- },
2420
-
2421
- "series": [{
2422
- "name": 'Speed',
2423
- "data": [80],
2424
- "tooltip": {
2425
- "valueSuffix": ' km/h'
2426
- },
2427
- "dataLabels": {
2428
- "format": '{y} km/h',
2429
- "borderWidth": 0,
2430
- "color": '#333333',
2431
- "style": {
2432
- "fontSize": '16px'
2433
- }
2434
- },
2435
- "dial": {
2436
- "radius": '80%',
2437
- "backgroundColor": 'gray',
2438
- "baseWidth": 12,
2439
- "baseLength": '0%',
2440
- "rearLength": '0%'
2441
- },
2442
- "pivot": {
2443
- "backgroundColor": 'gray',
2444
- "radius": 6
2445
- }
2446
- }]
2447
-
2448
- }
2449
- SAMPLE11={
2450
-
2451
- "title": {
2452
- "text": 'Highcharts Dependency Wheel'
2453
- },
2454
-
2455
- "accessibility": {
2456
- "point": {
2457
- "valueDescriptionFormat": '{index}. From {point.from} to {point.to}: {point.weight}.'
2458
- }
2459
- },
2460
-
2461
- "series": [{
2462
- "keys": ['from', 'to', 'weight'],
2463
- "data": [
2464
- ['Brazil', 'Portugal', 5],
2465
- ['Brazil', 'France', 1],
2466
- ['Brazil', 'Spain', 1],
2467
- ['Brazil', 'England', 1],
2468
- ['Canada', 'Portugal', 1],
2469
- ['Canada', 'France', 5],
2470
- ['Canada', 'England', 1],
2471
- ['Mexico', 'Portugal', 1],
2472
- ['Mexico', 'France', 1],
2473
- ['Mexico', 'Spain', 5],
2474
- ['Mexico', 'England', 1],
2475
- ['USA', 'Portugal', 1],
2476
- ['USA', 'France', 1],
2477
- ['USA', 'Spain', 1],
2478
- ['USA', 'England', 5],
2479
- ['Portugal', 'Angola', 2],
2480
- ['Portugal', 'Senegal', 1],
2481
- ['Portugal', 'Morocco', 1],
2482
- ['Portugal', 'South Africa', 3],
2483
- ['France', 'Angola', 1],
2484
- ['France', 'Senegal', 3],
2485
- ['France', 'Mali', 3],
2486
- ['France', 'Morocco', 3],
2487
- ['France', 'South Africa', 1],
2488
- ['Spain', 'Senegal', 1],
2489
- ['Spain', 'Morocco', 3],
2490
- ['Spain', 'South Africa', 1],
2491
- ['England', 'Angola', 1],
2492
- ['England', 'Senegal', 1],
2493
- ['England', 'Morocco', 2],
2494
- ['England', 'South Africa', 7],
2495
- ['South Africa', 'China', 5],
2496
- ['South Africa', 'India', 1],
2497
- ['South Africa', 'Japan', 3],
2498
- ['Angola', 'China', 5],
2499
- ['Angola', 'India', 1],
2500
- ['Angola', 'Japan', 3],
2501
- ['Senegal', 'China', 5],
2502
- ['Senegal', 'India', 1],
2503
- ['Senegal', 'Japan', 3],
2504
- ['Mali', 'China', 5],
2505
- ['Mali', 'India', 1],
2506
- ['Mali', 'Japan', 3],
2507
- ['Morocco', 'China', 5],
2508
- ['Morocco', 'India', 1],
2509
- ['Morocco', 'Japan', 3],
2510
- ['Japan', 'Brazil', 1]
2511
- ],
2512
- "type": 'dependencywheel',
2513
- "name": 'Dependency wheel series',
2514
- "dataLabels": {
2515
- "color": '#333',
2516
- "style": {
2517
- "textOutline": 'none'
2518
- },
2519
- "textPath": {
2520
- "enabled": True,
2521
- "attributes": {
2522
- "dy": 5
2523
- }
2524
- },
2525
- "distance": 10
2526
- },
2527
- "size": '95%'
2528
- }]
2529
-
2530
- }
2531
- frontend_dir = (Path(__file__).parent / "frontend").absolute()
2532
- _component_func = components.declare_component(
2533
- "streamlit_highcharts", path=str(frontend_dir)
2534
- )
2535
-
2536
- def streamlit_highcharts(
2537
- options=SAMPLE,
2538
- height=410,
2539
- key: Optional[str] = None,
2540
- ):
2541
- """
2542
- Add a descriptive docstring
2543
- """
2544
- component_value = _component_func(
2545
- options=options,key=key,height=height
2546
- )
2547
-
2548
- return component_value
2549
-
2550
-
2551
- # def main():
2552
- # st.write("## Example")
2553
- # selSample=st.selectbox("Choose a sample",[SAMPLE,SAMPLE2,SAMPLE3,SAMPLE4,SAMPLE5,SAMPLE6,SAMPLE7,SAMPLE8,SAMPLE9,SAMPLE10],format_func=lambda x: str(x["title"]["text"])
2554
- # )
2555
- # value = streamlit_highcharts(selSample,640)
2556
- # with st.expander("Show code...",expanded=False):
2557
- # st.code(str(selSample).replace("},","},\r\n"),language="python")
2558
-
2559
-
2560
-
2561
- # if __name__ == "__main__":
2562
- # main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
st_hc/frontend/index.html DELETED
@@ -1,36 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta http-equiv="X-UA-Compatible" content="IE=edge" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>streamlit-highcharts</title>
8
- <script src="https://code.highcharts.com/stock/highstock.js"></script>
9
- <script src="https://code.highcharts.com/highcharts-more.js"></script>
10
- <script src="https://code.highcharts.com/modules/heatmap.js"></script>
11
- <script src="https://code.highcharts.com/modules/tilemap.js"></script>
12
- <script src="https://code.highcharts.com/modules/streamgraph.js"></script>
13
- <script src="https://code.highcharts.com/modules/solid-gauge.js"></script>
14
- <script src="https://code.highcharts.com/modules/series-label.js"></script>
15
- <script src="https://code.highcharts.com/modules/data.js"></script>
16
- <script src="https://code.highcharts.com/modules/drilldown.js"></script>
17
- <script src="https://code.highcharts.com/modules/sankey.js"></script>
18
- <script src="https://code.highcharts.com/modules/dependency-wheel.js"></script>
19
- <script src="https://code.highcharts.com/modules/annotations.js"></script>
20
- <script src="https://code.highcharts.com/modules/exporting.js"></script>
21
- <script src="https://code.highcharts.com/modules/export-data.js"></script>
22
- <script src="https://code.highcharts.com/modules/accessibility.js"></script>
23
- <script src="https://github.highcharts.com/modules/parallel-coordinates.js"></script>
24
- <script src="https://code.highcharts.com/modules/draggable-points.js"></script>
25
- <script src="https://code.highcharts.com/modules/exporting.js"></script>
26
- <script src="https://code.highcharts.com/modules/export-data.js"></script>
27
- <script src="./streamlit-component-lib.js"></script>
28
- <script src="./main.js"></script>
29
- <link rel="stylesheet" href="./style.css" />
30
- </head>
31
- <body>
32
- <div id="root">
33
- <div id="container" ></div>
34
- </div>
35
- </body>
36
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
st_hc/frontend/main.js DELETED
@@ -1,42 +0,0 @@
1
- function sendValue(value) {
2
- Streamlit.setComponentValue(value)
3
- }
4
-
5
-
6
- function onRender(event) {
7
- if (true) {
8
- const {options,height} = event.detail.args
9
- Streamlit.setFrameHeight(height+20)
10
- document.getElementById("container").style.height=height+"px"
11
- let c = Highcharts.chart('container', options);
12
-
13
- let points = c.series[0].data.map((p) =>
14
- ({ x: Math.round(p.x),
15
- y: Math.round(p.y),
16
- color:p.color,
17
- name:p.name} ));
18
- sendValue(points);
19
-
20
- console.log(c.series[0].data);
21
-
22
- c.series[0].data.forEach((point, i) => {
23
-
24
- point.graphic.element.onclick = () => {
25
-
26
- let points = c.series[0].data.map((p) =>
27
- ({ x: Math.round(p.x),
28
- y: Math.round(p.y),
29
- color:p.color,
30
- name:p.name} ));
31
-
32
- console.log(points);
33
- sendValue(points);
34
-
35
- }
36
- })
37
- window.rendered = true
38
- }
39
- }
40
-
41
- Streamlit.events.addEventListener(Streamlit.RENDER_EVENT, onRender)
42
- Streamlit.setComponentReady()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
st_hc/frontend/streamlit-component-lib.js DELETED
@@ -1,34 +0,0 @@
1
-
2
- // Borrowed minimalistic Streamlit API from Thiago
3
- // https://discuss.streamlit.io/t/code-snippet-create-components-without-any-frontend-tooling-no-react-babel-webpack-etc/13064
4
- function sendMessageToStreamlitClient(type, data) {
5
- console.log(type, data)
6
- const outData = Object.assign({
7
- isStreamlitMessage: true,
8
- type: type,
9
- }, data);
10
- window.parent.postMessage(outData, "*");
11
- }
12
-
13
- const Streamlit = {
14
- setComponentReady: function() {
15
- sendMessageToStreamlitClient("streamlit:componentReady", {apiVersion: 1});
16
- },
17
- setFrameHeight: function(height) {
18
- sendMessageToStreamlitClient("streamlit:setFrameHeight", {height: height});
19
- },
20
- setComponentValue: function(value) {
21
- sendMessageToStreamlitClient("streamlit:setComponentValue", {value: value});
22
- },
23
- RENDER_EVENT: "streamlit:render",
24
- events: {
25
- addEventListener: function(type, callback) {
26
- window.addEventListener("message", function(event) {
27
- if (event.data.type === type) {
28
- event.detail = event.data
29
- callback(event);
30
- }
31
- });
32
- }
33
- }
34
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
st_hc/frontend/style.css DELETED
@@ -1,9 +0,0 @@
1
- text.highcharts-credits {
2
- display: none;
3
- }
4
- .highcharts-exporting-group {
5
- display: none;
6
- }
7
- div[class*="chartMenu"] {
8
- display: none;
9
- }
 
 
 
 
 
 
 
 
 
 
temp.html DELETED
@@ -1,310 +0,0 @@
1
-
2
- <!DOCTYPE html>
3
- <html lang="en">
4
- <head>
5
- <meta charset="UTF-8">
6
- <title>Cartographie des parties prenantes bziiit</title>
7
- <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
8
- <style>
9
- body {
10
- font-family: 'Roboto', sans-serif;
11
- margin: 20px;
12
- }
13
- h1, h2, h3, h4, h5, h6 {
14
- font-weight: bold;
15
- }
16
- .page-break {
17
- page-break-before: always;
18
- margin: 50px;
19
- height: 50px;
20
- }
21
- </style>
22
- </head>
23
- <body>
24
- <div style="text-align: center;">
25
- <h1>Cartographie des parties prenantes "bziiit"</h1>
26
- <p>Date de l'analyse IA RSE : 2024-07-19</p>
27
- <p>IA utilisées :</p>
28
- <p>GPT-4o</p>
29
- <img src="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" alt="Logo" style="width: 150px;"/>
30
- </div>
31
- <div class="page-break"></div>
32
- <div style="text-align: center; margin-top: 20px;">
33
- <img src="data:image/png;base64,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VXjvP/Jk99Qz3l6pj7ovbkpedXOJtT/yijWZTuNCeJXUEF6BHsIrwDMoZYM3gC502YS8bh7e7jY+dI876avvdJPvu6303VYR3vv3Pcx954Vvd7P22t89fWCf0n3UfWET8qqbScz9kVfM6Yw1NoRXSQ3hFeghvAI8g1I2eAPoQpex5/XA737r/uPeX43c4dEbP+oO++X3St1xWeG97Q//2N0065Ujbf7pfoe7SU/YvVQfdV8Ue15184i9P/KKPaF240N4ldQQXoEewivAMyhlgzeALnQZe17XPnCX2/TIA6Pu8A9/drV72g/+vetddxPe3+4x0W06eo775VNOG9XWjAmT3CmTDuzavsUFsedlwSTmPskr5nR4wtuPdBBegSrCK8AzKGWDN4AudBlzXsWnu/nbnPjg3e7Im77Q8WlvJ+H1T3VvPXqO27H3AW3pxfqUN+a8hGmYbCl5NTFanvAqqSG8Aj2EV4BnUMoGbwBd6DLmvH6w4173wx33drw7L76H/OK77om3/XCX871F4fXndH992HHujiP+z5iim3UW61nemPMSpmGypeTVxGgRXiU1hPdxekNDQ27x4sWtP02bNs2tXr3aTZkypfXndevWucHBwdb/P2vWLLd06VI3MDDgEF5l6tVfywZfP3Olx5jzuvK+2909jz1S+va8/E588B63+6M73KR7tzgvvA/sN835ows79t6/q+TmO9p/twnu7H0PLd13XRfGnFddDJrUD3k1Ka1srAivkhrC65zzsuuFNi+5GdTi99asWdP61oIFCxBeZeYZ1LLBG0AXuow1r07HGcrebrczvN3aifFYQ6x5dWM5Xr9PXk1MHuFVUhv3wrtt2za3bNkyd8EFF7gZM2bswtIL7vTp0928efNa38sL8Pr1690T9prs5syZo2RAbU0E2OBrAh2om1jzuvPRYfeV+++U7lIV3jMnH+wO3mNAGkPo4ljzCn2fqbRHXk1MEuFVUhv3wrtp0ya3aNEit3379hGOc+fObT3BHR4edsuXL3czZ84cEV5//YoVK9ySJUvchg0bEF5l9tVcywZfM3Cxu1jz2vLoDveN+38t3Z0qvM+f/EQ3bY+J0hhCF8eaV+j7TKU98mpikgivktq4F17/xNaf0c3O5fonvgsXLnRnn322O+uss1rC65/ueun1X0Xh3bnnXm727NlKBtTWROCxxx5r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ZcK7adMmt2LFCrdkyRK3YcOG0sK76Ze/dCsvvdQtfsMbxpRb/0TTf73pVa9q/V//VPezX/1q6wnwTT/96ciRhv/63vda38v/c3++1v//Dz70kHtwxw73/R/+sHUUwUu2b8sfi/Bf3Z4s+/Eufve73fYHHhiZYZe85S0jRxrKtuWv82Pde+JE983vfrfVVtZOJtKnPfvZ7rNf+Urreyvf+tYWn7HaL45rzvOfP8Kr21JIWaC63XsTv59yXv0S3vzRAZ95/omvl84HH3yw9T9/tGCfffZxl6xa5Y6cMaP19DV/pKF4bbsnx3mJvXXTptaRheeedpr75ON7zIvmzm09VfXXfXH9+pEpmLXln7xe9vhRsfwxB9/Wu1ascM8//XT3scsvd4dPm9Z6Muzbf8Zxx4205WvOffnL3fK//3u3ffv2XY5KFJ+GX7xyZRR/MYhlLaa8vmJhHHocCK9GdNwLrxfczZs3O39EwX+1E14vu156/VdReCc99JCbPXt21xS83F30nve4gw86yC36m79pey518GMfa7Uz/y//svV/r//Rj9z6r3+9df2Pf/Yz9y+f+lRLFltiODjo/uYVr3BHTp/ufNv5P/t2Nlx3nbv4zW9ufd9/Xfm1r7kvffObrXp/BthfM+2QQ9zZp5++y9izsZ71vOe1vp/9+a//4i/cCcccU6kt3++HPvUpt+xv/7ZV6+8pfx/tmIw11jOe8xy36p//2c19wQtabfmn5p+76ir34jPOKHXO97HHHmvd62677dY1Ly6wJ5ByXiGE995t29ySt73N/dXrXueOP/FE52X3vStXugOnTnV/84Y3OP/9d19yifNPgJ905JHuny+91G249lr3D5dc0vrz+s9/3v3guuvchYsXu5tvusn96wc/6Fa8611uvylTdrm23Wzw7fkv39fPb73VveOii9xJz362e/2b3tTqOz+2/LW+5obrrtulv7u2bm2N5Y7bb2+19exTTmm17b+y9o55xjNG7s23f/DBB7dqhnfsGNVfdv3sOXPc3HPOaY3PC/1bL7qodX98OZfy+ko130kTUF4l23EvvP7prj+aUPzyH057y1ve4t7znvd0fMLrhffMM88slYEXxyXvfa/71Z13jlzvJfLsF7yg9ed/evzJyOvPO6/15xtuusld6YX3/PPdzbfc0hLFFRde2BJWf+3hXlhf8IJR1/kP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">
34
- </div>
35
- <p><span style="display: inline-block; width: 15px; height: 15px; border-radius: 50%; background-color: #830c5d;"></span> <b>Adetem</b>: Pouvoir:50% Influence:50%</p>
36
-
37
- <p><span style="display: inline-block; width: 15px; height: 15px; border-radius: 50%; background-color: #4e2834;"></span> <b>Bordeaux technowest</b>: Pouvoir:55% Influence:50%</p>
38
-
39
- <p><span style="display: inline-block; width: 15px; height: 15px; border-radius: 50%; background-color: #88e8d7;"></span> <b>Digital aquitaine</b>: Pouvoir:60% Influence:50%</p>
40
-
41
- <p><span style="display: inline-block; width: 15px; height: 15px; border-radius: 50%; background-color: #e07e1d;"></span> <b>Hub france ia</b>: Pouvoir:65% Influence:50%</p>
42
-
43
- <p><span style="display: inline-block; width: 15px; height: 15px; border-radius: 50%; background-color: #797e1a;"></span> <b>La french tech bordeaux</b>: Pouvoir:71% Influence:26%</p>
44
-
45
- <p><span style="display: inline-block; width: 15px; height: 15px; border-radius: 50%; background-color: #4a3a56;"></span> <b>Mouvement impact france</b>: Pouvoir:75% Influence:50%</p>
46
-
47
- <p><span style="display: inline-block; width: 15px; height: 15px; border-radius: 50%; background-color: #b08d0b;"></span> <b>Vivexalto</b>: Pouvoir:77% Influence:82%</p>
48
-
49
- <div class="page-break"></div>
50
- <h2>RESUME</h2>
51
- <p>Dans cette conversation, nous avons discuté de la cartographie des parties prenantes de la marque bziiit. Voici un résumé des points clés :</p>
52
-
53
- <ol>
54
- <li><p><strong>Parties Prenantes Actuellement Identifiées</strong> : La marque bziiit a déjà identifié sept parties prenantes principales avec des niveaux de pouvoir et d'influence variés :</p>
55
-
56
- <ul>
57
- <li>Adetem (Pouvoir : 50%, Influence : 50%)</li>
58
- <li>Bordeaux Technowest (Pouvoir : 55%, Influence : 50%)</li>
59
- <li>Digital Aquitaine (Pouvoir : 60%, Influence : 50%)</li>
60
- <li>Hub France IA (Pouvoir : 65%, Influence : 50%)</li>
61
- <li>La French Tech Bordeaux (Pouvoir : 71%, Influence : 26%)</li>
62
- <li>Mouvement Impact France (Pouvoir : 75%, Influence : 50%)</li>
63
- <li>Vivexalto (Pouvoir : 77%, Influence : 82%)</li>
64
- </ul></li>
65
- <li><p><strong>Catégorisation des Parties Prenantes</strong> : Les parties prenantes sont segmentées en quatre catégories selon leur pouvoir et influence :</p>
66
-
67
- <ul>
68
- <li>Rendre satisfait : Pouvoir 50-100, Influence 0-50</li>
69
- <li>Gérer étroitement : Pouvoir 50-100, Influence 50-100</li>
70
- <li>Suivre de près : Pouvoir 0-50, Influence 0-50</li>
71
- <li>Tenir informé : Pouvoir 0-50, Influence 50-100</li>
72
- </ul></li>
73
- <li><p><strong>Suggestions pour Ajouter d'Autres Parties Prenantes</strong> : </p>
74
-
75
- <ul>
76
- <li>Clients et Utilisateurs Finaux : Pouvoir 40%, Influence 60% (Tenir informé)</li>
77
- <li>Investisseurs et Actionnaires : Pouvoir 85%, Influence 70% (Gérer étroitement)</li>
78
- <li>Fournisseurs et Partenaires Technologiques : Pouvoir 60%, Influence 55% (Gérer étroitement)</li>
79
- <li>Gouvernements et Régulateurs : Pouvoir 70%, Influence 80% (Gérer étroitement)</li>
80
- <li>Communautés Locales : Pouvoir 30%, Influence 40% (Suivre de près)</li>
81
- <li>Médias et Influenceurs : Pouvoir 45%, Influence 70% (Tenir informé)</li>
82
- </ul></li>
83
- </ol>
84
-
85
- <p>En intégrant ces groupes supplémentaires dans la cartographie des parties prenantes, bziiit pourrait mieux gérer ses relations et élaborer des stratégies plus efficaces pour atteindre ses objectifs organisationnels.</p>
86
-
87
- <hr />
88
-
89
- <hr />
90
-
91
- <div class="page-break"></div>
92
- <h2>Historique de la Conversation</h2>
93
- <h3>AI:</h3>
94
- <p>Salut, voici votre cartographie des parties prenantes. Que puis-je faire pour vous ?</p>
95
- <hr />
96
- <h3>Moi:</h3>
97
- <p>En plus des parties prenantes déjà identifiées que tu peux consulter, quels groupes ou individus, impactés par les activités de mon organisation, devrais-je ajouter dans notre cartographie des parties prenantes ?</p>
98
- <hr />
99
- <h3>AI:</h3>
100
- <p>Pour compléter la cartographie des parties prenantes de bziiit, il est crucial d'identifier et d'intégrer d'autres groupes ou individus qui sont impactés par les activités de l'organisation et qui pourraient influencer sa réussite ou son développement. Voici quelques suggestions basées sur le contexte de votre entreprise et les catégories de parties prenantes existantes :</p>
101
- <ol>
102
- <li><strong>Clients et Utilisateurs Finaux</strong> :</li>
103
- <li><strong>Pouvoir</strong> : 40%</li>
104
- <li><strong>Influence</strong> : 60%</li>
105
- <li><strong>Catégorie</strong> : Tenir informé</li>
106
- </ol>
107
- <p>Les clients et utilisateurs finaux sont essentiels car ils sont les destinataires directs des produits et services de bziiit. Bien qu'ils aient un pouvoir limité individuellement, leur influence collective peut être significative à travers les retours et la fidélité.</p>
108
- <ol>
109
- <li><strong>Investisseurs et Actionnaires</strong> :</li>
110
- <li><strong>Pouvoir</strong> : 85%</li>
111
- <li><strong>Influence</strong> : 70%</li>
112
- <li><strong>Catégorie</strong> : Gérer étroitement</li>
113
- </ol>
114
- <p>Les investisseurs et actionnaires ont un pouvoir et une influence importants en raison de leurs apports financiers et de leur intérêt direct dans la rentabilité de bziiit. Ils peuvent fortement influencer les décisions stratégiques de l'entreprise.</p>
115
- <ol>
116
- <li><strong>Fournisseurs et Partenaires Technologiques</strong> :</li>
117
- <li><strong>Pouvoir</strong> : 60%</li>
118
- <li><strong>Influence</strong> : 55%</li>
119
- <li><strong>Catégorie</strong> : Gérer étroitement</li>
120
- </ol>
121
- <p>Les fournisseurs et partenaires technologiques jouent un rôle crucial dans la chaîne d'approvisionnement et la capacité d'innovation de bziiit. Une gestion étroite de cette relation est nécessaire pour assurer la qualité et la continuité des opérations.</p>
122
- <ol>
123
- <li><strong>Gouvernements et Régulateurs</strong> :</li>
124
- <li><strong>Pouvoir</strong> : 70%</li>
125
- <li><strong>Influence</strong> : 80%</li>
126
- <li><strong>Catégorie</strong> : Gérer étroitement</li>
127
- </ol>
128
- <p>Les autorités gouvernementales et les organismes de régulation ont un pouvoir et une influence significatifs en raison de leur capacité à imposer des lois, des règlements et des politiques qui peuvent impacter directement les opérations de bziiit.</p>
129
- <ol>
130
- <li><strong>Communautés Locales</strong> :</li>
131
- <li><strong>Pouvoir</strong> : 30%</li>
132
- <li><strong>Influence</strong> : 40%</li>
133
- <li><strong>Catégorie</strong> : Suivre de près</li>
134
- </ol>
135
- <p>Les communautés locales où bziiit opère peuvent être affectées par ses activités, notamment en termes d'emploi, d'environnement et de responsabilité sociale. Bien que leur pouvoir soit limité, leur influence sur la perception publique de l'entreprise peut être importante.</p>
136
- <ol>
137
- <li><strong>Médias et Influenceurs</strong> :</li>
138
- <li><strong>Pouvoir</strong> : 45%</li>
139
- <li><strong>Influence</strong> : 70%</li>
140
- <li><strong>Catégorie</strong> : Tenir informé</li>
141
- </ol>
142
- <p>Les médias et les influenceurs peuvent fortement influencer l'image de marque de bziiit. Bien que leur pouvoir direct soit modéré, leur capacité à façonner l'opinion publique leur confère une influence significative.</p>
143
- <p>En ajoutant ces groupes à votre cartographie des parties prenantes, vous obtiendrez une vision plus complète des acteurs qui peuvent influencer ou être influencés par les activités de bziiit. Cela vous permettra de mieux gérer vos relations et de développer des stratégies plus robustes pour atteindre vos objectifs organisationnels.</p>
144
- <hr />
145
- <div class="page-break"></div>
146
- <h2>Emission de CO2</h2>
147
- <table border="1" class="dataframe">
148
- <thead>
149
- <tr style="text-align: right;">
150
- <th></th>
151
- <th>CodeCarbon (Cumulative)</th>
152
- <th>EcoLogits (par requete)</th>
153
- <th>EcoLogits (Cumulative)</th>
154
- <th>Consommation Totale</th>
155
- </tr>
156
- </thead>
157
- <tbody>
158
- <tr>
159
- <th>Collecte de documents et scrapping</th>
160
- <td>$2.61</td>
161
- <td>$0.00</td>
162
- <td>$0.00</td>
163
- <td>$2.61</td>
164
- </tr>
165
- <tr>
166
- <th>Extraction des parties prenantes (serveur et IA)</th>
167
- <td>$2.66</td>
168
- <td>$1.56</td>
169
- <td>$1.56</td>
170
- <td>$4.22</td>
171
- </tr>
172
- <tr>
173
- <th>Sauvegarde de la cartographie</th>
174
- <td>$7.64</td>
175
- <td>$0.00</td>
176
- <td>$1.56</td>
177
- <td>$9.20</td>
178
- </tr>
179
- <tr>
180
- <th>Dialogue avec chatBot IA</th>
181
- <td>$7.48</td>
182
- <td>$31.63</td>
183
- <td>$33.19</td>
184
- <td>$40.67</td>
185
- </tr>
186
- <tr>
187
- <th>Téléchargement cartographie</th>
188
- <td>$0.00</td>
189
- <td>$0.00</td>
190
- <td>$0.00</td>
191
- <td>$0.00</td>
192
- </tr>
193
- </tbody>
194
- </table>
195
- <div class="page-break"></div>
196
-
197
- <div style='display: flex; align-items: center;'>
198
- <h2 style='margin: 0;'>Votre consommation Carbone</h2>
199
- <img src='data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjYiIGhlaWdodD0iMjYiIHZpZXdCb3g9IjAgMCAyNiAyNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4NCjxwYXRoIGQ9Ik0zLjMzMTI2IDYuNTgxMjVDLTEuMTA0OTkgMTEuMDE3NSAtMS4xMDQ5OSAxOC4yIDMuMjk4NzYgMjIuNjM2M0M1LjY4NzUxIDE3LjExMTMgOS45NDUwMSAxMi40OTYzIDE1LjI1ODggOS43NUMxMC43NTc1IDEzLjU1MjUgNy42MDUwMSAxOC44NjYzIDYuNTAwMDEgMjQuODk1QzEwLjcyNSAyNi44OTM4IDE1LjkyNSAyNi4xNjI1IDE5LjQxODggMjIuNjY4OEMyNS4wNzM4IDE3LjAxMzggMjYgMCAyNiAwQzI2IDAgOC45ODYyNiAwLjkyNjI1IDMuMzMxMjYgNi41ODEyNVoiIGZpbGw9IiMxMjlDNzMiLz4NCjwvc3ZnPg0K' alt='svg' width='15' height='15' style='margin-left: 10px;'>
200
- </div>
201
- <br>
202
- <div style='text-align: center; margin-bottom: 10px;'><b>42.520</b> g eq. CO2</div><p>Dont :</p><p>- Empreinte serveur (via CodeCarbon) : <b>9.333</b> g eq. CO2 (21.95%)</p><p>- Empreinte IA (via EcoLogits) : <b>33.187</b> g eq. CO2 (78.05%)</p><h2>Votre équivalence</h2>
203
- <div style='display: flex; justify-content: space-around;'>
204
-
205
- <div>
206
-
207
- <div style='text-align: center;'>
208
- <a href='https://impactco2.fr/outils/comparateur?value=0.042520378554370936&comparisons=tgv,eauenbouteille,voiturethermique' target='_blank'><img src='https://impactco2.fr/icons/eauenbouteille.svg' alt='ml' width='50'></a>
209
- </div>
210
- <div style='text-align: center;'>
211
- <b>159.03</b> ml
212
- </div>
213
-
214
- </div>
215
- <div>
216
-
217
- <div style='text-align: center;'>
218
- <a href='https://impactco2.fr/outils/comparateur?value=0.042520378554370936&comparisons=tgv,eauenbouteille,voiturethermique' target='_blank'><img src='https://impactco2.fr/icons/tgv.svg' alt='km' width='50'></a>
219
- </div>
220
- <div style='text-align: center;'>
221
- <b>14.51</b> km
222
- </div>
223
-
224
- </div>
225
- <div>
226
-
227
- <div style='text-align: center;'>
228
- <a href='https://impactco2.fr/outils/comparateur?value=0.042520378554370936&comparisons=tgv,eauenbouteille,voiturethermique' target='_blank'><img src='https://impactco2.fr/icons/voiturethermique.svg' alt='m' width='50'></a>
229
- </div>
230
- <div style='text-align: center;'>
231
- <b>195.59</b> m
232
- </div>
233
-
234
- </div>
235
- </div><br>
236
- <br>
237
- <div style='display: flex; align-items: center;'>
238
- <p>Powered by <b>ADEME</b></p>
239
- <a href='https://www.ademe.fr' target='_blank'><img src='https://www.ademe.fr/wp-content/uploads/2022/11/ademe-logo-2022-1.svg' alt='svg' width='30' height='30' style='margin-left: 10px;'></a>
240
- </div>
241
- <br>
242
-
243
- <style>
244
- .centered-table {
245
- margin-left: auto;
246
- margin-right: auto;
247
- border-collapse: collapse;
248
- width: 50%;
249
- }
250
- .centered-table th, .centered-table td {
251
- border: 1px solid #ddd;
252
- padding: 8px;
253
- text-align: center;
254
- }
255
- .centered-table th {
256
- background-color: #f2f2f2;
257
- }
258
- </style>
259
-
260
- <h2 style="text-align:center;">DÉTAIL PAR TÂCHE</h2>
261
- <div style="overflow-x:auto;">
262
- <style type="text/css">
263
- #T_ce1ea_row0_col0, #T_ce1ea_row1_col0, #T_ce1ea_row2_col0 {
264
- background-color: rgba(0,128,0,0.5);
265
- }
266
- #T_ce1ea_row3_col0 {
267
- background-color: rgba(255,255,0,0.5);
268
- }
269
- #T_ce1ea_row4_col0 {
270
- background-color: white;
271
- }
272
- </style>
273
- <table id="T_ce1ea" class="centered-table">
274
- <thead>
275
- <tr>
276
- <th class="blank level0" >&nbsp;</th>
277
- <th id="T_ce1ea_level0_col0" class="col_heading level0 col0" >Consommation totale (g eqCo2)</th>
278
- </tr>
279
- </thead>
280
- <tbody>
281
- <tr>
282
- <th id="T_ce1ea_level0_row0" class="row_heading level0 row0" >Collecte de documents et scrapping</th>
283
- <td id="T_ce1ea_row0_col0" class="data row0 col0" >2.610000</td>
284
- </tr>
285
- <tr>
286
- <th id="T_ce1ea_level0_row1" class="row_heading level0 row1" >Extraction des parties prenantes (serveur et IA)</th>
287
- <td id="T_ce1ea_row1_col0" class="data row1 col0" >4.220000</td>
288
- </tr>
289
- <tr>
290
- <th id="T_ce1ea_level0_row2" class="row_heading level0 row2" >Sauvegarde de la cartographie</th>
291
- <td id="T_ce1ea_row2_col0" class="data row2 col0" >9.200000</td>
292
- </tr>
293
- <tr>
294
- <th id="T_ce1ea_level0_row3" class="row_heading level0 row3" >Dialogue avec chatBot IA</th>
295
- <td id="T_ce1ea_row3_col0" class="data row3 col0" >40.670000</td>
296
- </tr>
297
- <tr>
298
- <th id="T_ce1ea_level0_row4" class="row_heading level0 row4" >Téléchargement cartographie</th>
299
- <td id="T_ce1ea_row4_col0" class="data row4 col0" >-</td>
300
- </tr>
301
- </tbody>
302
- </table>
303
-
304
- </div>
305
-
306
- <h2>SYNTHESE (Dialogue IA et non IA)</h2>
307
- <div style="text-align:center;"><img src="data:image/png;base64,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uuo/3Vd/9VfzMR/zMXzWZ30Wn/3Zn80L8t3f/d28z/u8D8ePH+e93/u9OX78ON/93d/Nrbfeylu91Vvx0z/909zvsz/7s/mcz/kcPuuzPovP/uzP5n4//dM/zdu8zdvw4Ac/mLd+67fm1ltv5ad/+qd50IMexF//9V9z/PhxXhhJvNZrvRa//du/zQvy27/927zO67wOT3/603nwgx/M/T77sz+br/mar+G3fuu3ePCDH8zLvMzL8FIv9VL89E//NFdd9X8YALJtrrrqqv903/3d3813f/d389Vf/dW89Eu/NC/MX//1X/PZn/3Z3Hrrrdx666289Eu/NO/93u/Ne7/3e/NA3/3d3813f/d3897v/d6893u/Nw/013/913z2Z382f/3Xf83x48d56Zd+aT77sz+bBz/4wfxLXvu1X5uXfumX5qu/+qt5QV7mZV6Gl3qpl+K7v/u7eaDd3V3e+q3fmt/5nd8B4KVe6qX47d/+bY4fP85VV/0fBoBsm6uuuuqqf4Xd3V3++q//mpd+6Zfm+PHjPD+33norAA9+8IO56qr/BwCQbXPVVVddddVVV/17ACDb5qqrrrrqqquu+vcAQLbNVVddddVVV1317wGAbJurrrrqqquuuurfAwDZNlddddVVV1111b8HALJtrrrqqquuuuqqfw8AZNtcddVVV1111VX/HgDItrnqqquuuuqqq/49AJBtc9VVV1111VVX/XsAINvmqquuuuqqq6769wBAts1VV1111VVXXfXvAYBsm6uuuuqqq6666t8DANk2V1111VVXXXXVvwcAsm2uuuqqq6666qp/DwBk21x11VVXXXXVVf8eAMi2ueqqq6666qqr/j0AkG1z1VVXXXXVVVf9ewAg2+aqq6666qqrrvr3AEC2zVVXXXXVVVdd9e8BgGybq6666qqrrrrq3wMA2TZXXXXVVVddddW/BwCyba666qqrrrrqqn8PAGTbXHXVVVddddVV/x4AyLa56qqrrrrqqqv+PQCQbXPVVVddddVVV/17ACDb5qqrrrrqqquu+vcAQLbNVVddddVVV1317wGAbJurrrrqqquuuurfAwDZNlddddVVV1111b8HALJtrrrqqquuuuqqfw8AZNtcddVVV1111VX/HgDItrnqqquuuuqqq/49APhH8sbK5fnKHNUAAAAASUVORK5CYII=" alt="Pie chart"></div>
308
- </body>
309
- </html>
310
-