datacipen commited on
Commit
f4769ec
·
verified ·
1 Parent(s): 12073be

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +658 -67
app.py CHANGED
@@ -4,9 +4,10 @@ import requests
4
  import textwrap
5
  from offres_emploi import Api
6
  from offres_emploi.utils import dt_to_str_iso
7
- from dash import Dash, html, dcc, callback, Output, Input, dash_table, State, _dash_renderer
8
  import dash_bootstrap_components as dbc
9
  import plotly.express as px
 
10
  import dash_mantine_components as dmc
11
  from dash_iconify import DashIconify
12
  import pandas as pd
@@ -16,11 +17,12 @@ import plotly.io as pio
16
  from langchain_community.llms import HuggingFaceEndpoint
17
  from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
18
  from langchain.schema.output_parser import StrOutputParser
 
 
19
 
20
  from flask import Flask
21
 
22
  server = Flask(__name__)
23
-
24
  # external JavaScript files
25
  external_scripts = [
26
  'https://datacipen-eventia.hf.space/copilot/index.js'
@@ -50,6 +52,256 @@ pio.templates.default = "custom_plotly_dark"
50
 
51
  load_dotenv()
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  def localisation():
54
  ListCentroids = [
55
  { "ID": "01", "Longitude": 5.3245259, "Latitude":46.0666003 },
@@ -156,6 +408,35 @@ def localisation():
156
 
157
  return ListCentroids
158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  def connexion_France_Travail():
160
  client = Api(client_id=os.getenv('POLE_EMPLOI_CLIENT_ID'),
161
  client_secret=os.getenv('POLE_EMPLOI_CLIENT_SECRET'))
@@ -199,6 +480,11 @@ theme_toggle = dmc.Tooltip(
199
  arrowSize=6,
200
  )
201
 
 
 
 
 
 
202
  styleTitle = {
203
  "textAlign": "center"
204
  }
@@ -224,20 +510,23 @@ styleToggle = {
224
  "marginTop":"25px",
225
  "textAlign": "right",
226
  }
 
 
 
227
  styleSubmitBox = {
228
  "position":"fixed",
229
  "width": "100%",
230
  "top": "calc(100vh - 100px)",
231
  "right": "0"
232
  }
233
- datadefault = [
234
- {"value": "K2105", "label": "K2105"},
235
- {"value": "L1101", "label": "L1101"},
236
- {"value": "L1202", "label": "L1202"},
237
- {"value": "L1507", "label": "L1507"},
238
- {"value": "L1508", "label": "L1508"},
239
- {"value": "L1509", "label": "L1509"},
240
- ]
241
 
242
  def custom_error_handler(err):
243
  # This function defines what we want to happen when an exception occurs
@@ -327,7 +616,7 @@ class CustomDash(Dash):
327
  {config}
328
  {scripts}
329
  {renderer}
330
- <div id="custom-footer">My custom footer</div>
331
  </body>
332
  </html>
333
  '''.format(
@@ -353,7 +642,7 @@ app.layout = dmc.MantineProvider(
353
  placeholder="Selectionnez vos Codes ROME",
354
  id="framework-multi-select",
355
  value=['K2105', 'L1101', 'L1202', 'L1507', 'L1508', 'L1509'],
356
- data=datadefault,
357
  w=600,
358
  mt=10,
359
  styles={
@@ -384,48 +673,100 @@ app.layout = dmc.MantineProvider(
384
  dmc.GridCol(html.Div(dmc.Title(f"Le marché et les statistiques de l'emploi", order=1, size="30", my="20", id="chainlit-call-fn", style=styleTitle)), span=5),
385
  dmc.GridCol(html.Div(theme_toggle, style=styleToggle), span=1),
386
  dmc.GridCol(html.Div(dmc.Tooltip(dmc.Button(leftSection=DashIconify(icon="tabler:sparkles", width=30), id="drawer-demo-button"), label="IA générative sur les données",position="left",withArrow=True,arrowSize=6,), style=styleToggle), span=1),
387
- dmc.GridCol(html.Div(
388
- dcc.Loading(
389
- id="loadingRepartition",
390
- children=(dcc.Graph(id="figRepartition",selectedData={'points': [{'hovertext': ['01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','2A','2B','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35','36','37','38','39','40','41','42','43','44','45','46','47','48','49','50','51','52','53','54','55','56','57','58','59','60','61','62','63','64','65','66','67','68','69','70','71','72','73','74','75','76','77','78','79','80','81','82','83','84','85','86','87','88','89','90','91','92','93','94','95','971','972','973','974']}]})),
391
- type="default",
392
- )
393
- ), span=6),
394
- dmc.GridCol(html.Div(
395
- dcc.Loading(
396
- id="loadingEmplois",
397
- children=(dcc.Graph(id="figEmplois")),
398
- type="default",
399
- )
400
- ), span=6),
401
- dmc.GridCol(html.Div(
402
- dcc.Loading(
403
- id="loadingContrats",
404
- children=(dcc.Graph(id="figContrats")),
405
- type="default",
406
- )
407
- ), span=6),
408
- dmc.GridCol(html.Div(
409
- dcc.Loading(
410
- id="loadingExperiences",
411
- children=(dcc.Graph(id="figExperiences")),
412
- type="default",
413
- )
414
- ), span=6),
415
- dmc.GridCol(html.Div(
416
- dcc.Loading(
417
- id="loadingCompetences",
418
- children=(dcc.Graph(id="figCompetences")),
419
- type="default",
420
- )
421
- ), span=6),
422
- dmc.GridCol(html.Div(
423
- dcc.Loading(
424
- id="loadingTransversales",
425
- children=(dcc.Graph(id="figTransversales")),
426
- type="default",
427
- )
428
- ), span=6),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
429
  ],
430
  gutter="xs",
431
  )
@@ -435,7 +776,7 @@ app.layout = dmc.MantineProvider(
435
  )
436
  ],
437
  id="mantine-provider",
438
- forceColorScheme="dark",
439
  theme={
440
  "primaryColor": "indigo",
441
  "fontFamily": "'Inter', sans-serif",
@@ -494,6 +835,8 @@ def drawer_demo(n_clicks):
494
  Output(component_id='figRepartition', component_property='figure'),
495
  Output(component_id='figCompetences', component_property='figure'),
496
  Output(component_id='figTransversales', component_property='figure'),
 
 
497
  Input(component_id='framework-multi-select', component_property='value'),
498
  Input('figEmplois', 'selectedData'),
499
  Input("mantine-provider", "forceColorScheme"),
@@ -509,7 +852,9 @@ def create_repartition(array_value, selectedData, theme):
509
  plot_bgcolor = 'rgba(255, 255, 255, 1)'
510
 
511
  df_FT = API_France_Travail(array_value)
512
- df = df_FT[['intitule','typeContratLibelle','experienceLibelle','lieuTravail']].copy()
 
 
513
  df["lieuTravail"] = df["lieuTravail"].apply(lambda x: x['libelle']).apply(lambda x: x[0:3]).apply(lambda x: x.strip())
514
  df.drop(df[df['lieuTravail'] == 'Fra'].index, inplace = True)
515
  df.drop(df[df['lieuTravail'] == 'FRA'].index, inplace = True)
@@ -529,7 +874,6 @@ def create_repartition(array_value, selectedData, theme):
529
  options = df['intitule'].values.tolist()
530
  df = df[df['intitule'].isin(options)]
531
 
532
-
533
  ######## localisation ########
534
  ListCentroids = localisation()
535
  df_localisation = df.groupby('lieuTravail').size().reset_index(name='obs')
@@ -541,7 +885,6 @@ def create_repartition(array_value, selectedData, theme):
541
  df_localisation["latitude"] = df_localisation['latitude'].apply(lambda x:[loc['Latitude'] for loc in ListCentroids if loc['ID'] == x]).apply(lambda x:''.join(map(str, x)))
542
  df_localisation["latitude"] = pd.to_numeric(df_localisation["latitude"], downcast="float")
543
 
544
-
545
  res = requests.get(
546
  "https://raw.githubusercontent.com/codeforgermany/click_that_hood/main/public/data/france-regions.geojson"
547
  )
@@ -560,18 +903,18 @@ def create_repartition(array_value, selectedData, theme):
560
  ],
561
  },font=dict(size=10),paper_bgcolor=paper_bgcolor,autosize=True,clickmode='event+select'
562
  )
563
-
 
564
  df_FT.dropna(subset=['qualitesProfessionnelles','formations','competences'], inplace=True)
565
  df_FT["competences"] = df_FT["competences"].apply(lambda x:[str(e['libelle']) for e in x]).apply(lambda x:'; '.join(map(str, x)))
566
  df_FT["qualitesProfessionnelles"] = df_FT["qualitesProfessionnelles"].apply(lambda x:[str(e['libelle']) + ": " + str(e['description']) for e in x]).apply(lambda x:'; '.join(map(str, x)))
567
 
568
- ######## Compétences professionnelles ########
569
  df_comp = df_FT
570
  df_comp['competences'] = df_FT['competences'].str.split(';')
571
  df_comp = df_comp.explode('competences')
572
  df_comp = df_comp.groupby('competences').size().reset_index(name='obs')
573
  df_comp = df_comp.sort_values(by=['obs'])
574
- df_comp = df_comp.iloc[-20:]
575
  fig_competences = px.bar(df_comp, x='obs', y='competences', orientation='h', color='obs', height=600, template=template, title="Les principales compétences professionnelles", labels={'obs':'nombre'}, color_continuous_scale="Teal", text_auto=True).update_layout(font=dict(size=10),paper_bgcolor=paper_bgcolor,plot_bgcolor=plot_bgcolor,clickmode='event+select',autosize=True).update_traces(hovertemplate=df_comp["competences"] + ' <br>Nombre : %{x}', y=[y[:100] + "..." for y in df_comp['competences']], showlegend=False)
576
 
577
  ######## Compétences transversales ########
@@ -580,10 +923,23 @@ def create_repartition(array_value, selectedData, theme):
580
  df_comptransversales = df_transversales.explode('qualitesProfessionnelles')
581
  df_comptransversales = df_comptransversales.groupby('qualitesProfessionnelles').size().reset_index(name='obs')
582
  df_comptransversales = df_comptransversales.sort_values(by=['obs'])
583
- df_comptransversales = df_comptransversales.iloc[-20:]
584
  fig_transversales = px.bar(df_comptransversales, x='obs', y='qualitesProfessionnelles', orientation='h', color='obs', height=600, template=template, title="Les principales compétences transversales", labels={'obs':'nombre'}, color_continuous_scale="Teal", text_auto=True).update_layout(font=dict(size=10),paper_bgcolor=paper_bgcolor,plot_bgcolor=plot_bgcolor,autosize=True).update_traces(hovertemplate=df_comptransversales["qualitesProfessionnelles"] + ' <br>Nombre : %{x}', y=[y[:80] + "..." for y in df_comptransversales["qualitesProfessionnelles"]], showlegend=False)
585
 
586
- return fig_localisation, fig_competences, fig_transversales
 
 
 
 
 
 
 
 
 
 
 
 
 
587
 
588
  def create_emploi(df, theme):
589
  if theme == "dark":
@@ -629,6 +985,76 @@ def create_experience(df, theme):
629
 
630
  return fig_experience
631
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
632
  @callback(
633
  Output(component_id='figEmplois', component_property='figure'),
634
  Input('figRepartition', 'selectedData'),
@@ -718,6 +1144,171 @@ def update_experience(selectedData, array_value, theme):
718
 
719
  return create_experience(df, theme)
720
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
721
  ########### IA Chatbot ###########
722
  @app.callback(
723
  Output("display-conversation", "children"), [Input("store-conversation", "data")]
@@ -755,7 +1346,7 @@ def run_chatbot(n_clicks, n_submit, user_input, chat_history, array_value):
755
  list_FT = df_FT_Select.values.tolist()
756
  context = ''
757
  for i in range(0,len(list_FT)):
758
- context += "\n✔️ Emploi : " + str(list_FT[i][0]) + ";\n◉ Contrat : " + str(list_FT[i][1]) + ";\n◉ Compétences professionnelles : " + str(list_FT[i][3]) + ";\n" + "◉ Salaire : " + str(list_FT[i][6]) + ";\n◉ Qualification : " + str(list_FT[i][5]).replace("'libelle'","\n• 'libelle") + ";\n◉ Localisation : " + str(list_FT[i][7]) + ";\n◉ Expérience : " + str(list_FT[i][2]) + ";\n◉ Niveau de qualification : " + str(list_FT[i][8]) + ";\n◉ Description de l'emploi : " + str(list_FT[i][4]) + "\n"
759
  #context = df_FT.to_string(index=False)
760
  template = """<s>[INST] Vous êtes un ingénieur pédagogique de l'enseignement supérieur et vous êtes doué pour faire des analyses des formations de l'enseignement supérieur et de faire le rapprochement entre les compétences académiques et les compétences professionnelles attendues par le marché de l'emploi et les les recruteurs, en fonction des critères définis ci-avant. En fonction des informations suivantes et du contexte suivant seulement et strictement, répondez en langue française strictement à la question ci-dessous, en 5000 mots au moins. Lorsque cela est possible, cite les sources du contexte. Si vous ne pouvez pas répondre à la question sur la base des informations, dites que vous ne trouvez pas de réponse ou que vous ne parvenez pas à trouver de réponse. Essayez donc de comprendre en profondeur le contexte et répondez uniquement en vous basant sur les informations fournies. Ne générez pas de réponses non pertinentes.
761
  Répondez à la question ci-dessous à partir du contexte ci-dessous :
@@ -788,11 +1379,11 @@ def run_chatbot(n_clicks, n_submit, user_input, chat_history, array_value):
788
  #repo_id = "microsoft/Phi-3.5-mini-instruct"
789
  #mistral_url = "https://api-inference.huggingface.co/models/mistralai/Mixtral-8x22B-Instruct-v0.1"
790
  llm = HuggingFaceEndpoint(
791
- repo_id=repo_id, task="text2text-generation", max_new_tokens=8000, temperature=0.001, streaming=True
792
  )
793
  model_output = ""
794
  chain = prompt | llm
795
- for s in chain.stream({"question":user_input,"context":context_p}):
796
  model_output = model_output + s
797
  print(s, end="", flush=True)
798
 
@@ -809,6 +1400,6 @@ def run_chatbot(n_clicks, n_submit, user_input, chat_history, array_value):
809
  chat_history += f"{model_output}<split>"
810
 
811
  return chat_history, None
812
-
813
  if __name__ == '__main__':
814
  app.run_server(debug=True)
 
4
  import textwrap
5
  from offres_emploi import Api
6
  from offres_emploi.utils import dt_to_str_iso
7
+ from dash import Dash, html, dcc, callback, Output, Input, dash_table, State, _dash_renderer, clientside_callback
8
  import dash_bootstrap_components as dbc
9
  import plotly.express as px
10
+ import plotly.graph_objects as go
11
  import dash_mantine_components as dmc
12
  from dash_iconify import DashIconify
13
  import pandas as pd
 
17
  from langchain_community.llms import HuggingFaceEndpoint
18
  from langchain_core.prompts import ChatPromptTemplate, PromptTemplate
19
  from langchain.schema.output_parser import StrOutputParser
20
+ from pinecone import Pinecone
21
+ from bs4 import BeautifulSoup
22
 
23
  from flask import Flask
24
 
25
  server = Flask(__name__)
 
26
  # external JavaScript files
27
  external_scripts = [
28
  'https://datacipen-eventia.hf.space/copilot/index.js'
 
52
 
53
  load_dotenv()
54
 
55
+ def removeTags(all):
56
+ for data in all(['style', 'script']):
57
+ data.decompose()
58
+ return ''.join(all.stripped_strings)
59
+
60
+ def htmlToDataframe(htmlTable):
61
+ data = []
62
+ list_header = []
63
+ soup = BeautifulSoup(htmlTable,'html.parser')
64
+ header = soup.find_all("table")[0].find("tr")
65
+ for items in header:
66
+ try:
67
+ list_header.append(items.get_text())
68
+ except:
69
+ continue
70
+ HTML_data = soup.find_all("table")[0].find_all("tr")[1:]
71
+ for element in HTML_data:
72
+ sub_data = []
73
+ for sub_element in element:
74
+ try:
75
+ sub_data.append(sub_element.get_text())
76
+ except:
77
+ continue
78
+ data.append(sub_data)
79
+ dataFrame = pd.DataFrame(data = data, columns = list_header)
80
+ return dataFrame
81
+
82
+ def getSavoirFaireFromHTMLMetier(url):
83
+ response = requests.get(url)
84
+ soup = BeautifulSoup(response.text, "html.parser")
85
+ allSavoirFaire = soup.select('ul[data-cy="liste-savoir-faire-metier"] > li')
86
+ if len(allSavoirFaire) != 0:
87
+ allSF = "<table><tr><td>Savoir-faire</td><td>Libelle</td><td>Type</td><td>Categorie</td></tr>"
88
+ for i in range(0,len(allSavoirFaire)):
89
+ blockSavoirFaire = allSavoirFaire[i]
90
+ try:
91
+ soupSavoirFaire = BeautifulSoup(str(blockSavoirFaire), "html.parser")
92
+ titleSavoirFaire = soupSavoirFaire.select('h4.fm-block-form-title')
93
+ descriptSavoirFaire = soupSavoirFaire.select('div.fm-block-form-collapse-content')
94
+ if removeTags(titleSavoirFaire[0]) != None:
95
+ for j in range(0,len(descriptSavoirFaire)):
96
+ ssblockSavoirFaire = descriptSavoirFaire[j]
97
+ soupssSavoirFaire = BeautifulSoup(str(ssblockSavoirFaire), "html.parser")
98
+ sstitleSavoirFaire = soupssSavoirFaire.select('h5.fm-block-form-subtitle')
99
+ listSavoirFaire = soupssSavoirFaire.select('ul.list-unstyled > li')
100
+ if len(listSavoirFaire) != 0:
101
+ for k in range(0,len(listSavoirFaire)):
102
+ blockListSavoirFaire = removeTags(listSavoirFaire[k])
103
+ allSF += "<tr><td>" + removeTags(titleSavoirFaire[0]) + "</td><td>" + blockListSavoirFaire + "</td><td>" + removeTags(sstitleSavoirFaire[0]) + "</td><td>1</td></tr>"
104
+ except:
105
+ print("Pas de Savoir-Faire!")
106
+ allSF += "</table>"
107
+ return allSF
108
+
109
+ def getSavoirFromHTMLMetier(url):
110
+ response = requests.get(url)
111
+ soup = BeautifulSoup(response.text, "html.parser")
112
+ allSavoirFaire = soup.select('ul[data-cy="liste-savoir-metier"] > li')
113
+ if len(allSavoirFaire) != 0:
114
+ allSF = "<table><tr><td>Savoir-faire</td><td>Libelle</td><td>Categorie</td></tr>"
115
+ for i in range(0,len(allSavoirFaire)):
116
+ blockSavoirFaire = allSavoirFaire[i]
117
+ try:
118
+ soupSavoirFaire = BeautifulSoup(str(blockSavoirFaire), "html.parser")
119
+ titleSavoirFaire = soupSavoirFaire.select('h4.fm-block-form-title')
120
+ descriptSavoirFaire = soupSavoirFaire.select('div.fm-block-form-collapse-content')
121
+ if removeTags(titleSavoirFaire[0]) != None:
122
+ for j in range(0,len(descriptSavoirFaire)):
123
+ ssblockSavoirFaire = descriptSavoirFaire[j]
124
+ soupssSavoirFaire = BeautifulSoup(str(ssblockSavoirFaire), "html.parser")
125
+ listSavoirFaire = soupssSavoirFaire.select('ul.list-unstyled > li')
126
+ if len(listSavoirFaire) != 0:
127
+ for k in range(0,len(listSavoirFaire)):
128
+ blockListSavoirFaire = removeTags(listSavoirFaire[k])
129
+ allSF += "<tr><td>" + removeTags(titleSavoirFaire[0]) + "</td><td>" + blockListSavoirFaire + "</td><td>1</td></tr>"
130
+ except:
131
+ print("Pas de Savoir-Faire!")
132
+ allSF += "</table>"
133
+ return allSF
134
+
135
+ def getContextFromHTMLMetier(url):
136
+ response = requests.get(url)
137
+ soup = BeautifulSoup(response.text, "html.parser")
138
+ allContext = soup.select('div[data-cy="liste-contextes"] > div.fm-context')
139
+ count = 0
140
+ if len(allContext) != 0:
141
+ allSF = "<table><tr><td>Savoir-faire</td><td>Libelle</td><td>Categorie</td></tr>"
142
+ for i in range(0,len(allContext)):
143
+ count = count + 1
144
+ blockContext = allContext[i]
145
+ try:
146
+ soupContext = BeautifulSoup(str(blockContext), "html.parser")
147
+ titleSavoirFaire = soupContext.select('h3.fm-context-title')
148
+ descriptSavoirFaire = soupContext.select('ul > li')
149
+ if removeTags(titleSavoirFaire[0]) != None:
150
+ for j in range(0,len(descriptSavoirFaire)):
151
+ ssblockSavoirFaire = descriptSavoirFaire[j]
152
+ if len(ssblockSavoirFaire) != 0:
153
+ allSF += "<tr><td>" + removeTags(titleSavoirFaire[0]) + "</td><td>" + removeTags(ssblockSavoirFaire) + "</td><td>1</td></tr>"
154
+ except:
155
+ print("Pas de Savoir-Faire!")
156
+ allSF += "</table>"
157
+ return allSF
158
+
159
+ def datavisualisation_skills_context(df, template, paper_bgcolor, plot_bgcolor, title_template, codeRome):
160
+ train = df
161
+ array_df = list(df.columns)
162
+ if any(x == "Type" for x in array_df):
163
+ df1 = train.groupby(['Savoir-faire', 'Type'])['Categorie'].count().reset_index()
164
+ df1.columns = ['source', 'target', 'value']
165
+ df2 = train.groupby(['Type', 'Libelle'])['Categorie'].count().reset_index()
166
+ df2.columns = ['source', 'target', 'value']
167
+ all_links = pd.concat([df1, df2], axis=0)
168
+ else:
169
+ df1 = train.groupby(['Savoir-faire', 'Libelle'])['Categorie'].count().reset_index()
170
+ df1.columns = ['source', 'target', 'value']
171
+ all_links = df1
172
+
173
+ unique_source_target = list(pd.unique(all_links[['source', 'target']].values.ravel('K')))
174
+ mapping_dict = {k: v for v, k in enumerate(unique_source_target)}
175
+
176
+ all_links['source'] = all_links['source'].map(mapping_dict)
177
+ all_links['target'] = all_links['target'].map(mapping_dict)
178
+
179
+ links_dict = all_links.to_dict(orient='list')
180
+ #Sankey Diagram Code
181
+ colors = [
182
+ "blue","blueviolet","brown","burlywood","cadetblue",
183
+ "chartreuse","chocolate","coral","cornflowerblue",
184
+ "cornsilk","crimson","cyan","darkblue","darkcyan",
185
+ "darkgoldenrod","darkgray","darkgrey","darkgreen",
186
+ "darkkhaki","darkmagenta","darkolivegreen","darkorange",
187
+ "darkorchid","darkred","darksalmon","darkseagreen",
188
+ "darkslateblue","darkslategray","darkslategrey",
189
+ "darkturquoise","darkviolet","deeppink","deepskyblue",
190
+ "dimgray","dimgrey","dodgerblue","firebrick",
191
+ "floralwhite","forestgreen","fuchsia","gainsboro",
192
+ "ghostwhite","gold","goldenrod","gray","grey","green",
193
+ "greenyellow","honeydew","hotpink","indianred","indigo",
194
+ "ivory","khaki","lavender","lavenderblush","lawngreen",
195
+ "lemonchiffon","lightblue","lightcoral","lightcyan",
196
+ "lightgoldenrodyellow","lightgray","lightgrey",
197
+ "lightgreen","lightpink","lightsalmon","lightseagreen",
198
+ "lightskyblue","lightslategray","lightslategrey",
199
+ "lightsteelblue","lightyellow", "lime","limegreen",
200
+ "linen","magenta","maroon","mediumaquamarine",
201
+ "mediumblue","mediumorchid","mediumpurple",
202
+ "mediumseagreen","mediumslateblue","mediumspringgreen",
203
+ "mediumturquoise","mediumvioletred","midnightblue",
204
+ "mintcream","mistyrose","moccasin","navajowhite","navy",
205
+ "oldlace","olive","olivedrab","orange","orangered",
206
+ "orchid","palegoldenrod","palegreen","paleturquoise",
207
+ "palevioletred","papayawhip","peachpuff","peru","pink",
208
+ "plum","powderblue","purple","red","rosybrown",
209
+ "royalblue","rebeccapurple","saddlebrown","salmon",
210
+ "sandybrown","seagreen","seashell","sienna","silver",
211
+ "skyblue","slateblue","slategray","slategrey","snow",
212
+ "aliceblue","antiquewhite","aqua","aquamarine","azure",
213
+ "beige","bisque","black","blanchedalmond"
214
+ ]
215
+ array_label_rome = searchByRome(codeRome)
216
+ fig = go.Figure(data=[go.Sankey(
217
+ node = dict(
218
+ pad = 15,
219
+ thickness = 20,
220
+ line = dict(color = "black", width = 0.5),
221
+ label = unique_source_target,
222
+ color = colors
223
+ ),
224
+ link = dict(
225
+ source = links_dict["source"],
226
+ target = links_dict["target"],
227
+ value = links_dict["value"],
228
+ color="lightgrey"
229
+ ))]).update_layout(template=template, paper_bgcolor=paper_bgcolor, plot_bgcolor=plot_bgcolor, title_text=title_template + " du code ROME : " + array_label_rome[0]['label'], font_size=10,width=1000, height=800)
230
+ return fig
231
+
232
+ def datavisualisation_chiffres_cles_emplois(url):
233
+ response = requests.get(url)
234
+ soup = BeautifulSoup(response.text, "lxml")
235
+
236
+ alldemandeurs = ''
237
+ allsalaires = ''
238
+ alldifficultes = ''
239
+ allrepartitions = ''
240
+ allentreprises = ''
241
+ allembauches = soup.select('p.population_category')
242
+ allnumembauchesfirst = soup.select('p.population_main-num.data')
243
+ allnumembauches = removeTags(allnumembauchesfirst[0]).split('\xa0')
244
+ allnumembauches = ''.join(allnumembauches)
245
+ allnumoffres = removeTags(allnumembauchesfirst[1]).split('\xa0')
246
+ allnumoffres = ''.join(allnumoffres)
247
+ alldetailembauches = soup.select('p.hiring_text.ng-star-inserted')
248
+ allnumevolutionembauches = soup.select('p.main.ng-star-inserted')
249
+ alldetailevolutionembauches = soup.select('p.population_bubble-title')
250
+ alldemandeurs = "<table><tr><td>Indicateur</td><td>Valeur</td></tr><tr><td>" + removeTags(allembauches[0]) + " (" + removeTags(alldetailembauches[0]) + ");"
251
+ if len(alldetailevolutionembauches) >= 1 and len(allnumevolutionembauches) >= 1:
252
+ alldemandeurs += "\nÉvolution demandeurs d'emploi (" + removeTags(alldetailevolutionembauches[0]) + ": " + removeTags(allnumevolutionembauches[0]) + ")</td>"
253
+ else:
254
+ alldemandeurs += "</td>"
255
+ alldemandeurs += "<td>" + allnumembauches + "</td></tr>"
256
+ alldemandeurs += "<tr><td>" + removeTags(allembauches[1]) + " (" + removeTags(alldetailembauches[1]) + ");"
257
+ if len(alldetailevolutionembauches) >= 2 and len(allnumevolutionembauches) >= 2:
258
+ alldemandeurs += "\nÉvolution offres d'emploi (" + removeTags(alldetailevolutionembauches[1]) + ": " + removeTags(allnumevolutionembauches[1]) + ")</td>"
259
+ else:
260
+ alldemandeurs += "</td>"
261
+ alldemandeurs += "<td>" + allnumoffres + "</td></tr>"
262
+ alldemandeurs += "</table>"
263
+
264
+ allFAP = soup.select('tr.sectorTable__line.ng-star-inserted')
265
+ allcategorie = soup.select('td.sectorTable__cell')
266
+ alltypesalaires = soup.select('th.sectorTable__cell')
267
+ allFAPsalaires = soup.select('p.sectorTable__cellValue')
268
+ if len(allFAPsalaires) >= 3:
269
+ allsalaires = "<table><tr><td>categorie</td><td>emploi</td><td>salaire</td></tr>"
270
+ allsalaires += "<tr><td>" + removeTags(alltypesalaires[1]) + "</td><td>" + removeTags(allcategorie[0]) + "</td><td>" + removeTags(allFAPsalaires[0]).replace('\xa0','').replace(' ','').replace('€','') + "</td></tr>"
271
+ allsalaires += "<tr><td>" + removeTags(alltypesalaires[2]) + "</td><td>" + removeTags(allcategorie[0]) + "</td><td>" + removeTags(allFAPsalaires[1]).replace('\xa0','').replace(' ','').replace('€','') + "</td></tr>"
272
+ allsalaires += "<tr><td>" + removeTags(alltypesalaires[3]) + "</td><td>" + removeTags(allcategorie[0]) + "</td><td>" + removeTags(allFAPsalaires[2]).replace('\xa0','').replace(' ','').replace('€','') + "</td></tr>"
273
+ if len(allFAP) >= 2 and len(allFAPsalaires) == 6:
274
+ allsalaires += "<tr><td>" + removeTags(alltypesalaires[1]) + "</td><td>" + removeTags(allcategorie[4]) + "</td><td>" + removeTags(allFAPsalaires[3]).replace('\xa0','').replace(' ','').replace('€','') + "</td></tr>"
275
+ allsalaires += "<tr><td>" + removeTags(alltypesalaires[2]) + "</td><td>" + removeTags(allcategorie[4]) + "</td><td>" + removeTags(allFAPsalaires[4]).replace('\xa0','').replace(' ','').replace('€','') + "</td></tr>"
276
+ allsalaires += "<tr><td>" + removeTags(alltypesalaires[3]) + "</td><td>" + removeTags(allcategorie[4]) + "</td><td>" + removeTags(allFAPsalaires[5]).replace('\xa0','').replace(' ','').replace('€','') + "</td></tr>"
277
+ allsalaires += "</table>"
278
+
279
+ alltypedifficultes = soup.select('.tabs-main-content_persp-col2-bar.ng-star-inserted')
280
+ alldifficulte = soup.select('p.horizontal-graph_title')
281
+ allpcdifficulte = soup.select('div.horizontal-graph_data')
282
+ alldifficultes = "<table><tr><td>Indicateur</td><td>Valeur</td></tr>"
283
+ for i in range(0,len(alltypedifficultes)):
284
+ alldifficultes += "<tr><td>" + removeTags(alldifficulte[i]) + "</td><td>" + removeTags(allpcdifficulte[i]).replace('Pour le territoire principal FRANCE pour les ' + removeTags(alldifficulte[i]),'').replace('%','') + "</td></tr>"
285
+ alldifficultes += "</table>"
286
+
287
+ alltyperepartitions = soup.select('div.hiring-contract_legende_item.ng-star-inserted')
288
+ allrepartition = soup.select('p.hiring-contract_legende_item_label')
289
+ allpcrepartition = soup.select('span.hiring-contract_legende_item-first')
290
+ allrepartitions = "<table><tr><td>Indicateur</td><td>Valeur</td></tr>"
291
+ for i in range(0,len(alltyperepartitions)):
292
+ allrepartitions += "<tr><td>" + removeTags(allrepartition[i]).replace('(' + removeTags(allpcrepartition[i]) + ')','') + "</td><td>" + removeTags(allpcrepartition[i]).replace('%','').replace(',','.') + "</td></tr>"
293
+ allrepartitions += "</table>"
294
+
295
+ allentrepriserepartitions = soup.select('div.horizontal-graph_pattern.sm-bubble_wrapper > span')
296
+ allentreprise = soup.select('span.sr-only')
297
+ allpcentreprise = soup.select('span.data.ng-star-inserted')
298
+ allentreprises = "<table><tr><td>Indicateur</td><td>Valeur</td></tr>"
299
+ for i in range(0,len(allentrepriserepartitions)):
300
+ allentreprises += "<tr><td>" + removeTags(allentrepriserepartitions[i])[0:-4] + "</td><td>" + removeTags(allentrepriserepartitions[i])[-4:].replace('%','').replace(',','.') + "</td></tr>"
301
+ allentreprises += "</table>"
302
+
303
+ return [alldemandeurs, allsalaires, alldifficultes, allrepartitions, allentreprises]
304
+
305
  def localisation():
306
  ListCentroids = [
307
  { "ID": "01", "Longitude": 5.3245259, "Latitude":46.0666003 },
 
408
 
409
  return ListCentroids
410
 
411
+ def vectorDatabase_connexion():
412
+ pc = Pinecone(api_key=os.environ['PINECONE_API_KEY'])
413
+ index_name = "all-skills"
414
+ index = pc.Index(index_name)
415
+ return index
416
+
417
+ def searchByRome(codeRome):
418
+ index = vectorDatabase_connexion()
419
+ allRome = []
420
+ if codeRome:
421
+ all_docs = index.query(
422
+ top_k=1500,
423
+ vector= [0] * 768, # embedding dimension
424
+ namespace='',
425
+ filter={"categorie": {"$eq": "rome"},"rome": {"$eq": codeRome}},
426
+ include_metadata=True
427
+ )
428
+ else:
429
+ all_docs = index.query(
430
+ top_k=1500,
431
+ vector= [0] * 768, # embedding dimension
432
+ namespace='',
433
+ filter={"categorie": {"$eq": "rome"}},
434
+ include_metadata=True
435
+ )
436
+ for refRome in all_docs['matches']:
437
+ allRome.append({"value": refRome['metadata']['rome'], "label": refRome['metadata']['rome'] + " - " + refRome['metadata']['libelle_rome']})
438
+ return sorted(allRome, key=lambda element:element["value"])
439
+
440
  def connexion_France_Travail():
441
  client = Api(client_id=os.getenv('POLE_EMPLOI_CLIENT_ID'),
442
  client_secret=os.getenv('POLE_EMPLOI_CLIENT_SECRET'))
 
480
  arrowSize=6,
481
  )
482
 
483
+ styleRefresh = {
484
+ "color": "lightgrey",
485
+ "textDecoration" : "none"
486
+ }
487
+
488
  styleTitle = {
489
  "textAlign": "center"
490
  }
 
510
  "marginTop":"25px",
511
  "textAlign": "right",
512
  }
513
+ styleIcon = {
514
+ "marginTop":"10px",
515
+ }
516
  styleSubmitBox = {
517
  "position":"fixed",
518
  "width": "100%",
519
  "top": "calc(100vh - 100px)",
520
  "right": "0"
521
  }
522
+ #datadefault = [
523
+ # {"value": "K2105", "label": "K2105"},
524
+ # {"value": "L1101", "label": "L1101"},
525
+ # {"value": "L1202", "label": "L1202"},
526
+ # {"value": "L1507", "label": "L1507"},
527
+ # {"value": "L1508", "label": "L1508"},
528
+ # {"value": "L1509", "label": "L1509"},
529
+ #]
530
 
531
  def custom_error_handler(err):
532
  # This function defines what we want to happen when an exception occurs
 
616
  {config}
617
  {scripts}
618
  {renderer}
619
+ <div id="custom-footer"></div>
620
  </body>
621
  </html>
622
  '''.format(
 
642
  placeholder="Selectionnez vos Codes ROME",
643
  id="framework-multi-select",
644
  value=['K2105', 'L1101', 'L1202', 'L1507', 'L1508', 'L1509'],
645
+ data=searchByRome(''),
646
  w=600,
647
  mt=10,
648
  styles={
 
673
  dmc.GridCol(html.Div(dmc.Title(f"Le marché et les statistiques de l'emploi", order=1, size="30", my="20", id="chainlit-call-fn", style=styleTitle)), span=5),
674
  dmc.GridCol(html.Div(theme_toggle, style=styleToggle), span=1),
675
  dmc.GridCol(html.Div(dmc.Tooltip(dmc.Button(leftSection=DashIconify(icon="tabler:sparkles", width=30), id="drawer-demo-button"), label="IA générative sur les données",position="left",withArrow=True,arrowSize=6,), style=styleToggle), span=1),
676
+ dmc.GridCol(html.A(DashIconify(icon="tabler:restore", width=20), href='/', style=styleRefresh), p=0,style=styleUSERIA, span=12),
677
+ dmc.GridCol(dmc.Tabs(
678
+ [
679
+ dmc.TabsList(mx="auto",grow=True,
680
+ children=[
681
+ dmc.TabsTab("Marché de l'emploi", leftSection=DashIconify(icon="tabler:graph"), value="1"),
682
+ dmc.TabsTab("Statistiques de l'emploi", leftSection=DashIconify(icon="tabler:chart-pie"), value="2"),
683
+ dmc.TabsTab("Savoir-faire, Savoirs et Contexte des métiers", leftSection=DashIconify(icon="tabler:ikosaedr"), value="3"),
684
+ ]
685
+ ),
686
+ dmc.TabsPanel(
687
+ dmc.Grid(
688
+ children=[
689
+ dmc.GridCol(html.Div(
690
+ dcc.Loading(
691
+ id="loadingRepartition",
692
+ children=(dcc.Graph(id="figRepartition",selectedData={'points': [{'hovertext': ['01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','2A','2B','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35','36','37','38','39','40','41','42','43','44','45','46','47','48','49','50','51','52','53','54','55','56','57','58','59','60','61','62','63','64','65','66','67','68','69','70','71','72','73','74','75','76','77','78','79','80','81','82','83','84','85','86','87','88','89','90','91','92','93','94','95','971','972','973','974']}]})),
693
+ type="default",
694
+ )
695
+ ), span=6),
696
+ dmc.GridCol(html.Div(
697
+ dcc.Loading(
698
+ id="loadingEmplois",
699
+ children=(dcc.Graph(id="figEmplois")),
700
+ type="default",
701
+ )
702
+ ), span=6),
703
+ dmc.GridCol(html.Div(
704
+ dcc.Loading(
705
+ id="loadingContrats",
706
+ children=(dcc.Graph(id="figContrats")),
707
+ type="default",
708
+ )
709
+ ), span=6),
710
+ dmc.GridCol(html.Div(
711
+ dcc.Loading(
712
+ id="loadingExperiences",
713
+ children=(dcc.Graph(id="figExperiences")),
714
+ type="default",
715
+ )
716
+ ), span=6),
717
+ dmc.GridCol(html.Div(
718
+ dcc.Loading(
719
+ id="loadingCompetences",
720
+ children=(dcc.Graph(id="figCompetences")),
721
+ type="default",
722
+ )
723
+ ), span=6),
724
+ dmc.GridCol(html.Div(
725
+ dcc.Loading(
726
+ id="loadingTransversales",
727
+ children=(dcc.Graph(id="figTransversales")),
728
+ type="default",
729
+ )
730
+ ), span=6),
731
+ dmc.GridCol(html.Div(
732
+ dcc.Loading(
733
+ id="loadingNiveau",
734
+ children=(dcc.Graph(id="figNiveau")),
735
+ type="default",
736
+ )
737
+ ), span=6),
738
+ dmc.GridCol(html.Div(
739
+ dcc.Loading(
740
+ id="loadingSecteur",
741
+ children=(dcc.Graph(id="figSecteur")),
742
+ type="default",
743
+ )
744
+ ), span=6),
745
+ dmc.GridCol(html.Div(
746
+ dcc.Loading(
747
+ id="loadingTableau",
748
+ children=(dbc.Container(id="tableauEmplois")),
749
+ type="default",
750
+ )
751
+ ), span=12),
752
+ ]
753
+ )
754
+ , value="1"),
755
+ dmc.TabsPanel(
756
+ children=[
757
+ dmc.Button("Afficher les statistiques des métiers", mt=10, ml="auto", id="loading-button", leftSection=DashIconify(icon="tabler:chart-pie")),
758
+ html.Div(id="clicked-output"),
759
+ html.Div(id="clicked-output-tabs"),
760
+ ], value="2"),
761
+ dmc.TabsPanel(
762
+ children=[
763
+ dmc.Button("Afficher les savoirs des métiers", mt=10, ml="auto", id="loading-skills", leftSection=DashIconify(icon="tabler:ikosaedr")),
764
+ html.Div(id="clicked-output-skills"),
765
+ html.Div(id="clicked-output-skills-tabs"),
766
+ ], value="3"),
767
+ ],
768
+ value="1",
769
+ ), span=12),
770
  ],
771
  gutter="xs",
772
  )
 
776
  )
777
  ],
