dotoking commited on
Commit
2ec45a0
Β·
verified Β·
1 Parent(s): 6692b0e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +169 -79
app.py CHANGED
@@ -8,9 +8,10 @@ cear_analyzer = CEARModel()
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 = []
@@ -19,7 +20,9 @@ def build_dataframe_from_inputs(values):
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,
@@ -32,6 +35,7 @@ def build_dataframe_from_inputs(values):
32
  return pd.DataFrame(
33
  columns=["platform_name", "minutes_per_week", "variety_score"]
34
  )
 
35
  return pd.DataFrame(rows)
36
 
37
 
@@ -53,7 +57,7 @@ def analyze_user_data(
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):
@@ -87,13 +91,16 @@ def analyze_user_data(
87
  )
88
 
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
 
98
  # Call core CEAR model
99
  scores = cear_analyzer.calculate_scores(
@@ -133,18 +140,18 @@ No meaningful screen time was entered, so per-platform efficiency could not be c
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 = (
141
- f"Your average variety rating is **{avg_variety:.1f} / 10**, which suggests that your feeds feel "
142
- "quite repetitive and reinforce a narrow slice of content."
143
  )
144
  elif avg_variety > 7:
145
  variety_text = (
146
- f"Your average variety rating is **{avg_variety:.1f} / 10**, which suggests that you see a wide range "
147
- "of topics and styles. This broadens your exposure and slightly offsets some algorithmic risk."
148
  )
149
  else:
150
  variety_text = (
@@ -166,8 +173,8 @@ No meaningful screen time was entered, so per-platform efficiency could not be c
166
  )
167
  else:
168
  satisfaction_text = (
169
- "Your satisfaction is in the mid range, which suggests your feed is 'fine' but not fully optimized "
170
- "for how you would like to spend your attention."
171
  )
172
 
173
  fomo_text = ""
@@ -191,12 +198,19 @@ No meaningful screen time was entered, so per-platform efficiency could not be c
191
  f"- **Algorithmic Risk Score (A-Risk):** **{a:.2f}**",
192
  f"- **Platform Diversity Index (D-Index):** **{d:.2f}**",
193
  ]
 
194
  if avg_variety is not None:
195
- summary_lines.append(f"- **Average Variety Rating (0–10):** **{avg_variety:.2f}**")
 
 
196
  if satisfaction is not None:
197
- summary_lines.append(f"- **Feed Satisfaction (0–10):** **{satisfaction:.1f}**")
 
 
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:
@@ -208,59 +222,67 @@ No meaningful screen time was entered, so per-platform efficiency could not be c
208
  )
209
 
210
  # ---------------- Interpretation section ---------------- #
211
- summary_lines.extend([
212
- "",
213
- "### πŸ“ Interpretation",
214
- "",
215
- profile,
216
- "",
217
- variety_text,
218
- ])
 
 
 
219
  if satisfaction_text:
220
  summary_lines.append("")
221
  summary_lines.append(satisfaction_text)
 
222
  if fomo_text:
223
  summary_lines.append("")
224
  summary_lines.append(fomo_text)
225
 
226
  # ---------------- Survey explainer ---------------- #
227
- survey_explainer = """
228
- ### ℹ️ How your answers are used
229
-
230
- - **Minutes per week** drive the core scores. More time on high-weight platforms increases both C-Score and A-Risk, with diminishing returns for C-Score.
231
- - **Per-platform variety (0–10)** is combined into a minutes-weighted average. Low variety means you mainly see one type of content; high variety means you see a wider mix of topics and styles.
232
- - **Feed satisfaction (0–10)** does not change the scores; it is used to interpret whether your current pattern feels good or frustrating to you.
233
- - **FOMO (0–10)** is compared with your C-Score: high FOMO with low C-Score means you feel out of the loop, while low FOMO with low C-Score means you are detached by choice.
234
- """
 
 
 
 
235
  summary_lines.append("")
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:
250
  eff_df = pd.DataFrame(per_eff)
251
  if "platform_name" in eff_df.columns:
252
  eff_df = eff_df.rename(
253
- columns={"platform_name": "platform", "Cultural_Efficiency": "efficiency_score"}
 
 
 
254
  )
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,19 +291,15 @@ The C-Score uses a logarithmic transform of your weekly minutes, encoding dimini
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
 
