JerLag commited on
Commit
dbf4e3a
·
verified ·
1 Parent(s): 284b24d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +34 -44
app.py CHANGED
@@ -16,7 +16,7 @@ import plotly.io as pio
16
  VB_CSS = r"""
17
  @import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;700;800&display=swap');
18
 
19
- /* Variables Gradio (force clair + accents violet/cyan) */
20
  :root{
21
  --body-background-fill:#F8FAFC;
22
  --panel-background-fill:#FFFFFF;
@@ -29,21 +29,25 @@ VB_CSS = r"""
29
  --radius-lg:14px;
30
  --shadow-drop: 0 10px 26px rgba(2,6,23,.08);
31
 
 
32
  --color-accent:#7C3AED;
33
  --color-accent-soft:#EDE9FE;
34
  --button-primary-background-fill:#7C3AED;
35
  --button-primary-text-color:#ffffff;
36
 
 
37
  --input-background-fill:#FFFFFF;
38
  --input-border-color:#E2E8F0;
39
  --input-text-color:#0F172A;
40
  --input-placeholder-color:#6B7280;
41
 
 
42
  --table-row-background-fill:#FFFFFF;
43
  --table-row-text-color:#0F172A;
44
  --table-border-color:#E2E8F0;
45
  }
46
 
 
47
  :root{
48
  --vb-bg:#F8FAFC;
49
  --vb-card:#FFFFFF;
@@ -59,6 +63,7 @@ VB_CSS = r"""
59
  --vb-shadow:0 10px 26px rgba(2,6,23,.08);
60
  }
61
 
 
62
  * { color-scheme: light !important; }
63
  html,body,.gradio-container{
64
  background:var(--vb-bg) !important;
@@ -98,7 +103,7 @@ html,body,.gradio-container{
98
  box-shadow:var(--vb-shadow);
99
  }
100
 
101
- /* ---------- Labels / rubans ---------- */
102
  .gradio-container [data-testid="block-label"],
103
  .gradio-container .component-label,
104
  .gradio-container .label,
@@ -111,6 +116,8 @@ html,body,.gradio-container{
111
  border:none !important;
112
  padding:0 !important;
113
  }
 
 
114
  .gradio-container .wrap > div[class*="absolute"][class*="top-0"][class*="left-0"],
115
  .gradio-container [class*="absolute"][class*="top-0"][class*="left-0"][class*="rounded-b"],
116
  .gradio-container [class*="absolute"][class*="top-0"][class*="left-0"][class*="rounded-br"]{
@@ -120,7 +127,7 @@ html,body,.gradio-container{
120
  border:none !important;
121
  }
122
 
123
- /* ---------- Onglets ---------- */
124
  .gradio-container [role="tablist"],
125
  .gradio-container [role="tab"]{
126
  background:#fff !important;
@@ -131,6 +138,7 @@ html,body,.gradio-container{
131
  .gradio-container [role="tab"][aria-selected="true"]{
132
  background:linear-gradient(90deg, rgba(124,58,237,.12), rgba(6,182,212,.12)) !important;
133
  color:#0F172A !important;
 
134
  }
135
 
136
  /* ---------- Textes ---------- */
@@ -162,23 +170,9 @@ html,body,.gradio-container{
162
  box-shadow:0 0 0 2px rgba(124,58,237,.35), 0 0 0 4px rgba(6,182,212,.25) !important;
163
  }
164
 
165
- /* ---------- Checkboxes (même couleur que le bouton) ---------- */
166
- /* Fallback simple */
167
- .gradio-container input[type="checkbox"]{
168
- accent-color: var(--vb-primary) !important;
169
- }
170
- /* État coché : on superpose l'icône + le dégradé violet→cyan */
171
- .gradio-container input[type="checkbox"]:checked{
172
- background-image: var(--checkbox-check), linear-gradient(90deg, var(--vb-primary), var(--vb-primary-2)) !important;
173
- background-repeat: no-repeat, no-repeat !important;
174
- background-position: center center, center center !important;
175
- border-color: var(--vb-primary) !important;
176
- }
177
- /* Focus & hover cohérents */
178
- .gradio-container input[type="checkbox"]:focus-visible{
179
- outline: none !important;
180
- box-shadow: 0 0 0 2px rgba(124,58,237,.35), 0 0 0 4px rgba(6,182,212,.25) !important;
181
- }
182
 
183
  /* ---------- Sliders ---------- */
184
  .gradio-container .noUi-target{
@@ -205,30 +199,22 @@ html,body,.gradio-container{
205
  }
206
  .gradio-container .vb-cta:hover{transform:translateY(-2px);filter:brightness(1.05)}
207
 
208
- /* ---------- DataFrames / Tables (anti-bandes noires) ---------- */
209
- .gradio-container .table,
210
- .gradio-container .table * ,
211
- .gradio-container .svelte-virtual-table-viewport,
212
- .gradio-container .table-wrap{
213
- background:#fff !important;
214
- color:#0F172A !important;
215
- border-color:#E2E8F0 !important;
216
  }
217
- .gradio-container .table thead,
218
- .gradio-container .table thead tr,
219
- .gradio-container .table thead th{
 
220
  background:linear-gradient(90deg, rgba(124,58,237,.12), rgba(6,182,212,.12)) !important;
221
- color:#0F172A !important; border-bottom:1px solid #E2E8F0 !important;
222
  }
223
- .gradio-container .table thead th .header-button,
224
- .gradio-container .table thead th [class*="header-button"],
225
- .gradio-container .header-button{
226
- background:transparent !important; color:#0F172A !important;
227
- box-shadow:none !important; border:none !important;
228
  }
229
- .gradio-container .table caption{color:#0F172A !important}
230
 
231
- /* ---------- Files ---------- */
232
  .gradio-container [class*="file"]{
233
  background:#fff !important; color:#0F172A !important;
234
  border-color:var(--vb-border) !important;
@@ -271,6 +257,7 @@ LOGO_SVG = """<svg xmlns='http://www.w3.org/2000/svg' width='224' height='38' vi
271
  </svg>"""
272
 
273
  # ====================== UNIDECODE (fallback) ======================
 
274
  try:
275
  from unidecode import unidecode
276
  except Exception:
@@ -282,6 +269,7 @@ except Exception:
282
  return str(x)
283
 
284
  # ====================== THÉSAURUS ASSURANCE ======================
 
285
  THEMES = {
286
  "Remboursements santé":[r"\bremboursement[s]?\b", r"\bt[eé]l[eé]transmission\b", r"\bno[eé]mie\b",
287
  r"\bprise\s*en\s*charge[s]?\b", r"\btaux\s+de\s+remboursement[s]?\b", r"\b(ameli|cpam)\b",
@@ -313,6 +301,7 @@ THEMES = {
313
  }
314
 
315
  # ====================== SENTIMENT (règles) ======================
 
316
  POS_WORDS = {"bien":1.0,"super":1.2,"parfait":1.4,"excellent":1.5,"ravi":1.2,"satisfait":1.0,
317
  "rapide":0.8,"efficace":1.0,"fiable":1.0,"simple":0.8,"facile":0.8,"clair":0.8,"conforme":0.8,
318
  "sympa":0.8,"professionnel":1.0,"réactif":1.0,"reactif":1.0,"compétent":1.0,"competent":1.0,
@@ -327,6 +316,7 @@ DIMINISHERS = [r"\bun[e]?\s+peu\b", r"\bassez\b", r"\bplut[oô]t\b", r"\bl[eé]
327
  INTENSIFIER_W, DIMINISHER_W = 1.5, 0.7
328
 
329
  # ====================== OpenAI (optionnel) ======================
 
330
  OPENAI_AVAILABLE = False
331
  try:
332
  from openai import OpenAI
@@ -336,6 +326,7 @@ except Exception:
336
  OPENAI_AVAILABLE = False
337
 
338
  # ====================== UTILS ======================
 
339
  def normalize(t:str)->str:
340
  if not isinstance(t,str): return ""
341
  return re.sub(r"\s+"," ",t.strip())
@@ -460,6 +451,7 @@ def infer_nps_from_sentiment(label: str, score: float) -> int:
460
  return 8 if score >= 0 else 7
461
 
462
  # ====================== GRAPHIQUES ======================
 
463
  def fig_nps_gauge(nps: Optional[float]) -> go.Figure:
464
  v = 0.0 if nps is None else float(nps)
465
  return go.Figure(go.Indicator(mode="gauge+number", value=v,
@@ -485,6 +477,7 @@ def fig_theme_balance(themes_df: pd.DataFrame, k: int) -> go.Figure:
485
  fig.update_layout(xaxis_tickangle=-30); return fig
486
 
487
  # ====================== ANALYSE ======================
 
488
  def analyze_text(pasted_txt, has_sc, sep_chr,
489
  do_anonymize, use_oa_sent, use_oa_themes, use_oa_summary,
490
  oa_model, oa_temp, top_k):
@@ -584,7 +577,7 @@ def analyze_text(pasted_txt, has_sc, sep_chr,
584
  nps_label = "NPS global (inféré)" if any_inferred else "NPS global"
585
  lines=[ "# Synthèse NPS & ressentis clients",
586
  f"- **Méthode** : {method}",
587
- f"- **{nps_label}** : {nps:.1f}" if nps is not None else f"- **{nps_label}** : n/a" ]
588
  if dist:
589
  tot=sum(dist.values()); pos=dist.get("positive",0); neg=dist.get("negatif",0); neu=dist.get("neutre",0)
590
  lines.append(f"- **Répartition émotions** : positive {pos}/{tot}, neutre {neu}/{tot}, négative {neg}/{tot}")
@@ -648,10 +641,7 @@ def analyze_text(pasted_txt, has_sc, sep_chr,
648
 
649
  # ====================== UI ======================
650
 
651
- def apply_plotly_theme_wrapper():
652
- apply_plotly_theme()
653
-
654
- apply_plotly_theme_wrapper()
655
 
656
  with gr.Blocks(title="Verbatify — Analyse NPS", css=VB_CSS) as demo:
657
  gr.HTML(
 
16
  VB_CSS = r"""
17
  @import url('https://fonts.googleapis.com/css2?family=Manrope:wght@400;600;700;800&display=swap');
18
 
19
+ /* ---------- Variables Gradio (écrasent les thèmes host) ---------- */
20
  :root{
21
  --body-background-fill:#F8FAFC;
22
  --panel-background-fill:#FFFFFF;
 
29
  --radius-lg:14px;
30
  --shadow-drop: 0 10px 26px rgba(2,6,23,.08);
31
 
32
+ /* Accent (pas d’orange) */
33
  --color-accent:#7C3AED;
34
  --color-accent-soft:#EDE9FE;
35
  --button-primary-background-fill:#7C3AED;
36
  --button-primary-text-color:#ffffff;
37
 
38
+ /* Inputs */
39
  --input-background-fill:#FFFFFF;
40
  --input-border-color:#E2E8F0;
41
  --input-text-color:#0F172A;
42
  --input-placeholder-color:#6B7280;
43
 
44
+ /* Tables */
45
  --table-row-background-fill:#FFFFFF;
46
  --table-row-text-color:#0F172A;
47
  --table-border-color:#E2E8F0;
48
  }
49
 
50
+ /* Palette dégradé violet → cyan */
51
  :root{
52
  --vb-bg:#F8FAFC;
53
  --vb-card:#FFFFFF;
 
63
  --vb-shadow:0 10px 26px rgba(2,6,23,.08);
64
  }
65
 
66
+ /* Forcer un look clair */
67
  * { color-scheme: light !important; }
68
  html,body,.gradio-container{
69
  background:var(--vb-bg) !important;
 
103
  box-shadow:var(--vb-shadow);
104
  }
105
 
106
+ /* ---------- Labels / rubans (supprime les pills noires) ---------- */
107
  .gradio-container [data-testid="block-label"],
108
  .gradio-container .component-label,
109
  .gradio-container .label,
 
116
  border:none !important;
117
  padding:0 !important;
118
  }