778
  id="mantine-provider",
779
+ forceColorScheme="light",
780
  theme={
781
  "primaryColor": "indigo",
782
  "fontFamily": "'Inter', sans-serif",
 
835
  Output(component_id='figRepartition', component_property='figure'),
836
  Output(component_id='figCompetences', component_property='figure'),
837
  Output(component_id='figTransversales', component_property='figure'),
838
+ Output(component_id='figNiveau', component_property='figure'),
839
+ Output(component_id='figSecteur', component_property='figure'),
840
  Input(component_id='framework-multi-select', component_property='value'),
841
  Input('figEmplois', 'selectedData'),
842
  Input("mantine-provider", "forceColorScheme"),
 
852
  plot_bgcolor = 'rgba(255, 255, 255, 1)'
853
 
854
  df_FT = API_France_Travail(array_value)
855
+
856
+ ######## localisation ########
857
+ df = df_FT[['intitule','typeContratLibelle','experienceLibelle','lieuTravail','secteurActiviteLibelle']].copy()
858
  df["lieuTravail"] = df["lieuTravail"].apply(lambda x: x['libelle']).apply(lambda x: x[0:3]).apply(lambda x: x.strip())
859
  df.drop(df[df['lieuTravail'] == 'Fra'].index, inplace = True)
860
  df.drop(df[df['lieuTravail'] == 'FRA'].index, inplace = True)
 
874
  options = df['intitule'].values.tolist()
875
  df = df[df['intitule'].isin(options)]
876
 
 
877
  ######## localisation ########
878
  ListCentroids = localisation()
879
  df_localisation = df.groupby('lieuTravail').size().reset_index(name='obs')
 
885
  df_localisation["latitude"] = df_localisation['latitude'].apply(lambda x:[loc['Latitude'] for loc in ListCentroids if loc['ID'] == x]).apply(lambda x:''.join(map(str, x)))
886
  df_localisation["latitude"] = pd.to_numeric(df_localisation["latitude"], downcast="float")
887
 
 
888
  res = requests.get(
889
  "https://raw.githubusercontent.com/codeforgermany/click_that_hood/main/public/data/france-regions.geojson"
890
  )
 
903
  ],