@@ -290,6 +308,7 @@ Platforms near 100 are the ones that give you the most cultural exposure per min
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
@@ -304,8 +323,7 @@ def reset_all():
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
  )
@@ -315,44 +333,100 @@ with gr.Blocks() as demo:
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
@@ -383,16 +457,32 @@ with gr.Blocks() as demo:
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(
 
8
 
9
 
10
  def build_dataframe_from_inputs(values):
11
+ """
12
+ Build a DataFrame from a list of (platform_name, minutes, variety) tuples.
13
 
14
+ - If minutes <= 0, the row is excluded entirely.
15
  - Variety is only used when minutes > 0.
16
  """
17
  rows = []
 
20
  if minutes <= 0:
21
  # Ignore variety when there is no time invested
22
  continue
23
+
24
  variety = None if variety is None else float(variety)
25
+
26
  rows.append(
27
  {
28
  "platform_name": name,
 
35
  return pd.DataFrame(
36
  columns=["platform_name", "minutes_per_week", "variety_score"]
37
  )
38
+
39
  return pd.DataFrame(rows)
40
 
41
 
 
57
  feed_satisfaction,
58
  fomo_level,
59
  ):
60
+ # Track impossible input patterns for warnings (variety > 0, minutes <= 0)
61
  impossible_platforms = []
62
 
63
  def check_impossible(name, minutes, variety):
 
91
  )
92
 
93
  if df.empty:
94
+ summary_empty = (
95
+ "Please enter at least one platform with some weekly minutes."
 
 
 
 
96
  )
97
+ eff_md_empty = (
98
+ "### πŸ“ˆ Platform efficiency ranking\n\n"
99
+ "No meaningful screen time was entered, so per-platform efficiency "
100
+ "could not be calculated."
101
+ )
102
+ empty_df = pd.DataFrame(columns=["platform", "efficiency_score"])
103
+ return summary_empty, eff_md_empty, empty_df
104
 
105
  # Call core CEAR model
106
  scores = cear_analyzer.calculate_scores(
 
140
  # ---------------- Variety interpretation ---------------- #
141
  if avg_variety is None:
142
  variety_text = (
143
+ "You did not provide variety ratings (for platforms with minutes > 0), "
144
+ "so this analysis focuses only on time and platform mix."
145
  )
146
  elif avg_variety < 4:
147
  variety_text = (
148
+ f"Your average variety rating is **{avg_variety:.1f} / 10**, which suggests that your feeds "
149
+ "feel quite repetitive and reinforce a narrow slice of content."
150
  )
151
  elif avg_variety > 7:
152
  variety_text = (
153
+ f"Your average variety rating is **{avg_variety:.1f} / 10**, which suggests that you see a wide "
154
+ "range of topics and styles. This broadens your exposure and slightly offsets some algorithmic risk."
155
  )
156
  else:
157
  variety_text = (
 
173
  )
174
  else:
175
  satisfaction_text = (
176
+ "Your satisfaction is in the mid range, which suggests your feed is \"fine\" but not fully "
177
+ "optimized for how you would like to spend your attention."
178
  )
179
 
180
  fomo_text = ""
 
198
  f"- **Algorithmic Risk Score (A-Risk):** **{a:.2f}**",
199
  f"- **Platform Diversity Index (D-Index):** **{d:.2f}**",
200
  ]
201
+
202
  if avg_variety is not None:
203
+ summary_lines.append(
204
+ f"- **Average Variety Rating (0–10):** **{avg_variety:.2f}**"
205
+ )
206
  if satisfaction is not None:
207
+ summary_lines.append(
208
+ f"- **Feed Satisfaction (0–10):** **{satisfaction:.1f}**"
209
+ )
210
  if fomo is not None:
211
+ summary_lines.append(
212
+ f"- **FOMO / Out-of-the-loop (0–10):** **{fomo:.1f}**"
213
+ )
214
 
215
  # Impossible input warnings
216
  if impossible_platforms:
 
222
  )
223
 
224
  # ---------------- Interpretation section ---------------- #
225
+ summary_lines.extend(
226
+ [
227
+ "",
228
+ "### πŸ“ Interpretation",
229
+ "",
230
+ profile,
231
+ "",
232
+ variety_text,
233
+ ]
234
+ )
235
+
236
  if satisfaction_text:
237
  summary_lines.append("")
238
  summary_lines.append(satisfaction_text)