119
+
120
+ /* Ribbons collés en haut à gauche (bg-* neutral/gray…) */
121
  .gradio-container .wrap > div[class*="absolute"][class*="top-0"][class*="left-0"],
122
  .gradio-container [class*="absolute"][class*="top-0"][class*="left-0"][class*="rounded-b"],
123
  .gradio-container [class*="absolute"][class*="top-0"][class*="left-0"][class*="rounded-br"]{
 
127
  border:none !important;
128
  }
129
 
130
+ /* ---------- Onglets / pagination internes (DataFrame, etc.) ---------- */
131
  .gradio-container [role="tablist"],
132
  .gradio-container [role="tab"]{
133
  background:#fff !important;
 
138
  .gradio-container [role="tab"][aria-selected="true"]{
139
  background:linear-gradient(90deg, rgba(124,58,237,.12), rgba(6,182,212,.12)) !important;
140
  color:#0F172A !important;
141
+ border-color:var(--vb-border) !important;
142
  }
143
 
144
  /* ---------- Textes ---------- */
 
170
  box-shadow:0 0 0 2px rgba(124,58,237,.35), 0 0 0 4px rgba(6,182,212,.25) !important;
171
  }
172
 
173
+ /* Checkboxes / radios */
174
+ .gradio-container input[type="checkbox"],
175
+ .gradio-container input[type="radio"]{ accent-color:var(--vb-primary) !important }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
 