904
  },font=dict(size=10),paper_bgcolor=paper_bgcolor,autosize=True,clickmode='event+select'
905
  )
906
+
907
+ ######## Compétences professionnelles ########
908
  df_FT.dropna(subset=['qualitesProfessionnelles','formations','competences'], inplace=True)
909
  df_FT["competences"] = df_FT["competences"].apply(lambda x:[str(e['libelle']) for e in x]).apply(lambda x:'; '.join(map(str, x)))
910
  df_FT["qualitesProfessionnelles"] = df_FT["qualitesProfessionnelles"].apply(lambda x:[str(e['libelle']) + ": " + str(e['description']) for e in x]).apply(lambda x:'; '.join(map(str, x)))
911
 
 
912
  df_comp = df_FT
913
  df_comp['competences'] = df_FT['competences'].str.split(';')
914
  df_comp = df_comp.explode('competences')
915
  df_comp = df_comp.groupby('competences').size().reset_index(name='obs')
916
  df_comp = df_comp.sort_values(by=['obs'])
917
+ df_comp = df_comp.iloc[-25:]
918
  fig_competences = px.bar(df_comp, x='obs', y='competences', orientation='h', color='obs', height=600, template=template, title="Les principales compétences professionnelles", labels={'obs':'nombre'}, color_continuous_scale="Teal", text_auto=True).update_layout(font=dict(size=10),paper_bgcolor=paper_bgcolor,plot_bgcolor=plot_bgcolor,clickmode='event+select',autosize=True).update_traces(hovertemplate=df_comp["competences"] + ' <br>Nombre : %{x}', y=[y[:100] + "..." for y in df_comp['competences']], showlegend=False)
