dotoking commited on
Commit
07a0b02
·
verified ·
1 Parent(s): be89f48

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +198 -153
app.py CHANGED
@@ -4,131 +4,79 @@ import numpy as np
4
 
5
  from cear_model import CEARModel
6
 
7
- # Instantiate the core model once
8
  cear_analyzer = CEARModel()
9
 
10
- # Supported canonical platform names (must match what CEARModel expects)
11
- SUPPORTED_PLATFORMS = {
12
- "tiktok",
13
- "instagram",
14
- "youtube",
15
- "twitter",
16
- "reddit",
17
- "facebook",
18
- "other",
19
- }
20
 
21
- # Simple alias map for common variations typed by users
22
- PLATFORM_ALIASES = {
23
- "tik tok": "tiktok",
24
- "tt": "tiktok",
25
-
26
- "ig": "instagram",
27
- "insta": "instagram",
28
-
29
- "yt": "youtube",
30
- "you tube": "youtube",
31
-
32
- "x": "twitter",
33
-
34
- "fb": "facebook",
35
- "face book": "facebook",
36
- }
37
-
38
-
39
- def normalize_platform_name(name: str) -> str:
40
  """
41
- Normalize free-text platform names to the canonical set:
42
- tiktok, instagram, youtube, twitter, reddit, facebook, other.
43
- Unknown entries are mapped to 'other'.
44
  """
45
- if not isinstance(name, str):
46
- return "other"
47
-
48
- cleaned = name.strip().lower()
49
- if cleaned == "":
50
- return ""
51
-
52
- # Apply alias map
53
- cleaned = PLATFORM_ALIASES.get(cleaned, cleaned)
54
-
55
- # If not in supported set, bucket into 'other'
56
- if cleaned not in SUPPORTED_PLATFORMS:
57
- return "other"
58
-
59
- return cleaned
60
-
61
-
62
- def analyze_user_data(input_table):
63
- """
64
- Gradio callback for CEAR.
65
-
66
- input_table: list of lists from gr.Dataframe, e.g.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  [
68
- ["tiktok", 240, 5],
69
- ["instagram", 180, 6],
70
- ...
 
 
 
 
71
  ]
 
72
 
73
- Returns:
74
- summary_markdown (str), efficiency_dataframe (pd.DataFrame)
75
- """
76
- if not input_table:
77
- return "Please enter at least one platform and its weekly minutes.", pd.DataFrame()
78
-
79
- # Convert raw table to DataFrame. Support both 2- and 3-column input
80
- df = pd.DataFrame(input_table)
81
-
82
- if df.shape[1] == 2:
83
- df.columns = ["platform_name", "minutes_per_week"]
84
- df["variety_score"] = np.nan
85
- else:
86
- # Assume 3 columns: platform, minutes, variety
87
- df = df.iloc[:, :3] # ignore any extra accidental columns
88
- df.columns = ["platform_name", "minutes_per_week", "variety_score"]
89
-
90
- # Basic cleaning
91
- df["platform_name"] = df["platform_name"].astype(str)
92
- df["minutes_per_week"] = pd.to_numeric(df["minutes_per_week"], errors="coerce")
93
- df["variety_score"] = pd.to_numeric(df["variety_score"], errors="coerce")
94
-
95
- # Drop fully empty rows
96
- df = df.dropna(how="all")
97
- if df.empty:
98
- return "Please provide at least one platform with some minutes.", pd.DataFrame()
99
-
100
- # Normalize names and minutes
101
- df["platform_name"] = df["platform_name"].apply(normalize_platform_name)
102
- df["minutes_per_week"] = df["minutes_per_week"].fillna(0).clip(lower=0)
103
- df["variety_score"] = df["variety_score"].clip(lower=0, upper=10)
104
-
105
- # Drop rows with blank names
106
- df = df[df["platform_name"] != ""]
107
  if df.empty:
108
- return "Please provide at least one platform with some minutes.", pd.DataFrame()
109
-
110
- # Compute minutes-weighted average variety (if any variety data present)
111
- total_minutes = df["minutes_per_week"].sum()
112
- if total_minutes > 0 and df["variety_score"].notna().any():
113
- avg_variety = float(
114
- np.average(
115
- df["variety_score"].fillna(0),
116
- weights=df["minutes_per_week"]
117
- )
118
- )
119
- else:
120
- avg_variety = None
121
 
