bigroll commited on
Commit
448eb35
·
verified ·
1 Parent(s): c9bd21a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +29 -57
app.py CHANGED
@@ -58,12 +58,12 @@ def get_connection():
58
 
59
  con = get_connection()
60
 
61
- # --- Metadata for Filters ---
62
  @st.cache_data(ttl=600)
63
  def get_filter_options():
64
- # Trim and count for cleaner lookups
65
- entities = con.sql("SELECT trim(entity) as entity, COUNT(*) as c FROM sentiment_analysis GROUP BY 1 ORDER BY c DESC").df()
66
- domains = con.sql("SELECT trim(domain) as domain, COUNT(*) as c FROM sentiment_analysis GROUP BY 1 ORDER BY c DESC").df()
67
  return entities, domains
68
 
69
  df_entities, df_domains = get_filter_options()
@@ -72,7 +72,7 @@ df_entities, df_domains = get_filter_options()
72
  st.title(T[LANG]["title"])
73
 
74
  with st.sidebar:
75
- # Dictionary lookup prevents crashes on format_func
76
  entity_lookup = dict(zip(df_entities['entity'], df_entities['c']))
77
  domain_lookup = dict(zip(df_domains['domain'], df_domains['c']))
78
 
@@ -95,43 +95,25 @@ with st.sidebar:
95
  timeframes = {"All": 9999, "Last 7 Days": 7, "Last 30 Days": 30, "Last Year": 365}
96
  time_choice = st.selectbox("Timeframe", list(timeframes.keys()))
97
 
98
- # --- Logic: Stop if no selection ---
99
  if not selected_entities:
100
  st.info(T[LANG]["select_entities_prompt"])
101
  st.stop()
102
 
103
- # --- Dynamic Granularity Logic ---
104
- days = timeframes[time_choice]
105
- if days <= 50:
106
- bucket = "1 day"
107
- elif days <= 365:
108
- bucket = "1 week"
109
- else:
110
- bucket = "1 month"
111
-
112
- # --- Query Building ---
113
  where_clause = f"WHERE entity IN ({str(selected_entities)[1:-1]})"
114
  if selected_domains:
115
  where_clause += f" AND domain IN ({str(selected_domains)[1:-1]})"
116
-
117
- # FIXED: Use INTERVAL math instead of epoch subtraction
118
  if time_choice != "All":
119
- where_clause += f" AND created_at >= (now() - INTERVAL '{days}' DAY)"
120
 
121
- # --- CRITICAL FIX: Explicit Grouping for Smooth Lines ---
122
  if all_scores:
123
- # Unpivot case: Group by Date, Entity, and Score Type
124
- # If grouping by domain is active, add domain to the group key
125
- group_keys = "1, 2, 3"
126
- select_keys = "time_bucket(interval '{bucket}', created_at) as date, entity, score_name as score_type"
127
-
128
- if group_by_domain:
129
- select_keys += ", domain"
130
- group_keys += ", 4"
131
-
132
  sql_query = f"""
133
  SELECT
134
- {select_keys},
 
 
 
135
  AVG(score_value) as score
136
  FROM (
137
  UNPIVOT sentiment_analysis
@@ -139,26 +121,18 @@ if all_scores:
139
  INTO NAME score_name VALUE score_value
140
  )
141
  {where_clause}
142
- GROUP BY {group_keys}
143
- ORDER BY 1 ASC
144
  """
145
  else:
146
- # Standard case: Group by Date and Entity
147
- group_keys = "1, 2"
148
- select_keys = "time_bucket(interval '{bucket}', created_at) as date, entity"
149
-
150
- if group_by_domain:
151
- select_keys += ", domain"
152
- group_keys += ", 3"
153
-
154
  sql_query = f"""
155
  SELECT
156
- {select_keys},
 
 
157
  AVG({score_type}) as score
158
  FROM sentiment_analysis
159
  {where_clause}
160
- GROUP BY {group_keys}
161
- ORDER BY 1 ASC
162
  """
163
 
164
  # --- Execution & Plotting ---
@@ -171,34 +145,32 @@ except Exception as e:
171
  if filtered_df.empty:
172
  st.warning(T[LANG]["no_data"])
173
  else:
174
- # Build legend labels
 
175
  if group_by_domain:
176
  filtered_df["label"] = filtered_df["entity"] + " | " + filtered_df["domain"]
177
- else:
178
- filtered_df["label"] = filtered_df["entity"]
179
 
180
  if all_scores:
181
- filtered_df["label"] = filtered_df["label"] + " | " + filtered_df["score_type"]
 
 
182
 
183
- # Plot
184
  fig = px.line(
185
- filtered_df,
186
- x="date",
187
- y="score",
188
- color="label",
189
- title=f"{T[LANG]['scores_over_time']} (Avg by {bucket})",
190
- labels={"score": "Score", "date": "Date", "label": "Legend"},
191
- markers=True # Adds dots to points for clarity
192
  )