919
 
920
  ######## Compétences transversales ########
 
923
  df_comptransversales = df_transversales.explode('qualitesProfessionnelles')
924
  df_comptransversales = df_comptransversales.groupby('qualitesProfessionnelles').size().reset_index(name='obs')
925
  df_comptransversales = df_comptransversales.sort_values(by=['obs'])
926
+ df_comptransversales = df_comptransversales.iloc[-25:]
927
  fig_transversales = px.bar(df_comptransversales, x='obs', y='qualitesProfessionnelles', orientation='h', color='obs', height=600, template=template, title="Les principales compétences transversales", labels={'obs':'nombre'}, color_continuous_scale="Teal", text_auto=True).update_layout(font=dict(size=10),paper_bgcolor=paper_bgcolor,plot_bgcolor=plot_bgcolor,autosize=True).update_traces(hovertemplate=df_comptransversales["qualitesProfessionnelles"] + ' <br>Nombre : %{x}', y=[y[:80] + "..." for y in df_comptransversales["qualitesProfessionnelles"]], showlegend=False)
928
 
929
+ ######## Niveaux de qualification ########
930
+ df_niveau = df_FT
931
+ df_niveau["formations"] = df_niveau["formations"].apply(lambda x:[str(e['niveauLibelle']) for e in x]).apply(lambda x:'; '.join(map(str, x)))
932
+ df_niveau = df_niveau.groupby('formations').size().reset_index(name='obs')
933
+ fig_niveau = px.pie(df_niveau, names='formations', height=600, values='obs', color='obs', template=template, title="Les niveaux de qualification", labels={'obs':'nombre'}, color_discrete_sequence=px.colors.qualitative.Safe).update_traces(textposition='inside', textinfo='percent+label').update_layout(font=dict(size=10),paper_bgcolor=paper_bgcolor)
934
+
935
+ ######## Secteurs ########
936
+ df_secteur = df.groupby('secteurActiviteLibelle').size().reset_index(name='obs')
937
+ df_secteur = df_secteur.sort_values(by=['obs'])
938
+ df_secteur = df_secteur.iloc[-25:]
939
+ fig_secteur = px.bar(df_secteur, x='obs', y='secteurActiviteLibelle', height=600, orientation='h', color='obs', template=template, title="Les principaux secteurs d'activités", labels={'obs':'nombre'}, color_continuous_scale="Teal", text_auto=True).update_layout(font=dict(size=10),paper_bgcolor=paper_bgcolor,plot_bgcolor=plot_bgcolor,autosize=True).update_traces(hovertemplate=df_secteur["secteurActiviteLibelle"] + ' <br>Nombre : %{x}', y=[y[:80] + "..." for y in df_secteur["secteurActiviteLibelle"]], showlegend=False)
940
+
941
+
942
+ return fig_localisation, fig_competences, fig_transversales, fig_niveau, fig_secteur
943
 