122
- # Call the core CEAR model using only the columns it expects
123
- df_for_model = df[["platform_name", "minutes_per_week"]].copy()
124
- raw_scores = cear_analyzer.calculate_scores(df_for_model)
 
125
 
126
- c = float(raw_scores.get("C_Score", 0.0))
127
- a = float(raw_scores.get("A_Risk", 0.0))
128
- d = float(raw_scores.get("D_Index", 0.0))
129
- per_eff = raw_scores.get("Per_Platform_Efficiency", [])
 
 
 
130
 
131
- # Profile based on C and A
132
  if c >= 70 and a >= 70:
133
  profile = (
134
  "You are highly plugged into online culture, but that comes with high "
@@ -150,7 +98,7 @@ def analyze_user_data(input_table):
150
  "You are either deliberately detached or under-invested in highly trend-dense platforms."
151
  )
152
 
153
- # Variety interpretation snippet
154
  if avg_variety is None:
155
  variety_text = (
156
  "You did not provide variety ratings, so this analysis focuses only on time and platform mix."
@@ -158,19 +106,49 @@ def analyze_user_data(input_table):
158
  elif avg_variety < 4:
159
  variety_text = (
160
  f"Your average variety rating is **{avg_variety:.1f} / 10**, which suggests that your feeds feel "
161
- "quite repetitive. You may be seeing similar content types despite the time you invest."
162
  )
163
  elif avg_variety > 7:
164
  variety_text = (
165
  f"Your average variety rating is **{avg_variety:.1f} / 10**, which suggests that you see a wide range "
166
- "of topics and styles. This can broaden your cultural exposure and reduce some perceived stagnation."
167
  )
168
  else:
169
  variety_text = (
170
- f"Your average variety rating is **{avg_variety:.1f} / 10**, indicating a moderate mix of content types "
171
- "without being extremely narrow or extremely diverse."
172
  )
173
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  summary_lines = [
175
  "## 📊 CEAR Analysis Summary",
176
  "",
@@ -178,9 +156,18 @@ def analyze_user_data(input_table):
178
  f"- **Algorithmic Risk Score (A-Risk):** **{a:.2f}**",
179
  f"- **Platform Diversity Index (D-Index):** **{d:.2f}**",
180
  ]
181
-
182
  if avg_variety is not None:
183
- summary_lines.append(f"- **Average Variety Rating (0–10):** **{avg_variety:.2f}**")
 
 
 
 
 
 
 
 
 
 
184
 
185
  summary_lines.extend(
186
  [
@@ -190,20 +177,26 @@ def analyze_user_data(input_table):
190
  profile,
191
  "",
192
  variety_text,
193
- "",
194
- "The C-Score is based on a logarithmic transform of your weekly minutes, encoding diminishing "
195
- "returns as time increases. A-Risk reflects your raw time investment and how concentrated it is on "
196
- "a small set of high-weight platforms. D-Index captures how many platforms you use in a meaningful way "
197
- "(higher values mean your time is spread across more platforms).",
198
  ]
199
  )
 
 
 
 
 
 
 
 
 
 
 
 
200
 
201
  summary = "\n".join(summary_lines).strip()
202
 
203
- # Turn per-platform efficiency into a tidy table
204
  if isinstance(per_eff, list) and per_eff:
205
  eff_df = pd.DataFrame(per_eff)
206
- # Expect columns ['platform_name', 'Cultural_Efficiency']
207
  if "platform_name" in eff_df.columns:
208
  eff_df = eff_df.rename(
209
  columns={"platform_name": "platform", "Cultural_Efficiency": "efficiency_score"}
@@ -215,33 +208,85 @@ def analyze_user_data(input_table):
215
  return summary, eff_df
216
 
217
 
218
- # ---------- Gradio app definition ----------
219
-
220
- demo = gr.Interface(
221
- fn=analyze_user_data,
222
- inputs=gr.Dataframe(
223
- headers=["platform_name", "minutes_per_week", "variety_score (0–10, optional)"],
224
- row_count=5,
225
- col_count=(3, "fixed"),
226
- label="Weekly screen time (by platform)",
227
- value=[
228
- ["tiktok", 240, 4],
229
- ["instagram", 180, 5],
230
- ["youtube", 120, 7],
231
- ["twitter", 60, 6],
232
- ["reddit", 90, 8],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  ],
234
- ),
235
- outputs=[
236
- gr.Markdown(label="Score Results"),
237
- gr.Dataframe(label="Per-platform Cultural Efficiency"),
238
- ],
239
- title="CEAR – Cultural Exposure & Algorithmic Risk Analyzer",
240
- description=(
241
- "Enter your weekly screen time per platform (and optional variety ratings) to estimate your "
242
- "cultural connectedness, algorithmic risk, and per-platform efficiency."
243
- ),
244
- )
245
 
246
 
247
  if __name__ == "__main__":
 
4
 
5
  from cear_model import CEARModel
6
 
 
7
  cear_analyzer = CEARModel()
8
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ def build_dataframe_from_inputs(values):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  """
12
+ values: list of tuples [(platform_name, minutes, variety), ...]
13
+ Returns: DataFrame with platform_name, minutes_per_week, variety_score
 
14
  """
15
+ rows = []
16
+ for name, minutes, variety in values:
17
+ minutes = 0.0 if minutes is None else float(minutes)
18
+ variety = None if variety is None else float(variety)
19
+ # Keep row if there is any meaningful input
20
+ if minutes > 0 or (variety is not None and not np.isnan(variety)):
21
+ rows.append(
22
+ {
23
+ "platform_name": name,
24
+ "minutes_per_week": minutes,
25
+ "variety_score": variety,
26
+ }
27
+ )
28
+ if not rows:
29
+ return pd.DataFrame(columns=["platform_name", "minutes_per_week", "variety_score"])
30
+ return pd.DataFrame(rows)
31
+
32
+
33
+ def analyze_user_data(
34
+ tiktok_minutes,
35
+ tiktok_variety,
36
+ insta_minutes,
37
+ insta_variety,
38
+ youtube_minutes,
39
+ youtube_variety,
40
+ twitter_minutes,
41
+ twitter_variety,
42
+ reddit_minutes,
43
+ reddit_variety,
44
+ facebook_minutes,
45
+ facebook_variety,
46
+ other_minutes,
47
+ other_variety,
48
+ feed_satisfaction,
49
+ fomo_level,
50
+ ):
51
+ df = build_dataframe_from_inputs(
52
  [
53
+ ("tiktok", tiktok_minutes, tiktok_variety),
54
+ ("instagram", insta_minutes, insta_variety),
55
+ ("youtube", youtube_minutes, youtube_variety),
56
+ ("twitter", twitter_minutes, twitter_variety),
57
+ ("reddit", reddit_minutes, reddit_variety),
58
+ ("facebook", facebook_minutes, facebook_variety),
59
+ ("other", other_minutes, other_variety),
60
  ]
61
+ )
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  if df.empty:
64
+ return "Please enter at least one platform with some weekly minutes.", pd.DataFrame()
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
+ # Call core model
67
+ scores = cear_analyzer.calculate_scores(
68
+ df, satisfaction=feed_satisfaction, fomo=fomo_level
69
+ )
70
 
71
+ c = float(scores.get("C_Score", 0.0))
72
+ a = float(scores.get("A_Risk", 0.0))
73
+ d = float(scores.get("D_Index", 0.0))
74
+ avg_variety = scores.get("Avg_Variety", None)
75
+ satisfaction = scores.get("Satisfaction", None)
76
+ fomo = scores.get("FOMO", None)
77
+ per_eff = scores.get("Per_Platform_Efficiency", [])
78
 
79
+ # Profile from C and A
80
  if c >= 70 and a >= 70:
81
  profile = (
82
  "You are highly plugged into online culture, but that comes with high "
 
98
  "You are either deliberately detached or under-invested in highly trend-dense platforms."
99
  )
100
 
101
+ # Variety interpretation
102
  if avg_variety is None:
103
  variety_text = (
104
  "You did not provide variety ratings, so this analysis focuses only on time and platform mix."
 
106
  elif avg_variety < 4:
107
  variety_text = (
108
  f"Your average variety rating is **{avg_variety:.1f} / 10**, which suggests that your feeds feel "
109
+ "quite repetitive and may reinforce a narrow slice of content."
110
  )
111
  elif avg_variety > 7:
112
  variety_text = (
113
  f"Your average variety rating is **{avg_variety:.1f} / 10**, which suggests that you see a wide range "
114
+ "of topics and styles. This broadens your exposure and slightly offsets some algorithmic risk."
115
  )
116
  else:
117
  variety_text = (
118
+ f"Your average variety rating is **{avg_variety:.1f} / 10**, indicating a moderate mix of content types."
 
119
  )
120
 
121
+ # Satisfaction/FOMO interpretation
122
+ satisfaction_text = ""
123
+ if satisfaction is not None:
124
+ if satisfaction <= 3:
125
+ satisfaction_text = (
126
+ "You report low satisfaction with your feed, which suggests your current pattern might not "
127
+ "match what you actually want from social media."
128
+ )
129
+ elif satisfaction >= 8:
130
+ satisfaction_text = (
131
+ "You report high satisfaction with your feed, indicating your current pattern largely feels aligned "
132
+ "with your preferences."
133
+ )
134
+ else:
135
+ satisfaction_text = (
136
+ "Your satisfaction is in the middle range, which suggests room for improvement without a complete overhaul."
137
+ )
138
+
139
+ fomo_text = ""
140
+ if fomo is not None:
141
+ if fomo >= 7 and c < 50:
142
+ fomo_text = (
143
+ "You feel out of the loop and your relatively low C-Score supports that feeling. "
144
+ "A bit more time on trend-dense platforms could help if staying current matters to you."
145
+ )
146
+ elif fomo <= 3 and c < 40:
147
+ fomo_text = (
148
+ "You have limited exposure to trends but do not feel much FOMO, which suggests a comfortable "
149
+ "distance from viral culture."
150
+ )
151
+
152
  summary_lines = [
153
  "## 📊 CEAR Analysis Summary",
154
  "",
 
156
  f"- **Algorithmic Risk Score (A-Risk):** **{a:.2f}**",
157
  f"- **Platform Diversity Index (D-Index):** **{d:.2f}**",
158
  ]
 
159
  if avg_variety is not None:
160
+ summary_lines.append(
161
+ f"- **Average Variety Rating (0–10):** **{avg_variety:.2f}**"
162
+ )
163
+ if satisfaction is not None:
164
+ summary_lines.append(
165
+ f"- **Feed Satisfaction (0–10):** **{satisfaction:.1f}**"
166
+ )
167
+ if fomo is not None:
168
+ summary_lines.append(
169
+ f"- **FOMO / Out-of-the-loop feeling (0–10):** **{fomo:.1f}**"
170
+ )
171
 
172
  summary_lines.extend(
173
  [
 
177
  profile,
178
  "",
179
  variety_text,
 
 
 
 
 
180
  ]
181
  )
182
+ if satisfaction_text:
183
+ summary_lines.append("")
184
+ summary_lines.append(satisfaction_text)
185
+ if fomo_text:
186
+ summary_lines.append("")
187
+ summary_lines.append(fomo_text)
188
+
189
+ summary_lines.append(
190
+ "\nThe C-Score uses a logarithmic transform of your weekly minutes, encoding diminishing returns as time increases. "
191
+ "A-Risk reflects your raw time investment and how concentrated it is on a small set of high-weight platforms. "
192
+ "D-Index captures how many platforms you use in a meaningful way (higher values mean your time is spread across more platforms)."
193
+ )
194
 
195
  summary = "\n".join(summary_lines).strip()
196
 
197
+ # Per-platform efficiency table
198
  if isinstance(per_eff, list) and per_eff:
199
  eff_df = pd.DataFrame(per_eff)
 
200
  if "platform_name" in eff_df.columns:
201
  eff_df = eff_df.rename(
202
  columns={"platform_name": "platform", "Cultural_Efficiency": "efficiency_score"}
 
208
  return summary, eff_df
209
 
210
 
211
+ # ---------------- Gradio UI ----------------
212
+
213
+ with gr.Blocks() as demo:
214
+ gr.Markdown(
215
+ "# CEAR – Cultural Exposure & Algorithmic Risk Analyzer\n"
216
+ "Enter your weekly screen time per platform, rate the variety of each feed, and optionally report how satisfied "
217
+ "you are with your feed and how much FOMO you feel."
218
+ )
219
+
220
+ with gr.Row():
221
+ with gr.Column():
222
+ gr.Markdown("### Weekly minutes & per-platform variety (0–10)")
223
+
224
+ tiktok_minutes = gr.Number(label="TikTok minutes/week", value=240, precision=0)
225
+ tiktok_variety = gr.Slider(label="TikTok variety (0–10)", minimum=0, maximum=10, step=1, value=4)
226
+
227
+ insta_minutes = gr.Number(label="Instagram minutes/week", value=180, precision=0)
228
+ insta_variety = gr.Slider(label="Instagram variety (0–10)", minimum=0, maximum=10, step=1, value=5)
229
+
230
+ youtube_minutes = gr.Number(label="YouTube minutes/week", value=120, precision=0)
231
+ youtube_variety = gr.Slider(label="YouTube variety (0–10)", minimum=0, maximum=10, step=1, value=7)
232
+
233
+ twitter_minutes = gr.Number(label="Twitter/X minutes/week", value=60, precision=0)
234
+ twitter_variety = gr.Slider(label="Twitter/X variety (0–10)", minimum=0, maximum=10, step=1, value=6)
235
+
236
+ reddit_minutes = gr.Number(label="Reddit minutes/week", value=90, precision=0)
237
+ reddit_variety = gr.Slider(label="Reddit variety (0–10)", minimum=0, maximum=10, step=1, value=8)
238
+
239
+ facebook_minutes = gr.Number(label="Facebook minutes/week", value=45, precision=0)
240
+ facebook_variety = gr.Slider(label="Facebook variety (0–10)", minimum=0, maximum=10, step=1, value=3)
241
+
242
+ other_minutes = gr.Number(label="Other platforms minutes/week", value=30, precision=0)
243
+ other_variety = gr.Slider(label="Other platforms variety (0–10)", minimum=0, maximum=10, step=1, value=5)
244
+
245
+ with gr.Column():
246
+ gr.Markdown("### Self-report (global)")
247
+
248
+ feed_satisfaction = gr.Slider(
249
+ label="Feed satisfaction (0 = miserable, 10 = very happy)",
250
+ minimum=0,
251
+ maximum=10,
252
+ step=1,
253
+ value=6,
254
+ )
255
+ fomo_level = gr.Slider(
256
+ label="FOMO / out-of-the-loop feeling (0 = none, 10 = extreme)",
257
+ minimum=0,
258
+ maximum=10,
259
+ step=1,
260
+ value=4,
261
+ )
262
+
263
+ run_btn = gr.Button("Analyze")
264
+
265
+ summary_out = gr.Markdown(label="Score Results")
266
+ eff_out = gr.Dataframe(label="Per-platform Cultural Efficiency")
267
+
268
+ run_btn.click(
269
+ fn=analyze_user_data,
270
+ inputs=[
271
+ tiktok_minutes,
272
+ tiktok_variety,
273
+ insta_minutes,
274
+ insta_variety,
275
+ youtube_minutes,
276
+ youtube_variety,
277
+ twitter_minutes,
278
+ twitter_variety,
279
+ reddit_minutes,
280
+ reddit_variety,
281
+ facebook_minutes,
282
+ facebook_variety,
283
+ other_minutes,
284
+ other_variety,
285
+ feed_satisfaction,
286
+ fomo_level,
287
  ],
288
+ outputs=[summary_out, eff_out],
289
+ )
 
 
 
 
 
 
 
 
 
290
 
291
 
292
  if __name__ == "__main__":