193
 
194
- # Layout styling
195
  fig.update_layout(
196
  yaxis=dict(range=[-10, 10], gridcolor="lightgrey"),
197
  plot_bgcolor="white",
198
  legend=dict(orientation="h", y=-0.2, x=0.5, xanchor="center")
199
  )
200
 
201
- # Reference lines
202
  for val in [-5, 0, 5]:
203
  fig.add_hline(y=val, line_width=2 if val==0 else 1, line_dash="dash", line_color="black")
204
 
 
58
 
59
  con = get_connection()
60
 
61
+ # --- Cached Metadata for Filters ---
62
  @st.cache_data(ttl=600)
63
  def get_filter_options():
64
+ # Fast counts via DuckDB
65
+ entities = con.sql("SELECT entity, COUNT(*) as c FROM sentiment_analysis GROUP BY 1 ORDER BY c DESC").df()
66
+ domains = con.sql("SELECT domain, COUNT(*) as c FROM sentiment_analysis GROUP BY 1 ORDER BY c DESC").df()
67
  return entities, domains
68
 
69
  df_entities, df_domains = get_filter_options()
 
72
  st.title(T[LANG]["title"])
73
 
74
  with st.sidebar:
75
+ # Create lookup dictionaries to avoid filtering dataframes in the lambda (prevents crashes)
76
  entity_lookup = dict(zip(df_entities['entity'], df_entities['c']))
77
  domain_lookup = dict(zip(df_domains['domain'], df_domains['c']))
78
 
 
95
  timeframes = {"All": 9999, "Last 7 Days": 7, "Last 30 Days": 30, "Last Year": 365}
96
  time_choice = st.selectbox("Timeframe", list(timeframes.keys()))
97
 
98
+ # --- Query Building ---
99
  if not selected_entities:
100
  st.info(T[LANG]["select_entities_prompt"])
101
  st.stop()
102
 
103
+ # Dynamic SQL construction
 
 
 
 
 
 
 
 
 
104
  where_clause = f"WHERE entity IN ({str(selected_entities)[1:-1]})"
105
  if selected_domains:
106
  where_clause += f" AND domain IN ({str(selected_domains)[1:-1]})"
 
 
107
  if time_choice != "All":
108
+ where_clause += f" AND created_at >= (epoch(now()) - {timeframes[time_choice] * 86400})"
109
 
 
110
  if all_scores:
 
 
 
 
 
 
 
 
 
111
  sql_query = f"""
112
  SELECT
113
+ time_bucket(interval '1 day', created_at) as date,
114
+ entity,
115
+ domain,
116
+ score_name as score_type,
117
  AVG(score_value) as score
118
  FROM (
119
  UNPIVOT sentiment_analysis
 
121
  INTO NAME score_name VALUE score_value
122
  )
123
  {where_clause}
124
+ GROUP BY ALL
 
125
  """
126
  else:
 
 
 
 
 
 
 
 
127
  sql_query = f"""
128
  SELECT
129
+ time_bucket(interval '1 day', created_at) as date,
130
+ entity,
131
+ domain,
132
  AVG({score_type}) as score
133
  FROM sentiment_analysis
134
  {where_clause}
135
+ GROUP BY ALL
 
136
  """
137
 
138
  # --- Execution & Plotting ---
 
145
  if filtered_df.empty:
146
  st.warning(T[LANG]["no_data"])
147
  else:
148
+ # Build dynamic labels for the legend
149
+ color_col = "entity"
150
  if group_by_domain:
151
  filtered_df["label"] = filtered_df["entity"] + " | " + filtered_df["domain"]
152
+ color_col = "label"
 
153
 
154
  if all_scores:
155
+ current_label = filtered_df["label"] if group_by_domain else filtered_df["entity"]
156
+ filtered_df["label"] = current_label + " | " + filtered_df["score_type"]
157
+ color_col = "label"
158
 
 
159
  fig = px.line(
160
+ filtered_df.sort_values("date"),
161
+ x="date", y="score", color=color_col,
162
+ title=T[LANG]["scores_over_time"] if all_scores else T[LANG]["avg_over_time"].format(score_type.replace('_', ' ').title()),
163
+ labels={"score": "Sentiment Score", "date": "Date", "label": "Entity/Source"}
 
 
 
164
  )
165
 
166
+ # Styling
167
  fig.update_layout(
168
  yaxis=dict(range=[-10, 10], gridcolor="lightgrey"),
169
  plot_bgcolor="white",
170
  legend=dict(orientation="h", y=-0.2, x=0.5, xanchor="center")
171
  )
172
 
173
+ # Static horizontal reference lines
174
  for val in [-5, 0, 5]:
175
  fig.add_hline(y=val, line_width=2 if val==0 else 1, line_dash="dash", line_color="black")
176