177
  /* ---------- Sliders ---------- */
178
  .gradio-container .noUi-target{
 
199
  }
200
  .gradio-container .vb-cta:hover{transform:translateY(-2px);filter:brightness(1.05)}
201
 
202
+ /* ---------- DataFrames / Tables ---------- */
203
+ .gradio-container [data-testid="dataframe"]{
204
+ background:#fff !important; border:1px solid var(--vb-border) !important; border-radius:var(--vb-radius) !important;
 
 
 
 
 
205
  }
206
+ .gradio-container [data-testid="dataframe"] *{
207
+ background:#fff !important; color:#0F172A !important; border-color:#E2E8F0 !important;
208
+ }
209
+ .gradio-container thead th{
210
  background:linear-gradient(90deg, rgba(124,58,237,.12), rgba(6,182,212,.12)) !important;
211
+ color:#111827 !important; font-weight:800 !important; border-bottom:1px solid var(--vb-border) !important;
212
  }
213
+ .gradio-container td, .gradio-container th{
214
+ padding:10px !important; border-bottom:1px solid #F1F5F9 !important;
 
 
 
215
  }
 
216
 
217
+ /* ---------- Fichiers (gr.Files) ---------- */
218
  .gradio-container [class*="file"]{
219
  background:#fff !important; color:#0F172A !important;
220
  border-color:var(--vb-border) !important;
 
257
  </svg>"""
258
 
259
  # ====================== UNIDECODE (fallback) ======================
260
+
261
  try:
262
  from unidecode import unidecode
263
  except Exception:
 
269
  return str(x)
270
 
271
  # ====================== THÉSAURUS ASSURANCE ======================
272
+
273
  THEMES = {
274
  "Remboursements santé":[r"\bremboursement[s]?\b", r"\bt[eé]l[eé]transmission\b", r"\bno[eé]mie\b",
275
  r"\bprise\s*en\s*charge[s]?\b", r"\btaux\s+de\s+remboursement[s]?\b", r"\b(ameli|cpam)\b",
 
301
  }
302
 
303
  # ====================== SENTIMENT (règles) ======================
304
+
305
  POS_WORDS = {"bien":1.0,"super":1.2,"parfait":1.4,"excellent":1.5,"ravi":1.2,"satisfait":1.0,
306
  "rapide":0.8,"efficace":1.0,"fiable":1.0,"simple":0.8,"facile":0.8,"clair":0.8,"conforme":0.8,
307
  "sympa":0.8,"professionnel":1.0,"réactif":1.0,"reactif":1.0,"compétent":1.0,"competent":1.0,
 
316
  INTENSIFIER_W, DIMINISHER_W = 1.5, 0.7
317
 
318
  # ====================== OpenAI (optionnel) ======================
319
+
320
  OPENAI_AVAILABLE = False
321
  try:
322
  from openai import OpenAI
 
326
  OPENAI_AVAILABLE = False
327
 
328
  # ====================== UTILS ======================
329
+
330
  def normalize(t:str)->str:
331
  if not isinstance(t,str): return ""
332
  return re.sub(r"\s+"," ",t.strip())
 
451
  return 8 if score >= 0 else 7
452
 
453
  # ====================== GRAPHIQUES ======================
454
+
455
  def fig_nps_gauge(nps: Optional[float]) -> go.Figure:
456
  v = 0.0 if nps is None else float(nps)
457
  return go.Figure(go.Indicator(mode="gauge+number", value=v,
 
477
  fig.update_layout(xaxis_tickangle=-30); return fig
478
 
479
  # ====================== ANALYSE ======================
480
+
481
  def analyze_text(pasted_txt, has_sc, sep_chr,
482
  do_anonymize, use_oa_sent, use_oa_themes, use_oa_summary,
483
  oa_model, oa_temp, top_k):
 
577
  nps_label = "NPS global (inféré)" if any_inferred else "NPS global"
578
  lines=[ "# Synthèse NPS & ressentis clients",
579
  f"- **Méthode** : {method}",
580
+ f"- **{nps_label}** : {nps:.1f}" if nps is not None else f"- **{npslabel}** : n/a" ]
581
  if dist:
582
  tot=sum(dist.values()); pos=dist.get("positive",0); neg=dist.get("negatif",0); neu=dist.get("neutre",0)
583
  lines.append(f"- **Répartition émotions** : positive {pos}/{tot}, neutre {neu}/{tot}, négative {neg}/{tot}")
 
641
 
642
  # ====================== UI ======================
643
 
644
+ apply_plotly_theme()
 
 
 
645
 
646
  with gr.Blocks(title="Verbatify — Analyse NPS", css=VB_CSS) as demo:
647
  gr.HTML(