239
+
240
  if fomo_text:
241
  summary_lines.append("")
242
  summary_lines.append(fomo_text)
243
 
244
  # ---------------- Survey explainer ---------------- #
245
+ survey_explainer = (
246
+ "### ℹ️ How your answers are used\n\n"
247
+ "- **Minutes per week** drive the core scores. More time on high-weight platforms increases both "
248
+ "C-Score and A-Risk, with diminishing returns for C-Score.\n"
249
+ "- **Per-platform variety (0–10)** is combined into a minutes-weighted average. Low variety means you "
250
+ "mainly see one type of content; high variety means you see a wider mix of topics and styles.\n"
251
+ "- **Feed satisfaction (0–10)** does not change the scores; it is used to interpret whether your current "
252
+ "pattern feels good or frustrating to you.\n"
253
+ "- **FOMO (0–10)** is compared with your C-Score: high FOMO with low C-Score means you feel out of the loop, "
254
+ "while low FOMO with low C-Score means you are detached by choice."
255
+ )
256
+
257
  summary_lines.append("")
258
+ summary_lines.append(survey_explainer)
259
 
260
  summary_lines.append(
261
+ "\nThe C-Score uses a logarithmic transform of your weekly minutes, encoding diminishing returns as time "
262
+ "increases. A-Risk reflects your raw time investment and how concentrated it is on a small set of high-weight "
263
+ "platforms. D-Index captures how many platforms you use in a meaningful way (higher values mean your time is "
264
+ "spread across more platforms)."
265
  )
266
 
267
+ summary = "\n".join(summary_lines).strip()
 
268
 
269
  # ---------------- Per-platform efficiency table and explanation ---------------- #
270
  if isinstance(per_eff, list) and per_eff:
271
  eff_df = pd.DataFrame(per_eff)
272
  if "platform_name" in eff_df.columns:
273
  eff_df = eff_df.rename(
274
+ columns={
275
+ "platform_name": "platform",
276
+ "Cultural_Efficiency": "efficiency_score",
277
+ }
278
  )
279
  eff_df["efficiency_score"] = eff_df["efficiency_score"].round(1)
280
  eff_df = eff_df.sort_values("efficiency_score", ascending=False)
281
 
282
+ lines = ["### πŸ“ˆ Platform efficiency ranking (0–100)\n"]
 
283
  lines.append(
284
  "Higher scores mean more cultural exposure per minute. "
285
+ "The top platform in your current mix is set to 100 and others are scaled relative to it.\n"
 
286
  )
287
 
288
  for _, row in eff_df.iterrows():
 
291
  lines.append(f"- **{platform.capitalize()}**: {score:.1f}")
292
 
293
  lines.append(
294
+ "\nPlatforms near 100 are the ones that give you the most cultural exposure per minute in this configuration. "
 
295
  "Platforms with low scores cost more attention for less cultural gain."
296
  )
297
 
298
+ eff_md = "\n".join(lines)
 
299
  else:
300
  eff_df = pd.DataFrame(columns=["platform", "efficiency_score"])
301
  eff_md = (
302
+ "### πŸ“ˆ Platform efficiency ranking\n\n"
 
 
303
  "No meaningful screen time was entered, so per-platform efficiency could not be calculated."
304
  )
305
 
 
308
 
309
  # ---------------- Helper functions for reset buttons ---------------- #
310
 
311
+
312
  def reset_pair():
313
  """Return a pair of zeros for minutes and variety."""
314
  return 0, 0
 
323
 
324
  with gr.Blocks() as demo:
325
  gr.Markdown(
326
+ "# CEAR – Cultural Exposure & Algorithmic Risk Analyzer\n"
 
327
  "Enter your weekly screen time per platform, rate the variety of each feed, and optionally report how satisfied "
328
  "you are with your feed and how much FOMO you feel."
329
  )
 
333
  with gr.Column():
334
  # TikTok row
335
  with gr.Row():
336
+ tiktok_minutes = gr.Number(
337
+ label="TikTok minutes/week", value=240, precision=0
338
+ )
339
+ tiktok_variety = gr.Slider(
340
+ label="TikTok variety (0–10)",
341
+ minimum=0,
342
+ maximum=10,
343
+ step=1,
344
+ value=4,
345
+ )
346
  tiktok_reset_btn = gr.Button("Reset TikTok")