944
  def create_emploi(df, theme):
945
  if theme == "dark":
 
985
 
986
  return fig_experience
987
 
988
+ def create_tableau(df, theme):
989
+ if theme == "dark":
990
+ style_header = {
991
+ 'fontFamily': "'Inter', sans-serif",
992
+ 'fontSize': '10px',
993
+ 'backgroundColor': 'rgb(30, 30, 30)',
994
+ 'color': 'white'
995
+ }
996
+ style_data={
997
+ 'fontFamily': "'Inter', sans-serif",
998
+ 'fontSize': '10px',
999
+ 'backgroundColor': 'rgb(50, 50, 50)',
1000
+ 'color': 'white'
1001
+ }
1002
+ style_tooltip='background-color: lightgrey; font-family: "Inter", sans-serif; font-size:10px; color: white'
1003
+ else:
1004
+ style_header = {
1005
+ 'fontFamily': "'Inter', sans-serif",
1006
+ 'fontSize': '10px',
1007
+ 'backgroundColor': 'transparent',
1008
+ 'color': 'black'
1009
+ }
1010
+ style_data={
1011
+ 'fontFamily': "'Inter', sans-serif",
1012
+ 'fontSize': '10px',
1013
+ 'backgroundColor': 'transparent',
1014
+ 'color': 'black'
1015
+ }
1016
+ style_tooltip='background-color: lightgrey; font-family: "Inter", sans-serif; font-size:10px; color: black'
1017
+
1018
+ ######## Tableau des emplois ########
1019
+ #df = df.fillna('N/A').replace('', 'N/A')
1020
+ df_tableau = df[['origineOffre','intitule','typeContratLibelle','experienceLibelle','description','lieuTravail']].copy()
1021
+ dictHeader = {'origineOffre': 'Lien','intitule': 'Offre','typeContratLibelle': 'Type de contrat','experienceLibelle':'Expérience','description':'Détail','lieuTravail':'Département'}
1022
+ df_tableau.rename(columns=dictHeader,inplace=True)
1023
+ tableau_Emplois = dash_table.DataTable(
1024
+ data=df_tableau.to_dict('records'),
1025
+ sort_action='native',
1026
+ columns=[{'id': c, 'name': c, 'presentation': 'markdown'} if c == 'Lien' else {'id': c, 'name': c} for c in df_tableau.columns],
1027
+ filter_action="native",
1028
+ filter_options={"placeholder_text": "Filtrer les valeurs de la colonne..."},
1029
+ page_action='native',
1030
+ page_current= 0,
1031
+ page_size= 10,
1032
+ style_header=style_header,
1033
+ style_data=style_data,
1034
+ style_table={'overflowX': 'auto'},
1035
+ style_cell={
1036
+ 'overflow': 'hidden',
1037
+ 'textOverflow': 'ellipsis',
1038
+ 'maxWidth': 0,
1039
+ },
1040
+ tooltip_data=[
1041
+ {
1042
+ column: {'value': str(value), 'type': 'markdown'}
1043
+ for column, value in row.items()
1044
+ } for row in df_tableau.to_dict('records')
1045
+ ],
1046
+ css=[{
1047
+ 'selector': '.dash-table-tooltip',
1048
+ 'rule': style_tooltip
1049
+ },{
1050
+ 'selector': '.dash-table-tooltip > p',
1051
+ 'rule': style_tooltip
1052
+ }],
1053
+ tooltip_delay=0,
1054
+ tooltip_duration=None
1055
+ )
1056
+ return tableau_Emplois
1057
+
1058
  @callback(
1059
  Output(component_id='figEmplois', component_property='figure'),
1060
  Input('figRepartition', 'selectedData'),
 
1144
 
1145
  return create_experience(df, theme)
1146
 
1147
+ @callback(
1148
+ Output(component_id='tableauEmplois', component_property='children'),
1149
+ Input('figRepartition', 'selectedData'),
1150
+ Input(component_id='framework-multi-select', component_property='value'),
1151
+ Input("mantine-provider", "forceColorScheme"),
1152
+ )
1153
+
1154
+ def update_tableau(selectedData, array_value, theme):
1155
+ options = []
1156
+ if selectedData != None:
1157
+ if type(selectedData['points'][0]['hovertext']) == str:
1158
+ options.append(selectedData['points'][0]['hovertext'])
1159
+ else:
1160
+ options = selectedData['points'][0]['hovertext']
1161
+ else:
1162
+ options = ['01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','2A','2B','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35','36','37','38','39','40','41','42','43','44','45','46','47','48','49','50','51','52','53','54','55','56','57','58','59','60','61','62','63','64','65','66','67','68','69','70','71','72','73','74','75','76','77','78','79','80','81','82','83','84','85','86','87','88','89','90','91','92','93','94','95','971','972','973','974']
1163
+
1164
+ df_FT = API_France_Travail(array_value)
1165
+ df_FT["origineOffre"] = df_FT["origineOffre"].apply(lambda x: "[Voir l'offre sur le site web de France Travail](" + x['urlOrigine'] + ")")
1166
+ df_FT["lieuTravail"] = df_FT["lieuTravail"].apply(lambda x: x['libelle']).apply(lambda x: x[0:3]).apply(lambda x: x.strip())
1167
+ df_FT.drop(df_FT[df_FT['lieuTravail'] == 'Fra'].index, inplace = True)
1168
+ df_FT.drop(df_FT[df_FT['lieuTravail'] == 'FRA'].index, inplace = True)
1169
+ df_FT.drop(df_FT[df_FT['lieuTravail'] == 'Ile'].index, inplace = True)
1170
+ df_FT.drop(df_FT[df_FT['lieuTravail'] == 'Mar'].index, inplace = True)
1171
+ df_FT.drop(df_FT[df_FT['lieuTravail'] == 'Bou'].index, inplace = True)
1172
+ df_FT.drop(df_FT[df_FT['lieuTravail'] == '976'].index, inplace = True)
1173
+ df_FT = df_FT[df_FT['lieuTravail'].isin(options)]
1174
+
1175
+ return create_tableau(df_FT, theme)
1176
+
1177
+ clientside_callback(
1178
+ """
1179
+ function updateLoadingState(n_clicks) {
1180
+ return true
1181
+ }
1182
+ """,
1183
+ Output("loading-button", "loading", allow_duplicate=True),
1184
+ Input("loading-button", "n_clicks"),
1185
+ prevent_initial_call=True,
1186
+ )
1187
+
1188
+ @callback(
1189
+ Output("clicked-output", "children"),
1190
+ Output("clicked-output-tabs", "children"),
1191
+ Output("loading-button", "loading"),
1192
+ Input("loading-button", "n_clicks"),
1193
+ Input(component_id='framework-multi-select', component_property='value'),
1194
+ Input("mantine-provider", "forceColorScheme"),
1195
+ prevent_initial_call=True,
1196
+ )
1197
+ def load_from_stats(n_clicks, array_value, theme):
1198
+ if theme == "dark":
1199
+ template = "plotly_dark"
1200
+ paper_bgcolor = 'rgba(36, 36, 36, 1)'
1201
+ plot_bgcolor = 'rgba(36, 36, 36, 1)'
1202
+ style_header = {
1203
+ 'fontFamily': "'Inter', sans-serif",
1204
+ 'fontSize': '10px',
1205
+ 'backgroundColor': 'rgb(30, 30, 30)',
1206
+ 'color': 'white'
1207
+ }
1208
+ style_data={
1209
+ 'fontFamily': "'Inter', sans-serif",
1210
+ 'fontSize': '10px',
1211
+ 'backgroundColor': 'rgb(50, 50, 50)',
1212
+ 'color': 'white'
1213
+ }
1214
+ else:
1215
+ template = "ggplot2"
1216
+ paper_bgcolor = 'rgba(255, 255, 255, 1)'
1217
+ plot_bgcolor = 'rgba(255, 255, 255, 1)'
1218
+ style_header = {
1219
+ 'fontFamily': "'Inter', sans-serif",
1220
+ 'fontSize': '10px',
1221
+ 'backgroundColor': 'transparent',
1222
+ 'color': 'black'
1223
+ }
1224
+ style_data={
1225
+ 'fontFamily': "'Inter', sans-serif",
1226
+ 'fontSize': '10px',
1227
+ 'backgroundColor': 'transparent',
1228
+ 'color': 'black'
1229
+ }
1230
+ children = []
1231
+ children_tabs = []
1232
+ for j in range(0, len(array_value)):
1233
+ table = datavisualisation_chiffres_cles_emplois("https://dataemploi.pole-emploi.fr/metier/chiffres-cles/NAT/FR/" + array_value[j])
1234
+ array_label_rome = searchByRome(array_value[j])
1235
+ df_demandeur = htmlToDataframe(table[0])
1236
+ df_demandeur = df_demandeur.sort_values(by=['Indicateur'])
1237
+ fig_demandeur = px.histogram(df_demandeur, x='Indicateur', y='Valeur', height=800, template=template, title="Demandeurs d'emploi et offres d'emploi du code ROME : " + array_label_rome[0]['label'], color='Indicateur', labels={'Valeur':'Nombre'}, text_auto=True).update_layout(font=dict(size=9),paper_bgcolor=paper_bgcolor,plot_bgcolor=plot_bgcolor,autosize=True)
1238
+ children.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=(dcc.Graph(figure=fig_demandeur)),type="default")), span=6),)
1239
+ children_tabs.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=[dbc.Label("Demandeurs d'emploi et offres d'emploi du code ROME : " + array_label_rome[0]['label']),dash_table.DataTable(data=df_demandeur.to_dict('records'),sort_action='native', columns=[{'id': c, 'name': c} for c in df_demandeur.columns],page_action='native', page_current= 0,page_size= 10,style_header=style_header,style_data=style_data,style_table={'overflowX': 'auto'},style_cell={'overflow': 'hidden','textOverflow': 'ellipsis','maxWidth': 0,})],type="default")), span=12),)
1240
+ if len(table[1]) > 0:
1241
+ df_salaire = htmlToDataframe(table[1])
1242
+ df_salaire = df_salaire.sort_values(by=['salaire'])
1243
+ fig_salaire = px.histogram(df_salaire, x='emploi', y='salaire', height=600, template=template, barmode='group', title="Salaires médians du code ROME : " + array_label_rome[0]['label'], color='categorie', text_auto=True).update_layout(font=dict(size=9),paper_bgcolor=paper_bgcolor,plot_bgcolor=plot_bgcolor,autosize=True)
1244
+ children.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=(dcc.Graph(figure=fig_salaire)),type="default")), span=6),)
1245
+ children_tabs.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=[dbc.Label("Salaires médians du code ROME : " + array_label_rome[0]['label']),dash_table.DataTable(data=df_salaire.to_dict('records'),sort_action='native', columns=[{'id': c, 'name': c} for c in df_salaire.columns],page_action='native', page_current= 0,page_size= 10,style_header=style_header,style_data=style_data,style_table={'overflowX': 'auto'},style_cell={'overflow': 'hidden','textOverflow': 'ellipsis','maxWidth': 0,})],type="default")), span=12),)
1246
+ df_difficulte = htmlToDataframe(table[2])
1247
+ if len(df_difficulte) == 0:
1248
+ title = "Aucune donnée difficulté de recrutement renseignée!"
1249
+ else:
1250
+ title = "Difficulté de recrutement du code ROME : " + array_label_rome[0]['label']
1251
+ df_difficulte = df_difficulte.sort_values(by=['Valeur'])
1252
+ fig_difficulte = px.histogram(df_difficulte, x='Indicateur', y='Valeur', height=600, template=template, title=title, color='Indicateur', labels={'Valeur':'Pourcentage'}, text_auto=True).update_layout(font=dict(size=9),paper_bgcolor=paper_bgcolor,plot_bgcolor=plot_bgcolor,autosize=True)
1253
+ children.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=(dcc.Graph(figure=fig_difficulte)),type="default")), span=6))
1254
+ children_tabs.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=[dbc.Label(title),dash_table.DataTable(data=df_difficulte.to_dict('records'),sort_action='native', columns=[{'id': c, 'name': c} for c in df_difficulte.columns],page_action='native', page_current= 0,page_size= 10,style_header=style_header,style_data=style_data,style_table={'overflowX': 'auto'},style_cell={'overflow': 'hidden','textOverflow': 'ellipsis','maxWidth': 0,})],type="default")), span=12),)
1255
+ df_repartitionContrat = htmlToDataframe(table[3])
1256
+ df_repartitionContrat = df_repartitionContrat.sort_values(by=['Valeur'])
1257
+ fig_repartitionContrat = px.pie(df_repartitionContrat, names='Indicateur', values='Valeur', color='Indicateur', template=template, title="Répartition des embauches du métier : type de contrat du code ROME : " + array_label_rome[0]['label'], labels={'Valeur':'pourcentage'}, color_discrete_sequence=px.colors.qualitative.Safe).update_traces(textposition='inside', textinfo='percent+label').update_layout(font=dict(size=10),paper_bgcolor=paper_bgcolor)
1258
+ children.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=(dcc.Graph(figure=fig_repartitionContrat)),type="default")), span=6))
1259
+ children_tabs.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=[dbc.Label("Répartition des embauches du métier : type de contrat du code ROME : " + array_label_rome[0]['label']),dash_table.DataTable(data=df_repartitionContrat.to_dict('records'),sort_action='native', columns=[{'id': c, 'name': c} for c in df_repartitionContrat.columns],page_action='native', page_current= 0,page_size= 10,style_header=style_header,style_data=style_data,style_table={'overflowX': 'auto'},style_cell={'overflow': 'hidden','textOverflow': 'ellipsis','maxWidth': 0,})],type="default")), span=12),)
1260
+ df_repartitionEntreprise = htmlToDataframe(table[4])
1261
+ df_repartitionEntreprise = df_repartitionEntreprise.sort_values(by=['Valeur'])
1262
+ fig_repartitionEntreprise = px.pie(df_repartitionEntreprise, names='Indicateur', values='Valeur', color='Indicateur', template=template, title="Répartition des embauches du métier : type entreprise du code ROME : " + array_label_rome[0]['label'], labels={'Valeur':'pourcentage'}, color_discrete_sequence=px.colors.qualitative.Safe).update_traces(textposition='inside', textinfo='percent+label').update_layout(font=dict(size=10),paper_bgcolor=paper_bgcolor)
1263
+ children.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=(dcc.Graph(figure=fig_repartitionEntreprise)),type="default")), span=6))
1264
+ children_tabs.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=[dbc.Label("Répartition des embauches du métier : type entreprise du code ROME : " + array_label_rome[0]['label']),dash_table.DataTable(data=df_repartitionEntreprise.to_dict('records'),sort_action='native', columns=[{'id': c, 'name': c} for c in df_repartitionEntreprise.columns],page_action='native', page_current= 0,page_size= 10,style_header=style_header,style_data=style_data,style_table={'overflowX': 'auto'},style_cell={'overflow': 'hidden','textOverflow': 'ellipsis','maxWidth': 0,})],type="default")), span=12),)
1265
+ return dmc.Grid(children=children), dmc.Grid(children=children_tabs), False
1266
+
1267
+ clientside_callback(
1268
+ """
1269
+ function updateLoadingState(n_clicks) {
1270
+ return true
1271
+ }
1272
+ """,
1273
+ Output("loading-skills", "loading", allow_duplicate=True),
1274
+ Input("loading-skills", "n_clicks"),
1275
+ prevent_initial_call=True,
1276
+ )
1277
+
1278
+ @callback(
1279
+ Output("clicked-output-skills", "children"),
1280
+ Output("loading-skills", "loading"),
1281
+ Input("loading-skills", "n_clicks"),
1282
+ Input(component_id='framework-multi-select', component_property='value'),
1283
+ Input("mantine-provider", "forceColorScheme"),
1284
+ prevent_initial_call=True,
1285
+ )
1286
+
1287
+ def load_from_skills(n_clicks, array_value, theme):
1288
+ if theme == "dark":
1289
+ template = "plotly_dark"
1290
+ paper_bgcolor = 'rgba(36, 36, 36, 1)'
1291
+ plot_bgcolor = 'rgba(36, 36, 36, 1)'
1292
+ else:
1293
+ template = "ggplot2"
1294
+ paper_bgcolor = 'rgba(255, 255, 255, 1)'
1295
+ plot_bgcolor = 'rgba(255, 255, 255, 1)'
1296
+ children = []
1297
+ for j in range(0, len(array_value)):
1298
+ ficheSF = getSavoirFaireFromHTMLMetier("https://candidat.francetravail.fr/metierscope/fiche-metier/" + array_value[j])
1299
+ fig_SF = datavisualisation_skills_context(htmlToDataframe(ficheSF), template, paper_bgcolor, plot_bgcolor, "Savoir-faire", array_value[j])
1300
+ ficheSavoir = getSavoirFromHTMLMetier("https://candidat.francetravail.fr/metierscope/fiche-metier/" + array_value[j])
1301
+ fig_Savoir = datavisualisation_skills_context(htmlToDataframe(ficheSavoir), template, paper_bgcolor, plot_bgcolor, "Savoirs", array_value[j])
1302
+ ficheContext = getContextFromHTMLMetier("https://candidat.francetravail.fr/metierscope/fiche-metier/" + array_value[j])
1303
+ fig_Context = datavisualisation_skills_context(htmlToDataframe(ficheContext), template, paper_bgcolor, plot_bgcolor, "Contexte", array_value[j])
1304
+
1305
+ children.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=(dcc.Graph(figure=fig_SF)), type="default"), style=styleTitle), span=12),)
1306
+ children.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=(dcc.Graph(figure=fig_Savoir)), type="default"), style=styleTitle), span=12),)
1307
+ children.append(dmc.GridCol(html.Div(dcc.Loading(id="loadingPlot",children=(dcc.Graph(figure=fig_Context)), type="default"), style=styleTitle), span=12),)
1308
+
1309
+ return dmc.Grid(children=children), False
1310
+
1311
+
1312
  ########### IA Chatbot ###########
