dotoking commited on
Commit
6692b0e
Β·
verified Β·
1 Parent(s): 33d552c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +183 -97
app.py CHANGED
@@ -8,41 +8,26 @@ cear_analyzer = CEARModel()
8
 
9
 
10
  def build_dataframe_from_inputs(values):
11
- """Build DataFrame; ignore variety if minutes == 0."""
12
- rows = []
13
- for name, minutes, variety in values:
14
- minutes = 0.0 if minutes is None else float(minutes)
15
- if minutes == 0:
16
- variety = None
17
- else:
18
- variety = None if variety is None else float(variety)
19
- if minutes > 0:
20
- rows.append({
21
- "platform_name": name,
22
- "minutes_per_week": minutes,
23
- "variety_score": variety,
24
- })
25
- if not rows:
26
- return pd.DataFrame(columns=["platform_name", "minutes_per_week", "variety_score"])
27
- return pd.DataFrame(rows)(values):
28
  """Build a DataFrame from a list of (platform_name, minutes, variety) tuples.
29
 
30
- values: list[tuple[str, float | None, float | None]]
31
- Returns: DataFrame with columns [platform_name, minutes_per_week, variety_score]
32
  """
33
  rows = []
34
  for name, minutes, variety in values:
35
  minutes = 0.0 if minutes is None else float(minutes)
 
 
 
36
  variety = None if variety is None else float(variety)
37
- # Keep row if there is any meaningful input
38
- if minutes > 0 or (variety is not None and not np.isnan(variety)):
39
- rows.append(
40
- {
41
- "platform_name": name,
42
- "minutes_per_week": minutes,
43
- "variety_score": variety,
44
- }
45
- )
46
  if not rows:
47
  return pd.DataFrame(
48
  columns=["platform_name", "minutes_per_week", "variety_score"]
@@ -68,7 +53,27 @@ def analyze_user_data(
68
  feed_satisfaction,
69
  fomo_level,
70
  ):
71
- # Build the input DataFrame for the core model
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  df = build_dataframe_from_inputs(
73
  [
74
  ("tiktok", tiktok_minutes, tiktok_variety),
@@ -84,7 +89,9 @@ def analyze_user_data(
84
  if df.empty:
85
  return (
86
  "Please enter at least one platform with some weekly minutes.",
87
- "No meaningful screen time was entered, so per-platform efficiency could not be calculated.",
 
 
88
  pd.DataFrame(columns=["platform", "efficiency_score"]),
89
  )
90
 
@@ -126,7 +133,8 @@ def analyze_user_data(
126
  # ---------------- Variety interpretation ---------------- #
127
  if avg_variety is None:
128
  variety_text = (
129
- "You did not provide variety ratings, so this analysis focuses only on time and platform mix."
 
130
  )
131
  elif avg_variety < 4:
132
  variety_text = (
@@ -190,6 +198,15 @@ def analyze_user_data(
190
  if fomo is not None:
191
  summary_lines.append(f"- **FOMO / Out-of-the-loop (0–10):** **{fomo:.1f}**")
192
 
 
 
 
 
 
 
 
 
 
193
  # ---------------- Interpretation section ---------------- #
194
  summary_lines.extend([
195
  "",
@@ -219,12 +236,14 @@ def analyze_user_data(
219
  summary_lines.append(survey_explainer.strip())
220
 
221
  summary_lines.append(
222
- "\nThe C-Score uses a logarithmic transform of your weekly minutes, encoding diminishing returns as time increases. "
 
223
  "A-Risk reflects your raw time investment and how concentrated it is on a small set of high-weight platforms. "
224
  "D-Index captures how many platforms you use in a meaningful way (higher values mean your time is spread across more platforms)."
225
  )
226
 
227
- summary = "\n".join(summary_lines).strip()
 
228
 
229
  # ---------------- Per-platform efficiency table and explanation ---------------- #
230
  if isinstance(per_eff, list) and per_eff:
@@ -236,10 +255,12 @@ def analyze_user_data(
236
  eff_df["efficiency_score"] = eff_df["efficiency_score"].round(1)
237
  eff_df = eff_df.sort_values("efficiency_score", ascending=False)
238
 
239
- lines = ["### πŸ“ˆ Platform efficiency ranking (0–100)\n"]
 
240
  lines.append(
241
  "Higher scores mean more cultural exposure per minute. "
242
- "The top platform in your current mix is set to 100 and others are scaled relative to it.\n"
 
243
  )
244
 
245
  for _, row in eff_df.iterrows():
@@ -248,89 +269,154 @@ def analyze_user_data(
248
  lines.append(f"- **{platform.capitalize()}**: {score:.1f}")
249
 
250
  lines.append(
251
- "\nPlatforms near 100 are the ones that give you the most cultural exposure per minute in this configuration. "
 
252
  "Platforms with low scores cost more attention for less cultural gain."
253
  )
254
 
255
- eff_md = "\n".join(lines)
 
256
  else:
257
  eff_df = pd.DataFrame(columns=["platform", "efficiency_score"])
258
  eff_md = (
259
- "### πŸ“ˆ Platform efficiency ranking\n\n"
 
 
260
  "No meaningful screen time was entered, so per-platform efficiency could not be calculated."
261
  )
262
 
263
  return summary, eff_md, eff_df
264
 
265
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  # ---------------- Gradio UI ---------------- #
267
 
268
  with gr.Blocks() as demo:
269
  gr.Markdown(
270
- "# CEAR – Cultural Exposure & Algorithmic Risk Analyzer\n"
 
271
  "Enter your weekly screen time per platform, rate the variety of each feed, and optionally report how satisfied "
272
  "you are with your feed and how much FOMO you feel."
273
  )
274
 
275
- with gr.Row():
276
- with gr.Column():
277
- # Reset buttons per platform
278
- def reset_tiktok(): return 0, 0
279
- def reset_insta(): return 0, 0
280
- def reset_youtube(): return 0, 0
281
- def reset_twitter(): return 0, 0
282
- def reset_reddit(): return 0, 0
283
- def reset_facebook(): return 0, 0
284
- def reset_other(): return 0, 0
285
-
286
- gr.Markdown("### Weekly minutes & per-platform variety (0–10)")
287
- gr.Markdown("### Weekly minutes & per-platform variety (0–10)")
288
-
289
- tiktok_minutes = gr.Number(label="TikTok minutes/week", value=240, precision=0)
290
- tiktok_variety = gr.Slider(label="TikTok variety (0–10)", minimum=0, maximum=10, step=1, value=4)
291
-
292
- insta_minutes = gr.Number(label="Instagram minutes/week", value=180, precision=0)
293
- insta_variety = gr.Slider(label="Instagram variety (0–10)", minimum=0, maximum=10, step=1, value=5)
294
-
295
- youtube_minutes = gr.Number(label="YouTube minutes/week", value=120, precision=0)
296
- youtube_variety = gr.Slider(label="YouTube variety (0–10)", minimum=0, maximum=10, step=1, value=7)
297
-
298
- twitter_minutes = gr.Number(label="Twitter/X minutes/week", value=60, precision=0)
299
- twitter_variety = gr.Slider(label="Twitter/X variety (0–10)", minimum=0, maximum=10, step=1, value=6)
300
-
301
- reddit_minutes = gr.Number(label="Reddit minutes/week", value=90, precision=0)
302
- reddit_variety = gr.Slider(label="Reddit variety (0–10)", minimum=0, maximum=10, step=1, value=8)
303
-
304
- facebook_minutes = gr.Number(label="Facebook minutes/week", value=45, precision=0)
305
- facebook_variety = gr.Slider(label="Facebook variety (0–10)", minimum=0, maximum=10, step=1, value=3)
306
-
307
- other_minutes = gr.Number(label="Other platforms minutes/week", value=30, precision=0)
308
- other_variety = gr.Slider(label="Other platforms variety (0–10)", minimum=0, maximum=10, step=1, value=5)
309
-
310
- with gr.Column():
311
- gr.Markdown("### Self-report (global)")
312
-
313
- feed_satisfaction = gr.Slider(
314
- label="Feed satisfaction (0 = miserable, 10 = very happy)",
315
- minimum=0,
316
- maximum=10,
317
- step=1,
318
- value=6,
319
- )
320
- fomo_level = gr.Slider(
321
- label="FOMO / out-of-the-loop feeling (0 = none, 10 = extreme)",
322
- minimum=0,
323
- maximum=10,
324
- step=1,
325
- value=4,
326
- )
327
-
328
- run_btn = gr.Button("Analyze")
329
-
330
- summary_out = gr.Markdown(label="Score Results")
331
- eff_md_out = gr.Markdown(label="Per-platform Efficiency Summary")
332
- eff_table_out = gr.Dataframe(label="Per-platform Cultural Efficiency")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
333
 
 
334
  run_btn.click(
335
  fn=analyze_user_data,
336
  inputs=[
 
8
 
9
 
10
  def build_dataframe_from_inputs(values):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  """Build a DataFrame from a list of (platform_name, minutes, variety) tuples.
12
 
13
+ - If minutes == 0, the row is excluded entirely.
14
+ - Variety is only used when minutes > 0.
15
  """
16
  rows = []
17
  for name, minutes, variety in values:
18
  minutes = 0.0 if minutes is None else float(minutes)
19
+ if minutes <= 0:
20
+ # Ignore variety when there is no time invested
21
+ continue
22
  variety = None if variety is None else float(variety)
23
+ rows.append(
24
+ {
25
+ "platform_name": name,
26
+ "minutes_per_week": minutes,
27
+ "variety_score": variety,
28
+ }
29
+ )
30
+
 
31
  if not rows:
32
  return pd.DataFrame(
33
  columns=["platform_name", "minutes_per_week", "variety_score"]
 
53
  feed_satisfaction,
54
  fomo_level,
55
  ):
56
+ # Track impossible input patterns for warnings (variety > 0, minutes == 0)
57
+ impossible_platforms = []
58
+
59
+ def check_impossible(name, minutes, variety):
60
+ try:
61
+ m = 0.0 if minutes is None else float(minutes)
62
+ v = 0.0 if variety is None else float(variety)
63
+ except ValueError:
64
+ return
65
+ if m <= 0 and v > 0:
66
+ impossible_platforms.append(name)
67
+
68
+ check_impossible("TikTok", tiktok_minutes, tiktok_variety)
69
+ check_impossible("Instagram", insta_minutes, insta_variety)
70
+ check_impossible("YouTube", youtube_minutes, youtube_variety)
71
+ check_impossible("Twitter/X", twitter_minutes, twitter_variety)
72
+ check_impossible("Reddit", reddit_minutes, reddit_variety)
73
+ check_impossible("Facebook", facebook_minutes, facebook_variety)
74
+ check_impossible("Other", other_minutes, other_variety)
75
+
76
+ # Build the input DataFrame for the core model (only minutes > 0)
77
  df = build_dataframe_from_inputs(
78
  [
79
  ("tiktok", tiktok_minutes, tiktok_variety),
 
89
  if df.empty:
90
  return (
91
  "Please enter at least one platform with some weekly minutes.",
92
+ "### πŸ“ˆ Platform efficiency ranking
93
+
94
+ No meaningful screen time was entered, so per-platform efficiency could not be calculated.",
95
  pd.DataFrame(columns=["platform", "efficiency_score"]),
96
  )
97
 
 
133
  # ---------------- Variety interpretation ---------------- #
134
  if avg_variety is None:
135
  variety_text = (
136
+ "You did not provide variety ratings (for platforms with minutes > 0), so this analysis "
137
+ "focuses only on time and platform mix."
138
  )
139
  elif avg_variety < 4:
140
  variety_text = (
 
198
  if fomo is not None:
199
  summary_lines.append(f"- **FOMO / Out-of-the-loop (0–10):** **{fomo:.1f}**")
200
 
201
+ # Impossible input warnings
202
+ if impossible_platforms:
203
+ unique_list = sorted(set(impossible_platforms))
204
+ joined = ", ".join(unique_list)
205
+ summary_lines.append(
206
+ f"- ⚠️ You set a variety score > 0 but 0 minutes for: **{joined}**. "
207
+ "These variety inputs were ignored in the calculations."
208
+ )
209
+
210
  # ---------------- Interpretation section ---------------- #
211
  summary_lines.extend([
212
  "",
 
236
  summary_lines.append(survey_explainer.strip())
237
 
238
  summary_lines.append(
239
+ "
240
+ The C-Score uses a logarithmic transform of your weekly minutes, encoding diminishing returns as time increases. "
241
  "A-Risk reflects your raw time investment and how concentrated it is on a small set of high-weight platforms. "
242
  "D-Index captures how many platforms you use in a meaningful way (higher values mean your time is spread across more platforms)."
243
  )
244
 
245
+ summary = "
246
+ ".join(summary_lines).strip()
247
 
248
  # ---------------- Per-platform efficiency table and explanation ---------------- #
249
  if isinstance(per_eff, list) and per_eff:
 
255
  eff_df["efficiency_score"] = eff_df["efficiency_score"].round(1)
256
  eff_df = eff_df.sort_values("efficiency_score", ascending=False)
257
 
258
+ lines = ["### πŸ“ˆ Platform efficiency ranking (0–100)
259
+ "]
260
  lines.append(
261
  "Higher scores mean more cultural exposure per minute. "
262
+ "The top platform in your current mix is set to 100 and others are scaled relative to it.
263
+ "
264
  )
265
 
266
  for _, row in eff_df.iterrows():
 
269
  lines.append(f"- **{platform.capitalize()}**: {score:.1f}")
270
 
271
  lines.append(
272
+ "
273
+ Platforms near 100 are the ones that give you the most cultural exposure per minute in this configuration. "
274
  "Platforms with low scores cost more attention for less cultural gain."
275
  )
276
 
277
+ eff_md = "
278
+ ".join(lines)
279
  else:
280
  eff_df = pd.DataFrame(columns=["platform", "efficiency_score"])
281
  eff_md = (
282
+ "### πŸ“ˆ Platform efficiency ranking
283
+
284
+ "
285
  "No meaningful screen time was entered, so per-platform efficiency could not be calculated."
286
  )
287
 
288
  return summary, eff_md, eff_df
289
 
290
 
291
+ # ---------------- Helper functions for reset buttons ---------------- #
292
+
293
+ def reset_pair():
294
+ """Return a pair of zeros for minutes and variety."""
295
+ return 0, 0
296
+
297
+
298
+ def reset_all():
299
+ """Return zeros for all minutes and variety sliders (7 platforms * 2 values)."""
300
+ return (0, 0) * 7
301
+
302
+
303
  # ---------------- Gradio UI ---------------- #
304
 
305
  with gr.Blocks() as demo:
306
  gr.Markdown(
307
+ "# CEAR – Cultural Exposure & Algorithmic Risk Analyzer
308
+ "
309
  "Enter your weekly screen time per platform, rate the variety of each feed, and optionally report how satisfied "
310
  "you are with your feed and how much FOMO you feel."
311
  )
312
 
313
+ with gr.Accordion("1. Platform screen time & variety (per platform)", open=True):
314
+ with gr.Row():
315
+ with gr.Column():
316
+ # TikTok row
317
+ with gr.Row():
318
+ tiktok_minutes = gr.Number(label="TikTok minutes/week", value=240, precision=0)
319
+ tiktok_variety = gr.Slider(label="TikTok variety (0–10)", minimum=0, maximum=10, step=1, value=4)
320
+ tiktok_reset_btn = gr.Button("Reset TikTok")
321
+
322
+ # Instagram row
323
+ with gr.Row():
324
+ insta_minutes = gr.Number(label="Instagram minutes/week", value=180, precision=0)
325
+ insta_variety = gr.Slider(label="Instagram variety (0–10)", minimum=0, maximum=10, step=1, value=5)
326
+ insta_reset_btn = gr.Button("Reset Instagram")
327
+
328
+ # YouTube row
329
+ with gr.Row():
330
+ youtube_minutes = gr.Number(label="YouTube minutes/week", value=120, precision=0)
331
+ youtube_variety = gr.Slider(label="YouTube variety (0–10)", minimum=0, maximum=10, step=1, value=7)
332
+ youtube_reset_btn = gr.Button("Reset YouTube")
333
+
334
+ # Twitter/X row
335
+ with gr.Row():
336
+ twitter_minutes = gr.Number(label="Twitter/X minutes/week", value=60, precision=0)
337
+ twitter_variety = gr.Slider(label="Twitter/X variety (0–10)", minimum=0, maximum=10, step=1, value=6)
338
+ twitter_reset_btn = gr.Button("Reset Twitter/X")
339
+
340
+ # Reddit row
341
+ with gr.Row():
342
+ reddit_minutes = gr.Number(label="Reddit minutes/week", value=90, precision=0)
343
+ reddit_variety = gr.Slider(label="Reddit variety (0–10)", minimum=0, maximum=10, step=1, value=8)
344
+ reddit_reset_btn = gr.Button("Reset Reddit")
345
+
346
+ # Facebook row
347
+ with gr.Row():
348
+ facebook_minutes = gr.Number(label="Facebook minutes/week", value=45, precision=0)
349
+ facebook_variety = gr.Slider(label="Facebook variety (0–10)", minimum=0, maximum=10, step=1, value=3)
350
+ facebook_reset_btn = gr.Button("Reset Facebook")
351
+
352
+ # Other row
353
+ with gr.Row():
354
+ other_minutes = gr.Number(label="Other platforms minutes/week", value=30, precision=0)
355
+ other_variety = gr.Slider(label="Other platforms variety (0–10)", minimum=0, maximum=10, step=1, value=5)
356
+ other_reset_btn = gr.Button("Reset Other")
357
+
358
+ # Reset all button
359
+ reset_all_btn = gr.Button("Reset ALL platforms")
360
+
361
+ with gr.Accordion("2. Self-report sliders & results", open=True):
362
+ with gr.Row():
363
+ with gr.Column():
364
+ gr.Markdown("### Self-report (global)")
365
+
366
+ feed_satisfaction = gr.Slider(
367
+ label="Feed satisfaction (0 = miserable, 10 = very happy)",
368
+ minimum=0,
369
+ maximum=10,
370
+ step=1,
371
+ value=6,
372
+ )
373
+ fomo_level = gr.Slider(
374
+ label="FOMO / out-of-the-loop feeling (0 = none, 10 = extreme)",
375
+ minimum=0,
376
+ maximum=10,
377
+ step=1,
378
+ value=4,
379
+ )
380
+
381
+ run_btn = gr.Button("Analyze", variant="primary")
382
+
383
+ with gr.Column():
384
+ summary_out = gr.Markdown(label="Score Results")
385
+ eff_md_out = gr.Markdown(label="Per-platform Efficiency Summary")
386
+ eff_table_out = gr.Dataframe(label="Per-platform Cultural Efficiency")
387
+
388
+ # Wire up reset buttons (per platform)
389
+ tiktok_reset_btn.click(reset_pair, inputs=[], outputs=[tiktok_minutes, tiktok_variety])
390
+ insta_reset_btn.click(reset_pair, inputs=[], outputs=[insta_minutes, insta_variety])
391
+ youtube_reset_btn.click(reset_pair, inputs=[], outputs=[youtube_minutes, youtube_variety])
392
+ twitter_reset_btn.click(reset_pair, inputs=[], outputs=[twitter_minutes, twitter_variety])
393
+ reddit_reset_btn.click(reset_pair, inputs=[], outputs=[reddit_minutes, reddit_variety])
394
+ facebook_reset_btn.click(reset_pair, inputs=[], outputs=[facebook_minutes, facebook_variety])
395
+ other_reset_btn.click(reset_pair, inputs=[], outputs=[other_minutes, other_variety])
396
+
397
+ # Reset all platforms at once
398
+ reset_all_btn.click(
399
+ reset_all,
400
+ inputs=[],
401
+ outputs=[
402
+ tiktok_minutes,
403
+ tiktok_variety,
404
+ insta_minutes,
405
+ insta_variety,
406
+ youtube_minutes,
407
+ youtube_variety,
408
+ twitter_minutes,
409
+ twitter_variety,
410
+ reddit_minutes,
411
+ reddit_variety,
412
+ facebook_minutes,
413
+ facebook_variety,
414
+ other_minutes,
415
+ other_variety,
416
+ ],
417
+ )
418
 
419
+ # Run analysis
420
  run_btn.click(
421
  fn=analyze_user_data,
422
  inputs=[