347
 
348
  # Instagram row
349
  with gr.Row():
350
+ insta_minutes = gr.Number(
351
+ label="Instagram minutes/week", value=180, precision=0
352
+ )
353
+ insta_variety = gr.Slider(
354
+ label="Instagram variety (0–10)",
355
+ minimum=0,
356
+ maximum=10,
357
+ step=1,
358
+ value=5,
359
+ )
360
  insta_reset_btn = gr.Button("Reset Instagram")
361
 
362
  # YouTube row
363
  with gr.Row():
364
+ youtube_minutes = gr.Number(
365
+ label="YouTube minutes/week", value=120, precision=0
366
+ )
367
+ youtube_variety = gr.Slider(
368
+ label="YouTube variety (0–10)",
369
+ minimum=0,
370
+ maximum=10,
371
+ step=1,
372
+ value=7,
373
+ )
374
  youtube_reset_btn = gr.Button("Reset YouTube")
375
 
376
  # Twitter/X row
377
  with gr.Row():
378
+ twitter_minutes = gr.Number(
379
+ label="Twitter/X minutes/week", value=60, precision=0
380
+ )
381
+ twitter_variety = gr.Slider(
382
+ label="Twitter/X variety (0–10)",
383
+ minimum=0,
384
+ maximum=10,
385
+ step=1,
386
+ value=6,
387
+ )
388
  twitter_reset_btn = gr.Button("Reset Twitter/X")
389
 
390
  # Reddit row
391
  with gr.Row():
392
+ reddit_minutes = gr.Number(
393
+ label="Reddit minutes/week", value=90, precision=0
394
+ )
395
+ reddit_variety = gr.Slider(
396
+ label="Reddit variety (0–10)",
397
+ minimum=0,
398
+ maximum=10,
399
+ step=1,
400
+ value=8,
401
+ )
402
  reddit_reset_btn = gr.Button("Reset Reddit")
403
 
404
  # Facebook row
405
  with gr.Row():
406
+ facebook_minutes = gr.Number(
407
+ label="Facebook minutes/week", value=45, precision=0
408
+ )
409
+ facebook_variety = gr.Slider(
410
+ label="Facebook variety (0–10)",
411
+ minimum=0,
412
+ maximum=10,
413
+ step=1,
414
+ value=3,
415
+ )
416
  facebook_reset_btn = gr.Button("Reset Facebook")
417
 
418
  # Other row
419
  with gr.Row():
420
+ other_minutes = gr.Number(
421
+ label="Other platforms minutes/week", value=30, precision=0
422
+ )
423
+ other_variety = gr.Slider(
424
+ label="Other platforms variety (0–10)",
425
+ minimum=0,
426
+ maximum=10,
427
+ step=1,
428
+ value=5,
429
+ )
430
  other_reset_btn = gr.Button("Reset Other")
431
 
432
  # Reset all button
 
457
  with gr.Column():
458
  summary_out = gr.Markdown(label="Score Results")
459
  eff_md_out = gr.Markdown(label="Per-platform Efficiency Summary")
460
+ eff_table_out = gr.Dataframe(
461
+ label="Per-platform Cultural Efficiency"
462
+ )
463
 
464
  # Wire up reset buttons (per platform)
465
+ tiktok_reset_btn.click(
466
+ reset_pair, inputs=[], outputs=[tiktok_minutes, tiktok_variety]
467
+ )
468
+ insta_reset_btn.click(
469
+ reset_pair, inputs=[], outputs=[insta_minutes, insta_variety]
470
+ )
471
+ youtube_reset_btn.click(
472
+ reset_pair, inputs=[], outputs=[youtube_minutes, youtube_variety]
473
+ )
474
+ twitter_reset_btn.click(
475
+ reset_pair, inputs=[], outputs=[twitter_minutes, twitter_variety]
476
+ )
477
+ reddit_reset_btn.click(
478
+ reset_pair, inputs=[], outputs=[reddit_minutes, reddit_variety]
479
+ )
480
+ facebook_reset_btn.click(
481
+ reset_pair, inputs=[], outputs=[facebook_minutes, facebook_variety]
482
+ )
483
+ other_reset_btn.click(
484
+ reset_pair, inputs=[], outputs=[other_minutes, other_variety]
485
+ )
486
 
487
  # Reset all platforms at once
488
  reset_all_btn.click(