1313
  @app.callback(
1314
  Output("display-conversation", "children"), [Input("store-conversation", "data")]
 
1346
  list_FT = df_FT_Select.values.tolist()
1347
  context = ''
1348
  for i in range(0,len(list_FT)):
1349
+ context += "\n✔️ Emploi : " + str(list_FT[i][0]) + ";\n◉ Contrat : " + str(list_FT[i][1]) + ";\n◉ Compétences professionnelles : " + str(list_FT[i][3]).replace("{","").replace("}","").replace("[","").replace("]","").replace("code","").replace("libelle","") + ";\n" + "◉ Salaire : " + str(list_FT[i][6]).replace("{","").replace("}","").replace("[","").replace("]","") + ";\n◉ Qualification : " + str(list_FT[i][5]).replace("'libelle'","\n• 'libelle").replace("{","").replace("}","").replace("[","").replace("]","").replace("code","") + ";\n◉ Localisation : " + str(list_FT[i][7]).replace("{","").replace("}","").replace("[","").replace("]","") + ";\n◉ Expérience : " + str(list_FT[i][2]) + ";\n◉ Niveau de qualification : " + str(list_FT[i][8]).replace("{","").replace("}","").replace("[","").replace("]","") + ";\n◉ Description de l'emploi : " + str(list_FT[i][4]) + "\n"
1350
  #context = df_FT.to_string(index=False)
1351
  template = """<s>[INST] Vous êtes un ingénieur pédagogique de l'enseignement supérieur et vous êtes doué pour faire des analyses des formations de l'enseignement supérieur et de faire le rapprochement entre les compétences académiques et les compétences professionnelles attendues par le marché de l'emploi et les les recruteurs, en fonction des critères définis ci-avant. En fonction des informations suivantes et du contexte suivant seulement et strictement, répondez en langue française strictement à la question ci-dessous, en 5000 mots au moins. Lorsque cela est possible, cite les sources du contexte. Si vous ne pouvez pas répondre à la question sur la base des informations, dites que vous ne trouvez pas de réponse ou que vous ne parvenez pas à trouver de réponse. Essayez donc de comprendre en profondeur le contexte et répondez uniquement en vous basant sur les informations fournies. Ne générez pas de réponses non pertinentes.
1352
  Répondez à la question ci-dessous à partir du contexte ci-dessous :
 
1379
  #repo_id = "microsoft/Phi-3.5-mini-instruct"
1380
  #mistral_url = "https://api-inference.huggingface.co/models/mistralai/Mixtral-8x22B-Instruct-v0.1"
1381
  llm = HuggingFaceEndpoint(
1382
+ repo_id=repo_id, task="text2text-generation", max_new_tokens=8000, temperature=0.3, streaming=True
1383
  )
1384
  model_output = ""
1385
  chain = prompt | llm
1386
+ for s in chain.stream({"question":"D'après le contexte, " + user_input,"context":context_p}):
1387
  model_output = model_output + s
1388
  print(s, end="", flush=True)
1389
 
 
1400
  chat_history += f"{model_output}<split>"
1401
 
1402
  return chat_history, None
1403
+
1404
  if __name__ == '__main__':
1405
  app.run_server(debug=True)